- Merge the mount options at MNT_UPDATE time with vfs_mergeopts().
- Sanity check the mount options list (remove duplicates) with vfs_sanitizeopts(). - Fix some malloc(0)/free(NULL) bugs. Reviewed by: rwatson (some time ago)
This commit is contained in:
parent
a440a601c1
commit
72fda5bc50
@ -145,10 +145,24 @@ char *rootdevnames[2] = {NULL, NULL};
|
||||
static int setrootbyname(char *name);
|
||||
dev_t rootdev = NODEV;
|
||||
|
||||
/*
|
||||
* Release all resources related to the
|
||||
* mount options.
|
||||
*/
|
||||
/* Remove one mount option. */
|
||||
static void
|
||||
vfs_freeopt(struct vfsoptlist *opts, struct vfsopt *opt)
|
||||
{
|
||||
|
||||
TAILQ_REMOVE(opts, opt, link);
|
||||
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);
|
||||
}
|
||||
|
||||
/* Release all resources related to the mount options. */
|
||||
static void
|
||||
vfs_freeopts(struct vfsoptlist *opts)
|
||||
{
|
||||
@ -156,14 +170,43 @@ vfs_freeopts(struct vfsoptlist *opts)
|
||||
|
||||
while (!TAILQ_EMPTY(opts)) {
|
||||
opt = TAILQ_FIRST(opts);
|
||||
TAILQ_REMOVE(opts, opt, link);
|
||||
free(opt->name, M_MOUNT);
|
||||
free(opt->value, M_MOUNT);
|
||||
free(opt, M_MOUNT);
|
||||
vfs_freeopt(opts, opt);
|
||||
}
|
||||
free(opts, M_MOUNT);
|
||||
}
|
||||
|
||||
/*
|
||||
* If a mount option is specified several times,
|
||||
* (with or without the "no" prefix) only keep
|
||||
* the last occurence of it.
|
||||
*/
|
||||
static void
|
||||
vfs_sanitizeopts(struct vfsoptlist *opts)
|
||||
{
|
||||
struct vfsopt *opt, *opt2, *tmp;
|
||||
int noopt;
|
||||
|
||||
TAILQ_FOREACH_REVERSE(opt, opts, vfsoptlist, link) {
|
||||
if (strncmp(opt->name, "no", 2) == 0)
|
||||
noopt = 1;
|
||||
else
|
||||
noopt = 0;
|
||||
opt2 = TAILQ_PREV(opt, vfsoptlist, link);
|
||||
while (opt2 != NULL) {
|
||||
if (strcmp(opt2->name, opt->name) == 0 ||
|
||||
(noopt && strcmp(opt->name + 2, opt2->name) == 0) ||
|
||||
(!noopt && strncmp(opt2->name, "no", 2) == 0 &&
|
||||
strcmp(opt2->name + 2, opt->name) == 0)) {
|
||||
tmp = TAILQ_PREV(opt2, vfsoptlist, link);
|
||||
vfs_freeopt(opts, opt2);
|
||||
opt2 = tmp;
|
||||
} else {
|
||||
opt2 = TAILQ_PREV(opt2, vfsoptlist, link);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Build a linked list of mount options from a struct uio.
|
||||
*/
|
||||
@ -183,23 +226,30 @@ vfs_buildopts(struct uio *auio, struct vfsoptlist **options)
|
||||
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 = malloc(optlen, M_MOUNT, M_WAITOK);
|
||||
opt->len = optlen;
|
||||
if (auio->uio_segflg == UIO_SYSSPACE) {
|
||||
bcopy(auio->uio_iov[i].iov_base, opt->name, namelen);
|
||||
bcopy(auio->uio_iov[i + 1].iov_base, opt->value,
|
||||
optlen);
|
||||
if (optlen == 0) {
|
||||
opt->value = NULL;
|
||||
} else {
|
||||
error = copyin(auio->uio_iov[i].iov_base, opt->name,
|
||||
namelen);
|
||||
if (!error)
|
||||
opt->value = malloc(optlen, M_MOUNT, M_WAITOK);
|
||||
if (auio->uio_segflg == UIO_SYSSPACE) {
|
||||
bcopy(auio->uio_iov[i].iov_base, opt->name,
|
||||
namelen);
|
||||
bcopy(auio->uio_iov[i + 1].iov_base, opt->value,
|
||||
optlen);
|
||||
} else {
|
||||
error = copyin(auio->uio_iov[i].iov_base,
|
||||
opt->name, namelen);
|
||||
if (error)
|
||||
goto bad;
|
||||
error = copyin(auio->uio_iov[i + 1].iov_base,
|
||||
opt->value, optlen);
|
||||
if (error)
|
||||
goto bad;
|
||||
if (error)
|
||||
goto bad;
|
||||
}
|
||||
}
|
||||
TAILQ_INSERT_TAIL(opts, opt, link);
|
||||
}
|
||||
vfs_sanitizeopts(opts);
|
||||
*options = opts;
|
||||
return (0);
|
||||
bad:
|
||||
@ -207,6 +257,48 @@ bad:
|
||||
return (error);
|
||||
}
|
||||
|
||||
/*
|
||||
* Merge the old mount options with the new ones passed
|
||||
* in the MNT_UPDATE case.
|
||||
*/
|
||||
static void
|
||||
vfs_mergeopts(struct vfsoptlist *toopts, struct vfsoptlist *opts)
|
||||
{
|
||||
struct vfsopt *opt, *opt2, *new;
|
||||
|
||||
TAILQ_FOREACH(opt, opts, link) {
|
||||
/*
|
||||
* Check that this option hasn't been redefined
|
||||
* nor cancelled with a "no" mount option.
|
||||
*/
|
||||
opt2 = TAILQ_FIRST(toopts);
|
||||
while (opt2 != NULL) {
|
||||
if (strcmp(opt2->name, opt->name) == 0)
|
||||
goto next;
|
||||
if (strncmp(opt2->name, "no", 2) == 0 &&
|
||||
strcmp(opt2->name + 2, opt->name) == 0) {
|
||||
vfs_freeopt(toopts, opt2);
|
||||
goto next;
|
||||
}
|
||||
opt2 = TAILQ_NEXT(opt2, link);
|
||||
}
|
||||
/* We want this option, duplicate it. */
|
||||
new = malloc(sizeof(struct vfsopt), M_MOUNT, M_WAITOK);
|
||||
new->name = malloc(strlen(opt->name) + 1, M_MOUNT, M_WAITOK);
|
||||
strcpy(new->name, opt->name);
|
||||
if (opt->len != 0) {
|
||||
new->value = malloc(opt->len, M_MOUNT, M_WAITOK);
|
||||
bcopy(opt->value, new->value, opt->len);
|
||||
} else {
|
||||
new->value = NULL;
|
||||
}
|
||||
new->len = opt->len;
|
||||
TAILQ_INSERT_TAIL(toopts, new, link);
|
||||
next:
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* New mount API.
|
||||
*/
|
||||
@ -457,6 +549,8 @@ vfs_nmount(td, fsflags, fsoptions)
|
||||
mp->mnt_flag |= fsflags &
|
||||
(MNT_RELOAD | MNT_FORCE | MNT_UPDATE | MNT_SNAPSHOT);
|
||||
VOP_UNLOCK(vp, 0, td);
|
||||
mp->mnt_optnew = optlist;
|
||||
vfs_mergeopts(mp->mnt_optnew, mp->mnt_opt);
|
||||
goto update;
|
||||
}
|
||||
/*
|
||||
@ -549,9 +643,9 @@ vfs_nmount(td, fsflags, fsoptions)
|
||||
strncpy(mp->mnt_stat.f_mntonname, fspath, MNAMELEN);
|
||||
mp->mnt_iosize_max = DFLTPHYS;
|
||||
VOP_UNLOCK(vp, 0, td);
|
||||
mp->mnt_optnew = optlist;
|
||||
|
||||
update:
|
||||
mp->mnt_optnew = optlist;
|
||||
/*
|
||||
* Check if the fs implements the new VFS_NMOUNT()
|
||||
* function, since the new system call was used.
|
||||
|
Loading…
x
Reference in New Issue
Block a user