]> git.saurik.com Git - apple/xnu.git/blame - bsd/nfs/nfs_node.c
xnu-344.49.tar.gz
[apple/xnu.git] / bsd / nfs / nfs_node.c
CommitLineData
1c79356b
A
1/*
2 * Copyright (c) 2000 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
43866e37 6 * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved.
1c79356b 7 *
43866e37
A
8 * This file contains Original Code and/or Modifications of Original Code
9 * as defined in and that are subject to the Apple Public Source License
10 * Version 2.0 (the 'License'). You may not use this file except in
11 * compliance with the License. Please obtain a copy of the License at
12 * http://www.opensource.apple.com/apsl/ and read it before using this
13 * file.
14 *
15 * The Original Code and all software distributed under the License are
16 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
1c79356b
A
17 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
18 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
43866e37
A
19 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
20 * Please see the License for the specific language governing rights and
21 * limitations under the License.
1c79356b
A
22 *
23 * @APPLE_LICENSE_HEADER_END@
24 */
25/* Copyright (c) 1995 NeXT Computer, Inc. All Rights Reserved */
26/*
27 * Copyright (c) 1989, 1993
28 * The Regents of the University of California. All rights reserved.
29 *
30 * This code is derived from software contributed to Berkeley by
31 * Rick Macklem at The University of Guelph.
32 *
33 * Redistribution and use in source and binary forms, with or without
34 * modification, are permitted provided that the following conditions
35 * are met:
36 * 1. Redistributions of source code must retain the above copyright
37 * notice, this list of conditions and the following disclaimer.
38 * 2. Redistributions in binary form must reproduce the above copyright
39 * notice, this list of conditions and the following disclaimer in the
40 * documentation and/or other materials provided with the distribution.
41 * 3. All advertising materials mentioning features or use of this software
42 * must display the following acknowledgement:
43 * This product includes software developed by the University of
44 * California, Berkeley and its contributors.
45 * 4. Neither the name of the University nor the names of its contributors
46 * may be used to endorse or promote products derived from this software
47 * without specific prior written permission.
48 *
49 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
50 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
51 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
52 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
53 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
54 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
55 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
56 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
57 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
58 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
59 * SUCH DAMAGE.
60 *
61 * @(#)nfs_node.c 8.6 (Berkeley) 5/22/95
62 * FreeBSD-Id: nfs_node.c,v 1.22 1997/10/28 14:06:20 bde Exp $
63 */
64
65
66#include <sys/param.h>
67#include <sys/systm.h>
68#include <sys/proc.h>
69#include <sys/mount.h>
70#include <sys/namei.h>
71#include <sys/vnode.h>
72#include <sys/malloc.h>
73
74#include <nfs/rpcv2.h>
75#include <nfs/nfsproto.h>
76#include <nfs/nfs.h>
77#include <nfs/nfsnode.h>
78#include <nfs/nfsmount.h>
79
80#ifdef MALLOC_DEFINE
81static MALLOC_DEFINE(M_NFSNODE, "NFS node", "NFS vnode private part");
82#endif
83
84LIST_HEAD(nfsnodehashhead, nfsnode) *nfsnodehashtbl;
85u_long nfsnodehash;
86
87#define TRUE 1
88#define FALSE 0
89
90/*
91 * Initialize hash links for nfsnodes
92 * and build nfsnode free list.
93 */
94void
95nfs_nhinit()
96{
97 nfsnodehashtbl = hashinit(desiredvnodes, M_NFSNODE, &nfsnodehash);
98}
99
100/*
101 * Compute an entry in the NFS hash table structure
102 */
103u_long
104nfs_hash(fhp, fhsize)
105 register nfsfh_t *fhp;
106 int fhsize;
107{
108 register u_char *fhpp;
109 register u_long fhsum;
110 register int i;
111
112 fhpp = &fhp->fh_bytes[0];
113 fhsum = 0;
114 for (i = 0; i < fhsize; i++)
115 fhsum += *fhpp++;
116 return (fhsum);
117}
118
119/*
120 * Look up a vnode/nfsnode by file handle.
121 * Callers must check for mount points!!
122 * In all cases, a pointer to a
123 * nfsnode structure is returned.
124 */
125int nfs_node_hash_lock;
126
127int
128nfs_nget(mntp, fhp, fhsize, npp)
129 struct mount *mntp;
130 register nfsfh_t *fhp;
131 int fhsize;
132 struct nfsnode **npp;
133{
134 struct proc *p = current_proc(); /* XXX */
135 struct nfsnode *np;
136 struct nfsnodehashhead *nhpp;
137 register struct vnode *vp;
138 struct vnode *nvp;
139 int error;
140
141 /* Check for unmount in progress */
142 if (mntp->mnt_kern_flag & MNTK_UNMOUNT) {
143 *npp = 0;
144 return (EPERM);
145 }
146
147 nhpp = NFSNOHASH(nfs_hash(fhp, fhsize));
148loop:
149 for (np = nhpp->lh_first; np != 0; np = np->n_hash.le_next) {
150 if (mntp != NFSTOV(np)->v_mount || np->n_fhsize != fhsize ||
151 bcmp((caddr_t)fhp, (caddr_t)np->n_fhp, fhsize))
152 continue;
153 vp = NFSTOV(np);
154 if (vget(vp, LK_EXCLUSIVE, p))
155 goto loop;
156 *npp = np;
157 return(0);
158 }
159 /*
160 * Obtain a lock to prevent a race condition if the getnewvnode()
161 * or MALLOC() below happens to block.
162 */
163 if (nfs_node_hash_lock) {
164 while (nfs_node_hash_lock) {
165 nfs_node_hash_lock = -1;
166 tsleep(&nfs_node_hash_lock, PVM, "nfsngt", 0);
167 }
168 goto loop;
169 }
170 nfs_node_hash_lock = 1;
171
172 /*
173 * Do the MALLOC before the getnewvnode since doing so afterward
174 * might cause a bogus v_data pointer to get dereferenced
175 * elsewhere if MALLOC should block.
176 */
177 MALLOC_ZONE(np, struct nfsnode *, sizeof *np, M_NFSNODE, M_WAITOK);
178
179 error = getnewvnode(VT_NFS, mntp, nfsv2_vnodeop_p, &nvp);
180 if (error) {
181 if (nfs_node_hash_lock < 0)
182 wakeup(&nfs_node_hash_lock);
183 nfs_node_hash_lock = 0;
184 *npp = 0;
185 FREE_ZONE(np, sizeof *np, M_NFSNODE);
186 return (error);
187 }
188 vp = nvp;
189 bzero((caddr_t)np, sizeof *np);
190 vp->v_data = np;
191 np->n_vnode = vp;
192 /*
193 * Insert the nfsnode in the hash queue for its new file handle
194 */
195 LIST_INSERT_HEAD(nhpp, np, n_hash);
196 if (fhsize > NFS_SMALLFH) {
197 MALLOC_ZONE(np->n_fhp, nfsfh_t *,
198 fhsize, M_NFSBIGFH, M_WAITOK);
199 } else
200 np->n_fhp = &np->n_fh;
201 bcopy((caddr_t)fhp, (caddr_t)np->n_fhp, fhsize);
202 np->n_fhsize = fhsize;
203 *npp = np;
204
205 if (nfs_node_hash_lock < 0)
206 wakeup(&nfs_node_hash_lock);
207 nfs_node_hash_lock = 0;
208
209 /*
210 * Lock the new nfsnode.
211 */
fa4905b1 212 error = vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p);
1c79356b 213
fa4905b1 214 return (error);
1c79356b
A
215}
216
217int
218nfs_inactive(ap)
219 struct vop_inactive_args /* {
220 struct vnode *a_vp;
221 struct proc *a_p;
222 } */ *ap;
223{
224 register struct nfsnode *np;
225 register struct sillyrename *sp;
226 struct proc *p = current_proc(); /* XXX */
227 extern int prtactive;
228 struct ucred *cred;
229
230 np = VTONFS(ap->a_vp);
231 if (prtactive && ap->a_vp->v_usecount != 0)
232 vprint("nfs_inactive: pushing active", ap->a_vp);
233 if (ap->a_vp->v_type != VDIR) {
234 sp = np->n_sillyrename;
235 np->n_sillyrename = (struct sillyrename *)0;
236 } else
237 sp = (struct sillyrename *)0;
238
239 if (sp) {
240 /*
241 * Remove the silly file that was rename'd earlier
242 */
243#if DIAGNOSTIC
244 kprintf("nfs_inactive removing %s, dvp=%x, a_vp=%x, ap=%x, np=%x, sp=%x\n", &sp->s_name[0], (unsigned)sp->s_dvp, (unsigned)ap->a_vp, (unsigned)ap, (unsigned)np, (unsigned)sp);
245#endif
246 /*
247 * We get a reference (vget) to ensure getnewvnode()
248 * doesn't recycle vp while we're asleep awaiting I/O.
249 * Note we don't need the reference unless usecount is
250 * already zero. In the case of a forcible unmount it
251 * wont be zero and doing a vget would fail because
252 * vclean holds VXLOCK.
253 */
254 if (ap->a_vp->v_usecount > 0) {
255 VREF(ap->a_vp);
256 } else if (vget(ap->a_vp, 0, ap->a_p))
257 panic("nfs_inactive: vget failed");
258 (void) nfs_vinvalbuf(ap->a_vp, 0, sp->s_cred, p, 1);
fa4905b1 259 np->n_size = 0;
1c79356b
A
260 ubc_setsize(ap->a_vp, (off_t)0);
261
fa4905b1
A
262 /* We have a problem. The dvp could have gone away on us while
263 * in the unmount path. Thus it appears as VBAD and we cannot
264 * use it. If we tried locking the parent (future), for silly
1c79356b
A
265 * rename files, it is unclear where we would lock. The unmount
266 * code just pulls unlocked vnodes as it goes thru its list and
267 * yanks them. Could unmount be smarter to see if a busy reg vnode has
268 * a parent, and not yank it yet? Put in more passes at unmount
fa4905b1
A
269 * time? In the meantime, just check if it went away on us.
270 * Could have gone away during the nfs_vinvalbuf or ubc_setsize
271 * which block. Or perhaps even before nfs_inactive got called.
1c79356b
A
272 */
273 if ((sp->s_dvp)->v_type != VBAD)
274 nfs_removeit(sp); /* uses the dvp */
275 cred = sp->s_cred;
276 if (cred != NOCRED) {
277 sp->s_cred = NOCRED;
278 crfree(cred);
279 }
280 vrele(sp->s_dvp);
281 FREE_ZONE((caddr_t)sp, sizeof (struct sillyrename), M_NFSREQ);
282 vrele(ap->a_vp);
283 }
284 np->n_flag &= (NMODIFIED | NFLUSHINPROG | NFLUSHWANT | NQNFSEVICTED |
285 NQNFSNONCACHE | NQNFSWRITE);
286 VOP_UNLOCK(ap->a_vp, 0, ap->a_p);
287 return (0);
288}
289
290/*
291 * Reclaim an nfsnode so that it can be used for other purposes.
292 */
293int
294nfs_reclaim(ap)
295 struct vop_reclaim_args /* {
296 struct vnode *a_vp;
297 } */ *ap;
298{
299 register struct vnode *vp = ap->a_vp;
300 register struct nfsnode *np = VTONFS(vp);
301 register struct nfsmount *nmp = VFSTONFS(vp->v_mount);
302 register struct nfsdmap *dp, *dp2;
303 extern int prtactive;
304
305 if (prtactive && vp->v_usecount != 0)
306 vprint("nfs_reclaim: pushing active", vp);
307
308 LIST_REMOVE(np, n_hash);
309
310 /*
311 * In case we block during FREE_ZONEs below, get the entry out
312 * of tbe name cache now so subsequent lookups won't find it.
313 */
314 cache_purge(vp);
315
316 /*
317 * For nqnfs, take it off the timer queue as required.
318 */
319 if ((nmp->nm_flag & NFSMNT_NQNFS) && np->n_timer.cqe_next != 0) {
320 CIRCLEQ_REMOVE(&nmp->nm_timerhead, np, n_timer);
321 }
322
323 /*
324 * Free up any directory cookie structures and
325 * large file handle structures that might be associated with
326 * this nfs node.
327 */
328 if (vp->v_type == VDIR) {
329 dp = np->n_cookies.lh_first;
330 while (dp) {
331 dp2 = dp;
332 dp = dp->ndm_list.le_next;
333 FREE_ZONE((caddr_t)dp2,
334 sizeof (struct nfsdmap), M_NFSDIROFF);
335 }
336 }
337 if (np->n_fhsize > NFS_SMALLFH) {
338 FREE_ZONE((caddr_t)np->n_fhp, np->n_fhsize, M_NFSBIGFH);
339 }
340
341 FREE_ZONE(vp->v_data, sizeof (struct nfsnode), M_NFSNODE);
342 vp->v_data = (void *)0;
343 return (0);
344}
345
1c79356b
A
346/*
347 * Lock an nfsnode
348 */
349int
350nfs_lock(ap)
351 struct vop_lock_args /* {
fa4905b1
A
352 struct vnode *a_vp;
353 int a_flags;
354 struct proc *a_p;
1c79356b
A
355 } */ *ap;
356{
357 register struct vnode *vp = ap->a_vp;
358
359 /*
360 * Ugh, another place where interruptible mounts will get hung.
fa4905b1 361 * If you make this call interruptible, then you have to fix all
1c79356b
A
362 * the VOP_LOCK() calls to expect interruptibility.
363 */
1c79356b 364 if (vp->v_tag == VT_NON)
fa4905b1
A
365 return (ENOENT); /* ??? -- got to check something and error, but what? */
366
367 return(lockmgr(&VTONFS(vp)->n_lock, ap->a_flags, &vp->v_interlock,
368 ap->a_p));
369
1c79356b
A
370}
371
372/*
373 * Unlock an nfsnode
374 */
375int
376nfs_unlock(ap)
fa4905b1
A
377 struct vop_unlock_args /* {
378 struct vnode *a_vp;
379 int a_flags;
380 struct proc *a_p;
381 } */ *ap;
1c79356b 382{
fa4905b1 383 struct vnode *vp = ap->a_vp;
1c79356b 384
fa4905b1
A
385 return (lockmgr(&VTONFS(vp)->n_lock, ap->a_flags | LK_RELEASE,
386 &vp->v_interlock, ap->a_p));
1c79356b
A
387}
388
389/*
390 * Check for a locked nfsnode
391 */
392int
393nfs_islocked(ap)
394 struct vop_islocked_args /* {
395 struct vnode *a_vp;
396 } */ *ap;
397{
fa4905b1
A
398 return (lockstatus(&VTONFS(ap->a_vp)->n_lock));
399
1c79356b 400}
fa4905b1 401
1c79356b
A
402
403/*
404 * Nfs abort op, called after namei() when a CREATE/DELETE isn't actually
405 * done. Currently nothing to do.
406 */
407/* ARGSUSED */
408int
409nfs_abortop(ap)
410 struct vop_abortop_args /* {
411 struct vnode *a_dvp;
412 struct componentname *a_cnp;
413 } */ *ap;
414{
415
416 if ((ap->a_cnp->cn_flags & (HASBUF | SAVESTART)) == HASBUF)
417 FREE_ZONE(ap->a_cnp->cn_pnbuf, ap->a_cnp->cn_pnlen, M_NAMEI);
418 return (0);
419}