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);
_______________________________________________
svn-src-all@freebsd.org mailing list
http://lists.freebsd.org/mailman/listinfo/svn-src-all
To unsubscribe, send any mail to "svn-src-all-unsubscr...@freebsd.org"

Reply via email to