2005-01-06 23:35:40 +00:00
|
|
|
/*-
|
2004-12-03 22:38:06 +00:00
|
|
|
* Copyright (c) 1999-2004 Poul-Henning Kamp
|
2004-12-06 12:44:30 +00:00
|
|
|
* Copyright (c) 1999 Michael Smith
|
2002-07-02 17:09:22 +00:00
|
|
|
* Copyright (c) 1989, 1993
|
|
|
|
* The Regents of the University of California. All rights reserved.
|
|
|
|
* (c) UNIX System Laboratories, Inc.
|
|
|
|
* All or some portions of this file are derived from material licensed
|
|
|
|
* to the University of California by American Telephone and Telegraph
|
|
|
|
* Co. or Unix System Laboratories, Inc. and are reproduced herein with
|
|
|
|
* the permission of UNIX System Laboratories, Inc.
|
|
|
|
*
|
|
|
|
* Redistribution and use in source and binary forms, with or without
|
|
|
|
* modification, are permitted provided that the following conditions
|
|
|
|
* are met:
|
|
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
|
|
* notice, this list of conditions and the following disclaimer.
|
|
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
|
|
* documentation and/or other materials provided with the distribution.
|
|
|
|
* 4. Neither the name of the University nor the names of its contributors
|
|
|
|
* may be used to endorse or promote products derived from this software
|
|
|
|
* without specific prior written permission.
|
|
|
|
*
|
2002-07-02 18:33:32 +00:00
|
|
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
1994-05-24 10:09:53 +00:00
|
|
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
2002-07-02 18:33:32 +00:00
|
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
1994-05-24 10:09:53 +00:00
|
|
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
|
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
|
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
|
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
|
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
|
|
* SUCH DAMAGE.
|
|
|
|
*/
|
|
|
|
|
2003-06-11 00:56:59 +00:00
|
|
|
#include <sys/cdefs.h>
|
|
|
|
__FBSDID("$FreeBSD$");
|
|
|
|
|
1999-11-01 23:51:00 +00:00
|
|
|
#include <sys/param.h>
|
|
|
|
#include <sys/conf.h>
|
2008-03-31 12:01:21 +00:00
|
|
|
#include <sys/fcntl.h>
|
2004-02-02 19:02:05 +00:00
|
|
|
#include <sys/jail.h>
|
2002-07-02 17:09:22 +00:00
|
|
|
#include <sys/kernel.h>
|
2005-02-03 15:10:58 +00:00
|
|
|
#include <sys/libkern.h>
|
2002-08-01 17:47:56 +00:00
|
|
|
#include <sys/malloc.h>
|
2002-07-02 17:09:22 +00:00
|
|
|
#include <sys/mount.h>
|
|
|
|
#include <sys/mutex.h>
|
|
|
|
#include <sys/namei.h>
|
2006-11-06 13:42:10 +00:00
|
|
|
#include <sys/priv.h>
|
2001-05-01 08:13:21 +00:00
|
|
|
#include <sys/proc.h>
|
2003-01-01 01:56:19 +00:00
|
|
|
#include <sys/filedesc.h>
|
2002-07-02 17:09:22 +00:00
|
|
|
#include <sys/reboot.h>
|
2004-12-07 08:15:41 +00:00
|
|
|
#include <sys/syscallsubr.h>
|
2002-07-02 17:09:22 +00:00
|
|
|
#include <sys/sysproto.h>
|
|
|
|
#include <sys/sx.h>
|
|
|
|
#include <sys/sysctl.h>
|
|
|
|
#include <sys/sysent.h>
|
|
|
|
#include <sys/systm.h>
|
2002-07-05 05:15:30 +00:00
|
|
|
#include <sys/vnode.h>
|
2006-03-31 03:49:51 +00:00
|
|
|
#include <vm/uma.h>
|
2002-07-02 17:09:22 +00:00
|
|
|
|
2002-10-25 18:44:42 +00:00
|
|
|
#include <geom/geom.h>
|
|
|
|
|
2002-07-02 17:09:22 +00:00
|
|
|
#include <machine/stdarg.h>
|
1994-05-24 10:09:53 +00:00
|
|
|
|
2006-06-05 15:32:07 +00:00
|
|
|
#include <security/audit/audit.h>
|
2006-10-22 11:52:19 +00:00
|
|
|
#include <security/mac/mac_framework.h>
|
2006-06-05 15:32:07 +00:00
|
|
|
|
2002-07-02 17:09:22 +00:00
|
|
|
#include "opt_rootdevname.h"
|
2002-07-31 01:11:29 +00:00
|
|
|
#include "opt_mac.h"
|
2001-05-01 08:13:21 +00:00
|
|
|
|
2003-11-14 05:27:41 +00:00
|
|
|
#define ROOTNAME "root_device"
|
|
|
|
#define VFS_MOUNTARG_SIZE_MAX (1024 * 64)
|
2002-07-02 17:09:22 +00:00
|
|
|
|
2003-11-14 05:27:41 +00:00
|
|
|
static int vfs_domount(struct thread *td, const char *fstype,
|
2004-12-07 08:15:41 +00:00
|
|
|
char *fspath, int fsflags, void *fsdata);
|
2003-11-14 05:27:41 +00:00
|
|
|
static int vfs_mountroot_ask(void);
|
2004-07-30 22:08:52 +00:00
|
|
|
static int vfs_mountroot_try(const char *mountfrom);
|
2005-02-10 12:25:38 +00:00
|
|
|
static void free_mntarg(struct mntarg *ma);
|
2002-07-02 17:09:22 +00:00
|
|
|
|
2004-03-27 08:39:28 +00:00
|
|
|
static int usermount = 0;
|
|
|
|
SYSCTL_INT(_vfs, OID_AUTO, usermount, CTLFLAG_RW, &usermount, 0,
|
|
|
|
"Unprivileged users may mount and unmount file systems");
|
2001-05-01 08:13:21 +00:00
|
|
|
|
1999-11-01 23:51:00 +00:00
|
|
|
MALLOC_DEFINE(M_MOUNT, "mount", "vfs mount structure");
|
2006-01-09 20:42:19 +00:00
|
|
|
MALLOC_DEFINE(M_VNODE_MARKER, "vnodemarker", "vnode marker");
|
2006-03-31 03:49:51 +00:00
|
|
|
static uma_zone_t mount_zone;
|
1997-02-10 02:22:35 +00:00
|
|
|
|
2002-07-02 17:09:22 +00:00
|
|
|
/* List of mounted filesystems. */
|
|
|
|
struct mntlist mountlist = TAILQ_HEAD_INITIALIZER(mountlist);
|
|
|
|
|
|
|
|
/* For any iteration/modification of mountlist */
|
|
|
|
struct mtx mountlist_mtx;
|
2005-04-18 21:11:47 +00:00
|
|
|
MTX_SYSINIT(mountlist, &mountlist_mtx, "mountlist", MTX_DEF);
|
2002-07-02 17:09:22 +00:00
|
|
|
|
2002-03-05 15:38:49 +00:00
|
|
|
/*
|
|
|
|
* The vnode of the system's root (/ in the filesystem, without chroot
|
|
|
|
* active.)
|
|
|
|
*/
|
1999-11-01 23:51:00 +00:00
|
|
|
struct vnode *rootvnode;
|
1995-08-28 09:19:25 +00:00
|
|
|
|
2002-07-02 17:09:22 +00:00
|
|
|
/*
|
|
|
|
* The root filesystem is detailed in the kernel environment variable
|
|
|
|
* vfs.root.mountfrom, which is expected to be in the general format
|
|
|
|
*
|
|
|
|
* <vfsname>:[<path>]
|
|
|
|
* vfsname := the name of a VFS known to the kernel and capable
|
|
|
|
* of being mounted as root
|
|
|
|
* path := disk device name or other data used by the filesystem
|
|
|
|
* to locate its physical store
|
|
|
|
*/
|
|
|
|
|
2004-12-06 18:18:35 +00:00
|
|
|
/*
|
|
|
|
* Global opts, taken by all filesystems
|
|
|
|
*/
|
|
|
|
static const char *global_opts[] = {
|
2005-12-08 04:09:29 +00:00
|
|
|
"errmsg",
|
2004-12-06 18:18:35 +00:00
|
|
|
"fstype",
|
|
|
|
"fspath",
|
|
|
|
"ro",
|
2005-12-03 01:26:27 +00:00
|
|
|
"rw",
|
On 6.x this works:
% mount | grep home
/dev/ad4s1e on /home (ufs, local, noatime, soft-updates)
% mount -u -o atime /home
% mount | grep home
/dev/ad4s1e on /home (ufs, local, soft-updates)
Restore this behavior for on 7.x for the following mount options:
noatime, noclusterr, noclusterw, noexec, nosuid, nosymfollow
In addition, on 7.x, the following are equivalent:
mount -u -o atime /home
mount -u -o nonoatime /home
Ideally, when we introduce new mount options, we should avoid
options starting with "no". :)
Requested by: jhb
Reported by: Karol Kwiat <karol.kwiat gmail com>, Scott Hetzel <swhetzel gmail com>
Approved by: re (bmah)
Proxy commit for: rodrigc
2007-08-15 17:40:09 +00:00
|
|
|
"nosuid",
|
|
|
|
"noexec",
|
2004-12-06 18:18:35 +00:00
|
|
|
NULL
|
|
|
|
};
|
|
|
|
|
2003-11-14 05:27:41 +00:00
|
|
|
/*
|
1999-11-04 01:40:18 +00:00
|
|
|
* The root specifiers we will try if RB_CDROM is specified.
|
|
|
|
*/
|
|
|
|
static char *cdrom_rootdevnames[] = {
|
2003-09-26 09:07:27 +00:00
|
|
|
"cd9660:cd0",
|
|
|
|
"cd9660:acd0",
|
1999-11-04 01:40:18 +00:00
|
|
|
NULL
|
|
|
|
};
|
|
|
|
|
1999-11-01 23:51:00 +00:00
|
|
|
/* legacy find-root code */
|
|
|
|
char *rootdevnames[2] = {NULL, NULL};
|
2004-12-07 08:15:41 +00:00
|
|
|
#ifndef ROOTDEVNAME
|
|
|
|
# define ROOTDEVNAME NULL
|
2004-07-30 22:08:52 +00:00
|
|
|
#endif
|
2005-02-10 12:25:38 +00:00
|
|
|
static const char *ctrootdevname = ROOTDEVNAME;
|
2004-12-07 08:15:41 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* ---------------------------------------------------------------------
|
|
|
|
* Functions for building and sanitizing the mount options
|
|
|
|
*/
|
1995-08-28 09:19:25 +00:00
|
|
|
|
2002-07-19 16:05:31 +00:00
|
|
|
/* 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);
|
|
|
|
free(opt, M_MOUNT);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Release all resources related to the mount options. */
|
2007-03-31 22:44:45 +00:00
|
|
|
void
|
2002-07-02 17:09:22 +00:00
|
|
|
vfs_freeopts(struct vfsoptlist *opts)
|
|
|
|
{
|
|
|
|
struct vfsopt *opt;
|
|
|
|
|
|
|
|
while (!TAILQ_EMPTY(opts)) {
|
|
|
|
opt = TAILQ_FIRST(opts);
|
2002-07-19 16:05:31 +00:00
|
|
|
vfs_freeopt(opts, opt);
|
2002-07-02 17:09:22 +00:00
|
|
|
}
|
|
|
|
free(opts, M_MOUNT);
|
|
|
|
}
|
|
|
|
|
2006-12-16 15:44:03 +00:00
|
|
|
void
|
|
|
|
vfs_deleteopt(struct vfsoptlist *opts, const char *name)
|
|
|
|
{
|
|
|
|
struct vfsopt *opt, *temp;
|
2007-03-14 12:54:10 +00:00
|
|
|
|
2008-08-10 12:15:36 +00:00
|
|
|
if (opts == NULL)
|
|
|
|
return;
|
2006-12-16 15:44:03 +00:00
|
|
|
TAILQ_FOREACH_SAFE(opt, opts, link, temp) {
|
|
|
|
if (strcmp(opt->name, name) == 0)
|
|
|
|
vfs_freeopt(opts, opt);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2004-03-27 08:39:28 +00:00
|
|
|
/*
|
|
|
|
* Check if options are equal (with or without the "no" prefix).
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
vfs_equalopts(const char *opt1, const char *opt2)
|
|
|
|
{
|
2009-03-02 23:26:30 +00:00
|
|
|
char *p;
|
2004-03-27 08:39:28 +00:00
|
|
|
|
|
|
|
/* "opt" vs. "opt" or "noopt" vs. "noopt" */
|
|
|
|
if (strcmp(opt1, opt2) == 0)
|
|
|
|
return (1);
|
|
|
|
/* "noopt" vs. "opt" */
|
|
|
|
if (strncmp(opt1, "no", 2) == 0 && strcmp(opt1 + 2, opt2) == 0)
|
|
|
|
return (1);
|
|
|
|
/* "opt" vs. "noopt" */
|
|
|
|
if (strncmp(opt2, "no", 2) == 0 && strcmp(opt1, opt2 + 2) == 0)
|
|
|
|
return (1);
|
2009-03-02 23:26:30 +00:00
|
|
|
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);
|
|
|
|
}
|
2004-03-27 08:39:28 +00:00
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
2002-07-19 16:05:31 +00:00
|
|
|
/*
|
|
|
|
* 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;
|
|
|
|
|
|
|
|
TAILQ_FOREACH_REVERSE(opt, opts, vfsoptlist, link) {
|
|
|
|
opt2 = TAILQ_PREV(opt, vfsoptlist, link);
|
|
|
|
while (opt2 != NULL) {
|
2004-03-27 08:39:28 +00:00
|
|
|
if (vfs_equalopts(opt->name, opt2->name)) {
|
2002-07-19 16:05:31 +00:00
|
|
|
tmp = TAILQ_PREV(opt2, vfsoptlist, link);
|
|
|
|
vfs_freeopt(opts, opt2);
|
|
|
|
opt2 = tmp;
|
|
|
|
} else {
|
|
|
|
opt2 = TAILQ_PREV(opt2, vfsoptlist, link);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2002-07-02 17:09:22 +00:00
|
|
|
/*
|
|
|
|
* Build a linked list of mount options from a struct uio.
|
|
|
|
*/
|
2009-03-02 23:26:30 +00:00
|
|
|
int
|
2002-07-02 17:09:22 +00:00
|
|
|
vfs_buildopts(struct uio *auio, struct vfsoptlist **options)
|
|
|
|
{
|
|
|
|
struct vfsoptlist *opts;
|
|
|
|
struct vfsopt *opt;
|
2009-03-02 23:26:30 +00:00
|
|
|
size_t memused, namelen, optlen;
|
2002-07-02 17:09:22 +00:00
|
|
|
unsigned int i, iovcnt;
|
2009-03-02 23:26:30 +00:00
|
|
|
int error;
|
2002-07-02 17:09:22 +00:00
|
|
|
|
2003-02-19 05:47:46 +00:00
|
|
|
opts = malloc(sizeof(struct vfsoptlist), M_MOUNT, M_WAITOK);
|
2002-07-02 17:09:22 +00:00
|
|
|
TAILQ_INIT(opts);
|
2003-11-14 05:27:41 +00:00
|
|
|
memused = 0;
|
|
|
|
iovcnt = auio->uio_iovcnt;
|
2002-07-02 17:09:22 +00:00
|
|
|
for (i = 0; i < iovcnt; i += 2) {
|
|
|
|
namelen = auio->uio_iov[i].iov_len;
|
|
|
|
optlen = auio->uio_iov[i + 1].iov_len;
|
2003-11-14 05:27:41 +00:00
|
|
|
memused += sizeof(struct vfsopt) + optlen + namelen;
|
2003-11-12 02:54:47 +00:00
|
|
|
/*
|
|
|
|
* Avoid consuming too much memory, and attempts to overflow
|
2003-11-14 05:27:41 +00:00
|
|
|
* memused.
|
2003-11-12 02:54:47 +00:00
|
|
|
*/
|
|
|
|
if (memused > VFS_MOUNTARG_SIZE_MAX ||
|
|
|
|
optlen > VFS_MOUNTARG_SIZE_MAX ||
|
|
|
|
namelen > VFS_MOUNTARG_SIZE_MAX) {
|
|
|
|
error = EINVAL;
|
|
|
|
goto bad;
|
|
|
|
}
|
|
|
|
|
2009-03-02 23:26:30 +00:00
|
|
|
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);
|
|
|
|
|
2002-07-24 19:50:00 +00:00
|
|
|
if (auio->uio_segflg == UIO_SYSSPACE) {
|
|
|
|
bcopy(auio->uio_iov[i].iov_base, opt->name, namelen);
|
2002-07-02 17:09:22 +00:00
|
|
|
} else {
|
2002-07-24 19:50:00 +00:00
|
|
|
error = copyin(auio->uio_iov[i].iov_base, opt->name,
|
|
|
|
namelen);
|
|
|
|
if (error)
|
|
|
|
goto bad;
|
|
|
|
}
|
2003-11-14 05:27:41 +00:00
|
|
|
/* Ensure names are null-terminated strings. */
|
2009-03-02 23:26:30 +00:00
|
|
|
if (namelen == 0 || opt->name[namelen - 1] != '\0') {
|
2003-11-14 05:27:41 +00:00
|
|
|
error = EINVAL;
|
|
|
|
goto bad;
|
2003-11-12 02:54:47 +00:00
|
|
|
}
|
2002-07-24 19:50:00 +00:00
|
|
|
if (optlen != 0) {
|
2004-09-05 22:24:28 +00:00
|
|
|
opt->len = optlen;
|
2003-02-19 05:47:46 +00:00
|
|
|
opt->value = malloc(optlen, M_MOUNT, M_WAITOK);
|
2002-07-19 16:05:31 +00:00
|
|
|
if (auio->uio_segflg == UIO_SYSSPACE) {
|
|
|
|
bcopy(auio->uio_iov[i + 1].iov_base, opt->value,
|
|
|
|
optlen);
|
|
|
|
} else {
|
2002-07-02 17:09:22 +00:00
|
|
|
error = copyin(auio->uio_iov[i + 1].iov_base,
|
|
|
|
opt->value, optlen);
|
2002-07-19 16:05:31 +00:00
|
|
|
if (error)
|
|
|
|
goto bad;
|
|
|
|
}
|
2002-07-02 17:09:22 +00:00
|
|
|
}
|
|
|
|
}
|
2002-07-19 16:05:31 +00:00
|
|
|
vfs_sanitizeopts(opts);
|
2002-07-02 17:09:22 +00:00
|
|
|
*options = opts;
|
|
|
|
return (0);
|
|
|
|
bad:
|
|
|
|
vfs_freeopts(opts);
|
|
|
|
return (error);
|
|
|
|
}
|
|
|
|
|
2002-07-19 16:05:31 +00:00
|
|
|
/*
|
|
|
|
* Merge the old mount options with the new ones passed
|
|
|
|
* in the MNT_UPDATE case.
|
2008-02-18 10:10:42 +00:00
|
|
|
*
|
|
|
|
* XXX This function will keep a "nofoo" option in the
|
|
|
|
* new options if there is no matching "foo" option
|
|
|
|
* to be cancelled in the old options. This is a bug
|
|
|
|
* if the option's canonical name is "foo". E.g., "noro"
|
|
|
|
* shouldn't end up in the mount point's active options,
|
|
|
|
* but it can.
|
2002-07-19 16:05:31 +00:00
|
|
|
*/
|
|
|
|
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. */
|
2003-02-19 05:47:46 +00:00
|
|
|
new = malloc(sizeof(struct vfsopt), M_MOUNT, M_WAITOK);
|
|
|
|
new->name = malloc(strlen(opt->name) + 1, M_MOUNT, M_WAITOK);
|
2002-07-19 16:05:31 +00:00
|
|
|
strcpy(new->name, opt->name);
|
|
|
|
if (opt->len != 0) {
|
2003-02-19 05:47:46 +00:00
|
|
|
new->value = malloc(opt->len, M_MOUNT, M_WAITOK);
|
2002-07-19 16:05:31 +00:00
|
|
|
bcopy(opt->value, new->value, opt->len);
|
|
|
|
} else {
|
|
|
|
new->value = NULL;
|
|
|
|
}
|
|
|
|
new->len = opt->len;
|
2009-03-02 23:26:30 +00:00
|
|
|
new->seen = opt->seen;
|
2002-07-19 16:05:31 +00:00
|
|
|
TAILQ_INSERT_TAIL(toopts, new, link);
|
|
|
|
next:
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2002-07-02 17:09:22 +00:00
|
|
|
/*
|
2007-03-05 13:10:58 +00:00
|
|
|
* Mount a filesystem.
|
2002-07-02 17:09:22 +00:00
|
|
|
*/
|
|
|
|
int
|
|
|
|
nmount(td, uap)
|
|
|
|
struct thread *td;
|
|
|
|
struct nmount_args /* {
|
2002-12-14 02:07:32 +00:00
|
|
|
struct iovec *iovp;
|
|
|
|
unsigned int iovcnt;
|
|
|
|
int flags;
|
2002-07-02 17:09:22 +00:00
|
|
|
} */ *uap;
|
|
|
|
{
|
2004-07-10 15:42:16 +00:00
|
|
|
struct uio *auio;
|
2002-07-02 17:09:22 +00:00
|
|
|
int error;
|
2004-07-10 15:42:16 +00:00
|
|
|
u_int iovcnt;
|
2002-07-02 17:09:22 +00:00
|
|
|
|
2006-06-05 15:32:07 +00:00
|
|
|
AUDIT_ARG(fflags, uap->flags);
|
2009-02-05 15:03:35 +00:00
|
|
|
CTR4(KTR_VFS, "%s: iovp %p with iovcnt %d and flags %d", __func__,
|
|
|
|
uap->iovp, uap->iovcnt, uap->flags);
|
2006-06-05 15:32:07 +00:00
|
|
|
|
2007-10-27 15:59:18 +00:00
|
|
|
/*
|
|
|
|
* Filter out MNT_ROOTFS. We do not want clients of nmount() in
|
|
|
|
* userspace to set this flag, but we must filter it out if we want
|
|
|
|
* MNT_UPDATE on the root file system to work.
|
|
|
|
* MNT_ROOTFS should only be set in the kernel in vfs_mountroot_try().
|
|
|
|
*/
|
|
|
|
uap->flags &= ~MNT_ROOTFS;
|
2004-12-03 19:25:44 +00:00
|
|
|
|
2002-12-14 01:56:26 +00:00
|
|
|
iovcnt = uap->iovcnt;
|
2002-07-02 17:09:22 +00:00
|
|
|
/*
|
|
|
|
* Check that we have an even number of iovec's
|
|
|
|
* and that we have at least two options.
|
|
|
|
*/
|
2009-02-05 15:03:35 +00:00
|
|
|
if ((iovcnt & 1) || (iovcnt < 4)) {
|
|
|
|
CTR2(KTR_VFS, "%s: failed for invalid iovcnt %d", __func__,
|
|
|
|
uap->iovcnt);
|
2002-07-02 17:09:22 +00:00
|
|
|
return (EINVAL);
|
2009-02-05 15:03:35 +00:00
|
|
|
}
|
2004-12-03 19:25:44 +00:00
|
|
|
|
2004-07-10 15:42:16 +00:00
|
|
|
error = copyinuio(uap->iovp, iovcnt, &auio);
|
2009-02-05 15:03:35 +00:00
|
|
|
if (error) {
|
|
|
|
CTR2(KTR_VFS, "%s: failed for invalid uio op with %d errno",
|
|
|
|
__func__, error);
|
2004-07-10 15:42:16 +00:00
|
|
|
return (error);
|
2009-02-05 15:03:35 +00:00
|
|
|
}
|
2004-07-27 22:32:01 +00:00
|
|
|
error = vfs_donmount(td, uap->flags, auio);
|
2005-11-09 02:26:38 +00:00
|
|
|
|
2004-07-10 15:42:16 +00:00
|
|
|
free(auio, M_IOV);
|
2002-07-02 17:09:22 +00:00
|
|
|
return (error);
|
|
|
|
}
|
|
|
|
|
2004-12-07 08:15:41 +00:00
|
|
|
/*
|
|
|
|
* ---------------------------------------------------------------------
|
|
|
|
* Various utility functions
|
|
|
|
*/
|
|
|
|
|
2006-02-06 10:19:50 +00:00
|
|
|
void
|
|
|
|
vfs_ref(struct mount *mp)
|
|
|
|
{
|
|
|
|
|
2009-02-05 15:03:35 +00:00
|
|
|
CTR2(KTR_VFS, "%s: mp %p", __func__, mp);
|
2006-02-06 10:19:50 +00:00
|
|
|
MNT_ILOCK(mp);
|
|
|
|
MNT_REF(mp);
|
|
|
|
MNT_IUNLOCK(mp);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
vfs_rel(struct mount *mp)
|
|
|
|
{
|
|
|
|
|
2009-02-05 15:03:35 +00:00
|
|
|
CTR2(KTR_VFS, "%s: mp %p", __func__, mp);
|
2006-02-06 10:19:50 +00:00
|
|
|
MNT_ILOCK(mp);
|
|
|
|
MNT_REL(mp);
|
|
|
|
MNT_IUNLOCK(mp);
|
|
|
|
}
|
|
|
|
|
2006-03-31 03:49:51 +00:00
|
|
|
static int
|
|
|
|
mount_init(void *mem, int size, int flags)
|
|
|
|
{
|
|
|
|
struct mount *mp;
|
|
|
|
|
|
|
|
mp = (struct mount *)mem;
|
|
|
|
mtx_init(&mp->mnt_mtx, "struct mount mtx", NULL, MTX_DEF);
|
2008-06-09 10:31:38 +00:00
|
|
|
lockinit(&mp->mnt_explock, PVFS, "explock", 0, 0);
|
2006-03-31 03:49:51 +00:00
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
mount_fini(void *mem, int size)
|
|
|
|
{
|
|
|
|
struct mount *mp;
|
|
|
|
|
|
|
|
mp = (struct mount *)mem;
|
2008-06-09 10:31:38 +00:00
|
|
|
lockdestroy(&mp->mnt_explock);
|
2006-03-31 03:49:51 +00:00
|
|
|
mtx_destroy(&mp->mnt_mtx);
|
|
|
|
}
|
|
|
|
|
2002-07-02 17:09:22 +00:00
|
|
|
/*
|
2003-11-12 02:54:47 +00:00
|
|
|
* Allocate and initialize the mount point struct.
|
2002-07-02 17:09:22 +00:00
|
|
|
*/
|
2007-04-17 21:14:06 +00:00
|
|
|
struct mount *
|
2008-08-31 14:26:08 +00:00
|
|
|
vfs_mount_alloc(struct vnode *vp, struct vfsconf *vfsp, const char *fspath,
|
|
|
|
struct ucred *cred)
|
2002-07-02 17:09:22 +00:00
|
|
|
{
|
|
|
|
struct mount *mp;
|
2003-11-12 02:54:47 +00:00
|
|
|
|
2006-03-31 03:49:51 +00:00
|
|
|
mp = uma_zalloc(mount_zone, M_WAITOK);
|
|
|
|
bzero(&mp->mnt_startzero,
|
|
|
|
__rangeof(struct mount, mnt_startzero, mnt_endzero));
|
2003-11-12 02:54:47 +00:00
|
|
|
TAILQ_INIT(&mp->mnt_nvnodelist);
|
|
|
|
mp->mnt_nvnodelistsize = 0;
|
2006-02-06 10:19:50 +00:00
|
|
|
mp->mnt_ref = 0;
|
2008-11-02 10:15:42 +00:00
|
|
|
(void) vfs_busy(mp, MBF_NOWAIT);
|
2003-11-12 02:54:47 +00:00
|
|
|
mp->mnt_op = vfsp->vfc_vfsops;
|
|
|
|
mp->mnt_vfc = vfsp;
|
2006-02-06 10:19:50 +00:00
|
|
|
vfsp->vfc_refcount++; /* XXX Unlocked */
|
2003-11-12 02:54:47 +00:00
|
|
|
mp->mnt_stat.f_type = vfsp->vfc_typenum;
|
2006-10-03 10:47:04 +00:00
|
|
|
mp->mnt_gen++;
|
2003-11-12 02:54:47 +00:00
|
|
|
strlcpy(mp->mnt_stat.f_fstypename, vfsp->vfc_name, MFSNAMELEN);
|
|
|
|
mp->mnt_vnodecovered = vp;
|
2008-08-31 14:26:08 +00:00
|
|
|
mp->mnt_cred = crdup(cred);
|
|
|
|
mp->mnt_stat.f_owner = cred->cr_uid;
|
2003-11-12 02:54:47 +00:00
|
|
|
strlcpy(mp->mnt_stat.f_mntonname, fspath, MNAMELEN);
|
|
|
|
mp->mnt_iosize_max = DFLTPHYS;
|
|
|
|
#ifdef MAC
|
2007-10-24 19:04:04 +00:00
|
|
|
mac_mount_init(mp);
|
2008-08-31 14:26:08 +00:00
|
|
|
mac_mount_create(cred, mp);
|
2003-11-12 02:54:47 +00:00
|
|
|
#endif
|
2005-03-16 07:35:06 +00:00
|
|
|
arc4rand(&mp->mnt_hashseed, sizeof mp->mnt_hashseed, 0);
|
2005-12-20 00:43:51 +00:00
|
|
|
return (mp);
|
2003-11-12 02:54:47 +00:00
|
|
|
}
|
|
|
|
|
2003-11-14 05:27:41 +00:00
|
|
|
/*
|
|
|
|
* Destroy the mount struct previously allocated by vfs_mount_alloc().
|
|
|
|
*/
|
2007-03-31 22:44:45 +00:00
|
|
|
void
|
2006-06-02 20:29:02 +00:00
|
|
|
vfs_mount_destroy(struct mount *mp)
|
2003-11-12 02:54:47 +00:00
|
|
|
{
|
|
|
|
|
2006-01-09 20:42:19 +00:00
|
|
|
MNT_ILOCK(mp);
|
1) Fix a deadlock in the VFS:
- threadA runs vfs_rel(mp1)
- threadB does unmount the mp1 fs, sets MNTK_UNMOUNT and drop MNT_ILOCK()
- threadA runs vfs_busy(mp1) and, as long as, MNTK_UNMOUNT is set, sleeps
waiting for threadB to complete the unmount
- threadB, in vfs_mount_destroy(), finds mnt_lock > 0 and sleeps waiting
for the refcount to expire.
Fix the deadlock by adding a flag called MNTK_REFEXPIRE which signals the
unmounter is waiting for mnt_ref to expire.
The vfs_busy contenders got awake, fails, and if they retry the
MNTK_REFEXPIRE won't allow them to sleep again.
2) Simplify significantly the code of vfs_mount_destroy() trimming
unnecessary codes:
- as long as any reference exited, it is no-more possible to have
write-op (primarty and secondary) in progress.
- it is no needed to drop and reacquire the mount lock.
- filling the structures with dummy values is unuseful as long as
it is going to be freed.
Tested by: pho, Andrea Barberio <insomniac at slackware dot it>
Discussed with: kib
2008-12-16 23:16:10 +00:00
|
|
|
mp->mnt_kern_flag |= MNTK_REFEXPIRE;
|
|
|
|
if (mp->mnt_kern_flag & MNTK_MWAIT) {
|
|
|
|
mp->mnt_kern_flag &= ~MNTK_MWAIT;
|
|
|
|
wakeup(mp);
|
|
|
|
}
|
2008-11-02 10:15:42 +00:00
|
|
|
while (mp->mnt_ref)
|
|
|
|
msleep(mp, MNT_MTX(mp), PVFS, "mntref", 0);
|
1) Fix a deadlock in the VFS:
- threadA runs vfs_rel(mp1)
- threadB does unmount the mp1 fs, sets MNTK_UNMOUNT and drop MNT_ILOCK()
- threadA runs vfs_busy(mp1) and, as long as, MNTK_UNMOUNT is set, sleeps
waiting for threadB to complete the unmount
- threadB, in vfs_mount_destroy(), finds mnt_lock > 0 and sleeps waiting
for the refcount to expire.
Fix the deadlock by adding a flag called MNTK_REFEXPIRE which signals the
unmounter is waiting for mnt_ref to expire.
The vfs_busy contenders got awake, fails, and if they retry the
MNTK_REFEXPIRE won't allow them to sleep again.
2) Simplify significantly the code of vfs_mount_destroy() trimming
unnecessary codes:
- as long as any reference exited, it is no-more possible to have
write-op (primarty and secondary) in progress.
- it is no needed to drop and reacquire the mount lock.
- filling the structures with dummy values is unuseful as long as
it is going to be freed.
Tested by: pho, Andrea Barberio <insomniac at slackware dot it>
Discussed with: kib
2008-12-16 23:16:10 +00:00
|
|
|
KASSERT(mp->mnt_ref == 0,
|
|
|
|
("%s: invalid refcount in the drain path @ %s:%d", __func__,
|
|
|
|
__FILE__, __LINE__));
|
|
|
|
if (mp->mnt_writeopcount != 0)
|
|
|
|
panic("vfs_mount_destroy: nonzero writeopcount");
|
|
|
|
if (mp->mnt_secondary_writes != 0)
|
|
|
|
panic("vfs_mount_destroy: nonzero secondary_writes");
|
2003-11-12 02:54:47 +00:00
|
|
|
mp->mnt_vfc->vfc_refcount--;
|
2006-03-31 23:38:15 +00:00
|
|
|
if (!TAILQ_EMPTY(&mp->mnt_nvnodelist)) {
|
|
|
|
struct vnode *vp;
|
|
|
|
|
|
|
|
TAILQ_FOREACH(vp, &mp->mnt_nvnodelist, v_nmntvnodes)
|
|
|
|
vprint("", vp);
|
2003-11-12 02:54:47 +00:00
|
|
|
panic("unmount: dangling vnode");
|
2006-03-31 23:38:15 +00:00
|
|
|
}
|
2006-01-09 20:42:19 +00:00
|
|
|
if (mp->mnt_nvnodelistsize != 0)
|
|
|
|
panic("vfs_mount_destroy: nonzero nvnodelistsize");
|
1) Fix a deadlock in the VFS:
- threadA runs vfs_rel(mp1)
- threadB does unmount the mp1 fs, sets MNTK_UNMOUNT and drop MNT_ILOCK()
- threadA runs vfs_busy(mp1) and, as long as, MNTK_UNMOUNT is set, sleeps
waiting for threadB to complete the unmount
- threadB, in vfs_mount_destroy(), finds mnt_lock > 0 and sleeps waiting
for the refcount to expire.
Fix the deadlock by adding a flag called MNTK_REFEXPIRE which signals the
unmounter is waiting for mnt_ref to expire.
The vfs_busy contenders got awake, fails, and if they retry the
MNTK_REFEXPIRE won't allow them to sleep again.
2) Simplify significantly the code of vfs_mount_destroy() trimming
unnecessary codes:
- as long as any reference exited, it is no-more possible to have
write-op (primarty and secondary) in progress.
- it is no needed to drop and reacquire the mount lock.
- filling the structures with dummy values is unuseful as long as
it is going to be freed.
Tested by: pho, Andrea Barberio <insomniac at slackware dot it>
Discussed with: kib
2008-12-16 23:16:10 +00:00
|
|
|
if (mp->mnt_lockref != 0)
|
|
|
|
panic("vfs_mount_destroy: nonzero lock refcount");
|
2005-01-24 10:28:41 +00:00
|
|
|
MNT_IUNLOCK(mp);
|
2003-11-12 02:54:47 +00:00
|
|
|
#ifdef MAC
|
2007-10-24 19:04:04 +00:00
|
|
|
mac_mount_destroy(mp);
|
2003-11-12 02:54:47 +00:00
|
|
|
#endif
|
2003-11-23 17:13:48 +00:00
|
|
|
if (mp->mnt_opt != NULL)
|
2003-11-12 02:54:47 +00:00
|
|
|
vfs_freeopts(mp->mnt_opt);
|
|
|
|
crfree(mp->mnt_cred);
|
2006-03-31 03:49:51 +00:00
|
|
|
uma_zfree(mount_zone, mp);
|
2003-11-12 02:54:47 +00:00
|
|
|
}
|
|
|
|
|
2008-09-19 15:17:32 +00:00
|
|
|
int
|
2004-07-27 22:32:01 +00:00
|
|
|
vfs_donmount(struct thread *td, int fsflags, struct uio *fsoptions)
|
2003-11-12 02:54:47 +00:00
|
|
|
{
|
2002-07-02 17:09:22 +00:00
|
|
|
struct vfsoptlist *optlist;
|
2008-07-12 20:12:40 +00:00
|
|
|
struct vfsopt *opt, *noro_opt, *tmp_opt;
|
2005-11-23 20:51:15 +00:00
|
|
|
char *fstype, *fspath, *errmsg;
|
|
|
|
int error, fstypelen, fspathlen, errmsg_len, errmsg_pos;
|
2006-05-14 01:51:38 +00:00
|
|
|
int has_rw, has_noro;
|
2005-11-12 14:41:44 +00:00
|
|
|
|
2008-09-19 15:17:32 +00:00
|
|
|
errmsg = fspath = NULL;
|
|
|
|
errmsg_len = has_noro = has_rw = fspathlen = 0;
|
2005-11-23 20:51:15 +00:00
|
|
|
errmsg_pos = -1;
|
2002-07-02 17:09:22 +00:00
|
|
|
|
|
|
|
error = vfs_buildopts(fsoptions, &optlist);
|
|
|
|
if (error)
|
|
|
|
return (error);
|
|
|
|
|
2007-03-14 12:54:10 +00:00
|
|
|
if (vfs_getopt(optlist, "errmsg", (void **)&errmsg, &errmsg_len) == 0)
|
2005-11-23 20:51:15 +00:00
|
|
|
errmsg_pos = vfs_getopt_pos(optlist, "errmsg");
|
|
|
|
|
2002-07-02 17:09:22 +00:00
|
|
|
/*
|
|
|
|
* We need these two options before the others,
|
|
|
|
* and they are mandatory for any filesystem.
|
|
|
|
* Ensure they are NUL terminated as well.
|
|
|
|
*/
|
|
|
|
fstypelen = 0;
|
|
|
|
error = vfs_getopt(optlist, "fstype", (void **)&fstype, &fstypelen);
|
|
|
|
if (error || fstype[fstypelen - 1] != '\0') {
|
|
|
|
error = EINVAL;
|
2005-11-23 20:51:15 +00:00
|
|
|
if (errmsg != NULL)
|
|
|
|
strncpy(errmsg, "Invalid fstype", errmsg_len);
|
2003-11-12 02:54:47 +00:00
|
|
|
goto bail;
|
2002-07-02 17:09:22 +00:00
|
|
|
}
|
|
|
|
fspathlen = 0;
|
|
|
|
error = vfs_getopt(optlist, "fspath", (void **)&fspath, &fspathlen);
|
|
|
|
if (error || fspath[fspathlen - 1] != '\0') {
|
|
|
|
error = EINVAL;
|
2005-11-23 20:51:15 +00:00
|
|
|
if (errmsg != NULL)
|
|
|
|
strncpy(errmsg, "Invalid fspath", errmsg_len);
|
2003-11-12 02:54:47 +00:00
|
|
|
goto bail;
|
2002-07-02 17:09:22 +00:00
|
|
|
}
|
|
|
|
|
2005-11-18 01:31:10 +00:00
|
|
|
/*
|
|
|
|
* We need to see if we have the "update" option
|
|
|
|
* before we call vfs_domount(), since vfs_domount() has special
|
|
|
|
* logic based on MNT_UPDATE. This is very important
|
|
|
|
* when we want to update the root filesystem.
|
2007-03-14 12:54:10 +00:00
|
|
|
*/
|
2008-07-12 20:12:40 +00:00
|
|
|
TAILQ_FOREACH_SAFE(opt, optlist, link, tmp_opt) {
|
|
|
|
if (strcmp(opt->name, "update") == 0) {
|
2006-03-13 14:58:37 +00:00
|
|
|
fsflags |= MNT_UPDATE;
|
2008-07-12 20:12:40 +00:00
|
|
|
vfs_freeopt(optlist, opt);
|
|
|
|
}
|
2006-03-13 14:58:37 +00:00
|
|
|
else if (strcmp(opt->name, "async") == 0)
|
|
|
|
fsflags |= MNT_ASYNC;
|
2008-08-23 01:16:09 +00:00
|
|
|
else if (strcmp(opt->name, "force") == 0) {
|
2006-03-13 14:58:37 +00:00
|
|
|
fsflags |= MNT_FORCE;
|
2008-08-23 01:16:09 +00:00
|
|
|
vfs_freeopt(optlist, opt);
|
|
|
|
}
|
|
|
|
else if (strcmp(opt->name, "reload") == 0) {
|
|
|
|
fsflags |= MNT_RELOAD;
|
|
|
|
vfs_freeopt(optlist, opt);
|
|
|
|
}
|
2006-03-13 14:58:37 +00:00
|
|
|
else if (strcmp(opt->name, "multilabel") == 0)
|
|
|
|
fsflags |= MNT_MULTILABEL;
|
|
|
|
else if (strcmp(opt->name, "noasync") == 0)
|
|
|
|
fsflags &= ~MNT_ASYNC;
|
|
|
|
else if (strcmp(opt->name, "noatime") == 0)
|
|
|
|
fsflags |= MNT_NOATIME;
|
On 6.x this works:
% mount | grep home
/dev/ad4s1e on /home (ufs, local, noatime, soft-updates)
% mount -u -o atime /home
% mount | grep home
/dev/ad4s1e on /home (ufs, local, soft-updates)
Restore this behavior for on 7.x for the following mount options:
noatime, noclusterr, noclusterw, noexec, nosuid, nosymfollow
In addition, on 7.x, the following are equivalent:
mount -u -o atime /home
mount -u -o nonoatime /home
Ideally, when we introduce new mount options, we should avoid
options starting with "no". :)
Requested by: jhb
Reported by: Karol Kwiat <karol.kwiat gmail com>, Scott Hetzel <swhetzel gmail com>
Approved by: re (bmah)
Proxy commit for: rodrigc
2007-08-15 17:40:09 +00:00
|
|
|
else if (strcmp(opt->name, "atime") == 0) {
|
|
|
|
free(opt->name, M_MOUNT);
|
|
|
|
opt->name = strdup("nonoatime", M_MOUNT);
|
|
|
|
}
|
2006-03-13 14:58:37 +00:00
|
|
|
else if (strcmp(opt->name, "noclusterr") == 0)
|
|
|
|
fsflags |= MNT_NOCLUSTERR;
|
On 6.x this works:
% mount | grep home
/dev/ad4s1e on /home (ufs, local, noatime, soft-updates)
% mount -u -o atime /home
% mount | grep home
/dev/ad4s1e on /home (ufs, local, soft-updates)
Restore this behavior for on 7.x for the following mount options:
noatime, noclusterr, noclusterw, noexec, nosuid, nosymfollow
In addition, on 7.x, the following are equivalent:
mount -u -o atime /home
mount -u -o nonoatime /home
Ideally, when we introduce new mount options, we should avoid
options starting with "no". :)
Requested by: jhb
Reported by: Karol Kwiat <karol.kwiat gmail com>, Scott Hetzel <swhetzel gmail com>
Approved by: re (bmah)
Proxy commit for: rodrigc
2007-08-15 17:40:09 +00:00
|
|
|
else if (strcmp(opt->name, "clusterr") == 0) {
|
|
|
|
free(opt->name, M_MOUNT);
|
|
|
|
opt->name = strdup("nonoclusterr", M_MOUNT);
|
|
|
|
}
|
2006-03-13 14:58:37 +00:00
|
|
|
else if (strcmp(opt->name, "noclusterw") == 0)
|
|
|
|
fsflags |= MNT_NOCLUSTERW;
|
On 6.x this works:
% mount | grep home
/dev/ad4s1e on /home (ufs, local, noatime, soft-updates)
% mount -u -o atime /home
% mount | grep home
/dev/ad4s1e on /home (ufs, local, soft-updates)
Restore this behavior for on 7.x for the following mount options:
noatime, noclusterr, noclusterw, noexec, nosuid, nosymfollow
In addition, on 7.x, the following are equivalent:
mount -u -o atime /home
mount -u -o nonoatime /home
Ideally, when we introduce new mount options, we should avoid
options starting with "no". :)
Requested by: jhb
Reported by: Karol Kwiat <karol.kwiat gmail com>, Scott Hetzel <swhetzel gmail com>
Approved by: re (bmah)
Proxy commit for: rodrigc
2007-08-15 17:40:09 +00:00
|
|
|
else if (strcmp(opt->name, "clusterw") == 0) {
|
|
|
|
free(opt->name, M_MOUNT);
|
|
|
|
opt->name = strdup("nonoclusterw", M_MOUNT);
|
|
|
|
}
|
2006-03-13 14:58:37 +00:00
|
|
|
else if (strcmp(opt->name, "noexec") == 0)
|
|
|
|
fsflags |= MNT_NOEXEC;
|
On 6.x this works:
% mount | grep home
/dev/ad4s1e on /home (ufs, local, noatime, soft-updates)
% mount -u -o atime /home
% mount | grep home
/dev/ad4s1e on /home (ufs, local, soft-updates)
Restore this behavior for on 7.x for the following mount options:
noatime, noclusterr, noclusterw, noexec, nosuid, nosymfollow
In addition, on 7.x, the following are equivalent:
mount -u -o atime /home
mount -u -o nonoatime /home
Ideally, when we introduce new mount options, we should avoid
options starting with "no". :)
Requested by: jhb
Reported by: Karol Kwiat <karol.kwiat gmail com>, Scott Hetzel <swhetzel gmail com>
Approved by: re (bmah)
Proxy commit for: rodrigc
2007-08-15 17:40:09 +00:00
|
|
|
else if (strcmp(opt->name, "exec") == 0) {
|
|
|
|
free(opt->name, M_MOUNT);
|
|
|
|
opt->name = strdup("nonoexec", M_MOUNT);
|
|
|
|
}
|
2006-03-13 14:58:37 +00:00
|
|
|
else if (strcmp(opt->name, "nosuid") == 0)
|
|
|
|
fsflags |= MNT_NOSUID;
|
On 6.x this works:
% mount | grep home
/dev/ad4s1e on /home (ufs, local, noatime, soft-updates)
% mount -u -o atime /home
% mount | grep home
/dev/ad4s1e on /home (ufs, local, soft-updates)
Restore this behavior for on 7.x for the following mount options:
noatime, noclusterr, noclusterw, noexec, nosuid, nosymfollow
In addition, on 7.x, the following are equivalent:
mount -u -o atime /home
mount -u -o nonoatime /home
Ideally, when we introduce new mount options, we should avoid
options starting with "no". :)
Requested by: jhb
Reported by: Karol Kwiat <karol.kwiat gmail com>, Scott Hetzel <swhetzel gmail com>
Approved by: re (bmah)
Proxy commit for: rodrigc
2007-08-15 17:40:09 +00:00
|
|
|
else if (strcmp(opt->name, "suid") == 0) {
|
|
|
|
free(opt->name, M_MOUNT);
|
|
|
|
opt->name = strdup("nonosuid", M_MOUNT);
|
|
|
|
}
|
2006-03-13 14:58:37 +00:00
|
|
|
else if (strcmp(opt->name, "nosymfollow") == 0)
|
|
|
|
fsflags |= MNT_NOSYMFOLLOW;
|
On 6.x this works:
% mount | grep home
/dev/ad4s1e on /home (ufs, local, noatime, soft-updates)
% mount -u -o atime /home
% mount | grep home
/dev/ad4s1e on /home (ufs, local, soft-updates)
Restore this behavior for on 7.x for the following mount options:
noatime, noclusterr, noclusterw, noexec, nosuid, nosymfollow
In addition, on 7.x, the following are equivalent:
mount -u -o atime /home
mount -u -o nonoatime /home
Ideally, when we introduce new mount options, we should avoid
options starting with "no". :)
Requested by: jhb
Reported by: Karol Kwiat <karol.kwiat gmail com>, Scott Hetzel <swhetzel gmail com>
Approved by: re (bmah)
Proxy commit for: rodrigc
2007-08-15 17:40:09 +00:00
|
|
|
else if (strcmp(opt->name, "symfollow") == 0) {
|
|
|
|
free(opt->name, M_MOUNT);
|
|
|
|
opt->name = strdup("nonosymfollow", M_MOUNT);
|
|
|
|
}
|
2006-05-14 01:51:38 +00:00
|
|
|
else if (strcmp(opt->name, "noro") == 0) {
|
|
|
|
fsflags &= ~MNT_RDONLY;
|
|
|
|
has_noro = 1;
|
|
|
|
}
|
|
|
|
else if (strcmp(opt->name, "rw") == 0) {
|
2006-03-13 14:58:37 +00:00
|
|
|
fsflags &= ~MNT_RDONLY;
|
2006-05-14 01:51:38 +00:00
|
|
|
has_rw = 1;
|
|
|
|
}
|
2007-12-05 03:26:14 +00:00
|
|
|
else if (strcmp(opt->name, "ro") == 0)
|
2006-03-13 14:58:37 +00:00
|
|
|
fsflags |= MNT_RDONLY;
|
2007-12-05 03:26:14 +00:00
|
|
|
else if (strcmp(opt->name, "rdonly") == 0) {
|
|
|
|
free(opt->name, M_MOUNT);
|
|
|
|
opt->name = strdup("ro", M_MOUNT);
|
|
|
|
fsflags |= MNT_RDONLY;
|
|
|
|
}
|
2006-03-13 14:58:37 +00:00
|
|
|
else if (strcmp(opt->name, "suiddir") == 0)
|
|
|
|
fsflags |= MNT_SUIDDIR;
|
|
|
|
else if (strcmp(opt->name, "sync") == 0)
|
|
|
|
fsflags |= MNT_SYNCHRONOUS;
|
|
|
|
else if (strcmp(opt->name, "union") == 0)
|
|
|
|
fsflags |= MNT_UNION;
|
|
|
|
}
|
Parse more mount options in vfs_donmount(), before vfs_domount()
is called. It looks like there are lots of different mount flags checked
in vfs_domount(), so we need to do the parsing for these particular
mount flags earlier on. The new flags parsed are:
async, force, multilabel, noasync, noatime, noclusterr, noclusterw,
noexec, nosuid, nosymfollow, snapshot, suiddir, sync, union.
Existing code which uses mount() to mount UFS filesystems is not
affected, but new code which uses nmount() to mount UFS filesystems
should behave better.
2005-11-19 21:22:21 +00:00
|
|
|
|
2006-05-14 01:51:38 +00:00
|
|
|
/*
|
|
|
|
* If "rw" was specified as a mount option, and we
|
|
|
|
* are trying to update a mount-point from "ro" to "rw",
|
|
|
|
* we need a mount option "noro", since in vfs_mergeopts(),
|
|
|
|
* "noro" will cancel "ro", but "rw" will not do anything.
|
|
|
|
*/
|
|
|
|
if (has_rw && !has_noro) {
|
|
|
|
noro_opt = malloc(sizeof(struct vfsopt), M_MOUNT, M_WAITOK);
|
|
|
|
noro_opt->name = strdup("noro", M_MOUNT);
|
|
|
|
noro_opt->value = NULL;
|
|
|
|
noro_opt->len = 0;
|
2009-03-02 23:26:30 +00:00
|
|
|
noro_opt->pos = -1;
|
|
|
|
noro_opt->seen = 1;
|
2006-05-14 01:51:38 +00:00
|
|
|
TAILQ_INSERT_TAIL(optlist, noro_opt, link);
|
|
|
|
}
|
|
|
|
|
2002-07-02 17:09:22 +00:00
|
|
|
/*
|
|
|
|
* Be ultra-paranoid about making sure the type and fspath
|
|
|
|
* variables will fit in our mp buffers, including the
|
|
|
|
* terminating NUL.
|
|
|
|
*/
|
|
|
|
if (fstypelen >= MFSNAMELEN - 1 || fspathlen >= MNAMELEN - 1) {
|
|
|
|
error = ENAMETOOLONG;
|
2003-11-12 02:54:47 +00:00
|
|
|
goto bail;
|
2002-07-02 17:09:22 +00:00
|
|
|
}
|
|
|
|
|
2004-11-25 12:06:43 +00:00
|
|
|
mtx_lock(&Giant);
|
2004-12-07 08:15:41 +00:00
|
|
|
error = vfs_domount(td, fstype, fspath, fsflags, optlist);
|
2004-11-25 12:06:43 +00:00
|
|
|
mtx_unlock(&Giant);
|
2003-11-12 02:54:47 +00:00
|
|
|
bail:
|
2005-11-23 20:51:15 +00:00
|
|
|
/* copyout the errmsg */
|
|
|
|
if (errmsg_pos != -1 && ((2 * errmsg_pos + 1) < fsoptions->uio_iovcnt)
|
|
|
|
&& errmsg_len > 0 && errmsg != NULL) {
|
|
|
|
if (fsoptions->uio_segflg == UIO_SYSSPACE) {
|
2006-08-24 18:52:28 +00:00
|
|
|
bcopy(errmsg,
|
|
|
|
fsoptions->uio_iov[2 * errmsg_pos + 1].iov_base,
|
2005-11-23 20:51:15 +00:00
|
|
|
fsoptions->uio_iov[2 * errmsg_pos + 1].iov_len);
|
|
|
|
} else {
|
2006-08-24 18:52:28 +00:00
|
|
|
copyout(errmsg,
|
2005-11-23 20:51:15 +00:00
|
|
|
fsoptions->uio_iov[2 * errmsg_pos + 1].iov_base,
|
2006-08-24 18:52:28 +00:00
|
|
|
fsoptions->uio_iov[2 * errmsg_pos + 1].iov_len);
|
2007-03-14 12:54:10 +00:00
|
|
|
}
|
2005-11-09 02:26:38 +00:00
|
|
|
}
|
2005-11-23 20:51:15 +00:00
|
|
|
|
2005-11-12 14:41:44 +00:00
|
|
|
if (error != 0)
|
2003-11-12 02:54:47 +00:00
|
|
|
vfs_freeopts(optlist);
|
2002-07-02 17:09:22 +00:00
|
|
|
return (error);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Old mount API.
|
|
|
|
*/
|
|
|
|
#ifndef _SYS_SYSPROTO_H_
|
|
|
|
struct mount_args {
|
|
|
|
char *type;
|
|
|
|
char *path;
|
|
|
|
int flags;
|
|
|
|
caddr_t data;
|
|
|
|
};
|
|
|
|
#endif
|
|
|
|
/* ARGSUSED */
|
|
|
|
int
|
|
|
|
mount(td, uap)
|
|
|
|
struct thread *td;
|
|
|
|
struct mount_args /* {
|
2002-12-14 02:07:32 +00:00
|
|
|
char *type;
|
|
|
|
char *path;
|
|
|
|
int flags;
|
|
|
|
caddr_t data;
|
2002-07-02 17:09:22 +00:00
|
|
|
} */ *uap;
|
|
|
|
{
|
|
|
|
char *fstype;
|
2004-12-07 08:15:41 +00:00
|
|
|
struct vfsconf *vfsp = NULL;
|
2004-12-06 16:39:05 +00:00
|
|
|
struct mntarg *ma = NULL;
|
2002-07-02 17:09:22 +00:00
|
|
|
int error;
|
|
|
|
|
2006-06-05 15:32:07 +00:00
|
|
|
AUDIT_ARG(fflags, uap->flags);
|
|
|
|
|
2007-10-27 15:59:18 +00:00
|
|
|
/*
|
|
|
|
* Filter out MNT_ROOTFS. We do not want clients of mount() in
|
|
|
|
* userspace to set this flag, but we must filter it out if we want
|
|
|
|
* MNT_UPDATE on the root file system to work.
|
|
|
|
* MNT_ROOTFS should only be set in the kernel in vfs_mountroot_try().
|
|
|
|
*/
|
2004-12-07 06:58:42 +00:00
|
|
|
uap->flags &= ~MNT_ROOTFS;
|
2004-12-03 19:25:44 +00:00
|
|
|
|
2004-12-07 08:15:41 +00:00
|
|
|
fstype = malloc(MFSNAMELEN, M_TEMP, M_WAITOK);
|
2002-12-14 01:56:26 +00:00
|
|
|
error = copyinstr(uap->type, fstype, MFSNAMELEN, NULL);
|
2006-06-27 14:46:31 +00:00
|
|
|
if (error) {
|
|
|
|
free(fstype, M_TEMP);
|
|
|
|
return (error);
|
2004-11-25 12:06:43 +00:00
|
|
|
}
|
2006-06-27 14:46:31 +00:00
|
|
|
|
|
|
|
AUDIT_ARG(text, fstype);
|
|
|
|
mtx_lock(&Giant);
|
|
|
|
vfsp = vfs_byname_kld(fstype, td, &error);
|
2002-07-02 17:09:22 +00:00
|
|
|
free(fstype, M_TEMP);
|
2006-06-27 14:46:31 +00:00
|
|
|
if (vfsp == NULL) {
|
|
|
|
mtx_unlock(&Giant);
|
2004-12-07 08:15:41 +00:00
|
|
|
return (ENOENT);
|
2006-06-27 14:46:31 +00:00
|
|
|
}
|
|
|
|
if (vfsp->vfc_vfsops->vfs_cmount == NULL) {
|
|
|
|
mtx_unlock(&Giant);
|
2004-12-07 08:15:41 +00:00
|
|
|
return (EOPNOTSUPP);
|
2006-06-27 14:46:31 +00:00
|
|
|
}
|
2004-12-07 08:15:41 +00:00
|
|
|
|
|
|
|
ma = mount_argsu(ma, "fstype", uap->type, MNAMELEN);
|
|
|
|
ma = mount_argsu(ma, "fspath", uap->path, MNAMELEN);
|
|
|
|
ma = mount_argb(ma, uap->flags & MNT_RDONLY, "noro");
|
|
|
|
ma = mount_argb(ma, !(uap->flags & MNT_NOSUID), "nosuid");
|
|
|
|
ma = mount_argb(ma, !(uap->flags & MNT_NOEXEC), "noexec");
|
|
|
|
|
|
|
|
error = vfsp->vfc_vfsops->vfs_cmount(ma, uap->data, uap->flags, td);
|
2006-06-27 14:46:31 +00:00
|
|
|
mtx_unlock(&Giant);
|
2002-07-02 17:09:22 +00:00
|
|
|
return (error);
|
|
|
|
}
|
|
|
|
|
2004-12-07 08:15:41 +00:00
|
|
|
|
2003-11-12 02:54:47 +00:00
|
|
|
/*
|
|
|
|
* vfs_domount(): actually attempt a filesystem mount.
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
vfs_domount(
|
2006-08-25 15:13:49 +00:00
|
|
|
struct thread *td, /* Calling thread. */
|
2003-11-14 05:27:41 +00:00
|
|
|
const char *fstype, /* Filesystem type. */
|
|
|
|
char *fspath, /* Mount path. */
|
|
|
|
int fsflags, /* Flags common to all filesystems. */
|
2004-12-07 08:15:41 +00:00
|
|
|
void *fsdata /* Options local to the filesystem. */
|
2003-11-14 05:27:41 +00:00
|
|
|
)
|
2002-07-02 17:09:22 +00:00
|
|
|
{
|
|
|
|
struct vnode *vp;
|
|
|
|
struct mount *mp;
|
|
|
|
struct vfsconf *vfsp;
|
Implement support for RPCSEC_GSS authentication to both the NFS client
and server. This replaces the RPC implementation of the NFS client and
server with the newer RPC implementation originally developed
(actually ported from the userland sunrpc code) to support the NFS
Lock Manager. I have tested this code extensively and I believe it is
stable and that performance is at least equal to the legacy RPC
implementation.
The NFS code currently contains support for both the new RPC
implementation and the older legacy implementation inherited from the
original NFS codebase. The default is to use the new implementation -
add the NFS_LEGACYRPC option to fall back to the old code. When I
merge this support back to RELENG_7, I will probably change this so
that users have to 'opt in' to get the new code.
To use RPCSEC_GSS on either client or server, you must build a kernel
which includes the KGSSAPI option and the crypto device. On the
userland side, you must build at least a new libc, mountd, mount_nfs
and gssd. You must install new versions of /etc/rc.d/gssd and
/etc/rc.d/nfsd and add 'gssd_enable=YES' to /etc/rc.conf.
As long as gssd is running, you should be able to mount an NFS
filesystem from a server that requires RPCSEC_GSS authentication. The
mount itself can happen without any kerberos credentials but all
access to the filesystem will be denied unless the accessing user has
a valid ticket file in the standard place (/tmp/krb5cc_<uid>). There
is currently no support for situations where the ticket file is in a
different place, such as when the user logged in via SSH and has
delegated credentials from that login. This restriction is also
present in Solaris and Linux. In theory, we could improve this in
future, possibly using Brooks Davis' implementation of variant
symlinks.
Supporting RPCSEC_GSS on a server is nearly as simple. You must create
service creds for the server in the form 'nfs/<fqdn>@<REALM>' and
install them in /etc/krb5.keytab. The standard heimdal utility ktutil
makes this fairly easy. After the service creds have been created, you
can add a '-sec=krb5' option to /etc/exports and restart both mountd
and nfsd.
The only other difference an administrator should notice is that nfsd
doesn't fork to create service threads any more. In normal operation,
there will be two nfsd processes, one in userland waiting for TCP
connections and one in the kernel handling requests. The latter
process will create as many kthreads as required - these should be
visible via 'top -H'. The code has some support for varying the number
of service threads according to load but initially at least, nfsd uses
a fixed number of threads according to the value supplied to its '-n'
option.
Sponsored by: Isilon Systems
MFC after: 1 month
2008-11-03 10:38:00 +00:00
|
|
|
struct oexport_args oexport;
|
2006-05-26 00:32:21 +00:00
|
|
|
struct export_args export;
|
2006-09-26 04:15:04 +00:00
|
|
|
int error, flag = 0;
|
2002-07-02 17:09:22 +00:00
|
|
|
struct vattr va;
|
|
|
|
struct nameidata nd;
|
|
|
|
|
2004-11-25 12:06:43 +00:00
|
|
|
mtx_assert(&Giant, MA_OWNED);
|
2002-07-02 17:09:22 +00:00
|
|
|
/*
|
|
|
|
* Be ultra-paranoid about making sure the type and fspath
|
|
|
|
* variables will fit in our mp buffers, including the
|
|
|
|
* terminating NUL.
|
|
|
|
*/
|
|
|
|
if (strlen(fstype) >= MFSNAMELEN || strlen(fspath) >= MNAMELEN)
|
|
|
|
return (ENAMETOOLONG);
|
|
|
|
|
2007-03-14 13:09:59 +00:00
|
|
|
if (jailed(td->td_ucred) || usermount == 0) {
|
2006-11-06 13:42:10 +00:00
|
|
|
if ((error = priv_check(td, PRIV_VFS_MOUNT)) != 0)
|
2002-07-02 17:09:22 +00:00
|
|
|
return (error);
|
|
|
|
}
|
2004-03-27 08:09:00 +00:00
|
|
|
|
2002-07-02 17:09:22 +00:00
|
|
|
/*
|
2004-03-27 08:09:00 +00:00
|
|
|
* Do not allow NFS export or MNT_SUIDDIR by unprivileged users.
|
2002-07-02 17:09:22 +00:00
|
|
|
*/
|
2006-11-06 13:42:10 +00:00
|
|
|
if (fsflags & MNT_EXPORTED) {
|
|
|
|
error = priv_check(td, PRIV_VFS_MOUNT_EXPORTED);
|
|
|
|
if (error)
|
2002-07-02 17:09:22 +00:00
|
|
|
return (error);
|
|
|
|
}
|
2006-11-06 13:42:10 +00:00
|
|
|
if (fsflags & MNT_SUIDDIR) {
|
|
|
|
error = priv_check(td, PRIV_VFS_MOUNT_SUIDDIR);
|
|
|
|
if (error)
|
|
|
|
return (error);
|
|
|
|
}
|
2002-07-02 17:09:22 +00:00
|
|
|
/*
|
2006-11-06 13:42:10 +00:00
|
|
|
* Silently enforce MNT_NOSUID and MNT_USER for unprivileged users.
|
2002-07-02 17:09:22 +00:00
|
|
|
*/
|
2006-11-06 13:42:10 +00:00
|
|
|
if ((fsflags & (MNT_NOSUID | MNT_USER)) != (MNT_NOSUID | MNT_USER)) {
|
|
|
|
if (priv_check(td, PRIV_VFS_MOUNT_NONUSER) != 0)
|
|
|
|
fsflags |= MNT_NOSUID | MNT_USER;
|
|
|
|
}
|
2006-02-22 06:29:55 +00:00
|
|
|
|
|
|
|
/* Load KLDs before we lock the covered vnode to avoid reversals. */
|
|
|
|
vfsp = NULL;
|
|
|
|
if ((fsflags & MNT_UPDATE) == 0) {
|
|
|
|
/* Don't try to load KLDs if we're mounting the root. */
|
|
|
|
if (fsflags & MNT_ROOTFS)
|
|
|
|
vfsp = vfs_byname(fstype);
|
|
|
|
else
|
|
|
|
vfsp = vfs_byname_kld(fstype, td, &error);
|
|
|
|
if (vfsp == NULL)
|
|
|
|
return (ENODEV);
|
2007-04-05 21:03:05 +00:00
|
|
|
if (jailed(td->td_ucred) && !(vfsp->vfc_flags & VFCF_JAIL))
|
|
|
|
return (EPERM);
|
2006-02-22 06:29:55 +00:00
|
|
|
}
|
2002-07-02 17:09:22 +00:00
|
|
|
/*
|
|
|
|
* Get vnode to be covered
|
|
|
|
*/
|
2006-06-05 15:32:07 +00:00
|
|
|
NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF | AUDITVNODE1, UIO_SYSSPACE,
|
|
|
|
fspath, td);
|
2002-07-02 17:09:22 +00:00
|
|
|
if ((error = namei(&nd)) != 0)
|
|
|
|
return (error);
|
|
|
|
NDFREE(&nd, NDF_ONLY_PNBUF);
|
|
|
|
vp = nd.ni_vp;
|
|
|
|
if (fsflags & MNT_UPDATE) {
|
2002-08-04 10:29:36 +00:00
|
|
|
if ((vp->v_vflag & VV_ROOT) == 0) {
|
2002-07-02 17:09:22 +00:00
|
|
|
vput(vp);
|
|
|
|
return (EINVAL);
|
|
|
|
}
|
|
|
|
mp = vp->v_mount;
|
2006-09-26 04:12:49 +00:00
|
|
|
MNT_ILOCK(mp);
|
2002-07-02 17:09:22 +00:00
|
|
|
flag = mp->mnt_flag;
|
|
|
|
/*
|
|
|
|
* We only allow the filesystem to be reloaded if it
|
|
|
|
* is currently mounted read-only.
|
|
|
|
*/
|
|
|
|
if ((fsflags & MNT_RELOAD) &&
|
|
|
|
((mp->mnt_flag & MNT_RDONLY) == 0)) {
|
2006-09-26 04:12:49 +00:00
|
|
|
MNT_IUNLOCK(mp);
|
2002-07-02 17:09:22 +00:00
|
|
|
vput(vp);
|
|
|
|
return (EOPNOTSUPP); /* Needs translation */
|
|
|
|
}
|
2006-09-26 04:12:49 +00:00
|
|
|
MNT_IUNLOCK(mp);
|
2002-07-02 17:09:22 +00:00
|
|
|
/*
|
2004-03-27 08:39:28 +00:00
|
|
|
* Only privileged root, or (if MNT_USER is set) the user that
|
|
|
|
* did the original mount is permitted to update it.
|
2002-07-02 17:09:22 +00:00
|
|
|
*/
|
2004-07-06 09:39:32 +00:00
|
|
|
error = vfs_suser(mp, td);
|
|
|
|
if (error) {
|
|
|
|
vput(vp);
|
|
|
|
return (error);
|
2002-07-02 17:09:22 +00:00
|
|
|
}
|
2008-11-02 10:15:42 +00:00
|
|
|
if (vfs_busy(mp, MBF_NOWAIT)) {
|
2002-07-02 17:09:22 +00:00
|
|
|
vput(vp);
|
|
|
|
return (EBUSY);
|
|
|
|
}
|
2002-08-04 10:29:36 +00:00
|
|
|
VI_LOCK(vp);
|
|
|
|
if ((vp->v_iflag & VI_MOUNT) != 0 ||
|
|
|
|
vp->v_mountedhere != NULL) {
|
|
|
|
VI_UNLOCK(vp);
|
2008-08-31 14:26:08 +00:00
|
|
|
vfs_unbusy(mp);
|
2002-07-02 17:09:22 +00:00
|
|
|
vput(vp);
|
|
|
|
return (EBUSY);
|
|
|
|
}
|
2002-08-04 10:29:36 +00:00
|
|
|
vp->v_iflag |= VI_MOUNT;
|
|
|
|
VI_UNLOCK(vp);
|
2006-09-26 04:12:49 +00:00
|
|
|
MNT_ILOCK(mp);
|
2002-07-02 17:09:22 +00:00
|
|
|
mp->mnt_flag |= fsflags &
|
2004-12-07 08:15:41 +00:00
|
|
|
(MNT_RELOAD | MNT_FORCE | MNT_UPDATE | MNT_SNAPSHOT | MNT_ROOTFS);
|
2006-09-26 04:12:49 +00:00
|
|
|
MNT_IUNLOCK(mp);
|
2008-01-13 14:44:15 +00:00
|
|
|
VOP_UNLOCK(vp, 0);
|
2004-12-07 08:15:41 +00:00
|
|
|
mp->mnt_optnew = fsdata;
|
|
|
|
vfs_mergeopts(mp->mnt_optnew, mp->mnt_opt);
|
2004-07-27 22:32:01 +00:00
|
|
|
} else {
|
|
|
|
/*
|
|
|
|
* If the user is not root, ensure that they own the directory
|
|
|
|
* onto which we are attempting to mount.
|
|
|
|
*/
|
2008-08-28 15:23:18 +00:00
|
|
|
error = VOP_GETATTR(vp, &va, td->td_ucred);
|
2004-07-27 22:32:01 +00:00
|
|
|
if (error) {
|
2002-07-02 17:09:22 +00:00
|
|
|
vput(vp);
|
|
|
|
return (error);
|
|
|
|
}
|
2004-07-27 22:32:01 +00:00
|
|
|
if (va.va_uid != td->td_ucred->cr_uid) {
|
2006-11-06 13:42:10 +00:00
|
|
|
error = priv_check_cred(td->td_ucred, PRIV_VFS_ADMIN,
|
2007-06-12 00:12:01 +00:00
|
|
|
0);
|
2006-11-06 13:42:10 +00:00
|
|
|
if (error) {
|
2004-07-27 22:32:01 +00:00
|
|
|
vput(vp);
|
|
|
|
return (error);
|
|
|
|
}
|
2002-07-02 17:09:22 +00:00
|
|
|
}
|
2008-10-10 21:23:50 +00:00
|
|
|
error = vinvalbuf(vp, V_SAVE, 0, 0);
|
2004-12-07 08:15:41 +00:00
|
|
|
if (error != 0) {
|
2002-07-02 17:09:22 +00:00
|
|
|
vput(vp);
|
|
|
|
return (error);
|
|
|
|
}
|
2004-07-27 22:32:01 +00:00
|
|
|
if (vp->v_type != VDIR) {
|
2002-07-02 17:09:22 +00:00
|
|
|
vput(vp);
|
2004-07-27 22:32:01 +00:00
|
|
|
return (ENOTDIR);
|
2002-07-02 17:09:22 +00:00
|
|
|
}
|
2004-07-27 22:32:01 +00:00
|
|
|
VI_LOCK(vp);
|
|
|
|
if ((vp->v_iflag & VI_MOUNT) != 0 ||
|
|
|
|
vp->v_mountedhere != NULL) {
|
|
|
|
VI_UNLOCK(vp);
|
2002-07-02 17:09:22 +00:00
|
|
|
vput(vp);
|
2004-07-27 22:32:01 +00:00
|
|
|
return (EBUSY);
|
2002-07-02 17:09:22 +00:00
|
|
|
}
|
2004-07-27 22:32:01 +00:00
|
|
|
vp->v_iflag |= VI_MOUNT;
|
2002-08-04 10:29:36 +00:00
|
|
|
VI_UNLOCK(vp);
|
2002-07-02 17:09:22 +00:00
|
|
|
|
2004-07-27 22:32:01 +00:00
|
|
|
/*
|
|
|
|
* Allocate and initialize the filesystem.
|
|
|
|
*/
|
2008-08-31 14:26:08 +00:00
|
|
|
mp = vfs_mount_alloc(vp, vfsp, fspath, td->td_ucred);
|
2008-01-13 14:44:15 +00:00
|
|
|
VOP_UNLOCK(vp, 0);
|
2003-11-12 02:54:47 +00:00
|
|
|
|
2004-07-27 22:32:01 +00:00
|
|
|
/* XXXMAC: pass to vfs_mount_alloc? */
|
2004-12-07 08:15:41 +00:00
|
|
|
mp->mnt_optnew = fsdata;
|
2002-07-02 17:09:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Set the mount level flags.
|
|
|
|
*/
|
2006-09-26 04:12:49 +00:00
|
|
|
MNT_ILOCK(mp);
|
2006-09-26 04:20:09 +00:00
|
|
|
mp->mnt_flag = (mp->mnt_flag & ~MNT_UPDATEMASK) |
|
|
|
|
(fsflags & (MNT_UPDATEMASK | MNT_FORCE | MNT_ROOTFS |
|
|
|
|
MNT_RDONLY));
|
2006-09-26 04:15:59 +00:00
|
|
|
if ((mp->mnt_flag & MNT_ASYNC) == 0)
|
|
|
|
mp->mnt_kern_flag &= ~MNTK_ASYNC;
|
2006-09-26 04:12:49 +00:00
|
|
|
MNT_IUNLOCK(mp);
|
2002-07-02 17:09:22 +00:00
|
|
|
/*
|
|
|
|
* Mount the filesystem.
|
|
|
|
* XXX The final recipients of VFS_MOUNT just overwrite the ndp they
|
|
|
|
* get. No freeing of cn_pnbuf.
|
|
|
|
*/
|
2004-12-07 08:15:41 +00:00
|
|
|
error = VFS_MOUNT(mp, td);
|
2006-05-26 00:32:21 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Process the export option only if we are
|
|
|
|
* updating mount options.
|
|
|
|
*/
|
|
|
|
if (!error && (fsflags & MNT_UPDATE)) {
|
|
|
|
if (vfs_copyopt(mp->mnt_optnew, "export", &export,
|
|
|
|
sizeof(export)) == 0)
|
|
|
|
error = vfs_export(mp, &export);
|
Implement support for RPCSEC_GSS authentication to both the NFS client
and server. This replaces the RPC implementation of the NFS client and
server with the newer RPC implementation originally developed
(actually ported from the userland sunrpc code) to support the NFS
Lock Manager. I have tested this code extensively and I believe it is
stable and that performance is at least equal to the legacy RPC
implementation.
The NFS code currently contains support for both the new RPC
implementation and the older legacy implementation inherited from the
original NFS codebase. The default is to use the new implementation -
add the NFS_LEGACYRPC option to fall back to the old code. When I
merge this support back to RELENG_7, I will probably change this so
that users have to 'opt in' to get the new code.
To use RPCSEC_GSS on either client or server, you must build a kernel
which includes the KGSSAPI option and the crypto device. On the
userland side, you must build at least a new libc, mountd, mount_nfs
and gssd. You must install new versions of /etc/rc.d/gssd and
/etc/rc.d/nfsd and add 'gssd_enable=YES' to /etc/rc.conf.
As long as gssd is running, you should be able to mount an NFS
filesystem from a server that requires RPCSEC_GSS authentication. The
mount itself can happen without any kerberos credentials but all
access to the filesystem will be denied unless the accessing user has
a valid ticket file in the standard place (/tmp/krb5cc_<uid>). There
is currently no support for situations where the ticket file is in a
different place, such as when the user logged in via SSH and has
delegated credentials from that login. This restriction is also
present in Solaris and Linux. In theory, we could improve this in
future, possibly using Brooks Davis' implementation of variant
symlinks.
Supporting RPCSEC_GSS on a server is nearly as simple. You must create
service creds for the server in the form 'nfs/<fqdn>@<REALM>' and
install them in /etc/krb5.keytab. The standard heimdal utility ktutil
makes this fairly easy. After the service creds have been created, you
can add a '-sec=krb5' option to /etc/exports and restart both mountd
and nfsd.
The only other difference an administrator should notice is that nfsd
doesn't fork to create service threads any more. In normal operation,
there will be two nfsd processes, one in userland waiting for TCP
connections and one in the kernel handling requests. The latter
process will create as many kthreads as required - these should be
visible via 'top -H'. The code has some support for varying the number
of service threads according to load but initially at least, nfsd uses
a fixed number of threads according to the value supplied to its '-n'
option.
Sponsored by: Isilon Systems
MFC after: 1 month
2008-11-03 10:38:00 +00:00
|
|
|
else if (vfs_copyopt(mp->mnt_optnew, "export", &oexport,
|
|
|
|
sizeof(oexport)) == 0) {
|
|
|
|
export.ex_flags = oexport.ex_flags;
|
|
|
|
export.ex_root = oexport.ex_root;
|
|
|
|
export.ex_anon = oexport.ex_anon;
|
|
|
|
export.ex_addr = oexport.ex_addr;
|
|
|
|
export.ex_addrlen = oexport.ex_addrlen;
|
|
|
|
export.ex_mask = oexport.ex_mask;
|
|
|
|
export.ex_masklen = oexport.ex_masklen;
|
|
|
|
export.ex_indexfile = oexport.ex_indexfile;
|
|
|
|
export.ex_numsecflavors = 0;
|
|
|
|
error = vfs_export(mp, &export);
|
|
|
|
}
|
2006-05-26 00:32:21 +00:00
|
|
|
}
|
|
|
|
|
2003-11-12 02:54:47 +00:00
|
|
|
if (!error) {
|
|
|
|
if (mp->mnt_opt != NULL)
|
|
|
|
vfs_freeopts(mp->mnt_opt);
|
|
|
|
mp->mnt_opt = mp->mnt_optnew;
|
2006-01-15 20:01:05 +00:00
|
|
|
(void)VFS_STATFS(mp, &mp->mnt_stat, td);
|
2003-11-12 02:54:47 +00:00
|
|
|
}
|
|
|
|
/*
|
2003-11-14 05:27:41 +00:00
|
|
|
* Prevent external consumers of mount options from reading
|
|
|
|
* mnt_optnew.
|
|
|
|
*/
|
2003-11-12 02:54:47 +00:00
|
|
|
mp->mnt_optnew = NULL;
|
2002-07-02 17:09:22 +00:00
|
|
|
if (mp->mnt_flag & MNT_UPDATE) {
|
2006-09-26 04:12:49 +00:00
|
|
|
MNT_ILOCK(mp);
|
2006-09-26 04:15:04 +00:00
|
|
|
if (error)
|
2006-09-26 04:18:36 +00:00
|
|
|
mp->mnt_flag = (mp->mnt_flag & MNT_QUOTA) |
|
|
|
|
(flag & ~MNT_QUOTA);
|
2006-09-26 04:20:09 +00:00
|
|
|
else
|
|
|
|
mp->mnt_flag &= ~(MNT_UPDATE | MNT_RELOAD |
|
|
|
|
MNT_FORCE | MNT_SNAPSHOT);
|
2006-09-26 04:15:59 +00:00
|
|
|
if ((mp->mnt_flag & MNT_ASYNC) != 0 && mp->mnt_noasync == 0)
|
|
|
|
mp->mnt_kern_flag |= MNTK_ASYNC;
|
|
|
|
else
|
|
|
|
mp->mnt_kern_flag &= ~MNTK_ASYNC;
|
2006-09-26 04:12:49 +00:00
|
|
|
MNT_IUNLOCK(mp);
|
2002-07-02 17:09:22 +00:00
|
|
|
if ((mp->mnt_flag & MNT_RDONLY) == 0) {
|
|
|
|
if (mp->mnt_syncer == NULL)
|
|
|
|
error = vfs_allocate_syncvnode(mp);
|
|
|
|
} else {
|
|
|
|
if (mp->mnt_syncer != NULL)
|
2002-07-29 06:26:55 +00:00
|
|
|
vrele(mp->mnt_syncer);
|
2002-07-02 17:09:22 +00:00
|
|
|
mp->mnt_syncer = NULL;
|
|
|
|
}
|
2008-08-31 14:26:08 +00:00
|
|
|
vfs_unbusy(mp);
|
2002-08-04 10:29:36 +00:00
|
|
|
VI_LOCK(vp);
|
|
|
|
vp->v_iflag &= ~VI_MOUNT;
|
|
|
|
VI_UNLOCK(vp);
|
2002-07-02 17:09:22 +00:00
|
|
|
vrele(vp);
|
|
|
|
return (error);
|
|
|
|
}
|
2006-09-26 04:15:59 +00:00
|
|
|
MNT_ILOCK(mp);
|
|
|
|
if ((mp->mnt_flag & MNT_ASYNC) != 0 && mp->mnt_noasync == 0)
|
|
|
|
mp->mnt_kern_flag |= MNTK_ASYNC;
|
|
|
|
else
|
|
|
|
mp->mnt_kern_flag &= ~MNTK_ASYNC;
|
|
|
|
MNT_IUNLOCK(mp);
|
2008-01-10 01:10:58 +00:00
|
|
|
vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
|
2002-07-02 17:09:22 +00:00
|
|
|
/*
|
|
|
|
* Put the new filesystem on the mount list after root.
|
|
|
|
*/
|
|
|
|
cache_purge(vp);
|
|
|
|
if (!error) {
|
|
|
|
struct vnode *newdp;
|
|
|
|
|
2002-08-04 10:29:36 +00:00
|
|
|
VI_LOCK(vp);
|
|
|
|
vp->v_iflag &= ~VI_MOUNT;
|
|
|
|
VI_UNLOCK(vp);
|
2002-09-25 01:44:21 +00:00
|
|
|
vp->v_mountedhere = mp;
|
2002-07-02 17:09:22 +00:00
|
|
|
mtx_lock(&mountlist_mtx);
|
|
|
|
TAILQ_INSERT_TAIL(&mountlist, mp, mnt_list);
|
|
|
|
mtx_unlock(&mountlist_mtx);
|
2004-07-06 09:12:03 +00:00
|
|
|
vfs_event_signal(NULL, VQ_MOUNT, 0);
|
2005-03-24 07:31:38 +00:00
|
|
|
if (VFS_ROOT(mp, LK_EXCLUSIVE, &newdp, td))
|
2002-07-02 17:09:22 +00:00
|
|
|
panic("mount: lost mount");
|
2004-12-14 08:23:18 +00:00
|
|
|
mountcheckdirs(vp, newdp);
|
2002-07-02 17:09:22 +00:00
|
|
|
vput(newdp);
|
2008-01-13 14:44:15 +00:00
|
|
|
VOP_UNLOCK(vp, 0);
|
2002-07-02 17:09:22 +00:00
|
|
|
if ((mp->mnt_flag & MNT_RDONLY) == 0)
|
|
|
|
error = vfs_allocate_syncvnode(mp);
|
2008-08-31 14:26:08 +00:00
|
|
|
vfs_unbusy(mp);
|
2005-02-20 23:02:20 +00:00
|
|
|
if (error)
|
2002-07-02 17:09:22 +00:00
|
|
|
vrele(vp);
|
|
|
|
} else {
|
2002-08-04 10:29:36 +00:00
|
|
|
VI_LOCK(vp);
|
|
|
|
vp->v_iflag &= ~VI_MOUNT;
|
|
|
|
VI_UNLOCK(vp);
|
2008-08-31 14:26:08 +00:00
|
|
|
vfs_unbusy(mp);
|
2006-06-02 20:29:02 +00:00
|
|
|
vfs_mount_destroy(mp);
|
2002-07-02 17:09:22 +00:00
|
|
|
vput(vp);
|
|
|
|
}
|
|
|
|
return (error);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Unmount a filesystem.
|
|
|
|
*
|
2007-03-05 13:10:58 +00:00
|
|
|
* Note: unmount takes a path to the vnode mounted on as argument, not
|
|
|
|
* special file (as before).
|
2002-07-02 17:09:22 +00:00
|
|
|
*/
|
|
|
|
#ifndef _SYS_SYSPROTO_H_
|
|
|
|
struct unmount_args {
|
|
|
|
char *path;
|
|
|
|
int flags;
|
|
|
|
};
|
|
|
|
#endif
|
|
|
|
/* ARGSUSED */
|
|
|
|
int
|
|
|
|
unmount(td, uap)
|
|
|
|
struct thread *td;
|
|
|
|
register struct unmount_args /* {
|
2002-12-14 02:07:32 +00:00
|
|
|
char *path;
|
|
|
|
int flags;
|
2002-07-02 17:09:22 +00:00
|
|
|
} */ *uap;
|
|
|
|
{
|
|
|
|
struct mount *mp;
|
2003-07-01 17:40:23 +00:00
|
|
|
char *pathbuf;
|
|
|
|
int error, id0, id1;
|
2002-07-02 17:09:22 +00:00
|
|
|
|
2007-03-18 02:39:19 +00:00
|
|
|
if (jailed(td->td_ucred) || usermount == 0) {
|
2006-11-06 13:42:10 +00:00
|
|
|
error = priv_check(td, PRIV_VFS_UNMOUNT);
|
|
|
|
if (error)
|
2004-02-02 19:02:05 +00:00
|
|
|
return (error);
|
|
|
|
}
|
|
|
|
|
2003-07-01 17:40:23 +00:00
|
|
|
pathbuf = malloc(MNAMELEN, M_TEMP, M_WAITOK);
|
|
|
|
error = copyinstr(uap->path, pathbuf, MNAMELEN, NULL);
|
|
|
|
if (error) {
|
|
|
|
free(pathbuf, M_TEMP);
|
2002-07-02 17:09:22 +00:00
|
|
|
return (error);
|
2003-07-01 17:40:23 +00:00
|
|
|
}
|
2006-06-05 15:32:07 +00:00
|
|
|
AUDIT_ARG(upath, td, pathbuf, ARG_UPATH1);
|
2006-06-27 14:46:31 +00:00
|
|
|
mtx_lock(&Giant);
|
2003-07-01 17:40:23 +00:00
|
|
|
if (uap->flags & MNT_BYFSID) {
|
|
|
|
/* Decode the filesystem ID. */
|
|
|
|
if (sscanf(pathbuf, "FSID:%d:%d", &id0, &id1) != 2) {
|
2006-06-27 14:46:31 +00:00
|
|
|
mtx_unlock(&Giant);
|
2003-07-01 17:40:23 +00:00
|
|
|
free(pathbuf, M_TEMP);
|
|
|
|
return (EINVAL);
|
|
|
|
}
|
|
|
|
|
|
|
|
mtx_lock(&mountlist_mtx);
|
2004-03-27 08:39:28 +00:00
|
|
|
TAILQ_FOREACH_REVERSE(mp, &mountlist, mntlist, mnt_list) {
|
2003-07-01 17:40:23 +00:00
|
|
|
if (mp->mnt_stat.f_fsid.val[0] == id0 &&
|
|
|
|
mp->mnt_stat.f_fsid.val[1] == id1)
|
|
|
|
break;
|
2004-03-27 08:39:28 +00:00
|
|
|
}
|
2003-07-01 17:40:23 +00:00
|
|
|
mtx_unlock(&mountlist_mtx);
|
|
|
|
} else {
|
|
|
|
mtx_lock(&mountlist_mtx);
|
2004-03-27 08:39:28 +00:00
|
|
|
TAILQ_FOREACH_REVERSE(mp, &mountlist, mntlist, mnt_list) {
|
2003-07-01 17:40:23 +00:00
|
|
|
if (strcmp(mp->mnt_stat.f_mntonname, pathbuf) == 0)
|
|
|
|
break;
|
2004-03-27 08:39:28 +00:00
|
|
|
}
|
2003-07-01 17:40:23 +00:00
|
|
|
mtx_unlock(&mountlist_mtx);
|
|
|
|
}
|
|
|
|
free(pathbuf, M_TEMP);
|
2003-09-08 16:23:21 +00:00
|
|
|
if (mp == NULL) {
|
|
|
|
/*
|
|
|
|
* Previously we returned ENOENT for a nonexistent path and
|
|
|
|
* EINVAL for a non-mountpoint. We cannot tell these apart
|
|
|
|
* now, so in the !MNT_BYFSID case return the more likely
|
|
|
|
* EINVAL for compatibility.
|
|
|
|
*/
|
2006-06-27 14:46:31 +00:00
|
|
|
mtx_unlock(&Giant);
|
2003-09-08 16:23:21 +00:00
|
|
|
return ((uap->flags & MNT_BYFSID) ? ENOENT : EINVAL);
|
|
|
|
}
|
2002-07-02 17:09:22 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Don't allow unmounting the root filesystem.
|
|
|
|
*/
|
2006-06-27 14:46:31 +00:00
|
|
|
if (mp->mnt_flag & MNT_ROOTFS) {
|
|
|
|
mtx_unlock(&Giant);
|
2002-07-02 17:09:22 +00:00
|
|
|
return (EINVAL);
|
2006-06-27 14:46:31 +00:00
|
|
|
}
|
2004-11-25 12:06:43 +00:00
|
|
|
error = dounmount(mp, uap->flags, td);
|
|
|
|
mtx_unlock(&Giant);
|
|
|
|
return (error);
|
2002-07-02 17:09:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Do the actual filesystem unmount.
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
dounmount(mp, flags, td)
|
|
|
|
struct mount *mp;
|
|
|
|
int flags;
|
|
|
|
struct thread *td;
|
|
|
|
{
|
|
|
|
struct vnode *coveredvp, *fsrootvp;
|
|
|
|
int error;
|
|
|
|
int async_flag;
|
2006-10-03 10:47:04 +00:00
|
|
|
int mnt_gen_r;
|
2002-07-02 17:09:22 +00:00
|
|
|
|
2004-11-25 12:06:43 +00:00
|
|
|
mtx_assert(&Giant, MA_OWNED);
|
|
|
|
|
2006-09-18 15:35:22 +00:00
|
|
|
if ((coveredvp = mp->mnt_vnodecovered) != NULL) {
|
2006-10-03 10:47:04 +00:00
|
|
|
mnt_gen_r = mp->mnt_gen;
|
2006-09-18 15:35:22 +00:00
|
|
|
VI_LOCK(coveredvp);
|
|
|
|
vholdl(coveredvp);
|
2008-01-10 01:10:58 +00:00
|
|
|
vn_lock(coveredvp, LK_EXCLUSIVE | LK_INTERLOCK | LK_RETRY);
|
2006-09-18 15:35:22 +00:00
|
|
|
vdrop(coveredvp);
|
|
|
|
/*
|
|
|
|
* Check for mp being unmounted while waiting for the
|
|
|
|
* covered vnode lock.
|
|
|
|
*/
|
2006-10-03 10:47:04 +00:00
|
|
|
if (coveredvp->v_mountedhere != mp ||
|
|
|
|
coveredvp->v_mountedhere->mnt_gen != mnt_gen_r) {
|
2008-01-13 14:44:15 +00:00
|
|
|
VOP_UNLOCK(coveredvp, 0);
|
2006-09-18 15:35:22 +00:00
|
|
|
return (EBUSY);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/*
|
|
|
|
* Only privileged root, or (if MNT_USER is set) the user that did the
|
|
|
|
* original mount is permitted to unmount this filesystem.
|
|
|
|
*/
|
|
|
|
error = vfs_suser(mp, td);
|
|
|
|
if (error) {
|
2006-09-19 14:04:12 +00:00
|
|
|
if (coveredvp)
|
2008-01-13 14:44:15 +00:00
|
|
|
VOP_UNLOCK(coveredvp, 0);
|
2006-09-18 15:35:22 +00:00
|
|
|
return (error);
|
|
|
|
}
|
|
|
|
|
2005-01-24 10:28:41 +00:00
|
|
|
MNT_ILOCK(mp);
|
2002-07-02 17:09:22 +00:00
|
|
|
if (mp->mnt_kern_flag & MNTK_UNMOUNT) {
|
2005-01-24 10:28:41 +00:00
|
|
|
MNT_IUNLOCK(mp);
|
2006-02-22 06:29:55 +00:00
|
|
|
if (coveredvp)
|
2008-01-13 14:44:15 +00:00
|
|
|
VOP_UNLOCK(coveredvp, 0);
|
2002-07-02 17:09:22 +00:00
|
|
|
return (EBUSY);
|
|
|
|
}
|
2007-09-12 16:31:32 +00:00
|
|
|
mp->mnt_kern_flag |= MNTK_UNMOUNT | MNTK_NOINSMNTQ;
|
2002-07-02 17:09:22 +00:00
|
|
|
/* Allow filesystems to detect that a forced unmount is in progress. */
|
|
|
|
if (flags & MNT_FORCE)
|
|
|
|
mp->mnt_kern_flag |= MNTK_UNMOUNTF;
|
2008-11-02 10:15:42 +00:00
|
|
|
error = 0;
|
|
|
|
if (mp->mnt_lockref) {
|
2008-12-01 03:00:26 +00:00
|
|
|
if ((flags & MNT_FORCE) == 0) {
|
2008-11-02 10:15:42 +00:00
|
|
|
mp->mnt_kern_flag &= ~(MNTK_UNMOUNT | MNTK_NOINSMNTQ |
|
|
|
|
MNTK_UNMOUNTF);
|
|
|
|
if (mp->mnt_kern_flag & MNTK_MWAIT) {
|
|
|
|
mp->mnt_kern_flag &= ~MNTK_MWAIT;
|
|
|
|
wakeup(mp);
|
|
|
|
}
|
|
|
|
MNT_IUNLOCK(mp);
|
|
|
|
if (coveredvp)
|
|
|
|
VOP_UNLOCK(coveredvp, 0);
|
|
|
|
return (EBUSY);
|
|
|
|
}
|
|
|
|
mp->mnt_kern_flag |= MNTK_DRAINING;
|
|
|
|
error = msleep(&mp->mnt_lockref, MNT_MTX(mp), PVFS,
|
|
|
|
"mount drain", 0);
|
2002-07-02 17:09:22 +00:00
|
|
|
}
|
2008-11-02 10:15:42 +00:00
|
|
|
MNT_IUNLOCK(mp);
|
|
|
|
KASSERT(mp->mnt_lockref == 0,
|
|
|
|
("%s: invalid lock refcount in the drain path @ %s:%d",
|
|
|
|
__func__, __FILE__, __LINE__));
|
|
|
|
KASSERT(error == 0,
|
|
|
|
("%s: invalid return value for msleep in the drain path @ %s:%d",
|
|
|
|
__func__, __FILE__, __LINE__));
|
2002-07-02 17:09:22 +00:00
|
|
|
vn_start_write(NULL, &mp, V_WAIT);
|
|
|
|
|
|
|
|
if (mp->mnt_flag & MNT_EXPUBLIC)
|
|
|
|
vfs_setpublicfs(NULL, NULL, NULL);
|
|
|
|
|
|
|
|
vfs_msync(mp, MNT_WAIT);
|
2006-09-26 04:12:49 +00:00
|
|
|
MNT_ILOCK(mp);
|
2002-07-02 17:09:22 +00:00
|
|
|
async_flag = mp->mnt_flag & MNT_ASYNC;
|
2004-03-27 08:39:28 +00:00
|
|
|
mp->mnt_flag &= ~MNT_ASYNC;
|
2006-09-26 04:15:59 +00:00
|
|
|
mp->mnt_kern_flag &= ~MNTK_ASYNC;
|
2006-09-26 04:12:49 +00:00
|
|
|
MNT_IUNLOCK(mp);
|
2002-07-02 17:09:22 +00:00
|
|
|
cache_purgevfs(mp); /* remove cache entries for this file sys */
|
|
|
|
if (mp->mnt_syncer != NULL)
|
2002-07-29 06:26:55 +00:00
|
|
|
vrele(mp->mnt_syncer);
|
2003-11-30 23:30:09 +00:00
|
|
|
/*
|
|
|
|
* For forced unmounts, move process cdir/rdir refs on the fs root
|
|
|
|
* vnode to the covered vnode. For non-forced unmounts we want
|
|
|
|
* such references to cause an EBUSY error.
|
|
|
|
*/
|
2005-03-24 07:31:38 +00:00
|
|
|
if ((flags & MNT_FORCE) &&
|
|
|
|
VFS_ROOT(mp, LK_EXCLUSIVE, &fsrootvp, td) == 0) {
|
2002-07-02 17:09:22 +00:00
|
|
|
if (mp->mnt_vnodecovered != NULL)
|
2004-12-14 08:23:18 +00:00
|
|
|
mountcheckdirs(fsrootvp, mp->mnt_vnodecovered);
|
2002-07-02 17:09:22 +00:00
|
|
|
if (fsrootvp == rootvnode) {
|
|
|
|
vrele(rootvnode);
|
|
|
|
rootvnode = NULL;
|
|
|
|
}
|
|
|
|
vput(fsrootvp);
|
|
|
|
}
|
|
|
|
if (((mp->mnt_flag & MNT_RDONLY) ||
|
2005-01-11 07:36:22 +00:00
|
|
|
(error = VFS_SYNC(mp, MNT_WAIT, td)) == 0) ||
|
2002-07-02 17:09:22 +00:00
|
|
|
(flags & MNT_FORCE)) {
|
|
|
|
error = VFS_UNMOUNT(mp, flags, td);
|
|
|
|
}
|
|
|
|
vn_finished_write(mp);
|
2007-12-27 16:38:28 +00:00
|
|
|
/*
|
|
|
|
* If we failed to flush the dirty blocks for this mount point,
|
|
|
|
* undo all the cdir/rdir and rootvnode changes we made above.
|
|
|
|
* Unless we failed to do so because the device is reporting that
|
|
|
|
* it doesn't exist anymore.
|
|
|
|
*/
|
|
|
|
if (error && error != ENXIO) {
|
2005-03-24 07:31:38 +00:00
|
|
|
if ((flags & MNT_FORCE) &&
|
|
|
|
VFS_ROOT(mp, LK_EXCLUSIVE, &fsrootvp, td) == 0) {
|
2002-07-02 17:09:22 +00:00
|
|
|
if (mp->mnt_vnodecovered != NULL)
|
2004-12-14 08:23:18 +00:00
|
|
|
mountcheckdirs(mp->mnt_vnodecovered, fsrootvp);
|
2002-07-02 17:09:22 +00:00
|
|
|
if (rootvnode == NULL) {
|
|
|
|
rootvnode = fsrootvp;
|
|
|
|
vref(rootvnode);
|
|
|
|
}
|
|
|
|
vput(fsrootvp);
|
|
|
|
}
|
2005-01-24 10:28:41 +00:00
|
|
|
MNT_ILOCK(mp);
|
2007-09-12 16:31:32 +00:00
|
|
|
mp->mnt_kern_flag &= ~MNTK_NOINSMNTQ;
|
|
|
|
if ((mp->mnt_flag & MNT_RDONLY) == 0 && mp->mnt_syncer == NULL) {
|
|
|
|
MNT_IUNLOCK(mp);
|
|
|
|
(void) vfs_allocate_syncvnode(mp);
|
|
|
|
MNT_ILOCK(mp);
|
|
|
|
}
|
2002-07-02 17:09:22 +00:00
|
|
|
mp->mnt_kern_flag &= ~(MNTK_UNMOUNT | MNTK_UNMOUNTF);
|
|
|
|
mp->mnt_flag |= async_flag;
|
2006-09-26 04:15:59 +00:00
|
|
|
if ((mp->mnt_flag & MNT_ASYNC) != 0 && mp->mnt_noasync == 0)
|
|
|
|
mp->mnt_kern_flag |= MNTK_ASYNC;
|
2008-11-02 10:15:42 +00:00
|
|
|
if (mp->mnt_kern_flag & MNTK_MWAIT) {
|
|
|
|
mp->mnt_kern_flag &= ~MNTK_MWAIT;
|
2002-07-02 17:09:22 +00:00
|
|
|
wakeup(mp);
|
2008-11-02 10:15:42 +00:00
|
|
|
}
|
2005-01-24 10:28:41 +00:00
|
|
|
MNT_IUNLOCK(mp);
|
2006-02-22 06:29:55 +00:00
|
|
|
if (coveredvp)
|
2008-01-13 14:44:15 +00:00
|
|
|
VOP_UNLOCK(coveredvp, 0);
|
2002-07-02 17:09:22 +00:00
|
|
|
return (error);
|
|
|
|
}
|
|
|
|
mtx_lock(&mountlist_mtx);
|
|
|
|
TAILQ_REMOVE(&mountlist, mp, mnt_list);
|
2003-11-12 02:54:47 +00:00
|
|
|
mtx_unlock(&mountlist_mtx);
|
2006-02-22 06:29:55 +00:00
|
|
|
if (coveredvp != NULL) {
|
|
|
|
coveredvp->v_mountedhere = NULL;
|
|
|
|
vput(coveredvp);
|
|
|
|
}
|
2004-07-06 09:12:03 +00:00
|
|
|
vfs_event_signal(NULL, VQ_UNMOUNT, 0);
|
2006-06-02 20:29:02 +00:00
|
|
|
vfs_mount_destroy(mp);
|
2002-07-02 17:09:22 +00:00
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
2004-12-07 08:15:41 +00:00
|
|
|
/*
|
|
|
|
* ---------------------------------------------------------------------
|
|
|
|
* Mounting of root filesystem
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
2005-04-18 21:21:26 +00:00
|
|
|
struct root_hold_token {
|
2007-03-14 12:54:10 +00:00
|
|
|
const char *who;
|
2005-04-18 21:21:26 +00:00
|
|
|
LIST_ENTRY(root_hold_token) list;
|
|
|
|
};
|
|
|
|
|
|
|
|
static LIST_HEAD(, root_hold_token) root_holds =
|
|
|
|
LIST_HEAD_INITIALIZER(&root_holds);
|
|
|
|
|
2007-04-09 19:23:52 +00:00
|
|
|
static int root_mount_complete;
|
2007-04-03 11:45:28 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Hold root mount.
|
|
|
|
*/
|
2005-04-18 21:21:26 +00:00
|
|
|
struct root_hold_token *
|
|
|
|
root_mount_hold(const char *identifier)
|
|
|
|
{
|
|
|
|
struct root_hold_token *h;
|
|
|
|
|
|
|
|
h = malloc(sizeof *h, M_DEVBUF, M_ZERO | M_WAITOK);
|
|
|
|
h->who = identifier;
|
|
|
|
mtx_lock(&mountlist_mtx);
|
|
|
|
LIST_INSERT_HEAD(&root_holds, h, list);
|
|
|
|
mtx_unlock(&mountlist_mtx);
|
|
|
|
return (h);
|
|
|
|
}
|
|
|
|
|
2007-04-03 11:45:28 +00:00
|
|
|
/*
|
|
|
|
* Release root mount.
|
|
|
|
*/
|
2005-04-18 21:21:26 +00:00
|
|
|
void
|
|
|
|
root_mount_rel(struct root_hold_token *h)
|
|
|
|
{
|
|
|
|
|
|
|
|
mtx_lock(&mountlist_mtx);
|
|
|
|
LIST_REMOVE(h, list);
|
|
|
|
wakeup(&root_holds);
|
|
|
|
mtx_unlock(&mountlist_mtx);
|
|
|
|
free(h, M_DEVBUF);
|
|
|
|
}
|
|
|
|
|
2007-04-03 11:45:28 +00:00
|
|
|
/*
|
|
|
|
* Wait for all subsystems to release root mount.
|
|
|
|
*/
|
2005-04-18 21:21:26 +00:00
|
|
|
static void
|
2007-04-03 11:45:28 +00:00
|
|
|
root_mount_prepare(void)
|
2005-04-18 21:21:26 +00:00
|
|
|
{
|
|
|
|
struct root_hold_token *h;
|
|
|
|
|
2005-04-19 21:44:44 +00:00
|
|
|
for (;;) {
|
|
|
|
DROP_GIANT();
|
|
|
|
g_waitidle();
|
|
|
|
PICKUP_GIANT();
|
|
|
|
mtx_lock(&mountlist_mtx);
|
|
|
|
if (LIST_EMPTY(&root_holds)) {
|
|
|
|
mtx_unlock(&mountlist_mtx);
|
|
|
|
break;
|
|
|
|
}
|
2005-04-18 21:21:26 +00:00
|
|
|
printf("Root mount waiting for:");
|
|
|
|
LIST_FOREACH(h, &root_holds, list)
|
|
|
|
printf(" %s", h->who);
|
|
|
|
printf("\n");
|
2005-04-19 21:44:44 +00:00
|
|
|
msleep(&root_holds, &mountlist_mtx, PZERO | PDROP, "roothold",
|
|
|
|
hz);
|
2005-04-18 21:21:26 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2007-04-03 11:45:28 +00:00
|
|
|
/*
|
|
|
|
* Root was mounted, share the good news.
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
root_mount_done(void)
|
|
|
|
{
|
|
|
|
|
2007-04-09 19:23:52 +00:00
|
|
|
/*
|
2007-04-09 21:10:04 +00:00
|
|
|
* Use a mutex to prevent the wakeup being missed and waiting for
|
|
|
|
* an extra 1 second sleep.
|
2007-04-09 19:23:52 +00:00
|
|
|
*/
|
2007-04-09 21:10:04 +00:00
|
|
|
mtx_lock(&mountlist_mtx);
|
2007-04-03 11:45:28 +00:00
|
|
|
root_mount_complete = 1;
|
|
|
|
wakeup(&root_mount_complete);
|
2007-04-09 21:10:04 +00:00
|
|
|
mtx_unlock(&mountlist_mtx);
|
2007-04-03 11:45:28 +00:00
|
|
|
}
|
|
|
|
|
2007-04-09 19:23:52 +00:00
|
|
|
/*
|
2007-04-08 23:54:01 +00:00
|
|
|
* Return true if root is already mounted.
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
root_mounted(void)
|
|
|
|
{
|
|
|
|
|
2007-04-09 21:10:04 +00:00
|
|
|
/* No mutex is acquired here because int stores are atomic. */
|
2007-04-09 19:23:52 +00:00
|
|
|
return (root_mount_complete);
|
2007-04-08 23:54:01 +00:00
|
|
|
}
|
|
|
|
|
2007-04-03 11:45:28 +00:00
|
|
|
/*
|
|
|
|
* Wait until root is mounted.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
root_mount_wait(void)
|
|
|
|
{
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Panic on an obvious deadlock - the function can't be called from
|
|
|
|
* a thread which is doing the whole SYSINIT stuff.
|
|
|
|
*/
|
|
|
|
KASSERT(curthread->td_proc->p_pid != 0,
|
|
|
|
("root_mount_wait: cannot be called from the swapper thread"));
|
2007-04-09 21:10:04 +00:00
|
|
|
mtx_lock(&mountlist_mtx);
|
|
|
|
while (!root_mount_complete) {
|
|
|
|
msleep(&root_mount_complete, &mountlist_mtx, PZERO, "rootwait",
|
|
|
|
hz);
|
|
|
|
}
|
|
|
|
mtx_unlock(&mountlist_mtx);
|
2007-04-03 11:45:28 +00:00
|
|
|
}
|
|
|
|
|
2004-12-07 08:15:41 +00:00
|
|
|
static void
|
|
|
|
set_rootvnode(struct thread *td)
|
|
|
|
{
|
|
|
|
struct proc *p;
|
|
|
|
|
2005-03-24 07:31:38 +00:00
|
|
|
if (VFS_ROOT(TAILQ_FIRST(&mountlist), LK_EXCLUSIVE, &rootvnode, td))
|
2004-12-07 08:15:41 +00:00
|
|
|
panic("Cannot find root vnode");
|
|
|
|
|
|
|
|
p = td->td_proc;
|
2008-04-29 21:40:11 +00:00
|
|
|
FILEDESC_XLOCK(p->p_fd);
|
2004-12-07 08:15:41 +00:00
|
|
|
|
|
|
|
if (p->p_fd->fd_cdir != NULL)
|
|
|
|
vrele(p->p_fd->fd_cdir);
|
|
|
|
p->p_fd->fd_cdir = rootvnode;
|
|
|
|
VREF(rootvnode);
|
|
|
|
|
|
|
|
if (p->p_fd->fd_rdir != NULL)
|
|
|
|
vrele(p->p_fd->fd_rdir);
|
|
|
|
p->p_fd->fd_rdir = rootvnode;
|
|
|
|
VREF(rootvnode);
|
|
|
|
|
2008-04-29 21:40:11 +00:00
|
|
|
FILEDESC_XUNLOCK(p->p_fd);
|
2004-12-07 08:15:41 +00:00
|
|
|
|
2008-01-13 14:44:15 +00:00
|
|
|
VOP_UNLOCK(rootvnode, 0);
|
2008-04-08 17:53:33 +00:00
|
|
|
|
|
|
|
EVENTHANDLER_INVOKE(mountroot);
|
2004-12-07 08:15:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Mount /devfs as our root filesystem, but do not put it on the mountlist
|
|
|
|
* yet. Create a /dev -> / symlink so that absolute pathnames will lookup.
|
|
|
|
*/
|
|
|
|
|
2005-05-18 22:05:12 +00:00
|
|
|
static void
|
2004-12-07 08:15:41 +00:00
|
|
|
devfs_first(void)
|
|
|
|
{
|
|
|
|
struct thread *td = curthread;
|
2005-12-08 04:27:53 +00:00
|
|
|
struct vfsoptlist *opts;
|
2004-12-07 08:15:41 +00:00
|
|
|
struct vfsconf *vfsp;
|
|
|
|
struct mount *mp = NULL;
|
|
|
|
int error;
|
|
|
|
|
|
|
|
vfsp = vfs_byname("devfs");
|
|
|
|
KASSERT(vfsp != NULL, ("Could not find devfs by name"));
|
2007-03-14 12:54:10 +00:00
|
|
|
if (vfsp == NULL)
|
2005-05-18 22:05:12 +00:00
|
|
|
return;
|
2004-12-07 08:15:41 +00:00
|
|
|
|
2008-08-31 14:26:08 +00:00
|
|
|
mp = vfs_mount_alloc(NULLVP, vfsp, "/dev", td->td_ucred);
|
2004-12-07 08:15:41 +00:00
|
|
|
|
2005-12-19 16:27:13 +00:00
|
|
|
error = VFS_MOUNT(mp, td);
|
2004-12-07 08:15:41 +00:00
|
|
|
KASSERT(error == 0, ("VFS_MOUNT(devfs) failed %d", error));
|
|
|
|
if (error)
|
2005-05-18 22:05:12 +00:00
|
|
|
return;
|
2004-12-07 08:15:41 +00:00
|
|
|
|
2005-12-08 04:27:53 +00:00
|
|
|
opts = malloc(sizeof(struct vfsoptlist), M_MOUNT, M_WAITOK);
|
|
|
|
TAILQ_INIT(opts);
|
|
|
|
mp->mnt_opt = opts;
|
|
|
|
|
2004-12-07 08:15:41 +00:00
|
|
|
mtx_lock(&mountlist_mtx);
|
|
|
|
TAILQ_INSERT_HEAD(&mountlist, mp, mnt_list);
|
|
|
|
mtx_unlock(&mountlist_mtx);
|
|
|
|
|
|
|
|
set_rootvnode(td);
|
|
|
|
|
|
|
|
error = kern_symlink(td, "/", "dev", UIO_SYSSPACE);
|
2004-12-20 21:59:25 +00:00
|
|
|
if (error)
|
|
|
|
printf("kern_symlink /dev -> / returns %d\n", error);
|
2004-12-07 08:15:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Surgically move our devfs to be mounted on /dev.
|
|
|
|
*/
|
|
|
|
|
|
|
|
static void
|
|
|
|
devfs_fixup(struct thread *td)
|
|
|
|
{
|
|
|
|
struct nameidata nd;
|
|
|
|
int error;
|
|
|
|
struct vnode *vp, *dvp;
|
|
|
|
struct mount *mp;
|
|
|
|
|
|
|
|
/* Remove our devfs mount from the mountlist and purge the cache */
|
|
|
|
mtx_lock(&mountlist_mtx);
|
|
|
|
mp = TAILQ_FIRST(&mountlist);
|
|
|
|
TAILQ_REMOVE(&mountlist, mp, mnt_list);
|
|
|
|
mtx_unlock(&mountlist_mtx);
|
|
|
|
cache_purgevfs(mp);
|
|
|
|
|
2005-03-24 07:31:38 +00:00
|
|
|
VFS_ROOT(mp, LK_EXCLUSIVE, &dvp, td);
|
2004-12-07 08:15:41 +00:00
|
|
|
VI_LOCK(dvp);
|
|
|
|
dvp->v_iflag &= ~VI_MOUNT;
|
|
|
|
VI_UNLOCK(dvp);
|
2007-07-26 16:52:57 +00:00
|
|
|
dvp->v_mountedhere = NULL;
|
2004-12-07 08:15:41 +00:00
|
|
|
|
|
|
|
/* Set up the real rootvnode, and purge the cache */
|
|
|
|
TAILQ_FIRST(&mountlist)->mnt_vnodecovered = NULL;
|
|
|
|
set_rootvnode(td);
|
|
|
|
cache_purgevfs(rootvnode->v_mount);
|
|
|
|
|
|
|
|
NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF, UIO_SYSSPACE, "/dev", td);
|
|
|
|
error = namei(&nd);
|
|
|
|
if (error) {
|
2005-03-16 08:04:39 +00:00
|
|
|
printf("Lookup of /dev for devfs, error: %d\n", error);
|
2004-12-07 08:15:41 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
NDFREE(&nd, NDF_ONLY_PNBUF);
|
|
|
|
vp = nd.ni_vp;
|
|
|
|
if (vp->v_type != VDIR) {
|
|
|
|
vput(vp);
|
|
|
|
}
|
2008-10-10 21:23:50 +00:00
|
|
|
error = vinvalbuf(vp, V_SAVE, 0, 0);
|
2004-12-07 08:15:41 +00:00
|
|
|
if (error) {
|
|
|
|
vput(vp);
|
|
|
|
}
|
|
|
|
cache_purge(vp);
|
|
|
|
mp->mnt_vnodecovered = vp;
|
|
|
|
vp->v_mountedhere = mp;
|
|
|
|
mtx_lock(&mountlist_mtx);
|
|
|
|
TAILQ_INSERT_TAIL(&mountlist, mp, mnt_list);
|
|
|
|
mtx_unlock(&mountlist_mtx);
|
2008-01-13 14:44:15 +00:00
|
|
|
VOP_UNLOCK(vp, 0);
|
2004-12-07 08:15:41 +00:00
|
|
|
vput(dvp);
|
2008-08-31 14:26:08 +00:00
|
|
|
vfs_unbusy(mp);
|
2004-12-07 08:15:41 +00:00
|
|
|
|
2004-12-11 12:48:37 +00:00
|
|
|
/* Unlink the no longer needed /dev/dev -> / symlink */
|
2004-12-11 20:41:32 +00:00
|
|
|
kern_unlink(td, "/dev/dev", UIO_SYSSPACE);
|
2004-12-07 08:15:41 +00:00
|
|
|
}
|
|
|
|
|
2005-11-08 04:13:39 +00:00
|
|
|
/*
|
2007-03-14 12:54:10 +00:00
|
|
|
* Report errors during filesystem mounting.
|
2005-11-08 04:13:39 +00:00
|
|
|
*/
|
|
|
|
void
|
|
|
|
vfs_mount_error(struct mount *mp, const char *fmt, ...)
|
|
|
|
{
|
|
|
|
struct vfsoptlist *moptlist = mp->mnt_optnew;
|
|
|
|
va_list ap;
|
|
|
|
int error, len;
|
|
|
|
char *errmsg;
|
|
|
|
|
|
|
|
error = vfs_getopt(moptlist, "errmsg", (void **)&errmsg, &len);
|
2005-11-09 02:26:38 +00:00
|
|
|
if (error || errmsg == NULL || len <= 0)
|
2005-11-08 04:13:39 +00:00
|
|
|
return;
|
|
|
|
|
|
|
|
va_start(ap, fmt);
|
|
|
|
vsnprintf(errmsg, (size_t)len, fmt, ap);
|
|
|
|
va_end(ap);
|
|
|
|
}
|
|
|
|
|
2009-03-02 23:26:30 +00:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
1995-08-28 09:19:25 +00:00
|
|
|
/*
|
1999-11-01 23:51:00 +00:00
|
|
|
* Find and mount the root filesystem
|
1995-08-28 09:19:25 +00:00
|
|
|
*/
|
2002-03-08 10:33:11 +00:00
|
|
|
void
|
2002-07-03 08:52:37 +00:00
|
|
|
vfs_mountroot(void)
|
1995-08-28 09:19:25 +00:00
|
|
|
{
|
2003-11-14 05:27:41 +00:00
|
|
|
char *cp;
|
2004-10-05 11:26:43 +00:00
|
|
|
int error, i, asked = 0;
|
2004-07-30 22:08:52 +00:00
|
|
|
|
2007-04-03 11:45:28 +00:00
|
|
|
root_mount_prepare();
|
2004-07-30 22:08:52 +00:00
|
|
|
|
2006-03-31 03:49:51 +00:00
|
|
|
mount_zone = uma_zcreate("Mountpoints", sizeof(struct mount),
|
|
|
|
NULL, NULL, mount_init, mount_fini,
|
|
|
|
UMA_ALIGN_PTR, UMA_ZONE_NOFREE);
|
2005-05-12 01:49:51 +00:00
|
|
|
devfs_first();
|
|
|
|
|
2004-10-05 11:26:43 +00:00
|
|
|
/*
|
|
|
|
* We are booted with instructions to prompt for the root filesystem.
|
|
|
|
*/
|
|
|
|
if (boothowto & RB_ASKNAME) {
|
|
|
|
if (!vfs_mountroot_ask())
|
2007-04-03 11:45:28 +00:00
|
|
|
goto mounted;
|
2004-10-05 11:26:43 +00:00
|
|
|
asked = 1;
|
|
|
|
}
|
1995-08-28 09:19:25 +00:00
|
|
|
|
2004-10-05 11:26:43 +00:00
|
|
|
/*
|
|
|
|
* The root filesystem information is compiled in, and we are
|
|
|
|
* booted with instructions to use it.
|
|
|
|
*/
|
|
|
|
if (ctrootdevname != NULL && (boothowto & RB_DFLTROOT)) {
|
|
|
|
if (!vfs_mountroot_try(ctrootdevname))
|
2007-04-03 11:45:28 +00:00
|
|
|
goto mounted;
|
2004-10-05 11:26:43 +00:00
|
|
|
ctrootdevname = NULL;
|
|
|
|
}
|
1999-11-01 23:51:00 +00:00
|
|
|
|
2004-10-05 11:26:43 +00:00
|
|
|
/*
|
|
|
|
* We've been given the generic "use CDROM as root" flag. This is
|
|
|
|
* necessary because one media may be used in many different
|
|
|
|
* devices, so we need to search for them.
|
|
|
|
*/
|
|
|
|
if (boothowto & RB_CDROM) {
|
|
|
|
for (i = 0; cdrom_rootdevnames[i] != NULL; i++) {
|
|
|
|
if (!vfs_mountroot_try(cdrom_rootdevnames[i]))
|
2007-04-03 11:45:28 +00:00
|
|
|
goto mounted;
|
2004-09-23 10:13:18 +00:00
|
|
|
}
|
2004-10-05 11:26:43 +00:00
|
|
|
}
|
1999-09-19 06:24:21 +00:00
|
|
|
|
2004-10-05 11:26:43 +00:00
|
|
|
/*
|
|
|
|
* Try to use the value read by the loader from /etc/fstab, or
|
|
|
|
* supplied via some other means. This is the preferred
|
|
|
|
* mechanism.
|
|
|
|
*/
|
|
|
|
cp = getenv("vfs.root.mountfrom");
|
|
|
|
if (cp != NULL) {
|
|
|
|
error = vfs_mountroot_try(cp);
|
|
|
|
freeenv(cp);
|
|
|
|
if (!error)
|
2007-04-03 11:45:28 +00:00
|
|
|
goto mounted;
|
2004-09-23 10:13:18 +00:00
|
|
|
}
|
2004-10-05 11:26:43 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Try values that may have been computed by code during boot
|
|
|
|
*/
|
|
|
|
if (!vfs_mountroot_try(rootdevnames[0]))
|
2007-04-03 11:45:28 +00:00
|
|
|
goto mounted;
|
2004-10-05 11:26:43 +00:00
|
|
|
if (!vfs_mountroot_try(rootdevnames[1]))
|
2007-04-03 11:45:28 +00:00
|
|
|
goto mounted;
|
2004-10-05 11:26:43 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* If we (still) have a compiled-in default, try it.
|
|
|
|
*/
|
|
|
|
if (ctrootdevname != NULL)
|
|
|
|
if (!vfs_mountroot_try(ctrootdevname))
|
2007-04-03 11:45:28 +00:00
|
|
|
goto mounted;
|
2003-11-12 02:54:47 +00:00
|
|
|
/*
|
1999-11-01 23:51:00 +00:00
|
|
|
* Everything so far has failed, prompt on the console if we haven't
|
|
|
|
* already tried that.
|
|
|
|
*/
|
2004-07-30 22:08:52 +00:00
|
|
|
if (!asked)
|
|
|
|
if (!vfs_mountroot_ask())
|
2007-04-03 11:45:28 +00:00
|
|
|
goto mounted;
|
2004-12-07 08:15:41 +00:00
|
|
|
|
1999-11-01 23:51:00 +00:00
|
|
|
panic("Root mount failed, startup aborted.");
|
2007-04-03 11:45:28 +00:00
|
|
|
|
|
|
|
mounted:
|
|
|
|
root_mount_done();
|
1999-11-01 23:51:00 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Mount (mountfrom) as the root filesystem.
|
|
|
|
*/
|
|
|
|
static int
|
2004-07-30 22:08:52 +00:00
|
|
|
vfs_mountroot_try(const char *mountfrom)
|
1999-11-01 23:51:00 +00:00
|
|
|
{
|
2005-11-19 21:51:45 +00:00
|
|
|
struct mount *mp;
|
1999-11-01 23:51:00 +00:00
|
|
|
char *vfsname, *path;
|
2005-11-19 21:51:45 +00:00
|
|
|
time_t timebase;
|
1999-11-01 23:51:00 +00:00
|
|
|
int error;
|
1999-11-05 02:45:50 +00:00
|
|
|
char patt[32];
|
1999-11-01 23:51:00 +00:00
|
|
|
|
1999-11-06 07:43:22 +00:00
|
|
|
vfsname = NULL;
|
|
|
|
path = NULL;
|
|
|
|
mp = NULL;
|
|
|
|
error = EINVAL;
|
1999-11-01 23:51:00 +00:00
|
|
|
|
|
|
|
if (mountfrom == NULL)
|
2004-03-27 08:39:28 +00:00
|
|
|
return (error); /* don't complain */
|
2004-11-25 09:47:51 +00:00
|
|
|
printf("Trying to mount root from %s\n", mountfrom);
|
1999-11-01 23:51:00 +00:00
|
|
|
|
|
|
|
/* parse vfs name and path */
|
2003-02-19 05:47:46 +00:00
|
|
|
vfsname = malloc(MFSNAMELEN, M_MOUNT, M_WAITOK);
|
|
|
|
path = malloc(MNAMELEN, M_MOUNT, M_WAITOK);
|
1999-11-03 11:02:47 +00:00
|
|
|
vfsname[0] = path[0] = 0;
|
2003-11-12 17:09:12 +00:00
|
|
|
sprintf(patt, "%%%d[a-z0-9]:%%%ds", MFSNAMELEN, MNAMELEN);
|
2005-05-11 19:27:38 +00:00
|
|
|
if (sscanf(mountfrom, patt, vfsname, path) < 1)
|
|
|
|
goto out;
|
1999-11-01 23:51:00 +00:00
|
|
|
|
2004-11-25 09:47:51 +00:00
|
|
|
if (path[0] == '\0')
|
|
|
|
strcpy(path, ROOTNAME);
|
|
|
|
|
2004-12-07 08:15:41 +00:00
|
|
|
error = kernel_vmount(
|
2008-02-18 20:58:57 +00:00
|
|
|
MNT_RDONLY | MNT_ROOTFS,
|
2004-12-07 08:15:41 +00:00
|
|
|
"fstype", vfsname,
|
|
|
|
"fspath", "/",
|
|
|
|
"from", path,
|
|
|
|
NULL);
|
|
|
|
if (error == 0) {
|
2005-03-25 01:56:12 +00:00
|
|
|
/*
|
|
|
|
* We mount devfs prior to mounting the / FS, so the first
|
|
|
|
* entry will typically be devfs.
|
|
|
|
*/
|
2004-12-07 08:15:41 +00:00
|
|
|
mp = TAILQ_FIRST(&mountlist);
|
2005-03-25 01:56:12 +00:00
|
|
|
KASSERT(mp != NULL, ("%s: mountlist is empty", __func__));
|
2004-12-07 08:15:41 +00:00
|
|
|
|
2005-03-25 01:56:12 +00:00
|
|
|
/*
|
|
|
|
* Iterate over all currently mounted file systems and use
|
|
|
|
* the time stamp found to check and/or initialize the RTC.
|
|
|
|
* Typically devfs has no time stamp and the only other FS
|
|
|
|
* is the actual / FS.
|
2005-11-19 21:51:45 +00:00
|
|
|
* Call inittodr() only once and pass it the largest of the
|
|
|
|
* timestamps we encounter.
|
2005-03-25 01:56:12 +00:00
|
|
|
*/
|
2005-11-19 21:51:45 +00:00
|
|
|
timebase = 0;
|
2005-03-25 01:56:12 +00:00
|
|
|
do {
|
2005-11-19 21:51:45 +00:00
|
|
|
if (mp->mnt_time > timebase)
|
|
|
|
timebase = mp->mnt_time;
|
2005-03-25 01:56:12 +00:00
|
|
|
mp = TAILQ_NEXT(mp, mnt_list);
|
|
|
|
} while (mp != NULL);
|
2005-11-19 21:51:45 +00:00
|
|
|
inittodr(timebase);
|
2005-03-25 01:56:12 +00:00
|
|
|
|
2004-12-07 08:15:41 +00:00
|
|
|
devfs_fixup(curthread);
|
1999-11-28 22:20:18 +00:00
|
|
|
}
|
2005-05-11 19:27:38 +00:00
|
|
|
out:
|
|
|
|
free(path, M_MOUNT);
|
|
|
|
free(vfsname, M_MOUNT);
|
2004-03-27 08:39:28 +00:00
|
|
|
return (error);
|
1999-11-01 23:51:00 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2004-12-07 08:15:41 +00:00
|
|
|
* ---------------------------------------------------------------------
|
|
|
|
* Interactive root filesystem selection code.
|
1999-11-01 23:51:00 +00:00
|
|
|
*/
|
2004-12-07 08:15:41 +00:00
|
|
|
|
1999-11-01 23:51:00 +00:00
|
|
|
static int
|
|
|
|
vfs_mountroot_ask(void)
|
|
|
|
{
|
|
|
|
char name[128];
|
|
|
|
|
|
|
|
for(;;) {
|
|
|
|
printf("\nManual root filesystem specification:\n");
|
|
|
|
printf(" <fstype>:<device> Mount <device> using filesystem <fstype>\n");
|
2006-01-14 20:47:11 +00:00
|
|
|
#if defined(__amd64__) || defined(__i386__) || defined(__ia64__)
|
2001-11-24 01:34:12 +00:00
|
|
|
printf(" eg. ufs:da0s1a\n");
|
2001-11-20 23:48:00 +00:00
|
|
|
#else
|
2003-04-23 05:02:40 +00:00
|
|
|
printf(" eg. ufs:/dev/da0a\n");
|
2001-11-20 23:48:00 +00:00
|
|
|
#endif
|
1999-11-01 23:51:00 +00:00
|
|
|
printf(" ? List valid disk boot devices\n");
|
|
|
|
printf(" <empty line> Abort manual input\n");
|
2000-02-17 23:32:08 +00:00
|
|
|
printf("\nmountroot> ");
|
2005-02-03 15:10:58 +00:00
|
|
|
gets(name, sizeof(name), 1);
|
2004-03-27 08:39:28 +00:00
|
|
|
if (name[0] == '\0')
|
|
|
|
return (1);
|
1999-11-01 23:51:00 +00:00
|
|
|
if (name[0] == '?') {
|
2003-06-07 15:46:53 +00:00
|
|
|
printf("\nList of GEOM managed disk devices:\n ");
|
|
|
|
g_dev_print();
|
1999-11-01 23:51:00 +00:00
|
|
|
continue;
|
|
|
|
}
|
2004-10-05 11:26:43 +00:00
|
|
|
if (!vfs_mountroot_try(name))
|
2004-03-27 08:39:28 +00:00
|
|
|
return (0);
|
1999-11-01 23:51:00 +00:00
|
|
|
}
|
|
|
|
}
|
1995-08-28 09:19:25 +00:00
|
|
|
|
2004-12-06 18:18:35 +00:00
|
|
|
/*
|
|
|
|
* ---------------------------------------------------------------------
|
|
|
|
* Functions for querying mount options/arguments from filesystems.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Check that no unknown options are given
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
vfs_filteropt(struct vfsoptlist *opts, const char **legal)
|
|
|
|
{
|
|
|
|
struct vfsopt *opt;
|
On 6.x this works:
% mount | grep home
/dev/ad4s1e on /home (ufs, local, noatime, soft-updates)
% mount -u -o atime /home
% mount | grep home
/dev/ad4s1e on /home (ufs, local, soft-updates)
Restore this behavior for on 7.x for the following mount options:
noatime, noclusterr, noclusterw, noexec, nosuid, nosymfollow
In addition, on 7.x, the following are equivalent:
mount -u -o atime /home
mount -u -o nonoatime /home
Ideally, when we introduce new mount options, we should avoid
options starting with "no". :)
Requested by: jhb
Reported by: Karol Kwiat <karol.kwiat gmail com>, Scott Hetzel <swhetzel gmail com>
Approved by: re (bmah)
Proxy commit for: rodrigc
2007-08-15 17:40:09 +00:00
|
|
|
char errmsg[255];
|
|
|
|
const char **t, *p, *q;
|
|
|
|
int ret = 0;
|
2004-12-06 18:18:35 +00:00
|
|
|
|
|
|
|
TAILQ_FOREACH(opt, opts, link) {
|
|
|
|
p = opt->name;
|
On 6.x this works:
% mount | grep home
/dev/ad4s1e on /home (ufs, local, noatime, soft-updates)
% mount -u -o atime /home
% mount | grep home
/dev/ad4s1e on /home (ufs, local, soft-updates)
Restore this behavior for on 7.x for the following mount options:
noatime, noclusterr, noclusterw, noexec, nosuid, nosymfollow
In addition, on 7.x, the following are equivalent:
mount -u -o atime /home
mount -u -o nonoatime /home
Ideally, when we introduce new mount options, we should avoid
options starting with "no". :)
Requested by: jhb
Reported by: Karol Kwiat <karol.kwiat gmail com>, Scott Hetzel <swhetzel gmail com>
Approved by: re (bmah)
Proxy commit for: rodrigc
2007-08-15 17:40:09 +00:00
|
|
|
q = NULL;
|
2004-12-06 18:18:35 +00:00
|
|
|
if (p[0] == 'n' && p[1] == 'o')
|
On 6.x this works:
% mount | grep home
/dev/ad4s1e on /home (ufs, local, noatime, soft-updates)
% mount -u -o atime /home
% mount | grep home
/dev/ad4s1e on /home (ufs, local, soft-updates)
Restore this behavior for on 7.x for the following mount options:
noatime, noclusterr, noclusterw, noexec, nosuid, nosymfollow
In addition, on 7.x, the following are equivalent:
mount -u -o atime /home
mount -u -o nonoatime /home
Ideally, when we introduce new mount options, we should avoid
options starting with "no". :)
Requested by: jhb
Reported by: Karol Kwiat <karol.kwiat gmail com>, Scott Hetzel <swhetzel gmail com>
Approved by: re (bmah)
Proxy commit for: rodrigc
2007-08-15 17:40:09 +00:00
|
|
|
q = p + 2;
|
|
|
|
for(t = global_opts; *t != NULL; t++) {
|
|
|
|
if (strcmp(*t, p) == 0)
|
2004-12-06 18:18:35 +00:00
|
|
|
break;
|
On 6.x this works:
% mount | grep home
/dev/ad4s1e on /home (ufs, local, noatime, soft-updates)
% mount -u -o atime /home
% mount | grep home
/dev/ad4s1e on /home (ufs, local, soft-updates)
Restore this behavior for on 7.x for the following mount options:
noatime, noclusterr, noclusterw, noexec, nosuid, nosymfollow
In addition, on 7.x, the following are equivalent:
mount -u -o atime /home
mount -u -o nonoatime /home
Ideally, when we introduce new mount options, we should avoid
options starting with "no". :)
Requested by: jhb
Reported by: Karol Kwiat <karol.kwiat gmail com>, Scott Hetzel <swhetzel gmail com>
Approved by: re (bmah)
Proxy commit for: rodrigc
2007-08-15 17:40:09 +00:00
|
|
|
if (q != NULL) {
|
|
|
|
if (strcmp(*t, q) == 0)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2004-12-06 18:18:35 +00:00
|
|
|
if (*t != NULL)
|
|
|
|
continue;
|
On 6.x this works:
% mount | grep home
/dev/ad4s1e on /home (ufs, local, noatime, soft-updates)
% mount -u -o atime /home
% mount | grep home
/dev/ad4s1e on /home (ufs, local, soft-updates)
Restore this behavior for on 7.x for the following mount options:
noatime, noclusterr, noclusterw, noexec, nosuid, nosymfollow
In addition, on 7.x, the following are equivalent:
mount -u -o atime /home
mount -u -o nonoatime /home
Ideally, when we introduce new mount options, we should avoid
options starting with "no". :)
Requested by: jhb
Reported by: Karol Kwiat <karol.kwiat gmail com>, Scott Hetzel <swhetzel gmail com>
Approved by: re (bmah)
Proxy commit for: rodrigc
2007-08-15 17:40:09 +00:00
|
|
|
for(t = legal; *t != NULL; t++) {
|
|
|
|
if (strcmp(*t, p) == 0)
|
2004-12-06 18:18:35 +00:00
|
|
|
break;
|
On 6.x this works:
% mount | grep home
/dev/ad4s1e on /home (ufs, local, noatime, soft-updates)
% mount -u -o atime /home
% mount | grep home
/dev/ad4s1e on /home (ufs, local, soft-updates)
Restore this behavior for on 7.x for the following mount options:
noatime, noclusterr, noclusterw, noexec, nosuid, nosymfollow
In addition, on 7.x, the following are equivalent:
mount -u -o atime /home
mount -u -o nonoatime /home
Ideally, when we introduce new mount options, we should avoid
options starting with "no". :)
Requested by: jhb
Reported by: Karol Kwiat <karol.kwiat gmail com>, Scott Hetzel <swhetzel gmail com>
Approved by: re (bmah)
Proxy commit for: rodrigc
2007-08-15 17:40:09 +00:00
|
|
|
if (q != NULL) {
|
|
|
|
if (strcmp(*t, q) == 0)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2004-12-06 18:18:35 +00:00
|
|
|
if (*t != NULL)
|
|
|
|
continue;
|
2008-09-03 19:09:47 +00:00
|
|
|
snprintf(errmsg, sizeof(errmsg),
|
|
|
|
"mount option <%s> is unknown", p);
|
On 6.x this works:
% mount | grep home
/dev/ad4s1e on /home (ufs, local, noatime, soft-updates)
% mount -u -o atime /home
% mount | grep home
/dev/ad4s1e on /home (ufs, local, soft-updates)
Restore this behavior for on 7.x for the following mount options:
noatime, noclusterr, noclusterw, noexec, nosuid, nosymfollow
In addition, on 7.x, the following are equivalent:
mount -u -o atime /home
mount -u -o nonoatime /home
Ideally, when we introduce new mount options, we should avoid
options starting with "no". :)
Requested by: jhb
Reported by: Karol Kwiat <karol.kwiat gmail com>, Scott Hetzel <swhetzel gmail com>
Approved by: re (bmah)
Proxy commit for: rodrigc
2007-08-15 17:40:09 +00:00
|
|
|
printf("%s\n", errmsg);
|
|
|
|
ret = EINVAL;
|
2004-12-06 18:18:35 +00:00
|
|
|
}
|
On 6.x this works:
% mount | grep home
/dev/ad4s1e on /home (ufs, local, noatime, soft-updates)
% mount -u -o atime /home
% mount | grep home
/dev/ad4s1e on /home (ufs, local, soft-updates)
Restore this behavior for on 7.x for the following mount options:
noatime, noclusterr, noclusterw, noexec, nosuid, nosymfollow
In addition, on 7.x, the following are equivalent:
mount -u -o atime /home
mount -u -o nonoatime /home
Ideally, when we introduce new mount options, we should avoid
options starting with "no". :)
Requested by: jhb
Reported by: Karol Kwiat <karol.kwiat gmail com>, Scott Hetzel <swhetzel gmail com>
Approved by: re (bmah)
Proxy commit for: rodrigc
2007-08-15 17:40:09 +00:00
|
|
|
if (ret != 0) {
|
|
|
|
TAILQ_FOREACH(opt, opts, link) {
|
|
|
|
if (strcmp(opt->name, "errmsg") == 0) {
|
|
|
|
strncpy((char *)opt->value, errmsg, opt->len);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return (ret);
|
2004-12-06 18:18:35 +00:00
|
|
|
}
|
|
|
|
|
2002-07-02 17:09:22 +00:00
|
|
|
/*
|
|
|
|
* Get a mount option by its name.
|
|
|
|
*
|
|
|
|
* Return 0 if the option was found, ENOENT otherwise.
|
|
|
|
* If len is non-NULL it will be filled with the length
|
|
|
|
* of the option. If buf is non-NULL, it will be filled
|
|
|
|
* with the address of the option.
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
vfs_getopt(opts, name, buf, len)
|
|
|
|
struct vfsoptlist *opts;
|
|
|
|
const char *name;
|
|
|
|
void **buf;
|
|
|
|
int *len;
|
|
|
|
{
|
|
|
|
struct vfsopt *opt;
|
|
|
|
|
2003-02-14 13:30:25 +00:00
|
|
|
KASSERT(opts != NULL, ("vfs_getopt: caller passed 'opts' as NULL"));
|
2003-02-14 13:18:51 +00:00
|
|
|
|
2002-07-02 17:09:22 +00:00
|
|
|
TAILQ_FOREACH(opt, opts, link) {
|
|
|
|
if (strcmp(name, opt->name) == 0) {
|
2009-03-02 23:26:30 +00:00
|
|
|
opt->seen = 1;
|
2002-07-02 17:09:22 +00:00
|
|
|
if (len != NULL)
|
|
|
|
*len = opt->len;
|
|
|
|
if (buf != NULL)
|
|
|
|
*buf = opt->value;
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return (ENOENT);
|
|
|
|
}
|
|
|
|
|
2009-03-02 23:26:30 +00:00
|
|
|
int
|
2005-11-23 20:51:15 +00:00
|
|
|
vfs_getopt_pos(struct vfsoptlist *opts, const char *name)
|
|
|
|
{
|
|
|
|
struct vfsopt *opt;
|
|
|
|
|
|
|
|
if (opts == NULL)
|
|
|
|
return (-1);
|
|
|
|
|
|
|
|
TAILQ_FOREACH(opt, opts, link) {
|
2009-03-02 23:26:30 +00:00
|
|
|
if (strcmp(name, opt->name) == 0) {
|
|
|
|
opt->seen = 1;
|
|
|
|
return (opt->pos);
|
|
|
|
}
|
2005-11-23 20:51:15 +00:00
|
|
|
}
|
|
|
|
return (-1);
|
|
|
|
}
|
|
|
|
|
2004-12-06 18:18:35 +00:00
|
|
|
char *
|
|
|
|
vfs_getopts(struct vfsoptlist *opts, const char *name, int *error)
|
|
|
|
{
|
|
|
|
struct vfsopt *opt;
|
|
|
|
|
|
|
|
*error = 0;
|
|
|
|
TAILQ_FOREACH(opt, opts, link) {
|
|
|
|
if (strcmp(name, opt->name) != 0)
|
|
|
|
continue;
|
2009-03-02 23:26:30 +00:00
|
|
|
opt->seen = 1;
|
|
|
|
if (opt->len == 0 ||
|
|
|
|
((char *)opt->value)[opt->len - 1] != '\0') {
|
2004-12-06 18:18:35 +00:00
|
|
|
*error = EINVAL;
|
|
|
|
return (NULL);
|
|
|
|
}
|
|
|
|
return (opt->value);
|
|
|
|
}
|
2007-02-13 01:28:48 +00:00
|
|
|
*error = ENOENT;
|
2004-12-06 18:18:35 +00:00
|
|
|
return (NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
vfs_flagopt(struct vfsoptlist *opts, const char *name, u_int *w, u_int val)
|
|
|
|
{
|
|
|
|
struct vfsopt *opt;
|
|
|
|
|
|
|
|
TAILQ_FOREACH(opt, opts, link) {
|
|
|
|
if (strcmp(name, opt->name) == 0) {
|
2009-03-02 23:26:30 +00:00
|
|
|
opt->seen = 1;
|
2004-12-06 18:18:35 +00:00
|
|
|
if (w != NULL)
|
|
|
|
*w |= val;
|
|
|
|
return (1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (w != NULL)
|
|
|
|
*w &= ~val;
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
vfs_scanopt(struct vfsoptlist *opts, const char *name, const char *fmt, ...)
|
|
|
|
{
|
|
|
|
va_list ap;
|
|
|
|
struct vfsopt *opt;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
KASSERT(opts != NULL, ("vfs_getopt: caller passed 'opts' as NULL"));
|
|
|
|
|
|
|
|
TAILQ_FOREACH(opt, opts, link) {
|
|
|
|
if (strcmp(name, opt->name) != 0)
|
|
|
|
continue;
|
2009-03-02 23:26:30 +00:00
|
|
|
opt->seen = 1;
|
2007-12-31 23:44:53 +00:00
|
|
|
if (opt->len == 0 || opt->value == NULL)
|
|
|
|
return (0);
|
2004-12-06 18:18:35 +00:00
|
|
|
if (((char *)opt->value)[opt->len - 1] != '\0')
|
|
|
|
return (0);
|
|
|
|
va_start(ap, fmt);
|
|
|
|
ret = vsscanf(opt->value, fmt, ap);
|
|
|
|
va_end(ap);
|
|
|
|
return (ret);
|
|
|
|
}
|
|
|
|
return (0);
|
|
|
|
}
|
2004-12-07 08:15:41 +00:00
|
|
|
|
2009-03-02 23:26:30 +00:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2002-07-02 17:09:22 +00:00
|
|
|
/*
|
|
|
|
* Find and copy a mount option.
|
|
|
|
*
|
|
|
|
* The size of the buffer has to be specified
|
|
|
|
* in len, if it is not the same length as the
|
|
|
|
* mount option, EINVAL is returned.
|
|
|
|
* Returns ENOENT if the option is not found.
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
vfs_copyopt(opts, name, dest, len)
|
|
|
|
struct vfsoptlist *opts;
|
|
|
|
const char *name;
|
|
|
|
void *dest;
|
|
|
|
int len;
|
|
|
|
{
|
|
|
|
struct vfsopt *opt;
|
|
|
|
|
2003-02-14 13:30:25 +00:00
|
|
|
KASSERT(opts != NULL, ("vfs_copyopt: caller passed 'opts' as NULL"));
|
2003-02-14 13:18:51 +00:00
|
|
|
|
2002-07-02 17:09:22 +00:00
|
|
|
TAILQ_FOREACH(opt, opts, link) {
|
|
|
|
if (strcmp(name, opt->name) == 0) {
|
2009-03-02 23:26:30 +00:00
|
|
|
opt->seen = 1;
|
2002-07-02 17:09:22 +00:00
|
|
|
if (len != opt->len)
|
|
|
|
return (EINVAL);
|
|
|
|
bcopy(opt->value, dest, opt->len);
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return (ENOENT);
|
|
|
|
}
|
2004-07-04 08:52:35 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* This is a helper function for filesystems to traverse their
|
|
|
|
* vnodes. See MNT_VNODE_FOREACH() in sys/mount.h
|
|
|
|
*/
|
|
|
|
|
|
|
|
struct vnode *
|
2006-01-09 20:42:19 +00:00
|
|
|
__mnt_vnode_next(struct vnode **mvp, struct mount *mp)
|
2004-07-04 08:52:35 +00:00
|
|
|
{
|
|
|
|
struct vnode *vp;
|
|
|
|
|
2006-01-09 20:42:19 +00:00
|
|
|
mtx_assert(MNT_MTX(mp), MA_OWNED);
|
2004-11-25 12:06:43 +00:00
|
|
|
|
2006-01-09 20:42:19 +00:00
|
|
|
KASSERT((*mvp)->v_mount == mp, ("marker vnode mount list mismatch"));
|
2008-03-23 13:45:24 +00:00
|
|
|
if ((*mvp)->v_yield++ == 500) {
|
|
|
|
MNT_IUNLOCK(mp);
|
|
|
|
(*mvp)->v_yield = 0;
|
|
|
|
uio_yield();
|
|
|
|
MNT_ILOCK(mp);
|
|
|
|
}
|
2006-01-09 20:42:19 +00:00
|
|
|
vp = TAILQ_NEXT(*mvp, v_nmntvnodes);
|
2007-03-14 12:54:10 +00:00
|
|
|
while (vp != NULL && vp->v_type == VMARKER)
|
2006-01-09 20:42:19 +00:00
|
|
|
vp = TAILQ_NEXT(vp, v_nmntvnodes);
|
2007-03-14 12:54:10 +00:00
|
|
|
|
2004-07-04 08:52:35 +00:00
|
|
|
/* Check if we are done */
|
2006-01-09 20:42:19 +00:00
|
|
|
if (vp == NULL) {
|
|
|
|
__mnt_vnode_markerfree(mvp, mp);
|
2004-07-04 08:52:35 +00:00
|
|
|
return (NULL);
|
2006-01-09 20:42:19 +00:00
|
|
|
}
|
|
|
|
TAILQ_REMOVE(&mp->mnt_nvnodelist, *mvp, v_nmntvnodes);
|
|
|
|
TAILQ_INSERT_AFTER(&mp->mnt_nvnodelist, vp, *mvp, v_nmntvnodes);
|
2004-07-04 08:52:35 +00:00
|
|
|
return (vp);
|
|
|
|
}
|
2004-12-05 22:41:02 +00:00
|
|
|
|
2006-01-09 20:42:19 +00:00
|
|
|
struct vnode *
|
|
|
|
__mnt_vnode_first(struct vnode **mvp, struct mount *mp)
|
|
|
|
{
|
|
|
|
struct vnode *vp;
|
|
|
|
|
|
|
|
mtx_assert(MNT_MTX(mp), MA_OWNED);
|
|
|
|
|
|
|
|
vp = TAILQ_FIRST(&mp->mnt_nvnodelist);
|
2007-03-14 12:54:10 +00:00
|
|
|
while (vp != NULL && vp->v_type == VMARKER)
|
2006-01-09 20:42:19 +00:00
|
|
|
vp = TAILQ_NEXT(vp, v_nmntvnodes);
|
2007-03-14 12:54:10 +00:00
|
|
|
|
2006-01-09 20:42:19 +00:00
|
|
|
/* Check if we are done */
|
|
|
|
if (vp == NULL) {
|
|
|
|
*mvp = NULL;
|
|
|
|
return (NULL);
|
|
|
|
}
|
2008-11-03 20:00:35 +00:00
|
|
|
MNT_REF(mp);
|
2006-01-09 20:42:19 +00:00
|
|
|
MNT_IUNLOCK(mp);
|
|
|
|
*mvp = (struct vnode *) malloc(sizeof(struct vnode),
|
|
|
|
M_VNODE_MARKER,
|
|
|
|
M_WAITOK | M_ZERO);
|
|
|
|
MNT_ILOCK(mp);
|
|
|
|
(*mvp)->v_type = VMARKER;
|
|
|
|
|
|
|
|
vp = TAILQ_FIRST(&mp->mnt_nvnodelist);
|
2007-03-14 12:54:10 +00:00
|
|
|
while (vp != NULL && vp->v_type == VMARKER)
|
2006-01-09 20:42:19 +00:00
|
|
|
vp = TAILQ_NEXT(vp, v_nmntvnodes);
|
2007-03-14 12:54:10 +00:00
|
|
|
|
2006-01-09 20:42:19 +00:00
|
|
|
/* Check if we are done */
|
|
|
|
if (vp == NULL) {
|
|
|
|
MNT_IUNLOCK(mp);
|
|
|
|
free(*mvp, M_VNODE_MARKER);
|
|
|
|
MNT_ILOCK(mp);
|
|
|
|
*mvp = NULL;
|
2008-11-03 20:00:35 +00:00
|
|
|
MNT_REL(mp);
|
2006-01-09 20:42:19 +00:00
|
|
|
return (NULL);
|
|
|
|
}
|
|
|
|
(*mvp)->v_mount = mp;
|
|
|
|
TAILQ_INSERT_AFTER(&mp->mnt_nvnodelist, vp, *mvp, v_nmntvnodes);
|
|
|
|
return (vp);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
__mnt_vnode_markerfree(struct vnode **mvp, struct mount *mp)
|
|
|
|
{
|
|
|
|
|
|
|
|
if (*mvp == NULL)
|
|
|
|
return;
|
2007-03-14 12:54:10 +00:00
|
|
|
|
2006-01-09 20:42:19 +00:00
|
|
|
mtx_assert(MNT_MTX(mp), MA_OWNED);
|
|
|
|
|
|
|
|
KASSERT((*mvp)->v_mount == mp, ("marker vnode mount list mismatch"));
|
|
|
|
TAILQ_REMOVE(&mp->mnt_nvnodelist, *mvp, v_nmntvnodes);
|
|
|
|
MNT_IUNLOCK(mp);
|
|
|
|
free(*mvp, M_VNODE_MARKER);
|
|
|
|
MNT_ILOCK(mp);
|
|
|
|
*mvp = NULL;
|
2008-11-03 20:00:35 +00:00
|
|
|
MNT_REL(mp);
|
2006-01-09 20:42:19 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2004-12-05 22:41:02 +00:00
|
|
|
int
|
|
|
|
__vfs_statfs(struct mount *mp, struct statfs *sbp, struct thread *td)
|
|
|
|
{
|
|
|
|
int error;
|
|
|
|
|
|
|
|
error = mp->mnt_op->vfs_statfs(mp, &mp->mnt_stat, td);
|
|
|
|
if (sbp != &mp->mnt_stat)
|
2004-12-11 22:13:02 +00:00
|
|
|
*sbp = mp->mnt_stat;
|
2004-12-05 22:41:02 +00:00
|
|
|
return (error);
|
|
|
|
}
|
2004-12-06 13:01:41 +00:00
|
|
|
|
2004-12-06 18:18:35 +00:00
|
|
|
void
|
|
|
|
vfs_mountedfrom(struct mount *mp, const char *from)
|
|
|
|
{
|
|
|
|
|
|
|
|
bzero(mp->mnt_stat.f_mntfromname, sizeof mp->mnt_stat.f_mntfromname);
|
|
|
|
strlcpy(mp->mnt_stat.f_mntfromname, from,
|
|
|
|
sizeof mp->mnt_stat.f_mntfromname);
|
|
|
|
}
|
|
|
|
|
2004-12-06 13:01:41 +00:00
|
|
|
/*
|
|
|
|
* ---------------------------------------------------------------------
|
|
|
|
* This is the api for building mount args and mounting filesystems from
|
|
|
|
* inside the kernel.
|
|
|
|
*
|
|
|
|
* The API works by accumulation of individual args. First error is
|
|
|
|
* latched.
|
|
|
|
*
|
|
|
|
* XXX: should be documented in new manpage kernel_mount(9)
|
|
|
|
*/
|
|
|
|
|
|
|
|
/* A memory allocation which must be freed when we are done */
|
|
|
|
struct mntaarg {
|
|
|
|
SLIST_ENTRY(mntaarg) next;
|
|
|
|
};
|
|
|
|
|
|
|
|
/* The header for the mount arguments */
|
|
|
|
struct mntarg {
|
|
|
|
struct iovec *v;
|
|
|
|
int len;
|
|
|
|
int error;
|
|
|
|
SLIST_HEAD(, mntaarg) list;
|
|
|
|
};
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Add a boolean argument.
|
|
|
|
*
|
|
|
|
* flag is the boolean value.
|
|
|
|
* name must start with "no".
|
|
|
|
*/
|
|
|
|
struct mntarg *
|
|
|
|
mount_argb(struct mntarg *ma, int flag, const char *name)
|
|
|
|
{
|
|
|
|
|
|
|
|
KASSERT(name[0] == 'n' && name[1] == 'o',
|
|
|
|
("mount_argb(...,%s): name must start with 'no'", name));
|
|
|
|
|
|
|
|
return (mount_arg(ma, name + (flag ? 2 : 0), NULL, 0));
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Add an argument printf style
|
|
|
|
*/
|
|
|
|
struct mntarg *
|
|
|
|
mount_argf(struct mntarg *ma, const char *name, const char *fmt, ...)
|
|
|
|
{
|
|
|
|
va_list ap;
|
|
|
|
struct mntaarg *maa;
|
|
|
|
struct sbuf *sb;
|
|
|
|
int len;
|
|
|
|
|
|
|
|
if (ma == NULL) {
|
|
|
|
ma = malloc(sizeof *ma, M_MOUNT, M_WAITOK | M_ZERO);
|
|
|
|
SLIST_INIT(&ma->list);
|
|
|
|
}
|
|
|
|
if (ma->error)
|
|
|
|
return (ma);
|
|
|
|
|
|
|
|
ma->v = realloc(ma->v, sizeof *ma->v * (ma->len + 2),
|
|
|
|
M_MOUNT, M_WAITOK);
|
|
|
|
ma->v[ma->len].iov_base = (void *)(uintptr_t)name;
|
|
|
|
ma->v[ma->len].iov_len = strlen(name) + 1;
|
|
|
|
ma->len++;
|
|
|
|
|
2008-08-09 11:14:05 +00:00
|
|
|
sb = sbuf_new_auto();
|
2004-12-06 13:01:41 +00:00
|
|
|
va_start(ap, fmt);
|
|
|
|
sbuf_vprintf(sb, fmt, ap);
|
|
|
|
va_end(ap);
|
|
|
|
sbuf_finish(sb);
|
|
|
|
len = sbuf_len(sb) + 1;
|
|
|
|
maa = malloc(sizeof *maa + len, M_MOUNT, M_WAITOK | M_ZERO);
|
|
|
|
SLIST_INSERT_HEAD(&ma->list, maa, next);
|
|
|
|
bcopy(sbuf_data(sb), maa + 1, len);
|
|
|
|
sbuf_delete(sb);
|
|
|
|
|
|
|
|
ma->v[ma->len].iov_base = maa + 1;
|
|
|
|
ma->v[ma->len].iov_len = len;
|
|
|
|
ma->len++;
|
|
|
|
|
|
|
|
return (ma);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Add an argument which is a userland string.
|
|
|
|
*/
|
|
|
|
struct mntarg *
|
|
|
|
mount_argsu(struct mntarg *ma, const char *name, const void *val, int len)
|
|
|
|
{
|
|
|
|
struct mntaarg *maa;
|
|
|
|
char *tbuf;
|
|
|
|
|
|
|
|
if (val == NULL)
|
|
|
|
return (ma);
|
|
|
|
if (ma == NULL) {
|
|
|
|
ma = malloc(sizeof *ma, M_MOUNT, M_WAITOK | M_ZERO);
|
|
|
|
SLIST_INIT(&ma->list);
|
|
|
|
}
|
|
|
|
if (ma->error)
|
|
|
|
return (ma);
|
|
|
|
maa = malloc(sizeof *maa + len, M_MOUNT, M_WAITOK | M_ZERO);
|
|
|
|
SLIST_INSERT_HEAD(&ma->list, maa, next);
|
|
|
|
tbuf = (void *)(maa + 1);
|
|
|
|
ma->error = copyinstr(val, tbuf, len, NULL);
|
|
|
|
return (mount_arg(ma, name, tbuf, -1));
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Plain argument.
|
|
|
|
*
|
2008-02-14 17:04:31 +00:00
|
|
|
* If length is -1, treat value as a C string.
|
2004-12-06 13:01:41 +00:00
|
|
|
*/
|
|
|
|
struct mntarg *
|
|
|
|
mount_arg(struct mntarg *ma, const char *name, const void *val, int len)
|
|
|
|
{
|
|
|
|
|
|
|
|
if (ma == NULL) {
|
|
|
|
ma = malloc(sizeof *ma, M_MOUNT, M_WAITOK | M_ZERO);
|
|
|
|
SLIST_INIT(&ma->list);
|
|
|
|
}
|
|
|
|
if (ma->error)
|
|
|
|
return (ma);
|
|
|
|
|
|
|
|
ma->v = realloc(ma->v, sizeof *ma->v * (ma->len + 2),
|
|
|
|
M_MOUNT, M_WAITOK);
|
|
|
|
ma->v[ma->len].iov_base = (void *)(uintptr_t)name;
|
|
|
|
ma->v[ma->len].iov_len = strlen(name) + 1;
|
|
|
|
ma->len++;
|
|
|
|
|
|
|
|
ma->v[ma->len].iov_base = (void *)(uintptr_t)val;
|
|
|
|
if (len < 0)
|
|
|
|
ma->v[ma->len].iov_len = strlen(val) + 1;
|
|
|
|
else
|
|
|
|
ma->v[ma->len].iov_len = len;
|
|
|
|
ma->len++;
|
|
|
|
return (ma);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Free a mntarg structure
|
|
|
|
*/
|
2005-02-10 12:25:38 +00:00
|
|
|
static void
|
2004-12-06 13:01:41 +00:00
|
|
|
free_mntarg(struct mntarg *ma)
|
|
|
|
{
|
|
|
|
struct mntaarg *maa;
|
|
|
|
|
|
|
|
while (!SLIST_EMPTY(&ma->list)) {
|
|
|
|
maa = SLIST_FIRST(&ma->list);
|
|
|
|
SLIST_REMOVE_HEAD(&ma->list, next);
|
|
|
|
free(maa, M_MOUNT);
|
|
|
|
}
|
|
|
|
free(ma->v, M_MOUNT);
|
|
|
|
free(ma, M_MOUNT);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Mount a filesystem
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
kernel_mount(struct mntarg *ma, int flags)
|
|
|
|
{
|
|
|
|
struct uio auio;
|
|
|
|
int error;
|
|
|
|
|
|
|
|
KASSERT(ma != NULL, ("kernel_mount NULL ma"));
|
|
|
|
KASSERT(ma->v != NULL, ("kernel_mount NULL ma->v"));
|
|
|
|
KASSERT(!(ma->len & 1), ("kernel_mount odd ma->len (%d)", ma->len));
|
|
|
|
|
|
|
|
auio.uio_iov = ma->v;
|
|
|
|
auio.uio_iovcnt = ma->len;
|
|
|
|
auio.uio_segflg = UIO_SYSSPACE;
|
|
|
|
|
|
|
|
error = ma->error;
|
|
|
|
if (!error)
|
|
|
|
error = vfs_donmount(curthread, flags, &auio);
|
|
|
|
free_mntarg(ma);
|
|
|
|
return (error);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* A printflike function to mount a filesystem.
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
kernel_vmount(int flags, ...)
|
|
|
|
{
|
|
|
|
struct mntarg *ma = NULL;
|
|
|
|
va_list ap;
|
|
|
|
const char *cp;
|
|
|
|
const void *vp;
|
|
|
|
int error;
|
|
|
|
|
|
|
|
va_start(ap, flags);
|
|
|
|
for (;;) {
|
|
|
|
cp = va_arg(ap, const char *);
|
|
|
|
if (cp == NULL)
|
|
|
|
break;
|
|
|
|
vp = va_arg(ap, const void *);
|
2008-02-14 17:04:31 +00:00
|
|
|
ma = mount_arg(ma, cp, vp, (vp != NULL ? -1 : 0));
|
2004-12-06 13:01:41 +00:00
|
|
|
}
|
|
|
|
va_end(ap);
|
|
|
|
|
|
|
|
error = kernel_mount(ma, flags);
|
|
|
|
return (error);
|
|
|
|
}
|