svn commit: r189290 - in head: share/man/man9 sys/cddl/compat/opensolaris/kern sys/compat/freebsd32 sys/kern sys/sys

Jamie Gritton jamie at FreeBSD.org
Mon Mar 2 15:26:32 PST 2009


Author: jamie
Date: Mon Mar  2 23:26:30 2009
New Revision: 189290
URL: http://svn.freebsd.org/changeset/base/189290

Log:
  Extend the "vfsopt" mount options for more general use.  Make struct
  vfsopt and the vfs_buildopts function public, and add some new fields
  to struct vfsopt (pos and seen), and new functions vfs_getopt_pos and
  vfs_opterror.
  
  Further extend the interface to allow reading options from the kernel
  in addition to sending them to the kernel, with vfs_setopt and related
  functions.
  
  While this allows the "name=value" option interface to be used for more
  than just FS mounts (planned use is for jails), it retains the current
  "vfsopt" name and <sys/mount.h> requirement.
  
  Approved by:	bz (mentor)

Modified:
  head/share/man/man9/Makefile
  head/share/man/man9/vfs_getopt.9
  head/sys/cddl/compat/opensolaris/kern/opensolaris_vfs.c
  head/sys/compat/freebsd32/freebsd32_misc.c
  head/sys/kern/vfs_mount.c
  head/sys/sys/mount.h

Modified: head/share/man/man9/Makefile
==============================================================================
--- head/share/man/man9/Makefile	Mon Mar  2 22:16:50 2009	(r189289)
+++ head/share/man/man9/Makefile	Mon Mar  2 23:26:30 2009	(r189290)
@@ -1243,7 +1243,10 @@ MLINKS+=vfs_getopt.9 vfs_copyopt.9 \
 	vfs_getopt.9 vfs_filteropt.9 \
 	vfs_getopt.9 vfs_flagopt.9 \
 	vfs_getopt.9 vfs_getopts.9 \
-	vfs_getopt.9 vfs_scanopt.9
+	vfs_getopt.9 vfs_scanopt.9 \
+	vfs_getopt.9 vfs_setopt.9 \
+	vfs_getopt.9 vfs_setopt_part.9 \
+	vfs_getopt.9 vfs_setopts.9
 MLINKS+=VFS_LOCK_GIANT.9 VFS_UNLOCK_GIANT.9
 MLINKS+=vgone.9 vgonel.9
 MLINKS+=vhold.9 vdrop.9 \

Modified: head/share/man/man9/vfs_getopt.9
==============================================================================
--- head/share/man/man9/vfs_getopt.9	Mon Mar  2 22:16:50 2009	(r189289)
+++ head/share/man/man9/vfs_getopt.9	Mon Mar  2 23:26:30 2009	(r189290)
@@ -26,7 +26,7 @@
 .\"
 .\" $FreeBSD$
 .\"
-.Dd February 28, 2007
+.Dd March 2, 2009
 .Dt VFS_GETOPT 9
 .Os
 .Sh NAME
@@ -35,7 +35,10 @@
 .Nm vfs_flagopt ,
 .Nm vfs_scanopt ,
 .Nm vfs_copyopt ,
-.Nm vfs_filteropt
+.Nm vfs_filteropt ,
+.Nm vfs_setopt ,
+.Nm vfs_setopt_part ,
+.Nm vfs_setopts
 .Nd "manipulate mount options and their values"
 .Sh SYNOPSIS
 .In sys/param.h
@@ -62,6 +65,18 @@
 .Fo vfs_filteropt
 .Fa "struct vfsoptlist *opts" "const char **legal"
 .Fc
+.Ft int
+.Fo vfs_setopt
+.Fa "struct vfsoptlist *opts" "const char *name" "void *value" "int len"
+.Fc
+.Ft int
+.Fo vfs_setopt_part
+.Fa "struct vfsoptlist *opts" "const char *name" "void *value" "int len"
+.Fc
+.Ft int
+.Fo vfs_setopts
+.Fa "struct vfsoptlist *opts" "const char *name" "const char *value"
+.Fc
 .Sh DESCRIPTION
 The
 .Fn vfs_getopt
