xref: /freebsd/sys/fs/unionfs/union_vfsops.c (revision 0c43d89a0d8e976ca494d4837f4c1f3734d2c300)
1 /*
2  * Copyright (c) 1994 The Regents of the University of California.
3  * Copyright (c) 1994 Jan-Simon Pendry.
4  * All rights reserved.
5  *
6  * This code is derived from software donated to Berkeley by
7  * Jan-Simon Pendry.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in the
16  *    documentation and/or other materials provided with the distribution.
17  * 3. All advertising materials mentioning features or use of this software
18  *    must display the following acknowledgement:
19  *	This product includes software developed by the University of
20  *	California, Berkeley and its contributors.
21  * 4. Neither the name of the University nor the names of its contributors
22  *    may be used to endorse or promote products derived from this software
23  *    without specific prior written permission.
24  *
25  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
26  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
27  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
28  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
29  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
30  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
31  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
32  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
33  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
34  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
35  * SUCH DAMAGE.
36  *
37  *	@(#)union_vfsops.c	8.7 (Berkeley) 3/5/94
38  * $Id$
39  */
40 
41 /*
42  * Union Layer
43  */
44 
45 #include <sys/param.h>
46 #include <sys/systm.h>
47 #include <sys/time.h>
48 #include <sys/types.h>
49 #include <sys/proc.h>
50 #include <sys/vnode.h>
51 #include <sys/mount.h>
52 #include <sys/namei.h>
53 #include <sys/malloc.h>
54 #include <sys/filedesc.h>
55 #include <sys/queue.h>
56 #include <miscfs/union/union.h>
57 
58 /*
59  * Mount union filesystem
60  */
61 int
62 union_mount(mp, path, data, ndp, p)
63 	struct mount *mp;
64 	char *path;
65 	caddr_t data;
66 	struct nameidata *ndp;
67 	struct proc *p;
68 {
69 	int error = 0;
70 	struct union_args args;
71 	struct vnode *lowerrootvp = NULLVP;
72 	struct vnode *upperrootvp = NULLVP;
73 	struct union_mount *um;
74 	struct ucred *cred = 0;
75 	struct ucred *scred;
76 	struct vattr va;
77 	char *cp = 0;
78 	int len;
79 	u_int size;
80 
81 #ifdef UNION_DIAGNOSTIC
82 	printf("union_mount(mp = %x)\n", mp);
83 #endif
84 
85 	/*
86 	 * Update is a no-op
87 	 */
88 	if (mp->mnt_flag & MNT_UPDATE) {
89 		/*
90 		 * Need to provide.
91 		 * 1. a way to convert between rdonly and rdwr mounts.
92 		 * 2. support for nfs exports.
93 		 */
94 		error = EOPNOTSUPP;
95 		goto bad;
96 	}
97 
98 	/*
99 	 * Take a copy of the process's credentials.  This isn't
100 	 * quite right since the euid will always be zero and we
101 	 * want to get the "real" users credentials.  So fix up
102 	 * the uid field after taking the copy.
103 	 */
104 	cred = crdup(p->p_ucred);
105 	cred->cr_uid = p->p_cred->p_ruid;
106 
107 	/*
108 	 * Ensure the *real* user has write permission on the
109 	 * mounted-on directory.  This allows the mount_union
110 	 * command to be made setuid root so allowing anyone
111 	 * to do union mounts onto any directory on which they
112 	 * have write permission and which they also own.
113 	 */
114 	error = VOP_GETATTR(mp->mnt_vnodecovered, &va, cred, p);
115 	if (error)
116 		goto bad;
117 	if ((va.va_uid != cred->cr_uid) &&
118 	    (cred->cr_uid != 0)) {
119 		error = EACCES;
120 		goto bad;
121 	}
122 	error = VOP_ACCESS(mp->mnt_vnodecovered, VWRITE, cred, p);
123 	if (error)
124 		goto bad;
125 
126 	/*
127 	 * Get argument
128 	 */
129 	if (error = copyin(data, (caddr_t)&args, sizeof(struct union_args)))
130 		goto bad;
131 
132 	lowerrootvp = mp->mnt_vnodecovered;
133 	VREF(lowerrootvp);
134 
135 	/*
136 	 * Find upper node.  Use the real process credentials,
137 	 * not the effective ones since this will have come
138 	 * through a setuid process (mount_union).  All this
139 	 * messing around with permissions is entirely bogus
140 	 * and should be removed by allowing any user straight
141 	 * past the mount system call.
142 	 */
143 	scred = p->p_ucred;
144 	p->p_ucred = cred;
145 	NDINIT(ndp, LOOKUP, FOLLOW|WANTPARENT,
146 	       UIO_USERSPACE, args.target, p);
147 	p->p_ucred = scred;
148 
149 	if (error = namei(ndp))
150 		goto bad;
151 
152 	upperrootvp = ndp->ni_vp;
153 	vrele(ndp->ni_dvp);
154 	ndp->ni_dvp = NULL;
155 
156 	if (upperrootvp->v_type != VDIR) {
157 		error = EINVAL;
158 		goto bad;
159 	}
160 
161 	um = (struct union_mount *) malloc(sizeof(struct union_mount),
162 				M_UFSMNT, M_WAITOK);	/* XXX */
163 
164 	/*
165 	 * Keep a held reference to the target vnodes.
166 	 * They are vrele'd in union_unmount.
167 	 *
168 	 * Depending on the _BELOW flag, the filesystems are
169 	 * viewed in a different order.  In effect, this is the
170 	 * same as providing a mount under option to the mount syscall.
171 	 */
172 
173 	um->um_op = args.mntflags & UNMNT_OPMASK;
174 	switch (um->um_op) {
175 	case UNMNT_ABOVE:
176 		um->um_lowervp = lowerrootvp;
177 		um->um_uppervp = upperrootvp;
178 		break;
179 
180 	case UNMNT_BELOW:
181 		um->um_lowervp = upperrootvp;
182 		um->um_uppervp = lowerrootvp;
183 		break;
184 
185 	case UNMNT_REPLACE:
186 		vrele(lowerrootvp);
187 		lowerrootvp = NULLVP;
188 		um->um_uppervp = upperrootvp;
189 		um->um_lowervp = lowerrootvp;
190 		break;
191 
192 	default:
193 		error = EINVAL;
194 		goto bad;
195 	}
196 
197 	um->um_cred = cred;
198 	um->um_cmode = UN_DIRMODE &~ p->p_fd->fd_cmask;
199 
200 	/*
201 	 * Depending on what you think the MNT_LOCAL flag might mean,
202 	 * you may want the && to be || on the conditional below.
203 	 * At the moment it has been defined that the filesystem is
204 	 * only local if it is all local, ie the MNT_LOCAL flag implies
205 	 * that the entire namespace is local.  If you think the MNT_LOCAL
206 	 * flag implies that some of the files might be stored locally
207 	 * then you will want to change the conditional.
208 	 */
209 	if (um->um_op == UNMNT_ABOVE) {
210 		if (((um->um_lowervp == NULLVP) ||
211 		     (um->um_lowervp->v_mount->mnt_flag & MNT_LOCAL)) &&
212 		    (um->um_uppervp->v_mount->mnt_flag & MNT_LOCAL))
213 			mp->mnt_flag |= MNT_LOCAL;
214 	}
215 
216 	/*
217 	 * Copy in the upper layer's RDONLY flag.  This is for the benefit
218 	 * of lookup() which explicitly checks the flag, rather than asking
219 	 * the filesystem for it's own opinion.  This means, that an update
220 	 * mount of the underlying filesystem to go from rdonly to rdwr
221 	 * will leave the unioned view as read-only.
222 	 */
223 	mp->mnt_flag |= (um->um_uppervp->v_mount->mnt_flag & MNT_RDONLY);
224 
225 	/*
226 	 * This is a user mount.  Privilege check for unmount
227 	 * will be done in union_unmount.
228 	 */
229 	mp->mnt_flag |= MNT_USER;
230 
231 	mp->mnt_data = (qaddr_t) um;
232 	getnewfsid(mp, MOUNT_UNION);
233 
234 	(void) copyinstr(path, mp->mnt_stat.f_mntonname, MNAMELEN - 1, &size);
235 	bzero(mp->mnt_stat.f_mntonname + size, MNAMELEN - size);
236 
237 	switch (um->um_op) {
238 	case UNMNT_ABOVE:
239 		cp = "<above>";
240 		break;
241 	case UNMNT_BELOW:
242 		cp = "<below>";
243 		break;
244 	case UNMNT_REPLACE:
245 		cp = "";
246 		break;
247 	}
248 	len = strlen(cp);
249 	bcopy(cp, mp->mnt_stat.f_mntfromname, len);
250 
251 	cp = mp->mnt_stat.f_mntfromname + len;
252 	len = MNAMELEN - len;
253 
254 	(void) copyinstr(args.target, cp, len - 1, &size);
255 	bzero(cp + size, len - size);
256 
257 #ifdef UNION_DIAGNOSTIC
258 	printf("union_mount: from %s, on %s\n",
259 		mp->mnt_stat.f_mntfromname, mp->mnt_stat.f_mntonname);
260 #endif
261 	return (0);
262 
263 bad:
264 	if (cred)
265 		crfree(cred);
266 	if (upperrootvp)
267 		vrele(upperrootvp);
268 	if (lowerrootvp)
269 		vrele(lowerrootvp);
270 	return (error);
271 }
272 
273 /*
274  * VFS start.  Nothing needed here - the start routine
275  * on the underlying filesystem(s) will have been called
276  * when that filesystem was mounted.
277  */
278 int
279 union_start(mp, flags, p)
280 	struct mount *mp;
281 	int flags;
282 	struct proc *p;
283 {
284 
285 	return (0);
286 }
287 
288 /*
289  * Free reference to union layer
290  */
291 int
292 union_unmount(mp, mntflags, p)
293 	struct mount *mp;
294 	int mntflags;
295 	struct proc *p;
296 {
297 	struct union_mount *um = MOUNTTOUNIONMOUNT(mp);
298 	struct vnode *um_rootvp;
299 	int error;
300 	int flags = 0;
301 	extern int doforce;
302 
303 #ifdef UNION_DIAGNOSTIC
304 	printf("union_unmount(mp = %x)\n", mp);
305 #endif
306 
307 	/* only the mounter, or superuser can unmount */
308 	if ((p->p_cred->p_ruid != um->um_cred->cr_uid) &&
309 	    (error = suser(p->p_ucred, &p->p_acflag)))
310 		return (error);
311 
312 	if (mntflags & MNT_FORCE) {
313 		/* union can never be rootfs so don't check for it */
314 		if (!doforce)
315 			return (EINVAL);
316 		flags |= FORCECLOSE;
317 	}
318 
319 	if (error = union_root(mp, &um_rootvp))
320 		return (error);
321 	if (um_rootvp->v_usecount > 1) {
322 		vput(um_rootvp);
323 		return (EBUSY);
324 	}
325 	if (error = vflush(mp, um_rootvp, flags)) {
326 		vput(um_rootvp);
327 		return (error);
328 	}
329 
330 #ifdef UNION_DIAGNOSTIC
331 	vprint("alias root of lower", um_rootvp);
332 #endif
333 	/*
334 	 * Discard references to upper and lower target vnodes.
335 	 */
336 	if (um->um_lowervp)
337 		vrele(um->um_lowervp);
338 	vrele(um->um_uppervp);
339 	crfree(um->um_cred);
340 	/*
341 	 * Release reference on underlying root vnode
342 	 */
343 	vput(um_rootvp);
344 	/*
345 	 * And blow it away for future re-use
346 	 */
347 	vgone(um_rootvp);
348 	/*
349 	 * Finally, throw away the union_mount structure
350 	 */
351 	free(mp->mnt_data, M_UFSMNT);	/* XXX */
352 	mp->mnt_data = 0;
353 	return (0);
354 }
355 
356 int
357 union_root(mp, vpp)
358 	struct mount *mp;
359 	struct vnode **vpp;
360 {
361 	struct union_mount *um = MOUNTTOUNIONMOUNT(mp);
362 	int error;
363 	int loselock;
364 
365 #ifdef UNION_DIAGNOSTIC
366 	printf("union_root(mp = %x, lvp = %x, uvp = %x)\n", mp,
367 			um->um_lowervp,
368 			um->um_uppervp);
369 #endif
370 
371 	/*
372 	 * Return locked reference to root.
373 	 */
374 	VREF(um->um_uppervp);
375 	if ((um->um_op == UNMNT_BELOW) &&
376 	     VOP_ISLOCKED(um->um_uppervp)) {
377 		loselock = 1;
378 	} else {
379 		VOP_LOCK(um->um_uppervp);
380 		loselock = 0;
381 	}
382 	if (um->um_lowervp)
383 		VREF(um->um_lowervp);
384 	error = union_allocvp(vpp, mp,
385 			      (struct vnode *) 0,
386 			      (struct vnode *) 0,
387 			      (struct componentname *) 0,
388 			      um->um_uppervp,
389 			      um->um_lowervp);
390 
391 	if (error) {
392 		if (!loselock)
393 			VOP_UNLOCK(um->um_uppervp);
394 		vrele(um->um_uppervp);
395 		if (um->um_lowervp)
396 			vrele(um->um_lowervp);
397 	} else {
398 		(*vpp)->v_flag |= VROOT;
399 		if (loselock)
400 			VTOUNION(*vpp)->un_flags &= ~UN_ULOCK;
401 	}
402 
403 	return (error);
404 }
405 
406 int
407 union_quotactl(mp, cmd, uid, arg, p)
408 	struct mount *mp;
409 	int cmd;
410 	uid_t uid;
411 	caddr_t arg;
412 	struct proc *p;
413 {
414 
415 	return (EOPNOTSUPP);
416 }
417 
418 int
419 union_statfs(mp, sbp, p)
420 	struct mount *mp;
421 	struct statfs *sbp;
422 	struct proc *p;
423 {
424 	int error;
425 	struct union_mount *um = MOUNTTOUNIONMOUNT(mp);
426 	struct statfs mstat;
427 	int lbsize;
428 
429 #ifdef UNION_DIAGNOSTIC
430 	printf("union_statfs(mp = %x, lvp = %x, uvp = %x)\n", mp,
431 			um->um_lowervp,
432 	       		um->um_uppervp);
433 #endif
434 
435 	bzero(&mstat, sizeof(mstat));
436 
437 	if (um->um_lowervp) {
438 		error = VFS_STATFS(um->um_lowervp->v_mount, &mstat, p);
439 		if (error)
440 			return (error);
441 	}
442 
443 	/* now copy across the "interesting" information and fake the rest */
444 #if 0
445 	sbp->f_type = mstat.f_type;
446 	sbp->f_flags = mstat.f_flags;
447 	sbp->f_bsize = mstat.f_bsize;
448 	sbp->f_iosize = mstat.f_iosize;
449 #endif
450 	lbsize = mstat.f_bsize;
451 	sbp->f_blocks = mstat.f_blocks;
452 	sbp->f_bfree = mstat.f_bfree;
453 	sbp->f_bavail = mstat.f_bavail;
454 	sbp->f_files = mstat.f_files;
455 	sbp->f_ffree = mstat.f_ffree;
456 
457 	error = VFS_STATFS(um->um_uppervp->v_mount, &mstat, p);
458 	if (error)
459 		return (error);
460 
461 	sbp->f_type = MOUNT_UNION;
462 	sbp->f_flags = mstat.f_flags;
463 	sbp->f_bsize = mstat.f_bsize;
464 	sbp->f_iosize = mstat.f_iosize;
465 
466 	/*
467 	 * if the lower and upper blocksizes differ, then frig the
468 	 * block counts so that the sizes reported by df make some
469 	 * kind of sense.  none of this makes sense though.
470 	 */
471 
472 	if (mstat.f_bsize != lbsize) {
473 		sbp->f_blocks = sbp->f_blocks * lbsize / mstat.f_bsize;
474 		sbp->f_bfree = sbp->f_bfree * lbsize / mstat.f_bsize;
475 		sbp->f_bavail = sbp->f_bavail * lbsize / mstat.f_bsize;
476 	}
477 	sbp->f_blocks += mstat.f_blocks;
478 	sbp->f_bfree += mstat.f_bfree;
479 	sbp->f_bavail += mstat.f_bavail;
480 	sbp->f_files += mstat.f_files;
481 	sbp->f_ffree += mstat.f_ffree;
482 
483 	if (sbp != &mp->mnt_stat) {
484 		bcopy(&mp->mnt_stat.f_fsid, &sbp->f_fsid, sizeof(sbp->f_fsid));
485 		bcopy(mp->mnt_stat.f_mntonname, sbp->f_mntonname, MNAMELEN);
486 		bcopy(mp->mnt_stat.f_mntfromname, sbp->f_mntfromname, MNAMELEN);
487 	}
488 	return (0);
489 }
490 
491 int
492 union_sync(mp, waitfor, cred, p)
493 	struct mount *mp;
494 	int waitfor;
495 	struct ucred *cred;
496 	struct proc *p;
497 {
498 
499 	/*
500 	 * XXX - Assumes no data cached at union layer.
501 	 */
502 	return (0);
503 }
504 
505 int
506 union_vget(mp, ino, vpp)
507 	struct mount *mp;
508 	ino_t ino;
509 	struct vnode **vpp;
510 {
511 
512 	return (EOPNOTSUPP);
513 }
514 
515 int
516 union_fhtovp(mp, fidp, nam, vpp, exflagsp, credanonp)
517 	struct mount *mp;
518 	struct fid *fidp;
519 	struct mbuf *nam;
520 	struct vnode **vpp;
521 	int *exflagsp;
522 	struct ucred **credanonp;
523 {
524 
525 	return (EOPNOTSUPP);
526 }
527 
528 int
529 union_vptofh(vp, fhp)
530 	struct vnode *vp;
531 	struct fid *fhp;
532 {
533 
534 	return (EOPNOTSUPP);
535 }
536 
537 int union_init __P((void));
538 
539 struct vfsops union_vfsops = {
540 	union_mount,
541 	union_start,
542 	union_unmount,
543 	union_root,
544 	union_quotactl,
545 	union_statfs,
546 	union_sync,
547 	union_vget,
548 	union_fhtovp,
549 	union_vptofh,
550 	union_init,
551 };
552