Index: head/stand/common/help.common =================================================================== --- head/stand/common/help.common +++ head/stand/common/help.common @@ -99,6 +99,13 @@ List loaded modules. If [-v] is specified, print more details. ################################################################################ +# Tmap-vdisk DMap virtual disk + + map-vdisk filename + + Map file as virtual disk. + +################################################################################ # Tmore DPage files more [ ...] @@ -395,6 +402,13 @@ unload This command removes any kernel and all loaded modules from memory. + +################################################################################ +# Tunmap-vdisk DUnmap virtual disk + + unmap-vdisk diskname + + Delete virtual disk mapping. ################################################################################ # Tunset DUnset a variable Index: head/stand/common/vdisk.c =================================================================== --- head/stand/common/vdisk.c +++ head/stand/common/vdisk.c @@ -0,0 +1,417 @@ +/*- + * Copyright 2019 Toomas Soome + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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 +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static int vdisk_init(void); +static int vdisk_strategy(void *, int, daddr_t, size_t, char *, size_t *); +static int vdisk_open(struct open_file *, ...); +static int vdisk_close(struct open_file *); +static int vdisk_ioctl(struct open_file *, u_long, void *); +static int vdisk_print(int); + +struct devsw vdisk_dev = { + .dv_name = "vdisk", + .dv_type = DEVT_DISK, + .dv_init = vdisk_init, + .dv_strategy = vdisk_strategy, + .dv_open = vdisk_open, + .dv_close = vdisk_close, + .dv_ioctl = vdisk_ioctl, + .dv_print = vdisk_print, + .dv_cleanup = NULL +}; + +typedef STAILQ_HEAD(vdisk_info_list, vdisk_info) vdisk_info_list_t; + +typedef struct vdisk_info +{ + STAILQ_ENTRY(vdisk_info) vdisk_link; /* link in device list */ + char *vdisk_path; + int vdisk_unit; + int vdisk_fd; + uint64_t vdisk_size; /* size in bytes */ + uint32_t vdisk_sectorsz; + uint32_t vdisk_open; /* reference counter */ +} vdisk_info_t; + +static vdisk_info_list_t vdisk_list; /* list of mapped vdisks. */ + +static vdisk_info_t * +vdisk_get_info(struct devdesc *dev) +{ + vdisk_info_t *vd; + + STAILQ_FOREACH(vd, &vdisk_list, vdisk_link) { + if (vd->vdisk_unit == dev->d_unit) + return (vd); + } + return (vd); +} + +COMMAND_SET(map_vdisk, "map-vdisk", "map file as virtual disk", command_mapvd); + +static int +command_mapvd(int argc, char *argv[]) +{ + vdisk_info_t *vd, *p; + struct stat sb; + + if (argc != 2) { + printf("usage: %s filename\n", argv[0]); + return (CMD_ERROR); + } + + STAILQ_FOREACH(vd, &vdisk_list, vdisk_link) { + if (strcmp(vd->vdisk_path, argv[1]) == 0) { + printf("%s: file %s is already mapped as %s%d\n", + argv[0], argv[1], vdisk_dev.dv_name, + vd->vdisk_unit); + return (CMD_ERROR); + } + } + + if (stat(argv[1], &sb) < 0) { + /* + * ENOSYS is really ENOENT because we did try to walk + * through devsw list to try to open this file. + */ + if (errno == ENOSYS) + errno = ENOENT; + + printf("%s: stat failed: %s\n", argv[0], strerror(errno)); + return (CMD_ERROR); + } + + /* + * Avoid mapping small files. + */ + if (sb.st_size < 1024 * 1024) { + printf("%s: file %s is too small.\n", argv[0], argv[1]); + return (CMD_ERROR); + } + + vd = calloc(1, sizeof (*vd)); + if (vd == NULL) { + printf("%s: out of memory\n", argv[0]); + return (CMD_ERROR); + } + vd->vdisk_path = strdup(argv[1]); + if (vd->vdisk_path == NULL) { + free (vd); + printf("%s: out of memory\n", argv[0]); + return (CMD_ERROR); + } + vd->vdisk_fd = open(vd->vdisk_path, O_RDONLY); + if (vd->vdisk_fd < 0) { + printf("%s: open failed: %s\n", argv[0], strerror(errno)); + free(vd->vdisk_path); + free(vd); + return (CMD_ERROR); + } + + vd->vdisk_size = sb.st_size; + vd->vdisk_sectorsz = DEV_BSIZE; + STAILQ_FOREACH(p, &vdisk_list, vdisk_link) { + vdisk_info_t *n; + if (p->vdisk_unit == vd->vdisk_unit) { + vd->vdisk_unit++; + continue; + } + n = STAILQ_NEXT(p, vdisk_link); + if (p->vdisk_unit < vd->vdisk_unit) { + if (n == NULL) { + /* p is last elem */ + STAILQ_INSERT_TAIL(&vdisk_list, vd, vdisk_link); + break; + } + if (n->vdisk_unit > vd->vdisk_unit) { + /* p < vd < n */ + STAILQ_INSERT_AFTER(&vdisk_list, p, vd, + vdisk_link); + break; + } + /* else n < vd or n == vd */ + vd->vdisk_unit++; + continue; + } + /* p > vd only if p is the first element */ + STAILQ_INSERT_HEAD(&vdisk_list, vd, vdisk_link); + break; + } + + /* if the list was empty or contiguous */ + if (p == NULL) + STAILQ_INSERT_TAIL(&vdisk_list, vd, vdisk_link); + + printf("%s: file %s is mapped as %s%d\n", argv[0], vd->vdisk_path, + vdisk_dev.dv_name, vd->vdisk_unit); + return (CMD_OK); +} + +COMMAND_SET(unmap_vdisk, "unmap-vdisk", "unmap virtual disk", command_unmapvd); + +/* + * unmap-vdisk vdiskX + */ +static int +command_unmapvd(int argc, char *argv[]) +{ + size_t len; + vdisk_info_t *vd; + long unit; + char *end; + + if (argc != 2) { + printf("usage: %s %sN\n", argv[0], vdisk_dev.dv_name); + return (CMD_ERROR); + } + + len = strlen(vdisk_dev.dv_name); + if (strncmp(vdisk_dev.dv_name, argv[1], len) != 0) { + printf("%s: unknown device %s\n", argv[0], argv[1]); + return (CMD_ERROR); + } + errno = 0; + unit = strtol(argv[1] + len, &end, 10); + if (errno != 0 || (*end != '\0' && strcmp(end, ":") != 0)) { + printf("%s: unknown device %s\n", argv[0], argv[1]); + return (CMD_ERROR); + } + + STAILQ_FOREACH(vd, &vdisk_list, vdisk_link) { + if (vd->vdisk_unit == unit) + break; + } + + if (vd == NULL) { + printf("%s: unknown device %s\n", argv[0], argv[1]); + return (CMD_ERROR); + } + + if (vd->vdisk_open != 0) { + printf("%s: %s is in use, unable to unmap.\n", + argv[0], argv[1]); + return (CMD_ERROR); + } + + STAILQ_REMOVE(&vdisk_list, vd, vdisk_info, vdisk_link); + close(vd->vdisk_fd); + free(vd->vdisk_path); + free(vd); + printf("%s (%s) unmapped\n", argv[1], vd->vdisk_path); + + return (CMD_OK); +} + +static int +vdisk_init(void) +{ + STAILQ_INIT(&vdisk_list); + return (0); +} + +static int +vdisk_strategy(void *devdata, int rw, daddr_t blk, size_t size, + char *buf, size_t *rsize) +{ + struct disk_devdesc *dev; + vdisk_info_t *vd; + ssize_t rv; + + dev = devdata; + if (dev == NULL) + return (EINVAL); + vd = vdisk_get_info((struct devdesc *)dev); + if (vd == NULL) + return (EINVAL); + + if (size == 0 || (size % 512) != 0) + return (EIO); + + if (dev->dd.d_dev->dv_type == DEVT_DISK) { + daddr_t offset; + + offset = dev->d_offset * vd->vdisk_sectorsz; + offset /= 512; + blk += offset; + } + if (lseek(vd->vdisk_fd, blk << 9, SEEK_SET) == -1) + return (EIO); + + errno = 0; + switch (rw & F_MASK) { + case F_READ: + rv = read(vd->vdisk_fd, buf, size); + break; + case F_WRITE: + rv = write(vd->vdisk_fd, buf, size); + break; + default: + return (ENOSYS); + } + + if (errno == 0 && rsize != NULL) { + *rsize = rv; + } + return (errno); +} + +static int +vdisk_open(struct open_file *f, ...) +{ + va_list args; + struct disk_devdesc *dev; + vdisk_info_t *vd; + int rc = 0; + + va_start(args, f); + dev = va_arg(args, struct disk_devdesc *); + va_end(args); + if (dev == NULL) + return (EINVAL); + vd = vdisk_get_info((struct devdesc *)dev); + if (vd == NULL) + return (EINVAL); + + if (dev->dd.d_dev->dv_type == DEVT_DISK) { + rc = disk_open(dev, vd->vdisk_size, vd->vdisk_sectorsz); + } + if (rc == 0) + vd->vdisk_open++; + return (rc); +} + +static int +vdisk_close(struct open_file *f) +{ + struct disk_devdesc *dev; + vdisk_info_t *vd; + + dev = (struct disk_devdesc *)(f->f_devdata); + if (dev == NULL) + return (EINVAL); + vd = vdisk_get_info((struct devdesc *)dev); + if (vd == NULL) + return (EINVAL); + + vd->vdisk_open--; + if (dev->dd.d_dev->dv_type == DEVT_DISK) + return (disk_close(dev)); + return (0); +} + +static int +vdisk_ioctl(struct open_file *f, u_long cmd, void *data) +{ + struct disk_devdesc *dev; + vdisk_info_t *vd; + int rc; + + dev = (struct disk_devdesc *)(f->f_devdata); + if (dev == NULL) + return (EINVAL); + vd = vdisk_get_info((struct devdesc *)dev); + if (vd == NULL) + return (EINVAL); + + if (dev->dd.d_dev->dv_type == DEVT_DISK) { + rc = disk_ioctl(dev, cmd, data); + if (rc != ENOTTY) + return (rc); + } + + switch (cmd) { + case DIOCGSECTORSIZE: + *(u_int *)data = vd->vdisk_sectorsz; + break; + case DIOCGMEDIASIZE: + *(uint64_t *)data = vd->vdisk_size; + break; + default: + return (ENOTTY); + } + return (0); +} + +static int +vdisk_print(int verbose) +{ + int ret = 0; + vdisk_info_t *vd; + char line[80]; + + if (STAILQ_EMPTY(&vdisk_list)) + return (ret); + + printf("%s devices:", vdisk_dev.dv_name); + if ((ret = pager_output("\n")) != 0) + return (ret); + + STAILQ_FOREACH(vd, &vdisk_list, vdisk_link) { + struct disk_devdesc vd_dev; + + if (verbose) { + printf(" %s", vd->vdisk_path); + if ((ret = pager_output("\n")) != 0) + break; + } + snprintf(line, sizeof(line), + " %s%d", vdisk_dev.dv_name, vd->vdisk_unit); + printf("%s: %" PRIu64 " X %u blocks", line, + vd->vdisk_size / vd->vdisk_sectorsz, + vd->vdisk_sectorsz); + if ((ret = pager_output("\n")) != 0) + break; + + vd_dev.dd.d_dev = &vdisk_dev; + vd_dev.dd.d_unit = vd->vdisk_unit; + vd_dev.d_slice = -1; + vd_dev.d_partition = -1; + + ret = disk_open(&vd_dev, vd->vdisk_size, vd->vdisk_sectorsz); + if (ret == 0) { + ret = disk_print(&vd_dev, line, verbose); + disk_close(&vd_dev); + if (ret != 0) + break; + } else { + ret = 0; + } + } + + return (ret); +} Index: head/stand/efi/loader/conf.c =================================================================== --- head/stand/efi/loader/conf.c +++ head/stand/efi/loader/conf.c @@ -33,11 +33,14 @@ #include #include +extern struct devsw vdisk_dev; + struct devsw *devsw[] = { &efipart_fddev, &efipart_cddev, &efipart_hddev, &efinet_dev, + &vdisk_dev, #ifdef EFI_ZFS_BOOT &zfs_dev, #endif Index: head/stand/i386/loader/conf.c =================================================================== --- head/stand/i386/loader/conf.c +++ head/stand/i386/loader/conf.c @@ -48,6 +48,7 @@ #if defined(LOADER_FIREWIRE_SUPPORT) extern struct devsw fwohci; #endif +extern struct devsw vdisk_dev; /* Exported for libstand */ struct devsw *devsw[] = { @@ -60,6 +61,7 @@ #if defined(LOADER_FIREWIRE_SUPPORT) &fwohci, #endif + &vdisk_dev, #if defined(LOADER_ZFS_SUPPORT) &zfs_dev, #endif Index: head/stand/loader.mk =================================================================== --- head/stand/loader.mk +++ head/stand/loader.mk @@ -31,7 +31,7 @@ .endif .if ${LOADER_DISK_SUPPORT:Uyes} == "yes" -SRCS+= disk.c part.c +SRCS+= disk.c part.c vdisk.c .endif .if ${LOADER_NET_SUPPORT:Uno} == "yes"