]>
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
)
30 pthread_cond_signal (& cp
-> c_cacsh_cond
);
31 hfs_chash_unlock ( hfsmp
);
35 hfs_chash_wait_and_unlock ( struct hfsmount
* hfsmp
, struct cnode
* cp
)
37 SET ( cp
-> c_hflag
, H_WAITING
);
38 pthread_cond_wait (& cp
-> c_cacsh_cond
, & hfsmp
-> hfs_chash_mutex
);
39 hfs_chash_broadcast_and_unlock ( hfsmp
, cp
);
43 hfs_chash_raise_OpenLookupCounter ( struct cnode
* cp
)
45 if (! cp
|| cp
-> uOpenLookupRefCount
== UINT32_MAX
)
47 LFHFS_LOG ( LEVEL_ERROR
,
48 "hfs_chash_raise_OpenLookupCounter:"
49 "cp[ %p ] is NULL or reached max Open Lookup Counter" , cp
);
52 cp
-> uOpenLookupRefCount
++;
56 hfs_chash_lower_OpenLookupCounter ( struct cnode
* cp
)
58 if ( cp
-> uOpenLookupRefCount
== 0 )
60 LFHFS_LOG ( LEVEL_ERROR
, "hfs_chash_lower_OpenLookupCounter: reached min Open Lookup Counter \n " );
63 cp
-> uOpenLookupRefCount
--;
67 * Initialize cnode hash table.
74 void hfs_chash_lock ( struct hfsmount
* hfsmp
)
76 lf_lck_mtx_lock (& hfsmp
-> hfs_chash_mutex
);
79 void hfs_chash_lock_spin ( struct hfsmount
* hfsmp
)
81 lf_lck_mtx_lock_spin (& hfsmp
-> hfs_chash_mutex
);
85 void hfs_chash_unlock ( struct hfsmount
* hfsmp
)
87 lf_lck_mtx_unlock (& hfsmp
-> hfs_chash_mutex
);
91 hfs_chashinit_finish ( struct hfsmount
* hfsmp
)
93 lf_lck_mtx_init (& hfsmp
-> hfs_chash_mutex
);
94 hfsmp
-> hfs_cnodehashtbl
= hashinit ( DESIRED_VNODES
/ 4 , & hfsmp
-> hfs_cnodehash
);
98 hfs_delete_chash ( struct hfsmount
* hfsmp
)
101 hfs_chash_lock_spin ( hfsmp
);
103 for ( ino_t inum
= 0 ; inum
< ( DESIRED_VNODES
/ 4 ); inum
++)
105 for ( cp
= CNODEHASH ( hfsmp
, inum
)-> lh_first
; cp
; cp
= cp
-> c_hash
. le_next
) {
106 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
);
111 hfs_chash_unlock ( hfsmp
);
112 lf_lck_mtx_destroy (& hfsmp
-> hfs_chash_mutex
);
113 hfs_free ( hfsmp
-> hfs_cnodehashtbl
);
117 * Use the device, fileid pair to find the incore cnode.
118 * If no cnode if found one is created
120 * If it is in core, but locked, wait for it.
122 * If the cnode is C_DELETED, then return NULL since that
123 * inum is no longer valid for lookups (open-unlinked file).
125 * If the cnode is C_DELETED but also marked C_RENAMED, then that means
126 * the cnode was renamed over and a new entry exists in its place. The caller
127 * should re-drive the lookup to get the newer entry. In that case, we'll still
128 * return NULL for the cnode, but also return GNV_CHASH_RENAMED in the output flags
129 * of this function to indicate the caller that they should re-drive.
132 hfs_chash_getcnode ( struct hfsmount
* hfsmp
, ino_t inum
, struct vnode
** vpp
, int wantrsrc
, int skiplock
, int * out_flags
, int * hflags
)
135 struct cnode
* ncp
= NULL
;
139 * Go through the hash list
140 * If a cnode is in the process of being cleaned out or being
141 * allocated, wait for it to be finished and then try again.
144 hfs_chash_lock_spin ( hfsmp
);
146 for ( cp
= CNODEHASH ( hfsmp
, inum
)-> lh_first
; cp
; cp
= cp
-> c_hash
. le_next
)
148 if ( cp
-> c_fileid
!= inum
)
153 * Wait if cnode is being created, attached to or reclaimed.
155 if ( ISSET ( cp
-> c_hflag
, H_ALLOC
| H_ATTACH
| H_TRANSIT
))
157 hfs_chash_wait ( hfsmp
, cp
);
161 vp
= wantrsrc
? cp
-> c_rsrc_vp
: cp
-> c_vp
;
165 * The desired vnode isn't there so tag the cnode.
167 SET ( cp
-> c_hflag
, H_ATTACH
);
174 * someone else won the race to create
175 * this cnode and add it to the hash
176 * just dump our allocation
184 if ( hfs_lock ( cp
, HFS_TRY_EXCLUSIVE_LOCK
, HFS_LOCK_ALLOW_NOEXISTS
))
186 SET ( cp
-> c_hflag
, H_WAITING
);
187 hfs_chash_broadcast_and_unlock ( hfsmp
, cp
);
192 vp
= wantrsrc
? cp
-> c_rsrc_vp
: cp
-> c_vp
;
195 * Skip cnodes that are not in the name space anymore
196 * we need to check with the cnode lock held because
197 * we may have blocked acquiring the vnode ref or the
198 * lock on the cnode which would allow the node to be
201 * Don't return a cnode in this case since the inum
202 * is no longer valid for lookups.
204 if ((( cp
-> c_flag
& ( C_NOEXISTS
| C_DELETED
)) && ! wantrsrc
) ||
206 (( cp
-> uOpenLookupRefCount
== 0 ) ||
207 ( vp
-> uValidNodeMagic1
== VALID_NODE_BADMAGIC
) ||
208 ( vp
-> uValidNodeMagic2
== VALID_NODE_BADMAGIC
))))
211 if ( cp
-> c_flag
& C_RENAMED
)
224 hfs_chashwakeup ( hfsmp
, cp
, H_ATTACH
);
225 * hflags
&= ~ H_ATTACH
;
228 pthread_cond_signal (& cp
-> c_cacsh_cond
);
233 * out_flags
= GNV_CHASH_RENAMED
;
238 hfs_chash_raise_OpenLookupCounter ( cp
);
241 hfs_chash_broadcast_and_unlock ( hfsmp
, cp
);
248 * Allocate a new cnode
250 if ( skiplock
&& ! wantrsrc
)
252 LFHFS_LOG ( LEVEL_ERROR
, "hfs_chash_getcnode: should never get here when skiplock is set \n " );
258 hfs_chash_unlock ( hfsmp
);
259 ncp
= hfs_mallocz ( sizeof ( struct cnode
));
265 * since we dropped the chash lock,
266 * we need to go back and re-verify
267 * that this node hasn't come into
273 bzero ( ncp
, sizeof (* ncp
));
275 SET ( ncp
-> c_hflag
, H_ALLOC
);
277 ncp
-> c_fileid
= ( cnid_t
) inum
;
278 TAILQ_INIT (& ncp
-> c_hintlist
); /* make the list empty */
279 TAILQ_INIT (& ncp
-> c_originlist
);
281 lf_lck_rw_init (& ncp
-> c_rwlock
);
282 lf_cond_init (& ncp
-> c_cacsh_cond
);
286 ( void ) hfs_lock ( ncp
, HFS_EXCLUSIVE_LOCK
, HFS_LOCK_DEFAULT
);
289 /* Insert the new cnode with it's H_ALLOC flag set */
290 LIST_INSERT_HEAD ( CNODEHASH ( hfsmp
, inum
), ncp
, c_hash
);
291 hfs_chash_raise_OpenLookupCounter ( ncp
);
292 hfs_chash_unlock ( hfsmp
);
298 hfs_chashwakeup ( struct hfsmount
* hfsmp
, struct cnode
* cp
, int hflags
)
300 hfs_chash_lock_spin ( hfsmp
);
302 CLR ( cp
-> c_hflag
, hflags
);
304 if ( ISSET ( cp
-> c_hflag
, H_WAITING
)) {
305 CLR ( cp
-> c_hflag
, H_WAITING
);
308 hfs_chash_broadcast_and_unlock ( hfsmp
, cp
);
312 * Remove a cnode from the hash table and wakeup any waiters.
315 hfs_chash_abort ( struct hfsmount
* hfsmp
, struct cnode
* cp
)
317 hfs_chash_lock_spin ( hfsmp
);
319 LIST_REMOVE ( cp
, c_hash
);
320 cp
-> c_hash
. le_next
= NULL
;
321 cp
-> c_hash
. le_prev
= NULL
;
323 CLR ( cp
-> c_hflag
, H_ATTACH
| H_ALLOC
);
324 if ( ISSET ( cp
-> c_hflag
, H_WAITING
))
326 CLR ( cp
-> c_hflag
, H_WAITING
);
328 hfs_chash_broadcast_and_unlock ( hfsmp
, cp
);
332 * Use the device, inum pair to find the incore cnode.
334 * If it is in core, but locked, wait for it.
337 hfs_chash_getvnode ( struct hfsmount
* hfsmp
, ino_t inum
, int wantrsrc
, int skiplock
, int allow_deleted
)
343 * Go through the hash list
344 * If a cnode is in the process of being cleaned out or being
345 * allocated, wait for it to be finished and then try again.
348 hfs_chash_lock_spin ( hfsmp
);
350 for ( cp
= CNODEHASH ( hfsmp
, inum
)-> lh_first
; cp
; cp
= cp
-> c_hash
. le_next
) {
351 if ( cp
-> c_fileid
!= inum
)
353 /* Wait if cnode is being created or reclaimed. */
354 if ( ISSET ( cp
-> c_hflag
, H_ALLOC
| H_TRANSIT
| H_ATTACH
)) {
355 SET ( cp
-> c_hflag
, H_WAITING
);
356 hfs_chash_wait_and_unlock ( hfsmp
, cp
);
359 /* Obtain the desired vnode. */
360 vp
= wantrsrc
? cp
-> c_rsrc_vp
: cp
-> c_vp
;
368 if ( hfs_lock ( cp
, HFS_TRY_EXCLUSIVE_LOCK
, HFS_LOCK_ALLOW_NOEXISTS
))
370 SET ( cp
-> c_hflag
, H_WAITING
);
371 hfs_chash_broadcast_and_unlock ( hfsmp
, cp
);
376 vp
= wantrsrc
? cp
-> c_rsrc_vp
: cp
-> c_vp
;
379 * Skip cnodes that are not in the name space anymore
380 * we need to check with the cnode lock held because
381 * we may have blocked acquiring the vnode ref or the
382 * lock on the cnode which would allow the node to be
385 if (! allow_deleted
) {
386 if ( cp
-> c_flag
& ( C_NOEXISTS
| C_DELETED
)) {
387 if (! skiplock
) hfs_unlock ( cp
);
392 hfs_chash_raise_OpenLookupCounter ( cp
);
393 hfs_chash_broadcast_and_unlock ( hfsmp
, cp
);
398 hfs_chash_unlock ( hfsmp
);
403 hfs_chash_snoop ( struct hfsmount
* hfsmp
, ino_t inum
, int existence_only
,
404 int (* callout
)( const cnode_t
* cp
, void *), void * arg
)
410 * Go through the hash list
411 * If a cnode is in the process of being cleaned out or being
412 * allocated, wait for it to be finished and then try again.
414 hfs_chash_lock ( hfsmp
);
416 for ( cp
= CNODEHASH ( hfsmp
, inum
)-> lh_first
; cp
; cp
= cp
-> c_hash
. le_next
) {
417 if ( cp
-> c_fileid
!= inum
)
421 * Under normal circumstances, we would want to return ENOENT if a cnode is in
422 * the hash and it is marked C_NOEXISTS or C_DELETED. However, if the CNID
423 * namespace has wrapped around, then we have the possibility of collisions.
424 * In that case, we may use this function to validate whether or not we
425 * should trust the nextCNID value in the hfs mount point.
427 * If we didn't do this, then it would be possible for a cnode that is no longer backed
428 * by anything on-disk (C_NOEXISTS) to still exist in the hash along with its
429 * vnode. The cat_create routine could then create a new entry in the catalog
430 * re-using that CNID. Then subsequent hfs_getnewvnode calls will repeatedly fail
431 * trying to look it up/validate it because it is marked C_NOEXISTS. So we want
432 * to prevent that from happening as much as possible.
434 if ( existence_only
) {
439 /* Skip cnodes that have been removed from the catalog */
440 if ( cp
-> c_flag
& ( C_NOEXISTS
| C_DELETED
)) {
445 /* Skip cnodes being created or reclaimed. */
446 if (! ISSET ( cp
-> c_hflag
, H_ALLOC
| H_TRANSIT
| H_ATTACH
)) {
447 result
= callout ( cp
, arg
);
451 hfs_chash_unlock ( hfsmp
);
456 /* Search a cnode in the hash. This function does not return cnode which
457 * are getting created, destroyed or in transition. Note that this function
458 * does not acquire the cnode hash mutex, and expects the caller to acquire it.
459 * On success, returns pointer to the cnode found. On failure, returns NULL.
463 hfs_chash_search_cnid ( struct hfsmount
* hfsmp
, cnid_t cnid
)
467 for ( cp
= CNODEHASH ( hfsmp
, cnid
)-> lh_first
; cp
; cp
= cp
-> c_hash
. le_next
) {
468 if ( cp
-> c_fileid
== cnid
) {
473 /* If cnode is being created or reclaimed, return error. */
474 if ( cp
&& ISSET ( cp
-> c_hflag
, H_ALLOC
| H_TRANSIT
| H_ATTACH
)) {
481 /* Search a cnode corresponding to given device and ID in the hash. If the
482 * found cnode has kHFSHasChildLinkBit cleared, set it. If the cnode is not
483 * found, no new cnode is created and error is returned.
486 * -1 : The cnode was not found.
487 * 0 : The cnode was found, and the kHFSHasChildLinkBit was already set.
488 * 1 : The cnode was found, the kHFSHasChildLinkBit was not set, and the
489 * function had to set that bit.
492 hfs_chash_set_childlinkbit ( struct hfsmount
* hfsmp
, cnid_t cnid
)
497 hfs_chash_lock_spin ( hfsmp
);
499 cp
= hfs_chash_search_cnid ( hfsmp
, cnid
);
501 if ( cp
-> c_attr
. ca_recflags
& kHFSHasChildLinkMask
) {
504 cp
-> c_attr
. ca_recflags
|= kHFSHasChildLinkMask
;
508 hfs_chash_broadcast_and_unlock ( hfsmp
, cp
);
514 * Remove a cnode from the hash table.
515 * Need to lock cache from caller
518 hfs_chashremove ( struct hfsmount
* hfsmp
, struct cnode
* cp
)
520 hfs_chash_lock_spin ( hfsmp
);
522 /* Check if a vnode is getting attached */
523 if ( ISSET ( cp
-> c_hflag
, H_ATTACH
)) {
524 hfs_chash_broadcast_and_unlock ( hfsmp
, cp
);
527 if ( cp
-> c_hash
. le_next
|| cp
-> c_hash
. le_prev
) {
528 LIST_REMOVE ( cp
, c_hash
);
529 cp
-> c_hash
. le_next
= NULL
;
530 cp
-> c_hash
. le_prev
= NULL
;
533 hfs_chash_broadcast_and_unlock ( hfsmp
, cp
);
539 * mark a cnode as in transition
542 hfs_chash_mark_in_transit ( struct hfsmount
* hfsmp
, struct cnode
* cp
)
544 hfs_chash_lock_spin ( hfsmp
);
546 SET ( cp
-> c_hflag
, H_TRANSIT
);
548 hfs_chash_broadcast_and_unlock ( hfsmp
, cp
);