df: Refresh virt-df(1) man page.
[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., 675 Mass Ave, Cambridge, MA 02139, 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     err = errno;
107     if (r == -1)                /* if r == 0, file transfer ended already */
108       r = cancel_receive ();
109     errno = err;
110     reply_with_perror ("close: %s", filename);
111     return -1;
112   }
113
114   return 0;
115 }
116
117 /* Has one FileIn parameter. */
118 int
119 do_upload (const char *filename)
120 {
121   return upload (filename, O_WRONLY|O_CREAT|O_TRUNC|O_NOCTTY, 0);
122 }
123
124 /* Has one FileIn parameter. */
125 int
126 do_upload_offset (const char *filename, int64_t offset)
127 {
128   if (offset < 0) {
129     reply_with_perror ("%s: offset in file is negative", filename);
130     return -1;
131   }
132
133   return upload (filename, O_WRONLY|O_CREAT|O_NOCTTY, offset);
134 }
135
136 /* Has one FileOut parameter. */
137 int
138 do_download (const char *filename)
139 {
140   int fd, r, is_dev;
141   char buf[GUESTFS_MAX_CHUNK_SIZE];
142
143   is_dev = STRPREFIX (filename, "/dev/");
144
145   if (!is_dev) CHROOT_IN;
146   fd = open (filename, O_RDONLY);
147   if (!is_dev) CHROOT_OUT;
148   if (fd == -1) {
149     reply_with_perror ("%s", filename);
150     return -1;
151   }
152
153   /* Calculate the size of the file or device for notification messages. */
154   uint64_t total, sent = 0;
155   if (!is_dev) {
156     struct stat statbuf;
157     if (fstat (fd, &statbuf) == -1) {
158       reply_with_perror ("%s", filename);
159       close (fd);
160       return -1;
161     }
162     total = statbuf.st_size;
163   } else {
164     int64_t size = do_blockdev_getsize64 (filename);
165     if (size == -1) {
166       /* do_blockdev_getsize64 has already sent a reply. */
167       close (fd);
168       return -1;
169     }
170     total = (uint64_t) size;
171   }
172
173   /* Now we must send the reply message, before the file contents.  After
174    * this there is no opportunity in the protocol to send any error
175    * message back.  Instead we can only cancel the transfer.
176    */
177   reply (NULL, NULL);
178
179   while ((r = read (fd, buf, sizeof buf)) > 0) {
180     if (send_file_write (buf, r) < 0) {
181       close (fd);
182       return -1;
183     }
184
185     sent += r;
186     notify_progress (sent, total);
187   }
188
189   if (r == -1) {
190     perror (filename);
191     send_file_end (1);          /* Cancel. */
192     close (fd);
193     return -1;
194   }
195
196   if (close (fd) == -1) {
197     perror (filename);
198     send_file_end (1);          /* Cancel. */
199     return -1;
200   }
201
202   if (send_file_end (0))        /* Normal end of file. */
203     return -1;
204
205   return 0;
206 }
207
208 /* Has one FileOut parameter. */
209 int
210 do_download_offset (const char *filename, int64_t offset, int64_t size)
211 {
212   int fd, r, is_dev;
213   char buf[GUESTFS_MAX_CHUNK_SIZE];
214
215   if (offset < 0) {
216     reply_with_perror ("%s: offset in file is negative", filename);
217     return -1;
218   }
219
220   if (size < 0) {
221     reply_with_perror ("%s: size is negative", filename);
222     return -1;
223   }
224   uint64_t usize = (uint64_t) size;
225
226   is_dev = STRPREFIX (filename, "/dev/");
227
228   if (!is_dev) CHROOT_IN;
229   fd = open (filename, O_RDONLY);
230   if (!is_dev) CHROOT_OUT;
231   if (fd == -1) {
232     reply_with_perror ("%s", filename);
233     return -1;
234   }
235
236   if (offset) {
237     if (lseek (fd, offset, SEEK_SET) == -1) {
238       reply_with_perror ("lseek: %s", filename);
239       return -1;
240     }
241   }
242
243   uint64_t total = usize, sent = 0;
244
245   /* Now we must send the reply message, before the file contents.  After
246    * this there is no opportunity in the protocol to send any error
247    * message back.  Instead we can only cancel the transfer.
248    */
249   reply (NULL, NULL);
250
251   while (usize > 0) {
252     r = read (fd, buf, usize > sizeof buf ? sizeof buf : usize);
253     if (r == -1) {
254       perror (filename);
255       send_file_end (1);        /* Cancel. */
256       close (fd);
257       return -1;
258     }
259
260     if (r == 0)
261       /* The documentation leaves this case undefined.  Currently we
262        * just read fewer bytes than requested.
263        */
264       break;
265
266     if (send_file_write (buf, r) < 0) {
267       close (fd);
268       return -1;
269     }
270
271     sent += r;
272     usize -= r;
273     notify_progress (sent, total);
274   }
275
276   if (close (fd) == -1) {
277     perror (filename);
278     send_file_end (1);          /* Cancel. */
279     return -1;
280   }
281
282   if (send_file_end (0))        /* Normal end of file. */
283     return -1;
284
285   return 0;
286 }