diff options
Diffstat (limited to 'tools/blktap2/drivers/tapdisk2.c')
-rw-r--r-- | tools/blktap2/drivers/tapdisk2.c | 436 |
1 files changed, 436 insertions, 0 deletions
diff --git a/tools/blktap2/drivers/tapdisk2.c b/tools/blktap2/drivers/tapdisk2.c new file mode 100644 index 0000000000..45b27ecc19 --- /dev/null +++ b/tools/blktap2/drivers/tapdisk2.c @@ -0,0 +1,436 @@ +/* + * 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 <unistd.h> +#include <stdlib.h> +#include <string.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <sys/ioctl.h> + +#include "tapdisk.h" +#include "blktap2.h" +#include "tapdisk-vbd.h" +#include "tapdisk-utils.h" +#include "tapdisk-server.h" + +#define TAPDISK2_VBD 0 + +#define cprintf(_err, _f, _a...) \ + do { \ + if (child_out) { \ + fprintf(child_out, "%d: " _f, _err, ##_a); \ + fflush(child_out); \ + } \ + } while (0) + +#define CHILD_ERR(_err, _f, _a...) \ + do { \ + EPRINTF(_f, ##_a); \ + cprintf(_err, _f, ##_a); \ + } while (0) + +static int channel[2]; +static FILE *child_out; +static struct blktap2_handle handle; + +static int +tapdisk2_prepare_directory(void) +{ + int err; + char *ptr, *name, *start; + + err = access(BLKTAP2_DIRECTORY, W_OK | R_OK); + if (!err) + return 0; + + name = strdup(BLKTAP2_DIRECTORY); + if (!name) + return -ENOMEM; + + start = name; + + for (;;) { + ptr = strchr(start + 1, '/'); + if (ptr) + *ptr = '\0'; + + err = mkdir(name, 0755); + if (err && errno != EEXIST) { + err = -errno; + CHILD_ERR(err, "failed to create directory %s: %d\n", + name, err); + break; + } + + if (!ptr) + break; + else { + *ptr = '/'; + start = ptr + 1; + } + } + + free(name); + return err; +} + +static int +tapdisk2_make_device(char *devname, int major, int minor, int perm) +{ + int err; + struct stat st; + + err = tapdisk2_prepare_directory(); + if (err) + return err; + + if (!access(devname, F_OK)) + if (unlink(devname)) { + CHILD_ERR(errno, "error unlinking %s: %d\n", + devname, errno); + return -errno; + } + + err = mknod(devname, perm, makedev(major, minor)); + if (err) { + CHILD_ERR(errno, "mknod %s failed: %d\n", devname, -errno); + return -errno; + } + + DPRINTF("Created %s device\n", devname); + return 0; +} + +static int +tapdisk2_check_environment(void) +{ + FILE *f; + int err, minor; + char name[256]; + + if (!access(BLKTAP2_CONTROL_DEVICE, R_OK | W_OK)) + return 0; + + memset(name, 0, sizeof(name)); + + f = fopen("/proc/misc", "r"); + if (!f) { + CHILD_ERR(errno, "failed to open /proc/misc: %d\n", errno); + return -errno; + } + + while (fscanf(f, "%d %256s", &minor, name) == 2) + if (!strcmp(name, BLKTAP2_CONTROL_NAME)) { + err = tapdisk2_make_device(BLKTAP2_CONTROL_DEVICE, + MISC_MAJOR_NUMBER, + minor, S_IFCHR | 0600); + goto out; + } + + err = -ENOSYS; + CHILD_ERR(err, "didn't find %s in /proc/misc\n", BLKTAP2_CONTROL_NAME); + +out: + fclose(f); + return err; +} + +static void +tapdisk2_free_device(void) +{ + int fd, err; + + fd = open(BLKTAP2_CONTROL_DEVICE, O_RDONLY); + if (fd == -1) { + CHILD_ERR(errno, "failed to open control device: %d\n", errno); + return; + } + + err = ioctl(fd, BLKTAP2_IOCTL_FREE_TAP, handle.minor); + close(fd); +} + +static int +tapdisk2_prepare_device(void) +{ + char *name; + int fd, err; + + fd = open(BLKTAP2_CONTROL_DEVICE, O_RDONLY); + if (fd == -1) { + CHILD_ERR(errno, "failed to open control device: %d\n", errno); + return -errno; + } + + err = ioctl(fd, BLKTAP2_IOCTL_ALLOC_TAP, &handle); + close(fd); + if (err == -1) { + CHILD_ERR(errno, "failed to allocate new device: %d\n", errno); + return -errno; + } + + err = asprintf(&name, "%s%d", BLKTAP2_RING_DEVICE, handle.minor); + if (err == -1) { + err = -ENOMEM; + goto fail; + } + + err = tapdisk2_make_device(name, handle.ring, + handle.minor, S_IFCHR | 0600); + free(name); + if (err) { + CHILD_ERR(err, "creating ring device for %d failed: %d\n", + handle.minor, err); + goto fail; + } + + err = asprintf(&name, "%s%d", BLKTAP2_IO_DEVICE, handle.minor); + if (err == -1) { + err = -ENOMEM; + goto fail; + } + + err = tapdisk2_make_device(name, handle.device, + handle.minor, S_IFBLK | 0600); + free(name); + if (err) { + CHILD_ERR(err, "creating IO device for %d failed: %d\n", + handle.minor, err); + goto fail; + } + + DPRINTF("new interface: ring: %u, device: %u, minor: %u\n", + handle.ring, handle.device, handle.minor); + + return 0; + +fail: + tapdisk2_free_device(); + return err; +} + +static int +tapdisk2_open_device(int type, const char *path, const char *name) +{ + int err; + td_vbd_t *vbd; + image_t image; + char *devname; + struct blktap2_params params; + + err = tapdisk_vbd_initialize(-1, -1, TAPDISK2_VBD); + if (err) + return err; + + vbd = tapdisk_server_get_vbd(TAPDISK2_VBD); + if (!vbd) { + err = -ENODEV; + CHILD_ERR(err, "couldn't find vbd\n"); + return err; + } + + err = asprintf(&devname, "%s%d", BLKTAP2_RING_DEVICE, handle.minor); + if (err == -1) { + err = -ENOMEM; + CHILD_ERR(err, "couldn't allocate ring\n"); + return err; + } + + err = tapdisk_vbd_open(vbd, path, type, + TAPDISK_STORAGE_TYPE_DEFAULT, + devname, 0); + free(devname); + if (err) { + CHILD_ERR(err, "vbd open failed: %d\n", err); + return err; + } + + memset(¶ms, 0, sizeof(params)); + tapdisk_vbd_get_image_info(vbd, &image); + + params.capacity = image.size; + params.sector_size = image.secsize; + snprintf(params.name, sizeof(params.name) - 1, "%s", name); + + err = ioctl(vbd->ring.fd, BLKTAP2_IOCTL_CREATE_DEVICE, ¶ms); + if (err) { + err = -errno; + CHILD_ERR(err, "create device failed: %d\n", err); + return err; + } + + return 0; +} + +static int +tapdisk2_set_child_fds(void) +{ + int i, err; + + err = dup2(channel[1], STDOUT_FILENO); + if (err == -1) { + CHILD_ERR(errno, "failed duping pipe: %d\n", errno); + return errno; + } + + child_out = fdopen(STDOUT_FILENO, "w"); + if (!child_out) { + CHILD_ERR(errno, "failed setting child_out: %d\n", errno); + return errno; + } + + for (i = 0; i < sysconf(_SC_OPEN_MAX); i++) + if (i != STDOUT_FILENO) + close(i); + + return 0; +} + +static int +tapdisk2_create_device(const char *params) +{ + char *path; + int err, type; + + chdir("/"); + tapdisk_start_logging("tapdisk2"); + + err = tapdisk2_set_child_fds(); + if (err) + goto out; + + err = tapdisk2_check_environment(); + if (err) + goto out; + + err = tapdisk_parse_disk_type(params, &path, &type); + if (err) + goto out; + + err = tapdisk2_prepare_device(); + if (err) + goto out; + + err = tapdisk_server_initialize(NULL, NULL); + if (err) + goto fail; + + err = tapdisk2_open_device(type, path, params); + if (err) + goto fail; + + cprintf(0, "%s%d\n", BLKTAP2_IO_DEVICE, handle.minor); + close(STDOUT_FILENO); + + err = tapdisk_server_run(); + if (err) + goto fail; + + err = 0; + +out: + tapdisk_stop_logging(); + return err; + +fail: + tapdisk2_free_device(); + goto out; +} + +static int +tapdisk2_wait_for_device(void) +{ + int err; + char msg[1024]; + FILE *parent_in; + + close(channel[1]); + parent_in = fdopen(channel[0], "r"); + if (!parent_in) { + printf("failed to connect to child: %d\n", errno); + return errno; + } + + memset(msg, 0, sizeof(msg)); + if (fscanf(parent_in, "%d: %1023[^\n]", &err, msg) != 2) { + printf("unrecognized child response\n"); + return EINVAL; + } + + printf("%s\n", msg); + return (err >= 0 ? err : -err); +} + +static void +usage(const char *app, int err) +{ + fprintf(stderr, "usage: %s <-n file>\n", app); + exit(err); +} + +int +main(int argc, char *argv[]) +{ + int c; + char *params; + + params = NULL; + + while ((c = getopt(argc, argv, "n:h")) != -1) { + switch (c) { + case 'n': + params = optarg; + break; + case 'h': + usage(argv[0], 0); + default: + usage(argv[0], EINVAL); + } + } + + if (!params || optind != argc) + usage(argv[0], EINVAL); + + if (pipe(channel) == -1) { + printf("pipe failed: %d\n", errno); + return errno; + } + + switch (fork()) { + case -1: + printf("fork failed: %d\n", errno); + return errno; + case 0: + return tapdisk2_create_device(params); + default: + return tapdisk2_wait_for_device(); + } +} |