opensolaris_vfs.c revision 180652
1/*-
2 * Copyright (c) 2006-2007 Pawel Jakub Dawidek <pjd@FreeBSD.org>
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 */
26
27#include <sys/cdefs.h>
28__FBSDID("$FreeBSD: head/sys/cddl/compat/opensolaris/kern/opensolaris_vfs.c 180652 2008-07-21 09:45:44Z pjd $");
29
30#include <sys/param.h>
31#include <sys/kernel.h>
32#include <sys/systm.h>
33#include <sys/mount.h>
34#include <sys/cred.h>
35#include <sys/vfs.h>
36#include <sys/priv.h>
37#include <sys/libkern.h>
38
39MALLOC_DECLARE(M_MOUNT);
40
41TAILQ_HEAD(vfsoptlist, vfsopt);
42struct vfsopt {
43	TAILQ_ENTRY(vfsopt) link;
44	char	*name;
45	void	*value;
46	int	len;
47};
48
49void
50vfs_setmntopt(vfs_t *vfsp, const char *name, const char *arg,
51    int flags __unused)
52{
53	struct vfsopt *opt;
54	size_t namesize;
55
56	if (vfsp->mnt_opt == NULL) {
57		vfsp->mnt_opt = malloc(sizeof(*vfsp->mnt_opt), M_MOUNT, M_WAITOK);
58		TAILQ_INIT(vfsp->mnt_opt);
59	}
60
61	opt = malloc(sizeof(*opt), M_MOUNT, M_WAITOK);
62
63	namesize = strlen(name) + 1;
64	opt->name = malloc(namesize, M_MOUNT, M_WAITOK);
65	strlcpy(opt->name, name, namesize);
66
67	if (arg == NULL) {
68		opt->value = NULL;
69		opt->len = 0;
70	} else {
71		opt->len = strlen(arg) + 1;
72		opt->value = malloc(opt->len, M_MOUNT, M_WAITOK);
73		bcopy(arg, opt->value, opt->len);
74	}
75	/* TODO: Locking. */
76	TAILQ_INSERT_TAIL(vfsp->mnt_opt, opt, link);
77}
78
79void
80vfs_clearmntopt(vfs_t *vfsp, const char *name)
81{
82	struct vfsopt *opt;
83
84	if (vfsp->mnt_opt == NULL)
85		return;
86	/* TODO: Locking. */
87	TAILQ_FOREACH(opt, vfsp->mnt_opt, link) {
88		if (strcmp(opt->name, name) == 0)
89			break;
90	}
91	if (opt != NULL) {
92		TAILQ_REMOVE(vfsp->mnt_opt, opt, link);
93		free(opt->name, M_MOUNT);
94		if (opt->value != NULL)
95			free(opt->value, M_MOUNT);
96		free(opt, M_MOUNT);
97	}
98}
99
100int
101vfs_optionisset(const vfs_t *vfsp, const char *opt, char **argp)
102{
103	struct vfsoptlist *opts = vfsp->mnt_optnew;
104	int error;
105
106	if (opts == NULL)
107		return (0);
108	error = vfs_getopt(opts, opt, (void **)argp, NULL);
109	return (error != 0 ? 0 : 1);
110}
111
112int
113traverse(vnode_t **cvpp, int lktype)
114{
115	kthread_t *td = curthread;
116	vnode_t *cvp;
117	vnode_t *tvp;
118	vfs_t *vfsp;
119	int error;
120
121	cvp = *cvpp;
122	tvp = NULL;
123
124	/*
125	 * If this vnode is mounted on, then we transparently indirect
126	 * to the vnode which is the root of the mounted file system.
127	 * Before we do this we must check that an unmount is not in
128	 * progress on this vnode.
129	 */
130
131	for (;;) {
132		/*
133		 * Reached the end of the mount chain?
134		 */
135		vfsp = vn_mountedvfs(cvp);
136		if (vfsp == NULL)
137			break;
138		/*
139		 * tvp is NULL for *cvpp vnode, which we can't unlock.
140		 */
141		if (tvp != NULL)
142			vput(cvp);
143		else
144			vrele(cvp);
145
146		/*
147		 * The read lock must be held across the call to VFS_ROOT() to
148		 * prevent a concurrent unmount from destroying the vfs.
149		 */
150		error = VFS_ROOT(vfsp, lktype, &tvp, td);
151		if (error != 0)
152			return (error);
153		cvp = tvp;
154	}
155
156	*cvpp = cvp;
157	return (0);
158}
159
160int
161domount(kthread_t *td, vnode_t *vp, const char *fstype, char *fspath,
162    char *fspec, int fsflags)
163{
164	struct mount *mp;
165	struct vfsconf *vfsp;
166	struct ucred *newcr, *oldcr;
167	int error;
168
169	/*
170	 * Be ultra-paranoid about making sure the type and fspath
171	 * variables will fit in our mp buffers, including the
172	 * terminating NUL.
173	 */
174	if (strlen(fstype) >= MFSNAMELEN || strlen(fspath) >= MNAMELEN)
175		return (ENAMETOOLONG);
176
177	vfsp = vfs_byname_kld(fstype, td, &error);
178	if (vfsp == NULL)
179		return (ENODEV);
180
181	if (vp->v_type != VDIR)
182		return (ENOTDIR);
183	VI_LOCK(vp);
184	if ((vp->v_iflag & VI_MOUNT) != 0 ||
185	    vp->v_mountedhere != NULL) {
186		VI_UNLOCK(vp);
187		return (EBUSY);
188	}
189	vp->v_iflag |= VI_MOUNT;
190	VI_UNLOCK(vp);
191
192	/*
193	 * Allocate and initialize the filesystem.
194	 */
195	vn_lock(vp, LK_SHARED | LK_RETRY);
196	mp = vfs_mount_alloc(vp, vfsp, fspath, td);
197	VOP_UNLOCK(vp, 0);
198
199	mp->mnt_optnew = NULL;
200	vfs_setmntopt(mp, "from", fspec, 0);
201	mp->mnt_optnew = mp->mnt_opt;
202	mp->mnt_opt = NULL;
203
204	/*
205	 * Set the mount level flags.
206	 * crdup() can sleep, so do it before acquiring a mutex.
207	 */
208	newcr = crdup(kcred);
209	MNT_ILOCK(mp);
210	if (fsflags & MNT_RDONLY)
211		mp->mnt_flag |= MNT_RDONLY;
212	mp->mnt_flag &=~ MNT_UPDATEMASK;
213	mp->mnt_flag |= fsflags & (MNT_UPDATEMASK | MNT_FORCE | MNT_ROOTFS);
214	/*
215	 * Unprivileged user can trigger mounting a snapshot, but we don't want
216	 * him to unmount it, so we switch to privileged credentials.
217	 */
218	oldcr = mp->mnt_cred;
219	mp->mnt_cred = newcr;
220	mp->mnt_stat.f_owner = mp->mnt_cred->cr_uid;
221	MNT_IUNLOCK(mp);
222	crfree(oldcr);
223	/*
224	 * Mount the filesystem.
225	 * XXX The final recipients of VFS_MOUNT just overwrite the ndp they
226	 * get.  No freeing of cn_pnbuf.
227	 */
228	error = VFS_MOUNT(mp, td);
229
230	if (!error) {
231		if (mp->mnt_opt != NULL)
232			vfs_freeopts(mp->mnt_opt);
233		mp->mnt_opt = mp->mnt_optnew;
234		(void)VFS_STATFS(mp, &mp->mnt_stat, td);
235	}
236	/*
237	 * Prevent external consumers of mount options from reading
238	 * mnt_optnew.
239	*/
240	mp->mnt_optnew = NULL;
241	vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
242	/*
243	 * Put the new filesystem on the mount list after root.
244	 */
245#ifdef FREEBSD_NAMECACHE
246	cache_purge(vp);
247#endif
248	if (!error) {
249		vnode_t *mvp;
250
251		VI_LOCK(vp);
252		vp->v_iflag &= ~VI_MOUNT;
253		VI_UNLOCK(vp);
254		vp->v_mountedhere = mp;
255		mtx_lock(&mountlist_mtx);
256		TAILQ_INSERT_TAIL(&mountlist, mp, mnt_list);
257		mtx_unlock(&mountlist_mtx);
258		vfs_event_signal(NULL, VQ_MOUNT, 0);
259		if (VFS_ROOT(mp, LK_EXCLUSIVE, &mvp, td))
260			panic("mount: lost mount");
261		mountcheckdirs(vp, mvp);
262		vput(mvp);
263		VOP_UNLOCK(vp, 0);
264		if ((mp->mnt_flag & MNT_RDONLY) == 0)
265			error = vfs_allocate_syncvnode(mp);
266		vfs_unbusy(mp, td);
267		if (error)
268			vrele(vp);
269		else
270			vfs_mountedfrom(mp, fspec);
271	} else {
272		VI_LOCK(vp);
273		vp->v_iflag &= ~VI_MOUNT;
274		VI_UNLOCK(vp);
275		VOP_UNLOCK(vp, 0);
276		vfs_unbusy(mp, td);
277		vfs_mount_destroy(mp);
278	}
279	return (error);
280}
281