fish: Fix segfault in '-a' option.
[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 "../src/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   *size_r = statbuf.st_size;
146   /* The actual limit on messages is smaller than this.  This
147    * check just limits the amount of memory we'll try and allocate
148    * here.  If the message is larger than the real limit, that will
149    * be caught later when we try to serialize the message.
150    */
151   if (*size_r >= GUESTFS_MESSAGE_MAX) {
152     reply_with_error ("%s:%s: file is too large for the protocol",
153                       path, filename);
154     goto cleanup;
155   }
156
157   ret = malloc (*size_r);
158   if (ret == NULL) {
159     reply_with_perror ("malloc");
160     goto cleanup;
161   }
162
163   if (xread (fd, ret, *size_r) == -1) {
164     reply_with_perror ("read: %s:%s", path, filename);
165     free (ret);
166     ret = NULL;
167     goto cleanup;
168   }
169
170   if (close (fd) == -1) {
171     reply_with_perror ("close: %s:%s", path, filename);
172     free (ret);
173     ret = NULL;
174     goto cleanup;
175   }
176   fd = -1;
177
178  cleanup:
179   if (fd >= 0)
180     close (fd);
181
182   /* Remove the file. */
183   if (unlink (fullpath) == -1) {
184     fprintf (stderr, "unlink: ");
185     perror (fullpath);
186     /* non-fatal */
187   }
188
189   /* Remove the directories up to and including the temp directory. */
190   do {
191     char *p = strrchr (fullpath, '/');
192     if (!p) break;
193     *p = '\0';
194     if (rmdir (fullpath) == -1) {
195       fprintf (stderr, "rmdir: ");
196       perror (fullpath);
197       /* non-fatal */
198     }
199   } while (STRNEQ (fullpath, tmpdir));
200
201   return ret;
202 }