ia64/xen-unstable

view tools/blktap2/vhd/lib/vhd-util-resize.c @ 19817:b7f73a7f3078

blktap2: portability fixes for NetBSD

- Use standard off_t and lseek() instead of non-portable off64_t and
lseek64()
- Use uuid API as documented in DCE 1.1 RPC specification
- Add NetBSD implementation for blk_getimagesize() and
blk_getsectorsize()
- Use blk_getimagesize() and blk_getsectorsize()
- Fix uuid header check

Signed-off-by: Christoph Egger <Christoph.Egger@amd.com>
Signed-off-by: Keir Fraser <keir.fraser@citrix.com>
author Keir Fraser <keir.fraser@citrix.com>
date Tue Jun 23 17:24:14 2009 +0100 (2009-06-23)
parents 1c627434605e
children
line source
1 /* Copyright (c) 2008, XenSource Inc.
2 * All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are met:
6 * * Redistributions of source code must retain the above copyright
7 * notice, this list of conditions and the following disclaimer.
8 * * Redistributions in binary form must reproduce the above copyright
9 * notice, this list of conditions and the following disclaimer in the
10 * documentation and/or other materials provided with the distribution.
11 * * Neither the name of XenSource Inc. nor the names of its contributors
12 * may be used to endorse or promote products derived from this software
13 * without specific prior written permission.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
16 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
17 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
18 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
19 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
20 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
22 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
23 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
24 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27 #include <errno.h>
28 #include <fcntl.h>
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <unistd.h>
32 #include <string.h>
33 #include <syslog.h>
34 #include <inttypes.h>
35 #include <sys/mman.h>
37 #include "libvhd-journal.h"
39 #if 1
40 #define DFPRINTF(_f, _a...) fprintf(stdout, _f, ##_a)
41 #else
42 #define DFPRINTF(_f, _a...) ((void)0)
43 #endif
45 #define EPRINTF(_f, _a...) \
46 do { \
47 syslog(LOG_INFO, "%s: " _f, __func__, ##_a); \
48 DFPRINTF(_f, _a); \
49 } while (0)
51 typedef struct vhd_block {
52 uint32_t block;
53 uint32_t offset;
54 } vhd_block_t;
56 TEST_FAIL_EXTERN_VARS;
58 static inline uint32_t
59 secs_to_blocks_down(vhd_context_t *vhd, uint64_t secs)
60 {
61 return secs / vhd->spb;
62 }
64 static uint32_t
65 secs_to_blocks_up(vhd_context_t *vhd, uint64_t secs)
66 {
67 uint32_t blocks;
69 blocks = secs / vhd->spb;
70 if (secs % vhd->spb)
71 blocks++;
73 return blocks;
74 }
76 static int
77 vhd_fixed_shrink(vhd_journal_t *journal, uint64_t secs)
78 {
79 int err;
80 uint64_t new_eof;
81 vhd_context_t *vhd;
83 vhd = &journal->vhd;
85 new_eof = vhd->footer.curr_size - vhd_sectors_to_bytes(secs);
86 if (new_eof <= sizeof(vhd_footer_t))
87 return -EINVAL;
89 err = ftruncate(vhd->fd, new_eof);
90 if (err)
91 return errno;
93 vhd->footer.curr_size = new_eof;
94 return vhd_write_footer(vhd, &vhd->footer);
95 }
97 static int
98 vhd_write_zeros(vhd_journal_t *journal, off_t off, uint64_t size)
99 {
100 int err;
101 char *buf;
102 vhd_context_t *vhd;
103 uint64_t bytes, map;
105 vhd = &journal->vhd;
106 map = MIN(size, VHD_BLOCK_SIZE);
108 err = vhd_seek(vhd, off, SEEK_SET);
109 if (err)
110 return err;
112 buf = mmap(0, map, PROT_READ, MAP_SHARED | MAP_ANON, -1, 0);
113 if (buf == MAP_FAILED)
114 return -errno;
116 do {
117 bytes = MIN(size, map);
119 err = vhd_write(vhd, buf, bytes);
120 if (err)
121 break;
123 size -= bytes;
124 } while (size);
126 munmap(buf, map);
128 return err;
129 }
131 static int
132 vhd_fixed_grow(vhd_journal_t *journal, uint64_t secs)
133 {
134 int err;
135 vhd_context_t *vhd;
136 uint64_t size, eof, new_eof;
138 size = vhd_sectors_to_bytes(secs);
139 vhd = &journal->vhd;
141 err = vhd_seek(vhd, 0, SEEK_END);
142 if (err)
143 goto out;
145 eof = vhd_position(vhd);
146 if (eof == (off_t)-1) {
147 err = -errno;
148 goto out;
149 }
151 err = vhd_write_zeros(journal, eof - sizeof(vhd_footer_t), size);
152 if (err)
153 goto out;
155 new_eof = eof + size;
156 err = vhd_seek(vhd, new_eof, SEEK_SET);
157 if (err)
158 goto out;
160 vhd->footer.curr_size += size;
161 err = vhd_write_footer(vhd, &vhd->footer);
162 if (err)
163 goto out;
165 err = 0;
167 out:
168 return err;
169 }
171 static int
172 vhd_fixed_resize(vhd_journal_t *journal, uint64_t size)
173 {
174 int err;
175 vhd_context_t *vhd;
176 uint64_t cur_secs, new_secs;
178 vhd = &journal->vhd;
179 cur_secs = vhd->footer.curr_size >> VHD_SECTOR_SHIFT;
180 new_secs = size << (20 - VHD_SECTOR_SHIFT);
182 if (cur_secs == new_secs)
183 return 0;
184 else if (cur_secs > new_secs)
185 err = vhd_fixed_shrink(journal, cur_secs - new_secs);
186 else
187 err = vhd_fixed_grow(journal, new_secs - cur_secs);
189 return err;
190 }
192 static inline void
193 swap(vhd_block_t *list, int a, int b)
194 {
195 vhd_block_t tmp;
197 tmp = list[a];
198 list[a] = list[b];
199 list[b] = tmp;
200 }
202 static int
203 partition(vhd_block_t *list, int left, int right, int pidx)
204 {
205 int i, sidx;
206 long long pval;
208 sidx = left;
209 pval = list[pidx].offset;
210 swap(list, pidx, right);
212 for (i = left; i < right; i++)
213 if (list[i].offset >= pval) {
214 swap(list, sidx, i);
215 ++sidx;
216 }
218 swap(list, right, sidx);
219 return sidx;
220 }
222 static void
223 quicksort(vhd_block_t *list, int left, int right)
224 {
225 int pidx, new_pidx;
227 if (right < left)
228 return;
230 pidx = left;
231 new_pidx = partition(list, left, right, pidx);
232 quicksort(list, left, new_pidx - 1);
233 quicksort(list, new_pidx + 1, right);
234 }
236 static int
237 vhd_move_block(vhd_journal_t *journal, uint32_t src, off_t offset)
238 {
239 int err;
240 char *buf;
241 size_t size;
242 vhd_context_t *vhd;
243 off_t off, src_off;
245 buf = NULL;
246 vhd = &journal->vhd;
247 off = offset;
248 size = vhd_sectors_to_bytes(vhd->bm_secs);
249 src_off = vhd->bat.bat[src];
251 if (src_off == DD_BLK_UNUSED)
252 return -EINVAL;
253 src_off = vhd_sectors_to_bytes(src_off);
255 err = vhd_journal_add_block(journal, src,
256 VHD_JOURNAL_DATA | VHD_JOURNAL_METADATA);
257 if (err)
258 goto out;
260 err = vhd_read_bitmap(vhd, src, &buf);
261 if (err)
262 goto out;
264 err = vhd_seek(vhd, off, SEEK_SET);
265 if (err)
266 goto out;
268 err = vhd_write(vhd, buf, size);
269 if (err)
270 goto out;
272 free(buf);
273 buf = NULL;
274 off += size;
275 size = vhd_sectors_to_bytes(vhd->spb);
277 err = vhd_read_block(vhd, src, &buf);
278 if (err)
279 goto out;
281 err = vhd_seek(vhd, off, SEEK_SET);
282 if (err)
283 goto out;
285 err = vhd_write(vhd, buf, size);
286 if (err)
287 goto out;
289 vhd->bat.bat[src] = offset >> VHD_SECTOR_SHIFT;
291 err = vhd_write_zeros(journal, src_off,
292 vhd_sectors_to_bytes(vhd->bm_secs + vhd->spb));
294 out:
295 free(buf);
296 return err;
297 }
299 static int
300 vhd_clobber_block(vhd_journal_t *journal, uint32_t src, uint32_t dest)
301 {
302 int err;
303 off_t off;
304 vhd_context_t *vhd;
306 vhd = &journal->vhd;
307 off = vhd_sectors_to_bytes(vhd->bat.bat[dest]);
309 err = vhd_journal_add_block(journal, dest,
310 VHD_JOURNAL_DATA | VHD_JOURNAL_METADATA);
311 if (err)
312 return err;
314 err = vhd_move_block(journal, src, off);
315 if (err)
316 return err;
318 vhd->bat.bat[dest] = DD_BLK_UNUSED;
320 return 0;
321 }
323 /*
324 * remove a list of blocks from the vhd file
325 * if a block to be removed:
326 * - resides at the end of the file: simply clear its bat entry
327 * - resides elsewhere: move the last block in the file into its position
328 * and update the bat to reflect this
329 */
330 static int
331 vhd_defrag_shrink(vhd_journal_t *journal,
332 vhd_block_t *original_free_list, int free_cnt)
333 {
334 vhd_context_t *vhd;
335 int i, j, free_idx, err;
336 vhd_block_t *blocks, *free_list;
338 err = 0;
339 blocks = NULL;
340 free_list = NULL;
341 vhd = &journal->vhd;
343 blocks = malloc(vhd->bat.entries * sizeof(vhd_block_t));
344 if (!blocks) {
345 err = -ENOMEM;
346 goto out;
347 }
349 free_list = malloc(free_cnt * sizeof(vhd_block_t));
350 if (!free_list) {
351 err = -ENOMEM;
352 goto out;
353 }
355 for (i = 0; i < vhd->bat.entries; i++) {
356 blocks[i].block = i;
357 blocks[i].offset = vhd->bat.bat[i];
358 }
360 memcpy(free_list, original_free_list,
361 free_cnt * sizeof(vhd_block_t));
363 /* sort both the to-free list and the bat list
364 * in order of descending file offset */
365 quicksort(free_list, 0, free_cnt - 1);
366 quicksort(blocks, 0, vhd->bat.entries - 1);
368 for (i = 0, free_idx = 0;
369 i < vhd->bat.entries && free_idx < free_cnt; i++) {
370 vhd_block_t *b = blocks + i;
372 if (b->offset == DD_BLK_UNUSED)
373 continue;
375 for (j = free_idx; j < free_cnt; j++)
376 if (b->block == free_list[j].block) {
377 /* the last block in the file is in the list of
378 * blocks to remove; no need to shuffle the
379 * data -- just clear the bat entry */
380 vhd->bat.bat[free_list[j].block] = DD_BLK_UNUSED;
381 free_idx++;
382 continue;
383 }
385 err = vhd_clobber_block(journal, b->block,
386 free_list[free_idx++].block);
387 if (err)
388 goto out;
389 }
391 /* clear any bat entries for blocks we did not shuffle */
392 for (i = free_idx; i < free_cnt; i++)
393 vhd->bat.bat[free_list[i].block] = DD_BLK_UNUSED;
395 out:
396 free(blocks);
397 free(free_list);
399 return err;
400 }
402 static int
403 vhd_clear_bat_entries(vhd_journal_t *journal, uint32_t entries)
404 {
405 int i, err;
406 vhd_context_t *vhd;
407 off_t orig_map_off, new_map_off;
408 uint32_t orig_entries, new_entries;
410 vhd = &journal->vhd;
411 orig_entries = vhd->header.max_bat_size;
412 new_entries = orig_entries - entries;
414 if (vhd_has_batmap(vhd)) {
415 err = vhd_batmap_header_offset(vhd, &orig_map_off);
416 if (err)
417 return err;
418 }
420 /* update header */
421 vhd->header.max_bat_size = new_entries;
422 err = vhd_write_header(vhd, &vhd->header);
423 if (err)
424 return err;
426 /* update footer */
427 vhd->footer.curr_size = (uint64_t)new_entries * vhd->header.block_size;
428 vhd->footer.geometry = vhd_chs(vhd->footer.curr_size);
429 err = vhd_write_footer(vhd, &vhd->footer);
430 if (err)
431 return err;
433 /* update bat -- we don't reclaim space, just clear entries */
434 for (i = new_entries; i < orig_entries; i++)
435 vhd->bat.bat[i] = 0;
437 err = vhd_write_bat(vhd, &vhd->bat);
438 if (err)
439 return err;
441 /* update this after write_bat so the end of the bat is zeored */
442 vhd->bat.entries = new_entries;
444 if (!vhd_has_batmap(vhd))
445 return 0;
447 /* zero out old batmap header if new header has moved */
448 err = vhd_batmap_header_offset(vhd, &new_map_off);
449 if (err)
450 return err;
452 if (orig_map_off != new_map_off) {
453 size_t size;
455 size = vhd_bytes_padded(sizeof(struct dd_batmap_hdr));
457 err = vhd_write_zeros(journal, orig_map_off, size);
458 if (err)
459 return err;
460 }
462 /* update batmap -- clear entries for freed blocks */
463 for (i = new_entries; i < orig_entries; i++)
464 vhd_batmap_clear(vhd, &vhd->batmap, i);
466 err = vhd_write_batmap(vhd, &vhd->batmap);
467 if (err)
468 return err;
470 return 0;
471 }
473 static int
474 vhd_dynamic_shrink(vhd_journal_t *journal, uint64_t secs)
475 {
476 off_t eof;
477 uint32_t blocks;
478 vhd_context_t *vhd;
479 int i, j, err, free_cnt;
480 struct vhd_block *free_list;
482 printf("dynamic shrink not fully implemented\n");
483 return -ENOSYS;
485 eof = 0;
486 free_cnt = 0;
487 free_list = NULL;
488 vhd = &journal->vhd;
490 blocks = secs_to_blocks_down(vhd, secs);
491 if (blocks == 0)
492 return 0;
494 if (vhd_has_batmap(vhd)) {
495 err = vhd_get_batmap(vhd);
496 if (err)
497 return err;
498 }
500 free_list = malloc(blocks * sizeof(struct vhd_block));
501 if (!free_list)
502 return -ENOMEM;
504 for (i = vhd->bat.entries - 1, j = 0; i >= 0 && j < blocks; i--, j++) {
505 uint32_t blk = vhd->bat.bat[i];
507 if (blk != DD_BLK_UNUSED) {
508 free_list[free_cnt].block = i;
509 free_list[free_cnt].offset = blk;
510 free_cnt++;
511 }
512 }
514 if (free_cnt) {
515 err = vhd_defrag_shrink(journal, free_list, free_cnt);
516 if (err)
517 goto out;
518 }
520 err = vhd_clear_bat_entries(journal, blocks);
521 if (err)
522 goto out;
524 /* remove data beyond footer */
525 err = vhd_end_of_data(vhd, &eof);
526 if (err)
527 goto out;
529 err = ftruncate(vhd->fd, eof + sizeof(vhd_footer_t));
530 if (err) {
531 err = -errno;
532 goto out;
533 }
535 err = 0;
537 out:
538 free(free_list);
539 return err;
540 }
542 static inline void
543 vhd_first_data_block(vhd_context_t *vhd, vhd_block_t *block)
544 {
545 int i;
546 uint32_t blk;
548 memset(block, 0, sizeof(vhd_block_t));
550 for (i = 0; i < vhd->bat.entries; i++) {
551 blk = vhd->bat.bat[i];
553 if (blk != DD_BLK_UNUSED) {
554 if (!block->offset || blk < block->offset) {
555 block->block = i;
556 block->offset = blk;
557 }
558 }
559 }
560 }
562 static inline uint32_t
563 vhd_next_block_offset(vhd_context_t *vhd)
564 {
565 int i;
566 uint32_t blk, end, spp, next;
568 next = 0;
569 spp = getpagesize() >> VHD_SECTOR_SHIFT;
571 for (i = 0; i < vhd->bat.entries; i++) {
572 blk = vhd->bat.bat[i];
574 if (blk != DD_BLK_UNUSED) {
575 end = blk + vhd->spb + vhd->bm_secs;
576 next = MAX(next, end);
577 }
578 }
580 return next;
581 }
583 static inline int
584 in_range(off_t off, off_t start, off_t size)
585 {
586 return (start < off && start + size > off);
587 }
589 #define SKIP_HEADER 0x01
590 #define SKIP_BAT 0x02
591 #define SKIP_BATMAP 0x04
592 #define SKIP_PLOC 0x08
593 #define SKIP_DATA 0x10
595 static inline int
596 skip_check(int mode, int type)
597 {
598 return mode & type;
599 }
601 static int
602 vhd_check_for_clobber(vhd_context_t *vhd, off_t off, int mode)
603 {
604 int i, n;
605 char *msg;
606 size_t size;
607 vhd_block_t fb;
608 vhd_parent_locator_t *loc;
610 msg = NULL;
612 if (!vhd_type_dynamic(vhd))
613 return 0;
615 if (off < VHD_SECTOR_SIZE) {
616 msg = "backup footer";
617 goto fail;
618 }
620 if (!skip_check(mode, SKIP_HEADER))
621 if (in_range(off,
622 vhd->footer.data_offset, sizeof(vhd_header_t))) {
623 msg = "header";
624 goto fail;
625 }
627 if (!skip_check(mode, SKIP_BAT))
628 if (in_range(off, vhd->header.table_offset,
629 vhd_bytes_padded(vhd->header.max_bat_size *
630 sizeof(uint32_t)))) {
631 msg = "bat";
632 goto fail;
633 }
635 if (!skip_check(mode, SKIP_BATMAP))
636 if (vhd_has_batmap(vhd) &&
637 in_range(off, vhd->batmap.header.batmap_offset,
638 vhd_bytes_padded(vhd->batmap.header.batmap_size))) {
639 msg = "batmap";
640 goto fail;
641 }
643 if (!skip_check(mode, SKIP_PLOC)) {
644 n = sizeof(vhd->header.loc) / sizeof(vhd_parent_locator_t);
645 for (i = 0; i < n; i++) {
646 loc = vhd->header.loc + i;
647 if (loc->code == PLAT_CODE_NONE)
648 continue;
650 size = vhd_parent_locator_size(loc);
651 if (in_range(off, loc->data_offset, size)) {
652 msg = "parent locator";
653 goto fail;
654 }
655 }
656 }
658 if (!skip_check(mode, SKIP_DATA)) {
659 vhd_first_data_block(vhd, &fb);
660 if (fb.offset && in_range(off,
661 vhd_sectors_to_bytes(fb.offset),
662 VHD_BLOCK_SIZE)) {
663 msg = "data block";
664 goto fail;
665 }
666 }
668 return 0;
670 fail:
671 EPRINTF("write to 0x%08"PRIx64" would clobber %s\n", off, msg);
672 return -EINVAL;
673 }
675 /*
676 * take any metadata after the bat (@eob) and shift it
677 */
678 static int
679 vhd_shift_metadata(vhd_journal_t *journal, off_t eob,
680 size_t bat_needed, size_t map_needed)
681 {
682 int i, n, err;
683 vhd_context_t *vhd;
684 size_t size_needed;
685 char *buf, **locators;
686 vhd_parent_locator_t *loc;
688 vhd = &journal->vhd;
689 size_needed = bat_needed + map_needed;
691 n = sizeof(vhd->header.loc) / sizeof(vhd_parent_locator_t);
693 locators = calloc(n, sizeof(char *));
694 if (!locators)
695 return -ENOMEM;
697 for (i = 0; i < n; i++) {
698 size_t size;
700 loc = vhd->header.loc + i;
701 if (loc->code == PLAT_CODE_NONE)
702 continue;
704 if (loc->data_offset < eob)
705 continue;
707 size = vhd_parent_locator_size(loc);
708 err = posix_memalign((void **)&buf, VHD_SECTOR_SIZE, size);
709 if (err) {
710 err = -err;
711 buf = NULL;
712 goto out;
713 }
715 err = vhd_seek(vhd, loc->data_offset, SEEK_SET);
716 if (err)
717 goto out;
719 err = vhd_read(vhd, buf, size);
720 if (err)
721 goto out;
723 locators[i] = buf;
724 }
726 for (i = 0; i < n; i++) {
727 off_t off;
728 size_t size;
730 if (!locators[i])
731 continue;
733 loc = vhd->header.loc + i;
734 off = loc->data_offset + size_needed;
735 size = vhd_parent_locator_size(loc);
737 if (vhd_check_for_clobber(vhd, off + size, SKIP_PLOC)) {
738 EPRINTF("%s: shifting locator %d would clobber data\n",
739 vhd->file, i);
740 return -EINVAL;
741 }
743 err = vhd_seek(vhd, off, SEEK_SET);
744 if (err)
745 goto out;
747 err = vhd_write(vhd, locators[i], size);
748 if (err)
749 goto out;
751 free(locators[i]);
752 locators[i] = NULL;
753 loc->data_offset = off;
755 /* write the new header after writing the new bat */
756 }
758 if (vhd_has_batmap(vhd) && vhd->batmap.header.batmap_offset > eob) {
759 vhd->batmap.header.batmap_offset += bat_needed;
761 /* write the new batmap after writing the new bat */
762 }
764 err = 0;
766 out:
767 for (i = 0; i < n; i++)
768 free(locators[i]);
769 free(locators);
771 return err;
772 }
774 static int
775 vhd_add_bat_entries(vhd_journal_t *journal, int entries)
776 {
777 int i, err;
778 off_t off;
779 vhd_bat_t new_bat;
780 vhd_context_t *vhd;
781 uint32_t new_entries;
782 vhd_batmap_t new_batmap;
783 uint64_t bat_size, new_bat_size, map_size, new_map_size;
785 vhd = &journal->vhd;
786 new_entries = vhd->header.max_bat_size + entries;
788 bat_size = vhd_bytes_padded(vhd->header.max_bat_size *
789 sizeof(uint32_t));
790 new_bat_size = vhd_bytes_padded(new_entries * sizeof(uint32_t));
792 map_size = vhd_bytes_padded((vhd->header.max_bat_size + 7) >> 3);
793 new_map_size = vhd_bytes_padded((new_entries + 7) >> 3);
795 off = vhd->header.table_offset + new_bat_size;
796 if (vhd_check_for_clobber(vhd, off, SKIP_BAT | SKIP_BATMAP)) {
797 EPRINTF("%s: writing new bat of 0x%"PRIx64" bytes "
798 "at 0x%08"PRIx64" would clobber data\n",
799 vhd->file, new_bat_size, vhd->header.table_offset);
800 return -EINVAL;
801 }
803 if (vhd_has_batmap(vhd)) {
804 off = vhd->batmap.header.batmap_offset + new_map_size;
805 if (vhd_check_for_clobber(vhd, off, 0)) {
806 EPRINTF("%s: writing new batmap of 0x%"PRIx64" bytes"
807 " at 0x%08"PRIx64" would clobber data\n", vhd->file,
808 new_map_size, vhd->batmap.header.batmap_offset);
809 return -EINVAL;
810 }
811 }
813 /* update header */
814 vhd->header.max_bat_size = new_entries;
815 err = vhd_write_header(vhd, &vhd->header);
816 if (err)
817 return err;
819 /* update footer */
820 vhd->footer.curr_size = (uint64_t)new_entries * vhd->header.block_size;
821 vhd->footer.geometry = vhd_chs(vhd->footer.curr_size);
822 vhd->footer.checksum = vhd_checksum_footer(&vhd->footer);
823 err = vhd_write_footer(vhd, &vhd->footer);
824 if (err)
825 return err;
827 /* allocate new bat */
828 err = posix_memalign((void **)&new_bat.bat, VHD_SECTOR_SIZE, new_bat_size);
829 if (err)
830 return -err;
832 new_bat.spb = vhd->bat.spb;
833 new_bat.entries = new_entries;
834 memcpy(new_bat.bat, vhd->bat.bat, bat_size);
835 for (i = vhd->bat.entries; i < new_entries; i++)
836 new_bat.bat[i] = DD_BLK_UNUSED;
838 /* write new bat */
839 err = vhd_write_bat(vhd, &new_bat);
840 if (err) {
841 free(new_bat.bat);
842 return err;
843 }
845 /* update in-memory bat */
846 free(vhd->bat.bat);
847 vhd->bat = new_bat;
849 if (!vhd_has_batmap(vhd))
850 return 0;
852 /* allocate new batmap */
853 err = posix_memalign((void **)&new_batmap.map,
854 VHD_SECTOR_SIZE, new_map_size);
855 if (err)
856 return err;
858 new_batmap.header = vhd->batmap.header;
859 new_batmap.header.batmap_size = secs_round_up_no_zero(new_map_size);
860 memcpy(new_batmap.map, vhd->batmap.map, map_size);
861 memset(new_batmap.map + map_size, 0, new_map_size - map_size);
863 /* write new batmap */
864 err = vhd_write_batmap(vhd, &new_batmap);
865 if (err) {
866 free(new_batmap.map);
867 return err;
868 }
870 /* update in-memory batmap */
871 free(vhd->batmap.map);
872 vhd->batmap = new_batmap;
874 return 0;
875 }
877 static int
878 vhd_dynamic_grow(vhd_journal_t *journal, uint64_t secs)
879 {
880 int i, err;
881 off_t eob, eom;
882 vhd_context_t *vhd;
883 vhd_block_t first_block;
884 uint64_t blocks, size_needed;
885 uint64_t bat_needed, bat_size, bat_avail, bat_bytes, bat_secs;
886 uint64_t map_needed, map_size, map_avail, map_bytes, map_secs;
888 vhd = &journal->vhd;
890 size_needed = 0;
891 bat_needed = 0;
892 map_needed = 0;
894 /* number of vhd blocks to add */
895 blocks = secs_to_blocks_up(vhd, secs);
897 /* size in bytes needed for new bat entries */
898 bat_needed = blocks * sizeof(uint32_t);
899 map_needed = (blocks >> 3) + 1;
901 /* available bytes in current bat */
902 bat_bytes = vhd->header.max_bat_size * sizeof(uint32_t);
903 bat_secs = secs_round_up_no_zero(bat_bytes);
904 bat_size = vhd_sectors_to_bytes(bat_secs);
905 bat_avail = bat_size - bat_bytes;
907 if (vhd_has_batmap(vhd)) {
908 /* avaliable bytes in current batmap */
909 map_bytes = (vhd->header.max_bat_size + 7) >> 3;
910 map_secs = vhd->batmap.header.batmap_size;
911 map_size = vhd_sectors_to_bytes(map_secs);
912 map_avail = map_size - map_bytes;
913 } else {
914 map_needed = 0;
915 map_avail = 0;
916 }
918 /* we have enough space already; just extend the bat */
919 if (bat_needed <= bat_avail && map_needed <= map_avail)
920 goto add_entries;
922 /* we need to add new sectors to the bat */
923 if (bat_needed > bat_avail) {
924 bat_needed -= bat_avail;
925 bat_needed = vhd_bytes_padded(bat_needed);
926 } else
927 bat_needed = 0;
929 /* we need to add new sectors to the batmap */
930 if (map_needed > map_avail) {
931 map_needed -= map_avail;
932 map_needed = vhd_bytes_padded(map_needed);
933 } else
934 map_needed = 0;
936 /* how many additional bytes do we need? */
937 size_needed = bat_needed + map_needed;
939 /* calculate space between end of headers and beginning of data */
940 err = vhd_end_of_headers(vhd, &eom);
941 if (err)
942 return err;
944 eob = vhd->header.table_offset + vhd_sectors_to_bytes(bat_secs);
945 vhd_first_data_block(vhd, &first_block);
947 /* no blocks allocated; just shift post-bat metadata */
948 if (!first_block.offset)
949 goto shift_metadata;
951 /*
952 * not enough space --
953 * move vhd data blocks to the end of the file to make room
954 */
955 do {
956 off_t new_off, bm_size, gap_size;
958 new_off = vhd_sectors_to_bytes(vhd_next_block_offset(vhd));
960 /* data region of segment should begin on page boundary */
961 bm_size = vhd_sectors_to_bytes(vhd->bm_secs);
962 if ((new_off + bm_size) % 4096) {
963 gap_size = 4096 - ((new_off + bm_size) % 4096);
965 err = vhd_write_zeros(journal, new_off, gap_size);
966 if (err)
967 return err;
969 new_off += gap_size;
970 }
972 err = vhd_move_block(journal, first_block.block, new_off);
973 if (err)
974 return err;
976 vhd_first_data_block(vhd, &first_block);
978 } while (eom + size_needed >= vhd_sectors_to_bytes(first_block.offset));
980 TEST_FAIL_AT(FAIL_RESIZE_DATA_MOVED);
982 shift_metadata:
983 /* shift any metadata after the bat to make room for new bat sectors */
984 err = vhd_shift_metadata(journal, eob, bat_needed, map_needed);
985 if (err)
986 return err;
988 TEST_FAIL_AT(FAIL_RESIZE_METADATA_MOVED);
990 add_entries:
991 return vhd_add_bat_entries(journal, blocks);
992 }
994 static int
995 vhd_dynamic_resize(vhd_journal_t *journal, uint64_t size)
996 {
997 int err;
998 vhd_context_t *vhd;
999 uint64_t cur_secs, new_secs;
1001 vhd = &journal->vhd;
1002 cur_secs = vhd->footer.curr_size >> VHD_SECTOR_SHIFT;
1003 new_secs = size << (20 - VHD_SECTOR_SHIFT);
1005 if (cur_secs == new_secs)
1006 return 0;
1008 err = vhd_get_header(vhd);
1009 if (err)
1010 return err;
1012 err = vhd_get_bat(vhd);
1013 if (err)
1014 return err;
1016 if (vhd_has_batmap(vhd)) {
1017 err = vhd_get_batmap(vhd);
1018 if (err)
1019 return err;
1022 if (cur_secs > new_secs)
1023 err = vhd_dynamic_shrink(journal, cur_secs - new_secs);
1024 else
1025 err = vhd_dynamic_grow(journal, new_secs - cur_secs);
1027 return err;
1030 static int
1031 vhd_util_resize_check_creator(const char *name)
1033 int err;
1034 vhd_context_t vhd;
1036 err = vhd_open(&vhd, name, VHD_OPEN_RDONLY | VHD_OPEN_STRICT);
1037 if (err) {
1038 printf("error opening %s: %d\n", name, err);
1039 return err;
1042 if (!vhd_creator_tapdisk(&vhd)) {
1043 printf("%s not created by xen; resize not supported\n", name);
1044 err = -EINVAL;
1047 vhd_close(&vhd);
1048 return err;
1051 int
1052 vhd_util_resize(int argc, char **argv)
1054 char *name, *jname;
1055 uint64_t size;
1056 int c, err, jerr;
1057 vhd_journal_t journal;
1058 vhd_context_t *vhd;
1060 err = -EINVAL;
1061 size = 0;
1062 name = NULL;
1063 jname = NULL;
1065 optind = 0;
1066 while ((c = getopt(argc, argv, "n:j:s:h")) != -1) {
1067 switch (c) {
1068 case 'n':
1069 name = optarg;
1070 break;
1071 case 'j':
1072 jname = optarg;
1073 break;
1074 case 's':
1075 err = 0;
1076 size = strtoull(optarg, NULL, 10);
1077 break;
1078 case 'h':
1079 default:
1080 goto usage;
1084 if (err || !name || !jname || argc != optind)
1085 goto usage;
1087 err = vhd_util_resize_check_creator(name);
1088 if (err)
1089 return err;
1091 libvhd_set_log_level(1);
1092 err = vhd_journal_create(&journal, name, jname);
1093 if (err) {
1094 printf("creating journal failed: %d\n", err);
1095 return err;
1098 vhd = &journal.vhd;
1100 err = vhd_get_footer(vhd);
1101 if (err)
1102 goto out;
1104 TEST_FAIL_AT(FAIL_RESIZE_BEGIN);
1106 if (vhd_type_dynamic(vhd))
1107 err = vhd_dynamic_resize(&journal, size);
1108 else
1109 err = vhd_fixed_resize(&journal, size);
1111 TEST_FAIL_AT(FAIL_RESIZE_END);
1113 out:
1114 if (err) {
1115 printf("resize failed: %d\n", err);
1116 jerr = vhd_journal_revert(&journal);
1117 } else
1118 jerr = vhd_journal_commit(&journal);
1120 if (jerr) {
1121 printf("closing journal failed: %d\n", jerr);
1122 vhd_journal_close(&journal);
1123 } else
1124 vhd_journal_remove(&journal);
1126 return (err ? : jerr);
1128 usage:
1129 printf("options: <-n name> <-j journal> <-s size (in MB)> [-h help]\n");
1130 return -EINVAL;