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