Statistics
| Branch: | Revision:

root / vhd / lib / libvhd-index.c @ abdb293f

History | View | Annotate | Download (23.6 kB)

1
/*
2
 * Copyright (c) 2008, XenSource Inc.
3
 * Copyright (c) 2010, Citrix Systems, Inc.
4
 *
5
 * All rights reserved.
6
 *
7
 * Redistribution and use in source and binary forms, with or without
8
 * modification, are permitted provided that the following conditions are met:
9
 *     * Redistributions of source code must retain the above copyright
10
 *       notice, this list of conditions and the following disclaimer.
11
 *     * Redistributions in binary form must reproduce the above copyright
12
 *       notice, this list of conditions and the following disclaimer in the
13
 *       documentation and/or other materials provided with the distribution.
14
 *     * Neither the name of XenSource Inc. nor the names of its contributors
15
 *       may be used to endorse or promote products derived from this software
16
 *       without specific prior written permission.
17
 *
18
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
22
 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
23
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
24
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
25
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
26
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
27
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
28
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29
 */
30

    
31
#ifdef HAVE_CONFIG_H
32
#include "config.h"
33
#endif
34

    
35
#include <stdio.h>
36
#include <errno.h>
37
#include <fcntl.h>
38
#include <unistd.h>
39
#include <stdlib.h>
40
#include <string.h>
41
#include <libgen.h>
42
#include <limits.h>
43
#include <sys/stat.h>
44
#include <sys/types.h>
45

    
46
#include "libvhd.h"
47
#include "libvhd-index.h"
48
#include "relative-path.h"
49

    
50
typedef struct vhdi_path                    vhdi_path_t;
51
typedef struct vhdi_header                  vhdi_header_t;
52
typedef struct vhdi_bat_header              vhdi_bat_header_t;
53
typedef struct vhdi_file_table_header       vhdi_file_table_header_t;
54
typedef struct vhdi_file_table_entry        vhdi_file_table_entry_t;
55

    
56
static const char                           VHDI_HEADER_COOKIE[] = "vhdindex";
57
static const char                           VHDI_BAT_HEADER_COOKIE[] = "vhdi-bat";
58
static const char                           VHDI_FILE_TABLE_HEADER_COOKIE[] = "vhdifile";
59

    
60
struct vhdi_path {
61
        char                                path[VHD_MAX_NAME_LEN];
62
        uint16_t                            bytes;
63
};
64

    
65
struct vhdi_header {
66
        char                                cookie[8];
67
        uint32_t                            vhd_block_size;
68
        uint64_t                            table_offset;
69
};
70

    
71
struct vhdi_bat_header {
72
        char                                cookie[8];
73
        uint64_t                            vhd_blocks;
74
        uint32_t                            vhd_block_size;
75
        vhdi_path_t                         vhd_path;
76
        vhdi_path_t                         index_path;
77
        vhdi_path_t                         file_table_path;
78
        uint64_t                            table_offset;
79
};
80

    
81
struct vhdi_file_table_header {
82
        char                                cookie[8];
83
        uint32_t                            files;
84
        uint64_t                            table_offset;
85
};
86

    
87
struct vhdi_file_table_entry {
88
        vhdi_path_t                         p;
89
        vhdi_file_id_t                      file_id;
90
        uuid_t                              vhd_uuid;
91
        uint32_t                            vhd_timestamp;
92
};
93

    
94
static inline int
95
vhdi_seek(vhdi_context_t *ctx, off64_t off, int whence)
96
{
97
        int err;
98

    
99
        err = lseek64(ctx->fd, off, whence);
100
        if (err == (off64_t)-1)
101
                return -errno;
102

    
103
        return 0;
104
}
105

    
106
static inline off64_t
107
vhdi_position(vhdi_context_t *ctx)
108
{
109
        return lseek64(ctx->fd, 0, SEEK_CUR);
110
}
111

    
112
static inline int
113
vhdi_read(vhdi_context_t *ctx, void *buf, size_t size)
114
{
115
        int err;
116

    
117
        err = read(ctx->fd, buf, size);
118
        if (err != size)
119
                return (errno ? -errno : -EIO);
120

    
121
        return 0;
122
}
123

    
124
static inline int
125
vhdi_write(vhdi_context_t *ctx, void *buf, size_t size)
126
{
127
        int err;
128

    
129
        err = write(ctx->fd, buf, size);
130
        if (err != size)
131
                return (errno ? -errno : -EIO);
132

    
133
        return 0;
134
}
135

    
136
static inline int
137
vhdi_check_block_size(uint32_t block_size)
138
{
139
        int i, cnt;
140

    
141
        cnt = 0;
142
        for (i = 0; i < 32; i++)
143
                if ((block_size >> i) & 0x0001)
144
                        cnt++;
145

    
146
        if (cnt == 1)
147
                return 0;
148

    
149
        return -EINVAL;
150
}
151

    
152
static inline void
153
vhdi_header_in(vhdi_header_t *header)
154
{
155
        BE32_IN(&header->vhd_block_size);
156
        BE64_IN(&header->table_offset);
157
}
158

    
159
static inline void
160
vhdi_header_out(vhdi_header_t *header)
161
{
162
        BE32_OUT(&header->vhd_block_size);
163
        BE64_OUT(&header->table_offset);
164
}
165

    
166
static inline int
167
vhdi_header_validate(vhdi_header_t *header)
168
{
169
        if (memcmp(header->cookie, VHDI_HEADER_COOKIE, sizeof(header->cookie)))
170
                return -EINVAL;
171

    
172
        return vhdi_check_block_size(header->vhd_block_size);
173
}
174

    
175
void
176
vhdi_entry_in(vhdi_entry_t *entry)
177
{
178
        BE32_IN(&entry->file_id);
179
        BE32_IN(&entry->offset);
180
}
181

    
182
static inline vhdi_entry_t
183
vhdi_entry_out(vhdi_entry_t *entry)
184
{
185
        vhdi_entry_t e;
186

    
187
        e = *entry;
188
        BE32_OUT(&e.file_id);
189
        BE32_OUT(&e.offset);
190

    
191
        return e;
192
}
193

    
194
static inline void
195
vhdi_path_in(vhdi_path_t *path)
196
{
197
        BE16_IN(&path->bytes);
198
}
199

    
200
static inline void
201
vhdi_path_out(vhdi_path_t *path)
202
{
203
        BE16_OUT(&path->bytes);
204
}
205

    
206
static inline void
207
vhdi_bat_header_in(vhdi_bat_header_t *header)
208
{
209
        BE64_IN(&header->vhd_blocks);
210
        BE32_IN(&header->vhd_block_size);
211
        vhdi_path_in(&header->vhd_path);
212
        vhdi_path_in(&header->index_path);
213
        vhdi_path_in(&header->file_table_path);
214
        BE64_IN(&header->table_offset);
215
}
216

    
217
static inline void
218
vhdi_bat_header_out(vhdi_bat_header_t *header)
219
{
220
        BE64_OUT(&header->vhd_blocks);
221
        BE32_OUT(&header->vhd_block_size);
222
        vhdi_path_out(&header->vhd_path);
223
        vhdi_path_out(&header->index_path);
224
        vhdi_path_out(&header->file_table_path);
225
        BE64_OUT(&header->table_offset);
226
}
227

    
228
static inline int
229
vhdi_path_validate(vhdi_path_t *path)
230
{
231
        int i;
232

    
233
        if (path->bytes >= VHD_MAX_NAME_LEN - 1)
234
                return -ENAMETOOLONG;
235

    
236
        for (i = 0; i < path->bytes; i++)
237
                if (path->path[i] == '\0')
238
                        return 0;
239

    
240
        return -EINVAL;
241
}
242

    
243
static inline char *
244
vhdi_path_expand(const char *src, vhdi_path_t *dest, int *err)
245
{
246
        int len;
247
        char *path, *base, copy[VHD_MAX_NAME_LEN];
248
        char *absolute_path, __absolute_path[PATH_MAX];
249

    
250
        strcpy(copy, src);
251
        base = dirname(copy);
252

    
253
        *err = asprintf(&path, "%s/%s", base, dest->path);
254
        if (*err == -1) {
255
                *err = -ENOMEM;
256
                return NULL;
257
        }
258

    
259
        absolute_path = realpath(path, __absolute_path);
260
        free(path);
261
        if (absolute_path)
262
                absolute_path = strdup(absolute_path);
263
        if (!absolute_path) {
264
                *err = -errno;
265
                return NULL;
266
        }
267

    
268
        len = strnlen(absolute_path, VHD_MAX_NAME_LEN - 1);
269
        if (len == VHD_MAX_NAME_LEN - 1) {
270
                free(absolute_path);
271
                *err = -ENAMETOOLONG;
272
                return NULL;
273
        }
274

    
275
        *err = 0;
276
        return absolute_path;
277
}
278

    
279
static inline int
280
vhdi_bat_header_validate(vhdi_bat_header_t *header)
281
{
282
        int err;
283

    
284
        if (memcmp(header->cookie,
285
                   VHDI_BAT_HEADER_COOKIE, sizeof(header->cookie)))
286
                return -EINVAL;
287

    
288
        err = vhdi_check_block_size(header->vhd_block_size);
289
        if (err)
290
                return err;
291

    
292
        err = vhdi_path_validate(&header->vhd_path);
293
        if (err)
294
                return err;
295

    
296
        err = vhdi_path_validate(&header->index_path);
297
        if (err)
298
                return err;
299

    
300
        err = vhdi_path_validate(&header->file_table_path);
301
        if (err)
302
                return err;
303

    
304
        return 0;
305
}
306

    
307
static inline int
308
vhdi_bat_load_header(int fd, vhdi_bat_header_t *header)
309
{
310
        int err;
311

    
312
        err = lseek64(fd, 0, SEEK_SET);
313
        if (err == (off64_t)-1)
314
                return -errno;
315

    
316
        err = read(fd, header, sizeof(vhdi_bat_header_t));
317
        if (err != sizeof(vhdi_bat_header_t))
318
                return (errno ? -errno : -EIO);
319

    
320
        vhdi_bat_header_in(header);
321
        return vhdi_bat_header_validate(header);
322
}
323

    
324
static inline void
325
vhdi_file_table_header_in(vhdi_file_table_header_t *header)
326
{
327
        BE32_OUT(&header->files);
328
        BE64_OUT(&header->table_offset);
329
}
330

    
331
static inline void
332
vhdi_file_table_header_out(vhdi_file_table_header_t *header)
333
{
334
        BE32_OUT(&header->files);
335
        BE64_OUT(&header->table_offset);
336
}
337

    
338
static inline int
339
vhdi_file_table_header_validate(vhdi_file_table_header_t *header)
340
{
341
        if (memcmp(header->cookie,
342
                   VHDI_FILE_TABLE_HEADER_COOKIE, sizeof(header->cookie)))
343
                return -EINVAL;
344

    
345
        return 0;
346
}
347

    
348
static inline int
349
vhdi_file_table_load_header(int fd, vhdi_file_table_header_t *header)
350
{
351
        int err;
352

    
353
        err = lseek64(fd, 0, SEEK_SET);
354
        if (err == (off64_t)-1)
355
                return -errno;
356

    
357
        err = read(fd, header, sizeof(vhdi_file_table_header_t));
358
        if (err != sizeof(vhdi_file_table_header_t))
359
                return (errno ? -errno : -EIO);
360

    
361
        vhdi_file_table_header_in(header);
362
        return vhdi_file_table_header_validate(header);
363
}
364

    
365
static inline int
366
vhdi_file_table_write_header(int fd, vhdi_file_table_header_t *header)
367
{
368
        int err;
369

    
370
        err = lseek64(fd, 0, SEEK_SET);
371
        if (err == (off64_t)-1)
372
                return -errno;
373

    
374
        err = vhdi_file_table_header_validate(header);
375
        if (err)
376
                return err;
377

    
378
        vhdi_file_table_header_out(header);
379

    
380
        err = write(fd, header, sizeof(vhdi_file_table_header_t));
381
        if (err != sizeof(vhdi_file_table_header_t))
382
                return (errno ? -errno : -EIO);
383

    
384
        return 0;
385
}
386

    
387
static inline void
388
vhdi_file_table_entry_in(vhdi_file_table_entry_t *entry)
389
{
390
        vhdi_path_in(&entry->p);
391
        BE32_IN(&entry->file_id);
392
        BE32_IN(&entry->vhd_timestamp);
393
}
394

    
395
static inline void
396
vhdi_file_table_entry_out(vhdi_file_table_entry_t *entry)
397
{
398
        vhdi_path_out(&entry->p);
399
        BE32_OUT(&entry->file_id);
400
        BE32_OUT(&entry->vhd_timestamp);
401
}
402

    
403
static inline int
404
vhdi_file_table_entry_validate(vhdi_file_table_entry_t *entry)
405
{
406
        return vhdi_path_validate(&entry->p);
407
}
408

    
409
static inline int
410
vhdi_file_table_entry_validate_vhd(vhdi_file_table_entry_t *entry,
411
                                   const char *path)
