]>
git.saurik.com Git - apple/hfs.git/blob - livefiles_hfs_plugin/lf_hfs_chash.c
1 /* Copyright © 2017-2018 Apple Inc. All rights reserved.
6 * Created by Or Haimovich on 18/3/18.
9 #include "lf_hfs_chash.h"
10 #include "lf_hfs_cnode.h"
11 #include "lf_hfs_locks.h"
12 #include "lf_hfs_utils.h"
13 #include "lf_hfs_logger.h"
14 #include "lf_hfs_vfsutils.h"
16 #define DESIRED_VNODES (128) /* number of vnodes desired */
17 #define CNODEHASH(hfsmp, inum) (&hfsmp->hfs_cnodehashtbl[(inum) & hfsmp->hfs_cnodehash])
20 hfs_chash_wait ( struct hfsmount
* hfsmp
, struct cnode
* cp
)
22 SET ( cp
-> c_hflag
, H_WAITING
);
23 pthread_cond_wait (& cp
-> c_cacsh_cond
, & hfsmp
-> hfs_chash_mutex
);
27 hfs_chash_broadcast_and_unlock ( struct hfsmount
* hfsmp
, struct cnode
* cp
)
29 pthread_cond_signal (& cp
-> c_cacsh_cond
);
30 hfs_chash_unlock ( hfsmp
);
34 hfs_chash_raise_OpenLookupCounter ( struct cnode
* cp
)
36 if (! cp
|| cp
-> uOpenLookupRefCount
== UINT32_MAX
)
38 LFHFS_LOG ( LEVEL_ERROR
,
39 "hfs_chash_raise_OpenLookupCounter:"
40 "cp[ %p ] is NULL or reached max Open Lookup Counter" , cp
);
43 cp
-> uOpenLookupRefCount
++;
47 hfs_chash_lower_OpenLookupCounter ( struct cnode
* cp
)
49 if ( cp
-> uOpenLookupRefCount
== 0 )
51 LFHFS_LOG ( LEVEL_ERROR
, "hfs_chash_lower_OpenLookupCounter: reached min Open Lookup Counter \n " );
54 cp
-> uOpenLookupRefCount
--;
58 * Initialize cnode hash table.
65 void hfs_chash_lock ( struct hfsmount
* hfsmp
)
67 lf_lck_mtx_lock (& hfsmp
-> hfs_chash_mutex
);
70 void hfs_chash_lock_spin ( struct hfsmount
* hfsmp
)
72 lf_lck_mtx_lock_spin (& hfsmp
-> hfs_chash_mutex
);
76 void hfs_chash_unlock ( struct hfsmount
* hfsmp
)
78 lf_lck_mtx_unlock (& hfsmp
-> hfs_chash_mutex
);
82 hfs_chashinit_finish ( struct hfsmount
* hfsmp
)
84 lf_lck_mtx_init (& hfsmp
-> hfs_chash_mutex
);
85 hfsmp
-> hfs_cnodehashtbl
= hashinit ( DESIRED_VNODES
/ 4 , & hfsmp
-> hfs_cnodehash
);
89 hfs_delete_chash ( struct hfsmount
* hfsmp
)
92 hfs_chash_lock_spin ( hfsmp
);
94 for ( ino_t inum
= 0 ; inum
< ( DESIRED_VNODES
/ 4 ); inum
++)
96 for ( cp
= CNODEHASH ( hfsmp
, inum
)-> lh_first
; cp
; cp
= cp
-> c_hash
. le_next
) {
97 LFHFS_LOG ( LEVEL_ERROR
, "hfs_delete_chash: Cnode for file [ %s ], cnid: [ %d ] with open count [ %d ] left in the cache \n " , cp
-> c_desc
. cd_nameptr
, cp
-> c_desc
. cd_cnid
, cp
-> uOpenLookupRefCount
);
102 hfs_chash_unlock ( hfsmp
);
103 lf_lck_mtx_destroy (& hfsmp
-> hfs_chash_mutex
);
104 hfs_free ( hfsmp
-> hfs_cnodehashtbl
);
108 * Use the device, fileid pair to find the incore cnode.
109 * If no cnode if found one is created
111 * If it is in core, but locked, wait for it.
113 * If the cnode is C_DELETED, then return NULL since that
114 * inum is no longer valid for lookups (open-unlinked file).
116 * If the cnode is C_DELETED but also marked C_RENAMED, then that means
117 * the cnode was renamed over and a new entry exists in its place. The caller
118 * should re-drive the lookup to get the newer entry. In that case, we'll still
119 * return NULL for the cnode, but also return GNV_CHASH_RENAMED in the output flags
120 * of this function to indicate the caller that they should re-drive.
123 hfs_chash_getcnode ( struct hfsmount
* hfsmp
, ino_t inum
, struct vnode
** vpp
, int wantrsrc
, int skiplock
, int * out_flags
, int * hflags
)
126 struct cnode
* ncp
= NULL
;
130 * Go through the hash list
131 * If a cnode is in the process of being cleaned out or being
132 * allocated, wait for it to be finished and then try again.
135 hfs_chash_lock_spin ( hfsmp
);
137 for ( cp
= CNODEHASH ( hfsmp
, inum
)-> lh_first
; cp
; cp
= cp
-> c_hash
. le_next
)
139 if ( cp
-> c_fileid
!= inum
)
144 * Wait if cnode is being created, attached to or reclaimed.
146 if ( ISSET ( cp
-> c_hflag
, H_ALLOC
| H_ATTACH
| H_TRANSIT
))
148 hfs_chash_wait ( hfsmp
, cp
);
152 vp
= wantrsrc
? cp
-> c_rsrc_vp
: cp
-> c_vp
;
156 * The desired vnode isn't there so tag the cnode.
158 SET ( cp
-> c_hflag
, H_ATTACH
);
165 * someone else won the race to create
166 * this cnode and add it to the hash
167 * just dump our allocation
175 if ( hfs_lock ( cp
, HFS_TRY_EXCLUSIVE_LOCK
, HFS_LOCK_ALLOW_NOEXISTS
))
177 SET ( cp
-> c_hflag
, H_WAITING
);
178 hfs_chash_broadcast_and_unlock ( hfsmp
, cp
);
183 vp
= wantrsrc
? cp
-> c_rsrc_vp
: cp
-> c_vp
;
186 * Skip cnodes that are not in the name space anymore
187 * we need to check with the cnode lock held because
188 * we may have blocked acquiring the vnode ref or the
189 * lock on the cnode which would allow the node to be
192 * Don't return a cnode in this case since the inum
193 * is no longer valid for lookups.
195 if ((( cp
-> c_flag
& ( C_NOEXISTS
| C_DELETED
)) && ! wantrsrc
) ||
196 ( cp
-> uOpenLookupRefCount
== 0 ) ||
197 ( vp
-> uValidNodeMagic1
== VALID_NODE_BADMAGIC
) ||
198 ( vp
-> uValidNodeMagic2
== VALID_NODE_BADMAGIC
))
201 if ( cp
-> c_flag
& C_RENAMED
)
214 hfs_chashwakeup ( hfsmp
, cp
, H_ATTACH
);
215 * hflags
&= ~ H_ATTACH
;
222 * out_flags
= GNV_CHASH_RENAMED
;
226 if ( cp
) hfs_chash_raise_OpenLookupCounter ( cp
);
227 hfs_chash_broadcast_and_unlock ( hfsmp
, cp
);
233 * Allocate a new cnode
235 if ( skiplock
&& ! wantrsrc
)
237 LFHFS_LOG ( LEVEL_ERROR
, "hfs_chash_getcnode: should never get here when skiplock is set \n " );
243 hfs_chash_unlock ( hfsmp
);
245 ncp
= hfs_mallocz ( sizeof ( struct cnode
));
251 * since we dropped the chash lock,
252 * we need to go back and re-verify
253 * that this node hasn't come into
259 bzero ( ncp
, sizeof (* ncp
));
261 SET ( ncp
-> c_hflag
, H_ALLOC
);
263 ncp
-> c_fileid
= ( cnid_t
) inum
;
264 TAILQ_INIT (& ncp
-> c_hintlist
); /* make the list empty */
265 TAILQ_INIT (& ncp
-> c_originlist
);
267 lf_lck_rw_init (& ncp
-> c_rwlock
);
268 lf_cond_init (& ncp
-> c_cacsh_cond
);
272 ( void ) hfs_lock ( ncp
, HFS_EXCLUSIVE_LOCK
, HFS_LOCK_DEFAULT
);
275 /* Insert the new cnode with it's H_ALLOC flag set */
276 LIST_INSERT_HEAD ( CNODEHASH ( hfsmp
, inum
), ncp
, c_hash
);
277 hfs_chash_raise_OpenLookupCounter ( ncp
);
278 hfs_chash_unlock ( hfsmp
);
284 hfs_chashwakeup ( struct hfsmount
* hfsmp
, struct cnode
* cp
, int hflags
)
286 hfs_chash_lock_spin ( hfsmp
);
288 CLR ( cp
-> c_hflag
, hflags
);
290 if ( ISSET ( cp
-> c_hflag
, H_WAITING
)) {
291 CLR ( cp
-> c_hflag
, H_WAITING
);
292 pthread_cond_broadcast (& cp
-> c_cacsh_cond
);
295 hfs_chash_unlock ( hfsmp
);
299 * Remove a cnode from the hash table and wakeup any waiters.
302 hfs_chash_abort ( struct hfsmount
* hfsmp
, struct cnode
* cp
)
304 hfs_chash_lock_spin ( hfsmp
);
306 LIST_REMOVE ( cp
, c_hash
);
307 cp
-> c_hash
. le_next
= NULL
;
308 cp
-> c_hash
. le_prev
= NULL
;
310 CLR ( cp
-> c_hflag
, H_ATTACH
| H_ALLOC
);
311 if ( ISSET ( cp
-> c_hflag
, H_WAITING
))
313 CLR ( cp
-> c_hflag
, H_WAITING
);
314 pthread_cond_broadcast (& cp
-> c_cacsh_cond
);
316 hfs_chash_unlock ( hfsmp
);
320 * Use the device, inum pair to find the incore cnode.
322 * If it is in core, but locked, wait for it.
325 hfs_chash_getvnode ( struct hfsmount
* hfsmp
, ino_t inum
, int wantrsrc
, int skiplock
, int allow_deleted
)
331 * Go through the hash list
332 * If a cnode is in the process of being cleaned out or being
333 * allocated, wait for it to be finished and then try again.
336 hfs_chash_lock_spin ( hfsmp
);
338 for ( cp
= CNODEHASH ( hfsmp
, inum
)-> lh_first
; cp
; cp
= cp
-> c_hash
. le_next
) {
339 if ( cp
-> c_fileid
!= inum
)
341 /* Wait if cnode is being created or reclaimed. */
342 if ( ISSET ( cp
-> c_hflag
, H_ALLOC
| H_TRANSIT
| H_ATTACH
)) {
343 SET ( cp
-> c_hflag
, H_WAITING
);
344 hfs_chash_broadcast_and_unlock ( hfsmp
, cp
);
348 /* Obtain the desired vnode. */
349 vp
= wantrsrc
? cp
-> c_rsrc_vp
: cp
-> c_vp
;
357 if ( hfs_lock ( cp
, HFS_TRY_EXCLUSIVE_LOCK
, HFS_LOCK_ALLOW_NOEXISTS
))
359 SET ( cp
-> c_hflag
, H_WAITING
);
360 hfs_chash_broadcast_and_unlock ( hfsmp
, cp
);
365 vp
= wantrsrc
? cp
-> c_rsrc_vp
: cp
-> c_vp
;
368 * Skip cnodes that are not in the name space anymore
369 * we need to check with the cnode lock held because
370 * we may have blocked acquiring the vnode ref or the
371 * lock on the cnode which would allow the node to be
374 if (! allow_deleted
) {
375 if ( cp
-> c_flag
& ( C_NOEXISTS
| C_DELETED
)) {
376 if (! skiplock
) hfs_unlock ( cp
);
381 hfs_chash_raise_OpenLookupCounter ( cp
);
382 hfs_chash_broadcast_and_unlock ( hfsmp
, cp
);
386 hfs_chash_unlock ( hfsmp
);
391 hfs_chash_snoop ( struct hfsmount
* hfsmp
, ino_t inum
, int existence_only
,
392 int (* callout
)( const cnode_t
* cp
, void *), void * arg
)
398 * Go through the hash list
399 * If a cnode is in the process of being cleaned out or being
400 * allocated, wait for it to be finished and then try again.
402 hfs_chash_lock ( hfsmp
);
404 for ( cp
= CNODEHASH ( hfsmp
, inum
)-> lh_first
; cp
; cp
= cp
-> c_hash
. le_next
) {
405 if ( cp
-> c_fileid
!= inum
)
409 * Under normal circumstances, we would want to return ENOENT if a cnode is in
410 * the hash and it is marked C_NOEXISTS or C_DELETED. However, if the CNID
411 * namespace has wrapped around, then we have the possibility of collisions.
412 * In that case, we may use this function to validate whether or not we
413 * should trust the nextCNID value in the hfs mount point.
415 * If we didn't do this, then it would be possible for a cnode that is no longer backed
416 * by anything on-disk (C_NOEXISTS) to still exist in the hash along with its
417 * vnode. The cat_create routine could then create a new entry in the catalog
418 * re-using that CNID. Then subsequent hfs_getnewvnode calls will repeatedly fail
419 * trying to look it up/validate it because it is marked C_NOEXISTS. So we want
420 * to prevent that from happening as much as possible.
422 if ( existence_only
) {
427 /* Skip cnodes that have been removed from the catalog */
428 if ( cp
-> c_flag
& ( C_NOEXISTS
| C_DELETED
)) {
433 /* Skip cnodes being created or reclaimed. */
434 if (! ISSET ( cp
-> c_hflag
, H_ALLOC
| H_TRANSIT
| H_ATTACH
)) {
435 result
= callout ( cp
, arg
);
439 hfs_chash_unlock ( hfsmp
);
444 /* Search a cnode in the hash. This function does not return cnode which
445 * are getting created, destroyed or in transition. Note that this function
446 * does not acquire the cnode hash mutex, and expects the caller to acquire it.
447 * On success, returns pointer to the cnode found. On failure, returns NULL.
451 hfs_chash_search_cnid ( struct hfsmount
* hfsmp
, cnid_t cnid
)
455 for ( cp
= CNODEHASH ( hfsmp
, cnid
)-> lh_first
; cp
; cp
= cp
-> c_hash
. le_next
) {
456 if ( cp
-> c_fileid
== cnid
) {
461 /* If cnode is being created or reclaimed, return error. */
462 if ( cp
&& ISSET ( cp
-> c_hflag
, H_ALLOC
| H_TRANSIT
| H_ATTACH
)) {
469 /* Search a cnode corresponding to given device and ID in the hash. If the
470 * found cnode has kHFSHasChildLinkBit cleared, set it. If the cnode is not
471 * found, no new cnode is created and error is returned.
474 * -1 : The cnode was not found.
475 * 0 : The cnode was found, and the kHFSHasChildLinkBit was already set.
476 * 1 : The cnode was found, the kHFSHasChildLinkBit was not set, and the
477 * function had to set that bit.
480 hfs_chash_set_childlinkbit ( struct hfsmount
* hfsmp
, cnid_t cnid
)
485 hfs_chash_lock_spin ( hfsmp
);
487 cp
= hfs_chash_search_cnid ( hfsmp
, cnid
);
489 if ( cp
-> c_attr
. ca_recflags
& kHFSHasChildLinkMask
) {
492 cp
-> c_attr
. ca_recflags
|= kHFSHasChildLinkMask
;
496 hfs_chash_unlock ( hfsmp
);
502 * Remove a cnode from the hash table.
503 * Need to lock cache from caller
506 hfs_chashremove ( struct hfsmount
* hfsmp
, struct cnode
* cp
)
508 hfs_chash_lock_spin ( hfsmp
);
510 /* Check if a vnode is getting attached */
511 if ( ISSET ( cp
-> c_hflag
, H_ATTACH
)) {
512 hfs_chash_unlock ( hfsmp
);
515 if ( cp
-> c_hash
. le_next
|| cp
-> c_hash
. le_prev
) {
516 LIST_REMOVE ( cp
, c_hash
);
517 cp
-> c_hash
. le_next
= NULL
;
518 cp
-> c_hash
. le_prev
= NULL
;
521 hfs_chash_unlock ( hfsmp
);
527 * mark a cnode as in transition
530 hfs_chash_mark_in_transit ( struct hfsmount
* hfsmp
, struct cnode
* cp
)
532 hfs_chash_lock_spin ( hfsmp
);
534 SET ( cp
-> c_hflag
, H_TRANSIT
);
536 hfs_chash_unlock ( hfsmp
);