]> Zhao Yanbai Git Server - minix.git/commitdiff
Add VND driver, providing loopback devices 88/988/2
authorDavid van Moolenbroek <david@minix3.org>
Wed, 18 Sep 2013 12:02:17 +0000 (14:02 +0200)
committerLionel Sambuc <lionel@minix3.org>
Sat, 1 Mar 2014 08:04:54 +0000 (09:04 +0100)
Change-Id: I40fa695e28c67477a75383e6f1550e451afcab41

15 files changed:
commands/DESCRIBE/DESCRIBE.sh
commands/MAKEDEV/MAKEDEV.sh
distrib/sets/lists/minix/mi
drivers/Makefile
drivers/vnd/Makefile [new file with mode: 0644]
drivers/vnd/NOTES [new file with mode: 0644]
drivers/vnd/vnd.c [new file with mode: 0644]
etc/system.conf
include/minix/dmap.h
include/minix/ioctl.h
lib/libminc/Makefile
releasetools/nbsd_ports
sys/dev/Makefile
sys/dev/vndvar.h [new file with mode: 0644]
sys/sys/ioctl.h

index 63560ae368e30a665f5af944bdc3ddc5b37460b6..b028e76671e1ccbb361d34baf8b440324ffd320a 100644 (file)
@@ -196,6 +196,22 @@ do
     18,0)
        des="UNIX domain socket" dev=uds
        ;;
+    5[6-9],0|6[0-3],0)
+       drive=`expr $major - 56`
+       des="vnode disk $drive" dev=vnd$drive
+       ;;
+    5[6-9],[1-4]|6[0-3],[1-4])
+       drive=`expr $major - 56`
+       par=`expr $minor - 1`
+       des="vnode disk $drive partition $par" dev=vnd${drive}p${par}
+       ;;
+    5[6-9],12[89]|5[6-9],13[0-9]|5[6-9],14[0-3]|6[0-3],12[89]|5[6-9],13[0-9]|5[6-9],14[0-3])
+       drive=`expr $major - 56`
+       par=`expr \\( \\( $minor - 128 \\) / 4 \\) % 4`
+       sub=`expr \\( $minor - 128 \\) % 4`
+       des="vnode disk $drive partition $par slice $sub"
+       dev=vnd${drive}p${par}s${sub}
+       ;;
     BAD,BAD)
        des= dev=
        ;;
index 9a4c1a9cd0e0ea9a70a79756a3807774a2bd387b..83e2270a84db78861d697c615e53fda2bd53ceb7 100644 (file)
@@ -33,7 +33,9 @@ case $#:$1 in
        eepromb3s54 eepromb3s55 eepromb3s56 eepromb3s57 \
        tsl2550b1s39 tsl2550b2s39 tsl2550b3s39 \
        sht21b1s40 sht21b2s40 sht21b3s40 \
-       bmp085b1s77 bmp085b2s77 bmp085b3s77
+       bmp085b1s77 bmp085b2s77 bmp085b3s77 \
+       vnd0 vnd0p0 vnd0p0s0 vnd1 vnd1p0 vnd1p0s0 \
+       vnd2 vnd3 vnd4 vnd5 vnd6 vnd7
     ;;
 0:|1:-\?)
     cat >&2 <<EOF
@@ -67,6 +69,7 @@ Where key is one of the following:
   fbd                     # Make /dev/fbd
   hello                   # Make /dev/hello
   video                   # Make /dev/video
+  vnd0 vnd0p0 vnd0p0s0 .. # Make vnode disks /dev/vnd[0-7] and (sub)partitions
   std                    # All standard devices
 EOF
     exit 1
@@ -331,6 +334,46 @@ do
        $e mknod bmp085b${b}s77 c ${m} 0
        $e chmod 444 bmp085b${b}s77
        ;;
+    vnd[0-7])
+       # Whole vnode disk devices.
+       d=`expr $dev : 'vnd\\(.\\)'`    # Disk number.
+       maj=`expr $d + 56`              # Major device number.
+       $e mknod $dev b $maj 0
+       $e chmod 600 $dev
+       ;;
+    vnd[0-7]p[0-3])
+       # Vnode disk primary partitions.
+       n=`expr $dev : '\\(.*\\).'`     # Name prefix.
+       d=`expr $dev : 'vnd\\(.\\)'`    # Disk number.
+       maj=`expr $d + 56`              # Major device number.
+       alldev=
+
+       for p in 0 1 2 3
+       do
+           m=`expr 1 + $p`             # Minor device number.
+           $e mknod $n$p b $maj $m
+           alldev="$alldev $n$p"
+       done
+       echo $alldev | xargs $e chmod 600
+       ;;
+    vnd[0-7]p[0-3]s[0-3])
+       # Vnode disk subpartition.
+       n=`expr $dev : '\\(.*\\)...'`   # Name prefix.
+       d=`expr $dev : 'vnd\\(.\\)'`    # Disk number.
+       maj=`expr $d + 56`              # Major device number.
+       alldev=
+
+       for p in 0 1 2 3
+       do
+           for s in 0 1 2 3
+           do
+               m=`expr 128 + $p '*' 4 + $s`  # Minor device number.
+               $e mknod ${n}${p}s${s} b $maj $m
+               alldev="$alldev ${n}${p}s${s}"
+           done
+       done
+       echo $alldev | xargs $e chmod 600
+       ;;
     *)
        echo "$0: don't know about $dev" >&2
        ex=1
