1a43cf20731c374c907c51fb1a4fcd6a4f7269d2
[febootstrap.git] / febootstrap-supermin-helper.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_fnmatch (char **strings, const char *patt, int flags);
237 static char **filter_notmatching_substring (char **strings, const char *sub);
238 static void sort (char **strings, int (*compare) (const void *, const void *));
239 static int isdir (const char *path);
240
241 static int
242 reverse_filevercmp (const void *p1, const void *p2)
243 {
244   const char *s1 = * (char * const *) p1;
245   const char *s2 = * (char * const *) p2;
246
247   /* Note, arguments are reversed to achieve a reverse sort. */
248   return filevercmp (s2, s1);
249 }
250
251 /* Create the kernel.  This chooses an appropriate kernel and makes a
252  * symlink to it.
253  *
254  * Look for the most recent kernel named vmlinuz-*.<arch>* which has a
255  * corresponding directory in /lib/modules/. If the architecture is
256  * x86, look for any x86 kernel.
257  *
258  * RHEL 5 didn't append the arch to the kernel name, so look for
259  * kernels without arch second.
260  *
261  * If no suitable kernel can be found, exit with an error.
262  *
263  * This function returns the module path (ie. /lib/modules/<version>).
264  */
265 static const char *
266 create_kernel (const char *hostcpu, const char *kernel)
267 {
268   char **all_files = read_dir (KERNELDIR);
269
270   /* In original: ls -1dvr /boot/vmlinuz-*.$arch* 2>/dev/null | grep -v xen */
271   const char *patt;
272   if (hostcpu[0] == 'i' && hostcpu[2] == '8' && hostcpu[3] == '6' &&
273       hostcpu[4] == '\0')
274     patt = "vmlinuz-*.i?86*";
275   else
276     patt = xasprintf ("vmlinuz-*.%s*", hostcpu);
277
278   char **candidates;
279   candidates = filter_fnmatch (all_files, patt, FNM_NOESCAPE);
280   candidates = filter_notmatching_substring (candidates, "xen");
281
282   if (candidates[0] == NULL) {
283     /* In original: ls -1dvr /boot/vmlinuz-* 2>/dev/null | grep -v xen */
284     patt = "vmlinuz-*";
285     candidates = filter_fnmatch (all_files, patt, FNM_NOESCAPE);
286     candidates = filter_notmatching_substring (candidates, "xen");
287
288     if (candidates[0] == NULL)
289       goto no_kernels;
290   }
291
292   sort (candidates, reverse_filevercmp);
293
294   /* Choose the first candidate which has a corresponding /lib/modules
295    * directory.
296    */
297   int i;
298   for (i = 0; candidates[i] != NULL; ++i) {
299     if (verbose >= 2)
300       fprintf (stderr, "candidate kernel: " KERNELDIR "/%s\n", candidates[i]);
301
302     /* Ignore "vmlinuz-" at the beginning of the kernel name. */
303     const char *version = &candidates[i][8];
304
305     /* /lib/modules/<version> */
306     char *modpath = xasprintf (MODULESDIR "/%s", version);
307
308     if (verbose >= 2)
309       fprintf (stderr, "checking modpath %s is a directory\n", modpath);
310
311     if (isdir (modpath)) {
312       if (verbose >= 2)
313         fprintf (stderr, "picked %s because modpath %s exists\n",
314                  candidates[i], modpath);
315
316       char *tmp = xasprintf (KERNELDIR "/%s", candidates[i]);
317
318       if (verbose >= 2)
319         fprintf (stderr, "creating symlink %s -> %s\n", kernel, tmp);
320
321       if (symlink (tmp, kernel) == -1)
322         error (EXIT_FAILURE, errno, "symlink kernel");
323
324       free (tmp);
325
326       return modpath;
327     }
328   }
329
330   /* Print more diagnostics here than the old script did. */
331  no_kernels:
332   fprintf (stderr,
333            "febootstrap-supermin-helper: failed to find a suitable kernel.\n"
334            "I looked for kernels in " KERNELDIR " and modules in " MODULESDIR
335            ".\n"
336            "If this is a Xen guest, and you only have Xen domU kernels\n"
337            "installed, try installing a fullvirt kernel (only for\n"
338            "febootstrap use, you shouldn't boot the Xen guest with it).\n");
339   exit (EXIT_FAILURE);
340 }
341
342 static void iterate_inputs (char **inputs, int nr_inputs);
343 static void iterate_input_directory (const char *dirname, int dirfd);
344 static void write_kernel_modules (const char *whitelist, const char *modpath);
345 static void write_hostfiles (const char *hostfiles_file);
346 static void write_to_fd (const void *buffer, size_t len);
347 static void write_file_to_fd (const char *filename);
348 static void write_file_len_to_fd (const char *filename, size_t len);
349 static void write_padding (size_t len);
350 static char **load_file (const char *filename);
351 static void cpio_append_fts_entry (FTSENT *entry);
352 static void cpio_append_stat (const char *filename, struct stat *);
353 static void cpio_append (const char *filename);
354 static void cpio_append_trailer (void);
355
356 static int out_fd = -1;
357 static off_t out_offset = 0;
358
359 /* Create the appliance.
360  *
361  * The initrd consists of these components concatenated together:
362  *
363  * (1) The base skeleton appliance that we constructed at build time.
364  *     format = plain cpio
365  * (2) The host files which match wildcards in *.supermin.hostfiles.
366  *     input format = plain text, output format = plain cpio
367  * (3) The modules from modpath which are on the module whitelist.
368  *     output format = plain cpio
369  *
370  * The original shell script used the external cpio program to create
371  * parts (2) and (3), but we have decided it's going to be faster if
372  * we just write out the data outselves.  The reasons are that
373  * external cpio is slow (particularly when used with SELinux because
374  * it does 512 byte reads), and the format that we're writing is
375  * narrow and well understood, because we only care that the Linux
376  * kernel can read it.
377  *
378  * This version contains some improvements over the C version written
379  * for libguestfs, in that we can have multiple base images (or
380  * hostfiles) or use a directory to store these files.
381  */
382 static void
383 create_appliance (char **inputs, int nr_inputs,
384                   const char *whitelist,
385                   const char *modpath,
386                   const char *initrd)
387 {
388   out_fd = open (initrd, O_WRONLY | O_CREAT | O_TRUNC | O_NOCTTY, 0644);
389   if (out_fd == -1)
390     error (EXIT_FAILURE, errno, "open: %s", initrd);
391   out_offset = 0;
392
393   iterate_inputs (inputs, nr_inputs);
394
395   /* Kernel modules (3). */
396   write_kernel_modules (whitelist, modpath);
397
398   cpio_append_trailer ();
399
400   /* Finish off and close output file. */
401   if (close (out_fd) == -1)
402     error (EXIT_FAILURE, errno, "close: %s", initrd);
403 }
404
405 /* Iterate over the inputs to find out what they are, visiting
406  * directories if specified.
407  */
408 static void
409 iterate_inputs (char **inputs, int nr_inputs)
410 {
411   int i;
412   for (i = 0; i < nr_inputs; ++i) {
413     if (verbose)
414       print_timestamped_message ("visiting %s", inputs[i]);
415
416     int fd = open (inputs[i], O_RDONLY);
417     if (fd == -1)
418       error (EXIT_FAILURE, errno, "open: %s", inputs[i]);
419
420     struct stat statbuf;
421     if (fstat (fd, &statbuf) == -1)
422       error (EXIT_FAILURE, errno, "fstat: %s", inputs[i]);
423
424     /* Directory? */
425     if (S_ISDIR (statbuf.st_mode))
426       iterate_input_directory (inputs[i], fd);
427     else if (S_ISREG (statbuf.st_mode)) {
428       /* Is it a cpio file? */
429       char buf[6];
430       if (read (fd, buf, 6) == 6 && memcmp (buf, "070701", 6) == 0)
431         /* Yes, a cpio file.  This is a skeleton appliance, case (1). */
432         write_file_to_fd (inputs[i]);
433       else
434         /* No, must be hostfiles, case (2). */
435         write_hostfiles (inputs[i]);
436     }
437     else
438       error (EXIT_FAILURE, 0, "%s: input is not a regular file or directory",
439              inputs[i]);
440
441     close (fd);
442   }
443 }
444
445 static void
446 iterate_input_directory (const char *dirname, int dirfd)
447 {
448   char path[PATH_MAX];
449   strcpy (path, dirname);
450   size_t len = strlen (dirname);
451   path[len++] = '/';
452
453   char *inputs[] = { path };
454
455   DIR *dir = fdopendir (dirfd);
456   if (dir == NULL)
457     error (EXIT_FAILURE, errno, "fdopendir: %s", dirname);
458
459   struct dirent *d;
460   while ((errno = 0, d = readdir (dir)) != NULL) {
461     if (d->d_name[0] == '.') /* ignore ., .. and any hidden files. */
462       continue;
463
464     strcpy (&path[len], d->d_name);
465     iterate_inputs (inputs, 1);
466   }
467
468   if (errno != 0)
469     error (EXIT_FAILURE, errno, "readdir: %s", dirname);
470
471   if (closedir (dir) == -1)
472     error (EXIT_FAILURE, errno, "closedir: %s", dirname);
473 }
474
475 /* Copy kernel modules.
476  *
477  * Find every file under modpath.
478  *
479  * Exclude all *.ko files, *except* ones which match names in
480  * the whitelist (which may contain wildcards).  Include all
481  * other files.
482  *
483  * Add chosen files to the output.
484  *
485  * whitelist_file may be NULL, to include ALL kernel modules.
486  */
487 static void
488 write_kernel_modules (const char *whitelist_file, const char *modpath)
489 {
490   char **whitelist = NULL;
491   if (whitelist_file != NULL)
492     whitelist = load_file (whitelist_file);
493
494   char *paths[2] = { (char *) modpath, NULL };
495   FTS *fts = fts_open (paths, FTS_COMFOLLOW|FTS_PHYSICAL, NULL);
496   if (fts == NULL)
497     error (EXIT_FAILURE, errno, "write_kernel_modules: fts_open: %s", modpath);
498
499   for (;;) {
500     errno = 0;
501     FTSENT *entry = fts_read (fts);
502     if (entry == NULL && errno != 0)
503       error (EXIT_FAILURE, errno, "write_kernel_modules: fts_read: %s", modpath);
504     if (entry == NULL)
505       break;
506
507     /* Ignore directories being visited in post-order. */
508     if (entry->fts_info & FTS_DP)
509       continue;
510
511     /* Is it a *.ko file? */
512     if (entry->fts_namelen >= 3 &&
513         entry->fts_name[entry->fts_namelen-3] == '.' &&
514         entry->fts_name[entry->fts_namelen-2] == 'k' &&
515         entry->fts_name[entry->fts_namelen-1] == 'o') {
516       if (whitelist) {
517         /* Is it a *.ko file which is on the whitelist? */
518         size_t j;
519         for (j = 0; whitelist[j] != NULL; ++j) {
520           int r;
521           r = fnmatch (whitelist[j], entry->fts_name, 0);
522           if (r == 0) {
523             /* It's on the whitelist, so include it. */
524             if (verbose >= 2)
525               fprintf (stderr, "including kernel module %s (matches whitelist entry %s)\n",
526                        entry->fts_name, whitelist[j]);
527             cpio_append_fts_entry (entry);
528             break;
529           } else if (r != FNM_NOMATCH)
530             error (EXIT_FAILURE, 0, "internal error: fnmatch ('%s', '%s', %d) returned unexpected non-zero value %d\n",
531                    whitelist[j], entry->fts_name, 0, r);
532         } /* for (j) */
533       } else { /* whitelist == NULL, always include */
534         if (verbose >= 2)
535           fprintf (stderr, "including kernel module %s\n", entry->fts_name);
536         cpio_append_fts_entry (entry);
537       }
538     } else
539       /* It's some other sort of file, or a directory, always include. */
540       cpio_append_fts_entry (entry);
541   }
542
543   if (fts_close (fts) == -1)
544     error (EXIT_FAILURE, errno, "write_kernel_modules: fts_close: %s", modpath);
545 }
546
547 /* Copy the host files.
548  *
549  * Read the list of entries in hostfiles (which may contain
550  * wildcards).  Look them up in the filesystem, and add those files
551  * that exist.  Ignore any files that don't exist or are not readable.
552  */
553 static void
554 write_hostfiles (const char *hostfiles_file)
555 {
556   char **hostfiles = load_file (hostfiles_file);
557
558   /* Hostfiles list can contain "." before each path - ignore it.
559    * It also contains each directory name before we enter it.  But
560    * we don't read that until we see a wildcard for that directory.
561    */
562   size_t i, j;
563   for (i = 0; hostfiles[i] != NULL; ++i) {
564     char *hostfile = hostfiles[i];
565     if (hostfile[0] == '.')
566       hostfile++;
567
568     struct stat statbuf;
569
570     /* Is it a wildcard? */
571     if (strchr (hostfile, '*') || strchr (hostfile, '?')) {
572       char *dirname = xstrdup (hostfile);
573       char *patt = strrchr (dirname, '/');
574       assert (patt);
575       *patt++ = '\0';
576
577       char **files = read_dir (dirname);
578       files = filter_fnmatch (files, patt, FNM_NOESCAPE);
579
580       /* Add matching files. */
581       for (j = 0; files[j] != NULL; ++j) {
582         char *tmp = xasprintf ("%s/%s", dirname, files[j]);
583
584         if (verbose >= 2)
585           fprintf (stderr, "including host file %s (matches %s)\n", tmp, patt);
586
587         cpio_append (tmp);
588
589         free (tmp);
590       }
591     }
592     /* Else does this file/directory/whatever exist? */
593     else if (lstat (hostfile, &statbuf) == 0) {
594       if (verbose >= 2)
595         fprintf (stderr, "including host file %s (directly referenced)\n",
596                  hostfile);
597
598       cpio_append_stat (hostfile, &statbuf);
599     } /* Ignore files that don't exist. */
600   }
601 }
602
603 /*----------*/
604 /* Helper functions. */
605
606 static void
607 add_string (char ***argv, size_t *n_used, size_t *n_alloc, const char *str)
608 {
609   char *new_str;
610
611   if (*n_used >= *n_alloc)
612     *argv = x2nrealloc (*argv, n_alloc, sizeof (char *));
613
614   if (str)
615     new_str = xstrdup (str);
616   else
617     new_str = NULL;
618
619   (*argv)[*n_used] = new_str;
620
621   (*n_used)++;
622 }
623
624 static size_t
625 count_strings (char *const *argv)
626 {
627   size_t argc;
628
629   for (argc = 0; argv[argc] != NULL; ++argc)
630     ;
631   return argc;
632 }
633
634 struct dir_cache {
635   char *path;
636   char **files;
637 };
638
639 static size_t
640 dir_cache_hash (void const *x, size_t table_size)
641 {
642   struct dir_cache const *p = x;
643   return hash_pjw (p->path, table_size);
644 }
645
646 static bool
647 dir_cache_compare (void const *x, void const *y)
648 {
649   struct dir_cache const *p = x;
650   struct dir_cache const *q = y;
651   return strcmp (p->path, q->path) == 0;
652 }
653
654 /* Read a directory into a list of strings.
655  *
656  * Previously looked up directories are cached and returned quickly,
657  * saving some considerable amount of time compared to reading the
658  * directory over again.  However this means you really must not
659  * alter the array of strings that are returned.
660  *
661  * Returns an empty list if the directory cannot be opened.
662  */
663 static char **
664 read_dir (const char *name)
665 {
666   static Hash_table *ht = NULL;
667
668   if (!ht)
669     ht = hash_initialize (1024, NULL, dir_cache_hash, dir_cache_compare, NULL);
670
671   struct dir_cache key = { .path = (char *) name };
672   struct dir_cache *p = hash_lookup (ht, &key);
673   if (p)
674     return p->files;
675
676   char **files = NULL;
677   size_t n_used = 0, n_alloc = 0;
678
679   DIR *dir = opendir (name);
680   if (!dir) {
681     /* If it fails to open, that's OK, skip to the end. */
682     /*perror (name);*/
683     goto done;
684   }
685
686   for (;;) {
687     errno = 0;
688     struct dirent *d = readdir (dir);
689     if (d == NULL) {
690       if (errno != 0)
691         /* But if it fails here, after opening and potentially reading
692          * part of the directory, that's a proper failure - inform the
693          * user and exit.
694          */
695         error (EXIT_FAILURE, errno, "%s", name);
696       break;
697     }
698
699     add_string (&files, &n_used, &n_alloc, d->d_name);
700   }
701
702   if (closedir (dir) == -1)
703     error (EXIT_FAILURE, errno, "closedir: %s", name);
704
705  done:
706   /* NULL-terminate the array. */
707   add_string (&files, &n_used, &n_alloc, NULL);
708
709   /* Add it to the hash for next time. */
710   p = xmalloc (sizeof *p);
711   p->path = (char *) name;
712   p->files = files;
713   p = hash_insert (ht, p);
714   assert (p != NULL);
715
716   return files;
717 }
718
719 /* Filter a list of strings and return only those matching the wildcard. */
720 static char **
721 filter_fnmatch (char **strings, const char *patt, int flags)
722 {
723   char **out = NULL;
724   size_t n_used = 0, n_alloc = 0;
725
726   int i, r;
727   for (i = 0; strings[i] != NULL; ++i) {
728     r = fnmatch (patt, strings[i], flags);
729     if (r == 0)
730       add_string (&out, &n_used, &n_alloc, strings[i]);
731     else if (r != FNM_NOMATCH)
732       error (EXIT_FAILURE, 0, "internal error: fnmatch ('%s', '%s', %d) returned unexpected non-zero value %d\n",
733              patt, strings[i], flags, r);
734   }
735
736   add_string (&out, &n_used, &n_alloc, NULL);
737   return out;
738 }
739
740 /* Filter a list of strings and return only those which DON'T contain sub. */
741 static char **
742 filter_notmatching_substring (char **strings, const char *sub)
743 {
744   char **out = NULL;
745   size_t n_used = 0, n_alloc = 0;
746
747   int i;
748   for (i = 0; strings[i] != NULL; ++i) {
749     if (strstr (strings[i], sub) == NULL)
750       add_string (&out, &n_used, &n_alloc, strings[i]);
751   }
752
753   add_string (&out, &n_used, &n_alloc, NULL);
754   return out;
755 }
756
757 /* Sort a list of strings, in place, with the comparison function supplied. */
758 static void
759 sort (char **strings, int (*compare) (const void *, const void *))
760 {
761   qsort (strings, count_strings (strings), sizeof (char *), compare);
762 }
763
764 /* Return true iff path exists and is a directory.  This version
765  * follows symlinks.
766  */
767 static int
768 isdir (const char *path)
769 {
770   struct stat statbuf;
771
772   if (stat (path, &statbuf) == -1)
773     return 0;
774
775   return S_ISDIR (statbuf.st_mode);
776 }
777
778 /* Copy contents of buffer to out_fd and keep out_offset correct. */
779 static void
780 write_to_fd (const void *buffer, size_t len)
781 {
782   if (full_write (out_fd, buffer, len) != len)
783     error (EXIT_FAILURE, errno, "write");
784   out_offset += len;
785 }
786
787 /* Copy contents of file to out_fd. */
788 static void
789 write_file_to_fd (const char *filename)
790 {
791   char buffer[BUFFER_SIZE];
792   int fd2;
793   ssize_t r;
794
795   if (verbose >= 2)
796     fprintf (stderr, "write_file_to_fd %s -> %d\n", filename, out_fd);
797
798   fd2 = open (filename, O_RDONLY);
799   if (fd2 == -1)
800     error (EXIT_FAILURE, errno, "open: %s", filename);
801   for (;;) {
802     r = read (fd2, buffer, sizeof buffer);
803     if (r == 0)
804       break;
805     if (r == -1) {
806       if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR)
807         continue;
808       error (EXIT_FAILURE, errno, "read: %s", filename);
809     }
810     write_to_fd (buffer, r);
811   }
812
813   if (close (fd2) == -1)
814     error (EXIT_FAILURE, errno, "close: %s", filename);
815 }
816
817 /* Copy file of given length to output, and fail if the file has
818  * changed size.
819  */
820 static void
821 write_file_len_to_fd (const char *filename, size_t len)
822 {
823   char buffer[BUFFER_SIZE];
824   size_t count = 0;
825
826   if (verbose >= 2)
827     fprintf (stderr, "write_file_to_fd %s -> %d\n", filename, out_fd);
828
829   int fd2 = open (filename, O_RDONLY);
830   if (fd2 == -1)
831     error (EXIT_FAILURE, errno, "open: %s", filename);
832   for (;;) {
833     ssize_t r = read (fd2, buffer, sizeof buffer);
834     if (r == 0)
835       break;
836     if (r == -1) {
837       if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR)
838         continue;
839       error (EXIT_FAILURE, errno, "read: %s", filename);
840     }
841     write_to_fd (buffer, r);
842     count += r;
843     if (count > len)
844       error (EXIT_FAILURE, 0, "write_file_len_to_fd: %s: file has increased in size\n", filename);
845   }
846
847   if (close (fd2) == -1)
848     error (EXIT_FAILURE, errno, "close: %s", filename);
849
850   if (count != len)
851     error (EXIT_FAILURE, 0, "febootstrap-supermin-helper: write_file_len_to_fd: %s: file has changed size\n", filename);
852 }
853
854 /* Load in a file, returning a list of lines. */
855 static char **
856 load_file (const char *filename)
857 {
858   char **lines = 0;
859   size_t n_used = 0, n_alloc = 0;
860
861   FILE *fp;
862   fp = fopen (filename, "r");
863   if (fp == NULL)
864     error (EXIT_FAILURE, errno, "fopen: %s", filename);
865
866   char line[4096];
867   while (fgets (line, sizeof line, fp)) {
868     size_t len = strlen (line);
869     if (len > 0 && line[len-1] == '\n')
870       line[len-1] = '\0';
871     add_string (&lines, &n_used, &n_alloc, line);
872   }
873
874   add_string (&lines, &n_used, &n_alloc, NULL);
875   return lines;
876 }
877
878 /* Append the file pointed to by FTSENT to the cpio output. */
879 static void
880 cpio_append_fts_entry (FTSENT *entry)
881 {
882   if (entry->fts_info & FTS_NS || entry->fts_info & FTS_NSOK)
883     cpio_append (entry->fts_path);
884   else
885     cpio_append_stat (entry->fts_path, entry->fts_statp);
886 }
887
888 /* Append the file named 'filename' to the cpio output. */
889 static void
890 cpio_append (const char *filename)
891 {
892   struct stat statbuf;
893
894   if (lstat (filename, &statbuf) == -1)
895     error (EXIT_FAILURE, errno, "lstat: %s", filename);
896   cpio_append_stat (filename, &statbuf);
897 }
898
899 /* Append the file to the cpio output. */
900 #define PADDING(len) ((((len) + 3) & ~3) - (len))
901
902 #define CPIO_HEADER_LEN (6 + 13*8)
903
904 static void
905 cpio_append_stat (const char *filename, struct stat *statbuf)
906 {
907   const char *orig_filename = filename;
908
909   if (*filename == '/')
910     filename++;
911   if (*filename == '\0')
912     filename = ".";
913
914   if (verbose >= 2)
915     fprintf (stderr, "cpio_append_stat %s 0%o -> %d\n",
916              orig_filename, statbuf->st_mode, out_fd);
917
918   /* Regular files and symlinks are the only ones that have a "body"
919    * in this cpio entry.
920    */
921   int has_body = S_ISREG (statbuf->st_mode) || S_ISLNK (statbuf->st_mode);
922
923   size_t len = strlen (filename) + 1;
924
925   char header[CPIO_HEADER_LEN + 1];
926   snprintf (header, sizeof header,
927             "070701"            /* magic */
928             "%08X"              /* inode */
929             "%08X"              /* mode */
930             "%08X" "%08X"       /* uid, gid */
931             "%08X"              /* nlink */
932             "%08X"              /* mtime */
933             "%08X"              /* file length */
934             "%08X" "%08X"       /* device holding file major, minor */
935             "%08X" "%08X"       /* for specials, device major, minor */
936             "%08X"              /* name length (including \0 byte) */
937             "%08X",             /* checksum (not used by the kernel) */
938             (unsigned) statbuf->st_ino, statbuf->st_mode,
939             statbuf->st_uid, statbuf->st_gid,
940             (unsigned) statbuf->st_nlink, (unsigned) statbuf->st_mtime,
941             has_body ? (unsigned) statbuf->st_size : 0,
942             major (statbuf->st_dev), minor (statbuf->st_dev),
943             major (statbuf->st_rdev), minor (statbuf->st_rdev),
944             (unsigned) len, 0);
945
946   /* Write the header. */
947   write_to_fd (header, CPIO_HEADER_LEN);
948
949   /* Follow with the filename, and pad it. */
950   write_to_fd (filename, len);
951   size_t padding_len = PADDING (CPIO_HEADER_LEN + len);
952   write_padding (padding_len);
953
954   /* Follow with the file or symlink content, and pad it. */
955   if (has_body) {
956     if (S_ISREG (statbuf->st_mode))
957       write_file_len_to_fd (orig_filename, statbuf->st_size);
958     else if (S_ISLNK (statbuf->st_mode)) {
959       char tmp[PATH_MAX];
960       if (readlink (orig_filename, tmp, sizeof tmp) == -1)
961         error (EXIT_FAILURE, errno, "readlink: %s", orig_filename);
962       write_to_fd (tmp, statbuf->st_size);
963     }
964
965     padding_len = PADDING (statbuf->st_size);
966     write_padding (padding_len);
967   }
968 }
969
970 /* CPIO voodoo. */
971 static void
972 cpio_append_trailer (void)
973 {
974   struct stat statbuf;
975   memset (&statbuf, 0, sizeof statbuf);
976   statbuf.st_nlink = 1;
977   cpio_append_stat ("TRAILER!!!", &statbuf);
978
979   /* CPIO seems to pad up to the next block boundary, ie. up to
980    * the next 512 bytes.
981    */
982   write_padding (((out_offset + 511) & ~511) - out_offset);
983   assert ((out_offset & 511) == 0);
984 }
985
986 /* Write 'len' bytes of zeroes out. */
987 static void
988 write_padding (size_t len)
989 {
990   static const char buffer[512] = { 0 };
991
992   while (len > 0) {
993     size_t n = len < sizeof buffer ? len : sizeof buffer;
994     write_to_fd (buffer, n);
995     len -= n;
996   }
997 }