~ubuntu-branches/ubuntu/utopic/xen/utopic

« back to all changes in this revision

Viewing changes to tools/blktap2/vhd/lib/libvhd.c

  • Committer: Bazaar Package Importer
  • Author(s): Bastian Blank
  • Date: 2010-05-06 15:47:38 UTC
  • mto: (1.3.1) (15.1.1 sid) (4.1.1 experimental)
  • mto: This revision was merged to the branch mainline in revision 3.
  • Revision ID: james.westby@ubuntu.com-20100506154738-agoz0rlafrh1fnq7
Tags: upstream-4.0.0
ImportĀ upstreamĀ versionĀ 4.0.0

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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
#ifndef _GNU_SOURCE
 
28
#define _GNU_SOURCE
 
29
#endif
 
30
#include <stdio.h>
 
31
#include <errno.h>
 
32
#include <fcntl.h>
 
33
#include <stdlib.h>
 
34
#include <unistd.h>
 
35
#include <string.h>
 
36
#include <libgen.h>
 
37
#include <iconv.h>
 
38
#include <sys/mman.h>
 
39
#include <sys/stat.h>
 
40
 
 
41
#include "libvhd.h"
 
42
#include "relative-path.h"
 
43
 
 
44
static int libvhd_dbg = 0;
 
45
 
 
46
void
 
47
libvhd_set_log_level(int level)
 
48
{
 
49
        if (level)
 
50
                libvhd_dbg = 1;
 
51
}
 
52
 
 
53
#define VHDLOG(_f, _a...)                                               \
 
54
        do {                                                            \
 
55
                if (libvhd_dbg)                                         \
 
56
                        syslog(LOG_INFO, "libvhd::%s: "_f,              \
 
57
                               __func__, ##_a);                         \
 
58
        } while (0)
 
59
 
 
60
#define BIT_MASK 0x80
 
61
 
 
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"
 
71
};
 
72
int TEST_FAIL[NUM_FAIL_TESTS];
 
73
#endif // ENABLE_FAILURE_TESTING
 
74
 
 
75
static inline int
 
76
test_bit (volatile char *addr, int nr)
 
77
{
 
78
        return ((addr[nr >> 3] << (nr & 7)) & BIT_MASK) != 0;
 
79
}
 
80
 
 
81
static inline void
 
82
set_bit (volatile char *addr, int nr)
 
83
{
 
84
        addr[nr >> 3] |= (BIT_MASK >> (nr & 7));
 
85
}
 
86
 
 
87
static inline void
 
88
clear_bit (volatile char *addr, int nr)
 
89
{
 
90
        addr[nr >> 3] &= ~(BIT_MASK >> (nr & 7));
 
91
}
 
92
 
 
93
static inline int
 
94
old_test_bit(volatile char *addr, int nr)
 
95
{
 
96
        return (((uint32_t *)addr)[nr >> 5] >> (nr & 31)) & 1;
 
97
}
 
98
 
 
99
static inline void
 
100
old_set_bit(volatile char *addr, int nr)
 
101
{
 
102
        ((uint32_t *)addr)[nr >> 5] |= (1 << (nr & 31));
 
103
}
 
104
 
 
105
static inline void
 
106
old_clear_bit(volatile char *addr, int nr)
 
107
{
 
108
        ((uint32_t *)addr)[nr >> 5] &= ~(1 << (nr & 31));
 
109
}
 
110
 
 
111
void
 
112
vhd_footer_in(vhd_footer_t *footer)
 
113
{
 
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);
 
125
}
 
126
 
 
127
void
 
128
vhd_footer_out(vhd_footer_t *footer)
 
129
{
 
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);
 
141
}
 
142
 
 
143
void
 
144
vhd_header_in(vhd_header_t *header)
 
145
{
 
146
        int i, n;
 
147
 
 
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);
 
155
 
 
156
        n = sizeof(header->loc) / sizeof(vhd_parent_locator_t);
 
157
 
 
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);
 
163
        }
 
164
}
 
165
 
 
166
void
 
167
vhd_header_out(vhd_header_t *header)
 
168
{
 
169
        int i, n;
 
170
 
 
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);
 
178
 
 
179
        n = sizeof(header->loc) / sizeof(vhd_parent_locator_t);
 
180
 
 
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);
 
186
        }
 
187
}
 
188
 
 
189
void
 
190
vhd_batmap_header_in(vhd_batmap_t *batmap)
 
191
{
 
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);
 
196
}
 
197
 
 
198
void
 
199
vhd_batmap_header_out(vhd_batmap_t *batmap)
 
200
{
 
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);
 
205
}
 
206
 
 
207
void
 
208
vhd_bat_in(vhd_bat_t *bat)
 
209
{
 
210
        int i;
 
211
 
 
212
        for (i = 0; i < bat->entries; i++)
 
213
                BE32_IN(&bat->bat[i]);
 
214
}
 
215
 
 
216
void
 
217
vhd_bat_out(vhd_bat_t *bat)
 
218
{
 
219
        int i;
 
220
 
 
221
        for (i = 0; i < bat->entries; i++)
 
222
                BE32_OUT(&bat->bat[i]);
 
223
}
 
224
 
 
225
uint32_t
 
226
vhd_checksum_footer(vhd_footer_t *footer)
 
227
{
 
228
        int i;
 
229
        unsigned char *blob;
 
230
        uint32_t checksum, tmp;
 
231
 
 
232
        checksum         = 0;
 
233
        tmp              = footer->checksum;
 
234
        footer->checksum = 0;
 
235
 
 
236
        blob = (unsigned char *)footer;
 
237
        for (i = 0; i < sizeof(vhd_footer_t); i++)
 
238
                checksum += (uint32_t)blob[i];
 
239
 
 
240
        footer->checksum = tmp;
 
241
        return ~checksum;
 
242
}
 
243
 
 
244
int
 
245
vhd_validate_footer(vhd_footer_t *footer)
 
246
{
 
247
        int csize;
 
248
        uint32_t checksum;
 
249
 
 
250
        csize = sizeof(footer->cookie);
 
251
        if (memcmp(footer->cookie, HD_COOKIE, csize) != 0 &&
 
252
            memcmp(footer->cookie, VHD_POISON_COOKIE, csize) != 0) {
 
253
                char buf[9];
 
254
                strncpy(buf, footer->cookie, sizeof(buf));
 
255
                buf[sizeof(buf)-1]= '\0';
 
256
                VHDLOG("invalid footer cookie: %s\n", buf);
 
257
                return -EINVAL;
 
258
        }
 
259
 
 
260
        checksum = vhd_checksum_footer(footer);
 
261
        if (checksum != footer->checksum) {
 
262
                /*
 
263
                 * early td-util did not re-calculate
 
264
                 * checksum when marking vhds 'hidden'
 
265
                 */
 
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;
 
271
                        footer->hidden = 0;
 
272
                        checksum = vhd_checksum_footer(footer);
 
273
                        footer->hidden = tmp;
 
274
 
 
275
                        if (checksum == footer->checksum)
 
276
                                return 0;
 
277
                }
 
278
 
 
279
                VHDLOG("invalid footer checksum: "
 
280
                       "footer = 0x%08x, calculated = 0x%08x\n",
 
281
                       footer->checksum, checksum);
 
282
                return -EINVAL;
 
283
        }
 
284
 
 
285
        return 0;
 
286
}
 
287
 
 
288
uint32_t
 
289
vhd_checksum_header(vhd_header_t *header)
 
290
{
 
291
        int i;
 
292
        unsigned char *blob;
 
293
        uint32_t checksum, tmp;
 
294
 
 
295
        checksum         = 0;
 
296
        tmp              = header->checksum;
 
297
        header->checksum = 0;
 
298
 
 
299
        blob = (unsigned char *)header;
 
300
        for (i = 0; i < sizeof(vhd_header_t); i++)
 
301
                checksum += (uint32_t)blob[i];
 
302
 
 
303
        header->checksum = tmp;
 
304
        return ~checksum;
 
305
}
 
306
 
 
307
int
 
308
vhd_validate_header(vhd_header_t *header)
 
309
{
 
310
        int i, n;
 
311
        uint32_t checksum;
 
312
 
 
313
        if (memcmp(header->cookie, DD_COOKIE, 8) != 0) {
 
314
                char buf[9];
 
315
                strncpy(buf, header->cookie, sizeof(buf));
 
316
                buf[sizeof(buf)-1]= '\0';
 
317
                VHDLOG("invalid header cookie: %s\n", buf);
 
318
                return -EINVAL;
 
319
        }
 
320
 
 
321
        if (header->hdr_ver != 0x00010000) {
 
322
                VHDLOG("invalid header version 0x%08x\n", header->hdr_ver);
 
323
                return -EINVAL;
 
324
        }
 
325
 
 
326
        if (header->data_offset != 0xFFFFFFFFFFFFFFFF) {
 
327
                VHDLOG("invalid header data_offset 0x%016"PRIx64"\n",
 
328
                       header->data_offset);
 
329
                return -EINVAL;
 
330
        }
 
331
 
 
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))
 
335
                        return -EINVAL;
 
336
 
 
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);
 
342
                return -EINVAL;
 
343
        }
 
344
 
 
345
        return 0;
 
346
}
 
347
 
 
348
static inline int
 
349
vhd_validate_bat(vhd_bat_t *bat)
 
350
{
 
351
        if (!bat->bat)
 
352
                return -EINVAL;
 
353
 
 
354
        return 0;
 
355
}
 
356
 
 
357
uint32_t
 
358
vhd_checksum_batmap(vhd_batmap_t *batmap)
 
359
{
 
360
        int i, n;
 
361
        char *blob;
 
362
        uint32_t checksum;
 
363
 
 
364
        blob     = batmap->map;
 
365
        checksum = 0;
 
366
 
 
367
        n = vhd_sectors_to_bytes(batmap->header.batmap_size);
 
368
 
 
369
        for (i = 0; i < n; i++) {
 
370
                if (batmap->header.batmap_version == VHD_BATMAP_VERSION(1, 1))
 
371
                        checksum += (uint32_t)blob[i];
 
372
                else
 
373
                        checksum += (uint32_t)(unsigned char)blob[i];
 
374
        }
 
375
 
 
376
        return ~checksum;
 
377
}
 
378
 
 
379
int
 
380
vhd_validate_batmap_header(vhd_batmap_t *batmap)
 
381
{
 
382
        if (memcmp(batmap->header.cookie, VHD_BATMAP_COOKIE, 8))
 
383
                return -EINVAL;
 
384
 
 
385
        if (batmap->header.batmap_version > VHD_BATMAP_CURRENT_VERSION)
 
386
                return -EINVAL;
 
387
 
 
388
        return 0;
 
389
}
 
390
 
 
391
int
 
392
vhd_validate_batmap(vhd_batmap_t *batmap)
 
393
{
 
394
        uint32_t checksum;
 
395
 
 
396
        if (!batmap->map)
 
397
                return -EINVAL;
 
398
 
 
399
        checksum = vhd_checksum_batmap(batmap);
 
400
        if (checksum != batmap->header.checksum)
 
401
                return -EINVAL;
 
402
 
 
403
        return 0;
 
404
}
 
405
 
 
406
int
 
407
vhd_batmap_header_offset(vhd_context_t *ctx, off_t *_off)
 
408
{
 
409
        off_t off;
 
410
        size_t  bat;
 
411
 
 
412
        *_off = 0;
 
413
 
 
414
        off  = ctx->header.table_offset;
 
415
        bat  = ctx->header.max_bat_size * sizeof(uint32_t);
 
416
        off += vhd_bytes_padded(bat);
 
417
 
 
418
        *_off = off;
 
419
        return 0;
 
420
}
 
421
 
 
422
int
 
423
vhd_validate_platform_code(uint32_t code)
 
424
{
 
425
        switch (code) {
 
426
        case PLAT_CODE_NONE:
 
427
        case PLAT_CODE_WI2R:
 
428
        case PLAT_CODE_WI2K:
 
429
        case PLAT_CODE_W2RU:
 
430
        case PLAT_CODE_W2KU:
 
431
        case PLAT_CODE_MAC:
 
432
        case PLAT_CODE_MACX:
 
433
                return 0;
 
434
        default:
 
435
                VHDLOG("invalid parent locator code %u\n", code);
 
436
                return -EINVAL;
 
437
        }
 
438
}
 
439
 
 
440
int
 
441
vhd_parent_locator_count(vhd_context_t *ctx)
 
442
{
 
443
        return (sizeof(ctx->header.loc) / sizeof(vhd_parent_locator_t));
 
444
}
 
445
 
 
446
int
 
447
vhd_hidden(vhd_context_t *ctx, int *hidden)
 
448
{
 
449
        int err;
 
450
 
 
451
        *hidden = 0;
 
452
 
 
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))) {
 
456
                vhd_footer_t copy;
 
457
 
 
458
                err = vhd_read_footer_at(ctx, &copy, 0);
 
459
                if (err) {
 
460
                        VHDLOG("error reading backup footer of %s: %d\n",
 
461
                               ctx->file, err);
 
462
                        return err;
 
463
                }
 
464
                *hidden = copy.hidden;
 
465
        } else
 
466
                *hidden = ctx->footer.hidden;
 
467
 
 
468
        return 0;
 
469
}
 
470
 
 
471
int
 
472
vhd_chain_depth(vhd_context_t *ctx, int *depth)
 
473
{
 
474
        char *file;
 
475
        int err, cnt;
 
476
        vhd_context_t vhd, *cur;
 
477
 
 
478
        err    = 0;
 
479
        cnt    = 0;
 
480
        *depth = 0;
 
481
        file   = NULL;
 
482
        cur    = ctx;
 
483
 
 
484
        for (;;) {
 
485
                cnt++;
 
486
 
 
487
                if (cur->footer.type != HD_TYPE_DIFF)
 
488
                        break;
 
489
 
 
490
                if (vhd_parent_raw(cur)) {
 
491
                        cnt++;
 
492
                        break;
 
493
                }
 
494
 
 
495
                err = vhd_parent_locator_get(cur, &file);
 
496
                if (err) {
 
497
                        file = NULL;
 
498
                        break;
 
499
                }
 
500
 
 
501
                if (cur != ctx) {
 
502
                        vhd_close(cur);
 
503
                        cur = NULL;
 
504
                }
 
505
 
 
506
                err = vhd_open(&vhd, file, VHD_OPEN_RDONLY);
 
507
                if (err)
 
508
                        break;
 
509
 
 
510
                cur = &vhd;
 
511
                free(file);
 
512
                file = NULL;
 
513
        }
 
514
 
 
515
        free(file);
 
516
        if (cur && cur != ctx)
 
517
                vhd_close(cur);
 
518
 
 
519
        if (!err)
 
520
                *depth = cnt;
 
521
 
 
522
        return err;
 
523
}
 
