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