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
union_mount(mp,path,data,ndp,p)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
union_start(mp,flags,p)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
union_unmount(mp,mntflags,p)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
union_root(mp,vpp)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
union_statfs(mp,sbp,p)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