[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
|
Lists.xenproject.org is hosted with RackSpace, monitoring our |