Combine common code in daemon/tar.c.
[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 static int
39 do_tXz_in (const char *dir, const char *filter)
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 -%sxf -",
47                        dir, filter) == -1) {
48     err = errno;
49     r = cancel_receive ();
50     errno = err;
51     if (r != -2) reply_with_perror ("asprintf");
52     return -1;
53   }
54
55   if (verbose)
56     fprintf (stderr, "%s\n", cmd);
57
58   fp = popen (cmd, "w");
59   if (fp == NULL) {
60     err = errno;
61     r = cancel_receive ();
62     errno = err;
63     if (r != -2) reply_with_perror ("%s", cmd);
64     free (cmd);
65     return -1;
66   }
67   free (cmd);
68
69   /* The semantics of fwrite are too undefined, so write to the
70    * file descriptor directly instead.
71    */
72   int fd = fileno (fp);
73
74   r = receive_file (write_cb, &fd);
75   if (r == -1) {                /* write error */
76     if (cancel_receive () != -2)
77       reply_with_error ("write error on directory: %s", dir);
78     pclose (fp);
79     return -1;
80   }
81   if (r == -2) {                /* cancellation from library */
82     pclose (fp);
83     /* Do NOT send any error. */
84     return -1;
85   }
86
87   if (pclose (fp) != 0) {
88     if (r == -1)                /* if r == 0, file transfer ended already */
89       r = cancel_receive ();
90     if (r != -2)
91       reply_with_error ("tar subcommand failed on directory: %s", dir);
92     return -1;
93   }
94
95   return 0;
96 }
97
98 /* Has one FileIn parameter. */
99 int
100 do_tar_in (const char *dir)
101 {
102   return do_tXz_in (dir, "");
103 }
104
105 /* Has one FileIn parameter. */
106 int
107 do_tgz_in (const char *dir)
108 {
109   return do_tXz_in (dir, "z");
110 }
111
112 /* Has one FileIn parameter. */
113 int
114 do_txz_in (const char *dir)
115 {
116   return do_tXz_in (dir, "J");
117 }
118
119 /* Has one FileOut parameter. */
120 static int
121 do_tXz_out (const char *dir, const char *filter)
122 {
123   int r;
124   FILE *fp;
125   char *cmd;
126   char buf[GUESTFS_MAX_CHUNK_SIZE];
127
128   /* "tar -C /sysroot%s -zcf - ." but we have to quote the dir. */
129   if (asprintf_nowarn (&cmd, "tar -C %R -%scf - .", dir, filter) == -1) {
130     reply_with_perror ("asprintf");
131     return -1;
132   }
133
134   if (verbose)
135     fprintf (stderr, "%s\n", cmd);
136
137   fp = popen (cmd, "r");
138   if (fp == NULL) {
139     reply_with_perror ("%s", cmd);
140     free (cmd);
141     return -1;
142   }
143   free (cmd);
144
145   /* Now we must send the reply message, before the file contents.  After
146    * this there is no opportunity in the protocol to send any error
147    * message back.  Instead we can only cancel the transfer.
148    */
149   reply (NULL, NULL);
150
151   while ((r = fread (buf, 1, sizeof buf, fp)) > 0) {
152     if (send_file_write (buf, r) < 0) {
153       pclose (fp);
154       return -1;
155     }
156   }
157
158   if (ferror (fp)) {
159     perror (dir);
160     send_file_end (1);          /* Cancel. */
161     pclose (fp);
162     return -1;
163   }
164
165   if (pclose (fp) != 0) {
166     perror (dir);
167     send_file_end (1);          /* Cancel. */
168     return -1;
169   }
170
171   if (send_file_end (0))        /* Normal end of file. */
172     return -1;
173
174   return 0;
175 }
176
177 /* Has one FileOut parameter. */
178 int
179 do_tar_out (const char *dir)
180 {
181   return do_tXz_out (dir, "");
182 }
183
184 /* Has one FileOut parameter. */
185 int
186 do_tgz_out (const char *dir)
187 {
188   return do_tXz_out (dir, "z");
189 }
190
191 /* Has one FileOut parameter. */
192 int
193 do_txz_out (const char *dir)
194 {
195   return do_tXz_out (dir, "J");
196 }