1
/* Copyright (c) 2008, XenSource Inc.
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.
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.
42
#include "relative-path.h"
44
static int libvhd_dbg = 0;
47
libvhd_set_log_level(int level)
53
#define VHDLOG(_f, _a...) \
56
syslog(LOG_INFO, "libvhd::%s: "_f, \
62
#ifdef ENABLE_FAILURE_TESTING
63
const char* ENV_VAR_FAIL[NUM_FAIL_TESTS] = {
64
"VHD_UTIL_TEST_FAIL_REPARENT_BEGIN",
65
"VHD_UTIL_TEST_FAIL_REPARENT_LOCATOR",
66
"VHD_UTIL_TEST_FAIL_REPARENT_END",
67
"VHD_UTIL_TEST_FAIL_RESIZE_BEGIN",
68
"VHD_UTIL_TEST_FAIL_RESIZE_DATA_MOVED",
69
"VHD_UTIL_TEST_FAIL_RESIZE_METADATA_MOVED",
70
"VHD_UTIL_TEST_FAIL_RESIZE_END"
72
int TEST_FAIL[NUM_FAIL_TESTS];
73
#endif // ENABLE_FAILURE_TESTING
76
test_bit (volatile char *addr, int nr)
78
return ((addr[nr >> 3] << (nr & 7)) & BIT_MASK) != 0;
82
set_bit (volatile char *addr, int nr)
84
addr[nr >> 3] |= (BIT_MASK >> (nr & 7));
88
clear_bit (volatile char *addr, int nr)
90
addr[nr >> 3] &= ~(BIT_MASK >> (nr & 7));
94
old_test_bit(volatile char *addr, int nr)
96
return (((uint32_t *)addr)[nr >> 5] >> (nr & 31)) & 1;
100
old_set_bit(volatile char *addr, int nr)
102
((uint32_t *)addr)[nr >> 5] |= (1 << (nr & 31));
106
old_clear_bit(volatile char *addr, int nr)
108
((uint32_t *)addr)[nr >> 5] &= ~(1 << (nr & 31));
112
vhd_footer_in(vhd_footer_t *footer)
114
BE32_IN(&footer->features);
115
BE32_IN(&footer->ff_version);
116
BE64_IN(&footer->data_offset);
117
BE32_IN(&footer->timestamp);
118
BE32_IN(&footer->crtr_ver);
119
BE32_IN(&footer->crtr_os);
120
BE64_IN(&footer->orig_size);
121
BE64_IN(&footer->curr_size);
122
BE32_IN(&footer->geometry);
123
BE32_IN(&footer->type);
124
BE32_IN(&footer->checksum);
128
vhd_footer_out(vhd_footer_t *footer)
130
BE32_OUT(&footer->features);
131
BE32_OUT(&footer->ff_version);
132
BE64_OUT(&footer->data_offset);
133
BE32_OUT(&footer->timestamp);
134
BE32_OUT(&footer->crtr_ver);
135
BE32_OUT(&footer->crtr_os);
136
BE64_OUT(&footer->orig_size);
137
BE64_OUT(&footer->curr_size);
138
BE32_OUT(&footer->geometry);
139
BE32_OUT(&footer->type);
140
BE32_OUT(&footer->checksum);
144
vhd_header_in(vhd_header_t *header)
148
BE64_IN(&header->data_offset);
149
BE64_IN(&header->table_offset);
150
BE32_IN(&header->hdr_ver);
151
BE32_IN(&header->max_bat_size);
152
BE32_IN(&header->block_size);
153
BE32_IN(&header->checksum);
154
BE32_IN(&header->prt_ts);
156
n = sizeof(header->loc) / sizeof(vhd_parent_locator_t);
158
for (i = 0; i < n; i++) {
159
BE32_IN(&header->loc[i].code);
160
BE32_IN(&header->loc[i].data_space);
161
BE32_IN(&header->loc[i].data_len);
162
BE64_IN(&header->loc[i].data_offset);
167
vhd_header_out(vhd_header_t *header)
171
BE64_OUT(&header->data_offset);
172
BE64_OUT(&header->table_offset);
173
BE32_OUT(&header->hdr_ver);
174
BE32_OUT(&header->max_bat_size);
175
BE32_OUT(&header->block_size);
176
BE32_OUT(&header->checksum);
177
BE32_OUT(&header->prt_ts);
179
n = sizeof(header->loc) / sizeof(vhd_parent_locator_t);
181
for (i = 0; i < n; i++) {
182
BE32_OUT(&header->loc[i].code);
183
BE32_OUT(&header->loc[i].data_space);
184
BE32_OUT(&header->loc[i].data_len);
185
BE64_OUT(&header->loc[i].data_offset);
190
vhd_batmap_header_in(vhd_batmap_t *batmap)
192
BE64_IN(&batmap->header.batmap_offset);
193
BE32_IN(&batmap->header.batmap_size);
194
BE32_IN(&batmap->header.batmap_version);
195
BE32_IN(&batmap->header.checksum);
199
vhd_batmap_header_out(vhd_batmap_t *batmap)
201
BE64_OUT(&batmap->header.batmap_offset);
202
BE32_OUT(&batmap->header.batmap_size);
203
BE32_OUT(&batmap->header.batmap_version);
204
BE32_OUT(&batmap->header.checksum);
208
vhd_bat_in(vhd_bat_t *bat)
212
for (i = 0; i < bat->entries; i++)
213
BE32_IN(&bat->bat[i]);
217
vhd_bat_out(vhd_bat_t *bat)
221
for (i = 0; i < bat->entries; i++)
222
BE32_OUT(&bat->bat[i]);
226
vhd_checksum_footer(vhd_footer_t *footer)
230
uint32_t checksum, tmp;
233
tmp = footer->checksum;
234
footer->checksum = 0;
236
blob = (unsigned char *)footer;
237
for (i = 0; i < sizeof(vhd_footer_t); i++)
238
checksum += (uint32_t)blob[i];
240
footer->checksum = tmp;
245
vhd_validate_footer(vhd_footer_t *footer)
250
csize = sizeof(footer->cookie);
251
if (memcmp(footer->cookie, HD_COOKIE, csize) != 0 &&
252
memcmp(footer->cookie, VHD_POISON_COOKIE, csize) != 0) {
254
strncpy(buf, footer->cookie, sizeof(buf));
255
buf[sizeof(buf)-1]= '\0';
256
VHDLOG("invalid footer cookie: %s\n", buf);
260
checksum = vhd_checksum_footer(footer);
261
if (checksum != footer->checksum) {
263
* early td-util did not re-calculate
264
* checksum when marking vhds 'hidden'
266
if (footer->hidden &&
267
!strncmp(footer->crtr_app, "tap", 3) &&
268
(footer->crtr_ver == VHD_VERSION(0, 1) ||
269
footer->crtr_ver == VHD_VERSION(1, 1))) {
270
char tmp = footer->hidden;
272
checksum = vhd_checksum_footer(footer);
273
footer->hidden = tmp;
275
if (checksum == footer->checksum)
279
VHDLOG("invalid footer checksum: "
280
"footer = 0x%08x, calculated = 0x%08x\n",
281
footer->checksum, checksum);
289
vhd_checksum_header(vhd_header_t *header)
293
uint32_t checksum, tmp;
296
tmp = header->checksum;
297
header->checksum = 0;
299
blob = (unsigned char *)header;
300
for (i = 0; i < sizeof(vhd_header_t); i++)
301
checksum += (uint32_t)blob[i];
303
header->checksum = tmp;
308
vhd_validate_header(vhd_header_t *header)
313
if (memcmp(header->cookie, DD_COOKIE, 8) != 0) {
315
strncpy(buf, header->cookie, sizeof(buf));
316
buf[sizeof(buf)-1]= '\0';
317
VHDLOG("invalid header cookie: %s\n", buf);
321
if (header->hdr_ver != 0x00010000) {
322
VHDLOG("invalid header version 0x%08x\n", header->hdr_ver);
326
if (header->data_offset != 0xFFFFFFFFFFFFFFFF) {
327
VHDLOG("invalid header data_offset 0x%016"PRIx64"\n",
328
header->data_offset);
332
n = sizeof(header->loc) / sizeof(vhd_parent_locator_t);
333
for (i = 0; i < n; i++)
334
if (vhd_validate_platform_code(header->loc[i].code))
337
checksum = vhd_checksum_header(header);
338
if (checksum != header->checksum) {
339
VHDLOG("invalid header checksum: "
340
"header = 0x%08x, calculated = 0x%08x\n",
341
header->checksum, checksum);
349
vhd_validate_bat(vhd_bat_t *bat)
358
vhd_checksum_batmap(vhd_batmap_t *batmap)
367
n = vhd_sectors_to_bytes(batmap->header.batmap_size);
369
for (i = 0; i < n; i++) {
370
if (batmap->header.batmap_version == VHD_BATMAP_VERSION(1, 1))
371
checksum += (uint32_t)blob[i];
373
checksum += (uint32_t)(unsigned char)blob[i];
380
vhd_validate_batmap_header(vhd_batmap_t *batmap)
382
if (memcmp(batmap->header.cookie, VHD_BATMAP_COOKIE, 8))
385
if (batmap->header.batmap_version > VHD_BATMAP_CURRENT_VERSION)
392
vhd_validate_batmap(vhd_batmap_t *batmap)
399
checksum = vhd_checksum_batmap(batmap);
400
if (checksum != batmap->header.checksum)
407
vhd_batmap_header_offset(vhd_context_t *ctx, off_t *_off)
414
off = ctx->header.table_offset;
415
bat = ctx->header.max_bat_size * sizeof(uint32_t);
416
off += vhd_bytes_padded(bat);
423
vhd_validate_platform_code(uint32_t code)
435
VHDLOG("invalid parent locator code %u\n", code);
441
vhd_parent_locator_count(vhd_context_t *ctx)
443
return (sizeof(ctx->header.loc) / sizeof(vhd_parent_locator_t));
447
vhd_hidden(vhd_context_t *ctx, int *hidden)
453
if (vhd_type_dynamic(ctx) && vhd_creator_tapdisk(ctx) &&
454
(ctx->footer.crtr_ver == VHD_VERSION(0, 1) ||
455
ctx->footer.crtr_ver == VHD_VERSION(1, 1))) {
458
err = vhd_read_footer_at(ctx, ©, 0);
460
VHDLOG("error reading backup footer of %s: %d\n",
464
*hidden = copy.hidden;
466
*hidden = ctx->footer.hidden;
472
vhd_chain_depth(vhd_context_t *ctx, int *depth)
476
vhd_context_t vhd, *cur;
487
if (cur->footer.type != HD_TYPE_DIFF)
490
if (vhd_parent_raw(cur)) {
495
err = vhd_parent_locator_get(cur, &file);
506
err = vhd_open(&vhd, file, VHD_OPEN_RDONLY);
516
if (cur && cur != ctx)
526
vhd_batmap_test(vhd_context_t *ctx, vhd_batmap_t *batmap, uint32_t block)
528
if (!vhd_has_batmap(ctx) || !batmap->map)
531
if (block >= (batmap->header.batmap_size << (VHD_SECTOR_SHIFT + 3)))
534
return test_bit(batmap->map, block);
538
vhd_batmap_set(vhd_context_t *ctx, vhd_batmap_t *batmap, uint32_t block)
540
if (!vhd_has_batmap(ctx) || !batmap->map)
543
if (block >= (batmap->header.batmap_size << (VHD_SECTOR_SHIFT + 3)))
546
set_bit(batmap->map, block);
550
vhd_batmap_clear(vhd_context_t *ctx, vhd_batmap_t *batmap, uint32_t block)
552
if (!vhd_has_batmap(ctx) || !batmap->map)
555
if (block >= (batmap->header.batmap_size << (VHD_SECTOR_SHIFT + 3)))
558
clear_bit(batmap->map, block);
562
vhd_bitmap_test(vhd_context_t *ctx, char *map, uint32_t block)
564
if (vhd_creator_tapdisk(ctx) &&
565
ctx->footer.crtr_ver == 0x00000001)
566
return old_test_bit(map, block);
568
return test_bit(map, block);
572
vhd_bitmap_set(vhd_context_t *ctx, char *map, uint32_t block)
574
if (vhd_creator_tapdisk(ctx) &&
575
ctx->footer.crtr_ver == 0x00000001)
576
return old_set_bit(map, block);
578
return set_bit(map, block);
582
vhd_bitmap_clear(vhd_context_t *ctx, char *map, uint32_t block)
584
if (vhd_creator_tapdisk(ctx) &&
585
ctx->footer.crtr_ver == 0x00000001)
586
return old_clear_bit(map, block);
588
return clear_bit(map, block);
592
* returns absolute offset of the first
593
* byte of the file which is not vhd metadata
596
vhd_end_of_headers(vhd_context_t *ctx, off_t *end)
601
vhd_parent_locator_t *loc;
605
if (!vhd_type_dynamic(ctx))
608
eom = ctx->footer.data_offset + sizeof(vhd_header_t);
610
bat_bytes = vhd_bytes_padded(ctx->header.max_bat_size * sizeof(uint32_t));
611
bat_end = ctx->header.table_offset + bat_bytes;
613
eom = MAX(eom, bat_end);
615
if (vhd_has_batmap(ctx)) {
616
off_t hdr_end, hdr_secs, map_end, map_secs;
618
err = vhd_get_batmap(ctx);
622
hdr_secs = secs_round_up_no_zero(sizeof(vhd_batmap_header_t));
623
err = vhd_batmap_header_offset(ctx, &hdr_end);
627
hdr_end += vhd_sectors_to_bytes(hdr_secs);
628
eom = MAX(eom, hdr_end);
630
map_secs = ctx->batmap.header.batmap_size;
631
map_end = (ctx->batmap.header.batmap_offset +
632
vhd_sectors_to_bytes(map_secs));
633
eom = MAX(eom, map_end);
636
/* parent locators */
637
n = sizeof(ctx->header.loc) / sizeof(vhd_parent_locator_t);
639
for (i = 0; i < n; i++) {
642
loc = &ctx->header.loc[i];
643
if (loc->code == PLAT_CODE_NONE)
646
loc_end = loc->data_offset + vhd_parent_locator_size(loc);
647
eom = MAX(eom, loc_end);
655
vhd_end_of_data(vhd_context_t *ctx, off_t *end)
661
if (!vhd_type_dynamic(ctx)) {
662
err = vhd_seek(ctx, 0, SEEK_END);
666
max = vhd_position(ctx);
667
if (max == (off_t)-1)
670
*end = max - sizeof(vhd_footer_t);
674
err = vhd_end_of_headers(ctx, &max);
678
err = vhd_get_bat(ctx);
682
max >>= VHD_SECTOR_SHIFT;
684
for (i = 0; i < ctx->bat.entries; i++) {
685
blk = ctx->bat.bat[i];
687
if (blk != DD_BLK_UNUSED) {
688
blk += ctx->spb + ctx->bm_secs;
693
*end = vhd_sectors_to_bytes(max);
698
vhd_time(time_t time)
703
memset(&tm, 0, sizeof(struct tm));
707
micro_epoch = mktime(&tm);
709
return (uint32_t)(time - micro_epoch);
713
* Stringify the VHD timestamp for printing.
714
* As with ctime_r, target must be >=26 bytes.
717
vhd_time_to_string(uint32_t timestamp, char *target)
723
memset(&tm, 0, sizeof(struct tm));
725
/* VHD uses an epoch of 12:00AM, Jan 1, 2000. */
726
/* Need to adjust this to the expected epoch of 1970. */
732
t2 = t1 + (time_t)timestamp;
733
ctime_r(&t2, target);
735
/* handle mad ctime_r newline appending. */
736
if ((cr = strchr(target, '\n')) != NULL)
739
return (strlen(target));
743
* nabbed from vhd specs.
746
vhd_chs(uint64_t size)
748
uint32_t secs, cylinders, heads, spt, cth;
750
secs = secs_round_up_no_zero(size);
752
if (secs > 65535 * 16 * 255)
753
secs = 65535 * 16 * 255;
755
if (secs >= 65535 * 16 * 63) {
762
heads = (cth + 1023) / 1024;
767
if (cth >= (heads * 1024) || heads > 16) {
773
if (cth >= heads * 1024) {
780
cylinders = cth / heads;
782
return GEOM_ENCODE(cylinders, heads, spt);
786
vhd_get_footer(vhd_context_t *ctx)
788
if (!vhd_validate_footer(&ctx->footer))
791
return vhd_read_footer(ctx, &ctx->footer);
795
vhd_get_header(vhd_context_t *ctx)
797
if (!vhd_type_dynamic(ctx))
800
if (!vhd_validate_header(&ctx->header))
803
return vhd_read_header(ctx, &ctx->header);
807
vhd_get_bat(vhd_context_t *ctx)
809
if (!vhd_type_dynamic(ctx))
812
if (!vhd_validate_bat(&ctx->bat))
816
return vhd_read_bat(ctx, &ctx->bat);
820
vhd_get_batmap(vhd_context_t *ctx)
822
if (!vhd_has_batmap(ctx))
825
if (!vhd_validate_batmap(&ctx->batmap))
829
return vhd_read_batmap(ctx, &ctx->batmap);
833
vhd_put_footer(vhd_context_t *ctx)
835
memset(&ctx->footer, 0, sizeof(vhd_footer_t));
839
vhd_put_header(vhd_context_t *ctx)
841
memset(&ctx->header, 0, sizeof(vhd_header_t));
845
vhd_put_bat(vhd_context_t *ctx)
847
if (!vhd_type_dynamic(ctx))
851
memset(&ctx->bat, 0, sizeof(vhd_bat_t));
855
vhd_put_batmap(vhd_context_t *ctx)
857
if (!vhd_type_dynamic(ctx))
860
if (!vhd_has_batmap(ctx))
863
free(ctx->batmap.map);
864
memset(&ctx->batmap, 0, sizeof(vhd_batmap_t));
868
* look for 511 byte footer at end of file
871
vhd_read_short_footer(vhd_context_t *ctx, vhd_footer_t *footer)
879
err = vhd_seek(ctx, 0, SEEK_END);
883
eof = vhd_position(ctx);
884
if (eof == (off_t)-1) {
889
err = vhd_seek(ctx, eof - 511, SEEK_SET);
893
err = posix_memalign((void **)&buf,
894
VHD_SECTOR_SIZE, sizeof(vhd_footer_t));
901
memset(buf, 0, sizeof(vhd_footer_t));
904
* expecting short read here
906
vhd_read(ctx, buf, sizeof(vhd_footer_t));
908
memcpy(footer, buf, sizeof(vhd_footer_t));
910
vhd_footer_in(footer);
911
err = vhd_validate_footer(footer);
915
VHDLOG("%s: failed reading short footer: %d\n",
922
vhd_read_footer_at(vhd_context_t *ctx, vhd_footer_t *footer, off_t off)
929
err = vhd_seek(ctx, off, SEEK_SET);
933
err = posix_memalign((void **)&buf,
934
VHD_SECTOR_SIZE, sizeof(vhd_footer_t));
941
err = vhd_read(ctx, buf, sizeof(vhd_footer_t));
945
memcpy(footer, buf, sizeof(vhd_footer_t));
947
vhd_footer_in(footer);
948
err = vhd_validate_footer(footer);
952
VHDLOG("%s: reading footer at 0x%08"PRIx64" failed: %d\n",
953
ctx->file, off, err);
959
vhd_read_footer(vhd_context_t *ctx, vhd_footer_t *footer)
964
err = vhd_seek(ctx, 0, SEEK_END);
968
off = vhd_position(ctx);
969
if (off == (off_t)-1)
972
err = vhd_read_footer_at(ctx, footer, off - 512);
976
err = vhd_read_short_footer(ctx, footer);
980
if (ctx->oflags & VHD_OPEN_STRICT)
983
return vhd_read_footer_at(ctx, footer, 0);
987
vhd_read_header_at(vhd_context_t *ctx, vhd_header_t *header, off_t off)
994
if (!vhd_type_dynamic(ctx)) {
999
err = vhd_seek(ctx, off, SEEK_SET);
1003
err = posix_memalign((void **)&buf,
1004
VHD_SECTOR_SIZE, sizeof(vhd_header_t));
1011
err = vhd_read(ctx, buf, sizeof(vhd_header_t));
1015
memcpy(header, buf, sizeof(vhd_header_t));
1017
vhd_header_in(header);
1018
err = vhd_validate_header(header);
1022
VHDLOG("%s: reading header at 0x%08"PRIx64" failed: %d\n",
1023
ctx->file, off, err);
1029
vhd_read_header(vhd_context_t *ctx, vhd_header_t *header)
1034
if (!vhd_type_dynamic(ctx)) {
1035
VHDLOG("%s is not dynamic!\n", ctx->file);
1039
off = ctx->footer.data_offset;
1040
return vhd_read_header_at(ctx, header, off);
1044
vhd_read_bat(vhd_context_t *ctx, vhd_bat_t *bat)
1053
if (!vhd_type_dynamic(ctx)) {
1058
off = ctx->header.table_offset;
1059
size = vhd_bytes_padded(ctx->header.max_bat_size * sizeof(uint32_t));
1061
err = posix_memalign((void **)&buf, VHD_SECTOR_SIZE, size);
1068
err = vhd_seek(ctx, off, SEEK_SET);
1072
err = vhd_read(ctx, buf, size);
1076
bat->spb = ctx->header.block_size >> VHD_SECTOR_SHIFT;
1077
bat->entries = ctx->header.max_bat_size;
1078
bat->bat = (uint32_t *)buf;
1086
memset(bat, 0, sizeof(vhd_bat_t));
1087
VHDLOG("%s: failed to read bat: %d\n", ctx->file, err);
1092
vhd_read_batmap_header(vhd_context_t *ctx, vhd_batmap_t *batmap)
1101
err = vhd_batmap_header_offset(ctx, &off);
1105
err = vhd_seek(ctx, off, SEEK_SET);
1109
size = vhd_bytes_padded(sizeof(vhd_batmap_header_t));
1110
err = posix_memalign((void **)&buf, VHD_SECTOR_SIZE, size);
1117
err = vhd_read(ctx, buf, size);
1121
memcpy(&batmap->header, buf, sizeof(vhd_batmap_header_t));
1125
vhd_batmap_header_in(batmap);
1131
memset(&batmap->header, 0, sizeof(vhd_batmap_header_t));
1132
VHDLOG("%s: failed to read batmap header: %d\n", ctx->file, err);
1137
vhd_read_batmap_map(vhd_context_t *ctx, vhd_batmap_t *batmap)
1144
map_size = vhd_sectors_to_bytes(batmap->header.batmap_size);
1146
err = posix_memalign((void **)&buf, VHD_SECTOR_SIZE, map_size);
1153
off = batmap->header.batmap_offset;
1154
err = vhd_seek(ctx, off, SEEK_SET);
1158
err = vhd_read(ctx, buf, map_size);
1168
VHDLOG("%s: failed to read batmap: %d\n", ctx->file, err);
1173
vhd_read_batmap(vhd_context_t *ctx, vhd_batmap_t *batmap)
1177
if (!vhd_has_batmap(ctx))
1180
memset(batmap, 0, sizeof(vhd_batmap_t));
1182
err = vhd_read_batmap_header(ctx, batmap);
1186
err = vhd_validate_batmap_header(batmap);
1190
err = vhd_read_batmap_map(ctx, batmap);
1194
err = vhd_validate_batmap(batmap);
1202
memset(batmap, 0, sizeof(vhd_batmap_t));
1207
vhd_has_batmap(vhd_context_t *ctx)
1209
if (!vhd_type_dynamic(ctx))
1212
if (!vhd_creator_tapdisk(ctx))
1215
if (ctx->footer.crtr_ver <= VHD_VERSION(0, 1))
1218
if (ctx->footer.crtr_ver >= VHD_VERSION(1, 2))
1222
* VHDs of version 1.1 probably have a batmap, but may not
1223
* if they were updated from version 0.1 via vhd-update.
1225
if (!vhd_validate_batmap_header(&ctx->batmap))
1228
if (vhd_read_batmap_header(ctx, &ctx->batmap))
1231
return (!vhd_validate_batmap_header(&ctx->batmap));
1235
* Is this a block device (with a fixed size)? This affects whether the file
1236
* can be truncated and where the footer is written for VHDs.
1239
vhd_test_file_fixed(const char *file, int *is_block)
1244
err = stat(file, &stats);
1248
*is_block = !!(S_ISBLK(stats.st_mode));
1253
vhd_find_parent(vhd_context_t *ctx, const char *parent, char **_location)
1256
char *location, *cpath, *cdir, *path;
1267
if (parent[0] == '/') {
1268
if (!access(parent, R_OK)) {
1269
path = strdup(parent);
1277
/* check parent path relative to child's directory */
1278
cpath = realpath(ctx->file, NULL);
1284
cdir = dirname(cpath);
1285
if (asprintf(&location, "%s/%s", cdir, parent) == -1) {
1291
if (!access(location, R_OK)) {
1292
path = realpath(location, NULL);
1307
vhd_macx_encode_location(char *name, char **out, int *outlen)
1312
char *uri, *uri_utf8, *uri_utf8p, *ret;
1319
len = strlen(name) + strlen("file://");
1324
urip = uri = malloc(ibl + 1);
1325
uri_utf8 = uri_utf8p = malloc(obl);
1327
if (!uri || !uri_utf8)
1330
cd = iconv_open("UTF-8", "ASCII");
1331
if (cd == (iconv_t)-1) {
1336
snprintf(uri, ibl+1, "file://%s", name);
1342
&urip, &ibl, &uri_utf8p, &obl) == (size_t)-1 ||
1344
err = (errno ? -errno : -EIO);
1354
memcpy(ret, uri_utf8, len);
1361
if (cd != (iconv_t)-1)
1368
vhd_w2u_encode_location(char *name, char **out, int *outlen)
1373
char *uri, *uri_utf16, *uri_utf16p, *tmp, *ret;
1384
* relative paths must start with ".\"
1386
if (name[0] != '/') {
1387
tmp = strstr(name, "./");
1389
tmp += strlen("./");
1393
err = asprintf(&uri, ".\\%s", tmp);
1395
err = asprintf(&uri, "%s", name);
1401
while (*tmp != '\0') {
1412
uri_utf16 = uri_utf16p = malloc(obl);
1420
* little endian unicode here
1422
cd = iconv_open("UTF-16LE", "ASCII");
1423
if (cd == (iconv_t)-1) {
1432
&urip, &ibl, &uri_utf16p, &obl) == (size_t)-1 ||
1434
err = (errno ? -errno : -EIO);
1445
memcpy(ret, uri_utf16, len);
1453
if (cd != (iconv_t)-1)
1460
vhd_macx_decode_location(const char *in, char *out, int len)
1469
cd = iconv_open("ASCII", "UTF-8");
1470
if (cd == (iconv_t)-1)
1477
&in, &ibl, &out, &obl) == (size_t)-1 || ibl)
1483
if (strstr(name, "file://") != name)
1486
name += strlen("file://");
1488
return strdup(name);
1492
vhd_w2u_decode_location(const char *in, char *out, int len, char *utf_type)
1501
cd = iconv_open("ASCII", utf_type);
1502
if (cd == (iconv_t)-1)
1509
&in, &ibl, &out, &obl) == (size_t)-1 || ibl)
1516
while (tmp != out) {
1522
if (strstr(name, "C:") == name || strstr(name, "c:") == name)
1523
name += strlen("c:");
1525
return strdup(name);
1529
vhd_header_decode_parent(vhd_context_t *ctx, vhd_header_t *header, char **buf)
1531
char *code, out[512];
1533
if (vhd_creator_tapdisk(ctx) &&
1534
ctx->footer.crtr_ver == VHD_VERSION(0, 1))
1539
*buf = vhd_w2u_decode_location(header->prt_name, out, 512, code);
1540
return (*buf == NULL ? -EINVAL : 0);
1544
vhd_parent_locator_read(vhd_context_t *ctx,
1545
vhd_parent_locator_t *loc, char **parent)
1548
char *raw, *out, *name;
1555
if (ctx->footer.type != HD_TYPE_DIFF) {
1560
switch (loc->code) {
1561
case PLAT_CODE_MACX:
1562
case PLAT_CODE_W2KU:
1563
case PLAT_CODE_W2RU:
1570
err = vhd_seek(ctx, loc->data_offset, SEEK_SET);
1574
size = vhd_parent_locator_size(loc);
1580
err = posix_memalign((void **)&raw, VHD_SECTOR_SIZE, size);
1587
err = vhd_read(ctx, raw, size);
1591
out = malloc(loc->data_len + 1);
1597
switch (loc->code) {
1598
case PLAT_CODE_MACX:
1599
name = vhd_macx_decode_location(raw, out, loc->data_len);
1601
case PLAT_CODE_W2KU:
1602
case PLAT_CODE_W2RU:
1603
name = vhd_w2u_decode_location(raw, out,
1604
loc->data_len, UTF_16LE);
1621
VHDLOG("%s: error reading parent locator: %d\n",
1623
VHDLOG("%s: locator: code %u, space 0x%x, len 0x%x, "
1624
"off 0x%"PRIx64"\n", ctx->file, loc->code, loc->data_space,
1625
loc->data_len, loc->data_offset);
1632
vhd_parent_locator_get(vhd_context_t *ctx, char **parent)
1635
char *name, *location;
1636
vhd_parent_locator_t *loc;
1641
if (ctx->footer.type != HD_TYPE_DIFF)
1644
n = vhd_parent_locator_count(ctx);
1645
for (i = 0; i < n; i++) {
1646
loc = ctx->header.loc + i;
1647
err = vhd_parent_locator_read(ctx, loc, &name);
1651
err = vhd_find_parent(ctx, name, &location);
1653
VHDLOG("%s: couldn't find parent %s (%d)\n",
1654
ctx->file, name, err);
1667
vhd_parent_locator_write_at(vhd_context_t *ctx,
1668
const char *parent, off_t off, uint32_t code,
1669
size_t max_bytes, vhd_parent_locator_t *loc)
1673
char *absolute_path, *relative_path, *encoded, *block;
1675
memset(loc, 0, sizeof(vhd_parent_locator_t));
1677
if (ctx->footer.type != HD_TYPE_DIFF)
1680
absolute_path = NULL;
1681
relative_path = NULL;
1688
case PLAT_CODE_MACX:
1689
case PLAT_CODE_W2KU:
1690
case PLAT_CODE_W2RU:
1696
absolute_path = realpath(parent, NULL);
1697
if (!absolute_path) {
1702
err = stat(absolute_path, &stats);
1708
if (!S_ISREG(stats.st_mode) && !S_ISBLK(stats.st_mode)) {
1713
relative_path = relative_path_to(ctx->file, absolute_path, &err);
1714
if (!relative_path || err) {
1715
err = (err ? err : -EINVAL);
1720
case PLAT_CODE_MACX:
1721
err = vhd_macx_encode_location(relative_path, &encoded, &len);
1723
case PLAT_CODE_W2KU:
1724
case PLAT_CODE_W2RU:
1725
err = vhd_w2u_encode_location(relative_path, &encoded, &len);
1734
err = vhd_seek(ctx, off, SEEK_SET);
1738
size = vhd_bytes_padded(len);
1740
if (max_bytes && size > max_bytes) {
1741
err = -ENAMETOOLONG;
1745
err = posix_memalign((void **)&block, VHD_SECTOR_SIZE, size);
1752
memset(block, 0, size);
1753
memcpy(block, encoded, len);
1755
err = vhd_write(ctx, block, size);
1762
free(absolute_path);
1763
free(relative_path);
1770
loc->data_len = len;
1772
* write number of bytes ('size') instead of number of sectors
1773
* into loc->data_space to be compatible with MSFT, even though
1774
* this goes against the specs
1776
loc->data_space = size;
1777
loc->data_offset = off;
1784
vhd_footer_offset_at_eof(vhd_context_t *ctx, off_t *off)
1787
if ((err = vhd_seek(ctx, 0, SEEK_END)))
1789
*off = vhd_position(ctx) - sizeof(vhd_footer_t);
1794
vhd_read_bitmap(vhd_context_t *ctx, uint32_t block, char **bufp)
1805
if (!vhd_type_dynamic(ctx))
1808
err = vhd_get_bat(ctx);
1812
if (block >= ctx->bat.entries)
1815
blk = ctx->bat.bat[block];
1816
if (blk == DD_BLK_UNUSED)
1819
off = vhd_sectors_to_bytes(blk);
1820
size = vhd_bytes_padded(ctx->spb >> 3);
1822
err = vhd_seek(ctx, off, SEEK_SET);
1826
err = posix_memalign((void **)&buf, VHD_SECTOR_SIZE, size);
1830
err = vhd_read(ctx, buf, size);
1843
vhd_read_block(vhd_context_t *ctx, uint32_t block, char **bufp)
1854
if (!vhd_type_dynamic(ctx))
1857
err = vhd_get_bat(ctx);
1861
if (block >= ctx->bat.entries)
1864
blk = ctx->bat.bat[block];
1865
if (blk == DD_BLK_UNUSED)
1868
off = vhd_sectors_to_bytes(blk + ctx->bm_secs);
1869
size = vhd_sectors_to_bytes(ctx->spb);
1871
err = vhd_footer_offset_at_eof(ctx, &end);
1875
err = posix_memalign((void **)&buf, VHD_SECTOR_SIZE, size);
1881
if (end < off + ctx->header.block_size) {
1883
memset(buf + size, 0, ctx->header.block_size - size);
1886
err = vhd_seek(ctx, off, SEEK_SET);
1890
err = vhd_read(ctx, buf, size);
1903
vhd_write_footer_at(vhd_context_t *ctx, vhd_footer_t *footer, off_t off)
1910
err = posix_memalign((void **)&f,
1911
VHD_SECTOR_SIZE, sizeof(vhd_footer_t));
1918
memcpy(f, footer, sizeof(vhd_footer_t));
1919
f->checksum = vhd_checksum_footer(f);
1921
err = vhd_validate_footer(f);
1925
err = vhd_seek(ctx, off, SEEK_SET);
1931
err = vhd_write(ctx, f, sizeof(vhd_footer_t));
1935
VHDLOG("%s: failed writing footer at 0x%08"PRIx64": %d\n",
1936
ctx->file, off, err);
1942
vhd_write_footer(vhd_context_t *ctx, vhd_footer_t *footer)
1948
err = vhd_footer_offset_at_eof(ctx, &off);
1950
err = vhd_end_of_data(ctx, &off);
1954
err = vhd_write_footer_at(ctx, footer, off);
1958
if (!vhd_type_dynamic(ctx))
1961
return vhd_write_footer_at(ctx, footer, 0);
1965
vhd_write_header_at(vhd_context_t *ctx, vhd_header_t *header, off_t off)
1972
if (!vhd_type_dynamic(ctx)) {
1977
err = posix_memalign((void **)&h,
1978
VHD_SECTOR_SIZE, sizeof(vhd_header_t));
1985
memcpy(h, header, sizeof(vhd_header_t));
1987
h->checksum = vhd_checksum_header(h);
1988
err = vhd_validate_header(h);
1994
err = vhd_seek(ctx, off, SEEK_SET);
1998
err = vhd_write(ctx, h, sizeof(vhd_header_t));
2002
VHDLOG("%s: failed writing header at 0x%08"PRIx64": %d\n",
2003
ctx->file, off, err);
2009
vhd_write_header(vhd_context_t *ctx, vhd_header_t *header)
2014
if (!vhd_type_dynamic(ctx))
2017
off = ctx->footer.data_offset;
2018
return vhd_write_header_at(ctx, header, off);
2022
vhd_write_bat(vhd_context_t *ctx, vhd_bat_t *bat)
2029
if (!vhd_type_dynamic(ctx))
2032
err = vhd_validate_bat(&ctx->bat);
2036
err = vhd_validate_bat(bat);
2040
memset(&b, 0, sizeof(vhd_bat_t));
2042
off = ctx->header.table_offset;
2043
size = vhd_bytes_padded(bat->entries * sizeof(uint32_t));
2045
err = vhd_seek(ctx, off, SEEK_SET);
2049
err = posix_memalign((void **)&b.bat, VHD_SECTOR_SIZE, size);
2053
memcpy(b.bat, bat->bat, size);
2055
b.entries = bat->entries;
2058
err = vhd_write(ctx, b.bat, size);
2065
vhd_write_batmap(vhd_context_t *ctx, vhd_batmap_t *batmap)
2071
size_t size, map_size;
2076
if (!vhd_has_batmap(ctx)) {
2081
b.header = batmap->header;
2082
b.map = batmap->map;
2084
b.header.checksum = vhd_checksum_batmap(&b);
2085
err = vhd_validate_batmap(&b);
2089
off = b.header.batmap_offset;
2090
map_size = vhd_sectors_to_bytes(b.header.batmap_size);
2092
err = vhd_seek(ctx, off, SEEK_SET);
2096
err = posix_memalign((void **)&map, VHD_SECTOR_SIZE, map_size);
2103
memcpy(map, b.map, map_size);
2105
err = vhd_write(ctx, map, map_size);
2109
err = vhd_batmap_header_offset(ctx, &off);
2113
size = vhd_bytes_padded(sizeof(vhd_batmap_header_t));
2115
err = vhd_seek(ctx, off, SEEK_SET);
2119
err = posix_memalign((void **)&buf, VHD_SECTOR_SIZE, size);
2126
vhd_batmap_header_out(&b);
2127
memset(buf, 0, size);
2128
memcpy(buf, &b.header, sizeof(vhd_batmap_header_t));
2130
err = vhd_write(ctx, buf, size);
2134
VHDLOG("%s: failed writing batmap: %d\n", ctx->file, err);
2141
vhd_write_bitmap(vhd_context_t *ctx, uint32_t block, char *bitmap)
2148
if (!vhd_type_dynamic(ctx))
2151
err = vhd_validate_bat(&ctx->bat);
2155
if (block >= ctx->bat.entries)
2158
if ((unsigned long)bitmap & (VHD_SECTOR_SIZE - 1))
2161
blk = ctx->bat.bat[block];
2162
if (blk == DD_BLK_UNUSED)
2165
off = vhd_sectors_to_bytes(blk);
2166
size = vhd_sectors_to_bytes(ctx->bm_secs);
2168
err = vhd_seek(ctx, off, SEEK_SET);
2172
err = vhd_write(ctx, bitmap, size);
2180
vhd_write_block(vhd_context_t *ctx, uint32_t block, char *data)
2187
if (!vhd_type_dynamic(ctx))
2190
err = vhd_validate_bat(&ctx->bat);
2194
if (block >= ctx->bat.entries)
2197
if ((unsigned long)data & ~(VHD_SECTOR_SIZE -1))
2200
blk = ctx->bat.bat[block];
2201
if (blk == DD_BLK_UNUSED)
2204
off = vhd_sectors_to_bytes(blk + ctx->bm_secs);
2205
size = vhd_sectors_to_bytes(ctx->spb);
2207
err = vhd_seek(ctx, off, SEEK_SET);
2211
err = vhd_write(ctx, data, size);
2219
namedup(char **dup, const char *name)
2223
if (strnlen(name, MAX_NAME_LEN) >= MAX_NAME_LEN)
2224
return -ENAMETOOLONG;
2226
*dup = strdup(name);
2234
vhd_seek(vhd_context_t *ctx, off_t offset, int whence)
2238
off = lseek(ctx->fd, offset, whence);
2239
if (off == (off_t)-1) {
2240
VHDLOG("%s: seek(0x%08"PRIx64", %d) failed: %d\n",
2241
ctx->file, offset, whence, -errno);
2249
vhd_position(vhd_context_t *ctx)
2251
return lseek(ctx->fd, 0, SEEK_CUR);
2255
vhd_read(vhd_context_t *ctx, void *buf, size_t size)
2261
ret = read(ctx->fd, buf, size);
2265
VHDLOG("%s: read of %zu returned %zd, errno: %d\n",
2266
ctx->file, size, ret, -errno);
2268
return (errno ? -errno : -EIO);
2272
vhd_write(vhd_context_t *ctx, void *buf, size_t size)
2278
ret = write(ctx->fd, buf, size);
2282
VHDLOG("%s: write of %zu returned %zd, errno: %d\n",
2283
ctx->file, size, ret, -errno);
2285
return (errno ? -errno : -EIO);
2289
vhd_offset(vhd_context_t *ctx, uint32_t sector, uint32_t *offset)
2294
if (!vhd_type_dynamic(ctx))
2297
err = vhd_get_bat(ctx);
2301
block = sector / ctx->spb;
2302
if (ctx->bat.bat[block] == DD_BLK_UNUSED)
2303
*offset = DD_BLK_UNUSED;
2305
*offset = ctx->bat.bat[block] +
2306
ctx->bm_secs + (sector % ctx->spb);
2312
vhd_open_fast(vhd_context_t *ctx)
2318
size = sizeof(vhd_footer_t) + sizeof(vhd_header_t);
2319
err = posix_memalign((void **)&buf, VHD_SECTOR_SIZE, size);
2321
VHDLOG("failed allocating %s: %d\n", ctx->file, -err);
2325
err = vhd_read(ctx, buf, size);
2327
VHDLOG("failed reading %s: %d\n", ctx->file, err);
2331
memcpy(&ctx->footer, buf, sizeof(vhd_footer_t));
2332
vhd_footer_in(&ctx->footer);
2333
err = vhd_validate_footer(&ctx->footer);
2337
if (vhd_type_dynamic(ctx)) {
2338
if (ctx->footer.data_offset != sizeof(vhd_footer_t))
2339
err = vhd_read_header(ctx, &ctx->header);
2341
memcpy(&ctx->header,
2342
buf + sizeof(vhd_footer_t),
2343
sizeof(vhd_header_t));
2344
vhd_header_in(&ctx->header);
2345
err = vhd_validate_header(&ctx->header);
2351
ctx->spb = ctx->header.block_size >> VHD_SECTOR_SHIFT;
2352
ctx->bm_secs = secs_round_up_no_zero(ctx->spb >> 3);
2361
vhd_open(vhd_context_t *ctx, const char *file, int flags)
2365
if (flags & VHD_OPEN_STRICT)
2366
vhd_flag_clear(flags, VHD_OPEN_FAST);
2368
memset(ctx, 0, sizeof(vhd_context_t));
2370
ctx->oflags = flags;
2372
err = namedup(&ctx->file, file);
2376
oflags = O_DIRECT | O_LARGEFILE;
2377
if (flags & VHD_OPEN_RDONLY)
2379
if (flags & VHD_OPEN_RDWR)
2382
ctx->fd = open(ctx->file, oflags, 0644);
2383
if (ctx->fd == -1) {
2385
VHDLOG("failed to open %s: %d\n", ctx->file, err);
2389
err = vhd_test_file_fixed(ctx->file, &ctx->is_block);
2393
if (flags & VHD_OPEN_FAST) {
2394
err = vhd_open_fast(ctx);
2401
err = vhd_read_footer(ctx, &ctx->footer);
2405
if (!(flags & VHD_OPEN_IGNORE_DISABLED) && vhd_disabled(ctx)) {
2410
if (vhd_type_dynamic(ctx)) {
2411
err = vhd_read_header(ctx, &ctx->header);
2415
ctx->spb = ctx->header.block_size >> VHD_SECTOR_SHIFT;
2416
ctx->bm_secs = secs_round_up_no_zero(ctx->spb >> 3);
2425
memset(ctx, 0, sizeof(vhd_context_t));
2430
vhd_close(vhd_context_t *ctx)
2436
free(ctx->batmap.map);
2437
memset(ctx, 0, sizeof(vhd_context_t));
2441
vhd_initialize_footer(vhd_context_t *ctx, int type, uint64_t size)
2443
memset(&ctx->footer, 0, sizeof(vhd_footer_t));
2444
memcpy(ctx->footer.cookie, HD_COOKIE, sizeof(ctx->footer.cookie));
2445
ctx->footer.features = HD_RESERVED;
2446
ctx->footer.ff_version = HD_FF_VERSION;
2447
ctx->footer.timestamp = vhd_time(time(NULL));
2448
ctx->footer.crtr_ver = VHD_CURRENT_VERSION;
2449
ctx->footer.crtr_os = 0x00000000;
2450
ctx->footer.orig_size = size;
2451
ctx->footer.curr_size = size;
2452
ctx->footer.geometry = vhd_chs(size);
2453
ctx->footer.type = type;
2454
ctx->footer.saved = 0;
2455
ctx->footer.data_offset = 0xFFFFFFFFFFFFFFFF;
2456
strcpy(ctx->footer.crtr_app, "tap");
2457
blk_uuid_generate(&ctx->footer.uuid);
2461
vhd_initialize_header_parent_name(vhd_context_t *ctx, const char *parent_path)
2475
* big endian unicode here
2477
cd = iconv_open(UTF_16BE, "ASCII");
2478
if (cd == (iconv_t)-1) {
2483
ppath = strdup(parent_path);
2489
pname = basename(ppath);
2490
if (!strcmp(pname, "")) {
2495
ibl = strlen(pname);
2496
obl = sizeof(ctx->header.prt_name);
2497
dst = ctx->header.prt_name;
2499
memset(dst, 0, obl);
2505
&pname, &ibl, &dst, &obl) == (size_t)-1 || ibl)
2506
err = (errno ? -errno : -EINVAL);
2515
get_file_size(const char *name)
2520
fd = open(name, O_LARGEFILE | O_RDONLY);
2522
VHDLOG("unable to open '%s': %d\n", name, errno);
2525
end = lseek(fd, 0, SEEK_END);
2531
vhd_initialize_header(vhd_context_t *ctx, const char *parent_path,
2532
uint64_t size, int raw)
2536
vhd_context_t parent;
2538
if (!vhd_type_dynamic(ctx))
2541
memset(&ctx->header, 0, sizeof(vhd_header_t));
2542
memcpy(ctx->header.cookie, DD_COOKIE, sizeof(ctx->header.cookie));
2543
ctx->header.data_offset = (uint64_t)-1;
2544
ctx->header.table_offset = VHD_SECTOR_SIZE * 3; /* 1 ftr + 2 hdr */
2545
ctx->header.hdr_ver = DD_VERSION;
2546
ctx->header.block_size = VHD_BLOCK_SIZE;
2547
ctx->header.prt_ts = 0;
2548
ctx->header.res1 = 0;
2549
ctx->header.max_bat_size = (ctx->footer.curr_size +
2550
VHD_BLOCK_SIZE - 1) >> VHD_BLOCK_SHIFT;
2552
ctx->footer.data_offset = VHD_SECTOR_SIZE;
2554
if (ctx->footer.type == HD_TYPE_DYNAMIC)
2557
err = stat(parent_path, &stats);
2562
ctx->header.prt_ts = vhd_time(stats.st_mtime);
2564
size = get_file_size(parent_path);
2567
err = vhd_open(&parent, parent_path, VHD_OPEN_RDONLY);
2571
ctx->header.prt_ts = vhd_time(stats.st_mtime);
2572
blk_uuid_copy(&ctx->header.prt_uuid, &parent.footer.uuid);
2574
size = parent.footer.curr_size;
2577
ctx->footer.orig_size = size;
2578
ctx->footer.curr_size = size;
2579
ctx->footer.geometry = vhd_chs(size);
2580
ctx->header.max_bat_size =
2581
(size + VHD_BLOCK_SIZE - 1) >> VHD_BLOCK_SHIFT;
2583
return vhd_initialize_header_parent_name(ctx, parent_path);
2587
vhd_write_parent_locators(vhd_context_t *ctx, const char *parent)
2593
code = PLAT_CODE_NONE;
2595
if (ctx->footer.type != HD_TYPE_DIFF)
2598
off = ctx->batmap.header.batmap_offset +
2599
vhd_sectors_to_bytes(ctx->batmap.header.batmap_size);
2600
if (off & (VHD_SECTOR_SIZE - 1))
2601
off = vhd_bytes_padded(off);
2603
for (i = 0; i < 3; i++) {
2606
code = PLAT_CODE_MACX;
2609
code = PLAT_CODE_W2KU;
2612
code = PLAT_CODE_W2RU;
2616
err = vhd_parent_locator_write_at(ctx, parent, off, code,
2617
0, ctx->header.loc + i);
2621
off += vhd_parent_locator_size(ctx->header.loc + i);
2628
vhd_change_parent(vhd_context_t *child, char *parent_path, int raw)
2633
vhd_context_t parent;
2635
ppath = realpath(parent_path, NULL);
2637
VHDLOG("error resolving parent path %s for %s: %d\n",
2638
parent_path, child->file, errno);
2642
err = stat(ppath, &stats);
2648
if (!S_ISREG(stats.st_mode) && !S_ISBLK(stats.st_mode)) {
2654
blk_uuid_clear(&child->header.prt_uuid);
2656
err = vhd_open(&parent, ppath, VHD_OPEN_RDONLY);
2658
VHDLOG("error opening parent %s for %s: %d\n",
2659
ppath, child->file, err);
2662
blk_uuid_copy(&child->header.prt_uuid, &parent.footer.uuid);
2666
vhd_initialize_header_parent_name(child, ppath);
2667
child->header.prt_ts = vhd_time(stats.st_mtime);
2669
for (i = 0; i < vhd_parent_locator_count(child); i++) {
2670
vhd_parent_locator_t *loc = child->header.loc + i;
2671
size_t max = vhd_parent_locator_size(loc);
2673
switch (loc->code) {
2674
case PLAT_CODE_MACX:
2675
case PLAT_CODE_W2KU:
2676
case PLAT_CODE_W2RU:
2682
err = vhd_parent_locator_write_at(child, ppath,
2684
loc->code, max, loc);
2686
VHDLOG("error writing parent locator %d for %s: %d\n",
2687
i, child->file, err);
2692
TEST_FAIL_AT(FAIL_REPARENT_LOCATOR);
2694
err = vhd_write_header(child, &child->header);
2696
VHDLOG("error writing header for %s: %d\n", child->file, err);
2708
vhd_create_batmap(vhd_context_t *ctx)
2712
vhd_batmap_header_t *header;
2714
if (!vhd_type_dynamic(ctx))
2717
map_bytes = (ctx->header.max_bat_size + 7) >> 3;
2718
header = &ctx->batmap.header;
2720
memset(header, 0, sizeof(vhd_batmap_header_t));
2721
memcpy(header->cookie, VHD_BATMAP_COOKIE, sizeof(header->cookie));
2723
err = vhd_batmap_header_offset(ctx, &off);
2727
header->batmap_offset = off +
2728
vhd_bytes_padded(sizeof(vhd_batmap_header_t));
2729
header->batmap_size = secs_round_up_no_zero(map_bytes);
2730
header->batmap_version = VHD_BATMAP_CURRENT_VERSION;
2732
map_bytes = vhd_sectors_to_bytes(header->batmap_size);
2734
err = posix_memalign((void **)&ctx->batmap.map,
2735
VHD_SECTOR_SIZE, map_bytes);
2737
ctx->batmap.map = NULL;
2741
memset(ctx->batmap.map, 0, map_bytes);
2743
return vhd_write_batmap(ctx, &ctx->batmap);
2747
vhd_create_bat(vhd_context_t *ctx)
2752
if (!vhd_type_dynamic(ctx))
2755
size = vhd_bytes_padded(ctx->header.max_bat_size * sizeof(uint32_t));
2756
err = posix_memalign((void **)&ctx->bat.bat, VHD_SECTOR_SIZE, size);
2758
ctx->bat.bat = NULL;
2762
memset(ctx->bat.bat, 0, size);
2763
for (i = 0; i < ctx->header.max_bat_size; i++)
2764
ctx->bat.bat[i] = DD_BLK_UNUSED;
2766
err = vhd_seek(ctx, ctx->header.table_offset, SEEK_SET);
2770
ctx->bat.entries = ctx->header.max_bat_size;
2771
ctx->bat.spb = ctx->header.block_size >> VHD_SECTOR_SHIFT;
2773
return vhd_write_bat(ctx, &ctx->bat);
2777
vhd_initialize_fixed_disk(vhd_context_t *ctx)
2782
if (ctx->footer.type != HD_TYPE_FIXED)
2785
err = vhd_seek(ctx, 0, SEEK_SET);
2789
buf = mmap(0, VHD_BLOCK_SIZE, PROT_READ,
2790
MAP_SHARED | MAP_ANON, -1, 0);
2791
if (buf == MAP_FAILED)
2794
for (i = 0; i < ctx->footer.curr_size >> VHD_BLOCK_SHIFT; i++) {
2795
err = vhd_write(ctx, buf, VHD_BLOCK_SIZE);
2803
munmap(buf, VHD_BLOCK_SIZE);
2808
vhd_get_phys_size(vhd_context_t *ctx, off_t *size)
2812
if ((err = vhd_end_of_data(ctx, size)))
2814
*size += sizeof(vhd_footer_t);
2819
vhd_set_phys_size(vhd_context_t *ctx, off_t size)
2824
err = vhd_get_phys_size(ctx, &phys_size);
2827
if (size < phys_size) {
2828
// would result in data loss
2829
VHDLOG("ERROR: new size (%"PRIu64") < phys size (%"PRIu64")\n",
2833
return vhd_write_footer_at(ctx, &ctx->footer,
2834
size - sizeof(vhd_footer_t));
2838
__vhd_create(const char *name, const char *parent, uint64_t bytes, int type,
2839
vhd_flag_creat_t flags)
2844
vhd_footer_t *footer;
2845
vhd_header_t *header;
2846
uint64_t size, blks;
2853
case HD_TYPE_DYNAMIC:
2859
if (strnlen(name, VHD_MAX_NAME_LEN - 1) == VHD_MAX_NAME_LEN - 1)
2860
return -ENAMETOOLONG;
2862
memset(&ctx, 0, sizeof(vhd_context_t));
2863
footer = &ctx.footer;
2864
header = &ctx.header;
2865
blks = (bytes + VHD_BLOCK_SIZE - 1) >> VHD_BLOCK_SHIFT;
2866
size = blks << VHD_BLOCK_SHIFT;
2868
ctx.fd = open(name, O_WRONLY | O_CREAT |
2869
O_TRUNC | O_LARGEFILE | O_DIRECT, 0644);
2873
ctx.file = strdup(name);
2879
err = vhd_test_file_fixed(ctx.file, &ctx.is_block);
2883
vhd_initialize_footer(&ctx, type, size);
2885
if (type == HD_TYPE_FIXED) {
2886
err = vhd_initialize_fixed_disk(&ctx);
2890
int raw = vhd_flag_test(flags, VHD_FLAG_CREAT_PARENT_RAW);
2891
err = vhd_initialize_header(&ctx, parent, size, raw);
2895
err = vhd_write_footer_at(&ctx, &ctx.footer, 0);
2899
err = vhd_write_header_at(&ctx, &ctx.header, VHD_SECTOR_SIZE);
2903
err = vhd_create_batmap(&ctx);
2907
err = vhd_create_bat(&ctx);
2911
if (type == HD_TYPE_DIFF) {
2912
err = vhd_write_parent_locators(&ctx, parent);
2917
/* write header again since it may have changed */
2918
err = vhd_write_header_at(&ctx, &ctx.header, VHD_SECTOR_SIZE);
2923
err = vhd_seek(&ctx, 0, SEEK_END);
2927
off = vhd_position(&ctx);
2928
if (off == (off_t)-1) {
2934
off -= sizeof(vhd_footer_t);
2936
err = vhd_write_footer_at(&ctx, &ctx.footer, off);
2944
if (err && !ctx.is_block)
2950
vhd_create(const char *name, uint64_t bytes, int type, vhd_flag_creat_t flags)
2952
return __vhd_create(name, NULL, bytes, type, flags);
2956
vhd_snapshot(const char *name, uint64_t bytes, const char *parent,
2957
vhd_flag_creat_t flags)
2959
return __vhd_create(name, parent, bytes, HD_TYPE_DIFF, flags);
2963
__vhd_io_fixed_read(vhd_context_t *ctx,
2964
char *buf, uint64_t sec, uint32_t secs)
2968
err = vhd_seek(ctx, vhd_sectors_to_bytes(sec), SEEK_SET);
2972
return vhd_read(ctx, buf, vhd_sectors_to_bytes(secs));
2976
__vhd_io_dynamic_copy_data(vhd_context_t *ctx,
2977
char *map, int map_off,
2978
char *bitmap, int bitmap_off,
2979
char *dst, char *src, int secs)
2983
for (i = 0; i < secs; i++) {
2984
if (test_bit(map, map_off + i))
2987
if (ctx && !vhd_bitmap_test(ctx, bitmap, bitmap_off + i))
2990
memcpy(dst, src, VHD_SECTOR_SIZE);
2991
set_bit(map, map_off + i);
2994
src += VHD_SECTOR_SIZE;
2995
dst += VHD_SECTOR_SIZE;
3000
__vhd_io_dynamic_read_link(vhd_context_t *ctx, char *map,
3001
char *buf, uint64_t sector, uint32_t secs)
3005
int err, cnt, map_off;
3006
char *bitmap, *data, *src;
3011
blk = sector / ctx->spb;
3012
sec = sector % ctx->spb;
3013
off = ctx->bat.bat[blk];
3017
if (off == DD_BLK_UNUSED) {
3018
cnt = MIN(secs, ctx->spb);
3022
err = vhd_read_bitmap(ctx, blk, &bitmap);
3026
err = vhd_read_block(ctx, blk, &data);
3032
cnt = MIN(secs, ctx->spb - sec);
3033
src = data + vhd_sectors_to_bytes(sec);
3035
__vhd_io_dynamic_copy_data(ctx,
3047
buf += vhd_sectors_to_bytes(cnt);
3055
__raw_read_link(char *filename,
3056
char *map, char *buf, uint64_t sec, uint32_t secs)
3065
fd = open(filename, O_RDONLY | O_DIRECT | O_LARGEFILE);
3067
VHDLOG("%s: failed to open: %d\n", filename, -errno);
3071
off = lseek(fd, vhd_sectors_to_bytes(sec), SEEK_SET);
3072
if (off == (off_t)-1) {
3073
VHDLOG("%s: seek(0x%08"PRIx64") failed: %d\n",
3074
filename, vhd_sectors_to_bytes(sec), -errno);
3079
size = vhd_sectors_to_bytes(secs);
3080
err = posix_memalign((void **)&data, VHD_SECTOR_SIZE, size);
3084
err = read(fd, data, size);
3086
VHDLOG("%s: reading of %"PRIu64" returned %d, errno: %d\n",
3087
filename, size, err, -errno);
3089
err = errno ? -errno : -EIO;
3092
__vhd_io_dynamic_copy_data(NULL, map, 0, NULL, 0, buf, data, secs);
3102
__vhd_io_dynamic_read(vhd_context_t *ctx,
3103
char *buf, uint64_t sec, uint32_t secs)
3108
vhd_context_t parent, *vhd;
3110
err = vhd_get_bat(ctx);
3116
map = calloc(1, secs << (VHD_SECTOR_SHIFT - 3));
3120
memset(buf, 0, vhd_sectors_to_bytes(secs));
3123
err = __vhd_io_dynamic_read_link(vhd, map, buf, sec, secs);
3127
for (done = 0, i = 0; i < secs; i++)
3128
if (test_bit(map, i))
3136
if (vhd->footer.type == HD_TYPE_DIFF) {
3137
err = vhd_parent_locator_get(vhd, &next);
3140
if (vhd_parent_raw(vhd)) {
3141
err = __raw_read_link(next, map, buf, sec,
3154
err = vhd_open(vhd, next, VHD_OPEN_RDONLY);
3158
err = vhd_get_bat(vhd);
3176
vhd_io_read(vhd_context_t *ctx, char *buf, uint64_t sec, uint32_t secs)
3178
if (vhd_sectors_to_bytes(sec + secs) > ctx->footer.curr_size)
3181
if (!vhd_type_dynamic(ctx))
3182
return __vhd_io_fixed_read(ctx, buf, sec, secs);
3184
return __vhd_io_dynamic_read(ctx, buf, sec, secs);
3188
__vhd_io_fixed_write(vhd_context_t *ctx,
3189
char *buf, uint64_t sec, uint32_t secs)
3193
err = vhd_seek(ctx, vhd_sectors_to_bytes(sec), SEEK_SET);
3197
return vhd_write(ctx, buf, vhd_sectors_to_bytes(secs));
3201
__vhd_io_allocate_block(vhd_context_t *ctx, uint32_t block)
3206
int i, err, gap, spp;
3208
spp = getpagesize() >> VHD_SECTOR_SHIFT;
3210
err = vhd_end_of_data(ctx, &max);
3216
max >>= VHD_SECTOR_SHIFT;
3218
/* data region of segment should begin on page boundary */
3219
if ((max + ctx->bm_secs) % spp) {
3220
gap = (spp - ((max + ctx->bm_secs) % spp));
3224
err = vhd_seek(ctx, off, SEEK_SET);
3228
size = vhd_sectors_to_bytes(ctx->spb + ctx->bm_secs + gap);
3229
buf = mmap(0, size, PROT_READ, MAP_SHARED | MAP_ANON, -1, 0);
3230
if (buf == MAP_FAILED)
3233
err = vhd_write(ctx, buf, size);
3237
ctx->bat.bat[block] = max;
3238
err = vhd_write_bat(ctx, &ctx->bat);
3250
__vhd_io_dynamic_write(vhd_context_t *ctx,
3251
char *buf, uint64_t sector, uint32_t secs)
3256
int i, err, cnt, ret;
3258
if (vhd_sectors_to_bytes(sector + secs) > ctx->footer.curr_size)
3261
err = vhd_get_bat(ctx);
3265
if (vhd_has_batmap(ctx)) {
3266
err = vhd_get_batmap(ctx);
3272
blk = sector / ctx->spb;
3273
sec = sector % ctx->spb;
3275
off = ctx->bat.bat[blk];
3276
if (off == DD_BLK_UNUSED) {
3277
err = __vhd_io_allocate_block(ctx, blk);
3281
off = ctx->bat.bat[blk];
3284
off += ctx->bm_secs + sec;
3285
err = vhd_seek(ctx, vhd_sectors_to_bytes(off), SEEK_SET);
3289
cnt = MIN(secs, ctx->spb - sec);
3290
err = vhd_write(ctx, buf, vhd_sectors_to_bytes(cnt));
3294
if (vhd_has_batmap(ctx) &&
3295
vhd_batmap_test(ctx, &ctx->batmap, blk))
3298
err = vhd_read_bitmap(ctx, blk, &map);
3302
for (i = 0; i < cnt; i++)
3303
vhd_bitmap_set(ctx, map, sec + i);
3305
err = vhd_write_bitmap(ctx, blk, map);
3309
if (vhd_has_batmap(ctx)) {
3310
for (i = 0; i < ctx->spb; i++)
3311
if (!vhd_bitmap_test(ctx, map, i)) {
3316
vhd_batmap_set(ctx, &ctx->batmap, blk);
3317
err = vhd_write_batmap(ctx, &ctx->batmap);
3328
buf += vhd_sectors_to_bytes(cnt);
3334
ret = vhd_write_footer(ctx, &ctx->footer);
3335
return (err ? err : ret);
3343
vhd_io_write(vhd_context_t *ctx, char *buf, uint64_t sec, uint32_t secs)
3345
if (vhd_sectors_to_bytes(sec + secs) > ctx->footer.curr_size)
3348
if (!vhd_type_dynamic(ctx))
3349
return __vhd_io_fixed_write(ctx, buf, sec, secs);
3351
return __vhd_io_dynamic_write(ctx, buf, sec, secs);