524
 
 
525
int
 
526
vhd_batmap_test(vhd_context_t *ctx, vhd_batmap_t *batmap, uint32_t block)
 
527
{
 
528
        if (!vhd_has_batmap(ctx) || !batmap->map)
 
529
                return 0;
 
530
 
 
531
        if (block >= (batmap->header.batmap_size << (VHD_SECTOR_SHIFT + 3)))
 
532
                return 0;
 
533
 
 
534
        return test_bit(batmap->map, block);
 
535
}
 
536
 
 
537
void
 
538
vhd_batmap_set(vhd_context_t *ctx, vhd_batmap_t *batmap, uint32_t block)
 
539
{
 
540
        if (!vhd_has_batmap(ctx) || !batmap->map)
 
541
                return;
 
542
 
 
543
        if (block >= (batmap->header.batmap_size << (VHD_SECTOR_SHIFT + 3)))
 
544
                return;
 
545
 
 
546
        set_bit(batmap->map, block);
 
547
}
 
548
 
 
549
void
 
550
vhd_batmap_clear(vhd_context_t *ctx, vhd_batmap_t *batmap, uint32_t block)
 
551
{
 
552
        if (!vhd_has_batmap(ctx) || !batmap->map)
 
553
                return;
 
554
 
 
555
        if (block >= (batmap->header.batmap_size << (VHD_SECTOR_SHIFT + 3)))
 
556
                return;
 
557
 
 
558
        clear_bit(batmap->map, block);
 
559
}
 
560
 
 
561
int
 
562
vhd_bitmap_test(vhd_context_t *ctx, char *map, uint32_t block)
 
563
{
 
564
        if (vhd_creator_tapdisk(ctx) &&
 
565
            ctx->footer.crtr_ver == 0x00000001)
 
566
                return old_test_bit(map, block);
 
567
 
 
568
        return test_bit(map, block);
 
569
}
 
570
 
 
571
void
 
572
vhd_bitmap_set(vhd_context_t *ctx, char *map, uint32_t block)
 
573
{
 
574
        if (vhd_creator_tapdisk(ctx) &&
 
575
            ctx->footer.crtr_ver == 0x00000001)
 
576
                return old_set_bit(map, block);
 
577
 
 
578
        return set_bit(map, block);
 
579
}
 
580
 
 
581
void
 
582
vhd_bitmap_clear(vhd_context_t *ctx, char *map, uint32_t block)
 
583
{
 
584
        if (vhd_creator_tapdisk(ctx) &&
 
585
            ctx->footer.crtr_ver == 0x00000001)
 
586
                return old_clear_bit(map, block);
 
587
 
 
588
        return clear_bit(map, block);
 
589
}
 
590
 
 
591
/*
 
592
 * returns absolute offset of the first 
 
593
 * byte of the file which is not vhd metadata
 
594
 */
 
595
int
 
596
vhd_end_of_headers(vhd_context_t *ctx, off_t *end)
 
597
{
 
598
        int err, i, n;
 
599
        uint32_t bat_bytes;
 
600
        off_t eom, bat_end;
 
601
        vhd_parent_locator_t *loc;
 
602
 
 
603
        *end = 0;
 
604
 
 
605
        if (!vhd_type_dynamic(ctx))
 
606
                return 0;
 
607
 
 
608
        eom       = ctx->footer.data_offset + sizeof(vhd_header_t);
 
609
 
 
610
        bat_bytes = vhd_bytes_padded(ctx->header.max_bat_size * sizeof(uint32_t));
 
611
        bat_end   = ctx->header.table_offset + bat_bytes;
 
612
 
 
613
        eom       = MAX(eom, bat_end);
 
614
 
 
615
        if (vhd_has_batmap(ctx)) {
 
616
                off_t hdr_end, hdr_secs, map_end, map_secs;
 
617
 
 
618
                err = vhd_get_batmap(ctx);
 
619
                if (err)
 
620
                        return err;
 
621
 
 
622
                hdr_secs = secs_round_up_no_zero(sizeof(vhd_batmap_header_t));
 
623
                err      = vhd_batmap_header_offset(ctx, &hdr_end);
 
624
                if (err)
 
625
                        return err;
 
626
 
 
627
                hdr_end += vhd_sectors_to_bytes(hdr_secs);
 
628
                eom      = MAX(eom, hdr_end);
 
629
 
 
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);
 
634
        }
 
635
 
 
636
        /* parent locators */
 
637
        n = sizeof(ctx->header.loc) / sizeof(vhd_parent_locator_t);
 
638
 
 
639
        for (i = 0; i < n; i++) {
 
640
                off_t loc_end;
 
641
 
 
642
                loc = &ctx->header.loc[i];
 
643
                if (loc->code == PLAT_CODE_NONE)
 
644
                        continue;
 
645
 
 
646
                loc_end = loc->data_offset + vhd_parent_locator_size(loc);
 
647
                eom     = MAX(eom, loc_end);
 
648
        }
 
649
 
 
650
        *end = eom;
 
651
        return 0;
 
652
}
 
653
 
 
654
int
 
655
vhd_end_of_data(vhd_context_t *ctx, off_t *end)
 
656
{
 
657
        int i, err;
 
658
        off_t max;
 
659
        uint64_t blk;
 
660
 
 
661
        if (!vhd_type_dynamic(ctx)) {
 
662
                err = vhd_seek(ctx, 0, SEEK_END);
 
663
                if (err)
 
664
                        return err;
 
665
 
 
666
                max = vhd_position(ctx);
 
667
                if (max == (off_t)-1)
 
668
                        return -errno;
 
669
 
 
670
                *end = max - sizeof(vhd_footer_t);
 
671
                return 0;
 
672
        }
 
673
 
 
674
        err = vhd_end_of_headers(ctx, &max);
 
675
        if (err)
 
676
                return err;
 
677
 
 
678
        err = vhd_get_bat(ctx);
 
679
        if (err)
 
680
                return err;
 
681
 
 
682
        max >>= VHD_SECTOR_SHIFT;
 
683
 
 
684
        for (i = 0; i < ctx->bat.entries; i++) {
 
685
                blk = ctx->bat.bat[i];
 
686
 
 
687
                if (blk != DD_BLK_UNUSED) {
 
688
                        blk += ctx->spb + ctx->bm_secs;
 
689
                        max  = MAX(blk, max);
 
690
                }
 
691
        }
 
692
 
 
693
        *end = vhd_sectors_to_bytes(max);
 
694
        return 0;
 
695
}
 
696
 
 
697
uint32_t
 
698
vhd_time(time_t time)
 
699
{
 
700
        struct tm tm;
 
701
        time_t micro_epoch;
 
702
 
 
703
        memset(&tm, 0, sizeof(struct tm));
 
704
        tm.tm_year   = 100;
 
705
        tm.tm_mon    = 0;
 
706
        tm.tm_mday   = 1;
 
707
        micro_epoch  = mktime(&tm);
 
708
 
 
709
        return (uint32_t)(time - micro_epoch);
 
710
}
 
711
 
 
712
/* 
 
713
 * Stringify the VHD timestamp for printing.
 
714
 * As with ctime_r, target must be >=26 bytes.
 
715
 */
 
716
size_t 
 
717
vhd_time_to_string(uint32_t timestamp, char *target)
 
718
{
 
719
        char *cr;
 
720
        struct tm tm;
 
721
        time_t t1, t2;
 
722
 
 
723
        memset(&tm, 0, sizeof(struct tm));
 
724
 
 
725
        /* VHD uses an epoch of 12:00AM, Jan 1, 2000.         */
 
726
        /* Need to adjust this to the expected epoch of 1970. */
 
727
        tm.tm_year  = 100;
 
728
        tm.tm_mon   = 0;
 
729
        tm.tm_mday  = 1;
 
730
 
 
731
        t1 = mktime(&tm);
 
732
        t2 = t1 + (time_t)timestamp;
 
733
        ctime_r(&t2, target);
 
734
 
 
735
        /* handle mad ctime_r newline appending. */
 
736
        if ((cr = strchr(target, '\n')) != NULL)
 
737
                *cr = '\0';
 
738
 
 
739
        return (strlen(target));
 
740
}
 
741
 
 
742
/*
 
743
 * nabbed from vhd specs.
 
744
 */
 
745
uint32_t
 
746
vhd_chs(uint64_t size)
 
747
{
 
748
        uint32_t secs, cylinders, heads, spt, cth;
 
749
 
 
750
        secs = secs_round_up_no_zero(size);
 
751
 
 
752
        if (secs > 65535 * 16 * 255)
 
753
                secs = 65535 * 16 * 255;
 
754
 
 
755
        if (secs >= 65535 * 16 * 63) {
 
756
                spt   = 255;
 
757
                cth   = secs / spt;
 
758
                heads = 16;
 
759
        } else {
 
760
                spt   = 17;
 
761
                cth   = secs / spt;
 
762
                heads = (cth + 1023) / 1024;
 
763
 
 
764
                if (heads < 4)
 
765
                        heads = 4;
 
766
 
 
767
                if (cth >= (heads * 1024) || heads > 16) {
 
768
                        spt   = 31;
 
769
                        cth   = secs / spt;
 
770
                        heads = 16;
 
771
                }
 
772
 
 
773
                if (cth >= heads * 1024) {
 
774
                        spt   = 63;
 
775
                        cth   = secs / spt;
 
776
                        heads = 16;
 
777
                }
 
778
        }
 
779
 
 
780
        cylinders = cth / heads;
 
781
 
 
782
        return GEOM_ENCODE(cylinders, heads, spt);
 
783
}
 
784
 
 
785
int
 
786
vhd_get_footer(vhd_context_t *ctx)
 
787
{
 
788
        if (!vhd_validate_footer(&ctx->footer))
 
789
                return 0;
 
790
 
 
791
        return vhd_read_footer(ctx, &ctx->footer);
 
792
}
 
793
 
 
794
int
 
795
vhd_get_header(vhd_context_t *ctx)
 
796
{
 
797
        if (!vhd_type_dynamic(ctx))
 
798
                return -EINVAL;
 
799
 
 
800
        if (!vhd_validate_header(&ctx->header))
 
801
                return 0;
 
802
 
 
803
        return vhd_read_header(ctx, &ctx->header);
 
804
}
 
805
 
 
806
int
 
807
vhd_get_bat(vhd_context_t *ctx)
 
808
{
 
809
        if (!vhd_type_dynamic(ctx))
 
810
                return -EINVAL;
 
811
 
 
812
        if (!vhd_validate_bat(&ctx->bat))
 
813
                return 0;
 
814
 
 
815
        vhd_put_bat(ctx);
 
816
        return vhd_read_bat(ctx, &ctx->bat);
 
817
}
 
818
 
 
819
int
 
820
vhd_get_batmap(vhd_context_t *ctx)
 
821
{
 
822
        if (!vhd_has_batmap(ctx))
 
823
                return -EINVAL;
 
824
 
 
825
        if (!vhd_validate_batmap(&ctx->batmap))
 
826
                return 0;
 
827
 
 
828
        vhd_put_batmap(ctx);
 
829
        return vhd_read_batmap(ctx, &ctx->batmap);
 
830
}
 
831
 
 
832
void
 
833
vhd_put_footer(vhd_context_t *ctx)
 
834
{
 
835
        memset(&ctx->footer, 0, sizeof(vhd_footer_t));
 
836
}
 
837
 
 
838
void
 
839
vhd_put_header(vhd_context_t *ctx)
 
840
{
 
841
        memset(&ctx->header, 0, sizeof(vhd_header_t));
 
842
}
 
843
 
 
844
void
 
845
vhd_put_bat(vhd_context_t *ctx)
 
846
{
 
847
        if (!vhd_type_dynamic(ctx))
 
848
                return;
 
849
 
 
850
        free(ctx->bat.bat);
 
851
        memset(&ctx->bat, 0, sizeof(vhd_bat_t));
 
852
}
 
853
 
 
854
void
 
855
vhd_put_batmap(vhd_context_t *ctx)
 
856
{
 
857
        if (!vhd_type_dynamic(ctx))
 
858
                return;
 
859
 
 
860
        if (!vhd_has_batmap(ctx))
 
861
                return;
 
862
 
 
863
        free(ctx->batmap.map);
 
864
        memset(&ctx->batmap, 0, sizeof(vhd_batmap_t));
 
865
}
 
866
 
 
867
/*
 
868
 * look for 511 byte footer at end of file
 
869
 */
 
870
int
 
871
vhd_read_short_footer(vhd_context_t *ctx, vhd_footer_t *footer)
 
872
{
 
873
        int err;
 
874
        char *buf;
 
875
        off_t eof;
 
876
 
 
877
        buf = NULL;
 
878
 
 
879
        err = vhd_seek(ctx, 0, SEEK_END);
 
880
        if (err)
 
881
                goto out;
 
882
 
 
883
        eof = vhd_position(ctx);
 
884
        if (eof == (off_t)-1) {
 
885
                err = -errno;
 
886
                goto out;
 
887
        }
 
888
 
 
889
        err = vhd_seek(ctx, eof - 511, SEEK_SET);
 
890
        if (err)
 
891
                goto out;
 
892
 
 
893
        err = posix_memalign((void **)&buf,
 
894
                             VHD_SECTOR_SIZE, sizeof(vhd_footer_t));
 
895
        if (err) {
 
896
                buf = NULL;
 
897
                err = -err;
 
898
                goto out;
 
899
        }
 
900
 
 
901
        memset(buf, 0, sizeof(vhd_footer_t));
 
902
 
 
903
        /*
 
904
         * expecting short read here
 
905
         */
 
906
        vhd_read(ctx, buf, sizeof(vhd_footer_t));
 
907
 
 
908
        memcpy(footer, buf, sizeof(vhd_footer_t));
 
909
 
 
910
        vhd_footer_in(footer);
 
911
        err = vhd_validate_footer(footer);
 
912
 
 
913
out:
 
914
        if (err)
 
915
                VHDLOG("%s: failed reading short footer: %d\n",
 
916
                       ctx->file, err);
 
917
        free(buf);
 
918
        return err;
 
919
}
 
