Version 1.9.14.
[libguestfs.git] / daemon / initrd.c
1 /* libguestfs - the guestfsd daemon
2  * Copyright (C) 2009 Red Hat Inc.
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * This program 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
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17  */
18
19 #include <config.h>
20
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <limits.h>
25 #include <unistd.h>
26 #include <fcntl.h>
27 #include <sys/stat.h>
28
29 #include "guestfs_protocol.h"
30 #include "daemon.h"
31 #include "actions.h"
32
33 char **
34 do_initrd_list (const char *path)
35 {
36   FILE *fp;
37   char *cmd;
38   char filename[PATH_MAX];
39   char **filenames = NULL;
40   int size = 0, alloc = 0;
41   size_t len;
42
43   /* "zcat /sysroot/<path> | cpio --quiet -it", but path must be quoted. */
44   if (asprintf_nowarn (&cmd, "zcat %R | cpio --quiet -it", path) == -1) {
45     reply_with_perror ("asprintf");
46     return NULL;
47   }
48
49   if (verbose)
50     fprintf (stderr, "%s\n", cmd);
51
52   fp = popen (cmd, "r");
53   if (fp == NULL) {
54     reply_with_perror ("popen: %s", cmd);
55     free (cmd);
56     return NULL;
57   }
58   free (cmd);
59
60   while (fgets (filename, sizeof filename, fp) != NULL) {
61     len = strlen (filename);
62     if (len > 0 && filename[len-1] == '\n')
63       filename[len-1] = '\0';
64
65     if (add_string (&filenames, &size, &alloc, filename) == -1) {
66       pclose (fp);
67       return NULL;
68     }
69   }
70
71   if (add_string (&filenames, &size, &alloc, NULL) == -1) {
72     pclose (fp);
73     return NULL;
74   }
75
76   if (pclose (fp) != 0) {
77     reply_with_perror ("pclose");
78     free_strings (filenames);
79     return NULL;
80   }
81
82   return filenames;
83 }
84
85 char *
86 do_initrd_cat (const char *path, const char *filename, size_t *size_r)
87 {
88   char tmpdir[] = "/tmp/initrd-cat-XXXXXX";
89   if (mkdtemp (tmpdir) == NULL) {
90     reply_with_perror ("mkdtemp");
91     return NULL;
92   }
93
94   /* "zcat /sysroot/<path> | cpio --quiet -id file", but paths must be quoted */
95   char *cmd;
96   if (asprintf_nowarn (&cmd, "cd %Q && zcat %R | cpio --quiet -id %Q",
97                        tmpdir, path, filename) == -1) {
98     reply_with_perror ("asprintf");
99     rmdir (tmpdir);
100     return NULL;
101   }
102
103   /* Extract file into temporary directory.  This may create subdirs.
104    * It's also possible that this doesn't create anything at all
105    * (eg. if the named file does not exist in the cpio archive) --
106    * cpio is silent in this case.
107    */
108   int r = system (cmd);
109   if (r == -1) {
110     reply_with_perror ("command failed: %s", cmd);
111     rmdir (tmpdir);
112     return NULL;
113   }
114   if (WEXITSTATUS (r) != 0) {
115     reply_with_perror ("command failed with return code %d",
116                        WEXITSTATUS (r));
117     rmdir (tmpdir);
118     return NULL;
119   }
120
121   /* See if we got a file. */
122   char fullpath[PATH_MAX];
123   snprintf (fullpath, sizeof fullpath, "%s/%s", tmpdir, filename);
124
125   struct stat statbuf;
126   int fd;
127
128   fd = open (fullpath, O_RDONLY);
129   if (fd == -1) {
130     reply_with_perror ("open: %s:%s", path, filename);
131     rmdir (tmpdir);
132     return NULL;
133   }
134
135   /* From this point, we know the file exists, so we require full
136    * cleanup.
137    */
138   char *ret = NULL;
139
140   if (fstat (fd, &statbuf) == -1) {
141     reply_with_perror ("fstat: %s:%s", path, filename);
142     goto cleanup;
143   }
144
145   /* The actual limit on messages is smaller than this.  This
146    * check just limits the amount of memory we'll try and allocate
147    * here.  If the message is larger than the real limit, that will
148    * be caught later when we try to serialize the message.
149    */
150   if (statbuf.st_size >= GUESTFS_MESSAGE_MAX) {
151     reply_with_error ("%s:%s: file is too large for the protocol",
152                       path, filename);
153     goto cleanup;
154   }
155
156   ret = malloc (statbuf.st_size);
157   if (ret == NULL) {
158     reply_with_perror ("malloc");
159     goto cleanup;
160   }
161
162   if (xread (fd, ret, statbuf.st_size) == -1) {
163     reply_with_perror ("read: %s:%s", path, filename);
164     free (ret);
165     ret = NULL;
166     goto cleanup;
167   }
168
169   if (close (fd) == -1) {
170     reply_with_perror ("close: %s:%s", path, filename);
171     free (ret);
172     ret = NULL;
173     goto cleanup;
174   }
175   fd = -1;
176
177   /* Mustn't touch *size_r until we are sure that we won't return any
178    * error (RHBZ#589039).
179    */
180   *size_r = statbuf.st_size;
181
182  cleanup:
183   if (fd >= 0)
184     close (fd);
185
186   /* Remove the file. */
187   if (unlink (fullpath) == -1) {
188     fprintf (stderr, "unlink: ");
189     perror (fullpath);
190     /* non-fatal */
191   }
192
193   /* Remove the directories up to and including the temp directory. */
194   do {
195     char *p = strrchr (fullpath, '/');
196     if (!p) break;
197     *p = '\0';
198     if (rmdir (fullpath) == -1) {
199       fprintf (stderr, "rmdir: ");
200       perror (fullpath);
201       /* non-fatal */
202     }
203   } while (STRNEQ (fullpath, tmpdir));
204
205   return ret;
206 }