summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorjamie <jamie@FreeBSD.org>2009-03-02 23:26:30 +0000
committerjamie <jamie@FreeBSD.org>2009-03-02 23:26:30 +0000
commit63f98fcc6a46518f1e430026c32dd9c31dff0a84 (patch)
treeb070bef8a4d0b9e895916c453fb1a0a0f82434c0
parent12e34d9850ea5157971c208839b4af421d679674 (diff)
downloadFreeBSD-src-63f98fcc6a46518f1e430026c32dd9c31dff0a84.zip
FreeBSD-src-63f98fcc6a46518f1e430026c32dd9c31dff0a84.tar.gz
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)
-rw-r--r--share/man/man9/Makefile5
-rw-r--r--share/man/man9/vfs_getopt.959
-rw-r--r--sys/cddl/compat/opensolaris/kern/opensolaris_vfs.c25
-rw-r--r--sys/compat/freebsd32/freebsd32_misc.c11
-rw-r--r--sys/kern/vfs_mount.c169
-rw-r--r--sys/sys/mount.h23
6 files changed, 202 insertions, 90 deletions
diff --git a/share/man/man9/Makefile b/share/man/man9/Makefile
index b632778..e70ca9c 100644
--- a/share/man/man9/Makefile
+++ b/share/man/man9/Makefile
@@ -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 \
diff --git a/share/man/man9/vfs_getopt.9 b/share/man/man9/vfs_getopt.9
index c393209..8d8d3b3 100644
--- a/share/man/man9/vfs_getopt.9
+++ b/share/man/man9/vfs_getopt.9
@@ -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 were specified.
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 exist, or is not a valid string.
.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
diff --git a/sys/cddl/compat/opensolaris/kern/opensolaris_vfs.c b/sys/cddl/compat/opensolaris/kern/opensolaris_vfs.c
index f1bb4e2..045b8aa 100644
--- a/sys/cddl/compat/opensolaris/kern/opensolaris_vfs.c
+++ b/sys/cddl/compat/opensolaris/kern/opensolaris_vfs.c
@@ -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 *name, const char *arg,
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 *name, const char *arg,
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
diff --git a/sys/compat/freebsd32/freebsd32_misc.c b/sys/compat/freebsd32/freebsd32_misc.c
index 83fd67f..59809d8 100644
--- a/sys/compat/freebsd32/freebsd32_misc.c
+++ b/sys/compat/freebsd32/freebsd32_misc.c
@@ -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;
}
diff --git a/sys/kern/vfs_mount.c b/sys/kern/vfs_mount.c
index 1f767a3..e489d80 100644
--- a/sys/kern/vfs_mount.c
+++ b/sys/kern/vfs_mount.c
@@ -78,7 +78,6 @@ static int vfs_domount(struct thread *td, const char *fstype,
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_INITIALIZER(mountlist);
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, struct vfsopt *opt)
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, const char *name)
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 char *opt2)
/* "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 vfsoptlist **options)
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 vfsoptlist **options)
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, struct vfsoptlist *opts)
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 fsflags, struct uio *fsoptions)
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 char *fmt, ...)
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, const char *name, int *error)
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, const char *name, u_int *w, u_int val)
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, const char *name, const char *fmt, ...)
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, const char *name, const char *fmt, ...)
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);
diff --git a/sys/sys/mount.h b/sys/sys/mount.h
index 5dd7b74..f83686c 100644
--- a/sys/sys/mount.h
+++ b/sys/sys/mount.h
@@ -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);
OpenPOWER on IntegriCloud