update contrib (to 5.55)
[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 *
c80f414e 11 * @(#)union_vfsops.c 8.7 (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;
a1fa407d 46 struct union_mount *um;
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 70
692a90df
JSP
71 /*
72 * Take a copy of the process's credentials. This isn't
73 * quite right since the euid will always be zero and we
74 * want to get the "real" users credentials. So fix up
75 * the uid field after taking the copy.
76 */
77 cred = crdup(p->p_ucred);
78 cred->cr_uid = p->p_cred->p_ruid;
79
80 /*
81 * Ensure the *real* user has write permission on the
82 * mounted-on directory. This allows the mount_union
83 * command to be made setuid root so allowing anyone
84 * to do union mounts onto any directory on which they
bc0d9070 85 * have write permission and which they also own.
692a90df 86 */
c1b204a8
JSP
87 error = VOP_GETATTR(mp->mnt_vnodecovered, &va, cred, p);
88 if (error)
89 goto bad;
bc0d9070
JSP
90 if ((va.va_uid != cred->cr_uid) &&
91 (cred->cr_uid != 0)) {
c1b204a8
JSP
92 error = EACCES;
93 goto bad;
94 }
bc0d9070
JSP
95 error = VOP_ACCESS(mp->mnt_vnodecovered, VWRITE, cred, p);
96 if (error)
97 goto bad;
692a90df 98
a1fa407d
JSP
99 /*
100 * Get argument
101 */
102 if (error = copyin(data, (caddr_t)&args, sizeof(struct union_args)))
692a90df 103 goto bad;
a1fa407d
JSP
104
105 lowerrootvp = mp->mnt_vnodecovered;
106 VREF(lowerrootvp);
107
108 /*
9f7857db
JSP
109 * Find upper node. Use the real process credentials,
110 * not the effective ones since this will have come
111 * through a setuid process (mount_union). All this
112 * messing around with permissions is entirely bogus
113 * and should be removed by allowing any user straight
114 * past the mount system call.
a1fa407d 115 */
9f7857db
JSP
116 scred = p->p_ucred;
117 p->p_ucred = cred;
a1fa407d
JSP
118 NDINIT(ndp, LOOKUP, FOLLOW|WANTPARENT,
119 UIO_USERSPACE, args.target, p);
9f7857db
JSP
120 p->p_ucred = scred;
121
692a90df
JSP
122 if (error = namei(ndp))
123 goto bad;
124
a1fa407d
JSP
125 upperrootvp = ndp->ni_vp;
126 vrele(ndp->ni_dvp);
127 ndp->ni_dvp = NULL;
128
129 if (upperrootvp->v_type != VDIR) {
692a90df
JSP
130 error = EINVAL;
131 goto bad;
a1fa407d
JSP
132 }
133
134 um = (struct union_mount *) malloc(sizeof(struct union_mount),
135 M_UFSMNT, M_WAITOK); /* XXX */
136
137 /*
138 * Keep a held reference to the target vnodes.
139 * They are vrele'd in union_unmount.
692a90df
JSP
140 *
141 * Depending on the _BELOW flag, the filesystems are
142 * viewed in a different order. In effect, this is the
143 * same as providing a mount under option to the mount syscall.
a1fa407d 144 */
692a90df 145
c1b204a8
JSP
146 um->um_op = args.mntflags & UNMNT_OPMASK;
147 switch (um->um_op) {
692a90df
JSP
148 case UNMNT_ABOVE:
149 um->um_lowervp = lowerrootvp;
150 um->um_uppervp = upperrootvp;
151 break;
152
153 case UNMNT_BELOW:
154 um->um_lowervp = upperrootvp;
155 um->um_uppervp = lowerrootvp;
156 break;
157
158 case UNMNT_REPLACE:
159 vrele(lowerrootvp);
160 lowerrootvp = NULLVP;
161 um->um_uppervp = upperrootvp;
162 um->um_lowervp = lowerrootvp;
163 break;
164
165 default:
166 error = EINVAL;
167 goto bad;
168 }
169
170 um->um_cred = cred;
3b293975 171 um->um_cmode = UN_DIRMODE &~ p->p_fd->fd_cmask;
a1fa407d 172
3b293975
JSP
173 /*
174 * Depending on what you think the MNT_LOCAL flag might mean,
175 * you may want the && to be || on the conditional below.
176 * At the moment it has been defined that the filesystem is
177 * only local if it is all local, ie the MNT_LOCAL flag implies
178 * that the entire namespace is local. If you think the MNT_LOCAL
179 * flag implies that some of the files might be stored locally
180 * then you will want to change the conditional.
181 */
c1b204a8
JSP
182 if (um->um_op == UNMNT_ABOVE) {
183 if (((um->um_lowervp == NULLVP) ||
184 (um->um_lowervp->v_mount->mnt_flag & MNT_LOCAL)) &&
185 (um->um_uppervp->v_mount->mnt_flag & MNT_LOCAL))
186 mp->mnt_flag |= MNT_LOCAL;
187 }
3b293975 188
81be6ee0
JSP
189 /*
190 * Copy in the upper layer's RDONLY flag. This is for the benefit
191 * of lookup() which explicitly checks the flag, rather than asking
192 * the filesystem for it's own opinion. This means, that an update
193 * mount of the underlying filesystem to go from rdonly to rdwr
194 * will leave the unioned view as read-only.
195 */
692a90df 196 mp->mnt_flag |= (um->um_uppervp->v_mount->mnt_flag & MNT_RDONLY);
bc0d9070
JSP
197
198 /*
199 * This is a user mount. Privilege check for unmount
200 * will be done in union_unmount.
201 */
202 mp->mnt_flag |= MNT_USER;
203
a1fa407d
JSP
204 mp->mnt_data = (qaddr_t) um;
205 getnewfsid(mp, MOUNT_UNION);
206
207 (void) copyinstr(path, mp->mnt_stat.f_mntonname, MNAMELEN - 1, &size);
208 bzero(mp->mnt_stat.f_mntonname + size, MNAMELEN - size);
692a90df 209
c1b204a8 210 switch (um->um_op) {
692a90df 211 case UNMNT_ABOVE:
c80f414e 212 cp = "<above>";
692a90df
JSP
213 break;
214 case UNMNT_BELOW:
c80f414e 215 cp = "<below>";
692a90df
JSP
216 break;
217 case UNMNT_REPLACE:
c80f414e 218 cp = "";
692a90df
JSP
219 break;
220 }
221 len = strlen(cp);
222 bcopy(cp, mp->mnt_stat.f_mntfromname, len);
223
224 cp = mp->mnt_stat.f_mntfromname + len;
225 len = MNAMELEN - len;
226
227 (void) copyinstr(args.target, cp, len - 1, &size);
228 bzero(cp + size, len - size);
229
a1fa407d 230#ifdef UNION_DIAGNOSTIC
692a90df 231 printf("union_mount: from %s, on %s\n",
a1fa407d
JSP
232 mp->mnt_stat.f_mntfromname, mp->mnt_stat.f_mntonname);
233#endif
234 return (0);
692a90df
JSP
235
236bad:
237 if (cred)
238 crfree(cred);
239 if (upperrootvp)
240 vrele(upperrootvp);
241 if (lowerrootvp)
242 vrele(lowerrootvp);
243 return (error);
a1fa407d
JSP
244}
245
246/*
247 * VFS start. Nothing needed here - the start routine
248 * on the underlying filesystem(s) will have been called
249 * when that filesystem was mounted.
250 */
251int
252union_start(mp, flags, p)
253 struct mount *mp;
254 int flags;
255 struct proc *p;
256{
257
258 return (0);
259}
260
261/*
262 * Free reference to union layer
263 */
264int
265union_unmount(mp, mntflags, p)
266 struct mount *mp;
267 int mntflags;
268 struct proc *p;
269{
270 struct union_mount *um = MOUNTTOUNIONMOUNT(mp);
271 struct vnode *um_rootvp;
272 int error;
273 int flags = 0;
274 extern int doforce;
275
276#ifdef UNION_DIAGNOSTIC
277 printf("union_unmount(mp = %x)\n", mp);
278#endif
279
bc0d9070
JSP
280 /* only the mounter, or superuser can unmount */
281 if ((p->p_cred->p_ruid != um->um_cred->cr_uid) &&
282 (error = suser(p->p_ucred, &p->p_acflag)))
283 return (error);
284
a1fa407d
JSP
285 if (mntflags & MNT_FORCE) {
286 /* union can never be rootfs so don't check for it */
287 if (!doforce)
288 return (EINVAL);
289 flags |= FORCECLOSE;
290 }
291
292 if (error = union_root(mp, &um_rootvp))
293 return (error);
01dac67e
JSP
294 if (um_rootvp->v_usecount > 1) {
295 vput(um_rootvp);
a1fa407d 296 return (EBUSY);
01dac67e
JSP
297 }
298 if (error = vflush(mp, um_rootvp, flags)) {
299 vput(um_rootvp);
a1fa407d 300 return (error);
01dac67e 301 }
a1fa407d
JSP
302
303#ifdef UNION_DIAGNOSTIC
304 vprint("alias root of lower", um_rootvp);
305#endif
306 /*
307 * Discard references to upper and lower target vnodes.
308 */
692a90df
JSP
309 if (um->um_lowervp)
310 vrele(um->um_lowervp);
a1fa407d
JSP
311 vrele(um->um_uppervp);
312 crfree(um->um_cred);
313 /*
314 * Release reference on underlying root vnode
315 */
01dac67e 316 vput(um_rootvp);
a1fa407d
JSP
317 /*
318 * And blow it away for future re-use
319 */
320 vgone(um_rootvp);
321 /*
322 * Finally, throw away the union_mount structure
323 */
324 free(mp->mnt_data, M_UFSMNT); /* XXX */
325 mp->mnt_data = 0;
3b293975 326 return (0);
a1fa407d
JSP
327}
328
329int
330union_root(mp, vpp)
331 struct mount *mp;
332 struct vnode **vpp;
333{
334 struct union_mount *um = MOUNTTOUNIONMOUNT(mp);
335 int error;
c1b204a8 336 int loselock;
a1fa407d
JSP
337
338#ifdef UNION_DIAGNOSTIC
339 printf("union_root(mp = %x, lvp = %x, uvp = %x)\n", mp,
340 um->um_lowervp,
341 um->um_uppervp);
342#endif
343
344 /*
345 * Return locked reference to root.
346 */
01dac67e 347 VREF(um->um_uppervp);
c1b204a8
JSP
348 if ((um->um_op == UNMNT_BELOW) &&
349 VOP_ISLOCKED(um->um_uppervp)) {
350 loselock = 1;
351 } else {
352 VOP_LOCK(um->um_uppervp);
353 loselock = 0;
354 }
692a90df
JSP
355 if (um->um_lowervp)
356 VREF(um->um_lowervp);
79f80c71
JSP
357 error = union_allocvp(vpp, mp,
358 (struct vnode *) 0,
359 (struct vnode *) 0,
a1fa407d
JSP
360 (struct componentname *) 0,
361 um->um_uppervp,
362 um->um_lowervp);
01dac67e
JSP
363
364 if (error) {
c1b204a8
JSP
365 if (!loselock)
366 VOP_UNLOCK(um->um_uppervp);
01dac67e 367 vrele(um->um_uppervp);
692a90df
JSP
368 if (um->um_lowervp)
369 vrele(um->um_lowervp);
01dac67e 370 } else {
a1fa407d 371 (*vpp)->v_flag |= VROOT;
c1b204a8
JSP
372 if (loselock)
373 VTOUNION(*vpp)->un_flags &= ~UN_ULOCK;
01dac67e 374 }
a1fa407d
JSP
375
376 return (error);
377}
378
379int
380union_quotactl(mp, cmd, uid, arg, p)
381 struct mount *mp;
382 int cmd;
383 uid_t uid;
384 caddr_t arg;
385 struct proc *p;
386{
387
388 return (EOPNOTSUPP);
389}
390
391int
392union_statfs(mp, sbp, p)
393 struct mount *mp;
394 struct statfs *sbp;
395 struct proc *p;
396{
397 int error;
398 struct union_mount *um = MOUNTTOUNIONMOUNT(mp);
399 struct statfs mstat;
400 int lbsize;
401
402#ifdef UNION_DIAGNOSTIC
403 printf("union_statfs(mp = %x, lvp = %x, uvp = %x)\n", mp,
404 um->um_lowervp,
405 um->um_uppervp);
406#endif
407
408 bzero(&mstat, sizeof(mstat));
409
692a90df
JSP
410 if (um->um_lowervp) {
411 error = VFS_STATFS(um->um_lowervp->v_mount, &mstat, p);
412 if (error)
413 return (error);
414 }
a1fa407d
JSP
415
416 /* now copy across the "interesting" information and fake the rest */
417#if 0
418 sbp->f_type = mstat.f_type;
419 sbp->f_flags = mstat.f_flags;
420 sbp->f_bsize = mstat.f_bsize;
421 sbp->f_iosize = mstat.f_iosize;
422#endif
423 lbsize = mstat.f_bsize;
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 error = VFS_STATFS(um->um_uppervp->v_mount, &mstat, p);
431 if (error)
432 return (error);
433
84d17267 434 sbp->f_type = MOUNT_UNION;
a1fa407d
JSP
435 sbp->f_flags = mstat.f_flags;
436 sbp->f_bsize = mstat.f_bsize;
437 sbp->f_iosize = mstat.f_iosize;
438
439 /*
440 * if the lower and upper blocksizes differ, then frig the
441 * block counts so that the sizes reported by df make some
442 * kind of sense. none of this makes sense though.
443 */
444
445 if (mstat.f_bsize != lbsize) {
446 sbp->f_blocks = sbp->f_blocks * lbsize / mstat.f_bsize;
447 sbp->f_bfree = sbp->f_bfree * lbsize / mstat.f_bsize;
448 sbp->f_bavail = sbp->f_bavail * lbsize / mstat.f_bsize;
449 }
450 sbp->f_blocks += mstat.f_blocks;
451 sbp->f_bfree += mstat.f_bfree;
452 sbp->f_bavail += mstat.f_bavail;
453 sbp->f_files += mstat.f_files;
454 sbp->f_ffree += mstat.f_ffree;
455
456 if (sbp != &mp->mnt_stat) {
457 bcopy(&mp->mnt_stat.f_fsid, &sbp->f_fsid, sizeof(sbp->f_fsid));
458 bcopy(mp->mnt_stat.f_mntonname, sbp->f_mntonname, MNAMELEN);
459 bcopy(mp->mnt_stat.f_mntfromname, sbp->f_mntfromname, MNAMELEN);
460 }
461 return (0);
462}
463
464int
465union_sync(mp, waitfor, cred, p)
466 struct mount *mp;
467 int waitfor;
468 struct ucred *cred;
469 struct proc *p;
470{
471
472 /*
473 * XXX - Assumes no data cached at union layer.
474 */
475 return (0);
476}
477
478int
479union_vget(mp, ino, vpp)
480 struct mount *mp;
481 ino_t ino;
482 struct vnode **vpp;
483{
484
485 return (EOPNOTSUPP);
486}
487
488int
489union_fhtovp(mp, fidp, nam, vpp, exflagsp, credanonp)
490 struct mount *mp;
491 struct fid *fidp;
492 struct mbuf *nam;
493 struct vnode **vpp;
494 int *exflagsp;
495 struct ucred **credanonp;
496{
497
498 return (EOPNOTSUPP);
499}
500
501int
502union_vptofh(vp, fhp)
503 struct vnode *vp;
504 struct fid *fhp;
505{
506
507 return (EOPNOTSUPP);
508}
509
510int union_init __P((void));
511
512struct vfsops union_vfsops = {
513 union_mount,
514 union_start,
515 union_unmount,
516 union_root,
517 union_quotactl,
518 union_statfs,
519 union_sync,
520 union_vget,
521 union_fhtovp,
522 union_vptofh,
523 union_init,
524};