summaryrefslogtreecommitdiffstats
path: root/sys
diff options
context:
space:
mode:
authormux <mux@FreeBSD.org>2002-07-19 16:05:31 +0000
committermux <mux@FreeBSD.org>2002-07-19 16:05:31 +0000
commit77c14873f425e4be86a67aab8201b85866f564b1 (patch)
tree032fd0c67f9ab94c5c1252620b789140ac34db5b /sys
parentb2c9482532dab6b8837b0b4beaa610d38447a0bf (diff)
downloadFreeBSD-src-77c14873f425e4be86a67aab8201b85866f564b1.zip
FreeBSD-src-77c14873f425e4be86a67aab8201b85866f564b1.tar.gz
- 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)
Diffstat (limited to 'sys')
-rw-r--r--sys/kern/vfs_mount.c132
1 files changed, 113 insertions, 19 deletions
diff --git a/sys/kern/vfs_mount.c b/sys/kern/vfs_mount.c
index 991b2f1..89f9940 100644
--- a/sys/kern/vfs_mount.c
+++ b/sys/kern/vfs_mount.c
@@ -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,15 +170,44 @@ 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.
*/
static int
@@ -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:
@@ -208,6 +258,48 @@ bad:
}
/*
+ * 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.
*/
int
@@ -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.
OpenPOWER on IntegriCloud