412
{
413
        int err;
414
        vhd_context_t vhd;
415
        struct stat stats;
416

    
417
        err = stat(path, &stats);
418
        if (err == -1)
419
                return -errno;
420

    
421
        if (entry->vhd_timestamp != vhd_time(stats.st_mtime))
422
                return -EINVAL;
423

    
424
        err = vhd_open(&vhd, path, VHD_OPEN_RDONLY);
425
        if (err)
426
                return err;
427

    
428
        err = vhd_get_footer(&vhd);
429
        if (err)
430
                goto out;
431

    
432
        if (uuid_compare(entry->vhd_uuid, vhd.footer.uuid)) {
433
                err = -EINVAL;
434
                goto out;
435
        }
436

    
437
out:
438
        vhd_close(&vhd);
439
        return err;
440
}
441

    
442
int
443
vhdi_create(const char *name, uint32_t vhd_block_size)
444
{
445
        void *buf;
446
        int err, fd;
447
        size_t size;
448
        vhdi_header_t header;
449

    
450
        memset(&header, 0, sizeof(vhdi_header_t));
451

    
452
        err = vhdi_check_block_size(vhd_block_size);
453
        if (err)
454
                return err;
455

    
456
        err = access(name, F_OK);
457
        if (!err || errno != ENOENT)
458
                return (err ? err : -EEXIST);
459

    
460
        memcpy(header.cookie, VHDI_HEADER_COOKIE, sizeof(header.cookie));
461
        header.vhd_block_size = vhd_block_size;
462
        header.table_offset   = vhd_bytes_padded(sizeof(vhdi_header_t));
463

    
464
        err = vhdi_header_validate(&header);
465
        if (err)
466
                return err;
467

    
468
        vhdi_header_out(&header);
469

    
470
        size = vhd_bytes_padded(sizeof(vhdi_header_t));
471
        err  = posix_memalign(&buf, VHD_SECTOR_SIZE, size);
472
        if (err)
473
                return -err;
474

    
475
        memset(buf, 0, size);
476
        memcpy(buf, &header, sizeof(vhdi_header_t));
477

    
478
        fd = open(name, O_CREAT | O_TRUNC | O_RDWR, 0600);
479
        if (fd == -1)
480
                return -errno;
481

    
482
        err = write(fd, buf, size);
483
        if (err != size) {
484
                err = (errno ? -errno : -EIO);
485
                goto fail;
486
        }
487

    
488
        close(fd);
489
        free(buf);
490

    
491
        return 0;
492

    
493
fail:
494
        close(fd);
495
        free(buf);
496
        unlink(name);
497
        return err;
498
}
499

    
500
int
501
vhdi_open(vhdi_context_t *ctx, const char *file, int flags)
502
{
503
        int err, fd;
504
        size_t size;
505
        char *name;
506
        void *buf;
507
        vhdi_header_t header;
508

    
509
        buf = NULL;
510
        memset(ctx, 0, sizeof(vhdi_context_t));
511

    
512
        name = strdup(file);
513
        if (!name)
514
                return -ENOMEM;
515

    
516
        fd = open(file, flags | O_LARGEFILE);
517
        if (fd == -1) {
518
                free(name);
519
                return -errno;
520
        }
521

    
522
        size = vhd_bytes_padded(sizeof(vhdi_header_t));
523
        err = posix_memalign(&buf, VHD_SECTOR_SIZE, size);
524
        if (err) {
525
                err = -err;
526
                goto fail;
527
        }
528

    
529
        err = read(fd, buf, size);
530
        if (err != size) {
531
                err = (errno ? -errno : -EIO);
532
                goto fail;
533
        }
534

    
535
        memcpy(&header, buf, sizeof(vhdi_header_t));
536
        free(buf);
537
        buf = NULL;
538

    
539
        vhdi_header_in(&header);
540
        err = vhdi_header_validate(&header);
541
        if (err)
542
                goto fail;
543

    
544
        ctx->fd             = fd;
545
        ctx->name           = name;
546
        ctx->spb            = header.vhd_block_size >> VHD_SECTOR_SHIFT;
547
        ctx->vhd_block_size = header.vhd_block_size;
548

    
549
        return 0;
550

    
551
fail:
552
        close(fd);
553
        free(buf);
554
        free(name);
555
        return err;
556
}
557

    
558
void
559
vhdi_close(vhdi_context_t *ctx)
560
{
561
        close(ctx->fd);
562
        free(ctx->name);
563
}
564

    
565
int
566
vhdi_read_block(vhdi_context_t *ctx, vhdi_block_t *block, uint32_t sector)
567
{
568
        int i, err;
569
        size_t size;
570
        void *tab;
571

    
572
        err = vhdi_seek(ctx, vhd_sectors_to_bytes(sector), SEEK_SET);
573
        if (err)
574
                return err;
575

    
576
        size = vhd_bytes_padded(ctx->spb * sizeof(vhdi_entry_t));
577

    
578
        block->entries = ctx->spb;
579
        err = posix_memalign(&tab, VHD_SECTOR_SIZE, size);
580
        if (err)
581
                return -err;
582

    
583
        block->table = tab;
584

    
585
        err = vhdi_read(ctx, block->table, size);
586
        if (err)
587
                goto fail;
588

    
589
        for (i = 0; i < block->entries; i++)
590
                vhdi_entry_in(&block->table[i]);
591

    
592
        return 0;
593

    
594
fail:
595
        free(block->table);
596
        return err;
597
}
598

    
599
int
600
vhdi_write_block(vhdi_context_t *ctx, vhdi_block_t *block, uint32_t sector)
601
{
602
        void *buf;
603
        int i, err;
604
        size_t size;
605
        vhdi_entry_t *entries;
606

    
607
        err = vhdi_seek(ctx, vhd_sectors_to_bytes(sector), SEEK_SET);
608
        if (err)
609
                return err;
610

    
611
        size = vhd_bytes_padded(ctx->spb * sizeof(vhdi_entry_t));
612
        err = posix_memalign(&buf, VHD_SECTOR_SIZE, size);
613
        if (err)
614
                return -err;
615

    
616
        memset(buf, 0, size);
617
        entries = (vhdi_entry_t *)buf;
618

    
619
        for (i = 0; i < block->entries; i++)
620
                entries[i] = vhdi_entry_out(&block->table[i]);
621

    
622
        err = vhdi_write(ctx, entries, size);
623
        if (err)
624
                goto out;
625

    
626
        err = 0;
627

    
628
out:
629
        free(entries);
630
        return err;
631
}
632

    
633
int
634
vhdi_append_block(vhdi_context_t *ctx, vhdi_block_t *block, uint32_t *sector)
635
{
636
        void *buf;
637
        int i, err;
638
        off64_t off;
639
        size_t size;
640
        vhdi_entry_t *entries;
641

    
642
        err = vhdi_seek(ctx, 0, SEEK_END);
643
        if (err)
644
                return err;
645

    
646
        off = vhd_bytes_padded(vhdi_position(ctx));
647

    
648
        err = vhdi_seek(ctx, off, SEEK_SET);
649
        if (err)
650
                return err;
651

    
652
        size = vhd_bytes_padded(block->entries * sizeof(vhdi_entry_t));
653
        err = posix_memalign(&buf, VHD_SECTOR_SIZE, size);
654
        if (err)
655
                return -err;
656

    
657
        memset(buf, 0, size);
658
        entries = buf;
659

    
660
        for (i = 0; i < block->entries; i++)
661
                entries[i] = vhdi_entry_out(&block->table[i]);
662

    
663
        err = vhdi_write(ctx, entries, block->entries * sizeof(vhdi_entry_t));
664
        if (err)
665
                goto out;
666

    
667
        err     = 0;
668
        *sector = off >> VHD_SECTOR_SHIFT;
669
out:
670
        if (err) {
671
                int gcc = ftruncate(ctx->fd, off);
672
                if (gcc) {}
673
        }
674
        free(entries);
675
        return err;
676
}
677

    
678
static int
679
vhdi_copy_path_to(vhdi_path_t *path, const char *src, const char *dest)
680
{
681
        int len, err;
682
        char *file, *relative_path, copy[VHD_MAX_NAME_LEN];
683
        char *absolute_path, __absolute_path[PATH_MAX];
684

    
685
        strcpy(copy, dest);
686

    
687
        file          = basename(copy);
688
        absolute_path = realpath(copy, __absolute_path);
689
        relative_path = NULL;
690

    
691
        if (!absolute_path) {
692
                err = -errno;
693
                goto out;
694
        }
695

    
696
        if (!strcmp(file, "")) {
697
                err = -EINVAL;
698
                goto out;
699
        }
700

    
701
        relative_path = relative_path_to((char *)src, absolute_path, &err);
702
        if (!relative_path || err) {
703
                err = (err ? err : -EINVAL);
704
                goto out;
705
        }
706

    
707
        len = strnlen(relative_path, VHD_MAX_NAME_LEN - 1);
708
        if (len == VHD_MAX_NAME_LEN - 1) {
709
                err = -ENAMETOOLONG;
710
                goto out;
711
        }
712

    
713
        strcpy(path->path, relative_path);
714
        path->bytes = len + 1;
715

    
716
        err = 0;
717

    
718
out:
719
        free(relative_path);
720
        return err;
721
}
722

    
723
int
724
vhdi_bat_create(const char *name, const char *vhd,
725
                const char *index, const char *file_table)
