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 : : ssize_t ret;
50 : :
51 : 0 : errno = 0;
52 : 0 : off = lseek64(fd, (off64_t)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 err;
82 : : uint32_t i;
83 : 0 : int64_t coalesced_size = 0;
84 : : char *buf;
85 : : char *map;
86 : : uint64_t sec, secs;
87 : :
88 : 0 : buf = NULL;
89 : 0 : map = NULL;
90 : 0 : sec = block * vhd->spb;
91 : :
92 [ # # ]: 0 : if (vhd->bat.bat[block] == DD_BLK_UNUSED)
93 : 0 : return 0;
94 : :
95 [ # # ][ # # ]: 0 : if (vhd_has_batmap(vhd) && vhd_batmap_test(vhd, &vhd->batmap, block)) {
96 : 0 : err = vhd_read_block(vhd, block, &buf);
97 [ # # ]: 0 : if (err)
98 : : goto done;
99 : :
100 [ # # ]: 0 : if (parent->file)
101 : 0 : err = vhd_io_write(parent, buf, sec, vhd->spb);
102 : : else
103 : 0 : err = __raw_io_write(parent_fd, buf, sec, vhd->spb);
104 : :
105 [ # # ]: 0 : if (err == 0)
106 : 0 : coalesced_size = vhd->spb;
107 : :
108 : : goto done;
109 : : }
110 : :
111 : 0 : err = vhd_read_bitmap(vhd, block, &map);
112 [ # # ]: 0 : if (err)
113 : : goto done;
114 : :
115 : 0 : err = posix_memalign((void *)&buf, 4096, vhd->header.block_size);
116 [ # # ]: 0 : if (err) {
117 : 0 : err = -err;
118 : 0 : goto done;
119 : : }
120 : :
121 [ # # ]: 0 : for (i = 0; i < vhd->spb; i++) {
122 [ # # ]: 0 : if (!vhd_bitmap_test(vhd, map, i))
123 : 0 : continue;
124 : :
125 [ # # ]: 0 : for (secs = 0; i + secs < vhd->spb; secs++)
126 [ # # ]: 0 : if (!vhd_bitmap_test(vhd, map, i + secs))
127 : : break;
128 : :
129 : 0 : err = vhd_read_at(vhd, block, i, vhd_sectors_to_bytes(secs),
130 : 0 : buf + vhd_sectors_to_bytes(i));
131 [ # # ]: 0 : if (err)
132 : : goto done;
133 : :
134 [ # # ]: 0 : if (parent->file)
135 : 0 : err = vhd_io_write(parent,
136 : 0 : buf + vhd_sectors_to_bytes(i),
137 : : sec + i, secs);
138 : : else
139 : 0 : err = __raw_io_write(parent_fd,
140 : 0 : buf + vhd_sectors_to_bytes(i),
141 : : sec + i, secs);
142 [ # # ]: 0 : if (err)
143 : : goto done;
144 : :
145 : 0 : coalesced_size += (int64_t)secs;
146 : 0 : i += (uint32_t)secs;
147 : : }
148 : :
149 : : err = 0;
150 : :
151 : : done:
152 : 0 : free(buf);
153 : 0 : free(map);
154 [ # # ]: 0 : if (err < 0)
155 : 0 : return err;
156 : :
157 : : return coalesced_size;
158 : : }
159 : :
160 : : /**
161 : : * Coalesce VHD to its immediate parent
162 : : *
163 : : * @param[in] from the VHD to coalesce
164 : : * @param[in] to the VHD to coalesce to or NULL if raw
165 : : * @param[in] to_fd the raws file to coalesce to or NULL if to is to be used
166 : : * @param[in] progess whether to report progress as the operation is being performed
167 : : * @return positive number of sectors coalesced or negative errno in the case of failure
168 : : */
169 : : static int64_t
170 : 0 : vhd_util_coalesce_onto(vhd_context_t *from,
171 : : vhd_context_t *to, int to_fd, int progress)
172 : : {
173 : : int i;
174 : : int64_t err;
175 : 0 : int64_t coalesced_size = 0;
176 : :
177 : 0 : err = vhd_get_bat(from);
178 [ # # ]: 0 : if (err)
179 : : goto out;
180 : :
181 [ # # ]: 0 : if (vhd_has_batmap(from)) {
182 : 0 : err = vhd_get_batmap(from);
183 [ # # ]: 0 : if (err)
184 : : goto out;
185 : : }
186 : :
187 [ # # ]: 0 : for (i = 0; i < from->bat.entries; i++) {
188 [ # # ]: 0 : if (progress) {
189 : 0 : printf("\r%6.2f%%",
190 : 0 : ((float)i / (float)from->bat.entries) * 100.00);
191 : 0 : fflush(stdout);
192 : : }
193 : 0 : err = vhd_util_coalesce_block(from, to, to_fd, (uint64_t)i);
194 [ # # ]: 0 : if (err < 0)
195 : : goto out;
196 : :
197 : 0 : coalesced_size += err;
198 : : }
199 : :
200 : 0 : err = 0;
201 : :
202 [ # # ]: 0 : if (progress)
203 : : printf("\r100.00%%\n");
204 : :
205 : : out:
206 [ # # ]: 0 : if (err < 0)
207 : : return err;
208 : :
209 : 0 : return coalesced_size;
210 : : }
211 : :
212 : : /**
213 : : * Coalesce the VHD to its immediate parent
214 : : *
215 : : * @param[in] name the name (path) of the VHD to coalesce
216 : : * @param[in] sparse whether the parent VHD should be written sparsely
217 : : * @param[in] progess whether to report progress as the operation is being performed
218 : : * @return positive number of sectors coalesced or negative errno in the case of failure
219 : : */
220 : : static int64_t
221 : 0 : vhd_util_coalesce_parent(const char *name, int sparse, int progress)
222 : : {
223 : : char *pname;
224 : : int64_t err;
225 : : int parent_fd;
226 : : vhd_context_t vhd, parent;
227 : :
228 : 0 : parent_fd = -1;
229 : 0 : parent.file = NULL;
230 : :
231 : 0 : err = vhd_open(&vhd, name, VHD_OPEN_RDONLY);
232 [ # # ]: 0 : if (err) {
233 : : printf("error opening %s: %" PRId64 "\n", name, err);
234 : 0 : return err;
235 : : }
236 : :
237 [ # # ]: 0 : if (vhd.footer.type != HD_TYPE_DIFF) {
238 : : printf("coalescing of non-differencing disks is not supported\n");
239 : 0 : vhd_close(&vhd);
240 : : return -EINVAL;
241 : : }
242 : :
243 : 0 : err = vhd_parent_locator_get(&vhd, &pname);
244 [ # # ]: 0 : if (err) {
245 : : printf("error finding %s parent: %" PRId64 "\n", name, err);
246 : 0 : vhd_close(&vhd);
247 : : return err;
248 : : }
249 : :
250 [ # # ]: 0 : if (vhd_parent_raw(&vhd)) {
251 : 0 : parent_fd = open_optional_odirect(pname, O_RDWR | O_DIRECT | O_LARGEFILE, 0644);
252 [ # # ]: 0 : if (parent_fd == -1) {
253 : 0 : err = -errno;
254 : 0 : printf("failed to open parent %s: %" PRId64 "\n", pname, err);
255 : 0 : free(pname);
256 : 0 : vhd_close(&vhd);
257 : : return err;
258 : : }
259 : : } else {
260 [ # # ]: 0 : int flags = (sparse ? VHD_OPEN_IO_WRITE_SPARSE : 0);
261 [ # # ]: 0 : if (sparse) printf("opening for sparse writes\n");
262 : 0 : err = vhd_open(&parent, pname, VHD_OPEN_RDWR | flags);
263 [ # # ]: 0 : if (err) {
264 : 0 : printf("error opening %s: %" PRId64 "\n", pname, err);
265 : 0 : free(pname);
266 : 0 : vhd_close(&vhd);
267 : : return err;
268 : : }
269 : : }
270 : :
271 : 0 : err = vhd_util_coalesce_onto(&vhd, &parent, parent_fd, progress);
272 : :
273 : 0 : free(pname);
274 : 0 : vhd_close(&vhd);
275 [ # # ]: 0 : if (parent.file)
276 : 0 : vhd_close(&parent);
277 : : else
278 : 0 : close(parent_fd);
279 : 0 : return err;
280 : : }
281 : :
282 : : int
283 : 0 : vhd_util_coalesce(int argc, char **argv)
284 : : {
285 : : char *name;
286 : : int c, progress, sparse;
287 : : int64_t result;
288 : :
289 : 0 : name = NULL;
290 : 0 : sparse = 0;
291 : 0 : progress = 0;
292 : :
293 [ # # ]: 0 : if (!argc || !argv)
294 : : goto usage;
295 : :
296 : 0 : optind = 0;
297 [ # # ]: 0 : while ((c = getopt(argc, argv, "n:o:a:x:sph")) != -1) {
298 [ # # # # ]: 0 : switch (c) {
299 : : case 'n':
300 : 0 : name = optarg;
301 : 0 : break;
302 : : case 's':
303 : : sparse = 1;
304 : : break;
305 : : case 'p':
306 : 0 : progress = 1;
307 : 0 : break;
308 : : case 'h':
309 : : default:
310 : : goto usage;
311 : : }
312 : : }
313 : :
314 [ # # ][ # # ]: 0 : if (!name || optind != argc)
315 : : goto usage;
316 : :
317 : 0 : result = vhd_util_coalesce_parent(name, sparse, progress);
318 : :
319 [ # # ]: 0 : if (result < 0) {
320 : : /* -ve errors will be in range for int */
321 : 0 : printf("error coalescing: %d\n", (int)result);
322 : 0 : return result;
323 : : }
324 : :
325 : : printf("Coalesced %" PRId64 " sectors", result);
326 : 0 : return 0;
327 : :
328 : : usage:
329 : : printf("options: <-n name> "
330 : : "[-s sparse] [-p progress] "
331 : : "[-h help]\n");
332 : 0 : return -EINVAL;
333 : : }
|