2 * Copyright (c) 2000 Apple Computer, Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved.
8 * This file contains Original Code and/or Modifications of Original Code
9 * as defined in and that are subject to the Apple Public Source License
10 * Version 2.0 (the 'License'). You may not use this file except in
11 * compliance with the License. Please obtain a copy of the License at
12 * http://www.opensource.apple.com/apsl/ and read it before using this
15 * The Original Code and all software distributed under the License are
16 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
17 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
18 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
20 * Please see the License for the specific language governing rights and
21 * limitations under the License.
23 * @APPLE_LICENSE_HEADER_END@
26 File: CatalogIterators.c
28 Contains: Catalog Iterator Implementation
32 Copyright: © 1997-1998 by Apple Computer, Inc., all rights reserved.
38 Other Contact: Mark Day
40 Technology: Mac OS File System
47 Change History (most recent first):
48 <MacOSX> 4/23/98 djb Re-enable InvalidateCatalogCache (was commented out).
49 <MacOSX> 4/6/98 djb Add locking for cache globals (list) and iterators.
50 <MacOSX> 4/2/98 djb Define gCatalogCacheGlobals here instead of FSVars.
51 <MacOSX> 3/31/98 djb Sync up with final HFSVolumes.h header file.
53 <CS3> 11/13/97 djb Radar #1683572 - Fix for indexed GetFileInfo.
54 <CS2> 10/17/97 msd Bug 1683506. Add support for long Unicode names in
55 CatalogIterators. Added a single global buffer for long Unicode
56 names; it is used by at most one CatalogIterator at a time.
57 <CS1> 10/1/97 djb first checked in
61 #include "../../hfs_macos_defs.h"
62 #include "../../hfs.h"
63 #include "../../hfs_dbg.h"
64 #include "../../hfs_format.h"
66 #include "../headers/FileMgrInternal.h"
67 #include "../headers/BTreesInternal.h"
68 #include "../headers/CatalogPrivate.h"
71 #include <sys/param.h>
72 #include <sys/systm.h>
73 #include <libkern/libkern.h>
76 static void InsertCatalogIteratorAsMRU( CatalogCacheGlobals
*cacheGlobals
, CatalogIterator
*iterator
);
78 static void InsertCatalogIteratorAsLRU( CatalogCacheGlobals
*cacheGlobals
, CatalogIterator
*iterator
);
80 static void PrepareForLongName( CatalogIterator
*iterator
);
83 #if TARGET_API_MACOS_X
84 CatalogCacheGlobals
*gCatalogCacheGlobals
;
86 #define GetCatalogCacheGlobals() (gCatalogCacheGlobals)
88 #define CATALOG_ITER_LIST_LOCK(g) simple_lock(&(g)->simplelock)
90 #define CATALOG_ITER_LIST_UNLOCK(g) simple_unlock(&(g)->simplelock)
92 #define CI_LOCK(i) lockmgr(&(i)->iterator_lock, LK_EXCLUSIVE, (simple_lock_t) 0, current_proc())
94 #define CI_UNLOCK(i) lockmgr(&(i)->iterator_lock, LK_RELEASE, (simple_lock_t) 0, current_proc())
96 #define CI_SLEEPLESS_LOCK(i) lockmgr(&(i)->iterator_lock, LK_EXCLUSIVE | LK_NOWAIT, (simple_lock_t) 0, current_proc())
98 #define CI_LOCK_FROM_LIST(g,i) lockmgr(&(i)->iterator_lock, LK_EXCLUSIVE | LK_INTERLOCK, &(g)->simplelock, current_proc())
100 #else /* TARGET_API_MACOS_X */
102 #define GetCatalogCacheGlobals() ((CatalogCacheGlobals*) ((FSVarsRec*) LMGetFSMVars()->gCatalogCacheGlobals))
104 #define CATALOG_ITER_LIST_LOCK(g)
106 #define CATALOG_ITER_LIST_UNLOCK(g)
110 #define CI_UNLOCK(i) 0
112 #define CI_SLEEPLESS_LOCK(i) 0
114 #define CI_LOCK_FROM_LIST(g,i) 0
119 //_______________________________________________________________________________
120 // Routine: InitCatalogCache
122 // Function: Allocates cache, and initializes all the cache structures.
124 //_______________________________________________________________________________
126 InitCatalogCache(void)
128 CatalogCacheGlobals
* cacheGlobals
;
129 CatalogIterator
* iterator
;
136 cacheSize
= sizeof(CatalogCacheGlobals
) + ( kCatalogIteratorCount
* sizeof(CatalogIterator
) );
137 cacheGlobals
= (CatalogCacheGlobals
*) NewPtrSysClear( cacheSize
);
139 cacheGlobals
->iteratorCount
= kCatalogIteratorCount
;
141 lastIterator
= kCatalogIteratorCount
- 1; // last iterator number, since they start at 0
143 // Initialize the MRU order for the cache
144 cacheGlobals
->mru
= (CatalogIterator
*) ( (Ptr
)cacheGlobals
+ sizeof(CatalogCacheGlobals
) );
146 // Initialize the LRU order for the cache
147 cacheGlobals
->lru
= (CatalogIterator
*) ( (Ptr
)(cacheGlobals
->mru
) + (lastIterator
* sizeof(CatalogIterator
)) );
150 // Traverse iterators, setting initial mru, lru, and default values
151 for ( i
= 0, iterator
= cacheGlobals
->mru
; i
< kCatalogIteratorCount
; i
++, iterator
= iterator
->nextMRU
)
153 if ( i
== lastIterator
)
154 iterator
->nextMRU
= nil
; // terminate the list
156 iterator
->nextMRU
= (CatalogIterator
*) ( (Ptr
)iterator
+ sizeof(CatalogIterator
) );
159 iterator
->nextLRU
= nil
; // terminate the list
161 iterator
->nextLRU
= (CatalogIterator
*) ( (Ptr
)iterator
- sizeof(CatalogIterator
) );
163 #if TARGET_API_MACOS_X
164 lockinit(&iterator
->iterator_lock
, PINOD
, "hfs_catalog_iterator", 0, 0);
168 #if TARGET_API_MAC_OS8
169 (FSVarsRec
*) LMGetFSMVars()->gCatalogCacheGlobals
= (Ptr
) cacheGlobals
;
172 #if TARGET_API_MACOS_X
173 gCatalogCacheGlobals
= cacheGlobals
;
174 simple_lock_init(&cacheGlobals
->simplelock
);
181 //_______________________________________________________________________________
182 // Routine: InvalidateCatalogCache
184 // Function: Trash any interators matching volume parameter
186 //_______________________________________________________________________________
187 void PrintCatalogIterator( void );
190 InvalidateCatalogCache( ExtendedVCB
*volume
)
192 TrashCatalogIterator( volume
, 0 );
196 //_______________________________________________________________________________
197 // Routine: PrintCatalogIterator
199 // Function: Prints all interators
201 //_______________________________________________________________________________
204 PrintCatalogIterator( void )
206 CatalogIterator
*iterator
;
207 CatalogCacheGlobals
*cacheGlobals
= GetCatalogCacheGlobals();
210 PRINTIT("CatalogCacheGlobals @ 0x%08lX are:\n", (unsigned long)cacheGlobals
);
211 PRINTIT("\titeratorCount: %ld \n", cacheGlobals
->iteratorCount
);
212 PRINTIT("\tmru: 0x%08lX \n", (unsigned long)cacheGlobals
->mru
);
213 PRINTIT("\tlru: 0x%08lX \n", (unsigned long)cacheGlobals
->lru
);
215 for ( iterator
= cacheGlobals
->mru
, i
=0 ; iterator
!= nil
&& i
<32 ; iterator
= iterator
->nextMRU
, i
++)
218 PRINTIT(" i: 0x%08lX", (unsigned long)iterator
);
219 PRINTIT(" M: 0x%08lX", (unsigned long)iterator
->nextMRU
);
220 PRINTIT(" L: 0x%08lX", (unsigned long)iterator
->nextLRU
);
226 //_______________________________________________________________________________
227 // Routine: TrashCatalogIterator
229 // Function: Trash any interators matching volume and folder parameters
231 //_______________________________________________________________________________
233 TrashCatalogIterator( const ExtendedVCB
*volume
, HFSCatalogNodeID folderID
)
235 CatalogIterator
*iterator
;
236 CatalogCacheGlobals
*cacheGlobals
= GetCatalogCacheGlobals();
238 CATALOG_ITER_LIST_LOCK(cacheGlobals
);
240 for ( iterator
= cacheGlobals
->mru
; iterator
!= nil
; iterator
= iterator
->nextMRU
)
244 // first match the volume
245 if ( iterator
->volume
!= volume
)
248 // now match the folder (or all folders if 0)
249 if ( (folderID
== 0) || (folderID
== iterator
->folderID
) )
251 CatalogIterator
*next
;
253 iterator
->volume
= 0; // trash it
254 iterator
->folderID
= 0;
256 next
= iterator
->nextMRU
; // remember the next iterator
258 // if iterator is not already last then make it last
261 InsertCatalogIteratorAsLRU( cacheGlobals
, iterator
);
263 // iterator->nextMRU will always be zero (since we moved it to the end)
264 // so set up the next iterator manually (we know its not nil)
266 goto top
; // process the next iterator
271 CATALOG_ITER_LIST_UNLOCK(cacheGlobals
);
275 //_______________________________________________________________________________
276 // Routine: AgeCatalogIterator
278 // Function: Move iterator to the end of the list...
280 //_______________________________________________________________________________
282 AgeCatalogIterator ( CatalogIterator
*catalogIterator
)
284 CatalogCacheGlobals
* cacheGlobals
= GetCatalogCacheGlobals();
286 CATALOG_ITER_LIST_LOCK(cacheGlobals
);
288 //PRINTIT(" AgeCatalogIterator: v=%d, d=%ld, i=%d\n", catalogIterator->volRefNum, catalogIterator->folderID, catalogIterator->currentIndex);
290 InsertCatalogIteratorAsLRU( cacheGlobals
, catalogIterator
);
292 CATALOG_ITER_LIST_UNLOCK(cacheGlobals
);
296 //_______________________________________________________________________________
297 // Routine: GetCatalogIterator
299 // Function: Release interest in Catalog iterator
301 //_______________________________________________________________________________
303 ReleaseCatalogIterator( CatalogIterator
* catalogIterator
)
305 #if TARGET_API_MACOS_X
306 //PRINTIT(" ReleaseCatalogIterator: v=%d, d=%ld, i=%d\n", catalogIterator->volRefNum, catalogIterator->folderID, catalogIterator->currentIndex);
307 return CI_UNLOCK(catalogIterator
);
314 //_______________________________________________________________________________
315 // Routine: GetCatalogIterator
317 // Function: Returns an iterator associated with the volume, folderID, index,
318 // and iterationType (kIterateFilesOnly or kIterateAll).
319 // Searches the cache in MRU order.
320 // Inserts the resulting iterator at the head of mru automatically
322 // Note: The returned iterator is locked and ReleaseCatalogIterator must
323 // be called to unlock it.
325 //_______________________________________________________________________________
328 GetCatalogIterator(ExtendedVCB
*volume
, HFSCatalogNodeID folderID
, UInt32 offset
)
330 CatalogCacheGlobals
*cacheGlobals
= GetCatalogCacheGlobals();
331 CatalogIterator
*iterator
;
332 CatalogIterator
*bestIterator
;
336 CATALOG_ITER_LIST_LOCK(cacheGlobals
);
338 for (iterator
= cacheGlobals
->mru
; iterator
!= nil
; iterator
= iterator
->nextMRU
) {
340 /* first make sure volume and folder id match */
341 if ((iterator
->volume
!= volume
) || (iterator
->folderID
!= folderID
)) {
345 /* ignore busy iterators */
346 if ( CI_SLEEPLESS_LOCK(iterator
) == EBUSY
) {
347 //PRINTIT(" GetCatalogIterator: busy v=%d, d=%ld, i=%d\n", volume, folderID, iterator->currentIndex);
351 /* we matched volume, folder id, now check the offset */
352 if ( iterator
->currentOffset
== offset
|| iterator
->nextOffset
== offset
) {
353 bestIterator
= iterator
; // we scored! - so get out of this loop
354 break; // break with iterator locked
357 (void) CI_UNLOCK(iterator
); // unlock iterator before moving to the next one
360 // check if we didn't get one or if the one we got is too far away...
361 if (bestIterator
== NULL
)
363 bestIterator
= cacheGlobals
->lru
; // start over with a new iterator
365 //PRINTIT(" GetCatalogIterator: recycle v=%d, d=%ld, i=%d\n", bestIterator->volume, bestIterator->folderID, bestIterator->currentIndex);
366 (void) CI_LOCK_FROM_LIST(cacheGlobals
, bestIterator
); // XXX we should not eat the error!
368 CATALOG_ITER_LIST_LOCK(cacheGlobals
); // grab the lock again for MRU Insert below...
370 bestIterator
->volume
= volume
; // update the iterator's volume
371 bestIterator
->folderID
= folderID
; // ... and folderID
372 bestIterator
->currentIndex
= 0xFFFFFFFF; // ... and offspring index marker
373 bestIterator
->currentOffset
= 0xFFFFFFFF;
374 bestIterator
->nextOffset
= 0xFFFFFFFF;
376 bestIterator
->btreeNodeHint
= 0;
377 bestIterator
->btreeIndexHint
= 0;
378 bestIterator
->parentID
= folderID
; // set key to folderID + empty name
379 bestIterator
->folderName
.unicodeName
.length
= 0; // clear pascal/unicode name
381 if ( volume
->vcbSigWord
== kHFSPlusSigWord
)
382 bestIterator
->nameType
= kShortUnicodeName
;
384 bestIterator
->nameType
= kShortPascalName
;
387 //PRINTIT(" GetCatalogIterator: found v=%d, d=%ld, i=%d\n", bestIterator->volume, bestIterator->folderID, bestIterator->currentIndex);
390 // put this iterator at the front of the list
391 InsertCatalogIteratorAsMRU( cacheGlobals
, bestIterator
);
393 CATALOG_ITER_LIST_UNLOCK(cacheGlobals
);
395 return bestIterator
; // return our best shot
397 } /* GetCatalogIterator */
400 //_______________________________________________________________________________
401 // Routine: UpdateBtreeIterator
403 // Function: Fills in a BTreeIterator from a CatalogIterator
405 // Assumes: catalogIterator->nameType is correctly initialized!
406 // catalogIterator is locked (MacOS X)
407 //_______________________________________________________________________________
409 UpdateBtreeIterator(const CatalogIterator
*catalogIterator
, BTreeIterator
*btreeIterator
)
411 CatalogName
* nodeName
;
415 btreeIterator
->hint
.writeCount
= 0;
416 btreeIterator
->hint
.nodeNum
= catalogIterator
->btreeNodeHint
;
417 btreeIterator
->hint
.index
= catalogIterator
->btreeIndexHint
;
419 switch (catalogIterator
->nameType
)
421 case kShortPascalName
:
422 if ( catalogIterator
->folderName
.pascalName
[0] > 0 )
423 nodeName
= (CatalogName
*) catalogIterator
->folderName
.pascalName
;
430 case kShortUnicodeName
:
431 if ( catalogIterator
->folderName
.unicodeName
.length
> 0 )
432 nodeName
= (CatalogName
*) &catalogIterator
->folderName
.unicodeName
;
439 case kLongUnicodeName
:
440 if ( catalogIterator
->folderName
.longNamePtr
->length
> 0 )
441 nodeName
= (CatalogName
*) catalogIterator
->folderName
.longNamePtr
;
452 BuildCatalogKey(catalogIterator
->parentID
, nodeName
, isHFSPlus
, (CatalogKey
*) &btreeIterator
->key
);
456 //_______________________________________________________________________________
457 // Routine: UpdateCatalogIterator
459 // Function: Updates a CatalogIterator from a BTreeIterator
461 // Assumes: catalogIterator->nameType is correctly initialized!
462 // catalogIterator is locked (MacOS X)
463 //_______________________________________________________________________________
465 UpdateCatalogIterator (const BTreeIterator
*btreeIterator
, CatalogIterator
*catalogIterator
)
470 CatalogKey
* catalogKey
;
473 catalogIterator
->btreeNodeHint
= btreeIterator
->hint
.nodeNum
;
474 catalogIterator
->btreeIndexHint
= btreeIterator
->hint
.index
;
476 catalogKey
= (CatalogKey
*) &btreeIterator
->key
;
478 switch (catalogIterator
->nameType
)
480 case kShortPascalName
:
481 catalogIterator
->parentID
= catalogKey
->hfs
.parentID
;
483 dstName
= catalogIterator
->folderName
.pascalName
;
484 srcName
= catalogKey
->hfs
.nodeName
;
485 nameSize
= catalogKey
->hfs
.nodeName
[0] + sizeof(UInt8
);
488 case kShortUnicodeName
:
489 catalogIterator
->parentID
= catalogKey
->hfsPlus
.parentID
;
491 dstName
= &catalogIterator
->folderName
.unicodeName
;
492 srcName
= &catalogKey
->hfsPlus
.nodeName
;
493 nameSize
= (catalogKey
->hfsPlus
.nodeName
.length
+ 1) * sizeof(UInt16
);
495 // See if we need to make this iterator use long names
496 if ( nameSize
> sizeof(catalogIterator
->folderName
.unicodeName
) )
498 PrepareForLongName(catalogIterator
); // Find a long name buffer to use
499 dstName
= catalogIterator
->folderName
.longNamePtr
;
503 case kLongUnicodeName
:
504 catalogIterator
->parentID
= catalogKey
->hfsPlus
.parentID
;
506 dstName
= catalogIterator
->folderName
.longNamePtr
;
507 srcName
= &catalogKey
->hfsPlus
.nodeName
;
508 nameSize
= (catalogKey
->hfsPlus
.nodeName
.length
+ 1) * sizeof(UInt16
);
515 if (catalogIterator
->parentID
!= catalogIterator
->folderID
)
516 catalogIterator
->nextOffset
= 0xFFFFFFFF;
518 BlockMoveData(srcName
, dstName
, nameSize
);
520 } // end UpdateCatalogIterator
523 //_______________________________________________________________________________
524 // Routine: InsertCatalogIteratorAsMRU
526 // Function: Moves catalog iterator to head of mru order in double linked list
528 // Assumes list simple lock is held
529 //_______________________________________________________________________________
531 InsertCatalogIteratorAsMRU ( CatalogCacheGlobals
*cacheGlobals
, CatalogIterator
*iterator
)
533 CatalogIterator
*swapIterator
;
535 if ( cacheGlobals
->mru
!= iterator
) // if it's not already the mru iterator
537 swapIterator
= cacheGlobals
->mru
; // put it in the front of the double queue
538 cacheGlobals
->mru
= iterator
;
539 iterator
->nextLRU
->nextMRU
= iterator
->nextMRU
;
540 if ( iterator
->nextMRU
!= nil
)
541 iterator
->nextMRU
->nextLRU
= iterator
->nextLRU
;
543 cacheGlobals
->lru
= iterator
->nextLRU
;
544 iterator
->nextMRU
= swapIterator
;
545 iterator
->nextLRU
= nil
;
546 swapIterator
->nextLRU
= iterator
;
551 //________________________________________________________________________________
552 // Routine: InsertCatalogIteratorAsLRU
554 // Function: Moves catalog iterator to head of lru order in double linked list
556 // Assumes list simple lock is held
557 //_______________________________________________________________________________
559 InsertCatalogIteratorAsLRU ( CatalogCacheGlobals
*cacheGlobals
, CatalogIterator
*iterator
)
561 CatalogIterator
*swapIterator
;
563 if ( cacheGlobals
->lru
!= iterator
)
565 swapIterator
= cacheGlobals
->lru
;
566 cacheGlobals
->lru
= iterator
;
567 iterator
->nextMRU
->nextLRU
= iterator
->nextLRU
;
568 if ( iterator
->nextLRU
!= nil
)
569 iterator
->nextLRU
->nextMRU
= iterator
->nextMRU
;
571 cacheGlobals
->mru
= iterator
->nextMRU
;
572 iterator
->nextLRU
= swapIterator
;
573 iterator
->nextMRU
= nil
;
574 swapIterator
->nextMRU
= iterator
;
580 //_______________________________________________________________________________
581 // Routine: PrepareForLongName
583 // Function: Takes a CatalogIterator whose nameType is kShortUnicodeName, and
584 // changes the nameType to kLongUnicodeName.
586 // Since long Unicode names aren't stored in the CatalogIterator itself, we have
587 // to point to an HFSUniStr255 for storage. In the current implementation, we have
588 // just one such global buffer in the cache globals. We'll set the iterator to
589 // point to the global buffer and invalidate the iterator that was using it
590 // (i.e. the iterator whose nameType is kLongUnicodeName).
592 // Eventually, we might want to have a list of long name buffers which we recycle
593 // using an LRU algorithm. Or perhaps, some other way....
595 // Assumes: catalogIterator is locked (MacOS X)
596 //_______________________________________________________________________________
598 PrepareForLongName ( CatalogIterator
*iterator
)
600 CatalogCacheGlobals
*cacheGlobals
= GetCatalogCacheGlobals();
601 CatalogIterator
*iter
;
603 if (DEBUG_BUILD
&& iterator
->nameType
!= kShortUnicodeName
)
604 DebugStr("\p PrepareForLongName: nameType is wrong!");
607 // Walk through all the iterators. The first iterator whose nameType
608 // is kLongUnicodeName is invalidated (because it is using the global
609 // long name buffer).
612 CATALOG_ITER_LIST_LOCK(cacheGlobals
);
614 for ( iter
= cacheGlobals
->mru
; iter
!= nil
; iter
= iter
->nextMRU
)
616 if (iter
->nameType
== kLongUnicodeName
)
618 // if iterator is not already last then make it last
619 if ( iter
->nextMRU
!= nil
)
620 InsertCatalogIteratorAsLRU( cacheGlobals
, iter
);
622 (void) CI_LOCK_FROM_LIST(cacheGlobals
,iter
);
623 iter
->volume
= 0; // trash it
625 (void) CI_UNLOCK(iter
);
627 #if TARGET_API_MACOS_X
634 * if iter is nil then none of the iterators was using the LongUnicodeName buffer
637 CATALOG_ITER_LIST_UNLOCK(cacheGlobals
);
640 // Change the nameType of this iterator and point to the global
641 // long name buffer. Note - this iterator is already locked
643 iterator
->nameType
= kLongUnicodeName
;
644 iterator
->folderName
.longNamePtr
= &cacheGlobals
->longName
;