index 17b3d94f11a508d434c490775622bd0166f1b780..ded48abb50d7eca7715969f9ae61b00e57ab39b2 100644 (file)
 ./usr/include/dev                      minix-sys
 ./usr/include/dev/i2c                  minix-sys
 ./usr/include/dev/i2c/i2c_io.h         minix-sys
+./usr/include/dev/vndvar.h             minix-sys
 ./usr/include/dirent.h                 minix-sys
 ./usr/include/disktab.h                        minix-sys
 ./usr/include/dlfcn.h                  minix-sys
 ./usr/sbin/vfs                         minix-sys
 ./usr/sbin/vipw                                minix-sys
 ./usr/sbin/vm                          minix-sys
+./usr/sbin/vnd                         minix-sys
 ./usr/sbin/zic                         minix-sys
 ./usr/share                            minix-sys
 ./usr/share/atf                                minix-sys       atf
index 907ad6d9d741cc895a2f9eb8631d83f89b2a6c4a..dd7ed24d41ec5c97838cdc0bbb19475ac8e94643 100644 (file)
@@ -19,12 +19,12 @@ SUBDIR= log tty
 SUBDIR= ahci amddev atl2 at_wini audio dec21140A dp8390 dpeth \
        e1000 fbd filter floppy fxp hello lance log mmc orinoco pci printer \
        random readclock rtl8139 rtl8169 ti1225 tty vbox acpi \
-       virtio_blk virtio_net
+       virtio_blk virtio_net vnd
 .endif
 
 .if ${MACHINE_ARCH} == "earm"
 SUBDIR= bmp085 cat24c256 fb gpio i2c mmc lan8710a log readclock \
-       sht21 tda19988 tps65217 tps65950 tsl2550 tty random
+       sht21 tda19988 tps65217 tps65950 tsl2550 tty random vnd
 .endif
 
 .endif # ${MKIMAGEONLY} != "yes"
