aboutsummaryrefslogtreecommitdiffstats
path: root/tools/blktap2/drivers/tapdisk2.c
diff options
context:
space:
mode:
Diffstat (limited to 'tools/blktap2/drivers/tapdisk2.c')
-rw-r--r--tools/blktap2/drivers/tapdisk2.c436
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(&params, 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, &params);
+ 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();
+ }
+}