Per my recent email to kernel@, Attached is a little patch to get vnconfig to list configured / available vn devices adapted from
& imho improved from OpenBSD.

Unless there are objections, I'll probably commit within the next couple of days - I had a couple of questions / notes though particularly
on the VNIOCGET ioctl kernel code:

- I'm implicitly trusting the input, assuming that this is
  checked on kernel copy-in time. Is this a correct assumption
  (I didn't have time to track it down)

- The 'struct vn_user' doesn't store the vn file path, so I use the
  vfs cache to re-lookup the vnode pointer backing the VN -
  is this silly?

  Also, I'm not sure if I needed to lock anything before using
  the vfs cache, so there might be problems there.

- I've not yet run this under jails, which may mean a path disclosure
  problem. Suggestions on fixing this if it is an issue welcome.

- I wasn't quite sure on how to do the swap size calculation for swap
  backed vns in some cases & got bored at that point. Should work for
  the normal case. Yea, lame excuse, I know.

Tested briefly on a vmware UP VM & vkernel so far.

Next up: Add a mount_vnd interface to vnconfig, after which it
might make sense to move this piece into /usr/src/sbin
instead of usr.sbin.
Index: sys/dev/disk/vn/vn.c
===================================================================
RCS file: /home/dcvs/src/sys/dev/disk/vn/vn.c,v
retrieving revision 1.35
diff -u -r1.35 vn.c
--- sys/dev/disk/vn/vn.c        19 May 2007 20:31:16 -0000      1.35
+++ sys/dev/disk/vn/vn.c        8 Jan 2008 16:38:44 -0000
@@ -140,6 +140,7 @@
 
 static int     vnsetcred (struct vn_softc *vn, struct ucred *cred);
 static void    vnclear (struct vn_softc *vn);
+static int     vnget (cdev_t dev, struct vn_softc *vn , struct vn_user *vnu);
 static int     vn_modevent (module_t, int, void *);
 static int     vniocattach_file (struct vn_softc *, struct vn_ioctl *, cdev_t 
dev, int flag, struct ucred *cred);
 static int     vniocattach_swap (struct vn_softc *, struct vn_ioctl *, cdev_t 
dev, int flag, struct ucred *cred);
@@ -457,6 +458,7 @@
        case VNIOCDETACH:
        case VNIOCGSET:
        case VNIOCGCLEAR:
+       case VNIOCGET:
        case VNIOCUSET:
        case VNIOCUCLEAR:
                goto vn_specific;
@@ -511,6 +513,10 @@
                        kprintf("vnioctl: CLRed\n");
                break;
 
+       case VNIOCGET:
+               error = vnget(dev, vn, (struct vn_user *) ap->a_data);
+               break;
+
        case VNIOCGSET:
                vn_options |= *f;
                *f = vn_options;
@@ -760,6 +766,88 @@
        vn->sc_size = 0;
 }
 
