appliance: Remove repo from appliance filename (RHBZ#638901).
[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." host_cpu;
43 static const char *initrd_name = "initramfs." 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             "'%s/supermin.d' "
166             host_cpu,
167             g->verbose ? " --verbose" : "",
168             supermin_path);
169
170   if (g->verbose)
171     guestfs___print_timestamped_message (g, "%s", cmd);
172
173   /* Errors here are non-fatal, so we don't need to call error(). */
174   FILE *pp = popen (cmd, "r");
175   if (pp == NULL)
176     return NULL;
177
178   char checksum[256];
179   if (fgets (checksum, sizeof checksum, pp) == NULL) {
180     pclose (pp);
181     return NULL;
182   }
183
184   if (pclose (pp) == -1) {
185     perror ("pclose");
186     return NULL;
187   }
188
189   len = strlen (checksum);
190
191   if (len < 16) {               /* sanity check */
192     fprintf (stderr, "libguestfs: internal error: febootstrap-supermin-helper -f checksum returned a short string\n");
193     return NULL;
194   }
195
196   if (len > 0 && checksum[len-1] == '\n')
197     checksum[--len] = '\0';
198
199   return safe_strndup (g, checksum, len);
200 }
201
202 /* Check for cached appliance in $TMPDIR/$checksum.  Check it exists
203  * and passes some basic security checks.
204  *
205  * Returns:
206  * 1 = exists, and passes
207  * 0 = does not exist
208  * -1 = error which should abort the whole launch process
209  */
210 static int
211 security_check_cache_file (guestfs_h *g, const char *filename,
212                            const struct stat *statbuf)
213 {
214   uid_t uid = geteuid ();
215
216   if (statbuf->st_uid != uid) {
217     error (g, ("libguestfs cached appliance %s is not owned by UID %d\n"),
218            filename, uid);
219     return -1;
220   }
221
222   if ((statbuf->st_mode & 0022) != 0) {
223     error (g, ("libguestfs cached appliance %s is writable by group or other (mode %o)\n"),
224            filename, statbuf->st_mode);
225     return -1;
226   }
227
228   return 0;
229 }
230
231 static int
232 check_for_cached_appliance (guestfs_h *g,
233                             const char *supermin_path, const char *checksum,
234                             char **kernel, char **initrd, char **appliance)
235 {
236   const char *tmpdir = guestfs_tmpdir ();
237
238   size_t len = strlen (tmpdir) + strlen (checksum) + 2;
239   char cachedir[len];
240   snprintf (cachedir, len, "%s/%s", tmpdir, checksum);
241
242   /* Touch the directory to prevent it being deleting in a rare race
243    * between us doing the checks and a tmp cleaner running.  Note this
244    * doesn't create the directory, and we ignore any error.
245    */
246   (void) utime (cachedir, NULL);
247
248   /* See if the cache directory exists and passes some simple checks
249    * to make sure it has not been tampered with.  Note that geteuid()
250    * forms a part of the checksum.
251    */
252   struct stat statbuf;
253   if (lstat (cachedir, &statbuf) == -1)
254     return 0;
255
256   if (security_check_cache_file (g, cachedir, &statbuf) == -1)
257     return -1;
258
259   int ret;
260
261   *kernel = safe_malloc (g, len + 8 /* / + "kernel" + \0 */);
262   *initrd = safe_malloc (g, len + 8 /* / + "initrd" + \0 */);
263   *appliance = safe_malloc (g, len + 6 /* / + "root" + \0 */);
264   sprintf (*kernel, "%s/kernel", cachedir);
265   sprintf (*initrd, "%s/initrd", cachedir);
266   sprintf (*appliance, "%s/root", cachedir);
267
268   /* Touch the files to prevent them being deleted, and to bring the
269    * cache up to date.  Note this doesn't create the files.
270    */
271   (void) utime (*kernel, NULL);
272
273   /* NB. *kernel is a symlink, so we want to check the kernel, not the
274    * link (stat, not lstat).  We don't do a security check on the
275    * kernel since it's always under /boot.
276    */
277   if (stat (*kernel, &statbuf) == -1) {
278     ret = 0;
279     goto error;
280   }
281
282   (void) utime (*initrd, NULL);
283
284   if (lstat (*initrd, &statbuf) == -1) {
285     ret = 0;
286     goto error;
287   }
288
289   if (security_check_cache_file (g, *initrd, &statbuf) == -1) {
290     ret = -1;
291     goto error;
292   }
293
294   (void) utime (*appliance, NULL);
295
296   if (lstat (*appliance, &statbuf) == -1) {
297     ret = 0;
298     goto error;
299   }
300
301   if (security_check_cache_file (g, *appliance, &statbuf) == -1) {
302     ret = -1;
303     goto error;
304   }
305
306   /* Exists! */
307   return 1;
308
309  error:
310   free (*kernel);
311   free (*initrd);
312   free (*appliance);
313   return ret;
314 }
315
316 /* Build supermin appliance from supermin_path to $TMPDIR/$checksum.
317  *
318  * Returns:
319  * 0 = built
320  * -1 = error (aborts launch)
321  */
322 static int
323 build_supermin_appliance (guestfs_h *g,
324                           const char *supermin_path, const char *checksum,
325                           char **kernel, char **initrd, char **appliance)
326 {
327   if (g->verbose)
328     guestfs___print_timestamped_message (g, "begin building supermin appliance");
329
330   const char *tmpdir = guestfs_tmpdir ();
331   size_t cdlen = strlen (tmpdir) + strlen (checksum) + 2;
332   char cachedir[cdlen];
333   snprintf (cachedir, cdlen, "%s/%s", tmpdir, checksum);
334
335   /* Don't worry about this failing, because the
336    * febootstrap-supermin-helper command will fail if the directory
337    * doesn't exist.  Note the directory might already exist, eg. if a
338    * tmp cleaner has removed the existing appliance but not the
339    * directory itself.
340    */
341   (void) mkdir (cachedir, 0755);
342
343   if (g->verbose)
344     guestfs___print_timestamped_message (g, "run febootstrap-supermin-helper");
345
346   int r = run_supermin_helper (g, supermin_path, cachedir, cdlen);
347   if (r == -1)
348     return -1;
349
350   if (g->verbose)
351     guestfs___print_timestamped_message (g, "finished building supermin appliance");
352
353   *kernel = safe_malloc (g, cdlen + 8 /* / + "kernel" + \0 */);
354   *initrd = safe_malloc (g, cdlen + 8 /* / + "initrd" + \0 */);
355   *appliance = safe_malloc (g, cdlen + 6 /* / + "root" + \0 */);
356   sprintf (*kernel, "%s/kernel", cachedir);
357   sprintf (*initrd, "%s/initrd", cachedir);
358   sprintf (*appliance, "%s/root", cachedir);
359
360   return 0;
361 }
362
363 /* Run febootstrap-supermin-helper and tell it to generate the
364  * appliance.  Note that we have to do an explicit fork/exec here.
365  * 'system' goes via the shell, and on systems that have bash, bash
366  * has a misfeature where it resets the euid to uid which breaks
367  * virt-v2v.  'posix_spawn' was also considered but that doesn't allow
368  * us to reset the umask.
369  */
370 static int
371 run_supermin_helper (guestfs_h *g, const char *supermin_path,
372                      const char *cachedir, size_t cdlen)
373 {
374   pid_t pid = fork ();
375   if (pid == -1) {
376     perrorf (g, "fork");
377     return -1;
378   }
379
380   if (pid > 0) {                /* Parent. */
381     int status;
382     if (waitpid (pid, &status, 0) == -1) {
383       perrorf (g, "waitpid");
384       return -1;
385     }
386     if (!WIFEXITED (status) || WEXITSTATUS (status) != 0) {
387       error (g, _("external command failed, see earlier error messages"));
388       return -1;
389     }
390     return 0;
391   }
392
393   /* Child. */
394
395   /* Set a sensible umask in the subprocess, so kernel and initrd
396    * output files are world-readable (RHBZ#610880).
397    */
398   umask (0022);
399
400   /* Set uid/gid in the child.  This is a workaround for a misfeature
401    * in bash which breaks virt-v2v - see the comment at the top of
402    * this function.
403    */
404   if (getuid () == 0) {
405     int egid = getegid ();
406     int euid = geteuid ();
407
408     if (egid != 0 || euid != 0) {
409       if (seteuid (0) == -1) {
410         perror ("seteuid");
411         _exit (EXIT_FAILURE);
412       }
413
414       if (setgid (egid) == -1) {
415         perror ("setgid");
416         _exit (EXIT_FAILURE);
417       }
418
419       if (setuid (euid) == -1) {
420         perror ("setuid");
421         _exit (EXIT_FAILURE);
422       }
423     }
424   }
425
426   size_t pathlen = strlen (supermin_path);
427
428   const char *argv[30];
429   size_t i = 0;
430
431   argv[i++] = "febootstrap-supermin-helper";
432   if (g->verbose)
433     argv[i++] = "--verbose";
434   argv[i++] = "-f";
435   argv[i++] = "ext2";
436   char supermin_d[pathlen + 32];
437   snprintf (supermin_d, pathlen + 32, "%s/supermin.d", supermin_path);
438   argv[i++] = supermin_d;
439   argv[i++] = host_cpu;
440   char kernel[cdlen + 32];
441   snprintf (kernel, cdlen + 32, "%s/kernel", cachedir);
442   argv[i++] = kernel;
443   char initrd[cdlen + 32];
444   snprintf (initrd, cdlen + 32, "%s/initrd", cachedir);
445   argv[i++] = initrd;
446   char root[cdlen + 32];
447   snprintf (root, cdlen + 32, "%s/root", cachedir);
448   argv[i++] = root;
449   argv[i++] = NULL;
450
451   execvp ("febootstrap-supermin-helper", (char * const *) argv);
452   perror ("execvp");
453   _exit (EXIT_FAILURE);
454 }
455
456 /* Search elements of g->path, returning the first path element which
457  * matches the predicate function 'pred'.
458  *
459  * Function 'pred' must return a true or false value.  If it returns
460  * -1 then the entire search is aborted.
461  *
462  * Return values:
463  * 1 = a path element matched, it is returned in *pelem_ret and must be
464  *     freed by the caller,
465  * 0 = no path element matched, *pelem_ret is set to NULL, or
466  * -1 = error which aborts the launch process
467  */
468 static int
469 find_path (guestfs_h *g,
470            int (*pred) (guestfs_h *g, const char *pelem, void *data),
471            void *data,
472            char **pelem_ret)
473 {
474   size_t len;
475   int r;
476   const char *pelem = g->path;
477
478   /* Note that if g->path is an empty string, we want to check the
479    * current directory (for backwards compatibility with
480    * libguestfs < 1.5.4).
481    */
482   do {
483     len = strcspn (pelem, ":");
484
485     /* Empty element or "." means current directory. */
486     if (len == 0)
487       *pelem_ret = safe_strdup (g, ".");
488     else
489       *pelem_ret = safe_strndup (g, pelem, len);
490
491     r = pred (g, *pelem_ret, data);
492     if (r == -1) {
493       free (*pelem_ret);
494       return -1;
495     }
496
497     if (r != 0)                 /* predicate matched */
498       return 1;
499
500     free (*pelem_ret);
501
502     if (pelem[len] == ':')
503       pelem += len + 1;
504     else
505       pelem += len;
506   } while (*pelem);
507
508   /* Predicate didn't match on any path element. */
509   *pelem_ret = NULL;
510   return 0;
511 }
512
513 /* Returns true iff file is contained in dir. */
514 static int
515 dir_contains_file (const char *dir, const char *file)
516 {
517   size_t dirlen = strlen (dir);
518   size_t filelen = strlen (file);
519   size_t len = dirlen + filelen + 2;
520   char path[len];
521
522   snprintf (path, len, "%s/%s", dir, file);
523   return access (path, F_OK) == 0;
524 }
525
526 /* Returns true iff every listed file is contained in 'dir'. */
527 static int
528 dir_contains_files (const char *dir, ...)
529 {
530   va_list args;
531   const char *file;
532
533   va_start (args, dir);
534   while ((file = va_arg (args, const char *)) != NULL) {
535     if (!dir_contains_file (dir, file)) {
536       va_end (args);
537       return 0;
538     }
539   }
540   va_end (args);
541   return 1;
542 }