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