xref: /netbsd/external/cddl/osnet/sys/kern/vfs.c (revision 6550d01e)
1 /*	$NetBSD: vfs.c,v 1.3 2010/06/24 13:03:05 hannken 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 
42 MALLOC_DECLARE(M_MOUNT);
43 
44 int
45 lookupname(char *dirname, enum uio_seg seg, vnode_t **dirvpp, vnode_t **compvpp)
46 {
47 	struct nameidata nd;
48 	int error;
49 
50 	error = 0;
51 
52 	KASSERT(dirvpp == NULL);
53 
54 	error = namei_simple_kernel(dirname,  NSM_FOLLOW_NOEMULROOT, compvpp);
55 
56 	return error;
57 }
58 
59 
60 int
61 lookupnameat(char *dirname, enum uio_seg seg, vnode_t **dirvpp,
62 	vnode_t **compvpp, vnode_t *startvp)
63 {
64 
65 	struct nameidata nd;
66 	int error;
67 
68 	error = EOPNOTSUPP;
69 
70 /*      XXX Disable until I upgrade testing kernel.
71         KASSERT(dirvpp == NULL);
72 
73 	NDINIT(&nd, LOOKUP, FOLLOW, UIO_SYSSPACE, dirname);
74 
75 	if ((error = nameiat(&nd, startvp)) != 0)
76 		return error;
77 
78 	*compvpp = nd.ni_vp;*/
79 
80 	return (error);
81 }
82 
83 
84 void
85 vfs_setmntopt(vfs_t *vfsp, const char *name, const char *arg,
86     int flags)
87 {
88 
89 	if (strcmp("ro", name) == 0)
90 		vfsp->mnt_flag |= MNT_RDONLY;
91 
92 	if (strcmp("rw", name) == 0)
93 		vfsp->mnt_flag &= ~MNT_RDONLY;
94 
95 	if (strcmp("nodevices", name) == 0)
96 		vfsp->mnt_flag |= MNT_NODEV;
97 
98 	if (strcmp("noatime", name) == 0)
99 		vfsp->mnt_flag |= MNT_NOATIME;
100 
101 	if (strcmp("atime", name) == 0)
102 		vfsp->mnt_flag &= ~MNT_NOATIME;
103 
104 	if (strcmp("nosuid", name) == 0)
105 		vfsp->mnt_flag |= MNT_NOSUID;
106 
107 	if (strcmp("suid", name) == 0)
108 		vfsp->mnt_flag &= ~MNT_NOSUID;
109 
110 	if (strcmp("noexec", name) == 0)
111 		vfsp->mnt_flag |= MNT_NOEXEC;
112 
113 	if (strcmp("exec", name) == 0)
114 		vfsp->mnt_flag &= ~MNT_NOEXEC;
115 
116 	vfsp->mnt_flag |= MNT_LOCAL;
117 }
118 
119 void
120 vfs_clearmntopt(vfs_t *vfsp, const char *name)
121 {
122 
123 	if (strcmp("ro", name) == 0)
124 		vfsp->mnt_flag |= MNT_RDONLY;
125 
126 	if (strcmp("rw", name) == 0)
127 		vfsp->mnt_flag &= ~MNT_RDONLY;
128 
129 	if (strcmp("nodevices", name) == 0)
130 		vfsp->mnt_flag &= ~MNT_NODEV;
131 
132 	if (strcmp("noatime", name) == 0)
133 		vfsp->mnt_flag &= ~MNT_NOATIME;
134 
135 	if (strcmp("atime", name) == 0)
136 		vfsp->mnt_flag |= MNT_NOATIME;
137 
138 	if (strcmp("nosuid", name) == 0)
139 		vfsp->mnt_flag &= ~MNT_NOSUID;
140 
141 	if (strcmp("suid", name) == 0)
142 		vfsp->mnt_flag |= MNT_NOSUID;
143 
144 	if (strcmp("noexec", name) == 0)
145 		vfsp->mnt_flag &= ~MNT_NOEXEC;
146 
147 	if (strcmp("exec", name) == 0)
148 		vfsp->mnt_flag |= MNT_NOEXEC;
149 }
150 
151 int
152 vfs_optionisset(const vfs_t *vfsp, const char *name, char **argp)
153 {
154 
155 	if (strcmp("ro", name) == 0)
156 		return (vfsp->mnt_flag & MNT_RDONLY) != 0;
157 
158 	if (strcmp("rw", name) == 0)
159 		return (vfsp->mnt_flag & MNT_RDONLY) == 0;
160 
161 	if (strcmp("nodevices", name) == 0)
162 		return (vfsp->mnt_flag & MNT_NODEV) != 0;
163 
164 	if (strcmp("noatime", name) == 0)
165 		return (vfsp->mnt_flag & MNT_NOATIME) != 0;
166 
167 	if (strcmp("atime", name) == 0)
168 		return (vfsp->mnt_flag & MNT_NOATIME) == 0;
169 
170 	if (strcmp("nosuid", name) == 0)
171 		return (vfsp->mnt_flag & MNT_NOSUID) != 0;
172 
173 	if (strcmp("suid", name) == 0)
174 		return (vfsp->mnt_flag & MNT_NOSUID) == 0;
175 
176 	if (strcmp("noexec", name) == 0)
177 		return (vfsp->mnt_flag & MNT_NOEXEC) != 0;
178 
179 	if (strcmp("exec", name) == 0)
180 		return (vfsp->mnt_flag & MNT_NOEXEC) == 0;
181 
182 	return 0;
183 }
184 
185 #ifdef PORT_FREEBSD
186 int
187 traverse(vnode_t **cvpp, int lktype)
188 {
189 	kthread_t *td = curthread;
190 	vnode_t *cvp;
191 	vnode_t *tvp;
192 	vfs_t *vfsp;
193 	int error;
194 
195 	cvp = *cvpp;
196 	tvp = NULL;
197 
198 	/*
199 	 * If this vnode is mounted on, then we transparently indirect
200 	 * to the vnode which is the root of the mounted file system.
201 	 * Before we do this we must check that an unmount is not in
202 	 * progress on this vnode.
203 	 */
204 
205 	for (;;) {
206 		/*
207 		 * Reached the end of the mount chain?
208 		 */
209 		vfsp = vn_mountedvfs(cvp);
210 		if (vfsp == NULL)
211 			break;
212 		/*
213 		 * tvp is NULL for *cvpp vnode, which we can't unlock.
214 		 */
215 		if (tvp != NULL)
216 			vput(cvp);
217 		else
218 			vrele(cvp);
219 
220 		/*
221 		 * The read lock must be held across the call to VFS_ROOT() to
222 		 * prevent a concurrent unmount from destroying the vfs.
223 		 */
224 		error = VFS_ROOT(vfsp, &tvp);
225 		if (error != 0)
226 			return (error);
227 		cvp = tvp;
228 	}
229 
230 	*cvpp = cvp;
231 	return (0);
232 }
233 
234 int
235 domount(kthread_t *td, vnode_t *vp, const char *fstype, char *fspath,
236     char *fspec, int fsflags)
237 {
238 	struct mount *mp;
239 	struct vfsconf *vfsp;
240 	struct ucred *newcr, *oldcr;
241 	int error;
242 
243 	/*
244 	 * Be ultra-paranoid about making sure the type and fspath
245 	 * variables will fit in our mp buffers, including the
246 	 * terminating NUL.
247 	 */
248 	if (strlen(fstype) >= MFSNAMELEN || strlen(fspath) >= MNAMELEN)
249 		return (ENAMETOOLONG);
250 
251 	vfsp = vfs_byname_kld(fstype, td, &error);
252 	if (vfsp == NULL)
253 		return (ENODEV);
254 
255 	if (vp->v_type != VDIR)
256 		return (ENOTDIR);
257 	simple_lock(&vp->v_interlock);
258 	if ((vp->v_iflag & VI_MOUNT) != 0 ||
259 	    vp->v_mountedhere != NULL) {
260 		simple_unlock(&vp->v_interlock);
261 		return (EBUSY);
262 	}
263 	vp->v_iflag |= VI_MOUNT;
264 	simple_unlock(&vp->v_interlock);
265 
266 	/*
267 	 * Allocate and initialize the filesystem.
268 	 */
269 	vn_lock(vp, LK_SHARED | LK_RETRY);
270 	mp = vfs_mount_alloc(vp, vfsp, fspath, td);
271 	VOP_UNLOCK(vp);
272 
273 	mp->mnt_optnew = NULL;
274 	vfs_setmntopt(mp, "from", fspec, 0);
275 	mp->mnt_optnew = mp->mnt_opt;
276 	mp->mnt_opt = NULL;
277 
278 	/*
279 	 * Set the mount level flags.
280 	 * crdup() can sleep, so do it before acquiring a mutex.
281 	 */
282 	newcr = crdup(kcred);
283 	MNT_ILOCK(mp);
284 	if (fsflags & MNT_RDONLY)
285 		mp->mnt_flag |= MNT_RDONLY;
286 	mp->mnt_flag &=~ MNT_UPDATEMASK;
287 	mp->mnt_flag |= fsflags & (MNT_UPDATEMASK | MNT_FORCE | MNT_ROOTFS);
288 	/*
289 	 * Unprivileged user can trigger mounting a snapshot, but we don't want
290 	 * him to unmount it, so we switch to privileged credentials.
291 	 */
292 	oldcr = mp->mnt_cred;
293 	mp->mnt_cred = newcr;
294 	mp->mnt_stat.f_owner = mp->mnt_cred->cr_uid;
295 	MNT_IUNLOCK(mp);
296 	crfree(oldcr);
297 	/*
298 	 * Mount the filesystem.
299 	 * XXX The final recipients of VFS_MOUNT just overwrite the ndp they
300 	 * get.  No freeing of cn_pnbuf.
301 	 */
302 	error = VFS_MOUNT(mp, td);
303 
304 	if (!error) {
305 		if (mp->mnt_opt != NULL)
306 			vfs_freeopts(mp->mnt_opt);
307 		mp->mnt_opt = mp->mnt_optnew;
308 		(void)VFS_STATFS(mp, &mp->mnt_stat, td);
309 	}
310 	/*
311 	 * Prevent external consumers of mount options from reading
312 	 * mnt_optnew.
313 	*/
314 	mp->mnt_optnew = NULL;
315 	vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
316 	/*
317 	 * Put the new filesystem on the mount list after root.
318 	 */
319 #ifdef FREEBSD_NAMECACHE
320 	cache_purge(vp);
321 #endif
322 	if (!error) {
323 		vnode_t *mvp;
324 
325 		simple_lock(&vp->v_interlock);
326 		vp->v_iflag &= ~VI_MOUNT;
327 		simple_unlock(&vp->v_interlock);
328 		vp->v_mountedhere = mp;
329 		mutex_enter(&mountlist_lock);
330 		CIRCLEQ_INSERT_TAIL(&mountlist, mp, mnt_list);
331 		mutex_exit(&mountlist_lock);
332 		vfs_event_signal(NULL, VQ_MOUNT, 0);
333 		if (VFS_ROOT(mp, LK_EXCLUSIVE, &mvp, td))
334 			panic("mount: lost mount");
335 		mountcheckdirs(vp, mvp);
336 		vput(mvp);
337 		VOP_UNLOCK(vp);
338 		if ((mp->mnt_flag & MNT_RDONLY) == 0)
339 			error = vfs_allocate_syncvnode(mp);
340 		vfs_unbusy(mp, td);
341 		if (error)
342 			vrele(vp);
343 		else
344 			vfs_mountedfrom(mp, fspec);
345 	} else {
346 		simple_lock(&vp->v_interlock);
347 		vp->v_iflag &= ~VI_MOUNT;
348 		simple_unlock(&vp->v_interlock);
349 		VOP_UNLOCK(vp);
350 		vfs_unbusy(mp, td);
351 		vfs_mount_destroy(mp);
352 	}
353 	return (error);
354 }
355 #endif
356