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);
59 /* Locate or build the appliance.
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.
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
73 * The process is as follows:
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).
79 * (2) Calculate the checksum of this supermin appliance.
81 * (3) Check whether a cached appliance with the checksum calculated
82 * in (2) exists and passes basic security checks. If so, return
85 * (4) Try to build the supermin appliance. If this is successful,
88 * (5) Check each element of g->path, looking for an ordinary appliance.
89 * If one is found, return it.
91 * The supermin appliance cache directory lives in
92 * $TMPDIR/.guestfs-$UID/ and consists of four files:
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
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.
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:
108 * $TMPDIR/.guestfs-$UID/kernel.$PID
109 * $TMPDIR/.guestfs-$UID/initrd.$PID
110 * $TMPDIR/.guestfs-$UID/root.$PID
112 * A lock is taken on "checksum" while we perform the link.
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).
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.
128 guestfs___build_appliance (guestfs_h *g,
129 char **kernel, char **initrd, char **appliance)
132 uid_t uid = geteuid ();
136 r = find_path (g, contains_supermin_appliance, NULL, &supermin_path);
141 /* Step (2): calculate checksum. */
142 char *checksum = calculate_supermin_checksum (g, supermin_path);
144 /* Step (3): cached appliance exists? */
145 r = check_for_cached_appliance (g, supermin_path, checksum, uid,
146 kernel, initrd, appliance);
148 free (supermin_path);
150 return r == 1 ? 0 : -1;
153 /* Step (4): build supermin appliance. */
154 r = build_supermin_appliance (g, supermin_path, checksum, uid,
155 kernel, initrd, appliance);
156 free (supermin_path);
160 free (supermin_path);
165 r = find_path (g, contains_ordinary_appliance, NULL, &path);
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);
181 error (g, _("cannot find any suitable libguestfs supermin or ordinary appliance on LIBGUESTFS_PATH (search path: %s)"),
187 contains_ordinary_appliance (guestfs_h *g, const char *path, void *data)
189 return dir_contains_files (path, kernel_name, initrd_name, NULL);
193 contains_supermin_appliance (guestfs_h *g, const char *path, void *data)
195 return dir_contains_files (path, "supermin.d", NULL);
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.
203 calculate_supermin_checksum (guestfs_h *g, const char *supermin_path)
205 size_t len = 2 * strlen (supermin_path) + 256;
207 int pass_u_g_args = getuid () != geteuid () || getgid () != getegid ();
211 "febootstrap-supermin-helper%s "
215 g->verbose ? " --verbose" : "",
219 "febootstrap-supermin-helper%s "
225 g->verbose ? " --verbose" : "",
226 geteuid (), getegid (),
230 guestfs___print_timestamped_message (g, "%s", cmd);
232 /* Errors here are non-fatal, so we don't need to call error(). */
233 FILE *pp = popen (cmd, "r");
238 if (fgets (checksum, sizeof checksum, pp) == NULL) {
243 if (pclose (pp) == -1) {
244 warning (g, "pclose: %m");
248 len = strlen (checksum);
250 if (len < 16) { /* sanity check */
251 warning (g, "febootstrap-supermin-helper -f checksum returned a short string");
255 if (len > 0 && checksum[len-1] == '\n')
256 checksum[--len] = '\0';
258 return safe_strndup (g, checksum, len);
262 process_exists (int pid)
264 if (kill (pid, 0) == 0)
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
280 garbage_collect_appliances (const char *cachedir)
286 dir = opendir (cachedir);
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);
306 check_for_cached_appliance (guestfs_h *g,
307 const char *supermin_path, const char *checksum,
309 char **kernel, char **initrd, char **appliance)
311 const char *tmpdir = guestfs___persistent_tmpdir ();
313 /* len must be longer than the length of any pathname we can
314 * generate in this function.
316 size_t len = strlen (tmpdir) + 128;
318 snprintf (cachedir, len, "%s/.guestfs-%d", tmpdir, uid);
320 snprintf (filename, len, "%s/checksum", cachedir);
322 (void) mkdir (cachedir, 0755);
324 /* See if the cache directory exists and passes some simple checks
325 * to make sure it has not been tampered with.
328 if (lstat (cachedir, &statbuf) == -1)
330 if (statbuf.st_uid != uid) {
331 error (g, _("security: cached appliance %s is not owned by UID %d"),
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);
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);
346 (void) utime (cachedir, NULL);
348 garbage_collect_appliances (cachedir);
350 /* Try to open and acquire a lock on the checksum file. */
351 int fd = open (filename, O_RDONLY);
355 (void) futimens (fd, NULL);
357 (void) futimes (fd, NULL);
361 fl.l_whence = SEEK_SET;
365 if (fcntl (fd, F_SETLKW, &fl) == -1) {
368 perrorf (g, "fcntl: F_SETLKW: %s", filename);
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);
378 perrorf (g, "read: %s", filename);
382 if ((size_t) rr != clen) {
387 if (memcmp (checksum, checksum_on_disk, clen) != 0) {
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.
395 if (hard_link_to_cached_appliance (g, cachedir,
396 kernel, initrd, appliance) == -1) {
401 /* Releases the lock on checksum. */
402 if (close (fd) == -1) {
403 perrorf (g, "close");
411 /* Build supermin appliance from supermin_path to $TMPDIR/.guestfs-$UID.
415 * -1 = error (aborts launch)
418 build_supermin_appliance (guestfs_h *g,
419 const char *supermin_path, const char *checksum,
421 char **kernel, char **initrd, char **appliance)
424 guestfs___print_timestamped_message (g, "begin building supermin appliance");
426 const char *tmpdir = guestfs___persistent_tmpdir ();
428 /* len must be longer than the length of any pathname we can
429 * generate in this function.
431 size_t len = strlen (tmpdir) + 128;
433 /* Build the appliance into a temporary directory. */
435 snprintf (tmpcd, len, "%s/guestfs.XXXXXX", tmpdir);
437 if (mkdtemp (tmpcd) == NULL) {
438 perrorf (g, "mkdtemp");
443 guestfs___print_timestamped_message (g, "run febootstrap-supermin-helper");
445 int r = run_supermin_helper (g, supermin_path, tmpcd, len);
450 guestfs___print_timestamped_message (g, "finished building supermin appliance");
453 snprintf (cachedir, len, "%s/.guestfs-%d", tmpdir, uid);
456 snprintf (filename, len, "%s/checksum", cachedir);
458 /* Open and acquire write lock on checksum file. The file might
459 * not exist, in which case we want to create it.
461 int fd = open (filename, O_WRONLY|O_CREAT, 0755);
463 perrorf (g, "open: %s", filename);
468 fl.l_whence = SEEK_SET;
472 if (fcntl (fd, F_SETLKW, &fl) == -1) {
475 perrorf (g, "fcntl: F_SETLKW: %s", filename);
480 /* At this point we have acquired a write lock on the checksum
481 * file so we go ahead and replace it with the new checksum, and
482 * rename in appliance files into this directory.
484 size_t clen = strlen (checksum);
485 if (ftruncate (fd, clen) == -1) {
486 perrorf (g, "ftruncate: %s", filename);
491 ssize_t rr = write (fd, checksum, clen);
493 perrorf (g, "write: %s", filename);
497 if ((size_t) rr != clen) {
498 error (g, "partial write: %s", filename);
503 snprintf (filename, len, "%s/kernel", tmpcd);
504 snprintf (filename2, len, "%s/kernel", cachedir);
506 if (rename (filename, filename2) == -1) {
507 perrorf (g, "rename: %s %s", filename, filename2);
512 snprintf (filename, len, "%s/initrd", tmpcd);
513 snprintf (filename2, len, "%s/initrd", cachedir);
515 if (rename (filename, filename2) == -1) {
516 perrorf (g, "rename: %s %s", filename, filename2);
521 snprintf (filename, len, "%s/root", tmpcd);
522 snprintf (filename2, len, "%s/root", cachedir);
524 if (rename (filename, filename2) == -1) {
525 perrorf (g, "rename: %s %s", filename, filename2);
532 /* Now finish off by linking to the cached appliance and returning it. */
533 if (hard_link_to_cached_appliance (g, cachedir,
534 kernel, initrd, appliance) == -1) {
539 /* Releases the lock on checksum. */
540 if (close (fd) == -1) {
541 perrorf (g, "close");
548 /* NB: lock on checksum file must be held when this is called. */
550 hard_link_to_cached_appliance (guestfs_h *g,
551 const char *cachedir,
552 char **kernel, char **initrd, char **appliance)
554 pid_t pid = getpid ();
555 size_t len = strlen (cachedir) + 32;
557 *kernel = safe_malloc (g, len);
558 *initrd = safe_malloc (g, len);
559 *appliance = safe_malloc (g, len);
560 snprintf (*kernel, len, "%s/kernel.%d", cachedir, pid);
561 snprintf (*initrd, len, "%s/initrd.%d", cachedir, pid);
562 snprintf (*appliance, len, "%s/root.%d", cachedir, pid);
565 snprintf (filename, len, "%s/kernel", cachedir);
566 (void) unlink (*kernel);
567 if (link (filename, *kernel) == -1) {
568 perrorf (g, "link: %s %s", filename, *kernel);
571 (void) lutimes (filename, NULL); /* lutimes because it's a symlink */
573 snprintf (filename, len, "%s/initrd", cachedir);
574 (void) unlink (*initrd);
575 if (link (filename, *initrd) == -1) {
576 perrorf (g, "link: %s %s", filename, *initrd);
579 (void) utime (filename, NULL);
581 snprintf (filename, len, "%s/root", cachedir);
582 (void) unlink (*appliance);
583 if (link (filename, *appliance) == -1) {
584 perrorf (g, "link: %s %s", filename, *appliance);
587 (void) utime (filename, NULL);
598 /* Run febootstrap-supermin-helper and tell it to generate the
602 run_supermin_helper (guestfs_h *g, const char *supermin_path,
603 const char *cachedir, size_t cdlen)
605 size_t pathlen = strlen (supermin_path);
607 const char *argv[30];
611 snprintf (uid, sizeof uid, "%i", geteuid ());
613 snprintf (gid, sizeof gid, "%i", getegid ());
614 char supermin_d[pathlen + 32];
615 snprintf (supermin_d, pathlen + 32, "%s/supermin.d", supermin_path);
616 char kernel[cdlen + 32];
617 snprintf (kernel, cdlen + 32, "%s/kernel", cachedir);
618 char initrd[cdlen + 32];
619 snprintf (initrd, cdlen + 32, "%s/initrd", cachedir);
620 char root[cdlen + 32];
621 snprintf (root, cdlen + 32, "%s/root", cachedir);
623 int pass_u_g_args = getuid () != geteuid () || getgid () != getegid ();
625 argv[i++] = "febootstrap-supermin-helper";
627 argv[i++] = "--verbose";
636 argv[i++] = supermin_d;
637 argv[i++] = host_cpu;
649 if (pid > 0) { /* Parent. */
651 guestfs___print_timestamped_argv (g, argv);
654 if (waitpid (pid, &status, 0) == -1) {
655 perrorf (g, "waitpid");
658 if (!WIFEXITED (status) || WEXITSTATUS (status) != 0) {
659 error (g, _("external command failed, see earlier error messages"));
667 /* Set a sensible umask in the subprocess, so kernel and initrd
668 * output files are world-readable (RHBZ#610880).
672 execvp ("febootstrap-supermin-helper", (char * const *) argv);
674 _exit (EXIT_FAILURE);
677 /* Search elements of g->path, returning the first path element which
678 * matches the predicate function 'pred'.
680 * Function 'pred' must return a true or false value. If it returns
681 * -1 then the entire search is aborted.
684 * 1 = a path element matched, it is returned in *pelem_ret and must be
685 * freed by the caller,
686 * 0 = no path element matched, *pelem_ret is set to NULL, or
687 * -1 = error which aborts the launch process
690 find_path (guestfs_h *g,
691 int (*pred) (guestfs_h *g, const char *pelem, void *data),
697 const char *pelem = g->path;
699 /* Note that if g->path is an empty string, we want to check the
700 * current directory (for backwards compatibility with
701 * libguestfs < 1.5.4).
704 len = strcspn (pelem, ":");
706 /* Empty element or "." means current directory. */
708 *pelem_ret = safe_strdup (g, ".");
710 *pelem_ret = safe_strndup (g, pelem, len);
712 r = pred (g, *pelem_ret, data);
718 if (r != 0) /* predicate matched */
723 if (pelem[len] == ':')
729 /* Predicate didn't match on any path element. */
734 /* Returns true iff file is contained in dir. */
736 dir_contains_file (const char *dir, const char *file)
738 size_t dirlen = strlen (dir);
739 size_t filelen = strlen (file);
740 size_t len = dirlen + filelen + 2;
743 snprintf (path, len, "%s/%s", dir, file);
744 return access (path, F_OK) == 0;
747 /* Returns true iff every listed file is contained in 'dir'. */
749 dir_contains_files (const char *dir, ...)
754 va_start (args, dir);
755 while ((file = va_arg (args, const char *)) != NULL) {
756 if (!dir_contains_file (dir, file)) {