+/*
+ *     vnget:
+ *
+ *     populate a struct vn_user for the VNIOCGET ioctl.
+ *     interface conventions defined in sys/sys/vnioctl.h.
+ */
+
+static int
+vnget(cdev_t dev, struct vn_softc *vn, struct vn_user *vnu)
+{
+       int error, found = 0; 
+       char *freepath, *fullpath;
+       struct namecache *ncp;
+       struct nchandle nch;
+       struct vattr vattr;
+
+       if (vnu->vnu_unit == -1) {
+               vnu->vnu_unit = dkunit(dev);
+       }
+       else if (vnu->vnu_unit < 0)
+               return (EINVAL);
+
+       SLIST_FOREACH(vn, &vn_list, sc_list) {
+
+               if(vn->sc_unit != vnu->vnu_unit)
+                       continue;
+
+               found = 1;
+
+               if (vn->sc_flags & VNF_INITED && vn->sc_vp != NULL) {
+
+                       /* note: u_cred checked in vnioctl above */
+                       error = VOP_GETATTR(vn->sc_vp, &vattr);
+                       if (error)
+                               return (error);
+
+                       /* vfs_cache.c:vn_fullpath expects valid proc */
+                       TAILQ_FOREACH(ncp, &vn->sc_vp->v_namecache, nc_vnode) {
+                               if (ncp->nc_nlen)
+                               break;
+                       }
+                       if (ncp == NULL)
+                               return (EINVAL); /* panic ? */
+
+                       nch.ncp = ncp;
+                       nch.mount = vn->sc_vp->v_mount;
+                       error = cache_fullpath(NULL, &nch, 
+                                       &fullpath, &freepath);
+                       
+                       if (error) {
+                               kprintf("vnget: unable to resolve vp %p\n",
+                                       vn->sc_vp);
+                               return(error);
+                       }
+                       
+                       strlcpy(vnu->vnu_file, fullpath,
+                               sizeof(vnu->vnu_file));
+                       kfree(freepath, M_TEMP);
+                       vnu->vnu_dev = vattr.va_fsid;
+                       vnu->vnu_ino = vattr.va_fileid;
+
+               } 
+               else if (vn->sc_flags & VNF_INITED && vn->sc_object != NULL){
+
+                       strlcpy(vnu->vnu_file, _VN_USER_SWAP,
+                               sizeof(vnu->vnu_file));
+                       vnu->vnu_size = vn->sc_size;
+                       vnu->vnu_secsize = vn->sc_secsize;
+               } else {
+                       bzero(vnu->vnu_file, sizeof(vnu->vnu_file));
+                       vnu->vnu_dev = 0;
+                       vnu->vnu_ino = 0;
+               }
+               break;
+       }
+
+       if (!found)
+               return(ENXIO);
+
+       return(0);
+}
+
 static int
 vnsize(struct dev_psize_args *ap)
 {
Index: sys/sys/vnioctl.h
===================================================================
RCS file: /home/dcvs/src/sys/sys/vnioctl.h,v
retrieving revision 1.3
diff -u -r1.3 vnioctl.h
--- sys/sys/vnioctl.h   15 May 2007 22:44:19 -0000      1.3
+++ sys/sys/vnioctl.h   8 Jan 2008 00:26:54 -0000
@@ -46,6 +46,10 @@
 #ifndef _SYS_VNIOCTL_H_
 #define _SYS_VNIOCTL_H_
 
+#ifndef _SYS_PARAMH_H_
+#include <sys/param.h>         /* PATH_MAX */
+#endif
+
 /*
  * Ioctl definitions for file (vnode) disk pseudo-device.
  */
@@ -58,6 +62,39 @@
 };
 
 /*
+ * Structure used by userland to query vn devices.
+ *
+ * In file-backed configurations, vnu_file will contain the full path to
+ * the backing file, with vnu_dev and vnu_ino pointing to the appropriate
+ * filesystem entries for that file.
+ *
+ * In swap-backed configurations, vnu_file will contain the token "swap",
+ * with vnu_size and vnu_secsize indicating the portion of virtual memory
+ * in use by the vnode disk.
+ *
+ * Todo: verify vnu_file within a jail - path disclosure problem ?
+ */
+
+
+struct vn_user {
+        int            vnu_unit;               /* vn unit */
+        char           vnu_file[PATH_MAX];     /* vn description */
+#define _VN_USER_SWAP  "swap"                  /* indicates swap-backed vn */
+       union {
+               dev_t           dev;            /* vn device */
+               u_int64_t       size;           /* size per vnu_secsize */
+       } data1;
+       union {
+               ino_t           ino;            /* vn inode */
+               int             secsize;        /* sector size */
+       } data2;
+};
+#define vnu_dev                data1.dev
+#define vnu_size       data1.size
+#define vnu_ino                data2.ino
+#define vnu_secsize    data2.secsize
+
+/*
  * Before you can use a unit, it must be configured with VNIOCSET.
  * The configuration persists across opens and closes of the device;
  * an VNIOCCLR must be used to reset a configuration.  An attempt to
@@ -69,6 +106,7 @@
 #define VNIOCGCLEAR    _IOWR('F', 3, u_long )          /* reset --//-- */
 #define VNIOCUSET      _IOWR('F', 4, u_long )          /* set unit option */
 #define VNIOCUCLEAR    _IOWR('F', 5, u_long )          /* reset --//-- */
+#define VNIOCGET       _IOWR('F', 6, struct vn_user)   /* get disk info */
 
 #define VN_LABELS      0x1     /* Use disk(/slice) labels */
 #define VN_FOLLOW      0x2     /* Debug flow in vn driver */
Index: usr.sbin/vnconfig/vnconfig.8
===================================================================
RCS file: /home/dcvs/src/usr.sbin/vnconfig/vnconfig.8,v
retrieving revision 1.5
diff -u -r1.5 vnconfig.8
--- usr.sbin/vnconfig/vnconfig.8        10 Aug 2007 18:28:27 -0000      1.5
+++ usr.sbin/vnconfig/vnconfig.8        7 Jan 2008 18:33:35 -0000
@@ -60,10 +60,13 @@
 .Op Fl s Ar option
 .Op Fl r Ar option
 .Op Fl f Ar config_file
+.Nm
+.Fl l
+.Op Ar special_file Ar ...
 .Sh DESCRIPTION
 The
 .Nm
-command configures and enables vnode pseudo disk devices.
+command configures, enables and lists vnode pseudo disk devices.
 The first form of the command will associate the special file
 .Ar special_file
 with the regular file
@@ -102,6 +105,11 @@
 as an alternate config file.
 .It Fl g
 Fiddle global options.
+.It Fl l Ar special_file...
+List the vn devices and indicate which ones are in use.
+If a
+.Ar special_file
+list is given, only those devices will be described.
 .It Fl r Ar flag
 Reset
 .Ar flag .
