Move febootstrap-supermin-helper into helper/ subdirectory.
[febootstrap.git] / helper / main.c
1 /* febootstrap-supermin-helper reimplementation in C.
2  * Copyright (C) 2009-2010 Red Hat Inc.
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17  */
18
19 /* This script builds the supermin appliance on the fly each
20  * time the appliance runs.
21  *
22  * *NOTE*: This program is designed to be very short-lived, and so we
23  * don't normally bother to free up any memory that we allocate.
24  * That's not completely true - we free up stuff if it's obvious and
25  * easy to free up, and ignore the rest.
26  */
27
28 #include <config.h>
29
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <stdarg.h>
33 #include <stdint.h>
34 #include <string.h>
35 #include <errno.h>
36 #include <fcntl.h>
37 #include <inttypes.h>
38 #include <unistd.h>
39 #include <getopt.h>
40 #include <limits.h>
41 #include <fnmatch.h>
42 #include <dirent.h>
43 #include <sys/types.h>
44 #include <sys/time.h>
45 #include <sys/stat.h>
46 #include <assert.h>
47
48 #include "error.h"
49 #include "filevercmp.h"
50 #include "fts_.h"
51 #include "full-write.h"
52 #include "hash.h"
53 #include "hash-pjw.h"
54 #include "xalloc.h"
55 #include "xvasprintf.h"
56
57 /* Directory containing candidate kernels.  We could make this
58  * configurable at some point.
59  */
60 #define KERNELDIR "/boot"
61 #define MODULESDIR "/lib/modules"
62
63 /* Buffer size used in copy operations throughout.  Large for
64  * greatest efficiency.
65  */
66 #define BUFFER_SIZE 65536
67
68 static struct timeval start_t;
69 static int verbose = 0;
70
71 static void print_timestamped_message (const char *fs, ...);
72 static const char *create_kernel (const char *hostcpu, const char *kernel);
73 static void create_appliance (char **inputs, int nr_inputs, const char *whitelist, const char *modpath, const char *initrd);
74
75 enum { HELP_OPTION = CHAR_MAX + 1 };
76
77 static const char *options = "k:vV";
78 static const struct option long_options[] = {
79   { "help", 0, 0, HELP_OPTION },
80   { "kmods", required_argument, 0, 'k' },
81   { "verbose", 0, 0, 'v' },
82   { "version", 0, 0, 'V' },
83   { 0, 0, 0, 0 }
84 };
85
86 static void
87 usage (const char *progname)
88 {
89   printf ("%s: build the supermin appliance on the fly\n"
90           "\n"
91           "Usage:\n"
92           "  %s [-options] inputs [...] whitelist host_cpu kernel initrd\n"
93           "  %s --help\n"
94           "  %s --version\n"
95           "\n"
96           "This script is used by febootstrap to build the supermin appliance\n"
97           "(kernel and initrd output files).  You should NOT need to run this\n"
98           "program directly except if you are debugging tricky supermin\n"
99           "appliance problems.\n"
100           "\n"
101           "NB: The kernel and initrd parameters are OUTPUT parameters.  If\n"
102           "those files exist, they are overwritten by the output.\n"
103           "\n"
104           "Options:\n"
105           "  --help\n"
106           "       Display this help text and exit.\n"
107           "  -k file | --kmods file\n"
108           "       Specify kernel module whitelist.\n"
109           "  --verbose | -v\n"
110           "       Enable verbose messages (give multiple times for more verbosity).\n"
111           "  --version | -V\n"
112           "       Display version number and exit.\n",
113           progname, progname, progname, progname);
114 }
115
116 int
117 main (int argc, char *argv[])
118 {
119   /* First thing: start the clock. */
120   gettimeofday (&start_t, NULL);
121
122   const char *whitelist = NULL;
123
124   /* Command line arguments. */
125   for (;;) {
126     int c = getopt_long (argc, argv, options, long_options, NULL);
127     if (c == -1) break;
128
129     switch (c) {
130     case HELP_OPTION:
131       usage (argv[0]);
132       exit (EXIT_SUCCESS);
133
134     case 'k':
135       whitelist = optarg;
136       break;
137
138     case 'v':
139       verbose++;
140       break;
141
142     case 'V':
143       printf (PACKAGE_NAME " " PACKAGE_VERSION "\n");
144       exit (EXIT_SUCCESS);
145
146     default:
147       usage (argv[0]);
148       exit (EXIT_FAILURE);
149     }
150   }
151
152   char **inputs = &argv[optind];
153   int nr_inputs = argc - optind - 3;
154
155   if (nr_inputs < 1) {
156     usage (argv[0]);
157     exit (EXIT_FAILURE);
158   }
159
160   /* See: https://bugzilla.redhat.com/show_bug.cgi?id=558593 */
161   const char *hostcpu = argv[argc-3];
162
163   /* Output files. */
164   const char *kernel = argv[argc-2];
165   const char *initrd = argv[argc-1];
166
167   if (verbose) {
168     print_timestamped_message ("whitelist = %s, "
169                                "host_cpu = %s, "
170                                "kernel = %s, "
171                                "initrd = %s",
172                                whitelist ? : "(not specified)",
173                                hostcpu, kernel, initrd);
174     int i;
175     for (i = 0; i < nr_inputs; ++i)
176       print_timestamped_message ("inputs[%d] = %s", i, inputs[i]);
177   }
178
179   /* Remove the output files if they exist. */
180   unlink (kernel);
181   unlink (initrd);
182
183   /* Create kernel output file. */
184   const char *modpath;
185   modpath = create_kernel (hostcpu, kernel);
186
187   if (verbose)
188     print_timestamped_message ("finished creating kernel");
189
190   /* Create the appliance. */
191   create_appliance (inputs, nr_inputs, whitelist, modpath, initrd);
192
193   if (verbose)
194     print_timestamped_message ("finished creating appliance");
195
196   exit (EXIT_SUCCESS);
197 }
198
199 /* Compute Y - X and return the result in milliseconds.
200  * Approximately the same as this code:
201  * http://www.mpp.mpg.de/~huber/util/timevaldiff.c
202  */
203 static int64_t
204 timeval_diff (const struct timeval *x, const struct timeval *y)
205 {
206   int64_t msec;
207
208   msec = (y->tv_sec - x->tv_sec) * 1000;
209   msec += (y->tv_usec - x->tv_usec) / 1000;
210   return msec;
211 }
212
213 static void
214 print_timestamped_message (const char *fs, ...)
215 {
216   struct timeval tv;
217   gettimeofday (&tv, NULL);
218
219   va_list args;
220   char *msg;
221   int err;
222
223   va_start (args, fs);
224   err = vasprintf (&msg, fs, args);
225   va_end (args);
226
227   if (err < 0) return;
228
229   fprintf (stderr, "supermin helper [%05" PRIi64 "ms] %s\n",
230            timeval_diff (&start_t, &tv), msg);
231
232   free (msg);
233 }
234
235 static char **read_dir (const char *dir);
236 static char **filter (char **strings, int (*)(const char *));
237 static char **filter_fnmatch (char **strings, const char *patt, int flags);
238 static char **filter_notmatching_substring (char **strings, const char *sub);
239 static void sort (char **strings, int (*compare) (const void *, const void *));
240 static int isdir (const char *path);
241
242 static int
243 reverse_filevercmp (const void *p1, const void *p2)
244 {
245   const char *s1 = * (char * const *) p1;
246   const char *s2 = * (char * const *) p2;
247
248   /* Note, arguments are reversed to achieve a reverse sort. */
249   return filevercmp (s2, s1);
250 }
251
252 static char *
253 get_modpath (const char *kernel_name)
254 {
255   /* Ignore "vmlinuz-" at the beginning of the kernel name. */
256   const char *version = &kernel_name[8];
257
258   /* /lib/modules/<version> */
259   char *modpath = xasprintf (MODULESDIR "/%s", version);
260   if (!modpath) {
261     perror ("xasprintf");
262     exit (EXIT_FAILURE);
263   }
264
265   return modpath;
266 }
267
268 /* kernel_name is "vmlinuz-*".  Check if there is a corresponding
269  * module path in /lib/modules.
270  */
271 static int
272 has_modpath (const char *kernel_name)
273 {
274   char *modpath = get_modpath (kernel_name);
275
276   if (verbose)
277     fprintf (stderr, "checking modpath %s is a directory\n", modpath);
278
279   int r = isdir (modpath);
280
281   if (r) {
282     if (verbose)
283       fprintf (stderr, "picked %s because modpath %s exists\n",
284                kernel_name, modpath);
285     free (modpath);
286     return 1;
287   }
288   else {
289     free (modpath);
290     return 0;
291   }
292 }
293
294 /* Create the kernel.  This chooses an appropriate kernel and makes a
295  * symlink to it.
296  *
297  * Look for the most recent kernel named vmlinuz-*.<arch>* which has a
298  * corresponding directory in /lib/modules/. If the architecture is
299  * x86, look for any x86 kernel.
300  *
301  * RHEL 5 didn't append the arch to the kernel name, so look for
302  * kernels without arch second.
303  *
304  * If no suitable kernel can be found, exit with an error.
305  *
306  * This function returns the module path (ie. /lib/modules/<version>).
307  */
308 static const char *
309 create_kernel (const char *hostcpu, const char *kernel)
310 {
311   char **all_files = read_dir (KERNELDIR);
312
313   /* In original: ls -1dvr /boot/vmlinuz-*.$arch* 2>/dev/null | grep -v xen */
314   const char *patt;
315   if (hostcpu[0] == 'i' && hostcpu[2] == '8' && hostcpu[3] == '6' &&
316       hostcpu[4] == '\0')
317     patt = "vmlinuz-*.i?86*";
318   else
319     patt = xasprintf ("vmlinuz-*.%s*", hostcpu);
320
321   char **candidates;
322   candidates = filter_fnmatch (all_files, patt, FNM_NOESCAPE);
323   candidates = filter_notmatching_substring (candidates, "xen");
324   candidates = filter (candidates, has_modpath);
325
326   if (candidates[0] == NULL) {
327     /* In original: ls -1dvr /boot/vmlinuz-* 2>/dev/null | grep -v xen */
328     patt = "vmlinuz-*";
329     candidates = filter_fnmatch (all_files, patt, FNM_NOESCAPE);
330     candidates = filter_notmatching_substring (candidates, "xen");
331     candidates = filter (candidates, has_modpath);
332
333     if (candidates[0] == NULL)
334       goto no_kernels;
335   }
336
337   sort (candidates, reverse_filevercmp);
338
339   /* Choose the first candidate. */
340   char *tmp = xasprintf (KERNELDIR "/%s", candidates[0]);
341
342   if (verbose)
343     fprintf (stderr, "creating symlink %s -> %s\n", kernel, tmp);
344
345   if (symlink (tmp, kernel) == -1)
346     error (EXIT_FAILURE, errno, "symlink kernel");
347
348   free (tmp);
349
350   return get_modpath (candidates[0]);
351
352   /* Print more diagnostics here than the old script did. */
353  no_kernels:
354   fprintf (stderr,
355            "febootstrap-supermin-helper: failed to find a suitable kernel.\n"
356            "I looked for kernels in " KERNELDIR " and modules in " MODULESDIR
357            ".\n"
358            "If this is a Xen guest, and you only have Xen domU kernels\n"
359            "installed, try installing a fullvirt kernel (only for\n"
360            "febootstrap use, you shouldn't boot the Xen guest with it).\n");
361   exit (EXIT_FAILURE);
362 }
363
364 static void iterate_inputs (char **inputs, int nr_inputs);
365 static void iterate_input_directory (const char *dirname, int dirfd);
366 static void write_kernel_modules (const char *whitelist, const char *modpath);
367 static void write_hostfiles (const char *hostfiles_file);
368 static void write_to_fd (const void *buffer, size_t len);
369 static void write_file_to_fd (const char *filename);
370 static void write_file_len_to_fd (const char *filename, size_t len);
371 static void write_padding (size_t len);
372 static char **load_file (const char *filename);
373 static void cpio_append_fts_entry (FTSENT *entry);
374 static void cpio_append_stat (const char *filename, struct stat *);
375 static void cpio_append (const char *filename);
376 static void cpio_append_trailer (void);
377
378 static int out_fd = -1;
379 static off_t out_offset = 0;
380
381 /* Create the appliance.
382  *
383  * The initrd consists of these components concatenated together:
384  *
385  * (1) The base skeleton appliance that we constructed at build time.
386  *     format = plain cpio
387  * (2) The host files which match wildcards in *.supermin.hostfiles.
388  *     input format = plain text, output format = plain cpio
389  * (3) The modules from modpath which are on the module whitelist.
390  *     output format = plain cpio
391  *
392  * The original shell script used the external cpio program to create
393  * parts (2) and (3), but we have decided it's going to be faster if
394  * we just write out the data outselves.  The reasons are that
395  * external cpio is slow (particularly when used with SELinux because
396  * it does 512 byte reads), and the format that we're writing is
397  * narrow and well understood, because we only care that the Linux
398  * kernel can read it.
399  *
400  * This version contains some improvements over the C version written
401  * for libguestfs, in that we can have multiple base images (or
402  * hostfiles) or use a directory to store these files.
403  */
404 static void
405 create_appliance (char **inputs, int nr_inputs,
406                   const char *whitelist,
407                   const char *modpath,
408                   const char *initrd)
409 {
410   out_fd = open (initrd, O_WRONLY | O_CREAT | O_TRUNC | O_NOCTTY, 0644);
411   if (out_fd == -1)
412     error (EXIT_FAILURE, errno, "open: %s", initrd);
413   out_offset = 0;
414
415   iterate_inputs (inputs, nr_inputs);
416
417   /* Kernel modules (3). */
418   write_kernel_modules (whitelist, modpath);
419
420   cpio_append_trailer ();
421
422   /* Finish off and close output file. */
423   if (close (out_fd) == -1)
424     error (EXIT_FAILURE, errno, "close: %s", initrd);
425 }
426
427 /* Iterate over the inputs to find out what they are, visiting
428  * directories if specified.
429  */
430 static void
431 iterate_inputs (char **inputs, int nr_inputs)
432 {
433   int i;
434   for (i = 0; i < nr_inputs; ++i) {
435     if (verbose)
436       print_timestamped_message ("visiting %s", inputs[i]);
437
438     int fd = open (inputs[i], O_RDONLY);
439     if (fd == -1)
440       error (EXIT_FAILURE, errno, "open: %s", inputs[i]);
441
442     struct stat statbuf;
443     if (fstat (fd, &statbuf) == -1)
444       error (EXIT_FAILURE, errno, "fstat: %s", inputs[i]);
445
446     /* Directory? */
447     if (S_ISDIR (statbuf.st_mode))
448       iterate_input_directory (inputs[i], fd);
449     else if (S_ISREG (statbuf.st_mode)) {
450       /* Is it a cpio file? */
451       char buf[6];
452       if (read (fd, buf, 6) == 6 && memcmp (buf, "070701", 6) == 0)
453         /* Yes, a cpio file.  This is a skeleton appliance, case (1). */
454         write_file_to_fd (inputs[i]);
455       else
456         /* No, must be hostfiles, case (2). */
457         write_hostfiles (inputs[i]);
458     }
459     else
460       error (EXIT_FAILURE, 0, "%s: input is not a regular file or directory",
461              inputs[i]);
462
463     close (fd);
464   }
465 }
466
467 static void
468 iterate_input_directory (const char *dirname, int dirfd)
469 {
470   char path[PATH_MAX];
471   strcpy (path, dirname);
472   size_t len = strlen (dirname);
473   path[len++] = '/';
474
475   char *inputs[] = { path };
476
477   DIR *dir = fdopendir (dirfd);
478   if (dir == NULL)
479     error (EXIT_FAILURE, errno, "fdopendir: %s", dirname);
480
481   struct dirent *d;
482   while ((errno = 0, d = readdir (dir)) != NULL) {
483     if (d->d_name[0] == '.') /* ignore ., .. and any hidden files. */
484       continue;
485
486     strcpy (&path[len], d->d_name);
487     iterate_inputs (inputs, 1);
488   }
489
490   if (errno != 0)
491     error (EXIT_FAILURE, errno, "readdir: %s", dirname);
492
493   if (closedir (dir) == -1)
494     error (EXIT_FAILURE, errno, "closedir: %s", dirname);
495 }
496
497 /* Copy kernel modules.
498  *
499  * Find every file under modpath.
500  *
501  * Exclude all *.ko files, *except* ones which match names in
502  * the whitelist (which may contain wildcards).  Include all
503  * other files.
504  *
505  * Add chosen files to the output.
506  *
507  * whitelist_file may be NULL, to include ALL kernel modules.
508  */
509 static void
510 write_kernel_modules (const char *whitelist_file, const char *modpath)
511 {
512   char **whitelist = NULL;
513   if (whitelist_file != NULL)
514     whitelist = load_file (whitelist_file);
515
516   char *paths[2] = { (char *) modpath, NULL };
517   FTS *fts = fts_open (paths, FTS_COMFOLLOW|FTS_PHYSICAL, NULL);
518   if (fts == NULL)
519     error (EXIT_FAILURE, errno, "write_kernel_modules: fts_open: %s", modpath);
520
521   for (;;) {
522     errno = 0;
523     FTSENT *entry = fts_read (fts);
524     if (entry == NULL && errno != 0)
525       error (EXIT_FAILURE, errno, "write_kernel_modules: fts_read: %s", modpath);
526     if (entry == NULL)
527       break;
528
529     /* Ignore directories being visited in post-order. */
530     if (entry->fts_info & FTS_DP)
531       continue;
532
533     /* Is it a *.ko file? */
534     if (entry->fts_namelen >= 3 &&
535         entry->fts_name[entry->fts_namelen-3] == '.' &&
536         entry->fts_name[entry->fts_namelen-2] == 'k' &&
537         entry->fts_name[entry->fts_namelen-1] == 'o') {
538       if (whitelist) {
539         /* Is it a *.ko file which is on the whitelist? */
540         size_t j;
541         for (j = 0; whitelist[j] != NULL; ++j) {
542           int r;
543           r = fnmatch (whitelist[j], entry->fts_name, 0);
544           if (r == 0) {
545             /* It's on the whitelist, so include it. */
546             if (verbose >= 2)
547               fprintf (stderr, "including kernel module %s (matches whitelist entry %s)\n",
548                        entry->fts_name, whitelist[j]);
549             cpio_append_fts_entry (entry);
550             break;
551           } else if (r != FNM_NOMATCH)
552             error (EXIT_FAILURE, 0, "internal error: fnmatch ('%s', '%s', %d) returned unexpected non-zero value %d\n",
553                    whitelist[j], entry->fts_name, 0, r);
554         } /* for (j) */
555       } else { /* whitelist == NULL, always include */
556         if (verbose >= 2)
557           fprintf (stderr, "including kernel module %s\n", entry->fts_name);
558         cpio_append_fts_entry (entry);
559       }
560     } else
561       /* It's some other sort of file, or a directory, always include. */
562       cpio_append_fts_entry (entry);
563   }
564
565   if (fts_close (fts) == -1)
566     error (EXIT_FAILURE, errno, "write_kernel_modules: fts_close: %s", modpath);
567 }
568
569 /* Copy the host files.
570  *
571  * Read the list of entries in hostfiles (which may contain
572  * wildcards).  Look them up in the filesystem, and add those files
573  * that exist.  Ignore any files that don't exist or are not readable.
574  */
575 static void
576 write_hostfiles (const char *hostfiles_file)
577 {
578   char **hostfiles = load_file (hostfiles_file);
579
580   /* Hostfiles list can contain "." before each path - ignore it.
581    * It also contains each directory name before we enter it.  But
582    * we don't read that until we see a wildcard for that directory.
583    */
584   size_t i, j;
585   for (i = 0; hostfiles[i] != NULL; ++i) {
586     char *hostfile = hostfiles[i];
587     if (hostfile[0] == '.')
588       hostfile++;
589
590     struct stat statbuf;
591
592     /* Is it a wildcard? */
593     if (strchr (hostfile, '*') || strchr (hostfile, '?')) {
594       char *dirname = xstrdup (hostfile);
595       char *patt = strrchr (dirname, '/');
596       assert (patt);
597       *patt++ = '\0';
598
599       char **files = read_dir (dirname);
600       files = filter_fnmatch (files, patt, FNM_NOESCAPE);
601
602       /* Add matching files. */
603       for (j = 0; files[j] != NULL; ++j) {
604         char *tmp = xasprintf ("%s/%s", dirname, files[j]);
605
606         if (verbose >= 2)
607           fprintf (stderr, "including host file %s (matches %s)\n", tmp, patt);
608
609         cpio_append (tmp);
610
611         free (tmp);
612       }
613     }
614     /* Else does this file/directory/whatever exist? */
615     else if (lstat (hostfile, &statbuf) == 0) {
616       if (verbose >= 2)
617         fprintf (stderr, "including host file %s (directly referenced)\n",
618                  hostfile);
619
620       cpio_append_stat (hostfile, &statbuf);
621     } /* Ignore files that don't exist. */
622   }
623 }
624
625 /*----------*/
626 /* Helper functions. */
627
628 static void
629 add_string (char ***argv, size_t *n_used, size_t *n_alloc, const char *str)
630 {
631   char *new_str;
632
633   if (*n_used >= *n_alloc)
634     *argv = x2nrealloc (*argv, n_alloc, sizeof (char *));
635
636   if (str)
637     new_str = xstrdup (str);
638   else
639     new_str = NULL;
640
641   (*argv)[*n_used] = new_str;
642
643   (*n_used)++;
644 }
645
646 static size_t
647 count_strings (char *const *argv)
648 {
649   size_t argc;
650
651   for (argc = 0; argv[argc] != NULL; ++argc)
652     ;
653   return argc;
654 }
655
656 struct dir_cache {
657   char *path;
658   char **files;
659 };
660
661 static size_t
662 dir_cache_hash (void const *x, size_t table_size)
663 {
664   struct dir_cache const *p = x;
665   return hash_pjw (p->path, table_size);
666 }
667
668 static bool
669 dir_cache_compare (void const *x, void const *y)
670 {
671   struct dir_cache const *p = x;
672   struct dir_cache const *q = y;
673   return strcmp (p->path, q->path) == 0;
674 }
675
676 /* Read a directory into a list of strings.
677  *
678  * Previously looked up directories are cached and returned quickly,
679  * saving some considerable amount of time compared to reading the
680  * directory over again.  However this means you really must not
681  * alter the array of strings that are returned.
682  *
683  * Returns an empty list if the directory cannot be opened.
684  */
685 static char **
686 read_dir (const char *name)
687 {
688   static Hash_table *ht = NULL;
689
690   if (!ht)
691     ht = hash_initialize (1024, NULL, dir_cache_hash, dir_cache_compare, NULL);
692
693   struct dir_cache key = { .path = (char *) name };
694   struct dir_cache *p = hash_lookup (ht, &key);
695   if (p)
696     return p->files;
697
698   char **files = NULL;
699   size_t n_used = 0, n_alloc = 0;
700
701   DIR *dir = opendir (name);
702   if (!dir) {
703     /* If it fails to open, that's OK, skip to the end. */
704     /*perror (name);*/
705     goto done;
706   }
707
708   for (;;) {
709     errno = 0;
710     struct dirent *d = readdir (dir);
711     if (d == NULL) {
712       if (errno != 0)
713         /* But if it fails here, after opening and potentially reading
714          * part of the directory, that's a proper failure - inform the
715          * user and exit.
716          */
717         error (EXIT_FAILURE, errno, "%s", name);
718       break;
719     }
720
721     add_string (&files, &n_used, &n_alloc, d->d_name);
722   }
723
724   if (closedir (dir) == -1)
725     error (EXIT_FAILURE, errno, "closedir: %s", name);
726
727  done:
728   /* NULL-terminate the array. */
729   add_string (&files, &n_used, &n_alloc, NULL);
730
731   /* Add it to the hash for next time. */
732   p = xmalloc (sizeof *p);
733   p->path = (char *) name;
734   p->files = files;
735   p = hash_insert (ht, p);
736   assert (p != NULL);
737
738   return files;
739 }
740
741 /* Filter a list of strings, returning only those where f(s) != 0. */
742 static char **
743 filter (char **strings, int (*f) (const char *))
744 {
745   char **out = NULL;
746   size_t n_used = 0, n_alloc = 0;
747
748   int i;
749   for (i = 0; strings[i] != NULL; ++i) {
750     if (f (strings[i]) != 0)
751       add_string (&out, &n_used, &n_alloc, strings[i]);
752   }
753
754   add_string (&out, &n_used, &n_alloc, NULL);
755   return out;
756 }
757
758 /* Filter a list of strings and return only those matching the wildcard. */
759 static char **
760 filter_fnmatch (char **strings, const char *patt, int flags)
761 {
762   char **out = NULL;
763   size_t n_used = 0, n_alloc = 0;
764
765   int i, r;
766   for (i = 0; strings[i] != NULL; ++i) {
767     r = fnmatch (patt, strings[i], flags);
768     if (r == 0)
769       add_string (&out, &n_used, &n_alloc, strings[i]);
770     else if (r != FNM_NOMATCH)
771       error (EXIT_FAILURE, 0, "internal error: fnmatch ('%s', '%s', %d) returned unexpected non-zero value %d\n",
772              patt, strings[i], flags, r);
773   }
774
775   add_string (&out, &n_used, &n_alloc, NULL);
776   return out;
777 }
778
779 /* Filter a list of strings and return only those which DON'T contain sub. */
780 static char **
781 filter_notmatching_substring (char **strings, const char *sub)
782 {
783   char **out = NULL;
784   size_t n_used = 0, n_alloc = 0;
785
786   int i;
787   for (i = 0; strings[i] != NULL; ++i) {
788     if (strstr (strings[i], sub) == NULL)
789       add_string (&out, &n_used, &n_alloc, strings[i]);
790   }
791
792   add_string (&out, &n_used, &n_alloc, NULL);
793   return out;
794 }
795
796 /* Sort a list of strings, in place, with the comparison function supplied. */
797 static void
798 sort (char **strings, int (*compare) (const void *, const void *))
799 {
800   qsort (strings, count_strings (strings), sizeof (char *), compare);
801 }
802
803 /* Return true iff path exists and is a directory.  This version
804  * follows symlinks.
805  */
806 static int
807 isdir (const char *path)
808 {
809   struct stat statbuf;
810
811   if (stat (path, &statbuf) == -1)
812     return 0;
813
814   return S_ISDIR (statbuf.st_mode);
815 }
816
817 /* Copy contents of buffer to out_fd and keep out_offset correct. */
818 static void
819 write_to_fd (const void *buffer, size_t len)
820 {
821   if (full_write (out_fd, buffer, len) != len)
822     error (EXIT_FAILURE, errno, "write");
823   out_offset += len;
824 }
825
826 /* Copy contents of file to out_fd. */
827 static void
828 write_file_to_fd (const char *filename)
829 {
830   char buffer[BUFFER_SIZE];
831   int fd2;
832   ssize_t r;
833
834   if (verbose >= 2)
835     fprintf (stderr, "write_file_to_fd %s -> %d\n", filename, out_fd);
836
837   fd2 = open (filename, O_RDONLY);
838   if (fd2 == -1)
839     error (EXIT_FAILURE, errno, "open: %s", filename);
840   for (;;) {
841     r = read (fd2, buffer, sizeof buffer);
842     if (r == 0)
843       break;
844     if (r == -1) {
845       if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR)
846         continue;
847       error (EXIT_FAILURE, errno, "read: %s", filename);
848     }
849     write_to_fd (buffer, r);
850   }
851
852   if (close (fd2) == -1)
853     error (EXIT_FAILURE, errno, "close: %s", filename);
854 }
855
856 /* Copy file of given length to output, and fail if the file has
857  * changed size.
858  */
859 static void
860 write_file_len_to_fd (const char *filename, size_t len)
861 {
862   char buffer[BUFFER_SIZE];
863   size_t count = 0;
864
865   if (verbose >= 2)
866     fprintf (stderr, "write_file_to_fd %s -> %d\n", filename, out_fd);
867
868   int fd2 = open (filename, O_RDONLY);
869   if (fd2 == -1)
870     error (EXIT_FAILURE, errno, "open: %s", filename);
871   for (;;) {
872     ssize_t r = read (fd2, buffer, sizeof buffer);
873     if (r == 0)
874       break;
875     if (r == -1) {
876       if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR)
877         continue;
878       error (EXIT_FAILURE, errno, "read: %s", filename);
879     }
880     write_to_fd (buffer, r);
881     count += r;
882     if (count > len)
883       error (EXIT_FAILURE, 0, "write_file_len_to_fd: %s: file has increased in size\n", filename);
884   }
885
886   if (close (fd2) == -1)
887     error (EXIT_FAILURE, errno, "close: %s", filename);
888
889   if (count != len)
890     error (EXIT_FAILURE, 0, "febootstrap-supermin-helper: write_file_len_to_fd: %s: file has changed size\n", filename);
891 }
892
893 /* Load in a file, returning a list of lines. */
894 static char **
895 load_file (const char *filename)
896 {
897   char **lines = 0;
898   size_t n_used = 0, n_alloc = 0;
899
900   FILE *fp;
901   fp = fopen (filename, "r");
902   if (fp == NULL)
903     error (EXIT_FAILURE, errno, "fopen: %s", filename);
904
905   char line[4096];
906   while (fgets (line, sizeof line, fp)) {
907     size_t len = strlen (line);
908     if (len > 0 && line[len-1] == '\n')
909       line[len-1] = '\0';
910     add_string (&lines, &n_used, &n_alloc, line);
911   }
912
913   add_string (&lines, &n_used, &n_alloc, NULL);
914   return lines;
915 }
916
917 /* Append the file pointed to by FTSENT to the cpio output. */
918 static void
919 cpio_append_fts_entry (FTSENT *entry)
920 {
921   if (entry->fts_info & FTS_NS || entry->fts_info & FTS_NSOK)
922     cpio_append (entry->fts_path);
923   else
924     cpio_append_stat (entry->fts_path, entry->fts_statp);
925 }
926
927 /* Append the file named 'filename' to the cpio output. */
928 static void
929 cpio_append (const char *filename)
930 {
931   struct stat statbuf;
932
933   if (lstat (filename, &statbuf) == -1)
934     error (EXIT_FAILURE, errno, "lstat: %s", filename);
935   cpio_append_stat (filename, &statbuf);
936 }
937
938 /* Append the file to the cpio output. */
939 #define PADDING(len) ((((len) + 3) & ~3) - (len))
940
941 #define CPIO_HEADER_LEN (6 + 13*8)
942
943 static void
944 cpio_append_stat (const char *filename, struct stat *statbuf)
945 {
946   const char *orig_filename = filename;
947
948   if (*filename == '/')
949     filename++;
950   if (*filename == '\0')
951     filename = ".";
952
953   if (verbose >= 2)
954     fprintf (stderr, "cpio_append_stat %s 0%o -> %d\n",
955              orig_filename, statbuf->st_mode, out_fd);
956
957   /* Regular files and symlinks are the only ones that have a "body"
958    * in this cpio entry.
959    */
960   int has_body = S_ISREG (statbuf->st_mode) || S_ISLNK (statbuf->st_mode);
961
962   size_t len = strlen (filename) + 1;
963
964   char header[CPIO_HEADER_LEN + 1];
965   snprintf (header, sizeof header,
966             "070701"            /* magic */
967             "%08X"              /* inode */
968             "%08X"              /* mode */
969             "%08X" "%08X"       /* uid, gid */
970             "%08X"              /* nlink */
971             "%08X"              /* mtime */
972             "%08X"              /* file length */
973             "%08X" "%08X"       /* device holding file major, minor */
974             "%08X" "%08X"       /* for specials, device major, minor */
975             "%08X"              /* name length (including \0 byte) */
976             "%08X",             /* checksum (not used by the kernel) */
977             (unsigned) statbuf->st_ino, statbuf->st_mode,
978             statbuf->st_uid, statbuf->st_gid,
979             (unsigned) statbuf->st_nlink, (unsigned) statbuf->st_mtime,
980             has_body ? (unsigned) statbuf->st_size : 0,
981             major (statbuf->st_dev), minor (statbuf->st_dev),
982             major (statbuf->st_rdev), minor (statbuf->st_rdev),
983             (unsigned) len, 0);
984
985   /* Write the header. */
986   write_to_fd (header, CPIO_HEADER_LEN);
987
988   /* Follow with the filename, and pad it. */
989   write_to_fd (filename, len);
990   size_t padding_len = PADDING (CPIO_HEADER_LEN + len);
991   write_padding (padding_len);
992
993   /* Follow with the file or symlink content, and pad it. */
994   if (has_body) {
995     if (S_ISREG (statbuf->st_mode))
996       write_file_len_to_fd (orig_filename, statbuf->st_size);
997     else if (S_ISLNK (statbuf->st_mode)) {
998       char tmp[PATH_MAX];
999       if (readlink (orig_filename, tmp, sizeof tmp) == -1)
1000         error (EXIT_FAILURE, errno, "readlink: %s", orig_filename);
1001       write_to_fd (tmp, statbuf->st_size);
1002     }
1003
1004     padding_len = PADDING (statbuf->st_size);
1005     write_padding (padding_len);
1006   }
1007 }
1008
1009 /* CPIO voodoo. */
1010 static void
1011 cpio_append_trailer (void)
1012 {
1013   struct stat statbuf;
1014   memset (&statbuf, 0, sizeof statbuf);
1015   statbuf.st_nlink = 1;
1016   cpio_append_stat ("TRAILER!!!", &statbuf);
1017
1018   /* CPIO seems to pad up to the next block boundary, ie. up to
1019    * the next 512 bytes.
1020    */
1021   write_padding (((out_offset + 511) & ~511) - out_offset);
1022   assert ((out_offset & 511) == 0);
1023 }
1024
1025 /* Write 'len' bytes of zeroes out. */
1026 static void
1027 write_padding (size_t len)
1028 {
1029   static const char buffer[512] = { 0 };
1030
1031   while (len > 0) {
1032     size_t n = len < sizeof buffer ? len : sizeof buffer;
1033     write_to_fd (buffer, n);
1034     len -= n;
1035   }
1036 }