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,
55 .dv_cleanup = nullsys,
56 .dv_fmtdev = disk_fmtdev,
57 .dv_parsedev = disk_parsedev,
60 typedef STAILQ_HEAD(vdisk_info_list, vdisk_info) vdisk_info_list_t;
62 typedef struct vdisk_info
64 STAILQ_ENTRY(vdisk_info) vdisk_link; /* link in device list */
68 uint64_t vdisk_size; /* size in bytes */
69 uint32_t vdisk_sectorsz;
70 uint32_t vdisk_open; /* reference counter */
73 static vdisk_info_list_t vdisk_list; /* list of mapped vdisks. */
76 vdisk_get_info(struct devdesc *dev)
80 STAILQ_FOREACH(vd, &vdisk_list, vdisk_link) {
81 if (vd->vdisk_unit == dev->d_unit)
87 COMMAND_SET(map_vdisk, "map-vdisk", "map file as virtual disk", command_mapvd);
90 command_mapvd(int argc, char *argv[])
96 printf("usage: %s filename\n", argv[0]);
100 STAILQ_FOREACH(vd, &vdisk_list, vdisk_link) {
101 if (strcmp(vd->vdisk_path, argv[1]) == 0) {
102 printf("%s: file %s is already mapped as %s%d\n",
103 argv[0], argv[1], vdisk_dev.dv_name,
109 if (stat(argv[1], &sb) < 0) {
111 * ENOSYS is really ENOENT because we did try to walk
112 * through devsw list to try to open this file.
117 printf("%s: stat failed: %s\n", argv[0], strerror(errno));
122 * Avoid mapping small files.
124 if (sb.st_size < 1024 * 1024) {
125 printf("%s: file %s is too small.\n", argv[0], argv[1]);
129 vd = calloc(1, sizeof (*vd));
131 printf("%s: out of memory\n", argv[0]);
134 vd->vdisk_path = strdup(argv[1]);
135 if (vd->vdisk_path == NULL) {
137 printf("%s: out of memory\n", argv[0]);
140 vd->vdisk_fd = open(vd->vdisk_path, O_RDONLY);
141 if (vd->vdisk_fd < 0) {
142 printf("%s: open failed: %s\n", argv[0], strerror(errno));
143 free(vd->vdisk_path);
148 vd->vdisk_size = sb.st_size;
149 vd->vdisk_sectorsz = DEV_BSIZE;
150 STAILQ_FOREACH(p, &vdisk_list, vdisk_link) {
152 if (p->vdisk_unit == vd->vdisk_unit) {
156 n = STAILQ_NEXT(p, vdisk_link);
157 if (p->vdisk_unit < vd->vdisk_unit) {
160 STAILQ_INSERT_TAIL(&vdisk_list, vd, vdisk_link);
163 if (n->vdisk_unit > vd->vdisk_unit) {
165 STAILQ_INSERT_AFTER(&vdisk_list, p, vd,
169 /* else n < vd or n == vd */
173 /* p > vd only if p is the first element */
174 STAILQ_INSERT_HEAD(&vdisk_list, vd, vdisk_link);
178 /* if the list was empty or contiguous */
180 STAILQ_INSERT_TAIL(&vdisk_list, vd, vdisk_link);
182 printf("%s: file %s is mapped as %s%d\n", argv[0], vd->vdisk_path,
183 vdisk_dev.dv_name, vd->vdisk_unit);
187 COMMAND_SET(unmap_vdisk, "unmap-vdisk", "unmap virtual disk", command_unmapvd);
193 command_unmapvd(int argc, char *argv[])
201 printf("usage: %s %sN\n", argv[0], vdisk_dev.dv_name);
205 len = strlen(vdisk_dev.dv_name);
206 if (strncmp(vdisk_dev.dv_name, argv[1], len) != 0) {
207 printf("%s: unknown device %s\n", argv[0], argv[1]);
211 unit = strtol(argv[1] + len, &end, 10);
212 if (errno != 0 || (*end != '\0' && strcmp(end, ":") != 0)) {
213 printf("%s: unknown device %s\n", argv[0], argv[1]);
217 STAILQ_FOREACH(vd, &vdisk_list, vdisk_link) {
218 if (vd->vdisk_unit == unit)
223 printf("%s: unknown device %s\n", argv[0], argv[1]);
227 if (vd->vdisk_open != 0) {
228 printf("%s: %s is in use, unable to unmap.\n",
233 STAILQ_REMOVE(&vdisk_list, vd, vdisk_info, vdisk_link);
234 (void) close(vd->vdisk_fd);
235 printf("%s (%s) unmapped\n", argv[1], vd->vdisk_path);
236 free(vd->vdisk_path);
245 STAILQ_INIT(&vdisk_list);
250 vdisk_strategy(void *devdata, int rw, daddr_t blk, size_t size,
251 char *buf, size_t *rsize)
253 struct disk_devdesc *dev;
260 vd = vdisk_get_info((struct devdesc *)dev);
264 if (size == 0 || (size % 512) != 0)
267 if (dev->dd.d_dev->dv_type == DEVT_DISK) {
270 offset = dev->d_offset * vd->vdisk_sectorsz;
274 if (lseek(vd->vdisk_fd, blk << 9, SEEK_SET) == -1)
278 switch (rw & F_MASK) {
280 rv = read(vd->vdisk_fd, buf, size);
283 rv = write(vd->vdisk_fd, buf, size);
289 if (errno == 0 && rsize != NULL) {
296 vdisk_open(struct open_file *f, ...)
299 struct disk_devdesc *dev;
304 dev = va_arg(args, struct disk_devdesc *);
308 vd = vdisk_get_info((struct devdesc *)dev);
312 if (dev->dd.d_dev->dv_type == DEVT_DISK) {
313 rc = disk_open(dev, vd->vdisk_size, vd->vdisk_sectorsz);
321 vdisk_close(struct open_file *f)
323 struct disk_devdesc *dev;
326 dev = (struct disk_devdesc *)(f->f_devdata);
329 vd = vdisk_get_info((struct devdesc *)dev);
334 if (dev->dd.d_dev->dv_type == DEVT_DISK)
335 return (disk_close(dev));
340 vdisk_ioctl(struct open_file *f, u_long cmd, void *data)
342 struct disk_devdesc *dev;
346 dev = (struct disk_devdesc *)(f->f_devdata);
349 vd = vdisk_get_info((struct devdesc *)dev);
353 if (dev->dd.d_dev->dv_type == DEVT_DISK) {
354 rc = disk_ioctl(dev, cmd, data);
360 case DIOCGSECTORSIZE:
361 *(u_int *)data = vd->vdisk_sectorsz;
364 *(uint64_t *)data = vd->vdisk_size;
373 vdisk_print(int verbose)
379 if (STAILQ_EMPTY(&vdisk_list))
382 printf("%s devices:", vdisk_dev.dv_name);
383 if ((ret = pager_output("\n")) != 0)
386 STAILQ_FOREACH(vd, &vdisk_list, vdisk_link) {
387 struct disk_devdesc vd_dev;
390 printf(" %s", vd->vdisk_path);
391 if ((ret = pager_output("\n")) != 0)
394 snprintf(line, sizeof(line),
395 " %s%d", vdisk_dev.dv_name, vd->vdisk_unit);
396 printf("%s: %" PRIu64 " X %u blocks", line,
397 vd->vdisk_size / vd->vdisk_sectorsz,
399 if ((ret = pager_output("\n")) != 0)
402 vd_dev.dd.d_dev = &vdisk_dev;
403 vd_dev.dd.d_unit = vd->vdisk_unit;
405 vd_dev.d_partition = -1;
407 ret = disk_open(&vd_dev, vd->vdisk_size, vd->vdisk_sectorsz);
409 ret = disk_print(&vd_dev, line, verbose);