]>
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
) ||
197 (( cp
-> uOpenLookupRefCount
== 0 ) ||
198 ( vp
-> uValidNodeMagic1
== VALID_NODE_BADMAGIC
) ||
199 ( vp
-> uValidNodeMagic2
== VALID_NODE_BADMAGIC
))))
202 if ( cp
-> c_flag
& C_RENAMED
)
215 hfs_chashwakeup ( hfsmp
, cp
, H_ATTACH
);
216 * hflags
&= ~ H_ATTACH
;
223 * out_flags
= GNV_CHASH_RENAMED
;
227 if ( cp
) hfs_chash_raise_OpenLookupCounter ( cp
);
228 hfs_chash_broadcast_and_unlock ( hfsmp
, cp
);
234 * Allocate a new cnode
236 if ( skiplock
&& ! wantrsrc
)
238 LFHFS_LOG ( LEVEL_ERROR
, "hfs_chash_getcnode: should never get here when skiplock is set \n " );
244 hfs_chash_unlock ( hfsmp
);
246 ncp
= hfs_mallocz ( sizeof ( struct cnode
));
252 * since we dropped the chash lock,
253 * we need to go back and re-verify
254 * that this node hasn't come into
260 bzero ( ncp
, sizeof (* ncp
));
262 SET ( ncp
-> c_hflag
, H_ALLOC
);
264 ncp
-> c_fileid
= ( cnid_t
) inum
;
265 TAILQ_INIT (& ncp
-> c_hintlist
); /* make the list empty */
266 TAILQ_INIT (& ncp
-> c_originlist
);
268 lf_lck_rw_init (& ncp
-> c_rwlock
);
269 lf_cond_init (& ncp
-> c_cacsh_cond
);
273 ( void ) hfs_lock ( ncp
, HFS_EXCLUSIVE_LOCK
, HFS_LOCK_DEFAULT
);
276 /* Insert the new cnode with it's H_ALLOC flag set */
277 LIST_INSERT_HEAD ( CNODEHASH ( hfsmp
, inum
), ncp
, c_hash
);
278 hfs_chash_raise_OpenLookupCounter ( ncp
);
279 hfs_chash_unlock ( hfsmp
);
285 hfs_chashwakeup ( struct hfsmount
* hfsmp
, struct cnode
* cp
, int hflags
)
287 hfs_chash_lock_spin ( hfsmp
);
289 CLR ( cp
-> c_hflag
, hflags
);
291 if ( ISSET ( cp
-> c_hflag
, H_WAITING
)) {
292 CLR ( cp
-> c_hflag
, H_WAITING
);
293 pthread_cond_broadcast (& cp
-> c_cacsh_cond
);
296 hfs_chash_unlock ( hfsmp
);
300 * Remove a cnode from the hash table and wakeup any waiters.
303 hfs_chash_abort ( struct hfsmount
* hfsmp
, struct cnode
* cp
)
305 hfs_chash_lock_spin ( hfsmp
);
307 LIST_REMOVE ( cp
, c_hash
);
308 cp
-> c_hash
. le_next
= NULL
;
309 cp
-> c_hash
. le_prev
= NULL
;
311 CLR ( cp
-> c_hflag
, H_ATTACH
| H_ALLOC
);
312 if ( ISSET ( cp
-> c_hflag
, H_WAITING
))
314 CLR ( cp
-> c_hflag
, H_WAITING
);
315 pthread_cond_broadcast (& cp
-> c_cacsh_cond
);
317 hfs_chash_unlock ( hfsmp
);
321 * Use the device, inum pair to find the incore cnode.
323 * If it is in core, but locked, wait for it.
326 hfs_chash_getvnode ( struct hfsmount
* hfsmp
, ino_t inum
, int wantrsrc
, int skiplock
, int allow_deleted
)
332 * Go through the hash list
333 * If a cnode is in the process of being cleaned out or being
334 * allocated, wait for it to be finished and then try again.
337 hfs_chash_lock_spin ( hfsmp
);
339 for ( cp
= CNODEHASH ( hfsmp
, inum
)-> lh_first
; cp
; cp
= cp
-> c_hash
. le_next
) {
340 if ( cp
-> c_fileid
!= inum
)
342 /* Wait if cnode is being created or reclaimed. */
343 if ( ISSET ( cp
-> c_hflag
, H_ALLOC
| H_TRANSIT
| H_ATTACH
)) {
344 SET ( cp
-> c_hflag
, H_WAITING
);
345 hfs_chash_broadcast_and_unlock ( hfsmp
, cp
);
349 /* Obtain the desired vnode. */
350 vp
= wantrsrc
? cp
-> c_rsrc_vp
: cp
-> c_vp
;
358 if ( hfs_lock ( cp
, HFS_TRY_EXCLUSIVE_LOCK
, HFS_LOCK_ALLOW_NOEXISTS
))
360 SET ( cp
-> c_hflag
, H_WAITING
);
361 hfs_chash_broadcast_and_unlock ( hfsmp
, cp
);
366 vp
= wantrsrc
? cp
-> c_rsrc_vp
: cp
-> c_vp
;
369 * Skip cnodes that are not in the name space anymore
370 * we need to check with the cnode lock held because
371 * we may have blocked acquiring the vnode ref or the
372 * lock on the cnode which would allow the node to be
375 if (! allow_deleted
) {
376 if ( cp
-> c_flag
& ( C_NOEXISTS
| C_DELETED
)) {
377 if (! skiplock
) hfs_unlock ( cp
);
382 hfs_chash_raise_OpenLookupCounter ( cp
);
383 hfs_chash_broadcast_and_unlock ( hfsmp
, cp
);
387 hfs_chash_unlock ( hfsmp
);
392 hfs_chash_snoop ( struct hfsmount
* hfsmp
, ino_t inum
, int existence_only
,
393 int (* callout
)( const cnode_t
* cp
, void *), void * arg
)
399 * Go through the hash list
400 * If a cnode is in the process of being cleaned out or being
401 * allocated, wait for it to be finished and then try again.
403 hfs_chash_lock ( hfsmp
);
405 for ( cp
= CNODEHASH ( hfsmp
, inum
)-> lh_first
; cp
; cp
= cp
-> c_hash
. le_next
) {
406 if ( cp
-> c_fileid
!= inum
)
410 * Under normal circumstances, we would want to return ENOENT if a cnode is in
411 * the hash and it is marked C_NOEXISTS or C_DELETED. However, if the CNID
412 * namespace has wrapped around, then we have the possibility of collisions.
413 * In that case, we may use this function to validate whether or not we
414 * should trust the nextCNID value in the hfs mount point.
416 * If we didn't do this, then it would be possible for a cnode that is no longer backed
417 * by anything on-disk (C_NOEXISTS) to still exist in the hash along with its
418 * vnode. The cat_create routine could then create a new entry in the catalog
419 * re-using that CNID. Then subsequent hfs_getnewvnode calls will repeatedly fail
420 * trying to look it up/validate it because it is marked C_NOEXISTS. So we want
421 * to prevent that from happening as much as possible.
423 if ( existence_only
) {
428 /* Skip cnodes that have been removed from the catalog */
429 if ( cp
-> c_flag
& ( C_NOEXISTS
| C_DELETED
)) {
434 /* Skip cnodes being created or reclaimed. */
435 if (! ISSET ( cp
-> c_hflag
, H_ALLOC
| H_TRANSIT
| H_ATTACH
)) {
436 result
= callout ( cp
, arg
);
440 hfs_chash_unlock ( hfsmp
);
445 /* Search a cnode in the hash. This function does not return cnode which
446 * are getting created, destroyed or in transition. Note that this function
447 * does not acquire the cnode hash mutex, and expects the caller to acquire it.
448 * On success, returns pointer to the cnode found. On failure, returns NULL.
452 hfs_chash_search_cnid ( struct hfsmount
* hfsmp
, cnid_t cnid
)
456 for ( cp
= CNODEHASH ( hfsmp
, cnid
)-> lh_first
; cp
; cp
= cp
-> c_hash
. le_next
) {
457 if ( cp
-> c_fileid
== cnid
) {
462 /* If cnode is being created or reclaimed, return error. */
463 if ( cp
&& ISSET ( cp
-> c_hflag
, H_ALLOC
| H_TRANSIT
| H_ATTACH
)) {
470 /* Search a cnode corresponding to given device and ID in the hash. If the
471 * found cnode has kHFSHasChildLinkBit cleared, set it. If the cnode is not
472 * found, no new cnode is created and error is returned.
475 * -1 : The cnode was not found.
476 * 0 : The cnode was found, and the kHFSHasChildLinkBit was already set.
477 * 1 : The cnode was found, the kHFSHasChildLinkBit was not set, and the
478 * function had to set that bit.
481 hfs_chash_set_childlinkbit ( struct hfsmount
* hfsmp
, cnid_t cnid
)
486 hfs_chash_lock_spin ( hfsmp
);
488 cp
= hfs_chash_search_cnid ( hfsmp
, cnid
);
490 if ( cp
-> c_attr
. ca_recflags
& kHFSHasChildLinkMask
) {
493 cp
-> c_attr
. ca_recflags
|= kHFSHasChildLinkMask
;
497 hfs_chash_unlock ( hfsmp
);
503 * Remove a cnode from the hash table.
504 * Need to lock cache from caller
507 hfs_chashremove ( struct hfsmount
* hfsmp
, struct cnode
* cp
)
509 hfs_chash_lock_spin ( hfsmp
);
511 /* Check if a vnode is getting attached */
512 if ( ISSET ( cp
-> c_hflag
, H_ATTACH
)) {
513 hfs_chash_unlock ( hfsmp
);
516 if ( cp
-> c_hash
. le_next
|| cp
-> c_hash
. le_prev
) {
517 LIST_REMOVE ( cp
, c_hash
);
518 cp
-> c_hash
. le_next
= NULL
;
519 cp
-> c_hash
. le_prev
= NULL
;
522 hfs_chash_unlock ( hfsmp
);
528 * mark a cnode as in transition
531 hfs_chash_mark_in_transit ( struct hfsmount
* hfsmp
, struct cnode
* cp
)
533 hfs_chash_lock_spin ( hfsmp
);
535 SET ( cp
-> c_hflag
, H_TRANSIT
);
537 hfs_chash_unlock ( hfsmp
);