920
 
 
921
int
 
922
vhd_read_footer_at(vhd_context_t *ctx, vhd_footer_t *footer, off_t off)
 
923
{
 
924
        int err;
 
925
        char *buf;
 
926
 
 
927
        buf = NULL;
 
928
 
 
929
        err = vhd_seek(ctx, off, SEEK_SET);
 
930
        if (err)
 
931
                goto out;
 
932
 
 
933
        err = posix_memalign((void **)&buf,
 
934
                             VHD_SECTOR_SIZE, sizeof(vhd_footer_t));
 
935
        if (err) {
 
936
                buf = NULL;
 
937
                err = -err;
 
938
                goto out;
 
939
        }
 
940
 
 
941
        err = vhd_read(ctx, buf, sizeof(vhd_footer_t));
 
942
        if (err)
 
943
                goto out;
 
944
 
 
945
        memcpy(footer, buf, sizeof(vhd_footer_t));
 
946
 
 
947
        vhd_footer_in(footer);
 
948
        err = vhd_validate_footer(footer);
 
949
 
 
950
out:
 
951
        if (err)
 
952
                VHDLOG("%s: reading footer at 0x%08"PRIx64" failed: %d\n",
 
953
                       ctx->file, off, err);
 
954
        free(buf);
 
955
        return err;
 
956
}
 
957
 
 
958
int
 
959
vhd_read_footer(vhd_context_t *ctx, vhd_footer_t *footer)
 
960
{
 
961
        int err;
 
962
        off_t off;
 
963
 
 
964
        err = vhd_seek(ctx, 0, SEEK_END);
 
965
        if (err)
 
966
                return err;
 
967
 
 
968
        off = vhd_position(ctx);
 
969
        if (off == (off_t)-1)
 
970
                return -errno;
 
971
 
 
972
        err = vhd_read_footer_at(ctx, footer, off - 512);
 
973
        if (err != -EINVAL)
 
974
                return err;
 
975
 
 
976
        err = vhd_read_short_footer(ctx, footer);
 
977
        if (err != -EINVAL)
 
978
                return err;
 
979
 
 
980
        if (ctx->oflags & VHD_OPEN_STRICT)
 
981
                return -EINVAL;
 
982
 
 
983
        return vhd_read_footer_at(ctx, footer, 0);
 
984
}
 
985
 
 
986
int
 
987
vhd_read_header_at(vhd_context_t *ctx, vhd_header_t *header, off_t off)
 
988
{
 
989
        int err;
 
990
        char *buf;
 
991
 
 
992
        buf = NULL;
 
993
 
 
994
        if (!vhd_type_dynamic(ctx)) {
 
995
                err = -EINVAL;
 
996
                goto out;
 
997
        }
 
998
 
 
999
        err = vhd_seek(ctx, off, SEEK_SET);
 
1000
        if (err)
 
1001
                goto out;
 
1002
 
 
1003
        err = posix_memalign((void **)&buf,
 
1004
                             VHD_SECTOR_SIZE, sizeof(vhd_header_t));
 
1005
        if (err) {
 
1006
                buf = NULL;
 
1007
                err = -err;
 
1008
                goto out;
 
1009
        }
 
1010
 
 
1011
        err = vhd_read(ctx, buf, sizeof(vhd_header_t));
 
1012
        if (err)
 
1013
                goto out;
 
1014
 
 
1015
        memcpy(header, buf, sizeof(vhd_header_t));
 
1016
 
 
1017
        vhd_header_in(header);
 
1018
        err = vhd_validate_header(header);
 
1019
 
 
1020
out:
 
1021
        if (err)
 
1022
                VHDLOG("%s: reading header at 0x%08"PRIx64" failed: %d\n",
 
1023
                       ctx->file, off, err);
 
1024
        free(buf);
 
1025
        return err;
 
1026
}
 
1027
 
 
1028
int
 
1029
vhd_read_header(vhd_context_t *ctx, vhd_header_t *header)
 
1030
{
 
1031
        int err;
 
1032
        off_t off;
 
1033
 
 
1034
        if (!vhd_type_dynamic(ctx)) {
 
1035
                VHDLOG("%s is not dynamic!\n", ctx->file);
 
1036
                return -EINVAL;
 
1037
        }
 
1038
 
 
1039
        off = ctx->footer.data_offset;
 
1040
        return vhd_read_header_at(ctx, header, off);
 
1041
}
 
1042
 
 
1043
int
 
1044
vhd_read_bat(vhd_context_t *ctx, vhd_bat_t *bat)
 
1045
{
 
1046
        int err;
 
1047
        char *buf;
 
1048
        off_t off;
 
1049
        size_t size;
 
1050
 
 
1051
        buf  = NULL;
 
1052
 
 
1053
        if (!vhd_type_dynamic(ctx)) {
 
1054
                err = -EINVAL;
 
1055
                goto fail;
 
1056
        }
 
1057
 
 
1058
        off  = ctx->header.table_offset;
 
1059
        size = vhd_bytes_padded(ctx->header.max_bat_size * sizeof(uint32_t));
 
1060
 
 
1061
        err  = posix_memalign((void **)&buf, VHD_SECTOR_SIZE, size);
 
1062
        if (err) {
 
1063
                buf = NULL;
 
1064
                err = -err;
 
1065
                goto fail;
 
1066
        }
 
1067
 
 
1068
        err = vhd_seek(ctx, off, SEEK_SET);
 
1069
        if (err)
 
1070
                goto fail;
 
1071
 
 
1072
        err = vhd_read(ctx, buf, size);
 
1073
        if (err)
 
1074
                goto fail;
 
1075
 
 
1076
        bat->spb     = ctx->header.block_size >> VHD_SECTOR_SHIFT;
 
1077
        bat->entries = ctx->header.max_bat_size;
 
1078
        bat->bat     = (uint32_t *)buf;
 
1079
 
 
1080
        vhd_bat_in(bat);
 
1081
 
 
1082
        return 0;
 
1083
 
 
1084
fail:
 
1085
        free(buf);
 
1086
        memset(bat, 0, sizeof(vhd_bat_t));
 
1087
        VHDLOG("%s: failed to read bat: %d\n", ctx->file, err);
 
1088
        return err;
 
1089
}
 
1090
 
 
1091
static int
 
1092
vhd_read_batmap_header(vhd_context_t *ctx, vhd_batmap_t *batmap)
 
1093
{
 
1094
        int err;
 
1095
        char *buf;
 
1096
        off_t off;
 
1097
        size_t size;
 
1098
 
 
1099
        buf = NULL;
 
1100
 
 
1101
        err = vhd_batmap_header_offset(ctx, &off);
 
1102
        if (err)
 
1103
                goto fail;
 
1104
 
 
1105
        err = vhd_seek(ctx, off, SEEK_SET);
 
1106
        if (err)
 
1107
                goto fail;
 
1108
 
 
1109
        size = vhd_bytes_padded(sizeof(vhd_batmap_header_t));
 
1110
        err  = posix_memalign((void **)&buf, VHD_SECTOR_SIZE, size);
 
1111
        if (err) {
 
1112
                buf = NULL;
 
1113
                err = -err;
 
1114
                goto fail;
 
1115
        }
 
1116
 
 
1117
        err = vhd_read(ctx, buf, size);
 
1118
        if (err)
 
1119
                goto fail;
 
1120
 
 
1121
        memcpy(&batmap->header, buf, sizeof(vhd_batmap_header_t));
 
1122
        free(buf);
 
1123
        buf = NULL;
 
1124
 
 
1125
        vhd_batmap_header_in(batmap);
 
1126
 
 
1127
        return 0;
 
1128
 
 
1129
fail:
 
1130
        free(buf);
 
1131
        memset(&batmap->header, 0, sizeof(vhd_batmap_header_t));
 
1132
        VHDLOG("%s: failed to read batmap header: %d\n", ctx->file, err);
 
1133
        return err;
 
1134
}
 
1135
 
 
1136
static int
 
1137
vhd_read_batmap_map(vhd_context_t *ctx, vhd_batmap_t *batmap)
 
1138
{
 
1139
        int err;
 
1140
        char *buf;
 
1141
        off_t off;
 
1142
        size_t map_size;
 
1143
 
 
1144
        map_size = vhd_sectors_to_bytes(batmap->header.batmap_size);
 
1145
 
 
1146
        err = posix_memalign((void **)&buf, VHD_SECTOR_SIZE, map_size);
 
1147
        if (err) {
 
1148
                buf = NULL;
 
1149
                err = -err;
 
1150
                goto fail;
 
1151
        }
 
1152
 
 
1153
        off  = batmap->header.batmap_offset;
 
1154
        err  = vhd_seek(ctx, off, SEEK_SET);
 
1155
        if (err)
 
1156
                goto fail;
 
1157
 
 
1158
        err  = vhd_read(ctx, buf, map_size);
 
1159
        if (err)
 
1160
                goto fail;
 
1161
 
 
1162
        batmap->map = buf;
 
1163
        return 0;
 
1164
 
 
1165
fail:
 
1166
        free(buf);
 
1167
        batmap->map = NULL;
 
1168
        VHDLOG("%s: failed to read batmap: %d\n", ctx->file, err);
 
1169
        return err;
 
1170
}
 
1171
 
 
1172
int
 
1173
vhd_read_batmap(vhd_context_t *ctx, vhd_batmap_t *batmap)
 
1174
{
 
1175
        int err;
 
1176
 
 
1177
        if (!vhd_has_batmap(ctx))
 
1178
                return -EINVAL;
 
1179
 
 
1180
        memset(batmap, 0, sizeof(vhd_batmap_t));
 
1181
 
 
1182
        err = vhd_read_batmap_header(ctx, batmap);
 
1183
        if (err)
 
1184
                return err;
 
1185
 
 
1186
        err = vhd_validate_batmap_header(batmap);
 
1187
        if (err)
 
1188
                return err;
 
1189
 
 
1190
        err = vhd_read_batmap_map(ctx, batmap);
 
1191
        if (err)
 
1192
                return err;
 
1193
 
 
1194
        err = vhd_validate_batmap(batmap);
 
1195
        if (err)
 
1196
                goto fail;
 
1197
 
 
1198
        return 0;
 
1199
 
 
1200
fail:
 
1201
        free(batmap->map);
 
1202
        memset(batmap, 0, sizeof(vhd_batmap_t));
 
1203
        return err;
 
1204
}
 
1205
 
 
1206
int
 
1207
vhd_has_batmap(vhd_context_t *ctx)
 
1208
{
 
1209
        if (!vhd_type_dynamic(ctx))
 
1210
                return 0;
 
1211
 
 
1212
        if (!vhd_creator_tapdisk(ctx))
 
1213
                return 0;
 
1214
 
 
1215
        if (ctx->footer.crtr_ver <= VHD_VERSION(0, 1))
 
1216
                return 0;
 
1217
 
 
1218
        if (ctx->footer.crtr_ver >= VHD_VERSION(1, 2))
 
1219
                return 1;
 
1220
 
 
1221
        /*
 
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.
 
1224
         */
 
1225
        if (!vhd_validate_batmap_header(&ctx->batmap))
 
1226
                return 1;
 
1227
 
 
1228
        if (vhd_read_batmap_header(ctx, &ctx->batmap))
 
1229
                return 0;
 
1230
 
 
1231
        return (!vhd_validate_batmap_header(&ctx->batmap));
 
1232
}
 
1233
 
 
1234
/* 
 
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.
 
1237
 */
 
1238
int
 
1239
vhd_test_file_fixed(const char *file, int *is_block)
 
1240
{
 
1241
        int err;
 
1242
        struct stat stats;
 
1243
 
 
1244
        err = stat(file, &stats);
 
1245
        if (err == -1)
 
1246
                return -errno;
 
1247
 
 
1248
        *is_block = !!(S_ISBLK(stats.st_mode));
 
1249
        return err;
 
1250
}
 
1251
 
 
1252
int
 
1253
vhd_find_parent(vhd_context_t *ctx, const char *parent, char **_location)
 
1254
{
 
1255
        int err;
 
1256
        char *location, *cpath, *cdir, *path;
 
1257
 
 
1258
        err        = 0;
 
1259
        path       = NULL;
 
1260
        cpath      = NULL;
 
1261
        location   = NULL;
 
1262
        *_location = NULL;
 
1263
 
 
1264
        if (!parent)
 
1265
                return -EINVAL;
 
1266
 
 
1267
        if (parent[0] == '/') {
 
1268
                if (!access(parent, R_OK)) {
 
1269
                        path = strdup(parent);
 
1270
                        if (!path)
 
1271
                                return -ENOMEM;
 
1272
                        *_location = path;
 
1273
                        return 0;
 
1274
                }
 
1275
        }
 
1276
 
 
1277
        /* check parent path relative to child's directory */
 
1278
        cpath = realpath(ctx->file, NULL);
 
1279
        if (!cpath) {
 
1280
                err = -errno;
 
1281
                goto out;
 
1282
        }
 
1283
 
 
1284
        cdir = dirname(cpath);
 
1285
        if (asprintf(&location, "%s/%s", cdir, parent) == -1) {
 
1286
                err = -errno;
 
1287
                location = NULL;
 
1288
                goto out;
 
1289
        }
 
1290
 
 
1291
        if (!access(location, R_OK)) {
 
1292
                path = realpath(location, NULL);
 
1293
                if (path) {
 
1294
                        *_location = path;
 
1295
                        return 0;
 
1296
                }
 
1297
        }
 
1298
        err = -errno;
 
1299
 
 
1300
out:
 
1301
        free(location);
 
1302
        free(cpath);
 
1303
        return err;
 
1304
}
 
1305
 
 
1306
static int 
 
1307
vhd_macx_encode_location(char *name, char **out, int *outlen)
 
