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