diff --git a/drivers/vnd/Makefile b/drivers/vnd/Makefile
new file mode 100644 (file)
index 0000000..493430b
--- /dev/null
@@ -0,0 +1,12 @@
+# Makefile for the VNode Disk driver (VND)
+PROG=  vnd
+SRCS=  vnd.c
+
+DPADD+=        ${LIBBLOCKDRIVER} ${LIBSYS}
+LDADD+=        -lblockdriver -lsys
+
+MAN=
+
+BINDIR?= /usr/sbin
+
+.include <minix.service.mk>
diff --git a/drivers/vnd/NOTES b/drivers/vnd/NOTES
new file mode 100644 (file)
index 0000000..382ead6
--- /dev/null
@@ -0,0 +1,85 @@
+Development notes regarding VND. Original document by David van Moolenbroek.
+
+
+DESIGN DECISIONS
+
+As simple as the VND driver implementation looks, several important decisions
+had to be made in the design process. These decisions are listed here.
+
+Multiple instances instead of a single instance: The decision to spawn a
+separate driver instance for each VND unit was not ideologically inspired, but
+rather based on a practical issue. Namely, users may reasonably expect to be
+able to set up a VND using a backing file that resides on a file system hosted
+on another VND. If one single driver instance were to host both VND units, its
+implementation would have to perform all its backcalls to VFS asynchronously,
+so as to be able to process another incoming request that was initiated as part
+of such an ongoing backcall. As of writing, MINIX3 does not support any form of
+asynchronous I/O, but this would not even be sufficient: the asynchrony would
+have to extend even to the close(2) call that takes place during device
+unconfiguration, as this call could spark I/O to another VND device.
+Ultimately, using one driver instance per VND unit avoids these complications
+altogether, thus making nesting possible with a maximum depth of the number of
+VFS threads. Of course, this comes at the cost of having more VND driver
+processes; in order to avoid this cost in the common case, driver instances are
+dynamically started and stopped by vndconfig(8).
+
+dupfrom(2) instead of openas(2): Compared to the NetBSD interface, the MINIX3
+VND API requires that the user program configuring a device pass in a file
+descriptor in the vnd_ioctl structure instead of a pointer to a path name.
+While binary compatibility with NetBSD would be impossible anyway (MINIX3 can
+not support pointers in IOCTL data structures), providing a path name buffer
+would be closer to what NetBSD does. There are two reasons behind the choice to
+pass in a file descriptor instead. First, performing an open(2)-like call as
+a driver backcall is tricky in terms of avoiding deadlocks in VFS, since it
+would by nature violate the VFS locking order. On top of that, special
+provisions would have to be added to support opening a file in the context of
+another process so that chrooted processes would be supported, for example.
+In contrast, copying a file descriptor to a remote process is relatively easy
+because there is only one potential deadlock case to cover - that of the given
+file descriptor identifying the VFS filp object used to control the very same
+device - and VFS need only implement a procedure that very much resembles
+sending a file descriptor across a UNIX domain socket. Second, since passing a
+file descriptor is effectively passing an object capability, it is easier to
+improve the isolation of the VND drivers in the future, as described below.
+
+No separate control device: The driver uses the same minor (block) device for
+configuration and for actual (whole-disk) I/O, instead of exposing a separate
+device that exists only for the purpose of configuring the device. The reason
+for this is that such a control device simply does not fit the NetBSD
+opendisk(3) API. While MINIX3 may at some point implement support for NetBSD's
+notion of raw devices, such raw devices are still expected to support I/O, and
+that means they cannot be control-only. In this regard, it should be mentioned
+that the entire VND infrastructure relies on block caches being invalidated
+properly upon (un)configuration of VND units, and that such invalidation
+(through the REQ_FLUSH file system request) is currently initiated only by
+closing block devices. Support for configuration or I/O through character
+devices would thus require more work on that side first. In any case, the
+primary downside of not having a separate control device is that handling
+access permissions on device open is a bit of a hack in order to keep the
+MINIX3 userland happy.
+
+
+FUTURE IMPROVEMENTS
+
+Currently, the VND driver instances are run as root just and only because the
+dupfrom(2) call requires root. Obviously, nonroot user processes should never
+be able to copy file descriptors from arbitrary processes, and thus, some
+security check is required there. However, an access control list for VFS calls
+would be a much better solution: in that case, VND driver processes can be
+given exclusive rights to the use of the dupfrom(2) call, while they can be
+given a normal driver UID at the same time.
+
+In MINIX3's dependability model, drivers are generally not considered to be
+malicious. However, the VND case is interesting because it is possible to
+isolate individual driver instances to the point of actual "least authority".
+The dupfrom(2) call currently allows any file descriptor to be copied, but it
+would be possible to extend the scheme to let user processes (and vndconfig(8)
+in particular) mark the file descriptors that may be the target of a dupfrom(2)
+call. One of several schemes may be implemented in VFS for this purpose. For
+example, each process could be allowed to mark one of its file descriptors as
+"copyable" using a new VFS call, and VFS would then allow dupfrom(2) only on a
+"copyable" file descriptor from a process blocked on a call to the driver that
+invoked dupfrom(2). This approach precludes hiding a VND driver behind a RAID
+or FBD (etc) driver, but more sophisticated approaches can solve that as well.
+Regardless of the scheme, the end result would be a situation where the VND
+drivers are strictly limited to operating on the resources given to them.
diff --git a/drivers/vnd/vnd.c b/drivers/vnd/vnd.c
new file mode 100644 (file)
index 0000000..422ce00
--- /dev/null
@@ -0,0 +1,601 @@
+/* VNode Disk driver, by D.C. van Moolenbroek <david@minix3.org> */
+
+#include <minix/drivers.h>
+#include <minix/blockdriver.h>
+#include <minix/drvlib.h>
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <assert.h>
+
+#define VND_BUF_SIZE   65536
+
+static struct {
+       int fd;                 /* file descriptor for the underlying file */
+       int openct;             /* number of times the device is open */
+       int exiting;            /* exit after the last close? */
+       int rdonly;             /* is the device set up read-only? */
+       dev_t dev;              /* device on which the file resides */
+       ino_t ino;              /* inode number of the file */
+       struct device part[DEV_PER_DRIVE];      /* partition bases and sizes */
+       struct device subpart[SUB_PER_DRIVE];   /* same for subpartitions */
+       struct part_geom geom;  /* geometry information */
+       char *buf;              /* intermediate I/O transfer buffer */
+} state;
+
+static unsigned int instance;
+
+static int vnd_open(devminor_t, int);
+static int vnd_close(devminor_t);
+static int vnd_transfer(devminor_t, int, u64_t, endpoint_t, iovec_t *,
+       unsigned int, int);
+static int vnd_ioctl(devminor_t, unsigned long, endpoint_t, cp_grant_id_t,
+       endpoint_t);
+static struct device *vnd_part(devminor_t);
+static void vnd_geometry(devminor_t, struct part_geom *);
+
+static struct blockdriver vnd_dtab = {
+       .bdr_type       = BLOCKDRIVER_TYPE_DISK,
+       .bdr_open       = vnd_open,
+       .bdr_close      = vnd_close,
+       .bdr_transfer   = vnd_transfer,
+       .bdr_ioctl      = vnd_ioctl,
+       .bdr_part       = vnd_part,
+       .bdr_geometry   = vnd_geometry
+};
+
+/*
+ * Parse partition tables.
+ */
+static void
+vnd_partition(void)
+{
+       memset(state.part, 0, sizeof(state.part));
+       memset(state.subpart, 0, sizeof(state.subpart));
+
+       state.part[0].dv_size = state.geom.size;
+
+       partition(&vnd_dtab, 0, P_PRIMARY, FALSE /*atapi*/);
+}
+
+/*
+ * Open a device.
+ */
+static int
+vnd_open(devminor_t minor, int access)
+{
+       /* No sub/partition devices are available before initialization. */
+       if (state.fd == -1 && minor != 0)
+               return ENXIO;
+       else if (state.fd != -1 && vnd_part(minor) == NULL)
+               return ENXIO;
+
+       /*
+        * If the device either is not configured or configured as read-only,
+        * block open calls that request write permission.  This is what user-
+        * land expects, although it does mean that vnconfig(8) has to open the
+        * device as read-only in order to (un)configure it.
+        */
+       if (access & BDEV_W_BIT) {
+               if (state.fd == -1)
+                       return ENXIO;
+               if (state.rdonly)
+                       return EACCES;
+       }
+
+       /*
+        * Userland expects that if the device is opened after having been
+        * fully closed, partition tables are (re)parsed.  Since we already
+        * parse partition tables upon initialization, we could skip this for
+        * the first open, but that would introduce more state.
+        */
+       if (state.fd != -1 && state.openct == 0) {
+               vnd_partition();
+
+               /* Make sure our target device didn't just disappear. */
+               if (vnd_part(minor) == NULL)
+                       return ENXIO;
+       }
+
+       state.openct++;
+
+       return OK;
+}
+
+/*
+ * Close a device.
+ */
+static int
+vnd_close(devminor_t UNUSED(minor))
+{
+       if (state.openct == 0) {
+               printf("VND%u: closing already-closed device\n", instance);
+               return EINVAL;
+       }
+
+       state.openct--;
+
+       if (state.exiting)
+               blockdriver_terminate();
+
+       return OK;
+}
+
+/*
+ * Copy a number of bytes from or to the caller, to or from the intermediate
+ * buffer.  If the given endpoint is SELF, a local memory copy must be made.
+ */
+static int
+vnd_copy(iovec_s_t *iov, size_t iov_off, size_t bytes, endpoint_t endpt,
+       int do_write)
+{
+       struct vscp_vec vvec[SCPVEC_NR], *vvp;
+       size_t off, chunk;
+       int count;
+       char *ptr;
+
+       assert(bytes > 0 && bytes <= VND_BUF_SIZE);
+
+       vvp = vvec;
+       count = 0;
+
+       for (off = 0; off < bytes; off += chunk) {
+               chunk = MIN(bytes - off, iov->iov_size - iov_off);
+
+               if (endpt == SELF) {
+                       ptr = (char *) iov->iov_grant + iov_off;
+
+                       if (do_write)
+                               memcpy(&state.buf[off], ptr, chunk);
+                       else
+                               memcpy(ptr, &state.buf[off], chunk);
+               } else {
+                       assert(count < SCPVEC_NR); /* SCPVEC_NR >= NR_IOREQS */
+
+                       vvp->v_from = do_write ? endpt : SELF;
+                       vvp->v_to = do_write ? SELF : endpt;
+                       vvp->v_bytes = chunk;
+                       vvp->v_gid = iov->iov_grant;
+                       vvp->v_offset = iov_off;
+                       vvp->v_addr = (vir_bytes) &state.buf[off];
+
+                       vvp++;
+                       count++;
+               }
+
+               iov_off += chunk;
+               if (iov_off == iov->iov_size) {
+                       iov++;
+                       iov_off = 0;
+               }
+       }
+
+       if (endpt != SELF)
+               return sys_vsafecopy(vvec, count);
+       else
+               return OK;
+}
+
+/*
+ * Advance the given I/O vector, and the offset into its first element, by the
+ * given number of bytes.
+ */
+static iovec_s_t *
+vnd_advance(iovec_s_t *iov, size_t *iov_offp, size_t bytes)
+{
+       size_t iov_off;
+
+       assert(bytes > 0 && bytes <= VND_BUF_SIZE);
+
+       iov_off = *iov_offp;
+
+       while (bytes > 0) {
+               if (bytes >= iov->iov_size - iov_off) {
+                       bytes -= iov->iov_size - iov_off;
+                       iov++;
+                       iov_off = 0;
+               } else {
+                       iov_off += bytes;
+                       bytes = 0;
+               }
+       }
+
+       *iov_offp = iov_off;
+       return iov;
+}
+
+/*
+ * Perform data transfer on the selected device.
+ */
+static int
+vnd_transfer(devminor_t minor, int do_write, u64_t position,
+       endpoint_t endpt, iovec_t *iovt, unsigned int nr_req, int flags)
+{
+       struct device *dv;
+       iovec_s_t *iov;
+       size_t off, chunk, bytes, iov_off;
+       ssize_t r;
+       unsigned int i;
+
+       iov = (iovec_s_t *) iovt;
+
+       if (state.fd == -1 || (dv = vnd_part(minor)) == NULL)
+               return ENXIO;
+
+       /* Prevent write operations on devices opened as write-only. */
+       if (do_write && state.rdonly)
+               return EACCES;
+
+       /* Determine the total number of bytes to transfer. */
+       if (position >= dv->dv_size)
+               return 0;
+
+       bytes = 0;
+
+       for (i = 0; i < nr_req; i++) {
+               if (iov[i].iov_size == 0 || iov[i].iov_size > LONG_MAX)
+                       return EINVAL;
+               bytes += iov[i].iov_size;
+               if (bytes > LONG_MAX)
+                       return EINVAL;
+       }
+
+       if (bytes > dv->dv_size - position)
+               bytes = dv->dv_size - position;
+
+       position += dv->dv_base;
+
+       /* Perform the actual transfer, in chunks if necessary. */
+       iov_off = 0;
+
+       for (off = 0; off < bytes; off += chunk) {
+               chunk = MIN(bytes - off, VND_BUF_SIZE);
+
+               assert((unsigned int) (iov - (iovec_s_t *) iovt) < nr_req);
+
+               /* For reads, read in the data for the chunk; possibly less. */
+               if (!do_write) {
+                       chunk = r = pread64(state.fd, state.buf, chunk,
+                           position);
+
+                       if (r < 0) {
+                               printf("VND%u: pread failed (%d)\n", instance,
+                                   -errno);
+                               return -errno;
+                       }
+                       if (r == 0)
+                               break;
+               }
+
+               /* Copy the data for this chunk from or to the caller. */
+               if ((r = vnd_copy(iov, iov_off, chunk, endpt, do_write)) < 0) {
+                       printf("VND%u: data copy failed (%d)\n", instance, r);
+                       return r;
+               }
+
+               /* For writes, write the data to the file; possibly less. */
+               if (do_write) {
+                       chunk = r = pwrite64(state.fd, state.buf, chunk,
+                           position);
+
+                       if (r <= 0) {
+                               if (r < 0)
+                                       r = -errno;
+                               printf("VND%u: pwrite failed (%d)\n", instance,
+                                   r);
+                               return (r < 0) ? r : EIO;
+                       }
+               }
+
+               /* Move ahead on the I/O vector and the file position. */
+               iov = vnd_advance(iov, &iov_off, chunk);
+
+               position += chunk;
+       }
+
+       /* If force-write is requested, flush the underlying file to disk. */
+       if (do_write && (flags & BDEV_FORCEWRITE))
+               fsync(state.fd);
+
+       /* Return the number of bytes transferred. */
+       return off;
+}
+
+/*
+ * Initialize the size and geometry for the device and any partitions.  If the
+ * user provided a geometry, this will be used; otherwise, a geometry will be
+ * computed.
+ */
+static int
+vnd_layout(u64_t size, struct vnd_ioctl *vnd)
+{
+       u64_t sectors;
+
+       state.geom.base = 0ULL;
+
+       if (vnd->vnd_flags & VNDIOF_HASGEOM) {
+               /*
+                * The geometry determines the accessible part of the file.
+                * The resulting size must not exceed the file size.
+                */
+               state.geom.cylinders = vnd->vnd_geom.vng_ncylinders;
+               state.geom.heads = vnd->vnd_geom.vng_ntracks;
+               state.geom.sectors = vnd->vnd_geom.vng_nsectors;
+
+               state.geom.size = (u64_t) state.geom.cylinders *
+                   state.geom.heads * state.geom.sectors *
+                   vnd->vnd_geom.vng_secsize;
+               if (state.geom.size == 0 || state.geom.size > size)
+                       return EINVAL;
+       } else {
+               sectors = size / SECTOR_SIZE;
+               state.geom.size = sectors * SECTOR_SIZE;
+
+               if (sectors >= 32 * 64) {
+                       state.geom.cylinders = sectors / (32 * 64);
+                       state.geom.heads = 64;
+                       state.geom.sectors = 32;
+               } else {
+                       state.geom.cylinders = sectors;
+                       state.geom.heads = 1;
+                       state.geom.sectors = 1;
+               }
+       }
+
+       /*
+        * Parse partition tables immediately, so that (sub)partitions can be
+        * opened right away.  The first open will perform the same procedure,
+        * but that is only necessary to match userland expectations.
+        */
+       vnd_partition();
+
+       return OK;
+}
+
+/*
+ * Process I/O control requests.
+ */
+static int
+vnd_ioctl(devminor_t UNUSED(minor), unsigned long request, endpoint_t endpt,
+       cp_grant_id_t grant, endpoint_t user_endpt)
+{
+       struct vnd_ioctl vnd;
+       struct vnd_user vnu;
+       struct stat st;
+       int r;
+
+       switch (request) {
+       case VNDIOCSET:
+               /*
+                * The VND must not be busy.  Note that the caller has the
+                * device open to perform the IOCTL request.
+                */
+               if (state.fd != -1 || state.openct != 1)
+                       return EBUSY;
+
+               if ((r = sys_safecopyfrom(endpt, grant, 0, (vir_bytes) &vnd,
+                   sizeof(vnd))) != OK)
+                       return r;
+
+               /*
+                * Issue a special VFS backcall that copies a file descriptor
+                * to the current process, from the user process ultimately
+                * making the IOCTL call.  The result is either a newly
+                * allocated file descriptor or an error.
+                */
+               if ((state.fd = dupfrom(user_endpt, vnd.vnd_fildes)) == -1)
+                       return -errno;
+
+               /* The target file must be regular. */
+               if (fstat(state.fd, &st) == -1) {
+                       printf("VND%u: fstat failed (%d)\n", instance, -errno);
+                       r = -errno;
+               }
+               if (r == OK && !S_ISREG(st.st_mode))
+                       r = EINVAL;
+
+               /*
+                * Allocate memory for an intermediate I/O transfer buffer. In
+                * order to save on memory in the common case, the buffer is
+                * only allocated when the vnd is in use.  We use mmap instead
+                * of malloc to allow the memory to be actually freed later.
+                */
+               if (r == OK) {
+                       state.buf = minix_mmap(NULL, VND_BUF_SIZE, PROT_READ |
+                           PROT_WRITE, MAP_ANON | MAP_PRIVATE, -1, 0);
+                       if (state.buf == MAP_FAILED)
+                               r = ENOMEM;
+               }
+
+               if (r != OK) {
+                       close(state.fd);
+                       state.fd = -1;
+                       return r;
+               }
+
+               /* Set various device state fields. */
+               state.dev = st.st_dev;
+               state.ino = st.st_ino;
+               state.rdonly = !!(vnd.vnd_flags & VNDIOF_READONLY);
+
+               r = vnd_layout(st.st_size, &vnd);
+
+               /* Upon success, return the device size to userland. */
+               if (r == OK) {
+                       vnd.vnd_size = state.geom.size;
+
+                       r = sys_safecopyto(endpt, grant, 0, (vir_bytes) &vnd,
+                           sizeof(vnd));
+               }
+
+               if (r != OK) {
+                       minix_munmap(state.buf, VND_BUF_SIZE);
+                       close(state.fd);
+                       state.fd = -1;
+               }
+
+               return r;
+
+       case VNDIOCCLR:
+               /* The VND can only be cleared if it has been configured. */
+               if (state.fd == -1)
+                       return ENXIO;
+
+               if ((r = sys_safecopyfrom(endpt, grant, 0, (vir_bytes) &vnd,
+                   sizeof(vnd))) != OK)
+                       return r;
+
+               /* The caller has the device open to do the IOCTL request. */
+               if (!(vnd.vnd_flags & VNDIOF_FORCE) && state.openct != 1)
+                       return EBUSY;
+
+               /*
+                * Close the associated file descriptor immediately, but do not
+                * allow reuse until the device has been closed by the other
+                * users.
+                */
+               minix_munmap(state.buf, VND_BUF_SIZE);
+               close(state.fd);
+               state.fd = -1;
+
+               return OK;
+
+       case VNDIOCGET:
+               /*
+                * We need not copy in the given structure.  It would contain
+                * the requested unit number, but each driver instance provides
+                * only one unit anyway.
+                */
+
+               memset(&vnu, 0, sizeof(vnu));
+
+               vnu.vnu_unit = instance;
+
+               /* Leave these fields zeroed if the device is not in use. */
+               if (state.fd != -1) {
+                       vnu.vnu_dev = state.dev;
+                       vnu.vnu_ino = state.ino;
+               }
+
+               return sys_safecopyto(endpt, grant, 0, (vir_bytes) &vnu,
+                   sizeof(vnu));
+
+       case DIOCOPENCT:
+               return sys_safecopyto(endpt, grant, 0,
+                   (vir_bytes) &state.openct, sizeof(state.openct));
+
+       case DIOCFLUSH:
+               if (state.fd == -1)
+                       return ENXIO;
+
+               fsync(state.fd);
+
+               return OK;
+       }
+
+       return ENOTTY;
+}
+
+/*
+ * Return a pointer to the partition structure for the given minor device.
+ */
+static struct device *
+vnd_part(devminor_t minor)
+{
+       if (minor >= 0 && minor < DEV_PER_DRIVE)
+               return &state.part[minor];
+       else if ((unsigned int) (minor -= MINOR_d0p0s0) < SUB_PER_DRIVE)
+               return &state.subpart[minor];
+       else
+               return NULL;
+}
+
+/*
+ * Return geometry information.
+ */
+static void
+vnd_geometry(devminor_t UNUSED(minor), struct part_geom *part)
+{
+       part->cylinders = state.geom.cylinders;
+       part->heads = state.geom.heads;
+       part->sectors = state.geom.sectors;
+}
+
+/*
+ * Initialize the device.
+ */
+static int
+vnd_init(int UNUSED(type), sef_init_info_t *UNUSED(info))
+{
+       long v;
+
+       /*
+        * No support for crash recovery.  The driver would have no way to
+        * reacquire the file descriptor for the target file.
+        */
+
+       /*
+        * The instance number is used for two purposes: reporting errors, and
+        * returning the proper unit number to userland in VNDIOCGET calls.
+        */
+       v = 0;
+       (void) env_parse("instance", "d", 0, &v, 0, 255);
+       instance = (unsigned int) v;
+
+       state.openct = 0;
+       state.exiting = FALSE;
+       state.fd = -1;
+
+       return OK;
+}
+
+/*
+ * Process an incoming signal.
+ */
+static void
+vnd_signal(int signo)
+{
+
+       /* In case of a termination signal, initiate driver shutdown. */
+       if (signo != SIGTERM)
+               return;
+
+       state.exiting = TRUE;
+
+       /* Keep running until the device has been fully closed. */
+       if (state.openct == 0)
+               blockdriver_terminate();
+}
+
+/*
+ * Set callbacks and initialize the System Event Framework (SEF).
+ */
+static void
+vnd_startup(void)
+{
+
+       /* Register init and signal callbacks. */
+       sef_setcb_init_fresh(vnd_init);
+       sef_setcb_signal_handler(vnd_signal);
+
+       /* Let SEF perform startup. */
+       sef_startup();
+}
+
+/*
+ * Driver task.
+ */
+int
+main(int argc, char **argv)
+{
+
+       /* Initialize the driver. */
+       env_setargs(argc, argv);
+       vnd_startup();
+
+       /* Process requests until shutdown. */
+       blockdriver_task(&vnd_dtab);
+
+       return 0;
+}
index 950cd6b08670b25752507718d2788d5655921673..591b70962360759121decea0fbd256c75805b069 100644 (file)
@@ -684,3 +684,11 @@ service fbd
                at_wini
        ;
 };
