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 <string.h>
41 : : #include <syslog.h>
42 : : #include <inttypes.h>
43 : : #include <sys/mman.h>
44 : :
45 : : #include "libvhd-journal.h"
46 : :
47 : : #if 1
48 : : #define DFPRINTF(_f, _a...) fprintf(stdout, _f, ##_a)
49 : : #else
50 : : #define DFPRINTF(_f, _a...) ((void)0)
51 : : #endif
52 : :
53 : : #define EPRINTF(_f, _a...) \
54 : : do { \
55 : : syslog(LOG_INFO, "%s: " _f, __func__, ##_a); \
56 : : DFPRINTF(_f, _a); \
57 : : } while (0)
58 : :
59 : : typedef struct vhd_block {
60 : : uint32_t block;
61 : : uint32_t offset;
62 : : } vhd_block_t;
63 : :
64 : : TEST_FAIL_EXTERN_VARS;
65 : :
66 : : static inline uint32_t
67 : : secs_to_blocks_down(vhd_context_t *vhd, uint64_t secs)
68 : : {
69 : : return secs / vhd->spb;
70 : : }
71 : :
72 : : static uint32_t
73 : 0 : secs_to_blocks_up(vhd_context_t *vhd, uint64_t secs)
74 : : {
75 : : uint32_t blocks;
76 : :
77 : 0 : blocks = secs / vhd->spb;
78 [ # # ]: 0 : if (secs % vhd->spb)
79 : 0 : blocks++;
80 : :
81 : 0 : return blocks;
82 : : }
83 : :
84 : : static int
85 : 0 : vhd_fixed_shrink(vhd_journal_t *journal, uint64_t secs)
86 : : {
87 : : int err;
88 : : uint64_t new_eof;
89 : : vhd_context_t *vhd;
90 : :
91 : 0 : vhd = &journal->vhd;
92 : :
93 : 0 : new_eof = vhd->footer.curr_size - vhd_sectors_to_bytes(secs);
94 [ # # ]: 0 : if (new_eof <= sizeof(vhd_footer_t))
95 : : return -EINVAL;
96 : :
97 : 0 : err = ftruncate(vhd->fd, new_eof);
98 [ # # ]: 0 : if (err)
99 : 0 : return errno;
100 : :
101 : 0 : vhd->footer.curr_size = new_eof;
102 : 0 : return vhd_write_footer(vhd, &vhd->footer);
103 : : }
104 : :
105 : : static int
106 : 0 : vhd_write_zeros(vhd_journal_t *journal, off64_t off, uint64_t size)
107 : : {
108 : : int err;
109 : : char *buf;
110 : : vhd_context_t *vhd;
111 : : uint64_t bytes, map;
112 : :
113 : 0 : vhd = &journal->vhd;
114 : 0 : map = MIN(size, VHD_BLOCK_SIZE);
115 : :
116 : 0 : err = vhd_seek(vhd, off, SEEK_SET);
117 [ # # ]: 0 : if (err)
118 : : return err;
119 : :
120 : 0 : buf = mmap(0, map, PROT_READ, MAP_SHARED | MAP_ANONYMOUS, -1, 0);
121 [ # # ]: 0 : if (buf == MAP_FAILED)
122 : 0 : return -errno;
123 : :
124 : : do {
125 : 0 : bytes = MIN(size, map);
126 : :
127 : 0 : err = vhd_write(vhd, buf, bytes);
128 [ # # ]: 0 : if (err)
129 : : break;
130 : :
131 : 0 : size -= bytes;
132 [ # # ]: 0 : } while (size);
133 : :
134 : 0 : munmap(buf, map);
135 : :
136 : 0 : return err;
137 : : }
138 : :
139 : : static int
140 : 0 : vhd_fixed_grow(vhd_journal_t *journal, uint64_t secs)
141 : : {
142 : : int err;
143 : : vhd_context_t *vhd;
144 : : uint64_t size, eof, new_eof;
145 : :
146 : 0 : size = vhd_sectors_to_bytes(secs);
147 : 0 : vhd = &journal->vhd;
148 : :
149 : 0 : err = vhd_seek(vhd, 0, SEEK_END);
150 [ # # ]: 0 : if (err)
151 : : goto out;
152 : :
153 : 0 : eof = vhd_position(vhd);
154 [ # # ]: 0 : if (eof == (off64_t)-1) {
155 : 0 : err = -errno;
156 : 0 : goto out;
157 : : }
158 : :
159 : 0 : err = vhd_write_zeros(journal, eof - sizeof(vhd_footer_t), size);
160 [ # # ]: 0 : if (err)
161 : : goto out;
162 : :
163 : 0 : new_eof = eof + size;
164 : 0 : err = vhd_seek(vhd, new_eof, SEEK_SET);
165 [ # # ]: 0 : if (err)
166 : : goto out;
167 : :
168 : 0 : vhd->footer.curr_size += size;
169 : 0 : err = vhd_write_footer(vhd, &vhd->footer);
170 [ # # ]: 0 : if (err)
171 : : goto out;
172 : :
173 : 0 : err = 0;
174 : :
175 : : out:
176 : 0 : return err;
177 : : }
178 : :
179 : : static int
180 : 0 : vhd_fixed_resize(vhd_journal_t *journal, uint64_t size)
181 : : {
182 : : int err;
183 : : vhd_context_t *vhd;
184 : : uint64_t cur_secs, new_secs;
185 : :
186 : 0 : vhd = &journal->vhd;
187 : 0 : cur_secs = vhd->footer.curr_size >> VHD_SECTOR_SHIFT;
188 : 0 : new_secs = size << (20 - VHD_SECTOR_SHIFT);
189 : :
190 [ # # ]: 0 : if (cur_secs == new_secs)
191 : : return 0;
192 [ # # ]: 0 : else if (cur_secs > new_secs)
193 : 0 : err = vhd_fixed_shrink(journal, cur_secs - new_secs);
194 : : else
195 : 0 : err = vhd_fixed_grow(journal, new_secs - cur_secs);
196 : :
197 : 0 : return err;
198 : : }
199 : :
200 : : static inline void
201 : : swap(vhd_block_t *list, int a, int b)
202 : : {
203 : : vhd_block_t tmp;
204 : :
205 : : tmp = list[a];
206 : : list[a] = list[b];
207 : : list[b] = tmp;
208 : : }
209 : :
210 : : #if 0
211 : : static int
212 : : partition(vhd_block_t *list, int left, int right, int pidx)
213 : : {
214 : : int i, sidx;
215 : : long long pval;
216 : :
217 : : sidx = left;
218 : : pval = list[pidx].offset;
219 : : swap(list, pidx, right);
220 : :
221 : : for (i = left; i < right; i++)
222 : : if (list[i].offset >= pval) {
223 : : swap(list, sidx, i);
224 : : ++sidx;
225 : : }
226 : :
227 : : swap(list, right, sidx);
228 : : return sidx;
229 : : }
230 : : #endif
231 : :
232 : : #if 0
233 : : static void
234 : : quicksort(vhd_block_t *list, int left, int right)
235 : : {
236 : : int pidx, new_pidx;
237 : :
238 : : if (right < left)
239 : : return;
240 : :
241 : : pidx = left;
242 : : new_pidx = partition(list, left, right, pidx);
243 : : quicksort(list, left, new_pidx - 1);
244 : : quicksort(list, new_pidx + 1, right);
245 : : }
246 : : #endif
247 : :
248 : : static int
249 : 0 : vhd_move_block(vhd_journal_t *journal, uint32_t src, off64_t offset)
250 : : {
251 : : int err;
252 : : char *buf;
253 : : size_t size;
254 : : vhd_context_t *vhd;
255 : : off64_t off, src_off;
256 : :
257 : 0 : buf = NULL;
258 : 0 : vhd = &journal->vhd;
259 : 0 : off = offset;
260 : 0 : size = vhd_sectors_to_bytes(vhd->bm_secs);
261 : 0 : src_off = vhd->bat.bat[src];
262 : :
263 [ # # ]: 0 : if (src_off == DD_BLK_UNUSED)
264 : 0 : return -EINVAL;
265 : 0 : src_off = vhd_sectors_to_bytes(src_off);
266 : :
267 : 0 : err = vhd_journal_add_block(journal, src,
268 : : VHD_JOURNAL_DATA | VHD_JOURNAL_METADATA);
269 [ # # ]: 0 : if (err)
270 : : goto out;
271 : :
272 : 0 : err = vhd_read_bitmap(vhd, src, &buf);
273 [ # # ]: 0 : if (err)
274 : : goto out;
275 : :
276 : 0 : err = vhd_seek(vhd, off, SEEK_SET);
277 [ # # ]: 0 : if (err)
278 : : goto out;
279 : :
280 : 0 : err = vhd_write(vhd, buf, size);
281 [ # # ]: 0 : if (err)
282 : : goto out;
283 : :
284 : 0 : free(buf);
285 : 0 : buf = NULL;
286 : 0 : off += size;
287 : 0 : size = vhd_sectors_to_bytes(vhd->spb);
288 : :
289 : 0 : err = vhd_read_block(vhd, src, &buf);
290 [ # # ]: 0 : if (err)
291 : : goto out;
292 : :
293 : 0 : err = vhd_seek(vhd, off, SEEK_SET);
294 [ # # ]: 0 : if (err)
295 : : goto out;
296 : :
297 : 0 : err = vhd_write(vhd, buf, size);
298 [ # # ]: 0 : if (err)
299 : : goto out;
300 : :
301 : 0 : vhd->bat.bat[src] = offset >> VHD_SECTOR_SHIFT;
302 : :
303 : 0 : err = vhd_write_zeros(journal, src_off,
304 : 0 : vhd_sectors_to_bytes(vhd->bm_secs + vhd->spb));
305 : :
306 : : out:
307 : 0 : free(buf);
308 : 0 : return err;
309 : : }
310 : :
311 : : #if 0
312 : : static int
313 : : vhd_clobber_block(vhd_journal_t *journal, uint32_t src, uint32_t dest)
314 : : {
315 : : int err;
316 : : off64_t off;
317 : : vhd_context_t *vhd;
318 : :
319 : : vhd = &journal->vhd;
320 : : off = vhd_sectors_to_bytes(vhd->bat.bat[dest]);
321 : :
322 : : err = vhd_journal_add_block(journal, dest,
323 : : VHD_JOURNAL_DATA | VHD_JOURNAL_METADATA);
324 : : if (err)
325 : : return err;
326 : :
327 : : err = vhd_move_block(journal, src, off);
328 : : if (err)
329 : : return err;
330 : :
331 : : vhd->bat.bat[dest] = DD_BLK_UNUSED;
332 : :
333 : : return 0;
334 : : }
335 : : #endif
336 : :
337 : : #if 0
338 : : /*
339 : : * remove a list of blocks from the vhd file
340 : : * if a block to be removed:
341 : : * - resides at the end of the file: simply clear its bat entry
342 : : * - resides elsewhere: move the last block in the file into its position
343 : : * and update the bat to reflect this
344 : : */
345 : : static int
346 : : vhd_defrag_shrink(vhd_journal_t *journal,
347 : : vhd_block_t *original_free_list, int free_cnt)
348 : : {
349 : : vhd_context_t *vhd;
350 : : int i, j, free_idx, err;
351 : : vhd_block_t *blocks, *free_list;
352 : :
353 : : err = 0;
354 : : blocks = NULL;
355 : : free_list = NULL;
356 : : vhd = &journal->vhd;
357 : :
358 : : blocks = malloc(vhd->bat.entries * sizeof(vhd_block_t));
359 : : if (!blocks) {
360 : : err = -ENOMEM;
361 : : goto out;
362 : : }
363 : :
364 : : free_list = malloc(free_cnt * sizeof(vhd_block_t));
365 : : if (!free_list) {
366 : : err = -ENOMEM;
367 : : goto out;
368 : : }
369 : :
370 : : for (i = 0; i < vhd->bat.entries; i++) {
371 : : blocks[i].block = i;
372 : : blocks[i].offset = vhd->bat.bat[i];
373 : : }
374 : :
375 : : memcpy(free_list, original_free_list,
376 : : free_cnt * sizeof(vhd_block_t));
377 : :
378 : : /* sort both the to-free list and the bat list
379 : : * in order of descending file offset */
380 : : quicksort(free_list, 0, free_cnt - 1);
381 : : quicksort(blocks, 0, vhd->bat.entries - 1);
382 : :
383 : : for (i = 0, free_idx = 0;
384 : : i < vhd->bat.entries && free_idx < free_cnt; i++) {
385 : : vhd_block_t *b = blocks + i;
386 : :
387 : : if (b->offset == DD_BLK_UNUSED)
388 : : continue;
389 : :
390 : : for (j = free_idx; j < free_cnt; j++)
391 : : if (b->block == free_list[j].block) {
392 : : /* the last block in the file is in the list of
393 : : * blocks to remove; no need to shuffle the
394 : : * data -- just clear the bat entry */
395 : : vhd->bat.bat[free_list[j].block] = DD_BLK_UNUSED;
396 : : free_idx++;
397 : : continue;
398 : : }
399 : :
400 : : err = vhd_clobber_block(journal, b->block,
401 : : free_list[free_idx++].block);
402 : : if (err)
403 : : goto out;
404 : : }
405 : :
406 : : /* clear any bat entries for blocks we did not shuffle */
407 : : for (i = free_idx; i < free_cnt; i++)
408 : : vhd->bat.bat[free_list[i].block] = DD_BLK_UNUSED;
409 : :
410 : : out:
411 : : free(blocks);
412 : : free(free_list);
413 : :
414 : : return err;
415 : : }
416 : : #endif
417 : :
418 : : #if 0
419 : : static int
420 : : vhd_clear_bat_entries(vhd_journal_t *journal, uint32_t entries)
421 : : {
422 : : int i, err;
423 : : vhd_context_t *vhd;
424 : : off64_t orig_map_off, new_map_off;
425 : : uint32_t orig_entries, new_entries;
426 : :
427 : : vhd = &journal->vhd;
428 : : orig_entries = vhd->header.max_bat_size;
429 : : new_entries = orig_entries - entries;
430 : :
431 : : if (vhd_has_batmap(vhd)) {
432 : : err = vhd_batmap_header_offset(vhd, &orig_map_off);
433 : : if (err)
434 : : return err;
435 : : }
436 : :
437 : : /* update header */
438 : : vhd->header.max_bat_size = new_entries;
439 : : err = vhd_write_header(vhd, &vhd->header);
440 : : if (err)
441 : : return err;
442 : :
443 : : /* update footer */
444 : : vhd->footer.curr_size = (uint64_t)new_entries * vhd->header.block_size;
445 : : vhd->footer.geometry = vhd_chs(vhd->footer.curr_size);
446 : : err = vhd_write_footer(vhd, &vhd->footer);
447 : : if (err)
448 : : return err;
449 : :
450 : : /* update bat -- we don't reclaim space, just clear entries */
451 : : for (i = new_entries; i < orig_entries; i++)
452 : : vhd->bat.bat[i] = 0;
453 : :
454 : : err = vhd_write_bat(vhd, &vhd->bat);
455 : : if (err)
456 : : return err;
457 : :
458 : : /* update this after write_bat so the end of the bat is zeored */
459 : : vhd->bat.entries = new_entries;
460 : :
461 : : if (!vhd_has_batmap(vhd))
462 : : return 0;
463 : :
464 : : /* zero out old batmap header if new header has moved */
465 : : err = vhd_batmap_header_offset(vhd, &new_map_off);
466 : : if (err)
467 : : return err;
468 : :
469 : : if (orig_map_off != new_map_off) {
470 : : size_t size;
471 : :
472 : : size = vhd_bytes_padded(sizeof(struct dd_batmap_hdr));
473 : :
474 : : err = vhd_write_zeros(journal, orig_map_off, size);
475 : : if (err)
476 : : return err;
477 : : }
478 : :
479 : : /* update batmap -- clear entries for freed blocks */
480 : : for (i = new_entries; i < orig_entries; i++)
481 : : vhd_batmap_clear(vhd, &vhd->batmap, i);
482 : :
483 : : err = vhd_write_batmap(vhd, &vhd->batmap);
484 : : if (err)
485 : : return err;
486 : :
487 : : return 0;
488 : : }
489 : : #endif
490 : :
491 : : static int
492 : 0 : vhd_dynamic_shrink(vhd_journal_t *journal, uint64_t secs)
493 : : {
494 : : printf("dynamic shrink not fully implemented\n");
495 : 0 : return -ENOSYS;
496 : : #if 0
497 : : off64_t eof;
498 : : uint32_t blocks;
499 : : vhd_context_t *vhd;
500 : : int i, j, err, free_cnt;
501 : : struct vhd_block *free_list;
502 : :
503 : : eof = 0;
504 : : free_cnt = 0;
505 : : free_list = NULL;
506 : : vhd = &journal->vhd;
507 : :
508 : : blocks = secs_to_blocks_down(vhd, secs);
509 : : if (blocks == 0)
510 : : return 0;
511 : :
512 : : if (vhd_has_batmap(vhd)) {
513 : : err = vhd_get_batmap(vhd);
514 : : if (err)
515 : : return err;
516 : : }
517 : :
518 : : free_list = malloc(blocks * sizeof(struct vhd_block));
519 : : if (!free_list)
520 : : return -ENOMEM;
521 : :
522 : : for (i = vhd->bat.entries - 1, j = 0; i >= 0 && j < blocks; i--, j++) {
523 : : uint32_t blk = vhd->bat.bat[i];
524 : :
525 : : if (blk != DD_BLK_UNUSED) {
526 : : free_list[free_cnt].block = i;
527 : : free_list[free_cnt].offset = blk;
528 : : free_cnt++;
529 : : }
530 : : }
531 : :
532 : : if (free_cnt) {
533 : : err = vhd_defrag_shrink(journal, free_list, free_cnt);
534 : : if (err)
535 : : goto out;
536 : : }
537 : :
538 : : err = vhd_clear_bat_entries(journal, blocks);
539 : : if (err)
540 : : goto out;
541 : :
542 : : /* remove data beyond footer */
543 : : err = vhd_end_of_data(vhd, &eof);
544 : : if (err)
545 : : goto out;
546 : :
547 : : err = ftruncate(vhd->fd, eof + sizeof(vhd_footer_t));
548 : : if (err) {
549 : : err = -errno;
550 : : goto out;
551 : : }
552 : :
553 : : err = 0;
554 : :
555 : : out:
556 : : free(free_list);
557 : : return err;
558 : : #endif
559 : : }
560 : :
561 : : static inline void
562 : 0 : vhd_first_data_block(vhd_context_t *vhd, vhd_block_t *block)
563 : : {
564 : : int i;
565 : : uint32_t blk;
566 : :
567 : : memset(block, 0, sizeof(vhd_block_t));
568 : :
569 [ # # ]: 0 : for (i = 0; i < vhd->bat.entries; i++) {
570 : 0 : blk = vhd->bat.bat[i];
571 : :
572 [ # # ]: 0 : if (blk != DD_BLK_UNUSED) {
573 [ # # ][ # # ]: 0 : if (!block->offset || blk < block->offset) {
574 : 0 : block->block = i;
575 : 0 : block->offset = blk;
576 : : }
577 : : }
578 : : }
579 : 0 : }
580 : :
581 : : static inline uint32_t
582 : : vhd_next_block_offset(vhd_context_t *vhd)
583 : : {
584 : : int i;
585 : : uint32_t blk, end, next;
586 : :
587 : 0 : next = 0;
588 : :
589 [ # # ]: 0 : for (i = 0; i < vhd->bat.entries; i++) {
590 : 0 : blk = vhd->bat.bat[i];
591 : :
592 [ # # ]: 0 : if (blk != DD_BLK_UNUSED) {
593 : 0 : end = blk + vhd->spb + vhd->bm_secs;
594 : 0 : next = MAX(next, end);
595 : : }
596 : : }
597 : :
598 : : return next;
599 : : }
600 : :
601 : : static inline int
602 : : in_range(off64_t off, off64_t start, off64_t size)
603 : : {
604 [ # # ][ # # ]: 0 : return (start < off && start + size > off);
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ][ # # ]
605 : : }
606 : :
607 : : #define SKIP_HEADER 0x01
608 : : #define SKIP_BAT 0x02
609 : : #define SKIP_BATMAP 0x04
610 : : #define SKIP_PLOC 0x08
611 : : #define SKIP_DATA 0x10
612 : :
613 : : static inline int
614 : : skip_check(int mode, int type)
615 : : {
616 : 0 : return mode & type;
617 : : }
618 : :
619 : : static int
620 : 0 : vhd_check_for_clobber(vhd_context_t *vhd, off64_t off, int mode)
621 : : {
622 : : int i, n;
623 : : char *msg;
624 : : size_t size;
625 : : vhd_block_t fb;
626 : 0 : vhd_parent_locator_t *loc;
627 : :
628 : 0 : msg = NULL;
629 : :
630 [ # # ]: 0 : if (!vhd_type_dynamic(vhd))
631 : 0 : return 0;
632 : :
633 [ # # ]: 0 : if (off < VHD_SECTOR_SIZE) {
634 : : msg = "backup footer";
635 : : goto fail;
636 : : }
637 : :
638 [ # # ]: 0 : if (!skip_check(mode, SKIP_HEADER))
639 [ # # ]: 0 : if (in_range(off,
640 : 0 : vhd->footer.data_offset, sizeof(vhd_header_t))) {
641 : : msg = "header";
642 : : goto fail;
643 : : }
644 : :
645 [ # # ]: 0 : if (!skip_check(mode, SKIP_BAT))
646 [ # # ]: 0 : if (in_range(off, vhd->header.table_offset,
647 : 0 : vhd_bytes_padded(vhd->header.max_bat_size *
648 : : sizeof(uint32_t)))) {
649 : : msg = "bat";
650 : : goto fail;
651 : : }
652 : :
653 [ # # ]: 0 : if (!skip_check(mode, SKIP_BATMAP))
654 [ # # ][ # # ]: 0 : if (vhd_has_batmap(vhd) &&
655 : 0 : in_range(off, vhd->batmap.header.batmap_offset,
656 : 0 : vhd_bytes_padded(vhd->batmap.header.batmap_size))) {
657 : : msg = "batmap";
658 : : goto fail;
659 : : }
660 : :
661 [ # # ]: 0 : if (!skip_check(mode, SKIP_PLOC)) {
662 : : n = sizeof(vhd->header.loc) / sizeof(vhd_parent_locator_t);
663 [ # # ]: 0 : for (i = 0; i < n; i++) {
664 : 0 : loc = vhd->header.loc + i;
665 [ # # ]: 0 : if (loc->code == PLAT_CODE_NONE)
666 : 0 : continue;
667 : :
668 : 0 : size = vhd_parent_locator_size(loc);
669 [ # # ]: 0 : if (in_range(off, loc->data_offset, size)) {
670 : : msg = "parent locator";
671 : : goto fail;
672 : : }
673 : : }
674 : : }
675 : :
676 [ # # ]: 0 : if (!skip_check(mode, SKIP_DATA)) {
677 : 0 : vhd_first_data_block(vhd, &fb);
678 [ # # ][ # # ]: 0 : if (fb.offset && in_range(off,
679 : 0 : vhd_sectors_to_bytes(fb.offset),
680 : : VHD_BLOCK_SIZE)) {
681 : : msg = "data block";
682 : : goto fail;
683 : : }
684 : : }
685 : :
686 : : return 0;
687 : :
688 : : fail:
689 : 0 : EPRINTF("write to 0x%08"PRIx64" would clobber %s\n", off, msg);
690 : : return -EINVAL;
691 : : }
692 : :
693 : : /*
694 : : * take any metadata after the bat (@eob) and shift it
695 : : */
696 : : static int
697 : 0 : vhd_shift_metadata(vhd_journal_t *journal, off64_t eob,
698 : : size_t bat_needed, size_t map_needed)
699 : : {
700 : : int i, n, err;
701 : : vhd_context_t *vhd;
702 : : size_t size_needed;
703 : 0 : void *buf = NULL;
704 : : char **locators;
705 : 0 : vhd_parent_locator_t *loc;
706 : :
707 : 0 : vhd = &journal->vhd;
708 : 0 : size_needed = bat_needed + map_needed;
709 : :
710 : 0 : n = sizeof(vhd->header.loc) / sizeof(vhd_parent_locator_t);
711 : :
712 : 0 : locators = calloc(n, sizeof(char *));
713 [ # # ]: 0 : if (!locators)
714 : 0 : return -ENOMEM;
715 : :
716 [ # # ]: 0 : for (i = 0; i < n; i++) {
717 : : size_t size;
718 : :
719 : 0 : loc = vhd->header.loc + i;
720 [ # # ]: 0 : if (loc->code == PLAT_CODE_NONE)
721 : 0 : continue;
722 : :
723 [ # # ]: 0 : if (loc->data_offset < eob)
724 : 0 : continue;
725 : :
726 : 0 : size = vhd_parent_locator_size(loc);
727 : 0 : err = posix_memalign(&buf, VHD_SECTOR_SIZE, size);
728 [ # # ]: 0 : if (err) {
729 : 0 : err = -err;
730 : 0 : goto out;
731 : : }
732 : :
733 : 0 : err = vhd_seek(vhd, loc->data_offset, SEEK_SET);
734 [ # # ]: 0 : if (err)
735 : : goto out;
736 : :
737 : 0 : err = vhd_read(vhd, buf, size);
738 [ # # ]: 0 : if (err)
739 : : goto out;
740 : :
741 : 0 : locators[i] = buf;
742 : : }
743 : :
744 : 0 : buf = NULL;
745 : :
746 [ # # ]: 0 : for (i = 0; i < n; i++) {
747 : : off64_t off;
748 : : size_t size;
749 : :
750 [ # # ]: 0 : if (!locators[i])
751 : 0 : continue;
752 : :
753 : 0 : loc = vhd->header.loc + i;
754 : 0 : off = loc->data_offset + size_needed;
755 : 0 : size = vhd_parent_locator_size(loc);
756 : :
757 [ # # ]: 0 : if (vhd_check_for_clobber(vhd, off + size, SKIP_PLOC)) {
758 : 0 : EPRINTF("%s: shifting locator %d would clobber data\n",
759 : : vhd->file, i);
760 : : err = -EINVAL;
761 : : goto out;
762 : : }
763 : :
764 : 0 : err = vhd_seek(vhd, off, SEEK_SET);
765 [ # # ]: 0 : if (err)
766 : : goto out;
767 : :
768 : 0 : err = vhd_write(vhd, locators[i], size);
769 [ # # ]: 0 : if (err)
770 : : goto out;
771 : :
772 : 0 : free(locators[i]);
773 : 0 : locators[i] = NULL;
774 : 0 : loc->data_offset = off;
775 : :
776 : : /* write the new header after writing the new bat */
777 : : }
778 : :
779 [ # # ][ # # ]: 0 : if (vhd_has_batmap(vhd) && vhd->batmap.header.batmap_offset > eob) {
780 : 0 : vhd->batmap.header.batmap_offset += bat_needed;
781 : :
782 : : /* write the new batmap after writing the new bat */
783 : : }
784 : :
785 : : err = 0;
786 : :
787 : : out:
788 : 0 : free(buf);
789 [ # # ]: 0 : for (i = 0; i < n; i++)
790 : 0 : free(locators[i]);
791 : 0 : free(locators);
792 : :
793 : 0 : return err;
794 : : }
795 : :
796 : : static int
797 : 0 : vhd_add_bat_entries(vhd_journal_t *journal, int entries)
798 : : {
799 : : int i, err;
800 : : off64_t off;
801 : : vhd_bat_t new_bat;
802 : : vhd_context_t *vhd;
803 : : uint32_t new_entries;
804 : : vhd_batmap_t new_batmap;
805 : : uint64_t bat_size, new_bat_size, map_size, new_map_size;
806 : : void *bat, *map;
807 : :
808 : 0 : vhd = &journal->vhd;
809 : 0 : new_entries = vhd->header.max_bat_size + entries;
810 : :
811 : 0 : bat_size = vhd_bytes_padded(vhd->header.max_bat_size *
812 : : sizeof(uint32_t));
813 : 0 : new_bat_size = vhd_bytes_padded(new_entries * sizeof(uint32_t));
814 : :
815 : 0 : map_size = vhd_bytes_padded((vhd->header.max_bat_size + 7) >> 3);
816 : 0 : new_map_size = vhd_bytes_padded((new_entries + 7) >> 3);
817 : :
818 : 0 : off = vhd->header.table_offset + new_bat_size;
819 [ # # ]: 0 : if (vhd_check_for_clobber(vhd, off, SKIP_BAT | SKIP_BATMAP)) {
820 : 0 : EPRINTF("%s: writing new bat of 0x%"PRIx64" bytes "
821 : : "at 0x%08"PRIx64" would clobber data\n",
822 : : vhd->file, new_bat_size, vhd->header.table_offset);
823 : 0 : return -EINVAL;
824 : : }
825 : :
826 [ # # ]: 0 : if (vhd_has_batmap(vhd)) {
827 : 0 : off = vhd->batmap.header.batmap_offset + new_map_size;
828 [ # # ]: 0 : if (vhd_check_for_clobber(vhd, off, 0)) {
829 : 0 : EPRINTF("%s: writing new batmap of 0x%"PRIx64" bytes"
830 : : " at 0x%08"PRIx64" would clobber data\n", vhd->file,
831 : : new_map_size, vhd->batmap.header.batmap_offset);
832 : : return -EINVAL;
833 : : }
834 : : }
835 : :
836 : : /* update header */
837 : 0 : vhd->header.max_bat_size = new_entries;
838 : 0 : err = vhd_write_header(vhd, &vhd->header);
839 [ # # ]: 0 : if (err)
840 : : return err;
841 : :
842 : : /* allocate new bat */
843 : 0 : err = posix_memalign(&bat, VHD_SECTOR_SIZE, new_bat_size);
844 [ # # ]: 0 : if (err)
845 : 0 : return -err;
846 : :
847 : 0 : new_bat.bat = bat;
848 : 0 : new_bat.spb = vhd->bat.spb;
849 : 0 : new_bat.entries = new_entries;
850 : 0 : memcpy(new_bat.bat, vhd->bat.bat, bat_size);
851 [ # # ]: 0 : for (i = vhd->bat.entries; i < new_entries; i++)
852 : 0 : new_bat.bat[i] = DD_BLK_UNUSED;
853 : :
854 : : /* write new bat */
855 : 0 : err = vhd_write_bat(vhd, &new_bat);
856 [ # # ]: 0 : if (err) {
857 : 0 : free(new_bat.bat);
858 : 0 : return err;
859 : : }
860 : :
861 : : /* update in-memory bat */
862 : 0 : free(vhd->bat.bat);
863 : 0 : vhd->bat = new_bat;
864 : :
865 [ # # ]: 0 : if (!vhd_has_batmap(vhd))
866 : : return 0;
867 : :
868 : : /* allocate new batmap */
869 : 0 : err = posix_memalign(&map, VHD_SECTOR_SIZE, new_map_size);
870 [ # # ]: 0 : if (err)
871 : : return err;
872 : :
873 : 0 : new_batmap.map = map;
874 : 0 : new_batmap.header = vhd->batmap.header;
875 : 0 : new_batmap.header.batmap_size = secs_round_up_no_zero(new_map_size);
876 : 0 : memcpy(new_batmap.map, vhd->batmap.map, map_size);
877 : 0 : memset(new_batmap.map + map_size, 0, new_map_size - map_size);
878 : :
879 : : /* write new batmap */
880 : 0 : err = vhd_write_batmap(vhd, &new_batmap);
881 [ # # ]: 0 : if (err) {
882 : 0 : free(new_batmap.map);
883 : 0 : return err;
884 : : }
885 : :
886 : : /* update in-memory batmap */
887 : 0 : free(vhd->batmap.map);
888 : 0 : vhd->batmap = new_batmap;
889 : :
890 : : /* update footer */
891 : 0 : vhd->footer.curr_size = (uint64_t)new_entries * vhd->header.block_size;
892 : 0 : vhd->footer.geometry = vhd_chs(vhd->footer.curr_size);
893 : 0 : vhd->footer.checksum = vhd_checksum_footer(&vhd->footer);
894 : 0 : err = vhd_write_footer(vhd, &vhd->footer);
895 [ # # ]: 0 : if (err)
896 : 0 : return err;
897 : :
898 : : return 0;
899 : : }
900 : :
901 : : static int
902 : 0 : vhd_dynamic_grow(vhd_journal_t *journal, uint64_t secs)
903 : : {
904 : : int err;
905 : : off64_t eob, eom;
906 : 0 : vhd_context_t *vhd;
907 : : vhd_block_t first_block;
908 : : uint64_t blocks, size_needed;
909 : : uint64_t bat_needed, bat_size, bat_avail, bat_bytes, bat_secs;
910 : : uint64_t map_needed, map_size, map_avail, map_bytes, map_secs;
911 : :
912 : 0 : vhd = &journal->vhd;
913 : :
914 : 0 : size_needed = 0;
915 : 0 : bat_needed = 0;
916 : 0 : map_needed = 0;
917 : :
918 : : /* number of vhd blocks to add */
919 : 0 : blocks = secs_to_blocks_up(vhd, secs);
920 : :
921 : : /* size in bytes needed for new bat entries */
922 : 0 : bat_needed = blocks * sizeof(uint32_t);
923 : 0 : map_needed = (blocks >> 3) + 1;
924 : :
925 : : /* available bytes in current bat */
926 : 0 : bat_bytes = vhd->header.max_bat_size * sizeof(uint32_t);
927 : 0 : bat_secs = secs_round_up_no_zero(bat_bytes);
928 : 0 : bat_size = vhd_sectors_to_bytes(bat_secs);
929 : 0 : bat_avail = bat_size - bat_bytes;
930 : :
931 [ # # ]: 0 : if (vhd_has_batmap(vhd)) {
932 : : /* avaliable bytes in current batmap */
933 : 0 : map_bytes = (vhd->header.max_bat_size + 7) >> 3;
934 : 0 : map_secs = vhd->batmap.header.batmap_size;
935 : 0 : map_size = vhd_sectors_to_bytes(map_secs);
936 : 0 : map_avail = map_size - map_bytes;
937 : : } else {
938 : : map_needed = 0;
939 : : map_avail = 0;
940 : : }
941 : :
942 : : /* we have enough space already; just extend the bat */
943 [ # # ]: 0 : if (bat_needed <= bat_avail && map_needed <= map_avail)
944 : : goto add_entries;
945 : :
946 : : /* we need to add new sectors to the bat */
947 [ # # ]: 0 : if (bat_needed > bat_avail) {
948 : 0 : bat_needed -= bat_avail;
949 : 0 : bat_needed = vhd_bytes_padded(bat_needed);
950 : : } else
951 : : bat_needed = 0;
952 : :
953 : : /* we need to add new sectors to the batmap */
954 [ # # ]: 0 : if (map_needed > map_avail) {
955 : 0 : map_needed -= map_avail;
956 : 0 : map_needed = vhd_bytes_padded(map_needed);
957 : : } else
958 : : map_needed = 0;
959 : :
960 : : /* how many additional bytes do we need? */
961 : 0 : size_needed = bat_needed + map_needed;
962 : :
963 : : /* calculate space between end of headers and beginning of data */
964 : 0 : err = vhd_end_of_headers(vhd, &eom);
965 [ # # ]: 0 : if (err)
966 : 0 : return err;
967 : :
968 : 0 : eob = vhd->header.table_offset + vhd_sectors_to_bytes(bat_secs);
969 : 0 : vhd_first_data_block(vhd, &first_block);
970 : :
971 : : /* no blocks allocated; just shift post-bat metadata */
972 [ # # ]: 0 : if (!first_block.offset)
973 : : goto shift_metadata;
974 : :
975 : : /*
976 : : * not enough space --
977 : : * move vhd data blocks to the end of the file to make room
978 : : */
979 : : do {
980 : : off64_t new_off, bm_size, gap_size;
981 : :
982 : 0 : new_off = vhd_sectors_to_bytes(vhd_next_block_offset(vhd));
983 : :
984 : : /* data region of segment should begin on page boundary */
985 : 0 : bm_size = vhd_sectors_to_bytes(vhd->bm_secs);
986 [ # # ]: 0 : if ((new_off + bm_size) % 4096) {
987 : 0 : gap_size = 4096 - ((new_off + bm_size) % 4096);
988 : :
989 : 0 : err = vhd_write_zeros(journal, new_off, gap_size);
990 [ # # ]: 0 : if (err)
991 : : return err;
992 : :
993 : 0 : new_off += gap_size;
994 : : }
995 : :
996 : 0 : err = vhd_move_block(journal, first_block.block, new_off);
997 [ # # ]: 0 : if (err)
998 : : return err;
999 : :
1000 : 0 : vhd_first_data_block(vhd, &first_block);
1001 : :
1002 [ # # ]: 0 : } while (eom + size_needed >= vhd_sectors_to_bytes(first_block.offset));
1003 : :
1004 [ # # ]: 0 : TEST_FAIL_AT(FAIL_RESIZE_DATA_MOVED);
1005 : :
1006 : : shift_metadata:
1007 : : /* shift any metadata after the bat to make room for new bat sectors */
1008 : 0 : err = vhd_shift_metadata(journal, eob, bat_needed, map_needed);
1009 [ # # ]: 0 : if (err)
1010 : : return err;
1011 : :
1012 [ # # ]: 0 : TEST_FAIL_AT(FAIL_RESIZE_METADATA_MOVED);
1013 : :
1014 : : add_entries:
1015 : 0 : return vhd_add_bat_entries(journal, blocks);
1016 : : }
1017 : :
1018 : : static int
1019 : 0 : vhd_dynamic_resize(vhd_journal_t *journal, uint64_t size)
1020 : : {
1021 : : int err;
1022 : : vhd_context_t *vhd;
1023 : : uint64_t cur_secs, new_secs;
1024 : :
1025 : 0 : vhd = &journal->vhd;
1026 : 0 : cur_secs = vhd->footer.curr_size >> VHD_SECTOR_SHIFT;
1027 : 0 : new_secs = size << (20 - VHD_SECTOR_SHIFT);
1028 : :
1029 [ # # ]: 0 : if (cur_secs == new_secs)
1030 : : return 0;
1031 : :
1032 : 0 : err = vhd_get_header(vhd);
1033 [ # # ]: 0 : if (err)
1034 : : return err;
1035 : :
1036 : 0 : err = vhd_get_bat(vhd);
1037 [ # # ]: 0 : if (err)
1038 : : return err;
1039 : :
1040 [ # # ]: 0 : if (vhd_has_batmap(vhd)) {
1041 : 0 : err = vhd_get_batmap(vhd);
1042 [ # # ]: 0 : if (err)
1043 : : return err;
1044 : : }
1045 : :
1046 [ # # ]: 0 : if (cur_secs > new_secs)
1047 : 0 : err = vhd_dynamic_shrink(journal, cur_secs - new_secs);
1048 : : else
1049 : 0 : err = vhd_dynamic_grow(journal, new_secs - cur_secs);
1050 : :
1051 : 0 : return err;
1052 : : }
1053 : :
1054 : : static int
1055 : 0 : vhd_util_resize_check_creator(const char *name)
1056 : : {
1057 : : int err;
1058 : : vhd_context_t vhd;
1059 : :
1060 : 0 : err = vhd_open(&vhd, name, VHD_OPEN_RDONLY | VHD_OPEN_STRICT);
1061 [ # # ]: 0 : if (err) {
1062 : : printf("error opening %s: %d\n", name, err);
1063 : 0 : return err;
1064 : : }
1065 : :
1066 [ # # ]: 0 : if (!vhd_creator_tapdisk(&vhd)) {
1067 : : printf("%s not created by xen; resize not supported\n", name);
1068 : : err = -EINVAL;
1069 : : }
1070 : :
1071 : 0 : vhd_close(&vhd);
1072 : : return err;
1073 : : }
1074 : :
1075 : : static int
1076 : 0 : vhd_dynamic_grow_fast(const char *name, uint64_t bytes)
1077 : : {
1078 : : vhd_context_t vhd;
1079 : : uint64_t blks, size;
1080 : : int err;
1081 : :
1082 : 0 : err = vhd_open(&vhd, name, VHD_OPEN_RDWR);
1083 [ # # ]: 0 : if (err)
1084 : 0 : return err;
1085 : :
1086 : 0 : err = vhd_get_bat(&vhd);
1087 [ # # ]: 0 : if (err)
1088 : : goto done;
1089 : :
1090 [ # # ]: 0 : if (vhd_has_batmap(&vhd)) {
1091 : 0 : err = vhd_get_batmap(&vhd);
1092 [ # # ]: 0 : if (err)
1093 : : goto done;
1094 : : }
1095 : :
1096 : 0 : blks = (bytes + VHD_BLOCK_SIZE - 1) >> VHD_BLOCK_SHIFT;
1097 : 0 : size = blks << VHD_BLOCK_SHIFT;
1098 [ # # ]: 0 : if (size < vhd.footer.curr_size) {
1099 : 0 : printf("%s: size (%"PRIu64") < curr size (%"PRIu64")\n",
1100 : : name, size, vhd.footer.curr_size);
1101 : : err = -EINVAL;
1102 : : goto done;
1103 : : }
1104 [ # # ]: 0 : if (size == vhd.footer.curr_size)
1105 : : goto done;
1106 : :
1107 : 0 : err = vhd_set_virt_size(&vhd, size);
1108 : :
1109 : : done:
1110 : 0 : vhd_close(&vhd);
1111 : : return err;
1112 : : }
1113 : :
1114 : : int
1115 : 0 : vhd_util_resize(int argc, char **argv)
1116 : : {
1117 : : char *name, *jname;
1118 : : uint64_t size;
1119 : : int fast, c, err, jerr;
1120 : : vhd_journal_t journal;
1121 : 0 : vhd_context_t *vhd;
1122 : :
1123 : 0 : err = -EINVAL;
1124 : 0 : size = 0;
1125 : 0 : name = NULL;
1126 : 0 : jname = NULL;
1127 : 0 : fast = 0;
1128 : :
1129 : 0 : optind = 0;
1130 [ # # ]: 0 : while ((c = getopt(argc, argv, "n:s:j:fh")) != -1) {
1131 [ # # # # : 0 : switch (c) {
# ]
1132 : : case 'n':
1133 : 0 : name = optarg;
1134 : 0 : break;
1135 : : case 'j':
1136 : 0 : jname = optarg;
1137 : 0 : break;
1138 : : case 'f':
1139 : : fast = 1;
1140 : : break;
1141 : : case 's':
1142 : 0 : err = 0;
1143 : 0 : size = strtoull(optarg, NULL, 10);
1144 : 0 : break;
1145 : : case 'h':
1146 : : default:
1147 : : goto usage;
1148 : : }
1149 : : }
1150 : :
1151 [ # # ][ # # ]: 0 : if (err || !name || (!jname && !fast) || argc != optind)
[ # # ]
1152 : : goto usage;
1153 : :
1154 [ # # ]: 0 : if (jname && fast)
1155 : : goto usage;
1156 : :
1157 : 0 : err = vhd_util_resize_check_creator(name);
1158 [ # # ]: 0 : if (err)
1159 : 0 : return err;
1160 : :
1161 : 0 : libvhd_set_log_level(1);
1162 : :
1163 [ # # ]: 0 : if (fast)
1164 : 0 : return vhd_dynamic_grow_fast(name, size << 20);
1165 : :
1166 : 0 : err = vhd_journal_create(&journal, name, jname);
1167 [ # # ]: 0 : if (err) {
1168 : : printf("creating journal failed: %d\n", err);
1169 : : return err;
1170 : : }
1171 : :
1172 : 0 : vhd = &journal.vhd;
1173 : :
1174 : 0 : err = vhd_get_footer(vhd);
1175 [ # # ]: 0 : if (err)
1176 : : goto out;
1177 : :
1178 [ # # ]: 0 : TEST_FAIL_AT(FAIL_RESIZE_BEGIN);
1179 : :
1180 [ # # ]: 0 : if (vhd_type_dynamic(vhd))
1181 : 0 : err = vhd_dynamic_resize(&journal, size);
1182 : : else
1183 : 0 : err = vhd_fixed_resize(&journal, size);
1184 : :
1185 [ # # ]: 0 : TEST_FAIL_AT(FAIL_RESIZE_END);
1186 : :
1187 : : out:
1188 [ # # ]: 0 : if (err) {
1189 : : printf("resize failed: %d\n", err);
1190 : 0 : jerr = vhd_journal_revert(&journal);
1191 : : } else
1192 : 0 : jerr = vhd_journal_commit(&journal);
1193 : :
1194 [ # # ]: 0 : if (jerr) {
1195 : : printf("closing journal failed: %d\n", jerr);
1196 : 0 : vhd_journal_close(&journal);
1197 : : } else
1198 : 0 : vhd_journal_remove(&journal);
1199 : :
1200 [ # # ]: 0 : return (err ? : jerr);
1201 : :
1202 : : usage:
1203 : : printf("options: <-n name> <-s size (in MB)> (<-j journal>|<-f fast>) "
1204 : : "[-h help]\n\n"
1205 : : "The resize operation can only be performed offline "
1206 : : "and must be journaled because resizing the metadata "
1207 : : "might require moving data blocks. However, if a "
1208 : : "VHD was created with -S <msize> option (during "
1209 : : "vhd-util create/snapshot), which preallocates the "
1210 : : "metadata for growing the VHD up to size <msize>, then "
1211 : : "resizing such a VHD up to <msize> can be performed "
1212 : : "online without journaling (-f option).\n");
1213 : : return -EINVAL;
1214 : : }
|