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	1.3 (Berkeley) 02/01/94
12  */
13 
14 /*
15  * Null Layer
16  * (See union_vnops.c for a description of what this does.)
17  */
18 
19 #include <sys/param.h>
20 #include <sys/systm.h>
21 #include <sys/time.h>
22 #include <sys/types.h>
23 #include <sys/proc.h>
24 #include <sys/vnode.h>
25 #include <sys/mount.h>
26 #include <sys/namei.h>
27 #include <sys/malloc.h>
28 #include "union.h"
29 
30 /*
31  * Mount union filesystem
32  */
33 int
34 union_mount(mp, path, data, ndp, p)
35 	struct mount *mp;
36 	char *path;
37 	caddr_t data;
38 	struct nameidata *ndp;
39 	struct proc *p;
40 {
41 	int error = 0;
42 	struct union_args args;
43 	struct vnode *lowerrootvp;
44 	struct vnode *upperrootvp;
45 	struct union_mount *um;
46 	u_int size;
47 
48 #ifdef UNION_DIAGNOSTIC
49 	printf("union_mount(mp = %x)\n", mp);
50 #endif
51 
52 	/*
53 	 * Update is a no-op
54 	 */
55 	if (mp->mnt_flag & MNT_UPDATE)
56 		return (EOPNOTSUPP);
57 
58 	/*
59 	 * Get argument
60 	 */
61 	if (error = copyin(data, (caddr_t)&args, sizeof(struct union_args)))
62 		return (error);
63 
64 	lowerrootvp = mp->mnt_vnodecovered;
65 	VREF(lowerrootvp);
66 
67 	/*
68 	 * Find upper node
69 	 */
70 	NDINIT(ndp, LOOKUP, FOLLOW|WANTPARENT,
71 	       UIO_USERSPACE, args.target, p);
72 	if (error = namei(ndp)) {
73 		vrele(lowerrootvp);
74 		return (error);
75 	}
76 	upperrootvp = ndp->ni_vp;
77 	vrele(ndp->ni_dvp);
78 	ndp->ni_dvp = NULL;
79 
80 	if (upperrootvp->v_type != VDIR) {
81 		vrele(upperrootvp);
82 		return (EINVAL);
83 	}
84 
85 	um = (struct union_mount *) malloc(sizeof(struct union_mount),
86 				M_UFSMNT, M_WAITOK);	/* XXX */
87 
88 	/*
89 	 * Keep a held reference to the target vnodes.
90 	 * They are vrele'd in union_unmount.
91 	 */
92 	um->um_lowervp = lowerrootvp;
93 	um->um_uppervp = upperrootvp;
94 	/*
95 	 * Take a copy of the process's credentials.  This isn't
96 	 * quite right since the euid will always be zero and we
97 	 * want to get the "real" users credentials.  So fix up
98 	 * the uid field after taking the copy.
99 	 */
100 	um->um_cred = crdup(p->p_ucred);
101 	um->um_cred->cr_uid = p->p_cred->p_ruid;
102 
103 	if ((lowerrootvp->v_mount->mnt_flag & MNT_LOCAL) ||
104 	    (upperrootvp->v_mount->mnt_flag & MNT_LOCAL))
105 		mp->mnt_flag |= MNT_LOCAL;
106 	mp->mnt_data = (qaddr_t) um;
107 	getnewfsid(mp, MOUNT_UNION);
108 
109 	(void) copyinstr(path, mp->mnt_stat.f_mntonname, MNAMELEN - 1, &size);
110 	bzero(mp->mnt_stat.f_mntonname + size, MNAMELEN - size);
111 	(void) copyinstr(args.target, mp->mnt_stat.f_mntfromname, MNAMELEN - 1,
112 	    &size);
113 	bzero(mp->mnt_stat.f_mntfromname + size, MNAMELEN - size);
114 #ifdef UNION_DIAGNOSTIC
115 	printf("union_mount: upper %s, lower at %s\n",
116 		mp->mnt_stat.f_mntfromname, mp->mnt_stat.f_mntonname);
117 #endif
118 	return (0);
119 }
120 
121 /*
122  * VFS start.  Nothing needed here - the start routine
123  * on the underlying filesystem(s) will have been called
124  * when that filesystem was mounted.
125  */
126 int
127 union_start(mp, flags, p)
128 	struct mount *mp;
129 	int flags;
130 	struct proc *p;
131 {
132 
133 	return (0);
134 }
135 
136 /*
137  * Free reference to union layer
138  */
139 int
140 union_unmount(mp, mntflags, p)
141 	struct mount *mp;
142 	int mntflags;
143 	struct proc *p;
144 {
145 	struct union_mount *um = MOUNTTOUNIONMOUNT(mp);
146 	struct vnode *um_rootvp;
147 	int error;
148 	int flags = 0;
149 	extern int doforce;
150 
151 #ifdef UNION_DIAGNOSTIC
152 	printf("union_unmount(mp = %x)\n", mp);
153 #endif
154 
155 	if (mntflags & MNT_FORCE) {
156 		/* union can never be rootfs so don't check for it */
157 		if (!doforce)
158 			return (EINVAL);
159 		flags |= FORCECLOSE;
160 	}
161 
162 	if (error = union_root(mp, &um_rootvp))
163 		return (error);
164 	if (um_rootvp->v_usecount > 1) {
165 		vput(um_rootvp);
166 		return (EBUSY);
167 	}
168 	if (error = vflush(mp, um_rootvp, flags)) {
169 		vput(um_rootvp);
170 		return (error);
171 	}
172 
173 #ifdef UNION_DIAGNOSTIC
174 	vprint("alias root of lower", um_rootvp);
175 #endif
176 	/*
177 	 * Discard references to upper and lower target vnodes.
178 	 */
179 	vrele(um->um_lowervp);
180 	vrele(um->um_uppervp);
181 	crfree(um->um_cred);
182 	/*
183 	 * Release reference on underlying root vnode
184 	 */
185 	vput(um_rootvp);
186 	/*
187 	 * And blow it away for future re-use
188 	 */
189 	vgone(um_rootvp);
190 	/*
191 	 * Finally, throw away the union_mount structure
192 	 */
193 	free(mp->mnt_data, M_UFSMNT);	/* XXX */
194 	mp->mnt_data = 0;
195 	return 0;
196 }
197 
198 int
199 union_root(mp, vpp)
200 	struct mount *mp;
201 	struct vnode **vpp;
202 {
203 	struct union_mount *um = MOUNTTOUNIONMOUNT(mp);
204 	int error;
205 
206 #ifdef UNION_DIAGNOSTIC
207 	printf("union_root(mp = %x, lvp = %x, uvp = %x)\n", mp,
208 			um->um_lowervp,
209 			um->um_uppervp);
210 #endif
211 
212 	/*
213 	 * Return locked reference to root.
214 	 */
215 	VREF(um->um_uppervp);
216 	VREF(um->um_lowervp);
217 	error = union_allocvp(vpp, mp, (struct vnode *) 0,
218 			      (struct componentname *) 0,
219 			      um->um_uppervp,
220 			      um->um_lowervp);
221 
222 	if (error) {
223 		vrele(um->um_uppervp);
224 		vrele(um->um_lowervp);
225 	} else {
226 		(*vpp)->v_flag |= VROOT;
227 	}
228 
229 	return (error);
230 }
231 
232 int
233 union_quotactl(mp, cmd, uid, arg, p)
234 	struct mount *mp;
235 	int cmd;
236 	uid_t uid;
237 	caddr_t arg;
238 	struct proc *p;
239 {
240 
241 	return (EOPNOTSUPP);
242 }
243 
244 int
245 union_statfs(mp, sbp, p)
246 	struct mount *mp;
247 	struct statfs *sbp;
248 	struct proc *p;
249 {
250 	int error;
251 	struct union_mount *um = MOUNTTOUNIONMOUNT(mp);
252 	struct statfs mstat;
253 	int lbsize;
254 
255 #ifdef UNION_DIAGNOSTIC
256 	printf("union_statfs(mp = %x, lvp = %x, uvp = %x)\n", mp,
257 			um->um_lowervp,
258 	       		um->um_uppervp);
259 #endif
260 
261 	bzero(&mstat, sizeof(mstat));
262 
263 	error = VFS_STATFS(um->um_lowervp->v_mount, &mstat, p);
264 	if (error)
265 		return (error);
266 
267 	/* now copy across the "interesting" information and fake the rest */
268 #if 0
269 	sbp->f_type = mstat.f_type;
270 	sbp->f_flags = mstat.f_flags;
271 	sbp->f_bsize = mstat.f_bsize;
272 	sbp->f_iosize = mstat.f_iosize;
273 #endif
274 	lbsize = mstat.f_bsize;
275 	sbp->f_blocks = mstat.f_blocks;
276 	sbp->f_bfree = mstat.f_bfree;
277 	sbp->f_bavail = mstat.f_bavail;
278 	sbp->f_files = mstat.f_files;
279 	sbp->f_ffree = mstat.f_ffree;
280 
281 	error = VFS_STATFS(um->um_uppervp->v_mount, &mstat, p);
282 	if (error)
283 		return (error);
284 
285 	sbp->f_type = mstat.f_type;
286 	sbp->f_flags = mstat.f_flags;
287 	sbp->f_bsize = mstat.f_bsize;
288 	sbp->f_iosize = mstat.f_iosize;
289 
290 	/*
291 	 * if the lower and upper blocksizes differ, then frig the
292 	 * block counts so that the sizes reported by df make some
293 	 * kind of sense.  none of this makes sense though.
294 	 */
295 
296 	if (mstat.f_bsize != lbsize) {
297 		sbp->f_blocks = sbp->f_blocks * lbsize / mstat.f_bsize;
298 		sbp->f_bfree = sbp->f_bfree * lbsize / mstat.f_bsize;
299 		sbp->f_bavail = sbp->f_bavail * lbsize / mstat.f_bsize;
300 	}
301 	sbp->f_blocks += mstat.f_blocks;
302 	sbp->f_bfree += mstat.f_bfree;
303 	sbp->f_bavail += mstat.f_bavail;
304 	sbp->f_files += mstat.f_files;
305 	sbp->f_ffree += mstat.f_ffree;
306 
307 	if (sbp != &mp->mnt_stat) {
308 		bcopy(&mp->mnt_stat.f_fsid, &sbp->f_fsid, sizeof(sbp->f_fsid));
309 		bcopy(mp->mnt_stat.f_mntonname, sbp->f_mntonname, MNAMELEN);
310 		bcopy(mp->mnt_stat.f_mntfromname, sbp->f_mntfromname, MNAMELEN);
311 	}
312 	return (0);
313 }
314 
315 int
316 union_sync(mp, waitfor, cred, p)
317 	struct mount *mp;
318 	int waitfor;
319 	struct ucred *cred;
320 	struct proc *p;
321 {
322 
323 	/*
324 	 * XXX - Assumes no data cached at union layer.
325 	 */
326 	return (0);
327 }
328 
329 int
330 union_vget(mp, ino, vpp)
331 	struct mount *mp;
332 	ino_t ino;
333 	struct vnode **vpp;
334 {
335 
336 	return (EOPNOTSUPP);
337 }
338 
339 int
340 union_fhtovp(mp, fidp, nam, vpp, exflagsp, credanonp)
341 	struct mount *mp;
342 	struct fid *fidp;
343 	struct mbuf *nam;
344 	struct vnode **vpp;
345 	int *exflagsp;
346 	struct ucred **credanonp;
347 {
348 
349 	return (EOPNOTSUPP);
350 }
351 
352 int
353 union_vptofh(vp, fhp)
354 	struct vnode *vp;
355 	struct fid *fhp;
356 {
357 
358 	return (EOPNOTSUPP);
359 }
360 
361 int union_init __P((void));
362 
363 struct vfsops union_vfsops = {
364 	union_mount,
365 	union_start,
366 	union_unmount,
367 	union_root,
368 	union_quotactl,
369 	union_statfs,
370 	union_sync,
371 	union_vget,
372 	union_fhtovp,
373 	union_vptofh,
374 	union_init,
375 };
376