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