]> git.saurik.com Git - apple/xnu.git/blame - bsd/miscfs/nullfs/null_subr.c
xnu-3789.1.32.tar.gz
[apple/xnu.git] / bsd / miscfs / nullfs / null_subr.c
CommitLineData
39037602
A
1/*
2 * Copyright (c) 2016 Apple Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24/*-
25 * Portions Copyright (c) 1992, 1993
26 * The Regents of the University of California. All rights reserved.
27 *
28 * This code is derived from software donated to Berkeley by
29 * Jan-Simon Pendry.
30 *
31 * Redistribution and use in source and binary forms, with or without
32 * modification, are permitted provided that the following conditions
33 * are met:
34 * 1. Redistributions of source code must retain the above copyright
35 * notice, this list of conditions and the following disclaimer.
36 * 2. Redistributions in binary form must reproduce the above copyright
37 * notice, this list of conditions and the following disclaimer in the
38 * documentation and/or other materials provided with the distribution.
39 * 4. Neither the name of the University nor the names of its contributors
40 * may be used to endorse or promote products derived from this software
41 * without specific prior written permission.
42 *
43 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
44 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
45 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
46 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
47 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
48 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
49 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
50 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
51 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
52 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
53 * SUCH DAMAGE.
54 *
55 * @(#)null_subr.c 8.7 (Berkeley) 5/14/95
56 *
57 * $FreeBSD$
58 */
59#include <sys/param.h>
60#include <sys/systm.h>
61#include <sys/kernel.h>
62#include <sys/lock.h>
63#include <sys/malloc.h>
64#include <sys/mount.h>
65#include <sys/proc.h>
66#include <sys/vnode.h>
67
68#include "nullfs.h"
69
70/*
71 * Null layer cache:
72 * Each cache entry holds a reference to the lower vnode
73 * along with a pointer to the alias vnode. When an
74 * entry is added the lower vnode is VREF'd. When the
75 * alias is removed the lower vnode is vrele'd.
76 */
77
78#define NULL_HASH_SIZE (desiredvnodes / 10)
79
80/* osx doesn't really have the functionality freebsd uses here..gonna try this
81 * hacked hash...*/
82#define NULL_NHASH(vp) (&null_node_hashtbl[((((uintptr_t)vp) >> vnsz2log) + (uintptr_t)vnode_mount(vp)) & null_hash_mask])
83
84static LIST_HEAD(null_node_hashhead, null_node) * null_node_hashtbl;
85static lck_mtx_t null_hashmtx;
86static lck_attr_t * null_hashlck_attr;
87static lck_grp_t * null_hashlck_grp;
88static lck_grp_attr_t * null_hashlck_grp_attr;
89static u_long null_hash_mask;
90
91/* os x doesn't have hashes built into vnode. gonna try doing what freebsd does
92 anyway
93 Don't want to create a dependency on vnode_internal.h and the real struct
94 vnode.
95 9 is an eyeball of the log 2 size of vnode */
96static int vnsz2log = 9;
97
98static int null_hashins(struct mount *, struct null_node *, struct vnode **);
99
100int
101nullfs_init_lck(lck_mtx_t * lck)
102{
103 int error = 1;
104 if (lck && null_hashlck_grp && null_hashlck_attr) {
105 lck_mtx_init(lck, null_hashlck_grp, null_hashlck_attr);
106 error = 0;
107 }
108 return error;
109}
110
111int
112nullfs_destroy_lck(lck_mtx_t * lck)
113{
114 int error = 1;
115 if (lck && null_hashlck_grp) {
116 lck_mtx_destroy(lck, null_hashlck_grp);
117 error = 0;
118 }
119 return error;
120}
121
122/*
123 * Initialise cache headers
124 */
125int
126nullfs_init(__unused struct vfsconf * vfsp)
127{
128 NULLFSDEBUG("%s\n", __FUNCTION__);
129
130 /* assuming for now that this happens immediately and by default after fs
131 * installation */
132 null_hashlck_grp_attr = lck_grp_attr_alloc_init();
133 if (null_hashlck_grp_attr == NULL) {
134 goto error;
135 }
136 null_hashlck_grp = lck_grp_alloc_init("com.apple.filesystems.nullfs", null_hashlck_grp_attr);
137 if (null_hashlck_grp == NULL) {
138 goto error;
139 }
140 null_hashlck_attr = lck_attr_alloc_init();
141 if (null_hashlck_attr == NULL) {
142 goto error;
143 }
144
145 lck_mtx_init(&null_hashmtx, null_hashlck_grp, null_hashlck_attr);
146 null_node_hashtbl = hashinit(NULL_HASH_SIZE, M_TEMP, &null_hash_mask);
147 NULLFSDEBUG("%s finished\n", __FUNCTION__);
148 return (0);
149error:
150 printf("NULLFS: failed to get lock element\n");
151 if (null_hashlck_grp_attr) {
152 lck_grp_attr_free(null_hashlck_grp_attr);
153 null_hashlck_grp_attr = NULL;
154 }
155 if (null_hashlck_grp) {
156 lck_grp_free(null_hashlck_grp);
157 null_hashlck_grp = NULL;
158 }
159 if (null_hashlck_attr) {
160 lck_attr_free(null_hashlck_attr);
161 null_hashlck_attr = NULL;
162 }
163 return KERN_FAILURE;
164}
165
166int
167nullfs_uninit()
168{
169 /* This gets called when the fs is uninstalled, there wasn't an exact
170 * equivalent in vfsops */
171 lck_mtx_destroy(&null_hashmtx, null_hashlck_grp);
172 FREE(null_node_hashtbl, M_TEMP);
173 if (null_hashlck_grp_attr) {
174 lck_grp_attr_free(null_hashlck_grp_attr);
175 null_hashlck_grp_attr = NULL;
176 }
177 if (null_hashlck_grp) {
178 lck_grp_free(null_hashlck_grp);
179 null_hashlck_grp = NULL;
180 }
181 if (null_hashlck_attr) {
182 lck_attr_free(null_hashlck_attr);
183 null_hashlck_attr = NULL;
184 }
185 return (0);
186}
187
188/*
189 * Find the nullfs vnode mapped to lowervp. Return it in *vpp with an iocount if found.
190 * Return 0 on success. On failure *vpp will be null and a non-zero error code will be returned.
191 */
192int
193null_hashget(struct mount * mp, struct vnode * lowervp, struct vnode ** vpp)
194{
195 struct null_node_hashhead * hd;
196 struct null_node * a;
197 struct vnode * vp;
198 int error = ENOENT;
199
200 /*
201 * Find hash base, and then search the (two-way) linked
202 * list looking for a null_node structure which is referencing
203 * the lower vnode. We only give up our reference at reclaim so
204 * just check whether the lowervp has gotten pulled from under us
205 */
206 hd = NULL_NHASH(lowervp);
207 lck_mtx_lock(&null_hashmtx);
208 LIST_FOREACH(a, hd, null_hash)
209 {
210 if (a->null_lowervp == lowervp && vnode_mount(NULLTOV(a)) == mp) {
211 vp = NULLTOV(a);
212 if (a->null_lowervid != vnode_vid(lowervp)) {
213 /*lowervp has reved */
214 error = EIO;
215 } else {
216 /* if we found something then get an iocount on it */
217 error = vnode_getwithvid(vp, a->null_myvid);
218 if (error == 0) {
219 *vpp = vp;
220 }
221 }
222 break;
223 }
224 }
225 lck_mtx_unlock(&null_hashmtx);
226 return error;
227}
228
229/*
230 * Act like null_hashget, but add passed null_node to hash if no existing
231 * node found.
232 */
233static int
234null_hashins(struct mount * mp, struct null_node * xp, struct vnode ** vpp)
235{
236 struct null_node_hashhead * hd;
237 struct null_node * oxp;
238 struct vnode * ovp;
239 int error = 0;
240
241 hd = NULL_NHASH(xp->null_lowervp);
242 lck_mtx_lock(&null_hashmtx);
243 LIST_FOREACH(oxp, hd, null_hash)
244 {
245 if (oxp->null_lowervp == xp->null_lowervp && vnode_mount(NULLTOV(oxp)) == mp) {
246 /*
247 * See null_hashget for a description of this
248 * operation.
249 */
250 ovp = NULLTOV(oxp);
251 if (oxp->null_lowervid != vnode_vid(oxp->null_lowervp)) {
252 /*vp doesn't exist so return null (not sure we are actually gonna catch
253 recycle right now
254 This is an exceptional case right now, it suggests the vnode we are
255 trying to add has been recycled
256 don't add it.*/
257 error = EIO;
258 goto end;
259 }
260 /* if we found something in the hash map then grab an iocount */
261 error = vnode_getwithvid(ovp, oxp->null_myvid);
262 if (error == 0) {
263 *vpp = ovp;
264 }
265 goto end;
266 }
267 }
268 /* if it wasn't in the hash map then the vnode pointed to by xp already has a
269 * iocount so don't bother */
270 LIST_INSERT_HEAD(hd, xp, null_hash);
271 xp->null_flags |= NULL_FLAG_HASHED;
272end:
273 lck_mtx_unlock(&null_hashmtx);
274 return error;
275}
276
277/*
278 * Remove node from hash.
279 */
280void
281null_hashrem(struct null_node * xp)
282{
283 lck_mtx_lock(&null_hashmtx);
284 LIST_REMOVE(xp, null_hash);
285 lck_mtx_unlock(&null_hashmtx);
286}
287
288static struct null_node *
289null_nodecreate(struct vnode * lowervp)
290{
291 struct null_node * xp;
292
293 MALLOC(xp, struct null_node *, sizeof(struct null_node), M_TEMP, M_WAITOK | M_ZERO);
294 if (xp != NULL) {
295 if (lowervp) {
296 xp->null_lowervp = lowervp;
297 xp->null_lowervid = vnode_vid(lowervp);
298 }
299 }
300 return xp;
301}
302
303/* assumption is that vnode has iocount on it after vnode create */
304int
305null_getnewvnode(
306 struct mount * mp, struct vnode * lowervp, struct vnode * dvp, struct vnode ** vpp, struct componentname * cnp, int root)
307{
308 struct vnode_fsparam vnfs_param;
309 int error = 0;
310 enum vtype type = VDIR;
311 struct null_node * xp = null_nodecreate(lowervp);
312
313 if (xp == NULL) {
314 return ENOMEM;
315 }
316
317 if (lowervp) {
318 type = vnode_vtype(lowervp);
319 }
320
321 vnfs_param.vnfs_mp = mp;
322 vnfs_param.vnfs_vtype = type;
323 vnfs_param.vnfs_str = "nullfs";
324 vnfs_param.vnfs_dvp = dvp;
325 vnfs_param.vnfs_fsnode = (void *)xp;
326 vnfs_param.vnfs_vops = nullfs_vnodeop_p;
327 vnfs_param.vnfs_markroot = root;
328 vnfs_param.vnfs_marksystem = 0;
329 vnfs_param.vnfs_rdev = 0;
330 vnfs_param.vnfs_filesize = 0; // set this to 0 since we should only be shadowing non-regular files
331 vnfs_param.vnfs_cnp = cnp;
332 vnfs_param.vnfs_flags = VNFS_ADDFSREF;
333
334 error = vnode_create(VNCREATE_FLAVOR, VCREATESIZE, &vnfs_param, vpp);
335 if (error == 0) {
336 xp->null_vnode = *vpp;
337 xp->null_myvid = vnode_vid(*vpp);
338 vnode_settag(*vpp, VT_NULL);
339 } else {
340 FREE(xp, M_TEMP);
341 }
342 return error;
343}
344
345/*
346 * Make a new or get existing nullfs node.
347 * Vp is the alias vnode, lowervp is the lower vnode.
348 *
349 * lowervp is assumed to have an iocount on it from the caller
350 */
351int
352null_nodeget(
353 struct mount * mp, struct vnode * lowervp, struct vnode * dvp, struct vnode ** vpp, struct componentname * cnp, int root)
354{
355 struct vnode * vp;
356 int error;
357
358 /* Lookup the hash firstly. */
359 error = null_hashget(mp, lowervp, vpp);
360 /* ENOENT means it wasn't found, EIO is a failure we should bail from, 0 is it
361 * was found */
362 if (error != ENOENT) {
363 /* null_hashget checked the vid, so if we got something here its legit to
364 * the best of our knowledge*/
365 /* if we found something then there is an iocount on vpp,
366 if we didn't find something then vpp shouldn't be used by the caller */
367 return error;
368 }
369
370 /*
371 * We do not serialize vnode creation, instead we will check for
372 * duplicates later, when adding new vnode to hash.
373 */
374 error = vnode_ref(lowervp); // take a ref on lowervp so we let the system know we care about it
375 if(error)
376 {
377 // Failed to get a reference on the lower vp so bail. Lowervp may be gone already.
378 return error;
379 }
380
381 error = null_getnewvnode(mp, lowervp, dvp, &vp, cnp, root);
382
383 if (error) {
384 vnode_rele(lowervp);
385 return (error);
386 }
387
388 /*
389 * Atomically insert our new node into the hash or vget existing
390 * if someone else has beaten us to it.
391 */
392 error = null_hashins(mp, VTONULL(vp), vpp);
393 if (error || *vpp != NULL) {
394 /* recycle will call reclaim which will get rid of the internals */
395 vnode_recycle(vp);
396 vnode_put(vp);
397 /* if we found vpp, then null_hashins put an iocount on it */
398 return error;
399 }
400
401 /* vp has an iocount from null_getnewvnode */
402 *vpp = vp;
403
404 return (0);
405}