From owner-svn-src-all@FreeBSD.ORG Mon Mar 2 23:26:31 2009 Return-Path: Delivered-To: svn-src-all@freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:4f8:fff6::34]) by hub.freebsd.org (Postfix) with ESMTP id 6E215106566B; Mon, 2 Mar 2009 23:26:31 +0000 (UTC) (envelope-from jamie@FreeBSD.org) Received: from svn.freebsd.org (svn.freebsd.org [IPv6:2001:4f8:fff6::2c]) by mx1.freebsd.org (Postfix) with ESMTP id 5A5268FC0A; Mon, 2 Mar 2009 23:26:31 +0000 (UTC) (envelope-from jamie@FreeBSD.org) Received: from svn.freebsd.org (localhost [127.0.0.1]) by svn.freebsd.org (8.14.3/8.14.3) with ESMTP id n22NQV0U089159; Mon, 2 Mar 2009 23:26:31 GMT (envelope-from jamie@svn.freebsd.org) Received: (from jamie@localhost) by svn.freebsd.org (8.14.3/8.14.3/Submit) id n22NQVeb089153; Mon, 2 Mar 2009 23:26:31 GMT (envelope-from jamie@svn.freebsd.org) Message-Id: <200903022326.n22NQVeb089153@svn.freebsd.org> From: Jamie Gritton Date: Mon, 2 Mar 2009 23:26:31 +0000 (UTC) To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org X-SVN-Group: head MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Cc: Subject: svn commit: r189290 - in head: share/man/man9 sys/cddl/compat/opensolaris/kern sys/compat/freebsd32 sys/kern sys/sys X-BeenThere: svn-src-all@freebsd.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: "SVN commit messages for the entire src tree \(except for " user" and " projects" \)" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Mon, 02 Mar 2009 23:26:32 -0000 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 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);