Branch data Line data Source code
1 : : /*
2 : : * Copyright (c) 2016, Citrix Systems, Inc.
3 : : *
4 : : * All rights reserved.
5 : : *
6 : : * Redistribution and use in source and binary forms, with or without
7 : : * modification, are permitted provided that the following conditions are met:
8 : : *
9 : : * 1. Redistributions of source code must retain the above copyright
10 : : * notice, this list of conditions and the following disclaimer.
11 : : * 2. Redistributions in binary form must reproduce the above copyright
12 : : * notice, this list of conditions and the following disclaimer in the
13 : : * documentation and/or other materials provided with the distribution.
14 : : * 3. Neither the name of the copyright holder nor the names of its
15 : : * contributors may be used to endorse or promote products derived from
16 : : * this software without specific prior written permission.
17 : : *
18 : : * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 : : * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 : : * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 : : * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
22 : : * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
23 : : * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
24 : : * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
25 : : * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
26 : : * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
27 : : * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
28 : : * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 : : */
30 : :
31 : : #ifdef HAVE_CONFIG_H
32 : : #include "config.h"
33 : : #endif
34 : :
35 : : #include <errno.h>
36 : : #include <fcntl.h>
37 : : #include <stdio.h>
38 : : #include <stdlib.h>
39 : : #include <unistd.h>
40 : : #include <limits.h>
41 : :
42 : : #include "libvhd.h"
43 : : #include "canonpath.h"
44 : :
45 : : static int
46 : 0 : __raw_io_write(int fd, char* buf, uint64_t sec, uint32_t secs)
47 : : {
48 : : off64_t off;
49 : : size_t ret;
50 : :
51 : 0 : errno = 0;
52 : 0 : off = lseek64(fd, vhd_sectors_to_bytes(sec), SEEK_SET);
53 [ # # ]: 0 : if (off == (off64_t)-1) {
54 : 0 : printf("raw parent: seek(0x%08"PRIx64") failed: %d\n",
55 : 0 : vhd_sectors_to_bytes(sec), -errno);
56 : 0 : return -errno;
57 : : }
58 : :
59 : 0 : ret = write(fd, buf, vhd_sectors_to_bytes(secs));
60 [ # # ]: 0 : if (ret == vhd_sectors_to_bytes(secs))
61 : : return 0;
62 : :
63 : 0 : printf("raw parent: write of 0x%"PRIx64" returned %zd, errno: %d\n",
64 : 0 : vhd_sectors_to_bytes(secs), ret, -errno);
65 [ # # ]: 0 : return (errno ? -errno : -EIO);
66 : : }
67 : :
68 : : /**
69 : : * Coalesce a VHD allocation block
70 : : *
71 : : * @param[in] vhd the VHD being coalesced
72 : : * @param[in] parent the VHD to coalesce to unless raw
73 : : * @param[in] parent_fd raw FD to coalesce to unless VHD parent
74 : : * @param[in] parent block the allocation block number to coalese
75 : : * @return the number of sectors coalesced or negative errno on failure
76 : : */
77 : : static int64_t
78 : 0 : vhd_util_coalesce_block(vhd_context_t *vhd, vhd_context_t *parent,
79 : : int parent_fd, uint64_t block)
80 : : {
81 : : int i, err;
82 : 0 : int64_t coalesced_size = 0;
83 : : char *buf;
84 : : char *map;
85 : : uint64_t sec, secs;
86 : :
87 : 0 : buf = NULL;
88 : 0 : map = NULL;
89 : 0 : sec = block * vhd->spb;
90 : :
91 [ # # ]: 0 : if (vhd->bat.bat[block] == DD_BLK_UNUSED)
92 : 0 : return 0;
93 : :
94 [ # # ][ # # ]: 0 : if (vhd_has_batmap(vhd) && vhd_batmap_test(vhd, &vhd->batmap, block)) {
95 : 0 : err = vhd_read_block(vhd, block, &buf);
96 [ # # ]: 0 : if (err)
97 : : goto done;
98 : :
99 [ # # ]: 0 : if (parent->file)
100 : 0 : err = vhd_io_write(parent, buf, sec, vhd->spb);
101 : : else
102 : 0 : err = __raw_io_write(parent_fd, buf, sec, vhd->spb);
103 : :
104 [ # # ]: 0 : if (err == 0)
105 : 0 : coalesced_size = vhd->spb;
106 : :
107 : : goto done;
108 : : }
109 : :
110 : 0 : err = vhd_read_bitmap(vhd, block, &map);
111 [ # # ]: 0 : if (err)
112 : : goto done;
113 : :
114 : 0 : err = posix_memalign((void *)&buf, 4096, vhd->header.block_size);
115 [ # # ]: 0 : if (err) {
116 : 0 : err = -err;
117 : 0 : goto done;
118 : : }
119 : :
120 [ # # ]: 0 : for (i = 0; i < vhd->spb; i++) {
121 [ # # ]: 0 : if (!vhd_bitmap_test(vhd, map, i))
122 : 0 : continue;
123 : :
124 [ # # ]: 0 : for (secs = 0; i + secs < vhd->spb; secs++)
125 [ # # ]: 0 : if (!vhd_bitmap_test(vhd, map, i + secs))
126 : : break;
127 : :
128 : 0 : err = vhd_read_at(vhd, block, i, vhd_sectors_to_bytes(secs),
129 : 0 : buf + vhd_sectors_to_bytes(i));
130 [ # # ]: 0 : if (err)
131 : : goto done;
132 : :
133 [ # # ]: 0 : if (parent->file)
134 : 0 : err = vhd_io_write(parent,
135 : 0 : buf + vhd_sectors_to_bytes(i),
136 : : sec + i, secs);
137 : : else
138 : 0 : err = __raw_io_write(parent_fd,
139 : 0 : buf + vhd_sectors_to_bytes(i),
140 : : sec + i, secs);
141 [ # # ]: 0 : if (err)
142 : : goto done;
143 : :
144 : 0 : coalesced_size += secs;
145 : 0 : i += secs;
146 : : }
147 : :
148 : : err = 0;
149 : :
150 : : done:
151 : 0 : free(buf);
152 : 0 : free(map);
153 [ # # ]: 0 : if (err < 0)
154 : 0 : return err;
155 : :
156 : : return coalesced_size;
157 : : }
158 : :
159 : : /**
160 : : * Coalesce VHD to its immediate parent
161 : : *
162 : : * @param[in] from the VHD to coalesce
163 : : * @param[in] to the VHD to coalesce to or NULL if raw
164 : : * @param[in] to_fd the raws file to coalesce to or NULL if to is to be used
165 : : * @param[in] progess whether to report progress as the operation is being performed
166 : : * @return positive number of sectors coalesced or negative errno in the case of failure
167 : : */
168 : : static int64_t
169 : 0 : vhd_util_coalesce_onto(vhd_context_t *from,
170 : : vhd_context_t *to, int to_fd, int progress)
171 : : {
172 : : int i, err;
173 : 0 : int64_t coalesced_size = 0;
174 : :
175 : 0 : err = vhd_get_bat(from);
176 [ # # ]: 0 : if (err)
177 : : goto out;
178 : :
179 [ # # ]: 0 : if (vhd_has_batmap(from)) {
180 : 0 : err = vhd_get_batmap(from);
181 [ # # ]: 0 : if (err)
182 : : goto out;
183 : : }
184 : :
185 [ # # ]: 0 : for (i = 0; i < from->bat.entries; i++) {
186 [ # # ]: 0 : if (progress) {
187 : 0 : printf("\r%6.2f%%",
188 : 0 : ((float)i / (float)from->bat.entries) * 100.00);
189 : 0 : fflush(stdout);
190 : : }
191 : 0 : err = vhd_util_coalesce_block(from, to, to_fd, i);
192 [ # # ]: 0 : if (err < 0)
193 : : goto out;
194 : :
195 : 0 : coalesced_size += err;
196 : : }
197 : :
198 : 0 : err = 0;
199 : :
200 [ # # ]: 0 : if (progress)
201 : : printf("\r100.00%%\n");
202 : :
203 : : out:
204 [ # # ]: 0 : if (err < 0)
205 : 0 : return err;
206 : :
207 : : return coalesced_size;
208 : : }
209 : :
210 : : /**
211 : : * Coalesce the VHD to its immediate parent
212 : : *
213 : : * @param[in] name the name (path) of the VHD to coalesce
214 : : * @param[in] sparse whether the parent VHD should be written sparsely
215 : : * @param[in] progess whether to report progress as the operation is being performed
216 : : * @return positive number of sectors coalesced or negative errno in the case of failure
217 : : */
218 : : static int64_t
219 : 0 : vhd_util_coalesce_parent(const char *name, int sparse, int progress)
220 : : {
221 : : char *pname;
222 : : int err, parent_fd;
223 : : vhd_context_t vhd, parent;
224 : :
225 : 0 : parent_fd = -1;
226 : 0 : parent.file = NULL;
227 : :
228 : 0 : err = vhd_open(&vhd, name, VHD_OPEN_RDONLY);
229 [ # # ]: 0 : if (err) {
230 : : printf("error opening %s: %d\n", name, err);
231 : 0 : return err;
232 : : }
233 : :
234 [ # # ]: 0 : if (vhd.footer.type != HD_TYPE_DIFF) {
235 : : printf("coalescing of non-differencing disks is not supported\n");
236 : 0 : vhd_close(&vhd);
237 : : return -EINVAL;
238 : : }
239 : :
240 : 0 : err = vhd_parent_locator_get(&vhd, &pname);
241 [ # # ]: 0 : if (err) {
242 : : printf("error finding %s parent: %d\n", name, err);
243 : 0 : vhd_close(&vhd);
244 : 0 : return err;
245 : : }
246 : :
247 [ # # ]: 0 : if (vhd_parent_raw(&vhd)) {
248 : 0 : parent_fd = open_optional_odirect(pname, O_RDWR | O_DIRECT | O_LARGEFILE, 0644);
249 [ # # ]: 0 : if (parent_fd == -1) {
250 : 0 : err = -errno;
251 : 0 : printf("failed to open parent %s: %d\n", pname, err);
252 : 0 : free(pname);
253 : 0 : vhd_close(&vhd);
254 : 0 : return err;
255 : : }
256 : : } else {
257 [ # # ]: 0 : int flags = (sparse ? VHD_OPEN_IO_WRITE_SPARSE : 0);
258 [ # # ]: 0 : if (sparse) printf("opening for sparse writes\n");
259 : 0 : err = vhd_open(&parent, pname, VHD_OPEN_RDWR | flags);
260 [ # # ]: 0 : if (err) {
261 : 0 : printf("error opening %s: %d\n", pname, err);
262 : 0 : free(pname);
263 : 0 : vhd_close(&vhd);
264 : 0 : return err;
265 : : }
266 : : }
267 : :
268 : 0 : err = vhd_util_coalesce_onto(&vhd, &parent, parent_fd, progress);
269 : :
270 : 0 : free(pname);
271 : 0 : vhd_close(&vhd);
272 [ # # ]: 0 : if (parent.file)
273 : 0 : vhd_close(&parent);
274 : : else
275 : 0 : close(parent_fd);
276 : 0 : return err;
277 : : }
278 : :
279 : : int
280 : 0 : vhd_util_coalesce(int argc, char **argv)
281 : : {
282 : : char *name;
283 : : int c, progress, sparse;
284 : : int64_t result;
285 : :
286 : 0 : name = NULL;
287 : 0 : sparse = 0;
288 : 0 : progress = 0;
289 : :
290 [ # # ]: 0 : if (!argc || !argv)
291 : : goto usage;
292 : :
293 : 0 : optind = 0;
294 [ # # ]: 0 : while ((c = getopt(argc, argv, "n:o:a:x:sph")) != -1) {
295 [ # # # # ]: 0 : switch (c) {
296 : : case 'n':
297 : 0 : name = optarg;
298 : 0 : break;
299 : : case 's':
300 : : sparse = 1;
301 : : break;
302 : : case 'p':
303 : 0 : progress = 1;
304 : 0 : break;
305 : : case 'h':
306 : : default:
307 : : goto usage;
308 : : }
309 : : }
310 : :
311 [ # # ][ # # ]: 0 : if (!name || optind != argc)
312 : : goto usage;
313 : :
314 : 0 : result = vhd_util_coalesce_parent(name, sparse, progress);
315 : :
316 [ # # ]: 0 : if (result < 0) {
317 : : /* -ve errors will be in range for int */
318 : 0 : printf("error coalescing: %d\n", (int)result);
319 : 0 : return result;
320 : : }
321 : :
322 : : printf("Coalesced %" PRId64 " sectors", result);
323 : 0 : return 0;
324 : :
325 : : usage:
326 : : printf("options: <-n name> "
327 : : "[-s sparse] [-p progress] "
328 : : "[-h help]\n");
329 : 0 : return -EINVAL;
330 : : }
|