+
+service vnd
+{
+       ipc
+               SYSTEM VFS RS VM
+       ;
+       uid     0;      # only for dupfrom(2)
+};
index 087eac85e4bdee23b84a0fe3d02d08f874480e78..c7b59d11ea67f9b2030cda7c477ff59eabbaf634 100644 (file)
@@ -71,7 +71,7 @@ enum dev_style { STYLE_NDEV, STYLE_DEV, STYLE_TTY, STYLE_CTTY };
 #define BMP085B1S77_MAJOR        53    /* 53 = /dev/bmp085b1s77 (bmp085)     */
 #define BMP085B2S77_MAJOR        54    /* 54 = /dev/bmp085b2s77 (bmp085)     */
 #define BMP085B3S77_MAJOR        55    /* 55 = /dev/bmp085b3s77 (bmp085)     */
-
+                                       /* 56-63 = /dev/vnd[0-7] (vnd)        */
 
 /* Minor device numbers for memory driver. */
 #  define RAM_DEV_OLD                     0    /* minor device for /dev/ram */
index f679fff01b8f9cdd11ba64160620b51f568f5b75..f85c59f2750a7a66bfac2d81b31e852a6dacc491 100644 (file)
@@ -34,6 +34,7 @@
                                _IOC_IN)
 #define _IORW(x,y,t)   ((x << 8) | y | ((sizeof(t) & _IOCPARM_MASK) << 16) |\
                                _IOC_INOUT)
