VROOT flag is set in union_allocvp
[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 *
e7e8ff37 11 * @(#)union_vfsops.c 8.8 (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 {
c1b204a8
JSP
371 if (loselock)
372 VTOUNION(*vpp)->un_flags &= ~UN_ULOCK;
01dac67e 373 }
a1fa407d
JSP
374
375 return (error);
376}
377
378int
379union_quotactl(mp, cmd, uid, arg, p)
380 struct mount *mp;
381 int cmd;
382 uid_t uid;
383 caddr_t arg;
384 struct proc *p;
385{
386
387 return (EOPNOTSUPP);
388}
389
390int
391union_statfs(mp, sbp, p)
392 struct mount *mp;
393 struct statfs *sbp;
394 struct proc *p;
395{
396 int error;
397 struct union_mount *um = MOUNTTOUNIONMOUNT(mp);
398 struct statfs mstat;
399 int lbsize;
400
401#ifdef UNION_DIAGNOSTIC
402 printf("union_statfs(mp = %x, lvp = %x, uvp = %x)\n", mp,
403 um->um_lowervp,
404 um->um_uppervp);
405#endif
406
407 bzero(&mstat, sizeof(mstat));
408
692a90df
JSP
409 if (um->um_lowervp) {
410 error = VFS_STATFS(um->um_lowervp->v_mount, &mstat, p);
411 if (error)
412 return (error);
413 }
a1fa407d
JSP
414
415 /* now copy across the "interesting" information and fake the rest */
416#if 0
417 sbp->f_type = mstat.f_type;
418 sbp->f_flags = mstat.f_flags;
419 sbp->f_bsize = mstat.f_bsize;
420 sbp->f_iosize = mstat.f_iosize;
421#endif
422 lbsize = mstat.f_bsize;
423 sbp->f_blocks = mstat.f_blocks;
424 sbp->f_bfree = mstat.f_bfree;
425 sbp->f_bavail = mstat.f_bavail;
426 sbp->f_files = mstat.f_files;
427 sbp->f_ffree = mstat.f_ffree;
428
429 error = VFS_STATFS(um->um_uppervp->v_mount, &mstat, p);
430 if (error)
431 return (error);
432
84d17267 433 sbp->f_type = MOUNT_UNION;
a1fa407d
JSP
434 sbp->f_flags = mstat.f_flags;
435 sbp->f_bsize = mstat.f_bsize;
436 sbp->f_iosize = mstat.f_iosize;
437
438 /*
439 * if the lower and upper blocksizes differ, then frig the
440 * block counts so that the sizes reported by df make some
441 * kind of sense. none of this makes sense though.
442 */
443
444 if (mstat.f_bsize != lbsize) {
445 sbp->f_blocks = sbp->f_blocks * lbsize / mstat.f_bsize;
446 sbp->f_bfree = sbp->f_bfree * lbsize / mstat.f_bsize;
447 sbp->f_bavail = sbp->f_bavail * lbsize / mstat.f_bsize;
448 }
449 sbp->f_blocks += mstat.f_blocks;
450 sbp->f_bfree += mstat.f_bfree;
451 sbp->f_bavail += mstat.f_bavail;
452 sbp->f_files += mstat.f_files;
453 sbp->f_ffree += mstat.f_ffree;
454
455 if (sbp != &mp->mnt_stat) {
456 bcopy(&mp->mnt_stat.f_fsid, &sbp->f_fsid, sizeof(sbp->f_fsid));
457 bcopy(mp->mnt_stat.f_mntonname, sbp->f_mntonname, MNAMELEN);
458 bcopy(mp->mnt_stat.f_mntfromname, sbp->f_mntfromname, MNAMELEN);
459 }
460 return (0);
461}
462
463int
464union_sync(mp, waitfor, cred, p)
465 struct mount *mp;
466 int waitfor;
467 struct ucred *cred;
468 struct proc *p;
469{
470
471 /*
472 * XXX - Assumes no data cached at union layer.
473 */
474 return (0);
475}
476
477int
478union_vget(mp, ino, vpp)
479 struct mount *mp;
480 ino_t ino;
481 struct vnode **vpp;
482{
483
484 return (EOPNOTSUPP);
485}
486
487int
488union_fhtovp(mp, fidp, nam, vpp, exflagsp, credanonp)
489 struct mount *mp;
490 struct fid *fidp;
491 struct mbuf *nam;
492 struct vnode **vpp;
493 int *exflagsp;
494 struct ucred **credanonp;
495{
496
497 return (EOPNOTSUPP);
498}
499
500int
501union_vptofh(vp, fhp)
502 struct vnode *vp;
503 struct fid *fhp;
504{
505
506 return (EOPNOTSUPP);
507}
508
509int union_init __P((void));
510
511struct vfsops union_vfsops = {
512 union_mount,
513 union_start,
514 union_unmount,
515 union_root,
516 union_quotactl,
517 union_statfs,
518 union_sync,
519 union_vget,
520 union_fhtovp,
521 union_vptofh,
522 union_init,
523};