1308
{
 
1309
        iconv_t cd;
 
1310
        int len, err;
 
1311
        size_t ibl, obl;
 
1312
        char *uri, *uri_utf8, *uri_utf8p, *ret;
 
1313
        const char *urip;
 
1314
 
 
1315
        err     = 0;
 
1316
        ret     = NULL;
 
1317
        *out    = NULL;
 
1318
        *outlen = 0;
 
1319
        len     = strlen(name) + strlen("file://");
 
1320
 
 
1321
        ibl     = len;
 
1322
        obl     = len;
 
1323
 
 
1324
        urip = uri = malloc(ibl + 1);
 
1325
        uri_utf8 = uri_utf8p = malloc(obl);
 
1326
 
 
1327
        if (!uri || !uri_utf8)
 
1328
                return -ENOMEM;
 
1329
 
 
1330
        cd = iconv_open("UTF-8", "ASCII");
 
1331
        if (cd == (iconv_t)-1) {
 
1332
                err = -errno;
 
1333
                goto out;
 
1334
        }
 
1335
 
 
1336
        snprintf(uri, ibl+1, "file://%s", name);
 
1337
 
 
1338
        if (iconv(cd,
 
1339
#ifdef __linux__
 
1340
            (char **)
 
1341
#endif
 
1342
            &urip, &ibl, &uri_utf8p, &obl) == (size_t)-1 ||
 
1343
            ibl || obl) {
 
1344
                err = (errno ? -errno : -EIO);
 
1345
                goto out;
 
1346
        }
 
1347
 
 
1348
        ret = malloc(len);
 
1349
        if (!ret) {
 
1350
                err = -ENOMEM;
 
1351
                goto out;
 
1352
        }
 
1353
 
 
1354
        memcpy(ret, uri_utf8, len);
 
1355
        *outlen = len;
 
1356
        *out    = ret;
 
1357
 
 
1358
 out:
 
1359
        free(uri);
 
1360
        free(uri_utf8);
 
1361
        if (cd != (iconv_t)-1)
 
1362
                iconv_close(cd);
 
1363
 
 
1364
        return err;
 
1365
}
 
1366
 
 
1367
static int
 
1368
vhd_w2u_encode_location(char *name, char **out, int *outlen)
 
1369
{
 
1370
        iconv_t cd;
 
1371
        int len, err;
 
1372
        size_t ibl, obl;
 
1373
        char *uri, *uri_utf16, *uri_utf16p, *tmp, *ret;
 
1374
        const char *urip;
 
1375
 
 
1376
        err     = 0;
 
1377
        ret     = NULL;
 
1378
        *out    = NULL;
 
1379
        *outlen = 0;
 
1380
        cd      = (iconv_t) -1;
 
1381
 
 
1382
        /* 
 
1383
         * MICROSOFT_COMPAT
 
1384
         * relative paths must start with ".\" 
 
1385
         */
 
1386
        if (name[0] != '/') {
 
1387
                tmp = strstr(name, "./");
 
1388
                if (tmp == name)
 
1389
                        tmp += strlen("./");
 
1390
                else
 
1391
                        tmp = name;
 
1392
 
 
1393
                err = asprintf(&uri, ".\\%s", tmp);
 
1394
        } else
 
1395
                err = asprintf(&uri, "%s", name);
 
1396
 
 
1397
        if (err == -1)
 
1398
                return -ENOMEM;
 
1399
 
 
1400
        tmp = uri;
 
1401
        while (*tmp != '\0') {
 
1402
                if (*tmp == '/')
 
1403
                        *tmp = '\\';
 
1404
                tmp++;
 
1405
        }
 
1406
 
 
1407
        len  = strlen(uri);
 
1408
        ibl  = len;
 
1409
        obl  = len * 2;
 
1410
        urip = uri;
 
1411
 
 
1412
        uri_utf16 = uri_utf16p = malloc(obl);
 
1413
        if (!uri_utf16) {
 
1414
                err = -ENOMEM;
 
1415
                goto out;
 
1416
        }
 
1417
 
 
1418
        /* 
 
1419
         * MICROSOFT_COMPAT
 
1420
         * little endian unicode here 
 
1421
         */
 
1422
        cd = iconv_open("UTF-16LE", "ASCII");
 
1423
        if (cd == (iconv_t)-1) {
 
1424
                err = -errno;
 
1425
                goto out;
 
1426
        }
 
1427
 
 
1428
        if (iconv(cd,
 
1429
#ifdef __linux__
 
1430
            (char **)
 
1431
#endif
 
1432
            &urip, &ibl, &uri_utf16p, &obl) == (size_t)-1 ||
 
1433
            ibl || obl) {
 
1434
                err = (errno ? -errno : -EIO);
 
1435
                goto out;
 
1436
        }
 
1437
 
 
1438
        len = len * 2;
 
1439
        ret = malloc(len);
 
1440
        if (!ret) {
 
1441
                err = -ENOMEM;
 
1442
                goto out;
 
1443
        }
 
1444
 
 
1445
        memcpy(ret, uri_utf16, len);
 
1446
        *outlen = len;
 
1447
        *out    = ret;
 
1448
        err     = 0;
 
1449
 
 
1450
 out:
 
1451
        free(uri);
 
1452
        free(uri_utf16);
 
1453
        if (cd != (iconv_t)-1)
 
1454
                iconv_close(cd);
 
1455
 
 
1456
        return err;
 
1457
}
 
1458
 
 
1459
static char *
 
1460
vhd_macx_decode_location(const char *in, char *out, int len)
 
1461
{
 
1462
        iconv_t cd;
 
1463
        char *name;
 
1464
        size_t ibl, obl;
 
1465
 
 
1466
        name = out;
 
1467
        ibl  = obl = len;
 
1468
 
 
1469
        cd = iconv_open("ASCII", "UTF-8");
 
1470
        if (cd == (iconv_t)-1) 
 
1471
                return NULL;
 
1472
 
 
1473
        if (iconv(cd,
 
1474
#ifdef __linux__
 
1475
                (char **)
 
1476
#endif
 
1477
                &in, &ibl, &out, &obl) == (size_t)-1 || ibl)
 
1478
                return NULL;
 
1479
 
 
1480
        iconv_close(cd);
 
1481
        *out = '\0';
 
1482
 
 
1483
        if (strstr(name, "file://") != name)
 
1484
                return NULL;
 
1485
 
 
1486
        name += strlen("file://");
 
1487
 
 
1488
        return strdup(name);
 
1489
}
 
1490
 
 
1491
static char *
 
1492
vhd_w2u_decode_location(const char *in, char *out, int len, char *utf_type)
 
1493
{
 
1494
        iconv_t cd;
 
1495
        char *name, *tmp;
 
1496
        size_t ibl, obl;
 
1497
 
 
1498
        tmp = name = out;
 
1499
        ibl = obl  = len;
 
1500
 
 
1501
        cd = iconv_open("ASCII", utf_type);
 
1502
        if (cd == (iconv_t)-1) 
 
1503
                return NULL;
 
1504
 
 
1505
        if (iconv(cd,
 
1506
#ifdef __linux__
 
1507
                (char **)
 
1508
#endif
 
1509
                &in, &ibl, &out, &obl) == (size_t)-1 || ibl)
 
1510
                return NULL;
 
1511
 
 
1512
        iconv_close(cd);
 
1513
        *out = '\0';
 
1514
 
 
1515
        /* TODO: spaces */
 
1516
        while (tmp != out) {
 
1517
                if (*tmp == '\\')
 
1518
                        *tmp = '/';
 
1519
                tmp++;
 
1520
        }
 
1521
 
 
1522
        if (strstr(name, "C:") == name || strstr(name, "c:") == name)
 
1523
                name += strlen("c:");
 
1524
 
 
1525
        return strdup(name);
 
1526
}
 
1527
 
 
1528
int
 
1529
vhd_header_decode_parent(vhd_context_t *ctx, vhd_header_t *header, char **buf)
 
1530
{
 
1531
        char *code, out[512];
 
1532
 
 
1533
        if (vhd_creator_tapdisk(ctx) &&
 
1534
            ctx->footer.crtr_ver == VHD_VERSION(0, 1))
 
1535
                code = UTF_16;
 
1536
        else
 
1537
                code = UTF_16BE;
 
1538
 
 
1539
        *buf = vhd_w2u_decode_location(header->prt_name, out, 512, code);
 
1540
        return (*buf == NULL ? -EINVAL : 0);
 
1541
}
 
1542
 
 
1543
int
 
1544
vhd_parent_locator_read(vhd_context_t *ctx,
 
1545
                        vhd_parent_locator_t *loc, char **parent)
 
1546
{
 
1547
        int err, size;
 
1548
        char *raw, *out, *name;
 
1549
 
 
1550
        raw     = NULL;
 
1551
        out     = NULL;
 
1552
        name    = NULL;
 
1553
        *parent = NULL;
 
1554
 
 
1555
        if (ctx->footer.type != HD_TYPE_DIFF) {
 
1556
                err = -EINVAL;
 
1557
                goto out;
 
1558
        }
 
1559
 
 
1560
        switch (loc->code) {
 
1561
        case PLAT_CODE_MACX:
 
1562
        case PLAT_CODE_W2KU:
 
1563
        case PLAT_CODE_W2RU:
 
1564
                break;
 
1565
        default:
 
1566
                err = -EINVAL;
 
1567
                goto out;
 
1568
        }
 
1569
 
 
1570
        err = vhd_seek(ctx, loc->data_offset, SEEK_SET);
 
1571
        if (err)
 
1572
                goto out;
 
1573
 
 
1574
        size = vhd_parent_locator_size(loc);
 
1575
        if (size <= 0) {
 
1576
                err = -EINVAL;
 
1577
                goto out;
 
1578
        }
 
1579
 
 
1580
        err = posix_memalign((void **)&raw, VHD_SECTOR_SIZE, size);
 
1581
        if (err) {
 
1582
                raw = NULL;
 
1583
                err = -err;
 
1584
                goto out;
 
1585
        }
 
1586
 
 
1587
        err = vhd_read(ctx, raw, size);
 
1588
        if (err)
 
1589
                goto out;
 
1590
 
 
1591
        out = malloc(loc->data_len + 1);
 
1592
        if (!out) {
 
1593
                err = -ENOMEM;
 
1594
                goto out;
 
1595
        }
 
1596
 
 
1597
        switch (loc->code) {
 
1598
        case PLAT_CODE_MACX:
 
1599
                name = vhd_macx_decode_location(raw, out, loc->data_len);
 
1600
                break;
 
1601
        case PLAT_CODE_W2KU:
 
1602
        case PLAT_CODE_W2RU:
 
1603
                name = vhd_w2u_decode_location(raw, out,
 
1604
                                               loc->data_len, UTF_16LE);
 
1605
                break;
 
1606
        }
 
1607
 
 
1608
        if (!name) {
 
1609
                err = -EINVAL;
 
1610
                goto out;
 
1611
        }
 
1612
 
 
1613
        err     = 0;
 
1614
        *parent = name;
 
1615
 
 
1616
out:
 
1617
        free(raw);
 
1618
        free(out);
 
1619
 
 
1620
        if (err) {
 
1621
                VHDLOG("%s: error reading parent locator: %d\n",
 
1622
                       ctx->file, err);
 
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);
 
1626
        }
 
1627
 
 
1628
        return err;
 
1629
}
 
1630
 
 
1631
int
 
1632
vhd_parent_locator_get(vhd_context_t *ctx, char **parent)
 
1633
{
 
1634
        int i, n, err;
 
1635
        char *name, *location;
 
1636
        vhd_parent_locator_t *loc;
 
1637
 
 
1638
        err     = 0;
 
1639
        *parent = NULL;
 
1640
 
 
1641
        if (ctx->footer.type != HD_TYPE_DIFF)
 
1642
                return -EINVAL;
 
1643
 
 
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);
 
1648
                if (err)
 
1649
                        continue;
 
1650
 
 
1651
                err = vhd_find_parent(ctx, name, &location);
 
1652
                if (err)
 
1653
                        VHDLOG("%s: couldn't find parent %s (%d)\n",
 
1654
                               ctx->file, name, err);
 
1655
                free(name);
 
1656
 
 
1657
                if (!err) {
 
1658
                        *parent = location;
 
1659
                        return 0;
 
1660
                }
 
1661
        }
 
1662
 
 
1663
        return err;
 
1664
}
 
1665
 
 
1666
int
 
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)
 
1670
{
 
1671
        struct stat stats;
 
1672
        int err, len, size;
 
1673
        char *absolute_path, *relative_path, *encoded, *block;
 
1674
 
 
1675
        memset(loc, 0, sizeof(vhd_parent_locator_t));
 
1676
 
 
1677
        if (ctx->footer.type != HD_TYPE_DIFF)
 
1678
                return -EINVAL;
 
1679
 
 
1680
        absolute_path = NULL;
 
1681
        relative_path = NULL;
 
1682
        encoded       = NULL;
 
1683
        block         = NULL;
 
1684
        size          = 0;
 
1685
        len           = 0;
 
1686
 
 
1687
        switch (code) {
 
1688
        case PLAT_CODE_MACX:
 
1689
        case PLAT_CODE_W2KU:
 
1690
        case PLAT_CODE_W2RU:
 
1691
                break;
 
1692
        default:
 
1693
                return -EINVAL;
 
1694
        }
 
1695
 
 
1696
        absolute_path = realpath(parent, NULL);
 
1697
        if (!absolute_path) {
 
1698
                err = -errno;
 
1699
                goto out;
 
1700
        }
 
1701
 
 
1702
        err = stat(absolute_path, &stats);
 
1703
        if (err) {
 
1704
                err = -errno;
 
1705
                goto out;
 
1706
        }
 
1707
 
 
1708
        if (!S_ISREG(stats.st_mode) && !S_ISBLK(stats.st_mode)) {
 
1709
                err = -EINVAL;
 
1710
                goto out;
 
1711
        }
 
1712
 
 
1713
        relative_path = relative_path_to(ctx->file, absolute_path, &err);
 
1714
        if (!relative_path || err) {
 
1715
                err = (err ? err : -EINVAL);
 
1716
                goto out;
 
1717
        }
 
1718
 
 
1719
        switch (code) {
 
1720
        case PLAT_CODE_MACX:
 
1721
                err = vhd_macx_encode_location(relative_path, &encoded, &len);
 
1722
                break;
 
1723
        case PLAT_CODE_W2KU:
 
1724
        case PLAT_CODE_W2RU:
 
1725
                err = vhd_w2u_encode_location(relative_path, &encoded, &len);
 
1726
                break;
 
1727
        default:
 
1728
                err = -EINVAL;
 
1729
        }
 
1730
 
 
1731
        if (err)
 
1732
                goto out;
 
1733
 
 
1734
        err = vhd_seek(ctx, off, SEEK_SET);
 
1735
        if (err)
 
1736
                goto out;
 
1737
 
 
1738
        size = vhd_bytes_padded(len);
 
1739
 
 
1740
        if (max_bytes && size > max_bytes) {
 
1741
                err = -ENAMETOOLONG;
 
1742
                goto out;
 
1743
        }
 
1744
 
 
1745
        err  = posix_memalign((void **)&block, VHD_SECTOR_SIZE, size);
 
1746
        if (err) {
 
1747
                block = NULL;
 
1748
                err   = -err;
 
1749
                goto out;
 
1750
        }
 
1751
 
 
1752
        memset(block, 0, size);
 
1753
        memcpy(block, encoded, len);
 
1754
 
 
1755
        err = vhd_write(ctx, block, size);
 
1756
        if (err)
 
1757
                goto out;
 
1758
 
 
1759
        err = 0;
 
1760
 
 
1761
out:
 
1762
        free(absolute_path);
 
1763
        free(relative_path);
 
1764
        free(encoded);
 
1765
        free(block);
 
1766
 
 
1767
        if (!err) {
 
1768
                loc->res         = 0;
 
1769
                loc->code        = code;
 
1770
                loc->data_len    = len;
 
1771
                /*
 
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
 
1775
                 */
 
1776
                loc->data_space  = size; 
 
1777
                loc->data_offset = off;
 
1778
        }
 
1779
 
 
1780
        return err;
 
1781
}
 
