xref: /freebsd/stand/common/vdisk.c (revision ad759c73522efb606135e2891ac03864926b80a3)
14914ee11SToomas Soome /*-
24914ee11SToomas Soome  * Copyright 2019 Toomas Soome <tsoome@me.com>
34914ee11SToomas Soome  *
44914ee11SToomas Soome  * Redistribution and use in source and binary forms, with or without
54914ee11SToomas Soome  * modification, are permitted provided that the following conditions
64914ee11SToomas Soome  * are met:
74914ee11SToomas Soome  * 1. Redistributions of source code must retain the above copyright
84914ee11SToomas Soome  *    notice, this list of conditions and the following disclaimer.
94914ee11SToomas Soome  * 2. Redistributions in binary form must reproduce the above copyright
104914ee11SToomas Soome  *    notice, this list of conditions and the following disclaimer in the
114914ee11SToomas Soome  *    documentation and/or other materials provided with the distribution.
124914ee11SToomas Soome  *
134914ee11SToomas Soome  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
144914ee11SToomas Soome  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
154914ee11SToomas Soome  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
164914ee11SToomas Soome  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
174914ee11SToomas Soome  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
184914ee11SToomas Soome  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
194914ee11SToomas Soome  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
204914ee11SToomas Soome  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
214914ee11SToomas Soome  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
224914ee11SToomas Soome  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
234914ee11SToomas Soome  * SUCH DAMAGE.
244914ee11SToomas Soome  */
254914ee11SToomas Soome 
264914ee11SToomas Soome #include <sys/cdefs.h>
274914ee11SToomas Soome __FBSDID("$FreeBSD$");
284914ee11SToomas Soome 
294914ee11SToomas Soome #include <stand.h>
304914ee11SToomas Soome #include <stdarg.h>
314914ee11SToomas Soome #include <machine/_inttypes.h>
324914ee11SToomas Soome #include <bootstrap.h>
334914ee11SToomas Soome #include <sys/disk.h>
344914ee11SToomas Soome #include <sys/errno.h>
354914ee11SToomas Soome #include <sys/queue.h>
364914ee11SToomas Soome #include <sys/param.h>
374914ee11SToomas Soome #include <disk.h>
384914ee11SToomas Soome 
394914ee11SToomas Soome static int vdisk_init(void);
404914ee11SToomas Soome static int vdisk_strategy(void *, int, daddr_t, size_t, char *, size_t *);
414914ee11SToomas Soome static int vdisk_open(struct open_file *, ...);
424914ee11SToomas Soome static int vdisk_close(struct open_file *);
434914ee11SToomas Soome static int vdisk_ioctl(struct open_file *, u_long, void *);
444914ee11SToomas Soome static int vdisk_print(int);
454914ee11SToomas Soome 
464914ee11SToomas Soome struct devsw vdisk_dev = {
474914ee11SToomas Soome 	.dv_name = "vdisk",
484914ee11SToomas Soome 	.dv_type = DEVT_DISK,
494914ee11SToomas Soome 	.dv_init = vdisk_init,
504914ee11SToomas Soome 	.dv_strategy = vdisk_strategy,
514914ee11SToomas Soome 	.dv_open = vdisk_open,
524914ee11SToomas Soome 	.dv_close = vdisk_close,
534914ee11SToomas Soome 	.dv_ioctl = vdisk_ioctl,
544914ee11SToomas Soome 	.dv_print = vdisk_print,
55e98f952cSWarner Losh 	.dv_cleanup = nullsys,
56*ad759c73SWarner Losh 	.dv_fmtdev = disk_fmtdev,
574914ee11SToomas Soome };
584914ee11SToomas Soome 
594914ee11SToomas Soome typedef STAILQ_HEAD(vdisk_info_list, vdisk_info) vdisk_info_list_t;
604914ee11SToomas Soome 
614914ee11SToomas Soome typedef struct vdisk_info
624914ee11SToomas Soome {
634914ee11SToomas Soome 	STAILQ_ENTRY(vdisk_info)	vdisk_link; /* link in device list */
644914ee11SToomas Soome 	char			*vdisk_path;
654914ee11SToomas Soome 	int			vdisk_unit;
664914ee11SToomas Soome 	int			vdisk_fd;
674914ee11SToomas Soome 	uint64_t		vdisk_size;	/* size in bytes */
684914ee11SToomas Soome 	uint32_t		vdisk_sectorsz;
694914ee11SToomas Soome 	uint32_t		vdisk_open;	/* reference counter */
704914ee11SToomas Soome } vdisk_info_t;
714914ee11SToomas Soome 
724914ee11SToomas Soome static vdisk_info_list_t vdisk_list;	/* list of mapped vdisks. */
734914ee11SToomas Soome 
744914ee11SToomas Soome static vdisk_info_t *
754914ee11SToomas Soome vdisk_get_info(struct devdesc *dev)
764914ee11SToomas Soome {
774914ee11SToomas Soome 	vdisk_info_t *vd;
784914ee11SToomas Soome 
794914ee11SToomas Soome 	STAILQ_FOREACH(vd, &vdisk_list, vdisk_link) {
804914ee11SToomas Soome 		if (vd->vdisk_unit == dev->d_unit)
814914ee11SToomas Soome 			return (vd);
824914ee11SToomas Soome 	}
834914ee11SToomas Soome 	return (vd);
844914ee11SToomas Soome }
854914ee11SToomas Soome 
864914ee11SToomas Soome COMMAND_SET(map_vdisk, "map-vdisk", "map file as virtual disk", command_mapvd);
874914ee11SToomas Soome 
884914ee11SToomas Soome static int
894914ee11SToomas Soome command_mapvd(int argc, char *argv[])
904914ee11SToomas Soome {
914914ee11SToomas Soome 	vdisk_info_t *vd, *p;
924914ee11SToomas Soome 	struct stat sb;
934914ee11SToomas Soome 
944914ee11SToomas Soome 	if (argc != 2) {
954914ee11SToomas Soome 		printf("usage: %s filename\n", argv[0]);
964914ee11SToomas Soome 		return (CMD_ERROR);
974914ee11SToomas Soome 	}
984914ee11SToomas Soome 
994914ee11SToomas Soome 	STAILQ_FOREACH(vd, &vdisk_list, vdisk_link) {
1004914ee11SToomas Soome 		if (strcmp(vd->vdisk_path, argv[1]) == 0) {
1014914ee11SToomas Soome 			printf("%s: file %s is already mapped as %s%d\n",
1024914ee11SToomas Soome 			    argv[0], argv[1], vdisk_dev.dv_name,
1034914ee11SToomas Soome 			    vd->vdisk_unit);
1044914ee11SToomas Soome 			return (CMD_ERROR);
1054914ee11SToomas Soome 		}
1064914ee11SToomas Soome 	}
1074914ee11SToomas Soome 
1084914ee11SToomas Soome 	if (stat(argv[1], &sb) < 0) {
1094914ee11SToomas Soome 		/*
1104914ee11SToomas Soome 		 * ENOSYS is really ENOENT because we did try to walk
1114914ee11SToomas Soome 		 * through devsw list to try to open this file.
1124914ee11SToomas Soome 		 */
1134914ee11SToomas Soome 		if (errno == ENOSYS)
1144914ee11SToomas Soome 			errno = ENOENT;
1154914ee11SToomas Soome 
1164914ee11SToomas Soome 		printf("%s: stat failed: %s\n", argv[0], strerror(errno));
1174914ee11SToomas Soome 		return (CMD_ERROR);
1184914ee11SToomas Soome 	}
1194914ee11SToomas Soome 
1204914ee11SToomas Soome 	/*
1214914ee11SToomas Soome 	 * Avoid mapping small files.
1224914ee11SToomas Soome 	 */
1234914ee11SToomas Soome 	if (sb.st_size < 1024 * 1024) {
1244914ee11SToomas Soome 		printf("%s: file %s is too small.\n", argv[0], argv[1]);
1254914ee11SToomas Soome 		return (CMD_ERROR);
1264914ee11SToomas Soome 	}
1274914ee11SToomas Soome 
1284914ee11SToomas Soome 	vd = calloc(1, sizeof (*vd));
1294914ee11SToomas Soome 	if (vd == NULL) {
1304914ee11SToomas Soome 		printf("%s: out of memory\n", argv[0]);
1314914ee11SToomas Soome 		return (CMD_ERROR);
1324914ee11SToomas Soome 	}
1334914ee11SToomas Soome 	vd->vdisk_path = strdup(argv[1]);
1344914ee11SToomas Soome 	if (vd->vdisk_path == NULL) {
1354914ee11SToomas Soome 		free (vd);
1364914ee11SToomas Soome 		printf("%s: out of memory\n", argv[0]);
1374914ee11SToomas Soome 		return (CMD_ERROR);
1384914ee11SToomas Soome 	}
1394914ee11SToomas Soome 	vd->vdisk_fd = open(vd->vdisk_path, O_RDONLY);
1404914ee11SToomas Soome 	if (vd->vdisk_fd < 0) {
1414914ee11SToomas Soome 		printf("%s: open failed: %s\n", argv[0], strerror(errno));
1424914ee11SToomas Soome 		free(vd->vdisk_path);
1434914ee11SToomas Soome 		free(vd);
1444914ee11SToomas Soome 		return (CMD_ERROR);
1454914ee11SToomas Soome 	}
1464914ee11SToomas Soome 
1474914ee11SToomas Soome 	vd->vdisk_size = sb.st_size;
1484914ee11SToomas Soome 	vd->vdisk_sectorsz = DEV_BSIZE;
1494914ee11SToomas Soome 	STAILQ_FOREACH(p, &vdisk_list, vdisk_link) {
1504914ee11SToomas Soome 		vdisk_info_t *n;
1514914ee11SToomas Soome 		if (p->vdisk_unit == vd->vdisk_unit) {
1524914ee11SToomas Soome 			vd->vdisk_unit++;
1534914ee11SToomas Soome 			continue;
1544914ee11SToomas Soome 		}
1554914ee11SToomas Soome 		n = STAILQ_NEXT(p, vdisk_link);
1564914ee11SToomas Soome 		if (p->vdisk_unit < vd->vdisk_unit) {
1574914ee11SToomas Soome 			if (n == NULL) {
1584914ee11SToomas Soome 				/* p is last elem */
1594914ee11SToomas Soome 				STAILQ_INSERT_TAIL(&vdisk_list, vd, vdisk_link);
1604914ee11SToomas Soome 				break;
1614914ee11SToomas Soome 			}
1624914ee11SToomas Soome 			if (n->vdisk_unit > vd->vdisk_unit) {
1634914ee11SToomas Soome 				/* p < vd < n */
1644914ee11SToomas Soome 				STAILQ_INSERT_AFTER(&vdisk_list, p, vd,
1654914ee11SToomas Soome 				    vdisk_link);
1664914ee11SToomas Soome 				break;
1674914ee11SToomas Soome 			}
1684914ee11SToomas Soome 			/* else n < vd or n == vd */
1694914ee11SToomas Soome 			vd->vdisk_unit++;
1704914ee11SToomas Soome 			continue;
1714914ee11SToomas Soome 		}
1724914ee11SToomas Soome 		/* p > vd only if p is the first element */
1734914ee11SToomas Soome 		STAILQ_INSERT_HEAD(&vdisk_list, vd, vdisk_link);
1744914ee11SToomas Soome 		break;
1754914ee11SToomas Soome 	}
1764914ee11SToomas Soome 
1774914ee11SToomas Soome 	/* if the list was empty or contiguous */
1784914ee11SToomas Soome 	if (p == NULL)
1794914ee11SToomas Soome 		STAILQ_INSERT_TAIL(&vdisk_list, vd, vdisk_link);
1804914ee11SToomas Soome 
1814914ee11SToomas Soome 	printf("%s: file %s is mapped as %s%d\n", argv[0], vd->vdisk_path,
1824914ee11SToomas Soome 	    vdisk_dev.dv_name, vd->vdisk_unit);
1834914ee11SToomas Soome 	return (CMD_OK);
1844914ee11SToomas Soome }
1854914ee11SToomas Soome 
1864914ee11SToomas Soome COMMAND_SET(unmap_vdisk, "unmap-vdisk", "unmap virtual disk", command_unmapvd);
1874914ee11SToomas Soome 
1884914ee11SToomas Soome /*
1894914ee11SToomas Soome  * unmap-vdisk vdiskX
1904914ee11SToomas Soome  */
1914914ee11SToomas Soome static int
1924914ee11SToomas Soome command_unmapvd(int argc, char *argv[])
1934914ee11SToomas Soome {
1944914ee11SToomas Soome 	size_t len;
1954914ee11SToomas Soome 	vdisk_info_t *vd;
1964914ee11SToomas Soome 	long unit;
1974914ee11SToomas Soome 	char *end;
1984914ee11SToomas Soome 
1994914ee11SToomas Soome 	if (argc != 2) {
2004914ee11SToomas Soome 		printf("usage: %s %sN\n", argv[0], vdisk_dev.dv_name);
2014914ee11SToomas Soome 		return (CMD_ERROR);
2024914ee11SToomas Soome 	}
2034914ee11SToomas Soome 
2044914ee11SToomas Soome 	len = strlen(vdisk_dev.dv_name);
2054914ee11SToomas Soome 	if (strncmp(vdisk_dev.dv_name, argv[1], len) != 0) {
2064914ee11SToomas Soome 		printf("%s: unknown device %s\n", argv[0], argv[1]);
2074914ee11SToomas Soome 		return (CMD_ERROR);
2084914ee11SToomas Soome 	}
2094914ee11SToomas Soome 	errno = 0;
2104914ee11SToomas Soome 	unit = strtol(argv[1] + len, &end, 10);
2114914ee11SToomas Soome 	if (errno != 0 || (*end != '\0' && strcmp(end, ":") != 0)) {
2124914ee11SToomas Soome 		printf("%s: unknown device %s\n", argv[0], argv[1]);
2134914ee11SToomas Soome 		return (CMD_ERROR);
2144914ee11SToomas Soome 	}
2154914ee11SToomas Soome 
2164914ee11SToomas Soome 	STAILQ_FOREACH(vd, &vdisk_list, vdisk_link) {
2174914ee11SToomas Soome 		if (vd->vdisk_unit == unit)
2184914ee11SToomas Soome 			break;
2194914ee11SToomas Soome 	}
2204914ee11SToomas Soome 
2214914ee11SToomas Soome 	if (vd == NULL) {
2224914ee11SToomas Soome 		printf("%s: unknown device %s\n", argv[0], argv[1]);
2234914ee11SToomas Soome 		return (CMD_ERROR);
2244914ee11SToomas Soome 	}
2254914ee11SToomas Soome 
2264914ee11SToomas Soome 	if (vd->vdisk_open != 0) {
2274914ee11SToomas Soome 		printf("%s: %s is in use, unable to unmap.\n",
2284914ee11SToomas Soome 		    argv[0], argv[1]);
2294914ee11SToomas Soome 		return (CMD_ERROR);
2304914ee11SToomas Soome 	}
2314914ee11SToomas Soome 
2324914ee11SToomas Soome 	STAILQ_REMOVE(&vdisk_list, vd, vdisk_info, vdisk_link);
23368031519SToomas Soome 	(void) close(vd->vdisk_fd);
23468031519SToomas Soome 	printf("%s (%s) unmapped\n", argv[1], vd->vdisk_path);
2354914ee11SToomas Soome 	free(vd->vdisk_path);
2364914ee11SToomas Soome 	free(vd);
2374914ee11SToomas Soome 
2384914ee11SToomas Soome 	return (CMD_OK);
2394914ee11SToomas Soome }
2404914ee11SToomas Soome 
2414914ee11SToomas Soome static int
2424914ee11SToomas Soome vdisk_init(void)
2434914ee11SToomas Soome {
2444914ee11SToomas Soome 	STAILQ_INIT(&vdisk_list);
2454914ee11SToomas Soome 	return (0);
2464914ee11SToomas Soome }
2474914ee11SToomas Soome 
2484914ee11SToomas Soome static int
2494914ee11SToomas Soome vdisk_strategy(void *devdata, int rw, daddr_t blk, size_t size,
2504914ee11SToomas Soome     char *buf, size_t *rsize)
2514914ee11SToomas Soome {
2524914ee11SToomas Soome 	struct disk_devdesc *dev;
2534914ee11SToomas Soome 	vdisk_info_t *vd;
2544914ee11SToomas Soome 	ssize_t rv;
2554914ee11SToomas Soome 
2564914ee11SToomas Soome 	dev = devdata;
2574914ee11SToomas Soome 	if (dev == NULL)
2584914ee11SToomas Soome 		return (EINVAL);
2594914ee11SToomas Soome 	vd = vdisk_get_info((struct devdesc *)dev);
2604914ee11SToomas Soome 	if (vd == NULL)
2614914ee11SToomas Soome 		return (EINVAL);
2624914ee11SToomas Soome 
2634914ee11SToomas Soome 	if (size == 0 || (size % 512) != 0)
2644914ee11SToomas Soome 		return (EIO);
2654914ee11SToomas Soome 
2664914ee11SToomas Soome 	if (dev->dd.d_dev->dv_type == DEVT_DISK) {
2674914ee11SToomas Soome 		daddr_t offset;
2684914ee11SToomas Soome 
2694914ee11SToomas Soome 		offset = dev->d_offset * vd->vdisk_sectorsz;
2704914ee11SToomas Soome 		offset /= 512;
2714914ee11SToomas Soome 		blk += offset;
2724914ee11SToomas Soome 	}
2734914ee11SToomas Soome 	if (lseek(vd->vdisk_fd, blk << 9, SEEK_SET) == -1)
2744914ee11SToomas Soome 		return (EIO);
2754914ee11SToomas Soome 
2764914ee11SToomas Soome 	errno = 0;
2774914ee11SToomas Soome 	switch (rw & F_MASK) {
2784914ee11SToomas Soome 	case F_READ:
2794914ee11SToomas Soome 		rv = read(vd->vdisk_fd, buf, size);
2804914ee11SToomas Soome 		break;
2814914ee11SToomas Soome 	case F_WRITE:
2824914ee11SToomas Soome 		rv = write(vd->vdisk_fd, buf, size);
2834914ee11SToomas Soome 		break;
2844914ee11SToomas Soome 	default:
2854914ee11SToomas Soome 		return (ENOSYS);
2864914ee11SToomas Soome 	}
2874914ee11SToomas Soome 
2884914ee11SToomas Soome 	if (errno == 0 && rsize != NULL) {
2894914ee11SToomas Soome 		*rsize = rv;
2904914ee11SToomas Soome 	}
2914914ee11SToomas Soome 	return (errno);
2924914ee11SToomas Soome }
2934914ee11SToomas Soome 
2944914ee11SToomas Soome static int
2954914ee11SToomas Soome vdisk_open(struct open_file *f, ...)
2964914ee11SToomas Soome {
2974914ee11SToomas Soome 	va_list args;
2984914ee11SToomas Soome 	struct disk_devdesc *dev;
2994914ee11SToomas Soome 	vdisk_info_t *vd;
3004914ee11SToomas Soome 	int rc = 0;
3014914ee11SToomas Soome 
3024914ee11SToomas Soome 	va_start(args, f);
3034914ee11SToomas Soome 	dev = va_arg(args, struct disk_devdesc *);
3044914ee11SToomas Soome 	va_end(args);
3054914ee11SToomas Soome 	if (dev == NULL)
3064914ee11SToomas Soome 		return (EINVAL);
3074914ee11SToomas Soome 	vd = vdisk_get_info((struct devdesc *)dev);
3084914ee11SToomas Soome 	if (vd == NULL)
3094914ee11SToomas Soome 		return (EINVAL);
3104914ee11SToomas Soome 
3114914ee11SToomas Soome 	if (dev->dd.d_dev->dv_type == DEVT_DISK) {
3124914ee11SToomas Soome 		rc = disk_open(dev, vd->vdisk_size, vd->vdisk_sectorsz);
3134914ee11SToomas Soome 	}
3144914ee11SToomas Soome 	if (rc == 0)
3154914ee11SToomas Soome 		vd->vdisk_open++;
3164914ee11SToomas Soome 	return (rc);
3174914ee11SToomas Soome }
3184914ee11SToomas Soome 
3194914ee11SToomas Soome static int
3204914ee11SToomas Soome vdisk_close(struct open_file *f)
3214914ee11SToomas Soome {
3224914ee11SToomas Soome 	struct disk_devdesc *dev;
3234914ee11SToomas Soome 	vdisk_info_t *vd;
3244914ee11SToomas Soome 
3254914ee11SToomas Soome 	dev = (struct disk_devdesc *)(f->f_devdata);
3264914ee11SToomas Soome 	if (dev == NULL)
3274914ee11SToomas Soome 		return (EINVAL);
3284914ee11SToomas Soome 	vd = vdisk_get_info((struct devdesc *)dev);
3294914ee11SToomas Soome 	if (vd == NULL)
3304914ee11SToomas Soome 		return (EINVAL);
3314914ee11SToomas Soome 
3324914ee11SToomas Soome 	vd->vdisk_open--;
3334914ee11SToomas Soome 	if (dev->dd.d_dev->dv_type == DEVT_DISK)
3344914ee11SToomas Soome 		return (disk_close(dev));
3354914ee11SToomas Soome 	return (0);
3364914ee11SToomas Soome }
3374914ee11SToomas Soome 
3384914ee11SToomas Soome static int
3394914ee11SToomas Soome vdisk_ioctl(struct open_file *f, u_long cmd, void *data)
3404914ee11SToomas Soome {
3414914ee11SToomas Soome 	struct disk_devdesc *dev;
3424914ee11SToomas Soome 	vdisk_info_t *vd;
3434914ee11SToomas Soome 	int rc;
3444914ee11SToomas Soome 
3454914ee11SToomas Soome 	dev = (struct disk_devdesc *)(f->f_devdata);
3464914ee11SToomas Soome 	if (dev == NULL)
3474914ee11SToomas Soome 		return (EINVAL);
3484914ee11SToomas Soome 	vd = vdisk_get_info((struct devdesc *)dev);
3494914ee11SToomas Soome 	if (vd == NULL)
3504914ee11SToomas Soome 		return (EINVAL);
3514914ee11SToomas Soome 
3524914ee11SToomas Soome 	if (dev->dd.d_dev->dv_type == DEVT_DISK) {
3534914ee11SToomas Soome 		rc = disk_ioctl(dev, cmd, data);
3544914ee11SToomas Soome 		if (rc != ENOTTY)
3554914ee11SToomas Soome 			return (rc);
3564914ee11SToomas Soome 	}
3574914ee11SToomas Soome 
3584914ee11SToomas Soome 	switch (cmd) {
3594914ee11SToomas Soome 	case DIOCGSECTORSIZE:
3604914ee11SToomas Soome 		*(u_int *)data = vd->vdisk_sectorsz;
3614914ee11SToomas Soome 		break;
3624914ee11SToomas Soome 	case DIOCGMEDIASIZE:
3634914ee11SToomas Soome 		*(uint64_t *)data = vd->vdisk_size;
3644914ee11SToomas Soome 		break;
3654914ee11SToomas Soome 	default:
3664914ee11SToomas Soome 		return (ENOTTY);
3674914ee11SToomas Soome 	}
3684914ee11SToomas Soome 	return (0);
3694914ee11SToomas Soome }
3704914ee11SToomas Soome 
3714914ee11SToomas Soome static int
3724914ee11SToomas Soome vdisk_print(int verbose)
3734914ee11SToomas Soome {
3744914ee11SToomas Soome 	int ret = 0;
3754914ee11SToomas Soome 	vdisk_info_t *vd;
3764914ee11SToomas Soome 	char line[80];
3774914ee11SToomas Soome 
3784914ee11SToomas Soome 	if (STAILQ_EMPTY(&vdisk_list))
3794914ee11SToomas Soome 		return (ret);
3804914ee11SToomas Soome 
3814914ee11SToomas Soome 	printf("%s devices:", vdisk_dev.dv_name);
3824914ee11SToomas Soome 	if ((ret = pager_output("\n")) != 0)
3834914ee11SToomas Soome 		return (ret);
3844914ee11SToomas Soome 
3854914ee11SToomas Soome 	STAILQ_FOREACH(vd, &vdisk_list, vdisk_link) {
3864914ee11SToomas Soome 		struct disk_devdesc vd_dev;
3874914ee11SToomas Soome 
3884914ee11SToomas Soome 		if (verbose) {
3894914ee11SToomas Soome 			printf("  %s", vd->vdisk_path);
3904914ee11SToomas Soome 			if ((ret = pager_output("\n")) != 0)
3914914ee11SToomas Soome 				break;
3924914ee11SToomas Soome 		}
3934914ee11SToomas Soome 		snprintf(line, sizeof(line),
3944914ee11SToomas Soome 		    "    %s%d", vdisk_dev.dv_name, vd->vdisk_unit);
3954914ee11SToomas Soome 		printf("%s:    %" PRIu64 " X %u blocks", line,
3964914ee11SToomas Soome 		    vd->vdisk_size / vd->vdisk_sectorsz,
3974914ee11SToomas Soome 		    vd->vdisk_sectorsz);
3984914ee11SToomas Soome 		if ((ret = pager_output("\n")) != 0)
3994914ee11SToomas Soome 			break;
4004914ee11SToomas Soome 
4014914ee11SToomas Soome 		vd_dev.dd.d_dev = &vdisk_dev;
4024914ee11SToomas Soome 		vd_dev.dd.d_unit = vd->vdisk_unit;
4034914ee11SToomas Soome 		vd_dev.d_slice = -1;
4044914ee11SToomas Soome 		vd_dev.d_partition = -1;
4054914ee11SToomas Soome 
4064914ee11SToomas Soome 		ret = disk_open(&vd_dev, vd->vdisk_size, vd->vdisk_sectorsz);
4074914ee11SToomas Soome 		if (ret == 0) {
4084914ee11SToomas Soome 			ret = disk_print(&vd_dev, line, verbose);
4094914ee11SToomas Soome 			disk_close(&vd_dev);
4104914ee11SToomas Soome 			if (ret != 0)
4114914ee11SToomas Soome 				break;
4124914ee11SToomas Soome 		} else {
4134914ee11SToomas Soome 			ret = 0;
4144914ee11SToomas Soome 		}
4154914ee11SToomas Soome 	}
4164914ee11SToomas Soome 
4174914ee11SToomas Soome 	return (ret);
4184914ee11SToomas Soome }
419