null_vfsops.c revision 56272
1214571Sdim/*
2214571Sdim * Copyright (c) 1992, 1993, 1995
3214571Sdim *	The Regents of the University of California.  All rights reserved.
4214571Sdim *
5214571Sdim * This code is derived from software donated to Berkeley by
6214571Sdim * Jan-Simon Pendry.
7214571Sdim *
8214571Sdim * Redistribution and use in source and binary forms, with or without
9214571Sdim * modification, are permitted provided that the following conditions
10214571Sdim * are met:
11214571Sdim * 1. Redistributions of source code must retain the above copyright
12214571Sdim *    notice, this list of conditions and the following disclaimer.
13214571Sdim * 2. Redistributions in binary form must reproduce the above copyright
14214571Sdim *    notice, this list of conditions and the following disclaimer in the
15214571Sdim *    documentation and/or other materials provided with the distribution.
16214571Sdim * 3. All advertising materials mentioning features or use of this software
17214571Sdim *    must display the following acknowledgement:
18214571Sdim *	This product includes software developed by the University of
19214571Sdim *	California, Berkeley and its contributors.
20214571Sdim * 4. Neither the name of the University nor the names of its contributors
21214571Sdim *    may be used to endorse or promote products derived from this software
22214571Sdim *    without specific prior written permission.
23214571Sdim *
24214571Sdim * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25214571Sdim * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26214571Sdim * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27214571Sdim * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28214571Sdim * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29214571Sdim * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30214571Sdim * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31214571Sdim * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32214571Sdim * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33214571Sdim * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34214571Sdim * SUCH DAMAGE.
35214571Sdim *
36214571Sdim *	@(#)null_vfsops.c	8.2 (Berkeley) 1/21/94
37214571Sdim *
38214571Sdim * @(#)lofs_vfsops.c	1.2 (Berkeley) 6/18/92
39214571Sdim * $FreeBSD: head/sys/fs/nullfs/null_vfsops.c 56272 2000-01-19 06:07:34Z rwatson $
40214571Sdim */
41214571Sdim
42214571Sdim/*
43214571Sdim * Null Layer
44214571Sdim * (See null_vnops.c for a description of what this does.)
45214571Sdim */
46214571Sdim
47214571Sdim#include <sys/param.h>
48214571Sdim#include <sys/systm.h>
49214571Sdim#include <sys/kernel.h>
50214571Sdim#include <sys/proc.h>
51214571Sdim#include <sys/malloc.h>
52214571Sdim#include <sys/vnode.h>
53214571Sdim#include <sys/mount.h>
54214571Sdim#include <sys/namei.h>
55214571Sdim#include <miscfs/nullfs/null.h>
56214571Sdim#include <vm/vm_zone.h>
57214571Sdim
58214571Sdimstatic MALLOC_DEFINE(M_NULLFSMNT, "NULLFS mount", "NULLFS mount structure");
59214571Sdim
60214571Sdimstatic int	nullfs_fhtovp __P((struct mount *mp, struct fid *fidp,
61214571Sdim				   struct vnode **vpp));
62214571Sdimstatic int	nullfs_checkexp __P((struct mount *mp, struct sockaddr *nam,
63214571Sdim				    int *extflagsp, struct ucred **credanonp));
64214571Sdimstatic int	nullfs_mount __P((struct mount *mp, char *path, caddr_t data,
65214571Sdim				  struct nameidata *ndp, struct proc *p));
66214571Sdimstatic int	nullfs_quotactl __P((struct mount *mp, int cmd, uid_t uid,
67214571Sdim				     caddr_t arg, struct proc *p));
68214571Sdimstatic int	nullfs_root __P((struct mount *mp, struct vnode **vpp));
69214571Sdimstatic int	nullfs_start __P((struct mount *mp, int flags, struct proc *p));
70214571Sdimstatic int	nullfs_statfs __P((struct mount *mp, struct statfs *sbp,
71214571Sdim				   struct proc *p));
72214571Sdimstatic int	nullfs_sync __P((struct mount *mp, int waitfor,
73214571Sdim				 struct ucred *cred, struct proc *p));
74214571Sdimstatic int	nullfs_unmount __P((struct mount *mp, int mntflags,
75214571Sdim				    struct proc *p));
76214571Sdimstatic int	nullfs_vget __P((struct mount *mp, ino_t ino,
77214571Sdim				 struct vnode **vpp));
78214571Sdimstatic int	nullfs_vptofh __P((struct vnode *vp, struct fid *fhp));
79214571Sdim
80214571Sdim/*
81214571Sdim * Mount null layer
82214571Sdim */
83214571Sdimstatic int
84214571Sdimnullfs_mount(mp, path, data, ndp, p)
85214571Sdim	struct mount *mp;
86214571Sdim	char *path;
87214571Sdim	caddr_t data;
88214571Sdim	struct nameidata *ndp;
89214571Sdim	struct proc *p;
90214571Sdim{
91214571Sdim	int error = 0;
92214571Sdim	struct null_args args;
93214571Sdim	struct vnode *lowerrootvp, *vp;
94214571Sdim	struct vnode *nullm_rootvp;
95214571Sdim	struct null_mount *xmp;
96214571Sdim	u_int size;
97214571Sdim	int isvnunlocked = 0;
98214571Sdim
99214571Sdim#ifdef DEBUG
100214571Sdim	printf("nullfs_mount(mp = %p)\n", (void *)mp);
101214571Sdim#endif
102214571Sdim
103214571Sdim	/*
104214571Sdim	 * Update is a no-op
105214571Sdim	 */
106214571Sdim	if (mp->mnt_flag & MNT_UPDATE) {
107214571Sdim		return (EOPNOTSUPP);
108214571Sdim		/* return VFS_MOUNT(MOUNTTONULLMOUNT(mp)->nullm_vfs, path, data, ndp, p);*/
109214571Sdim	}
110214571Sdim
111214571Sdim	/*
112214571Sdim	 * Get argument
113214571Sdim	 */
114214571Sdim	error = copyin(data, (caddr_t)&args, sizeof(struct null_args));
115214571Sdim	if (error)
116214571Sdim		return (error);
117214571Sdim
118214571Sdim	/*
119214571Sdim	 * Unlock lower node to avoid deadlock.
120214571Sdim	 * (XXX) VOP_ISLOCKED is needed?
121214571Sdim	 */
122214571Sdim	if ((mp->mnt_vnodecovered->v_op == null_vnodeop_p) &&
123214571Sdim		VOP_ISLOCKED(mp->mnt_vnodecovered, NULL)) {
124214571Sdim		VOP_UNLOCK(mp->mnt_vnodecovered, 0, p);
125214571Sdim		isvnunlocked = 1;
126214571Sdim	}
127214571Sdim	/*
128214571Sdim	 * Find lower node
129214571Sdim	 */
130214571Sdim	NDINIT(ndp, LOOKUP, FOLLOW|WANTPARENT|LOCKLEAF,
131214571Sdim		UIO_USERSPACE, args.target, p);
132214571Sdim	error = namei(ndp);
133214571Sdim	/*
134214571Sdim	 * Re-lock vnode.
135214571Sdim	 */
136214571Sdim	if (isvnunlocked && !VOP_ISLOCKED(mp->mnt_vnodecovered, NULL))
137214571Sdim		vn_lock(mp->mnt_vnodecovered, LK_EXCLUSIVE | LK_RETRY, p);
138214571Sdim
139214571Sdim	if (error)
140214571Sdim		return (error);
141214571Sdim	NDFREE(ndp, NDF_ONLY_PNBUF);
142214571Sdim
143214571Sdim	/*
144214571Sdim	 * Sanity check on lower vnode
145214571Sdim	 */
146214571Sdim	lowerrootvp = ndp->ni_vp;
147214571Sdim
148214571Sdim	vrele(ndp->ni_dvp);
149214571Sdim	ndp->ni_dvp = NULLVP;
150214571Sdim
151214571Sdim	/*
152214571Sdim	 * Check multi null mount to avoid `lock against myself' panic.
153214571Sdim	 */
154214571Sdim	if (lowerrootvp == VTONULL(mp->mnt_vnodecovered)->null_lowervp) {
155214571Sdim#ifdef DEBUG
156214571Sdim		printf("nullfs_mount: multi null mount?\n");
157214571Sdim#endif
158214571Sdim		return (EDEADLK);
159214571Sdim	}
160214571Sdim
161214571Sdim	xmp = (struct null_mount *) malloc(sizeof(struct null_mount),
162214571Sdim				M_NULLFSMNT, M_WAITOK);	/* XXX */
163214571Sdim
164214571Sdim	/*
165214571Sdim	 * Save reference to underlying FS
166214571Sdim	 */
167214571Sdim	xmp->nullm_vfs = lowerrootvp->v_mount;
168214571Sdim
169214571Sdim	/*
170214571Sdim	 * Save reference.  Each mount also holds
171214571Sdim	 * a reference on the root vnode.
172214571Sdim	 */
173214571Sdim	error = null_node_create(mp, lowerrootvp, &vp);
174214571Sdim	/*
175214571Sdim	 * Unlock the node (either the lower or the alias)
176214571Sdim	 */
177214571Sdim	VOP_UNLOCK(vp, 0, p);
178214571Sdim	/*
179214571Sdim	 * Make sure the node alias worked
180214571Sdim	 */
181214571Sdim	if (error) {
182214571Sdim		vrele(lowerrootvp);
183214571Sdim		free(xmp, M_NULLFSMNT);	/* XXX */
184214571Sdim		return (error);
185214571Sdim	}
186214571Sdim
187214571Sdim	/*
188214571Sdim	 * Keep a held reference to the root vnode.
189214571Sdim	 * It is vrele'd in nullfs_unmount.
190214571Sdim	 */
191214571Sdim	nullm_rootvp = vp;
192214571Sdim	nullm_rootvp->v_flag |= VROOT;
193214571Sdim	xmp->nullm_rootvp = nullm_rootvp;
194214571Sdim	if (NULLVPTOLOWERVP(nullm_rootvp)->v_mount->mnt_flag & MNT_LOCAL)
195214571Sdim		mp->mnt_flag |= MNT_LOCAL;
196214571Sdim	mp->mnt_data = (qaddr_t) xmp;
197214571Sdim	vfs_getnewfsid(mp);
198214571Sdim
199214571Sdim	(void) copyinstr(path, mp->mnt_stat.f_mntonname, MNAMELEN - 1, &size);
200214571Sdim	bzero(mp->mnt_stat.f_mntonname + size, MNAMELEN - size);
201214571Sdim	(void) copyinstr(args.target, mp->mnt_stat.f_mntfromname, MNAMELEN - 1,
202214571Sdim	    &size);
203214571Sdim	bzero(mp->mnt_stat.f_mntfromname + size, MNAMELEN - size);
204214571Sdim	(void)nullfs_statfs(mp, &mp->mnt_stat, p);
205214571Sdim#ifdef DEBUG
206214571Sdim	printf("nullfs_mount: lower %s, alias at %s\n",
207214571Sdim		mp->mnt_stat.f_mntfromname, mp->mnt_stat.f_mntonname);
208214571Sdim#endif
209214571Sdim	return (0);
210214571Sdim}
211214571Sdim
212214571Sdim/*
213214571Sdim * VFS start.  Nothing needed here - the start routine
214214571Sdim * on the underlying filesystem will have been called
215214571Sdim * when that filesystem was mounted.
216214571Sdim */
217214571Sdimstatic int
218214571Sdimnullfs_start(mp, flags, p)
219214571Sdim	struct mount *mp;
220214571Sdim	int flags;
221214571Sdim	struct proc *p;
222214571Sdim{
223214571Sdim	return (0);
224214571Sdim	/* return VFS_START(MOUNTTONULLMOUNT(mp)->nullm_vfs, flags, p); */
225214571Sdim}
226214571Sdim
227214571Sdim/*
228214571Sdim * Free reference to null layer
229214571Sdim */
230214571Sdimstatic int
231214571Sdimnullfs_unmount(mp, mntflags, p)
232214571Sdim	struct mount *mp;
233214571Sdim	int mntflags;
234214571Sdim	struct proc *p;
235214571Sdim{
236214571Sdim	struct vnode *nullm_rootvp = MOUNTTONULLMOUNT(mp)->nullm_rootvp;
237214571Sdim	int error;
238214571Sdim	int flags = 0;
239214571Sdim
240214571Sdim#ifdef DEBUG
241214571Sdim	printf("nullfs_unmount(mp = %p)\n", (void *)mp);
242214571Sdim#endif
243214571Sdim
244214571Sdim	if (mntflags & MNT_FORCE)
245214571Sdim		flags |= FORCECLOSE;
246214571Sdim
247214571Sdim	/*
248214571Sdim	 * Clear out buffer cache.  I don't think we
249214571Sdim	 * ever get anything cached at this level at the
250214571Sdim	 * moment, but who knows...
251214571Sdim	 */
252214571Sdim#if 0
253214571Sdim	mntflushbuf(mp, 0);
254214571Sdim	if (mntinvalbuf(mp, 1))
255214571Sdim		return (EBUSY);
256214571Sdim#endif
257214571Sdim	if (nullm_rootvp->v_usecount > 1)
258214571Sdim		return (EBUSY);
259214571Sdim	error = vflush(mp, nullm_rootvp, flags);
260214571Sdim	if (error)
261214571Sdim		return (error);
262214571Sdim
263214571Sdim#ifdef DEBUG
264214571Sdim	vprint("alias root of lower", nullm_rootvp);
265214571Sdim#endif
266214571Sdim	/*
267214571Sdim	 * Release reference on underlying root vnode
268214571Sdim	 */
269214571Sdim	vrele(nullm_rootvp);
270214571Sdim	/*
271214571Sdim	 * And blow it away for future re-use
272214571Sdim	 */
273214571Sdim	vgone(nullm_rootvp);
274214571Sdim	/*
275214571Sdim	 * Finally, throw away the null_mount structure
276214571Sdim	 */
277214571Sdim	free(mp->mnt_data, M_NULLFSMNT);	/* XXX */
278214571Sdim	mp->mnt_data = 0;
279214571Sdim	return 0;
280214571Sdim}
281214571Sdim
282214571Sdimstatic int
283214571Sdimnullfs_root(mp, vpp)
284214571Sdim	struct mount *mp;
285214571Sdim	struct vnode **vpp;
286214571Sdim{
287214571Sdim	struct proc *p = curproc;	/* XXX */
288214571Sdim	struct vnode *vp;
289214571Sdim
290214571Sdim#ifdef DEBUG
291214571Sdim	printf("nullfs_root(mp = %p, vp = %p->%p)\n", (void *)mp,
292214571Sdim	    (void *)MOUNTTONULLMOUNT(mp)->nullm_rootvp,
293214571Sdim	    (void *)NULLVPTOLOWERVP(MOUNTTONULLMOUNT(mp)->nullm_rootvp));
294214571Sdim#endif
295214571Sdim
296214571Sdim	/*
297214571Sdim	 * Return locked reference to root.
298214571Sdim	 */
299214571Sdim	vp = MOUNTTONULLMOUNT(mp)->nullm_rootvp;
300214571Sdim	VREF(vp);
301214571Sdim	if (VOP_ISLOCKED(vp, NULL)) {
302214571Sdim		/*
303214571Sdim		 * XXX
304214571Sdim		 * Should we check type of node?
305214571Sdim		 */
306214571Sdim#ifdef DEBUG
307214571Sdim		printf("nullfs_root: multi null mount?\n");
308214571Sdim#endif
309214571Sdim		vrele(vp);
310214571Sdim		return (EDEADLK);
311214571Sdim	} else
312214571Sdim		vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p);
313214571Sdim	*vpp = vp;
314214571Sdim	return 0;
315214571Sdim}
316214571Sdim
317214571Sdimstatic int
318214571Sdimnullfs_quotactl(mp, cmd, uid, arg, p)
319214571Sdim	struct mount *mp;
320214571Sdim	int cmd;
321214571Sdim	uid_t uid;
322214571Sdim	caddr_t arg;
323214571Sdim	struct proc *p;
324214571Sdim{
325214571Sdim	return VFS_QUOTACTL(MOUNTTONULLMOUNT(mp)->nullm_vfs, cmd, uid, arg, p);
326214571Sdim}
327214571Sdim
328214571Sdimstatic int
329214571Sdimnullfs_statfs(mp, sbp, p)
330214571Sdim	struct mount *mp;
331214571Sdim	struct statfs *sbp;
332214571Sdim	struct proc *p;
333214571Sdim{
334214571Sdim	int error;
335214571Sdim	struct statfs mstat;
336214571Sdim
337214571Sdim#ifdef DEBUG
338214571Sdim	printf("nullfs_statfs(mp = %p, vp = %p->%p)\n", (void *)mp,
339214571Sdim	    (void *)MOUNTTONULLMOUNT(mp)->nullm_rootvp,
340214571Sdim	    (void *)NULLVPTOLOWERVP(MOUNTTONULLMOUNT(mp)->nullm_rootvp));
341214571Sdim#endif
342214571Sdim
343214571Sdim	bzero(&mstat, sizeof(mstat));
344214571Sdim
345214571Sdim	error = VFS_STATFS(MOUNTTONULLMOUNT(mp)->nullm_vfs, &mstat, p);
346214571Sdim	if (error)
347214571Sdim		return (error);
348214571Sdim
349214571Sdim	/* now copy across the "interesting" information and fake the rest */
350214571Sdim	sbp->f_type = mstat.f_type;
351214571Sdim	sbp->f_flags = mstat.f_flags;
352214571Sdim	sbp->f_bsize = mstat.f_bsize;
353214571Sdim	sbp->f_iosize = mstat.f_iosize;
354214571Sdim	sbp->f_blocks = mstat.f_blocks;
355214571Sdim	sbp->f_bfree = mstat.f_bfree;
356214571Sdim	sbp->f_bavail = mstat.f_bavail;
357214571Sdim	sbp->f_files = mstat.f_files;
358214571Sdim	sbp->f_ffree = mstat.f_ffree;
359214571Sdim	if (sbp != &mp->mnt_stat) {
360214571Sdim		bcopy(&mp->mnt_stat.f_fsid, &sbp->f_fsid, sizeof(sbp->f_fsid));
361214571Sdim		bcopy(mp->mnt_stat.f_mntonname, sbp->f_mntonname, MNAMELEN);
362214571Sdim		bcopy(mp->mnt_stat.f_mntfromname, sbp->f_mntfromname, MNAMELEN);
363214571Sdim	}
364214571Sdim	return (0);
365214571Sdim}
366214571Sdim
367214571Sdimstatic int
368214571Sdimnullfs_sync(mp, waitfor, cred, p)
369214571Sdim	struct mount *mp;
370214571Sdim	int waitfor;
371214571Sdim	struct ucred *cred;
372214571Sdim	struct proc *p;
373214571Sdim{
374214571Sdim	/*
375214571Sdim	 * XXX - Assumes no data cached at null layer.
376214571Sdim	 */
377214571Sdim	return (0);
378214571Sdim}
379214571Sdim
380214571Sdimstatic int
381214571Sdimnullfs_vget(mp, ino, vpp)
382214571Sdim	struct mount *mp;
383214571Sdim	ino_t ino;
384214571Sdim	struct vnode **vpp;
385214571Sdim{
386214571Sdim
387214571Sdim	return VFS_VGET(MOUNTTONULLMOUNT(mp)->nullm_vfs, ino, vpp);
388214571Sdim}
389214571Sdim
390214571Sdimstatic int
391214571Sdimnullfs_fhtovp(mp, fidp, vpp)
392214571Sdim	struct mount *mp;
393214571Sdim	struct fid *fidp;
394214571Sdim	struct vnode **vpp;
395214571Sdim{
396214571Sdim
397214571Sdim	return VFS_FHTOVP(MOUNTTONULLMOUNT(mp)->nullm_vfs, fidp, vpp);
398214571Sdim}
399214571Sdim
400214571Sdimstatic int
401214571Sdimnullfs_checkexp(mp, nam, extflagsp, credanonp)
402214571Sdim	struct mount *mp;
403214571Sdim	struct sockaddr *nam;
404214571Sdim	int *extflagsp;
405214571Sdim	struct ucred **credanonp;
406214571Sdim{
407214571Sdim
408214571Sdim	return VFS_CHECKEXP(MOUNTTONULLMOUNT(mp)->nullm_vfs, nam,
409214571Sdim		extflagsp, credanonp);
410214571Sdim}
411214571Sdim
412214571Sdimstatic int
413214571Sdimnullfs_vptofh(vp, fhp)
414214571Sdim	struct vnode *vp;
415214571Sdim	struct fid *fhp;
416214571Sdim{
417214571Sdim	return VFS_VPTOFH(NULLVPTOLOWERVP(vp), fhp);
418214571Sdim}
419214571Sdim
420214571Sdimstatic int
421214571Sdimnullfs_extattrctl(mp, cmd, attrname, arg, p)
422214571Sdim	struct mount *mp;
423214571Sdim	int cmd;
424214571Sdim	const char *attrname;
425214571Sdim	caddr_t arg;
426214571Sdim	struct proc *p;
427214571Sdim{
428214571Sdim	return VFS_EXTATTRCTL(MOUNTTONULLMOUNT(mp)->nullm_vfs, cmd, attrname,
429214571Sdim	    arg, p);
430214571Sdim}
431214571Sdim
432214571Sdim
433214571Sdimstatic struct vfsops null_vfsops = {
434214571Sdim	nullfs_mount,
435214571Sdim	nullfs_start,
436214571Sdim	nullfs_unmount,
437214571Sdim	nullfs_root,
438214571Sdim	nullfs_quotactl,
439214571Sdim	nullfs_statfs,
440214571Sdim	nullfs_sync,
441214571Sdim	nullfs_vget,
442214571Sdim	nullfs_fhtovp,
443214571Sdim	nullfs_checkexp,
444214571Sdim	nullfs_vptofh,
445214571Sdim	nullfs_init,
446214571Sdim	vfs_stduninit,
447214571Sdim	nullfs_extattrctl,
448214571Sdim};
449214571Sdim
450214571SdimVFS_SET(null_vfsops, null, VFCF_LOOPBACK);
451214571Sdim