726
{
727
        int err, fd;
728
        off64_t off;
729
        vhd_context_t ctx;
730
        vhdi_bat_header_t header;
731

    
732
        memset(&header, 0, sizeof(vhdi_bat_header_t));
733

    
734
        err = access(name, F_OK);
735
        if (!err || errno != ENOENT)
736
                return (err ? -err : -EEXIST);
737

    
738
        err = vhd_open(&ctx, vhd, VHD_OPEN_RDONLY);
739
        if (err)
740
                return err;
741

    
742
        err = vhd_get_header(&ctx);
743
        if (err) {
744
                vhd_close(&ctx);
745
                return err;
746
        }
747

    
748
        header.vhd_blocks     = ctx.header.max_bat_size;
749
        header.vhd_block_size = ctx.header.block_size;
750

    
751
        vhd_close(&ctx);
752

    
753
        fd = open(name, O_CREAT | O_TRUNC | O_RDWR, 0600);
754
        if (fd == -1)
755
                return -errno;
756

    
757
        err = vhdi_copy_path_to(&header.vhd_path, name, vhd);
758
        if (err)
759
                goto fail;
760

    
761
        err = vhdi_copy_path_to(&header.index_path, name, index);
762
        if (err)
763
                goto fail;
764

    
765
        err = vhdi_copy_path_to(&header.file_table_path, name, file_table);
766
        if (err)
767
                goto fail;
768

    
769
        off = vhd_bytes_padded(sizeof(vhdi_bat_header_t));
770

    
771
        header.table_offset = off;
772
        memcpy(header.cookie, VHDI_BAT_HEADER_COOKIE, sizeof(header.cookie));
773

    
774
        err = vhdi_bat_header_validate(&header);
775
        if (err)
776
                goto fail;
777

    
778
        vhdi_bat_header_out(&header);
779

    
780
        err = write(fd, &header, sizeof(vhdi_bat_header_t));
781
        if (err != sizeof(vhdi_bat_header_t)) {
782
                err = (errno ? -errno : -EIO);
783
                goto fail;
784
        }
785

    
786
        close(fd);
787
        return 0;
788

    
789
fail:
790
        close(fd);
791
        unlink(name);
792
        return err;
793
}
794

    
795
int
796
vhdi_bat_load(const char *name, vhdi_bat_t *bat)
797
{
798
        char *path;
799
        int err, fd;
800
        size_t size;
801
        uint32_t *table;
802
        vhdi_bat_header_t header;
803

    
804
        table = NULL;
805

    
806
        fd = open(name, O_RDONLY | O_LARGEFILE);
807
        if (fd == -1)
808
                return -errno;
809

    
810
        err = vhdi_bat_load_header(fd, &header);
811
        if (err)
812
                goto out;
813

    
814
        size = header.vhd_blocks * sizeof(uint32_t);
815
        table = malloc(size);
816
        if (!table) {
817
                err = -ENOMEM;
818
                goto out;
819
        }
820

    
821
        err = lseek64(fd, header.table_offset, SEEK_SET);
822
        if (err == (off64_t)-1) {
823
                err = -errno;
824
                goto out;
825
        }
826

    
827
        err = read(fd, table, size);
828
        if (err != size) {
829
                err = (errno ? -errno : -EIO);
830
                goto out;
831
        }
832

    
833
        path = vhdi_path_expand(name, &header.vhd_path, &err);
834
        if (err)
835
                goto out;
836
        strcpy(bat->vhd_path, path);
837
        free(path);
838

    
839
        err = access(bat->vhd_path, F_OK);
840
        if (err == -1) {
841
                err = -errno;
842
                goto out;
843
        }
844

    
845
        path = vhdi_path_expand(name, &header.index_path, &err);
846
        if (err)
847
                goto out;
848
        strcpy(bat->index_path, path);
849
        free(path);
850

    
851
        err = access(bat->index_path, F_OK);
852
        if (err == -1) {
853
                err = -errno;
854
                goto out;
855
        }
856

    
857
        path = vhdi_path_expand(name, &header.file_table_path, &err);
858
        if (err)
859
                goto out;
860
        strcpy(bat->file_table_path, path);
861
        free(path);
862

    
863
        err = access(bat->file_table_path, F_OK);
864
        if (err == -1) {
865
                err = -errno;
866
                goto out;
867
        }
868

    
869
        bat->vhd_blocks     = header.vhd_blocks;
870
        bat->vhd_block_size = header.vhd_block_size;
871
        bat->table          = table;
872

    
873
        err = 0;
874

    
875
out:
876
        close(fd);
877
        if (err) {
878
                free(table);
879
                memset(bat, 0, sizeof(vhdi_bat_t));
880
        }
881

    
882
        return err;
883
}
884

    
885
int
886
vhdi_bat_write(const char *name, vhdi_bat_t *bat)
887
{
888
        int err, fd;
889
        size_t size;
890
        vhdi_bat_header_t header;
891

    
892
        fd = open(name, O_RDWR | O_LARGEFILE);
893
        if (fd == -1)
894
                return -errno;
895

    
896
        err = vhdi_bat_load_header(fd, &header);
897
        if (err)
898
                goto out;
899

    
900
        if (header.vhd_blocks != bat->vhd_blocks ||
901
            header.vhd_block_size != bat->vhd_block_size) {
902
                err = -EINVAL;
903
                goto out;
904
        }
905

    
906
        err = lseek64(fd, header.table_offset, SEEK_SET);
907
        if (err == (off64_t)-1) {
908
                err = -errno;
909
                goto out;
910
        }
911

    
912
        size = bat->vhd_blocks * sizeof(uint32_t);
913
        err = write(fd, bat->table, size);
914
        if (err != size) {
915
                err = (errno ? -errno : -EIO);
916
                goto out;
917
        }
918

    
919
        err = 0;
920

    
921
out:
922
        close(fd);
923
        return err;
924
}
925

    
926
int
927
vhdi_file_table_create(const char *file)
928
{
929
        int err, fd;
930
        off64_t off;
931
        vhdi_file_table_header_t header;
932

    
933
        memset(&header, 0, sizeof(vhdi_file_table_header_t));
934

    
935
        err = access(file, F_OK);
936
        if (!err || errno != ENOENT)
937
                return (err ? err : -EEXIST);
938

    
939
        off = vhd_bytes_padded(sizeof(vhdi_file_table_header_t));
940

    
941
        header.files        = 0;
942
        header.table_offset = off;
943
        memcpy(header.cookie,
944
               VHDI_FILE_TABLE_HEADER_COOKIE, sizeof(header.cookie));
945

    
946
        vhdi_file_table_header_out(&header);
947

    
948
        fd = open(file, O_CREAT | O_TRUNC | O_RDWR, 0600);
949
        if (fd == -1)
950
                return -errno;
951

    
952
        err = write(fd, &header, sizeof(vhdi_file_table_header_t));
953
        if (err != sizeof(vhdi_file_table_header_t)) {
954
                err = (errno ? -errno : -EIO);
955
                goto out;
956
        }
957

    
958
        err = 0;
959

    
960
out:
961
        close(fd);
962
        return err;
963
}
964

    
965
int
966
vhdi_file_table_load(const char *name, vhdi_file_table_t *table)
967
{
968
        off64_t off;
969
        size_t size;
970
        int err, i, fd;
971
        vhdi_file_table_header_t header;
972
        vhdi_file_table_entry_t *entries;
973

    
974
        entries = NULL;
975

    
976
        fd = open(name, O_RDONLY | O_LARGEFILE);
977
        if (fd == -1)
978
                return -errno;
979

    
980
        err = vhdi_file_table_load_header(fd, &header);
981
        if (err)
982
                goto out;
983

    
984
        if (!header.files)
985
                goto out;
986

    
987
        table->table = calloc(header.files, sizeof(vhdi_file_ref_t));
988
        if (!table->table) {
989
                err = -ENOMEM;
990
                goto out;
991
        }
992

    
993
        off = header.table_offset;
994
        err = lseek64(fd, off, SEEK_SET);
995
        if (err == (off64_t)-1) {
996
                err = -errno;
997
                goto out;
998
        }
999

    
1000
        size = header.files * sizeof(vhdi_file_table_entry_t);
1001
        entries = calloc(header.files, sizeof(vhdi_file_table_entry_t));
1002
        if (!entries) {
1003
                err = -ENOMEM;
1004
                goto out;
1005
        }
1006

    
1007
        err = read(fd, entries, size);
1008
        if (err != size) {
1009
                err = (errno ? -errno : -EIO);
1010
                goto out;
1011
        }
1012

    
1013
        for (i = 0; i < header.files; i++) {
1014
                vhdi_file_table_entry_t *entry;
1015

    
1016
                entry = entries + i;
1017
                vhdi_file_table_entry_in(entry);
1018

    
1019
                err = vhdi_file_table_entry_validate(entry);
1020
                if (err)
1021
                        goto out;
1022

    
1023
                table->table[i].path = vhdi_path_expand(name,
1024
                                                        &entry->p, &err);
1025
                if (err)
1026
                        goto out;
1027

    
1028
                err = vhdi_file_table_entry_validate_vhd(entry,
1029
                                                         table->table[i].path);
1030
                if (err)
1031
                        goto out;
1032

    
1033
                table->table[i].file_id       = entry->file_id;
1034
                table->table[i].vhd_timestamp = entry->vhd_timestamp;
1035
                uuid_copy(table->table[i].vhd_uuid, entry->vhd_uuid);
1036
        }
1037

    
1038
        err = 0;
1039
        table->entries = header.files;
1040

    
1041
out:
1042
        close(fd);
1043
        free(entries);
1044
        if (err) {
1045
                if (table->table) {
1046
                        for (i = 0; i < header.files; i++)
1047
                                free(table->table[i].path);
1048
                        free(table->table);
1049
                }
1050
                memset(table, 0, sizeof(vhdi_file_table_t));
1051
        }
1052

    
1053
        return err;
1054
}
1055

    
1056
static int
1057
vhdi_file_table_next_fid(const char *name,
1058
                         const char *file, vhdi_file_id_t *fid)
