1/*	$NetBSD: vfs.c,v 1.4 2011/04/02 04:57:35 rmind Exp $	*/
2
3/*-
4 * Copyright (c) 2006-2007 Pawel Jakub Dawidek <pjd@FreeBSD.org>
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 *    notice, this list of conditions and the following disclaimer in the
14 *    documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
27 */
28
29#include <sys/cdefs.h>
30/* __FBSDID("$FreeBSD: src/sys/compat/opensolaris/kern/opensolaris_vfs.c,v 1.7 2007/11/01 08:58:29 pjd Exp $"); */
31
32#include <sys/param.h>
33#include <sys/kernel.h>
34#include <sys/systm.h>
35#include <sys/mount.h>
36#include <sys/cred.h>
37#include <sys/vfs.h>
38#include <sys/pathname.h>
39#include <sys/priv.h>
40#include <lib/libkern/libkern.h>
41
42int
43lookupname(char *dirname, enum uio_seg seg, vnode_t **dirvpp, vnode_t **compvpp)
44{
45	struct nameidata nd;
46	int error;
47
48	error = 0;
49
50	KASSERT(dirvpp == NULL);
51
52	error = namei_simple_kernel(dirname,  NSM_FOLLOW_NOEMULROOT, compvpp);
53
54	return error;
55}
56
57
58int
59lookupnameat(char *dirname, enum uio_seg seg, vnode_t **dirvpp,
60	vnode_t **compvpp, vnode_t *startvp)
61{
62
63	struct nameidata nd;
64	int error;
65
66	error = EOPNOTSUPP;
67
68/*      XXX Disable until I upgrade testing kernel.
69        KASSERT(dirvpp == NULL);
70
71	NDINIT(&nd, LOOKUP, FOLLOW, UIO_SYSSPACE, dirname);
72
73	if ((error = nameiat(&nd, startvp)) != 0)
74		return error;
75
76	*compvpp = nd.ni_vp;*/
77
78	return (error);
79}
80
81
82void
83vfs_setmntopt(vfs_t *vfsp, const char *name, const char *arg,
84    int flags)
85{
86
87	if (strcmp("ro", name) == 0)
88		vfsp->mnt_flag |= MNT_RDONLY;
89
90	if (strcmp("rw", name) == 0)
91		vfsp->mnt_flag &= ~MNT_RDONLY;
92
93	if (strcmp("nodevices", name) == 0)
94		vfsp->mnt_flag |= MNT_NODEV;
95
96	if (strcmp("noatime", name) == 0)
97		vfsp->mnt_flag |= MNT_NOATIME;
98
99	if (strcmp("atime", name) == 0)
100		vfsp->mnt_flag &= ~MNT_NOATIME;
101
102	if (strcmp("nosuid", name) == 0)
103		vfsp->mnt_flag |= MNT_NOSUID;
104
105	if (strcmp("suid", name) == 0)
106		vfsp->mnt_flag &= ~MNT_NOSUID;
107
108	if (strcmp("noexec", name) == 0)
109		vfsp->mnt_flag |= MNT_NOEXEC;
110
111	if (strcmp("exec", name) == 0)
112		vfsp->mnt_flag &= ~MNT_NOEXEC;
113
114	vfsp->mnt_flag |= MNT_LOCAL;
115}
116
117void
118vfs_clearmntopt(vfs_t *vfsp, const char *name)
119{
120
121	if (strcmp("ro", name) == 0)
122		vfsp->mnt_flag |= MNT_RDONLY;
123
124	if (strcmp("rw", name) == 0)
125		vfsp->mnt_flag &= ~MNT_RDONLY;
126
127	if (strcmp("nodevices", name) == 0)
128		vfsp->mnt_flag &= ~MNT_NODEV;
129
130	if (strcmp("noatime", name) == 0)
131		vfsp->mnt_flag &= ~MNT_NOATIME;
132
133	if (strcmp("atime", name) == 0)
134		vfsp->mnt_flag |= MNT_NOATIME;
135
136	if (strcmp("nosuid", name) == 0)
137		vfsp->mnt_flag &= ~MNT_NOSUID;
138
139	if (strcmp("suid", name) == 0)
140		vfsp->mnt_flag |= MNT_NOSUID;
141
142	if (strcmp("noexec", name) == 0)
143		vfsp->mnt_flag &= ~MNT_NOEXEC;
144
145	if (strcmp("exec", name) == 0)
146		vfsp->mnt_flag |= MNT_NOEXEC;
147}
148
149int
150vfs_optionisset(const vfs_t *vfsp, const char *name, char **argp)
151{
152
153	if (strcmp("ro", name) == 0)
154		return (vfsp->mnt_flag & MNT_RDONLY) != 0;
155
156	if (strcmp("rw", name) == 0)
157		return (vfsp->mnt_flag & MNT_RDONLY) == 0;
158
159	if (strcmp("nodevices", name) == 0)
160		return (vfsp->mnt_flag & MNT_NODEV) != 0;
161
162	if (strcmp("noatime", name) == 0)
163		return (vfsp->mnt_flag & MNT_NOATIME) != 0;
164
165	if (strcmp("atime", name) == 0)
166		return (vfsp->mnt_flag & MNT_NOATIME) == 0;
167
168	if (strcmp("nosuid", name) == 0)
169		return (vfsp->mnt_flag & MNT_NOSUID) != 0;
170
171	if (strcmp("suid", name) == 0)
172		return (vfsp->mnt_flag & MNT_NOSUID) == 0;
173
174	if (strcmp("noexec", name) == 0)
175		return (vfsp->mnt_flag & MNT_NOEXEC) != 0;
176
177	if (strcmp("exec", name) == 0)
178		return (vfsp->mnt_flag & MNT_NOEXEC) == 0;
179
180	return 0;
181}
182
183#ifdef PORT_FREEBSD
184int
185traverse(vnode_t **cvpp, int lktype)
186{
187	kthread_t *td = curthread;
188	vnode_t *cvp;
189	vnode_t *tvp;
190	vfs_t *vfsp;
191	int error;
192
193	cvp = *cvpp;
194	tvp = NULL;
195
196	/*
197	 * If this vnode is mounted on, then we transparently indirect
198	 * to the vnode which is the root of the mounted file system.
199	 * Before we do this we must check that an unmount is not in
200	 * progress on this vnode.
201	 */
202
203	for (;;) {
204		/*
205		 * Reached the end of the mount chain?
206		 */
207		vfsp = vn_mountedvfs(cvp);
208		if (vfsp == NULL)
209			break;
210		/*
211		 * tvp is NULL for *cvpp vnode, which we can't unlock.
212		 */
213		if (tvp != NULL)
214			vput(cvp);
215		else
216			vrele(cvp);
217
218		/*
219		 * The read lock must be held across the call to VFS_ROOT() to
220		 * prevent a concurrent unmount from destroying the vfs.
221		 */
222		error = VFS_ROOT(vfsp, &tvp);
223		if (error != 0)
224			return (error);
225		cvp = tvp;
226	}
227
228	*cvpp = cvp;
229	return (0);
230}
231
232int
233domount(kthread_t *td, vnode_t *vp, const char *fstype, char *fspath,
234    char *fspec, int fsflags)
235{
236	struct mount *mp;
237	struct vfsconf *vfsp;
238	struct ucred *newcr, *oldcr;
239	int error;
240
241	/*
242	 * Be ultra-paranoid about making sure the type and fspath
243	 * variables will fit in our mp buffers, including the
244	 * terminating NUL.
245	 */
246	if (strlen(fstype) >= MFSNAMELEN || strlen(fspath) >= MNAMELEN)
247		return (ENAMETOOLONG);
248
249	vfsp = vfs_byname_kld(fstype, td, &error);
250	if (vfsp == NULL)
251		return (ENODEV);
252
253	if (vp->v_type != VDIR)
254		return (ENOTDIR);
255	simple_lock(&vp->v_interlock);
256	if ((vp->v_iflag & VI_MOUNT) != 0 ||
257	    vp->v_mountedhere != NULL) {
258		simple_unlock(&vp->v_interlock);
259		return (EBUSY);
260	}
261	vp->v_iflag |= VI_MOUNT;
262	simple_unlock(&vp->v_interlock);
263
264	/*
265	 * Allocate and initialize the filesystem.
266	 */
267	vn_lock(vp, LK_SHARED | LK_RETRY);
268	mp = vfs_mount_alloc(vp, vfsp, fspath, td);
269	VOP_UNLOCK(vp);
270
271	mp->mnt_optnew = NULL;
272	vfs_setmntopt(mp, "from", fspec, 0);
273	mp->mnt_optnew = mp->mnt_opt;
274	mp->mnt_opt = NULL;
275
276	/*
277	 * Set the mount level flags.
278	 * crdup() can sleep, so do it before acquiring a mutex.
279	 */
280	newcr = crdup(kcred);
281	MNT_ILOCK(mp);
282	if (fsflags & MNT_RDONLY)
283		mp->mnt_flag |= MNT_RDONLY;
284	mp->mnt_flag &=~ MNT_UPDATEMASK;
285	mp->mnt_flag |= fsflags & (MNT_UPDATEMASK | MNT_FORCE | MNT_ROOTFS);
286	/*
287	 * Unprivileged user can trigger mounting a snapshot, but we don't want
288	 * him to unmount it, so we switch to privileged credentials.
289	 */
290	oldcr = mp->mnt_cred;
291	mp->mnt_cred = newcr;
292	mp->mnt_stat.f_owner = mp->mnt_cred->cr_uid;
293	MNT_IUNLOCK(mp);
294	crfree(oldcr);
295	/*
296	 * Mount the filesystem.
297	 * XXX The final recipients of VFS_MOUNT just overwrite the ndp they
298	 * get.  No freeing of cn_pnbuf.
299	 */
300	error = VFS_MOUNT(mp, td);
301
302	if (!error) {
303		if (mp->mnt_opt != NULL)
304			vfs_freeopts(mp->mnt_opt);
305		mp->mnt_opt = mp->mnt_optnew;
306		(void)VFS_STATFS(mp, &mp->mnt_stat, td);
307	}
308	/*
309	 * Prevent external consumers of mount options from reading
310	 * mnt_optnew.
311	*/
312	mp->mnt_optnew = NULL;
313	vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
314	/*
315	 * Put the new filesystem on the mount list after root.
316	 */
317#ifdef FREEBSD_NAMECACHE
318	cache_purge(vp);
319#endif
320	if (!error) {
321		vnode_t *mvp;
322
323		simple_lock(&vp->v_interlock);
324		vp->v_iflag &= ~VI_MOUNT;
325		simple_unlock(&vp->v_interlock);
326		vp->v_mountedhere = mp;
327		mutex_enter(&mountlist_lock);
328		CIRCLEQ_INSERT_TAIL(&mountlist, mp, mnt_list);
329		mutex_exit(&mountlist_lock);
330		vfs_event_signal(NULL, VQ_MOUNT, 0);
331		if (VFS_ROOT(mp, LK_EXCLUSIVE, &mvp, td))
332			panic("mount: lost mount");
333		mountcheckdirs(vp, mvp);
334		vput(mvp);
335		VOP_UNLOCK(vp);
336		if ((mp->mnt_flag & MNT_RDONLY) == 0)
337			error = vfs_allocate_syncvnode(mp);
338		vfs_unbusy(mp, td);
339		if (error)
340			vrele(vp);
341		else
342			vfs_mountedfrom(mp, fspec);
343	} else {
344		simple_lock(&vp->v_interlock);
345		vp->v_iflag &= ~VI_MOUNT;
346		simple_unlock(&vp->v_interlock);
347		VOP_UNLOCK(vp);
348		vfs_unbusy(mp, td);
349		vfs_mount_destroy(mp);
350	}
351	return (error);
352}
353#endif
354