Branch data Line data Source code
1 : : /*
2 : : * Copyright (c) 2016, Citrix Systems, Inc.
3 : : *
4 : : * All rights reserved.
5 : : *
6 : : * Redistribution and use in source and binary forms, with or without
7 : : * modification, are permitted provided that the following conditions are met:
8 : : *
9 : : * 1. Redistributions of source code must retain the above copyright
10 : : * notice, this list of conditions and the following disclaimer.
11 : : * 2. Redistributions in binary form must reproduce the above copyright
12 : : * notice, this list of conditions and the following disclaimer in the
13 : : * documentation and/or other materials provided with the distribution.
14 : : * 3. Neither the name of the copyright holder nor the names of its
15 : : * contributors may be used to endorse or promote products derived from
16 : : * this software without specific prior written permission.
17 : : *
18 : : * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 : : * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 : : * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 : : * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
22 : : * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
23 : : * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
24 : : * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
25 : : * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
26 : : * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
27 : : * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
28 : : * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 : : */
30 : :
31 : : #ifdef HAVE_CONFIG_H
32 : : #include "config.h"
33 : : #endif
34 : :
35 : : #include <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 : : #include "canonpath.h"
50 : : #include "util.h"
51 : :
52 : : typedef struct vhdi_path vhdi_path_t;
53 : : typedef struct vhdi_header vhdi_header_t;
54 : : typedef struct vhdi_bat_header vhdi_bat_header_t;
55 : : typedef struct vhdi_file_table_header vhdi_file_table_header_t;
56 : : typedef struct vhdi_file_table_entry vhdi_file_table_entry_t;
57 : :
58 : : static const char VHDI_HEADER_COOKIE[] = "vhdindex";
59 : : static const char VHDI_BAT_HEADER_COOKIE[] = "vhdi-bat";
60 : : static const char VHDI_FILE_TABLE_HEADER_COOKIE[] = "vhdifile";
61 : :
62 : : struct vhdi_path {
63 : : char path[VHD_MAX_NAME_LEN];
64 : : uint16_t bytes;
65 : : };
66 : :
67 : : struct vhdi_header {
68 : : char cookie[8];
69 : : uint32_t vhd_block_size;
70 : : uint64_t table_offset;
71 : : };
72 : :
73 : : struct vhdi_bat_header {
74 : : char cookie[8];
75 : : uint64_t vhd_blocks;
76 : : uint32_t vhd_block_size;
77 : : vhdi_path_t vhd_path;
78 : : vhdi_path_t index_path;
79 : : vhdi_path_t file_table_path;
80 : : uint64_t table_offset;
81 : : };
82 : :
83 : : struct vhdi_file_table_header {
84 : : char cookie[8];
85 : : uint32_t files;
86 : : uint64_t table_offset;
87 : : };
88 : :
89 : : struct vhdi_file_table_entry {
90 : : vhdi_path_t p;
91 : : vhdi_file_id_t file_id;
92 : : uuid_t vhd_uuid;
93 : : uint32_t vhd_timestamp;
94 : : };
95 : :
96 : : static inline int
97 : 0 : vhdi_seek(vhdi_context_t *ctx, off64_t off, int whence)
98 : : {
99 : : int err;
100 : :
101 : 0 : err = lseek64(ctx->fd, off, whence);
102 [ # # ]: 0 : if (err == (off64_t)-1)
103 : 0 : return -errno;
104 : :
105 : : return 0;
106 : : }
107 : :
108 : : static inline off64_t
109 : 0 : vhdi_position(vhdi_context_t *ctx)
110 : : {
111 : 0 : return lseek64(ctx->fd, 0, SEEK_CUR);
112 : : }
113 : :
114 : : static inline int
115 : 0 : vhdi_read(vhdi_context_t *ctx, void *buf, size_t size)
116 : : {
117 : : int err;
118 : :
119 : 0 : err = read(ctx->fd, buf, size);
120 [ # # ]: 0 : if (err != size)
121 [ # # ]: 0 : return (errno ? -errno : -EIO);
122 : :
123 : : return 0;
124 : : }
125 : :
126 : : static inline int
127 : 0 : vhdi_write(vhdi_context_t *ctx, void *buf, size_t size)
128 : : {
129 : : int err;
130 : :
131 : 0 : err = write(ctx->fd, buf, size);
132 [ # # ]: 0 : if (err != size)
133 [ # # ]: 0 : return (errno ? -errno : -EIO);
134 : :
135 : : return 0;
136 : : }
137 : :
138 : : static inline int
139 : : vhdi_check_block_size(uint32_t block_size)
140 : : {
141 : : int i, cnt;
142 : :
143 : 0 : cnt = 0;
144 [ # # ][ # # ]: 0 : for (i = 0; i < 32; i++)
[ # # ]
145 [ # # ][ # # ]: 0 : if ((block_size >> i) & 0x0001)
[ # # ]
146 : 0 : cnt++;
147 : :
148 [ # # ][ # # ]: 0 : if (cnt == 1)
[ # # ]
149 : : return 0;
150 : :
151 : : return -EINVAL;
152 : : }
153 : :
154 : : static inline void
155 : 0 : vhdi_header_in(vhdi_header_t *header)
156 : : {
157 : 0 : BE32_IN(&header->vhd_block_size);
158 : 0 : BE64_IN(&header->table_offset);
159 : 0 : }
160 : :
161 : : static inline void
162 : 0 : vhdi_header_out(vhdi_header_t *header)
163 : : {
164 : 0 : BE32_OUT(&header->vhd_block_size);
165 : 0 : BE64_OUT(&header->table_offset);
166 : 0 : }
167 : :
168 : : static inline int
169 : 0 : vhdi_header_validate(vhdi_header_t *header)
170 : : {
171 [ # # ]: 0 : if (memcmp(header->cookie, VHDI_HEADER_COOKIE, sizeof(header->cookie)))
172 : : return -EINVAL;
173 : :
174 : 0 : return vhdi_check_block_size(header->vhd_block_size);
175 : : }
176 : :
177 : : void
178 : 0 : vhdi_entry_in(vhdi_entry_t *entry)
179 : : {
180 : 0 : BE32_IN(&entry->file_id);
181 : 0 : BE32_IN(&entry->offset);
182 : 0 : }
183 : :
184 : : static inline vhdi_entry_t
185 : : vhdi_entry_out(vhdi_entry_t *entry)
186 : : {
187 : : vhdi_entry_t e;
188 : :
189 : 0 : e = *entry;
190 : 0 : BE32_OUT(&e.file_id);
191 : 0 : BE32_OUT(&e.offset);
192 : :
193 : : return e;
194 : : }
195 : :
196 : : static inline void
197 : 0 : vhdi_path_in(vhdi_path_t *path)
198 : : {
199 [ # # ][ # # ]: 0 : BE16_IN(&path->bytes);
[ # # ][ # # ]
200 : 0 : }
201 : :
202 : : static inline void
203 : 0 : vhdi_path_out(vhdi_path_t *path)
204 : : {
205 [ # # ][ # # ]: 0 : BE16_OUT(&path->bytes);
[ # # ][ # # ]
206 : 0 : }
207 : :
208 : : static inline void
209 : 0 : vhdi_bat_header_in(vhdi_bat_header_t *header)
210 : : {
211 : 0 : BE64_IN(&header->vhd_blocks);
212 : 0 : BE32_IN(&header->vhd_block_size);
213 : 0 : vhdi_path_in(&header->vhd_path);
214 : 0 : vhdi_path_in(&header->index_path);
215 : 0 : vhdi_path_in(&header->file_table_path);
216 : 0 : BE64_IN(&header->table_offset);
217 : 0 : }
218 : :
219 : : static inline void
220 : 0 : vhdi_bat_header_out(vhdi_bat_header_t *header)
221 : : {
222 : 0 : BE64_OUT(&header->vhd_blocks);
223 : 0 : BE32_OUT(&header->vhd_block_size);
224 : 0 : vhdi_path_out(&header->vhd_path);
225 : 0 : vhdi_path_out(&header->index_path);
226 : 0 : vhdi_path_out(&header->file_table_path);
227 : 0 : BE64_OUT(&header->table_offset);
228 : 0 : }
229 : :
230 : : static inline int
231 : : vhdi_path_validate(vhdi_path_t *path)
232 : : {
233 : : int i;
234 : :
235 [ # # ][ # # ]: 0 : if (path->bytes >= VHD_MAX_NAME_LEN - 1)
[ # # ][ # # ]
236 : : return -ENAMETOOLONG;
237 : :
238 [ # # ][ # # ]: 0 : for (i = 0; i < path->bytes; i++)
[ # # ][ # # ]
239 [ # # ][ # # ]: 0 : if (path->path[i] == '\0')
[ # # ][ # # ]
240 : : return 0;
241 : :
242 : : return -EINVAL;
243 : : }
244 : :
245 : : static inline char *
246 : 0 : vhdi_path_expand(const char *src, vhdi_path_t *dest, int *err)
247 : : {
248 : : int len;
249 : : char *path, *base, copy[VHD_MAX_NAME_LEN];
250 : : char *absolute_path, __absolute_path[PATH_MAX];
251 : :
252 : 0 : safe_strncpy(copy, src, sizeof(copy));
253 : 0 : base = dirname(copy);
254 : :
255 : 0 : *err = asprintf(&path, "%s/%s", base, dest->path);
256 [ # # ]: 0 : if (*err == -1) {
257 : 0 : *err = -ENOMEM;
258 : 0 : return NULL;
259 : : }
260 : :
261 : 0 : absolute_path = canonpath(path, __absolute_path, sizeof(__absolute_path));
262 : 0 : free(path);
263 [ # # ]: 0 : if (absolute_path)
264 : 0 : absolute_path = strdup(absolute_path);
265 [ # # ]: 0 : if (!absolute_path) {
266 : 0 : *err = -errno;
267 : 0 : return NULL;
268 : : }
269 : :
270 : 0 : len = strnlen(absolute_path, VHD_MAX_NAME_LEN - 1);
271 [ # # ]: 0 : if (len == VHD_MAX_NAME_LEN - 1) {
272 : 0 : free(absolute_path);
273 : 0 : *err = -ENAMETOOLONG;
274 : 0 : return NULL;
275 : : }
276 : :
277 : 0 : *err = 0;
278 : 0 : return absolute_path;
279 : : }
280 : :
281 : : static inline int
282 : 0 : vhdi_bat_header_validate(vhdi_bat_header_t *header)
283 : : {
284 : : int err;
285 : :
286 [ # # ]: 0 : if (memcmp(header->cookie,
287 : : VHDI_BAT_HEADER_COOKIE, sizeof(header->cookie)))
288 : : return -EINVAL;
289 : :
290 : 0 : err = vhdi_check_block_size(header->vhd_block_size);
291 [ # # ]: 0 : if (err)
292 : : return err;
293 : :
294 : 0 : err = vhdi_path_validate(&header->vhd_path);
295 [ # # ]: 0 : if (err)
296 : : return err;
297 : :
298 : 0 : err = vhdi_path_validate(&header->index_path);
299 [ # # ]: 0 : if (err)
300 : : return err;
301 : :
302 : 0 : err = vhdi_path_validate(&header->file_table_path);
303 [ # # ]: 0 : if (err)
304 : 0 : return err;
305 : :
306 : : return 0;
307 : : }
308 : :
309 : : static inline int
310 : 0 : vhdi_bat_load_header(int fd, vhdi_bat_header_t *header)
311 : : {
312 : : int err;
313 : :
314 : 0 : err = lseek64(fd, 0, SEEK_SET);
315 [ # # ]: 0 : if (err == (off64_t)-1)
316 : 0 : return -errno;
317 : :
318 : 0 : err = read(fd, header, sizeof(vhdi_bat_header_t));
319 [ # # ]: 0 : if (err != sizeof(vhdi_bat_header_t))
320 [ # # ]: 0 : return (errno ? -errno : -EIO);
321 : :
322 : 0 : vhdi_bat_header_in(header);
323 : 0 : return vhdi_bat_header_validate(header);
324 : : }
325 : :
326 : : static inline void
327 : 0 : vhdi_file_table_header_in(vhdi_file_table_header_t *header)
328 : : {
329 : 0 : BE32_OUT(&header->files);
330 : 0 : BE64_OUT(&header->table_offset);
331 : 0 : }
332 : :
333 : : static inline void
334 : 0 : vhdi_file_table_header_out(vhdi_file_table_header_t *header)
335 : : {
336 : 0 : BE32_OUT(&header->files);
337 : 0 : BE64_OUT(&header->table_offset);
338 : 0 : }
339 : :
340 : : static inline int
341 : : vhdi_file_table_header_validate(vhdi_file_table_header_t *header)
342 : : {
343 [ # # ][ # # ]: 0 : if (memcmp(header->cookie,
344 : : VHDI_FILE_TABLE_HEADER_COOKIE, sizeof(header->cookie)))
345 : : return -EINVAL;
346 : :
347 : : return 0;
348 : : }
349 : :
350 : : static inline int
351 : 0 : vhdi_file_table_load_header(int fd, vhdi_file_table_header_t *header)
352 : : {
353 : : int err;
354 : :
355 : 0 : err = lseek64(fd, 0, SEEK_SET);
356 [ # # ]: 0 : if (err == (off64_t)-1)
357 : 0 : return -errno;
358 : :
359 : 0 : err = read(fd, header, sizeof(vhdi_file_table_header_t));
360 [ # # ]: 0 : if (err != sizeof(vhdi_file_table_header_t))
361 [ # # ]: 0 : return (errno ? -errno : -EIO);
362 : :
363 : 0 : vhdi_file_table_header_in(header);
364 : 0 : return vhdi_file_table_header_validate(header);
365 : : }
366 : :
367 : : static inline int
368 : 0 : vhdi_file_table_write_header(int fd, vhdi_file_table_header_t *header)
369 : : {
370 : : int err;
371 : :
372 : 0 : err = lseek64(fd, 0, SEEK_SET);
373 [ # # ]: 0 : if (err == (off64_t)-1)
374 : 0 : return -errno;
375 : :
376 : 0 : err = vhdi_file_table_header_validate(header);
377 [ # # ]: 0 : if (err)
378 : : return err;
379 : :
380 : 0 : vhdi_file_table_header_out(header);
381 : :
382 : 0 : err = write(fd, header, sizeof(vhdi_file_table_header_t));
383 [ # # ]: 0 : if (err != sizeof(vhdi_file_table_header_t))
384 [ # # ]: 0 : return (errno ? -errno : -EIO);
385 : :
386 : : return 0;
387 : : }
388 : :
389 : : static inline void
390 : : vhdi_file_table_entry_in(vhdi_file_table_entry_t *entry)
391 : : {
392 : 0 : vhdi_path_in(&entry->p);
393 : 0 : BE32_IN(&entry->file_id);
394 : 0 : BE32_IN(&entry->vhd_timestamp);
395 : : }
396 : :
397 : : static inline void
398 : : vhdi_file_table_entry_out(vhdi_file_table_entry_t *entry)
399 : : {
400 : 0 : vhdi_path_out(&entry->p);
401 : 0 : BE32_OUT(&entry->file_id);
402 : 0 : BE32_OUT(&entry->vhd_timestamp);
403 : : }
404 : :
405 : : static inline int
406 : : vhdi_file_table_entry_validate(vhdi_file_table_entry_t *entry)
407 : : {
408 : 0 : return vhdi_path_validate(&entry->p);
409 : : }
410 : :
411 : : static inline int
412 : 0 : vhdi_file_table_entry_validate_vhd(vhdi_file_table_entry_t *entry,
413 : : const char *path)
414 : : {
415 : : int err;
416 : : vhd_context_t vhd;
417 : : struct stat stats;
418 : :
419 : 0 : err = stat(path, &stats);
420 [ # # ]: 0 : if (err == -1)
421 : 0 : return -errno;
422 : :
423 [ # # ]: 0 : if (entry->vhd_timestamp != vhd_time(stats.st_mtime))
424 : : return -EINVAL;
425 : :
426 : 0 : err = vhd_open(&vhd, path, VHD_OPEN_RDONLY);
427 [ # # ]: 0 : if (err)
428 : : return err;
429 : :
430 : 0 : err = vhd_get_footer(&vhd);
431 [ # # ]: 0 : if (err)
432 : : goto out;
433 : :
434 [ # # ]: 0 : if (uuid_compare(entry->vhd_uuid, vhd.footer.uuid)) {
435 : 0 : err = -EINVAL;
436 : 0 : goto out;
437 : : }
438 : :
439 : : out:
440 : 0 : vhd_close(&vhd);
441 : : return err;
442 : : }
443 : :
444 : : int
445 : 0 : vhdi_create(const char *name, uint32_t vhd_block_size)
446 : : {
447 : : void *buf;
448 : : int err, fd;
449 : : size_t size;
450 : : vhdi_header_t header;
451 : :
452 : : memset(&header, 0, sizeof(vhdi_header_t));
453 : :
454 : 0 : err = vhdi_check_block_size(vhd_block_size);
455 [ # # ]: 0 : if (err)
456 : 0 : return err;
457 : :
458 : 0 : err = access(name, F_OK);
459 [ # # ][ # # ]: 0 : if (!err || errno != ENOENT)
460 [ # # ]: 0 : return (err ? err : -EEXIST);
461 : :
462 : : memcpy(header.cookie, VHDI_HEADER_COOKIE, sizeof(header.cookie));
463 : 0 : header.vhd_block_size = vhd_block_size;
464 : 0 : header.table_offset = vhd_bytes_padded(sizeof(vhdi_header_t));
465 : :
466 : 0 : err = vhdi_header_validate(&header);
467 [ # # ]: 0 : if (err)
468 : : return err;
469 : :
470 : 0 : vhdi_header_out(&header);
471 : :
472 : 0 : size = vhd_bytes_padded(sizeof(vhdi_header_t));
473 : 0 : err = posix_memalign(&buf, VHD_SECTOR_SIZE, size);
474 [ # # ]: 0 : if (err)
475 : 0 : return -err;
476 : :
477 : 0 : memset(buf, 0, size);
478 : 0 : memcpy(buf, &header, sizeof(vhdi_header_t));
479 : :
480 : 0 : fd = open(name, O_CREAT | O_TRUNC | O_RDWR, 0600);
481 [ # # ]: 0 : if (fd == -1) {
482 : 0 : err = -errno;
483 : 0 : goto fail;
484 : : }
485 : :
486 : 0 : err = write(fd, buf, size);
487 [ # # ]: 0 : if (err != size) {
488 [ # # ]: 0 : err = (errno ? -errno : -EIO);
489 : 0 : goto fail;
490 : : }
491 : :
492 : 0 : close(fd);
493 : 0 : free(buf);
494 : :
495 : 0 : return 0;
496 : :
497 : : fail:
498 [ # # ]: 0 : if (fd >= 0) {
499 : 0 : close(fd);
500 : 0 : unlink(name);
501 : : }
502 : 0 : free(buf);
503 : 0 : return err;
504 : : }
505 : :
506 : : int
507 : 0 : vhdi_open(vhdi_context_t *ctx, const char *file, int flags)
508 : : {
509 : : int err, fd;
510 : : size_t size;
511 : : char *name;
512 : : void *buf;
513 : : vhdi_header_t header;
514 : :
515 : 0 : buf = NULL;
516 : : memset(ctx, 0, sizeof(vhdi_context_t));
517 : :
518 : 0 : name = strdup(file);
519 [ # # ]: 0 : if (!name)
520 : 0 : return -ENOMEM;
521 : :
522 : 0 : fd = open(file, flags | O_LARGEFILE);
523 [ # # ]: 0 : if (fd == -1) {
524 : 0 : free(name);
525 : 0 : return -errno;
526 : : }
527 : :
528 : 0 : size = vhd_bytes_padded(sizeof(vhdi_header_t));
529 : 0 : err = posix_memalign(&buf, VHD_SECTOR_SIZE, size);
530 [ # # ]: 0 : if (err) {
531 : 0 : err = -err;
532 : 0 : goto fail;
533 : : }
534 : :
535 : 0 : err = read(fd, buf, size);
536 [ # # ]: 0 : if (err != size) {
537 [ # # ]: 0 : err = (errno ? -errno : -EIO);
538 : 0 : goto fail;
539 : : }
540 : :
541 : 0 : memcpy(&header, buf, sizeof(vhdi_header_t));
542 : 0 : free(buf);
543 : 0 : buf = NULL;
544 : :
545 : 0 : vhdi_header_in(&header);
546 : 0 : err = vhdi_header_validate(&header);
547 [ # # ]: 0 : if (err)
548 : : goto fail;
549 : :
550 : 0 : ctx->fd = fd;
551 : 0 : ctx->name = name;
552 : 0 : ctx->spb = header.vhd_block_size >> VHD_SECTOR_SHIFT;
553 : 0 : ctx->vhd_block_size = header.vhd_block_size;
554 : :
555 : 0 : return 0;
556 : :
557 : : fail:
558 : 0 : close(fd);
559 : 0 : free(buf);
560 : 0 : free(name);
561 : 0 : return err;
562 : : }
563 : :
564 : : void
565 : 0 : vhdi_close(vhdi_context_t *ctx)
566 : : {
567 : 0 : close(ctx->fd);
568 : 0 : free(ctx->name);
569 : 0 : }
570 : :
571 : : int
572 : 0 : vhdi_read_block(vhdi_context_t *ctx, vhdi_block_t *block, uint32_t sector)
573 : : {
574 : : int i, err;
575 : : size_t size;
576 : : void *tab;
577 : :
578 : 0 : err = vhdi_seek(ctx, vhd_sectors_to_bytes(sector), SEEK_SET);
579 [ # # ]: 0 : if (err)
580 : 0 : return err;
581 : :
582 : 0 : size = vhd_bytes_padded(ctx->spb * sizeof(vhdi_entry_t));
583 : :
584 : 0 : block->entries = ctx->spb;
585 : 0 : err = posix_memalign(&tab, VHD_SECTOR_SIZE, size);
586 [ # # ]: 0 : if (err)
587 : 0 : return -err;
588 : :
589 : 0 : block->table = tab;
590 : :
591 : 0 : err = vhdi_read(ctx, block->table, size);
592 [ # # ]: 0 : if (err)
593 : : goto fail;
594 : :
595 [ # # ]: 0 : for (i = 0; i < block->entries; i++)
596 : 0 : vhdi_entry_in(&block->table[i]);
597 : :
598 : : return 0;
599 : :
600 : : fail:
601 : 0 : free(block->table);
602 : 0 : return err;
603 : : }
604 : :
605 : : int
606 : 0 : vhdi_write_block(vhdi_context_t *ctx, vhdi_block_t *block, uint32_t sector)
607 : : {
608 : : void *buf;
609 : : int i, err;
610 : : size_t size;
611 : : vhdi_entry_t *entries;
612 : :
613 : 0 : err = vhdi_seek(ctx, vhd_sectors_to_bytes(sector), SEEK_SET);
614 [ # # ]: 0 : if (err)
615 : 0 : return err;
616 : :
617 : 0 : size = vhd_bytes_padded(ctx->spb * sizeof(vhdi_entry_t));
618 : 0 : err = posix_memalign(&buf, VHD_SECTOR_SIZE, size);
619 [ # # ]: 0 : if (err)
620 : 0 : return -err;
621 : :
622 : 0 : memset(buf, 0, size);
623 : 0 : entries = (vhdi_entry_t *)buf;
624 : :
625 [ # # ]: 0 : for (i = 0; i < block->entries; i++)
626 : 0 : entries[i] = vhdi_entry_out(&block->table[i]);
627 : :
628 : 0 : err = vhdi_write(ctx, entries, size);
629 [ # # ]: 0 : if (err)
630 : : goto out;
631 : :
632 : 0 : err = 0;
633 : :
634 : : out:
635 : 0 : free(entries);
636 : 0 : return err;
637 : : }
638 : :
639 : : int
640 : 0 : vhdi_append_block(vhdi_context_t *ctx, vhdi_block_t *block, uint32_t *sector)
641 : : {
642 : : void *buf;
643 : : int i, err;
644 : : off64_t off;
645 : : size_t size;
646 : : vhdi_entry_t *entries;
647 : :
648 : 0 : err = vhdi_seek(ctx, 0, SEEK_END);
649 [ # # ]: 0 : if (err)
650 : 0 : return err;
651 : :
652 : 0 : off = vhd_bytes_padded(vhdi_position(ctx));
653 : :
654 : 0 : err = vhdi_seek(ctx, off, SEEK_SET);
655 [ # # ]: 0 : if (err)
656 : : return err;
657 : :
658 : 0 : size = vhd_bytes_padded(block->entries * sizeof(vhdi_entry_t));
659 : 0 : err = posix_memalign(&buf, VHD_SECTOR_SIZE, size);
660 [ # # ]: 0 : if (err)
661 : 0 : return -err;
662 : :
663 : 0 : memset(buf, 0, size);
664 : 0 : entries = buf;
665 : :
666 [ # # ]: 0 : for (i = 0; i < block->entries; i++)
667 : 0 : entries[i] = vhdi_entry_out(&block->table[i]);
668 : :
669 : 0 : err = vhdi_write(ctx, entries, block->entries * sizeof(vhdi_entry_t));
670 [ # # ]: 0 : if (err)
671 : : goto out;
672 : :
673 : 0 : err = 0;
674 : 0 : *sector = off >> VHD_SECTOR_SHIFT;
675 : : out:
676 [ # # ]: 0 : if (err) {
677 : 0 : int gcc = ftruncate(ctx->fd, off);
678 : : if (gcc) {}
679 : : }
680 : 0 : free(entries);
681 : 0 : return err;
682 : : }
683 : :
684 : : static int
685 : 0 : vhdi_copy_path_to(vhdi_path_t *path, const char *src, const char *dest, size_t dest_size)
686 : : {
687 : : int len, err;
688 : : char *file, *relative_path, copy[VHD_MAX_NAME_LEN];
689 : : char *absolute_path, __absolute_path[PATH_MAX];
690 : :
691 : 0 : safe_strncpy(copy, dest, sizeof(copy));
692 : :
693 : 0 : file = basename(copy);
694 : 0 : absolute_path = canonpath(copy, __absolute_path, sizeof(__absolute_path));
695 : 0 : relative_path = NULL;
696 : :
697 [ # # ]: 0 : if (!absolute_path) {
698 : 0 : err = -errno;
699 : 0 : goto out;
700 : : }
701 : :
702 [ # # ]: 0 : if (!strcmp(file, "")) {
703 : 0 : err = -EINVAL;
704 : 0 : goto out;
705 : : }
706 : :
707 : 0 : relative_path = relative_path_to((char *)src, absolute_path, &err);
708 [ # # ][ # # ]: 0 : if (!relative_path || err) {
709 [ # # ]: 0 : err = (err ? err : -EINVAL);
710 : 0 : goto out;
711 : : }
712 : :
713 : 0 : len = strnlen(relative_path, VHD_MAX_NAME_LEN - 1);
714 [ # # ]: 0 : if (len == VHD_MAX_NAME_LEN - 1) {
715 : 0 : err = -ENAMETOOLONG;
716 : 0 : goto out;
717 : : }
718 : :
719 : 0 : safe_strncpy(path->path, relative_path, dest_size);
720 : 0 : path->bytes = len + 1;
721 : :
722 : 0 : err = 0;
723 : :
724 : : out:
725 : 0 : free(relative_path);
726 : 0 : return err;
727 : : }
728 : :
729 : : int
730 : 0 : vhdi_bat_create(const char *name, const char *vhd,
731 : : const char *index, const char *file_table)
732 : : {
733 : : int err, fd;
734 : : off64_t off;
735 : : vhd_context_t ctx;
736 : : vhdi_bat_header_t header;
737 : :
738 : : memset(&header, 0, sizeof(vhdi_bat_header_t));
739 : :
740 : 0 : err = access(name, F_OK);
741 [ # # ][ # # ]: 0 : if (!err || errno != ENOENT)
742 [ # # ]: 0 : return (err ? -err : -EEXIST);
743 : :
744 : 0 : err = vhd_open(&ctx, vhd, VHD_OPEN_RDONLY);
745 [ # # ]: 0 : if (err)
746 : : return err;
747 : :
748 : 0 : err = vhd_get_header(&ctx);
749 [ # # ]: 0 : if (err) {
750 : 0 : vhd_close(&ctx);
751 : : return err;
752 : : }
753 : :
754 : 0 : header.vhd_blocks = ctx.header.max_bat_size;
755 : 0 : header.vhd_block_size = ctx.header.block_size;
756 : :
757 : 0 : vhd_close(&ctx);
758 : :
759 : 0 : fd = open(name, O_CREAT | O_TRUNC | O_RDWR, 0600);
760 [ # # ]: 0 : if (fd == -1)
761 : 0 : return -errno;
762 : :
763 : 0 : err = vhdi_copy_path_to(&header.vhd_path, name, vhd, sizeof(header.vhd_path.path));
764 [ # # ]: 0 : if (err)
765 : : goto fail;
766 : :
767 : 0 : err = vhdi_copy_path_to(&header.index_path, name, index, sizeof(header.index_path.path));
768 [ # # ]: 0 : if (err)
769 : : goto fail;
770 : :
771 : 0 : err = vhdi_copy_path_to(&header.file_table_path, name, file_table, sizeof(header.file_table_path.path));
772 [ # # ]: 0 : if (err)
773 : : goto fail;
774 : :
775 : 0 : off = vhd_bytes_padded(sizeof(vhdi_bat_header_t));
776 : :
777 : 0 : header.table_offset = off;
778 : : memcpy(header.cookie, VHDI_BAT_HEADER_COOKIE, sizeof(header.cookie));
779 : :
780 : 0 : err = vhdi_bat_header_validate(&header);
781 [ # # ]: 0 : if (err)
782 : : goto fail;
783 : :
784 : 0 : vhdi_bat_header_out(&header);
785 : :
786 : 0 : err = write(fd, &header, sizeof(vhdi_bat_header_t));
787 [ # # ]: 0 : if (err != sizeof(vhdi_bat_header_t)) {
788 [ # # ]: 0 : err = (errno ? -errno : -EIO);
789 : 0 : goto fail;
790 : : }
791 : :
792 : 0 : close(fd);
793 : : return 0;
794 : :
795 : : fail:
796 : 0 : close(fd);
797 : 0 : unlink(name);
798 : 0 : return err;
799 : : }
800 : :
801 : : int
802 : 0 : vhdi_bat_load(const char *name, vhdi_bat_t *bat)
803 : : {
804 : : char *path;
805 : : int err, fd;
806 : : size_t size;
807 : : uint32_t *table;
808 : : vhdi_bat_header_t header;
809 : :
810 : 0 : table = NULL;
811 : :
812 : 0 : fd = open(name, O_RDONLY | O_LARGEFILE);
813 [ # # ]: 0 : if (fd == -1)
814 : 0 : return -errno;
815 : :
816 : 0 : err = vhdi_bat_load_header(fd, &header);
817 [ # # ]: 0 : if (err)
818 : : goto out;
819 : :
820 : 0 : size = header.vhd_blocks * sizeof(uint32_t);
821 : 0 : table = malloc(size);
822 [ # # ]: 0 : if (!table) {
823 : 0 : err = -ENOMEM;
824 : 0 : goto out;
825 : : }
826 : :
827 : 0 : err = lseek64(fd, header.table_offset, SEEK_SET);
828 [ # # ]: 0 : if (err == (off64_t)-1) {
829 : 0 : err = -errno;
830 : 0 : goto out;
831 : : }
832 : :
833 : 0 : err = read(fd, table, size);
834 [ # # ]: 0 : if (err != size) {
835 [ # # ]: 0 : err = (errno ? -errno : -EIO);
836 : 0 : goto out;
837 : : }
838 : :
839 : 0 : path = vhdi_path_expand(name, &header.vhd_path, &err);
840 [ # # ]: 0 : if (err)
841 : : goto out;
842 : 0 : safe_strncpy(bat->vhd_path, path, sizeof(bat->vhd_path));
843 : 0 : free(path);
844 : :
845 : 0 : err = access(bat->vhd_path, F_OK);
846 [ # # ]: 0 : if (err == -1) {
847 : 0 : err = -errno;
848 : 0 : goto out;
849 : : }
850 : :
851 : 0 : path = vhdi_path_expand(name, &header.index_path, &err);
852 [ # # ]: 0 : if (err)
853 : : goto out;
854 : 0 : safe_strncpy(bat->index_path, path, sizeof(bat->index_path));
855 : 0 : free(path);
856 : :
857 : 0 : err = access(bat->index_path, F_OK);
858 [ # # ]: 0 : if (err == -1) {
859 : 0 : err = -errno;
860 : 0 : goto out;
861 : : }
862 : :
863 : 0 : path = vhdi_path_expand(name, &header.file_table_path, &err);
864 [ # # ]: 0 : if (err)
865 : : goto out;
866 : 0 : safe_strncpy(bat->file_table_path, path, sizeof(bat->file_table_path));
867 : 0 : free(path);
868 : :
869 : 0 : err = access(bat->file_table_path, F_OK);
870 [ # # ]: 0 : if (err == -1) {
871 : 0 : err = -errno;
872 : 0 : goto out;
873 : : }
874 : :
875 : 0 : bat->vhd_blocks = header.vhd_blocks;
876 : 0 : bat->vhd_block_size = header.vhd_block_size;
877 : 0 : bat->table = table;
878 : :
879 : 0 : err = 0;
880 : :
881 : : out:
882 : 0 : close(fd);
883 [ # # ]: 0 : if (err) {
884 : 0 : free(table);
885 : : memset(bat, 0, sizeof(vhdi_bat_t));
886 : : }
887 : :
888 : 0 : return err;
889 : : }
890 : :
891 : : int
892 : 0 : vhdi_bat_write(const char *name, vhdi_bat_t *bat)
893 : : {
894 : : int err, fd;
895 : : size_t size;
896 : : vhdi_bat_header_t header;
897 : :
898 : 0 : fd = open(name, O_RDWR | O_LARGEFILE);
899 [ # # ]: 0 : if (fd == -1)
900 : 0 : return -errno;
901 : :
902 : 0 : err = vhdi_bat_load_header(fd, &header);
903 [ # # ]: 0 : if (err)
904 : : goto out;
905 : :
906 [ # # ][ # # ]: 0 : if (header.vhd_blocks != bat->vhd_blocks ||
907 : 0 : header.vhd_block_size != bat->vhd_block_size) {
908 : : err = -EINVAL;
909 : : goto out;
910 : : }
911 : :
912 : 0 : err = lseek64(fd, header.table_offset, SEEK_SET);
913 [ # # ]: 0 : if (err == (off64_t)-1) {
914 : 0 : err = -errno;
915 : 0 : goto out;
916 : : }
917 : :
918 : 0 : size = bat->vhd_blocks * sizeof(uint32_t);
919 : 0 : err = write(fd, bat->table, size);
920 [ # # ]: 0 : if (err != size) {
921 [ # # ]: 0 : err = (errno ? -errno : -EIO);
922 : 0 : goto out;
923 : : }
924 : :
925 : : err = 0;
926 : :
927 : : out:
928 : 0 : close(fd);
929 : : return err;
930 : : }
931 : :
932 : : int
933 : 0 : vhdi_file_table_create(const char *file)
934 : : {
935 : : int err, fd;
936 : : off64_t off;
937 : : vhdi_file_table_header_t header;
938 : :
939 : : memset(&header, 0, sizeof(vhdi_file_table_header_t));
940 : :
941 : 0 : err = access(file, F_OK);
942 [ # # ][ # # ]: 0 : if (!err || errno != ENOENT)
943 [ # # ]: 0 : return (err ? err : -EEXIST);
944 : :
945 : 0 : off = vhd_bytes_padded(sizeof(vhdi_file_table_header_t));
946 : :
947 : 0 : header.files = 0;
948 : 0 : header.table_offset = off;
949 : : memcpy(header.cookie,
950 : : VHDI_FILE_TABLE_HEADER_COOKIE, sizeof(header.cookie));
951 : :
952 : 0 : vhdi_file_table_header_out(&header);
953 : :
954 : 0 : fd = open(file, O_CREAT | O_TRUNC | O_RDWR, 0600);
955 [ # # ]: 0 : if (fd == -1)
956 : 0 : return -errno;
957 : :
958 : 0 : err = write(fd, &header, sizeof(vhdi_file_table_header_t));
959 [ # # ]: 0 : if (err != sizeof(vhdi_file_table_header_t)) {
960 [ # # ]: 0 : err = (errno ? -errno : -EIO);
961 : 0 : goto out;
962 : : }
963 : :
964 : : err = 0;
965 : :
966 : : out:
967 : 0 : close(fd);
968 : : return err;
969 : : }
970 : :
971 : : int
972 : 0 : vhdi_file_table_load(const char *name, vhdi_file_table_t *table)
973 : : {
974 : : off64_t off;
975 : : size_t size;
976 : : int err, i, fd;
977 : : vhdi_file_table_header_t header;
978 : : vhdi_file_table_entry_t *entries;
979 : :
980 : 0 : entries = NULL;
981 : :
982 : 0 : fd = open(name, O_RDONLY | O_LARGEFILE);
983 [ # # ]: 0 : if (fd == -1)
984 : 0 : return -errno;
985 : :
986 : 0 : err = vhdi_file_table_load_header(fd, &header);
987 [ # # ]: 0 : if (err)
988 : : goto out;
989 : :
990 [ # # ]: 0 : if (!header.files)
991 : : goto out;
992 : :
993 : 0 : table->table = calloc(header.files, sizeof(vhdi_file_ref_t));
994 [ # # ]: 0 : if (!table->table) {
995 : 0 : err = -ENOMEM;
996 : 0 : goto out;
997 : : }
998 : :
999 : 0 : off = header.table_offset;
1000 : 0 : err = lseek64(fd, off, SEEK_SET);
1001 [ # # ]: 0 : if (err == (off64_t)-1) {
1002 : 0 : err = -errno;
1003 : 0 : goto out;
1004 : : }
1005 : :
1006 : 0 : size = header.files * sizeof(vhdi_file_table_entry_t);
1007 : 0 : entries = calloc(header.files, sizeof(vhdi_file_table_entry_t));
1008 [ # # ]: 0 : if (!entries) {
1009 : 0 : err = -ENOMEM;
1010 : 0 : goto out;
1011 : : }
1012 : :
1013 : 0 : err = read(fd, entries, size);
1014 [ # # ]: 0 : if (err != size) {
1015 [ # # ]: 0 : err = (errno ? -errno : -EIO);
1016 : 0 : goto out;
1017 : : }
1018 : :
1019 [ # # ]: 0 : for (i = 0; i < header.files; i++) {
1020 : : vhdi_file_table_entry_t *entry;
1021 : :
1022 : 0 : entry = entries + i;
1023 : : vhdi_file_table_entry_in(entry);
1024 : :
1025 : 0 : err = vhdi_file_table_entry_validate(entry);
1026 [ # # ]: 0 : if (err)
1027 : : goto out;
1028 : :
1029 : 0 : table->table[i].path = vhdi_path_expand(name,
1030 : : &entry->p, &err);
1031 [ # # ]: 0 : if (err)
1032 : : goto out;
1033 : :
1034 : 0 : err = vhdi_file_table_entry_validate_vhd(entry,
1035 : 0 : table->table[i].path);
1036 [ # # ]: 0 : if (err)
1037 : : goto out;
1038 : :
1039 : 0 : table->table[i].file_id = entry->file_id;
1040 : 0 : table->table[i].vhd_timestamp = entry->vhd_timestamp;
1041 : 0 : uuid_copy(table->table[i].vhd_uuid, entry->vhd_uuid);
1042 : : }
1043 : :
1044 : 0 : err = 0;
1045 : 0 : table->entries = header.files;
1046 : :
1047 : : out:
1048 : 0 : close(fd);
1049 : 0 : free(entries);
1050 [ # # ]: 0 : if (err) {
1051 [ # # ]: 0 : if (table->table) {
1052 [ # # ]: 0 : for (i = 0; i < header.files; i++)
1053 : 0 : free(table->table[i].path);
1054 : 0 : free(table->table);
1055 : : }
1056 : : memset(table, 0, sizeof(vhdi_file_table_t));
1057 : : }
1058 : :
1059 : 0 : return err;
1060 : : }
1061 : :
1062 : : static int
1063 : 0 : vhdi_file_table_next_fid(const char *name,
1064 : : const char *file, vhdi_file_id_t *fid)
1065 : : {
1066 : : int i, err;
1067 : : char *path, __path[PATH_MAX];
1068 : : vhdi_file_id_t max;
1069 : : vhdi_file_table_t files;
1070 : :
1071 : 0 : max = 0;
1072 : 0 : path = NULL;
1073 : :
1074 : 0 : err = vhdi_file_table_load(name, &files);
1075 [ # # ]: 0 : if (err)
1076 : 0 : return err;
1077 : :
1078 : 0 : path = canonpath(file, __path, sizeof(__path));
1079 [ # # ]: 0 : if (!path) {
1080 : 0 : err = -errno;
1081 : 0 : goto out;
1082 : : }
1083 : :
1084 [ # # ]: 0 : for (i = 0; i < files.entries; i++) {
1085 [ # # ]: 0 : if (!strcmp(path, files.table[i].path)) {
1086 : : err = -EEXIST;
1087 : : goto out;
1088 : : }
1089 : :
1090 : 0 : max = MAX(max, files.table[i].file_id);
1091 : : }
1092 : :
1093 : 0 : *fid = max + 1;
1094 : 0 : err = 0;
1095 : :
1096 : : out:
1097 : 0 : vhdi_file_table_free(&files);
1098 : :
1099 : 0 : return err;
1100 : : }
1101 : :
1102 : : static inline int
1103 : 0 : vhdi_file_table_entry_initialize(vhdi_file_table_entry_t *entry,
1104 : : const char *file_table, const char *file,
1105 : : vhdi_file_id_t fid)
1106 : : {
1107 : : int err;
1108 : : struct stat stats;
1109 : : vhd_context_t vhd;
1110 : :
1111 : : memset(entry, 0, sizeof(vhdi_file_table_entry_t));
1112 : :
1113 : 0 : err = stat(file, &stats);
1114 [ # # ]: 0 : if (err == -1)
1115 : 0 : return -errno;
1116 : :
1117 : 0 : entry->file_id = fid;
1118 : 0 : entry->vhd_timestamp = vhd_time(stats.st_mtime);
1119 : :
1120 : 0 : err = vhd_open(&vhd, file, VHD_OPEN_RDONLY);
1121 [ # # ]: 0 : if (err)
1122 : : goto out;
1123 : :
1124 : 0 : err = vhd_get_footer(&vhd);
1125 [ # # ]: 0 : if (err) {
1126 : 0 : vhd_close(&vhd);
1127 : : goto out;
1128 : : }
1129 : :
1130 : 0 : uuid_copy(entry->vhd_uuid, vhd.footer.uuid);
1131 : :
1132 : 0 : vhd_close(&vhd);
1133 : :
1134 : 0 : err = vhdi_copy_path_to(&entry->p, file_table, file, sizeof(entry->p.path));
1135 [ # # ]: 0 : if (err)
1136 : : goto out;
1137 : :
1138 : 0 : err = 0;
1139 : :
1140 : : out:
1141 [ # # ]: 0 : if (err)
1142 : : memset(entry, 0, sizeof(vhdi_file_table_entry_t));
1143 : :
1144 : 0 : return err;
1145 : : }
1146 : :
1147 : : int
1148 : 0 : vhdi_file_table_add(const char *name, const char *file, vhdi_file_id_t *_fid)
1149 : : {
1150 : : off64_t off;
1151 : : size_t size;
1152 : : vhdi_file_id_t fid;
1153 : : int err, fd, len;
1154 : : vhdi_file_table_entry_t entry;
1155 : : vhdi_file_table_header_t header;
1156 : :
1157 : 0 : off = 0;
1158 : 0 : fid = 0;
1159 : 0 : *_fid = 0;
1160 : :
1161 : 0 : len = strnlen(file, VHD_MAX_NAME_LEN - 1);
1162 [ # # ]: 0 : if (len == VHD_MAX_NAME_LEN - 1)
1163 : 0 : return -ENAMETOOLONG;
1164 : :
1165 : 0 : err = vhdi_file_table_next_fid(name, file, &fid);
1166 [ # # ]: 0 : if (err)
1167 : : return err;
1168 : :
1169 : 0 : fd = open(name, O_RDWR | O_LARGEFILE);
1170 [ # # ]: 0 : if (fd == -1)
1171 : 0 : return -errno;
1172 : :
1173 : 0 : err = vhdi_file_table_load_header(fd, &header);
1174 [ # # ]: 0 : if (err)
1175 : : goto out;
1176 : :
1177 : 0 : size = sizeof(vhdi_file_table_entry_t);
1178 : 0 : off = header.table_offset + size * header.files;
1179 : :
1180 : 0 : err = lseek64(fd, off, SEEK_SET);
1181 [ # # ]: 0 : if (err == (off64_t)-1) {
1182 : 0 : err = -errno;
1183 : 0 : goto out;
1184 : : }
1185 : :
1186 : 0 : err = vhdi_file_table_entry_initialize(&entry, name, file, fid);
1187 [ # # ]: 0 : if (err)
1188 : : goto out;
1189 : :
1190 : : vhdi_file_table_entry_out(&entry);
1191 : :
1192 : 0 : err = write(fd, &entry, size);
1193 [ # # ]: 0 : if (err != size) {
1194 [ # # ]: 0 : err = (errno ? -errno : -EIO);
1195 : 0 : goto out;
1196 : : }
1197 : :
1198 : 0 : header.files++;
1199 : 0 : err = vhdi_file_table_write_header(fd, &header);
1200 [ # # ]: 0 : if (err)
1201 : : goto out;
1202 : :
1203 : 0 : err = 0;
1204 : 0 : *_fid = fid;
1205 : :
1206 : : out:
1207 [ # # ]: 0 : if (err && off) {
1208 : 0 : int gcc = ftruncate(fd, off);
1209 : : if (gcc) {};
1210 : : }
1211 : 0 : close(fd);
1212 : :
1213 : : return err;
1214 : : }
1215 : :
1216 : : void
1217 : 0 : vhdi_file_table_free(vhdi_file_table_t *table)
1218 : : {
1219 : : int i;
1220 : :
1221 [ # # ]: 0 : if (table->table) {
1222 [ # # ]: 0 : for (i = 0; i < table->entries; i++)
1223 : 0 : free(table->table[i].path);
1224 : 0 : free(table->table);
1225 : : }
1226 : :
1227 : : memset(table, 0, sizeof(vhdi_file_table_t));
1228 : 0 : }
|