aboutsummaryrefslogtreecommitdiffstats
path: root/tools/blktap2/vhd/lib/libvhd-journal.c
diff options
context:
space:
mode:
Diffstat (limited to 'tools/blktap2/vhd/lib/libvhd-journal.c')
-rw-r--r--tools/blktap2/vhd/lib/libvhd-journal.c1534
1 files changed, 1534 insertions, 0 deletions
diff --git a/tools/blktap2/vhd/lib/libvhd-journal.c b/tools/blktap2/vhd/lib/libvhd-journal.c
new file mode 100644
index 0000000000..c52affea1a
--- /dev/null
+++ b/tools/blktap2/vhd/lib/libvhd-journal.c
@@ -0,0 +1,1534 @@
+/* Copyright (c) 2008, XenSource Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of XenSource Inc. nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
+ * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+#include <stdio.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+
+#include "atomicio.h"
+#include "libvhd-journal.h"
+
+#define VHD_JOURNAL_ENTRY_TYPE_FOOTER_P 1
+#define VHD_JOURNAL_ENTRY_TYPE_FOOTER_C 2
+#define VHD_JOURNAL_ENTRY_TYPE_HEADER 3
+#define VHD_JOURNAL_ENTRY_TYPE_LOCATOR 4
+#define VHD_JOURNAL_ENTRY_TYPE_BAT 5
+#define VHD_JOURNAL_ENTRY_TYPE_BATMAP_H 6
+#define VHD_JOURNAL_ENTRY_TYPE_BATMAP_M 7
+#define VHD_JOURNAL_ENTRY_TYPE_DATA 8
+
+typedef struct vhd_journal_entry {
+ uint64_t cookie;
+ uint32_t type;
+ uint32_t size;
+ uint64_t offset;
+ uint32_t checksum;
+} vhd_journal_entry_t;
+
+static inline int
+vhd_journal_seek(vhd_journal_t *j, off64_t offset, int whence)
+{
+ off64_t off;
+
+ off = lseek64(j->jfd, offset, whence);
+ if (off == (off64_t)-1)
+ return -errno;
+
+ return 0;
+}
+
+static inline off64_t
+vhd_journal_position(vhd_journal_t *j)
+{
+ return lseek64(j->jfd, 0, SEEK_CUR);
+}
+
+static inline int
+vhd_journal_read(vhd_journal_t *j, void *buf, size_t size)
+{
+ ssize_t ret;
+
+ errno = 0;
+
+ ret = atomicio(read, j->jfd, buf, size);
+ if (ret != size)
+ return (errno ? -errno : -EIO);
+
+ return 0;
+}
+
+static inline int
+vhd_journal_write(vhd_journal_t *j, void *buf, size_t size)
+{
+ ssize_t ret;
+
+ errno = 0;
+
+ ret = atomicio(vwrite, j->jfd, buf, size);
+ if (ret != size)
+ return (errno ? -errno : -EIO);
+
+ return 0;
+}
+
+static inline int
+vhd_journal_truncate(vhd_journal_t *j, off64_t length)
+{
+ int err;
+
+ err = ftruncate(j->jfd, length);
+ if (err == -1)
+ return -errno;
+
+ return 0;
+}
+
+static inline int
+vhd_journal_sync(vhd_journal_t *j)
+{
+ int err;
+
+ err = fdatasync(j->jfd);
+ if (err)
+ return -errno;
+
+ return 0;
+}
+
+static inline void
+vhd_journal_header_in(vhd_journal_header_t *header)
+{
+ BE64_IN(&header->vhd_footer_offset);
+ BE32_IN(&header->journal_data_entries);
+ BE32_IN(&header->journal_metadata_entries);
+ BE64_IN(&header->journal_data_offset);
+ BE64_IN(&header->journal_metadata_offset);
+}
+
+static inline void
+vhd_journal_header_out(vhd_journal_header_t *header)
+{
+ BE64_OUT(&header->vhd_footer_offset);
+ BE32_OUT(&header->journal_data_entries);
+ BE32_OUT(&header->journal_metadata_entries);
+ BE64_OUT(&header->journal_data_offset);
+ BE64_OUT(&header->journal_metadata_offset);
+}
+
+static int
+vhd_journal_validate_header(vhd_journal_t *j, vhd_journal_header_t *header)
+{
+ int err;
+ off64_t eof;
+
+ if (memcmp(header->cookie,
+ VHD_JOURNAL_HEADER_COOKIE, sizeof(header->cookie)))
+ return -EINVAL;
+
+ err = vhd_journal_seek(j, j->header.journal_eof, SEEK_SET);
+ if (err)
+ return err;
+
+ eof = vhd_journal_position(j);
+ if (eof == (off64_t)-1)
+ return -errno;
+
+ if (j->header.journal_data_offset > j->header.journal_eof)
+ return -EINVAL;
+
+ if (j->header.journal_metadata_offset > j->header.journal_eof)
+ return -EINVAL;
+
+ return 0;
+}
+
+static int
+vhd_journal_read_journal_header(vhd_journal_t *j, vhd_journal_header_t *header)
+{
+ int err;
+ size_t size;
+
+ size = sizeof(vhd_journal_header_t);
+ err = vhd_journal_seek(j, 0, SEEK_SET);
+ if (err)
+ return err;
+
+ err = vhd_journal_read(j, header, size);
+ if (err)
+ return err;
+
+ vhd_journal_header_in(header);
+
+ return vhd_journal_validate_header(j, header);
+}
+
+static int
+vhd_journal_write_header(vhd_journal_t *j, vhd_journal_header_t *header)
+{
+ int err;
+ size_t size;
+ vhd_journal_header_t h;
+
+ memcpy(&h, header, sizeof(vhd_journal_header_t));
+
+ err = vhd_journal_validate_header(j, &h);
+ if (err)
+ return err;
+
+ vhd_journal_header_out(&h);
+ size = sizeof(vhd_journal_header_t);
+
+ err = vhd_journal_seek(j, 0, SEEK_SET);
+ if (err)
+ return err;
+
+ err = vhd_journal_write(j, &h, size);
+ if (err)
+ return err;
+
+ return 0;
+}
+
+static int
+vhd_journal_add_journal_header(vhd_journal_t *j)
+{
+ int err;
+ off64_t off;
+ vhd_context_t *vhd;
+
+ vhd = &j->vhd;
+ memset(&j->header, 0, sizeof(vhd_journal_header_t));
+
+ err = vhd_seek(vhd, 0, SEEK_END);
+ if (err)
+ return err;
+
+ off = vhd_position(vhd);
+ if (off == (off64_t)-1)
+ return -errno;
+
+ err = vhd_get_footer(vhd);
+ if (err)
+ return err;
+
+ uuid_copy(j->header.uuid, vhd->footer.uuid);
+ memcpy(j->header.cookie,
+ VHD_JOURNAL_HEADER_COOKIE, sizeof(j->header.cookie));
+ j->header.vhd_footer_offset = off - sizeof(vhd_footer_t);
+ j->header.journal_eof = sizeof(vhd_journal_header_t);
+
+ return vhd_journal_write_header(j, &j->header);
+}
+
+static void
+vhd_journal_entry_in(vhd_journal_entry_t *entry)
+{
+ BE32_IN(&entry->type);
+ BE32_IN(&entry->size);
+ BE64_IN(&entry->offset);
+ BE64_IN(&entry->cookie);
+ BE32_IN(&entry->checksum);
+}
+
+static void
+vhd_journal_entry_out(vhd_journal_entry_t *entry)
+{
+ BE32_OUT(&entry->type);
+ BE32_OUT(&entry->size);
+ BE64_OUT(&entry->offset);
+ BE64_OUT(&entry->cookie);
+ BE32_OUT(&entry->checksum);
+}
+
+static uint32_t
+vhd_journal_checksum_entry(vhd_journal_entry_t *entry, char *buf, size_t size)
+{
+ int i;
+ unsigned char *blob;
+ uint32_t checksum, tmp;
+
+ checksum = 0;
+ tmp = entry->checksum;
+ entry->checksum = 0;
+
+ blob = (unsigned char *)entry;
+ for (i = 0; i < sizeof(vhd_journal_entry_t); i++)
+ checksum += blob[i];
+
+ blob = (unsigned char *)buf;
+ for (i = 0; i < size; i++)
+ checksum += blob[i];
+
+ entry->checksum = tmp;
+ return ~checksum;
+}
+
+static int
+vhd_journal_validate_entry(vhd_journal_entry_t *entry)
+{
+ if (entry->size == 0)
+ return -EINVAL;
+
+ if (entry->size & (VHD_SECTOR_SIZE - 1))
+ return -EINVAL;
+
+ if (entry->cookie != VHD_JOURNAL_ENTRY_COOKIE)
+ return -EINVAL;
+
+ return 0;
+}
+
+static int
+vhd_journal_read_entry(vhd_journal_t *j, vhd_journal_entry_t *entry)
+{
+ int err;
+
+ err = vhd_journal_read(j, entry, sizeof(vhd_journal_entry_t));
+ if (err)
+ return err;
+
+ vhd_journal_entry_in(entry);
+ return vhd_journal_validate_entry(entry);
+}
+
+static int
+vhd_journal_write_entry(vhd_journal_t *j, vhd_journal_entry_t *entry)
+{
+ int err;
+ vhd_journal_entry_t e;
+
+ err = vhd_journal_validate_entry(entry);
+ if (err)
+ return err;
+
+ memcpy(&e, entry, sizeof(vhd_journal_entry_t));
+ vhd_journal_entry_out(&e);
+
+ err = vhd_journal_write(j, &e, sizeof(vhd_journal_entry_t));
+ if (err)
+ err;
+
+ return 0;
+}
+
+static int
+vhd_journal_validate_entry_data(vhd_journal_entry_t *entry, char *buf)
+{
+ int err;
+ uint32_t checksum;
+
+ err = 0;
+ checksum = vhd_journal_checksum_entry(entry, buf, entry->size);
+
+ if (checksum != entry->checksum)
+ return -EINVAL;
+
+ return err;
+}
+
+static int
+vhd_journal_update(vhd_journal_t *j, off64_t offset,
+ char *buf, size_t size, uint32_t type)
+{
+ int err;
+ off64_t eof;
+ uint64_t *off, off_bak;
+ uint32_t *entries;
+ vhd_journal_entry_t entry;
+
+ entry.type = type;
+ entry.size = size;
+ entry.offset = offset;
+ entry.cookie = VHD_JOURNAL_ENTRY_COOKIE;
+ entry.checksum = vhd_journal_checksum_entry(&entry, buf, size);
+
+ err = vhd_journal_seek(j, j->header.journal_eof, SEEK_SET);
+ if (err)
+ return err;
+
+ err = vhd_journal_write_entry(j, &entry);
+ if (err)
+ goto fail;
+
+ err = vhd_journal_write(j, buf, size);
+ if (err)
+ goto fail;
+
+ if (type == VHD_JOURNAL_ENTRY_TYPE_DATA) {
+ off = &j->header.journal_data_offset;
+ entries = &j->header.journal_data_entries;
+ } else {
+ off = &j->header.journal_metadata_offset;
+ entries = &j->header.journal_metadata_entries;
+ }
+
+ off_bak = *off;
+ if (!(*entries)++)
+ *off = j->header.journal_eof;
+ j->header.journal_eof += (size + sizeof(vhd_journal_entry_t));
+
+ err = vhd_journal_write_header(j, &j->header);
+ if (err) {
+ if (!--(*entries))
+ *off = off_bak;
+ j->header.journal_eof -= (size + sizeof(vhd_journal_entry_t));
+ goto fail;
+ }
+
+ return 0;
+
+fail:
+ if (!j->is_block)
+ vhd_journal_truncate(j, j->header.journal_eof);
+ return err;
+}
+
+static int
+vhd_journal_add_footer(vhd_journal_t *j)
+{
+ int err;
+ off64_t off;
+ vhd_context_t *vhd;
+ vhd_footer_t footer;
+
+ vhd = &j->vhd;
+
+ err = vhd_seek(vhd, 0, SEEK_END);
+ if (err)
+ return err;
+
+ off = vhd_position(vhd);
+ if (off == (off64_t)-1)
+ return -errno;
+
+ err = vhd_read_footer_at(vhd, &footer, off - sizeof(vhd_footer_t));
+ if (err)
+ return err;
+
+ vhd_footer_out(&footer);
+ err = vhd_journal_update(j, off - sizeof(vhd_footer_t),
+ (char *)&footer,
+ sizeof(vhd_footer_t),
+ VHD_JOURNAL_ENTRY_TYPE_FOOTER_P);
+ if (err)
+ return err;
+
+ if (!vhd_type_dynamic(vhd))
+ return 0;
+
+ err = vhd_read_footer_at(vhd, &footer, 0);
+ if (err)
+ return err;
+
+ vhd_footer_out(&footer);
+ err = vhd_journal_update(j, 0,
+ (char *)&footer,
+ sizeof(vhd_footer_t),
+ VHD_JOURNAL_ENTRY_TYPE_FOOTER_C);
+
+ return err;
+}
+
+static int
+vhd_journal_add_header(vhd_journal_t *j)
+{
+ int err;
+ off64_t off;
+ vhd_context_t *vhd;
+ vhd_header_t header;
+
+ vhd = &j->vhd;
+
+ err = vhd_read_header(vhd, &header);
+ if (err)
+ return err;
+
+ off = vhd->footer.data_offset;
+
+ vhd_header_out(&header);
+ err = vhd_journal_update(j, off,
+ (char *)&header,
+ sizeof(vhd_header_t),
+ VHD_JOURNAL_ENTRY_TYPE_HEADER);
+
+ return err;
+}
+
+static int
+vhd_journal_add_locators(vhd_journal_t *j)
+{
+ int i, n, err;
+ vhd_context_t *vhd;
+
+ vhd = &j->vhd;
+
+ err = vhd_get_header(vhd);
+ if (err)
+ return err;
+
+ n = sizeof(vhd->header.loc) / sizeof(vhd_parent_locator_t);
+ for (i = 0; i < n; i++) {
+ char *buf;
+ off64_t off;
+ size_t size;
+ vhd_parent_locator_t *loc;
+
+ loc = vhd->header.loc + i;
+ err = vhd_validate_platform_code(loc->code);
+ if (err)
+ return err;
+
+ if (loc->code == PLAT_CODE_NONE)
+ continue;
+
+ off = loc->data_offset;
+ size = vhd_parent_locator_size(loc);
+
+ err = posix_memalign((void **)&buf, VHD_SECTOR_SIZE, size);
+ if (err)
+ return -err;
+
+ err = vhd_seek(vhd, off, SEEK_SET);
+ if (err)
+ goto end;
+
+ err = vhd_read(vhd, buf, size);
+ if (err)
+ goto end;
+
+ err = vhd_journal_update(j, off, buf, size,
+ VHD_JOURNAL_ENTRY_TYPE_LOCATOR);
+ if (err)
+ goto end;
+
+ err = 0;
+
+ end:
+ free(buf);
+ if (err)
+ break;
+ }
+
+ return err;
+}
+
+static int
+vhd_journal_add_bat(vhd_journal_t *j)
+{
+ int err;
+ off64_t off;
+ size_t size;
+ vhd_bat_t bat;
+ vhd_context_t *vhd;
+
+ vhd = &j->vhd;
+
+ err = vhd_get_header(vhd);
+ if (err)
+ return err;
+
+ err = vhd_read_bat(vhd, &bat);
+ if (err)
+ return err;
+
+ off = vhd->header.table_offset;
+ size = vhd_bytes_padded(bat.entries * sizeof(uint32_t));
+
+ vhd_bat_out(&bat);
+ err = vhd_journal_update(j, off, (char *)bat.bat, size,
+ VHD_JOURNAL_ENTRY_TYPE_BAT);
+
+ free(bat.bat);
+ return err;
+}
+
+static int
+vhd_journal_add_batmap(vhd_journal_t *j)
+{
+ int err;
+ off64_t off;
+ size_t size;
+ vhd_context_t *vhd;
+ vhd_batmap_t batmap;
+
+ vhd = &j->vhd;
+
+ err = vhd_batmap_header_offset(vhd, &off);
+ if (err)
+ return err;
+
+ err = vhd_read_batmap(vhd, &batmap);
+ if (err)
+ return err;
+
+ size = vhd_bytes_padded(sizeof(struct dd_batmap_hdr));
+
+ vhd_batmap_header_out(&batmap);
+ err = vhd_journal_update(j, off, (char *)&batmap.header, size,
+ VHD_JOURNAL_ENTRY_TYPE_BATMAP_H);
+ if (err)
+ goto out;
+
+ vhd_batmap_header_in(&batmap);
+ off = batmap.header.batmap_offset;
+ size = vhd_sectors_to_bytes(batmap.header.batmap_size);
+
+ err = vhd_journal_update(j, off, batmap.map, size,
+ VHD_JOURNAL_ENTRY_TYPE_BATMAP_M);
+
+out:
+ free(batmap.map);
+ return err;
+}
+
+static int
+vhd_journal_add_metadata(vhd_journal_t *j)
+{
+ int err;
+ off64_t eof;
+ vhd_context_t *vhd;
+
+ vhd = &j->vhd;
+
+ err = vhd_journal_add_footer(j);
+ if (err)
+ return err;
+
+ if (!vhd_type_dynamic(vhd))
+ return 0;
+
+ err = vhd_journal_add_header(j);
+ if (err)
+ return err;
+
+ err = vhd_journal_add_locators(j);
+ if (err)
+ return err;
+
+ err = vhd_journal_add_bat(j);
+ if (err)
+ return err;
+
+ if (vhd_has_batmap(vhd)) {
+ err = vhd_journal_add_batmap(j);
+ if (err)
+ return err;
+ }
+
+ j->header.journal_data_offset = j->header.journal_eof;
+ return vhd_journal_write_header(j, &j->header);
+}
+
+static int
+__vhd_journal_read_footer(vhd_journal_t *j,
+ vhd_footer_t *footer, uint32_t type)
+{
+ int err;
+ vhd_journal_entry_t entry;
+
+ err = vhd_journal_read_entry(j, &entry);
+ if (err)
+ return err;
+
+ if (entry.type != type)
+ return -EINVAL;
+
+ if (entry.size != sizeof(vhd_footer_t))
+ return -EINVAL;
+
+ err = vhd_journal_read(j, footer, entry.size);
+ if (err)
+ return err;
+
+ vhd_footer_in(footer);
+ return vhd_validate_footer(footer);
+}
+
+static int
+vhd_journal_read_footer(vhd_journal_t *j, vhd_footer_t *footer)
+{
+ return __vhd_journal_read_footer(j, footer,
+ VHD_JOURNAL_ENTRY_TYPE_FOOTER_P);
+}
+
+static int
+vhd_journal_read_footer_copy(vhd_journal_t *j, vhd_footer_t *footer)
+{
+ return __vhd_journal_read_footer(j, footer,
+ VHD_JOURNAL_ENTRY_TYPE_FOOTER_C);
+}
+
+static int
+vhd_journal_read_header(vhd_journal_t *j, vhd_header_t *header)
+{
+ int err;
+ vhd_journal_entry_t entry;
+
+ err = vhd_journal_read_entry(j, &entry);
+ if (err)
+ return err;
+
+ if (entry.type != VHD_JOURNAL_ENTRY_TYPE_HEADER)
+ return -EINVAL;
+
+ if (entry.size != sizeof(vhd_header_t))
+ return -EINVAL;
+
+ err = vhd_journal_read(j, header, entry.size);
+ if (err)
+ return err;
+
+ vhd_header_in(header);
+ return vhd_validate_header(header);
+}
+
+static int
+vhd_journal_read_locators(vhd_journal_t *j, char ***locators, int *locs)
+{
+ int err, n, _locs;
+ char **_locators, *buf;
+ off_t pos;
+ vhd_journal_entry_t entry;
+
+ _locs = 0;
+ *locs = 0;
+ *locators = NULL;
+
+ n = sizeof(j->vhd.header.loc) / sizeof(vhd_parent_locator_t);
+ _locators = calloc(n, sizeof(char *));
+ if (!_locators)
+ return -ENOMEM;
+
+ for (;;) {
+ buf = NULL;
+
+ pos = vhd_journal_position(j);
+ err = vhd_journal_read_entry(j, &entry);
+ if (err)
+ goto fail;
+
+ if (entry.type != VHD_JOURNAL_ENTRY_TYPE_LOCATOR) {
+ err = vhd_journal_seek(j, pos, SEEK_SET);
+ if (err)
+ goto fail;
+ break;
+ }
+
+ if (_locs >= n) {
+ err = -EINVAL;
+ goto fail;
+ }
+
+ err = posix_memalign((void **)&buf,
+ VHD_SECTOR_SIZE, entry.size);
+ if (err) {
+ err = -err;
+ buf = NULL;
+ goto fail;
+ }
+
+ err = vhd_journal_read(j, buf, entry.size);
+ if (err)
+ goto fail;
+
+ _locators[_locs++] = buf;
+ err = 0;
+ }
+
+
+ *locs = _locs;
+ *locators = _locators;
+
+ return 0;
+
+fail:
+ if (_locators) {
+ for (n = 0; n < _locs; n++)
+ free(_locators[n]);
+ free(_locators);
+ }
+ return err;
+}
+
+static int
+vhd_journal_read_bat(vhd_journal_t *j, vhd_bat_t *bat)
+{
+ int err;
+ size_t size;
+ vhd_context_t *vhd;
+ vhd_journal_entry_t entry;
+
+ vhd = &j->vhd;
+
+ size = vhd_bytes_padded(vhd->header.max_bat_size * sizeof(uint32_t));
+
+ err = vhd_journal_read_entry(j, &entry);
+ if (err)
+ return err;
+
+ if (entry.type != VHD_JOURNAL_ENTRY_TYPE_BAT)
+ return -EINVAL;
+
+ if (entry.size != size)
+ return -EINVAL;
+
+ if (entry.offset != vhd->header.table_offset)
+ return -EINVAL;
+
+ err = posix_memalign((void **)&bat->bat, VHD_SECTOR_SIZE, size);
+ if (err)
+ return -err;
+
+ err = vhd_journal_read(j, bat->bat, entry.size);
+ if (err)
+ goto fail;
+
+ bat->spb = vhd->header.block_size >> VHD_SECTOR_SHIFT;
+ bat->entries = vhd->header.max_bat_size;
+ vhd_bat_in(bat);
+
+ return 0;
+
+fail:
+ free(bat->bat);
+ bat->bat = NULL;
+ return err;
+}
+
+static int
+vhd_journal_read_batmap_header(vhd_journal_t *j, vhd_batmap_t *batmap)
+{
+ int err;
+ char *buf;
+ size_t size;
+ vhd_journal_entry_t entry;
+
+ size = vhd_bytes_padded(sizeof(struct dd_batmap_hdr));
+
+ err = vhd_journal_read_entry(j, &entry);
+ if (err)
+ return err;
+
+ if (entry.type != VHD_JOURNAL_ENTRY_TYPE_BATMAP_H)
+ return -EINVAL;
+
+ if (entry.size != size)
+ return -EINVAL;
+
+ err = posix_memalign((void **)&buf, VHD_SECTOR_SIZE, size);
+ if (err)
+ return err;
+
+ err = vhd_journal_read(j, buf, entry.size);
+ if (err) {
+ free(buf);
+ return err;
+ }
+
+ memcpy(&batmap->header, buf, sizeof(batmap->header));
+
+ vhd_batmap_header_in(batmap);
+ return vhd_validate_batmap_header(batmap);
+}
+
+static int
+vhd_journal_read_batmap_map(vhd_journal_t *j, vhd_batmap_t *batmap)
+{
+ int err;
+ vhd_journal_entry_t entry;
+
+ err = vhd_journal_read_entry(j, &entry);
+ if (err)
+ return err;
+
+ if (entry.type != VHD_JOURNAL_ENTRY_TYPE_BATMAP_M)
+ return -EINVAL;
+
+ if (entry.size != vhd_sectors_to_bytes(batmap->header.batmap_size))
+ return -EINVAL;
+
+ if (entry.offset != batmap->header.batmap_offset)
+ return -EINVAL;
+
+ err = posix_memalign((void **)&batmap->map,
+ VHD_SECTOR_SIZE, entry.size);
+ if (err)
+ return -err;
+
+ err = vhd_journal_read(j, batmap->map, entry.size);
+ if (err) {
+ free(batmap->map);
+ batmap->map = NULL;
+ return err;
+ }
+
+ return 0;
+}
+
+static int
+vhd_journal_read_batmap(vhd_journal_t *j, vhd_batmap_t *batmap)
+{
+ int err;
+
+ err = vhd_journal_read_batmap_header(j, batmap);
+ if (err)
+ return err;
+
+ err = vhd_journal_read_batmap_map(j, batmap);
+ if (err)
+ return err;
+
+ err = vhd_validate_batmap(batmap);
+ if (err) {
+ free(batmap->map);
+ batmap->map = NULL;
+ return err;
+ }
+
+ return 0;
+}
+
+static int
+vhd_journal_restore_footer(vhd_journal_t *j, vhd_footer_t *footer)
+{
+ return vhd_write_footer_at(&j->vhd, footer,
+ j->header.vhd_footer_offset);
+}
+
+static int
+vhd_journal_restore_footer_copy(vhd_journal_t *j, vhd_footer_t *footer)
+{
+ return vhd_write_footer_at(&j->vhd, footer, 0);
+}
+
+static int
+vhd_journal_restore_header(vhd_journal_t *j, vhd_header_t *header)
+{
+ off64_t off;
+ vhd_context_t *vhd;
+
+ vhd = &j->vhd;
+ off = vhd->footer.data_offset;
+
+ return vhd_write_header_at(&j->vhd, header, off);
+}
+
+static int
+vhd_journal_restore_locators(vhd_journal_t *j, char **locators, int locs)
+{
+ size_t size;
+ vhd_context_t *vhd;
+ int i, n, lidx, err;
+ vhd_parent_locator_t *loc;
+
+ lidx = 0;
+ vhd = &j->vhd;
+
+ n = sizeof(vhd->header.loc) / sizeof(vhd_parent_locator_t);
+
+ for (i = 0; i < n && lidx < locs; i++) {
+ loc = vhd->header.loc + i;
+ if (loc->code == PLAT_CODE_NONE)
+ continue;
+
+ err = vhd_seek(vhd, loc->data_offset, SEEK_SET);
+ if (err)
+ return err;
+
+ size = vhd_parent_locator_size(loc);
+ err = vhd_write(vhd, locators[lidx++], size);
+ if (err)
+ return err;
+ }
+
+ return 0;
+}
+
+static int
+vhd_journal_restore_bat(vhd_journal_t *j, vhd_bat_t *bat)
+{
+ return vhd_write_bat(&j->vhd, bat);
+}
+
+static int
+vhd_journal_restore_batmap(vhd_journal_t *j, vhd_batmap_t *batmap)
+{
+ return vhd_write_batmap(&j->vhd, batmap);
+}
+
+static int
+vhd_journal_restore_metadata(vhd_journal_t *j)
+{
+ off64_t off;
+ char **locators;
+ vhd_footer_t copy;
+ vhd_context_t *vhd;
+ int i, locs, hlocs, err;
+
+ vhd = &j->vhd;
+ locs = 0;
+ hlocs = 0;
+ locators = NULL;
+
+ err = vhd_journal_seek(j, sizeof(vhd_journal_header_t), SEEK_SET);
+ if (err)
+ return err;
+
+ err = vhd_journal_read_footer(j, &vhd->footer);
+ if (err)
+ return err;
+
+ if (!vhd_type_dynamic(vhd))
+ goto restore;
+
+ err = vhd_journal_read_footer_copy(j, &copy);
+ if (err)
+ return err;
+
+ err = vhd_journal_read_header(j, &vhd->header);
+ if (err)
+ return err;
+
+ for (hlocs = 0, i = 0; i < vhd_parent_locator_count(vhd); i++) {
+ if (vhd_validate_platform_code(vhd->header.loc[i].code))
+ return err;
+
+ if (vhd->header.loc[i].code != PLAT_CODE_NONE)
+ hlocs++;
+ }
+
+ if (hlocs) {
+ err = vhd_journal_read_locators(j, &locators, &locs);
+ if (err)
+ return err;
+
+ if (hlocs != locs) {
+ err = -EINVAL;
+ goto out;
+ }
+ }
+
+ err = vhd_journal_read_bat(j, &vhd->bat);
+ if (err)
+ goto out;
+
+ if (vhd_has_batmap(vhd)) {
+ err = vhd_journal_read_batmap(j, &vhd->batmap);
+ if (err)
+ goto out;
+ }
+
+restore:
+ off = vhd_journal_position(j);
+ if (off == (off64_t)-1)
+ return -errno;
+
+ if (j->header.journal_data_offset != off)
+ return -EINVAL;
+
+ err = vhd_journal_restore_footer(j, &vhd->footer);
+ if (err)
+ goto out;
+
+ if (!vhd_type_dynamic(vhd))
+ goto out;
+
+ err = vhd_journal_restore_footer_copy(j, &copy);
+ if (err)
+ goto out;
+
+ err = vhd_journal_restore_header(j, &vhd->header);
+ if (err)
+ goto out;
+
+ if (locs) {
+ err = vhd_journal_restore_locators(j, locators, locs);
+ if (err)
+ goto out;
+ }
+
+ err = vhd_journal_restore_bat(j, &vhd->bat);
+ if (err)
+ goto out;
+
+ if (vhd_has_batmap(vhd)) {
+ err = vhd_journal_restore_batmap(j, &vhd->batmap);
+ if (err)
+ goto out;
+ }
+
+ err = 0;
+
+out:
+ if (locators) {
+ for (i = 0; i < locs; i++)
+ free(locators[i]);
+ free(locators);
+ }
+
+ if (!err && !vhd->is_block)
+ err = ftruncate(vhd->fd,
+ j->header.vhd_footer_offset +
+ sizeof(vhd_footer_t));
+
+ return err;
+}
+
+static int
+vhd_journal_disable_vhd(vhd_journal_t *j)
+{
+ int err;
+ vhd_context_t *vhd;
+
+ vhd = &j->vhd;
+
+ err = vhd_get_footer(vhd);
+ if (err)
+ return err;
+
+ memcpy(&vhd->footer.cookie,
+ VHD_POISON_COOKIE, sizeof(vhd->footer.cookie));
+ vhd->footer.checksum = vhd_checksum_footer(&vhd->footer);
+
+ err = vhd_write_footer(vhd, &vhd->footer);
+ if (err)
+ return err;
+
+ return 0;
+}
+
+static int
+vhd_journal_enable_vhd(vhd_journal_t *j)
+{
+ int err;
+ vhd_context_t *vhd;
+
+ vhd = &j->vhd;
+
+ err = vhd_get_footer(vhd);
+ if (err)
+ return err;
+
+ if (!vhd_disabled(vhd))
+ return 0;
+
+ memcpy(&vhd->footer.cookie, HD_COOKIE, sizeof(vhd->footer.cookie));
+ vhd->footer.checksum = vhd_checksum_footer(&vhd->footer);
+
+ err = vhd_write_footer(vhd, &vhd->footer);
+ if (err)
+ return err;
+
+ return 0;
+}
+
+int
+vhd_journal_close(vhd_journal_t *j)
+{
+ if (j->jfd)
+ close(j->jfd);
+
+ vhd_close(&j->vhd);
+ free(j->jname);
+
+ return 0;
+}
+
+int
+vhd_journal_remove(vhd_journal_t *j)
+{
+ int err;
+
+ err = vhd_journal_enable_vhd(j);
+ if (err)
+ return err;
+
+ if (j->jfd) {
+ close(j->jfd);
+ if (!j->is_block)
+ unlink(j->jname);
+ }
+
+ vhd_close(&j->vhd);
+ free(j->jname);
+
+ return 0;
+}
+
+int
+vhd_journal_open(vhd_journal_t *j, const char *file, const char *jfile)
+{
+ int err;
+ vhd_context_t *vhd;
+
+ memset(j, 0, sizeof(vhd_journal_t));
+
+ j->jfd = -1;
+ vhd = &j->vhd;
+
+ j->jname = strdup(jfile);
+ if (j->jname == NULL)
+ return -ENOMEM;
+
+ j->jfd = open(j->jname, O_LARGEFILE | O_RDWR);
+ if (j->jfd == -1) {
+ err = -errno;
+ goto fail;
+ }
+
+ err = vhd_test_file_fixed(j->jname, &j->is_block);
+ if (err)
+ goto fail;
+
+ vhd->fd = open(file, O_LARGEFILE | O_RDWR | O_DIRECT);
+ if (vhd->fd == -1) {
+ err = -errno;
+ goto fail;
+ }
+
+ err = vhd_test_file_fixed(file, &vhd->is_block);
+ if (err)
+ goto fail;
+
+ err = vhd_journal_read_journal_header(j, &j->header);
+ if (err)
+ goto fail;
+
+ err = vhd_journal_restore_metadata(j);
+ if (err)
+ goto fail;
+
+ close(vhd->fd);
+ free(vhd->bat.bat);
+ free(vhd->batmap.map);
+
+ err = vhd_open(vhd, file, VHD_OPEN_RDWR);
+ if (err)
+ goto fail;
+
+ err = vhd_get_bat(vhd);
+ if (err)
+ goto fail;
+
+ if (vhd_has_batmap(vhd)) {
+ err = vhd_get_batmap(vhd);
+ if (err)
+ goto fail;
+ }
+
+ err = vhd_journal_disable_vhd(j);
+ if (err)
+ goto fail;
+
+ return 0;
+
+fail:
+ vhd_journal_close(j);
+ return err;
+}
+
+int
+vhd_journal_create(vhd_journal_t *j, const char *file, const char *jfile)
+{
+ char *buf;
+ int i, err;
+ size_t size;
+ off64_t off;
+ struct stat stats;
+
+ memset(j, 0, sizeof(vhd_journal_t));
+ j->jfd = -1;
+
+ j->jname = strdup(jfile);
+ if (j->jname == NULL) {
+ err = -ENOMEM;
+ goto fail1;
+ }
+
+ if (access(j->jname, F_OK) == 0) {
+ err = vhd_test_file_fixed(j->jname, &j->is_block);
+ if (err)
+ goto fail1;
+
+ if (!j->is_block) {
+ err = -EEXIST;
+ goto fail1;
+ }
+ }
+
+ if (j->is_block)
+ j->jfd = open(j->jname, O_LARGEFILE | O_RDWR, 0644);
+ else
+ j->jfd = open(j->jname,
+ O_CREAT | O_TRUNC | O_LARGEFILE | O_RDWR, 0644);
+ if (j->jfd == -1) {
+ err = -errno;
+ goto fail1;
+ }
+
+ err = vhd_open(&j->vhd, file, VHD_OPEN_RDWR | VHD_OPEN_STRICT);
+ if (err)
+ goto fail1;
+
+ err = vhd_get_bat(&j->vhd);
+ if (err)
+ goto fail2;
+
+ if (vhd_has_batmap(&j->vhd)) {
+ err = vhd_get_batmap(&j->vhd);
+ if (err)
+ goto fail2;
+ }
+
+ err = vhd_journal_add_journal_header(j);
+ if (err)
+ goto fail2;
+
+ err = vhd_journal_add_metadata(j);
+ if (err)
+ goto fail2;
+
+ err = vhd_journal_disable_vhd(j);
+ if (err)
+ goto fail2;
+
+ err = vhd_journal_sync(j);
+ if (err)
+ goto fail2;
+
+ return 0;
+
+fail1:
+ if (j->jfd != -1) {
+ close(j->jfd);
+ if (!j->is_block)
+ unlink(j->jname);
+ }
+ free(j->jname);
+ memset(j, 0, sizeof(vhd_journal_t));
+
+ return err;
+
+fail2:
+ vhd_journal_remove(j);
+ return err;
+}
+
+int
+vhd_journal_add_block(vhd_journal_t *j, uint32_t block, char mode)
+{
+ int err;
+ char *buf;
+ off64_t off;
+ size_t size;
+ uint64_t blk;
+ vhd_context_t *vhd;
+
+ buf = NULL;
+ vhd = &j->vhd;
+
+ if (!vhd_type_dynamic(vhd))
+ return -EINVAL;
+
+ err = vhd_get_bat(vhd);
+ if (err)
+ return err;
+
+ if (block >= vhd->bat.entries)
+ return -ERANGE;
+
+ blk = vhd->bat.bat[block];
+ if (blk == DD_BLK_UNUSED)
+ return 0;
+
+ off = vhd_sectors_to_bytes(blk);
+
+ if (mode & VHD_JOURNAL_METADATA) {
+ size = vhd_sectors_to_bytes(vhd->bm_secs);
+
+ err = vhd_read_bitmap(vhd, block, &buf);
+ if (err)
+ return err;
+
+ err = vhd_journal_update(j, off, buf, size,
+ VHD_JOURNAL_ENTRY_TYPE_DATA);
+
+ free(buf);
+
+ if (err)
+ return err;
+ }
+
+ if (mode & VHD_JOURNAL_DATA) {
+ off += vhd_sectors_to_bytes(vhd->bm_secs);
+ size = vhd_sectors_to_bytes(vhd->spb);
+
+ err = vhd_read_block(vhd, block, &buf);
+ if (err)
+ return err;
+
+ err = vhd_journal_update(j, off, buf, size,
+ VHD_JOURNAL_ENTRY_TYPE_DATA);
+ free(buf);
+
+ if (err)
+ return err;
+ }
+
+ return vhd_journal_sync(j);
+}
+
+/*
+ * commit indicates the transaction completed
+ * successfully and we can remove the undo log
+ */
+int
+vhd_journal_commit(vhd_journal_t *j)
+{
+ int err;
+
+ j->header.journal_data_entries = 0;
+ j->header.journal_metadata_entries = 0;
+ j->header.journal_data_offset = 0;
+ j->header.journal_metadata_offset = 0;
+
+ err = vhd_journal_write_header(j, &j->header);
+ if (err)
+ return err;
+
+ if (!j->is_block)
+ err = vhd_journal_truncate(j, sizeof(vhd_journal_header_t));
+ if (err)
+ return -errno;
+
+ return 0;
+}
+
+/*
+ * revert indicates the transaction failed
+ * and we should revert any changes via the undo log
+ */
+int
+vhd_journal_revert(vhd_journal_t *j)
+{
+ int i, err;
+ char *buf, *file;
+ vhd_context_t *vhd;
+ vhd_journal_entry_t entry;
+
+ err = 0;
+ vhd = &j->vhd;
+ buf = NULL;
+
+ file = strdup(vhd->file);
+ if (!file)
+ return -ENOMEM;
+
+ vhd_close(&j->vhd);
+ j->vhd.fd = open(file, O_RDWR | O_DIRECT | O_LARGEFILE);
+ if (j->vhd.fd == -1) {
+ free(file);
+ return -errno;
+ }
+
+ err = vhd_test_file_fixed(file, &vhd->is_block);
+ if (err) {
+ free(file);
+ return err;
+ }
+
+ err = vhd_journal_restore_metadata(j);
+ if (err) {
+ free(file);
+ return err;
+ }
+
+ close(vhd->fd);
+ free(vhd->bat.bat);
+ free(vhd->batmap.map);
+
+ err = vhd_open(vhd, file, VHD_OPEN_RDWR);
+ free(file);
+ if (err)
+ return err;
+
+ err = vhd_journal_seek(j, j->header.journal_data_offset, SEEK_SET);
+ if (err)
+ return err;
+
+ for (i = 0; i < j->header.journal_data_entries; i++) {
+ err = vhd_journal_read_entry(j, &entry);
+ if (err)
+ goto end;
+
+ err = posix_memalign((void **)&buf,
+ VHD_SECTOR_SIZE, entry.size);
+ if (err) {
+ err = -err;
+ buf = NULL;
+ goto end;
+ }
+
+ err = vhd_journal_read(j, buf, entry.size);
+ if (err)
+ goto end;
+
+ err = vhd_journal_validate_entry_data(&entry, buf);
+ if (err)
+ goto end;
+
+ err = vhd_seek(vhd, entry.offset, SEEK_SET);
+ if (err)
+ goto end;
+
+ err = vhd_write(vhd, buf, entry.size);
+ if (err)
+ goto end;
+
+ err = 0;
+
+ end:
+ free(buf);
+ buf = NULL;
+ if (err)
+ break;
+ }
+
+ if (err)
+ return err;
+
+ if (!vhd->is_block) {
+ err = ftruncate(vhd->fd, j->header.vhd_footer_offset +
+ sizeof(vhd_footer_t));
+ if (err)
+ return -errno;
+ }
+
+ return vhd_journal_sync(j);
+}