1782
 
 
1783
static int
 
1784
vhd_footer_offset_at_eof(vhd_context_t *ctx, off_t *off)
 
1785
{
 
1786
        int err;
 
1787
        if ((err = vhd_seek(ctx, 0, SEEK_END)))
 
1788
                return errno;
 
1789
        *off = vhd_position(ctx) - sizeof(vhd_footer_t);
 
1790
        return 0;
 
1791
}
 
1792
 
 
1793
int
 
1794
vhd_read_bitmap(vhd_context_t *ctx, uint32_t block, char **bufp)
 
1795
{
 
1796
        int err;
 
1797
        char *buf;
 
1798
        size_t size;
 
1799
        off_t off;
 
1800
        uint64_t blk;
 
1801
 
 
1802
        buf   = NULL;
 
1803
        *bufp = NULL;
 
1804
 
 
1805
        if (!vhd_type_dynamic(ctx))
 
1806
                return -EINVAL;
 
1807
 
 
1808
        err = vhd_get_bat(ctx);
 
1809
        if (err)
 
1810
                return err;
 
1811
 
 
1812
        if (block >= ctx->bat.entries)
 
1813
                return -ERANGE;
 
1814
 
 
1815
        blk  = ctx->bat.bat[block];
 
1816
        if (blk == DD_BLK_UNUSED)
 
1817
                return -EINVAL;
 
1818
 
 
1819
        off  = vhd_sectors_to_bytes(blk);
 
1820
        size = vhd_bytes_padded(ctx->spb >> 3);
 
1821
 
 
1822
        err  = vhd_seek(ctx, off, SEEK_SET);
 
1823
        if (err)
 
1824
                return err;
 
1825
 
 
1826
        err  = posix_memalign((void **)&buf, VHD_SECTOR_SIZE, size);
 
1827
        if (err)
 
1828
                return -err;
 
1829
 
 
1830
        err  = vhd_read(ctx, buf, size);
 
1831
        if (err)
 
1832
                goto fail;
 
1833
 
 
1834
        *bufp = buf;
 
1835
        return 0;
 
1836
 
 
1837
fail:
 
1838
        free(buf);
 
1839
        return err;
 
1840
}
 
1841
 
 
1842
int
 
1843
vhd_read_block(vhd_context_t *ctx, uint32_t block, char **bufp)
 
1844
{
 
1845
        int err;
 
1846
        char *buf;
 
1847
        size_t size;
 
1848
        uint64_t blk;
 
1849
        off_t end, off;
 
1850
 
 
1851
        buf   = NULL;
 
1852
        *bufp = NULL;
 
1853
 
 
1854
        if (!vhd_type_dynamic(ctx))
 
1855
                return -EINVAL;
 
1856
 
 
1857
        err = vhd_get_bat(ctx);
 
1858
        if (err)
 
1859
                return err;
 
1860
 
 
1861
        if (block >= ctx->bat.entries)
 
1862
                return -ERANGE;
 
1863
 
 
1864
        blk  = ctx->bat.bat[block];
 
1865
        if (blk == DD_BLK_UNUSED)
 
1866
                return -EINVAL;
 
1867
 
 
1868
        off  = vhd_sectors_to_bytes(blk + ctx->bm_secs);
 
1869
        size = vhd_sectors_to_bytes(ctx->spb);
 
1870
 
 
1871
        err  = vhd_footer_offset_at_eof(ctx, &end);
 
1872
        if (err)
 
1873
                return err;
 
1874
 
 
1875
        err  = posix_memalign((void **)&buf, VHD_SECTOR_SIZE, size);
 
1876
        if (err) {
 
1877
                err = -err;
 
1878
                goto fail;
 
1879
        }
 
1880
 
 
1881
        if (end < off + ctx->header.block_size) {
 
1882
                size = end - off;
 
1883
                memset(buf + size, 0, ctx->header.block_size - size);
 
1884
        }
 
1885
 
 
1886
        err  = vhd_seek(ctx, off, SEEK_SET);
 
1887
        if (err)
 
1888
                goto fail;
 
1889
 
 
1890
        err  = vhd_read(ctx, buf, size);
 
1891
        if (err)
 
1892
                goto fail;
 
1893
 
 
1894
        *bufp = buf;
 
1895
        return 0;
 
1896
 
 
1897
fail:
 
1898
        free(buf);
 
1899
        return err;
 
1900
}
 
1901
 
 
1902
int
 
1903
vhd_write_footer_at(vhd_context_t *ctx, vhd_footer_t *footer, off_t off)
 
1904
{
 
1905
        int err;
 
1906
        vhd_footer_t *f;
 
1907
 
 
1908
        f = NULL;
 
1909
 
 
1910
        err = posix_memalign((void **)&f,
 
1911
                             VHD_SECTOR_SIZE, sizeof(vhd_footer_t));
 
1912
        if (err) {
 
1913
                f   = NULL;
 
1914
                err = -err;
 
1915
                goto out;
 
1916
        }
 
1917
 
 
1918
        memcpy(f, footer, sizeof(vhd_footer_t));
 
1919
        f->checksum = vhd_checksum_footer(f);
 
1920
 
 
1921
        err = vhd_validate_footer(f);
 
1922
        if (err)
 
1923
                goto out;
 
1924
 
 
1925
        err = vhd_seek(ctx, off, SEEK_SET);
 
1926
        if (err)
 
1927
                goto out;
 
1928
 
 
1929
        vhd_footer_out(f);
 
1930
 
 
1931
        err = vhd_write(ctx, f, sizeof(vhd_footer_t));
 
1932
 
 
1933
out:
 
1934
        if (err)
 
1935
                VHDLOG("%s: failed writing footer at 0x%08"PRIx64": %d\n",
 
1936
                       ctx->file, off, err);
 
1937
        free(f);
 
1938
        return err;
 
1939
}
 
1940
 
 
1941
int
 
1942
vhd_write_footer(vhd_context_t *ctx, vhd_footer_t *footer)
 
1943
{
 
1944
        int err;
 
1945
        off_t off;
 
1946
 
 
1947
        if (ctx->is_block)
 
1948
                err = vhd_footer_offset_at_eof(ctx, &off);
 
1949
        else
 
1950
                err = vhd_end_of_data(ctx, &off);
 
1951
        if (err)
 
1952
                return err;
 
1953
 
 
1954
        err = vhd_write_footer_at(ctx, footer, off);
 
1955
        if (err)
 
1956
                return err;
 
1957
 
 
1958
        if (!vhd_type_dynamic(ctx))
 
1959
                return 0;
 
1960
 
 
1961
        return vhd_write_footer_at(ctx, footer, 0);
 
1962
}
 
1963
 
 
1964
int
 
1965
vhd_write_header_at(vhd_context_t *ctx, vhd_header_t *header, off_t off)
 
1966
{
 
1967
        int err;
 
1968
        vhd_header_t *h;
 
1969
 
 
1970
        h = NULL;
 
1971
 
 
1972
        if (!vhd_type_dynamic(ctx)) {
 
1973
                err = -EINVAL;
 
1974
                goto out;
 
1975
        }
 
1976
 
 
1977
        err = posix_memalign((void **)&h,
 
1978
                             VHD_SECTOR_SIZE, sizeof(vhd_header_t));
 
1979
        if (err) {
 
1980
                h   = NULL;
 
1981
                err = -err;
 
1982
                goto out;
 
1983
        }
 
1984
 
 
1985
        memcpy(h, header, sizeof(vhd_header_t));
 
1986
 
 
1987
        h->checksum = vhd_checksum_header(h);
 
1988
        err = vhd_validate_header(h);
 
1989
        if (err)
 
1990
                goto out;
 
1991
 
 
1992
        vhd_header_out(h);
 
1993
 
 
1994
        err = vhd_seek(ctx, off, SEEK_SET);
 
1995
        if (err)
 
1996
                goto out;
 
1997
 
 
1998
        err = vhd_write(ctx, h, sizeof(vhd_header_t));
 
1999
 
 
2000
out:
 
2001
        if (err)
 
2002
                VHDLOG("%s: failed writing header at 0x%08"PRIx64": %d\n",
 
2003
                       ctx->file, off, err);
 
2004
        free(h);
 
2005
        return err;
 
2006
}
 
2007
 
 
2008
int
 
2009
vhd_write_header(vhd_context_t *ctx, vhd_header_t *header)
 
2010
{
 
2011
        int err;
 
2012
        off_t off;
 
2013
 
 
2014
        if (!vhd_type_dynamic(ctx))
 
2015
                return -EINVAL;
 
2016
 
 
2017
        off = ctx->footer.data_offset;
 
2018
        return vhd_write_header_at(ctx, header, off);
 
2019
}
 
2020
 
 
2021
int
 
2022
vhd_write_bat(vhd_context_t *ctx, vhd_bat_t *bat)
 
2023
{
 
2024
        int err;
 
2025
        off_t off;
 
2026
        vhd_bat_t b;
 
2027
        size_t size;
 
2028
 
 
2029
        if (!vhd_type_dynamic(ctx))
 
2030
                return -EINVAL;
 
2031
 
 
2032
        err = vhd_validate_bat(&ctx->bat);
 
2033
        if (err)
 
2034
                return err;
 
2035
 
 
2036
        err = vhd_validate_bat(bat);
 
2037
        if (err)
 
2038
                return err;
 
2039
 
 
2040
        memset(&b, 0, sizeof(vhd_bat_t));
 
2041
 
 
2042
        off  = ctx->header.table_offset;
 
2043
        size = vhd_bytes_padded(bat->entries * sizeof(uint32_t));
 
2044
 
 
2045
        err  = vhd_seek(ctx, off, SEEK_SET);
 
2046
        if (err)
 
2047
                return err;
 
2048
 
 
2049
        err  = posix_memalign((void **)&b.bat, VHD_SECTOR_SIZE, size);
 
2050
        if (err)
 
2051
                return -err;
 
2052
 
 
2053
        memcpy(b.bat, bat->bat, size);
 
2054
        b.spb     = bat->spb;
 
2055
        b.entries = bat->entries;
 
2056
        vhd_bat_out(&b);
 
2057
 
 
2058
        err = vhd_write(ctx, b.bat, size);
 
2059
        free(b.bat);
 
2060
 
 
2061
        return err;
 
2062
}
 
2063
 
 
2064
int
 
2065
vhd_write_batmap(vhd_context_t *ctx, vhd_batmap_t *batmap)
 
2066
{
 
2067
        int err;
 
2068
        off_t off;
 
2069
        vhd_batmap_t b;
 
2070
        char *buf, *map;
 
2071
        size_t size, map_size;
 
2072
 
 
2073
        buf      = NULL;
 
2074
        map      = NULL;
 
2075
 
 
2076
        if (!vhd_has_batmap(ctx)) {
 
2077
                err = -EINVAL;
 
2078
                goto out;
 
2079
        }
 
2080
 
 
2081
        b.header = batmap->header;
 
2082
        b.map    = batmap->map;
 
2083
 
 
2084
        b.header.checksum = vhd_checksum_batmap(&b);
 
2085
        err = vhd_validate_batmap(&b);
 
2086
        if (err)
 
2087
                goto out;
 
2088
 
 
2089
        off      = b.header.batmap_offset;
 
2090
        map_size = vhd_sectors_to_bytes(b.header.batmap_size);
 
2091
 
 
2092
        err  = vhd_seek(ctx, off, SEEK_SET);
 
2093
        if (err)
 
2094
                goto out;
 
2095
 
 
2096
        err  = posix_memalign((void **)&map, VHD_SECTOR_SIZE, map_size);
 
2097
        if (err) {
 
2098
                map = NULL;
 
2099
                err = -err;
 
2100
                goto out;
 
2101
        }
 
2102
 
 
2103
        memcpy(map, b.map, map_size);
 
2104
 
 
2105
        err  = vhd_write(ctx, map, map_size);
 
2106
        if (err)
 
2107
                goto out;
 
2108
 
 
2109
        err  = vhd_batmap_header_offset(ctx, &off);
 
2110
        if (err)
 
2111
                goto out;
 
2112
 
 
2113
        size = vhd_bytes_padded(sizeof(vhd_batmap_header_t));
 
2114
 
 
2115
        err  = vhd_seek(ctx, off, SEEK_SET);
 
2116
        if (err)
 
2117
                goto out;
 
2118
 
 
2119
        err  = posix_memalign((void **)&buf, VHD_SECTOR_SIZE, size);
 
2120
        if (err) {
 
2121
                err = -err;
 
2122
                buf = NULL;
 
2123
                goto out;
 
2124
        }
 
2125
 
 
2126
        vhd_batmap_header_out(&b);
 
2127
        memset(buf, 0, size);
 
2128
        memcpy(buf, &b.header, sizeof(vhd_batmap_header_t));
 
2129
 
 
2130
        err  = vhd_write(ctx, buf, size);
 
2131
 
 
2132
out:
 
2133
        if (err)
 
2134
                VHDLOG("%s: failed writing batmap: %d\n", ctx->file, err);
 
2135
        free(buf);
 
2136
        free(map);
 
2137
        return 0;
 
2138
}
 
2139
 
 
2140
int
 
2141
vhd_write_bitmap(vhd_context_t *ctx, uint32_t block, char *bitmap)
 
