]> git.saurik.com Git - apple/xnu.git/blob - bsd/nfs/nfs_node.c
xnu-517.3.15.tar.gz
[apple/xnu.git] / bsd / nfs / nfs_node.c
1 /*
2 * Copyright (c) 2000-2003 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved.
7 *
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
17 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
18 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
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.
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 LIST_HEAD(nfsnodehashhead, nfsnode) *nfsnodehashtbl;
81 u_long nfsnodehash;
82
83 #define TRUE 1
84 #define FALSE 0
85
86 /*
87 * Initialize hash links for nfsnodes
88 * and build nfsnode free list.
89 */
90 void
91 nfs_nhinit()
92 {
93 nfsnodehashtbl = hashinit(desiredvnodes, M_NFSNODE, &nfsnodehash);
94 }
95
96 /*
97 * Compute an entry in the NFS hash table structure
98 */
99 u_long
100 nfs_hash(fhp, fhsize)
101 register nfsfh_t *fhp;
102 int fhsize;
103 {
104 register u_char *fhpp;
105 register u_long fhsum;
106 register int i;
107
108 fhpp = &fhp->fh_bytes[0];
109 fhsum = 0;
110 for (i = 0; i < fhsize; i++)
111 fhsum += *fhpp++;
112 return (fhsum);
113 }
114
115 /*
116 * Look up a vnode/nfsnode by file handle.
117 * Callers must check for mount points!!
118 * In all cases, a pointer to a
119 * nfsnode structure is returned.
120 */
121 int nfs_node_hash_lock;
122
123 int
124 nfs_nget(mntp, fhp, fhsize, npp)
125 struct mount *mntp;
126 register nfsfh_t *fhp;
127 int fhsize;
128 struct nfsnode **npp;
129 {
130 struct proc *p = current_proc(); /* XXX */
131 struct nfsnode *np;
132 struct nfsnodehashhead *nhpp;
133 register struct vnode *vp;
134 struct vnode *nvp;
135 int error;
136 struct mount *mp;
137
138 /* Check for unmount in progress */
139 if (!mntp || (mntp->mnt_kern_flag & MNTK_UNMOUNT)) {
140 *npp = 0;
141 return (!mntp ? ENXIO : EPERM);
142 }
143
144 nhpp = NFSNOHASH(nfs_hash(fhp, fhsize));
145 loop:
146 for (np = nhpp->lh_first; np != 0; np = np->n_hash.le_next) {
147 mp = (np->n_flag & NINIT) ? np->n_mount : NFSTOV(np)->v_mount;
148 if (mntp != mp || np->n_fhsize != fhsize ||
149 bcmp((caddr_t)fhp, (caddr_t)np->n_fhp, fhsize))
150 continue;
151 /* if the node is still being initialized, sleep on it */
152 if (np->n_flag & NINIT) {
153 np->n_flag |= NWINIT;
154 tsleep(np, PINOD, "nfsngt", 0);
155 goto loop;
156 }
157 vp = NFSTOV(np);
158 if (vget(vp, LK_EXCLUSIVE, p))
159 goto loop;
160 *npp = np;
161 return(0);
162 }
163 /*
164 * Obtain a lock to prevent a race condition if the getnewvnode()
165 * or MALLOC() below happens to block.
166 */
167 if (nfs_node_hash_lock) {
168 while (nfs_node_hash_lock) {
169 nfs_node_hash_lock = -1;
170 tsleep(&nfs_node_hash_lock, PVM, "nfsngt", 0);
171 }
172 goto loop;
173 }
174 nfs_node_hash_lock = 1;
175
176 /*
177 * allocate and initialize nfsnode and stick it in the hash
178 * before calling getnewvnode(). Anyone finding it in the
179 * hash before initialization is complete will wait for it.
180 */
181 MALLOC_ZONE(np, struct nfsnode *, sizeof *np, M_NFSNODE, M_WAITOK);
182 bzero((caddr_t)np, sizeof *np);
183 np->n_flag |= NINIT;
184 np->n_mount = mntp;
185 lockinit(&np->n_lock, PINOD, "nfsnode", 0, 0);
186 /* lock the new nfsnode */
187 lockmgr(&np->n_lock, LK_EXCLUSIVE, NULL, p);
188
189 /* Insert the nfsnode in the hash queue for its new file handle */
190 if (fhsize > NFS_SMALLFH) {
191 MALLOC_ZONE(np->n_fhp, nfsfh_t *,
192 fhsize, M_NFSBIGFH, M_WAITOK);
193 } else
194 np->n_fhp = &np->n_fh;
195 bcopy((caddr_t)fhp, (caddr_t)np->n_fhp, fhsize);
196 np->n_fhsize = fhsize;
197 LIST_INSERT_HEAD(nhpp, np, n_hash);
198 np->n_flag |= NHASHED;
199
200 /* release lock on hash table */
201 if (nfs_node_hash_lock < 0)
202 wakeup(&nfs_node_hash_lock);
203 nfs_node_hash_lock = 0;
204
205 /* now, attempt to get a new vnode */
206 error = getnewvnode(VT_NFS, mntp, nfsv2_vnodeop_p, &nvp);
207 if (error) {
208 LIST_REMOVE(np, n_hash);
209 np->n_flag &= ~NHASHED;
210 if (np->n_fhsize > NFS_SMALLFH)
211 FREE_ZONE((caddr_t)np->n_fhp, np->n_fhsize, M_NFSBIGFH);
212 FREE_ZONE(np, sizeof *np, M_NFSNODE);
213 *npp = 0;
214 return (error);
215 }
216 vp = nvp;
217 vp->v_data = np;
218 np->n_vnode = vp;
219 *npp = np;
220
221 /* node is now initialized, check if anyone's waiting for it */
222 np->n_flag &= ~NINIT;
223 if (np->n_flag & NWINIT) {
224 np->n_flag &= ~NWINIT;
225 wakeup((caddr_t)np);
226 }
227
228 return (error);
229 }
230
231 int
232 nfs_inactive(ap)
233 struct vop_inactive_args /* {
234 struct vnode *a_vp;
235 struct proc *a_p;
236 } */ *ap;
237 {
238 register struct nfsnode *np;
239 register struct sillyrename *sp;
240 struct proc *p = current_proc(); /* XXX */
241 extern int prtactive;
242 struct ucred *cred;
243
244 np = VTONFS(ap->a_vp);
245 if (prtactive && ap->a_vp->v_usecount != 0)
246 vprint("nfs_inactive: pushing active", ap->a_vp);
247 if (ap->a_vp->v_type != VDIR) {
248 sp = np->n_sillyrename;
249 np->n_sillyrename = (struct sillyrename *)0;
250 } else
251 sp = (struct sillyrename *)0;
252
253 if (sp) {
254 /*
255 * Remove the silly file that was rename'd earlier
256 */
257 #if DIAGNOSTIC
258 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);
259 #endif
260 (void) nfs_vinvalbuf(ap->a_vp, 0, sp->s_cred, p, 1);
261 np->n_size = 0;
262 ubc_setsize(ap->a_vp, (off_t)0);
263 nfs_removeit(sp);
264 /*
265 * remove nfsnode from hash now so we can't accidentally find it
266 * again if another object gets created with the same filehandle
267 * before this vnode gets reclaimed
268 */
269 LIST_REMOVE(np, n_hash);
270 np->n_flag &= ~NHASHED;
271 cred = sp->s_cred;
272 if (cred != NOCRED) {
273 sp->s_cred = NOCRED;
274 crfree(cred);
275 }
276 vrele(sp->s_dvp);
277 FREE_ZONE((caddr_t)sp, sizeof (struct sillyrename), M_NFSREQ);
278 }
279 np->n_flag &= (NMODIFIED | NFLUSHINPROG | NFLUSHWANT | NQNFSEVICTED |
280 NQNFSNONCACHE | NQNFSWRITE | NHASHED);
281 VOP_UNLOCK(ap->a_vp, 0, ap->a_p);
282 return (0);
283 }
284
285 /*
286 * Reclaim an nfsnode so that it can be used for other purposes.
287 */
288 int
289 nfs_reclaim(ap)
290 struct vop_reclaim_args /* {
291 struct vnode *a_vp;
292 } */ *ap;
293 {
294 register struct vnode *vp = ap->a_vp;
295 register struct nfsnode *np = VTONFS(vp);
296 register struct nfsmount *nmp = VFSTONFS(vp->v_mount);
297 register struct nfsdmap *dp, *dp2;
298 extern int prtactive;
299
300 if (prtactive && vp->v_usecount != 0)
301 vprint("nfs_reclaim: pushing active", vp);
302
303 if (np->n_flag & NHASHED) {
304 LIST_REMOVE(np, n_hash);
305 np->n_flag &= ~NHASHED;
306 }
307
308 /*
309 * In case we block during FREE_ZONEs below, get the entry out
310 * of tbe name cache now so subsequent lookups won't find it.
311 */
312 cache_purge(vp);
313
314 /*
315 * For nqnfs, take it off the timer queue as required.
316 */
317 if ((nmp->nm_flag & NFSMNT_NQNFS) && np->n_timer.cqe_next != 0) {
318 CIRCLEQ_REMOVE(&nmp->nm_timerhead, np, n_timer);
319 }
320
321 /*
322 * Free up any directory cookie structures and
323 * large file handle structures that might be associated with
324 * this nfs node.
325 */
326 if (vp->v_type == VDIR) {
327 dp = np->n_cookies.lh_first;
328 while (dp) {
329 dp2 = dp;
330 dp = dp->ndm_list.le_next;
331 FREE_ZONE((caddr_t)dp2,
332 sizeof (struct nfsdmap), M_NFSDIROFF);
333 }
334 }
335 if (np->n_fhsize > NFS_SMALLFH) {
336 FREE_ZONE((caddr_t)np->n_fhp, np->n_fhsize, M_NFSBIGFH);
337 }
338
339 FREE_ZONE(vp->v_data, sizeof (struct nfsnode), M_NFSNODE);
340 vp->v_data = (void *)0;
341 return (0);
342 }
343
344 /*
345 * Lock an nfsnode
346 */
347 int
348 nfs_lock(ap)
349 struct vop_lock_args /* {
350 struct vnode *a_vp;
351 int a_flags;
352 struct proc *a_p;
353 } */ *ap;
354 {
355 register struct vnode *vp = ap->a_vp;
356
357 /*
358 * Ugh, another place where interruptible mounts will get hung.
359 * If you make this call interruptible, then you have to fix all
360 * the VOP_LOCK() calls to expect interruptibility.
361 */
362 if (vp->v_tag == VT_NON)
363 return (ENOENT); /* ??? -- got to check something and error, but what? */
364
365 return(lockmgr(&VTONFS(vp)->n_lock, ap->a_flags, &vp->v_interlock,
366 ap->a_p));
367
368 }
369
370 /*
371 * Unlock an nfsnode
372 */
373 int
374 nfs_unlock(ap)
375 struct vop_unlock_args /* {
376 struct vnode *a_vp;
377 int a_flags;
378 struct proc *a_p;
379 } */ *ap;
380 {
381 struct vnode *vp = ap->a_vp;
382
383 return (lockmgr(&VTONFS(vp)->n_lock, ap->a_flags | LK_RELEASE,
384 &vp->v_interlock, ap->a_p));
385 }
386
387 /*
388 * Check for a locked nfsnode
389 */
390 int
391 nfs_islocked(ap)
392 struct vop_islocked_args /* {
393 struct vnode *a_vp;
394 } */ *ap;
395 {
396 return (lockstatus(&VTONFS(ap->a_vp)->n_lock));
397
398 }