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