2142
{
 
2143
        int err;
 
2144
        off_t off;
 
2145
        uint64_t blk;
 
2146
        size_t secs, size;
 
2147
 
 
2148
        if (!vhd_type_dynamic(ctx))
 
2149
                return -EINVAL;
 
2150
 
 
2151
        err = vhd_validate_bat(&ctx->bat);
 
2152
        if (err)
 
2153
                return err;
 
2154
 
 
2155
        if (block >= ctx->bat.entries)
 
2156
                return -ERANGE;
 
2157
 
 
2158
        if ((unsigned long)bitmap & (VHD_SECTOR_SIZE - 1))
 
2159
                return -EINVAL;
 
2160
 
 
2161
        blk  = ctx->bat.bat[block];
 
2162
        if (blk == DD_BLK_UNUSED)
 
2163
                return -EINVAL;
 
2164
 
 
2165
        off  = vhd_sectors_to_bytes(blk);
 
2166
        size = vhd_sectors_to_bytes(ctx->bm_secs);
 
2167
 
 
2168
        err  = vhd_seek(ctx, off, SEEK_SET);
 
2169
        if (err)
 
2170
                return err;
 
2171
 
 
2172
        err  = vhd_write(ctx, bitmap, size);
 
2173
        if (err)
 
2174
                return err;
 
2175
 
 
2176
        return 0;
 
2177
}
 
2178
 
 
2179
int
 
2180
vhd_write_block(vhd_context_t *ctx, uint32_t block, char *data)
 
2181
{
 
2182
        int err;
 
2183
        off_t off;
 
2184
        size_t size;
 
2185
        uint64_t blk;
 
2186
 
 
2187
        if (!vhd_type_dynamic(ctx))
 
2188
                return -EINVAL;
 
2189
 
 
2190
        err = vhd_validate_bat(&ctx->bat);
 
2191
        if (err)
 
2192
                return err;
 
2193
 
 
2194
        if (block >= ctx->bat.entries)
 
2195
                return -ERANGE;
 
2196
 
 
2197
        if ((unsigned long)data & ~(VHD_SECTOR_SIZE -1))
 
2198
                return -EINVAL;
 
2199
 
 
2200
        blk  = ctx->bat.bat[block];
 
2201
        if (blk == DD_BLK_UNUSED)
 
2202
                return -EINVAL;
 
2203
 
 
2204
        off  = vhd_sectors_to_bytes(blk + ctx->bm_secs);
 
2205
        size = vhd_sectors_to_bytes(ctx->spb);
 
2206
 
 
2207
        err  = vhd_seek(ctx, off, SEEK_SET);
 
2208
        if (err)
 
2209
                return err;
 
2210
 
 
2211
        err  = vhd_write(ctx, data, size);
 
2212
        if (err)
 
2213
                return err;
 
2214
 
 
2215
        return 0;
 
2216
}
 
2217
 
 
2218
static inline int
 
2219
namedup(char **dup, const char *name)
 
2220
{
 
2221
        *dup = NULL;
 
2222
 
 
2223
        if (strnlen(name, MAX_NAME_LEN) >= MAX_NAME_LEN)
 
2224
                return -ENAMETOOLONG;
 
2225
        
 
2226
        *dup = strdup(name);
 
2227
        if (*dup == NULL)
 
2228
                return -ENOMEM;
 
2229
 
 
2230
        return 0;
 
2231
}
 
2232
 
 
2233
int
 
2234
vhd_seek(vhd_context_t *ctx, off_t offset, int whence)
 
2235
{
 
2236
        off_t off;
 
2237
 
 
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);
 
2242
                return -errno;
 
2243
        }
 
2244
 
 
2245
        return 0;
 
2246
}
 
2247
 
 
2248
off_t
 
2249
vhd_position(vhd_context_t *ctx)
 
2250
{
 
2251
        return lseek(ctx->fd, 0, SEEK_CUR);
 
2252
}
 
2253
 
 
2254
int
 
2255
vhd_read(vhd_context_t *ctx, void *buf, size_t size)
 
2256
{
 
2257
        size_t ret;
 
2258
 
 
2259
        errno = 0;
 
2260
 
 
2261
        ret = read(ctx->fd, buf, size);
 
2262
        if (ret == size)
 
2263
                return 0;
 
2264
 
 
2265
        VHDLOG("%s: read of %zu returned %zd, errno: %d\n",
 
2266
               ctx->file, size, ret, -errno);
 
2267
 
 
2268
        return (errno ? -errno : -EIO);
 
2269
}
 
2270
 
 
2271
int
 
2272
vhd_write(vhd_context_t *ctx, void *buf, size_t size)
 
2273
{
 
2274
        size_t ret;
 
2275
 
 
2276
        errno = 0;
 
2277
 
 
2278
        ret = write(ctx->fd, buf, size);
 
2279
        if (ret == size)
 
2280
                return 0;
 
2281
 
 
2282
        VHDLOG("%s: write of %zu returned %zd, errno: %d\n",
 
2283
               ctx->file, size, ret, -errno);
 
2284
 
 
2285
        return (errno ? -errno : -EIO);
 
2286
}
 
2287
 
 
2288
int
 
2289
vhd_offset(vhd_context_t *ctx, uint32_t sector, uint32_t *offset)
 
2290
{
 
2291
        int err;
 
2292
        uint32_t block;
 
2293
 
 
2294
        if (!vhd_type_dynamic(ctx))
 
2295
                return sector;
 
2296
 
 
2297
        err = vhd_get_bat(ctx);
 
2298
        if (err)
 
2299
                return err;
 
2300
 
 
2301
        block = sector / ctx->spb;
 
2302
        if (ctx->bat.bat[block] == DD_BLK_UNUSED)
 
2303
                *offset = DD_BLK_UNUSED;
 
2304
        else
 
2305
                *offset = ctx->bat.bat[block] +
 
2306
                        ctx->bm_secs + (sector % ctx->spb);
 
2307
 
 
2308
        return 0;
 
2309
}
 
2310
 
 
2311
int
 
2312
vhd_open_fast(vhd_context_t *ctx)
 
2313
{
 
2314
        int err;
 
2315
        char *buf;
 
2316
        size_t size;
 
2317
 
 
2318
        size = sizeof(vhd_footer_t) + sizeof(vhd_header_t);
 
2319
        err  = posix_memalign((void **)&buf, VHD_SECTOR_SIZE, size);
 
2320
        if (err) {
 
2321
                VHDLOG("failed allocating %s: %d\n", ctx->file, -err);
 
2322
                return -err;
 
2323
        }
 
2324
 
 
2325
        err = vhd_read(ctx, buf, size);
 
2326
        if (err) {
 
2327
                VHDLOG("failed reading %s: %d\n", ctx->file, err);
 
2328
                goto out;
 
2329
        }
 
2330
 
 
2331
        memcpy(&ctx->footer, buf, sizeof(vhd_footer_t));
 
2332
        vhd_footer_in(&ctx->footer);
 
2333
        err = vhd_validate_footer(&ctx->footer);
 
2334
        if (err)
 
2335
                goto out;
 
2336
 
 
2337
        if (vhd_type_dynamic(ctx)) {
 
2338
                if (ctx->footer.data_offset != sizeof(vhd_footer_t))
 
2339
                        err = vhd_read_header(ctx, &ctx->header);
 
2340
                else {
 
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);
 
2346
                }
 
2347
 
 
2348
                if (err)
 
2349
                        goto out;
 
2350
 
 
2351
                ctx->spb     = ctx->header.block_size >> VHD_SECTOR_SHIFT;
 
2352
                ctx->bm_secs = secs_round_up_no_zero(ctx->spb >> 3);
 
2353
        }
 
2354
 
 
2355
out:
 
2356
        free(buf);
 
2357
        return err;
 
2358
}
 
2359
 
 
2360
int
 
2361
vhd_open(vhd_context_t *ctx, const char *file, int flags)
 
2362
{
 
2363
        int err, oflags;
 
2364
 
 
2365
        if (flags & VHD_OPEN_STRICT)
 
2366
                vhd_flag_clear(flags, VHD_OPEN_FAST);
 
2367
 
 
2368
        memset(ctx, 0, sizeof(vhd_context_t));
 
2369
        ctx->fd     = -1;
 
2370
        ctx->oflags = flags;
 
2371
 
 
2372
        err = namedup(&ctx->file, file);
 
2373
        if (err)
 
2374
                return err;
 
2375
 
 
2376
        oflags = O_DIRECT | O_LARGEFILE;
 
2377
        if (flags & VHD_OPEN_RDONLY)
 
2378
                oflags |= O_RDONLY;
 
2379
        if (flags & VHD_OPEN_RDWR)
 
2380
                oflags |= O_RDWR;
 
2381
 
 
2382
        ctx->fd = open(ctx->file, oflags, 0644);
 
2383
        if (ctx->fd == -1) {
 
2384
                err = -errno;
 
2385
                VHDLOG("failed to open %s: %d\n", ctx->file, err);
 
2386
                goto fail;
 
2387
        }
 
2388
 
 
2389
        err = vhd_test_file_fixed(ctx->file, &ctx->is_block);
 
2390
        if (err)
 
2391
                goto fail;
 
2392
 
 
2393
        if (flags & VHD_OPEN_FAST) {
 
2394
                err = vhd_open_fast(ctx);
 
2395
                if (err)
 
2396
                        goto fail;
 
2397
 
 
2398
                return 0;
 
2399
        }
 
2400
 
 
2401
        err = vhd_read_footer(ctx, &ctx->footer);
 
2402
        if (err)
 
2403
                goto fail;
 
2404
 
 
2405
        if (!(flags & VHD_OPEN_IGNORE_DISABLED) && vhd_disabled(ctx)) {
 
2406
                err = -EINVAL;
 
2407
                goto fail;
 
2408
        }
 
2409
 
 
2410
        if (vhd_type_dynamic(ctx)) {
 
2411
                err = vhd_read_header(ctx, &ctx->header);
 
2412
                if (err)
 
2413
                        goto fail;
 
2414
 
 
2415
                ctx->spb     = ctx->header.block_size >> VHD_SECTOR_SHIFT;
 
2416
                ctx->bm_secs = secs_round_up_no_zero(ctx->spb >> 3);
 
2417
        }
 
2418
 
 
2419
        return 0;
 
2420
 
 
2421
fail:
 
2422
        if (ctx->fd != -1)
 
2423
                close(ctx->fd);
 
2424
        free(ctx->file);
 
2425
        memset(ctx, 0, sizeof(vhd_context_t));
 
2426
        return err;
 
2427
}
 
2428
 
 
2429
void
 
2430
vhd_close(vhd_context_t *ctx)
 
2431
{
 
2432
        if (ctx->file)
 
2433
                close(ctx->fd);
 
2434
        free(ctx->file);
 
2435
        free(ctx->bat.bat);
 
2436
        free(ctx->batmap.map);
 
2437
        memset(ctx, 0, sizeof(vhd_context_t));
 
2438
}
 
2439
 
 
2440
static inline void
 
2441
vhd_initialize_footer(vhd_context_t *ctx, int type, uint64_t size)
 
2442
{
 
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);
 
2458
}
 
2459
 
 
2460
static int
 
2461
vhd_initialize_header_parent_name(vhd_context_t *ctx, const char *parent_path)
 
2462
{
 
2463
        int err;
 
2464
        iconv_t cd;
 
2465
        size_t ibl, obl;
 
2466
        char *ppath, *dst;
 
2467
        const char *pname;
 
2468
 
 
2469
        err   = 0;
 
2470
        pname = NULL;
 
2471
        ppath = NULL;
 
2472
 
 
2473
        /*
 
2474
         * MICROSOFT_COMPAT
 
2475
         * big endian unicode here 
 
2476
         */
 
2477
        cd = iconv_open(UTF_16BE, "ASCII");
 
2478
        if (cd == (iconv_t)-1) {
 
2479
                err = -errno;
 
2480
                goto out;
 
2481
        }
 
2482
 
 
2483
        ppath = strdup(parent_path);
 
2484
        if (!ppath) {
 
2485
                err = -ENOMEM;
 
2486
                goto out;
 
2487
        }
 
2488
 
 
2489
        pname = basename(ppath);
 
2490
        if (!strcmp(pname, "")) {
 
2491
                err = -EINVAL;
 
2492
                goto out;
 
2493
        }
 
2494
 
 
2495
        ibl = strlen(pname);
 
2496
        obl = sizeof(ctx->header.prt_name);
 
2497
        dst = ctx->header.prt_name;
 
2498
 
 
2499
        memset(dst, 0, obl);
 
2500
 
 
2501
        if (iconv(cd,
 
2502
#ifdef __linux__
 
2503
                (char **)
 
2504
#endif
 
2505
                &pname, &ibl, &dst, &obl) == (size_t)-1 || ibl)
 
2506
                err = (errno ? -errno : -EINVAL);
 
2507
 
 
2508
out:
 
2509
        iconv_close(cd);
 
2510
        free(ppath);
 
2511
        return err;
 
2512
}
 
2513
 
 
2514
static off_t
 
2515
get_file_size(const char *name)
 
2516
{
 
2517
        int fd;
 
2518
        off_t end;
 
2519
 
 
2520
        fd = open(name, O_LARGEFILE | O_RDONLY);
 
2521
        if (fd == -1) {
 
2522
                VHDLOG("unable to open '%s': %d\n", name, errno);
 
2523
                return -errno;
 
2524
        }
 
2525
        end = lseek(fd, 0, SEEK_END);
 
2526
        close(fd); 
 
2527
        return end;
 
2528
}
 
2529
 
 
2530
static int
 
2531
vhd_initialize_header(vhd_context_t *ctx, const char *parent_path, 
 
2532
                uint64_t size, int raw)
 
2533
{
 
2534
        int err;
 
2535
        struct stat stats;
 
2536
        vhd_context_t parent;
 
2537
 
 
2538
        if (!vhd_type_dynamic(ctx))
 
2539
                return -EINVAL;
 
2540
 
 
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;
 
2551
 
 
2552
        ctx->footer.data_offset  = VHD_SECTOR_SIZE;
 
2553
 
 
2554
        if (ctx->footer.type == HD_TYPE_DYNAMIC)
 
2555
                return 0;
 
2556
 
 
2557
        err = stat(parent_path, &stats);
 
2558
        if (err == -1)
 
2559
                return -errno;
 
2560
 
 
2561
        if (raw) {
 
2562
                ctx->header.prt_ts = vhd_time(stats.st_mtime);
 
2563
                if (!size)
 
2564
                        size = get_file_size(parent_path);
 
2565
        }
 
2566
        else {
 
2567
                err = vhd_open(&parent, parent_path, VHD_OPEN_RDONLY);
 
2568
                if (err)
 
2569
                        return err;
 
2570
 
 
2571
                ctx->header.prt_ts = vhd_time(stats.st_mtime);
 
2572
                blk_uuid_copy(&ctx->header.prt_uuid, &parent.footer.uuid);
 
2573
                if (!size)
 
2574
                        size = parent.footer.curr_size;
 
2575
                vhd_close(&parent);
 
2576
        }
 
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;
 
2582
 
 
2583
        return vhd_initialize_header_parent_name(ctx, parent_path);
 
2584
}
 
