1 /*
2  * Copyright (c) 1994, 1995 The Regents of the University of California.
3  * Copyright (c) 1994, 1995 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.20 (Berkeley) 05/20/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 
247 #ifdef UNION_DIAGNOSTIC
248 	printf("union_unmount(mp = %x)\n", mp);
249 #endif
250 
251 	if (mntflags & MNT_FORCE)
252 		flags |= FORCECLOSE;
253 
254 	if (error = union_root(mp, &um_rootvp))
255 		return (error);
256 
257 	/*
258 	 * Keep flushing vnodes from the mount list.
259 	 * This is needed because of the un_pvp held
260 	 * reference to the parent vnode.
261 	 * If more vnodes have been freed on a given pass,
262 	 * the try again.  The loop will iterate at most
263 	 * (d) times, where (d) is the maximum tree depth
264 	 * in the filesystem.
265 	 */
266 	for (freeing = 0; vflush(mp, um_rootvp, flags) != 0;) {
267 		struct vnode *vp;
268 		int n;
269 
270 		/* count #vnodes held on mount list */
271 		for (n = 0, vp = mp->mnt_vnodelist.lh_first;
272 				vp != NULLVP;
273 				vp = vp->v_mntvnodes.le_next)
274 			n++;
275 
276 		/* if this is unchanged then stop */
277 		if (n == freeing)
278 			break;
279 
280 		/* otherwise try once more time */
281 		freeing = n;
282 	}
283 
284 	/* At this point the root vnode should have a single reference */
285 	if (um_rootvp->v_usecount > 1) {
286 		vput(um_rootvp);
287 		return (EBUSY);
288 	}
289 
290 #ifdef UNION_DIAGNOSTIC
291 	vprint("union root", um_rootvp);
292 #endif
293 	/*
294 	 * Discard references to upper and lower target vnodes.
295 	 */
296 	if (um->um_lowervp)
297 		vrele(um->um_lowervp);
298 	vrele(um->um_uppervp);
299 	crfree(um->um_cred);
300 	/*
301 	 * Release reference on underlying root vnode
302 	 */
303 	vput(um_rootvp);
304 	/*
305 	 * And blow it away for future re-use
306 	 */
307 	vgone(um_rootvp);
308 	/*
309 	 * Finally, throw away the union_mount structure
310 	 */
311 	free(mp->mnt_data, M_UFSMNT);	/* XXX */
312 	mp->mnt_data = 0;
313 	return (0);
314 }
315 
316 int
317 union_root(mp, vpp)
318 	struct mount *mp;
319 	struct vnode **vpp;
320 {
321 	struct proc *p = curproc;	/* XXX */
322 	struct union_mount *um = MOUNTTOUNIONMOUNT(mp);
323 	int error;
324 	int loselock;
325 
326 	/*
327 	 * Return locked reference to root.
328 	 */
329 	VREF(um->um_uppervp);
330 	if ((um->um_op == UNMNT_BELOW) &&
331 	     VOP_ISLOCKED(um->um_uppervp)) {
332 		loselock = 1;
333 	} else {
334 		vn_lock(um->um_uppervp, LK_EXCLUSIVE | LK_RETRY, p);
335 		loselock = 0;
336 	}
337 	if (um->um_lowervp)
338 		VREF(um->um_lowervp);
339 	error = union_allocvp(vpp, mp,
340 			      (struct vnode *) 0,
341 			      (struct vnode *) 0,
342 			      (struct componentname *) 0,
343 			      um->um_uppervp,
344 			      um->um_lowervp,
345 			      1);
346 
347 	if (error) {
348 		if (loselock)
349 			vrele(um->um_uppervp);
350 		else
351 			vput(um->um_uppervp);
352 		if (um->um_lowervp)
353 			vrele(um->um_lowervp);
354 	} else {
355 		if (loselock)
356 			VTOUNION(*vpp)->un_flags &= ~UN_ULOCK;
357 	}
358 
359 	return (error);
360 }
361 
362 int
363 union_statfs(mp, sbp, p)
364 	struct mount *mp;
365 	struct statfs *sbp;
366 	struct proc *p;
367 {
368 	int error;
369 	struct union_mount *um = MOUNTTOUNIONMOUNT(mp);
370 	struct statfs mstat;
371 	int lbsize;
372 
373 #ifdef UNION_DIAGNOSTIC
374 	printf("union_statfs(mp = %x, lvp = %x, uvp = %x)\n", mp,
375 			um->um_lowervp,
376 	       		um->um_uppervp);
377 #endif
378 
379 	bzero(&mstat, sizeof(mstat));
380 
381 	if (um->um_lowervp) {
382 		error = VFS_STATFS(um->um_lowervp->v_mount, &mstat, p);
383 		if (error)
384 			return (error);
385 	}
386 
387 	/* now copy across the "interesting" information and fake the rest */
388 #if 0
389 	sbp->f_type = mstat.f_type;
390 	sbp->f_flags = mstat.f_flags;
391 	sbp->f_bsize = mstat.f_bsize;
392 	sbp->f_iosize = mstat.f_iosize;
393 #endif
394 	lbsize = mstat.f_bsize;
395 	sbp->f_blocks = mstat.f_blocks;
396 	sbp->f_bfree = mstat.f_bfree;
397 	sbp->f_bavail = mstat.f_bavail;
398 	sbp->f_files = mstat.f_files;
399 	sbp->f_ffree = mstat.f_ffree;
400 
401 	error = VFS_STATFS(um->um_uppervp->v_mount, &mstat, p);
402 	if (error)
403 		return (error);
404 
405 	sbp->f_flags = mstat.f_flags;
406 	sbp->f_bsize = mstat.f_bsize;
407 	sbp->f_iosize = mstat.f_iosize;
408 
409 	/*
410 	 * if the lower and upper blocksizes differ, then frig the
411 	 * block counts so that the sizes reported by df make some
412 	 * kind of sense.  none of this makes sense though.
413 	 */
414 
415 	if (mstat.f_bsize != lbsize)
416 		sbp->f_blocks = sbp->f_blocks * lbsize / mstat.f_bsize;
417 
418 	/*
419 	 * The "total" fields count total resources in all layers,
420 	 * the "free" fields count only those resources which are
421 	 * free in the upper layer (since only the upper layer
422 	 * is writeable).
423 	 */
424 	sbp->f_blocks += mstat.f_blocks;
425 	sbp->f_bfree = mstat.f_bfree;
426 	sbp->f_bavail = mstat.f_bavail;
427 	sbp->f_files += mstat.f_files;
428 	sbp->f_ffree = mstat.f_ffree;
429 
430 	if (sbp != &mp->mnt_stat) {
431 		sbp->f_type = mp->mnt_vfc->vfc_typenum;
432 		bcopy(&mp->mnt_stat.f_fsid, &sbp->f_fsid, sizeof(sbp->f_fsid));
433 		bcopy(mp->mnt_stat.f_mntonname, sbp->f_mntonname, MNAMELEN);
434 		bcopy(mp->mnt_stat.f_mntfromname, sbp->f_mntfromname, MNAMELEN);
435 	}
436 	return (0);
437 }
438 
439 /*
440  * XXX - Assumes no data cached at union layer.
441  */
442 #define union_sync ((int (*) __P((struct mount *, int, struct ucred *, \
443 	    struct proc *)))nullop)
444 
445 #define union_fhtovp ((int (*) __P((struct mount *, struct fid *, \
446 	    struct mbuf *, struct vnode **, int *, struct ucred **)))eopnotsupp)
447 int union_init __P((struct vfsconf *));
448 #define union_quotactl ((int (*) __P((struct mount *, int, uid_t, caddr_t, \
449 	    struct proc *)))eopnotsupp)
450 #define union_sysctl ((int (*) __P((int *, u_int, void *, size_t *, void *, \
451 	    size_t, struct proc *)))eopnotsupp)
452 #define union_vget ((int (*) __P((struct mount *, ino_t, struct vnode **))) \
453 	    eopnotsupp)
454 #define union_vptofh ((int (*) __P((struct vnode *, struct fid *)))eopnotsupp)
455 
456 struct vfsops union_vfsops = {
457 	union_mount,
458 	union_start,
459 	union_unmount,
460 	union_root,
461 	union_quotactl,
462 	union_statfs,
463 	union_sync,
464 	union_vget,
465 	union_fhtovp,
466 	union_vptofh,
467 	union_init,
468 	union_sysctl,
469 };
470