2 * Copyright (C) 2010-2011 Red Hat Inc.
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.
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.
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
31 #include <sys/select.h>
35 #ifdef HAVE_SYS_TYPES_H
36 #include <sys/types.h>
40 #include "guestfs-internal.h"
41 #include "guestfs-internal-actions.h"
42 #include "guestfs_protocol.h"
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";
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[]);
60 /* Locate or build the appliance.
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.
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
74 * The process is as follows:
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).
80 * (2) Calculate the checksum of this supermin appliance.
82 * (3) Check whether a cached appliance with the checksum calculated
83 * in (2) exists and passes basic security checks. If so, return
86 * (4) Try to build the supermin appliance. If this is successful,
89 * (5) Check each element of g->path, looking for an ordinary appliance.
90 * If one is found, return it.
92 * The supermin appliance cache directory lives in
93 * $TMPDIR/.guestfs-$UID/ and consists of four files:
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
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.
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:
109 * $TMPDIR/.guestfs-$UID/kernel.$PID
110 * $TMPDIR/.guestfs-$UID/initrd.$PID
111 * $TMPDIR/.guestfs-$UID/root.$PID
113 * A lock is taken on "checksum" while we perform the link.
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).
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.
129 guestfs___build_appliance (guestfs_h *g,
130 char **kernel, char **initrd, char **appliance)
133 uid_t uid = geteuid ();
137 r = find_path (g, contains_supermin_appliance, NULL, &supermin_path);
142 /* Step (2): calculate checksum. */
143 char *checksum = calculate_supermin_checksum (g, supermin_path);
145 /* Step (3): cached appliance exists? */
146 r = check_for_cached_appliance (g, supermin_path, checksum, uid,
147 kernel, initrd, appliance);
149 free (supermin_path);
151 return r == 1 ? 0 : -1;
154 /* Step (4): build supermin appliance. */
155 r = build_supermin_appliance (g, supermin_path, checksum, uid,
156 kernel, initrd, appliance);
157 free (supermin_path);
161 free (supermin_path);
166 r = find_path (g, contains_ordinary_appliance, NULL, &path);
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);
182 error (g, _("cannot find any suitable libguestfs supermin or ordinary appliance on LIBGUESTFS_PATH (search path: %s)"),
188 contains_ordinary_appliance (guestfs_h *g, const char *path, void *data)
190 return dir_contains_files (path, kernel_name, initrd_name, NULL);
194 contains_supermin_appliance (guestfs_h *g, const char *path, void *data)
196 return dir_contains_files (path, "supermin.d", NULL);
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.
204 calculate_supermin_checksum (guestfs_h *g, const char *supermin_path)
206 size_t len = 2 * strlen (supermin_path) + 256;
208 int pass_u_g_args = getuid () != geteuid () || getgid () != getegid ();
212 "febootstrap-supermin-helper%s "
216 g->verbose ? " --verbose" : "",
220 "febootstrap-supermin-helper%s "
226 g->verbose ? " --verbose" : "",
227 geteuid (), getegid (),
231 guestfs___print_timestamped_message (g, "%s", cmd);
233 /* Errors here are non-fatal, so we don't need to call error(). */
234 FILE *pp = popen (cmd, "r");
239 if (fgets (checksum, sizeof checksum, pp) == NULL) {
244 if (pclose (pp) != 0) {
245 warning (g, "pclose: %m");
249 len = strlen (checksum);
251 if (len < 16) { /* sanity check */
252 warning (g, "febootstrap-supermin-helper -f checksum returned a short string");
256 if (len > 0 && checksum[len-1] == '\n')
257 checksum[--len] = '\0';
259 return safe_strndup (g, checksum, len);
263 process_exists (int pid)
265 if (kill (pid, 0) == 0)
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
281 garbage_collect_appliances (const char *cachedir)
287 dir = opendir (cachedir);
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);
307 check_for_cached_appliance (guestfs_h *g,
308 const char *supermin_path, const char *checksum,
310 char **kernel, char **initrd, char **appliance)
312 const char *tmpdir = guestfs___persistent_tmpdir ();
314 /* len must be longer than the length of any pathname we can
315 * generate in this function.
317 size_t len = strlen (tmpdir) + 128;
319 snprintf (cachedir, len, "%s/.guestfs-%d", tmpdir, uid);
321 snprintf (filename, len, "%s/checksum", cachedir);
323 (void) mkdir (cachedir, 0755);
325 /* See if the cache directory exists and passes some simple checks
326 * to make sure it has not been tampered with.
329 if (lstat (cachedir, &statbuf) == -1)
331 if (statbuf.st_uid != uid) {
332 error (g, _("security: cached appliance %s is not owned by UID %d"),
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);
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);
347 (void) utime (cachedir, NULL);
349 garbage_collect_appliances (cachedir);
351 /* Try to open and acquire a lock on the checksum file. */
352 int fd = open (filename, O_RDONLY);
356 (void) futimens (fd, NULL);
358 (void) futimes (fd, NULL);
362 fl.l_whence = SEEK_SET;
366 if (fcntl (fd, F_SETLKW, &fl) == -1) {
369 perrorf (g, "fcntl: F_SETLKW: %s", filename);
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);
379 perrorf (g, "read: %s", filename);
383 if ((size_t) rr != clen) {
388 if (memcmp (checksum, checksum_on_disk, clen) != 0) {
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.
396 if (hard_link_to_cached_appliance (g, cachedir,
397 kernel, initrd, appliance) == -1) {
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.
418 /* Build supermin appliance from supermin_path to $TMPDIR/.guestfs-$UID.
422 * -1 = error (aborts launch)
425 build_supermin_appliance (guestfs_h *g,
426 const char *supermin_path, const char *checksum,
428 char **kernel, char **initrd, char **appliance)
431 guestfs___print_timestamped_message (g, "begin building supermin appliance");
433 const char *tmpdir = guestfs___persistent_tmpdir ();
435 /* len must be longer than the length of any pathname we can
436 * generate in this function.
438 size_t len = strlen (tmpdir) + 128;
440 /* Build the appliance into a temporary directory. */
442 snprintf (tmpcd, len, "%s/guestfs.XXXXXX", tmpdir);
444 if (mkdtemp (tmpcd) == NULL) {
445 perrorf (g, "mkdtemp");
450 guestfs___print_timestamped_message (g, "run febootstrap-supermin-helper");
452 int r = run_supermin_helper (g, supermin_path, tmpcd, len);
457 guestfs___print_timestamped_message (g, "finished building supermin appliance");
460 snprintf (cachedir, len, "%s/.guestfs-%d", tmpdir, uid);
463 snprintf (filename, len, "%s/checksum", cachedir);
465 /* Open and acquire write lock on checksum file. The file might
466 * not exist, in which case we want to create it.
468 int fd = open (filename, O_WRONLY|O_CREAT, 0755);
470 perrorf (g, "open: %s", filename);
475 fl.l_whence = SEEK_SET;
479 if (fcntl (fd, F_SETLKW, &fl) == -1) {
482 perrorf (g, "fcntl: F_SETLKW: %s", filename);
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.
491 size_t clen = strlen (checksum);
492 if (ftruncate (fd, clen) == -1) {
493 perrorf (g, "ftruncate: %s", filename);
498 ssize_t rr = write (fd, checksum, clen);
500 perrorf (g, "write: %s", filename);
504 if ((size_t) rr != clen) {
505 error (g, "partial write: %s", filename);
510 snprintf (filename, len, "%s/kernel", tmpcd);
511 snprintf (filename2, len, "%s/kernel", cachedir);
513 if (rename (filename, filename2) == -1) {
514 perrorf (g, "rename: %s %s", filename, filename2);
519 snprintf (filename, len, "%s/initrd", tmpcd);
520 snprintf (filename2, len, "%s/initrd", cachedir);
522 if (rename (filename, filename2) == -1) {
523 perrorf (g, "rename: %s %s", filename, filename2);
528 snprintf (filename, len, "%s/root", tmpcd);
529 snprintf (filename2, len, "%s/root", cachedir);
531 if (rename (filename, filename2) == -1) {
532 perrorf (g, "rename: %s %s", filename, filename2);
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) {
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.
561 /* NB: lock on checksum file must be held when this is called. */
563 hard_link_to_cached_appliance (guestfs_h *g,
564 const char *cachedir,
565 char **kernel, char **initrd, char **appliance)
567 pid_t pid = getpid ();
568 size_t len = strlen (cachedir) + 32;
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);
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);
584 (void) lutimes (filename, NULL); /* lutimes because it's a symlink */
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);
592 (void) utime (filename, NULL);
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);
600 (void) utime (filename, NULL);
611 /* Run febootstrap-supermin-helper and tell it to generate the
615 run_supermin_helper (guestfs_h *g, const char *supermin_path,
616 const char *cachedir, size_t cdlen)
618 size_t pathlen = strlen (supermin_path);
620 const char *argv[30];
624 snprintf (uid, sizeof uid, "%i", geteuid ());
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);
636 int pass_u_g_args = getuid () != geteuid () || getgid () != getegid ();
638 argv[i++] = "febootstrap-supermin-helper";
640 argv[i++] = "--verbose";
649 argv[i++] = supermin_d;
650 argv[i++] = host_cpu;
657 print_febootstrap_command_line (g, argv);
665 if (pid > 0) { /* Parent. */
667 if (waitpid (pid, &status, 0) == -1) {
668 perrorf (g, "waitpid");
671 if (!WIFEXITED (status) || WEXITSTATUS (status) != 0) {
672 error (g, _("external command failed, see earlier error messages"));
680 /* Set a sensible umask in the subprocess, so kernel and initrd
681 * output files are world-readable (RHBZ#610880).
685 execvp ("febootstrap-supermin-helper", (char * const *) argv);
687 _exit (EXIT_FAILURE);
691 print_febootstrap_command_line (guestfs_h *g, const char *argv[])
698 /* Calculate length of the buffer needed. This is an overestimate. */
700 for (i = 0; argv[i] != NULL; ++i)
701 len += strlen (argv[i]) + 32;
705 warning (g, "malloc: %m");
710 for (i = 0; argv[i] != NULL; ++i) {
712 strcpy (&buf[len], " ");
716 /* Does it need shell quoting? This only deals with simple cases. */
717 needs_quote = strcspn (argv[i], " ") != strlen (argv[i]);
720 strcpy (&buf[len], "'");
724 strcpy (&buf[len], argv[i]);
725 len += strlen (argv[i]);
728 strcpy (&buf[len], "'");
733 guestfs___print_timestamped_message (g, "%s", buf);
738 /* Search elements of g->path, returning the first path element which
739 * matches the predicate function 'pred'.
741 * Function 'pred' must return a true or false value. If it returns
742 * -1 then the entire search is aborted.
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
751 find_path (guestfs_h *g,
752 int (*pred) (guestfs_h *g, const char *pelem, void *data),
758 const char *pelem = g->path;
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).
765 len = strcspn (pelem, ":");
767 /* Empty element or "." means current directory. */
769 *pelem_ret = safe_strdup (g, ".");
771 *pelem_ret = safe_strndup (g, pelem, len);
773 r = pred (g, *pelem_ret, data);
779 if (r != 0) /* predicate matched */
784 if (pelem[len] == ':')
790 /* Predicate didn't match on any path element. */
795 /* Returns true iff file is contained in dir. */
797 dir_contains_file (const char *dir, const char *file)
799 size_t dirlen = strlen (dir);
800 size_t filelen = strlen (file);
801 size_t len = dirlen + filelen + 2;
804 snprintf (path, len, "%s/%s", dir, file);
805 return access (path, F_OK) == 0;
808 /* Returns true iff every listed file is contained in 'dir'. */
810 dir_contains_files (const char *dir, ...)
815 va_start (args, dir);
816 while ((file = va_arg (args, const char *)) != NULL) {
817 if (!dir_contains_file (dir, file)) {