extra-tests: Add an extra suppression for OCaml 3.11.2 in RHEL 6.
[libguestfs.git] / src / appliance.c
1 /* libguestfs
2  * Copyright (C) 2010-2011 Red Hat Inc.
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library 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 GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17  */
18
19 #include <config.h>
20
21 #include <errno.h>
22 #include <dirent.h>
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <stdarg.h>
26 #include <unistd.h>
27 #include <string.h>
28 #include <fcntl.h>
29 #include <time.h>
30 #include <sys/stat.h>
31 #include <sys/select.h>
32 #include <sys/wait.h>
33 #include <utime.h>
34
35 #ifdef HAVE_SYS_TYPES_H
36 #include <sys/types.h>
37 #endif
38
39 #include "guestfs.h"
40 #include "guestfs-internal.h"
41 #include "guestfs-internal-actions.h"
42 #include "guestfs_protocol.h"
43
44 /* Old-style appliance is going to be obsoleted. */
45 static const char *kernel_name = "vmlinuz." host_cpu;
46 static const char *initrd_name = "initramfs." host_cpu ".img";
47
48 static int find_path (guestfs_h *g, int (*pred) (guestfs_h *g, const char *pelem, void *data), void *data, char **pelem);
49 static int dir_contains_file (const char *dir, const char *file);
50 static int dir_contains_files (const char *dir, ...);
51 static int contains_ordinary_appliance (guestfs_h *g, const char *path, void *data);
52 static int contains_supermin_appliance (guestfs_h *g, const char *path, void *data);
53 static char *calculate_supermin_checksum (guestfs_h *g, const char *supermin_path);
54 static int check_for_cached_appliance (guestfs_h *g, const char *supermin_path, const char *checksum, uid_t uid, char **kernel, char **initrd, char **appliance);
55 static int build_supermin_appliance (guestfs_h *g, const char *supermin_path, const char *checksum, uid_t uid, char **kernel, char **initrd, char **appliance);
56 static int hard_link_to_cached_appliance (guestfs_h *g, const char *cachedir, char **kernel, char **initrd, char **appliance);
57 static int run_supermin_helper (guestfs_h *g, const char *supermin_path, const char *cachedir, size_t cdlen);
58 static void print_febootstrap_command_line (guestfs_h *g, const char *argv[]);
59
60 /* Locate or build the appliance.
61  *
62  * This function locates or builds the appliance as necessary,
63  * handling the supermin appliance, caching of supermin-built
64  * appliances, or using an ordinary appliance.
65  *
66  * The return value is 0 = good, -1 = error.  Returned in '*kernel'
67  * will be the name of the kernel to use, '*initrd' the name of the
68  * initrd, '*appliance' the name of the ext2 root filesystem.
69  * '*appliance' can be NULL, meaning that we are using an ordinary
70  * (non-ext2) appliance.  All three strings must be freed by the
71  * caller.  However the referenced files themselves must not be
72  * deleted.
73  *
74  * The process is as follows:
75  *
76  * (1) Look for the first element of g->path which contains a
77  * supermin appliance skeleton.  If no element has this, skip
78  * straight to step (5).
79  *
80  * (2) Calculate the checksum of this supermin appliance.
81  *
82  * (3) Check whether a cached appliance with the checksum calculated
83  * in (2) exists and passes basic security checks.  If so, return
84  * this appliance.
85  *
86  * (4) Try to build the supermin appliance.  If this is successful,
87  * return it.
88  *
89  * (5) Check each element of g->path, looking for an ordinary appliance.
90  * If one is found, return it.
91  *
92  * The supermin appliance cache directory lives in
93  * $TMPDIR/.guestfs-$UID/ and consists of four files:
94  *
95  *   $TMPDIR/.guestfs-$UID/checksum       - the checksum
96  *   $TMPDIR/.guestfs-$UID/kernel         - symlink to the kernel
97  *   $TMPDIR/.guestfs-$UID/initrd         - the febootstrap initrd
98  *   $TMPDIR/.guestfs-$UID/root           - the appliance
99  *
100  * Since multiple instances of libguestfs with the same UID may be
101  * racing to create an appliance, we need to be careful when building
102  * and using the appliance.
103  *
104  * If a cached appliance with checksum exists (step (2) above) then we
105  * make a hard link to it with our current PID, so that we have a copy
106  * even if the appliance is replaced by another process building an
107  * appliance afterwards:
108  *
109  *   $TMPDIR/.guestfs-$UID/kernel.$PID
110  *   $TMPDIR/.guestfs-$UID/initrd.$PID
111  *   $TMPDIR/.guestfs-$UID/root.$PID
112  *
113  * A lock is taken on "checksum" while we perform the link.
114  *
115  * Linked files are deleted by a garbage collection sweep which can be
116  * initiated by any libguestfs process with the same UID when the
117  * corresponding PID no longer exists.  (This is safe: the parent is
118  * always around in guestfs_launch() while qemu is starting up, and
119  * after that qemu will either have finished with the files or be
120  * holding them open, so we can unlink them).
121  *
122  * When building a new appliance (step (3)), it is built into randomly
123  * named temporary files in the $TMPDIR.  Then a lock is acquired on
124  * $TMPDIR/.guestfs-$UID/checksum (this file being created if
125  * necessary), the files are renamed into their final location, and
126  * the lock is released.
127  */
128 int
129 guestfs___build_appliance (guestfs_h *g,
130                            char **kernel, char **initrd, char **appliance)
131 {
132   int r;
133   uid_t uid = geteuid ();
134
135   /* Step (1). */
136   char *supermin_path;
137   r = find_path (g, contains_supermin_appliance, NULL, &supermin_path);
138   if (r == -1)
139     return -1;
140
141   if (r == 1) {
142     /* Step (2): calculate checksum. */
143     char *checksum = calculate_supermin_checksum (g, supermin_path);
144     if (checksum) {
145       /* Step (3): cached appliance exists? */
146       r = check_for_cached_appliance (g, supermin_path, checksum, uid,
147                                       kernel, initrd, appliance);
148       if (r != 0) {
149         free (supermin_path);
150         free (checksum);
151         return r == 1 ? 0 : -1;
152       }
153
154       /* Step (4): build supermin appliance. */
155       r = build_supermin_appliance (g, supermin_path, checksum, uid,
156                                     kernel, initrd, appliance);
157       free (supermin_path);
158       free (checksum);
159       return r;
160     }
161     free (supermin_path);
162   }
163
164   /* Step (5). */
165   char *path;
166   r = find_path (g, contains_ordinary_appliance, NULL, &path);
167   if (r == -1)
168     return -1;
169
170   if (r == 1) {
171     size_t len = strlen (path);
172     *kernel = safe_malloc (g, len + strlen (kernel_name) + 2);
173     *initrd = safe_malloc (g, len + strlen (initrd_name) + 2);
174     sprintf (*kernel, "%s/%s", path, kernel_name);
175     sprintf (*initrd, "%s/%s", path, initrd_name);
176     *appliance = NULL;
177
178     free (path);
179     return 0;
180   }
181
182   error (g, _("cannot find any suitable libguestfs supermin or ordinary appliance on LIBGUESTFS_PATH (search path: %s)"),
183          g->path);
184   return -1;
185 }
186
187 static int
188 contains_ordinary_appliance (guestfs_h *g, const char *path, void *data)
189 {
190   return dir_contains_files (path, kernel_name, initrd_name, NULL);
191 }
192
193 static int
194 contains_supermin_appliance (guestfs_h *g, const char *path, void *data)
195 {
196   return dir_contains_files (path, "supermin.d", NULL);
197 }
198
199 /* supermin_path is a path which is known to contain a supermin
200  * appliance.  Using febootstrap-supermin-helper -f checksum calculate
201  * the checksum so we can see if it is cached.
202  */
203 static char *
204 calculate_supermin_checksum (guestfs_h *g, const char *supermin_path)
205 {
206   size_t len = 2 * strlen (supermin_path) + 256;
207   char cmd[len];
208   int pass_u_g_args = getuid () != geteuid () || getgid () != getegid ();
209
210   if (!pass_u_g_args)
211     snprintf (cmd, len,
212               "febootstrap-supermin-helper%s "
213               "-f checksum "
214               "'%s/supermin.d' "
215               host_cpu,
216               g->verbose ? " --verbose" : "",
217               supermin_path);
218   else
219     snprintf (cmd, len,
220               "febootstrap-supermin-helper%s "
221               "-u %i "
222               "-g %i "
223               "-f checksum "
224               "'%s/supermin.d' "
225               host_cpu,
226               g->verbose ? " --verbose" : "",
227               geteuid (), getegid (),
228               supermin_path);
229
230   if (g->verbose)
231     guestfs___print_timestamped_message (g, "%s", cmd);
232
233   /* Errors here are non-fatal, so we don't need to call error(). */
234   FILE *pp = popen (cmd, "r");
235   if (pp == NULL)
236     return NULL;
237
238   char checksum[256];
239   if (fgets (checksum, sizeof checksum, pp) == NULL) {
240     pclose (pp);
241     return NULL;
242   }
243
244   if (pclose (pp) != 0) {
245     warning (g, "pclose: %m");
246     return NULL;
247   }
248
249   len = strlen (checksum);
250
251   if (len < 16) {               /* sanity check */
252     warning (g, "febootstrap-supermin-helper -f checksum returned a short string");
253     return NULL;
254   }
255
256   if (len > 0 && checksum[len-1] == '\n')
257     checksum[--len] = '\0';
258
259   return safe_strndup (g, checksum, len);
260 }
261
262 static int
263 process_exists (int pid)
264 {
265   if (kill (pid, 0) == 0)
266     return 1;
267
268   if (errno == ESRCH)
269     return 0;
270
271   return -1;
272 }
273
274 /* Garbage collect appliance hard links.  Files that match
275  * (kernel|initrd|root).$PID where the corresponding PID doesn't exist
276  * are deleted.  Note that errors in this function don't matter.
277  * There may also be other libguestfs processes racing to do the same
278  * thing here.
279  */
280 static void
281 garbage_collect_appliances (const char *cachedir)
282 {
283   DIR *dir;
284   struct dirent *d;
285   int pid;
286
287   dir = opendir (cachedir);
288   if (dir == NULL)
289     return;
290
291   while ((d = readdir (dir)) != NULL) {
292     if (sscanf (d->d_name, "kernel.%d", &pid) == 1 &&
293         process_exists (pid) == 0)
294       unlinkat (dirfd (dir), d->d_name, 0);
295     else if (sscanf (d->d_name, "initrd.%d", &pid) == 1 &&
296              process_exists (pid) == 0)
297       unlinkat (dirfd (dir), d->d_name, 0);
298     else if (sscanf (d->d_name, "root.%d", &pid) == 1 &&
299              process_exists (pid) == 0)
300       unlinkat (dirfd (dir), d->d_name, 0);
301   }
302
303   closedir (dir);
304 }
305
306 static int
307 check_for_cached_appliance (guestfs_h *g,
308                             const char *supermin_path, const char *checksum,
309                             uid_t uid,
310                             char **kernel, char **initrd, char **appliance)
311 {
312   const char *tmpdir = guestfs___persistent_tmpdir ();
313
314   /* len must be longer than the length of any pathname we can
315    * generate in this function.
316    */
317   size_t len = strlen (tmpdir) + 128;
318   char cachedir[len];
319   snprintf (cachedir, len, "%s/.guestfs-%d", tmpdir, uid);
320   char filename[len];
321   snprintf (filename, len, "%s/checksum", cachedir);
322
323   (void) mkdir (cachedir, 0755);
324
325   /* See if the cache directory exists and passes some simple checks
326    * to make sure it has not been tampered with.
327    */
328   struct stat statbuf;
329   if (lstat (cachedir, &statbuf) == -1)
330     return 0;
331   if (statbuf.st_uid != uid) {
332     error (g, _("security: cached appliance %s is not owned by UID %d"),
333            filename, uid);
334     return -1;
335   }
336   if (!S_ISDIR (statbuf.st_mode)) {
337     error (g, _("security: cached appliance %s is not a directory (mode %o)"),
338            filename, statbuf.st_mode);
339     return -1;
340   }
341   if ((statbuf.st_mode & 0022) != 0) {
342     error (g, _("security: cached appliance %s is writable by group or other (mode %o)"),
343            cachedir, statbuf.st_mode);
344     return -1;
345   }
346
347   (void) utime (cachedir, NULL);
348
349   garbage_collect_appliances (cachedir);
350
351   /* Try to open and acquire a lock on the checksum file. */
352   int fd = open (filename, O_RDONLY);
353   if (fd == -1)
354     return 0;
355 #ifdef HAVE_FUTIMENS
356   (void) futimens (fd, NULL);
357 #else
358   (void) futimes (fd, NULL);
359 #endif
360   struct flock fl;
361   fl.l_type = F_RDLCK;
362   fl.l_whence = SEEK_SET;
363   fl.l_start = 0;
364   fl.l_len = 1;
365  again:
366   if (fcntl (fd, F_SETLKW, &fl) == -1) {
367     if (errno == EINTR)
368       goto again;
369     perrorf (g, "fcntl: F_SETLKW: %s", filename);
370     close (fd);
371     return -1;
372   }
373
374   /* Read the checksum file. */
375   size_t clen = strlen (checksum);
376   char checksum_on_disk[clen];
377   ssize_t rr = read (fd, checksum_on_disk, clen);
378   if (rr == -1) {
379     perrorf (g, "read: %s", filename);
380     close (fd);
381     return -1;
382   }
383   if ((size_t) rr != clen) {
384     close (fd);
385     return 0;
386   }
387
388   if (memcmp (checksum, checksum_on_disk, clen) != 0) {
389     close (fd);
390     return 0;
391   }
392
393   /* At this point, cachedir exists, and checksum matches, and we have
394    * a read lock on the checksum file.  Make hard links to the files.
395    */
396   if (hard_link_to_cached_appliance (g, cachedir,
397                                      kernel, initrd, appliance) == -1) {
398     close (fd);
399     return -1;
400   }
401
402   /* Releases the lock on checksum. */
403   if (close (fd) == -1) {
404     perrorf (g, "close");
405     /* Allocated in hard_link_to_cached_appliance above, must be
406      * freed along this error path.
407      */
408     free (*kernel);
409     free (*initrd);
410     free (*appliance);
411     return -1;
412   }
413
414   /* Exists! */
415   return 1;
416 }
417
418 /* Build supermin appliance from supermin_path to $TMPDIR/.guestfs-$UID.
419  *
420  * Returns:
421  * 0 = built
422  * -1 = error (aborts launch)
423  */
424 static int
425 build_supermin_appliance (guestfs_h *g,
426                           const char *supermin_path, const char *checksum,
427                           uid_t uid,
428                           char **kernel, char **initrd, char **appliance)
429 {
430   if (g->verbose)
431     guestfs___print_timestamped_message (g, "begin building supermin appliance");
432
433   const char *tmpdir = guestfs___persistent_tmpdir ();
434
435   /* len must be longer than the length of any pathname we can
436    * generate in this function.
437    */
438   size_t len = strlen (tmpdir) + 128;
439
440   /* Build the appliance into a temporary directory. */
441   char tmpcd[len];
442   snprintf (tmpcd, len, "%s/guestfs.XXXXXX", tmpdir);
443
444   if (mkdtemp (tmpcd) == NULL) {
445     perrorf (g, "mkdtemp");
446     return -1;
447   }
448
449   if (g->verbose)
450     guestfs___print_timestamped_message (g, "run febootstrap-supermin-helper");
451
452   int r = run_supermin_helper (g, supermin_path, tmpcd, len);
453   if (r == -1)
454     return -1;
455
456   if (g->verbose)
457     guestfs___print_timestamped_message (g, "finished building supermin appliance");
458
459   char cachedir[len];
460   snprintf (cachedir, len, "%s/.guestfs-%d", tmpdir, uid);
461   char filename[len];
462   char filename2[len];
463   snprintf (filename, len, "%s/checksum", cachedir);
464
465   /* Open and acquire write lock on checksum file.  The file might
466    * not exist, in which case we want to create it.
467    */
468   int fd = open (filename, O_WRONLY|O_CREAT, 0755);
469   if (fd == -1) {
470     perrorf (g, "open: %s", filename);
471     return -1;
472   }
473   struct flock fl;
474   fl.l_type = F_WRLCK;
475   fl.l_whence = SEEK_SET;
476   fl.l_start = 0;
477   fl.l_len = 1;
478  again:
479   if (fcntl (fd, F_SETLKW, &fl) == -1) {
480     if (errno == EINTR)
481       goto again;
482     perrorf (g, "fcntl: F_SETLKW: %s", filename);
483     close (fd);
484     return -1;
485   }
486
487   /* At this point we have acquired a write lock on the checksum
488    * file so we go ahead and replace it with the new checksum, and
489    * rename in appliance files into this directory.
490    */
491   size_t clen = strlen (checksum);
492   if (ftruncate (fd, clen) == -1) {
493     perrorf (g, "ftruncate: %s", filename);
494     close (fd);
495     return -1;
496   }
497
498   ssize_t rr = write (fd, checksum, clen);
499   if (rr == -1) {
500     perrorf (g, "write: %s", filename);
501     close (fd);
502     return -1;
503   }
504   if ((size_t) rr != clen) {
505     error (g, "partial write: %s", filename);
506     close (fd);
507     return -1;
508   }
509
510   snprintf (filename, len, "%s/kernel", tmpcd);
511   snprintf (filename2, len, "%s/kernel", cachedir);
512   unlink (filename2);
513   if (rename (filename, filename2) == -1) {
514     perrorf (g, "rename: %s %s", filename, filename2);
515     close (fd);
516     return -1;
517   }
518
519   snprintf (filename, len, "%s/initrd", tmpcd);
520   snprintf (filename2, len, "%s/initrd", cachedir);
521   unlink (filename2);
522   if (rename (filename, filename2) == -1) {
523     perrorf (g, "rename: %s %s", filename, filename2);
524     close (fd);
525     return -1;
526   }
527
528   snprintf (filename, len, "%s/root", tmpcd);
529   snprintf (filename2, len, "%s/root", cachedir);
530   unlink (filename2);
531   if (rename (filename, filename2) == -1) {
532     perrorf (g, "rename: %s %s", filename, filename2);
533     close (fd);
534     return -1;
535   }
536
537   rmdir (tmpcd);
538
539   /* Now finish off by linking to the cached appliance and returning it. */
540   if (hard_link_to_cached_appliance (g, cachedir,
541                                      kernel, initrd, appliance) == -1) {
542     close (fd);
543     return -1;
544   }
545
546   /* Releases the lock on checksum. */
547   if (close (fd) == -1) {
548     perrorf (g, "close");
549     /* Allocated in hard_link_to_cached_appliance above, must be
550      * freed along this error path.
551      */
552     free (*kernel);
553     free (*initrd);
554     free (*appliance);
555     return -1;
556   }
557
558   return 0;
559 }
560
561 /* NB: lock on checksum file must be held when this is called. */
562 static int
563 hard_link_to_cached_appliance (guestfs_h *g,
564                                const char *cachedir,
565                                char **kernel, char **initrd, char **appliance)
566 {
567   pid_t pid = getpid ();
568   size_t len = strlen (cachedir) + 32;
569
570   *kernel = safe_malloc (g, len);
571   *initrd = safe_malloc (g, len);
572   *appliance = safe_malloc (g, len);
573   snprintf (*kernel, len, "%s/kernel.%d", cachedir, pid);
574   snprintf (*initrd, len, "%s/initrd.%d", cachedir, pid);
575   snprintf (*appliance, len, "%s/root.%d", cachedir, pid);
576
577   char filename[len];
578   snprintf (filename, len, "%s/kernel", cachedir);
579   (void) unlink (*kernel);
580   if (link (filename, *kernel) == -1) {
581     perrorf (g, "link: %s %s", filename, *kernel);
582     goto error;
583   }
584   (void) lutimes (filename, NULL); /* lutimes because it's a symlink */
585
586   snprintf (filename, len, "%s/initrd", cachedir);
587   (void) unlink (*initrd);
588   if (link (filename, *initrd) == -1) {
589     perrorf (g, "link: %s %s", filename, *initrd);
590     goto error;
591   }
592   (void) utime (filename, NULL);
593
594   snprintf (filename, len, "%s/root", cachedir);
595   (void) unlink (*appliance);
596   if (link (filename, *appliance) == -1) {
597     perrorf (g, "link: %s %s", filename, *appliance);
598     goto error;
599   }
600   (void) utime (filename, NULL);
601
602   return 0;
603
604  error:
605   free (*kernel);
606   free (*initrd);
607   free (*appliance);
608   return -1;
609 }
610
611 /* Run febootstrap-supermin-helper and tell it to generate the
612  * appliance.
613  */
614 static int
615 run_supermin_helper (guestfs_h *g, const char *supermin_path,
616                      const char *cachedir, size_t cdlen)
617 {
618   size_t pathlen = strlen (supermin_path);
619
620   const char *argv[30];
621   size_t i = 0;
622
623   char uid[32];
624   snprintf (uid, sizeof uid, "%i", geteuid ());
625   char gid[32];
626   snprintf (gid, sizeof gid, "%i", getegid ());
627   char supermin_d[pathlen + 32];
628   snprintf (supermin_d, pathlen + 32, "%s/supermin.d", supermin_path);
629   char kernel[cdlen + 32];
630   snprintf (kernel, cdlen + 32, "%s/kernel", cachedir);
631   char initrd[cdlen + 32];
632   snprintf (initrd, cdlen + 32, "%s/initrd", cachedir);
633   char root[cdlen + 32];
634   snprintf (root, cdlen + 32, "%s/root", cachedir);
635
636   int pass_u_g_args = getuid () != geteuid () || getgid () != getegid ();
637
638   argv[i++] = "febootstrap-supermin-helper";
639   if (g->verbose)
640     argv[i++] = "--verbose";
641   if (pass_u_g_args) {
642     argv[i++] = "-u";
643     argv[i++] = uid;
644     argv[i++] = "-g";
645     argv[i++] = gid;
646   }
647   argv[i++] = "-f";
648   argv[i++] = "ext2";
649   argv[i++] = supermin_d;
650   argv[i++] = host_cpu;
651   argv[i++] = kernel;
652   argv[i++] = initrd;
653   argv[i++] = root;
654   argv[i++] = NULL;
655
656   if (g->verbose)
657     print_febootstrap_command_line (g, argv);
658
659   pid_t pid = fork ();
660   if (pid == -1) {
661     perrorf (g, "fork");
662     return -1;
663   }
664
665   if (pid > 0) {                /* Parent. */
666     int status;
667     if (waitpid (pid, &status, 0) == -1) {
668       perrorf (g, "waitpid");
669       return -1;
670     }
671     if (!WIFEXITED (status) || WEXITSTATUS (status) != 0) {
672       error (g, _("external command failed, see earlier error messages"));
673       return -1;
674     }
675     return 0;
676   }
677
678   /* Child. */
679
680   /* Set a sensible umask in the subprocess, so kernel and initrd
681    * output files are world-readable (RHBZ#610880).
682    */
683   umask (0022);
684
685   execvp ("febootstrap-supermin-helper", (char * const *) argv);
686   perror ("execvp");
687   _exit (EXIT_FAILURE);
688 }
689
690 static void
691 print_febootstrap_command_line (guestfs_h *g, const char *argv[])
692 {
693   int i;
694   int needs_quote;
695   char *buf;
696   size_t len;
697
698   /* Calculate length of the buffer needed.  This is an overestimate. */
699   len = 0;
700   for (i = 0; argv[i] != NULL; ++i)
701     len += strlen (argv[i]) + 32;
702
703   buf = malloc (len);
704   if (buf == NULL) {
705     warning (g, "malloc: %m");
706     return;
707   }
708
709   len = 0;
710   for (i = 0; argv[i] != NULL; ++i) {
711     if (i > 0) {
712       strcpy (&buf[len], " ");
713       len++;
714     }
715
716     /* Does it need shell quoting?  This only deals with simple cases. */
717     needs_quote = strcspn (argv[i], " ") != strlen (argv[i]);
718
719     if (needs_quote) {
720       strcpy (&buf[len], "'");
721       len++;
722     }
723
724     strcpy (&buf[len], argv[i]);
725     len += strlen (argv[i]);
726
727     if (needs_quote) {
728       strcpy (&buf[len], "'");
729       len++;
730     }
731   }
732
733   guestfs___print_timestamped_message (g, "%s", buf);
734
735   free (buf);
736 }
737
738 /* Search elements of g->path, returning the first path element which
739  * matches the predicate function 'pred'.
740  *
741  * Function 'pred' must return a true or false value.  If it returns
742  * -1 then the entire search is aborted.
743  *
744  * Return values:
745  * 1 = a path element matched, it is returned in *pelem_ret and must be
746  *     freed by the caller,
747  * 0 = no path element matched, *pelem_ret is set to NULL, or
748  * -1 = error which aborts the launch process
749  */
750 static int
751 find_path (guestfs_h *g,
752            int (*pred) (guestfs_h *g, const char *pelem, void *data),
753            void *data,
754            char **pelem_ret)
755 {
756   size_t len;
757   int r;
758   const char *pelem = g->path;
759
760   /* Note that if g->path is an empty string, we want to check the
761    * current directory (for backwards compatibility with
762    * libguestfs < 1.5.4).
763    */
764   do {
765     len = strcspn (pelem, ":");
766
767     /* Empty element or "." means current directory. */
768     if (len == 0)
769       *pelem_ret = safe_strdup (g, ".");
770     else
771       *pelem_ret = safe_strndup (g, pelem, len);
772
773     r = pred (g, *pelem_ret, data);
774     if (r == -1) {
775       free (*pelem_ret);
776       return -1;
777     }
778
779     if (r != 0)                 /* predicate matched */
780       return 1;
781
782     free (*pelem_ret);
783
784     if (pelem[len] == ':')
785       pelem += len + 1;
786     else
787       pelem += len;
788   } while (*pelem);
789
790   /* Predicate didn't match on any path element. */
791   *pelem_ret = NULL;
792   return 0;
793 }
794
795 /* Returns true iff file is contained in dir. */
796 static int
797 dir_contains_file (const char *dir, const char *file)
798 {
799   size_t dirlen = strlen (dir);
800   size_t filelen = strlen (file);
801   size_t len = dirlen + filelen + 2;
802   char path[len];
803
804   snprintf (path, len, "%s/%s", dir, file);
805   return access (path, F_OK) == 0;
806 }
807
808 /* Returns true iff every listed file is contained in 'dir'. */
809 static int
810 dir_contains_files (const char *dir, ...)
811 {
812   va_list args;
813   const char *file;
814
815   va_start (args, dir);
816   while ((file = va_arg (args, const char *)) != NULL) {
817     if (!dir_contains_file (dir, file)) {
818       va_end (args);
819       return 0;
820     }
821   }
822   va_end (args);
823   return 1;
824 }