+#define _IOWR(x,y,t)   _IORW(x,y,t)    /* NetBSD compatibility */
 
 #define _IOW_BIG(y,t)  (y | ((sizeof(t) & _IOCPARM_MASK_BIG) << 8) \
        | _IOC_IN | _IOC_BIG)
index 2530ed93843b392267d6b88b198e7baf4fcab060..99ba07153b1248b5352a89e2c460dbd22eaea403 100644 (file)
@@ -143,12 +143,13 @@ CPPFLAGS.${i}+= -I${LIBCDIR}/locale
 .endfor
 
 # Import from sys-minix
-.for i in access.c brk.c close.c environ.c execve.c fork.c \
+.for i in access.c brk.c close.c environ.c execve.c fork.c fsync.c \
        getgid.c getpid.c geteuid.c getuid.c gettimeofday.c getvfsstat.c \
-       link.c loadname.c _mcontext.c mknod.c mmap.c nanosleep.c open.c \
-       read.c reboot.c sbrk.c select.c setuid.c sigprocmask.c stack_utils.c \
-       stat.c stime.c syscall.c _ucontext.c umask.c unlink.c waitpid.c \
-       brksize.S _ipc.S _senda.S ucontext.S mmap.c init.c
+       init.c link.c loadname.c lseek.c lseek64.c _mcontext.c mknod.c \
+       mmap.c nanosleep.c open.c pread.c pwrite.c read.c reboot.c sbrk.c \
+       select.c setuid.c sigprocmask.c stack_utils.c stat.c stime.c \
+       syscall.c _ucontext.c umask.c unlink.c waitpid.c write.c \
+       brksize.S _ipc.S _senda.S ucontext.S
 .PATH.c: ${LIBCDIR}/sys-minix
 .PATH.S: ${ARCHDIR}/sys-minix
 SRCS+= ${i}
