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, &copy);
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, &copy);
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