1 /* Copyright (c) 2008, XenSource Inc.
2 * All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are met:
6 * * Redistributions of source code must retain the above copyright
7 * notice, this list of conditions and the following disclaimer.
8 * * Redistributions in binary form must reproduce the above copyright
9 * notice, this list of conditions and the following disclaimer in the
10 * documentation and/or other materials provided with the distribution.
11 * * Neither the name of XenSource Inc. nor the names of its contributors
12 * may be used to endorse or promote products derived from this software
13 * without specific prior written permission.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
16 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
17 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
18 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
19 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
20 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
22 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
23 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
24 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27 #include <stdio.h>
28 #include <errno.h>
29 #include <fcntl.h>
30 #include <stdlib.h>
31 #include <unistd.h>
32 #include <string.h>
33
34 #include "atomicio.h"
35 #include "libvhd-journal.h"
36
37 #define VHD_JOURNAL_ENTRY_TYPE_FOOTER_P 1
38 #define VHD_JOURNAL_ENTRY_TYPE_FOOTER_C 2
39 #define VHD_JOURNAL_ENTRY_TYPE_HEADER 3
40 #define VHD_JOURNAL_ENTRY_TYPE_LOCATOR 4
41 #define VHD_JOURNAL_ENTRY_TYPE_BAT 5
42 #define VHD_JOURNAL_ENTRY_TYPE_BATMAP_H 6
43 #define VHD_JOURNAL_ENTRY_TYPE_BATMAP_M 7
44 #define VHD_JOURNAL_ENTRY_TYPE_DATA 8
45
46 typedef struct vhd_journal_entry {
47 uint64_t cookie;
48 uint32_t type;
49 uint32_t size;
50 uint64_t offset;
51 uint32_t checksum;
52 } vhd_journal_entry_t;
53
54 static inline int
vhd_journal_seek(vhd_journal_t * j,off_t offset,int whence)55 vhd_journal_seek(vhd_journal_t *j, off_t offset, int whence)
56 {
57 off_t off;
58
59 off = lseek(j->jfd, offset, whence);
60 if (off == (off_t)-1)
61 return -errno;
62
63 return 0;
64 }
65
66 static inline off_t
vhd_journal_position(vhd_journal_t * j)67 vhd_journal_position(vhd_journal_t *j)
68 {
69 return lseek(j->jfd, 0, SEEK_CUR);
70 }
71
72 static inline int
vhd_journal_read(vhd_journal_t * j,void * buf,size_t size)73 vhd_journal_read(vhd_journal_t *j, void *buf, size_t size)
74 {
75 ssize_t ret;
76
77 errno = 0;
78
79 ret = atomicio(read, j->jfd, buf, size);
80 if (ret != size)
81 return (errno ? -errno : -EIO);
82
83 return 0;
84 }
85
86 static inline int
vhd_journal_write(vhd_journal_t * j,void * buf,size_t size)87 vhd_journal_write(vhd_journal_t *j, void *buf, size_t size)
88 {
89 ssize_t ret;
90
91 errno = 0;
92
93 ret = atomicio(vwrite, j->jfd, buf, size);
94 if (ret != size)
95 return (errno ? -errno : -EIO);
96
97 return 0;
98 }
99
100 static inline int
vhd_journal_truncate(vhd_journal_t * j,off_t length)101 vhd_journal_truncate(vhd_journal_t *j, off_t length)
102 {
103 int err;
104
105 err = ftruncate(j->jfd, length);
106 if (err == -1)
107 return -errno;
108
109 return 0;
110 }
111
112 static inline int
vhd_journal_sync(vhd_journal_t * j)113 vhd_journal_sync(vhd_journal_t *j)
114 {
115 int err;
116
117 err = fdatasync(j->jfd);
118 if (err)
119 return -errno;
120
121 return 0;
122 }
123
124 static inline void
vhd_journal_header_in(vhd_journal_header_t * header)125 vhd_journal_header_in(vhd_journal_header_t *header)
126 {
127 BE64_IN(&header->vhd_footer_offset);
128 BE32_IN(&header->journal_data_entries);
129 BE32_IN(&header->journal_metadata_entries);
130 BE64_IN(&header->journal_data_offset);
131 BE64_IN(&header->journal_metadata_offset);
132 }
133
134 static inline void
vhd_journal_header_out(vhd_journal_header_t * header)135 vhd_journal_header_out(vhd_journal_header_t *header)
136 {
137 BE64_OUT(&header->vhd_footer_offset);
138 BE32_OUT(&header->journal_data_entries);
139 BE32_OUT(&header->journal_metadata_entries);
140 BE64_OUT(&header->journal_data_offset);
141 BE64_OUT(&header->journal_metadata_offset);
142 }
143
144 static int
vhd_journal_validate_header(vhd_journal_t * j,vhd_journal_header_t * header)145 vhd_journal_validate_header(vhd_journal_t *j, vhd_journal_header_t *header)
146 {
147 int err;
148 off_t eof;
149
150 if (memcmp(header->cookie,
151 VHD_JOURNAL_HEADER_COOKIE, sizeof(header->cookie)))
152 return -EINVAL;
153
154 err = vhd_journal_seek(j, j->header.journal_eof, SEEK_SET);
155 if (err)
156 return err;
157
158 eof = vhd_journal_position(j);
159 if (eof == (off_t)-1)
160 return -errno;
161
162 if (j->header.journal_data_offset > j->header.journal_eof)
163 return -EINVAL;
164
165 if (j->header.journal_metadata_offset > j->header.journal_eof)
166 return -EINVAL;
167
168 return 0;
169 }
170
171 static int
vhd_journal_read_journal_header(vhd_journal_t * j,vhd_journal_header_t * header)172 vhd_journal_read_journal_header(vhd_journal_t *j, vhd_journal_header_t *header)
173 {
174 int err;
175 size_t size;
176
177 size = sizeof(vhd_journal_header_t);
178 err = vhd_journal_seek(j, 0, SEEK_SET);
179 if (err)
180 return err;
181
182 err = vhd_journal_read(j, header, size);
183 if (err)
184 return err;
185
186 vhd_journal_header_in(header);
187
188 return vhd_journal_validate_header(j, header);
189 }
190
191 static int
vhd_journal_write_header(vhd_journal_t * j,vhd_journal_header_t * header)192 vhd_journal_write_header(vhd_journal_t *j, vhd_journal_header_t *header)
193 {
194 int err;
195 size_t size;
196 vhd_journal_header_t h;
197
198 memcpy(&h, header, sizeof(vhd_journal_header_t));
199
200 err = vhd_journal_validate_header(j, &h);
201 if (err)
202 return err;
203
204 vhd_journal_header_out(&h);
205 size = sizeof(vhd_journal_header_t);
206
207 err = vhd_journal_seek(j, 0, SEEK_SET);
208 if (err)
209 return err;
210
211 err = vhd_journal_write(j, &h, size);
212 if (err)
213 return err;
214
215 return 0;
216 }
217
218 static int
vhd_journal_add_journal_header(vhd_journal_t * j)219 vhd_journal_add_journal_header(vhd_journal_t *j)
220 {
221 int err;
222 off_t off;
223 vhd_context_t *vhd;
224
225 vhd = &j->vhd;
226 memset(&j->header, 0, sizeof(vhd_journal_header_t));
227
228 err = vhd_seek(vhd, 0, SEEK_END);
229 if (err)
230 return err;
231
232 off = vhd_position(vhd);
233 if (off == (off_t)-1)
234 return -errno;
235
236 err = vhd_get_footer(vhd);
237 if (err)
238 return err;
239
240 vhd_uuid_copy(&j->header.uuid, &vhd->footer.uuid);
241 memcpy(j->header.cookie,
242 VHD_JOURNAL_HEADER_COOKIE, sizeof(j->header.cookie));
243 j->header.vhd_footer_offset = off - sizeof(vhd_footer_t);
244 j->header.journal_eof = sizeof(vhd_journal_header_t);
245
246 return vhd_journal_write_header(j, &j->header);
247 }
248
249 static void
vhd_journal_entry_in(vhd_journal_entry_t * entry)250 vhd_journal_entry_in(vhd_journal_entry_t *entry)
251 {
252 BE32_IN(&entry->type);
253 BE32_IN(&entry->size);
254 BE64_IN(&entry->offset);
255 BE64_IN(&entry->cookie);
256 BE32_IN(&entry->checksum);
257 }
258
259 static void
vhd_journal_entry_out(vhd_journal_entry_t * entry)260 vhd_journal_entry_out(vhd_journal_entry_t *entry)
261 {
262 BE32_OUT(&entry->type);
263 BE32_OUT(&entry->size);
264 BE64_OUT(&entry->offset);
265 BE64_OUT(&entry->cookie);
266 BE32_OUT(&entry->checksum);
267 }
268
269 static uint32_t
vhd_journal_checksum_entry(vhd_journal_entry_t * entry,char * buf,size_t size)270 vhd_journal_checksum_entry(vhd_journal_entry_t *entry, char *buf, size_t size)
271 {
272 int i;
273 unsigned char *blob;
274 uint32_t checksum, tmp;
275
276 checksum = 0;
277 tmp = entry->checksum;
278 entry->checksum = 0;
279
280 blob = (unsigned char *)entry;
281 for (i = 0; i < sizeof(vhd_journal_entry_t); i++)
282 checksum += blob[i];
283
284 blob = (unsigned char *)buf;
285 for (i = 0; i < size; i++)
286 checksum += blob[i];
287
288 entry->checksum = tmp;
289 return ~checksum;
290 }
291
292 static int
vhd_journal_validate_entry(vhd_journal_entry_t * entry)293 vhd_journal_validate_entry(vhd_journal_entry_t *entry)
294 {
295 if (entry->size == 0)
296 return -EINVAL;
297
298 if (entry->size & (VHD_SECTOR_SIZE - 1))
299 return -EINVAL;
300
301 if (entry->cookie != VHD_JOURNAL_ENTRY_COOKIE)
302 return -EINVAL;
303
304 return 0;
305 }
306
307 static int
vhd_journal_read_entry(vhd_journal_t * j,vhd_journal_entry_t * entry)308 vhd_journal_read_entry(vhd_journal_t *j, vhd_journal_entry_t *entry)
309 {
310 int err;
311
312 err = vhd_journal_read(j, entry, sizeof(vhd_journal_entry_t));
313 if (err)
314 return err;
315
316 vhd_journal_entry_in(entry);
317 return vhd_journal_validate_entry(entry);
318 }
319
320 static int
vhd_journal_write_entry(vhd_journal_t * j,vhd_journal_entry_t * entry)321 vhd_journal_write_entry(vhd_journal_t *j, vhd_journal_entry_t *entry)
322 {
323 int err;
324 vhd_journal_entry_t e;
325
326 err = vhd_journal_validate_entry(entry);
327 if (err)
328 return err;
329
330 memcpy(&e, entry, sizeof(vhd_journal_entry_t));
331 vhd_journal_entry_out(&e);
332
333 err = vhd_journal_write(j, &e, sizeof(vhd_journal_entry_t));
334 if (err)
335 return err;
336
337 return 0;
338 }
339
340 static int
vhd_journal_validate_entry_data(vhd_journal_entry_t * entry,char * buf)341 vhd_journal_validate_entry_data(vhd_journal_entry_t *entry, char *buf)
342 {
343 int err;
344 uint32_t checksum;
345
346 err = 0;
347 checksum = vhd_journal_checksum_entry(entry, buf, entry->size);
348
349 if (checksum != entry->checksum)
350 return -EINVAL;
351
352 return err;
353 }
354
355 static int
vhd_journal_update(vhd_journal_t * j,off_t offset,char * buf,size_t size,uint32_t type)356 vhd_journal_update(vhd_journal_t *j, off_t offset,
357 char *buf, size_t size, uint32_t type)
358 {
359 int err;
360 off_t eof;
361 uint64_t *off, off_bak;
362 uint32_t *entries;
363 vhd_journal_entry_t entry;
364
365 entry.type = type;
366 entry.size = size;
367 entry.offset = offset;
368 entry.cookie = VHD_JOURNAL_ENTRY_COOKIE;
369 entry.checksum = vhd_journal_checksum_entry(&entry, buf, size);
370
371 err = vhd_journal_seek(j, j->header.journal_eof, SEEK_SET);
372 if (err)
373 return err;
374
375 err = vhd_journal_write_entry(j, &entry);
376 if (err)
377 goto fail;
378
379 err = vhd_journal_write(j, buf, size);
380 if (err)
381 goto fail;
382
383 if (type == VHD_JOURNAL_ENTRY_TYPE_DATA) {
384 off = &j->header.journal_data_offset;
385 entries = &j->header.journal_data_entries;
386 } else {
387 off = &j->header.journal_metadata_offset;
388 entries = &j->header.journal_metadata_entries;
389 }
390
391 off_bak = *off;
392 if (!(*entries)++)
393 *off = j->header.journal_eof;
394 j->header.journal_eof += (size + sizeof(vhd_journal_entry_t));
395
396 err = vhd_journal_write_header(j, &j->header);
397 if (err) {
398 if (!--(*entries))
399 *off = off_bak;
400 j->header.journal_eof -= (size + sizeof(vhd_journal_entry_t));
401 goto fail;
402 }
403
404 return 0;
405
406 fail:
407 if (!j->is_block)
408 vhd_journal_truncate(j, j->header.journal_eof);
409 return err;
410 }
411
412 static int
vhd_journal_add_footer(vhd_journal_t * j)413 vhd_journal_add_footer(vhd_journal_t *j)
414 {
415 int err;
416 off_t off;
417 vhd_context_t *vhd;
418 vhd_footer_t footer;
419
420 vhd = &j->vhd;
421
422 err = vhd_seek(vhd, 0, SEEK_END);
423 if (err)
424 return err;
425
426 off = vhd_position(vhd);
427 if (off == (off_t)-1)
428 return -errno;
429
430 err = vhd_read_footer_at(vhd, &footer, off - sizeof(vhd_footer_t));
431 if (err)
432 return err;
433
434 vhd_footer_out(&footer);
435 err = vhd_journal_update(j, off - sizeof(vhd_footer_t),
436 (char *)&footer,
437 sizeof(vhd_footer_t),
438 VHD_JOURNAL_ENTRY_TYPE_FOOTER_P);
439 if (err)
440 return err;
441
442 if (!vhd_type_dynamic(vhd))
443 return 0;
444
445 err = vhd_read_footer_at(vhd, &footer, 0);
446 if (err)
447 return err;
448
449 vhd_footer_out(&footer);
450 err = vhd_journal_update(j, 0,
451 (char *)&footer,
452 sizeof(vhd_footer_t),
453 VHD_JOURNAL_ENTRY_TYPE_FOOTER_C);
454
455 return err;
456 }
457
458 static int
vhd_journal_add_header(vhd_journal_t * j)459 vhd_journal_add_header(vhd_journal_t *j)
460 {
461 int err;
462 off_t off;
463 vhd_context_t *vhd;
464 vhd_header_t header;
465
466 vhd = &j->vhd;
467
468 err = vhd_read_header(vhd, &header);
469 if (err)
470 return err;
471
472 off = vhd->footer.data_offset;
473
474 vhd_header_out(&header);
475 err = vhd_journal_update(j, off,
476 (char *)&header,
477 sizeof(vhd_header_t),
478 VHD_JOURNAL_ENTRY_TYPE_HEADER);
479
480 return err;
481 }
482
483 static int
vhd_journal_add_locators(vhd_journal_t * j)484 vhd_journal_add_locators(vhd_journal_t *j)
485 {
486 int i, n, err;
487 vhd_context_t *vhd;
488
489 vhd = &j->vhd;
490
491 err = vhd_get_header(vhd);
492 if (err)
493 return err;
494
495 n = sizeof(vhd->header.loc) / sizeof(vhd_parent_locator_t);
496 for (i = 0; i < n; i++) {
497 char *buf;
498 off_t off;
499 size_t size;
500 vhd_parent_locator_t *loc;
501
502 loc = vhd->header.loc + i;
503 err = vhd_validate_platform_code(loc->code);
504 if (err)
505 return err;
506
507 if (loc->code == PLAT_CODE_NONE)
508 continue;
509
510 off = loc->data_offset;
511 size = vhd_parent_locator_size(loc);
512
513 err = posix_memalign((void **)&buf, VHD_SECTOR_SIZE, size);
514 if (err)
515 return -err;
516
517 err = vhd_seek(vhd, off, SEEK_SET);
518 if (err)
519 goto end;
520
521 err = vhd_read(vhd, buf, size);
522 if (err)
523 goto end;
524
525 err = vhd_journal_update(j, off, buf, size,
526 VHD_JOURNAL_ENTRY_TYPE_LOCATOR);
527 if (err)
528 goto end;
529
530 err = 0;
531
532 end:
533 free(buf);
534 if (err)
535 break;
536 }
537
538 return err;
539 }
540
541 static int
vhd_journal_add_bat(vhd_journal_t * j)542 vhd_journal_add_bat(vhd_journal_t *j)
543 {
544 int err;
545 off_t off;
546 size_t size;
547 vhd_bat_t bat;
548 vhd_context_t *vhd;
549
550 vhd = &j->vhd;
551
552 err = vhd_get_header(vhd);
553 if (err)
554 return err;
555
556 err = vhd_read_bat(vhd, &bat);
557 if (err)
558 return err;
559
560 off = vhd->header.table_offset;
561 size = vhd_bytes_padded(bat.entries * sizeof(uint32_t));
562
563 vhd_bat_out(&bat);
564 err = vhd_journal_update(j, off, (char *)bat.bat, size,
565 VHD_JOURNAL_ENTRY_TYPE_BAT);
566
567 free(bat.bat);
568 return err;
569 }
570
571 static int
vhd_journal_add_batmap(vhd_journal_t * j)572 vhd_journal_add_batmap(vhd_journal_t *j)
573 {
574 int err;
575 off_t off;
576 size_t size;
577 vhd_context_t *vhd;
578 vhd_batmap_t batmap;
579
580 vhd = &j->vhd;
581
582 err = vhd_batmap_header_offset(vhd, &off);
583 if (err)
584 return err;
585
586 err = vhd_read_batmap(vhd, &batmap);
587 if (err)
588 return err;
589
590 size = vhd_bytes_padded(sizeof(struct dd_batmap_hdr));
591
592 vhd_batmap_header_out(&batmap);
593 err = vhd_journal_update(j, off, (char *)&batmap.header, size,
594 VHD_JOURNAL_ENTRY_TYPE_BATMAP_H);
595 if (err)
596 goto out;
597
598 vhd_batmap_header_in(&batmap);
599 off = batmap.header.batmap_offset;
600 size = vhd_sectors_to_bytes(batmap.header.batmap_size);
601
602 err = vhd_journal_update(j, off, batmap.map, size,
603 VHD_JOURNAL_ENTRY_TYPE_BATMAP_M);
604
605 out:
606 free(batmap.map);
607 return err;
608 }
609
610 static int
vhd_journal_add_metadata(vhd_journal_t * j)611 vhd_journal_add_metadata(vhd_journal_t *j)
612 {
613 int err;
614 off_t eof;
615 vhd_context_t *vhd;
616
617 vhd = &j->vhd;
618
619 err = vhd_journal_add_footer(j);
620 if (err)
621 return err;
622
623 if (!vhd_type_dynamic(vhd))
624 return 0;
625
626 err = vhd_journal_add_header(j);
627 if (err)
628 return err;
629
630 err = vhd_journal_add_locators(j);
631 if (err)
632 return err;
633
634 err = vhd_journal_add_bat(j);
635 if (err)
636 return err;
637
638 if (vhd_has_batmap(vhd)) {
639 err = vhd_journal_add_batmap(j);
640 if (err)
641 return err;
642 }
643
644 j->header.journal_data_offset = j->header.journal_eof;
645 return vhd_journal_write_header(j, &j->header);
646 }
647
648 static int
__vhd_journal_read_footer(vhd_journal_t * j,vhd_footer_t * footer,uint32_t type)649 __vhd_journal_read_footer(vhd_journal_t *j,
650 vhd_footer_t *footer, uint32_t type)
651 {
652 int err;
653 vhd_journal_entry_t entry;
654
655 err = vhd_journal_read_entry(j, &entry);
656 if (err)
657 return err;
658
659 if (entry.type != type)
660 return -EINVAL;
661
662 if (entry.size != sizeof(vhd_footer_t))
663 return -EINVAL;
664
665 err = vhd_journal_read(j, footer, entry.size);
666 if (err)
667 return err;
668
669 vhd_footer_in(footer);
670 return vhd_validate_footer(footer);
671 }
672
673 static int
vhd_journal_read_footer(vhd_journal_t * j,vhd_footer_t * footer)674 vhd_journal_read_footer(vhd_journal_t *j, vhd_footer_t *footer)
675 {
676 return __vhd_journal_read_footer(j, footer,
677 VHD_JOURNAL_ENTRY_TYPE_FOOTER_P);
678 }
679
680 static int
vhd_journal_read_footer_copy(vhd_journal_t * j,vhd_footer_t * footer)681 vhd_journal_read_footer_copy(vhd_journal_t *j, vhd_footer_t *footer)
682 {
683 return __vhd_journal_read_footer(j, footer,
684 VHD_JOURNAL_ENTRY_TYPE_FOOTER_C);
685 }
686
687 static int
vhd_journal_read_header(vhd_journal_t * j,vhd_header_t * header)688 vhd_journal_read_header(vhd_journal_t *j, vhd_header_t *header)
689 {
690 int err;
691 vhd_journal_entry_t entry;
692
693 err = vhd_journal_read_entry(j, &entry);
694 if (err)
695 return err;
696
697 if (entry.type != VHD_JOURNAL_ENTRY_TYPE_HEADER)
698 return -EINVAL;
699
700 if (entry.size != sizeof(vhd_header_t))
701 return -EINVAL;
702
703 err = vhd_journal_read(j, header, entry.size);
704 if (err)
705 return err;
706
707 vhd_header_in(header);
708 return vhd_validate_header(header);
709 }
710
711 static int
vhd_journal_read_locators(vhd_journal_t * j,char *** locators,int * locs)712 vhd_journal_read_locators(vhd_journal_t *j, char ***locators, int *locs)
713 {
714 int err, n, _locs;
715 char **_locators, *buf;
716 off_t pos;
717 vhd_journal_entry_t entry;
718
719 _locs = 0;
720 *locs = 0;
721 *locators = NULL;
722
723 n = sizeof(j->vhd.header.loc) / sizeof(vhd_parent_locator_t);
724 _locators = calloc(n, sizeof(char *));
725 if (!_locators)
726 return -ENOMEM;
727
728 for (;;) {
729 buf = NULL;
730
731 pos = vhd_journal_position(j);
732 err = vhd_journal_read_entry(j, &entry);
733 if (err)
734 goto fail;
735
736 if (entry.type != VHD_JOURNAL_ENTRY_TYPE_LOCATOR) {
737 err = vhd_journal_seek(j, pos, SEEK_SET);
738 if (err)
739 goto fail;
740 break;
741 }
742
743 if (_locs >= n) {
744 err = -EINVAL;
745 goto fail;
746 }
747
748 err = posix_memalign((void **)&buf,
749 VHD_SECTOR_SIZE, entry.size);
750 if (err) {
751 err = -err;
752 buf = NULL;
753 goto fail;
754 }
755
756 err = vhd_journal_read(j, buf, entry.size);
757 if (err)
758 goto fail;
759
760 _locators[_locs++] = buf;
761 err = 0;
762 }
763
764
765 *locs = _locs;
766 *locators = _locators;
767
768 return 0;
769
770 fail:
771 if (_locators) {
772 for (n = 0; n < _locs; n++)
773 free(_locators[n]);
774 free(_locators);
775 }
776 return err;
777 }
778
779 static int
vhd_journal_read_bat(vhd_journal_t * j,vhd_bat_t * bat)780 vhd_journal_read_bat(vhd_journal_t *j, vhd_bat_t *bat)
781 {
782 int err;
783 size_t size;
784 vhd_context_t *vhd;
785 vhd_journal_entry_t entry;
786
787 vhd = &j->vhd;
788
789 size = vhd_bytes_padded(vhd->header.max_bat_size * sizeof(uint32_t));
790
791 err = vhd_journal_read_entry(j, &entry);
792 if (err)
793 return err;
794
795 if (entry.type != VHD_JOURNAL_ENTRY_TYPE_BAT)
796 return -EINVAL;
797
798 if (entry.size != size)
799 return -EINVAL;
800
801 if (entry.offset != vhd->header.table_offset)
802 return -EINVAL;
803
804 err = posix_memalign((void **)&bat->bat, VHD_SECTOR_SIZE, size);
805 if (err)
806 return -err;
807
808 err = vhd_journal_read(j, bat->bat, entry.size);
809 if (err)
810 goto fail;
811
812 bat->spb = vhd->header.block_size >> VHD_SECTOR_SHIFT;
813 bat->entries = vhd->header.max_bat_size;
814 vhd_bat_in(bat);
815
816 return 0;
817
818 fail:
819 free(bat->bat);
820 bat->bat = NULL;
821 return err;
822 }
823
824 static int
vhd_journal_read_batmap_header(vhd_journal_t * j,vhd_batmap_t * batmap)825 vhd_journal_read_batmap_header(vhd_journal_t *j, vhd_batmap_t *batmap)
826 {
827 int err;
828 char *buf;
829 size_t size;
830 vhd_journal_entry_t entry;
831
832 size = vhd_bytes_padded(sizeof(struct dd_batmap_hdr));
833
834 err = vhd_journal_read_entry(j, &entry);
835 if (err)
836 return err;
837
838 if (entry.type != VHD_JOURNAL_ENTRY_TYPE_BATMAP_H)
839 return -EINVAL;
840
841 if (entry.size != size)
842 return -EINVAL;
843
844 err = posix_memalign((void **)&buf, VHD_SECTOR_SIZE, size);
845 if (err)
846 return err;
847
848 err = vhd_journal_read(j, buf, entry.size);
849 if (err) {
850 free(buf);
851 return err;
852 }
853
854 memcpy(&batmap->header, buf, sizeof(batmap->header));
855
856 vhd_batmap_header_in(batmap);
857 return vhd_validate_batmap_header(batmap);
858 }
859
860 static int
vhd_journal_read_batmap_map(vhd_journal_t * j,vhd_batmap_t * batmap)861 vhd_journal_read_batmap_map(vhd_journal_t *j, vhd_batmap_t *batmap)
862 {
863 int err;
864 vhd_journal_entry_t entry;
865
866 err = vhd_journal_read_entry(j, &entry);
867 if (err)
868 return err;
869
870 if (entry.type != VHD_JOURNAL_ENTRY_TYPE_BATMAP_M)
871 return -EINVAL;
872
873 if (entry.size != vhd_sectors_to_bytes(batmap->header.batmap_size))
874 return -EINVAL;
875
876 if (entry.offset != batmap->header.batmap_offset)
877 return -EINVAL;
878
879 err = posix_memalign((void **)&batmap->map,
880 VHD_SECTOR_SIZE, entry.size);
881 if (err)
882 return -err;
883
884 err = vhd_journal_read(j, batmap->map, entry.size);
885 if (err) {
886 free(batmap->map);
887 batmap->map = NULL;
888 return err;
889 }
890
891 return 0;
892 }
893
894 static int
vhd_journal_read_batmap(vhd_journal_t * j,vhd_batmap_t * batmap)895 vhd_journal_read_batmap(vhd_journal_t *j, vhd_batmap_t *batmap)
896 {
897 int err;
898
899 err = vhd_journal_read_batmap_header(j, batmap);
900 if (err)
901 return err;
902
903 err = vhd_journal_read_batmap_map(j, batmap);
904 if (err)
905 return err;
906
907 err = vhd_validate_batmap(batmap);
908 if (err) {
909 free(batmap->map);
910 batmap->map = NULL;
911 return err;
912 }
913
914 return 0;
915 }
916
917 static int
vhd_journal_restore_footer(vhd_journal_t * j,vhd_footer_t * footer)918 vhd_journal_restore_footer(vhd_journal_t *j, vhd_footer_t *footer)
919 {
920 return vhd_write_footer_at(&j->vhd, footer,
921 j->header.vhd_footer_offset);
922 }
923
924 static int
vhd_journal_restore_footer_copy(vhd_journal_t * j,vhd_footer_t * footer)925 vhd_journal_restore_footer_copy(vhd_journal_t *j, vhd_footer_t *footer)
926 {
927 return vhd_write_footer_at(&j->vhd, footer, 0);
928 }
929
930 static int
vhd_journal_restore_header(vhd_journal_t * j,vhd_header_t * header)931 vhd_journal_restore_header(vhd_journal_t *j, vhd_header_t *header)
932 {
933 off_t off;
934 vhd_context_t *vhd;
935
936 vhd = &j->vhd;
937 off = vhd->footer.data_offset;
938
939 return vhd_write_header_at(&j->vhd, header, off);
940 }
941
942 static int
vhd_journal_restore_locators(vhd_journal_t * j,char ** locators,int locs)943 vhd_journal_restore_locators(vhd_journal_t *j, char **locators, int locs)
944 {
945 size_t size;
946 vhd_context_t *vhd;
947 int i, n, lidx, err;
948 vhd_parent_locator_t *loc;
949
950 lidx = 0;
951 vhd = &j->vhd;
952
953 n = sizeof(vhd->header.loc) / sizeof(vhd_parent_locator_t);
954
955 for (i = 0; i < n && lidx < locs; i++) {
956 loc = vhd->header.loc + i;
957 if (loc->code == PLAT_CODE_NONE)
958 continue;
959
960 err = vhd_seek(vhd, loc->data_offset, SEEK_SET);
961 if (err)
962 return err;
963
964 size = vhd_parent_locator_size(loc);
965 err = vhd_write(vhd, locators[lidx++], size);
966 if (err)
967 return err;
968 }
969
970 return 0;
971 }
972
973 static int
vhd_journal_restore_bat(vhd_journal_t * j,vhd_bat_t * bat)974 vhd_journal_restore_bat(vhd_journal_t *j, vhd_bat_t *bat)
975 {
976 return vhd_write_bat(&j->vhd, bat);
977 }
978
979 static int
vhd_journal_restore_batmap(vhd_journal_t * j,vhd_batmap_t * batmap)980 vhd_journal_restore_batmap(vhd_journal_t *j, vhd_batmap_t *batmap)
981 {
982 return vhd_write_batmap(&j->vhd, batmap);
983 }
984
985 static int
vhd_journal_restore_metadata(vhd_journal_t * j)986 vhd_journal_restore_metadata(vhd_journal_t *j)
987 {
988 off_t off;
989 char **locators;
990 vhd_footer_t copy;
991 vhd_context_t *vhd;
992 int i, locs, hlocs, err;
993
994 vhd = &j->vhd;
995 locs = 0;
996 hlocs = 0;
997 locators = NULL;
998
999 err = vhd_journal_seek(j, sizeof(vhd_journal_header_t), SEEK_SET);
1000 if (err)
1001 return err;
1002
1003 err = vhd_journal_read_footer(j, &vhd->footer);
1004 if (err)
1005 return err;
1006
1007 if (!vhd_type_dynamic(vhd))
1008 goto restore;
1009
1010 err = vhd_journal_read_footer_copy(j, ©);
1011 if (err)
1012 return err;
1013
1014 err = vhd_journal_read_header(j, &vhd->header);
1015 if (err)
1016 return err;
1017
1018 for (hlocs = 0, i = 0; i < vhd_parent_locator_count(vhd); i++) {
1019 if (vhd_validate_platform_code(vhd->header.loc[i].code))
1020 return err;
1021
1022 if (vhd->header.loc[i].code != PLAT_CODE_NONE)
1023 hlocs++;
1024 }
1025
1026 if (hlocs) {
1027 err = vhd_journal_read_locators(j, &locators, &locs);
1028 if (err)
1029 return err;
1030
1031 if (hlocs != locs) {
1032 err = -EINVAL;
1033 goto out;
1034 }
1035 }
1036
1037 err = vhd_journal_read_bat(j, &vhd->bat);
1038 if (err)
1039 goto out;
1040
1041 if (vhd_has_batmap(vhd)) {
1042 err = vhd_journal_read_batmap(j, &vhd->batmap);
1043 if (err)
1044 goto out;
1045 }
1046
1047 restore:
1048 off = vhd_journal_position(j);
1049 if (off == (off_t)-1)
1050 return -errno;
1051
1052 if (j->header.journal_data_offset != off)
1053 return -EINVAL;
1054
1055 err = vhd_journal_restore_footer(j, &vhd->footer);
1056 if (err)
1057 goto out;
1058
1059 if (!vhd_type_dynamic(vhd))
1060 goto out;
1061
1062 err = vhd_journal_restore_footer_copy(j, ©);
1063 if (err)
1064 goto out;
1065
1066 err = vhd_journal_restore_header(j, &vhd->header);
1067 if (err)
1068 goto out;
1069
1070 if (locs) {
1071 err = vhd_journal_restore_locators(j, locators, locs);
1072 if (err)
1073 goto out;
1074 }
1075
1076 err = vhd_journal_restore_bat(j, &vhd->bat);
1077 if (err)
1078 goto out;
1079
1080 if (vhd_has_batmap(vhd)) {
1081 err = vhd_journal_restore_batmap(j, &vhd->batmap);
1082 if (err)
1083 goto out;
1084 }
1085
1086 err = 0;
1087
1088 out:
1089 if (locators) {
1090 for (i = 0; i < locs; i++)
1091 free(locators[i]);
1092 free(locators);
1093 }
1094
1095 if (!err && !vhd->is_block)
1096 err = ftruncate(vhd->fd,
1097 j->header.vhd_footer_offset +
1098 sizeof(vhd_footer_t));
1099
1100 return err;
1101 }
1102
1103 static int
vhd_journal_disable_vhd(vhd_journal_t * j)1104 vhd_journal_disable_vhd(vhd_journal_t *j)
1105 {
1106 int err;
1107 vhd_context_t *vhd;
1108
1109 vhd = &j->vhd;
1110
1111 err = vhd_get_footer(vhd);
1112 if (err)
1113 return err;
1114
1115 memcpy(&vhd->footer.cookie,
1116 VHD_POISON_COOKIE, sizeof(vhd->footer.cookie));
1117 vhd->footer.checksum = vhd_checksum_footer(&vhd->footer);
1118
1119 err = vhd_write_footer(vhd, &vhd->footer);
1120 if (err)
1121 return err;
1122
1123 return 0;
1124 }
1125
1126 static int
vhd_journal_enable_vhd(vhd_journal_t * j)1127 vhd_journal_enable_vhd(vhd_journal_t *j)
1128 {
1129 int err;
1130 vhd_context_t *vhd;
1131
1132 vhd = &j->vhd;
1133
1134 err = vhd_get_footer(vhd);
1135 if (err)
1136 return err;
1137
1138 if (!vhd_disabled(vhd))
1139 return 0;
1140
1141 memcpy(&vhd->footer.cookie, HD_COOKIE, sizeof(vhd->footer.cookie));
1142 vhd->footer.checksum = vhd_checksum_footer(&vhd->footer);
1143
1144 err = vhd_write_footer(vhd, &vhd->footer);
1145 if (err)
1146 return err;
1147
1148 return 0;
1149 }
1150
1151 int
vhd_journal_close(vhd_journal_t * j)1152 vhd_journal_close(vhd_journal_t *j)
1153 {
1154 if (j->jfd)
1155 close(j->jfd);
1156
1157 vhd_close(&j->vhd);
1158 free(j->jname);
1159
1160 return 0;
1161 }
1162
1163 int
vhd_journal_remove(vhd_journal_t * j)1164 vhd_journal_remove(vhd_journal_t *j)
1165 {
1166 int err;
1167
1168 err = vhd_journal_enable_vhd(j);
1169 if (err)
1170 return err;
1171
1172 if (j->jfd) {
1173 close(j->jfd);
1174 if (!j->is_block)
1175 unlink(j->jname);
1176 }
1177
1178 vhd_close(&j->vhd);
1179 free(j->jname);
1180
1181 return 0;
1182 }
1183
1184 int
vhd_journal_open(vhd_journal_t * j,const char * file,const char * jfile)1185 vhd_journal_open(vhd_journal_t *j, const char *file, const char *jfile)
1186 {
1187 int err;
1188 vhd_context_t *vhd;
1189
1190 memset(j, 0, sizeof(vhd_journal_t));
1191
1192 j->jfd = -1;
1193 vhd = &j->vhd;
1194
1195 j->jname = strdup(jfile);
1196 if (j->jname == NULL)
1197 return -ENOMEM;
1198
1199 j->jfd = open(j->jname, O_LARGEFILE | O_RDWR);
1200 if (j->jfd == -1) {
1201 err = -errno;
1202 goto fail;
1203 }
1204
1205 err = vhd_test_file_fixed(j->jname, &j->is_block);
1206 if (err)
1207 goto fail;
1208
1209 vhd->fd = open(file, O_LARGEFILE | O_RDWR | O_DIRECT);
1210 if (vhd->fd == -1) {
1211 err = -errno;
1212 goto fail;
1213 }
1214
1215 err = vhd_test_file_fixed(file, &vhd->is_block);
1216 if (err)
1217 goto fail;
1218
1219 err = vhd_journal_read_journal_header(j, &j->header);
1220 if (err)
1221 goto fail;
1222
1223 err = vhd_journal_restore_metadata(j);
1224 if (err)
1225 goto fail;
1226
1227 close(vhd->fd);
1228 free(vhd->bat.bat);
1229 free(vhd->batmap.map);
1230
1231 err = vhd_open(vhd, file, VHD_OPEN_RDWR);
1232 if (err)
1233 goto fail;
1234
1235 err = vhd_get_bat(vhd);
1236 if (err)
1237 goto fail;
1238
1239 if (vhd_has_batmap(vhd)) {
1240 err = vhd_get_batmap(vhd);
1241 if (err)
1242 goto fail;
1243 }
1244
1245 err = vhd_journal_disable_vhd(j);
1246 if (err)
1247 goto fail;
1248
1249 return 0;
1250
1251 fail:
1252 vhd_journal_close(j);
1253 return err;
1254 }
1255
1256 int
vhd_journal_create(vhd_journal_t * j,const char * file,const char * jfile)1257 vhd_journal_create(vhd_journal_t *j, const char *file, const char *jfile)
1258 {
1259 char *buf;
1260 int i, err;
1261 size_t size;
1262 off_t off;
1263
1264 memset(j, 0, sizeof(vhd_journal_t));
1265 j->jfd = -1;
1266
1267 j->jname = strdup(jfile);
1268 if (j->jname == NULL) {
1269 err = -ENOMEM;
1270 goto fail1;
1271 }
1272
1273 if (access(j->jname, F_OK) == 0) {
1274 err = vhd_test_file_fixed(j->jname, &j->is_block);
1275 if (err)
1276 goto fail1;
1277
1278 if (!j->is_block) {
1279 err = -EEXIST;
1280 goto fail1;
1281 }
1282 }
1283
1284 if (j->is_block)
1285 j->jfd = open(j->jname, O_LARGEFILE | O_RDWR, 0644);
1286 else
1287 j->jfd = open(j->jname,
1288 O_CREAT | O_TRUNC | O_LARGEFILE | O_RDWR, 0644);
1289 if (j->jfd == -1) {
1290 err = -errno;
1291 goto fail1;
1292 }
1293
1294 err = vhd_open(&j->vhd, file, VHD_OPEN_RDWR | VHD_OPEN_STRICT);
1295 if (err)
1296 goto fail1;
1297
1298 err = vhd_get_bat(&j->vhd);
1299 if (err)
1300 goto fail2;
1301
1302 if (vhd_has_batmap(&j->vhd)) {
1303 err = vhd_get_batmap(&j->vhd);
1304 if (err)
1305 goto fail2;
1306 }
1307
1308 err = vhd_journal_add_journal_header(j);
1309 if (err)
1310 goto fail2;
1311
1312 err = vhd_journal_add_metadata(j);
1313 if (err)
1314 goto fail2;
1315
1316 err = vhd_journal_disable_vhd(j);
1317 if (err)
1318 goto fail2;
1319
1320 err = vhd_journal_sync(j);
1321 if (err)
1322 goto fail2;
1323
1324 return 0;
1325
1326 fail1:
1327 if (j->jfd != -1) {
1328 close(j->jfd);
1329 if (!j->is_block)
1330 unlink(j->jname);
1331 }
1332 free(j->jname);
1333 memset(j, 0, sizeof(vhd_journal_t));
1334
1335 return err;
1336
1337 fail2:
1338 vhd_journal_remove(j);
1339 return err;
1340 }
1341
1342 int
vhd_journal_add_block(vhd_journal_t * j,uint32_t block,char mode)1343 vhd_journal_add_block(vhd_journal_t *j, uint32_t block, char mode)
1344 {
1345 int err;
1346 char *buf;
1347 off_t off;
1348 size_t size;
1349 uint64_t blk;
1350 vhd_context_t *vhd;
1351
1352 buf = NULL;
1353 vhd = &j->vhd;
1354
1355 if (!vhd_type_dynamic(vhd))
1356 return -EINVAL;
1357
1358 err = vhd_get_bat(vhd);
1359 if (err)
1360 return err;
1361
1362 if (block >= vhd->bat.entries)
1363 return -ERANGE;
1364
1365 blk = vhd->bat.bat[block];
1366 if (blk == DD_BLK_UNUSED)
1367 return 0;
1368
1369 off = vhd_sectors_to_bytes(blk);
1370
1371 if (mode & VHD_JOURNAL_METADATA) {
1372 size = vhd_sectors_to_bytes(vhd->bm_secs);
1373
1374 err = vhd_read_bitmap(vhd, block, &buf);
1375 if (err)
1376 return err;
1377
1378 err = vhd_journal_update(j, off, buf, size,
1379 VHD_JOURNAL_ENTRY_TYPE_DATA);
1380
1381 free(buf);
1382
1383 if (err)
1384 return err;
1385 }
1386
1387 if (mode & VHD_JOURNAL_DATA) {
1388 off += vhd_sectors_to_bytes(vhd->bm_secs);
1389 size = vhd_sectors_to_bytes(vhd->spb);
1390
1391 err = vhd_read_block(vhd, block, &buf);
1392 if (err)
1393 return err;
1394
1395 err = vhd_journal_update(j, off, buf, size,
1396 VHD_JOURNAL_ENTRY_TYPE_DATA);
1397 free(buf);
1398
1399 if (err)
1400 return err;
1401 }
1402
1403 return vhd_journal_sync(j);
1404 }
1405
1406 /*
1407 * commit indicates the transaction completed
1408 * successfully and we can remove the undo log
1409 */
1410 int
vhd_journal_commit(vhd_journal_t * j)1411 vhd_journal_commit(vhd_journal_t *j)
1412 {
1413 int err;
1414
1415 j->header.journal_data_entries = 0;
1416 j->header.journal_metadata_entries = 0;
1417 j->header.journal_data_offset = 0;
1418 j->header.journal_metadata_offset = 0;
1419
1420 err = vhd_journal_write_header(j, &j->header);
1421 if (err)
1422 return err;
1423
1424 if (!j->is_block)
1425 err = vhd_journal_truncate(j, sizeof(vhd_journal_header_t));
1426 if (err)
1427 return -errno;
1428
1429 return 0;
1430 }
1431
1432 /*
1433 * revert indicates the transaction failed
1434 * and we should revert any changes via the undo log
1435 */
1436 int
vhd_journal_revert(vhd_journal_t * j)1437 vhd_journal_revert(vhd_journal_t *j)
1438 {
1439 int i, err;
1440 char *buf, *file;
1441 vhd_context_t *vhd;
1442 vhd_journal_entry_t entry;
1443
1444 err = 0;
1445 vhd = &j->vhd;
1446 buf = NULL;
1447
1448 file = strdup(vhd->file);
1449 if (!file)
1450 return -ENOMEM;
1451
1452 vhd_close(&j->vhd);
1453 j->vhd.fd = open(file, O_RDWR | O_DIRECT | O_LARGEFILE);
1454 if (j->vhd.fd == -1) {
1455 free(file);
1456 return -errno;
1457 }
1458
1459 err = vhd_test_file_fixed(file, &vhd->is_block);
1460 if (err) {
1461 free(file);
1462 return err;
1463 }
1464
1465 err = vhd_journal_restore_metadata(j);
1466 if (err) {
1467 free(file);
1468 return err;
1469 }
1470
1471 close(vhd->fd);
1472 free(vhd->bat.bat);
1473 free(vhd->batmap.map);
1474
1475 err = vhd_open(vhd, file, VHD_OPEN_RDWR);
1476 free(file);
1477 if (err)
1478 return err;
1479
1480 err = vhd_journal_seek(j, j->header.journal_data_offset, SEEK_SET);
1481 if (err)
1482 return err;
1483
1484 for (i = 0; i < j->header.journal_data_entries; i++) {
1485 err = vhd_journal_read_entry(j, &entry);
1486 if (err)
1487 goto end;
1488
1489 err = posix_memalign((void **)&buf,
1490 VHD_SECTOR_SIZE, entry.size);
1491 if (err) {
1492 err = -err;
1493 buf = NULL;
1494 goto end;
1495 }
1496
1497 err = vhd_journal_read(j, buf, entry.size);
1498 if (err)
1499 goto end;
1500
1501 err = vhd_journal_validate_entry_data(&entry, buf);
1502 if (err)
1503 goto end;
1504
1505 err = vhd_seek(vhd, entry.offset, SEEK_SET);
1506 if (err)
1507 goto end;
1508
1509 err = vhd_write(vhd, buf, entry.size);
1510 if (err)
1511 goto end;
1512
1513 err = 0;
1514
1515 end:
1516 free(buf);
1517 buf = NULL;
1518 if (err)
1519 break;
1520 }
1521
1522 if (err)
1523 return err;
1524
1525 if (!vhd->is_block) {
1526 err = ftruncate(vhd->fd, j->header.vhd_footer_offset +
1527 sizeof(vhd_footer_t));
1528 if (err)
1529 return -errno;
1530 }
1531
1532 return vhd_journal_sync(j);
1533 }
1534