Enable parallel builds (Jim Meyering).
[libguestfs.git] / daemon / tar.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 <fcntl.h>
25
26 #include "../src/guestfs_protocol.h"
27 #include "daemon.h"
28 #include "actions.h"
29
30 static int
31 fwrite_cb (void *fp_ptr, const void *buf, int len)
32 {
33   FILE *fp = *(FILE **)fp_ptr;
34   return fwrite (buf, len, 1, fp) == 1 ? 0 : -1;
35 }
36
37 /* Has one FileIn parameter. */
38 int
39 do_tar_in (const char *dir)
40 {
41   int err, r, len;
42   FILE *fp;
43   char *cmd;
44
45   if (!root_mounted || dir[0] != '/') {
46     cancel_receive ();
47     reply_with_error ("tar-in: root must be mounted and path must be absolute");
48     return -1;
49   }
50
51   /* "tar -C /sysroot%s -xf -" but we have to quote the dir. */
52   len = 2 * strlen (dir) + 32;
53   cmd = malloc (len);
54   if (!cmd) {
55     err = errno;
56     cancel_receive ();
57     errno = err;
58     reply_with_perror ("malloc");
59     return -1;
60   }
61   strcpy (cmd, "tar -C /sysroot");
62   shell_quote (cmd+15, len-15, dir);
63   strcat (cmd, " -xf -");
64
65   fp = popen (cmd, "w");
66   if (fp == NULL) {
67     err = errno;
68     cancel_receive ();
69     errno = err;
70     reply_with_perror ("%s", cmd);
71     return -1;
72   }
73
74   r = receive_file (fwrite_cb, &fp);
75   if (r == -1) {                /* write error */
76     err = errno;
77     cancel_receive ();
78     errno = err;
79     reply_with_perror ("write: %s", dir);
80     pclose (fp);
81     return -1;
82   }
83   if (r == -2) {                /* cancellation from library */
84     pclose (fp);
85     /* Do NOT send any error. */
86     return -1;
87   }
88
89   if (pclose (fp) == -1) {
90     err = errno;
91     cancel_receive ();
92     errno = err;
93     reply_with_perror ("pclose: %s", dir);
94     return -1;
95   }
96
97   return 0;
98 }
99
100 /* Has one FileOut parameter. */
101 int
102 do_tar_out (const char *dir)
103 {
104   int r, len;
105   FILE *fp;
106   char *cmd;
107   char buf[GUESTFS_MAX_CHUNK_SIZE];
108
109   NEED_ROOT (-1);
110   ABS_PATH (dir, -1);
111
112   /* "tar -C /sysroot%s -cf - ." but we have to quote the dir. */
113   len = 2 * strlen (dir) + 32;
114   cmd = malloc (len);
115   if (!cmd) {
116     reply_with_perror ("malloc");
117     return -1;
118   }
119   strcpy (cmd, "tar -C /sysroot");
120   shell_quote (cmd+15, len-15, dir);
121   strcat (cmd, " -cf - .");
122
123   fp = popen (cmd, "r");
124   if (fp == NULL) {
125     reply_with_perror ("%s", cmd);
126     return -1;
127   }
128
129   /* Now we must send the reply message, before the file contents.  After
130    * this there is no opportunity in the protocol to send any error
131    * message back.  Instead we can only cancel the transfer.
132    */
133   reply (NULL, NULL);
134
135   while ((r = fread (buf, 1, sizeof buf, fp)) > 0) {
136     if (send_file_write (buf, r) < 0) {
137       pclose (fp);
138       return -1;
139     }
140   }
141
142   if (ferror (fp)) {
143     perror (dir);
144     send_file_end (1);          /* Cancel. */
145     pclose (fp);
146     return -1;
147   }
148
149   if (pclose (fp) == -1) {
150     perror (dir);
151     send_file_end (1);          /* Cancel. */
152     return -1;
153   }
154
155   send_file_end (0);            /* Normal end of file. */
156   return 0;
157 }
158
159 /* Has one FileIn parameter. */
160 int
161 do_tgz_in (const char *dir)
162 {
163   int err, r, len;
164   FILE *fp;
165   char *cmd;
166
167   if (!root_mounted || dir[0] != '/') {
168     cancel_receive ();
169     reply_with_error ("tar-in: root must be mounted and path must be absolute");
170     return -1;
171   }
172
173   /* "tar -C /sysroot%s -zxf -" but we have to quote the dir. */
174   len = 2 * strlen (dir) + 32;
175   cmd = malloc (len);
176   if (!cmd) {
177     err = errno;
178     cancel_receive ();
179     errno = err;
180     reply_with_perror ("malloc");
181     return -1;
182   }
183   strcpy (cmd, "tar -C /sysroot");
184   shell_quote (cmd+15, len-15, dir);
185   strcat (cmd, " -zxf -");
186
187   fp = popen (cmd, "w");
188   if (fp == NULL) {
189     err = errno;
190     cancel_receive ();
191     errno = err;
192     reply_with_perror ("%s", cmd);
193     return -1;
194   }
195
196   r = receive_file (fwrite_cb, &fp);
197   if (r == -1) {                /* write error */
198     err = errno;
199     cancel_receive ();
200     errno = err;
201     reply_with_perror ("write: %s", dir);
202     pclose (fp);
203     return -1;
204   }
205   if (r == -2) {                /* cancellation from library */
206     pclose (fp);
207     /* Do NOT send any error. */
208     return -1;
209   }
210
211   if (pclose (fp) == -1) {
212     err = errno;
213     cancel_receive ();
214     errno = err;
215     reply_with_perror ("pclose: %s", dir);
216     return -1;
217   }
218
219   return 0;
220 }
221
222 /* Has one FileOut parameter. */
223 int
224 do_tgz_out (const char *dir)
225 {
226   int r, len;
227   FILE *fp;
228   char *cmd;
229   char buf[GUESTFS_MAX_CHUNK_SIZE];
230
231   NEED_ROOT (-1);
232   ABS_PATH (dir, -1);
233
234   /* "tar -C /sysroot%s -zcf - ." but we have to quote the dir. */
235   len = 2 * strlen (dir) + 32;
236   cmd = malloc (len);
237   if (!cmd) {
238     reply_with_perror ("malloc");
239     return -1;
240   }
241   strcpy (cmd, "tar -C /sysroot");
242   shell_quote (cmd+15, len-15, dir);
243   strcat (cmd, " -zcf - .");
244
245   fp = popen (cmd, "r");
246   if (fp == NULL) {
247     reply_with_perror ("%s", cmd);
248     return -1;
249   }
250
251   /* Now we must send the reply message, before the file contents.  After
252    * this there is no opportunity in the protocol to send any error
253    * message back.  Instead we can only cancel the transfer.
254    */
255   reply (NULL, NULL);
256
257   while ((r = fread (buf, 1, sizeof buf, fp)) > 0) {
258     if (send_file_write (buf, r) < 0) {
259       pclose (fp);
260       return -1;
261     }
262   }
263
264   if (ferror (fp)) {
265     perror (dir);
266     send_file_end (1);          /* Cancel. */
267     pclose (fp);
268     return -1;
269   }
270
271   if (pclose (fp) == -1) {
272     perror (dir);
273     send_file_end (1);          /* Cancel. */
274     return -1;
275   }
276
277   send_file_end (0);            /* Normal end of file. */
278   return 0;
279 }