union_statfs should only return free space information
[unix-history] / usr / src / sys / miscfs / union / union_vfsops.c
CommitLineData
a1fa407d
JSP
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 *
8583490d 11 * @(#)union_vfsops.c 8.14 (Berkeley) %G%
a1fa407d
JSP
12 */
13
14/*
81be6ee0 15 * Union Layer
a1fa407d
JSP
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>
3b293975 27#include <sys/filedesc.h>
58572fc0 28#include <sys/queue.h>
2e1ae99e 29#include <miscfs/union/union.h>
a1fa407d
JSP
30
31/*
32 * Mount union filesystem
33 */
34int
35union_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;
692a90df
JSP
44 struct vnode *lowerrootvp = NULLVP;
45 struct vnode *upperrootvp = NULLVP;
88edbcd4 46 struct union_mount *um = 0;
692a90df 47 struct ucred *cred = 0;
9f7857db 48 struct ucred *scred;
c1b204a8 49 struct vattr va;
692a90df
JSP
50 char *cp;
51 int len;
a1fa407d
JSP
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 */
81be6ee0
JSP
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 */
692a90df
JSP
67 error = EOPNOTSUPP;
68 goto bad;
81be6ee0 69 }
a1fa407d
JSP
70
71 /*
72 * Get argument
73 */
74 if (error = copyin(data, (caddr_t)&args, sizeof(struct union_args)))
692a90df 75 goto bad;
a1fa407d
JSP
76
77 lowerrootvp = mp->mnt_vnodecovered;
78 VREF(lowerrootvp);
79
80 /*
88edbcd4 81 * Find upper node.
a1fa407d
JSP
82 */
83 NDINIT(ndp, LOOKUP, FOLLOW|WANTPARENT,
84 UIO_USERSPACE, args.target, p);
9f7857db 85
692a90df
JSP
86 if (error = namei(ndp))
87 goto bad;
88
a1fa407d
JSP
89 upperrootvp = ndp->ni_vp;
90 vrele(ndp->ni_dvp);
91 ndp->ni_dvp = NULL;
92
93 if (upperrootvp->v_type != VDIR) {
692a90df
JSP
94 error = EINVAL;
95 goto bad;
a1fa407d
JSP
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.
692a90df
JSP
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.
a1fa407d 108 */
692a90df 109
c1b204a8
JSP
110 um->um_op = args.mntflags & UNMNT_OPMASK;
111 switch (um->um_op) {
692a90df
JSP
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
88edbcd4
JSP
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);
3b293975 146 um->um_cmode = UN_DIRMODE &~ p->p_fd->fd_cmask;
a1fa407d 147
3b293975
JSP
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 */
c1b204a8
JSP
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 }
3b293975 163
81be6ee0
JSP
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 */
692a90df 171 mp->mnt_flag |= (um->um_uppervp->v_mount->mnt_flag & MNT_RDONLY);
bc0d9070 172
a1fa407d
JSP
173 mp->mnt_data = (qaddr_t) um;
174 getnewfsid(mp, MOUNT_UNION);
175
176 (void) copyinstr(path, mp->mnt_stat.f_mntonname, MNAMELEN - 1, &size);
177 bzero(mp->mnt_stat.f_mntonname + size, MNAMELEN - size);
692a90df 178
c1b204a8 179 switch (um->um_op) {
692a90df 180 case UNMNT_ABOVE:
178b36c0 181 cp = "<above>:";
692a90df
JSP
182 break;
183 case UNMNT_BELOW:
178b36c0 184 cp = "<below>:";
692a90df
JSP
185 break;
186 case UNMNT_REPLACE:
c80f414e 187 cp = "";
692a90df
JSP
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
a1fa407d 199#ifdef UNION_DIAGNOSTIC
692a90df 200 printf("union_mount: from %s, on %s\n",
a1fa407d
JSP
201 mp->mnt_stat.f_mntfromname, mp->mnt_stat.f_mntonname);
202#endif
203 return (0);
692a90df
JSP
204
205bad:
88edbcd4
JSP
206 if (um)
207 free(um, M_UFSMNT);
692a90df
JSP
208 if (cred)
209 crfree(cred);
210 if (upperrootvp)
211 vrele(upperrootvp);
212 if (lowerrootvp)
213 vrele(lowerrootvp);
214 return (error);
a1fa407d
JSP
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 */
222int
223union_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 */
235int
236union_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;
9192c54f 244 int freeing;
a1fa407d
JSP
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);
9192c54f
JSP
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 */
01dac67e
JSP
290 if (um_rootvp->v_usecount > 1) {
291 vput(um_rootvp);
a1fa407d 292 return (EBUSY);
01dac67e 293 }
a1fa407d
JSP
294
295#ifdef UNION_DIAGNOSTIC
9192c54f 296 vprint("union root", um_rootvp);
a1fa407d
JSP
297#endif
298 /*
299 * Discard references to upper and lower target vnodes.
300 */
692a90df
JSP
301 if (um->um_lowervp)
302 vrele(um->um_lowervp);
a1fa407d
JSP
303 vrele(um->um_uppervp);
304 crfree(um->um_cred);
305 /*
306 * Release reference on underlying root vnode
307 */
01dac67e 308 vput(um_rootvp);
a1fa407d
JSP
309 /*
310 * And blow it away for future re-use
311 */
312 vgone(um_rootvp);
313 /*
314 * Finally, throw away the union_mount structure
315 */
316 free(mp->mnt_data, M_UFSMNT); /* XXX */
317 mp->mnt_data = 0;
3b293975 318 return (0);
a1fa407d
JSP
319}
320
321int
322union_root(mp, vpp)
323 struct mount *mp;
324 struct vnode **vpp;
325{
326 struct union_mount *um = MOUNTTOUNIONMOUNT(mp);
327 int error;
c1b204a8 328 int loselock;
a1fa407d 329
a1fa407d
JSP
330 /*
331 * Return locked reference to root.
332 */
01dac67e 333 VREF(um->um_uppervp);
c1b204a8
JSP
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 }
692a90df
JSP
341 if (um->um_lowervp)
342 VREF(um->um_lowervp);
79f80c71
JSP
343 error = union_allocvp(vpp, mp,
344 (struct vnode *) 0,
345 (struct vnode *) 0,
a1fa407d
JSP
346 (struct componentname *) 0,
347 um->um_uppervp,
825ff80f
JSP
348 um->um_lowervp,
349 1);
01dac67e
JSP
350
351 if (error) {
c1b204a8
JSP
352 if (!loselock)
353 VOP_UNLOCK(um->um_uppervp);
01dac67e 354 vrele(um->um_uppervp);
692a90df
JSP
355 if (um->um_lowervp)
356 vrele(um->um_lowervp);
01dac67e 357 } else {
c1b204a8
JSP
358 if (loselock)
359 VTOUNION(*vpp)->un_flags &= ~UN_ULOCK;
01dac67e 360 }
a1fa407d
JSP
361
362 return (error);
363}
364
365int
366union_quotactl(mp, cmd, uid, arg, p)
367 struct mount *mp;
368 int cmd;
369 uid_t uid;
370 caddr_t arg;
371 struct proc *p;
372{
373
374 return (EOPNOTSUPP);
375}
376
377int
378union_statfs(mp, sbp, p)
379 struct mount *mp;
380 struct statfs *sbp;
381 struct proc *p;
382{
383 int error;
384 struct union_mount *um = MOUNTTOUNIONMOUNT(mp);
385 struct statfs mstat;
386 int lbsize;
387
388#ifdef UNION_DIAGNOSTIC
389 printf("union_statfs(mp = %x, lvp = %x, uvp = %x)\n", mp,
390 um->um_lowervp,
391 um->um_uppervp);
392#endif
393
394 bzero(&mstat, sizeof(mstat));
395
692a90df
JSP
396 if (um->um_lowervp) {
397 error = VFS_STATFS(um->um_lowervp->v_mount, &mstat, p);
398 if (error)
399 return (error);
400 }
a1fa407d
JSP
401
402 /* now copy across the "interesting" information and fake the rest */
403#if 0
404 sbp->f_type = mstat.f_type;
405 sbp->f_flags = mstat.f_flags;
406 sbp->f_bsize = mstat.f_bsize;
407 sbp->f_iosize = mstat.f_iosize;
408#endif
409 lbsize = mstat.f_bsize;
410 sbp->f_blocks = mstat.f_blocks;
411 sbp->f_bfree = mstat.f_bfree;
412 sbp->f_bavail = mstat.f_bavail;
413 sbp->f_files = mstat.f_files;
414 sbp->f_ffree = mstat.f_ffree;
415
416 error = VFS_STATFS(um->um_uppervp->v_mount, &mstat, p);
417 if (error)
418 return (error);
419
84d17267 420 sbp->f_type = MOUNT_UNION;
a1fa407d
JSP
421 sbp->f_flags = mstat.f_flags;
422 sbp->f_bsize = mstat.f_bsize;
423 sbp->f_iosize = mstat.f_iosize;
424
425 /*
426 * if the lower and upper blocksizes differ, then frig the
427 * block counts so that the sizes reported by df make some
428 * kind of sense. none of this makes sense though.
429 */
430
8583490d 431 if (mstat.f_bsize != lbsize)
a1fa407d 432 sbp->f_blocks = sbp->f_blocks * lbsize / mstat.f_bsize;
8583490d
JSP
433
434 /*
435 * The "total" fields count total resources in all layers,
436 * the "free" fields count only those resources which are
437 * free in the upper layer (since only the upper layer
438 * is writeable).
439 */
a1fa407d 440 sbp->f_blocks += mstat.f_blocks;
8583490d
JSP
441 sbp->f_bfree = mstat.f_bfree;
442 sbp->f_bavail = mstat.f_bavail;
a1fa407d 443 sbp->f_files += mstat.f_files;
8583490d 444 sbp->f_ffree = mstat.f_ffree;
a1fa407d
JSP
445
446 if (sbp != &mp->mnt_stat) {
447 bcopy(&mp->mnt_stat.f_fsid, &sbp->f_fsid, sizeof(sbp->f_fsid));
448 bcopy(mp->mnt_stat.f_mntonname, sbp->f_mntonname, MNAMELEN);
449 bcopy(mp->mnt_stat.f_mntfromname, sbp->f_mntfromname, MNAMELEN);
450 }
451 return (0);
452}
453
454int
455union_sync(mp, waitfor, cred, p)
456 struct mount *mp;
457 int waitfor;
458 struct ucred *cred;
459 struct proc *p;
460{
461
462 /*
463 * XXX - Assumes no data cached at union layer.
464 */
465 return (0);
466}
467
468int
469union_vget(mp, ino, vpp)
470 struct mount *mp;
471 ino_t ino;
472 struct vnode **vpp;
473{
474
475 return (EOPNOTSUPP);
476}
477
478int
479union_fhtovp(mp, fidp, nam, vpp, exflagsp, credanonp)
480 struct mount *mp;
481 struct fid *fidp;
482 struct mbuf *nam;
483 struct vnode **vpp;
484 int *exflagsp;
485 struct ucred **credanonp;
486{
487
488 return (EOPNOTSUPP);
489}
490
491int
492union_vptofh(vp, fhp)
493 struct vnode *vp;
494 struct fid *fhp;
495{
496
497 return (EOPNOTSUPP);
498}
499
500int union_init __P((void));
501
502struct vfsops union_vfsops = {
503 union_mount,
504 union_start,
505 union_unmount,
506 union_root,
507 union_quotactl,
508 union_statfs,
509 union_sync,
510 union_vget,
511 union_fhtovp,
512 union_vptofh,
513 union_init,
514};