Document ambiguity between devices and paths in API.
[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 <stdio.h>
22 #include <stdlib.h>
23 #include <stdarg.h>
24 #include <unistd.h>
25 #include <string.h>
26 #include <fcntl.h>
27 #include <time.h>
28 #include <sys/stat.h>
29 #include <sys/select.h>
30 #include <sys/wait.h>
31 #include <utime.h>
32
33 #ifdef HAVE_SYS_TYPES_H
34 #include <sys/types.h>
35 #endif
36
37 #include "guestfs.h"
38 #include "guestfs-internal.h"
39 #include "guestfs-internal-actions.h"
40 #include "guestfs_protocol.h"
41
42 static const char *kernel_name = "vmlinuz." REPO "." host_cpu;
43 static const char *initrd_name = "initramfs." REPO "." host_cpu ".img";
44
45 static int find_path (guestfs_h *g, int (*pred) (guestfs_h *g, const char *pelem, void *data), void *data, char **pelem);
46 static int dir_contains_file (const char *dir, const char *file);
47 static int dir_contains_files (const char *dir, ...);
48 static int contains_supermin_appliance (guestfs_h *g, const char *path, void *data);
49 static int contains_ordinary_appliance (guestfs_h *g, const char *path, void *data);
50 static char *calculate_supermin_checksum (guestfs_h *g, const char *supermin_path);
51 static int check_for_cached_appliance (guestfs_h *g, const char *supermin_path, const char *checksum, char **kernel, char **initrd, char **appliance);
52 static int build_supermin_appliance (guestfs_h *g, const char *supermin_path, const char *checksum, char **kernel, char **initrd, char **appliance);
53 static int run_supermin_helper (guestfs_h *g, const char *supermin_path, const char *cachedir, size_t cdlen);
54
55 /* Locate or build the appliance.
56  *
57  * This function locates or builds the appliance as necessary,
58  * handling the supermin appliance, caching of supermin-built
59  * appliances, or using an ordinary appliance.
60  *
61  * The return value is 0 = good, -1 = error.  Returned in '*kernel'
62  * will be the name of the kernel to use, '*initrd' the name of the
63  * initrd, '*appliance' the name of the ext2 root filesystem.
64  * '*appliance' can be NULL, meaning that we are using an ordinary
65  * (non-ext2) appliance.  All three strings must be freed by the
66  * caller.  However the referenced files themselves must not be
67  * deleted.
68  *
69  * The process is as follows:
70  *
71  * (1) Look for the first element of g->path which contains a
72  * supermin appliance skeleton.  If no element has this, skip
73  * straight to step (5).
74  * (2) Calculate the checksum of this supermin appliance.
75  * (3) Check whether $TMPDIR/$checksum/ directory exists, contains
76  * a cached appliance, and passes basic security checks.  If so,
77  * return this appliance.
78  * (4) Try to build the supermin appliance into $TMPDIR/$checksum/.
79  * If this is successful, return it.
80  * (5) Check each element of g->path, looking for an ordinary appliance.
81  * If one is found, return it.
82  */
83 int
84 guestfs___build_appliance (guestfs_h *g,
85                            char **kernel, char **initrd, char **appliance)
86 {
87   int r;
88
89   /* Step (1). */
90   char *supermin_path;
91   r = find_path (g, contains_supermin_appliance, NULL, &supermin_path);
92   if (r == -1)
93     return -1;
94
95   if (r == 1) {
96     /* Step (2): calculate checksum. */
97     char *checksum = calculate_supermin_checksum (g, supermin_path);
98     if (checksum) {
99       /* Step (3): cached appliance exists? */
100       r = check_for_cached_appliance (g, supermin_path, checksum,
101                                       kernel, initrd, appliance);
102       if (r != 0) {
103         free (supermin_path);
104         free (checksum);
105         return r == 1 ? 0 : -1;
106       }
107
108       /* Step (4): build supermin appliance. */
109       r = build_supermin_appliance (g, supermin_path, checksum,
110                                     kernel, initrd, appliance);
111       free (supermin_path);
112       free (checksum);
113       return r;
114     }
115     free (supermin_path);
116   }
117
118   /* Step (5). */
119   char *path;
120   r = find_path (g, contains_ordinary_appliance, NULL, &path);
121   if (r == -1)
122     return -1;
123
124   if (r == 1) {
125     size_t len = strlen (path);
126     *kernel = safe_malloc (g, len + strlen (kernel_name) + 2);
127     *initrd = safe_malloc (g, len + strlen (initrd_name) + 2);
128     sprintf (*kernel, "%s/%s", path, kernel_name);
129     sprintf (*initrd, "%s/%s", path, initrd_name);
130     *appliance = NULL;
131
132     free (path);
133     return 0;
134   }
135
136   error (g, _("cannot find any suitable libguestfs supermin or ordinary appliance on LIBGUESTFS_PATH (search path: %s)"),
137          g->path);
138   return -1;
139 }
140
141 static int
142 contains_supermin_appliance (guestfs_h *g, const char *path, void *data)
143 {
144   return dir_contains_files (path, "supermin.d", "kmod.whitelist", NULL);
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 /* supermin_path is a path which is known to contain a supermin
154  * appliance.  Using febootstrap-supermin-helper -f checksum calculate
155  * the checksum so we can see if it is cached.
156  */
157 static char *
158 calculate_supermin_checksum (guestfs_h *g, const char *supermin_path)
159 {
160   size_t len = 2 * strlen (supermin_path) + 256;
161   char cmd[len];
162   snprintf (cmd, len,
163             "febootstrap-supermin-helper%s "
164             "-f checksum "
165             "-k '%s/kmod.whitelist' "
166             "'%s/supermin.d' "
167             host_cpu,
168             g->verbose ? " --verbose" : "",
169             supermin_path,
170             supermin_path);
171
172   if (g->verbose)
173     guestfs___print_timestamped_message (g, "%s", cmd);
174
175   /* Errors here are non-fatal, so we don't need to call error(). */
176   FILE *pp = popen (cmd, "r");
177   if (pp == NULL)
178     return NULL;
179
180   char checksum[256];
181   if (fgets (checksum, sizeof checksum, pp) == NULL) {
182     pclose (pp);
183     return NULL;
184   }
185
186   if (pclose (pp) == -1) {
187     perror ("pclose");
188     return NULL;
189   }
190
191   len = strlen (checksum);
192
193   if (len < 16) {               /* sanity check */
194     fprintf (stderr, "libguestfs: internal error: febootstrap-supermin-helper -f checksum returned a short string\n");
195     return NULL;
196   }
197
198   if (len > 0 && checksum[len-1] == '\n')
199     checksum[--len] = '\0';
200
201   return safe_strndup (g, checksum, len);
202 }
203
204 /* Check for cached appliance in $TMPDIR/$checksum.  Check it exists
205  * and passes some basic security checks.
206  *
207  * Returns:
208  * 1 = exists, and passes
209  * 0 = does not exist
210  * -1 = error which should abort the whole launch process
211  */
212 static int
213 security_check_cache_file (guestfs_h *g, const char *filename,
214                            const struct stat *statbuf)
215 {
216   uid_t uid = geteuid ();
217
218   if (statbuf->st_uid != uid) {
219     error (g, ("libguestfs cached appliance %s is not owned by UID %d\n"),
220            filename, uid);
221     return -1;
222   }
223
224   if ((statbuf->st_mode & 0022) != 0) {
225     error (g, ("libguestfs cached appliance %s is writable by group or other (mode %o)\n"),
226            filename, statbuf->st_mode);
227     return -1;
228   }
229
230   return 0;
231 }
232
233 static int
234 check_for_cached_appliance (guestfs_h *g,
235                             const char *supermin_path, const char *checksum,
236                             char **kernel, char **initrd, char **appliance)
237 {
238   const char *tmpdir = guestfs_tmpdir ();
239
240   size_t len = strlen (tmpdir) + strlen (checksum) + 2;
241   char cachedir[len];
242   snprintf (cachedir, len, "%s/%s", tmpdir, checksum);
243
244   /* Touch the directory to prevent it being deleting in a rare race
245    * between us doing the checks and a tmp cleaner running.  Note this
246    * doesn't create the directory, and we ignore any error.
247    */
248   (void) utime (cachedir, NULL);
249
250   /* See if the cache directory exists and passes some simple checks
251    * to make sure it has not been tampered with.  Note that geteuid()
252    * forms a part of the checksum.
253    */
254   struct stat statbuf;
255   if (lstat (cachedir, &statbuf) == -1)
256     return 0;
257
258   if (security_check_cache_file (g, cachedir, &statbuf) == -1)
259     return -1;
260
261   int ret;
262
263   *kernel = safe_malloc (g, len + 8 /* / + "kernel" + \0 */);
264   *initrd = safe_malloc (g, len + 8 /* / + "initrd" + \0 */);
265   *appliance = safe_malloc (g, len + 6 /* / + "root" + \0 */);
266   sprintf (*kernel, "%s/kernel", cachedir);
267   sprintf (*initrd, "%s/initrd", cachedir);
268   sprintf (*appliance, "%s/root", cachedir);
269
270   /* Touch the files to prevent them being deleted, and to bring the
271    * cache up to date.  Note this doesn't create the files.
272    */
273   (void) utime (*kernel, NULL);
274
275   /* NB. *kernel is a symlink, so we want to check the kernel, not the
276    * link (stat, not lstat).  We don't do a security check on the
277    * kernel since it's always under /boot.
278    */
279   if (stat (*kernel, &statbuf) == -1) {
280     ret = 0;
281     goto error;
282   }
283
284   (void) utime (*initrd, NULL);
285
286   if (lstat (*initrd, &statbuf) == -1) {
287     ret = 0;
288     goto error;
289   }
290
291   if (security_check_cache_file (g, *initrd, &statbuf) == -1) {
292     ret = -1;
293     goto error;
294   }
295
296   (void) utime (*appliance, NULL);
297
298   if (lstat (*appliance, &statbuf) == -1) {
299     ret = 0;
300     goto error;
301   }
302
303   if (security_check_cache_file (g, *appliance, &statbuf) == -1) {
304     ret = -1;
305     goto error;
306   }
307
308   /* Exists! */
309   return 1;
310
311  error:
312   free (*kernel);
313   free (*initrd);
314   free (*appliance);
315   return ret;
316 }
317
318 /* Build supermin appliance from supermin_path to $TMPDIR/$checksum.
319  *
320  * Returns:
321  * 0 = built
322  * -1 = error (aborts launch)
323  */
324 static int
325 build_supermin_appliance (guestfs_h *g,
326                           const char *supermin_path, const char *checksum,
327                           char **kernel, char **initrd, char **appliance)
328 {
329   if (g->verbose)
330     guestfs___print_timestamped_message (g, "begin building supermin appliance");
331
332   const char *tmpdir = guestfs_tmpdir ();
333   size_t cdlen = strlen (tmpdir) + strlen (checksum) + 2;
334   char cachedir[cdlen];
335   snprintf (cachedir, cdlen, "%s/%s", tmpdir, checksum);
336
337   /* Don't worry about this failing, because the
338    * febootstrap-supermin-helper command will fail if the directory
339    * doesn't exist.  Note the directory might already exist, eg. if a
340    * tmp cleaner has removed the existing appliance but not the
341    * directory itself.
342    */
343   (void) mkdir (cachedir, 0755);
344
345   if (g->verbose)
346     guestfs___print_timestamped_message (g, "run febootstrap-supermin-helper");
347
348   int r = run_supermin_helper (g, supermin_path, cachedir, cdlen);
349   if (r == -1)
350     return -1;
351
352   if (g->verbose)
353     guestfs___print_timestamped_message (g, "finished building supermin appliance");
354
355   *kernel = safe_malloc (g, cdlen + 8 /* / + "kernel" + \0 */);
356   *initrd = safe_malloc (g, cdlen + 8 /* / + "initrd" + \0 */);
357   *appliance = safe_malloc (g, cdlen + 6 /* / + "root" + \0 */);
358   sprintf (*kernel, "%s/kernel", cachedir);
359   sprintf (*initrd, "%s/initrd", cachedir);
360   sprintf (*appliance, "%s/root", cachedir);
361
362   return 0;
363 }
364
365 /* Run febootstrap-supermin-helper and tell it to generate the
366  * appliance.  Note that we have to do an explicit fork/exec here.
367  * 'system' goes via the shell, and on systems that have bash, bash
368  * has a misfeature where it resets the euid to uid which breaks
369  * virt-v2v.  'posix_spawn' was also considered but that doesn't allow
370  * us to reset the umask.
371  */
372 static int
373 run_supermin_helper (guestfs_h *g, const char *supermin_path,
374                      const char *cachedir, size_t cdlen)
375 {
376   pid_t pid = fork ();
377   if (pid == -1) {
378     perrorf (g, "fork");
379     return -1;
380   }
381
382   if (pid > 0) {                /* Parent. */
383     int status;
384     if (waitpid (pid, &status, 0) == -1) {
385       perrorf (g, "waitpid");
386       return -1;
387     }
388     if (!WIFEXITED (status) || WEXITSTATUS (status) != 0) {
389       error (g, _("external command failed, see earlier error messages"));
390       return -1;
391     }
392     return 0;
393   }
394
395   /* Child. */
396
397   /* Set a sensible umask in the subprocess, so kernel and initrd
398    * output files are world-readable (RHBZ#610880).
399    */
400   umask (0022);
401
402   /* Set uid/gid in the child.  This is a workaround for a misfeature
403    * in bash which breaks virt-v2v - see the comment at the top of
404    * this function.
405    */
406   if (getuid () == 0) {
407     int egid = getegid ();
408     int euid = geteuid ();
409
410     if (egid != 0 || euid != 0) {
411       if (seteuid (0) == -1) {
412         perror ("seteuid");
413         _exit (EXIT_FAILURE);
414       }
415
416       if (setgid (egid) == -1) {
417         perror ("setgid");
418         _exit (EXIT_FAILURE);
419       }
420
421       if (setuid (euid) == -1) {
422         perror ("setuid");
423         _exit (EXIT_FAILURE);
424       }
425     }
426   }
427
428   size_t pathlen = strlen (supermin_path);
429
430   const char *argv[30];
431   size_t i = 0;
432
433   argv[i++] = "febootstrap-supermin-helper";
434   if (g->verbose)
435     argv[i++] = "--verbose";
436   argv[i++] = "-f";
437   argv[i++] = "ext2";
438   argv[i++] = "-k";
439   char whitelist[pathlen + 32];
440   snprintf (whitelist, pathlen + 32, "%s/kmod.whitelist", supermin_path);
441   argv[i++] = whitelist;
442   char supermin_d[pathlen + 32];
443   snprintf (supermin_d, pathlen + 32, "%s/supermin.d", supermin_path);
444   argv[i++] = supermin_d;
445   argv[i++] = host_cpu;
446   char kernel[cdlen + 32];
447   snprintf (kernel, cdlen + 32, "%s/kernel", cachedir);
448   argv[i++] = kernel;
449   char initrd[cdlen + 32];
450   snprintf (initrd, cdlen + 32, "%s/initrd", cachedir);
451   argv[i++] = initrd;
452   char root[cdlen + 32];
453   snprintf (root, cdlen + 32, "%s/root", cachedir);
454   argv[i++] = root;
455   argv[i++] = NULL;
456
457   execvp ("febootstrap-supermin-helper", (char * const *) argv);
458   perror ("execvp");
459   _exit (EXIT_FAILURE);
460 }
461
462 /* Search elements of g->path, returning the first path element which
463  * matches the predicate function 'pred'.
464  *
465  * Function 'pred' must return a true or false value.  If it returns
466  * -1 then the entire search is aborted.
467  *
468  * Return values:
469  * 1 = a path element matched, it is returned in *pelem_ret and must be
470  *     freed by the caller,
471  * 0 = no path element matched, *pelem_ret is set to NULL, or
472  * -1 = error which aborts the launch process
473  */
474 static int
475 find_path (guestfs_h *g,
476            int (*pred) (guestfs_h *g, const char *pelem, void *data),
477            void *data,
478            char **pelem_ret)
479 {
480   size_t len;
481   int r;
482   const char *pelem = g->path;
483
484   /* Note that if g->path is an empty string, we want to check the
485    * current directory (for backwards compatibility with
486    * libguestfs < 1.5.4).
487    */
488   do {
489     len = strcspn (pelem, ":");
490
491     /* Empty element or "." means current directory. */
492     if (len == 0)
493       *pelem_ret = safe_strdup (g, ".");
494     else
495       *pelem_ret = safe_strndup (g, pelem, len);
496
497     r = pred (g, *pelem_ret, data);
498     if (r == -1) {
499       free (*pelem_ret);
500       return -1;
501     }
502
503     if (r != 0)                 /* predicate matched */
504       return 1;
505
506     free (*pelem_ret);
507
508     if (pelem[len] == ':')
509       pelem += len + 1;
510     else
511       pelem += len;
512   } while (*pelem);
513
514   /* Predicate didn't match on any path element. */
515   *pelem_ret = NULL;
516   return 0;
517 }
518
519 /* Returns true iff file is contained in dir. */
520 static int
521 dir_contains_file (const char *dir, const char *file)
522 {
523   size_t dirlen = strlen (dir);
524   size_t filelen = strlen (file);
525   size_t len = dirlen + filelen + 2;
526   char path[len];
527
528   snprintf (path, len, "%s/%s", dir, file);
529   return access (path, F_OK) == 0;
530 }
531
532 /* Returns true iff every listed file is contained in 'dir'. */
533 static int
534 dir_contains_files (const char *dir, ...)
535 {
536   va_list args;
537   const char *file;
538
539   va_start (args, dir);
540   while ((file = va_arg (args, const char *)) != NULL) {
541     if (!dir_contains_file (dir, file)) {
542       va_end (args);
543       return 0;
544     }
545   }
546   va_end (args);
547   return 1;
548 }