vnconfig -l support patch (Re: vn(4) RFC Misc.)

Chris Turner c.turner at 199technologies.org
Mon Jan 14 03:08:11 PST 2008


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)
 {




More information about the Submit mailing list