daemon: debug segv correct use of dereferencing NULL.
[libguestfs.git] / daemon / upload.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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17  */
18
19 #include <config.h>
20
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <stdint.h>
24 #include <string.h>
25 #include <fcntl.h>
26 #include <unistd.h>
27 #include <sys/types.h>
28 #include <sys/stat.h>
29
30 #include "guestfs_protocol.h"
31 #include "daemon.h"
32 #include "actions.h"
33
34 struct write_cb_data {
35   int fd;                       /* file descriptor */
36   uint64_t written;             /* bytes written so far */
37 };
38
39 static int
40 write_cb (void *data_vp, const void *buf, size_t len)
41 {
42   struct write_cb_data *data = data_vp;
43   int r;
44
45   r = xwrite (data->fd, buf, len);
46   if (r == -1)
47     return -1;
48
49   data->written += len;
50
51   if (progress_hint > 0)
52     notify_progress (data->written, progress_hint);
53
54   return 0;
55 }
56
57 /* Has one FileIn parameter. */
58 static int
59 upload (const char *filename, int flags, int64_t offset)
60 {
61   struct write_cb_data data = { .written = 0 };
62   int err, r, is_dev;
63
64   is_dev = STRPREFIX (filename, "/dev/");
65
66   if (!is_dev) CHROOT_IN;
67   data.fd = open (filename, flags, 0666);
68   if (!is_dev) CHROOT_OUT;
69   if (data.fd == -1) {
70     err = errno;
71     r = cancel_receive ();
72     errno = err;
73     reply_with_perror ("%s", filename);
74     return -1;
75   }
76
77   if (offset) {
78     if (lseek (data.fd, offset, SEEK_SET) == -1) {
79       err = errno;
80       r = cancel_receive ();
81       errno = err;
82       reply_with_perror ("lseek: %s", filename);
83       return -1;
84     }
85   }
86
87   r = receive_file (write_cb, &data.fd);
88   if (r == -1) {                /* write error */
89     err = errno;
90     r = cancel_receive ();
91     errno = err;
92     reply_with_error ("write error: %s", filename);
93     close (data.fd);
94     return -1;
95   }
96   if (r == -2) {                /* cancellation from library */
97     /* This error is ignored by the library since it initiated the
98      * cancel.  Nevertheless we must send an error reply here.
99      */
100     reply_with_error ("file upload cancelled");
101     close (data.fd);
102     return -1;
103   }
104
105   if (close (data.fd) == -1) {
106     reply_with_perror ("close: %s", filename);
107     return -1;
108   }
109
110   return 0;
111 }
112
113 /* Has one FileIn parameter. */
114 int
115 do_upload (const char *filename)
116 {
117   return upload (filename, O_WRONLY|O_CREAT|O_TRUNC|O_NOCTTY, 0);
118 }
119
120 /* Has one FileIn parameter. */
121 int
122 do_upload_offset (const char *filename, int64_t offset)
123 {
124   if (offset < 0) {
125     reply_with_perror ("%s: offset in file is negative", filename);
126     return -1;
127   }
128
129   return upload (filename, O_WRONLY|O_CREAT|O_NOCTTY, offset);
130 }
131
132 /* Has one FileOut parameter. */
133 int
134 do_download (const char *filename)
135 {
136   int fd, r, is_dev;
137   char buf[GUESTFS_MAX_CHUNK_SIZE];
138
139   is_dev = STRPREFIX (filename, "/dev/");
140
141   if (!is_dev) CHROOT_IN;
142   fd = open (filename, O_RDONLY);
143   if (!is_dev) CHROOT_OUT;
144   if (fd == -1) {
145     reply_with_perror ("%s", filename);
146     return -1;
147   }
148
149   /* Calculate the size of the file or device for notification messages. */
150   uint64_t total, sent = 0;
151   if (!is_dev) {
152     struct stat statbuf;
153     if (fstat (fd, &statbuf) == -1) {
154       reply_with_perror ("%s", filename);
155       close (fd);
156       return -1;
157     }
158     total = statbuf.st_size;
159   } else {
160     int64_t size = do_blockdev_getsize64 (filename);
161     if (size == -1) {
162       /* do_blockdev_getsize64 has already sent a reply. */
163       close (fd);
164       return -1;
165     }
166     total = (uint64_t) size;
167   }
168
169   /* Now we must send the reply message, before the file contents.  After
170    * this there is no opportunity in the protocol to send any error
171    * message back.  Instead we can only cancel the transfer.
172    */
173   reply (NULL, NULL);
174
175   while ((r = read (fd, buf, sizeof buf)) > 0) {
176     if (send_file_write (buf, r) < 0) {
177       close (fd);
178       return -1;
179     }
180
181     sent += r;
182     notify_progress (sent, total);
183   }
184
185   if (r == -1) {
186     perror (filename);
187     send_file_end (1);          /* Cancel. */
188     close (fd);
189     return -1;
190   }
191
192   if (close (fd) == -1) {
193     perror (filename);
194     send_file_end (1);          /* Cancel. */
195     return -1;
196   }
197
198   if (send_file_end (0))        /* Normal end of file. */
199     return -1;
200
201   return 0;
202 }
203
204 /* Has one FileOut parameter. */
205 int
206 do_download_offset (const char *filename, int64_t offset, int64_t size)
207 {
208   int fd, r, is_dev;
209   char buf[GUESTFS_MAX_CHUNK_SIZE];
210
211   if (offset < 0) {
212     reply_with_perror ("%s: offset in file is negative", filename);
213     return -1;
214   }
215
216   if (size < 0) {
217     reply_with_perror ("%s: size is negative", filename);
218     return -1;
219   }
220   uint64_t usize = (uint64_t) size;
221
222   is_dev = STRPREFIX (filename, "/dev/");
223
224   if (!is_dev) CHROOT_IN;
225   fd = open (filename, O_RDONLY);
226   if (!is_dev) CHROOT_OUT;
227   if (fd == -1) {
228     reply_with_perror ("%s", filename);
229     return -1;
230   }
231
232   if (offset) {
233     if (lseek (fd, offset, SEEK_SET) == -1) {
234       reply_with_perror ("lseek: %s", filename);
235       return -1;
236     }
237   }
238
239   uint64_t total = usize, sent = 0;
240
241   /* Now we must send the reply message, before the file contents.  After
242    * this there is no opportunity in the protocol to send any error
243    * message back.  Instead we can only cancel the transfer.
244    */
245   reply (NULL, NULL);
246
247   while (usize > 0) {
248     r = read (fd, buf, usize > sizeof buf ? sizeof buf : usize);
249     if (r == -1) {
250       perror (filename);
251       send_file_end (1);        /* Cancel. */
252       close (fd);
253       return -1;
254     }
255
256     if (r == 0)
257       /* The documentation leaves this case undefined.  Currently we
258        * just read fewer bytes than requested.
259        */
260       break;
261
262     if (send_file_write (buf, r) < 0) {
263       close (fd);
264       return -1;
265     }
266
267     sent += r;
268     usize -= r;
269     notify_progress (sent, total);
270   }
271
272   if (close (fd) == -1) {
273     perror (filename);
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 }