]> git.saurik.com Git - apple/xnu.git/blob - bsd/nfs/nfs_node.c
xnu-792.25.20.tar.gz
[apple/xnu.git] / bsd / nfs / nfs_node.c
1 /*
2 * Copyright (c) 2000-2005 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * The contents of this file constitute Original Code as defined in and
7 * are subject to the Apple Public Source License Version 1.1 (the
8 * "License"). You may not use this file except in compliance with the
9 * License. Please obtain a copy of the License at
10 * http://www.apple.com/publicsource and read it before using this file.
11 *
12 * This Original Code and all software distributed under the License are
13 * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
14 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
15 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the
17 * License for the specific language governing rights and limitations
18 * under the License.
19 *
20 * @APPLE_LICENSE_HEADER_END@
21 */
22 /* Copyright (c) 1995 NeXT Computer, Inc. All Rights Reserved */
23 /*
24 * Copyright (c) 1989, 1993
25 * The Regents of the University of California. All rights reserved.
26 *
27 * This code is derived from software contributed to Berkeley by
28 * Rick Macklem at The University of Guelph.
29 *
30 * Redistribution and use in source and binary forms, with or without
31 * modification, are permitted provided that the following conditions
32 * are met:
33 * 1. Redistributions of source code must retain the above copyright
34 * notice, this list of conditions and the following disclaimer.
35 * 2. Redistributions in binary form must reproduce the above copyright
36 * notice, this list of conditions and the following disclaimer in the
37 * documentation and/or other materials provided with the distribution.
38 * 3. All advertising materials mentioning features or use of this software
39 * must display the following acknowledgement:
40 * This product includes software developed by the University of
41 * California, Berkeley and its contributors.
42 * 4. Neither the name of the University nor the names of its contributors
43 * may be used to endorse or promote products derived from this software
44 * without specific prior written permission.
45 *
46 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
47 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
48 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
49 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
50 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
51 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
52 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
53 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
54 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
55 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
56 * SUCH DAMAGE.
57 *
58 * @(#)nfs_node.c 8.6 (Berkeley) 5/22/95
59 * FreeBSD-Id: nfs_node.c,v 1.22 1997/10/28 14:06:20 bde Exp $
60 */
61
62
63 #include <sys/param.h>
64 #include <sys/systm.h>
65 #include <sys/proc.h>
66 #include <sys/kauth.h>
67 #include <sys/mount_internal.h>
68 #include <sys/vnode.h>
69 #include <sys/ubc.h>
70 #include <sys/malloc.h>
71
72 #include <nfs/rpcv2.h>
73 #include <nfs/nfsproto.h>
74 #include <nfs/nfs.h>
75 #include <nfs/nfsnode.h>
76 #include <nfs/nfsmount.h>
77
78 LIST_HEAD(nfsnodehashhead, nfsnode) *nfsnodehashtbl;
79 u_long nfsnodehash;
80
81 lck_grp_t * nfs_node_hash_lck_grp;
82 lck_grp_attr_t * nfs_node_hash_lck_grp_attr;
83 lck_attr_t * nfs_node_hash_lck_attr;
84 lck_mtx_t *nfs_node_hash_mutex;
85
86 /*
87 * Initialize hash links for nfsnodes
88 * and build nfsnode free list.
89 */
90 void
91 nfs_nhinit(void)
92 {
93 nfsnodehashtbl = hashinit(desiredvnodes, M_NFSNODE, &nfsnodehash);
94
95 nfs_node_hash_lck_grp_attr = lck_grp_attr_alloc_init();
96 nfs_node_hash_lck_grp = lck_grp_alloc_init("nfs_node_hash", nfs_node_hash_lck_grp_attr);
97
98 nfs_node_hash_lck_attr = lck_attr_alloc_init();
99
100 nfs_node_hash_mutex = lck_mtx_alloc_init(nfs_node_hash_lck_grp, nfs_node_hash_lck_attr);
101 }
102
103 /*
104 * Compute an entry in the NFS hash table structure
105 */
106 u_long
107 nfs_hash(u_char *fhp, int fhsize)
108 {
109 u_long fhsum;
110 int i;
111
112 fhsum = 0;
113 for (i = 0; i < fhsize; i++)
114 fhsum += *fhp++;
115 return (fhsum);
116 }
117
118 /*
119 * Look up a vnode/nfsnode by file handle.
120 * Callers must check for mount points!!
121 * In all cases, a pointer to a
122 * nfsnode structure is returned.
123 */
124 int
125 nfs_nget(
126 mount_t mntp,
127 vnode_t dvp,
128 struct componentname *cnp,
129 u_char *fhp,
130 int fhsize,
131 struct nfs_vattr *nvap,
132 u_int64_t *xidp,
133 int flags,
134 struct nfsnode **npp)
135 {
136 struct nfsnode *np;
137 struct nfsnodehashhead *nhpp;
138 vnode_t vp, nvp;
139 int error;
140 mount_t mp;
141 struct vnode_fsparam vfsp;
142 uint32_t vid;
143
144 /* Check for unmount in progress */
145 if (!mntp || (mntp->mnt_kern_flag & MNTK_UNMOUNT)) {
146 *npp = 0;
147 return (!mntp ? ENXIO : EPERM);
148 }
149
150 nhpp = NFSNOHASH(nfs_hash(fhp, fhsize));
151 loop:
152 lck_mtx_lock(nfs_node_hash_mutex);
153 for (np = nhpp->lh_first; np != 0; np = np->n_hash.le_next) {
154 mp = (np->n_flag & NINIT) ? np->n_mount : vnode_mount(NFSTOV(np));
155 if (mntp != mp || np->n_fhsize != fhsize ||
156 bcmp(fhp, np->n_fhp, fhsize))
157 continue;
158 /* if the node is still being initialized, sleep on it */
159 if (np->n_flag & NINIT) {
160 np->n_flag |= NWINIT;
161 msleep(np, nfs_node_hash_mutex, PDROP | PINOD, "nfs_nget", 0);
162 goto loop;
163 }
164 vp = NFSTOV(np);
165 vid = vnode_vid(vp);
166 lck_mtx_unlock(nfs_node_hash_mutex);
167 if ((error = vnode_getwithvid(vp, vid))) {
168 /*
169 * If vnode is being reclaimed or has already
170 * changed identity, no need to wait.
171 */
172 return (error);
173 }
174 /* update attributes */
175 error = nfs_loadattrcache(np, nvap, xidp, 0);
176 if (error) {
177 vnode_put(vp);
178 } else {
179 if (dvp && cnp && (flags & NG_MAKEENTRY))
180 cache_enter(dvp, vp, cnp);
181 *npp = np;
182 }
183 return(error);
184 }
185
186 /*
187 * allocate and initialize nfsnode and stick it in the hash
188 * before calling getnewvnode(). Anyone finding it in the
189 * hash before initialization is complete will wait for it.
190 */
191 MALLOC_ZONE(np, struct nfsnode *, sizeof *np, M_NFSNODE, M_WAITOK);
192 if (!np) {
193 lck_mtx_unlock(nfs_node_hash_mutex);
194 *npp = 0;
195 return (ENOMEM);
196 }
197 bzero((caddr_t)np, sizeof *np);
198 np->n_flag |= NINIT;
199 np->n_mount = mntp;
200
201 /* setup node's file handle */
202 if (fhsize > NFS_SMALLFH) {
203 MALLOC_ZONE(np->n_fhp, u_char *,
204 fhsize, M_NFSBIGFH, M_WAITOK);
205 if (!np->n_fhp) {
206 lck_mtx_unlock(nfs_node_hash_mutex);
207 FREE_ZONE(np, sizeof *np, M_NFSNODE);
208 *npp = 0;
209 return (ENOMEM);
210 }
211 } else {
212 np->n_fhp = &np->n_fh[0];
213 }
214 bcopy(fhp, np->n_fhp, fhsize);
215 np->n_fhsize = fhsize;
216
217 /* Insert the nfsnode in the hash queue for its new file handle */
218 np->n_flag |= NHASHED;
219 LIST_INSERT_HEAD(nhpp, np, n_hash);
220
221 /* release lock on hash table */
222 lck_mtx_unlock(nfs_node_hash_mutex);
223
224 /* do initial loading of attributes */
225 error = nfs_loadattrcache(np, nvap, xidp, 1);
226 if (error) {
227 lck_mtx_lock(nfs_node_hash_mutex);
228 LIST_REMOVE(np, n_hash);
229 np->n_flag &= ~(NHASHED|NINIT);
230 if (np->n_flag & NWINIT) {
231 np->n_flag &= ~NWINIT;
232 wakeup((caddr_t)np);
233 }
234 lck_mtx_unlock(nfs_node_hash_mutex);
235 if (np->n_fhsize > NFS_SMALLFH)
236 FREE_ZONE(np->n_fhp, np->n_fhsize, M_NFSBIGFH);
237 FREE_ZONE(np, sizeof *np, M_NFSNODE);
238 *npp = 0;
239 return (error);
240 }
241 np->n_mtime = nvap->nva_mtime;
242 if (nvap->nva_type == VDIR)
243 np->n_ncmtime = nvap->nva_mtime;
244 NMODEINVALIDATE(np);
245
246 /* now, attempt to get a new vnode */
247 vfsp.vnfs_mp = mntp;
248 vfsp.vnfs_vtype = nvap->nva_type;
249 vfsp.vnfs_str = "nfs";
250 vfsp.vnfs_dvp = dvp;
251 vfsp.vnfs_fsnode = np;
252 if (nvap->nva_type == VFIFO)
253 vfsp.vnfs_vops = fifo_nfsv2nodeop_p;
254 else if (nvap->nva_type == VBLK || nvap->nva_type == VCHR)
255 vfsp.vnfs_vops = spec_nfsv2nodeop_p;
256 else
257 vfsp.vnfs_vops = nfsv2_vnodeop_p;
258 vfsp.vnfs_markroot = (flags & NG_MARKROOT) ? 1 : 0;
259 vfsp.vnfs_marksystem = 0;
260 vfsp.vnfs_rdev = 0;
261 vfsp.vnfs_filesize = nvap->nva_size;
262 vfsp.vnfs_cnp = cnp;
263 if (dvp && cnp && (flags & NG_MAKEENTRY))
264 vfsp.vnfs_flags = 0;
265 else
266 vfsp.vnfs_flags = VNFS_NOCACHE;
267 error = vnode_create(VNCREATE_FLAVOR, VCREATESIZE, &vfsp, &nvp);
268 if (error) {
269 lck_mtx_lock(nfs_node_hash_mutex);
270 LIST_REMOVE(np, n_hash);
271 np->n_flag &= ~(NHASHED|NINIT);
272 if (np->n_flag & NWINIT) {
273 np->n_flag &= ~NWINIT;
274 wakeup((caddr_t)np);
275 }
276 lck_mtx_unlock(nfs_node_hash_mutex);
277 if (np->n_fhsize > NFS_SMALLFH)
278 FREE_ZONE(np->n_fhp, np->n_fhsize, M_NFSBIGFH);
279 FREE_ZONE(np, sizeof *np, M_NFSNODE);
280 *npp = 0;
281 return (error);
282 }
283 vp = nvp;
284 np->n_vnode = vp;
285 vnode_addfsref(vp);
286 vnode_settag(vp, VT_NFS); // XXX shouldn't this be a vnode_create() parameter?
287 *npp = np;
288 /* node is now initialized */
289
290 /* check if anyone's waiting on this node */
291 lck_mtx_lock(nfs_node_hash_mutex);
292 np->n_flag &= ~NINIT;
293 if (np->n_flag & NWINIT) {
294 np->n_flag &= ~NWINIT;
295 wakeup((caddr_t)np);
296 }
297 lck_mtx_unlock(nfs_node_hash_mutex);
298
299 return (error);
300 }
301
302
303 int
304 nfs_inactive(ap)
305 struct vnop_inactive_args /* {
306 struct vnodeop_desc *a_desc;
307 vnode_t a_vp;
308 vfs_context_t a_context;
309 } */ *ap;
310 {
311 register struct nfsnode *np;
312 register struct sillyrename *sp;
313
314 np = VTONFS(ap->a_vp);
315 if (vnode_vtype(ap->a_vp) != VDIR) {
316 sp = np->n_sillyrename;
317 np->n_sillyrename = (struct sillyrename *)0;
318 } else
319 sp = (struct sillyrename *)0;
320
321 if (sp) {
322 /*
323 * Remove the silly file that was rename'd earlier
324 */
325 #if DIAGNOSTIC
326 kprintf("nfs_inactive removing %s, dvp=%x, a_vp=%x, ap=%x, np=%x, sp=%x\n",
327 &sp->s_name[0], (unsigned)sp->s_dvp, (unsigned)ap->a_vp, (unsigned)ap,
328 (unsigned)np, (unsigned)sp);
329 #endif
330 nfs_vinvalbuf(ap->a_vp, 0, sp->s_cred, vfs_context_proc(ap->a_context), 1);
331 np->n_size = 0;
332 ubc_setsize(ap->a_vp, (off_t)0);
333 nfs_removeit(sp);
334 /*
335 * remove nfsnode from hash now so we can't accidentally find it
336 * again if another object gets created with the same filehandle
337 * before this vnode gets reclaimed
338 */
339 lck_mtx_lock(nfs_node_hash_mutex);
340 LIST_REMOVE(np, n_hash);
341 np->n_flag &= ~NHASHED;
342 lck_mtx_unlock(nfs_node_hash_mutex);
343 if (IS_VALID_CRED(sp->s_cred)) {
344 kauth_cred_unref(&sp->s_cred);
345 }
346 vnode_rele(sp->s_dvp);
347 FREE_ZONE((caddr_t)sp, sizeof (struct sillyrename), M_NFSREQ);
348 vnode_recycle(ap->a_vp);
349 }
350 /* clear all flags other than these */
351 np->n_flag &= (NMODIFIED | NFLUSHINPROG | NFLUSHWANT | NHASHED);
352 return (0);
353 }
354
355 /*
356 * Reclaim an nfsnode so that it can be used for other purposes.
357 */
358 int
359 nfs_reclaim(ap)
360 struct vnop_reclaim_args /* {
361 struct vnodeop_desc *a_desc;
362 vnode_t a_vp;
363 vfs_context_t a_context;
364 } */ *ap;
365 {
366 vnode_t vp = ap->a_vp;
367 struct nfsnode *np = VTONFS(vp);
368 struct nfsdmap *dp, *dp2;
369
370 vnode_removefsref(vp);
371
372 if (np->n_flag & NHASHED) {
373 lck_mtx_lock(nfs_node_hash_mutex);
374 LIST_REMOVE(np, n_hash);
375 np->n_flag &= ~NHASHED;
376 lck_mtx_unlock(nfs_node_hash_mutex);
377 }
378
379 /*
380 * Free up any directory cookie structures and
381 * large file handle structures that might be associated with
382 * this nfs node.
383 */
384 if (vnode_vtype(vp) == VDIR) {
385 dp = np->n_cookies.lh_first;
386 while (dp) {
387 dp2 = dp;
388 dp = dp->ndm_list.le_next;
389 FREE_ZONE((caddr_t)dp2,
390 sizeof (struct nfsdmap), M_NFSDIROFF);
391 }
392 }
393 if (np->n_fhsize > NFS_SMALLFH) {
394 FREE_ZONE(np->n_fhp, np->n_fhsize, M_NFSBIGFH);
395 }
396 vnode_clearfsnode(vp);
397
398 FREE_ZONE(np, sizeof(struct nfsnode), M_NFSNODE);
399 return (0);
400 }
401