3f5f60a614fecf3e93926c42cee93967644cc4f1
[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 write_cb (void *fd_ptr, const void *buf, size_t len)
32 {
33   int fd = *(int *)fd_ptr;
34   return xwrite (fd, buf, len);
35 }
36
37 /* Has one FileIn parameter. */
38 int
39 do_tar_in (const char *dir)
40 {
41   int err, r;
42   FILE *fp;
43   char *cmd;
44
45   /* "tar -C /sysroot%s -xf -" but we have to quote the dir. */
46   if (asprintf_nowarn (&cmd, "tar -C %R -xf -", dir) == -1) {
47     err = errno;
48     r = cancel_receive ();
49     errno = err;
50     if (r != -2) reply_with_perror ("asprintf");
51     return -1;
52   }
53
54   if (verbose)
55     fprintf (stderr, "%s\n", cmd);
56
57   fp = popen (cmd, "w");
58   if (fp == NULL) {
59     err = errno;
60     r = cancel_receive ();
61     errno = err;
62     if (r != -2) reply_with_perror ("%s", cmd);
63     free (cmd);
64     return -1;
65   }
66   free (cmd);
67
68   /* The semantics of fwrite are too undefined, so write to the
69    * file descriptor directly instead.
70    */
71   int fd = fileno (fp);
72
73   r = receive_file (write_cb, &fd);
74   if (r == -1) {                /* write error */
75     if (cancel_receive () != -2)
76       reply_with_error ("write error on directory: %s", dir);
77     pclose (fp);
78     return -1;
79   }
80   if (r == -2) {                /* cancellation from library */
81     pclose (fp);
82     /* Do NOT send any error. */
83     return -1;
84   }
85
86   if (pclose (fp) != 0) {
87     if (r == -1)                /* if r == 0, file transfer ended already */
88       r = cancel_receive ();
89     if (r != -2)
90       reply_with_error ("tar subcommand failed on directory: %s", dir);
91     return -1;
92   }
93
94   return 0;
95 }
96
97 /* Has one FileOut parameter. */
98 int
99 do_tar_out (const char *dir)
100 {
101   int r;
102   FILE *fp;
103   char *cmd;
104   char buf[GUESTFS_MAX_CHUNK_SIZE];
105
106   /* "tar -C /sysroot%s -cf - ." but we have to quote the dir. */
107   if (asprintf_nowarn (&cmd, "tar -C %R -cf - .", dir) == -1) {
108     reply_with_perror ("asprintf");
109     return -1;
110   }
111
112   if (verbose)
113     fprintf (stderr, "%s\n", cmd);
114
115   fp = popen (cmd, "r");
116   if (fp == NULL) {
117     reply_with_perror ("%s", cmd);
118     free (cmd);
119     return -1;
120   }
121   free (cmd);
122
123   /* Now we must send the reply message, before the file contents.  After
124    * this there is no opportunity in the protocol to send any error
125    * message back.  Instead we can only cancel the transfer.
126    */
127   reply (NULL, NULL);
128
129   while ((r = fread (buf, 1, sizeof buf, fp)) > 0) {
130     if (send_file_write (buf, r) < 0) {
131       pclose (fp);
132       return -1;
133     }
134   }
135
136   if (ferror (fp)) {
137     perror (dir);
138     send_file_end (1);          /* Cancel. */
139     pclose (fp);
140     return -1;
141   }
142
143   if (pclose (fp) != 0) {
144     perror (dir);
145     send_file_end (1);          /* Cancel. */
146     return -1;
147   }
148
149   if (send_file_end (0))        /* Normal end of file. */
150     return -1;
151
152   return 0;
153 }
154
155 /* Has one FileIn parameter. */
156 static int
157 do_tXz_in (const char *dir, char filter)
158 {
159   int err, r;
160   FILE *fp;
161   char *cmd;
162
163   /* "tar -C /sysroot%s -zxf -" but we have to quote the dir. */
164   if (asprintf_nowarn (&cmd, "tar -C %R -%cxf -", dir, filter) == -1) {
165     err = errno;
166     r = cancel_receive ();
167     errno = err;
168     if (r != -2) reply_with_perror ("asprintf");
169     return -1;
170   }
171
172   if (verbose)
173     fprintf (stderr, "%s\n", cmd);
174
175   fp = popen (cmd, "w");
176   if (fp == NULL) {
177     err = errno;
178     r = cancel_receive ();
179     errno = err;
180     if (r != -2) reply_with_perror ("%s", cmd);
181     free (cmd);
182     return -1;
183   }
184   free (cmd);
185
186   int fd = fileno (fp);
187
188   r = receive_file (write_cb, &fd);
189   if (r == -1) {                /* write error */
190     r = cancel_receive ();
191     if (r != -2) reply_with_error ("write error on directory: %s", dir);
192     pclose (fp);
193     return -1;
194   }
195   if (r == -2) {                /* cancellation from library */
196     pclose (fp);
197     /* Do NOT send any error. */
198     return -1;
199   }
200
201   if (pclose (fp) != 0) {
202     if (r == -1)                /* if r == 0, file transfer ended already */
203       r = cancel_receive ();
204     if (r != -2)
205       reply_with_error ("tar subcommand failed on directory: %s", dir);
206     return -1;
207   }
208
209   return 0;
210 }
211
212 /* Has one FileIn parameter. */
213 int
214 do_tgz_in (const char *dir)
215 {
216   return do_tXz_in (dir, 'z');
217 }
218
219 /* Has one FileIn parameter. */
220 int
221 do_txz_in (const char *dir)
222 {
223   return do_tXz_in (dir, 'J');
224 }
225
226 /* Has one FileOut parameter. */
227 static int
228 do_tXz_out (const char *dir, char filter)
229 {
230   int r;
231   FILE *fp;
232   char *cmd;
233   char buf[GUESTFS_MAX_CHUNK_SIZE];
234
235   /* "tar -C /sysroot%s -zcf - ." but we have to quote the dir. */
236   if (asprintf_nowarn (&cmd, "tar -C %R -%ccf - .", dir, filter) == -1) {
237     reply_with_perror ("asprintf");
238     return -1;
239   }
240
241   if (verbose)
242     fprintf (stderr, "%s\n", cmd);
243
244   fp = popen (cmd, "r");
245   if (fp == NULL) {
246     reply_with_perror ("%s", cmd);
247     free (cmd);
248     return -1;
249   }
250   free (cmd);
251
252   /* Now we must send the reply message, before the file contents.  After
253    * this there is no opportunity in the protocol to send any error
254    * message back.  Instead we can only cancel the transfer.
255    */
256   reply (NULL, NULL);
257
258   while ((r = fread (buf, 1, sizeof buf, fp)) > 0) {
259     if (send_file_write (buf, r) < 0) {
260       pclose (fp);
261       return -1;
262     }
263   }
264
265   if (ferror (fp)) {
266     perror (dir);
267     send_file_end (1);          /* Cancel. */
268     pclose (fp);
269     return -1;
270   }
271
272   if (pclose (fp) != 0) {
273     perror (dir);
274     send_file_end (1);          /* Cancel. */
275     return -1;
276   }
277
278   if (send_file_end (0))        /* Normal end of file. */
279     return -1;
280
281   return 0;
282 }
283
284 /* Has one FileOut parameter. */
285 int
286 do_tgz_out (const char *dir)
287 {
288   return do_tXz_out (dir, 'z');
289 }
290
291 /* Has one FileOut parameter. */
292 int
293 do_txz_out (const char *dir)
294 {
295   return do_tXz_out (dir, 'J');
296 }