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.16 (Berkeley) 03/29/95
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 = 0;
47 	struct ucred *cred = 0;
48 	struct ucred *scred;
49 	struct vattr va;
50 	char *cp;
51 	int len;
52 	u_int size;
53 
54 #ifdef UNION_DIAGNOSTIC
55 	printf("union_mount(mp = %x)\n", mp);
56 #endif
57 
58 	/*
59 	 * Update is a no-op
60 	 */
61 	if (mp->mnt_flag & MNT_UPDATE) {
62 		/*
63 		 * Need to provide.
64 		 * 1. a way to convert between rdonly and rdwr mounts.
65 		 * 2. support for nfs exports.
66 		 */
67 		error = EOPNOTSUPP;
68 		goto bad;
69 	}
70 
71 	/*
72 	 * Get argument
73 	 */
74 	if (error = copyin(data, (caddr_t)&args, sizeof(struct union_args)))
75 		goto bad;
76 
77 	lowerrootvp = mp->mnt_vnodecovered;
78 	VREF(lowerrootvp);
79 
80 	/*
81 	 * Find upper node.
82 	 */
83 	NDINIT(ndp, LOOKUP, FOLLOW|WANTPARENT,
84 	       UIO_USERSPACE, args.target, p);
85 
86 	if (error = namei(ndp))
87 		goto bad;
88 
89 	upperrootvp = ndp->ni_vp;
90 	vrele(ndp->ni_dvp);
91 	ndp->ni_dvp = NULL;
92 
93 	if (upperrootvp->v_type != VDIR) {
94 		error = EINVAL;
95 		goto bad;
96 	}
97 
98 	um = (struct union_mount *) malloc(sizeof(struct union_mount),
99 				M_UFSMNT, M_WAITOK);	/* XXX */
100 
101 	/*
102 	 * Keep a held reference to the target vnodes.
103 	 * They are vrele'd in union_unmount.
104 	 *
105 	 * Depending on the _BELOW flag, the filesystems are
106 	 * viewed in a different order.  In effect, this is the
107 	 * same as providing a mount under option to the mount syscall.
108 	 */
109 
110 	um->um_op = args.mntflags & UNMNT_OPMASK;
111 	switch (um->um_op) {
112 	case UNMNT_ABOVE:
113 		um->um_lowervp = lowerrootvp;
114 		um->um_uppervp = upperrootvp;
115 		break;
116 
117 	case UNMNT_BELOW:
118 		um->um_lowervp = upperrootvp;
119 		um->um_uppervp = lowerrootvp;
120 		break;
121 
122 	case UNMNT_REPLACE:
123 		vrele(lowerrootvp);
124 		lowerrootvp = NULLVP;
125 		um->um_uppervp = upperrootvp;
126 		um->um_lowervp = lowerrootvp;
127 		break;
128 
129 	default:
130 		error = EINVAL;
131 		goto bad;
132 	}
133 
134 	/*
135 	 * Unless the mount is readonly, ensure that the top layer
136 	 * supports whiteout operations
137 	 */
138 	if ((mp->mnt_flag & MNT_RDONLY) == 0) {
139 		error = VOP_WHITEOUT(um->um_uppervp, (struct componentname *) 0, LOOKUP);
140 		if (error)
141 			goto bad;
142 	}
143 
144 	um->um_cred = p->p_ucred;
145 	crhold(um->um_cred);
146 	um->um_cmode = UN_DIRMODE &~ p->p_fd->fd_cmask;
147 
148 	/*
149 	 * Depending on what you think the MNT_LOCAL flag might mean,
150 	 * you may want the && to be || on the conditional below.
151 	 * At the moment it has been defined that the filesystem is
152 	 * only local if it is all local, ie the MNT_LOCAL flag implies
153 	 * that the entire namespace is local.  If you think the MNT_LOCAL
154 	 * flag implies that some of the files might be stored locally
155 	 * then you will want to change the conditional.
156 	 */
157 	if (um->um_op == UNMNT_ABOVE) {
158 		if (((um->um_lowervp == NULLVP) ||
159 		     (um->um_lowervp->v_mount->mnt_flag & MNT_LOCAL)) &&
160 		    (um->um_uppervp->v_mount->mnt_flag & MNT_LOCAL))
161 			mp->mnt_flag |= MNT_LOCAL;
162 	}
163 
164 	/*
165 	 * Copy in the upper layer's RDONLY flag.  This is for the benefit
166 	 * of lookup() which explicitly checks the flag, rather than asking
167 	 * the filesystem for it's own opinion.  This means, that an update
168 	 * mount of the underlying filesystem to go from rdonly to rdwr
169 	 * will leave the unioned view as read-only.
170 	 */
171 	mp->mnt_flag |= (um->um_uppervp->v_mount->mnt_flag & MNT_RDONLY);
172 
173 	mp->mnt_data = (qaddr_t) um;
174 	vfs_getnewfsid(mp);
175 
176 	(void) copyinstr(path, mp->mnt_stat.f_mntonname, MNAMELEN - 1, &size);
177 	bzero(mp->mnt_stat.f_mntonname + size, MNAMELEN - size);
178 
179 	switch (um->um_op) {
180 	case UNMNT_ABOVE:
181 		cp = "<above>:";
182 		break;
183 	case UNMNT_BELOW:
184 		cp = "<below>:";
185 		break;
186 	case UNMNT_REPLACE:
187 		cp = "";
188 		break;
189 	}
190 	len = strlen(cp);
191 	bcopy(cp, mp->mnt_stat.f_mntfromname, len);
192 
193 	cp = mp->mnt_stat.f_mntfromname + len;
194 	len = MNAMELEN - len;
195 
196 	(void) copyinstr(args.target, cp, len - 1, &size);
197 	bzero(cp + size, len - size);
198 
199 #ifdef UNION_DIAGNOSTIC
200 	printf("union_mount: from %s, on %s\n",
201 		mp->mnt_stat.f_mntfromname, mp->mnt_stat.f_mntonname);
202 #endif
203 	return (0);
204 
205 bad:
206 	if (um)
207 		free(um, M_UFSMNT);
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 freeing;
245 	int flags = 0;
246 	extern int doforce;
247 
248 #ifdef UNION_DIAGNOSTIC
249 	printf("union_unmount(mp = %x)\n", mp);
250 #endif
251 
252 	if (mntflags & MNT_FORCE) {
253 		/* union can never be rootfs so don't check for it */
254 		if (!doforce)
255 			return (EINVAL);
256 		flags |= FORCECLOSE;
257 	}
258 
259 	if (error = union_root(mp, &um_rootvp))
260 		return (error);
261 
262 	/*
263 	 * Keep flushing vnodes from the mount list.
264 	 * This is needed because of the un_pvp held
265 	 * reference to the parent vnode.
266 	 * If more vnodes have been freed on a given pass,
267 	 * the try again.  The loop will iterate at most
268 	 * (d) times, where (d) is the maximum tree depth
269 	 * in the filesystem.
270 	 */
271 	for (freeing = 0; vflush(mp, um_rootvp, flags) != 0;) {
272 		struct vnode *vp;
273 		int n;
274 
275 		/* count #vnodes held on mount list */
276 		for (n = 0, vp = mp->mnt_vnodelist.lh_first;
277 				vp != NULLVP;
278 				vp = vp->v_mntvnodes.le_next)
279 			n++;
280 
281 		/* if this is unchanged then stop */
282 		if (n == freeing)
283 			break;
284 
285 		/* otherwise try once more time */
286 		freeing = n;
287 	}
288 
289 	/* At this point the root vnode should have a single reference */
290 	if (um_rootvp->v_usecount > 1) {
291 		vput(um_rootvp);
292 		return (EBUSY);
293 	}
294 
295 #ifdef UNION_DIAGNOSTIC
296 	vprint("union root", um_rootvp);
297 #endif
298 	/*
299 	 * Discard references to upper and lower target vnodes.
300 	 */
301 	if (um->um_lowervp)
302 		vrele(um->um_lowervp);
303 	vrele(um->um_uppervp);
304 	crfree(um->um_cred);
305 	/*
306 	 * Release reference on underlying root vnode
307 	 */
308 	vput(um_rootvp);
309 	/*
310 	 * And blow it away for future re-use
311 	 */
312 	VOP_REVOKE(um_rootvp, 0);
313 	/*
314 	 * Finally, throw away the union_mount structure
315 	 */
316 	free(mp->mnt_data, M_UFSMNT);	/* XXX */
317 	mp->mnt_data = 0;
318 	return (0);
319 }
320 
321 int
322 union_root(mp, vpp)
323 	struct mount *mp;
324 	struct vnode **vpp;
325 {
326 	struct union_mount *um = MOUNTTOUNIONMOUNT(mp);
327 	int error;
328 	int loselock;
329 
330 	/*
331 	 * Return locked reference to root.
332 	 */
333 	VREF(um->um_uppervp);
334 	if ((um->um_op == UNMNT_BELOW) &&
335 	     VOP_ISLOCKED(um->um_uppervp)) {
336 		loselock = 1;
337 	} else {
338 		VOP_LOCK(um->um_uppervp);
339 		loselock = 0;
340 	}
341 	if (um->um_lowervp)
342 		VREF(um->um_lowervp);
343 	error = union_allocvp(vpp, mp,
344 			      (struct vnode *) 0,
345 			      (struct vnode *) 0,
346 			      (struct componentname *) 0,
347 			      um->um_uppervp,
348 			      um->um_lowervp,
349 			      1);
350 
351 	if (error) {
352 		if (!loselock)
353 			VOP_UNLOCK(um->um_uppervp);
354 		vrele(um->um_uppervp);
355 		if (um->um_lowervp)
356 			vrele(um->um_lowervp);
357 	} else {
358 		if (loselock)
359 			VTOUNION(*vpp)->un_flags &= ~UN_ULOCK;
360 	}
361 
362 	return (error);
363 }
364 
365 int
366 union_statfs(mp, sbp, p)
367 	struct mount *mp;
368 	struct statfs *sbp;
369 	struct proc *p;
370 {
371 	int error;
372 	struct union_mount *um = MOUNTTOUNIONMOUNT(mp);
373 	struct statfs mstat;
374 	int lbsize;
375 
376 #ifdef UNION_DIAGNOSTIC
377 	printf("union_statfs(mp = %x, lvp = %x, uvp = %x)\n", mp,
378 			um->um_lowervp,
379 	       		um->um_uppervp);
380 #endif
381 
382 	bzero(&mstat, sizeof(mstat));
383 
384 	if (um->um_lowervp) {
385 		error = VFS_STATFS(um->um_lowervp->v_mount, &mstat, p);
386 		if (error)
387 			return (error);
388 	}
389 
390 	/* now copy across the "interesting" information and fake the rest */
391 #if 0
392 	sbp->f_type = mstat.f_type;
393 	sbp->f_flags = mstat.f_flags;
394 	sbp->f_bsize = mstat.f_bsize;
395 	sbp->f_iosize = mstat.f_iosize;
396 #endif
397 	lbsize = mstat.f_bsize;
398 	sbp->f_blocks = mstat.f_blocks;
399 	sbp->f_bfree = mstat.f_bfree;
400 	sbp->f_bavail = mstat.f_bavail;
401 	sbp->f_files = mstat.f_files;
402 	sbp->f_ffree = mstat.f_ffree;
403 
404 	error = VFS_STATFS(um->um_uppervp->v_mount, &mstat, p);
405 	if (error)
406 		return (error);
407 
408 	sbp->f_flags = mstat.f_flags;
409 	sbp->f_bsize = mstat.f_bsize;
410 	sbp->f_iosize = mstat.f_iosize;
411 
412 	/*
413 	 * if the lower and upper blocksizes differ, then frig the
414 	 * block counts so that the sizes reported by df make some
415 	 * kind of sense.  none of this makes sense though.
416 	 */
417 
418 	if (mstat.f_bsize != lbsize)
419 		sbp->f_blocks = sbp->f_blocks * lbsize / mstat.f_bsize;
420 
421 	/*
422 	 * The "total" fields count total resources in all layers,
423 	 * the "free" fields count only those resources which are
424 	 * free in the upper layer (since only the upper layer
425 	 * is writeable).
426 	 */
427 	sbp->f_blocks += mstat.f_blocks;
428 	sbp->f_bfree = mstat.f_bfree;
429 	sbp->f_bavail = mstat.f_bavail;
430 	sbp->f_files += mstat.f_files;
431 	sbp->f_ffree = mstat.f_ffree;
432 
433 	if (sbp != &mp->mnt_stat) {
434 		sbp->f_type = mp->mnt_vfc->vfc_typenum;
435 		bcopy(&mp->mnt_stat.f_fsid, &sbp->f_fsid, sizeof(sbp->f_fsid));
436 		bcopy(mp->mnt_stat.f_mntonname, sbp->f_mntonname, MNAMELEN);
437 		bcopy(mp->mnt_stat.f_mntfromname, sbp->f_mntfromname, MNAMELEN);
438 	}
439 	return (0);
440 }
441 
442 /*
443  * XXX - Assumes no data cached at union layer.
444  */
445 #define union_sync ((int (*) __P((struct mount *, int, struct ucred *, \
446 	    struct proc *)))nullop)
447 
448 #define union_fhtovp ((int (*) __P((struct mount *, struct fid *, \
449 	    struct mbuf *, struct vnode **, int *, struct ucred **)))eopnotsupp)
450 int union_init __P((struct vfsconf *));
451 #define union_quotactl ((int (*) __P((struct mount *, int, uid_t, caddr_t, \
452 	    struct proc *)))eopnotsupp)
453 #define union_sysctl ((int (*) __P((int *, u_int, void *, size_t *, void *, \
454 	    size_t, struct proc *)))eopnotsupp)
455 #define union_vget ((int (*) __P((struct mount *, ino_t, struct vnode **))) \
456 	    eopnotsupp)
457 #define union_vptofh ((int (*) __P((struct vnode *, struct fid *)))eopnotsupp)
458 
459 struct vfsops union_vfsops = {
460 	union_mount,
461 	union_start,
462 	union_unmount,
463 	union_root,
464 	union_quotactl,
465 	union_statfs,
466 	union_sync,
467 	union_vget,
468 	union_fhtovp,
469 	union_vptofh,
470 	union_init,
471 	union_sysctl,
472 };
473