BSD 4_4_Lite2 release
[unix-history] / usr / src / sys / miscfs / union / union_vfsops.c
CommitLineData
a1fa407d 1/*
e2640548
JSP
2 * Copyright (c) 1994, 1995 The Regents of the University of California.
3 * Copyright (c) 1994, 1995 Jan-Simon Pendry.
a1fa407d
JSP
4 * All rights reserved.
5 *
6 * This code is derived from software donated to Berkeley by
7 * Jan-Simon Pendry.
8 *
ed554bc5
C
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 * 1. Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in the
16 * documentation and/or other materials provided with the distribution.
17 * 3. All advertising materials mentioning features or use of this software
18 * must display the following acknowledgement:
19 * This product includes software developed by the University of
20 * California, Berkeley and its contributors.
21 * 4. Neither the name of the University nor the names of its contributors
22 * may be used to endorse or promote products derived from this software
23 * without specific prior written permission.
a1fa407d 24 *
ed554bc5
C
25 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
26 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
27 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
28 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
29 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
30 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
31 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
32 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
33 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
34 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
35 * SUCH DAMAGE.
36 *
fd88f5c5 37 * @(#)union_vfsops.c 8.20 (Berkeley) 5/20/95
a1fa407d
JSP
38 */
39
40/*
81be6ee0 41 * Union Layer
a1fa407d
JSP
42 */
43
44#include <sys/param.h>
45#include <sys/systm.h>
46#include <sys/time.h>
47#include <sys/types.h>
48#include <sys/proc.h>
49#include <sys/vnode.h>
50#include <sys/mount.h>
51#include <sys/namei.h>
52#include <sys/malloc.h>
3b293975 53#include <sys/filedesc.h>
58572fc0 54#include <sys/queue.h>
2e1ae99e 55#include <miscfs/union/union.h>
a1fa407d
JSP
56
57/*
58 * Mount union filesystem
59 */
60int
61union_mount(mp, path, data, ndp, p)
62 struct mount *mp;
63 char *path;
64 caddr_t data;
65 struct nameidata *ndp;
66 struct proc *p;
67{
68 int error = 0;
69 struct union_args args;
692a90df
JSP
70 struct vnode *lowerrootvp = NULLVP;
71 struct vnode *upperrootvp = NULLVP;
88edbcd4 72 struct union_mount *um = 0;
692a90df 73 struct ucred *cred = 0;
9f7857db 74 struct ucred *scred;
c1b204a8 75 struct vattr va;
692a90df
JSP
76 char *cp;
77 int len;
a1fa407d
JSP
78 u_int size;
79
80#ifdef UNION_DIAGNOSTIC
81 printf("union_mount(mp = %x)\n", mp);
82#endif
83
84 /*
85 * Update is a no-op
86 */
81be6ee0
JSP
87 if (mp->mnt_flag & MNT_UPDATE) {
88 /*
89 * Need to provide.
90 * 1. a way to convert between rdonly and rdwr mounts.
91 * 2. support for nfs exports.
92 */
692a90df
JSP
93 error = EOPNOTSUPP;
94 goto bad;
81be6ee0 95 }
a1fa407d
JSP
96
97 /*
98 * Get argument
99 */
100 if (error = copyin(data, (caddr_t)&args, sizeof(struct union_args)))
692a90df 101 goto bad;
a1fa407d
JSP
102
103 lowerrootvp = mp->mnt_vnodecovered;
104 VREF(lowerrootvp);
105
106 /*
88edbcd4 107 * Find upper node.
a1fa407d
JSP
108 */
109 NDINIT(ndp, LOOKUP, FOLLOW|WANTPARENT,
110 UIO_USERSPACE, args.target, p);
9f7857db 111
692a90df
JSP
112 if (error = namei(ndp))
113 goto bad;
114
a1fa407d
JSP
115 upperrootvp = ndp->ni_vp;
116 vrele(ndp->ni_dvp);
117 ndp->ni_dvp = NULL;
118
119 if (upperrootvp->v_type != VDIR) {
692a90df
JSP
120 error = EINVAL;
121 goto bad;
a1fa407d
JSP
122 }
123
124 um = (struct union_mount *) malloc(sizeof(struct union_mount),
125 M_UFSMNT, M_WAITOK); /* XXX */
126
127 /*
128 * Keep a held reference to the target vnodes.
129 * They are vrele'd in union_unmount.
692a90df
JSP
130 *
131 * Depending on the _BELOW flag, the filesystems are
132 * viewed in a different order. In effect, this is the
133 * same as providing a mount under option to the mount syscall.
a1fa407d 134 */
692a90df 135
c1b204a8
JSP
136 um->um_op = args.mntflags & UNMNT_OPMASK;
137 switch (um->um_op) {
692a90df
JSP
138 case UNMNT_ABOVE:
139 um->um_lowervp = lowerrootvp;
140 um->um_uppervp = upperrootvp;
141 break;
142
143 case UNMNT_BELOW:
144 um->um_lowervp = upperrootvp;
145 um->um_uppervp = lowerrootvp;
146 break;
147
148 case UNMNT_REPLACE:
149 vrele(lowerrootvp);
150 lowerrootvp = NULLVP;
151 um->um_uppervp = upperrootvp;
152 um->um_lowervp = lowerrootvp;
153 break;
154
155 default:
156 error = EINVAL;
157 goto bad;
158 }
159
88edbcd4
JSP
160 /*
161 * Unless the mount is readonly, ensure that the top layer
162 * supports whiteout operations
163 */
164 if ((mp->mnt_flag & MNT_RDONLY) == 0) {
165 error = VOP_WHITEOUT(um->um_uppervp, (struct componentname *) 0, LOOKUP);
166 if (error)
167 goto bad;
168 }
169
170 um->um_cred = p->p_ucred;
171 crhold(um->um_cred);
3b293975 172 um->um_cmode = UN_DIRMODE &~ p->p_fd->fd_cmask;
a1fa407d 173
3b293975
JSP
174 /*
175 * Depending on what you think the MNT_LOCAL flag might mean,
176 * you may want the && to be || on the conditional below.
177 * At the moment it has been defined that the filesystem is
178 * only local if it is all local, ie the MNT_LOCAL flag implies
179 * that the entire namespace is local. If you think the MNT_LOCAL
180 * flag implies that some of the files might be stored locally
181 * then you will want to change the conditional.
182 */
c1b204a8
JSP
183 if (um->um_op == UNMNT_ABOVE) {
184 if (((um->um_lowervp == NULLVP) ||
185 (um->um_lowervp->v_mount->mnt_flag & MNT_LOCAL)) &&
186 (um->um_uppervp->v_mount->mnt_flag & MNT_LOCAL))
187 mp->mnt_flag |= MNT_LOCAL;
188 }
3b293975 189
81be6ee0
JSP
190 /*
191 * Copy in the upper layer's RDONLY flag. This is for the benefit
192 * of lookup() which explicitly checks the flag, rather than asking
193 * the filesystem for it's own opinion. This means, that an update
194 * mount of the underlying filesystem to go from rdonly to rdwr
195 * will leave the unioned view as read-only.
196 */
692a90df 197 mp->mnt_flag |= (um->um_uppervp->v_mount->mnt_flag & MNT_RDONLY);
bc0d9070 198
a1fa407d 199 mp->mnt_data = (qaddr_t) um;
faa82b58 200 vfs_getnewfsid(mp);
a1fa407d
JSP
201
202 (void) copyinstr(path, mp->mnt_stat.f_mntonname, MNAMELEN - 1, &size);
203 bzero(mp->mnt_stat.f_mntonname + size, MNAMELEN - size);
692a90df 204
c1b204a8 205 switch (um->um_op) {
692a90df 206 case UNMNT_ABOVE:
178b36c0 207 cp = "<above>:";
692a90df
JSP
208 break;
209 case UNMNT_BELOW:
178b36c0 210 cp = "<below>:";
692a90df
JSP
211 break;
212 case UNMNT_REPLACE:
c80f414e 213 cp = "";
692a90df
JSP
214 break;
215 }
216 len = strlen(cp);
217 bcopy(cp, mp->mnt_stat.f_mntfromname, len);
218
219 cp = mp->mnt_stat.f_mntfromname + len;
220 len = MNAMELEN - len;
221
222 (void) copyinstr(args.target, cp, len - 1, &size);
223 bzero(cp + size, len - size);
224
a1fa407d 225#ifdef UNION_DIAGNOSTIC
692a90df 226 printf("union_mount: from %s, on %s\n",
a1fa407d
JSP
227 mp->mnt_stat.f_mntfromname, mp->mnt_stat.f_mntonname);
228#endif
229 return (0);
692a90df
JSP
230
231bad:
88edbcd4
JSP
232 if (um)
233 free(um, M_UFSMNT);
692a90df
JSP
234 if (cred)
235 crfree(cred);
236 if (upperrootvp)
237 vrele(upperrootvp);
238 if (lowerrootvp)
239 vrele(lowerrootvp);
240 return (error);
a1fa407d
JSP
241}
242
243/*
244 * VFS start. Nothing needed here - the start routine
245 * on the underlying filesystem(s) will have been called
246 * when that filesystem was mounted.
247 */
248int
249union_start(mp, flags, p)
250 struct mount *mp;
251 int flags;
252 struct proc *p;
253{
254
255 return (0);
256}
257
258/*
259 * Free reference to union layer
260 */
261int
262union_unmount(mp, mntflags, p)
263 struct mount *mp;
264 int mntflags;
265 struct proc *p;
266{
267 struct union_mount *um = MOUNTTOUNIONMOUNT(mp);
268 struct vnode *um_rootvp;
269 int error;
9192c54f 270 int freeing;
a1fa407d 271 int flags = 0;
a1fa407d
JSP
272
273#ifdef UNION_DIAGNOSTIC
274 printf("union_unmount(mp = %x)\n", mp);
275#endif
276
6902a390 277 if (mntflags & MNT_FORCE)
a1fa407d 278 flags |= FORCECLOSE;
a1fa407d
JSP
279
280 if (error = union_root(mp, &um_rootvp))
281 return (error);
9192c54f
JSP
282
283 /*
284 * Keep flushing vnodes from the mount list.
285 * This is needed because of the un_pvp held
286 * reference to the parent vnode.
287 * If more vnodes have been freed on a given pass,
288 * the try again. The loop will iterate at most
289 * (d) times, where (d) is the maximum tree depth
290 * in the filesystem.
291 */
292 for (freeing = 0; vflush(mp, um_rootvp, flags) != 0;) {
293 struct vnode *vp;
294 int n;
295
296 /* count #vnodes held on mount list */
297 for (n = 0, vp = mp->mnt_vnodelist.lh_first;
298 vp != NULLVP;
299 vp = vp->v_mntvnodes.le_next)
300 n++;
301
302 /* if this is unchanged then stop */
303 if (n == freeing)
304 break;
305
306 /* otherwise try once more time */
307 freeing = n;
308 }
309
310 /* At this point the root vnode should have a single reference */
01dac67e
JSP
311 if (um_rootvp->v_usecount > 1) {
312 vput(um_rootvp);
a1fa407d 313 return (EBUSY);
01dac67e 314 }
a1fa407d
JSP
315
316#ifdef UNION_DIAGNOSTIC
9192c54f 317 vprint("union root", um_rootvp);
a1fa407d
JSP
318#endif
319 /*
320 * Discard references to upper and lower target vnodes.
321 */
692a90df
JSP
322 if (um->um_lowervp)
323 vrele(um->um_lowervp);
a1fa407d
JSP
324 vrele(um->um_uppervp);
325 crfree(um->um_cred);
326 /*
327 * Release reference on underlying root vnode
328 */
01dac67e 329 vput(um_rootvp);
a1fa407d
JSP
330 /*
331 * And blow it away for future re-use
332 */
e2640548 333 vgone(um_rootvp);
a1fa407d
JSP
334 /*
335 * Finally, throw away the union_mount structure
336 */
337 free(mp->mnt_data, M_UFSMNT); /* XXX */
338 mp->mnt_data = 0;
3b293975 339 return (0);
a1fa407d
JSP
340}
341
342int
343union_root(mp, vpp)
344 struct mount *mp;
345 struct vnode **vpp;
346{
425b3035 347 struct proc *p = curproc; /* XXX */
a1fa407d
JSP
348 struct union_mount *um = MOUNTTOUNIONMOUNT(mp);
349 int error;
c1b204a8 350 int loselock;
a1fa407d 351
a1fa407d
JSP
352 /*
353 * Return locked reference to root.
354 */
01dac67e 355 VREF(um->um_uppervp);
c1b204a8
JSP
356 if ((um->um_op == UNMNT_BELOW) &&
357 VOP_ISLOCKED(um->um_uppervp)) {
358 loselock = 1;
359 } else {
425b3035 360 vn_lock(um->um_uppervp, LK_EXCLUSIVE | LK_RETRY, p);
c1b204a8
JSP
361 loselock = 0;
362 }
692a90df
JSP
363 if (um->um_lowervp)
364 VREF(um->um_lowervp);
79f80c71
JSP
365 error = union_allocvp(vpp, mp,
366 (struct vnode *) 0,
367 (struct vnode *) 0,
a1fa407d
JSP
368 (struct componentname *) 0,
369 um->um_uppervp,
825ff80f
JSP
370 um->um_lowervp,
371 1);
01dac67e
JSP
372
373 if (error) {
b6c9cd60
JSP
374 if (loselock)
375 vrele(um->um_uppervp);
376 else
377 vput(um->um_uppervp);
692a90df
JSP
378 if (um->um_lowervp)
379 vrele(um->um_lowervp);
01dac67e 380 } else {
c1b204a8
JSP
381 if (loselock)
382 VTOUNION(*vpp)->un_flags &= ~UN_ULOCK;
01dac67e 383 }
a1fa407d
JSP
384
385 return (error);
386}
387
a1fa407d
JSP
388int
389union_statfs(mp, sbp, p)
390 struct mount *mp;
391 struct statfs *sbp;
392 struct proc *p;
393{
394 int error;
395 struct union_mount *um = MOUNTTOUNIONMOUNT(mp);
396 struct statfs mstat;
397 int lbsize;
398
399#ifdef UNION_DIAGNOSTIC
400 printf("union_statfs(mp = %x, lvp = %x, uvp = %x)\n", mp,
401 um->um_lowervp,
402 um->um_uppervp);
403#endif
404
405 bzero(&mstat, sizeof(mstat));
406
692a90df
JSP
407 if (um->um_lowervp) {
408 error = VFS_STATFS(um->um_lowervp->v_mount, &mstat, p);
409 if (error)
410 return (error);
411 }
a1fa407d
JSP
412
413 /* now copy across the "interesting" information and fake the rest */
414#if 0
415 sbp->f_type = mstat.f_type;
416 sbp->f_flags = mstat.f_flags;
417 sbp->f_bsize = mstat.f_bsize;
418 sbp->f_iosize = mstat.f_iosize;
419#endif
420 lbsize = mstat.f_bsize;
421 sbp->f_blocks = mstat.f_blocks;
422 sbp->f_bfree = mstat.f_bfree;
423 sbp->f_bavail = mstat.f_bavail;
424 sbp->f_files = mstat.f_files;
425 sbp->f_ffree = mstat.f_ffree;
426
427 error = VFS_STATFS(um->um_uppervp->v_mount, &mstat, p);
428 if (error)
429 return (error);
430
a1fa407d
JSP
431 sbp->f_flags = mstat.f_flags;
432 sbp->f_bsize = mstat.f_bsize;
433 sbp->f_iosize = mstat.f_iosize;
434
435 /*
436 * if the lower and upper blocksizes differ, then frig the
437 * block counts so that the sizes reported by df make some
438 * kind of sense. none of this makes sense though.
439 */
440
8583490d 441 if (mstat.f_bsize != lbsize)
a1fa407d 442 sbp->f_blocks = sbp->f_blocks * lbsize / mstat.f_bsize;
8583490d
JSP
443
444 /*
445 * The "total" fields count total resources in all layers,
446 * the "free" fields count only those resources which are
447 * free in the upper layer (since only the upper layer
448 * is writeable).
449 */
a1fa407d 450 sbp->f_blocks += mstat.f_blocks;
8583490d
JSP
451 sbp->f_bfree = mstat.f_bfree;
452 sbp->f_bavail = mstat.f_bavail;
a1fa407d 453 sbp->f_files += mstat.f_files;
8583490d 454 sbp->f_ffree = mstat.f_ffree;
a1fa407d
JSP
455
456 if (sbp != &mp->mnt_stat) {
faa82b58 457 sbp->f_type = mp->mnt_vfc->vfc_typenum;
a1fa407d
JSP
458 bcopy(&mp->mnt_stat.f_fsid, &sbp->f_fsid, sizeof(sbp->f_fsid));
459 bcopy(mp->mnt_stat.f_mntonname, sbp->f_mntonname, MNAMELEN);
460 bcopy(mp->mnt_stat.f_mntfromname, sbp->f_mntfromname, MNAMELEN);
461 }
462 return (0);
463}
464
faa82b58
KM
465/*
466 * XXX - Assumes no data cached at union layer.
467 */
468#define union_sync ((int (*) __P((struct mount *, int, struct ucred *, \
469 struct proc *)))nullop)
470
471#define union_fhtovp ((int (*) __P((struct mount *, struct fid *, \
472 struct mbuf *, struct vnode **, int *, struct ucred **)))eopnotsupp)
473int union_init __P((struct vfsconf *));
474#define union_quotactl ((int (*) __P((struct mount *, int, uid_t, caddr_t, \
475 struct proc *)))eopnotsupp)
476#define union_sysctl ((int (*) __P((int *, u_int, void *, size_t *, void *, \
477 size_t, struct proc *)))eopnotsupp)
478#define union_vget ((int (*) __P((struct mount *, ino_t, struct vnode **))) \
479 eopnotsupp)
480#define union_vptofh ((int (*) __P((struct vnode *, struct fid *)))eopnotsupp)
a1fa407d
JSP
481
482struct vfsops union_vfsops = {
483 union_mount,
484 union_start,
485 union_unmount,
486 union_root,
487 union_quotactl,
488 union_statfs,
489 union_sync,
490 union_vget,
491 union_fhtovp,
492 union_vptofh,
493 union_init,
faa82b58 494 union_sysctl,
a1fa407d 495};