2585
 
 
2586
static int
 
2587
vhd_write_parent_locators(vhd_context_t *ctx, const char *parent)
 
2588
{
 
2589
        int i, err;
 
2590
        off_t off;
 
2591
        uint32_t code;
 
2592
 
 
2593
        code = PLAT_CODE_NONE;
 
2594
 
 
2595
        if (ctx->footer.type != HD_TYPE_DIFF)
 
2596
                return -EINVAL;
 
2597
 
 
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);
 
2602
 
 
2603
        for (i = 0; i < 3; i++) {
 
2604
                switch (i) {
 
2605
                case 0:
 
2606
                        code = PLAT_CODE_MACX;
 
2607
                        break;
 
2608
                case 1:
 
2609
                        code = PLAT_CODE_W2KU;
 
2610
                        break;
 
2611
                case 2:
 
2612
                        code = PLAT_CODE_W2RU;
 
2613
                        break;
 
2614
                }
 
2615
 
 
2616
                err = vhd_parent_locator_write_at(ctx, parent, off, code,
 
2617
                                                  0, ctx->header.loc + i);
 
2618
                if (err)
 
2619
                        return err;
 
2620
 
 
2621
                off += vhd_parent_locator_size(ctx->header.loc + i);
 
2622
        }
 
2623
 
 
2624
        return 0;
 
2625
}
 
2626
 
 
2627
int
 
2628
vhd_change_parent(vhd_context_t *child, char *parent_path, int raw)
 
2629
{
 
2630
        int i, err;
 
2631
        char *ppath;
 
2632
        struct stat stats;
 
2633
        vhd_context_t parent;
 
2634
 
 
2635
        ppath = realpath(parent_path, NULL);
 
2636
        if (!ppath) {
 
2637
                VHDLOG("error resolving parent path %s for %s: %d\n",
 
2638
                       parent_path, child->file, errno);
 
2639
                return -errno;
 
2640
        }
 
2641
 
 
2642
        err = stat(ppath, &stats);
 
2643
        if (err == -1) {
 
2644
                err = -errno;
 
2645
                goto out;
 
2646
        }
 
2647
 
 
2648
        if (!S_ISREG(stats.st_mode) && !S_ISBLK(stats.st_mode)) {
 
2649
                err = -EINVAL;
 
2650
                goto out;
 
2651
        }
 
2652
 
 
2653
        if (raw) {
 
2654
                blk_uuid_clear(&child->header.prt_uuid);
 
2655
        } else {
 
2656
                err = vhd_open(&parent, ppath, VHD_OPEN_RDONLY);
 
2657
                if (err) {
 
2658
                        VHDLOG("error opening parent %s for %s: %d\n",
 
2659
                               ppath, child->file, err);
 
2660
                        goto out;
 
2661
                }
 
2662
                blk_uuid_copy(&child->header.prt_uuid, &parent.footer.uuid);
 
2663
                vhd_close(&parent);
 
2664
        }
 
2665
 
 
2666
        vhd_initialize_header_parent_name(child, ppath);
 
2667
        child->header.prt_ts = vhd_time(stats.st_mtime);
 
2668
 
 
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);
 
2672
 
 
2673
                switch (loc->code) {
 
2674
                case PLAT_CODE_MACX:
 
2675
                case PLAT_CODE_W2KU:
 
2676
                case PLAT_CODE_W2RU:
 
2677
                        break;
 
2678
                default:
 
2679
                        continue;
 
2680
                }
 
2681
 
 
2682
                err = vhd_parent_locator_write_at(child, ppath,
 
2683
                                                  loc->data_offset,
 
2684
                                                  loc->code, max, loc);
 
2685
                if (err) {
 
2686
                        VHDLOG("error writing parent locator %d for %s: %d\n",
 
2687
                               i, child->file, err);
 
2688
                        goto out;
 
2689
                }
 
2690
        }
 
2691
 
 
2692
        TEST_FAIL_AT(FAIL_REPARENT_LOCATOR);
 
2693
 
 
2694
        err = vhd_write_header(child, &child->header);
 
2695
        if (err) {
 
2696
                VHDLOG("error writing header for %s: %d\n", child->file, err);
 
2697
                goto out;
 
2698
        }
 
2699
 
 
2700
        err = 0;
 
2701
 
 
2702
out:
 
2703
        free(ppath);
 
2704
        return err;
 
2705
}
 
2706
 
 
2707
static int
 
2708
vhd_create_batmap(vhd_context_t *ctx)
 
2709
{
 
2710
        off_t off;
 
2711
        int err, map_bytes;
 
2712
        vhd_batmap_header_t *header;
 
2713
 
 
2714
        if (!vhd_type_dynamic(ctx))
 
2715
                return -EINVAL;
 
2716
 
 
2717
        map_bytes = (ctx->header.max_bat_size + 7) >> 3;
 
2718
        header    = &ctx->batmap.header;
 
2719
 
 
2720
        memset(header, 0, sizeof(vhd_batmap_header_t));
 
2721
        memcpy(header->cookie, VHD_BATMAP_COOKIE, sizeof(header->cookie));
 
2722
 
 
2723
        err = vhd_batmap_header_offset(ctx, &off);
 
2724
        if (err)
 
2725
                return err;
 
2726
 
 
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;
 
2731
 
 
2732
        map_bytes = vhd_sectors_to_bytes(header->batmap_size);
 
2733
 
 
2734
        err = posix_memalign((void **)&ctx->batmap.map,
 
2735
                             VHD_SECTOR_SIZE, map_bytes);
 
2736
        if (err) {
 
2737
                ctx->batmap.map = NULL;
 
2738
                return -err;
 
2739
        }
 
2740
 
 
2741
        memset(ctx->batmap.map, 0, map_bytes);
 
2742
 
 
2743
        return vhd_write_batmap(ctx, &ctx->batmap);
 
2744
}
 
2745
 
 
2746
static int
 
2747
vhd_create_bat(vhd_context_t *ctx)
 
2748
{
 
2749
        int i, err;
 
2750
        size_t size;
 
2751
 
 
2752
        if (!vhd_type_dynamic(ctx))
 
2753
                return -EINVAL;
 
2754
 
 
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);
 
2757
        if (err) {
 
2758
                ctx->bat.bat = NULL;
 
2759
                return err;
 
2760
        }
 
2761
 
 
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;
 
2765
 
 
2766
        err = vhd_seek(ctx, ctx->header.table_offset, SEEK_SET);
 
2767
        if (err)
 
2768
                return err;
 
2769
 
 
2770
        ctx->bat.entries = ctx->header.max_bat_size;
 
2771
        ctx->bat.spb     = ctx->header.block_size >> VHD_SECTOR_SHIFT;
 
2772
 
 
2773
        return vhd_write_bat(ctx, &ctx->bat);
 
2774
}
 
2775
 
 
2776
static int
 
2777
vhd_initialize_fixed_disk(vhd_context_t *ctx)
 
2778
{
 
2779
        char *buf;
 
2780
        int i, err;
 
2781
 
 
2782
        if (ctx->footer.type != HD_TYPE_FIXED)
 
2783
                return -EINVAL;
 
2784
 
 
2785
        err = vhd_seek(ctx, 0, SEEK_SET);
 
2786
        if (err)
 
2787
                return err;
 
2788
 
 
2789
        buf = mmap(0, VHD_BLOCK_SIZE, PROT_READ,
 
2790
                   MAP_SHARED | MAP_ANON, -1, 0);
 
2791
        if (buf == MAP_FAILED)
 
2792
                return -errno;
 
2793
 
 
2794
        for (i = 0; i < ctx->footer.curr_size >> VHD_BLOCK_SHIFT; i++) {
 
2795
                err = vhd_write(ctx, buf, VHD_BLOCK_SIZE);
 
2796
                if (err)
 
2797
                        goto out;
 
2798
        }
 
2799
 
 
2800
        err = 0;
 
2801
 
 
2802
out:
 
2803
        munmap(buf, VHD_BLOCK_SIZE);
 
2804
        return err;
 
2805
}
 
2806
 
 
2807
int 
 
2808
vhd_get_phys_size(vhd_context_t *ctx, off_t *size)
 
2809
{
 
2810
        int err;
 
2811
 
 
2812
        if ((err = vhd_end_of_data(ctx, size)))
 
2813
                return err;
 
2814
        *size += sizeof(vhd_footer_t);
 
2815
        return 0;
 
2816
}
 
2817
 
 
2818
int 
 
2819
vhd_set_phys_size(vhd_context_t *ctx, off_t size)
 
2820
{
 
2821
        off_t phys_size;
 
2822
        int err;
 
2823
 
 
2824
        err = vhd_get_phys_size(ctx, &phys_size);
 
2825
        if (err)
 
2826
                return err;
 
2827
        if (size < phys_size) {
 
2828
                // would result in data loss
 
2829
                VHDLOG("ERROR: new size (%"PRIu64") < phys size (%"PRIu64")\n",
 
2830
                                size, phys_size);
 
2831
                return -EINVAL;
 
2832
        }
 
2833
        return vhd_write_footer_at(ctx, &ctx->footer, 
 
2834
                        size - sizeof(vhd_footer_t));
 
2835
}
 
2836
 
 
2837
static int
 
2838
__vhd_create(const char *name, const char *parent, uint64_t bytes, int type,
 
2839
                vhd_flag_creat_t flags)
 
2840
{
 
2841
        int err;
 
2842
        off_t off;
 
2843
        vhd_context_t ctx;
 
2844
        vhd_footer_t *footer;
 
2845
        vhd_header_t *header;
 
2846
        uint64_t size, blks;
 
2847
 
 
2848
        switch (type) {
 
2849
        case HD_TYPE_DIFF:
 
2850
                if (!parent)
 
2851
                        return -EINVAL;
 
2852
        case HD_TYPE_FIXED:
 
2853
        case HD_TYPE_DYNAMIC:
 
2854
                break;
 
2855
        default:
 
2856
                return -EINVAL;
 
2857
        }
 
2858
 
 
2859
        if (strnlen(name, VHD_MAX_NAME_LEN - 1) == VHD_MAX_NAME_LEN - 1)
 
2860
                return -ENAMETOOLONG;
 
2861
 
 
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;
 
2867
 
 
2868
        ctx.fd = open(name, O_WRONLY | O_CREAT |
 
2869
                      O_TRUNC | O_LARGEFILE | O_DIRECT, 0644);
 
2870
        if (ctx.fd == -1)
 
2871
                return -errno;
 
2872
 
 
2873
        ctx.file = strdup(name);
 
2874
        if (!ctx.file) {
 
2875
                err = -ENOMEM;
 
2876
                goto out;
 
2877
        }
 
2878
 
 
2879
        err = vhd_test_file_fixed(ctx.file, &ctx.is_block);
 
2880
        if (err)
 
2881
                goto out;
 
2882
 
 
2883
        vhd_initialize_footer(&ctx, type, size);
 
2884
 
 
2885
        if (type == HD_TYPE_FIXED) {
 
2886
                err = vhd_initialize_fixed_disk(&ctx);
 
2887
                if (err)
 
2888
                        goto out;
 
2889
        } else {
 
2890
                int raw = vhd_flag_test(flags, VHD_FLAG_CREAT_PARENT_RAW);
 
2891
                err = vhd_initialize_header(&ctx, parent, size, raw);
 
2892
                if (err)
 
2893
                        goto out;
 
2894
 
 
2895
                err = vhd_write_footer_at(&ctx, &ctx.footer, 0);
 
2896
                if (err)
 
2897
                        goto out;
 
2898
 
 
2899
                err = vhd_write_header_at(&ctx, &ctx.header, VHD_SECTOR_SIZE);
 
2900
                if (err)
 
2901
                        goto out;
 
2902
 
 
2903
                err = vhd_create_batmap(&ctx);
 
2904
                if (err)
 
2905
                        goto out;
 
2906
 
 
2907
                err = vhd_create_bat(&ctx);
 
2908
                if (err)
 
2909
                        goto out;
 
2910
 
 
2911
                if (type == HD_TYPE_DIFF) {
 
2912
                        err = vhd_write_parent_locators(&ctx, parent);
 
2913
                        if (err)
 
2914
                                goto out;
 
2915
                }
 
2916
 
 
2917
                /* write header again since it may have changed */
 
2918
                err = vhd_write_header_at(&ctx, &ctx.header, VHD_SECTOR_SIZE);
 
2919
                if (err)
 
2920
                        goto out;
 
2921
        }
 
2922
 
 
2923
        err = vhd_seek(&ctx, 0, SEEK_END);
 
2924
        if (err)
 
2925
                goto out;
 
2926
 
 
2927
        off = vhd_position(&ctx);
 
2928
        if (off == (off_t)-1) {
 
2929
                err = -errno;
 
2930
                goto out;
 
2931
        }
 
2932
 
 
2933
        if (ctx.is_block)
 
2934
                off -= sizeof(vhd_footer_t);
 
2935
 
 
2936
        err = vhd_write_footer_at(&ctx, &ctx.footer, off);
 
2937
        if (err)
 
2938
                goto out;
 
2939
 
 
2940
        err = 0;
 
2941
 
 
2942
out:
 
2943
        vhd_close(&ctx);
 
2944
        if (err && !ctx.is_block)
 
2945
                unlink(name);
 
2946
        return err;
 
2947
}
 
2948
 
 
2949
int
 
2950
vhd_create(const char *name, uint64_t bytes, int type, vhd_flag_creat_t flags)
 
2951
{
 
2952
        return __vhd_create(name, NULL, bytes, type, flags);
 
2953
}
 
2954
 
 
2955
int
 
2956
vhd_snapshot(const char *name, uint64_t bytes, const char *parent,
 
2957
                vhd_flag_creat_t flags)
 
