]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - stand/common/vdisk.c
bhyvectl(8): Normalize the man page date
[FreeBSD/FreeBSD.git] / stand / common / vdisk.c
1 /*-
2  * Copyright 2019 Toomas Soome <tsoome@me.com>
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
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.
12  *
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
23  * SUCH DAMAGE.
24  */
25
26 #include <sys/cdefs.h>
27 __FBSDID("$FreeBSD$");
28
29 #include <stand.h>
30 #include <stdarg.h>
31 #include <machine/_inttypes.h>
32 #include <bootstrap.h>
33 #include <sys/disk.h>
34 #include <sys/errno.h>
35 #include <sys/queue.h>
36 #include <sys/param.h>
37 #include <disk.h>
38
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);
45
46 struct devsw vdisk_dev = {
47         .dv_name = "vdisk",
48         .dv_type = DEVT_DISK,
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 = NULL
56 };
57
58 typedef STAILQ_HEAD(vdisk_info_list, vdisk_info) vdisk_info_list_t;
59
60 typedef struct vdisk_info
61 {
62         STAILQ_ENTRY(vdisk_info)        vdisk_link; /* link in device list */
63         char                    *vdisk_path;
64         int                     vdisk_unit;
65         int                     vdisk_fd;
66         uint64_t                vdisk_size;     /* size in bytes */
67         uint32_t                vdisk_sectorsz;
68         uint32_t                vdisk_open;     /* reference counter */
69 } vdisk_info_t;
70
71 static vdisk_info_list_t vdisk_list;    /* list of mapped vdisks. */
72
73 static vdisk_info_t *
74 vdisk_get_info(struct devdesc *dev)
75 {
76         vdisk_info_t *vd;
77
78         STAILQ_FOREACH(vd, &vdisk_list, vdisk_link) {
79                 if (vd->vdisk_unit == dev->d_unit)
80                         return (vd);
81         }
82         return (vd);
83 }
84
85 COMMAND_SET(map_vdisk, "map-vdisk", "map file as virtual disk", command_mapvd);
86
87 static int
88 command_mapvd(int argc, char *argv[])
89 {
90         vdisk_info_t *vd, *p;
91         struct stat sb;
92
93         if (argc != 2) {
94                 printf("usage: %s filename\n", argv[0]);
95                 return (CMD_ERROR);
96         }
97
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,
102                             vd->vdisk_unit);
103                         return (CMD_ERROR);
104                 }
105         }
106
107         if (stat(argv[1], &sb) < 0) {
108                 /*
109                  * ENOSYS is really ENOENT because we did try to walk
110                  * through devsw list to try to open this file.
111                  */
112                 if (errno == ENOSYS)
113                         errno = ENOENT;
114
115                 printf("%s: stat failed: %s\n", argv[0], strerror(errno));
116                 return (CMD_ERROR);
117         }
118
119         /*
120          * Avoid mapping small files.
121          */
122         if (sb.st_size < 1024 * 1024) {
123                 printf("%s: file %s is too small.\n", argv[0], argv[1]);
124                 return (CMD_ERROR);
125         }
126
127         vd = calloc(1, sizeof (*vd));
128         if (vd == NULL) {
129                 printf("%s: out of memory\n", argv[0]);
130                 return (CMD_ERROR);
131         }
132         vd->vdisk_path = strdup(argv[1]);
133         if (vd->vdisk_path == NULL) {
134                 free (vd);
135                 printf("%s: out of memory\n", argv[0]);
136                 return (CMD_ERROR);
137         }
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);
142                 free(vd);
143                 return (CMD_ERROR);
144         }
145
146         vd->vdisk_size = sb.st_size;
147         vd->vdisk_sectorsz = DEV_BSIZE;
148         STAILQ_FOREACH(p, &vdisk_list, vdisk_link) {
149                 vdisk_info_t *n;
150                 if (p->vdisk_unit == vd->vdisk_unit) {
151                         vd->vdisk_unit++;
152                         continue;
153                 }
154                 n = STAILQ_NEXT(p, vdisk_link);
155                 if (p->vdisk_unit < vd->vdisk_unit) {
156                         if (n == NULL) {
157                                 /* p is last elem */
158                                 STAILQ_INSERT_TAIL(&vdisk_list, vd, vdisk_link);
159                                 break;
160                         }
161                         if (n->vdisk_unit > vd->vdisk_unit) {
162                                 /* p < vd < n */
163                                 STAILQ_INSERT_AFTER(&vdisk_list, p, vd,
164                                     vdisk_link);
165                                 break;
166                         }
167                         /* else n < vd or n == vd */
168                         vd->vdisk_unit++;
169                         continue;
170                 }
171                 /* p > vd only if p is the first element */
172                 STAILQ_INSERT_HEAD(&vdisk_list, vd, vdisk_link);
173                 break;
174         }
175
176         /* if the list was empty or contiguous */
177         if (p == NULL)
178                 STAILQ_INSERT_TAIL(&vdisk_list, vd, vdisk_link);
179
180         printf("%s: file %s is mapped as %s%d\n", argv[0], vd->vdisk_path,
181             vdisk_dev.dv_name, vd->vdisk_unit);
182         return (CMD_OK);
183 }
184
185 COMMAND_SET(unmap_vdisk, "unmap-vdisk", "unmap virtual disk", command_unmapvd);
186
187 /*
188  * unmap-vdisk vdiskX
189  */
190 static int
191 command_unmapvd(int argc, char *argv[])
192 {
193         size_t len;
194         vdisk_info_t *vd;
195         long unit;
196         char *end;
197
198         if (argc != 2) {
199                 printf("usage: %s %sN\n", argv[0], vdisk_dev.dv_name);
200                 return (CMD_ERROR);
201         }
202
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]);
206                 return (CMD_ERROR);
207         }
208         errno = 0;
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]);
212                 return (CMD_ERROR);
213         }
214
215         STAILQ_FOREACH(vd, &vdisk_list, vdisk_link) {
216                 if (vd->vdisk_unit == unit)
217                         break;
218         }
219
220         if (vd == NULL) {
221                 printf("%s: unknown device %s\n", argv[0], argv[1]);
222                 return (CMD_ERROR);
223         }
224
225         if (vd->vdisk_open != 0) {
226                 printf("%s: %s is in use, unable to unmap.\n",
227                     argv[0], argv[1]);
228                 return (CMD_ERROR);
229         }
230
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);
235         free(vd);
236
237         return (CMD_OK);
238 }
239
240 static int
241 vdisk_init(void)
242 {
243         STAILQ_INIT(&vdisk_list);
244         return (0);
245 }
246
247 static int
248 vdisk_strategy(void *devdata, int rw, daddr_t blk, size_t size,
249     char *buf, size_t *rsize)
250 {
251         struct disk_devdesc *dev;
252         vdisk_info_t *vd;
253         ssize_t rv;
254
255         dev = devdata;
256         if (dev == NULL)
257                 return (EINVAL);
258         vd = vdisk_get_info((struct devdesc *)dev);
259         if (vd == NULL)
260                 return (EINVAL);
261
262         if (size == 0 || (size % 512) != 0)
263                 return (EIO);
264
265         if (dev->dd.d_dev->dv_type == DEVT_DISK) {
266                 daddr_t offset;
267
268                 offset = dev->d_offset * vd->vdisk_sectorsz;
269                 offset /= 512;
270                 blk += offset;
271         }
272         if (lseek(vd->vdisk_fd, blk << 9, SEEK_SET) == -1)
273                 return (EIO);
274
275         errno = 0;
276         switch (rw & F_MASK) {
277         case F_READ:
278                 rv = read(vd->vdisk_fd, buf, size);
279                 break;
280         case F_WRITE:
281                 rv = write(vd->vdisk_fd, buf, size);
282                 break;
283         default:
284                 return (ENOSYS);
285         }
286
287         if (errno == 0 && rsize != NULL) {
288                 *rsize = rv;
289         }
290         return (errno);
291 }
292
293 static int
294 vdisk_open(struct open_file *f, ...)
295 {
296         va_list args;
297         struct disk_devdesc *dev;
298         vdisk_info_t *vd;
299         int rc = 0;
300
301         va_start(args, f);
302         dev = va_arg(args, struct disk_devdesc *);
303         va_end(args);
304         if (dev == NULL)
305                 return (EINVAL);
306         vd = vdisk_get_info((struct devdesc *)dev);
307         if (vd == NULL)
308                 return (EINVAL);
309
310         if (dev->dd.d_dev->dv_type == DEVT_DISK) {
311                 rc = disk_open(dev, vd->vdisk_size, vd->vdisk_sectorsz);
312         }
313         if (rc == 0)
314                 vd->vdisk_open++;
315         return (rc);
316 }
317
318 static int
319 vdisk_close(struct open_file *f)
320 {
321         struct disk_devdesc *dev;
322         vdisk_info_t *vd;
323
324         dev = (struct disk_devdesc *)(f->f_devdata);
325         if (dev == NULL)
326                 return (EINVAL);
327         vd = vdisk_get_info((struct devdesc *)dev);
328         if (vd == NULL)
329                 return (EINVAL);
330
331         vd->vdisk_open--;
332         if (dev->dd.d_dev->dv_type == DEVT_DISK)
333                 return (disk_close(dev));
334         return (0);
335 }
336
337 static int
338 vdisk_ioctl(struct open_file *f, u_long cmd, void *data)
339 {
340         struct disk_devdesc *dev;
341         vdisk_info_t *vd;
342         int rc;
343
344         dev = (struct disk_devdesc *)(f->f_devdata);
345         if (dev == NULL)
346                 return (EINVAL);
347         vd = vdisk_get_info((struct devdesc *)dev);
348         if (vd == NULL)
349                 return (EINVAL);
350
351         if (dev->dd.d_dev->dv_type == DEVT_DISK) {
352                 rc = disk_ioctl(dev, cmd, data);
353                 if (rc != ENOTTY)
354                         return (rc);
355         }
356
357         switch (cmd) {
358         case DIOCGSECTORSIZE:
359                 *(u_int *)data = vd->vdisk_sectorsz;
360                 break;
361         case DIOCGMEDIASIZE:
362                 *(uint64_t *)data = vd->vdisk_size;
363                 break;
364         default:
365                 return (ENOTTY);
366         }
367         return (0);
368 }
369
370 static int
371 vdisk_print(int verbose)
372 {
373         int ret = 0;
374         vdisk_info_t *vd;
375         char line[80];
376
377         if (STAILQ_EMPTY(&vdisk_list))
378                 return (ret);
379
380         printf("%s devices:", vdisk_dev.dv_name);
381         if ((ret = pager_output("\n")) != 0)
382                 return (ret);
383
384         STAILQ_FOREACH(vd, &vdisk_list, vdisk_link) {
385                 struct disk_devdesc vd_dev;
386
387                 if (verbose) {
388                         printf("  %s", vd->vdisk_path);
389                         if ((ret = pager_output("\n")) != 0)
390                                 break;
391                 }
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,
396                     vd->vdisk_sectorsz);
397                 if ((ret = pager_output("\n")) != 0)
398                         break;
399
400                 vd_dev.dd.d_dev = &vdisk_dev;
401                 vd_dev.dd.d_unit = vd->vdisk_unit;
402                 vd_dev.d_slice = -1;
403                 vd_dev.d_partition = -1;
404
405                 ret = disk_open(&vd_dev, vd->vdisk_size, vd->vdisk_sectorsz);
406                 if (ret == 0) {
407                         ret = disk_print(&vd_dev, line, verbose);
408                         disk_close(&vd_dev);
409                         if (ret != 0)
410                                 break;
411                 } else {
412                         ret = 0;
413                 }
414         }
415
416         return (ret);
417 }