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