2958
{
 
2959
        return __vhd_create(name, parent, bytes, HD_TYPE_DIFF, flags);
 
2960
}
 
2961
 
 
2962
static int
 
2963
__vhd_io_fixed_read(vhd_context_t *ctx,
 
2964
                    char *buf, uint64_t sec, uint32_t secs)
 
2965
{
 
2966
        int err;
 
2967
 
 
2968
        err = vhd_seek(ctx, vhd_sectors_to_bytes(sec), SEEK_SET);
 
2969
        if (err)
 
2970
                return err;
 
2971
 
 
2972
        return vhd_read(ctx, buf, vhd_sectors_to_bytes(secs));
 
2973
}
 
2974
 
 
2975
static void
 
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)
 
2980
{
 
2981
        int i;
 
2982
 
 
2983
        for (i = 0; i < secs; i++) {
 
2984
                if (test_bit(map, map_off + i))
 
2985
                        goto next;
 
2986
 
 
2987
                if (ctx && !vhd_bitmap_test(ctx, bitmap, bitmap_off + i))
 
2988
                        goto next;
 
2989
 
 
2990
                memcpy(dst, src, VHD_SECTOR_SIZE);
 
2991
                set_bit(map, map_off + i);
 
2992
 
 
2993
        next:
 
2994
                src += VHD_SECTOR_SIZE;
 
2995
                dst += VHD_SECTOR_SIZE;
 
2996
        }
 
2997
}
 
2998
 
 
2999
static int
 
3000
__vhd_io_dynamic_read_link(vhd_context_t *ctx, char *map,
 
3001
                           char *buf, uint64_t sector, uint32_t secs)
 
3002
{
 
3003
        off_t off;
 
3004
        uint32_t blk, sec;
 
3005
        int err, cnt, map_off;
 
3006
        char *bitmap, *data, *src;
 
3007
 
 
3008
        map_off = 0;
 
3009
 
 
3010
        do {
 
3011
                blk    = sector / ctx->spb;
 
3012
                sec    = sector % ctx->spb;
 
3013
                off    = ctx->bat.bat[blk];
 
3014
                data   = NULL;
 
3015
                bitmap = NULL;
 
3016
 
 
3017
                if (off == DD_BLK_UNUSED) {
 
3018
                        cnt = MIN(secs, ctx->spb);
 
3019
                        goto next;
 
3020
                }
 
3021
 
 
3022
                err = vhd_read_bitmap(ctx, blk, &bitmap);
 
3023
                if (err)
 
3024
                        return err;
 
3025
 
 
3026
                err = vhd_read_block(ctx, blk, &data);
 
3027
                if (err) {
 
3028
                        free(bitmap);
 
3029
                        return err;
 
3030
                }
 
3031
 
 
3032
                cnt = MIN(secs, ctx->spb - sec);
 
3033
                src = data + vhd_sectors_to_bytes(sec);
 
3034
 
 
3035
                __vhd_io_dynamic_copy_data(ctx,
 
3036
                                           map, map_off,
 
3037
                                           bitmap, sec,
 
3038
                                           buf, src, cnt);
 
3039
 
 
3040
        next:
 
3041
                free(data);
 
3042
                free(bitmap);
 
3043
 
 
3044
                secs    -= cnt;
 
3045
                sector  += cnt;
 
3046
                map_off += cnt;
 
3047
                buf     += vhd_sectors_to_bytes(cnt);
 
3048
 
 
3049
        } while (secs);
 
3050
 
 
3051
        return 0;
 
3052
}
 
3053
 
 
3054
static int
 
3055
__raw_read_link(char *filename,
 
3056
                char *map, char *buf, uint64_t sec, uint32_t secs)
 
3057
{
 
3058
        int fd, err;
 
3059
        off_t off;
 
3060
        uint64_t size;
 
3061
        char *data;
 
3062
 
 
3063
        err = 0;
 
3064
        errno = 0;
 
3065
        fd = open(filename, O_RDONLY | O_DIRECT | O_LARGEFILE);
 
3066
        if (fd == -1) {
 
3067
                VHDLOG("%s: failed to open: %d\n", filename, -errno);
 
3068
                return -errno;
 
3069
        }
 
3070
 
 
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);
 
3075
                err = -errno;
 
3076
                goto close;
 
3077
        }
 
3078
 
 
3079
        size = vhd_sectors_to_bytes(secs);
 
3080
        err = posix_memalign((void **)&data, VHD_SECTOR_SIZE, size);
 
3081
        if (err)
 
3082
                goto close;
 
3083
 
 
3084
        err = read(fd, data, size);
 
3085
        if (err != size) {
 
3086
                VHDLOG("%s: reading of %"PRIu64" returned %d, errno: %d\n",
 
3087
                                filename, size, err, -errno);
 
3088
                free(data);
 
3089
                err = errno ? -errno : -EIO;
 
3090
                goto close;
 
3091
        }
 
3092
        __vhd_io_dynamic_copy_data(NULL, map, 0, NULL, 0, buf, data, secs);
 
3093
        free(data);
 
3094
        err = 0;
 
3095
 
 
3096
close:
 
3097
        close(fd);
 
3098
        return err;
 
3099
}
 
3100
 
 
3101
static int
 
3102
__vhd_io_dynamic_read(vhd_context_t *ctx,
 
3103
                      char *buf, uint64_t sec, uint32_t secs)
 
3104
{
 
3105
        int err;
 
3106
        uint32_t i, done;
 
3107
        char *map, *next;
 
3108
        vhd_context_t parent, *vhd;
 
3109
 
 
3110
        err  = vhd_get_bat(ctx);
 
3111
        if (err)
 
3112
                return err;
 
3113
 
 
3114
        vhd  = ctx;
 
3115
        next = NULL;
 
3116
        map  = calloc(1, secs << (VHD_SECTOR_SHIFT - 3));
 
3117
        if (!map)
 
3118
                return -ENOMEM;
 
3119
 
 
3120
        memset(buf, 0, vhd_sectors_to_bytes(secs));
 
3121
 
 
3122
        for (;;) {
 
3123
                err = __vhd_io_dynamic_read_link(vhd, map, buf, sec, secs);
 
3124
                if (err)
 
3125
                        goto close;
 
3126
 
 
3127
                for (done = 0, i = 0; i < secs; i++)
 
3128
                        if (test_bit(map, i))
 
3129
                                done++;
 
3130
 
 
3131
                if (done == secs) {
 
3132
                        err = 0;
 
3133
                        goto close;
 
3134
                }
 
3135
 
 
3136
                if (vhd->footer.type == HD_TYPE_DIFF) {
 
3137
                        err = vhd_parent_locator_get(vhd, &next);
 
3138
                        if (err)
 
3139
                                goto close;
 
3140
                        if (vhd_parent_raw(vhd)) {
 
3141
                                err = __raw_read_link(next, map, buf, sec,
 
3142
                                                secs);
 
3143
                                goto close;
 
3144
                        }
 
3145
                } else {
 
3146
                        err = 0;
 
3147
                        goto close;
 
3148
                }
 
3149
 
 
3150
                if (vhd != ctx)
 
3151
                        vhd_close(vhd);
 
3152
                vhd = &parent;
 
3153
 
 
3154
                err = vhd_open(vhd, next, VHD_OPEN_RDONLY);
 
3155
                if (err)
 
3156
                        goto out;
 
3157
 
 
3158
                err = vhd_get_bat(vhd);
 
3159
                if (err)
 
3160
                        goto close;
 
3161
 
 
3162
                free(next);
 
3163
                next = NULL;
 
3164
        }
 
3165
 
 
3166
close:
 
3167
        if (vhd != ctx)
 
3168
                vhd_close(vhd);
 
3169
out:
 
3170
        free(map);
 
3171
        free(next);
 
3172
        return err;
 
3173
}
 
3174
 
 
3175
int
 
3176
vhd_io_read(vhd_context_t *ctx, char *buf, uint64_t sec, uint32_t secs)
 
3177
{
 
3178
        if (vhd_sectors_to_bytes(sec + secs) > ctx->footer.curr_size)
 
3179
                return -ERANGE;
 
3180
 
 
3181
        if (!vhd_type_dynamic(ctx))
 
3182
                return __vhd_io_fixed_read(ctx, buf, sec, secs);
 
3183
 
 
3184
        return __vhd_io_dynamic_read(ctx, buf, sec, secs);
 
3185
}
 
3186
 
 
3187
static int
 
3188
__vhd_io_fixed_write(vhd_context_t *ctx,
 
3189
                     char *buf, uint64_t sec, uint32_t secs)
 
3190
{
 
3191
        int err;
 
3192
 
 
3193
        err = vhd_seek(ctx, vhd_sectors_to_bytes(sec), SEEK_SET);
 
3194
        if (err)
 
3195
                return err;
 
3196
 
 
3197
        return vhd_write(ctx, buf, vhd_sectors_to_bytes(secs));
 
3198
}
 
3199
 
 
3200
static int
 
3201
__vhd_io_allocate_block(vhd_context_t *ctx, uint32_t block)
 
3202
{
 
3203
        char *buf;
 
3204
        size_t size;
 
3205
        off_t off, max;
 
3206
        int i, err, gap, spp;
 
3207
 
 
3208
        spp = getpagesize() >> VHD_SECTOR_SHIFT;
 
3209
 
 
3210
        err = vhd_end_of_data(ctx, &max);
 
3211
        if (err)
 
3212
                return err;
 
3213
 
 
3214
        gap   = 0;
 
3215
        off   = max;
 
3216
        max >>= VHD_SECTOR_SHIFT;
 
3217
 
 
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));
 
3221
                max += gap;
 
3222
        }
 
3223
 
 
3224
        err = vhd_seek(ctx, off, SEEK_SET);
 
3225
        if (err)
 
3226
                return err;
 
3227
 
 
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)
 
3231
                return -errno;
 
3232
 
 
3233
        err = vhd_write(ctx, buf, size);
 
3234
        if (err)
 
3235
                goto out;
 
3236
 
 
3237
        ctx->bat.bat[block] = max;
 
3238
        err = vhd_write_bat(ctx, &ctx->bat);
 
3239
        if (err)
 
3240
                goto out;
 
3241
 
 
3242
        err = 0;
 
3243
 
 
3244
out:
 
3245
        munmap(buf, size);
 
3246
        return err;
 
3247
}
 
3248
 
 
3249
static int
 
3250
__vhd_io_dynamic_write(vhd_context_t *ctx,
 
3251
                       char *buf, uint64_t sector, uint32_t secs)
 
3252
{
 
3253
        char *map;
 
3254
        off_t off;
 
3255
        uint32_t blk, sec;
 
3256
        int i, err, cnt, ret;
 
3257
 
 
3258
        if (vhd_sectors_to_bytes(sector + secs) > ctx->footer.curr_size)
 
3259
                return -ERANGE;
 
3260
 
 
3261
        err = vhd_get_bat(ctx);
 
3262
        if (err)
 
3263
                return err;
 
3264
 
 
3265
        if (vhd_has_batmap(ctx)) {
 
3266
                err = vhd_get_batmap(ctx);
 
3267
                if (err)
 
3268
                        return err;
 
3269
        }
 
3270
 
 
3271
        do {
 
3272
                blk = sector / ctx->spb;
 
3273
                sec = sector % ctx->spb;
 
3274
 
 
3275
                off = ctx->bat.bat[blk];
 
3276
                if (off == DD_BLK_UNUSED) {
 
3277
                        err = __vhd_io_allocate_block(ctx, blk);
 
3278
                        if (err)
 
3279
                                return err;
 
3280
 
 
3281
                        off = ctx->bat.bat[blk];
 
3282
                }
 
3283
 
 
3284
                off += ctx->bm_secs + sec;
 
3285
                err  = vhd_seek(ctx, vhd_sectors_to_bytes(off), SEEK_SET);
 
3286
                if (err)
 
3287
                        return err;
 
3288
 
 
3289
                cnt = MIN(secs, ctx->spb - sec);
 
3290
                err = vhd_write(ctx, buf, vhd_sectors_to_bytes(cnt));
 
3291
                if (err)
 
3292
                        return err;
 
3293
 
 
3294
                if (vhd_has_batmap(ctx) &&
 
3295
                    vhd_batmap_test(ctx, &ctx->batmap, blk))
 
3296
                        goto next;
 
3297
 
 
3298
                err = vhd_read_bitmap(ctx, blk, &map);
 
3299
                if (err)
 
3300
                        return err;
 
3301
 
 
3302
                for (i = 0; i < cnt; i++)
 
3303
                        vhd_bitmap_set(ctx, map, sec + i);
 
3304
 
 
3305
                err = vhd_write_bitmap(ctx, blk, map);
 
3306
                if (err)
 
3307
                        goto fail;
 
3308
 
 
3309
                if (vhd_has_batmap(ctx)) {
 
3310
                        for (i = 0; i < ctx->spb; i++)
 
3311
                                if (!vhd_bitmap_test(ctx, map, i)) {
 
3312
                                        free(map);
 
3313
                                        goto next;
 
3314
                                }
 
3315
 
 
3316
                        vhd_batmap_set(ctx, &ctx->batmap, blk);
 
3317
                        err = vhd_write_batmap(ctx, &ctx->batmap);
 
3318
                        if (err)
 
3319
                                goto fail;
 
3320
                }
 
3321
 
 
3322
                free(map);
 
3323
                map = NULL;
 
3324
 
 
3325
        next:
 
3326
                secs   -= cnt;
 
3327
                sector += cnt;
 
3328
                buf    += vhd_sectors_to_bytes(cnt);
 
3329
        } while (secs);
 
3330
 
 
3331
        err = 0;
 
3332
 
 
3333
out:
 
3334
        ret = vhd_write_footer(ctx, &ctx->footer);
 
3335
        return (err ? err : ret);
 
3336
 
 
3337
fail:
 
3338
        free(map);
 
3339
        goto out;
 
3340
}
 
3341
 
 
3342
int
 
3343
vhd_io_write(vhd_context_t *ctx, char *buf, uint64_t sec, uint32_t secs)
 
3344
{
 
3345
        if (vhd_sectors_to_bytes(sec + secs) > ctx->footer.curr_size)
 
3346
                return -ERANGE;
 
3347
 
 
3348
        if (!vhd_type_dynamic(ctx))
 
3349
                return __vhd_io_fixed_write(ctx, buf, sec, secs);
 
3350
 
 
3351
        return __vhd_io_dynamic_write(ctx, buf, sec, secs);
 
3352
}