Update FSF address.
[libguestfs.git] / daemon / copy.c
1 /* libguestfs - the guestfsd daemon
2  * Copyright (C) 2011 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 <unistd.h>
25 #include <fcntl.h>
26 #include <sys/types.h>
27
28 #include "guestfs_protocol.h"
29 #include "daemon.h"
30 #include "actions.h"
31
32 #define DEST_FILE_FLAGS O_WRONLY|O_CREAT|O_TRUNC|O_NOCTTY, 0666
33 #define DEST_DEVICE_FLAGS O_WRONLY, 0
34
35 /* NB: We cheat slightly by assuming that optargs_bitmask is
36  * compatible for all four of the calls.  This is true provided they
37  * all take the same set of optional arguments.
38  */
39
40 /* Takes optional arguments, consult optargs_bitmask. */
41 static int
42 copy (const char *src, const char *src_display,
43       const char *dest, const char *dest_display,
44       int wrflags, int wrmode,
45       int64_t srcoffset, int64_t destoffset, int64_t size)
46 {
47   int64_t saved_size = size;
48   int src_fd, dest_fd;
49   char buf[BUFSIZ];
50   size_t n;
51   ssize_t r;
52
53   if ((optargs_bitmask & GUESTFS_COPY_DEVICE_TO_DEVICE_SRCOFFSET_BITMASK)) {
54     if (srcoffset < 0) {
55       reply_with_error ("srcoffset is negative");
56       return -1;
57     }
58   }
59   else
60     srcoffset = 0;
61
62   if ((optargs_bitmask & GUESTFS_COPY_DEVICE_TO_DEVICE_DESTOFFSET_BITMASK)) {
63     if (destoffset < 0) {
64       reply_with_error ("destoffset is negative");
65       return -1;
66     }
67   }
68   else
69     destoffset = 0;
70
71   if ((optargs_bitmask & GUESTFS_COPY_DEVICE_TO_DEVICE_SIZE_BITMASK)) {
72     if (size < 0) {
73       reply_with_error ("size is negative");
74       return -1;
75     }
76   }
77   else
78     size = -1;
79
80   /* Open source and destination. */
81   src_fd = open (src, O_RDONLY);
82   if (src_fd == -1) {
83     reply_with_perror ("%s", src_display);
84     return -1;
85   }
86
87   if (srcoffset > 0 && lseek (src_fd, srcoffset, SEEK_SET) == (off_t) -1) {
88     reply_with_perror ("lseek: %s", src_display);
89     close (src_fd);
90     return -1;
91   }
92
93   dest_fd = open (dest, wrflags, wrmode);
94   if (dest_fd == -1) {
95     reply_with_perror ("%s", dest_display);
96     close (src_fd);
97     return -1;
98   }
99
100   if (destoffset > 0 && lseek (dest_fd, destoffset, SEEK_SET) == (off_t) -1) {
101     reply_with_perror ("lseek: %s", dest_display);
102     close (src_fd);
103     close (dest_fd);
104     return -1;
105   }
106
107   if (size == -1)
108     pulse_mode_start ();
109
110   while (size != 0) {
111     /* Calculate bytes to copy. */
112     if (size == -1 || size > (int64_t) sizeof buf)
113       n = sizeof buf;
114     else
115       n = size;
116
117     r = read (src_fd, buf, n);
118     if (r == -1) {
119       if (size == -1)
120         pulse_mode_cancel ();
121       reply_with_perror ("read: %s", src_display);
122       close (src_fd);
123       close (dest_fd);
124       return -1;
125     }
126
127     if (r == 0) {
128       if (size == -1) /* if size == -1, this is normal end of loop */
129         break;
130       reply_with_error ("%s: input too short", src_display);
131       close (src_fd);
132       close (dest_fd);
133       return -1;
134     }
135
136     if (xwrite (dest_fd, buf, r) == -1) {
137       if (size == -1)
138         pulse_mode_cancel ();
139       reply_with_perror ("%s: write", dest_display);
140       close (src_fd);
141       close (dest_fd);
142       return -1;
143     }
144
145     if (size != -1) {
146       size -= r;
147       notify_progress ((uint64_t) (saved_size - size), (uint64_t) saved_size);
148     }
149   }
150
151   if (size == -1)
152     pulse_mode_end ();
153
154   if (close (src_fd) == -1) {
155     reply_with_perror ("close: %s", src_display);
156     close (dest_fd);
157     return -1;
158   }
159
160   if (close (dest_fd) == -1) {
161     reply_with_perror ("close: %s", dest_display);
162     return -1;
163   }
164
165   return 0;
166 }
167
168 int
169 do_copy_device_to_device (const char *src, const char *dest,
170                           int64_t srcoffset, int64_t destoffset, int64_t size)
171 {
172   return copy (src, src, dest, dest, DEST_DEVICE_FLAGS,
173                srcoffset, destoffset, size);
174 }
175
176 int
177 do_copy_device_to_file (const char *src, const char *dest,
178                         int64_t srcoffset, int64_t destoffset, int64_t size)
179 {
180   char *dest_buf;
181   int r;
182
183   dest_buf = sysroot_path (dest);
184   if (!dest_buf) {
185     reply_with_perror ("malloc");
186     return -1;
187   }
188
189   r = copy (src, src, dest_buf, dest, DEST_FILE_FLAGS,
190             srcoffset, destoffset, size);
191   free (dest_buf);
192
193   return r;
194 }
195
196 int
197 do_copy_file_to_device (const char *src, const char *dest,
198                         int64_t srcoffset, int64_t destoffset, int64_t size)
199 {
200   char *src_buf;
201   int r;
202
203   src_buf = sysroot_path (src);
204   if (!src_buf) {
205     reply_with_perror ("malloc");
206     return -1;
207   }
208
209   r = copy (src_buf, src, dest, dest, DEST_DEVICE_FLAGS,
210             srcoffset, destoffset, size);
211   free (src_buf);
212
213   return r;
214 }
215
216 int
217 do_copy_file_to_file (const char *src, const char *dest,
218                       int64_t srcoffset, int64_t destoffset, int64_t size)
219 {
220   char *src_buf, *dest_buf;
221   int r;
222
223   src_buf = sysroot_path (src);
224   if (!src_buf) {
225     reply_with_perror ("malloc");
226     return -1;
227   }
228
229   dest_buf = sysroot_path (dest);
230   if (!dest_buf) {
231     reply_with_perror ("malloc");
232     free (src_buf);
233     return -1;
234   }
235
236   r = copy (src_buf, src, dest_buf, dest, DEST_FILE_FLAGS,
237             srcoffset, destoffset, size);
238   free (src_buf);
239   free (dest_buf);
240
241   return r;
242 }