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)
This commit is contained in:
jamie 2009-03-02 23:26:30 +00:00
parent 12e34d9850
commit 63f98fcc6a
6 changed files with 202 additions and 90 deletions

View File

@ -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 \

View File

@ -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

View File

@ -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

View File

@ -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;
}

View File

@ -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);

View File

@ -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);