@@ -111,7 +126,7 @@ The
 .Fn vfs_scanopt
 function performs a
 .Xr vsscanf 3
-with the options value, using the given format,
+with the option's value, using the given format,
 into the specified variable arguments.
 The value must be a string (i.e.,
 .Dv NUL
@@ -119,10 +134,10 @@ terminated).
 .Pp
 The
 .Fn vfs_copyopt
-function creates a copy of the options value.
+function creates a copy of the option's value.
 The
 .Fa len
-argument must match the length of the options value exactly
+argument must match the length of the option's value exactly
 (i.e., a larger buffer will still cause
 .Fn vfs_copyout
 to fail with
@@ -134,6 +149,28 @@ function ensures that no unknown options
 A option is valid if its name matches one of the names in the
 list of legal names.
 An option may be prefixed with 'no', and still be considered valid.
+.Pp
+The
+.Fn vfs_setopt
+and
+.Fn vfs_setopt_part
+functions copy new data into the option's value.
+In
+.Fn vfs_setopt ,
+the
+.Fa len
+argument must match the length of the option's value exactly
+(i.e., a larger buffer will still cause
+.Fn vfs_copyout
+to fail with
+.Er EINVAL ) .
+.Pp
+The
+.Fn vfs_setopts
+function copies a new string into the option's value.
+The string, including
+.Dv NUL
+byte, must be no longer than the option's length.
 .Sh RETURN VALUES
 The
 .Fn vfs_getopt
@@ -179,7 +216,9 @@ not always mean the option does not exis
 .Pp
 The
 .Fn vfs_copyopt
-function returns 0 if the copy was successful,
+and
+.Fn vfs_setopt
+functions return 0 if the copy was successful,
 .Er EINVAL
 if the option was found but the lengths did not match, and
 .Er ENOENT
@@ -190,6 +229,14 @@ The
 function returns 0 if all of the options are legal; otherwise,
 .Er EINVAL
 is returned.
+.Pp
+The
+.Fn vfs_setopts
+function returns 0 if the copy was successful,
+.Er EINVAL
+if the option was found but the string was too long, and
+.Er ENOENT
+if the option was not found.
 .Sh AUTHORS
 .An -nosplit
 This manual page was written by

Modified: head/sys/cddl/compat/opensolaris/kern/opensolaris_vfs.c
==============================================================================
--- head/sys/cddl/compat/opensolaris/kern/opensolaris_vfs.c	Mon Mar  2 22:16:50 2009	(r189289)
+++ head/sys/cddl/compat/opensolaris/kern/opensolaris_vfs.c	Mon Mar  2 23:26:30 2009	(r189290)
@@ -39,14 +39,6 @@ __FBSDID("$FreeBSD$");
 
 MALLOC_DECLARE(M_MOUNT);
 
-TAILQ_HEAD(vfsoptlist, vfsopt);
-struct vfsopt {
-	TAILQ_ENTRY(vfsopt) link;
-	char	*name;
-	void	*value;
-	int	len;
-};
-
 void
 vfs_setmntopt(vfs_t *vfsp, const char *name, const char *arg,
     int flags __unused)
@@ -64,6 +56,8 @@ vfs_setmntopt(vfs_t *vfsp, const char *n
 	namesize = strlen(name) + 1;
 	opt->name = malloc(namesize, M_MOUNT, M_WAITOK);
 	strlcpy(opt->name, name, namesize);
+	opt->pos = -1;
+	opt->seen = 1;
 
 	if (arg == NULL) {
 		opt->value = NULL;
@@ -80,22 +74,9 @@ vfs_setmntopt(vfs_t *vfsp, const char *n
 void
 vfs_clearmntopt(vfs_t *vfsp, const char *name)
 {
-	struct vfsopt *opt;
 
-	if (vfsp->mnt_opt == NULL)
-		return;
 	/* TODO: Locking. */
-	TAILQ_FOREACH(opt, vfsp->mnt_opt, link) {
-		if (strcmp(opt->name, name) == 0)
-			break;
-	}
-	if (opt != NULL) {
-		TAILQ_REMOVE(vfsp->mnt_opt, opt, link);
-		free(opt->name, M_MOUNT);
-		if (opt->value != NULL)
-			free(opt->value, M_MOUNT);
-		free(opt, M_MOUNT);
-	}
+	vfs_deleteopt(vfsp->mnt_opt, name);
 }
 
 int

Modified: head/sys/compat/freebsd32/freebsd32_misc.c
==============================================================================
--- head/sys/compat/freebsd32/freebsd32_misc.c	Mon Mar  2 22:16:50 2009	(r189289)
+++ head/sys/compat/freebsd32/freebsd32_misc.c	Mon Mar  2 23:26:30 2009	(r189290)
@@ -2639,8 +2639,7 @@ freebsd32_nmount(struct thread *td,
     } */ *uap)
 {
 	struct uio *auio;
-	struct iovec *iov;
-	int error, k;
+	int error;
 
 	AUDIT_ARG(fflags, uap->flags);
 
@@ -2662,14 +2661,8 @@ freebsd32_nmount(struct thread *td,
 	error = freebsd32_copyinuio(uap->iovp, uap->iovcnt, &auio);
 	if (error)
 		return (error);
-	for (iov = auio->uio_iov, k = 0; k < uap->iovcnt; ++k, ++iov) {
-		if (iov->iov_len > MMAXOPTIONLEN) {
-			free(auio, M_IOV);
-			return (EINVAL);
-		}
-	}
-
 	error = vfs_donmount(td, uap->flags, auio);
+
 	free(auio, M_IOV);
 	return error;
 }

Modified: head/sys/kern/vfs_mount.c
==============================================================================
--- head/sys/kern/vfs_mount.c	Mon Mar  2 22:16:50 2009	(r189289)
+++ head/sys/kern/vfs_mount.c	Mon Mar  2 23:26:30 2009	(r189290)
@@ -78,7 +78,6 @@ static int	vfs_domount(struct thread *td
 static int	vfs_mountroot_ask(void);
 static int	vfs_mountroot_try(const char *mountfrom);
 static void	free_mntarg(struct mntarg *ma);
-static int	vfs_getopt_pos(struct vfsoptlist *opts, const char *name);
 
 static int	usermount = 0;
 SYSCTL_INT(_vfs, OID_AUTO, usermount, CTLFLAG_RW, &usermount, 0,
@@ -95,14 +94,6 @@ struct mntlist mountlist = TAILQ_HEAD_IN
 struct mtx mountlist_mtx;
 MTX_SYSINIT(mountlist, &mountlist_mtx, "mountlist", MTX_DEF);
 
-TAILQ_HEAD(vfsoptlist, vfsopt);
-struct vfsopt {
-	TAILQ_ENTRY(vfsopt) link;
-	char	*name;
-	void	*value;
-	int	len;
-};
-
 /*
  * The vnode of the system's root (/ in the filesystem, without chroot
  * active.)
@@ -164,11 +155,6 @@ vfs_freeopt(struct vfsoptlist *opts, str
 	free(opt->name, M_MOUNT);
 	if (opt->value != NULL)
 		free(opt->value, M_MOUNT);
-#ifdef INVARIANTS
-	else if (opt->len != 0)
-		panic("%s: mount option with NULL value but length != 0",
-		    __func__);
-#endif
 	free(opt, M_MOUNT);
 }
 
@@ -204,6 +190,7 @@ vfs_deleteopt(struct vfsoptlist *opts, c
 static int
 vfs_equalopts(const char *opt1, const char *opt2)
 {
+	char *p;
 
 	/* "opt" vs. "opt" or "noopt" vs. "noopt" */
 	if (strcmp(opt1, opt2) == 0)
@@ -214,6 +201,17 @@ vfs_equalopts(const char *opt1, const ch
 	/* "opt" vs. "noopt" */
 	if (strncmp(opt2, "no", 2) == 0 && strcmp(opt1, opt2 + 2) == 0)
 		return (1);
+	while ((p = strchr(opt1, '.')) != NULL &&
+	    !strncmp(opt1, opt2, ++p - opt1)) {
+		opt2 += p - opt1;
+		opt1 = p;
+		/* "foo.noopt" vs. "foo.opt" */
+		if (strncmp(opt1, "no", 2) == 0 && strcmp(opt1 + 2, opt2) == 0)
+			return (1);
+		/* "foo.opt" vs. "foo.noopt" */
+		if (strncmp(opt2, "no", 2) == 0 && strcmp(opt1, opt2 + 2) == 0)
+			return (1);
+	}
 	return (0);
 }
 
@@ -244,34 +242,23 @@ vfs_sanitizeopts(struct vfsoptlist *opts
 /*
  * Build a linked list of mount options from a struct uio.
  */
-static int
+int
 vfs_buildopts(struct uio *auio, struct vfsoptlist **options)
 {
 	struct vfsoptlist *opts;
 	struct vfsopt *opt;
-	size_t memused;
+	size_t memused, namelen, optlen;
 	unsigned int i, iovcnt;
-	int error, namelen, optlen;
+	int error;
 
 	opts = malloc(sizeof(struct vfsoptlist), M_MOUNT, M_WAITOK);
 	TAILQ_INIT(opts);
 	memused = 0;
 	iovcnt = auio->uio_iovcnt;
 	for (i = 0; i < iovcnt; i += 2) {
-		opt = malloc(sizeof(struct vfsopt), M_MOUNT, M_WAITOK);
 		namelen = auio->uio_iov[i].iov_len;
 		optlen = auio->uio_iov[i + 1].iov_len;
-		opt->name = malloc(namelen, M_MOUNT, M_WAITOK);
-		opt->value = NULL;
-		opt->len = 0;
-
-		/*
-		 * Do this early, so jumps to "bad" will free the current
-		 * option.
-		 */
-		TAILQ_INSERT_TAIL(opts, opt, link);
 		memused += sizeof(struct vfsopt) + optlen + namelen;
-
 		/*
 		 * Avoid consuming too much memory, and attempts to overflow
 		 * memused.
@@ -283,6 +270,19 @@ vfs_buildopts(struct uio *auio, struct v
 			goto bad;
 		}
 
+		opt = malloc(sizeof(struct vfsopt), M_MOUNT, M_WAITOK);
+		opt->name = malloc(namelen, M_MOUNT, M_WAITOK);
+		opt->value = NULL;
+		opt->len = 0;
+		opt->pos = i / 2;
+		opt->seen = 0;
+
+		/*
+		 * Do this early, so jumps to "bad" will free the current
+		 * option.
+		 */
+		TAILQ_INSERT_TAIL(opts, opt, link);
+
 		if (auio->uio_segflg == UIO_SYSSPACE) {
 			bcopy(auio->uio_iov[i].iov_base, opt->name, namelen);
 		} else {
@@ -292,7 +292,7 @@ vfs_buildopts(struct uio *auio, struct v
 				goto bad;
 		}
 		/* Ensure names are null-terminated strings. */
-		if (opt->name[namelen - 1] != '\0') {
+		if (namelen == 0 || opt->name[namelen - 1] != '\0') {
 			error = EINVAL;
 			goto bad;
 		}
@@ -361,6 +361,7 @@ vfs_mergeopts(struct vfsoptlist *toopts,
 			new->value = NULL;
 		}
 		new->len = opt->len;
+		new->seen = opt->seen;
 		TAILQ_INSERT_TAIL(toopts, new, link);
 next:
 		continue;
@@ -380,8 +381,6 @@ nmount(td, uap)
 	} */ *uap;
 {
 	struct uio *auio;
-	struct iovec *iov;
-	unsigned int i;
 	int error;
 	u_int iovcnt;
 
@@ -414,16 +413,6 @@ nmount(td, uap)
 		    __func__, error);
 		return (error);
 	}
-	iov = auio->uio_iov;
-	for (i = 0; i < iovcnt; i++) {
-		if (iov->iov_len > MMAXOPTIONLEN) {
-			free(auio, M_IOV);
-			CTR1(KTR_VFS, "%s: failed for invalid new auio",
-			    __func__);
-			return (EINVAL);
-		}
-		iov++;
-	}
 	error = vfs_donmount(td, uap->flags, auio);
 
 	free(auio, M_IOV);
@@ -692,6 +681,8 @@ vfs_donmount(struct thread *td, int fsfl
 		noro_opt->name = strdup("noro", M_MOUNT);
 		noro_opt->value = NULL;
 		noro_opt->len = 0;
+		noro_opt->pos = -1;
+		noro_opt->seen = 1;
 		TAILQ_INSERT_TAIL(optlist, noro_opt, link);
 	}
 
@@ -1611,6 +1602,22 @@ vfs_mount_error(struct mount *mp, const 
 	va_end(ap);
 }
 
+void
+vfs_opterror(struct vfsoptlist *opts, const char *fmt, ...)
+{
+	va_list ap;
+	int error, len;
+	char *errmsg;
+
+	error = vfs_getopt(opts, "errmsg", (void **)&errmsg, &len);
+	if (error || errmsg == NULL || len <= 0)
+		return;
+
+	va_start(ap, fmt);
+	vsnprintf(errmsg, (size_t)len, fmt, ap);
+	va_end(ap);
+}
+
 /*
  * Find and mount the root filesystem
  */
@@ -1880,6 +1887,7 @@ vfs_getopt(opts, name, buf, len)
 
 	TAILQ_FOREACH(opt, opts, link) {
 		if (strcmp(name, opt->name) == 0) {
+			opt->seen = 1;
 			if (len != NULL)
 				*len = opt->len;
 			if (buf != NULL)
@@ -1890,20 +1898,19 @@ vfs_getopt(opts, name, buf, len)
 	return (ENOENT);
 }
 
-static int
+int
 vfs_getopt_pos(struct vfsoptlist *opts, const char *name)
 {
 	struct vfsopt *opt;
-	int i;
 
 	if (opts == NULL)
 		return (-1);
 
-	i = 0;
 	TAILQ_FOREACH(opt, opts, link) {
-		if (strcmp(name, opt->name) == 0)
-			return (i);
-		++i;
+		if (strcmp(name, opt->name) == 0) {
+			opt->seen = 1;
+			return (opt->pos);
+		}
 	}
 	return (-1);
 }
@@ -1917,7 +1924,9 @@ vfs_getopts(struct vfsoptlist *opts, con
 	TAILQ_FOREACH(opt, opts, link) {
 		if (strcmp(name, opt->name) != 0)
 			continue;
-		if (((char *)opt->value)[opt->len - 1] != '\0') {
+		opt->seen = 1;
+		if (opt->len == 0 ||
+		    ((char *)opt->value)[opt->len - 1] != '\0') {
 			*error = EINVAL;
 			return (NULL);
 		}
@@ -1934,6 +1943,7 @@ vfs_flagopt(struct vfsoptlist *opts, con
 
 	TAILQ_FOREACH(opt, opts, link) {
 		if (strcmp(name, opt->name) == 0) {
+			opt->seen = 1;
 			if (w != NULL)
 				*w |= val;
 			return (1);
@@ -1956,6 +1966,7 @@ vfs_scanopt(struct vfsoptlist *opts, con
 	TAILQ_FOREACH(opt, opts, link) {
 		if (strcmp(name, opt->name) != 0)
 			continue;
+		opt->seen = 1;
 		if (opt->len == 0 || opt->value == NULL)
 			return (0);
 		if (((char *)opt->value)[opt->len - 1] != '\0')
@@ -1968,6 +1979,67 @@ vfs_scanopt(struct vfsoptlist *opts, con
 	return (0);
 }
 
+int
+vfs_setopt(struct vfsoptlist *opts, const char *name, void *value, int len)
+{
+	struct vfsopt *opt;
+
+	TAILQ_FOREACH(opt, opts, link) {
+		if (strcmp(name, opt->name) != 0)
+			continue;
+		opt->seen = 1;
+		if (opt->value == NULL)
+			opt->len = len;
+		else {
+			if (opt->len != len)
+				return (EINVAL);
+			bcopy(value, opt->value, len);
+		}
+		return (0);
+	}
+	return (ENOENT);
+}
+
+int
+vfs_setopt_part(struct vfsoptlist *opts, const char *name, void *value, int len)
+{
+	struct vfsopt *opt;
+
+	TAILQ_FOREACH(opt, opts, link) {
+		if (strcmp(name, opt->name) != 0)
+			continue;
+		opt->seen = 1;
+		if (opt->value == NULL)
+			opt->len = len;
+		else {
+			if (opt->len < len)
+				return (EINVAL);
+			opt->len = len;
+			bcopy(value, opt->value, len);
+		}
+		return (0);
+	}
+	return (ENOENT);
+}
+
+int
+vfs_setopts(struct vfsoptlist *opts, const char *name, const char *value)
+{
+	struct vfsopt *opt;
+
+	TAILQ_FOREACH(opt, opts, link) {
+		if (strcmp(name, opt->name) != 0)
+			continue;
+		opt->seen = 1;
+		if (opt->value == NULL)
+			opt->len = strlen(value) + 1;
+		else if (strlcpy(opt->value, value, opt->len) >= opt->len)
+			return (EINVAL);
+		return (0);
+	}
+	return (ENOENT);
+}
+
 /*
  * Find and copy a mount option.
  *
@@ -1989,6 +2061,7 @@ vfs_copyopt(opts, name, dest, len)
 
 	TAILQ_FOREACH(opt, opts, link) {
 		if (strcmp(name, opt->name) == 0) {
+			opt->seen = 1;
 			if (len != opt->len)
 				return (EINVAL);
 			bcopy(opt->value, dest, opt->len);

Modified: head/sys/sys/mount.h
==============================================================================
--- head/sys/sys/mount.h	Mon Mar  2 22:16:50 2009	(r189289)
+++ head/sys/sys/mount.h	Mon Mar  2 23:26:30 2009	(r189290)
@@ -127,12 +127,18 @@ struct ostatfs {
 	long	f_spare[2];		/* unused spare */
 };
 
-#define	MMAXOPTIONLEN	65536		/* maximum length of a mount option */
-
 TAILQ_HEAD(vnodelst, vnode);
 
-struct vfsoptlist;
-struct vfsopt;
+/* Mount options list */
+TAILQ_HEAD(vfsoptlist, vfsopt);
+struct vfsopt {
+	TAILQ_ENTRY(vfsopt) link;
+	char	*name;
+	void	*value;
+	int	len;
+	int	pos;
+	int	seen;
+};
 
 /*
  * Structure per mounted filesystem.  Each mounted filesystem has an
@@ -702,12 +708,21 @@ void	vfs_mount_destroy(struct mount *);
 void	vfs_event_signal(fsid_t *, u_int32_t, intptr_t);
 void	vfs_freeopts(struct vfsoptlist *opts);
 void	vfs_deleteopt(struct vfsoptlist *opts, const char *name);
+int	vfs_buildopts(struct uio *auio, struct vfsoptlist **options);
 int	vfs_flagopt(struct vfsoptlist *opts, const char *name, u_int *w, u_int val);
 int	vfs_getopt(struct vfsoptlist *, const char *, void **, int *);
+int	vfs_getopt_pos(struct vfsoptlist *opts, const char *name);
 char	*vfs_getopts(struct vfsoptlist *, const char *, int *error);
 int	vfs_copyopt(struct vfsoptlist *, const char *, void *, int);
 int	vfs_filteropt(struct vfsoptlist *, const char **legal);
+void	vfs_opterror(struct vfsoptlist *opts, const char *fmt, ...);
 int	vfs_scanopt(struct vfsoptlist *opts, const char *name, const char *fmt, ...);
+int	vfs_setopt(struct vfsoptlist *opts, const char *name, void *value,
+	    int len);
+int	vfs_setopt_part(struct vfsoptlist *opts, const char *name, void *value,
+	    int len);
+int	vfs_setopts(struct vfsoptlist *opts, const char *name,
+	    const char *value);
 int	vfs_setpublicfs			    /* set publicly exported fs */
 	    (struct mount *, struct netexport *, struct export_args *);
 void	vfs_msync(struct mount *, int);


More information about the svn-src-head mailing list