1059
{
1060
        int i, err;
1061
        char *path, __path[PATH_MAX];
1062
        vhdi_file_id_t max;
1063
        vhdi_file_table_t files;
1064

    
1065
        max  = 0;
1066
        path = NULL;
1067

    
1068
        err = vhdi_file_table_load(name, &files);
1069
        if (err)
1070
                return err;
1071

    
1072
        path = realpath(file, __path);
1073
        if (!path) {
1074
                err = -errno;
1075
                goto out;
1076
        }
1077

    
1078
        for (i = 0; i < files.entries; i++) {
1079
                if (!strcmp(path, files.table[i].path)) {
1080
                        err = -EEXIST;
1081
                        goto out;
1082
                }
1083

    
1084
                max = MAX(max, files.table[i].file_id);
1085
        }
1086

    
1087
        *fid = max + 1;
1088
        err  = 0;
1089

    
1090
out:
1091
        vhdi_file_table_free(&files);
1092

    
1093
        return err;
1094
}
1095

    
1096
static inline int
1097
vhdi_file_table_entry_initialize(vhdi_file_table_entry_t *entry,
1098
                                 const char *file_table, const char *file,
1099
                                 vhdi_file_id_t fid)
1100
{
1101
        int err;
1102
        struct stat stats;
1103
        vhd_context_t vhd;
1104

    
1105
        memset(entry, 0, sizeof(vhdi_file_table_entry_t));
1106

    
1107
        err = stat(file, &stats);
1108
        if (err == -1)
1109
                return -errno;
1110

    
1111
        entry->file_id       = fid;
1112
        entry->vhd_timestamp = vhd_time(stats.st_mtime);
1113

    
1114
        err = vhd_open(&vhd, file, VHD_OPEN_RDONLY);
1115
        if (err)
1116
                goto out;
1117

    
1118
        err = vhd_get_footer(&vhd);
1119
        if (err) {
1120
                vhd_close(&vhd);
1121
                goto out;
1122
        }
1123

    
1124
        uuid_copy(entry->vhd_uuid, vhd.footer.uuid);
1125

    
1126
        vhd_close(&vhd);
1127

    
1128
        err = vhdi_copy_path_to(&entry->p, file_table, file);
1129
        if (err)
1130
                goto out;
1131

    
1132
        err = 0;
1133

    
1134
out:
1135
        if (err)
1136
                memset(entry, 0, sizeof(vhdi_file_table_entry_t));
1137

    
1138
        return err;
1139
}
1140

    
1141
int
1142
vhdi_file_table_add(const char *name, const char *file, vhdi_file_id_t *_fid)
1143
{
1144
        off64_t off;
1145
        size_t size;
1146
        vhdi_file_id_t fid;
1147
        int err, fd, len;
1148
        vhdi_file_table_entry_t entry;
1149
        vhdi_file_table_header_t header;
1150

    
1151
        off   = 0;
1152
        fid   = 0;
1153
        *_fid = 0;
1154

    
1155
        len = strnlen(file, VHD_MAX_NAME_LEN - 1);
1156
        if (len == VHD_MAX_NAME_LEN - 1)
1157
                return -ENAMETOOLONG;
1158

    
1159
        err = vhdi_file_table_next_fid(name, file, &fid);
1160
        if (err)
1161
                return err;
1162

    
1163
        fd = open(name, O_RDWR | O_LARGEFILE);
1164
        if (fd == -1)
1165
                return -errno;
1166

    
1167
        err = vhdi_file_table_load_header(fd, &header);
1168
        if (err)
1169
                goto out;
1170

    
1171
        size = sizeof(vhdi_file_table_entry_t);
1172
        off  = header.table_offset + size * header.files;
1173

    
1174
        err = lseek64(fd, off, SEEK_SET);
1175
        if (err == (off64_t)-1) {
1176
                err = -errno;
1177
                goto out;
1178
        }
1179

    
1180
        err = vhdi_file_table_entry_initialize(&entry, name, file, fid);
1181
        if (err)
1182
                goto out;
1183

    
1184
        vhdi_file_table_entry_out(&entry);
1185

    
1186
        err = write(fd, &entry, size);
1187
        if (err != size) {
1188
                err = (errno ? -errno : -EIO);
1189
                goto out;
1190
        }
1191

    
1192
        header.files++;
1193
        err = vhdi_file_table_write_header(fd, &header);
1194
        if (err)
1195
                goto out;
1196

    
1197
        err   = 0;
1198
        *_fid = fid;
1199

    
1200
out:
1201
        if (err && off) {
1202
                int gcc = ftruncate(fd, off);
1203
                if (gcc) {};
1204
        }
1205
        close(fd);
1206

    
1207
        return err;
1208
}
1209

    
1210
void
1211
vhdi_file_table_free(vhdi_file_table_t *table)
1212
{
1213
        int i;
1214

    
1215
        if (table->table) {
1216
                for (i = 0; i < table->entries; i++)
1217
                        free(table->table[i].path);
1218
                free(table->table);
1219
        }
1220

    
1221
        memset(table, 0, sizeof(vhdi_file_table_t));
1222
}