2002-07-02 17:09:22 +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>
|
|
|
|
#include <sys/cons.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>
|
2002-07-31 01:11:29 +00:00
|
|
|
#include <sys/mac.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>
|
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>
|
|
|
|
#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>
|
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
|
|
|
|
2002-07-02 17:09:22 +00:00
|
|
|
#include "opt_rootdevname.h"
|
2000-03-20 16:28:35 +00:00
|
|
|
#include "opt_ddb.h"
|
2002-07-31 01:11:29 +00:00
|
|
|
#include "opt_mac.h"
|
2001-05-01 08:13:21 +00:00
|
|
|
|
2000-03-20 16:28:35 +00:00
|
|
|
#ifdef DDB
|
|
|
|
#include <ddb/ddb.h>
|
|
|
|
#endif
|
|
|
|
|
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
|
|
|
|
|
|
|
static void checkdirs(struct vnode *olddp, struct vnode *newdp);
|
2004-11-09 23:03:34 +00:00
|
|
|
static struct cdev *getdiskbyname(char *_name);
|
2002-07-02 17:09:22 +00:00
|
|
|
static void gets(char *cp);
|
2003-11-14 05:27:41 +00:00
|
|
|
static int vfs_domount(struct thread *td, const char *fstype,
|
|
|
|
char *fspath, int fsflags, void *fsdata, int compat);
|
|
|
|
static int vfs_mount_alloc(struct vnode *dvp, struct vfsconf *vfsp,
|
|
|
|
const char *fspath, struct thread *td, struct mount **mpp);
|
|
|
|
static int vfs_mountroot_ask(void);
|
2004-07-30 22:08:52 +00:00
|
|
|
static int vfs_mountroot_try(const char *mountfrom);
|
2004-07-27 22:32:01 +00:00
|
|
|
static int vfs_donmount(struct thread *td, int fsflags,
|
|
|
|
struct uio *fsoptions);
|
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");
|
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;
|
|
|
|
|
2004-12-06 12:36:17 +00:00
|
|
|
TAILQ_HEAD(vfsoptlist, vfsopt);
|
|
|
|
struct vfsopt {
|
|
|
|
TAILQ_ENTRY(vfsopt) link;
|
|
|
|
char *name;
|
|
|
|
void *value;
|
|
|
|
int len;
|
|
|
|
};
|
|
|
|
|
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[] = {
|
|
|
|
"fstype",
|
|
|
|
"fspath",
|
|
|
|
"ro",
|
|
|
|
"suid",
|
|
|
|
"exec",
|
|
|
|
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-06-17 17:16:53 +00:00
|
|
|
struct cdev *rootdev = NULL;
|
2004-07-30 22:08:52 +00:00
|
|
|
#ifdef ROOTDEVNAME
|
|
|
|
const char *ctrootdevname = ROOTDEVNAME;
|
|
|
|
#else
|
|
|
|
const char *ctrootdevname = NULL;
|
|
|
|
#endif
|
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);
|
|
|
|
#ifdef INVARIANTS
|
|
|
|
else if (opt->len != 0)
|
|
|
|
panic("%s: mount option with NULL value but length != 0",
|
|
|
|
__func__);
|
|
|
|
#endif
|
|
|
|
free(opt, M_MOUNT);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Release all resources related to the mount options. */
|
2002-07-02 17:09:22 +00:00
|
|
|
static void
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
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)
|
|
|
|
{
|
|
|
|
|
|
|
|
/* "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);
|
|
|
|
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.
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
vfs_buildopts(struct uio *auio, struct vfsoptlist **options)
|
|
|
|
{
|
|
|
|
struct vfsoptlist *opts;
|
|
|
|
struct vfsopt *opt;
|
2003-11-14 05:27:41 +00:00
|
|
|
size_t memused;
|
2002-07-02 17:09:22 +00:00
|
|
|
unsigned int i, iovcnt;
|
|
|
|
int error, namelen, optlen;
|
|
|
|
|
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) {
|
2003-02-19 05:47:46 +00:00
|
|
|
opt = malloc(sizeof(struct vfsopt), M_MOUNT, M_WAITOK);
|
2002-07-02 17:09:22 +00:00
|
|
|
namelen = auio->uio_iov[i].iov_len;
|
|
|
|
optlen = auio->uio_iov[i + 1].iov_len;
|
2003-02-19 05:47:46 +00:00
|
|
|
opt->name = malloc(namelen, M_MOUNT, M_WAITOK);
|
2002-07-24 19:50:00 +00:00
|
|
|
opt->value = NULL;
|
2004-09-05 22:24:28 +00:00
|
|
|
opt->len = 0;
|
2003-11-12 02:54:47 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Do this early, so jumps to "bad" will free the current
|
2003-11-14 05:27:41 +00:00
|
|
|
* option.
|
2003-11-12 02:54:47 +00:00
|
|
|
*/
|
|
|
|
TAILQ_INSERT_TAIL(opts, opt, link);
|
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;
|
|
|
|
}
|
|
|
|
|
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. */
|
2003-11-12 02:54:47 +00:00
|
|
|
if (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.
|
|
|
|
*/
|
|
|
|
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;
|
|
|
|
TAILQ_INSERT_TAIL(toopts, new, link);
|
|
|
|
next:
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2002-07-02 17:09:22 +00:00
|
|
|
/*
|
|
|
|
* New mount API.
|
|
|
|
*/
|
|
|
|
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;
|
|
|
|
struct iovec *iov;
|
2002-07-02 17:09:22 +00:00
|
|
|
unsigned int i;
|
|
|
|
int error;
|
2004-07-10 15:42:16 +00:00
|
|
|
u_int iovcnt;
|
2002-07-02 17:09:22 +00:00
|
|
|
|
2004-12-03 19:25:44 +00:00
|
|
|
/* Kick out MNT_ROOTFS early as it is legal internally */
|
|
|
|
if (uap->flags & MNT_ROOTFS)
|
|
|
|
return (EINVAL);
|
|
|
|
|
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.
|
|
|
|
*/
|
2004-07-10 15:42:16 +00:00
|
|
|
if ((iovcnt & 1) || (iovcnt < 4))
|
2002-07-02 17:09:22 +00:00
|
|
|
return (EINVAL);
|
2004-12-03 19:25:44 +00:00
|
|
|
|
2004-07-10 15:42:16 +00:00
|
|
|
error = copyinuio(uap->iovp, iovcnt, &auio);
|
|
|
|
if (error)
|
|
|
|
return (error);
|
|
|
|
iov = auio->uio_iov;
|
2002-07-02 17:09:22 +00:00
|
|
|
for (i = 0; i < iovcnt; i++) {
|
|
|
|
if (iov->iov_len > MMAXOPTIONLEN) {
|
2004-07-10 15:42:16 +00:00
|
|
|
free(auio, M_IOV);
|
|
|
|
return (EINVAL);
|
2002-07-02 17:09:22 +00:00
|
|
|
}
|
|
|
|
iov++;
|
|
|
|
}
|
2004-07-27 22:32:01 +00:00
|
|
|
error = vfs_donmount(td, uap->flags, auio);
|
2004-07-10 15:42:16 +00:00
|
|
|
free(auio, M_IOV);
|
2002-07-02 17:09:22 +00:00
|
|
|
return (error);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2003-11-12 02:54:47 +00:00
|
|
|
* Allocate and initialize the mount point struct.
|
2002-07-02 17:09:22 +00:00
|
|
|
*/
|
|
|
|
static int
|
2003-11-12 02:54:47 +00:00
|
|
|
vfs_mount_alloc(struct vnode *vp, struct vfsconf *vfsp,
|
|
|
|
const char *fspath, struct thread *td, struct mount **mpp)
|
2002-07-02 17:09:22 +00:00
|
|
|
{
|
|
|
|
struct mount *mp;
|
2003-11-12 02:54:47 +00:00
|
|
|
|
|
|
|
mp = malloc(sizeof(struct mount), M_MOUNT, M_WAITOK | M_ZERO);
|
|
|
|
TAILQ_INIT(&mp->mnt_nvnodelist);
|
|
|
|
mp->mnt_nvnodelistsize = 0;
|
|
|
|
mtx_init(&mp->mnt_mtx, "struct mount mtx", NULL, MTX_DEF);
|
|
|
|
lockinit(&mp->mnt_lock, PVFS, "vfslock", 0, LK_NOPAUSE);
|
|
|
|
vfs_busy(mp, LK_NOWAIT, 0, td);
|
|
|
|
mp->mnt_op = vfsp->vfc_vfsops;
|
|
|
|
mp->mnt_vfc = vfsp;
|
|
|
|
vfsp->vfc_refcount++;
|
|
|
|
mp->mnt_stat.f_type = vfsp->vfc_typenum;
|
|
|
|
mp->mnt_flag |= vfsp->vfc_flags & MNT_VISFLAGMASK;
|
|
|
|
strlcpy(mp->mnt_stat.f_fstypename, vfsp->vfc_name, MFSNAMELEN);
|
|
|
|
mp->mnt_vnodecovered = vp;
|
|
|
|
mp->mnt_cred = crdup(td->td_ucred);
|
|
|
|
mp->mnt_stat.f_owner = td->td_ucred->cr_uid;
|
|
|
|
strlcpy(mp->mnt_stat.f_mntonname, fspath, MNAMELEN);
|
|
|
|
mp->mnt_iosize_max = DFLTPHYS;
|
|
|
|
#ifdef MAC
|
|
|
|
mac_init_mount(mp);
|
|
|
|
mac_create_mount(td->td_ucred, mp);
|
|
|
|
#endif
|
|
|
|
*mpp = mp;
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
2003-11-14 05:27:41 +00:00
|
|
|
/*
|
|
|
|
* Destroy the mount struct previously allocated by vfs_mount_alloc().
|
|
|
|
*/
|
2003-11-12 02:54:47 +00:00
|
|
|
void
|
|
|
|
vfs_mount_destroy(struct mount *mp, struct thread *td)
|
|
|
|
{
|
|
|
|
|
|
|
|
mp->mnt_vfc->vfc_refcount--;
|
|
|
|
if (!TAILQ_EMPTY(&mp->mnt_nvnodelist))
|
|
|
|
panic("unmount: dangling vnode");
|
|
|
|
vfs_unbusy(mp,td);
|
|
|
|
lockdestroy(&mp->mnt_lock);
|
|
|
|
mtx_destroy(&mp->mnt_mtx);
|
|
|
|
if (mp->mnt_kern_flag & MNTK_MWAIT)
|
|
|
|
wakeup(mp);
|
|
|
|
#ifdef MAC
|
|
|
|
mac_destroy_mount(mp);
|
|
|
|
#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);
|
|
|
|
free(mp, M_MOUNT);
|
|
|
|
}
|
|
|
|
|
2004-07-27 22:32:01 +00:00
|
|
|
static int
|
|
|
|
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;
|
|
|
|
char *fstype, *fspath;
|
2003-11-14 05:27:41 +00:00
|
|
|
int error, fstypelen, fspathlen;
|
2002-07-02 17:09:22 +00:00
|
|
|
|
|
|
|
error = vfs_buildopts(fsoptions, &optlist);
|
|
|
|
if (error)
|
|
|
|
return (error);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* 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;
|
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;
|
2003-11-12 02:54:47 +00:00
|
|
|
goto bail;
|
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);
|
2003-11-12 02:54:47 +00:00
|
|
|
error = vfs_domount(td, fstype, fspath, fsflags, optlist, 0);
|
2004-11-25 12:06:43 +00:00
|
|
|
mtx_unlock(&Giant);
|
2003-11-12 02:54:47 +00:00
|
|
|
bail:
|
|
|
|
if (error)
|
|
|
|
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;
|
|
|
|
char *fspath;
|
2004-12-03 21:14:46 +00:00
|
|
|
struct vfsconf *vfsp;
|
2004-12-06 16:39:05 +00:00
|
|
|
struct mntarg *ma = NULL;
|
2002-07-02 17:09:22 +00:00
|
|
|
int error;
|
|
|
|
|
2004-12-03 19:25:44 +00:00
|
|
|
/* Kick out MNT_ROOTFS early as it is legal internally */
|
|
|
|
if (uap->flags & MNT_ROOTFS)
|
|
|
|
return (EINVAL);
|
|
|
|
|
2003-02-19 05:47:46 +00:00
|
|
|
fstype = malloc(MFSNAMELEN, M_TEMP, M_WAITOK);
|
2002-07-02 17:09:22 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* vfs_mount() actually takes a kernel string for `type' and
|
|
|
|
* `path' now, so extract them.
|
|
|
|
*/
|
2002-12-14 01:56:26 +00:00
|
|
|
error = copyinstr(uap->type, fstype, MFSNAMELEN, NULL);
|
2004-12-06 16:39:05 +00:00
|
|
|
mtx_lock(&Giant); /* XXX ? */
|
|
|
|
vfsp = vfs_byname_kld(fstype, td, &error);
|
|
|
|
mtx_unlock(&Giant); /* XXX ? */
|
|
|
|
if (vfsp == NULL) {
|
|
|
|
free(fstype, M_TEMP);
|
|
|
|
return (ENOENT);
|
|
|
|
}
|
|
|
|
fspath = malloc(MNAMELEN, M_TEMP, M_WAITOK);
|
|
|
|
error = copyinstr(uap->path, fspath, MNAMELEN, NULL);
|
|
|
|
if (error == 0 && vfsp->vfc_vfsops->vfs_cmount != NULL) {
|
|
|
|
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);
|
|
|
|
} else if (error == 0) {
|
2004-11-25 12:06:43 +00:00
|
|
|
mtx_lock(&Giant);
|
2004-12-06 16:39:05 +00:00
|
|
|
error = vfs_domount(td, fstype, fspath,
|
|
|
|
uap->flags, uap->data, 1);
|
2004-11-25 12:06:43 +00:00
|
|
|
mtx_unlock(&Giant);
|
|
|
|
}
|
2002-07-02 17:09:22 +00:00
|
|
|
free(fstype, M_TEMP);
|
|
|
|
free(fspath, M_TEMP);
|
|
|
|
return (error);
|
|
|
|
}
|
|
|
|
|
2003-11-12 02:54:47 +00:00
|
|
|
/*
|
|
|
|
* vfs_domount(): actually attempt a filesystem mount.
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
vfs_domount(
|
2003-11-14 05:27:41 +00:00
|
|
|
struct thread *td, /* Flags common to all filesystems. */
|
|
|
|
const char *fstype, /* Filesystem type. */
|
|
|
|
char *fspath, /* Mount path. */
|
|
|
|
int fsflags, /* Flags common to all filesystems. */
|
|
|
|
void *fsdata, /* Options local to the filesystem. */
|
|
|
|
int compat /* Invocation from compat syscall. */
|
|
|
|
)
|
2002-07-02 17:09:22 +00:00
|
|
|
{
|
|
|
|
struct vnode *vp;
|
|
|
|
struct mount *mp;
|
|
|
|
struct vfsconf *vfsp;
|
|
|
|
int error, flag = 0, kern_flag = 0;
|
|
|
|
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);
|
|
|
|
|
2004-02-02 19:02:05 +00:00
|
|
|
if (jailed(td->td_ucred))
|
|
|
|
return (EPERM);
|
2002-07-02 17:09:22 +00:00
|
|
|
if (usermount == 0) {
|
2004-03-27 08:39:28 +00:00
|
|
|
if ((error = suser(td)) != 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
|
|
|
*/
|
2004-03-27 08:09:00 +00:00
|
|
|
if (fsflags & (MNT_EXPORTED | MNT_SUIDDIR)) {
|
2004-03-27 08:39:28 +00:00
|
|
|
if ((error = suser(td)) != 0)
|
2002-07-02 17:09:22 +00:00
|
|
|
return (error);
|
|
|
|
}
|
|
|
|
/*
|
2004-11-26 19:28:39 +00:00
|
|
|
* Silently enforce MNT_NOSUID and MNT_USER for
|
2004-03-27 08:09:00 +00:00
|
|
|
* unprivileged users.
|
2002-07-02 17:09:22 +00:00
|
|
|
*/
|
2004-03-27 08:09:00 +00:00
|
|
|
if (suser(td) != 0)
|
2004-11-26 19:28:39 +00:00
|
|
|
fsflags |= MNT_NOSUID | MNT_USER;
|
2002-07-02 17:09:22 +00:00
|
|
|
/*
|
|
|
|
* Get vnode to be covered
|
|
|
|
*/
|
|
|
|
NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF, UIO_SYSSPACE, fspath, td);
|
|
|
|
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;
|
|
|
|
flag = mp->mnt_flag;
|
|
|
|
kern_flag = mp->mnt_kern_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)) {
|
|
|
|
vput(vp);
|
|
|
|
return (EOPNOTSUPP); /* Needs translation */
|
|
|
|
}
|
|
|
|
/*
|
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
|
|
|
}
|
|
|
|
if (vfs_busy(mp, LK_NOWAIT, 0, td)) {
|
|
|
|
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);
|
2002-07-02 17:09:22 +00:00
|
|
|
vfs_unbusy(mp, td);
|
|
|
|
vput(vp);
|
|
|
|
return (EBUSY);
|
|
|
|
}
|
2002-08-04 10:29:36 +00:00
|
|
|
vp->v_iflag |= VI_MOUNT;
|
|
|
|
VI_UNLOCK(vp);
|
2002-07-02 17:09:22 +00:00
|
|
|
mp->mnt_flag |= fsflags &
|
|
|
|
(MNT_RELOAD | MNT_FORCE | MNT_UPDATE | MNT_SNAPSHOT);
|
|
|
|
VOP_UNLOCK(vp, 0, td);
|
2003-11-12 02:54:47 +00:00
|
|
|
if (compat == 0) {
|
|
|
|
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.
|
|
|
|
*/
|
|
|
|
error = VOP_GETATTR(vp, &va, td->td_ucred, td);
|
|
|
|
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) {
|
|
|
|
if ((error = suser(td)) != 0) {
|
|
|
|
vput(vp);
|
|
|
|
return (error);
|
|
|
|
}
|
2002-07-02 17:09:22 +00:00
|
|
|
}
|
2004-07-27 22:32:01 +00:00
|
|
|
if ((error = vinvalbuf(vp, V_SAVE, td->td_ucred, td, 0, 0)) != 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-12-03 16:11:01 +00:00
|
|
|
vfsp = vfs_byname_kld(fstype, td, &error);
|
2002-07-02 17:09:22 +00:00
|
|
|
if (vfsp == NULL) {
|
2004-12-03 16:11:01 +00:00
|
|
|
vput(vp);
|
|
|
|
return (error);
|
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.
|
|
|
|
*/
|
|
|
|
error = vfs_mount_alloc(vp, vfsp, fspath, td, &mp);
|
|
|
|
if (error) {
|
|
|
|
vput(vp);
|
|
|
|
return (error);
|
|
|
|
}
|
|
|
|
VOP_UNLOCK(vp, 0, td);
|
2003-11-12 02:54:47 +00:00
|
|
|
|
2004-07-27 22:32:01 +00:00
|
|
|
/* XXXMAC: pass to vfs_mount_alloc? */
|
|
|
|
if (compat == 0)
|
|
|
|
mp->mnt_optnew = fsdata;
|
|
|
|
}
|
2002-07-02 17:09:22 +00:00
|
|
|
/*
|
2004-07-30 22:08:52 +00:00
|
|
|
* Check if the fs implements the type VFS_[O]MOUNT()
|
2003-11-12 02:54:47 +00:00
|
|
|
* function we are looking for.
|
2002-07-02 17:09:22 +00:00
|
|
|
*/
|
2004-11-25 12:19:24 +00:00
|
|
|
if ((compat && (mp->mnt_op->vfs_omount == NULL)) ||
|
|
|
|
(!compat && (mp->mnt_op->vfs_mount == NULL))) {
|
2003-11-12 02:54:47 +00:00
|
|
|
printf("%s doesn't support the %s mount syscall\n",
|
2004-03-27 08:39:28 +00:00
|
|
|
mp->mnt_vfc->vfc_name, compat ? "old" : "new");
|
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
|
|
|
if (mp->mnt_flag & MNT_UPDATE)
|
|
|
|
vfs_unbusy(mp, td);
|
2003-11-12 02:54:47 +00:00
|
|
|
else
|
|
|
|
vfs_mount_destroy(mp, td);
|
2002-07-02 17:09:22 +00:00
|
|
|
vrele(vp);
|
|
|
|
return (EOPNOTSUPP);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Set the mount level flags.
|
|
|
|
*/
|
|
|
|
if (fsflags & MNT_RDONLY)
|
|
|
|
mp->mnt_flag |= MNT_RDONLY;
|
|
|
|
else if (mp->mnt_flag & MNT_RDONLY)
|
|
|
|
mp->mnt_kern_flag |= MNTK_WANTRDWR;
|
|
|
|
mp->mnt_flag &=~ MNT_UPDATEMASK;
|
|
|
|
mp->mnt_flag |= fsflags & (MNT_UPDATEMASK | MNT_FORCE);
|
|
|
|
/*
|
|
|
|
* Mount the filesystem.
|
|
|
|
* XXX The final recipients of VFS_MOUNT just overwrite the ndp they
|
|
|
|
* get. No freeing of cn_pnbuf.
|
|
|
|
*/
|
2004-07-30 22:08:52 +00:00
|
|
|
if (compat)
|
|
|
|
error = VFS_OMOUNT(mp, fspath, fsdata, td);
|
|
|
|
else
|
|
|
|
error = VFS_MOUNT(mp, td);
|
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;
|
|
|
|
}
|
|
|
|
/*
|
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) {
|
|
|
|
if (mp->mnt_kern_flag & MNTK_WANTRDWR)
|
|
|
|
mp->mnt_flag &= ~MNT_RDONLY;
|
2004-03-27 08:39:28 +00:00
|
|
|
mp->mnt_flag &=
|
|
|
|
~(MNT_UPDATE | MNT_RELOAD | MNT_FORCE | MNT_SNAPSHOT);
|
|
|
|
mp->mnt_kern_flag &= ~MNTK_WANTRDWR;
|
2002-07-02 17:09:22 +00:00
|
|
|
if (error) {
|
|
|
|
mp->mnt_flag = flag;
|
|
|
|
mp->mnt_kern_flag = kern_flag;
|
|
|
|
}
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
vfs_unbusy(mp, td);
|
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);
|
|
|
|
}
|
|
|
|
vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, td);
|
|
|
|
/*
|
|
|
|
* 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);
|
2004-07-12 08:14:09 +00:00
|
|
|
if (VFS_ROOT(mp, &newdp, td))
|
2002-07-02 17:09:22 +00:00
|
|
|
panic("mount: lost mount");
|
|
|
|
checkdirs(vp, newdp);
|
|
|
|
vput(newdp);
|
|
|
|
VOP_UNLOCK(vp, 0, td);
|
|
|
|
if ((mp->mnt_flag & MNT_RDONLY) == 0)
|
|
|
|
error = vfs_allocate_syncvnode(mp);
|
|
|
|
vfs_unbusy(mp, td);
|
2004-02-18 05:20:54 +00:00
|
|
|
if (error || (error = VFS_START(mp, 0, td)) != 0)
|
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);
|
2003-11-12 02:54:47 +00:00
|
|
|
vfs_mount_destroy(mp, td);
|
2002-07-02 17:09:22 +00:00
|
|
|
vput(vp);
|
|
|
|
}
|
|
|
|
return (error);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Scan all active processes to see if any of them have a current
|
|
|
|
* or root directory of `olddp'. If so, replace them with the new
|
|
|
|
* mount point.
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
checkdirs(olddp, newdp)
|
|
|
|
struct vnode *olddp, *newdp;
|
|
|
|
{
|
|
|
|
struct filedesc *fdp;
|
|
|
|
struct proc *p;
|
|
|
|
int nrele;
|
|
|
|
|
2002-09-25 01:44:21 +00:00
|
|
|
if (vrefcnt(olddp) == 1)
|
2002-07-02 17:09:22 +00:00
|
|
|
return;
|
|
|
|
sx_slock(&allproc_lock);
|
|
|
|
LIST_FOREACH(p, &allproc, p_list) {
|
2003-02-15 05:52:56 +00:00
|
|
|
mtx_lock(&fdesc_mtx);
|
2002-07-02 17:09:22 +00:00
|
|
|
fdp = p->p_fd;
|
|
|
|
if (fdp == NULL) {
|
2003-02-15 05:52:56 +00:00
|
|
|
mtx_unlock(&fdesc_mtx);
|
2002-07-02 17:09:22 +00:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
nrele = 0;
|
2004-11-28 11:26:43 +00:00
|
|
|
FILEDESC_LOCK_FAST(fdp);
|
2002-07-02 17:09:22 +00:00
|
|
|
if (fdp->fd_cdir == olddp) {
|
2004-11-28 11:26:43 +00:00
|
|
|
vref(newdp);
|
2002-07-02 17:09:22 +00:00
|
|
|
fdp->fd_cdir = newdp;
|
|
|
|
nrele++;
|
|
|
|
}
|
|
|
|
if (fdp->fd_rdir == olddp) {
|
2004-11-28 11:26:43 +00:00
|
|
|
vref(newdp);
|
2002-07-02 17:09:22 +00:00
|
|
|
fdp->fd_rdir = newdp;
|
|
|
|
nrele++;
|
|
|
|
}
|
2004-11-28 11:26:43 +00:00
|
|
|
FILEDESC_UNLOCK_FAST(fdp);
|
2003-02-15 05:52:56 +00:00
|
|
|
mtx_unlock(&fdesc_mtx);
|
2002-07-02 17:09:22 +00:00
|
|
|
while (nrele--)
|
|
|
|
vrele(olddp);
|
|
|
|
}
|
|
|
|
sx_sunlock(&allproc_lock);
|
|
|
|
if (rootvnode == olddp) {
|
|
|
|
vrele(rootvnode);
|
2004-11-28 11:26:43 +00:00
|
|
|
vref(newdp);
|
2002-07-02 17:09:22 +00:00
|
|
|
rootvnode = newdp;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Unmount a filesystem.
|
|
|
|
*
|
|
|
|
* Note: unmount takes a path to the vnode mounted on as argument,
|
|
|
|
* not special file (as before).
|
|
|
|
*/
|
|
|
|
#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
|
|
|
|
2004-02-02 19:02:05 +00:00
|
|
|
if (jailed(td->td_ucred))
|
|
|
|
return (EPERM);
|
|
|
|
if (usermount == 0) {
|
|
|
|
if ((error = suser(td)) != 0)
|
|
|
|
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
|
|
|
}
|
|
|
|
if (uap->flags & MNT_BYFSID) {
|
|
|
|
/* Decode the filesystem ID. */
|
|
|
|
if (sscanf(pathbuf, "FSID:%d:%d", &id0, &id1) != 2) {
|
|
|
|
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.
|
|
|
|
*/
|
|
|
|
return ((uap->flags & MNT_BYFSID) ? ENOENT : EINVAL);
|
|
|
|
}
|
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 unmount this filesystem.
|
2002-07-02 17:09:22 +00:00
|
|
|
*/
|
2004-07-06 09:39:32 +00:00
|
|
|
error = vfs_suser(mp, td);
|
|
|
|
if (error)
|
|
|
|
return (error);
|
2002-07-02 17:09:22 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Don't allow unmounting the root filesystem.
|
|
|
|
*/
|
2003-07-01 17:40:23 +00:00
|
|
|
if (mp->mnt_flag & MNT_ROOTFS)
|
2002-07-02 17:09:22 +00:00
|
|
|
return (EINVAL);
|
2004-11-25 12:06:43 +00:00
|
|
|
mtx_lock(&Giant);
|
|
|
|
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;
|
|
|
|
|
2004-11-25 12:06:43 +00:00
|
|
|
mtx_assert(&Giant, MA_OWNED);
|
|
|
|
|
2002-07-02 17:09:22 +00:00
|
|
|
mtx_lock(&mountlist_mtx);
|
|
|
|
if (mp->mnt_kern_flag & MNTK_UNMOUNT) {
|
|
|
|
mtx_unlock(&mountlist_mtx);
|
|
|
|
return (EBUSY);
|
|
|
|
}
|
|
|
|
mp->mnt_kern_flag |= MNTK_UNMOUNT;
|
|
|
|
/* Allow filesystems to detect that a forced unmount is in progress. */
|
|
|
|
if (flags & MNT_FORCE)
|
|
|
|
mp->mnt_kern_flag |= MNTK_UNMOUNTF;
|
|
|
|
error = lockmgr(&mp->mnt_lock, LK_DRAIN | LK_INTERLOCK |
|
|
|
|
((flags & MNT_FORCE) ? 0 : LK_NOWAIT), &mountlist_mtx, td);
|
|
|
|
if (error) {
|
|
|
|
mp->mnt_kern_flag &= ~(MNTK_UNMOUNT | MNTK_UNMOUNTF);
|
|
|
|
if (mp->mnt_kern_flag & MNTK_MWAIT)
|
|
|
|
wakeup(mp);
|
|
|
|
return (error);
|
|
|
|
}
|
|
|
|
vn_start_write(NULL, &mp, V_WAIT);
|
|
|
|
|
|
|
|
if (mp->mnt_flag & MNT_EXPUBLIC)
|
|
|
|
vfs_setpublicfs(NULL, NULL, NULL);
|
|
|
|
|
|
|
|
vfs_msync(mp, MNT_WAIT);
|
|
|
|
async_flag = mp->mnt_flag & MNT_ASYNC;
|
2004-03-27 08:39:28 +00:00
|
|
|
mp->mnt_flag &= ~MNT_ASYNC;
|
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.
|
|
|
|
*/
|
2004-07-12 08:14:09 +00:00
|
|
|
if ((flags & MNT_FORCE) && VFS_ROOT(mp, &fsrootvp, td) == 0) {
|
2002-07-02 17:09:22 +00:00
|
|
|
if (mp->mnt_vnodecovered != NULL)
|
|
|
|
checkdirs(fsrootvp, mp->mnt_vnodecovered);
|
|
|
|
if (fsrootvp == rootvnode) {
|
|
|
|
vrele(rootvnode);
|
|
|
|
rootvnode = NULL;
|
|
|
|
}
|
|
|
|
vput(fsrootvp);
|
|
|
|
}
|
|
|
|
if (((mp->mnt_flag & MNT_RDONLY) ||
|
|
|
|
(error = VFS_SYNC(mp, MNT_WAIT, td->td_ucred, td)) == 0) ||
|
|
|
|
(flags & MNT_FORCE)) {
|
|
|
|
error = VFS_UNMOUNT(mp, flags, td);
|
|
|
|
}
|
|
|
|
vn_finished_write(mp);
|
|
|
|
if (error) {
|
|
|
|
/* Undo cdir/rdir and rootvnode changes made above. */
|
2004-07-12 08:14:09 +00:00
|
|
|
if ((flags & MNT_FORCE) && VFS_ROOT(mp, &fsrootvp, td) == 0) {
|
2002-07-02 17:09:22 +00:00
|
|
|
if (mp->mnt_vnodecovered != NULL)
|
|
|
|
checkdirs(mp->mnt_vnodecovered, fsrootvp);
|
|
|
|
if (rootvnode == NULL) {
|
|
|
|
rootvnode = fsrootvp;
|
|
|
|
vref(rootvnode);
|
|
|
|
}
|
|
|
|
vput(fsrootvp);
|
|
|
|
}
|
|
|
|
if ((mp->mnt_flag & MNT_RDONLY) == 0 && mp->mnt_syncer == NULL)
|
|
|
|
(void) vfs_allocate_syncvnode(mp);
|
|
|
|
mtx_lock(&mountlist_mtx);
|
|
|
|
mp->mnt_kern_flag &= ~(MNTK_UNMOUNT | MNTK_UNMOUNTF);
|
|
|
|
mp->mnt_flag |= async_flag;
|
|
|
|
lockmgr(&mp->mnt_lock, LK_RELEASE | LK_INTERLOCK,
|
|
|
|
&mountlist_mtx, td);
|
|
|
|
if (mp->mnt_kern_flag & MNTK_MWAIT)
|
|
|
|
wakeup(mp);
|
|
|
|
return (error);
|
|
|
|
}
|
|
|
|
mtx_lock(&mountlist_mtx);
|
|
|
|
TAILQ_REMOVE(&mountlist, mp, mnt_list);
|
2002-10-22 01:06:44 +00:00
|
|
|
if ((coveredvp = mp->mnt_vnodecovered) != NULL)
|
2002-07-02 17:09:22 +00:00
|
|
|
coveredvp->v_mountedhere = NULL;
|
2003-11-12 02:54:47 +00:00
|
|
|
mtx_unlock(&mountlist_mtx);
|
2004-07-06 09:12:03 +00:00
|
|
|
vfs_event_signal(NULL, VQ_UNMOUNT, 0);
|
2003-11-12 02:54:47 +00:00
|
|
|
vfs_mount_destroy(mp, td);
|
2002-07-02 17:09:22 +00:00
|
|
|
if (coveredvp != NULL)
|
|
|
|
vrele(coveredvp);
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
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;
|
2002-10-25 18:44:42 +00:00
|
|
|
|
2004-07-30 22:08:52 +00:00
|
|
|
|
2004-10-05 11:26:43 +00:00
|
|
|
/*
|
|
|
|
* Wait for GEOM to settle down
|
|
|
|
*/
|
2004-10-23 20:21:05 +00:00
|
|
|
DROP_GIANT();
|
2004-10-05 11:26:43 +00:00
|
|
|
g_waitidle();
|
2004-10-23 20:21:05 +00:00
|
|
|
PICKUP_GIANT();
|
2004-07-30 22:08:52 +00:00
|
|
|
|
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())
|
|
|
|
return;
|
|
|
|
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))
|
|
|
|
return;
|
|
|
|
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]))
|
2004-09-23 10:13:18 +00:00
|
|
|
return;
|
|
|
|
}
|
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)
|
1999-11-03 11:02:47 +00:00
|
|
|
return;
|
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]))
|
|
|
|
return;
|
|
|
|
if (!vfs_mountroot_try(rootdevnames[1]))
|
|
|
|
return;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If we (still) have a compiled-in default, try it.
|
|
|
|
*/
|
|
|
|
if (ctrootdevname != NULL)
|
|
|
|
if (!vfs_mountroot_try(ctrootdevname))
|
|
|
|
return;
|
|
|
|
|
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())
|
|
|
|
return;
|
1999-11-01 23:51:00 +00:00
|
|
|
panic("Root mount failed, startup aborted.");
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* 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
|
|
|
{
|
1999-11-03 11:02:47 +00:00
|
|
|
struct mount *mp;
|
2004-11-25 09:47:51 +00:00
|
|
|
struct thread *td = curthread;
|
|
|
|
struct vfsconf *vfsp;
|
1999-11-01 23:51:00 +00:00
|
|
|
char *vfsname, *path;
|
|
|
|
int error;
|
1999-11-05 02:45:50 +00:00
|
|
|
char patt[32];
|
2004-10-05 11:26:43 +00:00
|
|
|
int s;
|
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 */
|
1999-11-01 23:51:00 +00:00
|
|
|
|
2004-10-05 11:26:43 +00:00
|
|
|
s = splcam(); /* Overkill, but annoying without it */
|
2004-11-25 09:47:51 +00:00
|
|
|
printf("Trying to mount root from %s\n", mountfrom);
|
2004-10-05 11:26:43 +00:00
|
|
|
splx(s);
|
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);
|
1999-11-03 11:02:47 +00:00
|
|
|
if (sscanf(mountfrom, patt, vfsname, path) < 1)
|
1999-11-01 23:51:00 +00:00
|
|
|
goto done;
|
|
|
|
|
2004-11-25 09:47:51 +00:00
|
|
|
if (path[0] == '\0')
|
|
|
|
strcpy(path, ROOTNAME);
|
|
|
|
|
|
|
|
vfsp = vfs_byname(vfsname);
|
|
|
|
if (vfsp == NULL) {
|
|
|
|
printf("Can't find filesystem \"%s\"\n", vfsname);
|
1999-11-01 23:51:00 +00:00
|
|
|
goto done;
|
1997-12-01 11:34:41 +00:00
|
|
|
}
|
2004-11-25 09:47:51 +00:00
|
|
|
error = vfs_mount_alloc(NULLVP, vfsp, "/", td, &mp);
|
|
|
|
if (error) {
|
|
|
|
printf("Could not alloc mountpoint\n");
|
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
|
|
|
|
mp->mnt_flag |= MNT_RDONLY | MNT_ROOTFS;
|
|
|
|
|
|
|
|
strlcpy(mp->mnt_stat.f_mntfromname, path, MNAMELEN);
|
1995-08-28 09:19:25 +00:00
|
|
|
|
2004-07-30 22:08:52 +00:00
|
|
|
/*
|
|
|
|
* do our best to set rootdev
|
|
|
|
* XXX: This does not belong here!
|
|
|
|
*/
|
|
|
|
if (path[0] != '\0') {
|
|
|
|
struct cdev *diskdev;
|
|
|
|
diskdev = getdiskbyname(path);
|
|
|
|
if (diskdev != NULL)
|
|
|
|
rootdev = diskdev;
|
2004-10-05 11:26:43 +00:00
|
|
|
else
|
|
|
|
printf("setrootbyname failed\n");
|
2004-07-30 22:08:52 +00:00
|
|
|
}
|
1997-12-01 11:34:41 +00:00
|
|
|
|
2004-11-18 14:31:24 +00:00
|
|
|
error = VFS_OMOUNT(mp, path, NULL, curthread);
|
1999-11-01 23:51:00 +00:00
|
|
|
|
|
|
|
done:
|
1999-11-05 19:28:07 +00:00
|
|
|
if (vfsname != NULL)
|
|
|
|
free(vfsname, M_MOUNT);
|
|
|
|
if (path != NULL)
|
|
|
|
free(path, M_MOUNT);
|
1999-11-01 23:51:00 +00:00
|
|
|
if (error != 0) {
|
2003-11-12 02:54:47 +00:00
|
|
|
if (mp != NULL)
|
|
|
|
vfs_mount_destroy(mp, curthread);
|
2004-10-05 11:26:43 +00:00
|
|
|
printf("Root mount failed: %d\n", error);
|
1999-11-01 23:51:00 +00:00
|
|
|
} else {
|
2004-10-05 11:26:43 +00:00
|
|
|
|
1999-11-01 23:51:00 +00:00
|
|
|
/* register with list of mounted filesystems */
|
Change and clean the mutex lock interface.
mtx_enter(lock, type) becomes:
mtx_lock(lock) for sleep locks (MTX_DEF-initialized locks)
mtx_lock_spin(lock) for spin locks (MTX_SPIN-initialized)
similarily, for releasing a lock, we now have:
mtx_unlock(lock) for MTX_DEF and mtx_unlock_spin(lock) for MTX_SPIN.
We change the caller interface for the two different types of locks
because the semantics are entirely different for each case, and this
makes it explicitly clear and, at the same time, it rids us of the
extra `type' argument.
The enter->lock and exit->unlock change has been made with the idea
that we're "locking data" and not "entering locked code" in mind.
Further, remove all additional "flags" previously passed to the
lock acquire/release routines with the exception of two:
MTX_QUIET and MTX_NOSWITCH
The functionality of these flags is preserved and they can be passed
to the lock/unlock routines by calling the corresponding wrappers:
mtx_{lock, unlock}_flags(lock, flag(s)) and
mtx_{lock, unlock}_spin_flags(lock, flag(s)) for MTX_DEF and MTX_SPIN
locks, respectively.
Re-inline some lock acq/rel code; in the sleep lock case, we only
inline the _obtain_lock()s in order to ensure that the inlined code
fits into a cache line. In the spin lock case, we inline recursion and
actually only perform a function call if we need to spin. This change
has been made with the idea that we generally tend to avoid spin locks
and that also the spin locks that we do have and are heavily used
(i.e. sched_lock) do recurse, and therefore in an effort to reduce
function call overhead for some architectures (such as alpha), we
inline recursion for this case.
Create a new malloc type for the witness code and retire from using
the M_DEV type. The new type is called M_WITNESS and is only declared
if WITNESS is enabled.
Begin cleaning up some machdep/mutex.h code - specifically updated the
"optimized" inlined code in alpha/mutex.h and wrote MTX_LOCK_SPIN
and MTX_UNLOCK_SPIN asm macros for the i386/mutex.h as we presently
need those.
Finally, caught up to the interface changes in all sys code.
Contributors: jake, jhb, jasone (in no particular order)
2001-02-09 06:11:45 +00:00
|
|
|
mtx_lock(&mountlist_mtx);
|
1999-11-20 10:00:46 +00:00
|
|
|
TAILQ_INSERT_HEAD(&mountlist, mp, mnt_list);
|
Change and clean the mutex lock interface.
mtx_enter(lock, type) becomes:
mtx_lock(lock) for sleep locks (MTX_DEF-initialized locks)
mtx_lock_spin(lock) for spin locks (MTX_SPIN-initialized)
similarily, for releasing a lock, we now have:
mtx_unlock(lock) for MTX_DEF and mtx_unlock_spin(lock) for MTX_SPIN.
We change the caller interface for the two different types of locks
because the semantics are entirely different for each case, and this
makes it explicitly clear and, at the same time, it rids us of the
extra `type' argument.
The enter->lock and exit->unlock change has been made with the idea
that we're "locking data" and not "entering locked code" in mind.
Further, remove all additional "flags" previously passed to the
lock acquire/release routines with the exception of two:
MTX_QUIET and MTX_NOSWITCH
The functionality of these flags is preserved and they can be passed
to the lock/unlock routines by calling the corresponding wrappers:
mtx_{lock, unlock}_flags(lock, flag(s)) and
mtx_{lock, unlock}_spin_flags(lock, flag(s)) for MTX_DEF and MTX_SPIN
locks, respectively.
Re-inline some lock acq/rel code; in the sleep lock case, we only
inline the _obtain_lock()s in order to ensure that the inlined code
fits into a cache line. In the spin lock case, we inline recursion and
actually only perform a function call if we need to spin. This change
has been made with the idea that we generally tend to avoid spin locks
and that also the spin locks that we do have and are heavily used
(i.e. sched_lock) do recurse, and therefore in an effort to reduce
function call overhead for some architectures (such as alpha), we
inline recursion for this case.
Create a new malloc type for the witness code and retire from using
the M_DEV type. The new type is called M_WITNESS and is only declared
if WITNESS is enabled.
Begin cleaning up some machdep/mutex.h code - specifically updated the
"optimized" inlined code in alpha/mutex.h and wrote MTX_LOCK_SPIN
and MTX_UNLOCK_SPIN asm macros for the i386/mutex.h as we presently
need those.
Finally, caught up to the interface changes in all sys code.
Contributors: jake, jhb, jasone (in no particular order)
2001-02-09 06:11:45 +00:00
|
|
|
mtx_unlock(&mountlist_mtx);
|
1999-11-01 23:51:00 +00:00
|
|
|
|
2002-07-08 19:10:15 +00:00
|
|
|
/* sanity check system clock against root fs timestamp */
|
1999-11-01 23:51:00 +00:00
|
|
|
inittodr(mp->mnt_time);
|
2001-09-12 08:38:13 +00:00
|
|
|
vfs_unbusy(mp, curthread);
|
2002-07-08 19:10:15 +00:00
|
|
|
error = VFS_START(mp, 0, curthread);
|
1999-11-28 22:20:18 +00:00
|
|
|
}
|
2004-03-27 08:39:28 +00:00
|
|
|
return (error);
|
1999-11-01 23:51:00 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Spin prompting on the console for a suitable root filesystem
|
|
|
|
*/
|
|
|
|
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");
|
2001-11-20 23:48:00 +00:00
|
|
|
#if 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> ");
|
1999-11-01 23:51:00 +00:00
|
|
|
gets(name);
|
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
|
|
|
|
2002-03-05 15:38:49 +00:00
|
|
|
/*
|
|
|
|
* Local helper function for vfs_mountroot_ask.
|
|
|
|
*/
|
1999-11-01 23:51:00 +00:00
|
|
|
static void
|
|
|
|
gets(char *cp)
|
|
|
|
{
|
|
|
|
char *lp;
|
|
|
|
int c;
|
1995-08-28 09:19:25 +00:00
|
|
|
|
1999-11-01 23:51:00 +00:00
|
|
|
lp = cp;
|
|
|
|
for (;;) {
|
|
|
|
printf("%c", c = cngetc() & 0177);
|
|
|
|
switch (c) {
|
|
|
|
case -1:
|
|
|
|
case '\n':
|
|
|
|
case '\r':
|
|
|
|
*lp++ = '\0';
|
|
|
|
return;
|
|
|
|
case '\b':
|
|
|
|
case '\177':
|
|
|
|
if (lp > cp) {
|
|
|
|
printf(" \b");
|
|
|
|
lp--;
|
|
|
|
}
|
|
|
|
continue;
|
|
|
|
case '#':
|
|
|
|
lp--;
|
|
|
|
if (lp < cp)
|
|
|
|
lp = cp;
|
|
|
|
continue;
|
|
|
|
case '@':
|
|
|
|
case 'u' & 037:
|
|
|
|
lp = cp;
|
|
|
|
printf("%c", '\n');
|
|
|
|
continue;
|
|
|
|
default:
|
|
|
|
*lp++ = c;
|
|
|
|
}
|
|
|
|
}
|
1995-08-28 09:19:25 +00:00
|
|
|
}
|
1997-11-25 07:07:48 +00:00
|
|
|
|
1999-11-01 23:51:00 +00:00
|
|
|
/*
|
2004-06-17 21:24:13 +00:00
|
|
|
* Convert a given name to the cdev pointer of the device, which is probably
|
|
|
|
* but not by definition, a disk. Mount a DEVFS (on nothing), look the name
|
|
|
|
* up, extract the cdev from the vnode and unmount it again. Unfortunately
|
|
|
|
* we cannot use the vnode directly (because we unmount the DEVFS again)
|
|
|
|
* so the filesystems still have to do the bdevvp() stunt.
|
1999-11-01 23:51:00 +00:00
|
|
|
*/
|
2004-11-09 23:03:34 +00:00
|
|
|
static struct cdev *
|
2004-06-17 21:24:13 +00:00
|
|
|
getdiskbyname(char *name)
|
|
|
|
{
|
|
|
|
char *cp = name;
|
|
|
|
struct cdev *dev = NULL;
|
|
|
|
struct thread *td = curthread;
|
|
|
|
struct vfsconf *vfsp;
|
|
|
|
struct mount *mp = NULL;
|
|
|
|
struct vnode *vroot = NULL;
|
|
|
|
struct nameidata nid;
|
|
|
|
int error;
|
2000-08-24 15:36:55 +00:00
|
|
|
|
|
|
|
if (!bcmp(cp, "/dev/", 5))
|
|
|
|
cp += 5;
|
|
|
|
|
2004-06-17 21:24:13 +00:00
|
|
|
do {
|
2004-07-27 22:32:01 +00:00
|
|
|
vfsp = vfs_byname("devfs");
|
2004-06-17 21:24:13 +00:00
|
|
|
if (vfsp == NULL)
|
|
|
|
break;
|
|
|
|
error = vfs_mount_alloc(NULLVP, vfsp, "/dev", td, &mp);
|
|
|
|
if (error)
|
|
|
|
break;
|
|
|
|
mp->mnt_flag |= MNT_RDONLY;
|
|
|
|
|
2004-07-30 22:08:52 +00:00
|
|
|
error = VFS_MOUNT(mp, curthread);
|
2004-06-17 21:24:13 +00:00
|
|
|
if (error)
|
|
|
|
break;
|
|
|
|
VFS_START(mp, 0, td);
|
2004-07-12 08:14:09 +00:00
|
|
|
VFS_ROOT(mp, &vroot, td);
|
2004-06-17 21:24:13 +00:00
|
|
|
VOP_UNLOCK(vroot, 0, td);
|
|
|
|
|
|
|
|
NDINIT(&nid, LOOKUP, NOCACHE|FOLLOW,
|
|
|
|
UIO_SYSSPACE, cp, curthread);
|
|
|
|
nid.ni_startdir = vroot;
|
|
|
|
nid.ni_pathlen = strlen(cp);
|
2004-06-20 17:31:01 +00:00
|
|
|
nid.ni_cnd.cn_cred = curthread->td_ucred;
|
2004-06-17 21:24:13 +00:00
|
|
|
nid.ni_cnd.cn_nameptr = cp;
|
|
|
|
|
|
|
|
error = lookup(&nid);
|
|
|
|
if (error)
|
|
|
|
break;
|
2004-11-09 23:03:34 +00:00
|
|
|
if (nid.ni_vp->v_type != VCHR)
|
|
|
|
dev = NULL;
|
|
|
|
else
|
|
|
|
dev = nid.ni_vp->v_rdev;
|
2004-06-17 21:24:13 +00:00
|
|
|
NDFREE(&nid, 0);
|
|
|
|
} while (0);
|
|
|
|
|
|
|
|
if (vroot != NULL)
|
|
|
|
VFS_UNMOUNT(mp, 0, td);
|
|
|
|
if (mp != NULL)
|
|
|
|
vfs_mount_destroy(mp, td);
|
|
|
|
return (dev);
|
2000-03-20 16:28:35 +00:00
|
|
|
}
|
|
|
|
|
2004-06-16 09:47:26 +00:00
|
|
|
/* Show the struct cdev *for a disk specified by name */
|
2000-03-20 16:28:35 +00:00
|
|
|
#ifdef DDB
|
|
|
|
DB_SHOW_COMMAND(disk, db_getdiskbyname)
|
|
|
|
{
|
2004-06-16 09:47:26 +00:00
|
|
|
struct cdev *dev;
|
2000-03-20 16:28:35 +00:00
|
|
|
|
|
|
|
if (modif[0] == '\0') {
|
|
|
|
db_error("usage: show disk/devicename");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
dev = getdiskbyname(modif);
|
2004-06-17 17:16:53 +00:00
|
|
|
if (dev != NULL)
|
2004-06-16 09:47:26 +00:00
|
|
|
db_printf("struct cdev *= %p\n", dev);
|
2000-03-20 16:28:35 +00:00
|
|
|
else
|
|
|
|
db_printf("No disk device matched.\n");
|
|
|
|
}
|
|
|
|
#endif
|
2002-07-02 17:09:22 +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;
|
|
|
|
const char **t, *p;
|
|
|
|
|
|
|
|
|
|
|
|
TAILQ_FOREACH(opt, opts, link) {
|
|
|
|
p = opt->name;
|
|
|
|
if (p[0] == 'n' && p[1] == 'o')
|
|
|
|
p += 2;
|
|
|
|
for(t = global_opts; *t != NULL; t++)
|
|
|
|
if (!strcmp(*t, p))
|
|
|
|
break;
|
|
|
|
if (*t != NULL)
|
|
|
|
continue;
|
|
|
|
for(t = legal; *t != NULL; t++)
|
|
|
|
if (!strcmp(*t, p))
|
|
|
|
break;
|
|
|
|
if (*t != NULL)
|
|
|
|
continue;
|
|
|
|
printf("mount option <%s> is unknown\n", p);
|
|
|
|
return (EINVAL);
|
|
|
|
}
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
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) {
|
|
|
|
if (len != NULL)
|
|
|
|
*len = opt->len;
|
|
|
|
if (buf != NULL)
|
|
|
|
*buf = opt->value;
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return (ENOENT);
|
|
|
|
}
|
|
|
|
|
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;
|
|
|
|
if (((char *)opt->value)[opt->len - 1] != '\0') {
|
|
|
|
*error = EINVAL;
|
|
|
|
return (NULL);
|
|
|
|
}
|
|
|
|
return (opt->value);
|
|
|
|
}
|
|
|
|
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) {
|
|
|
|
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;
|
|
|
|
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);
|
|
|
|
}
|
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) {
|
|
|
|
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 *
|
|
|
|
__mnt_vnode_next(struct vnode **nvp, struct mount *mp)
|
|
|
|
{
|
|
|
|
struct vnode *vp;
|
|
|
|
|
|
|
|
mtx_assert(&mp->mnt_mtx, MA_OWNED);
|
2004-11-25 12:06:43 +00:00
|
|
|
|
2004-07-04 08:52:35 +00:00
|
|
|
vp = *nvp;
|
|
|
|
/* Check if we are done */
|
|
|
|
if (vp == NULL)
|
|
|
|
return (NULL);
|
|
|
|
/* If our next vnode is no longer ours, start over */
|
|
|
|
if (vp->v_mount != mp)
|
|
|
|
vp = TAILQ_FIRST(&mp->mnt_nvnodelist);
|
|
|
|
/* Save pointer to next vnode in list */
|
|
|
|
if (vp != NULL)
|
|
|
|
*nvp = TAILQ_NEXT(vp, v_nmntvnodes);
|
|
|
|
else
|
|
|
|
*nvp = NULL;
|
|
|
|
return (vp);
|
|
|
|
}
|
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)
|
|
|
|
memcpy(sbp, &mp->mnt_stat, sizeof sbp);
|
|
|
|
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++;
|
|
|
|
|
|
|
|
sb = sbuf_new(NULL, NULL, 0, SBUF_AUTOEXTEND);
|
|
|
|
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.
|
|
|
|
*
|
|
|
|
* If length is -1, use printf.
|
|
|
|
*/
|
|
|
|
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
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
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 *);
|
|
|
|
ma = mount_arg(ma, cp, vp, -1);
|
|
|
|
}
|
|
|
|
va_end(ap);
|
|
|
|
|
|
|
|
error = kernel_mount(ma, flags);
|
|
|
|
return (error);
|
|
|
|
}
|