build: Create an AUTHORS file.
[libguestfs.git] / src / appliance.c
1 /* libguestfs
2  * Copyright (C) 2010 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 static const char *kernel_name = "vmlinuz." host_cpu;
45 static const char *initrd_name = "initramfs." host_cpu ".img";
46
47 static int find_path (guestfs_h *g, int (*pred) (guestfs_h *g, const char *pelem, void *data), void *data, char **pelem);
48 static int dir_contains_file (const char *dir, const char *file);
49 static int dir_contains_files (const char *dir, ...);
50 static int contains_ordinary_appliance (guestfs_h *g, const char *path, void *data);
51 #if ENABLE_SUPERMIN
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, char **kernel, char **initrd, char **appliance);
55 static int build_supermin_appliance (guestfs_h *g, const char *supermin_path, const char *checksum, char **kernel, char **initrd, char **appliance);
56 static int run_supermin_helper (guestfs_h *g, const char *supermin_path, const char *cachedir, size_t cdlen);
57 #endif
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  * (2) Calculate the checksum of this supermin appliance.
79  * (3) Check whether $TMPDIR/$checksum/ directory exists, contains
80  * a cached appliance, and passes basic security checks.  If so,
81  * return this appliance.
82  * (4) Try to build the supermin appliance into $TMPDIR/$checksum/.
83  * If this is successful, return it.
84  * (5) Check each element of g->path, looking for an ordinary appliance.
85  * If one is found, return it.
86  */
87 int
88 guestfs___build_appliance (guestfs_h *g,
89                            char **kernel, char **initrd, char **appliance)
90 {
91   int r;
92
93 #if ENABLE_SUPERMIN
94   /* Step (1). */
95   char *supermin_path;
96   r = find_path (g, contains_supermin_appliance, NULL, &supermin_path);
97   if (r == -1)
98     return -1;
99
100   if (r == 1) {
101     /* Step (2): calculate checksum. */
102     char *checksum = calculate_supermin_checksum (g, supermin_path);
103     if (checksum) {
104       /* Step (3): cached appliance exists? */
105       r = check_for_cached_appliance (g, supermin_path, checksum,
106                                       kernel, initrd, appliance);
107       if (r != 0) {
108         free (supermin_path);
109         free (checksum);
110         return r == 1 ? 0 : -1;
111       }
112
113       /* Step (4): build supermin appliance. */
114       r = build_supermin_appliance (g, supermin_path, checksum,
115                                     kernel, initrd, appliance);
116       free (supermin_path);
117       free (checksum);
118       return r;
119     }
120     free (supermin_path);
121   }
122 #endif
123
124   /* Step (5). */
125   char *path;
126   r = find_path (g, contains_ordinary_appliance, NULL, &path);
127   if (r == -1)
128     return -1;
129
130   if (r == 1) {
131     size_t len = strlen (path);
132     *kernel = safe_malloc (g, len + strlen (kernel_name) + 2);
133     *initrd = safe_malloc (g, len + strlen (initrd_name) + 2);
134     sprintf (*kernel, "%s/%s", path, kernel_name);
135     sprintf (*initrd, "%s/%s", path, initrd_name);
136     *appliance = NULL;
137
138     free (path);
139     return 0;
140   }
141
142   error (g, _("cannot find any suitable libguestfs supermin or ordinary appliance on LIBGUESTFS_PATH (search path: %s)"),
143          g->path);
144   return -1;
145 }
146
147 static int
148 contains_ordinary_appliance (guestfs_h *g, const char *path, void *data)
149 {
150   return dir_contains_files (path, kernel_name, initrd_name, NULL);
151 }
152
153 #if ENABLE_SUPERMIN
154 static int
155 contains_supermin_appliance (guestfs_h *g, const char *path, void *data)
156 {
157   return dir_contains_files (path, "supermin.d", "kmod.whitelist", NULL);
158 }
159
160 /* supermin_path is a path which is known to contain a supermin
161  * appliance.  Using febootstrap-supermin-helper -f checksum calculate
162  * the checksum so we can see if it is cached.
163  */
164 static char *
165 calculate_supermin_checksum (guestfs_h *g, const char *supermin_path)
166 {
167   size_t len = 2 * strlen (supermin_path) + 256;
168   char cmd[len];
169   int pass_u_g_args = getuid () != geteuid () || getgid () != getegid ();
170
171   if (!pass_u_g_args)
172     snprintf (cmd, len,
173               "febootstrap-supermin-helper%s "
174               "-f checksum "
175               "'%s/supermin.d' "
176               host_cpu,
177               g->verbose ? " --verbose" : "",
178               supermin_path);
179   else
180     snprintf (cmd, len,
181               "febootstrap-supermin-helper%s "
182               "-u %i "
183               "-g %i "
184               "-f checksum "
185               "'%s/supermin.d' "
186               host_cpu,
187               g->verbose ? " --verbose" : "",
188               geteuid (), getegid (),
189               supermin_path);
190
191   if (g->verbose)
192     guestfs___print_timestamped_message (g, "%s", cmd);
193
194   /* Errors here are non-fatal, so we don't need to call error(). */
195   FILE *pp = popen (cmd, "r");
196   if (pp == NULL)
197     return NULL;
198
199   char checksum[256];
200   if (fgets (checksum, sizeof checksum, pp) == NULL) {
201     pclose (pp);
202     return NULL;
203   }
204
205   if (pclose (pp) == -1) {
206     perror ("pclose");
207     return NULL;
208   }
209
210   len = strlen (checksum);
211
212   if (len < 16) {               /* sanity check */
213     fprintf (stderr, "libguestfs: internal error: febootstrap-supermin-helper -f checksum returned a short string\n");
214     return NULL;
215   }
216
217   if (len > 0 && checksum[len-1] == '\n')
218     checksum[--len] = '\0';
219
220   return safe_strndup (g, checksum, len);
221 }
222
223 /* Check for cached appliance in $TMPDIR/$checksum.  Check it exists
224  * and passes some basic security checks.
225  *
226  * Returns:
227  * 1 = exists, and passes
228  * 0 = does not exist
229  * -1 = error which should abort the whole launch process
230  */
231 static int
232 security_check_cache_file (guestfs_h *g, const char *filename,
233                            const struct stat *statbuf)
234 {
235   uid_t uid = geteuid ();
236
237   if (statbuf->st_uid != uid) {
238     error (g, ("libguestfs cached appliance %s is not owned by UID %d\n"),
239            filename, uid);
240     return -1;
241   }
242
243   if ((statbuf->st_mode & 0022) != 0) {
244     error (g, ("libguestfs cached appliance %s is writable by group or other (mode %o)\n"),
245            filename, statbuf->st_mode);
246     return -1;
247   }
248
249   return 0;
250 }
251
252 static int
253 check_for_cached_appliance (guestfs_h *g,
254                             const char *supermin_path, const char *checksum,
255                             char **kernel, char **initrd, char **appliance)
256 {
257   const char *tmpdir = guestfs_tmpdir ();
258
259   size_t len = strlen (tmpdir) + strlen (checksum) + 10;
260   char cachedir[len];
261   snprintf (cachedir, len, "%s/guestfs.%s", tmpdir, checksum);
262
263   /* Touch the directory to prevent it being deleting in a rare race
264    * between us doing the checks and a tmp cleaner running.  Note this
265    * doesn't create the directory, and we ignore any error.
266    */
267   (void) utime (cachedir, NULL);
268
269   /* See if the cache directory exists and passes some simple checks
270    * to make sure it has not been tampered with.  Note that geteuid()
271    * forms a part of the checksum.
272    */
273   struct stat statbuf;
274   if (lstat (cachedir, &statbuf) == -1)
275     return 0;
276
277   if (security_check_cache_file (g, cachedir, &statbuf) == -1)
278     return -1;
279
280   int ret;
281
282   *kernel = safe_malloc (g, len + 8 /* / + "kernel" + \0 */);
283   *initrd = safe_malloc (g, len + 8 /* / + "initrd" + \0 */);
284   *appliance = safe_malloc (g, len + 6 /* / + "root" + \0 */);
285   sprintf (*kernel, "%s/kernel", cachedir);
286   sprintf (*initrd, "%s/initrd", cachedir);
287   sprintf (*appliance, "%s/root", cachedir);
288
289   /* Touch the files to prevent them being deleted, and to bring the
290    * cache up to date.  Note this doesn't create the files.
291    */
292   (void) utime (*kernel, NULL);
293
294   /* NB. *kernel is a symlink, so we want to check the kernel, not the
295    * link (stat, not lstat).  We don't do a security check on the
296    * kernel since it's always under /boot.
297    */
298   if (stat (*kernel, &statbuf) == -1) {
299     ret = 0;
300     goto error;
301   }
302
303   (void) utime (*initrd, NULL);
304
305   if (lstat (*initrd, &statbuf) == -1) {
306     ret = 0;
307     goto error;
308   }
309
310   if (security_check_cache_file (g, *initrd, &statbuf) == -1) {
311     ret = -1;
312     goto error;
313   }
314
315   (void) utime (*appliance, NULL);
316
317   if (lstat (*appliance, &statbuf) == -1) {
318     ret = 0;
319     goto error;
320   }
321
322   if (security_check_cache_file (g, *appliance, &statbuf) == -1) {
323     ret = -1;
324     goto error;
325   }
326
327   /* Exists! */
328   return 1;
329
330  error:
331   free (*kernel);
332   free (*initrd);
333   free (*appliance);
334   return ret;
335 }
336
337 /* Build supermin appliance from supermin_path to $TMPDIR/$checksum.
338  *
339  * Returns:
340  * 0 = built
341  * -1 = error (aborts launch)
342  */
343 static int
344 build_supermin_appliance (guestfs_h *g,
345                           const char *supermin_path, const char *checksum,
346                           char **kernel, char **initrd, char **appliance)
347 {
348   if (g->verbose)
349     guestfs___print_timestamped_message (g, "begin building supermin appliance");
350
351   const char *tmpdir = guestfs_tmpdir ();
352
353   size_t tmpcdlen = strlen (tmpdir) + 16;
354   char tmpcd[tmpcdlen];
355   snprintf (tmpcd, tmpcdlen, "%s/guestfs.XXXXXX", tmpdir);
356
357   if (NULL == mkdtemp (tmpcd)) {
358     error (g, _("failed to create temporary cache directory: %m"));
359     return -1;
360   }
361
362   if (g->verbose)
363     guestfs___print_timestamped_message (g, "run febootstrap-supermin-helper");
364
365   int r = run_supermin_helper (g, supermin_path, tmpcd, tmpcdlen);
366   if (r == -1)
367     return -1;
368
369   if (g->verbose)
370     guestfs___print_timestamped_message (g, "finished building supermin appliance");
371
372   size_t cdlen = strlen (tmpdir) + strlen (checksum) + 10;
373   char cachedir[cdlen];
374   snprintf (cachedir, cdlen, "%s/guestfs.%s", tmpdir, checksum);
375
376   /* Make the temporary directory world readable */
377   if (chmod (tmpcd, 0755) == -1) {
378     error (g, "chmod %s: %m", tmpcd);
379   }
380
381   /* Try to rename the temporary directory to its non-temporary name */
382   if (rename (tmpcd, cachedir) == -1) {
383     /* If the cache directory now exists, we may have been racing with another
384      * libguestfs process. Check the new directory and use it if it's valid. */
385     if (errno == ENOTEMPTY || errno == EEXIST) {
386       /* Appliance cache consists of 2 files and a symlink in the cache
387        * directory. Delete them first. */
388       DIR *dir = opendir (tmpcd);
389       if (dir == NULL) {
390         error (g, "opendir %s: %m", tmpcd);
391         return -1;
392       }
393
394       int fd = dirfd (dir);
395       if (fd == -1) {
396         error (g, "dirfd: %m");
397         closedir (dir);
398         return -1;
399       }
400
401       struct dirent *dirent;
402       for (;;) {
403         errno = 0;
404         dirent = readdir (dir);
405
406         if (dirent == NULL) {
407           break;
408         }
409
410         /* Check that dirent is a file so we don't try to delete . and .. */
411         struct stat st;
412         if (fstatat (fd, dirent->d_name, &st, AT_SYMLINK_NOFOLLOW) == -1) {
413           error (g, "fstatat %s: %m", dirent->d_name);
414           return -1;
415         }
416
417         if (!S_ISDIR(st.st_mode)) {
418           if (unlinkat (fd, dirent->d_name, 0) == -1) {
419             error (g, "unlinkat %s: %m", dirent->d_name);
420             closedir (dir);
421             return -1;
422           }
423         }
424       }
425
426       if (errno != 0) {
427         error (g, "readdir %s: %m", tmpcd);
428         closedir (dir);
429         return -1;
430       }
431
432       closedir (dir);
433
434       /* Delete the temporary cache directory itself. */
435       if (rmdir (tmpcd) == -1) {
436         error (g, "rmdir %s: %m", tmpcd);
437         return -1;
438       }
439
440       /* Check the new cache directory, and return it if valid */
441       return check_for_cached_appliance (g, supermin_path, checksum,
442                                          kernel, initrd, appliance);
443     }
444
445     else {
446       error (g, _("error renaming temporary cache directory: %m"));
447       return -1;
448     }
449   }
450
451   *kernel = safe_malloc (g, cdlen + 8 /* / + "kernel" + \0 */);
452   *initrd = safe_malloc (g, cdlen + 8 /* / + "initrd" + \0 */);
453   *appliance = safe_malloc (g, cdlen + 6 /* / + "root" + \0 */);
454   sprintf (*kernel, "%s/kernel", cachedir);
455   sprintf (*initrd, "%s/initrd", cachedir);
456   sprintf (*appliance, "%s/root", cachedir);
457
458   return 0;
459 }
460
461 /* Run febootstrap-supermin-helper and tell it to generate the
462  * appliance.
463  */
464 static int
465 run_supermin_helper (guestfs_h *g, const char *supermin_path,
466                      const char *cachedir, size_t cdlen)
467 {
468   size_t pathlen = strlen (supermin_path);
469
470   const char *argv[30];
471   size_t i = 0;
472
473   char uid[32];
474   snprintf (uid, sizeof uid, "%i", geteuid ());
475   char gid[32];
476   snprintf (gid, sizeof gid, "%i", getegid ());
477   char supermin_d[pathlen + 32];
478   snprintf (supermin_d, pathlen + 32, "%s/supermin.d", supermin_path);
479   char kernel[cdlen + 32];
480   snprintf (kernel, cdlen + 32, "%s/kernel", cachedir);
481   char initrd[cdlen + 32];
482   snprintf (initrd, cdlen + 32, "%s/initrd", cachedir);
483   char root[cdlen + 32];
484   snprintf (root, cdlen + 32, "%s/root", cachedir);
485
486   int pass_u_g_args = getuid () != geteuid () || getgid () != getegid ();
487
488   argv[i++] = "febootstrap-supermin-helper";
489   if (g->verbose)
490     argv[i++] = "--verbose";
491   if (pass_u_g_args) {
492     argv[i++] = "-u";
493     argv[i++] = uid;
494     argv[i++] = "-g";
495     argv[i++] = gid;
496   }
497   argv[i++] = "-f";
498   argv[i++] = "ext2";
499   argv[i++] = supermin_d;
500   argv[i++] = host_cpu;
501   argv[i++] = kernel;
502   argv[i++] = initrd;
503   argv[i++] = root;
504   argv[i++] = NULL;
505
506   pid_t pid = fork ();
507   if (pid == -1) {
508     perrorf (g, "fork");
509     return -1;
510   }
511
512   if (pid > 0) {                /* Parent. */
513     if (g->verbose)
514       guestfs___print_timestamped_argv (g, argv);
515
516     int status;
517     if (waitpid (pid, &status, 0) == -1) {
518       perrorf (g, "waitpid");
519       return -1;
520     }
521     if (!WIFEXITED (status) || WEXITSTATUS (status) != 0) {
522       error (g, _("external command failed, see earlier error messages"));
523       return -1;
524     }
525     return 0;
526   }
527
528   /* Child. */
529
530   /* Set a sensible umask in the subprocess, so kernel and initrd
531    * output files are world-readable (RHBZ#610880).
532    */
533   umask (0022);
534
535   execvp ("febootstrap-supermin-helper", (char * const *) argv);
536   perror ("execvp");
537   _exit (EXIT_FAILURE);
538 }
539 #endif
540
541 /* Search elements of g->path, returning the first path element which
542  * matches the predicate function 'pred'.
543  *
544  * Function 'pred' must return a true or false value.  If it returns
545  * -1 then the entire search is aborted.
546  *
547  * Return values:
548  * 1 = a path element matched, it is returned in *pelem_ret and must be
549  *     freed by the caller,
550  * 0 = no path element matched, *pelem_ret is set to NULL, or
551  * -1 = error which aborts the launch process
552  */
553 static int
554 find_path (guestfs_h *g,
555            int (*pred) (guestfs_h *g, const char *pelem, void *data),
556            void *data,
557            char **pelem_ret)
558 {
559   size_t len;
560   int r;
561   const char *pelem = g->path;
562
563   /* Note that if g->path is an empty string, we want to check the
564    * current directory (for backwards compatibility with
565    * libguestfs < 1.5.4).
566    */
567   do {
568     len = strcspn (pelem, ":");
569
570     /* Empty element or "." means current directory. */
571     if (len == 0)
572       *pelem_ret = safe_strdup (g, ".");
573     else
574       *pelem_ret = safe_strndup (g, pelem, len);
575
576     r = pred (g, *pelem_ret, data);
577     if (r == -1) {
578       free (*pelem_ret);
579       return -1;
580     }
581
582     if (r != 0)                 /* predicate matched */
583       return 1;
584
585     free (*pelem_ret);
586
587     if (pelem[len] == ':')
588       pelem += len + 1;
589     else
590       pelem += len;
591   } while (*pelem);
592
593   /* Predicate didn't match on any path element. */
594   *pelem_ret = NULL;
595   return 0;
596 }
597
598 /* Returns true iff file is contained in dir. */
599 static int
600 dir_contains_file (const char *dir, const char *file)
601 {
602   size_t dirlen = strlen (dir);
603   size_t filelen = strlen (file);
604   size_t len = dirlen + filelen + 2;
605   char path[len];
606
607   snprintf (path, len, "%s/%s", dir, file);
608   return access (path, F_OK) == 0;
609 }
610
611 /* Returns true iff every listed file is contained in 'dir'. */
612 static int
613 dir_contains_files (const char *dir, ...)
614 {
615   va_list args;
616   const char *file;
617
618   va_start (args, dir);
619   while ((file = va_arg (args, const char *)) != NULL) {
620     if (!dir_contains_file (dir, file)) {
621       va_end (args);
622       return 0;
623     }
624   }
625   va_end (args);
626   return 1;
627 }