index e55078b9ea563fad35b9b48502c5e5c1ff16c389..fda6c052cac02dbe5bbec62d9ff18478feaa2d4a 100644 (file)
 2013/04/23 12:00:00,sys/dev/i2c/Makefile
 2013/04/23 12:00:00,sys/dev/i2c/i2c_io.h
 2013/07/22 12:00:00,sys/dev/videomode
+2013/07/31 12:00:00,sys/dev/vndvar.h
 2012/01/16 18:47:57,sys/lib/libsa
 2012/10/17 12:00:00,sys/lib/libz
 2012/10/17 12:00:00,sys/Makefile
index 00d1bc537e41d4456447d3861e09f5afa8abd7f8..ed283898394b0f5c78898e0b6ae800e74f3cd686 100644 (file)
@@ -21,6 +21,8 @@ INCSDIR= /usr/include/dev
 # Only install includes which are used by userland
 INCS=  biovar.h ccdvar.h cgdvar.h fssvar.h keylock.h kttcpio.h lockstat.h \
        md.h vndvar.h
+.else
+INCS=  vndvar.h
 .endif
 
 .include <bsd.kinc.mk>
diff --git a/sys/dev/vndvar.h b/sys/dev/vndvar.h
new file mode 100644 (file)
index 0000000..cc06ce8
--- /dev/null
@@ -0,0 +1,218 @@
+/*     $NetBSD: vndvar.h,v 1.31 2011/06/29 09:12:42 hannken Exp $      */
+
+/*-
+ * Copyright (c) 1996, 1997, 1998 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Jason R. Thorpe.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * Copyright (c) 1988 University of Utah.
+ * Copyright (c) 1990, 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * the Systems Programming Group of the University of Utah Computer
+ * Science Department.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * from: Utah $Hdr: fdioctl.h 1.1 90/07/09$
+ *
+ *     @(#)vnioctl.h   8.1 (Berkeley) 6/10/93
+ */
+
+#ifndef _SYS_DEV_VNDVAR_H_
+#define _SYS_DEV_VNDVAR_H_
+
+#ifndef __minix
+#include <sys/pool.h>
+#endif
+
+/*
+ * Vnode disk pseudo-geometry information.
+ */
+struct vndgeom {
+       u_int32_t       vng_secsize;    /* # bytes per sector */
+       u_int32_t       vng_nsectors;   /* # data sectors per track */
+       u_int32_t       vng_ntracks;    /* # tracks per cylinder */
+       u_int32_t       vng_ncylinders; /* # cylinders per unit */
+};
+
+/*
+ * Ioctl definitions for file (vnode) disk pseudo-device.
+ */
+struct vnd_ioctl {
+#ifndef __minix
+       char            *vnd_file;      /* pathname of file to mount */
+#else
+       int             vnd_fildes;     /* file descriptor of file to mount */
+#endif
+       int             vnd_flags;      /* flags; see below */
+       struct vndgeom  vnd_geom;       /* geometry to emulate */
+#ifndef __minix
+       unsigned int    vnd_osize;      /* (returned) size of disk */
+#endif
+       uint64_t        vnd_size;       /* (returned) size of disk */
+};
+
+/* vnd_flags */
+#define        VNDIOF_HASGEOM  0x01            /* use specified geometry */
+#define        VNDIOF_READONLY 0x02            /* as read-only device */
+#define        VNDIOF_FORCE    0x04            /* force close */
+
+#ifndef __minix
+#ifdef _KERNEL
+
+struct vnode;
+
+/*
+ * A vnode disk's state information.
+ */
+struct vnd_softc {
+       device_t         sc_dev;
+       int              sc_flags;      /* flags */
+       size_t           sc_size;       /* size of vnd */
+       struct vnode    *sc_vp;         /* vnode */
+       kauth_cred_t     sc_cred;       /* credentials */
+       int              sc_maxactive;  /* max # of active requests */
+       struct bufq_state *sc_tab;      /* transfer queue */
+       int              sc_active;     /* number of active transfers */
+       struct disk      sc_dkdev;      /* generic disk device info */
+       struct vndgeom   sc_geom;       /* virtual geometry */
+       struct pool      sc_vxpool;     /* vndxfer pool */
+       struct pool      sc_vbpool;     /* vndbuf pool */
+       struct lwp      *sc_kthread;    /* kernel thread */
+       u_int32_t        sc_comp_blksz; /* precompressed block size */
+       u_int32_t        sc_comp_numoffs;/* count of compressed block offsets */
+       u_int64_t       *sc_comp_offsets;/* file idx's to compressed blocks */
+       unsigned char   *sc_comp_buff;  /* compressed data buffer */
+       unsigned char   *sc_comp_decombuf;/* decompressed data buffer */
+       int32_t          sc_comp_buffblk;/*current decompressed block */
+       z_stream         sc_comp_stream;/* decompress descriptor */
+};
+#endif
+
+/* sc_flags */
+#define        VNF_INITED      0x001   /* unit has been initialized */
+#define        VNF_WLABEL      0x002   /* label area is writable */
+#define        VNF_LABELLING   0x004   /* unit is currently being labelled */
+#define        VNF_WANTED      0x008   /* someone is waiting to obtain a lock */
+#define        VNF_LOCKED      0x010   /* unit is locked */
+#define        VNF_READONLY    0x020   /* unit is read-only */
+#define        VNF_KLABEL      0x040   /* keep label on close */
+#define        VNF_VLABEL      0x080   /* label is valid */
+#define        VNF_KTHREAD     0x100   /* thread is running */
+#define        VNF_VUNCONF     0x200   /* device is unconfiguring */
+#define VNF_COMP       0x400   /* file is compressed */
+#define VNF_CLEARING   0x800   /* unit is being torn down */
+#define VNF_USE_VN_RDWR        0x1000  /* have to use vn_rdwr() */
+
+/* structure of header in a compressed file */
+struct vnd_comp_header
+{
+       char preamble[128];
+       u_int32_t block_size;
+       u_int32_t num_blocks;
+};
+#endif
+
+/*
+ * A simple structure for describing which vnd units are in use.
+ */
+
+struct vnd_user {
+       int             vnu_unit;       /* which vnd unit */
+       dev_t           vnu_dev;        /* file is on this device... */
+       ino_t           vnu_ino;        /* ...at this inode */
+};
+
+/*
+ * Before you can use a unit, it must be configured with VNDIOCSET.
+ * The configuration persists across opens and closes of the device;
+ * an VNDIOCCLR must be used to reset a configuration.  An attempt to
+ * VNDIOCSET an already active unit will return EBUSY.
+ */
+#define VNDIOCSET      _IOWR('F', 0, struct vnd_ioctl) /* enable disk */
+#define VNDIOCCLR      _IOW('F', 1, struct vnd_ioctl)  /* disable disk */
+#define VNDIOCGET      _IOWR('F', 3, struct vnd_user)  /* get list */
+
+#ifdef _KERNEL
+/*
+ * Everything else is kernel-private, mostly exported for compat/netbsd32.
+ *
+ * NetBSD 3.0 had a 32-bit value for vnu_ino.
+ *
+ * NetBSD 5.0 had a 32-bit value for vnu_dev, and vnd_size.
+ */
+struct vnd_user30 {
+       int             vnu_unit;       /* which vnd unit */
+       uint32_t        vnu_dev;        /* file is on this device... */
+       uint32_t        vnu_ino;        /* ...at this inode */
+};
+#define VNDIOCGET30    _IOWR('F', 2, struct vnd_user30)        /* get list */
+
+struct vnd_user50 {
+       int             vnu_unit;       /* which vnd unit */
+       uint32_t        vnu_dev;        /* file is on this device... */
+       ino_t           vnu_ino;        /* ...at this inode */
+};
+#define VNDIOCGET50    _IOWR('F', 3, struct vnd_user50)        /* get list */
+
+struct vnd_ioctl50 {
+       char            *vnd_file;      /* pathname of file to mount */
+       int             vnd_flags;      /* flags; see below */
+       struct vndgeom  vnd_geom;       /* geometry to emulate */
+       unsigned int    vnd_size;       /* (returned) size of disk */
+};
+#define VNDIOCSET50    _IOWR('F', 0, struct vnd_ioctl50)
+#define VNDIOCCLR50    _IOW('F', 1, struct vnd_ioctl50)
+
+#endif /* _KERNEL */
+
+#endif /* _SYS_DEV_VNDVAR_H_ */
index ddd0979263c9a0006aa0e4c2f787ca45d301db4c..b85abb839b2ef0b93b29c6a547f11be46e2d0508 100644 (file)
@@ -23,6 +23,7 @@
 #include <sys/ioc_block.h>     /* 'b'                  */
 #include <sys/ioc_fbd.h>       /* 'B'                  */
 #include <sys/ioc_fb.h>                /* 'V'                  */
+#include <dev/vndvar.h>                /* 'F'                  */
 
 #if defined(_NETBSD_SOURCE)
 #define                TIOCDRAIN       TCDRAIN