[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Xen-devel] [PATCH 2 of 8] blktap3/vhd: Introduce libvhdio.c



This patch import libvhdio.c from blktap2.5. It seems to contain low-level
functions for opening/closing/reading/writing etc. VHD files.

Signed-off-by: Thanos Makatos <thanos.makatos@xxxxxxxxxx>

diff --git a/tools/blktap3/vhd/lib/libvhdio.c b/tools/blktap3/vhd/lib/libvhdio.c
new file mode 100644
--- /dev/null
+++ b/tools/blktap3/vhd/lib/libvhdio.c
@@ -0,0 +1,1619 @@
+/*
+ * Copyright (c) 2008, XenSource Inc.
+ * Copyright (c) 2010, Citrix Systems, 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
+
+#ifdef _LARGEFILE_SOURCE
+#undef _LARGEFILE_SOURCE
+#endif
+
+#ifdef _LARGEFILE64_SOURCE
+#undef _LARGEFILE64_SOURCE
+#endif
+
+#if defined(_FILE_OFFSET_BITS) && _FILE_OFFSET_BITS == 64
+#undef _FILE_OFFSET_BITS
+#define _FILE_OFFSET_BITS 32
+#endif
+
+/* TODO again? */
+#ifdef _LARGEFILE_SOURCE
+#undef _LARGEFILE_SOURCE
+#endif
+
+#include <time.h>
+#include <stdio.h>
+#include <errno.h>
+#include <dlfcn.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdarg.h>
+#include <signal.h>
+#include <malloc.h>
+#include <sys/stat.h>
+#include <linux/fs.h>
+#include <linux/hdreg.h>
+
+#define _FCNTL_H
+#include <bits/fcntl.h>
+
+#include "libvhd.h"
+#include "partition.h"
+
+/* TODO define in a common location */
+#define _ARRAY_SIZE(array) (sizeof(array) / sizeof(array[0]))
+
+#define __RESOLVE(func, name)                                          \
+       do {                                                            \
+               if (!_libvhd_io_initialized)                            \
+                       _libvhd_io_init();                              \
+               if (!(func))                                            \
+                       (func) = _get_std_fn((name));                   \
+       } while (0)
+
+#define _RESOLVE(func) __RESOLVE((func), __func__)
+
+#define LIBVHD_IO_DEBUG "LIBVHD_IO_DEBUG"
+#define LIBVHD_IO_DUMP  "LIBVHD_IO_DUMP"
+#define LIBVHD_IO_TEST  "LIBVHD_IO_TEST"
+
+static int libvhdio_logging;
+static FILE *libvhdio_log;
+#define LOG(_f, _a...)                                                 \
+       do {                                                            \
+               if (libvhdio_logging && libvhdio_log) {                 \
+                       fprintf(libvhdio_log, _f, ##_a);                \
+                       fflush(libvhdio_log);                           \
+               }                                                       \
+       } while (0)
+
+static int libvhdio_dump;
+#define DUMP(_buf, _size)                                              \
+       do {                                                            \
+               if (libvhdio_log && libvhdio_dump) {                    \
+                       int i;                                          \
+                       LOG("'");                                       \
+                       for (i = 0; i < (_size); i++)                   \
+                               fputc(((char *)(_buf))[i],              \
+                                     libvhdio_log);                    \
+                       LOG("'\n");                                     \
+               }                                                       \
+       } while (0)
+
+struct _function {
+    const char *name;
+    void *fn;
+};
+
+struct vhd_object {
+    vhd_context_t vhd;
+    int refcnt;
+    uint64_t ino;
+     TAILQ_ENTRY(vhd_object) entry;
+};
+
+TAILQ_HEAD(tqh_vhd_object, vhd_object);
+
+struct vhd_partition {
+    struct vhd_object *vhd_obj;
+    int partition;
+    int flags;
+    off64_t start;              /* in sectors */
+    off64_t end;                /* in sectors */
+    off64_t size;               /* in sectors */
+};
+
+struct vhd_fd_context {
+    struct vhd_partition vhd_part;
+    off64_t off;
+    int users;
+};
+
+typedef struct vhd_object vhd_object_t;
+typedef struct vhd_partition vhd_partition_t;
+typedef struct vhd_fd_context vhd_fd_context_t;
+typedef int (*_std_open_t) (const char *, int, int);
+typedef int (*_std_close_t) (int);
+typedef FILE *(*_std_fopen_t) (const char *, const char *);
+
+static struct _function _function_table[] = {
+    {.name = "open",.fn = NULL},
+    {.name = "open64",.fn = NULL},
+#ifdef __open_2
+    {.name = "__open_2",.fn = NULL},
+#endif                          // __open_2
+#ifdef __open64_2
+    {.name = "__open64_2",.fn = NULL},
+#endif                          // __open64_2
+    {.name = "close",.fn = NULL},
+    {.name = "dup",.fn = NULL},
+    {.name = "dup2",.fn = NULL},
+#ifdef dup3
+    {.name = "dup3",.fn = NULL},
+#endif                          // dup3
+    {.name = "lseek",.fn = NULL},
+    {.name = "lseek64",.fn = NULL},
+    {.name = "read",.fn = NULL},
+    {.name = "write",.fn = NULL},
+    {.name = "pread",.fn = NULL},
+    {.name = "pread64",.fn = NULL},
+    {.name = "pwrite",.fn = NULL},
+    {.name = "pwrite64",.fn = NULL},
+    {.name = "fsync",.fn = NULL},
+    {.name = "__xstat",.fn = NULL},
+    {.name = "__xstat64",.fn = NULL},
+    {.name = "__fxstat",.fn = NULL},
+    {.name = "__fxstat64",.fn = NULL},
+    {.name = "__lxstat",.fn = NULL},
+    {.name = "__lxstat64",.fn = NULL},
+    {.name = "ioctl",.fn = NULL},
+    {.name = "fcntl",.fn = NULL},
+
+    {.name = "fopen",.fn = NULL},
+    {.name = "fopen64",.fn = NULL},
+    {.name = "_IO_getc",.fn = NULL},
+    {.name = "fread",.fn = NULL},
+
+    {.name = "posix_memalign",.fn = NULL},
+};
+
+static int _libvhd_io_interpose = 1;
+static struct tqh_vhd_object _vhd_objects;
+static vhd_fd_context_t **_vhd_map;
+static int _vhd_map_size;
+
+static int _libvhd_io_initialized;
+static void _libvhd_io_init(void) __attribute__ ((constructor));
+
+static volatile sig_atomic_t _libvhd_io_reset_vhds;
+
+static void *_load_std_fn(const char *name)
+{
+    void *fn;
+    char *msg;
+
+    LOG("loading %s\n", name);
+
+    fn = dlsym(RTLD_NEXT, name);
+    msg = dlerror();
+    if (!fn || msg) {
+        LOG("dlsym '%s' failed: %s\n", name, msg);
+        exit(1);
+    }
+
+    return fn;
+}
+
+static void *_get_std_fn(const char *name)
+{
+    int i;
+
+    for (i = 0; i < _ARRAY_SIZE(_function_table); i++)
+        if (!strcmp(name, _function_table[i].name))
+            return _function_table[i].fn;
+
+    return NULL;
+}
+
+static void _init_vhd_log(void)
+{
+    int (*_std_dup) (int) = _load_std_fn("dup");
+    int log_fd = _std_dup(STDERR_FILENO);
+
+    libvhdio_log = fdopen(log_fd, "a");
+
+    if (getenv(LIBVHD_IO_DEBUG)) {
+        libvhdio_logging = 1;
+        libvhd_set_log_level(1);
+    }
+
+    if (getenv(LIBVHD_IO_DUMP))
+        libvhdio_dump = 1;
+}
+
+static void _init_vhd_map(void)
+{
+    _vhd_map_size = sysconf(_SC_OPEN_MAX);
+    _vhd_map = calloc(_vhd_map_size, sizeof(vhd_fd_context_t *));
+    if (!_vhd_map) {
+        LOG("failed to init vhd map\n");
+        exit(1);
+    }
+}
+
+static void _init_vhd_objs(void)
+{
+    TAILQ_INIT(&_vhd_objects);
+}
+
+static void _libvhd_io_reset(void)
+{
+    int i, err;
+
+    if (!_libvhd_io_interpose)
+        return;
+
+    _libvhd_io_reset_vhds = 0;
+
+    if (!_vhd_map)
+        return;
+
+    _libvhd_io_interpose = 0;
+
+    for (i = 0; i < _vhd_map_size; i++) {
+        int flags;
+        vhd_context_t *vhd;
+        char *child, *parent;
+        vhd_fd_context_t *vhd_fd = _vhd_map[i];
+
+        if (!vhd_fd)
+            continue;
+
+        vhd = &vhd_fd->vhd_part.vhd_obj->vhd;
+
+        flags = vhd->oflags;
+        child = strdup(vhd->file);
+        if (!child)
+            exit(ENOMEM);
+
+        LOG("resetting vhd fd %d user fd %d\n", vhd->fd, i);
+        vhd_close(vhd);
+
+        if (asprintf(&parent, "%s.%d.vhd", child, (int) time(NULL)) == -1)
+            exit(ENOMEM);
+
+        if (rename(child, parent))
+            exit(errno);
+
+        err = vhd_snapshot(child, 0, parent, 0, 0);
+        if (err) {
+            LOG("snapshot of %s failed on reset: %d\n", child, err);
+            exit(1);
+        }
+
+        err = vhd_open(vhd, child, flags);
+        if (err) {
+            LOG("opening new snapshot %s failed on reset: %d\n",
+                child, err);
+            exit(1);
+        }
+
+        LOG("snapshot %s %s vhd fd %d user fd %d\n",
+            child, parent, vhd->fd, i);
+
+        free(child);
+        free(parent);
+    }
+
+    _libvhd_io_interpose = 1;
+}
+
+static void _libvhd_io_continue(int signo __attribute__((unused)))
+{
+    _libvhd_io_reset_vhds = 1;
+}
+
+static void _init_vhd_test(void)
+{
+    if (getenv(LIBVHD_IO_TEST)) {
+        sigset_t set;
+        struct sigaction act;
+
+        if (sigemptyset(&set))
+            exit(1);
+
+        act = (struct sigaction) {
+        .sa_handler = _libvhd_io_continue,.sa_mask = set,.sa_flags = 0,};
+
+        if (sigaction(SIGCONT, &act, NULL)) {
+            LOG("failed to set signal handler: %d\n", errno);
+            exit(1);
+        }
+
+        LOG("testing enabled\n");
+    }
+}
+
+static void _libvhd_io_init(void)
+{
+    int i;
+
+    if (_libvhd_io_initialized)
+        return;
+
+    _init_vhd_log();
+    _init_vhd_map();
+    _init_vhd_objs();
+    _init_vhd_test();
+
+    for (i = 0; i < _ARRAY_SIZE(_function_table); i++)
+        _function_table[i].fn = _load_std_fn(_function_table[i].name);
+
+    LOG("\n");
+    _libvhd_io_initialized = 1;
+}
+
+static vhd_object_t *_libvhd_io_get_vhd(const char *path, int flags)
+{
+    struct stat64 st;
+    int err, vhd_flags;
+    vhd_object_t *tmp, *obj = NULL;
+
+    _libvhd_io_interpose = 0;
+
+    if (stat64(path, &st))
+        goto out;
+
+    TAILQ_FOREACH(tmp, &_vhd_objects, entry)
+        if (tmp->ino == st.st_ino) {
+        obj = tmp;
+        if (flags & (O_RDWR | O_WRONLY) &&
+            obj->vhd.oflags & VHD_OPEN_RDONLY) {
+            errno = EACCES;
+            obj = NULL;
+        }
+        goto out;
+    }
+
+    vhd_flags = VHD_OPEN_CACHED;
+
+    /*
+     * we open RDWR whenever we can since vhd objects may be shared and
+     * we don't have a clean way to switch RDONLY vhds to RDWR.  we'll
+     * only open RDONLY when (flags & O_RDONLY) and we lack permission
+     * to open RDWR.
+     */
+    if (access(path, W_OK) == -1) {
+        if (errno != EACCES)
+            goto out;
+
+        if (flags & (O_WRONLY | O_RDWR))
+            goto out;
+
+        vhd_flags |= VHD_OPEN_RDONLY;
+    } else {
+        vhd_flags |= VHD_OPEN_RDWR;
+    }
+
+    obj = malloc(sizeof(*obj));
+    if (!obj) {
+        errno = ENOMEM;
+        goto out;
+    }
+
+    obj->refcnt = 0;
+    obj->ino = st.st_ino;
+
+    err = vhd_open(&obj->vhd, path, vhd_flags);
+    if (err) {
+        free(obj);
+        obj = NULL;
+        errno = err;
+        goto out;
+    }
+
+    TAILQ_INSERT_HEAD(&_vhd_objects, obj, entry);
+
+  out:
+    _libvhd_io_interpose = 1;
+    if (obj) {
+        obj->refcnt++;
+        LOG("%s: %s 0x%" PRIx64 " 0x%x\n",
+            __func__, path, obj->ino, obj->refcnt);
+    }
+    return obj;
+}
+
+static void _libvhd_io_put_vhd(vhd_object_t * obj)
+{
+    LOG("%s: 0x%" PRIx64 " 0x%x\n", __func__, obj->ino, obj->refcnt - 1);
+    if (--obj->refcnt == 0) {
+        vhd_close(&obj->vhd);
+        TAILQ_REMOVE(&_vhd_objects, obj, entry);
+        free(obj);
+    }
+}
+
+static inline vhd_fd_context_t *_libvhd_io_map_get(int idx)
+{
+    if (_libvhd_io_reset_vhds)
+        _libvhd_io_reset();
+    return _vhd_map[idx];
+}
+
+static inline void _libvhd_io_map_set(int idx, vhd_fd_context_t * vhd_fd)
+{
+    vhd_fd->users++;
+    _vhd_map[idx] = vhd_fd;
+    LOG("mapping 0x%x to %s (0x%x users)\n",
+        idx, vhd_fd->vhd_part.vhd_obj->vhd.file, vhd_fd->users);
+}
+
+static inline void _libvhd_io_map_clear(int idx)
+{
+    vhd_fd_context_t *vhd_fd;
+
+    if (idx < 0 || idx >= _vhd_map_size)
+        return;
+
+    vhd_fd = _vhd_map[idx];
+    _vhd_map[idx] = NULL;
+
+    if (vhd_fd) {
+        if (--vhd_fd->users == 0) {
+            _libvhd_io_put_vhd(vhd_fd->vhd_part.vhd_obj);
+            free(vhd_fd);
+        }
+    }
+}
+
+static int
+_libvhd_io_read_bytes(vhd_partition_t * vhd_part,
+                      void *buf, size_t size, uint64_t off)
+{
+    int ret;
+    vhd_context_t *vhd = &vhd_part->vhd_obj->vhd;
+
+    _libvhd_io_interpose = 0;
+    ret = vhd_io_read_bytes(vhd, buf, size, off);
+    _libvhd_io_interpose = 1;
+
+    if (ret) {
+        LOG("vhd_io_read_bytes %s %p 0x%zx 0x%" PRIx64 " failed: %d\n",
+            vhd->file, buf, size, off, ret);
+        errno = -ret;
+        ret = 1;
+    } else {
+        LOG("vhd_io_read_bytes %s %p 0x%zx 0x%" PRIx64 "\n",
+            vhd->file, buf, size, off);
+        DUMP(buf, size);
+    }
+
+    return ret;
+}
+
+static int
+_libvhd_io_write_bytes(vhd_partition_t * vhd_part,
+                       const void *buf, size_t size, uint64_t off)
+{
+    int ret;
+    vhd_context_t *vhd = &vhd_part->vhd_obj->vhd;
+
+    _libvhd_io_interpose = 0;
+    ret = vhd_io_write_bytes(vhd, (void *) buf, size, off);
+    _libvhd_io_interpose = 1;
+
+    if (ret) {
+        LOG("vhd_io_write_bytes %s %p 0x%zx 0x%" PRIx64 " failed: %d\n",
+            vhd->file, buf, size, off, ret);
+        errno = -ret;
+        ret = 1;
+    } else {
+        LOG("vhd_io_write_bytes %s %p 0x%zx 0x%" PRIx64 "\n",
+            vhd->file, buf, size, off);
+        DUMP(buf, size);
+    }
+
+    return ret;
+}
+
+/*
+ * symlink pathnames like *.vhd[1-4] are treated specially
+ */
+static int
+_libvhd_io_guess_partition(const char *path, int *partition, int *skip)
+{
+    char *sfx;
+    int err, len;
+    struct stat64 st;
+
+    *skip = 0;
+    *partition = 0;
+
+    _libvhd_io_interpose = 0;
+    err = lstat64(path, &st);
+    _libvhd_io_interpose = 1;
+
+    if (err == -1)
+        return errno;
+
+    if ((st.st_mode & __S_IFMT) != __S_IFLNK) {
+        if (st.st_size < VHD_SECTOR_SIZE)
+            *skip = 1;
+        return 0;
+    }
+
+    sfx = strstr(path, ".vhd");
+    if (!sfx)
+        return 0;
+
+    sfx += strlen(".vhd");
+    len = strlen(sfx);
+    if (!len)
+        return 0;
+    if (len > 1)
+        return EINVAL;
+
+    switch (*sfx) {
+    case '1' ... '4':
+        *partition = atoi(sfx);
+        break;
+    default:
+        return EINVAL;
+    }
+
+    return 0;
+}
+
+static int
+_libvhd_io_init_partition(vhd_partition_t * vhd_part, int partition)
+{
+    int err;
+    vhd_context_t *vhd;
+    void *_p;
+    struct partition_table *pt;
+    struct primary_partition *p;
+
+    if (partition < 0 || partition > 4)
+        return ENOENT;
+
+    vhd = &vhd_part->vhd_obj->vhd;
+
+    if (!partition) {
+        vhd_part->partition = 0;
+        vhd_part->start = 0;
+        vhd_part->end = (vhd->footer.curr_size >> VHD_SECTOR_SHIFT);
+        vhd_part->size = vhd_part->end;
+        return 0;
+    }
+
+    err = posix_memalign(&_p, VHD_SECTOR_SIZE, VHD_SECTOR_SIZE);
+    if (err)
+        return err;
+    pt = _p;
+
+    err = _libvhd_io_read_bytes(vhd_part, pt, 512, 0);
+    if (err) {
+        LOG("reading partition failed: %d\n", err);
+        goto out;
+    }
+
+    partition_table_in(pt);
+    err = partition_table_validate(pt);
+    if (err) {
+        LOG("bad partition table read\n");
+        goto out;
+    }
+
+    p = pt->partitions + (partition - 1);
+    if (!p->lba || !p->blocks) {
+        err = ENOENT;
+        goto out;
+    }
+
+    vhd_part->partition = partition;
+    vhd_part->start = p->lba;
+    vhd_part->end = p->lba + p->blocks;
+    vhd_part->size = p->blocks;
+    err = 0;
+
+    LOG("%s: opening %s partition 0x%x start 0x%08" PRIx64 " end 0x%08"
+        PRIx64 "\n", __func__, vhd->file, partition, vhd_part->start,
+        vhd_part->end);
+
+  out:
+    free(pt);
+    return err;
+}
+
+static int
+_libvhd_io_vhd_open(vhd_partition_t * vhd_part, const char *path,
+                    int flags)
+{
+    int err, skip, partition;
+
+    memset(vhd_part, 0, sizeof(*vhd_part));
+    vhd_part->flags = flags;
+
+    err = _libvhd_io_guess_partition(path, &partition, &skip);
+    if (err)
+        return err;
+
+    if (skip)
+        return EINVAL;
+
+    LOG("%s: attempting vhd_open of %s\n", __func__, path);
+
+    vhd_part->vhd_obj = _libvhd_io_get_vhd(path, flags);
+    if (!vhd_part->vhd_obj)
+        err = errno;
+
+    if (!err) {
+        err = _libvhd_io_init_partition(vhd_part, partition);
+        if (err) {
+            _libvhd_io_put_vhd(vhd_part->vhd_obj);
+            memset(vhd_part, 0, sizeof(*vhd_part));
+        }
+    }
+
+    return (err >= 0 ? err : -err);
+}
+
+static int
+_libvhd_io_open(const char *pathname,
+                int flags, mode_t mode, _std_open_t _std_open)
+{
+    int err, fd;
+    vhd_fd_context_t *vhd_fd;
+
+    errno = 0;
+    vhd_fd = NULL;
+
+    vhd_fd = calloc(1, sizeof(*vhd_fd));
+    if (!vhd_fd) {
+        err = ENOMEM;
+        goto fail;
+    }
+
+    err = _libvhd_io_vhd_open(&vhd_fd->vhd_part, pathname, flags);
+    if (err) {
+        if (err == EINVAL || err == ENOENT)
+            goto std_open;
+
+        LOG("%s: vhd_open of %s failed: %d\n", __func__, pathname, err);
+        goto fail;
+    }
+#ifdef O_CLOEXEC
+    if (flags & (O_APPEND | O_ASYNC | O_CLOEXEC |
+                 O_DIRECTORY | O_NONBLOCK)) {
+#else
+    if (flags & (O_APPEND | O_ASYNC | O_DIRECTORY | O_NONBLOCK)) {
+#endif                          //O_CLOEXEC
+        LOG("%s: invalid flags for vhd_open: 0x%x\n", __func__, flags);
+        err = EINVAL;
+        goto fail;
+    }
+
+    fd = _std_open("/dev/null", O_RDONLY, 0);
+    if (fd == -1) {
+        err = errno;
+        goto fail;
+    }
+
+    _libvhd_io_map_set(fd, vhd_fd);
+    return fd;
+
+  std_open:
+    free(vhd_fd);
+    return _std_open(pathname, flags, mode);
+
+  fail:
+    if (vhd_fd && vhd_fd->vhd_part.vhd_obj)
+        _libvhd_io_put_vhd(vhd_fd->vhd_part.vhd_obj);
+    free(vhd_fd);
+    errno = err;
+    return -1;
+}
+
+static int _libvhd_io_close(int fd, _std_close_t _std_close)
+{
+    _libvhd_io_map_clear(fd);
+    return _std_close(fd);
+}
+
+static FILE *_libvhd_io_fopen(const char *path, const char *mode)
+{
+    char *m;
+    FILE *f;
+    int fd, flags;
+    vhd_fd_context_t *vhd_fd;
+    static _std_open_t _std_open64;
+
+    __RESOLVE(_std_open64, "open64");
+
+    flags = 0;
+    if (strchr(mode, 'a')) {
+        if (strchr(mode, '+'))
+            flags |= O_APPEND | O_RDWR;
+        else
+            flags |= O_APPEND | O_WRONLY;
+    }
+    if (strchr(mode, 'r')) {
+        if (strchr(mode, '+'))
+            flags |= O_RDWR;
+        else
+            flags |= O_RDONLY;
+    }
+    if (strchr(mode, 'w')) {
+        errno = EINVAL;
+        return NULL;
+    }
+
+    fd = _libvhd_io_open(path, flags, 0, _std_open64);
+    if (fd == -1)
+        return NULL;
+
+    vhd_fd = _libvhd_io_map_get(fd);
+    if (vhd_fd)
+        m = "r";
+    else
+        m = (char *) mode;
+
+    f = fdopen(fd, m);
+    if (!f) {
+        int err = errno;
+        close(fd);
+        errno = err;
+    }
+
+    return f;
+}
+
+static ssize_t
+_libvhd_io_pread(vhd_partition_t * vhd_part,
+                 void *buf, size_t count, off64_t offset)
+{
+    ssize_t ret;
+    off64_t psize;
+
+    ret = (ssize_t) - 1;
+    psize = vhd_part->size << VHD_SECTOR_SHIFT;
+
+    if (vhd_part->flags & O_WRONLY) {
+        errno = EPERM;
+        goto out;
+    }
+
+    if (offset >= psize) {
+        ret = 0;
+        goto out;
+    }
+
+    count = MIN(count, psize - offset);
+    offset += (vhd_part->start << VHD_SECTOR_SHIFT);
+
+    if (_libvhd_io_read_bytes(vhd_part, buf, count, offset))
+        goto out;
+
+    ret = count;
+
+  out:
+    return ret;
+}
+
+static ssize_t
+_libvhd_io_pwrite(vhd_partition_t * vhd_part,
+                  const void *buf, size_t count, off64_t offset)
+{
+    ssize_t ret;
+    off64_t psize;
+
+    ret = (ssize_t) - 1;
+    psize = vhd_part->size << VHD_SECTOR_SHIFT;
+
+    if (vhd_part->flags & O_RDONLY) {
+        errno = EPERM;
+        goto out;
+    }
+
+    if (offset >= psize) {
+        ret = 0;
+        goto out;
+    }
+
+    count = MIN(count, psize - offset);
+    offset += (vhd_part->start << VHD_SECTOR_SHIFT);
+
+    if (_libvhd_io_write_bytes(vhd_part, buf, count, offset))
+        goto out;
+
+    ret = count;
+
+  out:
+    return ret;
+}
+
+static int
+_libvhd_io_fstat(int version, vhd_partition_t * vhd_part,
+                 struct stat *stats)
+{
+    int ret;
+    static int (*_std___fxstat) (int, int, struct stat *);
+
+    __RESOLVE(_std___fxstat, "__fxstat");
+    ret = _std___fxstat(version, vhd_part->vhd_obj->vhd.fd, stats);
+    if (ret)
+        return ret;
+
+    /*
+     * emulate block device
+     */
+    stats->st_size = 0;
+    stats->st_blocks = 0;
+    stats->st_blksize = getpagesize();
+    stats->st_mode &= ~__S_IFREG;
+    stats->st_mode |= __S_IFBLK;
+
+    return 0;
+}
+
+static int
+_libvhd_io_fstat64(int version,
+                   vhd_partition_t * vhd_part, struct stat64 *stats)
+{
+    int ret;
+    static int (*_std___fxstat64) (int, int, struct stat64 *);
+
+    __RESOLVE(_std___fxstat64, "__fxstat64");
+    ret = _std___fxstat64(version, vhd_part->vhd_obj->vhd.fd, stats);
+    if (ret)
+        return ret;
+
+    /*
+     * emulate block device
+     */
+    stats->st_size = 0;
+    stats->st_blocks = 0;
+    stats->st_blksize = getpagesize();
+    stats->st_mode &= ~__S_IFREG;
+    stats->st_mode |= __S_IFBLK;
+
+    return 0;
+}
+
+static int
+_libvhd_io_stat(int version, const char *path, struct stat *stats)
+{
+    int err;
+    vhd_partition_t vhd_part;
+
+    err = _libvhd_io_vhd_open(&vhd_part, path, O_RDONLY);
+    if (err) {
+        errno = (err > 0 ? err : -err);
+        return -1;
+    }
+
+    err = _libvhd_io_fstat(version, &vhd_part, stats);
+    _libvhd_io_put_vhd(vhd_part.vhd_obj);
+
+    return err;
+}
+
+static int
+_libvhd_io_stat64(int version, const char *path, struct stat64 *stats)
+{
+    int err;
+    vhd_partition_t vhd_part;
+
+    err = _libvhd_io_vhd_open(&vhd_part, path, O_RDONLY);
+    if (err) {
+        errno = (err > 0 ? err : -err);
+        return -1;
+    }
+
+    err = _libvhd_io_fstat64(version, &vhd_part, stats);
+    _libvhd_io_put_vhd(vhd_part.vhd_obj);
+
+    return err;
+}
+
+int open(const char *pathname, int flags, mode_t _mode)
+{
+    int fd;
+    mode_t mode;
+    static _std_open_t _std_open;
+
+    _RESOLVE(_std_open);
+    mode = (flags & O_CREAT ? _mode : 0);
+
+    if (!_libvhd_io_interpose)
+        return _std_open(pathname, flags, mode);
+
+    fd = _libvhd_io_open(pathname, flags, mode, _std_open);
+
+    LOG("%s %s 0x%x 0x%x: 0x%x\n", __func__, pathname, flags, mode, fd);
+
+    return fd;
+}
+
+int open64(const char *pathname, int flags, mode_t _mode)
+{
+    int fd;
+    mode_t mode;
+    static _std_open_t _std_open64;
+
+    _RESOLVE(_std_open64);
+    mode = (flags & O_CREAT ? _mode : 0);
+
+    if (!_libvhd_io_interpose)
+        return _std_open64(pathname, flags, mode);
+
+    fd = _libvhd_io_open(pathname, flags, mode, _std_open64);
+
+    LOG("%s %s 0x%x 0x%x: 0x%x\n", __func__, pathname, flags, mode, fd);
+
+    return fd;
+}
+
+int __open_2(const char *pathname, int flags, mode_t _mode)
+{
+    int fd;
+    mode_t mode;
+    static _std_open_t _std___open_2;
+
+    _RESOLVE(_std___open_2);
+    mode = (flags & O_CREAT ? _mode : 0);
+
+    if (!_libvhd_io_interpose)
+        return _std___open_2(pathname, flags, mode);
+
+    fd = _libvhd_io_open(pathname, flags, mode, _std___open_2);
+
+    LOG("%s %s 0x%x 0x%x: 0x%x\n", __func__, pathname, flags, mode, fd);
+
+    return fd;
+}
+
+int __open64_2(const char *pathname, int flags, mode_t _mode)
+{
+    int fd;
+    mode_t mode;
+    static _std_open_t _std___open64_2;
+
+    _RESOLVE(_std___open64_2);
+    mode = (flags & O_CREAT ? _mode : 0);
+
+    if (!_libvhd_io_interpose)
+        return _std___open64_2(pathname, flags, mode);
+
+    fd = _libvhd_io_open(pathname, flags, mode, _std___open64_2);
+
+    LOG("%s %s 0x%x 0x%x: 0x%x\n", __func__, pathname, flags, mode, fd);
+
+    return fd;
+}
+
+int close(int fd)
+{
+    static _std_close_t _std_close;
+
+    _RESOLVE(_std_close);
+
+    LOG("%s 0x%x\n", __func__, fd);
+
+    return _libvhd_io_close(fd, _std_close);
+}
+
+int dup(int oldfd)
+{
+    int newfd;
+    vhd_fd_context_t *vhd_fd;
+    static int (*_std_dup) (int);
+
+    _RESOLVE(_std_dup);
+    vhd_fd = _libvhd_io_map_get(oldfd);
+
+    LOG("%s 0x%x\n", __func__, oldfd);
+
+    newfd = _std_dup(oldfd);
+    if (newfd != -1 && vhd_fd)
+        _libvhd_io_map_set(newfd, vhd_fd);
+
+    return newfd;
+}
+
+int dup2(int oldfd, int newfd)
+{
+    int ret;
+    vhd_fd_context_t *vhd_fd;
+    static int (*_std_dup2) (int, int);
+
+    _RESOLVE(_std_dup2);
+    vhd_fd = _libvhd_io_map_get(oldfd);
+
+    LOG("%s 0x%x 0x%x\n", __func__, oldfd, newfd);
+
+    ret = _std_dup2(oldfd, newfd);
+    if (ret != -1 && vhd_fd)
+        _libvhd_io_map_set(ret, vhd_fd);
+
+    return ret;
+}
+
+int dup3(int oldfd, int newfd, int flags)
+{
+    int ret;
+    vhd_fd_context_t *vhd_fd;
+    static int (*_std_dup3) (int, int, int);
+
+    _RESOLVE(_std_dup3);
+    vhd_fd = _libvhd_io_map_get(oldfd);
+
+    LOG("%s 0x%x 0x%x 0x%x\n", __func__, oldfd, newfd, flags);
+
+    /*
+     * TODO: handle O_CLOEXEC...
+     */
+    ret = _std_dup3(oldfd, newfd, flags);
+    if (ret != -1 && vhd_fd)
+        _libvhd_io_map_set(ret, vhd_fd);
+
+    return ret;
+}
+
+off_t lseek(int fd, off_t offset, int whence)
+{
+    off_t new_off;
+    vhd_fd_context_t *vhd_fd;
+    static off_t(*_std_lseek) (int, off_t, int);
+
+    _RESOLVE(_std_lseek);
+    vhd_fd = _libvhd_io_map_get(fd);
+
+    LOG("%s 0x%x 0x%lx 0x%x\n", __func__, fd, offset, whence);
+
+    if (!vhd_fd)
+        return _std_lseek(fd, offset, whence);
+
+    switch (whence) {
+    case SEEK_SET:
+        new_off = offset;
+        break;
+    case SEEK_CUR:
+        new_off = vhd_fd->off + offset;
+        break;
+    case SEEK_END:
+        new_off = (vhd_fd->vhd_part.size << VHD_SECTOR_SHIFT) + offset;
+        break;
+    default:
+        errno = EINVAL;
+        return (off_t) - 1;
+    }
+
+    if (new_off < 0 || new_off > vhd_fd->vhd_part.size << VHD_SECTOR_SHIFT) {
+        errno = EINVAL;
+        return (off_t) - 1;
+    }
+
+    vhd_fd->off = new_off;
+    return vhd_fd->off;
+}
+
+off64_t lseek64(int fd, off64_t offset, int whence)
+{
+    off64_t new_off;
+    vhd_fd_context_t *vhd_fd;
+    static off64_t(*_std_lseek64) (int, off64_t, int);
+
+    _RESOLVE(_std_lseek64);
+    vhd_fd = _libvhd_io_map_get(fd);
+
+    LOG("%s 0x%x 0x%" PRIx64 " 0x%x\n", __func__, fd, offset, whence);
+
+    if (!vhd_fd)
+        return _std_lseek64(fd, offset, whence);
+
+    switch (whence) {
+    case SEEK_SET:
+        new_off = offset;
+        break;
+    case SEEK_CUR:
+        new_off = vhd_fd->off + offset;
+        break;
+    case SEEK_END:
+        new_off = (vhd_fd->vhd_part.size << VHD_SECTOR_SHIFT) + offset;
+        break;
+    default:
+        errno = EINVAL;
+        return (off64_t) - 1;
+    }
+
+    if (new_off < 0 || new_off > vhd_fd->vhd_part.size << VHD_SECTOR_SHIFT) {
+        errno = EINVAL;
+        return (off64_t) - 1;
+    }
+
+    vhd_fd->off = new_off;
+    return vhd_fd->off;
+}
+
+ssize_t read(int fd, void *buf, size_t count)
+{
+    ssize_t ret;
+    vhd_fd_context_t *vhd_fd;
+    static ssize_t(*_std_read) (int, void *, size_t);
+
+    _RESOLVE(_std_read);
+    vhd_fd = _libvhd_io_map_get(fd);
+
+    LOG("%s 0x%x %p 0x%zx\n", __func__, fd, buf, count);
+
+    if (!vhd_fd)
+        return _std_read(fd, buf, count);
+
+    ret = _libvhd_io_pread(&vhd_fd->vhd_part, buf, count, vhd_fd->off);
+    if (ret != -1)
+        vhd_fd->off += count;
+
+    return ret;
+}
+
+ssize_t write(int fd, const void *buf, size_t count)
+{
+    ssize_t ret;
+    vhd_fd_context_t *vhd_fd;
+    static ssize_t(*_std_write) (int, const void *, size_t);
+
+    _RESOLVE(_std_write);
+    vhd_fd = _libvhd_io_map_get(fd);
+
+    LOG("%s 0x%x %p 0x%zx\n", __func__, fd, buf, count);
+
+    if (!vhd_fd)
+        return _std_write(fd, buf, count);
+
+    ret = _libvhd_io_pwrite(&vhd_fd->vhd_part, buf, count, vhd_fd->off);
+    if (ret != -1)
+        vhd_fd->off += count;
+
+    return ret;
+}
+
+ssize_t pread(int fd, void *buf, size_t count, off_t offset)
+{
+    vhd_fd_context_t *vhd_fd;
+    static ssize_t(*_std_pread) (int, void *, size_t, off_t);
+
+    _RESOLVE(_std_pread);
+    vhd_fd = _libvhd_io_map_get(fd);
+
+    LOG("%s 0x%x %p 0x%zx 0x%lx\n", __func__, fd, buf, count, offset);
+
+    if (!vhd_fd)
+        return _std_pread(fd, buf, count, offset);
+
+    return _libvhd_io_pread(&vhd_fd->vhd_part, buf, count, offset);
+}
+
+ssize_t pread64(int fd, void *buf, size_t count, off64_t offset)
+{
+    vhd_fd_context_t *vhd_fd;
+    static ssize_t(*_std_pread64) (int, void *, size_t, off64_t);
+
+    _RESOLVE(_std_pread64);
+    vhd_fd = _libvhd_io_map_get(fd);
+
+    LOG("%s 0x%x %p 0x%zx 0x%" PRIx64 "\n", __func__, fd, buf, count,
+        offset);
+
+    if (!vhd_fd)
+        return _std_pread64(fd, buf, count, offset);
+
+    return _libvhd_io_pread(&vhd_fd->vhd_part, buf, count, offset);
+}
+
+ssize_t pwrite(int fd, const void *buf, size_t count, off_t offset)
+{
+    vhd_fd_context_t *vhd_fd;
+    static ssize_t(*_std_pwrite) (int, const void *, size_t, off_t);
+
+    _RESOLVE(_std_pwrite);
+    vhd_fd = _libvhd_io_map_get(fd);
+
+    LOG("%s 0x%x %p 0x%zx, 0x%lx\n", __func__, fd, buf, count, offset);
+
+    if (!vhd_fd)
+        return _std_pwrite(fd, buf, count, offset);
+
+    return _libvhd_io_pwrite(&vhd_fd->vhd_part, buf, count, offset);
+}
+
+ssize_t pwrite64(int fd, const void *buf, size_t count, off64_t offset)
+{
+    vhd_fd_context_t *vhd_fd;
+    static ssize_t(*_std_pwrite64) (int, const void *, size_t, off64_t);
+
+    _RESOLVE(_std_pwrite64);
+    vhd_fd = _libvhd_io_map_get(fd);
+
+    LOG("%s 0x%x %p 0x%zx, 0x%" PRIx64 "\n", __func__, fd, buf, count,
+        offset);
+
+    if (!vhd_fd)
+        return _std_pwrite64(fd, buf, count, offset);
+
+    return _libvhd_io_pwrite(&vhd_fd->vhd_part, buf, count, offset);
+}
+
+int fsync(int fd)
+{
+    vhd_fd_context_t *vhd_fd;
+    static int (*_std_fsync) (int);
+
+    _RESOLVE(_std_fsync);
+    vhd_fd = _libvhd_io_map_get(fd);
+    if (!vhd_fd)
+        return _std_fsync(fd);
+
+    LOG("%s 0x%x\n", __func__, fd);
+
+    return _std_fsync(vhd_fd->vhd_part.vhd_obj->vhd.fd);
+}
+
+int __xstat(int version, const char *path, struct stat *buf)
+{
+    int ret;
+    static int (*_std___xstat) (int, const char *, struct stat *);
+
+    _RESOLVE(_std___xstat);
+    if (!_libvhd_io_interpose)
+        return _std___xstat(version, path, buf);
+
+    LOG("%s 0x%x %s %p\n", __func__, version, path, buf);
+
+    ret = _libvhd_io_stat(version, path, buf);
+    if (ret)
+        ret = _std___xstat(version, path, buf);
+
+    return ret;
+}
+
+int __xstat64(int version, const char *path, struct stat64 *buf)
+{
+    int ret;
+    static int (*_std___xstat64) (int, const char *, struct stat64 *);
+
+    _RESOLVE(_std___xstat64);
+    if (!_libvhd_io_interpose)
+        return _std___xstat64(version, path, buf);
+
+    LOG("%s 0x%x %s %p\n", __func__, version, path, buf);
+
+    ret = _libvhd_io_stat64(version, path, buf);
+    if (ret)
+        ret = _std___xstat64(version, path, buf);
+
+
+    return ret;
+}
+
+int __fxstat(int version, int fd, struct stat *buf)
+{
+    vhd_fd_context_t *vhd_fd;
+    static int (*_std___fxstat) (int, int, struct stat *);
+
+    _RESOLVE(_std___fxstat);
+    vhd_fd = _libvhd_io_map_get(fd);
+
+    LOG("%s 0x%x 0x%x %p\n", __func__, version, fd, buf);
+
+    if (vhd_fd)
+        return _libvhd_io_fstat(version, &vhd_fd->vhd_part, buf);
+    else
+        return _std___fxstat(version, fd, buf);
+}
+
+int __fxstat64(int version, int fd, struct stat64 *buf)
+{
+    vhd_fd_context_t *vhd_fd;
+    static int (*_std___fxstat64) (int, int, struct stat64 *);
+
+    _RESOLVE(_std___fxstat64);
+    vhd_fd = _libvhd_io_map_get(fd);
+
+    LOG("%s 0x%x 0x%x %p\n", __func__, version, fd, buf);
+
+    if (vhd_fd)
+        return _libvhd_io_fstat64(version, &vhd_fd->vhd_part, buf);
+    else
+        return _std___fxstat64(version, fd, buf);
+}
+
+/*
+ * NB: symlinks to vhds will be stat'ed rather than lstat'ed.
+ */
+int __lxstat(int version, const char *path, struct stat *buf)
+{
+    int ret;
+    static int (*_std___lxstat) (int, const char *, struct stat *);
+
+    _RESOLVE(_std___lxstat);
+    if (!_libvhd_io_interpose)
+        return _std___lxstat(version, path, buf);
+
+    LOG("%s 0x%x %s %p\n", __func__, version, path, buf);
+
+    ret = _libvhd_io_stat(version, path, buf);
+    if (ret)
+        ret = _std___lxstat(version, path, buf);
+
+    return ret;
+}
+
+/*
+ * NB: symlinks to vhds will be stat'ed rather than lstat'ed.
+ */
+int __lxstat64(int version, const char *path, struct stat64 *buf)
+{
+    int ret;
+    static int (*_std___lxstat64) (int, const char *, struct stat64 *);
+
+    _RESOLVE(_std___lxstat64);
+    if (!_libvhd_io_interpose)
+        return _std___lxstat64(version, path, buf);
+
+    LOG("%s 0x%x %s %p\n", __func__, version, path, buf);
+
+    ret = _libvhd_io_stat64(version, path, buf);
+    if (ret)
+        ret = _std___lxstat64(version, path, buf);
+
+    return ret;
+}
+
+int ioctl(int fd, int request, char *argp)
+{
+    vhd_fd_context_t *vhd_fd;
+    static int (*_std_ioctl) (int, int, char *);
+
+    _RESOLVE(_std_ioctl);
+    vhd_fd = _libvhd_io_map_get(fd);
+    if (!vhd_fd)
+        return _std_ioctl(fd, request, argp);
+
+    LOG("%s 0x%x 0x%x %p\n", __func__, fd, request, argp);
+
+#ifdef BLKGETSIZE64
+    if (request == BLKGETSIZE64) {
+        uint64_t *size = (uint64_t *) argp;
+        *size = vhd_fd->vhd_part.size << VHD_SECTOR_SHIFT;
+        return 0;
+    }
+#endif
+#ifdef BLKGETSIZE
+    if (request == BLKGETSIZE) {
+        unsigned long *size = (unsigned long *) argp;
+        *size = vhd_fd->vhd_part.size << VHD_SECTOR_SHIFT;
+        return 0;
+    }
+#endif
+#ifdef BLKSSZGET
+    if (request == BLKSSZGET) {
+        int *sec_size = (int *) argp;
+        *sec_size = VHD_SECTOR_SIZE;
+        return 0;
+    }
+#endif
+#ifdef HDIO_GETGEO
+    if (request == HDIO_GETGEO) {
+        vhd_context_t *vhd = &vhd_fd->vhd_part.vhd_obj->vhd;
+        struct hd_geometry *geo = (struct hd_geometry *) argp;
+        geo->heads = GEOM_GET_HEADS(vhd->footer.geometry);
+        geo->sectors = GEOM_GET_SPT(vhd->footer.geometry);
+        geo->cylinders = GEOM_GET_CYLS(vhd->footer.geometry);
+        geo->start = vhd_fd->vhd_part.start;
+        return 0;
+    }
+#endif
+
+    return _std_ioctl(fd, request, argp);
+}
+
+int fcntl(int fd, int cmd, ...)
+{
+    int real_fd;
+    va_list args;
+    vhd_fd_context_t *vhd_fd;
+    static int (*_std_fcntl) (int, int, ...);
+
+    _RESOLVE(_std_fcntl);
+
+    real_fd = fd;
+    vhd_fd = _libvhd_io_map_get(fd);
+    if (vhd_fd)
+        real_fd = vhd_fd->vhd_part.vhd_obj->vhd.fd;
+
+    LOG("%s 0x%x 0x%x\n", __func__, fd, cmd);
+
+    switch (cmd) {
+    case F_GETFD:
+    case F_GETFL:
+    case F_GETOWN:
+    case F_GETSIG:
+    case F_GETLEASE:
+        LOG("%s 0x%x void\n", __func__, real_fd);
+        return _std_fcntl(real_fd, cmd);
+
+    case F_DUPFD:
+#ifdef F_DUPFD_CLOEXEC
+    case F_DUPFD_CLOEXEC:
+#endif                          // F_DUPFD_CLOEXEC
+    case F_SETFD:
+    case F_SETFL:
+    case F_SETOWN:
+    case F_SETSIG:
+    case F_SETLEASE:
+    case F_NOTIFY:
+        {
+            long arg;
+            va_start(args, cmd);
+            arg = va_arg(args, long);
+            va_end(args);
+            LOG("%s 0x%x long 0x%lx\n", __func__, real_fd, arg);
+            return _std_fcntl(real_fd, cmd, arg);
+        }
+
+    case F_SETLK:
+    case F_SETLKW:
+    case F_GETLK:
+        {
+            struct flock *flk;
+            va_start(args, cmd);
+            flk = va_arg(args, struct flock *);
+            va_end(args);
+            LOG("%s 0x%x lock %p\n", __func__, real_fd, flk);
+            return _std_fcntl(real_fd, cmd, flk);
+        }
+
+#if __WORDSIZE == 32
+    case F_SETLK64:
+    case F_SETLKW64:
+    case F_GETLK64:
+        {
+            struct flock64 *flk;
+            va_start(args, cmd);
+            flk = va_arg(args, struct flock64 *);
+            va_end(args);
+            LOG("%s 0x%x lock64 %p (%p)\n",
+                __func__, real_fd, flk, _std_fcntl);
+            return _std_fcntl(real_fd, cmd, flk);
+        }
+#endif
+
+    default:
+        LOG("%s unrecognized cmd\n", __func__);
+        errno = EINVAL;
+        return -1;
+    }
+}
+
+FILE *fopen(const char *path, const char *mode)
+{
+    FILE *f;
+    static _std_fopen_t _std_fopen;
+
+    _RESOLVE(_std_fopen);
+
+    if (!_libvhd_io_interpose || strchr(mode, 'w'))
+        return _std_fopen(path, mode);
+
+    f = _libvhd_io_fopen(path, mode);
+
+    LOG("%s %s %s: 0x%x\n", __func__, path, mode, (f ? fileno(f) : -1));
+
+    return f;
+}
+
+FILE *fopen64(const char *path, const char *mode)
+{
+    FILE *f;
+    static _std_fopen_t _std_fopen64;
+
+    _RESOLVE(_std_fopen64);
+
+    if (!_libvhd_io_interpose || strchr(mode, 'w'))
+        return _std_fopen64(path, mode);
+
+    f = _libvhd_io_fopen(path, mode);
+
+    LOG("%s %s %s: 0x%x\n", __func__, path, mode, (f ? fileno(f) : -1));
+
+    return f;
+}
+
+int _IO_getc(FILE * f)
+{
+    int cnt;
+    unsigned char c;
+    vhd_fd_context_t *vhd_fd;
+    static int (*_std__IO_getc) (FILE *);
+
+    _RESOLVE(_std__IO_getc);
+    vhd_fd = _libvhd_io_map_get(fileno(f));
+    if (!vhd_fd)
+        return _std__IO_getc(f);
+
+    LOG("%s %p (0x%x)\n", __func__, f, fileno(f));
+    cnt = _libvhd_io_pread(&vhd_fd->vhd_part, &c, sizeof(c), vhd_fd->off);
+    if (cnt > 0)
+        vhd_fd->off += cnt;
+
+    return (int) c;
+}
+
+#ifdef _IO_getc_unlocked
+#undef _IO_getc_unlocked
+#endif
+int _IO_getc_unlocked(FILE * f)
+{
+    return _IO_getc(f);
+}
+
+size_t fread(void *buf, size_t size, size_t n, FILE * f)
+{
+    ssize_t cnt;
+    vhd_fd_context_t *vhd_fd;
+    static size_t(*_std_fread) (void *, size_t, size_t, FILE *);
+
+    _RESOLVE(_std_fread);
+    vhd_fd = _libvhd_io_map_get(fileno(f));
+    if (!vhd_fd)
+        return _std_fread(buf, size, n, f);
+
+    LOG("%s %p 0x%zx 0x%zx %p (0x%x)\n",
+        __func__, buf, size, n, f, fileno(f));
+    cnt = _libvhd_io_pread(&vhd_fd->vhd_part, buf, n * size, vhd_fd->off);
+    if (cnt > 0) {
+        vhd_fd->off += cnt;
+        cnt /= size;
+    }
+
+    return cnt;
+}
+
+#ifdef fread_unlocked
+#undef fread_unlocked
+#endif
+size_t fread_unlocked(void *buf, size_t size, size_t n, FILE * f)
+{
+    return fread(buf, size, n, f);
+}
+
+/*
+ * sigh... preloading with bash causes problems, since bash has its own
+ * malloc(), memalign(), and free() functions, but no posix_memalign().
+ * this causes problems when libvhd free()'s posix_memalign()'ed memory.
+ */
+#define _libvhd_power_of_2(x) ((((x) - 1) & (x)) == 0)
+int posix_memalign(void **memptr, size_t alignment, size_t size)
+{
+    if (!alignment || alignment % sizeof(void *) ||
+        !_libvhd_power_of_2(alignment / sizeof(void *)))
+        return EINVAL;
+
+    *memptr = memalign(alignment, size);
+    if (!*memptr)
+        return ENOMEM;
+
+    return 0;
+}

_______________________________________________
Xen-devel mailing list
Xen-devel@xxxxxxxxxxxxx
http://lists.xen.org/xen-devel


 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.