xref: /illumos-gate/usr/src/boot/common/vdisk.c (revision 22028508)
1f4769751SToomas Soome /*
2f4769751SToomas Soome  * Copyright 2019 Toomas Soome <tsoome@me.com>
3f4769751SToomas Soome  *
4f4769751SToomas Soome  * Redistribution and use in source and binary forms, with or without
5f4769751SToomas Soome  * modification, are permitted provided that the following conditions
6f4769751SToomas Soome  * are met:
7f4769751SToomas Soome  * 1. Redistributions of source code must retain the above copyright
8f4769751SToomas Soome  *    notice, this list of conditions and the following disclaimer.
9f4769751SToomas Soome  * 2. Redistributions in binary form must reproduce the above copyright
10f4769751SToomas Soome  *    notice, this list of conditions and the following disclaimer in the
11f4769751SToomas Soome  *    documentation and/or other materials provided with the distribution.
12f4769751SToomas Soome  *
13f4769751SToomas Soome  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
14f4769751SToomas Soome  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15f4769751SToomas Soome  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16f4769751SToomas Soome  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
17f4769751SToomas Soome  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18f4769751SToomas Soome  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
19f4769751SToomas Soome  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
20f4769751SToomas Soome  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
21f4769751SToomas Soome  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22f4769751SToomas Soome  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
23f4769751SToomas Soome  * SUCH DAMAGE.
24f4769751SToomas Soome  */
25f4769751SToomas Soome 
26f4769751SToomas Soome #include <sys/cdefs.h>
27f4769751SToomas Soome 
28f4769751SToomas Soome #include <stand.h>
29f4769751SToomas Soome #include <stdarg.h>
30f4769751SToomas Soome #include <inttypes.h>
31f4769751SToomas Soome #include <bootstrap.h>
32f4769751SToomas Soome #include <sys/disk.h>
33f4769751SToomas Soome #include <sys/errno.h>
34f4769751SToomas Soome #include <sys/queue.h>
35f4769751SToomas Soome #include <sys/param.h>
36f4769751SToomas Soome #include <disk.h>
37f4769751SToomas Soome 
38f4769751SToomas Soome static int vdisk_init(void);
39f4769751SToomas Soome static int vdisk_strategy(void *, int, daddr_t, size_t, char *, size_t *);
40f4769751SToomas Soome static int vdisk_open(struct open_file *, ...);
41f4769751SToomas Soome static int vdisk_close(struct open_file *);
42f4769751SToomas Soome static int vdisk_ioctl(struct open_file *, ulong_t, void *);
43f4769751SToomas Soome static int vdisk_print(int);
44f4769751SToomas Soome 
45f4769751SToomas Soome struct devsw vdisk_dev = {
46f4769751SToomas Soome 	.dv_name = "vdisk",
47f4769751SToomas Soome 	.dv_type = DEVT_DISK,
48f4769751SToomas Soome 	.dv_init = vdisk_init,
49f4769751SToomas Soome 	.dv_strategy = vdisk_strategy,
50f4769751SToomas Soome 	.dv_open = vdisk_open,
51f4769751SToomas Soome 	.dv_close = vdisk_close,
52f4769751SToomas Soome 	.dv_ioctl = vdisk_ioctl,
53f4769751SToomas Soome 	.dv_print = vdisk_print,
54f4769751SToomas Soome 	.dv_cleanup = NULL
55f4769751SToomas Soome };
56f4769751SToomas Soome 
57f4769751SToomas Soome typedef STAILQ_HEAD(vdisk_info_list, vdisk_info) vdisk_info_list_t;
58f4769751SToomas Soome 
59f4769751SToomas Soome typedef struct vdisk_info
60f4769751SToomas Soome {
61f4769751SToomas Soome 	STAILQ_ENTRY(vdisk_info)	vdisk_link; /* link in device list */
62f4769751SToomas Soome 	char			*vdisk_path;
63f4769751SToomas Soome 	int			vdisk_unit;
64f4769751SToomas Soome 	int			vdisk_fd;
65f4769751SToomas Soome 	uint64_t		vdisk_size;	/* size in bytes */
66f4769751SToomas Soome 	uint32_t		vdisk_sectorsz;
67f4769751SToomas Soome 	uint32_t		vdisk_open;	/* reference counter */
68f4769751SToomas Soome } vdisk_info_t;
69f4769751SToomas Soome 
70f4769751SToomas Soome static vdisk_info_list_t vdisk_list;	/* list of mapped vdisks. */
71f4769751SToomas Soome 
72f4769751SToomas Soome static vdisk_info_t *
vdisk_get_info(struct devdesc * dev)73f4769751SToomas Soome vdisk_get_info(struct devdesc *dev)
74f4769751SToomas Soome {
75f4769751SToomas Soome 	vdisk_info_t *vd;
76f4769751SToomas Soome 
77f4769751SToomas Soome 	STAILQ_FOREACH(vd, &vdisk_list, vdisk_link) {
78f4769751SToomas Soome 		if (vd->vdisk_unit == dev->d_unit)
79f4769751SToomas Soome 			return (vd);
80f4769751SToomas Soome 	}
81f4769751SToomas Soome 	return (vd);
82f4769751SToomas Soome }
83f4769751SToomas Soome 
84f4769751SToomas Soome COMMAND_SET(map_vdisk, "map-vdisk", "map file as virtual disk", command_mapvd);
85f4769751SToomas Soome 
86f4769751SToomas Soome static int
command_mapvd(int argc,char * argv[])87f4769751SToomas Soome command_mapvd(int argc, char *argv[])
88f4769751SToomas Soome {
89f4769751SToomas Soome 	vdisk_info_t *vd, *p;
90f4769751SToomas Soome 	struct stat sb;
91f4769751SToomas Soome 
92f4769751SToomas Soome 	if (argc != 2) {
93f4769751SToomas Soome 		printf("usage: %s filename\n", argv[0]);
94f4769751SToomas Soome 		return (CMD_ERROR);
95f4769751SToomas Soome 	}
96f4769751SToomas Soome 
97f4769751SToomas Soome 	STAILQ_FOREACH(vd, &vdisk_list, vdisk_link) {
98f4769751SToomas Soome 		if (strcmp(vd->vdisk_path, argv[1]) == 0) {
99f4769751SToomas Soome 			printf("%s: file %s is already mapped as %s%d\n",
100f4769751SToomas Soome 			    argv[0], argv[1], vdisk_dev.dv_name,
101f4769751SToomas Soome 			    vd->vdisk_unit);
102f4769751SToomas Soome 			return (CMD_ERROR);
103f4769751SToomas Soome 		}
104f4769751SToomas Soome 	}
105f4769751SToomas Soome 
106f4769751SToomas Soome 	if (stat(argv[1], &sb) < 0) {
107f4769751SToomas Soome 		/*
108f4769751SToomas Soome 		 * ENOSYS is really ENOENT because we did try to walk
109f4769751SToomas Soome 		 * through devsw list to try to open this file.
110f4769751SToomas Soome 		 */
111f4769751SToomas Soome 		if (errno == ENOSYS)
112f4769751SToomas Soome 			errno = ENOENT;
113f4769751SToomas Soome 
114f4769751SToomas Soome 		printf("%s: stat failed: %s\n", argv[0], strerror(errno));
115f4769751SToomas Soome 		return (CMD_ERROR);
116f4769751SToomas Soome 	}
117f4769751SToomas Soome 
118f4769751SToomas Soome 	/*
119f4769751SToomas Soome 	 * Avoid mapping small files.
120f4769751SToomas Soome 	 */
121f4769751SToomas Soome 	if (sb.st_size < 1024 * 1024) {
122f4769751SToomas Soome 		printf("%s: file %s is too small.\n", argv[0], argv[1]);
123f4769751SToomas Soome 		return (CMD_ERROR);
124f4769751SToomas Soome 	}
125f4769751SToomas Soome 
126f4769751SToomas Soome 	vd = calloc(1, sizeof (*vd));
127f4769751SToomas Soome 	if (vd == NULL) {
128f4769751SToomas Soome 		printf("%s: out of memory\n", argv[0]);
129f4769751SToomas Soome 		return (CMD_ERROR);
130f4769751SToomas Soome 	}
131f4769751SToomas Soome 	vd->vdisk_path = strdup(argv[1]);
132f4769751SToomas Soome 	if (vd->vdisk_path == NULL) {
133f4769751SToomas Soome 		free(vd);
134f4769751SToomas Soome 		printf("%s: out of memory\n", argv[0]);
135f4769751SToomas Soome 		return (CMD_ERROR);
136f4769751SToomas Soome 	}
137f4769751SToomas Soome 	vd->vdisk_fd = open(vd->vdisk_path, O_RDONLY);
138f4769751SToomas Soome 	if (vd->vdisk_fd < 0) {
139f4769751SToomas Soome 		printf("%s: open failed: %s\n", argv[0], strerror(errno));
140f4769751SToomas Soome 		free(vd->vdisk_path);
141f4769751SToomas Soome 		free(vd);
142f4769751SToomas Soome 		return (CMD_ERROR);
143f4769751SToomas Soome 	}
144f4769751SToomas Soome 
145f4769751SToomas Soome 	vd->vdisk_size = sb.st_size;
146f4769751SToomas Soome 	vd->vdisk_sectorsz = DEV_BSIZE;
147f4769751SToomas Soome 	STAILQ_FOREACH(p, &vdisk_list, vdisk_link) {
148f4769751SToomas Soome 		vdisk_info_t *n;
149f4769751SToomas Soome 		if (p->vdisk_unit == vd->vdisk_unit) {
150f4769751SToomas Soome 			vd->vdisk_unit++;
151f4769751SToomas Soome 			continue;
152f4769751SToomas Soome 		}
153f4769751SToomas Soome 		n = STAILQ_NEXT(p, vdisk_link);
154f4769751SToomas Soome 		if (p->vdisk_unit < vd->vdisk_unit) {
155f4769751SToomas Soome 			if (n == NULL) {
156f4769751SToomas Soome 				/* p is last elem */
157f4769751SToomas Soome 				STAILQ_INSERT_TAIL(&vdisk_list, vd, vdisk_link);
158f4769751SToomas Soome 				break;
159f4769751SToomas Soome 			}
160f4769751SToomas Soome 			if (n->vdisk_unit > vd->vdisk_unit) {
161f4769751SToomas Soome 				/* p < vd < n */
162f4769751SToomas Soome 				STAILQ_INSERT_AFTER(&vdisk_list, p, vd,
163f4769751SToomas Soome 				    vdisk_link);
164f4769751SToomas Soome 				break;
165f4769751SToomas Soome 			}
166f4769751SToomas Soome 			/* else n < vd or n == vd */
167f4769751SToomas Soome 			vd->vdisk_unit++;
168f4769751SToomas Soome 			continue;
169f4769751SToomas Soome 		}
170f4769751SToomas Soome 		/* p > vd only if p is the first element */
171f4769751SToomas Soome 		STAILQ_INSERT_HEAD(&vdisk_list, vd, vdisk_link);
172f4769751SToomas Soome 		break;
173f4769751SToomas Soome 	}
174f4769751SToomas Soome 
175f4769751SToomas Soome 	/* if the list was empty or contiguous */
176f4769751SToomas Soome 	if (p == NULL)
177f4769751SToomas Soome 		STAILQ_INSERT_TAIL(&vdisk_list, vd, vdisk_link);
178f4769751SToomas Soome 
179f4769751SToomas Soome 	printf("%s: file %s is mapped as %s%d\n", argv[0], vd->vdisk_path,
180f4769751SToomas Soome 	    vdisk_dev.dv_name, vd->vdisk_unit);
181f4769751SToomas Soome 	return (CMD_OK);
182f4769751SToomas Soome }
183f4769751SToomas Soome 
184f4769751SToomas Soome COMMAND_SET(unmap_vdisk, "unmap-vdisk", "unmap virtual disk", command_unmapvd);
185f4769751SToomas Soome 
186f4769751SToomas Soome /*
187f4769751SToomas Soome  * unmap-vdisk vdiskX
188f4769751SToomas Soome  */
189f4769751SToomas Soome static int
command_unmapvd(int argc,char * argv[])190f4769751SToomas Soome command_unmapvd(int argc, char *argv[])
191f4769751SToomas Soome {
192f4769751SToomas Soome 	size_t len;
193f4769751SToomas Soome 	vdisk_info_t *vd;
194f4769751SToomas Soome 	long unit;
195f4769751SToomas Soome 	char *end;
196f4769751SToomas Soome 
197f4769751SToomas Soome 	if (argc != 2) {
198f4769751SToomas Soome 		printf("usage: %s %sN\n", argv[0], vdisk_dev.dv_name);
199f4769751SToomas Soome 		return (CMD_ERROR);
200f4769751SToomas Soome 	}
201f4769751SToomas Soome 
202f4769751SToomas Soome 	len = strlen(vdisk_dev.dv_name);
203f4769751SToomas Soome 	if (strncmp(vdisk_dev.dv_name, argv[1], len) != 0) {
204f4769751SToomas Soome 		printf("%s: unknown device %s\n", argv[0], argv[1]);
205f4769751SToomas Soome 		return (CMD_ERROR);
206f4769751SToomas Soome 	}
207f4769751SToomas Soome 	errno = 0;
208f4769751SToomas Soome 	unit = strtol(argv[1] + len, &end, 10);
209f4769751SToomas Soome 	if (errno != 0 || (*end != '\0' && strcmp(end, ":") != 0)) {
210f4769751SToomas Soome 		printf("%s: unknown device %s\n", argv[0], argv[1]);
211f4769751SToomas Soome 		return (CMD_ERROR);
212f4769751SToomas Soome 	}
213f4769751SToomas Soome 
214f4769751SToomas Soome 	STAILQ_FOREACH(vd, &vdisk_list, vdisk_link) {
215f4769751SToomas Soome 		if (vd->vdisk_unit == unit)
216f4769751SToomas Soome 			break;
217f4769751SToomas Soome 	}
218f4769751SToomas Soome 
219f4769751SToomas Soome 	if (vd == NULL) {
220f4769751SToomas Soome 		printf("%s: unknown device %s\n", argv[0], argv[1]);
221f4769751SToomas Soome 		return (CMD_ERROR);
222f4769751SToomas Soome 	}
223f4769751SToomas Soome 
224f4769751SToomas Soome 	if (vd->vdisk_open != 0) {
225f4769751SToomas Soome 		printf("%s: %s is in use, unable to unmap.\n",
226f4769751SToomas Soome 		    argv[0], argv[1]);
227f4769751SToomas Soome 		return (CMD_ERROR);
228f4769751SToomas Soome 	}
229f4769751SToomas Soome 
230f4769751SToomas Soome 	STAILQ_REMOVE(&vdisk_list, vd, vdisk_info, vdisk_link);
231fdf04373SToomas Soome 	(void) close(vd->vdisk_fd);
232fdf04373SToomas Soome 	printf("%s (%s) unmapped\n", argv[1], vd->vdisk_path);
233f4769751SToomas Soome 	free(vd->vdisk_path);
234f4769751SToomas Soome 	free(vd);
235f4769751SToomas Soome 
236f4769751SToomas Soome 	return (CMD_OK);
237f4769751SToomas Soome }
238f4769751SToomas Soome 
239f4769751SToomas Soome static int
vdisk_init(void)240f4769751SToomas Soome vdisk_init(void)
241f4769751SToomas Soome {
242f4769751SToomas Soome 	STAILQ_INIT(&vdisk_list);
243f4769751SToomas Soome 	return (0);
244f4769751SToomas Soome }
245f4769751SToomas Soome 
246f4769751SToomas Soome static int
vdisk_strategy(void * devdata,int rw,daddr_t blk,size_t size,char * buf,size_t * rsize)247f4769751SToomas Soome vdisk_strategy(void *devdata, int rw, daddr_t blk, size_t size,
248f4769751SToomas Soome     char *buf, size_t *rsize)
249f4769751SToomas Soome {
250f4769751SToomas Soome 	struct disk_devdesc *dev;
251f4769751SToomas Soome 	vdisk_info_t *vd;
252f4769751SToomas Soome 	ssize_t rv;
253f4769751SToomas Soome 
254f4769751SToomas Soome 	dev = devdata;
255f4769751SToomas Soome 	if (dev == NULL)
256f4769751SToomas Soome 		return (EINVAL);
257f4769751SToomas Soome 	vd = vdisk_get_info((struct devdesc *)dev);
258f4769751SToomas Soome 	if (vd == NULL)
259f4769751SToomas Soome 		return (EINVAL);
260f4769751SToomas Soome 
261f4769751SToomas Soome 	if (size == 0 || (size % 512) != 0)
262f4769751SToomas Soome 		return (EIO);
263f4769751SToomas Soome 
264f4769751SToomas Soome 	if (dev->dd.d_dev->dv_type == DEVT_DISK) {
265f4769751SToomas Soome 		daddr_t offset;
266f4769751SToomas Soome 
267f4769751SToomas Soome 		offset = dev->d_offset * vd->vdisk_sectorsz;
268f4769751SToomas Soome 		offset /= 512;
269f4769751SToomas Soome 		blk += offset;
270f4769751SToomas Soome 	}
271f4769751SToomas Soome 	if (lseek(vd->vdisk_fd, blk << 9, SEEK_SET) == -1)
272f4769751SToomas Soome 		return (EIO);
273f4769751SToomas Soome 
274f4769751SToomas Soome 	errno = 0;
275f4769751SToomas Soome 	switch (rw & F_MASK) {
276f4769751SToomas Soome 	case F_READ:
277f4769751SToomas Soome 		rv = read(vd->vdisk_fd, buf, size);
278f4769751SToomas Soome 		break;
279f4769751SToomas Soome 	case F_WRITE:
280f4769751SToomas Soome 		rv = write(vd->vdisk_fd, buf, size);
281f4769751SToomas Soome 		break;
282f4769751SToomas Soome 	default:
283f4769751SToomas Soome 		return (ENOSYS);
284f4769751SToomas Soome 	}
285f4769751SToomas Soome 
286f4769751SToomas Soome 	if (errno == 0 && rsize != NULL) {
287f4769751SToomas Soome 		*rsize = rv;
288f4769751SToomas Soome 	}
289f4769751SToomas Soome 	return (errno);
290f4769751SToomas Soome }
291f4769751SToomas Soome 
292f4769751SToomas Soome static int
vdisk_open(struct open_file * f,...)293f4769751SToomas Soome vdisk_open(struct open_file *f, ...)
294f4769751SToomas Soome {
295f4769751SToomas Soome 	va_list args;
296f4769751SToomas Soome 	struct disk_devdesc *dev;
297f4769751SToomas Soome 	vdisk_info_t *vd;
298f4769751SToomas Soome 	int rc = 0;
299f4769751SToomas Soome 
300f4769751SToomas Soome 	va_start(args, f);
301f4769751SToomas Soome 	dev = va_arg(args, struct disk_devdesc *);
302f4769751SToomas Soome 	va_end(args);
303f4769751SToomas Soome 	if (dev == NULL)
304f4769751SToomas Soome 		return (EINVAL);
305f4769751SToomas Soome 	vd = vdisk_get_info((struct devdesc *)dev);
306f4769751SToomas Soome 	if (vd == NULL)
307f4769751SToomas Soome 		return (EINVAL);
308f4769751SToomas Soome 
309f4769751SToomas Soome 	if (dev->dd.d_dev->dv_type == DEVT_DISK) {
310f4769751SToomas Soome 		rc = disk_open(dev, vd->vdisk_size, vd->vdisk_sectorsz);
311f4769751SToomas Soome 	}
312f4769751SToomas Soome 	if (rc == 0)
313f4769751SToomas Soome 		vd->vdisk_open++;
314f4769751SToomas Soome 	return (rc);
315f4769751SToomas Soome }
316f4769751SToomas Soome 
317f4769751SToomas Soome static int
vdisk_close(struct open_file * f)318f4769751SToomas Soome vdisk_close(struct open_file *f)
319f4769751SToomas Soome {
320f4769751SToomas Soome 	struct disk_devdesc *dev;
321f4769751SToomas Soome 	vdisk_info_t *vd;
322f4769751SToomas Soome 
323f4769751SToomas Soome 	dev = (struct disk_devdesc *)(f->f_devdata);
324f4769751SToomas Soome 	if (dev == NULL)
325f4769751SToomas Soome 		return (EINVAL);
326f4769751SToomas Soome 	vd = vdisk_get_info((struct devdesc *)dev);
327f4769751SToomas Soome 	if (vd == NULL)
328f4769751SToomas Soome 		return (EINVAL);
329f4769751SToomas Soome 
330f4769751SToomas Soome 	vd->vdisk_open--;
331f4769751SToomas Soome 	if (dev->dd.d_dev->dv_type == DEVT_DISK)
332f4769751SToomas Soome 		return (disk_close(dev));
333f4769751SToomas Soome 	return (0);
334f4769751SToomas Soome }
335f4769751SToomas Soome 
336f4769751SToomas Soome static int
vdisk_ioctl(struct open_file * f,ulong_t cmd,void * data)337f4769751SToomas Soome vdisk_ioctl(struct open_file *f, ulong_t cmd, void *data)
338f4769751SToomas Soome {
339f4769751SToomas Soome 	struct disk_devdesc *dev;
340f4769751SToomas Soome 	vdisk_info_t *vd;
341f4769751SToomas Soome 	int rc;
342f4769751SToomas Soome 
343f4769751SToomas Soome 	dev = (struct disk_devdesc *)(f->f_devdata);
344f4769751SToomas Soome 	if (dev == NULL)
345f4769751SToomas Soome 		return (EINVAL);
346f4769751SToomas Soome 	vd = vdisk_get_info((struct devdesc *)dev);
347f4769751SToomas Soome 	if (vd == NULL)
348f4769751SToomas Soome 		return (EINVAL);
349f4769751SToomas Soome 
350f4769751SToomas Soome 	if (dev->dd.d_dev->dv_type == DEVT_DISK) {
351f4769751SToomas Soome 		rc = disk_ioctl(dev, cmd, data);
352f4769751SToomas Soome 		if (rc != ENOTTY)
353f4769751SToomas Soome 			return (rc);
354f4769751SToomas Soome 	}
355f4769751SToomas Soome 
356f4769751SToomas Soome 	switch (cmd) {
357f4769751SToomas Soome 	case DIOCGSECTORSIZE:
358f4769751SToomas Soome 		*(uint_t *)data = vd->vdisk_sectorsz;
359f4769751SToomas Soome 		break;
360f4769751SToomas Soome 	case DIOCGMEDIASIZE:
361f4769751SToomas Soome 		*(uint64_t *)data = vd->vdisk_size;
362f4769751SToomas Soome 		break;
363f4769751SToomas Soome 	default:
364f4769751SToomas Soome 		return (ENOTTY);
365f4769751SToomas Soome 	}
366f4769751SToomas Soome 	return (0);
367f4769751SToomas Soome }
368f4769751SToomas Soome 
369f4769751SToomas Soome static int
vdisk_print(int verbose)370f4769751SToomas Soome vdisk_print(int verbose)
371f4769751SToomas Soome {
372f4769751SToomas Soome 	int ret = 0;
373f4769751SToomas Soome 	vdisk_info_t *vd;
374f4769751SToomas Soome 	char line[80];
375f4769751SToomas Soome 
376f4769751SToomas Soome 	if (STAILQ_EMPTY(&vdisk_list))
377f4769751SToomas Soome 		return (ret);
378f4769751SToomas Soome 
379f4769751SToomas Soome 	printf("%s devices:", vdisk_dev.dv_name);
380f4769751SToomas Soome 	if ((ret = pager_output("\n")) != 0)
381f4769751SToomas Soome 		return (ret);
382f4769751SToomas Soome 
383f4769751SToomas Soome 	STAILQ_FOREACH(vd, &vdisk_list, vdisk_link) {
384f4769751SToomas Soome 		struct disk_devdesc vd_dev;
385f4769751SToomas Soome 
386f4769751SToomas Soome 		if (verbose) {
387f4769751SToomas Soome 			printf("  %s", vd->vdisk_path);
388f4769751SToomas Soome 			if ((ret = pager_output("\n")) != 0)
389f4769751SToomas Soome 				break;
390f4769751SToomas Soome 		}
391f4769751SToomas Soome 		snprintf(line, sizeof (line),
392f4769751SToomas Soome 		    "    %s%d", vdisk_dev.dv_name, vd->vdisk_unit);
393f4769751SToomas Soome 		printf("%s:    %" PRIu64 " X %u blocks", line,
394f4769751SToomas Soome 		    vd->vdisk_size / vd->vdisk_sectorsz,
395f4769751SToomas Soome 		    vd->vdisk_sectorsz);
396f4769751SToomas Soome 		if ((ret = pager_output("\n")) != 0)
397f4769751SToomas Soome 			break;
398f4769751SToomas Soome 
399f4769751SToomas Soome 		vd_dev.dd.d_dev = &vdisk_dev;
400f4769751SToomas Soome 		vd_dev.dd.d_unit = vd->vdisk_unit;
401f4769751SToomas Soome 		vd_dev.d_slice = -1;
402f4769751SToomas Soome 		vd_dev.d_partition = -1;
403f4769751SToomas Soome 
404f4769751SToomas Soome 		ret = disk_open(&vd_dev, vd->vdisk_size, vd->vdisk_sectorsz);
405f4769751SToomas Soome 		if (ret == 0) {
406f4769751SToomas Soome 			ret = disk_print(&vd_dev, line, verbose);
407f4769751SToomas Soome 			disk_close(&vd_dev);
408f4769751SToomas Soome 			if (ret != 0)
409f4769751SToomas Soome 				break;
410f4769751SToomas Soome 		} else {
411f4769751SToomas Soome 			ret = 0;
412f4769751SToomas Soome 		}
413f4769751SToomas Soome 	}
414f4769751SToomas Soome 
415f4769751SToomas Soome 	return (ret);
416f4769751SToomas Soome }
417