1 /*	$NetBSD: vfs.c,v 1.6 2015/05/06 15:57:07 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 int
lookupname(char * dirname,enum uio_seg seg,vnode_t ** dirvpp,vnode_t ** compvpp)43 lookupname(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 
58 int
lookupnameat(char * dirname,enum uio_seg seg,vnode_t ** dirvpp,vnode_t ** compvpp,vnode_t * startvp)59 lookupnameat(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 
82 void
vfs_setmntopt(vfs_t * vfsp,const char * name,const char * arg,int flags)83 vfs_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 
117 void
vfs_clearmntopt(vfs_t * vfsp,const char * name)118 vfs_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 
149 int
vfs_optionisset(const vfs_t * vfsp,const char * name,char ** argp)150 vfs_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
184 int
traverse(vnode_t ** cvpp,int lktype)185 traverse(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 
232 int
domount(kthread_t * td,vnode_t * vp,const char * fstype,char * fspath,char * fspec,int fsflags)233 domount(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 		mountlist_append(mp);
328 		vfs_event_signal(NULL, VQ_MOUNT, 0);
329 		if (VFS_ROOT(mp, LK_EXCLUSIVE, &mvp, td))
330 			panic("mount: lost mount");
331 		mountcheckdirs(vp, mvp);
332 		vput(mvp);
333 		VOP_UNLOCK(vp);
334 		if ((mp->mnt_flag & MNT_RDONLY) == 0)
335 			vfs_syncer_add_to_worklist(mp);
336 		vfs_unbusy(mp, td);
337 		vfs_mountedfrom(mp, fspec);
338 	} else {
339 		simple_lock(&vp->v_interlock);
340 		vp->v_iflag &= ~VI_MOUNT;
341 		simple_unlock(&vp->v_interlock);
342 		VOP_UNLOCK(vp);
343 		vfs_unbusy(mp, td);
344 		vfs_mount_destroy(mp);
345 	}
346 	return (error);
347 }
348 #endif
349