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