aboutsummaryrefslogtreecommitdiffstats
path: root/tools/blktap2/vhd
diff options
context:
space:
mode:
authorKeir Fraser <keir.fraser@citrix.com>2009-05-26 11:52:31 +0100
committerKeir Fraser <keir.fraser@citrix.com>2009-05-26 11:52:31 +0100
commitbd5573a6301a6b229d36670a042a760f89b10dfd (patch)
tree3d9a729ca041e1b1999843a1be834cf7316eb021 /tools/blktap2/vhd
parent6009f4ddb2cdb8555d2d5e030d351893e971b995 (diff)
downloadxen-bd5573a6301a6b229d36670a042a760f89b10dfd.tar.gz
xen-bd5573a6301a6b229d36670a042a760f89b10dfd.tar.bz2
xen-bd5573a6301a6b229d36670a042a760f89b10dfd.zip
blktap2: a completely rewritten blktap implementation
Benefits to blktap2 over the old version of blktap: * Isolation from xenstore - Blktap devices are now created directly on the linux dom0 command line, rather than being spawned in response to XenStore events. This is handy for debugging, makes blktap generally easier to work with, and is a step toward a generic user-level block device implementation that is not Xen-specific. * Improved tapdisk infrastructure: simpler request forwarding, new request scheduler, request merging, more efficient use of AIO. * Improved tapdisk error handling and memory management. No allocations on the block data path, IO retry logic to protect guests transient block device failures. This has been tested and is known to work on weird environments such as NFS soft mounts. * Pause and snapshot of live virtual disks (see xmsnap script). * VHD support. The VHD code in this release has been rigorously tested, and represents a very mature implementation of the VHD image format. * No more duplication of mechanism with blkback. The blktap kernel module has changed dramatically from the original blktap. Blkback is now always used to talk to Xen guests, blktap just presents a Linux gendisk that blkback can export. This is done while preserving the zero-copy data path from domU to physical device. These patches deprecate the old blktap code, which can hopefully be removed from the tree completely at some point in the future. Signed-off-by: Jake Wires <jake.wires@citrix.com> Signed-off-by: Dutch Meyer <dmeyer@cs.ubc.ca>
Diffstat (limited to 'tools/blktap2/vhd')
-rw-r--r--tools/blktap2/vhd/Makefile55
-rw-r--r--tools/blktap2/vhd/lib/Makefile73
-rw-r--r--tools/blktap2/vhd/lib/atomicio.c61
-rw-r--r--tools/blktap2/vhd/lib/libvhd-journal.c1534
-rw-r--r--tools/blktap2/vhd/lib/libvhd.c3328
-rw-r--r--tools/blktap2/vhd/lib/relative-path.c299
-rw-r--r--tools/blktap2/vhd/lib/vhd-util-check.c977
-rw-r--r--tools/blktap2/vhd/lib/vhd-util-coalesce.c218
-rw-r--r--tools/blktap2/vhd/lib/vhd-util-create.c80
-rw-r--r--tools/blktap2/vhd/lib/vhd-util-fill.c105
-rw-r--r--tools/blktap2/vhd/lib/vhd-util-modify.c132
-rw-r--r--tools/blktap2/vhd/lib/vhd-util-query.c159
-rw-r--r--tools/blktap2/vhd/lib/vhd-util-read.c742
-rw-r--r--tools/blktap2/vhd/lib/vhd-util-repair.c84
-rw-r--r--tools/blktap2/vhd/lib/vhd-util-resize.c1131
-rw-r--r--tools/blktap2/vhd/lib/vhd-util-revert.c106
-rw-r--r--tools/blktap2/vhd/lib/vhd-util-scan.c1315
-rw-r--r--tools/blktap2/vhd/lib/vhd-util-set-field.c106
-rw-r--r--tools/blktap2/vhd/lib/vhd-util-snapshot.c216
-rw-r--r--tools/blktap2/vhd/vhd-update.c261
-rw-r--r--tools/blktap2/vhd/vhd-util.c160
21 files changed, 11142 insertions, 0 deletions
diff --git a/tools/blktap2/vhd/Makefile b/tools/blktap2/vhd/Makefile
new file mode 100644
index 0000000000..099a0baca7
--- /dev/null
+++ b/tools/blktap2/vhd/Makefile
@@ -0,0 +1,55 @@
+XEN_ROOT=../../../
+BLKTAP_ROOT := ../
+include $(XEN_ROOT)/tools/Rules.mk
+
+IBIN = vhd-util vhd-update
+INST_DIR = $(SBINDIR)
+
+LIBDIR = lib
+
+CFLAGS += -Werror
+CFLAGS += -Wno-unused
+CFLAGS += -I../include
+CFLAGS += -D_GNU_SOURCE
+
+ifeq ($(CONFIG_X86_64),y)
+CFLAGS += -fPIC
+endif
+
+ifeq ($(VHD_STATIC),y)
+CFLAGS += -static
+endif
+
+LIBS := -L$(LIBDIR) -lvhd
+LIBS += -luuid
+
+# Get gcc to generate the dependencies for us.
+CFLAGS += -Wp,-MD,.$(@F).d
+DEPS = .*.d
+
+all: build
+
+build: libvhd $(IBIN)
+
+libvhd:
+ @set -e
+ $(MAKE) -C $(LIBDIR) all
+
+vhd-util: vhd-util.o
+ $(CC) $(CFLAGS) -o vhd-util vhd-util.o $(LIBS)
+
+vhd-update: vhd-update.o
+ $(CC) $(CFLAGS) -o vhd-update vhd-update.o $(LIBS)
+
+install: all
+ $(MAKE) -C $(LIBDIR) install
+ $(INSTALL_DIR) -p $(DESTDIR)$(INST_DIR)
+ $(INSTALL_PROG) $(IBIN) $(DESTDIR)$(INST_DIR)
+
+clean:
+ $(MAKE) -C $(LIBDIR) clean
+ rm -rf *.o *~ $(DEPS) $(IBIN)
+
+.PHONY: all build clean install libvhd vhd-util vhd-update
+
+-include $(DEPS)
diff --git a/tools/blktap2/vhd/lib/Makefile b/tools/blktap2/vhd/lib/Makefile
new file mode 100644
index 0000000000..e26ef86403
--- /dev/null
+++ b/tools/blktap2/vhd/lib/Makefile
@@ -0,0 +1,73 @@
+XEN_ROOT=../../../../
+BLKTAP_ROOT := ../../
+include $(XEN_ROOT)/tools/Rules.mk
+
+LIBVHD-MAJOR = 1.0
+LIBVHD-MINOR = 0
+LIBVHD-SONAME = libvhd.so.$(LIBVHD-MAJOR)
+
+LVM-UTIL-OBJ := $(BLKTAP_ROOT)lvm/lvm-util.o
+
+LIBVHD-BUILD := libvhd.a
+
+INST-DIR = $(LIBDIR)
+
+CFLAGS += -Werror
+CFLAGS += -Wno-unused
+CFLAGS += -I../../include
+CFLAGS += -D_GNU_SOURCE
+CFLAGS += -fPIC
+CFLAGS += -g
+
+LIBS := -luuid
+
+# Get gcc to generate the dependencies for us.
+CFLAGS += -Wp,-MD,.$(@F).d
+DEPS = .*.d
+
+LIB-SRCS := libvhd.c
+LIB-SRCS += libvhd-journal.c
+LIB-SRCS += vhd-util-coalesce.c
+LIB-SRCS += vhd-util-create.c
+LIB-SRCS += vhd-util-fill.c
+LIB-SRCS += vhd-util-modify.c
+LIB-SRCS += vhd-util-query.c
+LIB-SRCS += vhd-util-read.c
+LIB-SRCS += vhd-util-repair.c
+LIB-SRCS += vhd-util-resize.c
+LIB-SRCS += vhd-util-revert.c
+LIB-SRCS += vhd-util-set-field.c
+LIB-SRCS += vhd-util-snapshot.c
+LIB-SRCS += vhd-util-scan.c
+LIB-SRCS += vhd-util-check.c
+LIB-SRCS += relative-path.c
+LIB-SRCS += atomicio.c
+
+LIB-OBJS = $(patsubst %.c,%.o,$(LIB-SRCS))
+LIB-OBJS += $(LVM-UTIL-OBJ)
+
+LIBVHD = libvhd.a libvhd.so.$(LIBVHD-MAJOR).$(LIBVHD-MINOR)
+
+all: build
+
+build: $(LIBVHD-BUILD)
+
+libvhd.a: $(LIB-OBJS)
+ $(CC) $(CFLAGS) -Wl,$(SONAME_LDFLAG),$(LIBVHD-SONAME) $(SHLIB_CFLAGS) \
+ -o libvhd.so.$(LIBVHD-MAJOR).$(LIBVHD-MINOR) $(LIBS) $^
+ ln -sf libvhd.so.$(LIBVHD-MAJOR).$(LIBVHD-MINOR) libvhd.so.$(LIBVHD-MAJOR)
+ ln -sf libvhd.so.$(LIBVHD-MAJOR) libvhd.so
+ $(AR) rc $@ $^
+
+install: all
+ $(INSTALL_DIR) -p $(DESTDIR)$(INST-DIR)
+ $(INSTALL_DATA) $(LIBVHD) $(DESTDIR)$(INST-DIR)
+ ln -sf libvhd.so.$(LIBVHD-MAJOR).$(LIBVHD-MINOR) $(DESTDIR)$(INST-DIR)/libvhd.so.$(LIBVHD-MAJOR)
+ ln -sf libvhd.so.$(LIBVHD-MAJOR) $(DESTDIR)$(INST-DIR)/libvhd.so
+
+clean:
+ rm -rf *.a *.so* *.o *~ $(DEPS) $(LIBVHD)
+
+.PHONY: all build clean install libvhd
+
+-include $(DEPS)
diff --git a/tools/blktap2/vhd/lib/atomicio.c b/tools/blktap2/vhd/lib/atomicio.c
new file mode 100644
index 0000000000..ae0e24b00a
--- /dev/null
+++ b/tools/blktap2/vhd/lib/atomicio.c
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2005 Anil Madhavapeddy. All rights reserved.
+ * Copyright (c) 1995,1999 Theo de Raadt. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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 <stdlib.h>
+#include <errno.h>
+#include "atomicio.h"
+
+/*
+ * ensure all of data on socket comes through. f==read || f==vwrite
+ */
+size_t
+atomicio(f, fd, _s, n)
+ ssize_t (*f) (int, void *, size_t);
+ int fd;
+ void *_s;
+ size_t n;
+{
+ char *s = _s;
+ size_t pos = 0;
+ ssize_t res;
+
+ while (n > pos) {
+ res = (f) (fd, s + pos, n - pos);
+ switch (res) {
+ case -1:
+ if (errno == EINTR || errno == EAGAIN)
+ continue;
+ return 0;
+ case 0:
+ errno = EPIPE;
+ return pos;
+ default:
+ pos += (size_t)res;
+ }
+ }
+ return (pos);
+}
+
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);
+}
diff --git a/tools/blktap2/vhd/lib/libvhd.c b/tools/blktap2/vhd/lib/libvhd.c
new file mode 100644
index 0000000000..1af30ad1f6
--- /dev/null
+++ b/tools/blktap2/vhd/lib/libvhd.c
@@ -0,0 +1,3328 @@
+/* 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.
+*/
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE
+#endif
+#include <stdio.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <libgen.h>
+#include <iconv.h>
+#include <sys/mman.h>
+
+#include "libvhd.h"
+#include "relative-path.h"
+
+static int libvhd_dbg = 0;
+
+void
+libvhd_set_log_level(int level)
+{
+ if (level)
+ libvhd_dbg = 1;
+}
+
+#define VHDLOG(_f, _a...) \
+ do { \
+ if (libvhd_dbg) \
+ syslog(LOG_INFO, "libvhd::%s: "_f, \
+ __func__, ##_a); \
+ } while (0)
+
+#define BIT_MASK 0x80
+
+#ifdef ENABLE_FAILURE_TESTING
+const char* ENV_VAR_FAIL[NUM_FAIL_TESTS] = {
+ "VHD_UTIL_TEST_FAIL_REPARENT_BEGIN",
+ "VHD_UTIL_TEST_FAIL_REPARENT_LOCATOR",
+ "VHD_UTIL_TEST_FAIL_REPARENT_END",
+ "VHD_UTIL_TEST_FAIL_RESIZE_BEGIN",
+ "VHD_UTIL_TEST_FAIL_RESIZE_DATA_MOVED",
+ "VHD_UTIL_TEST_FAIL_RESIZE_METADATA_MOVED",
+ "VHD_UTIL_TEST_FAIL_RESIZE_END"
+};
+int TEST_FAIL[NUM_FAIL_TESTS];
+#endif // ENABLE_FAILURE_TESTING
+
+static inline int
+test_bit (volatile char *addr, int nr)
+{
+ return ((addr[nr >> 3] << (nr & 7)) & BIT_MASK) != 0;
+}
+
+static inline void
+set_bit (volatile char *addr, int nr)
+{
+ addr[nr >> 3] |= (BIT_MASK >> (nr & 7));
+}
+
+static inline void
+clear_bit (volatile char *addr, int nr)
+{
+ addr[nr >> 3] &= ~(BIT_MASK >> (nr & 7));
+}
+
+static inline int
+old_test_bit(volatile char *addr, int nr)
+{
+ return (((uint32_t *)addr)[nr >> 5] >> (nr & 31)) & 1;
+}
+
+static inline void
+old_set_bit(volatile char *addr, int nr)
+{
+ ((uint32_t *)addr)[nr >> 5] |= (1 << (nr & 31));
+}
+
+static inline void
+old_clear_bit(volatile char *addr, int nr)
+{
+ ((uint32_t *)addr)[nr >> 5] &= ~(1 << (nr & 31));
+}
+
+void
+vhd_footer_in(vhd_footer_t *footer)
+{
+ BE32_IN(&footer->features);
+ BE32_IN(&footer->ff_version);
+ BE64_IN(&footer->data_offset);
+ BE32_IN(&footer->timestamp);
+ BE32_IN(&footer->crtr_ver);
+ BE32_IN(&footer->crtr_os);
+ BE64_IN(&footer->orig_size);
+ BE64_IN(&footer->curr_size);
+ BE32_IN(&footer->geometry);
+ BE32_IN(&footer->type);
+ BE32_IN(&footer->checksum);
+}
+
+void
+vhd_footer_out(vhd_footer_t *footer)
+{
+ BE32_OUT(&footer->features);
+ BE32_OUT(&footer->ff_version);
+ BE64_OUT(&footer->data_offset);
+ BE32_OUT(&footer->timestamp);
+ BE32_OUT(&footer->crtr_ver);
+ BE32_OUT(&footer->crtr_os);
+ BE64_OUT(&footer->orig_size);
+ BE64_OUT(&footer->curr_size);
+ BE32_OUT(&footer->geometry);
+ BE32_OUT(&footer->type);
+ BE32_OUT(&footer->checksum);
+}
+
+void
+vhd_header_in(vhd_header_t *header)
+{
+ int i, n;
+
+ BE64_IN(&header->data_offset);
+ BE64_IN(&header->table_offset);
+ BE32_IN(&header->hdr_ver);
+ BE32_IN(&header->max_bat_size);
+ BE32_IN(&header->block_size);
+ BE32_IN(&header->checksum);
+ BE32_IN(&header->prt_ts);
+
+ n = sizeof(header->loc) / sizeof(vhd_parent_locator_t);
+
+ for (i = 0; i < n; i++) {
+ BE32_IN(&header->loc[i].code);
+ BE32_IN(&header->loc[i].data_space);
+ BE32_IN(&header->loc[i].data_len);
+ BE64_IN(&header->loc[i].data_offset);
+ }
+}
+
+void
+vhd_header_out(vhd_header_t *header)
+{
+ int i, n;
+
+ BE64_OUT(&header->data_offset);
+ BE64_OUT(&header->table_offset);
+ BE32_OUT(&header->hdr_ver);
+ BE32_OUT(&header->max_bat_size);
+ BE32_OUT(&header->block_size);
+ BE32_OUT(&header->checksum);
+ BE32_OUT(&header->prt_ts);
+
+ n = sizeof(header->loc) / sizeof(vhd_parent_locator_t);
+
+ for (i = 0; i < n; i++) {
+ BE32_OUT(&header->loc[i].code);
+ BE32_OUT(&header->loc[i].data_space);
+ BE32_OUT(&header->loc[i].data_len);
+ BE64_OUT(&header->loc[i].data_offset);
+ }
+}
+
+void
+vhd_batmap_header_in(vhd_batmap_t *batmap)
+{
+ BE64_IN(&batmap->header.batmap_offset);
+ BE32_IN(&batmap->header.batmap_size);
+ BE32_IN(&batmap->header.batmap_version);
+ BE32_IN(&batmap->header.checksum);
+}
+
+void
+vhd_batmap_header_out(vhd_batmap_t *batmap)
+{
+ BE64_OUT(&batmap->header.batmap_offset);
+ BE32_OUT(&batmap->header.batmap_size);
+ BE32_OUT(&batmap->header.batmap_version);
+ BE32_OUT(&batmap->header.checksum);
+}
+
+void
+vhd_bat_in(vhd_bat_t *bat)
+{
+ int i;
+
+ for (i = 0; i < bat->entries; i++)
+ BE32_IN(&bat->bat[i]);
+}
+
+void
+vhd_bat_out(vhd_bat_t *bat)
+{
+ int i;
+
+ for (i = 0; i < bat->entries; i++)
+ BE32_OUT(&bat->bat[i]);
+}
+
+uint32_t
+vhd_checksum_footer(vhd_footer_t *footer)
+{
+ int i;
+ unsigned char *blob;
+ uint32_t checksum, tmp;
+
+ checksum = 0;
+ tmp = footer->checksum;
+ footer->checksum = 0;
+
+ blob = (unsigned char *)footer;
+ for (i = 0; i < sizeof(vhd_footer_t); i++)
+ checksum += (uint32_t)blob[i];
+
+ footer->checksum = tmp;
+ return ~checksum;
+}
+
+int
+vhd_validate_footer(vhd_footer_t *footer)
+{
+ int csize;
+ uint32_t checksum;
+
+ csize = sizeof(footer->cookie);
+ if (memcmp(footer->cookie, HD_COOKIE, csize) != 0 &&
+ memcmp(footer->cookie, VHD_POISON_COOKIE, csize) != 0) {
+ char buf[9];
+ memcpy(buf, footer->cookie, 8);
+ buf[8]= '\0';
+ VHDLOG("invalid footer cookie: %s\n", buf);
+ return -EINVAL;
+ }
+
+ checksum = vhd_checksum_footer(footer);
+ if (checksum != footer->checksum) {
+ /*
+ * early td-util did not re-calculate
+ * checksum when marking vhds 'hidden'
+ */
+ if (footer->hidden &&
+ !strncmp(footer->crtr_app, "tap", 3) &&
+ (footer->crtr_ver == VHD_VERSION(0, 1) ||
+ footer->crtr_ver == VHD_VERSION(1, 1))) {
+ char tmp = footer->hidden;
+ footer->hidden = 0;
+ checksum = vhd_checksum_footer(footer);
+ footer->hidden = tmp;
+
+ if (checksum == footer->checksum)
+ return 0;
+ }
+
+ VHDLOG("invalid footer checksum: "
+ "footer = 0x%08x, calculated = 0x%08x\n",
+ footer->checksum, checksum);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+uint32_t
+vhd_checksum_header(vhd_header_t *header)
+{
+ int i;
+ unsigned char *blob;
+ uint32_t checksum, tmp;
+
+ checksum = 0;
+ tmp = header->checksum;
+ header->checksum = 0;
+
+ blob = (unsigned char *)header;
+ for (i = 0; i < sizeof(vhd_header_t); i++)
+ checksum += (uint32_t)blob[i];
+
+ header->checksum = tmp;
+ return ~checksum;
+}
+
+int
+vhd_validate_header(vhd_header_t *header)
+{
+ int i, n;
+ uint32_t checksum;
+
+ if (memcmp(header->cookie, DD_COOKIE, 8) != 0) {
+ char buf[9];
+ memcpy(buf, header->cookie, 8);
+ buf[8] = '\0';
+ VHDLOG("invalid header cookie: %s\n", buf);
+ return -EINVAL;
+ }
+
+ if (header->hdr_ver != 0x00010000) {
+ VHDLOG("invalid header version 0x%08x\n", header->hdr_ver);
+ return -EINVAL;
+ }
+
+ if (header->data_offset != 0xFFFFFFFFFFFFFFFF) {
+ VHDLOG("invalid header data_offset 0x%016"PRIx64"\n",
+ header->data_offset);
+ return -EINVAL;
+ }
+
+ n = sizeof(header->loc) / sizeof(vhd_parent_locator_t);
+ for (i = 0; i < n; i++)
+ if (vhd_validate_platform_code(header->loc[i].code))
+ return -EINVAL;
+
+ checksum = vhd_checksum_header(header);
+ if (checksum != header->checksum) {
+ VHDLOG("invalid header checksum: "
+ "header = 0x%08x, calculated = 0x%08x\n",
+ header->checksum, checksum);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static inline int
+vhd_validate_bat(vhd_bat_t *bat)
+{
+ if (!bat->bat)
+ return -EINVAL;
+
+ return 0;
+}
+
+uint32_t
+vhd_checksum_batmap(vhd_batmap_t *batmap)
+{
+ int i, n;
+ char *blob;
+ uint32_t checksum;
+
+ blob = batmap->map;
+ checksum = 0;
+
+ n = vhd_sectors_to_bytes(batmap->header.batmap_size);
+
+ for (i = 0; i < n; i++) {
+ if (batmap->header.batmap_version == VHD_BATMAP_VERSION(1, 1))
+ checksum += (uint32_t)blob[i];
+ else
+ checksum += (uint32_t)(unsigned char)blob[i];
+ }
+
+ return ~checksum;
+}
+
+int
+vhd_validate_batmap_header(vhd_batmap_t *batmap)
+{
+ if (memcmp(batmap->header.cookie, VHD_BATMAP_COOKIE, 8))
+ return -EINVAL;
+
+ if (batmap->header.batmap_version > VHD_BATMAP_CURRENT_VERSION)
+ return -EINVAL;
+
+ return 0;
+}
+
+int
+vhd_validate_batmap(vhd_batmap_t *batmap)
+{
+ uint32_t checksum;
+
+ if (!batmap->map)
+ return -EINVAL;
+
+ checksum = vhd_checksum_batmap(batmap);
+ if (checksum != batmap->header.checksum)
+ return -EINVAL;
+
+ return 0;
+}
+
+int
+vhd_batmap_header_offset(vhd_context_t *ctx, off64_t *_off)
+{
+ off64_t off;
+ size_t bat;
+
+ *_off = 0;
+
+ off = ctx->header.table_offset;
+ bat = ctx->header.max_bat_size * sizeof(uint32_t);
+ off += vhd_bytes_padded(bat);
+
+ *_off = off;
+ return 0;
+}
+
+int
+vhd_validate_platform_code(uint32_t code)
+{
+ switch (code) {
+ case PLAT_CODE_NONE:
+ case PLAT_CODE_WI2R:
+ case PLAT_CODE_WI2K:
+ case PLAT_CODE_W2RU:
+ case PLAT_CODE_W2KU:
+ case PLAT_CODE_MAC:
+ case PLAT_CODE_MACX:
+ return 0;
+ default:
+ VHDLOG("invalid parent locator code %u\n", code);
+ return -EINVAL;
+ }
+}
+
+int
+vhd_parent_locator_count(vhd_context_t *ctx)
+{
+ return (sizeof(ctx->header.loc) / sizeof(vhd_parent_locator_t));
+}
+
+int
+vhd_hidden(vhd_context_t *ctx, int *hidden)
+{
+ int err;
+
+ *hidden = 0;
+
+ if (vhd_type_dynamic(ctx) && vhd_creator_tapdisk(ctx) &&
+ (ctx->footer.crtr_ver == VHD_VERSION(0, 1) ||
+ ctx->footer.crtr_ver == VHD_VERSION(1, 1))) {
+ vhd_footer_t copy;
+
+ err = vhd_read_footer_at(ctx, &copy, 0);
+ if (err) {
+ VHDLOG("error reading backup footer of %s: %d\n",
+ ctx->file, err);
+ return err;
+ }
+ *hidden = copy.hidden;
+ } else
+ *hidden = ctx->footer.hidden;
+
+ return 0;
+}
+
+int
+vhd_chain_depth(vhd_context_t *ctx, int *depth)
+{
+ char *file;
+ int err, cnt;
+ vhd_context_t vhd, *cur;
+
+ err = 0;
+ cnt = 0;
+ *depth = 0;
+ file = NULL;
+ cur = ctx;
+
+ for (;;) {
+ cnt++;
+
+ if (cur->footer.type != HD_TYPE_DIFF)
+ break;
+
+ if (vhd_parent_raw(cur)) {
+ cnt++;
+ break;
+ }
+
+ err = vhd_parent_locator_get(cur, &file);
+ if (err) {
+ file = NULL;
+ break;
+ }
+
+ if (cur != ctx) {
+ vhd_close(cur);
+ cur = NULL;
+ }
+
+ err = vhd_open(&vhd, file, VHD_OPEN_RDONLY);
+ if (err)
+ break;
+
+ cur = &vhd;
+ free(file);
+ file = NULL;
+ }
+
+ free(file);
+ if (cur && cur != ctx)
+ vhd_close(cur);
+
+ if (!err)
+ *depth = cnt;
+
+ return err;
+}
+
+int
+vhd_batmap_test(vhd_context_t *ctx, vhd_batmap_t *batmap, uint32_t block)
+{
+ if (!vhd_has_batmap(ctx) || !batmap->map)
+ return 0;
+
+ if (block >= (batmap->header.batmap_size << (VHD_SECTOR_SHIFT + 3)))
+ return 0;
+
+ return test_bit(batmap->map, block);
+}
+
+void
+vhd_batmap_set(vhd_context_t *ctx, vhd_batmap_t *batmap, uint32_t block)
+{
+ if (!vhd_has_batmap(ctx) || !batmap->map)
+ return;
+
+ if (block >= (batmap->header.batmap_size << (VHD_SECTOR_SHIFT + 3)))
+ return;
+
+ set_bit(batmap->map, block);
+}
+
+void
+vhd_batmap_clear(vhd_context_t *ctx, vhd_batmap_t *batmap, uint32_t block)
+{
+ if (!vhd_has_batmap(ctx) || !batmap->map)
+ return;
+
+ if (block >= (batmap->header.batmap_size << (VHD_SECTOR_SHIFT + 3)))
+ return;
+
+ clear_bit(batmap->map, block);
+}
+
+int
+vhd_bitmap_test(vhd_context_t *ctx, char *map, uint32_t block)
+{
+ if (vhd_creator_tapdisk(ctx) &&
+ ctx->footer.crtr_ver == 0x00000001)
+ return old_test_bit(map, block);
+
+ return test_bit(map, block);
+}
+
+void
+vhd_bitmap_set(vhd_context_t *ctx, char *map, uint32_t block)
+{
+ if (vhd_creator_tapdisk(ctx) &&
+ ctx->footer.crtr_ver == 0x00000001)
+ return old_set_bit(map, block);
+
+ return set_bit(map, block);
+}
+
+void
+vhd_bitmap_clear(vhd_context_t *ctx, char *map, uint32_t block)
+{
+ if (vhd_creator_tapdisk(ctx) &&
+ ctx->footer.crtr_ver == 0x00000001)
+ return old_clear_bit(map, block);
+
+ return clear_bit(map, block);
+}
+
+/*
+ * returns absolute offset of the first
+ * byte of the file which is not vhd metadata
+ */
+int
+vhd_end_of_headers(vhd_context_t *ctx, off64_t *end)
+{
+ int err, i, n;
+ uint32_t bat_bytes;
+ off64_t eom, bat_end;
+ vhd_parent_locator_t *loc;
+
+ *end = 0;
+
+ if (!vhd_type_dynamic(ctx))
+ return 0;
+
+ eom = ctx->footer.data_offset + sizeof(vhd_header_t);
+
+ bat_bytes = vhd_bytes_padded(ctx->header.max_bat_size * sizeof(uint32_t));
+ bat_end = ctx->header.table_offset + bat_bytes;
+
+ eom = MAX(eom, bat_end);
+
+ if (vhd_has_batmap(ctx)) {
+ off64_t hdr_end, hdr_secs, map_end, map_secs;
+
+ err = vhd_get_batmap(ctx);
+ if (err)
+ return err;
+
+ hdr_secs = secs_round_up_no_zero(sizeof(vhd_batmap_header_t));
+ err = vhd_batmap_header_offset(ctx, &hdr_end);
+ if (err)
+ return err;
+
+ hdr_end += vhd_sectors_to_bytes(hdr_secs);
+ eom = MAX(eom, hdr_end);
+
+ map_secs = ctx->batmap.header.batmap_size;
+ map_end = (ctx->batmap.header.batmap_offset +
+ vhd_sectors_to_bytes(map_secs));
+ eom = MAX(eom, map_end);
+ }
+
+ /* parent locators */
+ n = sizeof(ctx->header.loc) / sizeof(vhd_parent_locator_t);
+
+ for (i = 0; i < n; i++) {
+ off64_t loc_end;
+
+ loc = &ctx->header.loc[i];
+ if (loc->code == PLAT_CODE_NONE)
+ continue;
+
+ loc_end = loc->data_offset + vhd_parent_locator_size(loc);
+ eom = MAX(eom, loc_end);
+ }
+
+ *end = eom;
+ return 0;
+}
+
+int
+vhd_end_of_data(vhd_context_t *ctx, off64_t *end)
+{
+ int i, err;
+ off64_t max;
+ uint64_t blk;
+
+ if (!vhd_type_dynamic(ctx)) {
+ err = vhd_seek(ctx, 0, SEEK_END);
+ if (err)
+ return err;
+
+ max = vhd_position(ctx);
+ if (max == (off64_t)-1)
+ return -errno;
+
+ *end = max - sizeof(vhd_footer_t);
+ return 0;
+ }
+
+ err = vhd_end_of_headers(ctx, &max);
+ if (err)
+ return err;
+
+ err = vhd_get_bat(ctx);
+ if (err)
+ return err;
+
+ max >>= VHD_SECTOR_SHIFT;
+
+ for (i = 0; i < ctx->bat.entries; i++) {
+ blk = ctx->bat.bat[i];
+
+ if (blk != DD_BLK_UNUSED) {
+ blk += ctx->spb + ctx->bm_secs;
+ max = MAX(blk, max);
+ }
+ }
+
+ *end = vhd_sectors_to_bytes(max);
+ return 0;
+}
+
+uint32_t
+vhd_time(time_t time)
+{
+ struct tm tm;
+ time_t micro_epoch;
+
+ memset(&tm, 0, sizeof(struct tm));
+ tm.tm_year = 100;
+ tm.tm_mon = 0;
+ tm.tm_mday = 1;
+ micro_epoch = mktime(&tm);
+
+ return (uint32_t)(time - micro_epoch);
+}
+
+/*
+ * Stringify the VHD timestamp for printing.
+ * As with ctime_r, target must be >=26 bytes.
+ */
+size_t
+vhd_time_to_string(uint32_t timestamp, char *target)
+{
+ char *cr;
+ struct tm tm;
+ time_t t1, t2;
+
+ memset(&tm, 0, sizeof(struct tm));
+
+ /* VHD uses an epoch of 12:00AM, Jan 1, 2000. */
+ /* Need to adjust this to the expected epoch of 1970. */
+ tm.tm_year = 100;
+ tm.tm_mon = 0;
+ tm.tm_mday = 1;
+
+ t1 = mktime(&tm);
+ t2 = t1 + (time_t)timestamp;
+ ctime_r(&t2, target);
+
+ /* handle mad ctime_r newline appending. */
+ if ((cr = strchr(target, '\n')) != NULL)
+ *cr = '\0';
+
+ return (strlen(target));
+}
+
+/*
+ * nabbed from vhd specs.
+ */
+uint32_t
+vhd_chs(uint64_t size)
+{
+ uint32_t secs, cylinders, heads, spt, cth;
+
+ secs = secs_round_up_no_zero(size);
+
+ if (secs > 65535 * 16 * 255)
+ secs = 65535 * 16 * 255;
+
+ if (secs >= 65535 * 16 * 63) {
+ spt = 255;
+ cth = secs / spt;
+ heads = 16;
+ } else {
+ spt = 17;
+ cth = secs / spt;
+ heads = (cth + 1023) / 1024;
+
+ if (heads < 4)
+ heads = 4;
+
+ if (cth >= (heads * 1024) || heads > 16) {
+ spt = 31;
+ cth = secs / spt;
+ heads = 16;
+ }
+
+ if (cth >= heads * 1024) {
+ spt = 63;
+ cth = secs / spt;
+ heads = 16;
+ }
+ }
+
+ cylinders = cth / heads;
+
+ return GEOM_ENCODE(cylinders, heads, spt);
+}
+
+int
+vhd_get_footer(vhd_context_t *ctx)
+{
+ if (!vhd_validate_footer(&ctx->footer))
+ return 0;
+
+ return vhd_read_footer(ctx, &ctx->footer);
+}
+
+int
+vhd_get_header(vhd_context_t *ctx)
+{
+ if (!vhd_type_dynamic(ctx))
+ return -EINVAL;
+
+ if (!vhd_validate_header(&ctx->header))
+ return 0;
+
+ return vhd_read_header(ctx, &ctx->header);
+}
+
+int
+vhd_get_bat(vhd_context_t *ctx)
+{
+ if (!vhd_type_dynamic(ctx))
+ return -EINVAL;
+
+ if (!vhd_validate_bat(&ctx->bat))
+ return 0;
+
+ vhd_put_bat(ctx);
+ return vhd_read_bat(ctx, &ctx->bat);
+}
+
+int
+vhd_get_batmap(vhd_context_t *ctx)
+{
+ if (!vhd_has_batmap(ctx))
+ return -EINVAL;
+
+ if (!vhd_validate_batmap(&ctx->batmap))
+ return 0;
+
+ vhd_put_batmap(ctx);
+ return vhd_read_batmap(ctx, &ctx->batmap);
+}
+
+void
+vhd_put_footer(vhd_context_t *ctx)
+{
+ memset(&ctx->footer, 0, sizeof(vhd_footer_t));
+}
+
+void
+vhd_put_header(vhd_context_t *ctx)
+{
+ memset(&ctx->header, 0, sizeof(vhd_header_t));
+}
+
+void
+vhd_put_bat(vhd_context_t *ctx)
+{
+ if (!vhd_type_dynamic(ctx))
+ return;
+
+ free(ctx->bat.bat);
+ memset(&ctx->bat, 0, sizeof(vhd_bat_t));
+}
+
+void
+vhd_put_batmap(vhd_context_t *ctx)
+{
+ if (!vhd_type_dynamic(ctx))
+ return;
+
+ if (!vhd_has_batmap(ctx))
+ return;
+
+ free(ctx->batmap.map);
+ memset(&ctx->batmap, 0, sizeof(vhd_batmap_t));
+}
+
+/*
+ * look for 511 byte footer at end of file
+ */
+int
+vhd_read_short_footer(vhd_context_t *ctx, vhd_footer_t *footer)
+{
+ int err;
+ char *buf;
+ off64_t eof;
+
+ buf = NULL;
+
+ err = vhd_seek(ctx, 0, SEEK_END);
+ if (err)
+ goto out;
+
+ eof = vhd_position(ctx);
+ if (eof == (off64_t)-1) {
+ err = -errno;
+ goto out;
+ }
+
+ err = vhd_seek(ctx, eof - 511, SEEK_SET);
+ if (err)
+ goto out;
+
+ err = posix_memalign((void **)&buf,
+ VHD_SECTOR_SIZE, sizeof(vhd_footer_t));
+ if (err) {
+ buf = NULL;
+ err = -err;
+ goto out;
+ }
+
+ memset(buf, 0, sizeof(vhd_footer_t));
+
+ /*
+ * expecting short read here
+ */
+ vhd_read(ctx, buf, sizeof(vhd_footer_t));
+
+ memcpy(footer, buf, sizeof(vhd_footer_t));
+
+ vhd_footer_in(footer);
+ err = vhd_validate_footer(footer);
+
+out:
+ if (err)
+ VHDLOG("%s: failed reading short footer: %d\n",
+ ctx->file, err);
+ free(buf);
+ return err;
+}
+
+int
+vhd_read_footer_at(vhd_context_t *ctx, vhd_footer_t *footer, off64_t off)
+{
+ int err;
+ char *buf;
+
+ buf = NULL;
+
+ err = vhd_seek(ctx, off, SEEK_SET);
+ if (err)
+ goto out;
+
+ err = posix_memalign((void **)&buf,
+ VHD_SECTOR_SIZE, sizeof(vhd_footer_t));
+ if (err) {
+ buf = NULL;
+ err = -err;
+ goto out;
+ }
+
+ err = vhd_read(ctx, buf, sizeof(vhd_footer_t));
+ if (err)
+ goto out;
+
+ memcpy(footer, buf, sizeof(vhd_footer_t));
+
+ vhd_footer_in(footer);
+ err = vhd_validate_footer(footer);
+
+out:
+ if (err)
+ VHDLOG("%s: reading footer at 0x%08"PRIx64" failed: %d\n",
+ ctx->file, off, err);
+ free(buf);
+ return err;
+}
+
+int
+vhd_read_footer(vhd_context_t *ctx, vhd_footer_t *footer)
+{
+ int err;
+ off64_t off;
+
+ err = vhd_seek(ctx, 0, SEEK_END);
+ if (err)
+ return err;
+
+ off = vhd_position(ctx);
+ if (off == (off64_t)-1)
+ return -errno;
+
+ err = vhd_read_footer_at(ctx, footer, off - 512);
+ if (err != -EINVAL)
+ return err;
+
+ err = vhd_read_short_footer(ctx, footer);
+ if (err != -EINVAL)
+ return err;
+
+ if (ctx->oflags & VHD_OPEN_STRICT)
+ return -EINVAL;
+
+ return vhd_read_footer_at(ctx, footer, 0);
+}
+
+int
+vhd_read_header_at(vhd_context_t *ctx, vhd_header_t *header, off64_t off)
+{
+ int err;
+ char *buf;
+
+ buf = NULL;
+
+ if (!vhd_type_dynamic(ctx)) {
+ err = -EINVAL;
+ goto out;
+ }
+
+ err = vhd_seek(ctx, off, SEEK_SET);
+ if (err)
+ goto out;
+
+ err = posix_memalign((void **)&buf,
+ VHD_SECTOR_SIZE, sizeof(vhd_header_t));
+ if (err) {
+ buf = NULL;
+ err = -err;
+ goto out;
+ }
+
+ err = vhd_read(ctx, buf, sizeof(vhd_header_t));
+ if (err)
+ goto out;
+
+ memcpy(header, buf, sizeof(vhd_header_t));
+
+ vhd_header_in(header);
+ err = vhd_validate_header(header);
+
+out:
+ if (err)
+ VHDLOG("%s: reading header at 0x%08"PRIx64" failed: %d\n",
+ ctx->file, off, err);
+ free(buf);
+ return err;
+}
+
+int
+vhd_read_header(vhd_context_t *ctx, vhd_header_t *header)
+{
+ int err;
+ off64_t off;
+
+ if (!vhd_type_dynamic(ctx)) {
+ VHDLOG("%s is not dynamic!\n", ctx->file);
+ return -EINVAL;
+ }
+
+ off = ctx->footer.data_offset;
+ return vhd_read_header_at(ctx, header, off);
+}
+
+int
+vhd_read_bat(vhd_context_t *ctx, vhd_bat_t *bat)
+{
+ int err;
+ char *buf;
+ off64_t off;
+ size_t size;
+
+ buf = NULL;
+
+ if (!vhd_type_dynamic(ctx)) {
+ err = -EINVAL;
+ goto fail;
+ }
+
+ off = ctx->header.table_offset;
+ size = vhd_bytes_padded(ctx->header.max_bat_size * sizeof(uint32_t));
+
+ err = posix_memalign((void **)&buf, VHD_SECTOR_SIZE, size);
+ if (err) {
+ buf = NULL;
+ err = -err;
+ goto fail;
+ }
+
+ err = vhd_seek(ctx, off, SEEK_SET);
+ if (err)
+ goto fail;
+
+ err = vhd_read(ctx, buf, size);
+ if (err)
+ goto fail;
+
+ bat->spb = ctx->header.block_size >> VHD_SECTOR_SHIFT;
+ bat->entries = ctx->header.max_bat_size;
+ bat->bat = (uint32_t *)buf;
+
+ vhd_bat_in(bat);
+
+ return 0;
+
+fail:
+ free(buf);
+ memset(bat, 0, sizeof(vhd_bat_t));
+ VHDLOG("%s: failed to read bat: %d\n", ctx->file, err);
+ return err;
+}
+
+static int
+vhd_read_batmap_header(vhd_context_t *ctx, vhd_batmap_t *batmap)
+{
+ int err;
+ char *buf;
+ off64_t off;
+ size_t size;
+
+ buf = NULL;
+
+ err = vhd_batmap_header_offset(ctx, &off);
+ if (err)
+ goto fail;
+
+ err = vhd_seek(ctx, off, SEEK_SET);
+ if (err)
+ goto fail;
+
+ size = vhd_bytes_padded(sizeof(vhd_batmap_header_t));
+ err = posix_memalign((void **)&buf, VHD_SECTOR_SIZE, size);
+ if (err) {
+ buf = NULL;
+ err = -err;
+ goto fail;
+ }
+
+ err = vhd_read(ctx, buf, size);
+ if (err)
+ goto fail;
+
+ memcpy(&batmap->header, buf, sizeof(vhd_batmap_header_t));
+ free(buf);
+ buf = NULL;
+
+ vhd_batmap_header_in(batmap);
+
+ return 0;
+
+fail:
+ free(buf);
+ memset(&batmap->header, 0, sizeof(vhd_batmap_header_t));
+ VHDLOG("%s: failed to read batmap header: %d\n", ctx->file, err);
+ return err;
+}
+
+static int
+vhd_read_batmap_map(vhd_context_t *ctx, vhd_batmap_t *batmap)
+{
+ int err;
+ char *buf;
+ off64_t off;
+ size_t map_size;
+
+ map_size = vhd_sectors_to_bytes(batmap->header.batmap_size);
+
+ err = posix_memalign((void **)&buf, VHD_SECTOR_SIZE, map_size);
+ if (err) {
+ buf = NULL;
+ err = -err;
+ goto fail;
+ }
+
+ off = batmap->header.batmap_offset;
+ err = vhd_seek(ctx, off, SEEK_SET);
+ if (err)
+ goto fail;
+
+ err = vhd_read(ctx, buf, map_size);
+ if (err)
+ goto fail;
+
+ batmap->map = buf;
+ return 0;
+
+fail:
+ free(buf);
+ batmap->map = NULL;
+ VHDLOG("%s: failed to read batmap: %d\n", ctx->file, err);
+ return err;
+}
+
+int
+vhd_read_batmap(vhd_context_t *ctx, vhd_batmap_t *batmap)
+{
+ int err;
+
+ if (!vhd_has_batmap(ctx))
+ return -EINVAL;
+
+ memset(batmap, 0, sizeof(vhd_batmap_t));
+
+ err = vhd_read_batmap_header(ctx, batmap);
+ if (err)
+ return err;
+
+ err = vhd_validate_batmap_header(batmap);
+ if (err)
+ return err;
+
+ err = vhd_read_batmap_map(ctx, batmap);
+ if (err)
+ return err;
+
+ err = vhd_validate_batmap(batmap);
+ if (err)
+ goto fail;
+
+ return 0;
+
+fail:
+ free(batmap->map);
+ memset(batmap, 0, sizeof(vhd_batmap_t));
+ return err;
+}
+
+int
+vhd_has_batmap(vhd_context_t *ctx)
+{
+ if (!vhd_type_dynamic(ctx))
+ return 0;
+
+ if (!vhd_creator_tapdisk(ctx))
+ return 0;
+
+ if (ctx->footer.crtr_ver <= VHD_VERSION(0, 1))
+ return 0;
+
+ if (ctx->footer.crtr_ver >= VHD_VERSION(1, 2))
+ return 1;
+
+ /*
+ * VHDs of version 1.1 probably have a batmap, but may not
+ * if they were updated from version 0.1 via vhd-update.
+ */
+ if (!vhd_validate_batmap_header(&ctx->batmap))
+ return 1;
+
+ if (vhd_read_batmap_header(ctx, &ctx->batmap))
+ return 0;
+
+ return (!vhd_validate_batmap_header(&ctx->batmap));
+}
+
+/*
+ * Is this a block device (with a fixed size)? This affects whether the file
+ * can be truncated and where the footer is written for VHDs.
+ */
+int
+vhd_test_file_fixed(const char *file, int *is_block)
+{
+ int err;
+ struct stat stats;
+
+ err = stat(file, &stats);
+ if (err == -1)
+ return -errno;
+
+ *is_block = !!(S_ISBLK(stats.st_mode));
+ return err;
+}
+
+int
+vhd_find_parent(vhd_context_t *ctx, const char *parent, char **_location)
+{
+ int err;
+ char *location, *cpath, *cdir, *path;
+
+ err = 0;
+ path = NULL;
+ cpath = NULL;
+ location = NULL;
+ *_location = NULL;
+
+ if (!parent)
+ return -EINVAL;
+
+ if (parent[0] == '/') {
+ if (!access(parent, R_OK)) {
+ path = strdup(parent);
+ if (!path)
+ return -ENOMEM;
+ *_location = path;
+ return 0;
+ }
+ }
+
+ /* check parent path relative to child's directory */
+ cpath = realpath(ctx->file, NULL);
+ if (!cpath) {
+ err = -errno;
+ goto out;
+ }
+
+ cdir = dirname(cpath);
+ if (asprintf(&location, "%s/%s", cdir, parent) == -1) {
+ err = -errno;
+ location = NULL;
+ goto out;
+ }
+
+ if (!access(location, R_OK)) {
+ path = realpath(location, NULL);
+ if (path) {
+ *_location = path;
+ return 0;
+ }
+ }
+ err = -errno;
+
+out:
+ free(location);
+ free(cpath);
+ return err;
+}
+
+static int
+vhd_macx_encode_location(char *name, char **out, int *outlen)
+{
+ iconv_t cd;
+ int len, err;
+ size_t ibl, obl;
+ char *uri, *urip, *uri_utf8, *uri_utf8p, *ret;
+
+ err = 0;
+ ret = NULL;
+ *out = NULL;
+ *outlen = 0;
+ len = strlen(name) + strlen("file://");
+
+ ibl = len;
+ obl = len;
+
+ uri = urip = malloc(ibl + 1);
+ uri_utf8 = uri_utf8p = malloc(obl);
+
+ if (!uri || !uri_utf8)
+ return -ENOMEM;
+
+ cd = iconv_open("UTF-8", "ASCII");
+ if (cd == (iconv_t)-1) {
+ err = -errno;
+ goto out;
+ }
+
+ sprintf(uri, "file://%s", name);
+
+ if (iconv(cd, &urip, &ibl, &uri_utf8p, &obl) == (size_t)-1 ||
+ ibl || obl) {
+ err = (errno ? -errno : -EIO);
+ goto out;
+ }
+
+ ret = malloc(len);
+ if (!ret) {
+ err = -ENOMEM;
+ goto out;
+ }
+
+ memcpy(ret, uri_utf8, len);
+ *outlen = len;
+ *out = ret;
+
+ out:
+ free(uri);
+ free(uri_utf8);
+ if (cd != (iconv_t)-1)
+ iconv_close(cd);
+
+ return err;
+}
+
+static int
+vhd_w2u_encode_location(char *name, char **out, int *outlen)
+{
+ iconv_t cd;
+ int len, err;
+ size_t ibl, obl;
+ char *uri, *urip, *uri_utf16, *uri_utf16p, *tmp, *ret;
+
+ err = 0;
+ ret = NULL;
+ *out = NULL;
+ *outlen = 0;
+ cd = (iconv_t) -1;
+
+ /*
+ * MICROSOFT_COMPAT
+ * relative paths must start with ".\"
+ */
+ if (name[0] != '/') {
+ tmp = strstr(name, "./");
+ if (tmp == name)
+ tmp += strlen("./");
+ else
+ tmp = name;
+
+ err = asprintf(&uri, ".\\%s", tmp);
+ } else
+ err = asprintf(&uri, "%s", name);
+
+ if (err == -1)
+ return -ENOMEM;
+
+ tmp = uri;
+ while (*tmp != '\0') {
+ if (*tmp == '/')
+ *tmp = '\\';
+ tmp++;
+ }
+
+ len = strlen(uri);
+ ibl = len;
+ obl = len * 2;
+ urip = uri;
+
+ uri_utf16 = uri_utf16p = malloc(obl);
+ if (!uri_utf16) {
+ err = -ENOMEM;
+ goto out;
+ }
+
+ /*
+ * MICROSOFT_COMPAT
+ * little endian unicode here
+ */
+ cd = iconv_open("UTF-16LE", "ASCII");
+ if (cd == (iconv_t)-1) {
+ err = -errno;
+ goto out;
+ }
+
+ if (iconv(cd, &urip, &ibl, &uri_utf16p, &obl) == (size_t)-1 ||
+ ibl || obl) {
+ err = (errno ? -errno : -EIO);
+ goto out;
+ }
+
+ len = len * 2;
+ ret = malloc(len);
+ if (!ret) {
+ err = -ENOMEM;
+ goto out;
+ }
+
+ memcpy(ret, uri_utf16, len);
+ *outlen = len;
+ *out = ret;
+ err = 0;
+
+ out:
+ free(uri);
+ free(uri_utf16);
+ if (cd != (iconv_t)-1)
+ iconv_close(cd);
+
+ return err;
+}
+
+static char *
+vhd_macx_decode_location(char *in, char *out, int len)
+{
+ iconv_t cd;
+ char *name;
+ size_t ibl, obl;
+
+ name = out;
+ ibl = obl = len;
+
+ cd = iconv_open("ASCII", "UTF-8");
+ if (cd == (iconv_t)-1)
+ return NULL;
+
+ if (iconv(cd, &in, &ibl, &out, &obl) == (size_t)-1 || ibl)
+ return NULL;
+
+ iconv_close(cd);
+ *out = '\0';
+
+ if (strstr(name, "file://") != name)
+ return NULL;
+
+ name += strlen("file://");
+
+ return strdup(name);
+}
+
+static char *
+vhd_w2u_decode_location(char *in, char *out, int len, char *utf_type)
+{
+ iconv_t cd;
+ char *name, *tmp;
+ size_t ibl, obl;
+
+ tmp = name = out;
+ ibl = obl = len;
+
+ cd = iconv_open("ASCII", utf_type);
+ if (cd == (iconv_t)-1)
+ return NULL;
+
+ if (iconv(cd, &in, &ibl, &out, &obl) == (size_t)-1 || ibl)
+ return NULL;
+
+ iconv_close(cd);
+ *out = '\0';
+
+ /* TODO: spaces */
+ while (tmp != out) {
+ if (*tmp == '\\')
+ *tmp = '/';
+ tmp++;
+ }
+
+ if (strstr(name, "C:") == name || strstr(name, "c:") == name)
+ name += strlen("c:");
+
+ return strdup(name);
+}
+
+int
+vhd_header_decode_parent(vhd_context_t *ctx, vhd_header_t *header, char **buf)
+{
+ char *code, out[512];
+
+ if (vhd_creator_tapdisk(ctx) &&
+ ctx->footer.crtr_ver == VHD_VERSION(0, 1))
+ code = UTF_16;
+ else
+ code = UTF_16BE;
+
+ *buf = vhd_w2u_decode_location(header->prt_name, out, 512, code);
+ return (*buf == NULL ? -EINVAL : 0);
+}
+
+int
+vhd_parent_locator_read(vhd_context_t *ctx,
+ vhd_parent_locator_t *loc, char **parent)
+{
+ int err, size;
+ char *raw, *out, *name;
+
+ raw = NULL;
+ out = NULL;
+ name = NULL;
+ *parent = NULL;
+
+ if (ctx->footer.type != HD_TYPE_DIFF) {
+ err = -EINVAL;
+ goto out;
+ }
+
+ switch (loc->code) {
+ case PLAT_CODE_MACX:
+ case PLAT_CODE_W2KU:
+ case PLAT_CODE_W2RU:
+ break;
+ default:
+ err = -EINVAL;
+ goto out;
+ }
+
+ err = vhd_seek(ctx, loc->data_offset, SEEK_SET);
+ if (err)
+ goto out;
+
+ size = vhd_parent_locator_size(loc);
+ if (size <= 0) {
+ err = -EINVAL;
+ goto out;
+ }
+
+ err = posix_memalign((void **)&raw, VHD_SECTOR_SIZE, size);
+ if (err) {
+ raw = NULL;
+ err = -err;
+ goto out;
+ }
+
+ err = vhd_read(ctx, raw, size);
+ if (err)
+ goto out;
+
+ out = malloc(loc->data_len + 1);
+ if (!out) {
+ err = -ENOMEM;
+ goto out;
+ }
+
+ switch (loc->code) {
+ case PLAT_CODE_MACX:
+ name = vhd_macx_decode_location(raw, out, loc->data_len);
+ break;
+ case PLAT_CODE_W2KU:
+ case PLAT_CODE_W2RU:
+ name = vhd_w2u_decode_location(raw, out,
+ loc->data_len, UTF_16LE);
+ break;
+ }
+
+ if (!name) {
+ err = -EINVAL;
+ goto out;
+ }
+
+ err = 0;
+ *parent = name;
+
+out:
+ free(raw);
+ free(out);
+
+ if (err) {
+ VHDLOG("%s: error reading parent locator: %d\n",
+ ctx->file, err);
+ VHDLOG("%s: locator: code %u, space 0x%x, len 0x%x, "
+ "off 0x%"PRIx64"\n", ctx->file, loc->code, loc->data_space,
+ loc->data_len, loc->data_offset);
+ }
+
+ return err;
+}
+
+int
+vhd_parent_locator_get(vhd_context_t *ctx, char **parent)
+{
+ int i, n, err;
+ char *name, *location;
+ vhd_parent_locator_t *loc;
+
+ err = 0;
+ *parent = NULL;
+
+ if (ctx->footer.type != HD_TYPE_DIFF)
+ return -EINVAL;
+
+ n = vhd_parent_locator_count(ctx);
+ for (i = 0; i < n; i++) {
+ loc = ctx->header.loc + i;
+ err = vhd_parent_locator_read(ctx, loc, &name);
+ if (err)
+ continue;
+
+ err = vhd_find_parent(ctx, name, &location);
+ if (err)
+ VHDLOG("%s: couldn't find parent %s (%d)\n",
+ ctx->file, name, err);
+ free(name);
+
+ if (!err) {
+ *parent = location;
+ return 0;
+ }
+ }
+
+ return err;
+}
+
+int
+vhd_parent_locator_write_at(vhd_context_t *ctx,
+ const char *parent, off64_t off, uint32_t code,
+ size_t max_bytes, vhd_parent_locator_t *loc)
+{
+ struct stat stats;
+ int err, len, size;
+ char *absolute_path, *relative_path, *encoded, *block;
+
+ memset(loc, 0, sizeof(vhd_parent_locator_t));
+
+ if (ctx->footer.type != HD_TYPE_DIFF)
+ return -EINVAL;
+
+ absolute_path = NULL;
+ relative_path = NULL;
+ encoded = NULL;
+ block = NULL;
+ size = 0;
+ len = 0;
+
+ switch (code) {
+ case PLAT_CODE_MACX:
+ case PLAT_CODE_W2KU:
+ case PLAT_CODE_W2RU:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ absolute_path = realpath(parent, NULL);
+ if (!absolute_path) {
+ err = -errno;
+ goto out;
+ }
+
+ err = stat(absolute_path, &stats);
+ if (err) {
+ err = -errno;
+ goto out;
+ }
+
+ if (!S_ISREG(stats.st_mode) && !S_ISBLK(stats.st_mode)) {
+ err = -EINVAL;
+ goto out;
+ }
+
+ relative_path = relative_path_to(ctx->file, absolute_path, &err);
+ if (!relative_path || err) {
+ err = (err ? err : -EINVAL);
+ goto out;
+ }
+
+ switch (code) {
+ case PLAT_CODE_MACX:
+ err = vhd_macx_encode_location(relative_path, &encoded, &len);
+ break;
+ case PLAT_CODE_W2KU:
+ case PLAT_CODE_W2RU:
+ err = vhd_w2u_encode_location(relative_path, &encoded, &len);
+ break;
+ default:
+ err = -EINVAL;
+ }
+
+ if (err)
+ goto out;
+
+ err = vhd_seek(ctx, off, SEEK_SET);
+ if (err)
+ goto out;
+
+ size = vhd_bytes_padded(len);
+
+ if (max_bytes && size > max_bytes) {
+ err = -ENAMETOOLONG;
+ goto out;
+ }
+
+ err = posix_memalign((void **)&block, VHD_SECTOR_SIZE, size);
+ if (err) {
+ block = NULL;
+ err = -err;
+ goto out;
+ }
+
+ memset(block, 0, size);
+ memcpy(block, encoded, len);
+
+ err = vhd_write(ctx, block, size);
+ if (err)
+ goto out;
+
+ err = 0;
+
+out:
+ free(absolute_path);
+ free(relative_path);
+ free(encoded);
+ free(block);
+
+ if (!err) {
+ loc->res = 0;
+ loc->code = code;
+ loc->data_len = len;
+ /*
+ * write number of bytes ('size') instead of number of sectors
+ * into loc->data_space to be compatible with MSFT, even though
+ * this goes against the specs
+ */
+ loc->data_space = size;
+ loc->data_offset = off;
+ }
+
+ return err;
+}
+
+static int
+vhd_footer_offset_at_eof(vhd_context_t *ctx, off64_t *off)
+{
+ int err;
+ if ((err = vhd_seek(ctx, 0, SEEK_END)))
+ return errno;
+ *off = vhd_position(ctx) - sizeof(vhd_footer_t);
+ return 0;
+}
+
+int
+vhd_read_bitmap(vhd_context_t *ctx, uint32_t block, char **bufp)
+{
+ int err;
+ char *buf;
+ size_t size;
+ off64_t off;
+ uint64_t blk;
+
+ buf = NULL;
+ *bufp = NULL;
+
+ if (!vhd_type_dynamic(ctx))
+ return -EINVAL;
+
+ err = vhd_get_bat(ctx);
+ if (err)
+ return err;
+
+ if (block >= ctx->bat.entries)
+ return -ERANGE;
+
+ blk = ctx->bat.bat[block];
+ if (blk == DD_BLK_UNUSED)
+ return -EINVAL;
+
+ off = vhd_sectors_to_bytes(blk);
+ size = vhd_bytes_padded(ctx->spb >> 3);
+
+ err = vhd_seek(ctx, off, SEEK_SET);
+ if (err)
+ return err;
+
+ err = posix_memalign((void **)&buf, VHD_SECTOR_SIZE, size);
+ if (err)
+ return -err;
+
+ err = vhd_read(ctx, buf, size);
+ if (err)
+ goto fail;
+
+ *bufp = buf;
+ return 0;
+
+fail:
+ free(buf);
+ return err;
+}
+
+int
+vhd_read_block(vhd_context_t *ctx, uint32_t block, char **bufp)
+{
+ int err;
+ char *buf;
+ size_t size;
+ uint64_t blk;
+ off64_t end, off;
+
+ buf = NULL;
+ *bufp = NULL;
+
+ if (!vhd_type_dynamic(ctx))
+ return -EINVAL;
+
+ err = vhd_get_bat(ctx);
+ if (err)
+ return err;
+
+ if (block >= ctx->bat.entries)
+ return -ERANGE;
+
+ blk = ctx->bat.bat[block];
+ if (blk == DD_BLK_UNUSED)
+ return -EINVAL;
+
+ off = vhd_sectors_to_bytes(blk + ctx->bm_secs);
+ size = vhd_sectors_to_bytes(ctx->spb);
+
+ err = vhd_footer_offset_at_eof(ctx, &end);
+ if (err)
+ return err;
+
+ err = posix_memalign((void **)&buf, VHD_SECTOR_SIZE, size);
+ if (err) {
+ err = -err;
+ goto fail;
+ }
+
+ if (end < off + ctx->header.block_size) {
+ size = end - off;
+ memset(buf + size, 0, ctx->header.block_size - size);
+ }
+
+ err = vhd_seek(ctx, off, SEEK_SET);
+ if (err)
+ goto fail;
+
+ err = vhd_read(ctx, buf, size);
+ if (err)
+ goto fail;
+
+ *bufp = buf;
+ return 0;
+
+fail:
+ free(buf);
+ return err;
+}
+
+int
+vhd_write_footer_at(vhd_context_t *ctx, vhd_footer_t *footer, off64_t off)
+{
+ int err;
+ vhd_footer_t *f;
+
+ f = NULL;
+
+ err = posix_memalign((void **)&f,
+ VHD_SECTOR_SIZE, sizeof(vhd_footer_t));
+ if (err) {
+ f = NULL;
+ err = -err;
+ goto out;
+ }
+
+ memcpy(f, footer, sizeof(vhd_footer_t));
+ f->checksum = vhd_checksum_footer(f);
+
+ err = vhd_validate_footer(f);
+ if (err)
+ goto out;
+
+ err = vhd_seek(ctx, off, SEEK_SET);
+ if (err)
+ goto out;
+
+ vhd_footer_out(f);
+
+ err = vhd_write(ctx, f, sizeof(vhd_footer_t));
+
+out:
+ if (err)
+ VHDLOG("%s: failed writing footer at 0x%08"PRIx64": %d\n",
+ ctx->file, off, err);
+ free(f);
+ return err;
+}
+
+int
+vhd_write_footer(vhd_context_t *ctx, vhd_footer_t *footer)
+{
+ int err;
+ off64_t off;
+
+ if (ctx->is_block)
+ err = vhd_footer_offset_at_eof(ctx, &off);
+ else
+ err = vhd_end_of_data(ctx, &off);
+ if (err)
+ return err;
+
+ err = vhd_write_footer_at(ctx, footer, off);
+ if (err)
+ return err;
+
+ if (!vhd_type_dynamic(ctx))
+ return 0;
+
+ return vhd_write_footer_at(ctx, footer, 0);
+}
+
+int
+vhd_write_header_at(vhd_context_t *ctx, vhd_header_t *header, off64_t off)
+{
+ int err;
+ vhd_header_t *h;
+
+ h = NULL;
+
+ if (!vhd_type_dynamic(ctx)) {
+ err = -EINVAL;
+ goto out;
+ }
+
+ err = posix_memalign((void **)&h,
+ VHD_SECTOR_SIZE, sizeof(vhd_header_t));
+ if (err) {
+ h = NULL;
+ err = -err;
+ goto out;
+ }
+
+ memcpy(h, header, sizeof(vhd_header_t));
+
+ h->checksum = vhd_checksum_header(h);
+ err = vhd_validate_header(h);
+ if (err)
+ goto out;
+
+ vhd_header_out(h);
+
+ err = vhd_seek(ctx, off, SEEK_SET);
+ if (err)
+ goto out;
+
+ err = vhd_write(ctx, h, sizeof(vhd_header_t));
+
+out:
+ if (err)
+ VHDLOG("%s: failed writing header at 0x%08"PRIx64": %d\n",
+ ctx->file, off, err);
+ free(h);
+ return err;
+}
+
+int
+vhd_write_header(vhd_context_t *ctx, vhd_header_t *header)
+{
+ int err;
+ off64_t off;
+
+ if (!vhd_type_dynamic(ctx))
+ return -EINVAL;
+
+ off = ctx->footer.data_offset;
+ return vhd_write_header_at(ctx, header, off);
+}
+
+int
+vhd_write_bat(vhd_context_t *ctx, vhd_bat_t *bat)
+{
+ int err;
+ off64_t off;
+ vhd_bat_t b;
+ size_t size;
+
+ if (!vhd_type_dynamic(ctx))
+ return -EINVAL;
+
+ err = vhd_validate_bat(&ctx->bat);
+ if (err)
+ return err;
+
+ err = vhd_validate_bat(bat);
+ if (err)
+ return err;
+
+ memset(&b, 0, sizeof(vhd_bat_t));
+
+ off = ctx->header.table_offset;
+ size = vhd_bytes_padded(bat->entries * sizeof(uint32_t));
+
+ err = vhd_seek(ctx, off, SEEK_SET);
+ if (err)
+ return err;
+
+ err = posix_memalign((void **)&b.bat, VHD_SECTOR_SIZE, size);
+ if (err)
+ return -err;
+
+ memcpy(b.bat, bat->bat, size);
+ b.spb = bat->spb;
+ b.entries = bat->entries;
+ vhd_bat_out(&b);
+
+ err = vhd_write(ctx, b.bat, size);
+ free(b.bat);
+
+ return err;
+}
+
+int
+vhd_write_batmap(vhd_context_t *ctx, vhd_batmap_t *batmap)
+{
+ int err;
+ off64_t off;
+ vhd_batmap_t b;
+ char *buf, *map;
+ size_t size, map_size;
+
+ buf = NULL;
+ map = NULL;
+
+ if (!vhd_has_batmap(ctx)) {
+ err = -EINVAL;
+ goto out;
+ }
+
+ b.header = batmap->header;
+ b.map = batmap->map;
+
+ b.header.checksum = vhd_checksum_batmap(&b);
+ err = vhd_validate_batmap(&b);
+ if (err)
+ goto out;
+
+ off = b.header.batmap_offset;
+ map_size = vhd_sectors_to_bytes(b.header.batmap_size);
+
+ err = vhd_seek(ctx, off, SEEK_SET);
+ if (err)
+ goto out;
+
+ err = posix_memalign((void **)&map, VHD_SECTOR_SIZE, map_size);
+ if (err) {
+ map = NULL;
+ err = -err;
+ goto out;
+ }
+
+ memcpy(map, b.map, map_size);
+
+ err = vhd_write(ctx, map, map_size);
+ if (err)
+ goto out;
+
+ err = vhd_batmap_header_offset(ctx, &off);
+ if (err)
+ goto out;
+
+ size = vhd_bytes_padded(sizeof(vhd_batmap_header_t));
+
+ err = vhd_seek(ctx, off, SEEK_SET);
+ if (err)
+ goto out;
+
+ err = posix_memalign((void **)&buf, VHD_SECTOR_SIZE, size);
+ if (err) {
+ err = -err;
+ buf = NULL;
+ goto out;
+ }
+
+ vhd_batmap_header_out(&b);
+ memset(buf, 0, size);
+ memcpy(buf, &b.header, sizeof(vhd_batmap_header_t));
+
+ err = vhd_write(ctx, buf, size);
+
+out:
+ if (err)
+ VHDLOG("%s: failed writing batmap: %d\n", ctx->file, err);
+ free(buf);
+ free(map);
+ return 0;
+}
+
+int
+vhd_write_bitmap(vhd_context_t *ctx, uint32_t block, char *bitmap)
+{
+ int err;
+ off64_t off;
+ uint64_t blk;
+ size_t secs, size;
+
+ if (!vhd_type_dynamic(ctx))
+ return -EINVAL;
+
+ err = vhd_validate_bat(&ctx->bat);
+ if (err)
+ return err;
+
+ if (block >= ctx->bat.entries)
+ return -ERANGE;
+
+ if ((unsigned long)bitmap & (VHD_SECTOR_SIZE - 1))
+ return -EINVAL;
+
+ blk = ctx->bat.bat[block];
+ if (blk == DD_BLK_UNUSED)
+ return -EINVAL;
+
+ off = vhd_sectors_to_bytes(blk);
+ size = vhd_sectors_to_bytes(ctx->bm_secs);
+
+ err = vhd_seek(ctx, off, SEEK_SET);
+ if (err)
+ return err;
+
+ err = vhd_write(ctx, bitmap, size);
+ if (err)
+ return err;
+
+ return 0;
+}
+
+int
+vhd_write_block(vhd_context_t *ctx, uint32_t block, char *data)
+{
+ int err;
+ off64_t off;
+ size_t size;
+ uint64_t blk;
+
+ if (!vhd_type_dynamic(ctx))
+ return -EINVAL;
+
+ err = vhd_validate_bat(&ctx->bat);
+ if (err)
+ return err;
+
+ if (block >= ctx->bat.entries)
+ return -ERANGE;
+
+ if ((unsigned long)data & ~(VHD_SECTOR_SIZE -1))
+ return -EINVAL;
+
+ blk = ctx->bat.bat[block];
+ if (blk == DD_BLK_UNUSED)
+ return -EINVAL;
+
+ off = vhd_sectors_to_bytes(blk + ctx->bm_secs);
+ size = vhd_sectors_to_bytes(ctx->spb);
+
+ err = vhd_seek(ctx, off, SEEK_SET);
+ if (err)
+ return err;
+
+ err = vhd_write(ctx, data, size);
+ if (err)
+ return err;
+
+ return 0;
+}
+
+static inline int
+namedup(char **dup, const char *name)
+{
+ *dup = NULL;
+
+ if (strnlen(name, MAX_NAME_LEN) >= MAX_NAME_LEN)
+ return -ENAMETOOLONG;
+
+ *dup = strdup(name);
+ if (*dup == NULL)
+ return -ENOMEM;
+
+ return 0;
+}
+
+int
+vhd_seek(vhd_context_t *ctx, off64_t offset, int whence)
+{
+ off64_t off;
+
+ off = lseek64(ctx->fd, offset, whence);
+ if (off == (off64_t)-1) {
+ VHDLOG("%s: seek(0x%08"PRIx64", %d) failed: %d\n",
+ ctx->file, offset, whence, -errno);
+ return -errno;
+ }
+
+ return 0;
+}
+
+off64_t
+vhd_position(vhd_context_t *ctx)
+{
+ return lseek64(ctx->fd, 0, SEEK_CUR);
+}
+
+int
+vhd_read(vhd_context_t *ctx, void *buf, size_t size)
+{
+ size_t ret;
+
+ errno = 0;
+
+ ret = read(ctx->fd, buf, size);
+ if (ret == size)
+ return 0;
+
+ VHDLOG("%s: read of %zu returned %zd, errno: %d\n",
+ ctx->file, size, ret, -errno);
+
+ return (errno ? -errno : -EIO);
+}
+
+int
+vhd_write(vhd_context_t *ctx, void *buf, size_t size)
+{
+ size_t ret;
+
+ errno = 0;
+
+ ret = write(ctx->fd, buf, size);
+ if (ret == size)
+ return 0;
+
+ VHDLOG("%s: write of %zu returned %zd, errno: %d\n",
+ ctx->file, size, ret, -errno);
+
+ return (errno ? -errno : -EIO);
+}
+
+int
+vhd_offset(vhd_context_t *ctx, uint32_t sector, uint32_t *offset)
+{
+ int err;
+ uint32_t block;
+
+ if (!vhd_type_dynamic(ctx))
+ return sector;
+
+ err = vhd_get_bat(ctx);
+ if (err)
+ return err;
+
+ block = sector / ctx->spb;
+ if (ctx->bat.bat[block] == DD_BLK_UNUSED)
+ *offset = DD_BLK_UNUSED;
+ else
+ *offset = ctx->bat.bat[block] +
+ ctx->bm_secs + (sector % ctx->spb);
+
+ return 0;
+}
+
+int
+vhd_open_fast(vhd_context_t *ctx)
+{
+ int err;
+ char *buf;
+ size_t size;
+
+ size = sizeof(vhd_footer_t) + sizeof(vhd_header_t);
+ err = posix_memalign((void **)&buf, VHD_SECTOR_SIZE, size);
+ if (err) {
+ VHDLOG("failed allocating %s: %d\n", ctx->file, -err);
+ return -err;
+ }
+
+ err = vhd_read(ctx, buf, size);
+ if (err) {
+ VHDLOG("failed reading %s: %d\n", ctx->file, err);
+ goto out;
+ }
+
+ memcpy(&ctx->footer, buf, sizeof(vhd_footer_t));
+ vhd_footer_in(&ctx->footer);
+ err = vhd_validate_footer(&ctx->footer);
+ if (err)
+ goto out;
+
+ if (vhd_type_dynamic(ctx)) {
+ if (ctx->footer.data_offset != sizeof(vhd_footer_t))
+ err = vhd_read_header(ctx, &ctx->header);
+ else {
+ memcpy(&ctx->header,
+ buf + sizeof(vhd_footer_t),
+ sizeof(vhd_header_t));
+ vhd_header_in(&ctx->header);
+ err = vhd_validate_header(&ctx->header);
+ }
+
+ if (err)
+ goto out;
+
+ ctx->spb = ctx->header.block_size >> VHD_SECTOR_SHIFT;
+ ctx->bm_secs = secs_round_up_no_zero(ctx->spb >> 3);
+ }
+
+out:
+ free(buf);
+ return err;
+}
+
+int
+vhd_open(vhd_context_t *ctx, const char *file, int flags)
+{
+ int err, oflags;
+
+ if (flags & VHD_OPEN_STRICT)
+ vhd_flag_clear(flags, VHD_OPEN_FAST);
+
+ memset(ctx, 0, sizeof(vhd_context_t));
+ ctx->fd = -1;
+ ctx->oflags = flags;
+
+ err = namedup(&ctx->file, file);
+ if (err)
+ return err;
+
+ oflags = O_DIRECT | O_LARGEFILE;
+ if (flags & VHD_OPEN_RDONLY)
+ oflags |= O_RDONLY;
+ if (flags & VHD_OPEN_RDWR)
+ oflags |= O_RDWR;
+
+ ctx->fd = open(ctx->file, oflags, 0644);
+ if (ctx->fd == -1) {
+ err = -errno;
+ VHDLOG("failed to open %s: %d\n", ctx->file, err);
+ goto fail;
+ }
+
+ err = vhd_test_file_fixed(ctx->file, &ctx->is_block);
+ if (err)
+ goto fail;
+
+ if (flags & VHD_OPEN_FAST) {
+ err = vhd_open_fast(ctx);
+ if (err)
+ goto fail;
+
+ return 0;
+ }
+
+ err = vhd_read_footer(ctx, &ctx->footer);
+ if (err)
+ goto fail;
+
+ if (!(flags & VHD_OPEN_IGNORE_DISABLED) && vhd_disabled(ctx)) {
+ err = -EINVAL;
+ goto fail;
+ }
+
+ if (vhd_type_dynamic(ctx)) {
+ err = vhd_read_header(ctx, &ctx->header);
+ if (err)
+ goto fail;
+
+ ctx->spb = ctx->header.block_size >> VHD_SECTOR_SHIFT;
+ ctx->bm_secs = secs_round_up_no_zero(ctx->spb >> 3);
+ }
+
+ return 0;
+
+fail:
+ if (ctx->fd != -1)
+ close(ctx->fd);
+ free(ctx->file);
+ memset(ctx, 0, sizeof(vhd_context_t));
+ return err;
+}
+
+void
+vhd_close(vhd_context_t *ctx)
+{
+ if (ctx->file)
+ close(ctx->fd);
+ free(ctx->file);
+ free(ctx->bat.bat);
+ free(ctx->batmap.map);
+ memset(ctx, 0, sizeof(vhd_context_t));
+}
+
+static inline void
+vhd_initialize_footer(vhd_context_t *ctx, int type, uint64_t size)
+{
+ memset(&ctx->footer, 0, sizeof(vhd_footer_t));
+ memcpy(ctx->footer.cookie, HD_COOKIE, sizeof(ctx->footer.cookie));
+ ctx->footer.features = HD_RESERVED;
+ ctx->footer.ff_version = HD_FF_VERSION;
+ ctx->footer.timestamp = vhd_time(time(NULL));
+ ctx->footer.crtr_ver = VHD_CURRENT_VERSION;
+ ctx->footer.crtr_os = 0x00000000;
+ ctx->footer.orig_size = size;
+ ctx->footer.curr_size = size;
+ ctx->footer.geometry = vhd_chs(size);
+ ctx->footer.type = type;
+ ctx->footer.saved = 0;
+ ctx->footer.data_offset = 0xFFFFFFFFFFFFFFFF;
+ strcpy(ctx->footer.crtr_app, "tap");
+ uuid_generate(ctx->footer.uuid);
+}
+
+static int
+vhd_initialize_header_parent_name(vhd_context_t *ctx, const char *parent_path)
+{
+ int err;
+ iconv_t cd;
+ size_t ibl, obl;
+ char *pname, *ppath, *dst;
+
+ err = 0;
+ pname = NULL;
+ ppath = NULL;
+
+ /*
+ * MICROSOFT_COMPAT
+ * big endian unicode here
+ */
+ cd = iconv_open(UTF_16BE, "ASCII");
+ if (cd == (iconv_t)-1) {
+ err = -errno;
+ goto out;
+ }
+
+ ppath = strdup(parent_path);
+ if (!ppath) {
+ err = -ENOMEM;
+ goto out;
+ }
+
+ pname = basename(ppath);
+ if (!strcmp(pname, "")) {
+ err = -EINVAL;
+ goto out;
+ }
+
+ ibl = strlen(pname);
+ obl = sizeof(ctx->header.prt_name);
+ dst = ctx->header.prt_name;
+
+ memset(dst, 0, obl);
+
+ if (iconv(cd, &pname, &ibl, &dst, &obl) == (size_t)-1 || ibl)
+ err = (errno ? -errno : -EINVAL);
+
+out:
+ iconv_close(cd);
+ free(ppath);
+ return err;
+}
+
+static off64_t
+get_file_size(const char *name)
+{
+ int fd;
+ off64_t end;
+
+ fd = open(name, O_LARGEFILE | O_RDONLY);
+ if (fd == -1) {
+ VHDLOG("unable to open '%s': %d\n", name, errno);
+ return -errno;
+ }
+ end = lseek64(fd, 0, SEEK_END);
+ close(fd);
+ return end;
+}
+
+static int
+vhd_initialize_header(vhd_context_t *ctx, const char *parent_path,
+ uint64_t size, int raw)
+{
+ int err;
+ struct stat stats;
+ vhd_context_t parent;
+
+ if (!vhd_type_dynamic(ctx))
+ return -EINVAL;
+
+ memset(&ctx->header, 0, sizeof(vhd_header_t));
+ memcpy(ctx->header.cookie, DD_COOKIE, sizeof(ctx->header.cookie));
+ ctx->header.data_offset = (uint64_t)-1;
+ ctx->header.table_offset = VHD_SECTOR_SIZE * 3; /* 1 ftr + 2 hdr */
+ ctx->header.hdr_ver = DD_VERSION;
+ ctx->header.block_size = VHD_BLOCK_SIZE;
+ ctx->header.prt_ts = 0;
+ ctx->header.res1 = 0;
+ ctx->header.max_bat_size = (ctx->footer.curr_size +
+ VHD_BLOCK_SIZE - 1) >> VHD_BLOCK_SHIFT;
+
+ ctx->footer.data_offset = VHD_SECTOR_SIZE;
+
+ if (ctx->footer.type == HD_TYPE_DYNAMIC)
+ return 0;
+
+ err = stat(parent_path, &stats);
+ if (err == -1)
+ return -errno;
+
+ if (raw) {
+ ctx->header.prt_ts = vhd_time(stats.st_mtime);
+ if (!size)
+ size = get_file_size(parent_path);
+ }
+ else {
+ err = vhd_open(&parent, parent_path, VHD_OPEN_RDONLY);
+ if (err)
+ return err;
+
+ ctx->header.prt_ts = vhd_time(stats.st_mtime);
+ uuid_copy(ctx->header.prt_uuid, parent.footer.uuid);
+ if (!size)
+ size = parent.footer.curr_size;
+ vhd_close(&parent);
+ }
+ ctx->footer.orig_size = size;
+ ctx->footer.curr_size = size;
+ ctx->footer.geometry = vhd_chs(size);
+ ctx->header.max_bat_size =
+ (size + VHD_BLOCK_SIZE - 1) >> VHD_BLOCK_SHIFT;
+
+ return vhd_initialize_header_parent_name(ctx, parent_path);
+}
+
+static int
+vhd_write_parent_locators(vhd_context_t *ctx, const char *parent)
+{
+ int i, err;
+ off64_t off;
+ uint32_t code;
+
+ code = PLAT_CODE_NONE;
+
+ if (ctx->footer.type != HD_TYPE_DIFF)
+ return -EINVAL;
+
+ off = ctx->batmap.header.batmap_offset +
+ vhd_sectors_to_bytes(ctx->batmap.header.batmap_size);
+ if (off & (VHD_SECTOR_SIZE - 1))
+ off = vhd_bytes_padded(off);
+
+ for (i = 0; i < 3; i++) {
+ switch (i) {
+ case 0:
+ code = PLAT_CODE_MACX;
+ break;
+ case 1:
+ code = PLAT_CODE_W2KU;
+ break;
+ case 2:
+ code = PLAT_CODE_W2RU;
+ break;
+ }
+
+ err = vhd_parent_locator_write_at(ctx, parent, off, code,
+ 0, ctx->header.loc + i);
+ if (err)
+ return err;
+
+ off += vhd_parent_locator_size(ctx->header.loc + i);
+ }
+
+ return 0;
+}
+
+int
+vhd_change_parent(vhd_context_t *child, char *parent_path, int raw)
+{
+ int i, err;
+ char *ppath;
+ struct stat stats;
+ vhd_context_t parent;
+
+ ppath = realpath(parent_path, NULL);
+ if (!ppath) {
+ VHDLOG("error resolving parent path %s for %s: %d\n",
+ parent_path, child->file, errno);
+ return -errno;
+ }
+
+ err = stat(ppath, &stats);
+ if (err == -1) {
+ err = -errno;
+ goto out;
+ }
+
+ if (!S_ISREG(stats.st_mode) && !S_ISBLK(stats.st_mode)) {
+ err = -EINVAL;
+ goto out;
+ }
+
+ if (raw) {
+ uuid_clear(child->header.prt_uuid);
+ } else {
+ err = vhd_open(&parent, ppath, VHD_OPEN_RDONLY);
+ if (err) {
+ VHDLOG("error opening parent %s for %s: %d\n",
+ ppath, child->file, err);
+ goto out;
+ }
+ uuid_copy(child->header.prt_uuid, parent.footer.uuid);
+ vhd_close(&parent);
+ }
+
+ vhd_initialize_header_parent_name(child, ppath);
+ child->header.prt_ts = vhd_time(stats.st_mtime);
+
+ for (i = 0; i < vhd_parent_locator_count(child); i++) {
+ vhd_parent_locator_t *loc = child->header.loc + i;
+ size_t max = vhd_parent_locator_size(loc);
+
+ switch (loc->code) {
+ case PLAT_CODE_MACX:
+ case PLAT_CODE_W2KU:
+ case PLAT_CODE_W2RU:
+ break;
+ default:
+ continue;
+ }
+
+ err = vhd_parent_locator_write_at(child, ppath,
+ loc->data_offset,
+ loc->code, max, loc);
+ if (err) {
+ VHDLOG("error writing parent locator %d for %s: %d\n",
+ i, child->file, err);
+ goto out;
+ }
+ }
+
+ TEST_FAIL_AT(FAIL_REPARENT_LOCATOR);
+
+ err = vhd_write_header(child, &child->header);
+ if (err) {
+ VHDLOG("error writing header for %s: %d\n", child->file, err);
+ goto out;
+ }
+
+ err = 0;
+
+out:
+ free(ppath);
+ return err;
+}
+
+static int
+vhd_create_batmap(vhd_context_t *ctx)
+{
+ off64_t off;
+ int err, map_bytes;
+ vhd_batmap_header_t *header;
+
+ if (!vhd_type_dynamic(ctx))
+ return -EINVAL;
+
+ map_bytes = (ctx->header.max_bat_size + 7) >> 3;
+ header = &ctx->batmap.header;
+
+ memset(header, 0, sizeof(vhd_batmap_header_t));
+ memcpy(header->cookie, VHD_BATMAP_COOKIE, sizeof(header->cookie));
+
+ err = vhd_batmap_header_offset(ctx, &off);
+ if (err)
+ return err;
+
+ header->batmap_offset = off +
+ vhd_bytes_padded(sizeof(vhd_batmap_header_t));
+ header->batmap_size = secs_round_up_no_zero(map_bytes);
+ header->batmap_version = VHD_BATMAP_CURRENT_VERSION;
+
+ map_bytes = vhd_sectors_to_bytes(header->batmap_size);
+
+ err = posix_memalign((void **)&ctx->batmap.map,
+ VHD_SECTOR_SIZE, map_bytes);
+ if (err) {
+ ctx->batmap.map = NULL;
+ return -err;
+ }
+
+ memset(ctx->batmap.map, 0, map_bytes);
+
+ return vhd_write_batmap(ctx, &ctx->batmap);
+}
+
+static int
+vhd_create_bat(vhd_context_t *ctx)
+{
+ int i, err;
+ size_t size;
+
+ if (!vhd_type_dynamic(ctx))
+ return -EINVAL;
+
+ size = vhd_bytes_padded(ctx->header.max_bat_size * sizeof(uint32_t));
+ err = posix_memalign((void **)&ctx->bat.bat, VHD_SECTOR_SIZE, size);
+ if (err) {
+ ctx->bat.bat = NULL;
+ return err;
+ }
+
+ memset(ctx->bat.bat, 0, size);
+ for (i = 0; i < ctx->header.max_bat_size; i++)
+ ctx->bat.bat[i] = DD_BLK_UNUSED;
+
+ err = vhd_seek(ctx, ctx->header.table_offset, SEEK_SET);
+ if (err)
+ return err;
+
+ ctx->bat.entries = ctx->header.max_bat_size;
+ ctx->bat.spb = ctx->header.block_size >> VHD_SECTOR_SHIFT;
+
+ return vhd_write_bat(ctx, &ctx->bat);
+}
+
+static int
+vhd_initialize_fixed_disk(vhd_context_t *ctx)
+{
+ char *buf;
+ int i, err;
+
+ if (ctx->footer.type != HD_TYPE_FIXED)
+ return -EINVAL;
+
+ err = vhd_seek(ctx, 0, SEEK_SET);
+ if (err)
+ return err;
+
+ buf = mmap(0, VHD_BLOCK_SIZE, PROT_READ,
+ MAP_SHARED | MAP_ANONYMOUS, -1, 0);
+ if (buf == MAP_FAILED)
+ return -errno;
+
+ for (i = 0; i < ctx->footer.curr_size >> VHD_BLOCK_SHIFT; i++) {
+ err = vhd_write(ctx, buf, VHD_BLOCK_SIZE);
+ if (err)
+ goto out;
+ }
+
+ err = 0;
+
+out:
+ munmap(buf, VHD_BLOCK_SIZE);
+ return err;
+}
+
+int
+vhd_get_phys_size(vhd_context_t *ctx, off64_t *size)
+{
+ int err;
+
+ if ((err = vhd_end_of_data(ctx, size)))
+ return err;
+ *size += sizeof(vhd_footer_t);
+ return 0;
+}
+
+int
+vhd_set_phys_size(vhd_context_t *ctx, off64_t size)
+{
+ off64_t phys_size;
+ int err;
+
+ err = vhd_get_phys_size(ctx, &phys_size);
+ if (err)
+ return err;
+ if (size < phys_size) {
+ // would result in data loss
+ VHDLOG("ERROR: new size (%"PRIu64") < phys size (%"PRIu64")\n",
+ size, phys_size);
+ return -EINVAL;
+ }
+ return vhd_write_footer_at(ctx, &ctx->footer,
+ size - sizeof(vhd_footer_t));
+}
+
+static int
+__vhd_create(const char *name, const char *parent, uint64_t bytes, int type,
+ vhd_flag_creat_t flags)
+{
+ int err;
+ off64_t off;
+ vhd_context_t ctx;
+ vhd_footer_t *footer;
+ vhd_header_t *header;
+ uint64_t size, blks;
+
+ switch (type) {
+ case HD_TYPE_DIFF:
+ if (!parent)
+ return -EINVAL;
+ case HD_TYPE_FIXED:
+ case HD_TYPE_DYNAMIC:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (strnlen(name, VHD_MAX_NAME_LEN - 1) == VHD_MAX_NAME_LEN - 1)
+ return -ENAMETOOLONG;
+
+ memset(&ctx, 0, sizeof(vhd_context_t));
+ footer = &ctx.footer;
+ header = &ctx.header;
+ blks = (bytes + VHD_BLOCK_SIZE - 1) >> VHD_BLOCK_SHIFT;
+ size = blks << VHD_BLOCK_SHIFT;
+
+ ctx.fd = open(name, O_WRONLY | O_CREAT |
+ O_TRUNC | O_LARGEFILE | O_DIRECT, 0644);
+ if (ctx.fd == -1)
+ return -errno;
+
+ ctx.file = strdup(name);
+ if (!ctx.file) {
+ err = -ENOMEM;
+ goto out;
+ }
+
+ err = vhd_test_file_fixed(ctx.file, &ctx.is_block);
+ if (err)
+ goto out;
+
+ vhd_initialize_footer(&ctx, type, size);
+
+ if (type == HD_TYPE_FIXED) {
+ err = vhd_initialize_fixed_disk(&ctx);
+ if (err)
+ goto out;
+ } else {
+ int raw = vhd_flag_test(flags, VHD_FLAG_CREAT_PARENT_RAW);
+ err = vhd_initialize_header(&ctx, parent, size, raw);
+ if (err)
+ goto out;
+
+ err = vhd_write_footer_at(&ctx, &ctx.footer, 0);
+ if (err)
+ goto out;
+
+ err = vhd_write_header_at(&ctx, &ctx.header, VHD_SECTOR_SIZE);
+ if (err)
+ goto out;
+
+ err = vhd_create_batmap(&ctx);
+ if (err)
+ goto out;
+
+ err = vhd_create_bat(&ctx);
+ if (err)
+ goto out;
+
+ if (type == HD_TYPE_DIFF) {
+ err = vhd_write_parent_locators(&ctx, parent);
+ if (err)
+ goto out;
+ }
+
+ /* write header again since it may have changed */
+ err = vhd_write_header_at(&ctx, &ctx.header, VHD_SECTOR_SIZE);
+ if (err)
+ goto out;
+ }
+
+ err = vhd_seek(&ctx, 0, SEEK_END);
+ if (err)
+ goto out;
+
+ off = vhd_position(&ctx);
+ if (off == (off64_t)-1) {
+ err = -errno;
+ goto out;
+ }
+
+ if (ctx.is_block)
+ off -= sizeof(vhd_footer_t);
+
+ err = vhd_write_footer_at(&ctx, &ctx.footer, off);
+ if (err)
+ goto out;
+
+ err = 0;
+
+out:
+ vhd_close(&ctx);
+ if (err && !ctx.is_block)
+ unlink(name);
+ return err;
+}
+
+int
+vhd_create(const char *name, uint64_t bytes, int type, vhd_flag_creat_t flags)
+{
+ return __vhd_create(name, NULL, bytes, type, flags);
+}
+
+int
+vhd_snapshot(const char *name, uint64_t bytes, const char *parent,
+ vhd_flag_creat_t flags)
+{
+ return __vhd_create(name, parent, bytes, HD_TYPE_DIFF, flags);
+}
+
+static int
+__vhd_io_fixed_read(vhd_context_t *ctx,
+ char *buf, uint64_t sec, uint32_t secs)
+{
+ int err;
+
+ err = vhd_seek(ctx, vhd_sectors_to_bytes(sec), SEEK_SET);
+ if (err)
+ return err;
+
+ return vhd_read(ctx, buf, vhd_sectors_to_bytes(secs));
+}
+
+static void
+__vhd_io_dynamic_copy_data(vhd_context_t *ctx,
+ char *map, int map_off,
+ char *bitmap, int bitmap_off,
+ char *dst, char *src, int secs)
+{
+ int i;
+
+ for (i = 0; i < secs; i++) {
+ if (test_bit(map, map_off + i))
+ goto next;
+
+ if (ctx && !vhd_bitmap_test(ctx, bitmap, bitmap_off + i))
+ goto next;
+
+ memcpy(dst, src, VHD_SECTOR_SIZE);
+ set_bit(map, map_off + i);
+
+ next:
+ src += VHD_SECTOR_SIZE;
+ dst += VHD_SECTOR_SIZE;
+ }
+}
+
+static int
+__vhd_io_dynamic_read_link(vhd_context_t *ctx, char *map,
+ char *buf, uint64_t sector, uint32_t secs)
+{
+ off64_t off;
+ uint32_t blk, sec;
+ int err, cnt, map_off;
+ char *bitmap, *data, *src;
+
+ map_off = 0;
+
+ do {
+ blk = sector / ctx->spb;
+ sec = sector % ctx->spb;
+ off = ctx->bat.bat[blk];
+ data = NULL;
+ bitmap = NULL;
+
+ if (off == DD_BLK_UNUSED) {
+ cnt = MIN(secs, ctx->spb);
+ goto next;
+ }
+
+ err = vhd_read_bitmap(ctx, blk, &bitmap);
+ if (err)
+ return err;
+
+ err = vhd_read_block(ctx, blk, &data);
+ if (err) {
+ free(bitmap);
+ return err;
+ }
+
+ cnt = MIN(secs, ctx->spb - sec);
+ src = data + vhd_sectors_to_bytes(sec);
+
+ __vhd_io_dynamic_copy_data(ctx,
+ map, map_off,
+ bitmap, sec,
+ buf, src, cnt);
+
+ next:
+ free(data);
+ free(bitmap);
+
+ secs -= cnt;
+ sector += cnt;
+ map_off += cnt;
+ buf += vhd_sectors_to_bytes(cnt);
+
+ } while (secs);
+
+ return 0;
+}
+
+static int
+__raw_read_link(char *filename,
+ char *map, char *buf, uint64_t sec, uint32_t secs)
+{
+ int fd, err;
+ off64_t off;
+ uint64_t size;
+ char *data;
+
+ err = 0;
+ errno = 0;
+ fd = open(filename, O_RDONLY | O_DIRECT | O_LARGEFILE);
+ if (fd == -1) {
+ VHDLOG("%s: failed to open: %d\n", filename, -errno);
+ return -errno;
+ }
+
+ off = lseek64(fd, vhd_sectors_to_bytes(sec), SEEK_SET);
+ if (off == (off64_t)-1) {
+ VHDLOG("%s: seek(0x%08"PRIx64") failed: %d\n",
+ filename, vhd_sectors_to_bytes(sec), -errno);
+ err = -errno;
+ goto close;
+ }
+
+ size = vhd_sectors_to_bytes(secs);
+ err = posix_memalign((void **)&data, VHD_SECTOR_SIZE, size);
+ if (err)
+ goto close;
+
+ err = read(fd, data, size);
+ if (err != size) {
+ VHDLOG("%s: reading of %"PRIu64" returned %d, errno: %d\n",
+ filename, size, err, -errno);
+ free(data);
+ err = errno ? -errno : -EIO;
+ goto close;
+ }
+ __vhd_io_dynamic_copy_data(NULL, map, 0, NULL, 0, buf, data, secs);
+ free(data);
+ err = 0;
+
+close:
+ close(fd);
+ return err;
+}
+
+static int
+__vhd_io_dynamic_read(vhd_context_t *ctx,
+ char *buf, uint64_t sec, uint32_t secs)
+{
+ int err;
+ uint32_t i, done;
+ char *map, *next;
+ vhd_context_t parent, *vhd;
+
+ err = vhd_get_bat(ctx);
+ if (err)
+ return err;
+
+ vhd = ctx;
+ next = NULL;
+ map = calloc(1, secs << (VHD_SECTOR_SHIFT - 3));
+ if (!map)
+ return -ENOMEM;
+
+ memset(buf, 0, vhd_sectors_to_bytes(secs));
+
+ for (;;) {
+ err = __vhd_io_dynamic_read_link(vhd, map, buf, sec, secs);
+ if (err)
+ goto close;
+
+ for (done = 0, i = 0; i < secs; i++)
+ if (test_bit(map, i))
+ done++;
+
+ if (done == secs) {
+ err = 0;
+ goto close;
+ }
+
+ if (vhd->footer.type == HD_TYPE_DIFF) {
+ err = vhd_parent_locator_get(vhd, &next);
+ if (err)
+ goto close;
+ if (vhd_parent_raw(vhd)) {
+ err = __raw_read_link(next, map, buf, sec,
+ secs);
+ goto close;
+ }
+ } else {
+ err = 0;
+ goto close;
+ }
+
+ if (vhd != ctx)
+ vhd_close(vhd);
+ vhd = &parent;
+
+ err = vhd_open(vhd, next, VHD_OPEN_RDONLY);
+ if (err)
+ goto out;
+
+ err = vhd_get_bat(vhd);
+ if (err)
+ goto close;
+
+ free(next);
+ next = NULL;
+ }
+
+close:
+ if (vhd != ctx)
+ vhd_close(vhd);
+out:
+ free(map);
+ free(next);
+ return err;
+}
+
+int
+vhd_io_read(vhd_context_t *ctx, char *buf, uint64_t sec, uint32_t secs)
+{
+ if (vhd_sectors_to_bytes(sec + secs) > ctx->footer.curr_size)
+ return -ERANGE;
+
+ if (!vhd_type_dynamic(ctx))
+ return __vhd_io_fixed_read(ctx, buf, sec, secs);
+
+ return __vhd_io_dynamic_read(ctx, buf, sec, secs);
+}
+
+static int
+__vhd_io_fixed_write(vhd_context_t *ctx,
+ char *buf, uint64_t sec, uint32_t secs)
+{
+ int err;
+
+ err = vhd_seek(ctx, vhd_sectors_to_bytes(sec), SEEK_SET);
+ if (err)
+ return err;
+
+ return vhd_write(ctx, buf, vhd_sectors_to_bytes(secs));
+}
+
+static int
+__vhd_io_allocate_block(vhd_context_t *ctx, uint32_t block)
+{
+ char *buf;
+ size_t size;
+ off64_t off, max;
+ int i, err, gap, spp;
+
+ spp = getpagesize() >> VHD_SECTOR_SHIFT;
+
+ err = vhd_end_of_data(ctx, &max);
+ if (err)
+ return err;
+
+ gap = 0;
+ off = max;
+ max >>= VHD_SECTOR_SHIFT;
+
+ /* data region of segment should begin on page boundary */
+ if ((max + ctx->bm_secs) % spp) {
+ gap = (spp - ((max + ctx->bm_secs) % spp));
+ max += gap;
+ }
+
+ err = vhd_seek(ctx, off, SEEK_SET);
+ if (err)
+ return err;
+
+ size = vhd_sectors_to_bytes(ctx->spb + ctx->bm_secs + gap);
+ buf = mmap(0, size, PROT_READ, MAP_SHARED | MAP_ANONYMOUS, -1, 0);
+ if (buf == MAP_FAILED)
+ return -errno;
+
+ err = vhd_write(ctx, buf, size);
+ if (err)
+ goto out;
+
+ ctx->bat.bat[block] = max;
+ err = vhd_write_bat(ctx, &ctx->bat);
+ if (err)
+ goto out;
+
+ err = 0;
+
+out:
+ munmap(buf, size);
+ return err;
+}
+
+static int
+__vhd_io_dynamic_write(vhd_context_t *ctx,
+ char *buf, uint64_t sector, uint32_t secs)
+{
+ char *map;
+ off64_t off;
+ uint32_t blk, sec;
+ int i, err, cnt, ret;
+
+ if (vhd_sectors_to_bytes(sector + secs) > ctx->footer.curr_size)
+ return -ERANGE;
+
+ err = vhd_get_bat(ctx);
+ if (err)
+ return err;
+
+ if (vhd_has_batmap(ctx)) {
+ err = vhd_get_batmap(ctx);
+ if (err)
+ return err;
+ }
+
+ do {
+ blk = sector / ctx->spb;
+ sec = sector % ctx->spb;
+
+ off = ctx->bat.bat[blk];
+ if (off == DD_BLK_UNUSED) {
+ err = __vhd_io_allocate_block(ctx, blk);
+ if (err)
+ return err;
+
+ off = ctx->bat.bat[blk];
+ }
+
+ off += ctx->bm_secs + sec;
+ err = vhd_seek(ctx, vhd_sectors_to_bytes(off), SEEK_SET);
+ if (err)
+ return err;
+
+ cnt = MIN(secs, ctx->spb - sec);
+ err = vhd_write(ctx, buf, vhd_sectors_to_bytes(cnt));
+ if (err)
+ return err;
+
+ if (vhd_has_batmap(ctx) &&
+ vhd_batmap_test(ctx, &ctx->batmap, blk))
+ goto next;
+
+ err = vhd_read_bitmap(ctx, blk, &map);
+ if (err)
+ return err;
+
+ for (i = 0; i < cnt; i++)
+ vhd_bitmap_set(ctx, map, sec + i);
+
+ err = vhd_write_bitmap(ctx, blk, map);
+ if (err)
+ goto fail;
+
+ if (vhd_has_batmap(ctx)) {
+ for (i = 0; i < ctx->spb; i++)
+ if (!vhd_bitmap_test(ctx, map, i)) {
+ free(map);
+ goto next;
+ }
+
+ vhd_batmap_set(ctx, &ctx->batmap, blk);
+ err = vhd_write_batmap(ctx, &ctx->batmap);
+ if (err)
+ goto fail;
+ }
+
+ free(map);
+ map = NULL;
+
+ next:
+ secs -= cnt;
+ sector += cnt;
+ buf += vhd_sectors_to_bytes(cnt);
+ } while (secs);
+
+ err = 0;
+
+out:
+ ret = vhd_write_footer(ctx, &ctx->footer);
+ return (err ? err : ret);
+
+fail:
+ free(map);
+ goto out;
+}
+
+int
+vhd_io_write(vhd_context_t *ctx, char *buf, uint64_t sec, uint32_t secs)
+{
+ if (vhd_sectors_to_bytes(sec + secs) > ctx->footer.curr_size)
+ return -ERANGE;
+
+ if (!vhd_type_dynamic(ctx))
+ return __vhd_io_fixed_write(ctx, buf, sec, secs);
+
+ return __vhd_io_dynamic_write(ctx, buf, sec, secs);
+}
diff --git a/tools/blktap2/vhd/lib/relative-path.c b/tools/blktap2/vhd/lib/relative-path.c
new file mode 100644
index 0000000000..8b7cb71fc9
--- /dev/null
+++ b/tools/blktap2/vhd/lib/relative-path.c
@@ -0,0 +1,299 @@
+/* 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 <stdlib.h>
+#include <string.h>
+
+#include "relative-path.h"
+
+#define sfree(ptr) \
+do { \
+ free(ptr); \
+ ptr = NULL; \
+} while (0)
+
+/*
+ * count number of tokens between DELIMETER characters
+ */
+static int
+count_nodes(char *path)
+{
+ int i;
+ char *tmp;
+
+ if (!path)
+ return 0;
+
+ for (i = 0, tmp = path; *tmp != '\0'; tmp++)
+ if (*tmp == DELIMITER)
+ i++;
+
+ return i;
+}
+
+/*
+ * return copy of next node in @path, or NULL
+ * @path is moved to the end of the next node
+ * @err is set to -errno on failure
+ * copy should be freed
+ */
+static char *
+next_node(char **path, int *err)
+{
+ int ret;
+ char *tmp, *start;
+
+ if (!path || !*path) {
+ *err = -EINVAL;
+ return NULL;
+ }
+
+ *err = 0;
+ start = *path;
+
+ for (tmp = *path; *tmp != '\0'; tmp++)
+ if (*tmp == DELIMITER) {
+ int size;
+ char *node;
+
+ size = tmp - start + 1;
+ node = malloc(size);
+ if (!node) {
+ *err = -ENOMEM;
+ return NULL;
+ }
+
+ ret = snprintf(node, size, "%s", start);
+ if (ret < 0) {
+ free(node);
+ *err = -EINVAL;
+ return NULL;
+ }
+
+ *path = tmp;
+ return node;
+ }
+
+ return NULL;
+}
+
+/*
+ * count number of nodes in common betwee @to and @from
+ * returns number of common nodes, or -errno on failure
+ */
+static int
+count_common_nodes(char *to, char *from)
+{
+ int err, common;
+ char *to_node, *from_node;
+
+ if (!to || !from)
+ return -EINVAL;
+
+ err = 0;
+ common = 0;
+ to_node = NULL;
+ from_node = NULL;
+
+ do {
+ to_node = next_node(&to, &err);
+ if (err || !to_node)
+ break;
+
+ from_node = next_node(&from, &err);
+ if (err || !from_node)
+ break;
+
+ if (strncmp(to_node, from_node, MAX_NAME_LEN))
+ break;
+
+ ++to;
+ ++from;
+ ++common;
+ sfree(to_node);
+ sfree(from_node);
+
+ } while (1);
+
+ sfree(to_node);
+ sfree(from_node);
+
+ if (err)
+ return err;
+
+ return common;
+}
+
+/*
+ * construct path of @count '../', './' if @count is zero, or NULL on error
+ * result should be freed
+ */
+static char *
+up_nodes(int count)
+{
+ char *path, *tmp;
+ int i, ret, len, size;
+
+ if (!count)
+ return strdup("./");
+
+ len = strlen("../");
+ size = len * count;
+ if (size >= MAX_NAME_LEN)
+ return NULL;
+
+ path = malloc(size + 1);
+ if (!path)
+ return NULL;
+
+ tmp = path;
+ for (i = 0; i < count; i++) {
+ ret = sprintf(tmp, "../");
+ if (ret < 0 || ret != len) {
+ free(path);
+ return NULL;
+ }
+ tmp += ret;
+ }
+
+ return path;
+}
+
+/*
+ * return pointer to @offset'th node of path or NULL on error
+ */
+static char *
+node_offset(char *from, int offset)
+{
+ char *path;
+
+ if (!from || !offset)
+ return NULL;
+
+ for (path = from; *path != '\0'; path++) {
+ if (*path == DELIMITER)
+ if (--offset == 0)
+ return path + 1;
+ }
+
+ return NULL;
+}
+
+/*
+ * return a relative path from @from to @to
+ * result should be freed
+ */
+char *
+relative_path_to(char *from, char *to, int *err)
+{
+ int from_nodes, common;
+ char *to_absolute, *from_absolute;
+ char *up, *common_target_path, *relative_path;
+
+ *err = 0;
+ up = NULL;
+ to_absolute = NULL;
+ from_absolute = NULL;
+ relative_path = NULL;
+
+ if (strnlen(to, MAX_NAME_LEN) == MAX_NAME_LEN ||
+ strnlen(from, MAX_NAME_LEN) == MAX_NAME_LEN) {
+ EPRINTF("invalid input; max path length is %d\n",
+ MAX_NAME_LEN);
+ *err = -ENAMETOOLONG;
+ return NULL;
+ }
+
+ to_absolute = realpath(to, NULL);
+ if (!to_absolute) {
+ EPRINTF("failed to get absolute path of %s\n", to);
+ *err = -errno;
+ goto out;
+ }
+
+ from_absolute = realpath(from, NULL);
+ if (!from_absolute) {
+ EPRINTF("failed to get absolute path of %s\n", from);
+ *err = -errno;
+ goto out;
+ }
+
+ if (strnlen(to_absolute, MAX_NAME_LEN) == MAX_NAME_LEN ||
+ strnlen(from_absolute, MAX_NAME_LEN) == MAX_NAME_LEN) {
+ EPRINTF("invalid input; max path length is %d\n",
+ MAX_NAME_LEN);
+ *err = -ENAMETOOLONG;
+ goto out;
+ }
+
+ /* count nodes in source path */
+ from_nodes = count_nodes(from_absolute);
+
+ /* count nodes in common */
+ common = count_common_nodes(to_absolute + 1, from_absolute + 1);
+ if (common < 0) {
+ EPRINTF("failed to count common nodes of %s and %s: %d\n",
+ to_absolute, from_absolute, common);
+ *err = common;
+ goto out;
+ }
+
+ /* move up to common node */
+ up = up_nodes(from_nodes - common - 1);
+ if (!up) {
+ EPRINTF("failed to allocate relative path for %s: %d\n",
+ from_absolute, -ENOMEM);
+ *err = -ENOMEM;
+ goto out;
+ }
+
+ /* get path from common node to target */
+ common_target_path = node_offset(to_absolute, common + 1);
+ if (!common_target_path) {
+ EPRINTF("failed to find common target path to %s: %d\n",
+ to_absolute, -EINVAL);
+ *err = -EINVAL;
+ goto out;
+ }
+
+ /* get relative path */
+ if (asprintf(&relative_path, "%s%s", up, common_target_path) == -1) {
+ EPRINTF("failed to construct final path %s%s: %d\n",
+ up, common_target_path, -ENOMEM);
+ relative_path = NULL;
+ *err = -ENOMEM;
+ goto out;
+ }
+
+out:
+ sfree(up);
+ sfree(to_absolute);
+ sfree(from_absolute);
+
+ return relative_path;
+}
diff --git a/tools/blktap2/vhd/lib/vhd-util-check.c b/tools/blktap2/vhd/lib/vhd-util-check.c
new file mode 100644
index 0000000000..d7d588088a
--- /dev/null
+++ b/tools/blktap2/vhd/lib/vhd-util-check.c
@@ -0,0 +1,977 @@
+/* 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 <time.h>
+#include <stdio.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <libgen.h>
+#include <inttypes.h>
+#include <sys/stat.h>
+
+#include "libvhd.h"
+#include "vhd-util.h"
+
+// allow the VHD timestamp to be at most this many seconds into the future to
+// account for time skew with NFS servers
+#define TIMESTAMP_MAX_SLACK 1800
+
+static int
+vhd_util_check_zeros(void *buf, size_t size)
+{
+ int i;
+ char *p;
+
+ p = buf;
+ for (i = 0; i < size; i++)
+ if (p[i])
+ return i;
+
+ return 0;
+}
+
+static int
+vhd_util_check_footer_opened(vhd_footer_t *footer)
+{
+ int i, n;
+ uint32_t *buf;
+
+ buf = (uint32_t *)footer;
+ n = sizeof(*footer) / sizeof(uint32_t);
+
+ for (i = 0; i < n; i++)
+ if (buf[i] != 0xc7c7c7c7)
+ return 0;
+
+ return 1;
+}
+
+static char *
+vhd_util_check_validate_footer(vhd_footer_t *footer)
+{
+ int size;
+ uint32_t checksum, now;
+
+ size = sizeof(footer->cookie);
+ if (memcmp(footer->cookie, HD_COOKIE, size))
+ return "invalid cookie";
+
+ checksum = vhd_checksum_footer(footer);
+ if (checksum != footer->checksum) {
+ if (footer->hidden &&
+ !strncmp(footer->crtr_app, "tap", 3) &&
+ (footer->crtr_ver == VHD_VERSION(0, 1) ||
+ footer->crtr_ver == VHD_VERSION(1, 1))) {
+ char tmp = footer->hidden;
+ footer->hidden = 0;
+ checksum = vhd_checksum_footer(footer);
+ footer->hidden = tmp;
+
+ if (checksum == footer->checksum)
+ goto ok;
+ }
+
+ return "invalid checksum";
+ }
+
+ok:
+ if (!(footer->features & HD_RESERVED))
+ return "invalid 'reserved' feature";
+
+ if (footer->features & ~(HD_TEMPORARY | HD_RESERVED))
+ return "invalid extra features";
+
+ if (footer->ff_version != HD_FF_VERSION)
+ return "invalid file format version";
+
+ if (footer->type != HD_TYPE_DYNAMIC &&
+ footer->type != HD_TYPE_DIFF &&
+ footer->data_offset != ~(0ULL))
+ return "invalid data offset";
+
+ now = vhd_time(time(NULL));
+ if (footer->timestamp > now + TIMESTAMP_MAX_SLACK)
+ return "creation time in future";
+
+ if (!strncmp(footer->crtr_app, "tap", 3) &&
+ footer->crtr_ver > VHD_CURRENT_VERSION)
+ return "unsupported tap creator version";
+
+ if (vhd_chs(footer->curr_size) < footer->geometry)
+ return "geometry too large";
+
+ if (footer->type != HD_TYPE_FIXED &&
+ footer->type != HD_TYPE_DYNAMIC &&
+ footer->type != HD_TYPE_DIFF)
+ return "invalid type";
+
+ if (footer->saved && footer->saved != 1)
+ return "invalid 'saved' state";
+
+ if (footer->hidden && footer->hidden != 1)
+ return "invalid 'hidden' state";
+
+ if (vhd_util_check_zeros(footer->reserved,
+ sizeof(footer->reserved)))
+ return "invalid 'reserved' bits";
+
+ return NULL;
+}
+
+static char *
+vhd_util_check_validate_header(int fd, vhd_header_t *header)
+{
+ off64_t eof;
+ int i, cnt, size;
+ uint32_t checksum;
+
+ size = sizeof(header->cookie);
+ if (memcmp(header->cookie, DD_COOKIE, size))
+ return "invalid cookie";
+
+ checksum = vhd_checksum_header(header);
+ if (checksum != header->checksum)
+ return "invalid checksum";
+
+ if (header->hdr_ver != 0x00010000)
+ return "invalid header version";
+
+ if (header->data_offset != ~(0ULL))
+ return "invalid data offset";
+
+ eof = lseek64(fd, 0, SEEK_END);
+ if (eof == (off64_t)-1)
+ return "error finding eof";
+
+ if (header->table_offset <= 0 ||
+ header->table_offset % 512 ||
+ (header->table_offset +
+ (header->max_bat_size * sizeof(uint32_t)) >
+ eof - sizeof(vhd_footer_t)))
+ return "invalid table offset";
+
+ for (cnt = 0, i = 0; i < sizeof(header->block_size) * 8; i++)
+ if ((header->block_size >> i) & 1)
+ cnt++;
+
+ if (cnt != 1)
+ return "invalid block size";
+
+ if (header->res1)
+ return "invalid reserved bits";
+
+ if (vhd_util_check_zeros(header->res2, sizeof(header->res2)))
+ return "invalid reserved bits";
+
+ return NULL;
+}
+
+static char *
+vhd_util_check_validate_differencing_header(vhd_context_t *vhd)
+{
+ vhd_header_t *header;
+
+ header = &vhd->header;
+
+ if (vhd->footer.type == HD_TYPE_DIFF) {
+ char *parent;
+ uint32_t now;
+
+ now = vhd_time(time(NULL));
+ if (header->prt_ts > now + TIMESTAMP_MAX_SLACK)
+ return "parent creation time in future";
+
+ if (vhd_header_decode_parent(vhd, header, &parent))
+ return "invalid parent name";
+
+ free(parent);
+ } else {
+ if (vhd_util_check_zeros(header->prt_name,
+ sizeof(header->prt_name)))
+ return "invalid non-null parent name";
+
+ if (vhd_util_check_zeros(header->loc, sizeof(header->loc)))
+ return "invalid non-null parent locators";
+
+ if (!uuid_is_null(header->prt_uuid))
+ return "invalid non-null parent uuid";
+
+ if (header->prt_ts)
+ return "invalid non-zero parent timestamp";
+ }
+
+ return NULL;
+}
+
+static char *
+vhd_util_check_validate_batmap(vhd_context_t *vhd, vhd_batmap_t *batmap)
+{
+ int size;
+ off64_t eof;
+ uint32_t checksum;
+
+ size = sizeof(batmap->header.cookie);
+ if (memcmp(batmap->header.cookie, VHD_BATMAP_COOKIE, size))
+ return "invalid cookie";
+
+ if (batmap->header.batmap_version > VHD_BATMAP_CURRENT_VERSION)
+ return "unsupported batmap version";
+
+ checksum = vhd_checksum_batmap(batmap);
+ if (checksum != batmap->header.checksum)
+ return "invalid checksum";
+
+ if (!batmap->header.batmap_size)
+ return "invalid size zero";
+
+ eof = lseek64(vhd->fd, 0, SEEK_END);
+ if (eof == (off64_t)-1)
+ return "error finding eof";
+
+ if (!batmap->header.batmap_offset ||
+ batmap->header.batmap_offset % 512)
+ return "invalid batmap offset";
+
+ if ((batmap->header.batmap_offset +
+ vhd_sectors_to_bytes(batmap->header.batmap_size)) >
+ eof - sizeof(vhd_footer_t))
+ return "invalid batmap size";
+
+ return NULL;
+}
+
+static char *
+vhd_util_check_validate_parent_locator(vhd_context_t *vhd,
+ vhd_parent_locator_t *loc)
+{
+ off64_t eof;
+
+ if (vhd_validate_platform_code(loc->code))
+ return "invalid platform code";
+
+ if (loc->code == PLAT_CODE_NONE) {
+ if (vhd_util_check_zeros(loc, sizeof(*loc)))
+ return "non-zero locator";
+
+ return NULL;
+ }
+
+ if (!loc->data_offset)
+ return "invalid data offset";
+
+ if (!loc->data_space)
+ return "invalid data space";
+
+ if (!loc->data_len)
+ return "invalid data length";
+
+ eof = lseek64(vhd->fd, 0, SEEK_END);
+ if (eof == (off64_t)-1)
+ return "error finding eof";
+
+ if (loc->data_offset + vhd_parent_locator_size(loc) >
+ eof - sizeof(vhd_footer_t))
+ return "invalid size";
+
+ if (loc->res)
+ return "invalid reserved bits";
+
+ return NULL;
+}
+
+static char *
+vhd_util_check_validate_parent(vhd_context_t *vhd, const char *ppath)
+{
+ char *msg;
+ vhd_context_t parent;
+
+ msg = NULL;
+
+ if (vhd_parent_raw(vhd))
+ return msg;
+
+ if (vhd_open(&parent, ppath,
+ VHD_OPEN_RDONLY | VHD_OPEN_IGNORE_DISABLED))
+ return "error opening parent";
+
+ if (uuid_compare(vhd->header.prt_uuid, parent.footer.uuid)) {
+ msg = "invalid parent uuid";
+ goto out;
+ }
+
+out:
+ vhd_close(&parent);
+ return msg;
+}
+
+static int
+vhd_util_check_footer(int fd, vhd_footer_t *footer, int ignore)
+{
+ size_t size;
+ int err, opened;
+ char *msg, *buf;
+ off64_t eof, off;
+ vhd_footer_t primary, backup;
+
+ memset(&primary, 0, sizeof(primary));
+ memset(&backup, 0, sizeof(backup));
+
+ err = posix_memalign((void **)&buf, VHD_SECTOR_SIZE, sizeof(primary));
+ if (err) {
+ printf("error allocating buffer: %d\n", err);
+ return -err;
+ }
+
+ memset(buf, 0, sizeof(primary));
+
+ eof = lseek64(fd, 0, SEEK_END);
+ if (eof == (off64_t)-1) {
+ err = -errno;
+ printf("error calculating end of file: %d\n", err);
+ goto out;
+ }
+
+ size = ((eof % 512) ? 511 : 512);
+ eof = lseek64(fd, eof - size, SEEK_SET);
+ if (eof == (off64_t)-1) {
+ err = -errno;
+ printf("error calculating end of file: %d\n", err);
+ goto out;
+ }
+
+ err = read(fd, buf, 512);
+ if (err != size) {
+ err = (errno ? -errno : -EIO);
+ printf("error reading primary footer: %d\n", err);
+ goto out;
+ }
+
+ memcpy(&primary, buf, sizeof(primary));
+ opened = vhd_util_check_footer_opened(&primary);
+ vhd_footer_in(&primary);
+
+ msg = vhd_util_check_validate_footer(&primary);
+ if (msg) {
+ if (opened && ignore)
+ goto check_backup;
+
+ err = -EINVAL;
+ printf("primary footer invalid: %s\n", msg);
+ goto out;
+ }
+
+ if (primary.type == HD_TYPE_FIXED) {
+ err = 0;
+ goto out;
+ }
+
+check_backup:
+ off = lseek64(fd, 0, SEEK_SET);
+ if (off == (off64_t)-1) {
+ err = -errno;
+ printf("error seeking to backup footer: %d\n", err);
+ goto out;
+ }
+
+ size = 512;
+ memset(buf, 0, sizeof(primary));
+
+ err = read(fd, buf, size);
+ if (err != size) {
+ err = (errno ? -errno : -EIO);
+ printf("error reading backup footer: %d\n", err);
+ goto out;
+ }
+
+ memcpy(&backup, buf, sizeof(backup));
+ vhd_footer_in(&backup);
+
+ msg = vhd_util_check_validate_footer(&backup);
+ if (msg) {
+ err = -EINVAL;
+ printf("backup footer invalid: %s\n", msg);
+ goto out;
+ }
+
+ if (memcmp(&primary, &backup, sizeof(primary))) {
+ if (opened && ignore) {
+ memcpy(&primary, &backup, sizeof(primary));
+ goto ok;
+ }
+
+ if (backup.hidden &&
+ !strncmp(backup.crtr_app, "tap", 3) &&
+ (backup.crtr_ver == VHD_VERSION(0, 1) ||
+ backup.crtr_ver == VHD_VERSION(1, 1))) {
+ char cmp, tmp = backup.hidden;
+ backup.hidden = 0;
+ cmp = memcmp(&primary, &backup, sizeof(primary));
+ backup.hidden = tmp;
+ if (!cmp)
+ goto ok;
+ }
+
+ err = -EINVAL;
+ printf("primary and backup footers do not match\n");
+ goto out;
+ }
+
+ok:
+ err = 0;
+ memcpy(footer, &primary, sizeof(primary));
+
+out:
+ free(buf);
+ return err;
+}
+
+static int
+vhd_util_check_header(int fd, vhd_footer_t *footer)
+{
+ int err;
+ off64_t off;
+ char *msg, *buf;
+ vhd_header_t header;
+
+ err = posix_memalign((void **)&buf, VHD_SECTOR_SIZE, sizeof(header));
+ if (err) {
+ printf("error allocating header: %d\n", err);
+ return err;
+ }
+
+ off = footer->data_offset;
+ off = lseek64(fd, off, SEEK_SET);
+ if (off == (off64_t)-1) {
+ err = -errno;
+ printf("error seeking to header: %d\n", err);
+ goto out;
+ }
+
+ err = read(fd, buf, sizeof(header));
+ if (err != sizeof(header)) {
+ err = (errno ? -errno : -EIO);
+ printf("error reading header: %d\n", err);
+ goto out;
+ }
+
+ memcpy(&header, buf, sizeof(header));
+ vhd_header_in(&header);
+
+ msg = vhd_util_check_validate_header(fd, &header);
+ if (msg) {
+ err = -EINVAL;
+ printf("header is invalid: %s\n", msg);
+ goto out;
+ }
+
+ err = 0;
+
+out:
+ free(buf);
+ return err;
+}
+
+static int
+vhd_util_check_differencing_header(vhd_context_t *vhd)
+{
+ char *msg;
+
+ msg = vhd_util_check_validate_differencing_header(vhd);
+ if (msg) {
+ printf("differencing header is invalid: %s\n", msg);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int
+vhd_util_check_bat(vhd_context_t *vhd)
+{
+ off64_t eof, eoh;
+ int i, j, err, block_size;
+
+ err = vhd_seek(vhd, 0, SEEK_END);
+ if (err) {
+ printf("error calculating eof: %d\n", err);
+ return err;
+ }
+
+ eof = vhd_position(vhd);
+ if (eof == (off64_t)-1) {
+ printf("error calculating eof: %d\n", -errno);
+ return -errno;
+ }
+
+ /* adjust eof for vhds with short footers */
+ if (eof % 512) {
+ if (eof % 512 != 511) {
+ printf("invalid file size: 0x%"PRIx64"\n", eof);
+ return -EINVAL;
+ }
+
+ eof++;
+ }
+
+ err = vhd_get_bat(vhd);
+ if (err) {
+ printf("error reading bat: %d\n", err);
+ return err;
+ }
+
+ err = vhd_end_of_headers(vhd, &eoh);
+ if (err) {
+ printf("error calculating end of metadata: %d\n", err);
+ return err;
+ }
+
+ eof -= sizeof(vhd_footer_t);
+ eof >>= VHD_SECTOR_SHIFT;
+ eoh >>= VHD_SECTOR_SHIFT;
+ block_size = vhd->spb + vhd->bm_secs;
+
+ for (i = 0; i < vhd->header.max_bat_size; i++) {
+ uint32_t off = vhd->bat.bat[i];
+ if (off == DD_BLK_UNUSED)
+ continue;
+
+ if (off < eoh) {
+ printf("block %d (offset 0x%x) clobbers headers\n",
+ i, off);
+ return -EINVAL;
+ }
+
+ if (off + block_size > eof) {
+ printf("block %d (offset 0x%x) clobbers footer\n",
+ i, off);
+ return -EINVAL;
+ }
+
+ for (j = 0; j < vhd->header.max_bat_size; j++) {
+ uint32_t joff = vhd->bat.bat[j];
+
+ if (i == j)
+ continue;
+
+ if (joff == DD_BLK_UNUSED)
+ continue;
+
+ if (off == joff)
+ err = -EINVAL;
+
+ if (off > joff && off < joff + block_size)
+ err = -EINVAL;
+
+ if (off + block_size > joff &&
+ off + block_size < joff + block_size)
+ err = -EINVAL;
+
+ if (err) {
+ printf("block %d (offset 0x%x) clobbers "
+ "block %d (offset 0x%x)\n",
+ i, off, j, joff);
+ return err;
+ }
+ }
+ }
+
+ return 0;
+}
+
+static int
+vhd_util_check_batmap(vhd_context_t *vhd)
+{
+ char *msg;
+ int i, err;
+
+ err = vhd_get_bat(vhd);
+ if (err) {
+ printf("error reading bat: %d\n", err);
+ return err;
+ }
+
+ err = vhd_get_batmap(vhd);
+ if (err) {
+ printf("error reading batmap: %d\n", err);
+ return err;
+ }
+
+ msg = vhd_util_check_validate_batmap(vhd, &vhd->batmap);
+ if (msg) {
+ printf("batmap is invalid: %s\n", msg);
+ return -EINVAL;
+ }
+
+ for (i = 0; i < vhd->header.max_bat_size; i++) {
+ if (!vhd_batmap_test(vhd, &vhd->batmap, i))
+ continue;
+
+ if (vhd->bat.bat[i] == DD_BLK_UNUSED) {
+ printf("batmap shows unallocated block %d full\n", i);
+ return -EINVAL;
+ }
+ }
+
+ return 0;
+}
+
+static int
+vhd_util_check_parent_locators(vhd_context_t *vhd)
+{
+ int i, n, err;
+ vhd_parent_locator_t *loc;
+ char *msg, *file, *ppath, *location, *pname;
+ int mac, macx, w2ku, w2ru, wi2r, wi2k, found;
+
+ mac = 0;
+ macx = 0;
+ w2ku = 0;
+ w2ru = 0;
+ wi2r = 0;
+ wi2k = 0;
+ found = 0;
+ pname = NULL;
+ ppath = NULL;
+ location = NULL;
+
+ err = vhd_header_decode_parent(vhd, &vhd->header, &pname);
+ if (err) {
+ printf("error decoding parent name: %d\n", err);
+ return err;
+ }
+
+ n = sizeof(vhd->header.loc) / sizeof(vhd->header.loc[0]);
+ for (i = 0; i < n; i++) {
+ ppath = NULL;
+ location = NULL;
+ loc = vhd->header.loc + i;
+
+ msg = vhd_util_check_validate_parent_locator(vhd, loc);
+ if (msg) {
+ err = -EINVAL;
+ printf("invalid parent locator %d: %s\n", i, msg);
+ goto out;
+ }
+
+ if (loc->code == PLAT_CODE_NONE)
+ continue;
+
+ switch (loc->code) {
+ case PLAT_CODE_MACX:
+ if (macx++)
+ goto dup;
+ break;
+
+ case PLAT_CODE_MAC:
+ if (mac++)
+ goto dup;
+ break;
+
+ case PLAT_CODE_W2KU:
+ if (w2ku++)
+ goto dup;
+ break;
+
+ case PLAT_CODE_W2RU:
+ if (w2ru++)
+ goto dup;
+ break;
+
+ case PLAT_CODE_WI2R:
+ if (wi2r++)
+ goto dup;
+ break;
+
+ case PLAT_CODE_WI2K:
+ if (wi2k++)
+ goto dup;
+ break;
+
+ default:
+ err = -EINVAL;
+ printf("invalid platform code for locator %d\n", i);
+ goto out;
+ }
+
+ if (loc->code != PLAT_CODE_MACX &&
+ loc->code != PLAT_CODE_W2RU &&
+ loc->code != PLAT_CODE_W2KU)
+ continue;
+
+ err = vhd_parent_locator_read(vhd, loc, &ppath);
+ if (err) {
+ printf("error reading parent locator %d: %d\n", i, err);
+ goto out;
+ }
+
+ file = basename(ppath);
+ if (strcmp(pname, file)) {
+ err = -EINVAL;
+ printf("parent locator %d name (%s) does not match "
+ "header name (%s)\n", i, file, pname);
+ goto out;
+ }
+
+ err = vhd_find_parent(vhd, ppath, &location);
+ if (err) {
+ printf("error resolving %s: %d\n", ppath, err);
+ goto out;
+ }
+
+ err = access(location, R_OK);
+ if (err && loc->code == PLAT_CODE_MACX) {
+ err = -errno;
+ printf("parent locator %d points to missing file %s "
+ "(resolved to %s)\n", i, ppath, location);
+ goto out;
+ }
+
+ msg = vhd_util_check_validate_parent(vhd, location);
+ if (msg) {
+ err = -EINVAL;
+ printf("invalid parent %s: %s\n", location, msg);
+ goto out;
+ }
+
+ found++;
+ free(ppath);
+ free(location);
+ ppath = NULL;
+ location = NULL;
+
+ continue;
+
+ dup:
+ printf("duplicate platform code in locator %d: 0x%x\n",
+ i, loc->code);
+ err = -EINVAL;
+ goto out;
+ }
+
+ if (!found) {
+ err = -EINVAL;
+ printf("could not find parent %s\n", pname);
+ goto out;
+ }
+
+ err = 0;
+
+out:
+ free(pname);
+ free(ppath);
+ free(location);
+ return err;
+}
+
+static void
+vhd_util_dump_headers(const char *name)
+{
+ char *argv[] = { "read", "-p", "-n", (char *)name };
+ int argc = sizeof(argv) / sizeof(argv[0]);
+
+ printf("%s appears invalid; dumping metadata\n", name);
+ vhd_util_read(argc, argv);
+}
+
+static int
+vhd_util_check_vhd(const char *name, int ignore)
+{
+ int fd, err;
+ vhd_context_t vhd;
+ struct stat stats;
+ vhd_footer_t footer;
+
+ fd = -1;
+ memset(&vhd, 0, sizeof(vhd));
+
+ err = stat(name, &stats);
+ if (err == -1) {
+ printf("cannot stat %s: %d\n", name, errno);
+ return -errno;
+ }
+
+ if (!S_ISREG(stats.st_mode) && !S_ISBLK(stats.st_mode)) {
+ printf("%s is not a regular file or block device\n", name);
+ return -EINVAL;
+ }
+
+ fd = open(name, O_RDONLY | O_DIRECT | O_LARGEFILE);
+ if (fd == -1) {
+ printf("error opening %s\n", name);
+ return -errno;
+ }
+
+ err = vhd_util_check_footer(fd, &footer, ignore);
+ if (err)
+ goto out;
+
+ if (footer.type != HD_TYPE_DYNAMIC && footer.type != HD_TYPE_DIFF)
+ goto out;
+
+ err = vhd_util_check_header(fd, &footer);
+ if (err)
+ goto out;
+
+ err = vhd_open(&vhd, name, VHD_OPEN_RDONLY | VHD_OPEN_IGNORE_DISABLED);
+ if (err)
+ goto out;
+
+ err = vhd_util_check_differencing_header(&vhd);
+ if (err)
+ goto out;
+
+ err = vhd_util_check_bat(&vhd);
+ if (err)
+ goto out;
+
+ if (vhd_has_batmap(&vhd)) {
+ err = vhd_util_check_batmap(&vhd);
+ if (err)
+ goto out;
+ }
+
+ if (vhd.footer.type == HD_TYPE_DIFF) {
+ err = vhd_util_check_parent_locators(&vhd);
+ if (err)
+ goto out;
+ }
+
+ err = 0;
+ printf("%s is valid\n", name);
+
+out:
+ if (err)
+ vhd_util_dump_headers(name);
+ if (fd != -1)
+ close(fd);
+ vhd_close(&vhd);
+ return err;
+}
+
+static int
+vhd_util_check_parents(const char *name, int ignore)
+{
+ int err;
+ vhd_context_t vhd;
+ char *cur, *parent;
+
+ cur = (char *)name;
+
+ for (;;) {
+ err = vhd_open(&vhd, cur,
+ VHD_OPEN_RDONLY | VHD_OPEN_IGNORE_DISABLED);
+ if (err)
+ goto out;
+
+ if (vhd.footer.type != HD_TYPE_DIFF || vhd_parent_raw(&vhd)) {
+ vhd_close(&vhd);
+ goto out;
+ }
+
+ err = vhd_parent_locator_get(&vhd, &parent);
+ vhd_close(&vhd);
+
+ if (err) {
+ printf("error getting parent: %d\n", err);
+ goto out;
+ }
+
+ if (cur != name)
+ free(cur);
+ cur = parent;
+
+ err = vhd_util_check_vhd(cur, ignore);
+ if (err)
+ goto out;
+ }
+
+out:
+ if (err)
+ printf("error checking parents: %d\n", err);
+ if (cur != name)
+ free(cur);
+ return err;
+}
+
+int
+vhd_util_check(int argc, char **argv)
+{
+ char *name;
+ vhd_context_t vhd;
+ int c, err, ignore, parents;
+
+ if (!argc || !argv) {
+ err = -EINVAL;
+ goto usage;
+ }
+
+ ignore = 0;
+ parents = 0;
+ name = NULL;
+
+ optind = 0;
+ while ((c = getopt(argc, argv, "n:iph")) != -1) {
+ switch (c) {
+ case 'n':
+ name = optarg;
+ break;
+ case 'i':
+ ignore = 1;
+ break;
+ case 'p':
+ parents = 1;
+ break;
+ case 'h':
+ err = 0;
+ goto usage;
+ default:
+ err = -EINVAL;
+ goto usage;
+ }
+ }
+
+ if (!name || optind != argc) {
+ err = -EINVAL;
+ goto usage;
+ }
+
+ err = vhd_util_check_vhd(name, ignore);
+ if (err)
+ goto out;
+
+ if (parents)
+ err = vhd_util_check_parents(name, ignore);
+
+out:
+ return err;
+
+usage:
+ printf("options: -n <file> [-i ignore missing primary footers] "
+ "[-p check parents] [-h help]\n");
+ return err;
+}
diff --git a/tools/blktap2/vhd/lib/vhd-util-coalesce.c b/tools/blktap2/vhd/lib/vhd-util-coalesce.c
new file mode 100644
index 0000000000..f6461fc687
--- /dev/null
+++ b/tools/blktap2/vhd/lib/vhd-util-coalesce.c
@@ -0,0 +1,218 @@
+/* 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 <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include "libvhd.h"
+
+static int
+__raw_io_write(int fd, char* buf, uint64_t sec, uint32_t secs)
+{
+ off64_t off;
+ size_t ret;
+
+ errno = 0;
+ off = lseek64(fd, vhd_sectors_to_bytes(sec), SEEK_SET);
+ if (off == (off64_t)-1) {
+ printf("raw parent: seek(0x%08"PRIx64") failed: %d\n",
+ vhd_sectors_to_bytes(sec), -errno);
+ return -errno;
+ }
+
+ ret = write(fd, buf, vhd_sectors_to_bytes(secs));
+ if (ret == vhd_sectors_to_bytes(secs))
+ return 0;
+
+ printf("raw parent: write of 0x%"PRIx64" returned %zd, errno: %d\n",
+ vhd_sectors_to_bytes(secs), ret, -errno);
+ return (errno ? -errno : -EIO);
+}
+
+/*
+ * Use 'parent' if the parent is VHD, and 'parent_fd' if the parent is raw
+ */
+static int
+vhd_util_coalesce_block(vhd_context_t *vhd, vhd_context_t *parent,
+ int parent_fd, uint64_t block)
+{
+ int i, err;
+ char *buf, *map;
+ uint64_t sec, secs;
+
+ buf = NULL;
+ map = NULL;
+ sec = block * vhd->spb;
+
+ if (vhd->bat.bat[block] == DD_BLK_UNUSED)
+ return 0;
+
+ err = posix_memalign((void **)&buf, 4096, vhd->header.block_size);
+ if (err)
+ return -err;
+
+ err = vhd_io_read(vhd, buf, sec, vhd->spb);
+ if (err)
+ goto done;
+
+ if (vhd_has_batmap(vhd) && vhd_batmap_test(vhd, &vhd->batmap, block)) {
+ if (parent->file)
+ err = vhd_io_write(parent, buf, sec, vhd->spb);
+ else
+ err = __raw_io_write(parent_fd, buf, sec, vhd->spb);
+ goto done;
+ }
+
+ err = vhd_read_bitmap(vhd, block, &map);
+ if (err)
+ goto done;
+
+ for (i = 0; i < vhd->spb; i++) {
+ if (!vhd_bitmap_test(vhd, map, i))
+ continue;
+
+ for (secs = 0; i + secs < vhd->spb; secs++)
+ if (!vhd_bitmap_test(vhd, map, i + secs))
+ break;
+
+ if (parent->file)
+ err = vhd_io_write(parent,
+ buf + vhd_sectors_to_bytes(i),
+ sec + i, secs);
+ else
+ err = __raw_io_write(parent_fd,
+ buf + vhd_sectors_to_bytes(i),
+ sec + i, secs);
+ if (err)
+ goto done;
+
+ i += secs;
+ }
+
+ err = 0;
+
+done:
+ free(buf);
+ free(map);
+ return err;
+}
+
+int
+vhd_util_coalesce(int argc, char **argv)
+{
+ int err, c;
+ uint64_t i;
+ char *name, *pname;
+ vhd_context_t vhd, parent;
+ int parent_fd = -1;
+
+ name = NULL;
+ pname = NULL;
+ parent.file = NULL;
+
+ if (!argc || !argv)
+ goto usage;
+
+ optind = 0;
+ while ((c = getopt(argc, argv, "n:h")) != -1) {
+ switch (c) {
+ case 'n':
+ name = optarg;
+ break;
+ case 'h':
+ default:
+ goto usage;
+ }
+ }
+
+ if (!name || optind != argc)
+ goto usage;
+
+ err = vhd_open(&vhd, name, VHD_OPEN_RDONLY);
+ if (err) {
+ printf("error opening %s: %d\n", name, err);
+ return err;
+ }
+
+ err = vhd_parent_locator_get(&vhd, &pname);
+ if (err) {
+ printf("error finding %s parent: %d\n", name, err);
+ vhd_close(&vhd);
+ return err;
+ }
+
+ if (vhd_parent_raw(&vhd)) {
+ parent_fd = open(pname, O_RDWR | O_DIRECT | O_LARGEFILE, 0644);
+ if (parent_fd == -1) {
+ err = -errno;
+ printf("failed to open parent %s: %d\n", pname, err);
+ vhd_close(&vhd);
+ return err;
+ }
+ } else {
+ err = vhd_open(&parent, pname, VHD_OPEN_RDWR);
+ if (err) {
+ printf("error opening %s: %d\n", pname, err);
+ free(pname);
+ vhd_close(&vhd);
+ return err;
+ }
+ }
+
+ err = vhd_get_bat(&vhd);
+ if (err)
+ goto done;
+
+ if (vhd_has_batmap(&vhd)) {
+ err = vhd_get_batmap(&vhd);
+ if (err)
+ goto done;
+ }
+
+ for (i = 0; i < vhd.bat.entries; i++) {
+ err = vhd_util_coalesce_block(&vhd, &parent, parent_fd, i);
+ if (err)
+ goto done;
+ }
+
+ err = 0;
+
+ done:
+ free(pname);
+ vhd_close(&vhd);
+ if (parent.file)
+ vhd_close(&parent);
+ else
+ close(parent_fd);
+ return err;
+
+usage:
+ printf("options: <-n name> [-h help]\n");
+ return -EINVAL;
+}
diff --git a/tools/blktap2/vhd/lib/vhd-util-create.c b/tools/blktap2/vhd/lib/vhd-util-create.c
new file mode 100644
index 0000000000..a9bdf05fee
--- /dev/null
+++ b/tools/blktap2/vhd/lib/vhd-util-create.c
@@ -0,0 +1,80 @@
+/* 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 <stdlib.h>
+#include <unistd.h>
+
+#include "libvhd.h"
+
+int
+vhd_util_create(int argc, char **argv)
+{
+ char *name;
+ uint64_t size;
+ int c, sparse, err;
+ vhd_flag_creat_t flags;
+
+ err = -EINVAL;
+ size = 0;
+ sparse = 1;
+ name = NULL;
+ flags = 0;
+
+ if (!argc || !argv)
+ goto usage;
+
+ optind = 0;
+ while ((c = getopt(argc, argv, "n:s:rh")) != -1) {
+ switch (c) {
+ case 'n':
+ name = optarg;
+ break;
+ case 's':
+ err = 0;
+ size = strtoull(optarg, NULL, 10);
+ break;
+ case 'r':
+ sparse = 0;
+ break;
+ case 'h':
+ default:
+ goto usage;
+ }
+ }
+
+ if (err || !name || optind != argc)
+ goto usage;
+
+ return vhd_create(name, size << 20,
+ (sparse ? HD_TYPE_DYNAMIC : HD_TYPE_FIXED),
+ flags);
+
+usage:
+ printf("options: <-n name> <-s size (MB)> [-r reserve] [-h help]\n");
+ return -EINVAL;
+}
diff --git a/tools/blktap2/vhd/lib/vhd-util-fill.c b/tools/blktap2/vhd/lib/vhd-util-fill.c
new file mode 100644
index 0000000000..afbfccee48
--- /dev/null
+++ b/tools/blktap2/vhd/lib/vhd-util-fill.c
@@ -0,0 +1,105 @@
+/* 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 <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include "libvhd.h"
+
+int
+vhd_util_fill(int argc, char **argv)
+{
+ int err, c;
+ char *buf, *name;
+ vhd_context_t vhd;
+ uint64_t i, sec, secs;
+
+ buf = NULL;
+ name = NULL;
+
+ if (!argc || !argv)
+ goto usage;
+
+ optind = 0;
+ while ((c = getopt(argc, argv, "n:h")) != -1) {
+ switch (c) {
+ case 'n':
+ name = optarg;
+ break;
+ case 'h':
+ default:
+ goto usage;
+ }
+ }
+
+ if (!name || optind != argc)
+ goto usage;
+
+ err = vhd_open(&vhd, name, VHD_OPEN_RDWR);
+ if (err) {
+ printf("error opening %s: %d\n", name, err);
+ return err;
+ }
+
+ err = vhd_get_bat(&vhd);
+ if (err)
+ goto done;
+
+ err = posix_memalign((void **)&buf, 4096, vhd.header.block_size);
+ if (err) {
+ err = -err;
+ goto done;
+ }
+
+ sec = 0;
+ secs = vhd.header.block_size >> VHD_SECTOR_SHIFT;
+
+ for (i = 0; i < vhd.header.max_bat_size; i++) {
+ err = vhd_io_read(&vhd, buf, sec, secs);
+ if (err)
+ goto done;
+
+ err = vhd_io_write(&vhd, buf, sec, secs);
+ if (err)
+ goto done;
+
+ sec += secs;
+ }
+
+ err = 0;
+
+ done:
+ free(buf);
+ vhd_close(&vhd);
+ return err;
+
+usage:
+ printf("options: <-n name> [-h help]\n");
+ return -EINVAL;
+}
diff --git a/tools/blktap2/vhd/lib/vhd-util-modify.c b/tools/blktap2/vhd/lib/vhd-util-modify.c
new file mode 100644
index 0000000000..3b07e31b25
--- /dev/null
+++ b/tools/blktap2/vhd/lib/vhd-util-modify.c
@@ -0,0 +1,132 @@
+/* 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.
+ *
+ * Altering operations:
+ *
+ * 1. Change the parent pointer to another file.
+ * 2. Change the size of the file containing the VHD image. This does NOT
+ * affect the VHD disk capacity, only the physical size of the file containing
+ * the VHD. Naturally, it is not possible to set the file size to be less than
+ * the what VHD utilizes.
+ * The operation doesn't actually change the file size, but it writes the
+ * footer in the right location such that resizing the file (manually, as a
+ * separate step) will produce the correct results. If the new file size is
+ * greater than the current file size, the file must first be expanded and then
+ * altered with this operation. If the new size is smaller than the current
+ * size, the VHD must first be altered with this operation and then the file
+ * must be shrunk. Failing to resize the file will result in a corrupted VHD.
+ */
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include "libvhd.h"
+
+TEST_FAIL_EXTERN_VARS;
+
+int
+vhd_util_modify(int argc, char **argv)
+{
+ char *name;
+ vhd_context_t vhd;
+ int err, c, size, parent, parent_raw;
+ off64_t newsize = 0;
+ char *newparent = NULL;
+
+ name = NULL;
+ size = 0;
+ parent = 0;
+ parent_raw = 0;
+
+ optind = 0;
+ while ((c = getopt(argc, argv, "n:s:p:mh")) != -1) {
+ switch (c) {
+ case 'n':
+ name = optarg;
+ break;
+ case 's':
+ size = 1;
+ errno = 0;
+ newsize = strtoll(optarg, NULL, 10);
+ if (errno) {
+ fprintf(stderr, "Invalid size '%s'\n", optarg);
+ goto usage;
+ }
+ break;
+ case 'p':
+ parent = 1;
+ newparent = optarg;
+ break;
+ case 'm':
+ parent_raw = 1;
+ break;
+
+ case 'h':
+ default:
+ goto usage;
+ }
+ }
+
+ if (!name || optind != argc)
+ goto usage;
+
+ err = vhd_open(&vhd, name, VHD_OPEN_RDWR);
+ if (err) {
+ printf("error opening %s: %d\n", name, err);
+ return err;
+ }
+
+ if (size) {
+ err = vhd_set_phys_size(&vhd, newsize);
+ if (err)
+ printf("failed to set physical size to %"PRIu64":"
+ " %d\n", newsize, err);
+ }
+
+ if (parent) {
+ TEST_FAIL_AT(FAIL_REPARENT_BEGIN);
+ err = vhd_change_parent(&vhd, newparent, parent_raw);
+ if (err) {
+ printf("failed to set parent to '%s': %d\n",
+ newparent, err);
+ goto done;
+ }
+ TEST_FAIL_AT(FAIL_REPARENT_END);
+ }
+
+done:
+ vhd_close(&vhd);
+ return err;
+
+usage:
+ printf("*** Dangerous operations, use with care ***\n");
+ printf("options: <-n name> [-p NEW_PARENT set parent [-m raw]] "
+ "[-s NEW_SIZE set size] [-h help]\n");
+ return -EINVAL;
+}
diff --git a/tools/blktap2/vhd/lib/vhd-util-query.c b/tools/blktap2/vhd/lib/vhd-util-query.c
new file mode 100644
index 0000000000..3477a17f27
--- /dev/null
+++ b/tools/blktap2/vhd/lib/vhd-util-query.c
@@ -0,0 +1,159 @@
+/* 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 <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include "libvhd.h"
+
+int
+vhd_util_query(int argc, char **argv)
+{
+ char *name;
+ vhd_context_t vhd;
+ off64_t currsize;
+ int ret, err, c, size, physize, parent, fields, depth;
+
+ name = NULL;
+ size = 0;
+ physize = 0;
+ parent = 0;
+ fields = 0;
+ depth = 0;
+
+ if (!argc || !argv) {
+ err = -EINVAL;
+ goto usage;
+ }
+
+ optind = 0;
+ while ((c = getopt(argc, argv, "n:vspfdh")) != -1) {
+ switch (c) {
+ case 'n':
+ name = optarg;
+ break;
+ case 'v':
+ size = 1;
+ break;
+ case 's':
+ physize = 1;
+ break;
+ case 'p':
+ parent = 1;
+ break;
+ case 'f':
+ fields = 1;
+ break;
+ case 'd':
+ depth = 1;
+ break;
+ case 'h':
+ err = 0;
+ goto usage;
+ default:
+ err = -EINVAL;
+ goto usage;
+ }
+ }
+
+ if (!name || optind != argc) {
+ err = -EINVAL;
+ goto usage;
+ }
+
+ err = vhd_open(&vhd, name, VHD_OPEN_RDONLY | VHD_OPEN_IGNORE_DISABLED);
+ if (err) {
+ printf("error opening %s: %d\n", name, err);
+ return err;
+ }
+
+ if (size)
+ printf("%"PRIu64"\n", vhd.footer.curr_size >> 20);
+
+ if (physize) {
+ err = vhd_get_phys_size(&vhd, &currsize);
+ if (err)
+ printf("failed to get physical size: %d\n", err);
+ else
+ printf("%"PRIu64"\n", currsize);
+ }
+
+ if (parent) {
+ ret = 0;
+
+ if (vhd.footer.type != HD_TYPE_DIFF)
+ printf("%s has no parent\n", name);
+ else {
+ char *pname;
+
+ ret = vhd_parent_locator_get(&vhd, &pname);
+ if (ret)
+ printf("query failed\n");
+ else {
+ printf("%s\n", pname);
+ free(pname);
+ }
+ }
+
+ err = (err ? : ret);
+ }
+
+ if (fields) {
+ int hidden;
+
+ ret = vhd_hidden(&vhd, &hidden);
+ if (ret)
+ printf("error checking 'hidden' field: %d\n", ret);
+ else
+ printf("hidden: %d\n", hidden);
+
+ err = (err ? : ret);
+ }
+
+ if (depth) {
+ int length;
+
+ ret = vhd_chain_depth(&vhd, &length);
+ if (ret)
+ printf("error checking chain depth: %d\n", ret);
+ else
+ printf("chain depth: %d\n", length);
+
+ err = (err ? : ret);
+ }
+
+ vhd_close(&vhd);
+ return err;
+
+usage:
+ printf("options: <-n name> [-v print virtual size (in MB)] "
+ "[-s print physical utilization (bytes)] [-p print parent] "
+ "[-f print fields] [-d print chain depth] [-h help]\n");
+ return err;
+}
diff --git a/tools/blktap2/vhd/lib/vhd-util-read.c b/tools/blktap2/vhd/lib/vhd-util-read.c
new file mode 100644
index 0000000000..7b5246c5f7
--- /dev/null
+++ b/tools/blktap2/vhd/lib/vhd-util-read.c
@@ -0,0 +1,742 @@
+/* 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 <inttypes.h>
+
+#include "libvhd.h"
+#include "vhd-util.h"
+
+#define nsize 15
+static char nbuf[nsize];
+
+static inline char *
+__xconv(uint64_t num)
+{
+ snprintf(nbuf, nsize, "%#" PRIx64 , num);
+ return nbuf;
+}
+
+static inline char *
+__dconv(uint64_t num)
+{
+ snprintf(nbuf, nsize, "%" PRIu64, num);
+ return nbuf;
+}
+
+#define conv(hex, num) \
+ (hex ? __xconv((uint64_t)num) : __dconv((uint64_t)num))
+
+static void
+vhd_print_header(vhd_context_t *vhd, vhd_header_t *h, int hex)
+{
+ int err;
+ uint32_t cksm;
+ char uuid[37], time_str[26], cookie[9], out[512], *name;
+
+ printf("VHD Header Summary:\n-------------------\n");
+
+ snprintf(cookie, 9, "%s", h->cookie);
+ printf("Cookie : %s\n", cookie);
+
+ printf("Data offset (unusd) : %s\n", conv(hex, h->data_offset));
+ printf("Table offset : %s\n", conv(hex, h->table_offset));
+ printf("Header version : 0x%08x\n", h->hdr_ver);
+ printf("Max BAT size : %s\n", conv(hex, h->max_bat_size));
+ printf("Block size : %s ", conv(hex, h->block_size));
+ printf("(%s MB)\n", conv(hex, h->block_size >> 20));
+
+ err = vhd_header_decode_parent(vhd, h, &name);
+ printf("Parent name : %s\n",
+ (err ? "failed to read name" : name));
+ free(name);
+
+ uuid_unparse(h->prt_uuid, uuid);
+ printf("Parent UUID : %s\n", uuid);
+
+ vhd_time_to_string(h->prt_ts, time_str);
+ printf("Parent timestamp : %s\n", time_str);
+
+ cksm = vhd_checksum_header(h);
+ printf("Checksum : 0x%x|0x%x (%s)\n", h->checksum, cksm,
+ h->checksum == cksm ? "Good!" : "Bad!");
+ printf("\n");
+}
+
+static void
+vhd_print_footer(vhd_footer_t *f, int hex)
+{
+ uint64_t c, h, s;
+ uint32_t ff_maj, ff_min, cr_maj, cr_min, cksm, cksm_save;
+ char time_str[26], creator[5], uuid[37], cookie[9];
+
+ printf("VHD Footer Summary:\n-------------------\n");
+
+ snprintf(cookie, 9, "%s", f->cookie);
+ printf("Cookie : %s\n", cookie);
+
+ printf("Features : (0x%08x) %s%s\n", f->features,
+ (f->features & HD_TEMPORARY) ? "<TEMP>" : "",
+ (f->features & HD_RESERVED) ? "<RESV>" : "");
+
+ ff_maj = f->ff_version >> 16;
+ ff_min = f->ff_version & 0xffff;
+ printf("File format version : Major: %d, Minor: %d\n",
+ ff_maj, ff_min);
+
+ printf("Data offset : %s\n", conv(hex, f->data_offset));
+
+ vhd_time_to_string(f->timestamp, time_str);
+ printf("Timestamp : %s\n", time_str);
+
+ memcpy(creator, f->crtr_app, 4);
+ creator[4] = '\0';
+ printf("Creator Application : '%s'\n", creator);
+
+ cr_maj = f->crtr_ver >> 16;
+ cr_min = f->crtr_ver & 0xffff;
+ printf("Creator version : Major: %d, Minor: %d\n",
+ cr_maj, cr_min);
+
+ printf("Creator OS : %s\n",
+ ((f->crtr_os == HD_CR_OS_WINDOWS) ? "Windows" :
+ ((f->crtr_os == HD_CR_OS_MACINTOSH) ? "Macintosh" :
+ "Unknown!")));
+
+ printf("Original disk size : %s MB ", conv(hex, f->orig_size >> 20));
+ printf("(%s Bytes)\n", conv(hex, f->orig_size));
+
+ printf("Current disk size : %s MB ", conv(hex, f->curr_size >> 20));
+ printf("(%s Bytes)\n", conv(hex, f->curr_size));
+
+ c = f->geometry >> 16;
+ h = (f->geometry & 0x0000FF00) >> 8;
+ s = f->geometry & 0x000000FF;
+ printf("Geometry : Cyl: %s, ", conv(hex, c));
+ printf("Hds: %s, ", conv(hex, h));
+ printf("Sctrs: %s\n", conv(hex, s));
+ printf(" : = %s MB ", conv(hex, (c * h * s) >> 11));
+ printf("(%s Bytes)\n", conv(hex, c * h * s << 9));
+
+ printf("Disk type : %s\n",
+ f->type <= HD_TYPE_MAX ?
+ HD_TYPE_STR[f->type] : "Unknown type!\n");
+
+ cksm = vhd_checksum_footer(f);
+ printf("Checksum : 0x%x|0x%x (%s)\n", f->checksum, cksm,
+ f->checksum == cksm ? "Good!" : "Bad!");
+
+ uuid_unparse(f->uuid, uuid);
+ printf("UUID : %s\n", uuid);
+
+ printf("Saved state : %s\n", f->saved == 0 ? "No" : "Yes");
+ printf("Hidden : %d\n", f->hidden);
+ printf("\n");
+}
+
+static inline char *
+code_name(uint32_t code)
+{
+ switch(code) {
+ case PLAT_CODE_NONE:
+ return "PLAT_CODE_NONE";
+ case PLAT_CODE_WI2R:
+ return "PLAT_CODE_WI2R";
+ case PLAT_CODE_WI2K:
+ return "PLAT_CODE_WI2K";
+ case PLAT_CODE_W2RU:
+ return "PLAT_CODE_W2RU";
+ case PLAT_CODE_W2KU:
+ return "PLAT_CODE_W2KU";
+ case PLAT_CODE_MAC:
+ return "PLAT_CODE_MAC";
+ case PLAT_CODE_MACX:
+ return "PLAT_CODE_MACX";
+ default:
+ return "UNKOWN";
+ }
+}
+
+static void
+vhd_print_parent(vhd_context_t *vhd, vhd_parent_locator_t *loc)
+{
+ int err;
+ char *buf;
+
+ err = vhd_parent_locator_read(vhd, loc, &buf);
+ if (err) {
+ printf("failed to read parent name\n");
+ return;
+ }
+
+ printf(" decoded name : %s\n", buf);
+}
+
+static void
+vhd_print_parent_locators(vhd_context_t *vhd, int hex)
+{
+ int i, n;
+ vhd_parent_locator_t *loc;
+
+ printf("VHD Parent Locators:\n--------------------\n");
+
+ n = sizeof(vhd->header.loc) / sizeof(struct prt_loc);
+ for (i = 0; i < n; i++) {
+ loc = &vhd->header.loc[i];
+
+ if (loc->code == PLAT_CODE_NONE)
+ continue;
+
+ printf("locator: : %d\n", i);
+ printf(" code : %s\n",
+ code_name(loc->code));
+ printf(" data_space : %s\n",
+ conv(hex, loc->data_space));
+ printf(" data_length : %s\n",
+ conv(hex, loc->data_len));
+ printf(" data_offset : %s\n",
+ conv(hex, loc->data_offset));
+ vhd_print_parent(vhd, loc);
+ printf("\n");
+ }
+}
+
+static void
+vhd_print_batmap_header(vhd_batmap_t *batmap, int hex)
+{
+ uint32_t cksm;
+
+ printf("VHD Batmap Summary:\n-------------------\n");
+ printf("Batmap offset : %s\n",
+ conv(hex, batmap->header.batmap_offset));
+ printf("Batmap size (secs) : %s\n",
+ conv(hex, batmap->header.batmap_size));
+ printf("Batmap version : 0x%08x\n",
+ batmap->header.batmap_version);
+
+ cksm = vhd_checksum_batmap(batmap);
+ printf("Checksum : 0x%x|0x%x (%s)\n",
+ batmap->header.checksum, cksm,
+ (batmap->header.checksum == cksm ? "Good!" : "Bad!"));
+ printf("\n");
+}
+
+static inline int
+check_block_range(vhd_context_t *vhd, uint64_t block, int hex)
+{
+ if (block > vhd->header.max_bat_size) {
+ fprintf(stderr, "block %s past end of file\n",
+ conv(hex, block));
+ return -ERANGE;
+ }
+
+ return 0;
+}
+
+static int
+vhd_print_headers(vhd_context_t *vhd, int hex)
+{
+ int err;
+
+ vhd_print_footer(&vhd->footer, hex);
+
+ if (vhd_type_dynamic(vhd)) {
+ vhd_print_header(vhd, &vhd->header, hex);
+
+ if (vhd->footer.type == HD_TYPE_DIFF)
+ vhd_print_parent_locators(vhd, hex);
+
+ if (vhd_has_batmap(vhd)) {
+ err = vhd_get_batmap(vhd);
+ if (err) {
+ printf("failed to get batmap header\n");
+ return err;
+ }
+
+ vhd_print_batmap_header(&vhd->batmap, hex);
+ }
+ }
+
+ return 0;
+}
+
+static int
+vhd_dump_headers(const char *name, int hex)
+{
+ vhd_context_t vhd;
+
+ libvhd_set_log_level(1);
+ memset(&vhd, 0, sizeof(vhd));
+
+ printf("\n%s appears invalid; dumping headers\n\n", name);
+
+ vhd.fd = open(name, O_DIRECT | O_LARGEFILE | O_RDONLY);
+ if (vhd.fd == -1)
+ return -errno;
+
+ vhd.file = strdup(name);
+
+ vhd_read_footer(&vhd, &vhd.footer);
+ vhd_read_header(&vhd, &vhd.header);
+
+ vhd_print_footer(&vhd.footer, hex);
+ vhd_print_header(&vhd, &vhd.header, hex);
+
+ close(vhd.fd);
+ free(vhd.file);
+
+ return 0;
+}
+
+static int
+vhd_print_logical_to_physical(vhd_context_t *vhd,
+ uint64_t sector, int count, int hex)
+{
+ int i;
+ uint32_t blk, lsec;
+ uint64_t cur, offset;
+
+ if (vhd_sectors_to_bytes(sector + count) > vhd->footer.curr_size) {
+ fprintf(stderr, "sector %s past end of file\n",
+ conv(hex, sector + count));
+ return -ERANGE;
+ }
+
+ for (i = 0; i < count; i++) {
+ cur = sector + i;
+ blk = cur / vhd->spb;
+ lsec = cur % vhd->spb;
+ offset = vhd->bat.bat[blk];
+
+ if (offset != DD_BLK_UNUSED) {
+ offset += lsec + 1;
+ offset = vhd_sectors_to_bytes(offset);
+ }
+
+ printf("logical sector %s: ", conv(hex, cur));
+ printf("block number: %s, ", conv(hex, blk));
+ printf("sector offset: %s, ", conv(hex, lsec));
+ printf("file offset: %s\n", (offset == DD_BLK_UNUSED ?
+ "not allocated" : conv(hex, offset)));
+ }
+
+ return 0;
+}
+
+static int
+vhd_print_bat(vhd_context_t *vhd, uint64_t block, int count, int hex)
+{
+ int i;
+ uint64_t cur, offset;
+
+ if (check_block_range(vhd, block + count, hex))
+ return -ERANGE;
+
+ for (i = 0; i < count; i++) {
+ cur = block + i;
+ offset = vhd->bat.bat[cur];
+
+ printf("block: %s: ", conv(hex, cur));
+ printf("offset: %s\n",
+ (offset == DD_BLK_UNUSED ? "not allocated" :
+ conv(hex, vhd_sectors_to_bytes(offset))));
+ }
+
+ return 0;
+}
+
+static inline void
+write_full(int fd, void* buf, size_t count)
+{
+ ssize_t num_written = 0;
+ if (!buf) return;
+
+
+ while(count > 0) {
+
+ num_written = write(fd, buf, count);
+ if (num_written == -1) {
+ if (errno == EINTR)
+ continue;
+ else
+ return;
+ }
+
+ count -= num_written;
+ buf += num_written;
+ }
+}
+
+static int
+vhd_print_bitmap(vhd_context_t *vhd, uint64_t block, int count, int hex)
+{
+ char *buf;
+ int i, err;
+ uint64_t cur;
+
+ if (check_block_range(vhd, block + count, hex))
+ return -ERANGE;
+
+ for (i = 0; i < count; i++) {
+ cur = block + i;
+
+ if (vhd->bat.bat[cur] == DD_BLK_UNUSED) {
+ printf("block %s not allocated\n", conv(hex, cur));
+ continue;
+ }
+
+ err = vhd_read_bitmap(vhd, cur, &buf);
+ if (err)
+ goto out;
+
+ write_full(STDOUT_FILENO, buf,
+ vhd_sectors_to_bytes(vhd->bm_secs));
+ free(buf);
+ }
+
+ err = 0;
+out:
+ return err;
+}
+
+static int
+vhd_test_bitmap(vhd_context_t *vhd, uint64_t sector, int count, int hex)
+{
+ char *buf;
+ uint64_t cur;
+ int i, err, bit;
+ uint32_t blk, bm_blk, sec;
+
+ if (vhd_sectors_to_bytes(sector + count) > vhd->footer.curr_size) {
+ printf("sector %s past end of file\n", conv(hex, sector));
+ return -ERANGE;
+ }
+
+ bm_blk = -1;
+ buf = NULL;
+
+ for (i = 0; i < count; i++) {
+ cur = sector + i;
+ blk = cur / vhd->spb;
+ sec = cur % vhd->spb;
+
+ if (blk != bm_blk) {
+ bm_blk = blk;
+ free(buf);
+ buf = NULL;
+
+ if (vhd->bat.bat[blk] != DD_BLK_UNUSED) {
+ err = vhd_read_bitmap(vhd, blk, &buf);
+ if (err)
+ goto out;
+ }
+ }
+
+ if (vhd->bat.bat[blk] == DD_BLK_UNUSED)
+ bit = 0;
+ else
+ bit = vhd_bitmap_test(vhd, buf, blk);
+
+ print:
+ printf("block %s: ", conv(hex, blk));
+ printf("sec: %s: %d\n", conv(hex, sec), bit);
+ }
+
+ err = 0;
+ out:
+ free(buf);
+ return err;
+}
+
+static int
+vhd_print_batmap(vhd_context_t *vhd)
+{
+ int err;
+ size_t size;
+
+ err = vhd_get_batmap(vhd);
+ if (err) {
+ printf("failed to read batmap: %d\n", err);
+ return err;
+ }
+
+ size = vhd_sectors_to_bytes(vhd->batmap.header.batmap_size);
+ write_full(STDOUT_FILENO, vhd->batmap.map, size);
+
+ return 0;
+}
+
+static int
+vhd_test_batmap(vhd_context_t *vhd, uint64_t block, int count, int hex)
+{
+ int i, err;
+ uint64_t cur;
+
+ if (check_block_range(vhd, block + count, hex))
+ return -ERANGE;
+
+ err = vhd_get_batmap(vhd);
+ if (err) {
+ fprintf(stderr, "failed to get batmap\n");
+ return err;
+ }
+
+ for (i = 0; i < count; i++) {
+ cur = block + i;
+ fprintf(stderr, "batmap for block %s: %d\n", conv(hex, cur),
+ vhd_batmap_test(vhd, &vhd->batmap, cur));
+ }
+
+ return 0;
+}
+
+static int
+vhd_print_data(vhd_context_t *vhd, uint64_t block, int count, int hex)
+{
+ char *buf;
+ int i, err;
+ uint64_t cur;
+
+ err = 0;
+
+ if (check_block_range(vhd, block + count, hex))
+ return -ERANGE;
+
+ for (i = 0; i < count; i++) {
+ cur = block + i;
+
+ if (vhd->bat.bat[cur] == DD_BLK_UNUSED) {
+ printf("block %s not allocated\n", conv(hex, cur));
+ continue;
+ }
+
+ err = vhd_read_block(vhd, cur, &buf);
+ if (err)
+ break;
+
+ write_full(STDOUT_FILENO, buf, vhd->header.block_size);
+ free(buf);
+ }
+
+ return err;
+}
+
+static int
+vhd_read_data(vhd_context_t *vhd, uint64_t sec, int count, int hex)
+{
+ char *buf;
+ uint64_t cur;
+ int err, max, secs;
+
+ if (vhd_sectors_to_bytes(sec + count) > vhd->footer.curr_size)
+ return -ERANGE;
+
+ max = MIN(vhd_sectors_to_bytes(count), VHD_BLOCK_SIZE);
+ err = posix_memalign((void **)&buf, VHD_SECTOR_SIZE, max);
+ if (err)
+ return -err;
+
+ cur = sec;
+ while (count) {
+ secs = MIN((max >> VHD_SECTOR_SHIFT), count);
+ err = vhd_io_read(vhd, buf, cur, secs);
+ if (err)
+ break;
+
+ write_full(STDOUT_FILENO, buf, vhd_sectors_to_bytes(secs));
+
+ cur += secs;
+ count -= secs;
+ }
+
+ free(buf);
+ return err;
+}
+
+int
+vhd_util_read(int argc, char **argv)
+{
+ char *name;
+ vhd_context_t vhd;
+ int c, err, headers, hex;
+ uint64_t bat, bitmap, tbitmap, batmap, tbatmap, data, lsec, count, read;
+
+ err = 0;
+ hex = 0;
+ headers = 0;
+ count = 1;
+ bat = -1;
+ bitmap = -1;
+ tbitmap = -1;
+ batmap = -1;
+ tbatmap = -1;
+ data = -1;
+ lsec = -1;
+ read = -1;
+ name = NULL;
+
+ if (!argc || !argv)
+ goto usage;
+
+ optind = 0;
+ while ((c = getopt(argc, argv, "n:pt:b:m:i:aj:d:c:r:xh")) != -1) {
+ switch(c) {
+ case 'n':
+ name = optarg;
+ break;
+ case 'p':
+ headers = 1;
+ break;
+ case 't':
+ lsec = strtoul(optarg, NULL, 10);
+ break;
+ case 'b':
+ bat = strtoull(optarg, NULL, 10);
+ break;
+ case 'm':
+ bitmap = strtoull(optarg, NULL, 10);
+ break;
+ case 'i':
+ tbitmap = strtoul(optarg, NULL, 10);
+ break;
+ case 'a':
+ batmap = 1;
+ break;
+ case 'j':
+ tbatmap = strtoull(optarg, NULL, 10);
+ break;
+ case 'd':
+ data = strtoull(optarg, NULL, 10);
+ break;
+ case 'r':
+ read = strtoull(optarg, NULL, 10);
+ break;
+ case 'c':
+ count = strtoul(optarg, NULL, 10);
+ break;
+ case 'x':
+ hex = 1;
+ break;
+ case 'h':
+ default:
+ goto usage;
+ }
+ }
+
+ if (!name || optind != argc)
+ goto usage;
+
+ err = vhd_open(&vhd, name, VHD_OPEN_RDONLY | VHD_OPEN_IGNORE_DISABLED);
+ if (err) {
+ printf("Failed to open %s: %d\n", name, err);
+ vhd_dump_headers(name, hex);
+ return err;
+ }
+
+ err = vhd_get_bat(&vhd);
+ if (err) {
+ printf("Failed to get bat for %s: %d\n", name, err);
+ goto out;
+ }
+
+ if (headers)
+ vhd_print_headers(&vhd, hex);
+
+ if (lsec != -1) {
+ err = vhd_print_logical_to_physical(&vhd, lsec, count, hex);
+ if (err)
+ goto out;
+ }
+
+ if (bat != -1) {
+ err = vhd_print_bat(&vhd, bat, count, hex);
+ if (err)
+ goto out;
+ }
+
+ if (bitmap != -1) {
+ err = vhd_print_bitmap(&vhd, bitmap, count, hex);
+ if (err)
+ goto out;
+ }
+
+ if (tbitmap != -1) {
+ err = vhd_test_bitmap(&vhd, tbitmap, count, hex);
+ if (err)
+ goto out;
+ }
+
+ if (batmap != -1) {
+ err = vhd_print_batmap(&vhd);
+ if (err)
+ goto out;
+ }
+
+ if (tbatmap != -1) {
+ err = vhd_test_batmap(&vhd, tbatmap, count, hex);
+ if (err)
+ goto out;
+ }
+
+ if (data != -1) {
+ err = vhd_print_data(&vhd, data, count, hex);
+ if (err)
+ goto out;
+ }
+
+ if (read != -1) {
+ err = vhd_read_data(&vhd, read, count, hex);
+ if (err)
+ goto out;
+ }
+
+ err = 0;
+
+ out:
+ vhd_close(&vhd);
+ return err;
+
+ usage:
+ printf("options:\n"
+ "-h help\n"
+ "-n name\n"
+ "-p print VHD headers\n"
+ "-t sec translate logical sector to VHD location\n"
+ "-b blk print bat entry\n"
+ "-m blk print bitmap\n"
+ "-i sec test bitmap for logical sector\n"
+ "-a print batmap\n"
+ "-j blk test batmap for block\n"
+ "-d blk print data\n"
+ "-c num num units\n"
+ "-r sec read num sectors at sec\n"
+ "-x print in hex\n");
+ return EINVAL;
+}
diff --git a/tools/blktap2/vhd/lib/vhd-util-repair.c b/tools/blktap2/vhd/lib/vhd-util-repair.c
new file mode 100644
index 0000000000..a1d2c45c12
--- /dev/null
+++ b/tools/blktap2/vhd/lib/vhd-util-repair.c
@@ -0,0 +1,84 @@
+/* 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 <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include "libvhd.h"
+
+int
+vhd_util_repair(int argc, char **argv)
+{
+ char *name;
+ int err, c;
+ off64_t eof;
+ vhd_context_t vhd;
+
+ name = NULL;
+
+ if (!argc || !argv)
+ goto usage;
+
+ optind = 0;
+ while ((c = getopt(argc, argv, "n:h")) != -1) {
+ switch (c) {
+ case 'n':
+ name = optarg;
+ break;
+ case 'h':
+ default:
+ goto usage;
+ }
+ }
+
+ if (!name || optind != argc)
+ goto usage;
+
+ err = vhd_open(&vhd, name, VHD_OPEN_RDWR);
+ if (err) {
+ printf("error opening %s: %d\n", name, err);
+ return err;
+ }
+
+ err = vhd_end_of_data(&vhd, &eof);
+ if (err) {
+ printf("error finding end of data: %d\n", err);
+ goto done;
+ }
+
+ err = vhd_write_footer_at(&vhd, &vhd.footer, eof);
+
+ done:
+ vhd_close(&vhd);
+ return err;
+
+usage:
+ printf("options: <-n name> [-h help]\n");
+ return -EINVAL;
+}
diff --git a/tools/blktap2/vhd/lib/vhd-util-resize.c b/tools/blktap2/vhd/lib/vhd-util-resize.c
new file mode 100644
index 0000000000..0143d7a0d3
--- /dev/null
+++ b/tools/blktap2/vhd/lib/vhd-util-resize.c
@@ -0,0 +1,1131 @@
+/* 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 <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <syslog.h>
+#include <inttypes.h>
+#include <sys/mman.h>
+
+#include "libvhd-journal.h"
+
+#if 1
+#define DFPRINTF(_f, _a...) fprintf(stdout, _f, ##_a)
+#else
+#define DFPRINTF(_f, _a...) ((void)0)
+#endif
+
+#define EPRINTF(_f, _a...) \
+ do { \
+ syslog(LOG_INFO, "%s: " _f, __func__, ##_a); \
+ DFPRINTF(_f, _a); \
+ } while (0)
+
+typedef struct vhd_block {
+ uint32_t block;
+ uint32_t offset;
+} vhd_block_t;
+
+TEST_FAIL_EXTERN_VARS;
+
+static inline uint32_t
+secs_to_blocks_down(vhd_context_t *vhd, uint64_t secs)
+{
+ return secs / vhd->spb;
+}
+
+static uint32_t
+secs_to_blocks_up(vhd_context_t *vhd, uint64_t secs)
+{
+ uint32_t blocks;
+
+ blocks = secs / vhd->spb;
+ if (secs % vhd->spb)
+ blocks++;
+
+ return blocks;
+}
+
+static int
+vhd_fixed_shrink(vhd_journal_t *journal, uint64_t secs)
+{
+ int err;
+ uint64_t new_eof;
+ vhd_context_t *vhd;
+
+ vhd = &journal->vhd;
+
+ new_eof = vhd->footer.curr_size - vhd_sectors_to_bytes(secs);
+ if (new_eof <= sizeof(vhd_footer_t))
+ return -EINVAL;
+
+ err = ftruncate(vhd->fd, new_eof);
+ if (err)
+ return errno;
+
+ vhd->footer.curr_size = new_eof;
+ return vhd_write_footer(vhd, &vhd->footer);
+}
+
+static int
+vhd_write_zeros(vhd_journal_t *journal, off64_t off, uint64_t size)
+{
+ int err;
+ char *buf;
+ vhd_context_t *vhd;
+ uint64_t bytes, map;
+
+ vhd = &journal->vhd;
+ map = MIN(size, VHD_BLOCK_SIZE);
+
+ err = vhd_seek(vhd, off, SEEK_SET);
+ if (err)
+ return err;
+
+ buf = mmap(0, map, PROT_READ, MAP_SHARED | MAP_ANONYMOUS, -1, 0);
+ if (buf == MAP_FAILED)
+ return -errno;
+
+ do {
+ bytes = MIN(size, map);
+
+ err = vhd_write(vhd, buf, bytes);
+ if (err)
+ break;
+
+ size -= bytes;
+ } while (size);
+
+ munmap(buf, map);
+
+ return err;
+}
+
+static int
+vhd_fixed_grow(vhd_journal_t *journal, uint64_t secs)
+{
+ int err;
+ vhd_context_t *vhd;
+ uint64_t size, eof, new_eof;
+
+ size = vhd_sectors_to_bytes(secs);
+ vhd = &journal->vhd;
+
+ err = vhd_seek(vhd, 0, SEEK_END);
+ if (err)
+ goto out;
+
+ eof = vhd_position(vhd);
+ if (eof == (off64_t)-1) {
+ err = -errno;
+ goto out;
+ }
+
+ err = vhd_write_zeros(journal, eof - sizeof(vhd_footer_t), size);
+ if (err)
+ goto out;
+
+ new_eof = eof + size;
+ err = vhd_seek(vhd, new_eof, SEEK_SET);
+ if (err)
+ goto out;
+
+ vhd->footer.curr_size += size;
+ err = vhd_write_footer(vhd, &vhd->footer);
+ if (err)
+ goto out;
+
+ err = 0;
+
+out:
+ return err;
+}
+
+static int
+vhd_fixed_resize(vhd_journal_t *journal, uint64_t size)
+{
+ int err;
+ vhd_context_t *vhd;
+ uint64_t cur_secs, new_secs;
+
+ vhd = &journal->vhd;
+ cur_secs = vhd->footer.curr_size >> VHD_SECTOR_SHIFT;
+ new_secs = size << (20 - VHD_SECTOR_SHIFT);
+
+ if (cur_secs == new_secs)
+ return 0;
+ else if (cur_secs > new_secs)
+ err = vhd_fixed_shrink(journal, cur_secs - new_secs);
+ else
+ err = vhd_fixed_grow(journal, new_secs - cur_secs);
+
+ return err;
+}
+
+static inline void
+swap(vhd_block_t *list, int a, int b)
+{
+ vhd_block_t tmp;
+
+ tmp = list[a];
+ list[a] = list[b];
+ list[b] = tmp;
+}
+
+static int
+partition(vhd_block_t *list, int left, int right, int pidx)
+{
+ int i, sidx;
+ long long pval;
+
+ sidx = left;
+ pval = list[pidx].offset;
+ swap(list, pidx, right);
+
+ for (i = left; i < right; i++)
+ if (list[i].offset >= pval) {
+ swap(list, sidx, i);
+ ++sidx;
+ }
+
+ swap(list, right, sidx);
+ return sidx;
+}
+
+static void
+quicksort(vhd_block_t *list, int left, int right)
+{
+ int pidx, new_pidx;
+
+ if (right < left)
+ return;
+
+ pidx = left;
+ new_pidx = partition(list, left, right, pidx);
+ quicksort(list, left, new_pidx - 1);
+ quicksort(list, new_pidx + 1, right);
+}
+
+static int
+vhd_move_block(vhd_journal_t *journal, uint32_t src, off64_t offset)
+{
+ int err;
+ char *buf;
+ size_t size;
+ vhd_context_t *vhd;
+ off64_t off, src_off;
+
+ buf = NULL;
+ vhd = &journal->vhd;
+ off = offset;
+ size = vhd_sectors_to_bytes(vhd->bm_secs);
+ src_off = vhd->bat.bat[src];
+
+ if (src_off == DD_BLK_UNUSED)
+ return -EINVAL;
+ src_off = vhd_sectors_to_bytes(src_off);
+
+ err = vhd_journal_add_block(journal, src,
+ VHD_JOURNAL_DATA | VHD_JOURNAL_METADATA);
+ if (err)
+ goto out;
+
+ err = vhd_read_bitmap(vhd, src, &buf);
+ if (err)
+ goto out;
+
+ err = vhd_seek(vhd, off, SEEK_SET);
+ if (err)
+ goto out;
+
+ err = vhd_write(vhd, buf, size);
+ if (err)
+ goto out;
+
+ free(buf);
+ buf = NULL;
+ off += size;
+ size = vhd_sectors_to_bytes(vhd->spb);
+
+ err = vhd_read_block(vhd, src, &buf);
+ if (err)
+ goto out;
+
+ err = vhd_seek(vhd, off, SEEK_SET);
+ if (err)
+ goto out;
+
+ err = vhd_write(vhd, buf, size);
+ if (err)
+ goto out;
+
+ vhd->bat.bat[src] = offset >> VHD_SECTOR_SHIFT;
+
+ err = vhd_write_zeros(journal, src_off,
+ vhd_sectors_to_bytes(vhd->bm_secs + vhd->spb));
+
+out:
+ free(buf);
+ return err;
+}
+
+static int
+vhd_clobber_block(vhd_journal_t *journal, uint32_t src, uint32_t dest)
+{
+ int err;
+ off64_t off;
+ vhd_context_t *vhd;
+
+ vhd = &journal->vhd;
+ off = vhd_sectors_to_bytes(vhd->bat.bat[dest]);
+
+ err = vhd_journal_add_block(journal, dest,
+ VHD_JOURNAL_DATA | VHD_JOURNAL_METADATA);
+ if (err)
+ return err;
+
+ err = vhd_move_block(journal, src, off);
+ if (err)
+ return err;
+
+ vhd->bat.bat[dest] = DD_BLK_UNUSED;
+
+ return 0;
+}
+
+/*
+ * remove a list of blocks from the vhd file
+ * if a block to be removed:
+ * - resides at the end of the file: simply clear its bat entry
+ * - resides elsewhere: move the last block in the file into its position
+ * and update the bat to reflect this
+ */
+static int
+vhd_defrag_shrink(vhd_journal_t *journal,
+ vhd_block_t *original_free_list, int free_cnt)
+{
+ vhd_context_t *vhd;
+ int i, j, free_idx, err;
+ vhd_block_t *blocks, *free_list;
+
+ err = 0;
+ blocks = NULL;
+ free_list = NULL;
+ vhd = &journal->vhd;
+
+ blocks = malloc(vhd->bat.entries * sizeof(vhd_block_t));
+ if (!blocks) {
+ err = -ENOMEM;
+ goto out;
+ }
+
+ free_list = malloc(free_cnt * sizeof(vhd_block_t));
+ if (!free_list) {
+ err = -ENOMEM;
+ goto out;
+ }
+
+ for (i = 0; i < vhd->bat.entries; i++) {
+ blocks[i].block = i;
+ blocks[i].offset = vhd->bat.bat[i];
+ }
+
+ memcpy(free_list, original_free_list,
+ free_cnt * sizeof(vhd_block_t));
+
+ /* sort both the to-free list and the bat list
+ * in order of descending file offset */
+ quicksort(free_list, 0, free_cnt - 1);
+ quicksort(blocks, 0, vhd->bat.entries - 1);
+
+ for (i = 0, free_idx = 0;
+ i < vhd->bat.entries && free_idx < free_cnt; i++) {
+ vhd_block_t *b = blocks + i;
+
+ if (b->offset == DD_BLK_UNUSED)
+ continue;
+
+ for (j = free_idx; j < free_cnt; j++)
+ if (b->block == free_list[j].block) {
+ /* the last block in the file is in the list of
+ * blocks to remove; no need to shuffle the
+ * data -- just clear the bat entry */
+ vhd->bat.bat[free_list[j].block] = DD_BLK_UNUSED;
+ free_idx++;
+ continue;
+ }
+
+ err = vhd_clobber_block(journal, b->block,
+ free_list[free_idx++].block);
+ if (err)
+ goto out;
+ }
+
+ /* clear any bat entries for blocks we did not shuffle */
+ for (i = free_idx; i < free_cnt; i++)
+ vhd->bat.bat[free_list[i].block] = DD_BLK_UNUSED;
+
+out:
+ free(blocks);
+ free(free_list);
+
+ return err;
+}
+
+static int
+vhd_clear_bat_entries(vhd_journal_t *journal, uint32_t entries)
+{
+ int i, err;
+ vhd_context_t *vhd;
+ off64_t orig_map_off, new_map_off;
+ uint32_t orig_entries, new_entries;
+
+ vhd = &journal->vhd;
+ orig_entries = vhd->header.max_bat_size;
+ new_entries = orig_entries - entries;
+
+ if (vhd_has_batmap(vhd)) {
+ err = vhd_batmap_header_offset(vhd, &orig_map_off);
+ if (err)
+ return err;
+ }
+
+ /* update header */
+ vhd->header.max_bat_size = new_entries;
+ err = vhd_write_header(vhd, &vhd->header);
+ if (err)
+ return err;
+
+ /* update footer */
+ vhd->footer.curr_size = (uint64_t)new_entries * vhd->header.block_size;
+ vhd->footer.geometry = vhd_chs(vhd->footer.curr_size);
+ err = vhd_write_footer(vhd, &vhd->footer);
+ if (err)
+ return err;
+
+ /* update bat -- we don't reclaim space, just clear entries */
+ for (i = new_entries; i < orig_entries; i++)
+ vhd->bat.bat[i] = 0;
+
+ err = vhd_write_bat(vhd, &vhd->bat);
+ if (err)
+ return err;
+
+ /* update this after write_bat so the end of the bat is zeored */
+ vhd->bat.entries = new_entries;
+
+ if (!vhd_has_batmap(vhd))
+ return 0;
+
+ /* zero out old batmap header if new header has moved */
+ err = vhd_batmap_header_offset(vhd, &new_map_off);
+ if (err)
+ return err;
+
+ if (orig_map_off != new_map_off) {
+ size_t size;
+
+ size = vhd_bytes_padded(sizeof(struct dd_batmap_hdr));
+
+ err = vhd_write_zeros(journal, orig_map_off, size);
+ if (err)
+ return err;
+ }
+
+ /* update batmap -- clear entries for freed blocks */
+ for (i = new_entries; i < orig_entries; i++)
+ vhd_batmap_clear(vhd, &vhd->batmap, i);
+
+ err = vhd_write_batmap(vhd, &vhd->batmap);
+ if (err)
+ return err;
+
+ return 0;
+}
+
+static int
+vhd_dynamic_shrink(vhd_journal_t *journal, uint64_t secs)
+{
+ off64_t eof;
+ uint32_t blocks;
+ vhd_context_t *vhd;
+ int i, j, err, free_cnt;
+ struct vhd_block *free_list;
+
+ printf("dynamic shrink not fully implemented\n");
+ return -ENOSYS;
+
+ eof = 0;
+ free_cnt = 0;
+ free_list = NULL;
+ vhd = &journal->vhd;
+
+ blocks = secs_to_blocks_down(vhd, secs);
+ if (blocks == 0)
+ return 0;
+
+ if (vhd_has_batmap(vhd)) {
+ err = vhd_get_batmap(vhd);
+ if (err)
+ return err;
+ }
+
+ free_list = malloc(blocks * sizeof(struct vhd_block));
+ if (!free_list)
+ return -ENOMEM;
+
+ for (i = vhd->bat.entries - 1, j = 0; i >= 0 && j < blocks; i--, j++) {
+ uint32_t blk = vhd->bat.bat[i];
+
+ if (blk != DD_BLK_UNUSED) {
+ free_list[free_cnt].block = i;
+ free_list[free_cnt].offset = blk;
+ free_cnt++;
+ }
+ }
+
+ if (free_cnt) {
+ err = vhd_defrag_shrink(journal, free_list, free_cnt);
+ if (err)
+ goto out;
+ }
+
+ err = vhd_clear_bat_entries(journal, blocks);
+ if (err)
+ goto out;
+
+ /* remove data beyond footer */
+ err = vhd_end_of_data(vhd, &eof);
+ if (err)
+ goto out;
+
+ err = ftruncate(vhd->fd, eof + sizeof(vhd_footer_t));
+ if (err) {
+ err = -errno;
+ goto out;
+ }
+
+ err = 0;
+
+out:
+ free(free_list);
+ return err;
+}
+
+static inline void
+vhd_first_data_block(vhd_context_t *vhd, vhd_block_t *block)
+{
+ int i;
+ uint32_t blk;
+
+ memset(block, 0, sizeof(vhd_block_t));
+
+ for (i = 0; i < vhd->bat.entries; i++) {
+ blk = vhd->bat.bat[i];
+
+ if (blk != DD_BLK_UNUSED) {
+ if (!block->offset || blk < block->offset) {
+ block->block = i;
+ block->offset = blk;
+ }
+ }
+ }
+}
+
+static inline uint32_t
+vhd_next_block_offset(vhd_context_t *vhd)
+{
+ int i;
+ uint32_t blk, end, spp, next;
+
+ next = 0;
+ spp = getpagesize() >> VHD_SECTOR_SHIFT;
+
+ for (i = 0; i < vhd->bat.entries; i++) {
+ blk = vhd->bat.bat[i];
+
+ if (blk != DD_BLK_UNUSED) {
+ end = blk + vhd->spb + vhd->bm_secs;
+ next = MAX(next, end);
+ }
+ }
+
+ return next;
+}
+
+static inline int
+in_range(off64_t off, off64_t start, off64_t size)
+{
+ return (start < off && start + size > off);
+}
+
+#define SKIP_HEADER 0x01
+#define SKIP_BAT 0x02
+#define SKIP_BATMAP 0x04
+#define SKIP_PLOC 0x08
+#define SKIP_DATA 0x10
+
+static inline int
+skip_check(int mode, int type)
+{
+ return mode & type;
+}
+
+static int
+vhd_check_for_clobber(vhd_context_t *vhd, off64_t off, int mode)
+{
+ int i, n;
+ char *msg;
+ size_t size;
+ vhd_block_t fb;
+ vhd_parent_locator_t *loc;
+
+ msg = NULL;
+
+ if (!vhd_type_dynamic(vhd))
+ return 0;
+
+ if (off < VHD_SECTOR_SIZE) {
+ msg = "backup footer";
+ goto fail;
+ }
+
+ if (!skip_check(mode, SKIP_HEADER))
+ if (in_range(off,
+ vhd->footer.data_offset, sizeof(vhd_header_t))) {
+ msg = "header";
+ goto fail;
+ }
+
+ if (!skip_check(mode, SKIP_BAT))
+ if (in_range(off, vhd->header.table_offset,
+ vhd_bytes_padded(vhd->header.max_bat_size *
+ sizeof(uint32_t)))) {
+ msg = "bat";
+ goto fail;
+ }
+
+ if (!skip_check(mode, SKIP_BATMAP))
+ if (vhd_has_batmap(vhd) &&
+ in_range(off, vhd->batmap.header.batmap_offset,
+ vhd_bytes_padded(vhd->batmap.header.batmap_size))) {
+ msg = "batmap";
+ goto fail;
+ }
+
+ if (!skip_check(mode, SKIP_PLOC)) {
+ n = sizeof(vhd->header.loc) / sizeof(vhd_parent_locator_t);
+ for (i = 0; i < n; i++) {
+ loc = vhd->header.loc + i;
+ if (loc->code == PLAT_CODE_NONE)
+ continue;
+
+ size = vhd_parent_locator_size(loc);
+ if (in_range(off, loc->data_offset, size)) {
+ msg = "parent locator";
+ goto fail;
+ }
+ }
+ }
+
+ if (!skip_check(mode, SKIP_DATA)) {
+ vhd_first_data_block(vhd, &fb);
+ if (fb.offset && in_range(off,
+ vhd_sectors_to_bytes(fb.offset),
+ VHD_BLOCK_SIZE)) {
+ msg = "data block";
+ goto fail;
+ }
+ }
+
+ return 0;
+
+fail:
+ EPRINTF("write to 0x%08"PRIx64" would clobber %s\n", off, msg);
+ return -EINVAL;
+}
+
+/*
+ * take any metadata after the bat (@eob) and shift it
+ */
+static int
+vhd_shift_metadata(vhd_journal_t *journal, off64_t eob,
+ size_t bat_needed, size_t map_needed)
+{
+ int i, n, err;
+ vhd_context_t *vhd;
+ size_t size_needed;
+ char *buf, **locators;
+ vhd_parent_locator_t *loc;
+
+ vhd = &journal->vhd;
+ size_needed = bat_needed + map_needed;
+
+ n = sizeof(vhd->header.loc) / sizeof(vhd_parent_locator_t);
+
+ locators = calloc(n, sizeof(char *));
+ if (!locators)
+ return -ENOMEM;
+
+ for (i = 0; i < n; i++) {
+ size_t size;
+
+ loc = vhd->header.loc + i;
+ if (loc->code == PLAT_CODE_NONE)
+ continue;
+
+ if (loc->data_offset < eob)
+ continue;
+
+ size = vhd_parent_locator_size(loc);
+ err = posix_memalign((void **)&buf, VHD_SECTOR_SIZE, size);
+ if (err) {
+ err = -err;
+ buf = NULL;
+ goto out;
+ }
+
+ err = vhd_seek(vhd, loc->data_offset, SEEK_SET);
+ if (err)
+ goto out;
+
+ err = vhd_read(vhd, buf, size);
+ if (err)
+ goto out;
+
+ locators[i] = buf;
+ }
+
+ for (i = 0; i < n; i++) {
+ off64_t off;
+ size_t size;
+
+ if (!locators[i])
+ continue;
+
+ loc = vhd->header.loc + i;
+ off = loc->data_offset + size_needed;
+ size = vhd_parent_locator_size(loc);
+
+ if (vhd_check_for_clobber(vhd, off + size, SKIP_PLOC)) {
+ EPRINTF("%s: shifting locator %d would clobber data\n",
+ vhd->file, i);
+ return -EINVAL;
+ }
+
+ err = vhd_seek(vhd, off, SEEK_SET);
+ if (err)
+ goto out;
+
+ err = vhd_write(vhd, locators[i], size);
+ if (err)
+ goto out;
+
+ free(locators[i]);
+ locators[i] = NULL;
+ loc->data_offset = off;
+
+ /* write the new header after writing the new bat */
+ }
+
+ if (vhd_has_batmap(vhd) && vhd->batmap.header.batmap_offset > eob) {
+ vhd->batmap.header.batmap_offset += bat_needed;
+
+ /* write the new batmap after writing the new bat */
+ }
+
+ err = 0;
+
+out:
+ for (i = 0; i < n; i++)
+ free(locators[i]);
+ free(locators);
+
+ return err;
+}
+
+static int
+vhd_add_bat_entries(vhd_journal_t *journal, int entries)
+{
+ int i, err;
+ off64_t off;
+ vhd_bat_t new_bat;
+ vhd_context_t *vhd;
+ uint32_t new_entries;
+ vhd_batmap_t new_batmap;
+ uint64_t bat_size, new_bat_size, map_size, new_map_size;
+
+ vhd = &journal->vhd;
+ new_entries = vhd->header.max_bat_size + entries;
+
+ bat_size = vhd_bytes_padded(vhd->header.max_bat_size *
+ sizeof(uint32_t));
+ new_bat_size = vhd_bytes_padded(new_entries * sizeof(uint32_t));
+
+ map_size = vhd_bytes_padded((vhd->header.max_bat_size + 7) >> 3);
+ new_map_size = vhd_bytes_padded((new_entries + 7) >> 3);
+
+ off = vhd->header.table_offset + new_bat_size;
+ if (vhd_check_for_clobber(vhd, off, SKIP_BAT | SKIP_BATMAP)) {
+ EPRINTF("%s: writing new bat of 0x%"PRIx64" bytes "
+ "at 0x%08"PRIx64" would clobber data\n",
+ vhd->file, new_bat_size, vhd->header.table_offset);
+ return -EINVAL;
+ }
+
+ if (vhd_has_batmap(vhd)) {
+ off = vhd->batmap.header.batmap_offset + new_map_size;
+ if (vhd_check_for_clobber(vhd, off, 0)) {
+ EPRINTF("%s: writing new batmap of 0x%"PRIx64" bytes"
+ " at 0x%08"PRIx64" would clobber data\n", vhd->file,
+ new_map_size, vhd->batmap.header.batmap_offset);
+ return -EINVAL;
+ }
+ }
+
+ /* update header */
+ vhd->header.max_bat_size = new_entries;
+ err = vhd_write_header(vhd, &vhd->header);
+ if (err)
+ return err;
+
+ /* update footer */
+ vhd->footer.curr_size = (uint64_t)new_entries * vhd->header.block_size;
+ vhd->footer.geometry = vhd_chs(vhd->footer.curr_size);
+ vhd->footer.checksum = vhd_checksum_footer(&vhd->footer);
+ err = vhd_write_footer(vhd, &vhd->footer);
+ if (err)
+ return err;
+
+ /* allocate new bat */
+ err = posix_memalign((void **)&new_bat.bat, VHD_SECTOR_SIZE, new_bat_size);
+ if (err)
+ return -err;
+
+ new_bat.spb = vhd->bat.spb;
+ new_bat.entries = new_entries;
+ memcpy(new_bat.bat, vhd->bat.bat, bat_size);
+ for (i = vhd->bat.entries; i < new_entries; i++)
+ new_bat.bat[i] = DD_BLK_UNUSED;
+
+ /* write new bat */
+ err = vhd_write_bat(vhd, &new_bat);
+ if (err) {
+ free(new_bat.bat);
+ return err;
+ }
+
+ /* update in-memory bat */
+ free(vhd->bat.bat);
+ vhd->bat = new_bat;
+
+ if (!vhd_has_batmap(vhd))
+ return 0;
+
+ /* allocate new batmap */
+ err = posix_memalign((void **)&new_batmap.map,
+ VHD_SECTOR_SIZE, new_map_size);
+ if (err)
+ return err;
+
+ new_batmap.header = vhd->batmap.header;
+ new_batmap.header.batmap_size = secs_round_up_no_zero(new_map_size);
+ memcpy(new_batmap.map, vhd->batmap.map, map_size);
+ memset(new_batmap.map + map_size, 0, new_map_size - map_size);
+
+ /* write new batmap */
+ err = vhd_write_batmap(vhd, &new_batmap);
+ if (err) {
+ free(new_batmap.map);
+ return err;
+ }
+
+ /* update in-memory batmap */
+ free(vhd->batmap.map);
+ vhd->batmap = new_batmap;
+
+ return 0;
+}
+
+static int
+vhd_dynamic_grow(vhd_journal_t *journal, uint64_t secs)
+{
+ int i, err;
+ off64_t eob, eom;
+ vhd_context_t *vhd;
+ vhd_block_t first_block;
+ uint64_t blocks, size_needed;
+ uint64_t bat_needed, bat_size, bat_avail, bat_bytes, bat_secs;
+ uint64_t map_needed, map_size, map_avail, map_bytes, map_secs;
+
+ vhd = &journal->vhd;
+
+ size_needed = 0;
+ bat_needed = 0;
+ map_needed = 0;
+
+ /* number of vhd blocks to add */
+ blocks = secs_to_blocks_up(vhd, secs);
+
+ /* size in bytes needed for new bat entries */
+ bat_needed = blocks * sizeof(uint32_t);
+ map_needed = (blocks >> 3) + 1;
+
+ /* available bytes in current bat */
+ bat_bytes = vhd->header.max_bat_size * sizeof(uint32_t);
+ bat_secs = secs_round_up_no_zero(bat_bytes);
+ bat_size = vhd_sectors_to_bytes(bat_secs);
+ bat_avail = bat_size - bat_bytes;
+
+ if (vhd_has_batmap(vhd)) {
+ /* avaliable bytes in current batmap */
+ map_bytes = (vhd->header.max_bat_size + 7) >> 3;
+ map_secs = vhd->batmap.header.batmap_size;
+ map_size = vhd_sectors_to_bytes(map_secs);
+ map_avail = map_size - map_bytes;
+ } else {
+ map_needed = 0;
+ map_avail = 0;
+ }
+
+ /* we have enough space already; just extend the bat */
+ if (bat_needed <= bat_avail && map_needed <= map_avail)
+ goto add_entries;
+
+ /* we need to add new sectors to the bat */
+ if (bat_needed > bat_avail) {
+ bat_needed -= bat_avail;
+ bat_needed = vhd_bytes_padded(bat_needed);
+ } else
+ bat_needed = 0;
+
+ /* we need to add new sectors to the batmap */
+ if (map_needed > map_avail) {
+ map_needed -= map_avail;
+ map_needed = vhd_bytes_padded(map_needed);
+ } else
+ map_needed = 0;
+
+ /* how many additional bytes do we need? */
+ size_needed = bat_needed + map_needed;
+
+ /* calculate space between end of headers and beginning of data */
+ err = vhd_end_of_headers(vhd, &eom);
+ if (err)
+ return err;
+
+ eob = vhd->header.table_offset + vhd_sectors_to_bytes(bat_secs);
+ vhd_first_data_block(vhd, &first_block);
+
+ /* no blocks allocated; just shift post-bat metadata */
+ if (!first_block.offset)
+ goto shift_metadata;
+
+ /*
+ * not enough space --
+ * move vhd data blocks to the end of the file to make room
+ */
+ do {
+ off64_t new_off, bm_size, gap_size;
+
+ new_off = vhd_sectors_to_bytes(vhd_next_block_offset(vhd));
+
+ /* data region of segment should begin on page boundary */
+ bm_size = vhd_sectors_to_bytes(vhd->bm_secs);
+ if ((new_off + bm_size) % 4096) {
+ gap_size = 4096 - ((new_off + bm_size) % 4096);
+
+ err = vhd_write_zeros(journal, new_off, gap_size);
+ if (err)
+ return err;
+
+ new_off += gap_size;
+ }
+
+ err = vhd_move_block(journal, first_block.block, new_off);
+ if (err)
+ return err;
+
+ vhd_first_data_block(vhd, &first_block);
+
+ } while (eom + size_needed >= vhd_sectors_to_bytes(first_block.offset));
+
+ TEST_FAIL_AT(FAIL_RESIZE_DATA_MOVED);
+
+shift_metadata:
+ /* shift any metadata after the bat to make room for new bat sectors */
+ err = vhd_shift_metadata(journal, eob, bat_needed, map_needed);
+ if (err)
+ return err;
+
+ TEST_FAIL_AT(FAIL_RESIZE_METADATA_MOVED);
+
+add_entries:
+ return vhd_add_bat_entries(journal, blocks);
+}
+
+static int
+vhd_dynamic_resize(vhd_journal_t *journal, uint64_t size)
+{
+ int err;
+ vhd_context_t *vhd;
+ uint64_t cur_secs, new_secs;
+
+ vhd = &journal->vhd;
+ cur_secs = vhd->footer.curr_size >> VHD_SECTOR_SHIFT;
+ new_secs = size << (20 - VHD_SECTOR_SHIFT);
+
+ if (cur_secs == new_secs)
+ return 0;
+
+ err = vhd_get_header(vhd);
+ if (err)
+ return err;
+
+ err = vhd_get_bat(vhd);
+ if (err)
+ return err;
+
+ if (vhd_has_batmap(vhd)) {
+ err = vhd_get_batmap(vhd);
+ if (err)
+ return err;
+ }
+
+ if (cur_secs > new_secs)
+ err = vhd_dynamic_shrink(journal, cur_secs - new_secs);
+ else
+ err = vhd_dynamic_grow(journal, new_secs - cur_secs);
+
+ return err;
+}
+
+static int
+vhd_util_resize_check_creator(const char *name)
+{
+ int err;
+ vhd_context_t vhd;
+
+ err = vhd_open(&vhd, name, VHD_OPEN_RDONLY | VHD_OPEN_STRICT);
+ if (err) {
+ printf("error opening %s: %d\n", name, err);
+ return err;
+ }
+
+ if (!vhd_creator_tapdisk(&vhd)) {
+ printf("%s not created by xen; resize not supported\n", name);
+ err = -EINVAL;
+ }
+
+ vhd_close(&vhd);
+ return err;
+}
+
+int
+vhd_util_resize(int argc, char **argv)
+{
+ char *name, *jname;
+ uint64_t size;
+ int c, err, jerr;
+ vhd_journal_t journal;
+ vhd_context_t *vhd;
+
+ err = -EINVAL;
+ size = 0;
+ name = NULL;
+ jname = NULL;
+
+ optind = 0;
+ while ((c = getopt(argc, argv, "n:j:s:h")) != -1) {
+ switch (c) {
+ case 'n':
+ name = optarg;
+ break;
+ case 'j':
+ jname = optarg;
+ break;
+ case 's':
+ err = 0;
+ size = strtoull(optarg, NULL, 10);
+ break;
+ case 'h':
+ default:
+ goto usage;
+ }
+ }
+
+ if (err || !name || !jname || argc != optind)
+ goto usage;
+
+ err = vhd_util_resize_check_creator(name);
+ if (err)
+ return err;
+
+ libvhd_set_log_level(1);
+ err = vhd_journal_create(&journal, name, jname);
+ if (err) {
+ printf("creating journal failed: %d\n", err);
+ return err;
+ }
+
+ vhd = &journal.vhd;
+
+ err = vhd_get_footer(vhd);
+ if (err)
+ goto out;
+
+ TEST_FAIL_AT(FAIL_RESIZE_BEGIN);
+
+ if (vhd_type_dynamic(vhd))
+ err = vhd_dynamic_resize(&journal, size);
+ else
+ err = vhd_fixed_resize(&journal, size);
+
+ TEST_FAIL_AT(FAIL_RESIZE_END);
+
+out:
+ if (err) {
+ printf("resize failed: %d\n", err);
+ jerr = vhd_journal_revert(&journal);
+ } else
+ jerr = vhd_journal_commit(&journal);
+
+ if (jerr) {
+ printf("closing journal failed: %d\n", jerr);
+ vhd_journal_close(&journal);
+ } else
+ vhd_journal_remove(&journal);
+
+ return (err ? : jerr);
+
+usage:
+ printf("options: <-n name> <-j journal> <-s size (in MB)> [-h help]\n");
+ return -EINVAL;
+}
diff --git a/tools/blktap2/vhd/lib/vhd-util-revert.c b/tools/blktap2/vhd/lib/vhd-util-revert.c
new file mode 100644
index 0000000000..dab6e8b950
--- /dev/null
+++ b/tools/blktap2/vhd/lib/vhd-util-revert.c
@@ -0,0 +1,106 @@
+/* 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.
+ *
+ * Altering operations:
+ *
+ * 1. Change the parent pointer to another file.
+ * 2. Change the size of the file containing the VHD image. This does NOT
+ * affect the VHD disk capacity, only the physical size of the file containing
+ * the VHD. Naturally, it is not possible to set the file size to be less than
+ * the what VHD utilizes.
+ * The operation doesn't actually change the file size, but it writes the
+ * footer in the right location such that resizing the file (manually, as a
+ * separate step) will produce the correct results. If the new file size is
+ * greater than the current file size, the file must first be expanded and then
+ * altered with this operation. If the new size is smaller than the current
+ * size, the VHD must first be altered with this operation and then the file
+ * must be shrunk. Failing to resize the file will result in a corrupted VHD.
+*/
+
+#include <errno.h>
+//#include <fcntl.h>
+#include <stdio.h>
+//#include <stdlib.h>
+#include <unistd.h>
+
+#include "libvhd.h"
+#include "libvhd-journal.h"
+
+int
+vhd_util_revert(int argc, char **argv)
+{
+ char *name, *jname;
+ vhd_journal_t journal;
+ int c, err;
+
+ name = NULL;
+ jname = NULL;
+
+ optind = 0;
+ while ((c = getopt(argc, argv, "n:j:h")) != -1) {
+ switch (c) {
+ case 'n':
+ name = optarg;
+ break;
+ case 'j':
+ jname = optarg;
+ break;
+ case 'h':
+ default:
+ goto usage;
+ }
+ }
+
+ if (!name || !jname || argc != optind)
+ goto usage;
+
+ libvhd_set_log_level(1);
+ err = vhd_journal_open(&journal, name, jname);
+ if (err) {
+ printf("opening journal failed: %d\n", err);
+ return err;
+ }
+
+ err = vhd_journal_revert(&journal);
+ if (err) {
+ printf("reverting journal failed: %d\n", err);
+ vhd_journal_close(&journal);
+ return err;
+ }
+
+ err = vhd_journal_remove(&journal);
+ if (err) {
+ printf("removing journal failed: %d\n", err);
+ vhd_journal_close(&journal);
+ return err;
+ }
+
+ return 0;
+
+usage:
+ printf("options: <-n name> <-j journal> [-h help]\n");
+ return -EINVAL;
+}
diff --git a/tools/blktap2/vhd/lib/vhd-util-scan.c b/tools/blktap2/vhd/lib/vhd-util-scan.c
new file mode 100644
index 0000000000..4ecfb52e7d
--- /dev/null
+++ b/tools/blktap2/vhd/lib/vhd-util-scan.c
@@ -0,0 +1,1315 @@
+/* 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 <glob.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fnmatch.h>
+
+#include "list.h"
+#include "libvhd.h"
+#include "lvm-util.h"
+
+#define VHD_SCAN_FAST 0x01
+#define VHD_SCAN_PRETTY 0x02
+#define VHD_SCAN_VOLUME 0x04
+#define VHD_SCAN_NOFAIL 0x08
+#define VHD_SCAN_VERBOSE 0x10
+#define VHD_SCAN_PARENTS 0x20
+
+#define VHD_TYPE_RAW_FILE 0x01
+#define VHD_TYPE_VHD_FILE 0x02
+#define VHD_TYPE_RAW_VOLUME 0x04
+#define VHD_TYPE_VHD_VOLUME 0x08
+
+static inline int
+target_volume(uint8_t type)
+{
+ return (type == VHD_TYPE_RAW_VOLUME || type == VHD_TYPE_VHD_VOLUME);
+}
+
+static inline int
+target_vhd(uint8_t type)
+{
+ return (type == VHD_TYPE_VHD_FILE || type == VHD_TYPE_VHD_VOLUME);
+}
+
+struct target {
+ char name[VHD_MAX_NAME_LEN];
+ char device[VHD_MAX_NAME_LEN];
+ uint64_t size;
+ uint64_t start;
+ uint64_t end;
+ uint8_t type;
+};
+
+struct iterator {
+ int cur;
+ int cur_size;
+ int max_size;
+ struct target *targets;
+};
+
+struct vhd_image {
+ char *name;
+ char *parent;
+ uint64_t capacity;
+ off64_t size;
+ uint8_t hidden;
+ int error;
+ char *message;
+
+ struct target *target;
+
+ struct list_head sibling;
+ struct list_head children;
+ struct vhd_image *parent_image;
+};
+
+struct vhd_scan {
+ int cur;
+ int size;
+
+ int lists_cur;
+ int lists_size;
+
+ struct vhd_image **images;
+ struct vhd_image **lists;
+};
+
+static int flags;
+static struct vg vg;
+static struct vhd_scan scan;
+
+static int
+vhd_util_scan_pretty_allocate_list(int cnt)
+{
+ int i;
+ struct vhd_image *list;
+
+ memset(&scan, 0, sizeof(scan));
+
+ scan.lists_cur = 1;
+ scan.lists_size = 10;
+
+ scan.lists = calloc(scan.lists_size, sizeof(struct vhd_image *));
+ if (!scan.lists)
+ goto fail;
+
+ scan.lists[0] = calloc(cnt, sizeof(struct vhd_image));
+ if (!scan.lists[0])
+ goto fail;
+
+ scan.images = calloc(cnt, sizeof(struct vhd_image *));
+ if (!scan.images)
+ goto fail;
+
+ for (i = 0; i < cnt; i++)
+ scan.images[i] = scan.lists[0] + i;
+
+ scan.cur = 0;
+ scan.size = cnt;
+
+ return 0;
+
+fail:
+ if (scan.lists) {
+ free(scan.lists[0]);
+ free(scan.lists);
+ }
+
+ free(scan.images);
+ memset(&scan, 0, sizeof(scan));
+ return -ENOMEM;
+}
+
+static void
+vhd_util_scan_pretty_free_list(void)
+{
+ int i;
+
+ if (scan.lists) {
+ for (i = 0; i < scan.lists_cur; i++)
+ free(scan.lists[i]);
+ free(scan.lists);
+ }
+
+ free(scan.images);
+ memset(&scan, 0, sizeof(scan));
+}
+
+static int
+vhd_util_scan_pretty_add_image(struct vhd_image *image)
+{
+ int i;
+ struct vhd_image *img;
+
+ for (i = 0; i < scan.cur; i++) {
+ img = scan.images[i];
+ if (!strcmp(img->name, image->name))
+ return 0;
+ }
+
+ if (scan.cur >= scan.size) {
+ struct vhd_image *new, **list;
+
+ if (scan.lists_cur >= scan.lists_size) {
+ list = realloc(scan.lists, scan.lists_size * 2 *
+ sizeof(struct vhd_image *));
+ if (!list)
+ return -ENOMEM;
+
+ scan.lists_size *= 2;
+ scan.lists = list;
+ }
+
+ new = calloc(scan.size, sizeof(struct vhd_image));
+ if (!new)
+ return -ENOMEM;
+
+ scan.lists[scan.lists_cur++] = new;
+ scan.size *= 2;
+
+ list = realloc(scan.images, scan.size *
+ sizeof(struct vhd_image *));
+ if (!list)
+ return -ENOMEM;
+
+ scan.images = list;
+ for (i = 0; i + scan.cur < scan.size; i++)
+ scan.images[i + scan.cur] = new + i;
+ }
+
+ img = scan.images[scan.cur];
+ INIT_LIST_HEAD(&img->sibling);
+ INIT_LIST_HEAD(&img->children);
+
+ img->capacity = image->capacity;
+ img->size = image->size;
+ img->hidden = image->hidden;
+ img->error = image->error;
+ img->message = image->message;
+
+ img->name = strdup(image->name);
+ if (!img->name)
+ goto fail;
+
+ if (image->parent) {
+ img->parent = strdup(image->parent);
+ if (!img->parent)
+ goto fail;
+ }
+
+ scan.cur++;
+ return 0;
+
+fail:
+ free(img->name);
+ free(img->parent);
+ memset(img, 0, sizeof(*img));
+ return -ENOMEM;
+}
+
+static int
+vhd_util_scan_pretty_image_compare(const void *lhs, const void *rhs)
+{
+ struct vhd_image *l, *r;
+
+ l = *(struct vhd_image **)lhs;
+ r = *(struct vhd_image **)rhs;
+
+ return strcmp(l->name, r->name);
+}
+
+static void
+vhd_util_scan_print_image_indent(struct vhd_image *image, int tab)
+{
+ char *pad, *name, *pmsg, *parent;
+
+ pad = (tab ? " " : "");
+ name = image->name;
+ parent = (image->parent ? : "none");
+
+ if ((flags & VHD_SCAN_PRETTY) && image->parent && !image->parent_image)
+ pmsg = " (not found in scan)";
+ else
+ pmsg = "";
+
+ if (!(flags & VHD_SCAN_VERBOSE)) {
+ name = basename(image->name);
+ if (image->parent)
+ parent = basename(image->parent);
+ }
+
+ if (image->error)
+ printf("%*svhd=%s scan-error=%d error-message='%s'\n",
+ tab, pad, image->name, image->error, image->message);
+ else
+ printf("%*svhd=%s capacity=%"PRIu64" size=%"PRIu64" hidden=%u "
+ "parent=%s%s\n", tab, pad, name, image->capacity,
+ image->size, image->hidden, parent, pmsg);
+}
+
+static void
+vhd_util_scan_pretty_print_tree(struct vhd_image *image, int depth)
+{
+ struct vhd_image *img, *tmp;
+
+ vhd_util_scan_print_image_indent(image, depth * 3);
+
+ list_for_each_entry_safe(img, tmp, &image->children, sibling)
+ if (!img->hidden)
+ vhd_util_scan_pretty_print_tree(img, depth + 1);
+
+ list_for_each_entry_safe(img, tmp, &image->children, sibling)
+ if (img->hidden)
+ vhd_util_scan_pretty_print_tree(img, depth + 1);
+
+ free(image->name);
+ free(image->parent);
+
+ image->name = NULL;
+ image->parent = NULL;
+}
+
+static void
+vhd_util_scan_pretty_print_images(void)
+{
+ int i;
+ struct vhd_image *image, **parentp, *parent, *keyp, key;
+
+ qsort(scan.images, scan.cur, sizeof(scan.images[0]),
+ vhd_util_scan_pretty_image_compare);
+
+ for (i = 0; i < scan.cur; i++) {
+ image = scan.images[i];
+
+ if (!image->parent) {
+ image->parent_image = NULL;
+ continue;
+ }
+
+ memset(&key, 0, sizeof(key));
+ key.name = image->parent;
+ keyp = &key;
+
+ parentp = bsearch(&keyp, scan.images, scan.cur,
+ sizeof(scan.images[0]),
+ vhd_util_scan_pretty_image_compare);
+ if (!parentp) {
+ image->parent_image = NULL;
+ continue;
+ }
+
+ parent = *parentp;
+ image->parent_image = parent;
+ list_add_tail(&image->sibling, &parent->children);
+ }
+
+ for (i = 0; i < scan.cur; i++) {
+ image = scan.images[i];
+
+ if (image->parent_image || !image->hidden)
+ continue;
+
+ vhd_util_scan_pretty_print_tree(image, 0);
+ }
+
+ for (i = 0; i < scan.cur; i++) {
+ image = scan.images[i];
+
+ if (!image->name || image->parent_image)
+ continue;
+
+ vhd_util_scan_pretty_print_tree(image, 0);
+ }
+
+ for (i = 0; i < scan.cur; i++) {
+ image = scan.images[i];
+
+ if (!image->name)
+ continue;
+
+ vhd_util_scan_pretty_print_tree(image, 0);
+ }
+}
+
+static void
+vhd_util_scan_print_image(struct vhd_image *image)
+{
+ int err;
+
+ if (!image->error && (flags & VHD_SCAN_PRETTY)) {
+ err = vhd_util_scan_pretty_add_image(image);
+ if (!err)
+ return;
+
+ if (!image->error) {
+ image->error = err;
+ image->message = "allocating memory";
+ }
+ }
+
+ vhd_util_scan_print_image_indent(image, 0);
+}
+
+static int
+vhd_util_scan_error(const char *file, int err)
+{
+ struct vhd_image image;
+
+ memset(&image, 0, sizeof(image));
+ image.name = (char *)file;
+ image.error = err;
+ image.message = "failure scanning target";
+
+ vhd_util_scan_print_image(&image);
+
+ /*
+ if (flags & VHD_SCAN_NOFAIL)
+ return 0;
+ */
+
+ return err;
+}
+
+static vhd_parent_locator_t *
+vhd_util_scan_get_parent_locator(vhd_context_t *vhd)
+{
+ int i;
+ vhd_parent_locator_t *loc;
+
+ loc = NULL;
+
+ for (i = 0; i < 8; i++) {
+ if (vhd->header.loc[i].code == PLAT_CODE_MACX) {
+ loc = vhd->header.loc + i;
+ break;
+ }
+
+ if (vhd->header.loc[i].code == PLAT_CODE_W2RU)
+ loc = vhd->header.loc + i;
+
+ if (!loc && vhd->header.loc[i].code != PLAT_CODE_NONE)
+ loc = vhd->header.loc + i;
+ }
+
+ return loc;
+}
+
+static inline int
+copy_name(char *dst, const char *src)
+{
+ if (snprintf(dst, VHD_MAX_NAME_LEN, "%s", src) < VHD_MAX_NAME_LEN)
+ return 0;
+
+ return -ENAMETOOLONG;
+}
+
+/*
+ * LVHD stores realpath(parent) in parent locators, so
+ * /dev/<vol-group>/<lv-name> becomes /dev/mapper/<vol--group>-<lv--name>
+ */
+static int
+vhd_util_scan_extract_volume_name(char *dst, const char *src)
+{
+ int err;
+ char copy[VHD_MAX_NAME_LEN], *name, *s, *c;
+
+ name = strrchr(src, '/');
+ if (!name)
+ name = (char *)src;
+
+ /* convert single dashes to slashes, double dashes to single dashes */
+ for (c = copy, s = name; *s != '\0'; s++, c++) {
+ if (*s == '-') {
+ if (s[1] != '-')
+ *c = '/';
+ else {
+ s++;
+ *c = '-';
+ }
+ } else
+ *c = *s;
+ }
+
+ *c = '\0';
+ c = strrchr(copy, '/');
+ if (c == name) {
+ /* unrecognized format */
+ strcpy(dst, src);
+ return -EINVAL;
+ }
+
+ strcpy(dst, ++c);
+ return 0;
+}
+
+static int
+vhd_util_scan_get_volume_parent(vhd_context_t *vhd, struct vhd_image *image)
+{
+ int err;
+ char name[VHD_MAX_NAME_LEN];
+ vhd_parent_locator_t *loc, copy;
+
+ if (flags & VHD_SCAN_FAST) {
+ err = vhd_header_decode_parent(vhd,
+ &vhd->header, &image->parent);
+ if (!err)
+ goto found;
+ }
+
+ loc = vhd_util_scan_get_parent_locator(vhd);
+ if (!loc)
+ return -EINVAL;
+
+ copy = *loc;
+ copy.data_offset += image->target->start;
+ err = vhd_parent_locator_read(vhd, &copy, &image->parent);
+ if (err)
+ return err;
+
+found:
+ err = vhd_util_scan_extract_volume_name(name, image->parent);
+ if (!err)
+ return copy_name(image->parent, name);
+
+ return 0;
+}
+
+static int
+vhd_util_scan_get_parent(vhd_context_t *vhd, struct vhd_image *image)
+{
+ int i, err;
+ vhd_parent_locator_t *loc;
+
+ if (!target_vhd(image->target->type)) {
+ image->parent = NULL;
+ return 0;
+ }
+
+ loc = NULL;
+
+ if (target_volume(image->target->type))
+ return vhd_util_scan_get_volume_parent(vhd, image);
+
+ if (flags & VHD_SCAN_FAST) {
+ err = vhd_header_decode_parent(vhd,
+ &vhd->header, &image->parent);
+ if (!err)
+ return 0;
+ } else {
+ /*
+ * vhd_parent_locator_get checks for the existence of the
+ * parent file. if this call succeeds, all is well; if not,
+ * we'll try to return whatever string we have before failing
+ * outright.
+ */
+ err = vhd_parent_locator_get(vhd, &image->parent);
+ if (!err)
+ return 0;
+ }
+
+ loc = vhd_util_scan_get_parent_locator(vhd);
+ if (!loc)
+ return -EINVAL;
+
+ return vhd_parent_locator_read(vhd, loc, &image->parent);
+}
+
+static int
+vhd_util_scan_get_hidden(vhd_context_t *vhd, struct vhd_image *image)
+{
+ int err, hidden;
+
+ err = 0;
+ hidden = 0;
+
+ if (target_vhd(image->target->type))
+ err = vhd_hidden(vhd, &hidden);
+ else
+ hidden = 1;
+
+ if (err)
+ return err;
+
+ image->hidden = hidden;
+ return 0;
+}
+
+static int
+vhd_util_scan_get_size(vhd_context_t *vhd, struct vhd_image *image)
+{
+ image->size = image->target->size;
+
+ if (target_vhd(image->target->type))
+ image->capacity = vhd->footer.curr_size;
+ else
+ image->capacity = image->size;
+
+ return 0;
+}
+
+static int
+vhd_util_scan_open_file(vhd_context_t *vhd, struct vhd_image *image)
+{
+ int err, vhd_flags;
+
+ if (!target_vhd(image->target->type))
+ return 0;
+
+ vhd_flags = VHD_OPEN_RDONLY | VHD_OPEN_IGNORE_DISABLED;
+ if (flags & VHD_SCAN_FAST)
+ vhd_flags |= VHD_OPEN_FAST;
+
+ err = vhd_open(vhd, image->name, vhd_flags);
+ if (err) {
+ vhd->file = NULL;
+ image->message = "opening file";
+ image->error = err;
+ return image->error;
+ }
+
+ return 0;
+}
+
+static int
+vhd_util_scan_read_volume_headers(vhd_context_t *vhd, struct vhd_image *image)
+{
+ int err;
+ char *buf;
+ size_t size;
+ struct target *target;
+
+ buf = NULL;
+ target = image->target;
+ size = sizeof(vhd_footer_t) + sizeof(vhd_header_t);
+
+ err = posix_memalign((void **)&buf, VHD_SECTOR_SIZE, size);
+ if (err) {
+ buf = NULL;
+ image->message = "allocating image";
+ image->error = -err;
+ goto out;
+ }
+
+ err = vhd_seek(vhd, target->start, SEEK_SET);
+ if (err) {
+ image->message = "seeking to headers";
+ image->error = err;
+ goto out;
+ }
+
+ err = vhd_read(vhd, buf, size);
+ if (err) {
+ image->message = "reading headers";
+ image->error = err;
+ goto out;
+ }
+
+ memcpy(&vhd->footer, buf, sizeof(vhd_footer_t));
+ vhd_footer_in(&vhd->footer);
+ err = vhd_validate_footer(&vhd->footer);
+ if (err) {
+ image->message = "invalid footer";
+ image->error = err;
+ goto out;
+ }
+
+ /* lvhd vhds should always be dynamic */
+ if (vhd_type_dynamic(vhd)) {
+ if (vhd->footer.data_offset != sizeof(vhd_footer_t))
+ err = vhd_read_header_at(vhd, &vhd->header,
+ vhd->footer.data_offset +
+ target->start);
+ else {
+ memcpy(&vhd->header,
+ buf + sizeof(vhd_footer_t),
+ sizeof(vhd_header_t));
+ vhd_header_in(&vhd->header);
+ err = vhd_validate_header(&vhd->header);
+ }
+
+ if (err) {
+ image->message = "reading header";
+ image->error = err;
+ goto out;
+ }
+
+ vhd->spb = vhd->header.block_size >> VHD_SECTOR_SHIFT;
+ vhd->bm_secs = secs_round_up_no_zero(vhd->spb >> 3);
+ }
+
+out:
+ free(buf);
+ return image->error;
+}
+
+static int
+vhd_util_scan_open_volume(vhd_context_t *vhd, struct vhd_image *image)
+{
+ int err;
+ struct target *target;
+
+ target = image->target;
+ memset(vhd, 0, sizeof(*vhd));
+ vhd->oflags = VHD_OPEN_RDONLY | VHD_OPEN_FAST;
+
+ if (target->end - target->start < 4096) {
+ image->message = "device too small";
+ image->error = -EINVAL;
+ return image->error;
+ }
+
+ vhd->file = strdup(image->name);
+ if (!vhd->file) {
+ image->message = "allocating device";
+ image->error = -ENOMEM;
+ return image->error;
+ }
+
+ vhd->fd = open(target->device, O_RDONLY | O_DIRECT | O_LARGEFILE);
+ if (vhd->fd == -1) {
+ free(vhd->file);
+ vhd->file = NULL;
+
+ image->message = "opening device";
+ image->error = -errno;
+ return image->error;
+ }
+
+ if (target_vhd(target->type))
+ return vhd_util_scan_read_volume_headers(vhd, image);
+
+ return 0;
+}
+
+static int
+vhd_util_scan_open(vhd_context_t *vhd, struct vhd_image *image)
+{
+ struct target *target;
+
+ target = image->target;
+
+ if (target_volume(image->target->type) || !(flags & VHD_SCAN_PRETTY))
+ image->name = target->name;
+ else {
+ image->name = realpath(target->name, NULL);
+ if (!image->name) {
+ image->name = target->name;
+ image->message = "resolving name";
+ image->error = -errno;
+ return image->error;
+ }
+ }
+
+ if (target_volume(target->type))
+ return vhd_util_scan_open_volume(vhd, image);
+ else
+ return vhd_util_scan_open_file(vhd, image);
+}
+
+static int
+vhd_util_scan_init_file_target(struct target *target,
+ const char *file, uint8_t type)
+{
+ int err;
+ struct stat stats;
+
+ err = stat(file, &stats);
+ if (err == -1)
+ return -errno;
+
+ err = copy_name(target->name, file);
+ if (err)
+ return err;
+
+ err = copy_name(target->device, file);
+ if (err)
+ return err;
+
+ target->type = type;
+ target->start = 0;
+ target->size = stats.st_size;
+ target->end = stats.st_size;
+
+ return 0;
+}
+
+static int
+vhd_util_scan_init_volume_target(struct target *target,
+ struct lv *lv, uint8_t type)
+{
+ int err;
+
+ if (lv->first_segment.type != LVM_SEG_TYPE_LINEAR)
+ return -ENOSYS;
+
+ err = copy_name(target->name, lv->name);
+ if (err)
+ return err;
+
+ err = copy_name(target->device, lv->first_segment.device);
+ if (err)
+ return err;
+
+ target->type = type;
+ target->size = lv->size;
+ target->start = lv->first_segment.pe_start;
+ target->end = target->start + lv->first_segment.pe_size;
+
+ return 0;
+}
+
+static int
+iterator_init(struct iterator *itr, int cnt, struct target *targets)
+{
+ memset(itr, 0, sizeof(*itr));
+
+ itr->targets = malloc(sizeof(struct target) * cnt);
+ if (!itr->targets)
+ return -ENOMEM;
+
+ memcpy(itr->targets, targets, sizeof(struct target) * cnt);
+
+ itr->cur = 0;
+ itr->cur_size = cnt;
+ itr->max_size = cnt;
+
+ return 0;
+}
+
+static struct target *
+iterator_next(struct iterator *itr)
+{
+ if (itr->cur == itr->cur_size)
+ return NULL;
+
+ return itr->targets + itr->cur++;
+}
+
+static int
+iterator_add_file(struct iterator *itr,
+ struct target *target, const char *parent, uint8_t type)
+{
+ int i;
+ struct target *t;
+ char *lname, *rname;
+
+ for (i = 0; i < itr->cur_size; i++) {
+ t = itr->targets + i;
+ lname = basename((char *)t->name);
+ rname = basename((char *)parent);
+
+ if (!strcmp(lname, rname))
+ return -EEXIST;
+ }
+
+ return vhd_util_scan_init_file_target(target, parent, type);
+}
+
+static int
+iterator_add_volume(struct iterator *itr,
+ struct target *target, const char *parent, uint8_t type)
+{
+ int i, err;
+ struct lv *lv;
+
+ lv = NULL;
+ err = -ENOENT;
+
+ for (i = 0; i < itr->cur_size; i++)
+ if (!strcmp(parent, itr->targets[i].name))
+ return -EEXIST;
+
+ for (i = 0; i < vg.lv_cnt; i++) {
+ err = fnmatch(parent, vg.lvs[i].name, FNM_PATHNAME);
+ if (err != FNM_NOMATCH) {
+ lv = vg.lvs + i;
+ break;
+ }
+ }
+
+ if (err && err != FNM_PATHNAME)
+ return err;
+
+ if (!lv)
+ return -ENOENT;
+
+ return vhd_util_scan_init_volume_target(target, lv, type);
+}
+
+static int
+iterator_add(struct iterator *itr, const char *parent, uint8_t type)
+{
+ int err;
+ struct target *target;
+
+ if (itr->cur_size == itr->max_size) {
+ struct target *new;
+
+ new = realloc(itr->targets,
+ sizeof(struct target) *
+ itr->max_size * 2);
+ if (!new)
+ return -ENOMEM;
+
+ itr->max_size *= 2;
+ itr->targets = new;
+ }
+
+ target = itr->targets + itr->cur_size;
+
+ if (target_volume(type))
+ err = iterator_add_volume(itr, target, parent, type);
+ else
+ err = iterator_add_file(itr, target, parent, type);
+
+ if (err)
+ memset(target, 0, sizeof(*target));
+ else
+ itr->cur_size++;
+
+ return (err == -EEXIST ? 0 : err);
+}
+
+static void
+iterator_free(struct iterator *itr)
+{
+ free(itr->targets);
+ memset(itr, 0, sizeof(*itr));
+}
+
+static void
+vhd_util_scan_add_parent(struct iterator *itr,
+ vhd_context_t *vhd, struct vhd_image *image)
+{
+ int err;
+ uint8_t type;
+
+ if (vhd_parent_raw(vhd))
+ type = target_volume(image->target->type) ?
+ VHD_TYPE_RAW_VOLUME : VHD_TYPE_RAW_FILE;
+ else
+ type = target_volume(image->target->type) ?
+ VHD_TYPE_VHD_VOLUME : VHD_TYPE_VHD_FILE;
+
+ err = iterator_add(itr, image->parent, type);
+ if (err)
+ vhd_util_scan_error(image->parent, err);
+}
+
+static int
+vhd_util_scan_targets(int cnt, struct target *targets)
+{
+ int ret, err;
+ vhd_context_t vhd;
+ struct iterator itr;
+ struct target *target;
+ struct vhd_image image;
+
+ ret = 0;
+ err = 0;
+
+ err = iterator_init(&itr, cnt, targets);
+ if (err)
+ return err;
+
+ while ((target = iterator_next(&itr))) {
+ memset(&vhd, 0, sizeof(vhd));
+ memset(&image, 0, sizeof(image));
+
+ image.target = target;
+
+ err = vhd_util_scan_open(&vhd, &image);
+ if (err) {
+ ret = -EAGAIN;
+ goto end;
+ }
+
+ err = vhd_util_scan_get_size(&vhd, &image);
+ if (err) {
+ ret = -EAGAIN;
+ image.message = "getting physical size";
+ image.error = err;
+ goto end;
+ }
+
+ err = vhd_util_scan_get_hidden(&vhd, &image);
+ if (err) {
+ ret = -EAGAIN;
+ image.message = "checking 'hidden' field";
+ image.error = err;
+ goto end;
+ }
+
+ if (vhd.footer.type == HD_TYPE_DIFF) {
+ err = vhd_util_scan_get_parent(&vhd, &image);
+ if (err) {
+ ret = -EAGAIN;
+ image.message = "getting parent";
+ image.error = err;
+ goto end;
+ }
+ }
+
+ end:
+ vhd_util_scan_print_image(&image);
+
+ if (flags & VHD_SCAN_PARENTS && image.parent)
+ vhd_util_scan_add_parent(&itr, &vhd, &image);
+
+ if (vhd.file)
+ vhd_close(&vhd);
+ if (image.name != target->name)
+ free(image.name);
+ free(image.parent);
+
+ if (err && !(flags & VHD_SCAN_NOFAIL))
+ break;
+ }
+
+ iterator_free(&itr);
+
+ if (flags & VHD_SCAN_NOFAIL)
+ return ret;
+
+ return err;
+}
+
+static int
+vhd_util_scan_targets_pretty(int cnt, struct target *targets)
+{
+ int err;
+
+ err = vhd_util_scan_pretty_allocate_list(cnt);
+ if (err) {
+ printf("scan failed: no memory\n");
+ return -ENOMEM;
+ }
+
+ err = vhd_util_scan_targets(cnt, targets);
+
+ vhd_util_scan_pretty_print_images();
+ vhd_util_scan_pretty_free_list();
+
+ return ((flags & VHD_SCAN_NOFAIL) ? 0 : err);
+}
+
+static int
+vhd_util_scan_find_file_targets(int cnt, char **names,
+ const char *filter,
+ struct target **_targets, int *_total)
+{
+ glob_t g;
+ struct target *targets;
+ int i, globs, err, total;
+
+ total = cnt;
+ globs = 0;
+ *_total = 0;
+ *_targets = NULL;
+
+ memset(&g, 0, sizeof(g));
+
+ if (filter) {
+ int gflags = ((flags & VHD_SCAN_FAST) ? GLOB_NOSORT : 0);
+
+ errno = 0;
+ err = glob(filter, gflags, vhd_util_scan_error, &g);
+
+ switch (err) {
+ case GLOB_NOSPACE:
+ err = -ENOMEM;
+ break;
+ case GLOB_ABORTED:
+ err = -EIO;
+ break;
+ case GLOB_NOMATCH:
+ err = -errno;
+ break;
+ }
+
+ if (err) {
+ vhd_util_scan_error(filter, err);
+ return err;
+ }
+
+ globs = g.gl_pathc;
+ total += globs;
+ }
+
+ targets = calloc(total, sizeof(struct target));
+ if (!targets) {
+ err = -ENOMEM;
+ goto out;
+ }
+
+ for (i = 0; i < g.gl_pathc; i++) {
+ err = vhd_util_scan_init_file_target(targets + i,
+ g.gl_pathv[i],
+ VHD_TYPE_VHD_FILE);
+ if (err) {
+ vhd_util_scan_error(g.gl_pathv[i], err);
+ if (!(flags & VHD_SCAN_NOFAIL))
+ goto out;
+ }
+ }
+
+ for (i = 0; i + globs < total; i++) {
+ err = vhd_util_scan_init_file_target(targets + i + globs,
+ names[i],
+ VHD_TYPE_VHD_FILE);
+ if (err) {
+ vhd_util_scan_error(names[i], err);
+ if (!(flags & VHD_SCAN_NOFAIL))
+ goto out;
+ }
+ }
+
+ err = 0;
+ *_total = total;
+ *_targets = targets;
+
+out:
+ if (err)
+ free(targets);
+ if (filter)
+ globfree(&g);
+
+ return err;
+}
+
+static inline void
+swap_volume(struct lv *lvs, int dst, int src)
+{
+ struct lv copy, *ldst, *lsrc;
+
+ if (dst == src)
+ return;
+
+ lsrc = lvs + src;
+ ldst = lvs + dst;
+
+ memcpy(&copy, ldst, sizeof(copy));
+ memcpy(ldst, lsrc, sizeof(*ldst));
+ memcpy(lsrc, &copy, sizeof(copy));
+}
+
+static int
+vhd_util_scan_sort_volumes(struct lv *lvs, int cnt,
+ const char *filter, int *_matches)
+{
+ struct lv *lv;
+ int i, err, matches;
+
+ matches = 0;
+ *_matches = 0;
+
+ if (!filter)
+ return 0;
+
+ for (i = 0; i < cnt; i++) {
+ lv = lvs + i;
+
+ err = fnmatch(filter, lv->name, FNM_PATHNAME);
+ if (err) {
+ if (err != FNM_NOMATCH) {
+ vhd_util_scan_error(lv->name, err);
+ if (!(flags & VHD_SCAN_NOFAIL))
+ return err;
+ }
+
+ continue;
+ }
+
+ swap_volume(lvs, matches++, i);
+ }
+
+ *_matches = matches;
+ return 0;
+}
+
+static int
+vhd_util_scan_find_volume_targets(int cnt, char **names,
+ const char *volume, const char *filter,
+ struct target **_targets, int *_total)
+{
+ struct target *targets;
+ int i, err, total, matches;
+
+ *_total = 0;
+ *_targets = NULL;
+ targets = NULL;
+
+ err = lvm_scan_vg(volume, &vg);
+ if (err)
+ return err;
+
+ err = vhd_util_scan_sort_volumes(vg.lvs, vg.lv_cnt,
+ filter, &matches);
+ if (err)
+ goto out;
+
+ total = matches;
+ for (i = 0; i < cnt; i++) {
+ err = vhd_util_scan_sort_volumes(vg.lvs + total,
+ vg.lv_cnt - total,
+ names[i], &matches);
+ if (err)
+ goto out;
+
+ total += matches;
+ }
+
+ targets = calloc(total, sizeof(struct target));
+ if (!targets) {
+ err = -ENOMEM;
+ goto out;
+ }
+
+ for (i = 0; i < total; i++) {
+ err = vhd_util_scan_init_volume_target(targets + i,
+ vg.lvs + i,
+ VHD_TYPE_VHD_VOLUME);
+ if (err) {
+ vhd_util_scan_error(vg.lvs[i].name, err);
+ if (!(flags & VHD_SCAN_NOFAIL))
+ goto out;
+ }
+ }
+
+ err = 0;
+ *_total = total;
+ *_targets = targets;
+
+out:
+ if (err)
+ free(targets);
+ return err;
+}
+
+static int
+vhd_util_scan_find_targets(int cnt, char **names,
+ const char *volume, const char *filter,
+ struct target **targets, int *total)
+{
+ if (flags & VHD_SCAN_VOLUME)
+ return vhd_util_scan_find_volume_targets(cnt, names,
+ volume, filter,
+ targets, total);
+ return vhd_util_scan_find_file_targets(cnt, names,
+ filter, targets, total);
+}
+
+int
+vhd_util_scan(int argc, char **argv)
+{
+ int c, ret, err, cnt;
+ char *filter, *volume;
+ struct target *targets;
+
+ cnt = 0;
+ ret = 0;
+ err = 0;
+ flags = 0;
+ filter = NULL;
+ volume = NULL;
+ targets = NULL;
+
+ optind = 0;
+ while ((c = getopt(argc, argv, "m:fcl:pavh")) != -1) {
+ switch (c) {
+ case 'm':
+ filter = optarg;
+ break;
+ case 'f':
+ flags |= VHD_SCAN_FAST;
+ break;
+ case 'c':
+ flags |= VHD_SCAN_NOFAIL;
+ break;
+ case 'l':
+ volume = optarg;
+ flags |= VHD_SCAN_VOLUME;
+ break;
+ case 'p':
+ flags |= VHD_SCAN_PRETTY;
+ break;
+ case 'a':
+ flags |= VHD_SCAN_PARENTS;
+ break;
+ case 'v':
+ flags |= VHD_SCAN_VERBOSE;
+ break;
+ case 'h':
+ goto usage;
+ default:
+ err = -EINVAL;
+ goto usage;
+ }
+ }
+
+ if (!filter && argc - optind == 0) {
+ err = -EINVAL;
+ goto usage;
+ }
+
+ if (flags & VHD_SCAN_PRETTY)
+ flags &= ~VHD_SCAN_FAST;
+
+ err = vhd_util_scan_find_targets(argc - optind, argv + optind,
+ volume, filter, &targets, &cnt);
+ if (err) {
+ printf("scan failed: %d\n", err);
+ return err;
+ }
+
+ if (!cnt)
+ return 0;
+
+ if (flags & VHD_SCAN_PRETTY)
+ err = vhd_util_scan_targets_pretty(cnt, targets);
+ else
+ err = vhd_util_scan_targets(cnt, targets);
+
+ free(targets);
+ lvm_free_vg(&vg);
+
+ return ((flags & VHD_SCAN_NOFAIL) ? 0 : err);
+
+usage:
+ printf("usage: [OPTIONS] FILES\n"
+ "options: [-m match filter] [-f fast] [-c continue on failure] "
+ "[-l LVM volume] [-p pretty print] [-a scan parents] "
+ "[-v verbose] [-h help]\n");
+ return err;
+}
diff --git a/tools/blktap2/vhd/lib/vhd-util-set-field.c b/tools/blktap2/vhd/lib/vhd-util-set-field.c
new file mode 100644
index 0000000000..ac185735d9
--- /dev/null
+++ b/tools/blktap2/vhd/lib/vhd-util-set-field.c
@@ -0,0 +1,106 @@
+/* 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 <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include "libvhd.h"
+
+int
+vhd_util_set_field(int argc, char **argv)
+{
+ long value;
+ int err, c;
+ off64_t eof;
+ vhd_context_t vhd;
+ char *name, *field;
+
+ err = -EINVAL;
+ value = 0;
+ name = NULL;
+ field = NULL;
+
+ if (!argc || !argv)
+ goto usage;
+
+ optind = 0;
+ while ((c = getopt(argc, argv, "n:f:v:h")) != -1) {
+ switch (c) {
+ case 'n':
+ name = optarg;
+ break;
+ case 'f':
+ field = optarg;
+ break;
+ case 'v':
+ err = 0;
+ value = strtol(optarg, NULL, 10);
+ break;
+ case 'h':
+ default:
+ goto usage;
+ }
+ }
+
+ if (!name || !field || optind != argc || err)
+ goto usage;
+
+ if (strnlen(field, 25) >= 25) {
+ printf("invalid field\n");
+ goto usage;
+ }
+
+ if (strcmp(field, "hidden")) {
+ printf("invalid field %s\n", field);
+ goto usage;
+ }
+
+ if (value < 0 || value > 255) {
+ printf("invalid value %ld\n", value);
+ goto usage;
+ }
+
+ err = vhd_open(&vhd, name, VHD_OPEN_RDWR);
+ if (err) {
+ printf("error opening %s: %d\n", name, err);
+ return err;
+ }
+
+ vhd.footer.hidden = (char)value;
+
+ err = vhd_write_footer(&vhd, &vhd.footer);
+
+ done:
+ vhd_close(&vhd);
+ return err;
+
+usage:
+ printf("options: <-n name> <-f field> <-v value> [-h help]\n");
+ return -EINVAL;
+}
diff --git a/tools/blktap2/vhd/lib/vhd-util-snapshot.c b/tools/blktap2/vhd/lib/vhd-util-snapshot.c
new file mode 100644
index 0000000000..75960f96ea
--- /dev/null
+++ b/tools/blktap2/vhd/lib/vhd-util-snapshot.c
@@ -0,0 +1,216 @@
+/* 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 <errno.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include "libvhd.h"
+
+static int
+vhd_util_find_snapshot_target(const char *name, char **result, int *parent_raw)
+{
+ int i, err;
+ char *target;
+ vhd_context_t vhd;
+
+ *parent_raw = 0;
+ *result = NULL;
+
+ target = strdup(name);
+ if (!target)
+ return -ENOMEM;
+
+ for (;;) {
+ err = vhd_open(&vhd, target, VHD_OPEN_RDONLY);
+ if (err)
+ return err;
+
+ if (vhd.footer.type != HD_TYPE_DIFF)
+ goto out;
+
+ err = vhd_get_bat(&vhd);
+ if (err)
+ goto out;
+
+ for (i = 0; i < vhd.bat.entries; i++)
+ if (vhd.bat.bat[i] != DD_BLK_UNUSED)
+ goto out;
+
+ free(target);
+ err = vhd_parent_locator_get(&vhd, &target);
+ if (err)
+ goto out;
+
+ if (vhd_parent_raw(&vhd)) {
+ *parent_raw = 1;
+ goto out;
+ }
+
+ vhd_close(&vhd);
+ }
+
+out:
+ vhd_close(&vhd);
+ if (err)
+ free(target);
+ else
+ *result = target;
+
+ return err;
+}
+
+static int
+vhd_util_check_depth(const char *name, int *depth)
+{
+ int err;
+ vhd_context_t vhd;
+
+ err = vhd_open(&vhd, name, VHD_OPEN_RDONLY);
+ if (err)
+ return err;
+
+ err = vhd_chain_depth(&vhd, depth);
+ vhd_close(&vhd);
+
+ return err;
+}
+
+int
+vhd_util_snapshot(int argc, char **argv)
+{
+ vhd_flag_creat_t flags;
+ int c, err, prt_raw, limit;
+ char *name, *pname, *ppath, *backing;
+ uint64_t size;
+ vhd_context_t vhd;
+
+ name = NULL;
+ pname = NULL;
+ ppath = NULL;
+ backing = NULL;
+ size = 0;
+ flags = 0;
+ limit = 0;
+
+ if (!argc || !argv) {
+ err = -EINVAL;
+ goto usage;
+ }
+
+ optind = 0;
+ while ((c = getopt(argc, argv, "n:p:l:mh")) != -1) {
+ switch (c) {
+ case 'n':
+ name = optarg;
+ break;
+ case 'p':
+ pname = optarg;
+ break;
+ case 'l':
+ limit = strtol(optarg, NULL, 10);
+ break;
+ case 'm':
+ vhd_flag_set(flags, VHD_FLAG_CREAT_PARENT_RAW);
+ break;
+ case 'h':
+ err = 0;
+ goto usage;
+ default:
+ err = -EINVAL;
+ goto usage;
+ }
+ }
+
+ if (!name || !pname || optind != argc) {
+ err = -EINVAL;
+ goto usage;
+ }
+
+ ppath = realpath(pname, NULL);
+ if (!ppath)
+ return -errno;
+
+ if (vhd_flag_test(flags, VHD_FLAG_CREAT_PARENT_RAW)) {
+ backing = strdup(ppath);
+ if (!backing) {
+ err = -ENOMEM;
+ goto out;
+ }
+ } else {
+ err = vhd_util_find_snapshot_target(ppath, &backing, &prt_raw);
+ if (err) {
+ backing = NULL;
+ goto out;
+ }
+
+ /*
+ * if the sizes of the parent chain are non-uniform, we need to
+ * pick the right size: that of the supplied parent
+ */
+ if (strcmp(ppath, backing)) {
+ err = vhd_open(&vhd, ppath, VHD_OPEN_RDONLY);
+ if (err)
+ goto out;
+ size = vhd.footer.curr_size;
+ vhd_close(&vhd);
+ }
+
+ if (prt_raw)
+ vhd_flag_set(flags, VHD_FLAG_CREAT_PARENT_RAW);
+ }
+
+ if (limit && !vhd_flag_test(flags, VHD_FLAG_CREAT_PARENT_RAW)) {
+ int depth;
+
+ err = vhd_util_check_depth(backing, &depth);
+ if (err)
+ printf("error checking snapshot depth: %d\n", err);
+ else if (depth + 1 > limit) {
+ err = -ENOSPC;
+ printf("snapshot depth exceeded: "
+ "current depth: %d, limit: %d\n", depth, limit);
+ }
+
+ if (err)
+ goto out;
+ }
+
+ err = vhd_snapshot(name, size, backing, flags);
+
+out:
+ free(ppath);
+ free(backing);
+
+ return err;
+
+usage:
+ printf("options: <-n name> <-p parent name> [-l snapshot depth limit]"
+ " [-m parent_is_raw] [-h help]\n");
+ return err;
+}
diff --git a/tools/blktap2/vhd/vhd-update.c b/tools/blktap2/vhd/vhd-update.c
new file mode 100644
index 0000000000..fbc23cc7ae
--- /dev/null
+++ b/tools/blktap2/vhd/vhd-update.c
@@ -0,0 +1,261 @@
+/* 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.
+ *
+ * Before updating a VHD file, we create a journal consisting of:
+ * - all data at the beginning of the file, up to and including the BAT
+ * - each allocated bitmap (existing at the same offset in the journal as
+ * its corresponding bitmap in the original file)
+ * Updates are performed in place by writing appropriately
+ * transformed versions of journaled bitmaps to the original file.
+ */
+#include <stdio.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <endian.h>
+#include <byteswap.h>
+
+#include "atomicio.h"
+#include "libvhd.h"
+#include "libvhd-journal.h"
+
+static void
+usage(void)
+{
+ printf("usage: vhd-update <-n name> [-j existing journal] [-h]\n");
+ exit(EINVAL);
+}
+
+/*
+ * update vhd creator version to reflect its new bitmap ordering
+ */
+static inline int
+update_creator_version(vhd_journal_t *journal)
+{
+ journal->vhd.footer.crtr_ver = VHD_VERSION(1, 1);
+ return vhd_write_footer(&journal->vhd, &journal->vhd.footer);
+}
+
+static int
+journal_bitmaps(vhd_journal_t *journal)
+{
+ int i, err;
+
+ for (i = 0; i < journal->vhd.bat.entries; i++) {
+ err = vhd_journal_add_block(journal, i, VHD_JOURNAL_METADATA);
+ if (err)
+ return err;
+ }
+
+ return 0;
+}
+
+/*
+ * older VHD bitmaps were little endian
+ * and bits within a word were set from right to left
+ */
+static inline int
+old_test_bit(int nr, volatile void * addr)
+{
+ return (((unsigned long*)addr)[nr/(sizeof(unsigned long)*8)] >>
+ (nr % (sizeof(unsigned long)*8))) & 1;
+}
+
+/*
+ * new VHD bitmaps are big endian
+ * and bits within a word are set from left to right
+ */
+#define BIT_MASK 0x80
+static inline void
+new_set_bit (int nr, volatile char *addr)
+{
+ addr[nr >> 3] |= (BIT_MASK >> (nr & 7));
+}
+
+static void
+convert_bitmap(char *in, char *out, int bytes)
+{
+ int i;
+
+ memset(out, 0, bytes);
+
+ for (i = 0; i < bytes << 3; i++)
+ if (old_test_bit(i, (void *)in))
+ new_set_bit(i, out);
+}
+
+static int
+update_vhd(vhd_journal_t *journal, int rollback)
+{
+ int i, err;
+ size_t size;
+ char *buf, *converted;
+
+ buf = NULL;
+ converted = NULL;
+
+ size = vhd_bytes_padded(journal->vhd.spb / 8);
+ err = posix_memalign((void **)&converted, 512, size);
+ if (err) {
+ converted = NULL;
+ goto out;
+ }
+
+ for (i = 0; i < journal->vhd.bat.entries; i++) {
+ if (journal->vhd.bat.bat[i] == DD_BLK_UNUSED)
+ continue;
+
+ err = vhd_read_bitmap(&journal->vhd, i, &buf);
+ if (err)
+ goto out;
+
+ if (rollback)
+ memcpy(converted, buf, size);
+ else
+ convert_bitmap(buf, converted, size);
+
+ free(buf);
+
+ err = vhd_write_bitmap(&journal->vhd, i, converted);
+ if (err)
+ goto out;
+ }
+
+ err = 0;
+ out:
+ free(converted);
+ return err;
+}
+
+static int
+open_journal(vhd_journal_t *journal, const char *file, const char *jfile)
+{
+ int err;
+
+ err = vhd_journal_create(journal, file, jfile);
+ if (err) {
+ printf("error creating journal for %s: %d\n", file, err);
+ return err;
+ }
+
+ return 0;
+}
+
+static int
+close_journal(vhd_journal_t *journal, int err)
+{
+ if (err)
+ err = vhd_journal_revert(journal);
+ else
+ err = vhd_journal_commit(journal);
+
+ if (err)
+ return vhd_journal_close(journal);
+ else
+ return vhd_journal_remove(journal);
+}
+
+int
+main(int argc, char **argv)
+{
+ char *file, *jfile;
+ int c, err, rollback;
+ vhd_journal_t journal;
+
+ file = NULL;
+ jfile = NULL;
+ rollback = 0;
+
+ while ((c = getopt(argc, argv, "n:j:rh")) != -1) {
+ switch(c) {
+ case 'n':
+ file = optarg;
+ break;
+ case 'j':
+ jfile = optarg;
+ err = access(jfile, R_OK);
+ if (err == -1) {
+ printf("invalid journal arg %s\n", jfile);
+ return -errno;
+ }
+ break;
+ case 'r':
+ /* add a rollback option for debugging which
+ * pushes journalled bitmaps to original file
+ * without transforming them */
+ rollback = 1;
+ break;
+ default:
+ usage();
+ }
+ }
+
+ if (!file)
+ usage();
+
+ if (rollback && !jfile) {
+ printf("rollback requires a journal argument\n");
+ usage();
+ }
+
+ err = open_journal(&journal, file, jfile);
+ if (err)
+ return err;
+
+ if (!vhd_creator_tapdisk(&journal.vhd) ||
+ journal.vhd.footer.crtr_ver != VHD_VERSION(0, 1) ||
+ journal.vhd.footer.type == HD_TYPE_FIXED) {
+ err = 0;
+ goto out;
+ }
+
+ err = journal_bitmaps(&journal);
+ if (err) {
+ /* no changes to vhd file yet,
+ * so close the journal and bail */
+ vhd_journal_close(&journal);
+ return err;
+ }
+
+ err = update_vhd(&journal, rollback);
+ if (err) {
+ printf("update failed: %d; saving journal\n", err);
+ goto out;
+ }
+
+ err = update_creator_version(&journal);
+ if (err) {
+ printf("failed to udpate creator version: %d\n", err);
+ goto out;
+ }
+
+ err = 0;
+
+out:
+ err = close_journal(&journal, err);
+ return err;
+}
diff --git a/tools/blktap2/vhd/vhd-util.c b/tools/blktap2/vhd/vhd-util.c
new file mode 100644
index 0000000000..944a59e395
--- /dev/null
+++ b/tools/blktap2/vhd/vhd-util.c
@@ -0,0 +1,160 @@
+/* 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 <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "libvhd.h"
+#include "vhd-util.h"
+
+#if 1
+#define DFPRINTF(_f, _a...) fprintf(stdout, _f , ##_a)
+#else
+#define DFPRINTF(_f, _a...) ((void)0)
+#endif
+
+typedef int (*vhd_util_func_t) (int, char **);
+
+struct command {
+ char *name;
+ vhd_util_func_t func;
+};
+
+struct command commands[] = {
+ { .name = "create", .func = vhd_util_create },
+ { .name = "snapshot", .func = vhd_util_snapshot },
+ { .name = "query", .func = vhd_util_query },
+ { .name = "read", .func = vhd_util_read },
+ { .name = "set", .func = vhd_util_set_field },
+ { .name = "repair", .func = vhd_util_repair },
+ { .name = "resize", .func = vhd_util_resize },
+ { .name = "fill", .func = vhd_util_fill },
+ { .name = "coalesce", .func = vhd_util_coalesce },
+ { .name = "modify", .func = vhd_util_modify },
+ { .name = "scan", .func = vhd_util_scan },
+ { .name = "check", .func = vhd_util_check },
+ { .name = "revert", .func = vhd_util_revert },
+};
+
+#define print_commands() \
+ do { \
+ int i, n; \
+ n = sizeof(commands) / sizeof(struct command); \
+ printf("COMMAND := { "); \
+ printf("%s", commands[0].name); \
+ for (i = 1; i < n; i++) \
+ printf(" | %s", commands[i].name); \
+ printf(" }\n"); \
+ } while (0)
+
+TEST_FAIL_EXTERN_VARS;
+
+void
+help(void)
+{
+ printf("usage: vhd-util COMMAND [OPTIONS]\n");
+ print_commands();
+ exit(0);
+}
+
+struct command *
+get_command(char *command)
+{
+ int i, n;
+
+ if (strnlen(command, 25) >= 25)
+ return NULL;
+
+ n = sizeof(commands) / sizeof (struct command);
+
+ for (i = 0; i < n; i++)
+ if (!strcmp(command, commands[i].name))
+ return &commands[i];
+
+ return NULL;
+}
+
+int
+main(int argc, char *argv[])
+{
+ char **cargv;
+ struct command *cmd;
+ int cargc, i, cnt, ret;
+
+#ifdef CORE_DUMP
+ #include <sys/resource.h>
+ struct rlimit rlim;
+ rlim.rlim_cur = RLIM_INFINITY;
+ rlim.rlim_max = RLIM_INFINITY;
+ if (setrlimit(RLIMIT_CORE, &rlim) < 0)
+ fprintf(stderr, "setrlimit failed: %d\n", errno);
+#endif
+
+ ret = 0;
+
+ if (argc < 2)
+ help();
+
+ cargc = argc - 1;
+ cmd = get_command(argv[1]);
+ if (!cmd) {
+ fprintf(stderr, "invalid COMMAND %s\n", argv[1]);
+ help();
+ }
+
+ cargv = malloc(sizeof(char *) * cargc);
+ if (!cargv)
+ exit(ENOMEM);
+
+ cnt = 1;
+ cargv[0] = cmd->name;
+ for (i = 1; i < cargc; i++) {
+ char *arg = argv[i + (argc - cargc)];
+
+ if (!strcmp(arg, "--debug")) {
+ libvhd_set_log_level(1);
+ continue;
+ }
+
+ cargv[cnt++] = arg;
+ }
+
+#ifdef ENABLE_FAILURE_TESTING
+ for (i = 0; i < NUM_FAIL_TESTS; i++) {
+ TEST_FAIL[i] = 0;
+ if (getenv(ENV_VAR_FAIL[i]))
+ TEST_FAIL[i] = 1;
+ }
+#endif // ENABLE_FAILURE_TESTING
+
+ ret = cmd->func(cnt, cargv);
+
+ free(cargv);
+
+ return (ret >= 0 ? ret : -ret);
+}