2 * Copyright 2019 Toomas Soome <tsoome@me.com>
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
13 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
14 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
17 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
19 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
20 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
21 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 #include <sys/cdefs.h>
27 __FBSDID("$FreeBSD$");
31 #include <machine/_inttypes.h>
32 #include <bootstrap.h>
34 #include <sys/errno.h>
35 #include <sys/queue.h>
36 #include <sys/param.h>
39 static int vdisk_init(void);
40 static int vdisk_strategy(void *, int, daddr_t, size_t, char *, size_t *);
41 static int vdisk_open(struct open_file *, ...);
42 static int vdisk_close(struct open_file *);
43 static int vdisk_ioctl(struct open_file *, u_long, void *);
44 static int vdisk_print(int);
46 struct devsw vdisk_dev = {
49 .dv_init = vdisk_init,
50 .dv_strategy = vdisk_strategy,
51 .dv_open = vdisk_open,
52 .dv_close = vdisk_close,
53 .dv_ioctl = vdisk_ioctl,
54 .dv_print = vdisk_print,
58 typedef STAILQ_HEAD(vdisk_info_list, vdisk_info) vdisk_info_list_t;
60 typedef struct vdisk_info
62 STAILQ_ENTRY(vdisk_info) vdisk_link; /* link in device list */
66 uint64_t vdisk_size; /* size in bytes */
67 uint32_t vdisk_sectorsz;
68 uint32_t vdisk_open; /* reference counter */
71 static vdisk_info_list_t vdisk_list; /* list of mapped vdisks. */
74 vdisk_get_info(struct devdesc *dev)
78 STAILQ_FOREACH(vd, &vdisk_list, vdisk_link) {
79 if (vd->vdisk_unit == dev->d_unit)
85 COMMAND_SET(map_vdisk, "map-vdisk", "map file as virtual disk", command_mapvd);
88 command_mapvd(int argc, char *argv[])
94 printf("usage: %s filename\n", argv[0]);
98 STAILQ_FOREACH(vd, &vdisk_list, vdisk_link) {
99 if (strcmp(vd->vdisk_path, argv[1]) == 0) {
100 printf("%s: file %s is already mapped as %s%d\n",
101 argv[0], argv[1], vdisk_dev.dv_name,
107 if (stat(argv[1], &sb) < 0) {
109 * ENOSYS is really ENOENT because we did try to walk
110 * through devsw list to try to open this file.
115 printf("%s: stat failed: %s\n", argv[0], strerror(errno));
120 * Avoid mapping small files.
122 if (sb.st_size < 1024 * 1024) {
123 printf("%s: file %s is too small.\n", argv[0], argv[1]);
127 vd = calloc(1, sizeof (*vd));
129 printf("%s: out of memory\n", argv[0]);
132 vd->vdisk_path = strdup(argv[1]);
133 if (vd->vdisk_path == NULL) {
135 printf("%s: out of memory\n", argv[0]);
138 vd->vdisk_fd = open(vd->vdisk_path, O_RDONLY);
139 if (vd->vdisk_fd < 0) {
140 printf("%s: open failed: %s\n", argv[0], strerror(errno));
141 free(vd->vdisk_path);
146 vd->vdisk_size = sb.st_size;
147 vd->vdisk_sectorsz = DEV_BSIZE;
148 STAILQ_FOREACH(p, &vdisk_list, vdisk_link) {
150 if (p->vdisk_unit == vd->vdisk_unit) {
154 n = STAILQ_NEXT(p, vdisk_link);
155 if (p->vdisk_unit < vd->vdisk_unit) {
158 STAILQ_INSERT_TAIL(&vdisk_list, vd, vdisk_link);
161 if (n->vdisk_unit > vd->vdisk_unit) {
163 STAILQ_INSERT_AFTER(&vdisk_list, p, vd,
167 /* else n < vd or n == vd */
171 /* p > vd only if p is the first element */
172 STAILQ_INSERT_HEAD(&vdisk_list, vd, vdisk_link);
176 /* if the list was empty or contiguous */
178 STAILQ_INSERT_TAIL(&vdisk_list, vd, vdisk_link);
180 printf("%s: file %s is mapped as %s%d\n", argv[0], vd->vdisk_path,
181 vdisk_dev.dv_name, vd->vdisk_unit);
185 COMMAND_SET(unmap_vdisk, "unmap-vdisk", "unmap virtual disk", command_unmapvd);
191 command_unmapvd(int argc, char *argv[])
199 printf("usage: %s %sN\n", argv[0], vdisk_dev.dv_name);
203 len = strlen(vdisk_dev.dv_name);
204 if (strncmp(vdisk_dev.dv_name, argv[1], len) != 0) {
205 printf("%s: unknown device %s\n", argv[0], argv[1]);
209 unit = strtol(argv[1] + len, &end, 10);
210 if (errno != 0 || (*end != '\0' && strcmp(end, ":") != 0)) {
211 printf("%s: unknown device %s\n", argv[0], argv[1]);
215 STAILQ_FOREACH(vd, &vdisk_list, vdisk_link) {
216 if (vd->vdisk_unit == unit)
221 printf("%s: unknown device %s\n", argv[0], argv[1]);
225 if (vd->vdisk_open != 0) {
226 printf("%s: %s is in use, unable to unmap.\n",
231 STAILQ_REMOVE(&vdisk_list, vd, vdisk_info, vdisk_link);
232 (void) close(vd->vdisk_fd);
233 printf("%s (%s) unmapped\n", argv[1], vd->vdisk_path);
234 free(vd->vdisk_path);
243 STAILQ_INIT(&vdisk_list);
248 vdisk_strategy(void *devdata, int rw, daddr_t blk, size_t size,
249 char *buf, size_t *rsize)
251 struct disk_devdesc *dev;
258 vd = vdisk_get_info((struct devdesc *)dev);
262 if (size == 0 || (size % 512) != 0)
265 if (dev->dd.d_dev->dv_type == DEVT_DISK) {
268 offset = dev->d_offset * vd->vdisk_sectorsz;
272 if (lseek(vd->vdisk_fd, blk << 9, SEEK_SET) == -1)
276 switch (rw & F_MASK) {
278 rv = read(vd->vdisk_fd, buf, size);
281 rv = write(vd->vdisk_fd, buf, size);
287 if (errno == 0 && rsize != NULL) {
294 vdisk_open(struct open_file *f, ...)
297 struct disk_devdesc *dev;
302 dev = va_arg(args, struct disk_devdesc *);
306 vd = vdisk_get_info((struct devdesc *)dev);
310 if (dev->dd.d_dev->dv_type == DEVT_DISK) {
311 rc = disk_open(dev, vd->vdisk_size, vd->vdisk_sectorsz);
319 vdisk_close(struct open_file *f)
321 struct disk_devdesc *dev;
324 dev = (struct disk_devdesc *)(f->f_devdata);
327 vd = vdisk_get_info((struct devdesc *)dev);
332 if (dev->dd.d_dev->dv_type == DEVT_DISK)
333 return (disk_close(dev));
338 vdisk_ioctl(struct open_file *f, u_long cmd, void *data)
340 struct disk_devdesc *dev;
344 dev = (struct disk_devdesc *)(f->f_devdata);
347 vd = vdisk_get_info((struct devdesc *)dev);
351 if (dev->dd.d_dev->dv_type == DEVT_DISK) {
352 rc = disk_ioctl(dev, cmd, data);
358 case DIOCGSECTORSIZE:
359 *(u_int *)data = vd->vdisk_sectorsz;
362 *(uint64_t *)data = vd->vdisk_size;
371 vdisk_print(int verbose)
377 if (STAILQ_EMPTY(&vdisk_list))
380 printf("%s devices:", vdisk_dev.dv_name);
381 if ((ret = pager_output("\n")) != 0)
384 STAILQ_FOREACH(vd, &vdisk_list, vdisk_link) {
385 struct disk_devdesc vd_dev;
388 printf(" %s", vd->vdisk_path);
389 if ((ret = pager_output("\n")) != 0)
392 snprintf(line, sizeof(line),
393 " %s%d", vdisk_dev.dv_name, vd->vdisk_unit);
394 printf("%s: %" PRIu64 " X %u blocks", line,
395 vd->vdisk_size / vd->vdisk_sectorsz,
397 if ((ret = pager_output("\n")) != 0)
400 vd_dev.dd.d_dev = &vdisk_dev;
401 vd_dev.dd.d_unit = vd->vdisk_unit;
403 vd_dev.d_partition = -1;
405 ret = disk_open(&vd_dev, vd->vdisk_size, vd->vdisk_sectorsz);
407 ret = disk_print(&vd_dev, line, verbose);