Index: usr.sbin/vnconfig/vnconfig.c
===================================================================
RCS file: /home/dcvs/src/usr.sbin/vnconfig/vnconfig.c,v
retrieving revision 1.12
diff -u -r1.12 vnconfig.c
--- usr.sbin/vnconfig/vnconfig.c        19 Jun 2007 19:28:18 -0000      1.12
+++ usr.sbin/vnconfig/vnconfig.c        8 Jan 2008 17:27:47 -0000
@@ -90,12 +90,14 @@
 int all = 0;
 int verbose = 0;
 int global = 0;
+int listopt = 0;
 u_long setopt = 0;
 u_long resetopt = 0;
 char *configfile;
 
 int config(struct vndisk *);
 void getoptions(struct vndisk *, char *);
+int getinfo(const char *vname);
 char *rawdevice(char *);
 void readconfig(int);
 static void usage(void);
@@ -113,7 +115,7 @@
        char *s;
 
        configfile = _PATH_VNTAB;
-       while ((i = getopt(argc, argv, "acdef:gr:s:S:TZL:uv")) != -1)
+       while ((i = getopt(argc, argv, "acdef:glr:s:S:TZL:uv")) != -1)
                switch (i) {
 
                /* all -- use config file */
@@ -158,6 +160,10 @@
                        flags |= VN_RESET;
                        break;
 
+               case 'l':
+                       listopt = 1;
+                       break;
+
                /* set options */
                case 's':
                        for (s = strtok(optarg, ","); s; s = strtok(NULL, ",")) 
{
@@ -204,6 +210,16 @@
                if (kldload("vn") < 0 || modfind("vn") < 0)
                        warnx( "cannot find or load \"vn\" kernel module");
 
+       if (listopt) {
+               if(argc > optind)
+                       while(argc > optind) 
+                               rv += getinfo( argv[optind++]);
+               else {
+                       rv = getinfo( NULL );
+               }
+               exit(rv);
+       }
+
        if (flags == 0)
                flags = VN_CONFIG;
        if (all) {
@@ -240,6 +256,95 @@
        return 1;
 }
 
+/*
+ *
+ * GETINFO
+ *
+ *     Print vnode disk information to stdout for the device at
+ *     path 'vname', or all existing 'vn' devices if none is given. 
+ *     Any 'vn' devices must exist under /dev in order to be queried.
+ *
+ *     Todo: correctly use vm_secsize for swap-backed vn's ..
+ */
+
+int
+getinfo( const char *vname )
+{
+       int i, vd, printlim = 0;
+       char vnpath[PATH_MAX], *tmp;
+
+       struct vn_user vnu;
+       struct stat sb;
+
+       if (vname == NULL) {
+               i = 0;
+               printlim = 1024;
+       }
+       else {
+               tmp = (char *) vname;
+               while (*tmp != NULL) {
+                       if(isdigit(*tmp)){
+                               i = atoi(tmp);
+                               printlim = i + 1;
+                               break;
+                       }
+                       tmp++;
+               }
+               if (tmp == NULL) {
+                       printf("unknown vn device: %s", vname);
+                       return 1;
+               }
+       }
+
+       snprintf(vnpath, sizeof(vnpath), "/dev/vn%d", i);
+
+       vd = open((char *)vnpath, O_RDONLY);
+       if (vd < 0) {
+               err(1, "open: %s", vnpath);
+               return 1;
+       }
+
+       for (i; i<printlim; i++) {
+
+               bzero((void *) &vnpath, sizeof(vnpath));
+               bzero((void *) &sb, sizeof(struct stat));
+               bzero((void *) &vnu, sizeof(struct vn_user));
+
+               vnu.vnu_unit = i;
+
+               snprintf(vnpath, sizeof(vnpath), "/dev/vn%d", vnu.vnu_unit);
+
+               if(stat(vnpath, &sb) < 0)
+                       break;
+               else {
+                       if (ioctl(vd, VNIOCGET, &vnu) == -1) {
+                               if (errno != ENXIO) {
+                                       err(1, "ioctl: %s", vname);
+                                       close(vd);
+                                       return 1;
+                               }
+                       }
+
+                       fprintf(stdout, "vn%d: ", vnu.vnu_unit);
+
+                       if (vnu.vnu_file[0] == 0)
+                               fprintf(stdout, "not in use\n");
+                       else if ((strcmp(vnu.vnu_file, _VN_USER_SWAP)) == 0)
+                               fprintf(stdout,
+                                       "consuming %d VM pages\n",
+                                       vnu.vnu_size);
+                       else
+                               fprintf(stdout, 
+                                       "covering %s on %s, inode %d\n", 
+                                       vnu.vnu_file,
+                                       devname(vnu.vnu_dev, S_IFBLK), 
+                                       vnu.vnu_ino);
+               }
+       }
+       close(vd);
+       return 0;
+}
+
 int
 config(struct vndisk *vnp)
 {

Reply via email to