Annotation of XNU/bsd/hfs/hfscommon/Catalog/Catalog.c, revision 1.1

1.1     ! root        1: /*
        !             2:  * Copyright (c) 2000 Apple Computer, Inc. All rights reserved.
        !             3:  *
        !             4:  * @APPLE_LICENSE_HEADER_START@
        !             5:  * 
        !             6:  * The contents of this file constitute Original Code as defined in and
        !             7:  * are subject to the Apple Public Source License Version 1.1 (the
        !             8:  * "License").  You may not use this file except in compliance with the
        !             9:  * License.  Please obtain a copy of the License at
        !            10:  * http://www.apple.com/publicsource and read it before using this file.
        !            11:  * 
        !            12:  * This Original Code and all software distributed under the License are
        !            13:  * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
        !            14:  * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
        !            15:  * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
        !            16:  * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT.  Please see the
        !            17:  * License for the specific language governing rights and limitations
        !            18:  * under the License.
        !            19:  * 
        !            20:  * @APPLE_LICENSE_HEADER_END@
        !            21:  */
        !            22: /*
        !            23:        File:           Catalog.c
        !            24: 
        !            25:        Contains:       Catalog Manager Implementation
        !            26: 
        !            27:        Version:        HFS Plus 1.0
        !            28: 
        !            29:        Copyright:      � 1996-2000 by Apple Computer, Inc., all rights reserved.
        !            30: 
        !            31:        File Ownership:
        !            32: 
        !            33:                DRI:                            Don Brady
        !            34: 
        !            35:                Other Contact:          Mark Day
        !            36: 
        !            37:                Technology:                     xxx put technology here xxx
        !            38: 
        !            39:        Writers:
        !            40: 
        !            41:                (msd)   Mark Day
        !            42:                (DSH)   Deric Horn
        !            43:                (djb)   Don Brady
        !            44: 
        !            45:        Change History (most recent first):
        !            46:          <MacOSX>        2/2/99        djb             Fix CreateFileIDRef to copy entire name when creating thread record.
        !            47:          <MacOSX>        1/7/99        djb             Use a max bytes of 256 in calls to ConvertUnicodeToUTF8.
        !            48:          <MacOSX>       12/9/98        djb             UpdateCatalogNode only updates vcbLsMod if contentModDate changes.
        !            49:          <MacOSX>       11/5/98        djb             Add support for UTF-8 names.
        !            50:          <MacOSX>       8/31/98        djb             GetTimeLocal now takes an input.
        !            51:          <MacOSX>        7/8/98        ser             Added accessDate and AttributeModDate init. to create routine.
        !            52:          <MacOSX>        6/5/98        djb             Added CreateFileIDRef routine.
        !            53:          <MacOSX>        6/3/98        djb             Merge MoveCatalogRecord and RenameCatalogRecord into one routine.
        !            54:          <MacOSX>       4/17/98        djb             Add VCB locking.
        !            55:          <MacOSX>        4/6/98        djb             Catalog iterators now need to be released.
        !            56:          <MacOSX>        4/6/98        djb             Removed CreateVolumeCatalogCache and DisposeVolumeCatalogCache (obsolete).
        !            57:          <MacOSX>       3/31/98        djb             Make UpdateCatalogNode interface thread-safe.
        !            58:          <MacOSX>       3/31/98        djb             Sync up with final HFSVolumes.h header file.
        !            59:          <MacOSX>       3/17/98        djb             Fixed CreateCatalogNode interface to take kCatalogFolderNode and
        !            60:                                                                        kCatalogFileNode as type input.
        !            61: 
        !            62:          <CS36>        12/10/97        DSH             2201501, UpdateCatalogNode to only update CatalogRecords which
        !            63:                                                                        are under 2 Gig by checking the overloaded valence field.
        !            64:          <CS35>        11/20/97        djb             Radar #2002357. Fixing retry mechanism.
        !            65:          <CS34>        11/17/97        djb             PrepareInputName routine now returns an error.
        !            66:          <CS33>        11/13/97        djb             Radar #1683572. Add new GetCatalogOffspringFile routine for
        !            67:                                                                        PBGetFileInfo calls (support used to be in HFSPathnameCalls.a).
        !            68:          <CS32>         11/7/97        msd             Change calls to the wrapper routine CompareUnicodeNames() to use
        !            69:                                                                        the underlying routine FastUnicodeCompare() instead.
        !            70:          <CS31>        10/19/97        msd             Bug 1684586. GetCatInfo and SetCatInfo use only contentModDate.
        !            71:          <CS30>        10/17/97        djb             Change Catalog Create/Rename to use ConvertInputNameToUnicode.
        !            72:          <CS29>        10/13/97        djb             Update volumeNameEncodingHint when changing volume name. Change
        !            73:                                                                        name of GetSystemTextEncoding to GetDefaultTextEncoding.
        !            74:          <CS28>         10/1/97        djb             Add new catalog iterators and node cache to improve performance.
        !            75:          <CS27>         9/12/97        msd             In CreateCatalogNode, make sure parent is a folder, not a file.
        !            76:          <CS26>         9/10/97        msd             In RenameCatalogNodeUnicode, remove HFS-only code and make sure
        !            77:                                                                        the conversion context is set up and marked in the volume's
        !            78:                                                                        bitmap.
        !            79:          <CS25>          9/9/97        DSH             Added RelString_Glue to avoid having to link DFAEngine with
        !            80:                                                                        Interface.o
        !            81:          <CS24>          9/8/97        msd             Make sure a folder's modifyDate is set whenever its
        !            82:                                                                        contentModDate is set. In UpdateCatalogNode, make sure the
        !            83:                                                                        modifyDate is greater or equal to contentModDate; do a DebugStr
        !            84:                                                                        only for debug builds.
        !            85:          <CS23>          9/7/97        djb             Make some DebuStrs HFS_DIAGNOSTIC only.
        !            86:          <CS22>          9/4/97        djb             Add more Catalog Iterators, Instrument RelString.
        !            87:          <CS21>          9/4/97        msd             Remove call to PropertyDeleteObject.
        !            88:          <CS20>         8/18/97        DSH             Use RelString instead of FastRelString in DFA to avoid loading
        !            89:                                                                        branch island instead of table.
        !            90:          <CS19>         8/14/97        djb             Remove hard link support. Switch over to FastRelString.
        !            91:          <CS18>          8/8/97        djb             Fixed bugs in LinkCatalogNode.
        !            92:          <CS17>          8/5/97        djb             Don't restore vcbNxtCNID if thread exists (radar #1670614).
        !            93:          <CS16>         7/25/97        DSH             Pass heuristicHint to BTSearchRecord from GetCatalogOffspring.
        !            94:          <CS15>         7/18/97        msd             Include LowMemPriv.h. In LinkCatalogNode, now sets the
        !            95:                                                                        kInsertedFileThread2 flag correctly; should only affect error
        !            96:                                                                        recovery code.
        !            97:          <CS14>         7/16/97        DSH             FilesInternal.i renamed FileMgrInternal.i to avoid name
        !            98:                                                                        collision
        !            99:          <CS13>          7/8/97        DSH             Loading PrecompiledHeaders from define passed in on C line
        !           100:          <CS12>         6/27/97        msd             Add PBLongRename SPI. Added RenameCatalogNodeUnicode call, which
        !           101:                                                                        takes Unicode names for HFS Plus volumes. Removed calls to
        !           102:                                                                        Attributes module when creating, renaming or moving nodes.
        !           103:          <CS11>         6/24/97        djb             Validate the mangled name matches in
        !           104:                                                                        LocateCatalogNodeByMangledName.
        !           105:          <CS10>         6/24/97        djb             Add hard link support.
        !           106:           <CS9>         6/20/97        msd             Use contentModDate and attributeModDate fields instead of
        !           107:                                                                        modifyDate. Made CopyCatalogNodeData public.
        !           108:           <CS8>         6/18/97        djb             Add routines LocateCatalogNodeWithRetry & UpdateVolumeEncodings.
        !           109:                                                                        Add mangled name retry to DeleteCatalogNode, MoveCatalogNode and
        !           110:                                                                        RenameCatalogNode.
        !           111:           <CS7>         6/13/97        djb             Major changes for longname support and multiple scripts.
        !           112:           <CS6>          6/9/97        msd             Instead of calling GetDateTime, call GetTimeUTC or GetTimeLocal.
        !           113:                                                                        Dates on an HFS Plus volume need to be converted to/from UTC.
        !           114:           <CS5>          6/4/97        djb             Set textEncoding hint in Rename and Create. TrashCatalogIterator
        !           115:                                                                        was not always called with the correct folder ID.
        !           116:           <CS4>         5/21/97        djb             Turn off recursive iterators.
        !           117:           <CS3>         5/19/97        djb             Add support for B-tree iterators to GetCatalogOffspring.
        !           118:           <CS2>          5/9/97        djb             Get in sync with FilesInternal.i.
        !           119:           <CS1>         4/24/97        djb             First checked into Common System Project.
        !           120:         <HFS26>         4/11/97        DSH             Use extended VCB fields catalogRefNum, and extentsRefNum.
        !           121:         <HFS25>          4/4/97        djb             Get in sync with volume format changes.
        !           122:         <HFS24>         3/31/97        djb             Additional HFS Plus optimization added to GetCatalogNode.
        !           123:         <HFS23>         3/28/97        djb             Add Optimization to GetCatalogNode.
        !           124:         <HFS22>         3/27/97        djb             Unicode conversion routines now use byte counts.
        !           125:         <HFS21>         3/17/97        DSH             Casting to compile with SC, GetRecordSize ->
        !           126:                                                                        GetCatalogRecordSize, moved some prototypes to extern.
        !           127:         <HFS20>          3/5/97        msd             Add calls to Property Manager when catalog entries are created,
        !           128:                                                                        deleted, moved, renamed.
        !           129:         <HFS19>         2/19/97        djb             HFS Plus catalog keys no longer have a pad word.
        !           130:         <HFS18>         1/24/97        DSH             (djb) GetCatalogOffSpring() fix volume->vcbDirIDM = 0
        !           131:         <HFS17>         1/23/97        DSH             Truncate name to CMMaxCName characters in PrepareInputName().
        !           132:         <HFS16>         1/14/97        djb             Fixed RenameCatalogNode for case when just a cnid is passed.
        !           133:         <HFS15>         1/13/97        djb             Added support for varaible sized thread records in HFS+.
        !           134:         <HFS14>         1/11/97        DSH             Moving PrepareInputName() declaration fo FilesInternal.h
        !           135:         <HFS13>         1/10/97        djb             CopyCatalogNodeData was trashing the resource extents on HFS+.
        !           136:         <HFS12>         1/10/97        djb             CopyCatalogNodeData was trashing dataLogicalSize on HFS+ disks.
        !           137:         <HFS11>          1/9/97        djb             Get in sync with new HFSVolumesPriv.i.
        !           138:         <HFS10>          1/6/97        djb             Added name length checking to CompareExtendedCatalogKeys. Fixed
        !           139:                                                                        GetCatalogOffspring - it was not correctly passing the HFS+ flag
        !           140:                                                                        to PrepareOutputName. Fixed BuildKey for HFS+ keys.
        !           141:          <HFS9>          1/3/97        djb             Fixed termination bug in GetCatalogOffspring. Added support for
        !           142:                                                                        large keys. Integrated latest HFSVolumesPriv.h changes.
        !           143:          <HFS8>        12/19/96        DSH             Changed call from C_FlushMDB to HFS+ savy
        !           144:                                                                        FlushVolumeControlBlock()
        !           145:          <HFS7>        12/19/96        djb             Add new B-tree manager...
        !           146:          <HFS6>        12/13/96        djb             Fixing bugs for HFS+. Switch to HFSUnicodeWrappers routines.
        !           147:          <HFS5>        12/12/96        djb             Changed the SPI for GetCatalogNode, GetCatalogOffspring, and
        !           148:                                                                        UpdateCatalogNode.
        !           149:          <HFS4>        12/12/96        DSH             Removed static function declarations for functions used by
        !           150:                                                                        FileIDServices.c.
        !           151:          <HFS3>        11/11/96        djb             Added support for HFS+ Unicode names. Major changes throughout.
        !           152:          <HFS2>         11/4/96        djb             Added FSSpec output to GetCatalogNode and GetCatalogOffspring
        !           153:                                                                        routines.
        !           154:          <HFS1>        10/29/96        djb             first checked in
        !           155: 
        !           156: */
        !           157: 
        !           158: #pragma segment Catalog
        !           159: 
        !           160: #include       "../headers/FileMgrInternal.h"
        !           161: #include       "../headers/BTreesInternal.h"
        !           162: #include       "../headers/CatalogPrivate.h"
        !           163: #include       "../headers/HFSUnicodeWrappers.h"
        !           164: #include       "../headers/HFSInstrumentation.h"
        !           165: 
        !           166: 
        !           167: // External routines
        !           168: 
        !           169: extern SInt32 FastRelString( ConstStr255Param str1, ConstStr255Param str2 );
        !           170: 
        !           171: extern SInt16 RelString_Glue(StringPtr pStr1, StringPtr pStr2);
        !           172: 
        !           173: 
        !           174: // Internal routines
        !           175: 
        !           176: static OSErr IterateCatalogNode(ExtendedVCB *volume, CatalogIterator *catalogIterator,
        !           177:                                UInt16 index, FSSpec *nodeSpec, CatalogNodeData *nodeData,
        !           178:                                HFSCatalogNodeID *nodeID, SInt16 *nodeType);
        !           179: 
        !           180: void InitCatalogThreadRecord(ExtendedVCB *volume, UInt32 nodeType, CatalogKey *nodeKey,
        !           181:                                                         CatalogRecord *record, UInt32 *threadSize);
        !           182: 
        !           183: void InitCatalogRecord(ExtendedVCB *volume, UInt32 nodeType, UInt32 textEncoding,
        !           184:                                         CatalogRecord *record, UInt32 *recordSize, HFSCatalogNodeID *catalogNodeID);
        !           185: 
        !           186: #if HFS_DIAGNOSTIC
        !           187:                #include <sys/systm.h>
        !           188:            #define PRINTIT(A) kprintf A;
        !           189: #else
        !           190:            #define PRINTIT(A)
        !           191: #endif /* HFS_DIAGNOSTIC */
        !           192: 
        !           193: //_________________________________________________________________________________
        !           194: //     Exported Routines
        !           195: //
        !           196: //             CreateCatalogNode               -  Creates a new folder or file CNode.
        !           197: //             DeleteCatalogNode               -  Deletes an existing folder or file CNode.
        !           198: //             GetCatalogNode                  -  Locates an existing folder or file CNode.
        !           199: //             GetCatalogOffspringFile -  Gets an offspring file record from a folder.
        !           200: //             GetCatalogOffspring             -  Gets an offspring record from a folder.
        !           201: //             MoveRenameCatalogNode   -  Moves/Renames an existing folder or file CNode.
        !           202: //             UpdateCatalogNode               -  Marks a Catalog BTree node as 'dirty'.
        !           203: //             CreateFileIDRef                 -  Creates a file thread record for hfs file node
        !           204: //             CompareCatalogKeys              -  Compares two catalog keys.
        !           205: //
        !           206: //_________________________________________________________________________________
        !           207: 
        !           208: 
        !           209: //_________________________________________________________________________________
        !           210: //
        !           211: //     About date/time values:
        !           212: //
        !           213: //     Date/time values stored in control blocks and generic structures (such as
        !           214: //     CatalogNodeData) are always stored in local time.  Values stored in HFS volume
        !           215: //     format structures (such as B-tree records) are also stored in local time.
        !           216: //     Values stored in HFS Plus format structures are stored in UTC.
        !           217: //_________________________________________________________________________________
        !           218: 
        !           219: 
        !           220: // Implementation
        !           221: 
        !           222: 
        !           223: //_________________________________________________________________________________
        !           224: //     Routine:        CreateCatalogNode
        !           225: //
        !           226: //     Function:       Creates a new folder or file CNode.     A new folder or file
        !           227: //                             record is added to the catalog BTree.  If a folder CNode is
        !           228: //                             being created, a new thread record is also added.
        !           229: //
        !           230: //_________________________________________________________________________________
        !           231: 
        !           232: OSErr
        !           233: CreateCatalogNode ( ExtendedVCB *volume, HFSCatalogNodeID parentID, ConstUTF8Param name,
        !           234:                                        UInt32 nodeType, HFSCatalogNodeID *catalogNodeID, UInt32 *catalogHint)
        !           235: {
        !           236:        CatalogKey              nodeKey;                        // 518 bytes
        !           237:        CatalogRecord   nodeData;                       // 520 bytes
        !           238:        UInt32                  nodeDataSize;
        !           239:        CatalogRecord   parentThreadData;       // 520 bytes
        !           240:        HFSCatalogNodeID parentsParentID;
        !           241:        CatalogName             *parentNamePtr;
        !           242:        UInt32                  tempHint;
        !           243:        UInt32                  textEncoding;
        !           244:        UInt16                  tempSize;
        !           245:        OSErr                   result;
        !           246:        Boolean                 isHFSPlus = (volume->vcbSigWord == kHFSPlusSigWord);
        !           247: 
        !           248:        if (nodeType != kCatalogFolderNode && nodeType != kCatalogFileNode)
        !           249:                return paramErr;
        !           250: 
        !           251:        //--- make sure parent exists (by locating the parent's thread record)
        !           252:                        
        !           253:        result = LocateCatalogThread(volume, parentID, &parentThreadData, &tempSize, &tempHint);
        !           254:        ReturnIfError(result);
        !           255: 
        !           256:        TrashCatalogIterator(volume, parentID);         // invalidate any iterators for this parentID
        !           257: 
        !           258:        // save copy of parent's parentID and name.
        !           259: 
        !           260:        if (isHFSPlus)
        !           261:        {
        !           262:                if (parentThreadData.recordType != kHFSPlusFolderThreadRecord)
        !           263:                        return dirNFErr;
        !           264:                
        !           265:                parentsParentID = parentThreadData.hfsPlusThread.parentID;
        !           266:                parentNamePtr = (CatalogName*) &parentThreadData.hfsPlusThread.nodeName;
        !           267:        }
        !           268:        else
        !           269:        {
        !           270:                if (parentThreadData.recordType != kHFSFolderThreadRecord)
        !           271:                        return dirNFErr;
        !           272:                
        !           273:                parentsParentID = parentThreadData.hfsThread.parentID;
        !           274:                parentNamePtr = (CatalogName*) &parentThreadData.hfsThread.nodeName;
        !           275:        }
        !           276:        
        !           277:        // invalidate cache for parent since its about to change
        !           278:        InvalidateCatalogNodeCache(volume, parentsParentID);    
        !           279: 
        !           280:        //--- build key for new catalog node
        !           281:        result = BuildCatalogKeyUTF8(volume, parentID, name, kUndefinedStrLen, &nodeKey, &textEncoding);
        !           282:        ReturnIfError(result);
        !           283: 
        !           284:        //--- initialize catalog data record (for folder or file)
        !           285:        InitCatalogRecord(volume, nodeType, textEncoding, &nodeData, &nodeDataSize, catalogNodeID);
        !           286: 
        !           287:        //--- add new folder/file record to catalog BTree
        !           288:        result = InsertBTreeRecord(volume->catalogRefNum, &nodeKey, &nodeData, nodeDataSize, catalogHint);
        !           289:        if (result)
        !           290:        {
        !           291:                if (result == btExists)
        !           292:                        result = cmExists;
        !           293:                return result;
        !           294:        }
        !           295: 
        !           296:        //--- build thread record for new CNode
        !           297:        if (isHFSPlus || nodeType == kCatalogFolderNode)
        !           298:        {
        !           299:                HFSCatalogKey   threadKey;      // use the smaller key since name is null
        !           300:                CatalogRecord   threadData;     // 520 bytes
        !           301:                UInt32                  threadSize;
        !           302:                
        !           303:                BuildCatalogKey(*catalogNodeID, NULL, isHFSPlus, (CatalogKey*) &threadKey);
        !           304: 
        !           305:                InitCatalogThreadRecord(volume, nodeType, &nodeKey, &threadData, &threadSize);
        !           306: 
        !           307:                //--- add thread record to catalog BTree
        !           308:        
        !           309:                result = InsertBTreeRecord(volume->catalogRefNum, &threadKey, &threadData, threadSize, &tempHint);
        !           310: 
        !           311:                //--- couldn't add thread record, delete newly created folder record and exit
        !           312:                if (result)
        !           313:                {
        !           314:                        (void) DeleteBTreeRecord(volume->catalogRefNum, &nodeKey);
        !           315: 
        !           316:                        if ( result == btExists )               // <CS17>
        !           317:                                result = cmExists;                      // remap to a catalog error
        !           318: 
        !           319:                        return result;
        !           320:                }
        !           321:        }
        !           322: 
        !           323:        //--- update counters...
        !           324: 
        !           325:        result = UpdateFolderCount( volume, parentsParentID, parentNamePtr, nodeData.recordType, kNoHint, +1);
        !           326:        ReturnIfError(result);  /* XXX what about cleanup ??? */
        !           327:        
        !           328:        AdjustVolumeCounts(volume, nodeData.recordType, +1);
        !           329: 
        !           330:        result = FlushCatalog(volume);
        !           331: 
        !           332:        return result;
        !           333: 
        !           334: } // end CreateCatalogNode
        !           335: 
        !           336: 
        !           337: /*
        !           338:  * initialize catalog data record (for folder or file)
        !           339:  */
        !           340: void InitCatalogRecord(ExtendedVCB *volume, UInt32 nodeType, UInt32 textEncoding, CatalogRecord *record, UInt32 *recordSize, HFSCatalogNodeID *catalogNodeID)
        !           341: {
        !           342:        HFSCatalogNodeID nodeID;
        !           343:        UInt32 timeStamp;
        !           344: 
        !           345:        ClearMemory(record, sizeof(CatalogRecord));     // first clear the record
        !           346: 
        !           347:        VCB_LOCK(volume);
        !           348:        nodeID = volume->vcbNxtCNID++;          // get CNID and bump next available CNode ID
        !           349:        VCB_UNLOCK(volume);
        !           350:        *catalogNodeID = nodeID;                        // make sure it gets passed back
        !           351:        
        !           352:        if (volume->vcbSigWord == kHFSPlusSigWord)
        !           353:        {
        !           354:                timeStamp = GetTimeUTC();               // get current date/time (universal)
        !           355: 
        !           356:                UpdateVolumeEncodings(volume, textEncoding);
        !           357: 
        !           358:                if (nodeType == kCatalogFolderNode )
        !           359:                {
        !           360:                        record->recordType = kHFSPlusFolderRecord;
        !           361:                        record->hfsPlusFolder.folderID = nodeID;
        !           362:                        record->hfsPlusFolder.createDate = timeStamp;
        !           363:                        record->hfsPlusFolder.contentModDate = timeStamp;
        !           364:             record->hfsPlusFolder.accessDate = timeStamp;
        !           365:             record->hfsPlusFolder.attributeModDate     = timeStamp;
        !           366:                        record->hfsPlusFolder.textEncoding = textEncoding;
        !           367:                        *recordSize = sizeof(HFSPlusCatalogFolder);
        !           368:                //      threadType = kHFSPlusFolderThreadRecord;
        !           369:                }
        !           370:                else if (nodeType == kCatalogFileNode )
        !           371:                {
        !           372:                        record->recordType = kHFSPlusFileRecord;
        !           373:                        record->hfsPlusFile.fileID = nodeID;
        !           374:                        record->hfsPlusFile.createDate = timeStamp;
        !           375:             record->hfsPlusFile.contentModDate = timeStamp;
        !           376:             record->hfsPlusFile.accessDate = timeStamp;
        !           377:             record->hfsPlusFile.attributeModDate = timeStamp;
        !           378:                        record->hfsPlusFile.flags |= kHFSThreadExistsMask;
        !           379:                        record->hfsPlusFile.textEncoding = textEncoding;
        !           380:                        *recordSize = sizeof(HFSPlusCatalogFile);
        !           381:                //      threadType = kHFSPlusFileThreadRecord;
        !           382:                }
        !           383:        }
        !           384:        else /* standard hfs */
        !           385:        {
        !           386:                timeStamp = GetTimeLocal(true);         // get current local date/time
        !           387: 
        !           388:                if (nodeType == kCatalogFolderNode )
        !           389:                {
        !           390:                        record->recordType = kHFSFolderRecord;
        !           391:                        record->hfsFolder.folderID = nodeID;
        !           392:                        record->hfsFolder.createDate = timeStamp;
        !           393:                        record->hfsFolder.modifyDate = timeStamp;
        !           394:                        *recordSize = sizeof(HFSCatalogFolder);
        !           395:                //      threadType = kHFSFolderThreadRecord;
        !           396:                }
        !           397:                else if (nodeType == kCatalogFileNode )
        !           398:                {
        !           399:                        record->recordType = kHFSFileRecord;
        !           400:                        record->hfsFile.fileID = nodeID;
        !           401:                        record->hfsFile.createDate = timeStamp;
        !           402:                        record->hfsFile.modifyDate = timeStamp;
        !           403:                        *recordSize = sizeof(HFSCatalogFile);
        !           404:                }
        !           405:        }
        !           406: }
        !           407: 
        !           408: 
        !           409: void InitCatalogThreadRecord(ExtendedVCB *volume, UInt32 nodeType, CatalogKey *nodeKey,
        !           410:                                                         CatalogRecord *record, UInt32 *threadSize)
        !           411: {      
        !           412:        ClearMemory(record, sizeof(CatalogRecord) );    // first clear the record
        !           413: 
        !           414:        if (volume->vcbSigWord == kHFSPlusSigWord)
        !           415:        {
        !           416:                if (nodeType == kCatalogFolderNode)
        !           417:                        record->recordType = kHFSPlusFolderThreadRecord;
        !           418:                else
        !           419:                        record->recordType = kHFSPlusFileThreadRecord;
        !           420:                record->hfsPlusThread.parentID = nodeKey->hfsPlus.parentID;                     
        !           421:                *threadSize = sizeof(record->hfsPlusThread);
        !           422: 
        !           423:                // HFS Plus has varaible sized threads so adjust to actual length
        !           424:                *threadSize -= ( sizeof(record->hfsPlusThread.nodeName.unicode) -
        !           425:                                                (nodeKey->hfsPlus.nodeName.length * sizeof(UniChar)) );
        !           426:                BlockMoveData(&nodeKey->hfsPlus.nodeName, &record->hfsPlusThread.nodeName,
        !           427:                                          sizeof(UniChar) * (nodeKey->hfsPlus.nodeName.length + 1));
        !           428:     }
        !           429:     else // classic HFS
        !           430:     {
        !           431:                if (nodeType == kCatalogFolderNode)
        !           432:                        record->recordType = kHFSFolderThreadRecord;
        !           433:                else
        !           434:                        record->recordType = kHFSFileThreadRecord;
        !           435:                record->hfsThread.parentID = nodeKey->hfs.parentID;     
        !           436:                *threadSize = sizeof(record->hfsThread);
        !           437:                BlockMoveData(&nodeKey->hfs.nodeName, &record->hfsThread.nodeName,
        !           438:                                          nodeKey->hfs.nodeName[0] + 1);
        !           439:     }
        !           440: }
        !           441: 
        !           442: 
        !           443: //_________________________________________________________________________________
        !           444: //     Routine:        DeleteCatalogNode
        !           445: //
        !           446: //     Function:       Deletes an existing folder or file CNode. The thread record
        !           447: //                             is also deleted for directories and files that have thread
        !           448: //                             records.
        !           449: //
        !           450: //                             The valence for a folder must be zero before it can be deleted.
        !           451: //                             The rootfolder cannot be deleted.
        !           452: //
        !           453: //_________________________________________________________________________________
        !           454: 
        !           455: OSErr
        !           456: DeleteCatalogNode(ExtendedVCB *volume, HFSCatalogNodeID parentID, ConstUTF8Param name, UInt32 hint)
        !           457: {
        !           458:        CatalogKey                      key;            // 518 bytes
        !           459:        CatalogRecord           data;           // 520 bytes
        !           460:        UInt32                          nodeHint;
        !           461:        HFSCatalogNodeID        nodeID;
        !           462:        HFSCatalogNodeID        nodeParentID;
        !           463:        UInt16                          nodeType;
        !           464:        OSErr                           result;
        !           465:        Boolean                         isHFSPlus = (volume->vcbSigWord == kHFSPlusSigWord);
        !           466:        
        !           467:        //--- locate subject catalog node
        !           468: 
        !           469:        result = BuildCatalogKeyUTF8(volume, parentID, name, kUndefinedStrLen, &key, NULL);
        !           470:        ReturnIfError(result);
        !           471: 
        !           472:        result = LocateCatalogNodeByKey(volume, hint, &key, &data, &nodeHint);
        !           473: 
        !           474:        // if we did not find it by name, then look for an embedded file ID in a mangled name
        !           475:        if ( (result == cmNotFound) && isHFSPlus )
        !           476:         result = LocateCatalogNodeByMangledName(volume, parentID, name, kUndefinedStrLen, &key, &data, &nodeHint);
        !           477:        ReturnIfError(result);
        !           478: 
        !           479:        nodeParentID = isHFSPlus ? key.hfsPlus.parentID : key.hfs.parentID;     // establish real parent cnid
        !           480:        nodeType = data.recordType;             // establish cnode type
        !           481:        nodeID = 0;
        !           482: 
        !           483:        switch (nodeType)
        !           484:        {
        !           485:                case kHFSFolderRecord:
        !           486:                        if (data.hfsFolder.valence != 0)                // is it empty?
        !           487:                                return cmNotEmpty;
        !           488:                        
        !           489:                        nodeID = data.hfsFolder.folderID;
        !           490:                        break;
        !           491: 
        !           492:                case kHFSPlusFolderRecord:
        !           493:                        if (data.hfsPlusFolder.valence != 0)            // is it empty?
        !           494:                                return cmNotEmpty;
        !           495:                        
        !           496:                        nodeID = data.hfsPlusFolder.folderID;
        !           497:                        break;
        !           498: 
        !           499:                case kHFSFileRecord:
        !           500:                        if (data.hfsFile.flags & kHFSThreadExistsMask)
        !           501:                                nodeID = data.hfsFile.fileID;
        !           502:                        break;
        !           503: 
        !           504:                case kHFSPlusFileRecord:
        !           505:                        nodeID = data.hfsPlusFile.fileID;       // note: HFS Plus files always have a thread
        !           506:                        break;
        !           507: 
        !           508:                default:
        !           509:                        return cmNotFound;
        !           510:        }
        !           511: 
        !           512: 
        !           513:        if (nodeID == fsRtDirID)        // is this the root folder?
        !           514:                return cmRootCN;                // sorry, you can't delete the root!
        !           515: 
        !           516:        TrashCatalogIterator(volume, nodeParentID);                     // invalidate any iterators for this parentID
        !           517:        InvalidateCatalogNodeCache(volume, nodeParentID);       // and invalidate node cache
        !           518: 
        !           519:        //--- delete catalog records for CNode and file threads if they exist
        !           520: 
        !           521:        result = DeleteBTreeRecord(volume->catalogRefNum, &key);
        !           522:        ReturnIfError(result);
        !           523: 
        !           524:        if ( nodeID ) 
        !           525:        {
        !           526:                CatalogKey threadKey;   // 518 bytes
        !           527:                
        !           528:                BuildCatalogKey(nodeID, NULL, isHFSPlus, &threadKey);
        !           529:                
        !           530:                (void) DeleteBTreeRecord(volume->catalogRefNum, &threadKey);    // ignore errors for thread deletes
        !           531:        }
        !           532: 
        !           533:        //--- update counters...
        !           534: 
        !           535:        result = UpdateFolderCount(volume, nodeParentID, NULL, nodeType, kNoHint, -1);
        !           536:        ReturnIfError(result);
        !           537: 
        !           538:        AdjustVolumeCounts(volume, nodeType, -1);       // all done with this file or folder
        !           539: 
        !           540:        result = FlushCatalog(volume);
        !           541: 
        !           542:        return result;
        !           543: 
        !           544: } // end DeleteCatalogNode
        !           545: 
        !           546: 
        !           547: //_________________________________________________________________________________
        !           548: //     Routine:        GetCatalogNode
        !           549: //
        !           550: //     Function:       Locates an existing folder or file CNode and returns an FSSpec for
        !           551: //                             the CNode and a pointer to the CNode data record.
        !           552: //
        !           553: //_________________________________________________________________________________
        !           554: 
        !           555: OSErr
        !           556: GetCatalogNode( ExtendedVCB *volume, HFSCatalogNodeID parentID, ConstUTF8Param name, UInt32 nameLen, UInt32 hint,
        !           557:                                FSSpec *nodeSpec, CatalogNodeData *nodeData, UInt32 *newHint)
        !           558: {
        !           559:        CatalogKey *key;
        !           560:        CatalogRecord *record;
        !           561:        BTreeIterator searchIterator;
        !           562:        FSBufferDescriptor btRecord;
        !           563:        ByteCount actualDstLen;
        !           564:        UInt32 heuristicHint;
        !           565:        UInt32 *cachedHint;
        !           566:        FCB *fcb;
        !           567:        OSErr result = noErr;
        !           568:        UInt16 dataSize;
        !           569:        Boolean isHFSPlus = (volume->vcbSigWord == kHFSPlusSigWord);
        !           570: 
        !           571:        if (isHFSPlus) {
        !           572:                btRecord.bufferAddress = nodeData;
        !           573:                btRecord.itemSize = sizeof(CatalogNodeData);
        !           574:        } else {
        !           575:                btRecord.bufferAddress = &nodeData->cnd_extra;
        !           576:                btRecord.itemSize = sizeof(HFSCatalogFile);
        !           577:        }
        !           578: 
        !           579:        btRecord.itemCount = 1;
        !           580:        record = (CatalogRecord *) btRecord.bufferAddress;
        !           581:        key = (CatalogKey *) &searchIterator.key;
        !           582:        
        !           583:     if (name && nameLen == kUndefinedStrLen)
        !           584:                nameLen = strlen(name);
        !           585: 
        !           586:        result = BuildCatalogKeyUTF8(volume, parentID, name, nameLen, key, NULL);
        !           587:        ReturnIfError(result);
        !           588:        
        !           589:        fcb = GetFileControlBlock(volume->catalogRefNum);
        !           590:        searchIterator.hint.nodeNum = *newHint;
        !           591:        searchIterator.hint.index = 0;
        !           592:        
        !           593:        /*
        !           594:         * We pass a 2nd hint/guess into BTSearchRecord.  The heuristicHint
        !           595:         * is a mapping of dirID and nodeNumber, in hopes that the current
        !           596:         * search will be in the same node as the last search with the same
        !           597:         * parentID.
        !           598:         */
        !           599:        if (name != NULL && GetMRUCacheBlock(parentID, volume->hintCachePtr, (Ptr *)&cachedHint) == 0)
        !           600:                heuristicHint = *cachedHint;
        !           601:        else
        !           602:                heuristicHint = kInvalidMRUCacheKey;
        !           603: 
        !           604:        result = BTSearchRecord(fcb, &searchIterator, heuristicHint, &btRecord, &dataSize, &searchIterator);
        !           605:        if (result == btNotFound)
        !           606:                result = cmNotFound;    
        !           607: 
        !           608:        if (name != NULL && result == noErr)
        !           609:                InsertMRUCacheBlock(volume->hintCachePtr, parentID, (Ptr) &(searchIterator.hint.nodeNum));
        !           610: 
        !           611:        if (result == noErr) {
        !           612:                CatalogName *nodeName = NULL;
        !           613:                HFSCatalogNodeID threadParentID;
        !           614: 
        !           615:                /* if we got a thread record, then go look up real record */
        !           616:                switch (record->recordType) {
        !           617: 
        !           618:                case kHFSFileThreadRecord:
        !           619:                case kHFSFolderThreadRecord:
        !           620:                        threadParentID = record->hfsThread.parentID;
        !           621:                        nodeName = (CatalogName *) &record->hfsThread.nodeName;
        !           622:                        break;
        !           623: 
        !           624:                case kHFSPlusFileThreadRecord:
        !           625:                case kHFSPlusFolderThreadRecord:
        !           626:                        threadParentID = record->hfsPlusThread.parentID;
        !           627:                        nodeName = (CatalogName *) &record->hfsPlusThread.nodeName;     
        !           628:                        break;
        !           629: 
        !           630:                default:
        !           631:                        threadParentID = 0;
        !           632:                        *newHint = searchIterator.hint.nodeNum;
        !           633:                        break;
        !           634:                }
        !           635:                if (threadParentID) {
        !           636:                        BuildCatalogKey(threadParentID, nodeName, isHFSPlus, key);
        !           637:                        searchIterator.hint.nodeNum = kNoHint;
        !           638:                        searchIterator.hint.index = 0;
        !           639: 
        !           640:                        result = BTSearchRecord(fcb, &searchIterator, kInvalidMRUCacheKey, &btRecord, &dataSize, &searchIterator);
        !           641:                        if (result == btNotFound)
        !           642:                                result = cmNotFound;
        !           643:                        if (result == noErr)
        !           644:                                *newHint = searchIterator.hint.nodeNum;
        !           645:                }
        !           646:        }
        !           647:  
        !           648:    // if we did not find it by name, then look for an embedded file ID in a mangled name
        !           649:     if ( result == cmNotFound && isHFSPlus)
        !           650:         result = LocateCatalogNodeByMangledName(volume, parentID, name, nameLen, key, record, newHint);
        !           651:        ReturnIfError(result);
        !           652: 
        !           653:        nodeSpec->parID = isHFSPlus ? key->hfsPlus.parentID : key->hfs.parentID;
        !           654:        
        !           655:        if ( isHFSPlus )
        !           656:        {
        !           657:                        result = ConvertUnicodeToUTF8(key->hfsPlus.nodeName.length * sizeof(UniChar),
        !           658:                                                                                  key->hfsPlus.nodeName.unicode,
        !           659:                                                                                  NAME_MAX + 1, /* 255 + termination byte */
        !           660:                                                                                  &actualDstLen,
        !           661:                                                                                  nodeSpec->name);
        !           662:                }
        !           663:        else // classic HFS
        !           664:        {
        !           665:                /* convert data to HFS Plus format */
        !           666:                if (record->recordType == kHFSFolderRecord || record->recordType == kHFSFileRecord) {
        !           667:                        CopyCatalogNodeData(volume, record, nodeData);
        !           668:                        result = hfs_to_utf8(volume, key->hfs.nodeName, NAME_MAX + 1, &actualDstLen, nodeSpec->name);
        !           669:                } else
        !           670:                        result = cmNotFound;
        !           671:        }
        !           672: 
        !           673:   #if DEBUG_BUILD
        !           674:        if ( nodeData->nodeID > volume->vcbNxtCNID || nodeData->nodeID == 0)
        !           675:                DebugStr("\pGetCatalogNode bad file ID found!");
        !           676:   #endif
        !           677:  
        !           678:        return result;
        !           679: 
        !           680: } // end GetCatalogNode
        !           681: 
        !           682: 
        !           683: UInt32
        !           684: GetDirEntrySize(BTreeIterator *bip, ExtendedVCB * vol)
        !           685: {
        !           686:        CatalogKey *    ckp;
        !           687:        CatalogName *   cnp;
        !           688:        ByteCount       utf8chars;
        !           689:        UInt8           name[NAME_MAX + 1];
        !           690:        OSErr           result;
        !           691: 
        !           692:        ckp = (CatalogKey*) &bip->key;
        !           693: 
        !           694:        if (vol->vcbSigWord == kHFSPlusSigWord) {
        !           695:                cnp = (CatalogName*) &ckp->hfsPlus.nodeName;
        !           696:                result = ConvertUnicodeToUTF8(cnp->ustr.length * sizeof(UniChar),
        !           697:                                          cnp->ustr.unicode,
        !           698:                                          NAME_MAX + 1, /* 255 + termination byte */
        !           699:                                          &utf8chars,
        !           700:                                          name);
        !           701:                /*XXX ignoring error */
        !           702: 
        !           703:        } else { /* hfs */
        !           704:                cnp = (CatalogName*) ckp->hfs.nodeName;
        !           705:                result = hfs_to_utf8(vol, cnp->pstr, NAME_MAX + 1, &utf8chars, name);
        !           706:                /*XXX ignoring error */
        !           707:        }
        !           708: 
        !           709:        return DIRENTRY_SIZE(utf8chars);
        !           710: }
        !           711: 
        !           712: 
        !           713: OSErr
        !           714: PositionIterator(CatalogIterator *cip, UInt32 offset, BTreeIterator *bip, UInt16 *op)
        !           715: {
        !           716: #define CAT_START_OFFSET (2 * sizeof(struct hfsdotentry))
        !           717:        ExtendedVCB *   vol;
        !           718:        FCB *           fcb;
        !           719:        OSErr           result = 0;
        !           720: 
        !           721:        /* are we past the end of a directory? */
        !           722:        if (cip->folderID != cip->parentID)
        !           723:                return(cmNotFound);
        !           724: 
        !           725:        vol = cip->volume;
        !           726:        fcb = GetFileControlBlock(vol->catalogRefNum);
        !           727: 
        !           728:        /* make a btree iterator from catalog iterator */
        !           729:        UpdateBtreeIterator(cip, bip);
        !           730: 
        !           731:        if (cip->currentOffset == offset) {
        !           732:                *op = kBTreeCurrentRecord;
        !           733: 
        !           734:        } else if (cip->nextOffset == offset) {
        !           735:                *op = kBTreeNextRecord;
        !           736: 
        !           737:        } else { /* start from beginning */
        !           738:                UInt32  heuristicHint;
        !           739:                UInt32  *cachedHint;
        !           740: 
        !           741:                *op = kBTreeNextRecord;
        !           742: 
        !           743:                /*
        !           744:                 * We pass a 2nd hint/guess into BTSearchRecord.  The heuristicHint
        !           745:                 * is a mapping of dirID and nodeNumber, in hopes that the current
        !           746:                 * search will be in the same node as the last search with the same
        !           747:                 * parentID.
        !           748:                 */
        !           749:                result = GetMRUCacheBlock( cip->folderID, vol->hintCachePtr, (Ptr *)&cachedHint );
        !           750:                heuristicHint = (result == noErr) ? *cachedHint : kInvalidMRUCacheKey;
        !           751: 
        !           752:                /* Position iterator at the folder's thread record */
        !           753:                result = BTSearchRecord(fcb, bip, heuristicHint, NULL, NULL, bip);
        !           754:                if (result)
        !           755:                        goto exit;
        !           756: 
        !           757:                InsertMRUCacheBlock( vol->hintCachePtr, cip->folderID, (Ptr) &bip->hint.nodeNum );
        !           758:                
        !           759:                /* find offset (note: n^2 / 2) */
        !           760:                if (offset > CAT_START_OFFSET) { 
        !           761:                        HFSCatalogNodeID  pid, *idp;
        !           762:                        UInt32  curOffset, nextOffset;
        !           763: 
        !           764:                        /* get first record (ie offset 24) */
        !           765:                        result = BTIterateRecord( fcb, kBTreeNextRecord, bip, NULL, NULL );             
        !           766:                        if (result)
        !           767:                                goto exit;
        !           768: 
        !           769:                        if (vol->vcbSigWord == kHFSPlusSigWord)
        !           770:                                idp = &((CatalogKey*) &bip->key)->hfsPlus.parentID;
        !           771:                        else
        !           772:                                idp = &((CatalogKey*) &bip->key)->hfs.parentID;
        !           773:                        
        !           774:                        pid = *idp;
        !           775: 
        !           776:                        curOffset = CAT_START_OFFSET;
        !           777:                        nextOffset = GetDirEntrySize(bip, vol);
        !           778: 
        !           779:                        while (nextOffset < offset) {
        !           780:                                result = BTIterateRecord( fcb, kBTreeNextRecord, bip, NULL, NULL );             
        !           781:                                if (result)
        !           782:                                        goto exit;
        !           783:                                
        !           784:                                /* check for parent change */
        !           785:                                if (pid != *idp) {
        !           786:                                        result = cmNotFound;    /* offset past end of directory */
        !           787:                                        goto exit;
        !           788:                                }
        !           789: 
        !           790:                                curOffset = nextOffset;
        !           791:                                nextOffset += GetDirEntrySize(bip, vol);
        !           792:                        };
        !           793:        
        !           794:                        if (nextOffset != offset) {
        !           795:                                result = cmNotFound;
        !           796:                                goto exit;
        !           797:                        }
        !           798:        
        !           799:                        UpdateCatalogIterator(bip, cip);
        !           800:                        cip->currentOffset = curOffset;
        !           801:                        cip->nextOffset = nextOffset;
        !           802:                }
        !           803:        }
        !           804: 
        !           805: exit:  
        !           806:        if (result == btNotFound)
        !           807:                result = cmNotFound;
        !           808: 
        !           809:        return result;
        !           810: 
        !           811: } /* end PositionIterator */
        !           812: 
        !           813: 
        !           814: //_________________________________________________________________________________
        !           815: //     Routine:        GetCatalogOffspring
        !           816: //
        !           817: //     Function:       Gets an offspring record from a specified folder. The folder
        !           818: //                             is identified by it's folderID.  The desired offspring CNode is
        !           819: //                             indicated by the value of the offspring index (1 = 1st offspring
        !           820: //                             CNode, 2 = 2nd offspring CNode, etc.).
        !           821: //
        !           822: //_________________________________________________________________________________
        !           823: 
        !           824: OSErr
        !           825: GetCatalogOffspring(ExtendedVCB *volume, HFSCatalogNodeID folderID, UInt16 index,
        !           826:                    FSSpec *nodeSpec, CatalogNodeData *nodeData,
        !           827:                    HFSCatalogNodeID *nodeID, SInt16 *nodeType)
        !           828: {
        !           829:        CatalogIterator *       catalogIterator;
        !           830:        OSErr                           result;
        !           831: 
        !           832: 
        !           833:        if ( folderID == 0 )
        !           834:                return cmNotFound;
        !           835: 
        !           836:        /*
        !           837:         * return cmNotFound for index 32767, to prevent overflowing
        !           838:         * the index into negative numbers.
        !           839:         */
        !           840:        if ( index == 32767 )
        !           841:                return cmNotFound;
        !           842: 
        !           843:        // get best catalog iterator...
        !           844:        catalogIterator = oGetCatalogIterator(volume, folderID, index);
        !           845:        
        !           846:        result = IterateCatalogNode(volume, catalogIterator, index, nodeSpec,
        !           847:                                    nodeData, nodeID, nodeType);
        !           848: 
        !           849:        (void) ReleaseCatalogIterator(catalogIterator);
        !           850: 
        !           851:        return result;
        !           852: 
        !           853: } // end GetCatalogOffspring
        !           854: 
        !           855: 
        !           856: //_________________________________________________________________________________
        !           857: //     Routine:        IterateCatalogNode
        !           858: //
        !           859: //     Function:       Gets an offspring record from a specified folder. The folder
        !           860: //                             is identified by it's folderID.  The desired offspring CNode is
        !           861: //                             indicated by the value of the offspring index (1 = 1st offspring
        !           862: //                             CNode, 2 = 2nd offspring CNode, etc.).
        !           863: //
        !           864: //_________________________________________________________________________________
        !           865: 
        !           866: static OSErr
        !           867: IterateCatalogNode( ExtendedVCB *volume, CatalogIterator *catalogIterator, UInt16 index,
        !           868:                    FSSpec *nodeSpec, CatalogNodeData *nodeData, HFSCatalogNodeID *nodeID,
        !           869:                    SInt16 *nodeType )
        !           870: {
        !           871:        HFSCatalogNodeID        offspringParentID;
        !           872:        CatalogKey *            offspringKey;
        !           873:        CatalogName *           offspringName;
        !           874:        BTreeIterator           btreeIterator;
        !           875:        FSBufferDescriptor      btRecord;
        !           876:        CatalogRecord * record;
        !           877:        UInt8 databuf[32];      /* space for partial record */
        !           878:        FCB *                           fcb;
        !           879:        SInt16                          selectionIndex;
        !           880:        UInt16                          tempSize;
        !           881:        UInt16                          operation;
        !           882:        OSErr                           result;
        !           883:        Boolean                         isHFSPlus;
        !           884:        ByteCount                       actualDstLen;
        !           885: 
        !           886: 
        !           887:        isHFSPlus = (volume->vcbSigWord == kHFSPlusSigWord);
        !           888:        fcb = GetFileControlBlock(volume->catalogRefNum);
        !           889: 
        !           890:        // make a btree iterator from catalog iterator
        !           891:        UpdateBtreeIterator(catalogIterator, &btreeIterator);
        !           892: 
        !           893:        /* if client doesn't want data (ie readdir), just get type and id */
        !           894:        if (nodeData == NULL) {
        !           895:                /* data buf has space to cover all type/id offsets */
        !           896:                btRecord.bufferAddress = databuf;
        !           897:                btRecord.itemSize = sizeof(databuf);
        !           898:        } else if (isHFSPlus) {
        !           899:                btRecord.bufferAddress = nodeData;
        !           900:                btRecord.itemSize = sizeof(CatalogNodeData);
        !           901:        } else {
        !           902:                btRecord.bufferAddress = &nodeData->cnd_extra;
        !           903:                btRecord.itemSize = sizeof(HFSCatalogFile);
        !           904:        }
        !           905:        btRecord.itemCount = 1;
        !           906: 
        !           907:        //--- if neccessary position the iterator at the thread record for the specified folder
        !           908: 
        !           909:        if ( catalogIterator->currentIndex == 0 )       // is this a new iterator?
        !           910:        {
        !           911:                UInt32  heuristicHint;
        !           912:                UInt32  *cachedHint;
        !           913: 
        !           914:                //      We pass a 2nd hint/guess into BTSearchRecord.  The heuristicHint is a mapping of
        !           915:                //      dirID and nodeNumber, in hopes that the current search will be in the same node
        !           916:                //      as the last search with the same parentID.
        !           917:                result = GetMRUCacheBlock( catalogIterator->folderID, volume->hintCachePtr, (Ptr *)&cachedHint );
        !           918:                heuristicHint = (result == noErr) ? *cachedHint : kInvalidMRUCacheKey;
        !           919: 
        !           920:                result = BTSearchRecord( fcb, &btreeIterator, heuristicHint, &btRecord, &tempSize, &btreeIterator );
        !           921:                ExitOnError(result);
        !           922:                
        !           923:                UpdateCatalogIterator(&btreeIterator, catalogIterator); // update btree hint and key
        !           924: 
        !           925:                InsertMRUCacheBlock( volume->hintCachePtr, catalogIterator->folderID, (Ptr) &btreeIterator.hint.nodeNum );
        !           926:        }
        !           927: 
        !           928:        //--- get offspring record (relative to catalogIterator's position)
        !           929: 
        !           930:        selectionIndex = index - catalogIterator->currentIndex;
        !           931: 
        !           932:        // now we have to map index into next/prev operations...
        !           933:        if (selectionIndex == 1)
        !           934:        {
        !           935:                operation = kBTreeNextRecord;
        !           936:        }
        !           937:        else if (selectionIndex == -1)
        !           938:        {
        !           939:                operation = kBTreePrevRecord;
        !           940:        }
        !           941:        else if (selectionIndex == 0)
        !           942:        {
        !           943:                operation = kBTreeCurrentRecord;
        !           944:        }
        !           945:        else if (selectionIndex > 1)
        !           946:        {
        !           947:                UInt32  i;
        !           948:                
        !           949:                for (i = 1; i < selectionIndex; ++i)
        !           950:                {
        !           951:                        result = BTIterateRecord( fcb, kBTreeNextRecord, &btreeIterator, &btRecord, &tempSize );
        !           952:                        ExitOnError(result);
        !           953:                }
        !           954:                operation = kBTreeNextRecord;
        !           955:        }
        !           956:        else // (selectionIndex < -1)
        !           957:        {
        !           958:                SInt32  i;
        !           959: 
        !           960:                for (i = -1; i > selectionIndex; --i)
        !           961:                {
        !           962:                        result = BTIterateRecord( fcb, kBTreePrevRecord, &btreeIterator, &btRecord, &tempSize );
        !           963:                        ExitOnError(result);
        !           964:                }
        !           965:                operation = kBTreePrevRecord;
        !           966:        }
        !           967: 
        !           968:        result = BTIterateRecord( fcb, operation, &btreeIterator, &btRecord, &tempSize );
        !           969:        ExitOnError(result);
        !           970: 
        !           971:        offspringKey = (CatalogKey*) &btreeIterator.key;
        !           972: 
        !           973:        if (isHFSPlus)
        !           974:        {
        !           975:                offspringParentID = offspringKey->hfsPlus.parentID;
        !           976:                offspringName = (CatalogName*) &offspringKey->hfsPlus.nodeName;
        !           977:        }
        !           978:        else
        !           979:        {
        !           980:                offspringParentID = offspringKey->hfs.parentID;
        !           981:                offspringName = (CatalogName*) offspringKey->hfs.nodeName;
        !           982:        }
        !           983: 
        !           984:        if (offspringParentID != catalogIterator->folderID)             // different parent?
        !           985:        {
        !           986:                AgeCatalogIterator(catalogIterator);    // we reached the end, so don't hog the cache!
        !           987: 
        !           988:                result = cmNotFound;                            // must be done with this folder
        !           989:                goto ErrorExit;
        !           990:        }
        !           991: 
        !           992:        UpdateCatalogIterator(&btreeIterator, catalogIterator);         // update btree hint and key
        !           993:        catalogIterator->currentIndex = index;                                          // update the offspring index marker
        !           994: 
        !           995:        nodeSpec->parID = offspringParentID;
        !           996:        record = (CatalogRecord *) btRecord.bufferAddress;
        !           997: 
        !           998:        if (isHFSPlus)
        !           999:        {
        !          1000:                result = ConvertUnicodeToUTF8(offspringName->ustr.length * sizeof(UniChar),
        !          1001:                                                                          offspringName->ustr.unicode,
        !          1002:                                                                          NAME_MAX + 1, /* 255 + termination byte */
        !          1003:                                                                          &actualDstLen,
        !          1004:                                                                          nodeSpec->name);
        !          1005:                if (nodeData == NULL) {
        !          1006:                        *nodeType = record->recordType;
        !          1007:                        *nodeID = record->hfsPlusFolder.folderID;
        !          1008:                }
        !          1009:        }
        !          1010:        else /* hfs name */
        !          1011:        {
        !          1012:                if (record->recordType == kHFSFolderRecord || record->recordType == kHFSFileRecord) {
        !          1013: 
        !          1014:                        result = hfs_to_utf8(volume, offspringName->pstr, NAME_MAX + 1, &actualDstLen, nodeSpec->name);
        !          1015: 
        !          1016:                        if (nodeData == NULL) {
        !          1017:                                if (record->recordType == kHFSFileRecord) {
        !          1018:                                        *nodeType = kCatalogFileNode;
        !          1019:                                        *nodeID = record->hfsFile.fileID;
        !          1020:                                } else {
        !          1021:                                        *nodeType = kCatalogFolderNode;
        !          1022:                                        *nodeID = record->hfsFolder.folderID;
        !          1023:                                }
        !          1024:                        } else {
        !          1025:                                /* convert data to HFS Plus format */
        !          1026:                                CopyCatalogNodeData(volume, record, nodeData);
        !          1027:                        }
        !          1028:                } else {
        !          1029:                        result = cmNotFound;
        !          1030:                }
        !          1031:        }
        !          1032:        
        !          1033:        return result;
        !          1034: 
        !          1035: ErrorExit:
        !          1036: 
        !          1037:        if ( result == btNotFound )
        !          1038:                result = cmNotFound;
        !          1039: 
        !          1040:        return result;
        !          1041: 
        !          1042: } // end IterateCatalogNode
        !          1043: 
        !          1044: 
        !          1045: //_________________________________________________________________________________
        !          1046: //     Routine:        MoveRenameCatalogNode
        !          1047: //
        !          1048: //     Function:       Moves and/or rename an existing folder or file CNode.
        !          1049: //                             Note that when moving a folder, all decendants (its offspring,
        !          1050: //                             their offspring, etc.) are also moved.
        !          1051: //
        !          1052: // Assumes srcHint contains a text encoding that was set by a GetCatalogNode call
        !          1053: //_________________________________________________________________________________
        !          1054: 
        !          1055: OSErr
        !          1056: MoveRenameCatalogNode(ExtendedVCB *volume, HFSCatalogNodeID srcParentID, ConstUTF8Param srcName,
        !          1057:                                          UInt32 srcHint, HFSCatalogNodeID dstParentID, ConstUTF8Param dstName, UInt32 *newHint)
        !          1058: {
        !          1059:        CatalogKey                      srcKey;                 // 518 bytes
        !          1060:        CatalogRecord           srcRecord;              // 520 bytes
        !          1061:        CatalogKey                      dstKey;                 // 518 bytes
        !          1062:        CatalogKey                      dstFolderKey;   // 518 bytes
        !          1063:        HFSCatalogNodeID        dstFolderParentID = 0;
        !          1064:        UInt32                          dstFolderHint;
        !          1065:        CatalogName                *dstFolderNamePtr = NULL;
        !          1066:        CatalogRecord           tmpRecord;              // 520 bytes
        !          1067:        HFSCatalogNodeID        threadID;
        !          1068:        UInt32                          textEncoding;
        !          1069:        OSErr                           result;
        !          1070:        Boolean                         isNewName;
        !          1071:        Boolean                         isHFSPlus = (volume->vcbSigWord == kHFSPlusSigWord);
        !          1072:        Boolean                         isOrigDeleted = false;
        !          1073:        short srcNameLen;
        !          1074:        short dstNameLen;
        !          1075: 
        !          1076: 
        !          1077:        result = BuildCatalogKeyUTF8(volume, srcParentID, srcName, kUndefinedStrLen, &srcKey, &textEncoding);
        !          1078:        ReturnIfError(result);
        !          1079: 
        !          1080:        /* XXX can strlen and bcmp handle NULL pointers? */
        !          1081: 
        !          1082:        srcNameLen = strlen(srcName);
        !          1083:        dstNameLen = strlen(dstName);
        !          1084: 
        !          1085:        //--- check if names match
        !          1086: 
        !          1087:        if ((srcNameLen == dstNameLen) && (bcmp(srcName, dstName, srcNameLen) == 0))
        !          1088:        {
        !          1089:                isNewName = false;
        !          1090:                dstKey = srcKey;
        !          1091:         if ( isHFSPlus ) {
        !          1092:             dstKey.hfsPlus.parentID = dstParentID;                     // set parent ID
        !          1093:         }
        !          1094:         else {
        !          1095:             dstKey.hfs.parentID                = dstParentID;                  // set parent ID
        !          1096:                }
        !          1097:        }
        !          1098:        else /* names are different */
        !          1099:        {
        !          1100:                isNewName = true;
        !          1101:         result = BuildCatalogKeyUTF8(volume, dstParentID, dstName, kUndefinedStrLen, &dstKey, &textEncoding);
        !          1102:                ReturnIfError(result);
        !          1103:        }
        !          1104: 
        !          1105:        //--- make sure source record exists
        !          1106:        
        !          1107:        result = LocateCatalogNodeByKey(volume, srcHint, &srcKey, &srcRecord, &srcHint);
        !          1108: 
        !          1109:        // if we did not find it by name, then look for an embedded file ID in a mangled name
        !          1110:        if ( (result == cmNotFound) && isHFSPlus )
        !          1111:         result = LocateCatalogNodeByMangledName(volume, srcParentID, srcName, kUndefinedStrLen, &srcKey, &srcRecord, &srcHint);
        !          1112:        ReturnIfError(result);
        !          1113: 
        !          1114:        srcParentID = (isHFSPlus ? srcKey.hfsPlus.parentID : srcKey.hfs.parentID);
        !          1115: 
        !          1116:        // if we're moving then do some additional preflighting...
        !          1117: 
        !          1118:        if (srcParentID != dstParentID)
        !          1119:        {
        !          1120:                //--- make sure destination folder exists
        !          1121: 
        !          1122:                result = LocateCatalogNode(volume, dstParentID, NULL, kNoHint, &dstFolderKey, &tmpRecord, &dstFolderHint);
        !          1123:                ReturnIfError(result);
        !          1124:                        
        !          1125:                if (tmpRecord.recordType == kHFSPlusFolderRecord)
        !          1126:                {
        !          1127:                        dstParentID = tmpRecord.hfsPlusFolder.folderID;
        !          1128:                        dstFolderParentID = dstFolderKey.hfsPlus.parentID;
        !          1129:                        dstFolderNamePtr = (CatalogName*) &dstFolderKey.hfsPlus.nodeName;
        !          1130:                }
        !          1131:                else if (tmpRecord.recordType == kHFSFolderRecord)
        !          1132:                {
        !          1133:                        dstParentID = tmpRecord.hfsFolder.folderID;
        !          1134:                        dstFolderParentID = dstFolderKey.hfs.parentID;
        !          1135:                        dstFolderNamePtr = (CatalogName*) &dstFolderKey.hfs.nodeName;
        !          1136:                }
        !          1137:                else
        !          1138:                {
        !          1139:                        return badMovErr;
        !          1140:                }
        !          1141:        
        !          1142:                //--- if source is a folder, make sure its a proper move
        !          1143:        
        !          1144:                if (srcRecord.recordType == kHFSPlusFolderRecord || srcRecord.recordType == kHFSFolderRecord)
        !          1145:                {
        !          1146:                        HFSCatalogNodeID srcFolderID;
        !          1147:                        HFSCatalogNodeID ancestorParentID;
        !          1148:                        CatalogKey              tempKey;        // 518 bytes
        !          1149:                        UInt32                  tempHint;
        !          1150:        
        !          1151:                        if (isHFSPlus)
        !          1152:                        {
        !          1153:                                srcFolderID = srcRecord.hfsPlusFolder.folderID;
        !          1154:                                ancestorParentID = dstFolderKey.hfsPlus.parentID;
        !          1155:                        }
        !          1156:                        else
        !          1157:                        {
        !          1158:                                srcFolderID = srcRecord.hfsFolder.folderID;
        !          1159:                                ancestorParentID = dstFolderKey.hfs.parentID;
        !          1160:                        }
        !          1161:        
        !          1162:                        if ( srcFolderID == fsRtDirID   ||              // source == root?
        !          1163:                                 srcFolderID == dstParentID     ||              // source == destination?
        !          1164:                                 srcFolderID == ancestorParentID )      // source == destination's parent?
        !          1165:                        {
        !          1166:                                return badMovErr;
        !          1167:                        }
        !          1168:        
        !          1169:                        while (ancestorParentID > fsRtDirID)    // loop until we reach the root folder
        !          1170:                        {
        !          1171:                                // locate next folder up the tree...    
        !          1172:                                result = LocateCatalogNode(volume, ancestorParentID, NULL, kNoHint, &tempKey, &tmpRecord, &tempHint);
        !          1173:                                ReturnIfError(result);
        !          1174:                                
        !          1175:                                ancestorParentID = isHFSPlus ? tempKey.hfsPlus.parentID : tempKey.hfs.parentID;
        !          1176:        
        !          1177:                                if (srcFolderID == ancestorParentID)    // source = destination ancestor?
        !          1178:                                        return badMovErr;
        !          1179:                        }
        !          1180:                }
        !          1181: 
        !          1182:                TrashCatalogIterator(volume, dstParentID);              // invalidate any iterators for destination parentID
        !          1183:        }
        !          1184:        else /* (srcParentID == dstParentID) */
        !          1185:        {
        !          1186:                if ( !isNewName )
        !          1187:                {
        !          1188:                        *newHint = srcHint;             // they match, so we're all done!
        !          1189:                        return noErr;
        !          1190:                }
        !          1191:        }
        !          1192: 
        !          1193:        TrashCatalogIterator(volume, srcParentID);                      // invalidate any iterators for source's parentID
        !          1194:        InvalidateCatalogNodeCache(volume, srcParentID);        // invalidate node cache since parent changed
        !          1195: 
        !          1196:        if (isNewName && isHFSPlus)
        !          1197:        {
        !          1198:                // update textEncoding hint (works for folders and files)
        !          1199:                srcRecord.hfsPlusFolder.textEncoding = textEncoding;
        !          1200: 
        !          1201:                UpdateVolumeEncodings(volume, textEncoding);
        !          1202:        }
        !          1203: 
        !          1204:        //--- insert source CNode record in BTree with new key (a new parent id and/or new name)
        !          1205: 
        !          1206:        result = InsertBTreeRecord(volume->catalogRefNum, &dstKey, &srcRecord, GetCatalogRecordSize(&srcRecord), newHint);
        !          1207: 
        !          1208:        if (result == btExists)
        !          1209:        {
        !          1210:                UInt16 dataSize;
        !          1211: 
        !          1212:                /* XXX what about the case: move id1,foo to id2,FOO ?? */
        !          1213:                if (srcParentID != dstParentID || isNewName == false)
        !          1214:                        return cmExists;
        !          1215: 
        !          1216:                //--- new CNode name already exists in the same folder, locate the existing one
        !          1217:                result = SearchBTreeRecord(volume->catalogRefNum, &dstKey, srcHint,
        !          1218:                                                                        &dstFolderKey, &tmpRecord, &dataSize, newHint);
        !          1219: 
        !          1220:                if (result == btNotFound)
        !          1221:                        result = cmNotFound;    
        !          1222:                ReturnIfError(result);
        !          1223: 
        !          1224:                //--- check if its the same CNode (same name but different upper/lower case)
        !          1225:                        
        !          1226:                if (srcRecord.recordType != tmpRecord.recordType)
        !          1227:                        return cmExists;
        !          1228: 
        !          1229:                switch (srcRecord.recordType)
        !          1230:                {
        !          1231:                        case kHFSPlusFileRecord:        /* HFS Plus records share same cnid location */
        !          1232:                        case kHFSPlusFolderRecord:
        !          1233:                                if (srcRecord.hfsPlusFolder.folderID != tmpRecord.hfsPlusFolder.folderID)
        !          1234:                                        return cmExists;
        !          1235:                                break;
        !          1236: 
        !          1237:                        case kHFSFolderRecord:
        !          1238:                                if (srcRecord.hfsFolder.folderID != tmpRecord.hfsFolder.folderID)
        !          1239:                                        return cmExists;
        !          1240:                                break;
        !          1241: 
        !          1242:                        case kHFSFileRecord:
        !          1243:                                if (srcRecord.hfsFile.fileID != tmpRecord.hfsFile.fileID)
        !          1244:                                        return cmExists;
        !          1245:                                break;
        !          1246:                        
        !          1247:                        default:
        !          1248:                                return cmExists;
        !          1249:                }
        !          1250: 
        !          1251:                //--- same name but different case, so delete old and insert with new name...
        !          1252:        
        !          1253:                result = DeleteBTreeRecord(volume->catalogRefNum, &srcKey);
        !          1254:                ReturnIfError(result);
        !          1255:         isOrigDeleted = true;  // So we dont delete it again down below
        !          1256: 
        !          1257:                result = InsertBTreeRecord(volume->catalogRefNum, &dstKey, &srcRecord, dataSize, newHint);
        !          1258:        }
        !          1259:        ReturnIfError(result);
        !          1260: 
        !          1261:        //
        !          1262:        // from this point on we need to cleanup (ie delete the new record) if we encounter errors!     
        !          1263:        //
        !          1264: 
        !          1265:        //--- update thread record for node (if it exists)
        !          1266:        
        !          1267:        switch (srcRecord.recordType)
        !          1268:        {
        !          1269:                case kHFSPlusFileRecord:
        !          1270:                case kHFSPlusFolderRecord:
        !          1271:                        threadID = srcRecord.hfsPlusFolder.folderID;
        !          1272:                        break;
        !          1273: 
        !          1274:                case kHFSFolderRecord:
        !          1275:                        threadID = srcRecord.hfsFolder.folderID;
        !          1276:                        break;
        !          1277: 
        !          1278:                case kHFSFileRecord:
        !          1279:                        if (srcRecord.hfsFile.flags & kHFSThreadExistsMask)
        !          1280:                        {
        !          1281:                                threadID = srcRecord.hfsFile.fileID;
        !          1282:                                break;
        !          1283:                        }
        !          1284:                        /* fall through if no thread... */
        !          1285: 
        !          1286:                default:
        !          1287:                        threadID = 0;
        !          1288:        }
        !          1289: 
        !          1290:        if (threadID)
        !          1291:        {
        !          1292:                UInt32                  threadHint;
        !          1293:                CatalogKey              threadKey;              // 518 bytes
        !          1294:                CatalogRecord   threadRecord;   // 520 bytes
        !          1295:                UInt16                  threadSize;
        !          1296: 
        !          1297:                result = LocateCatalogRecord(volume, threadID, NULL, kNoHint, &threadKey, &threadRecord, &threadHint);
        !          1298:                if (result != noErr) goto Exit_Delete;
        !          1299:                
        !          1300:                if (isHFSPlus)
        !          1301:                {
        !          1302:                        if (srcParentID != dstParentID)
        !          1303:                                threadRecord.hfsPlusThread.parentID = dstParentID;
        !          1304:                        if (isNewName)
        !          1305:                                CopyCatalogName((CatalogName *)&dstKey.hfsPlus.nodeName, (CatalogName *) &threadRecord.hfsPlusThread.nodeName, isHFSPlus);
        !          1306: 
        !          1307:                        threadSize = sizeof(threadRecord.hfsPlusThread);
        !          1308:                        // HFS Plus has varaible sized threads so adjust to actual length
        !          1309:                        threadSize -= ( sizeof(threadRecord.hfsPlusThread.nodeName.unicode) - (threadRecord.hfsPlusThread.nodeName.length * sizeof(UniChar)) );
        !          1310:                }
        !          1311:                else
        !          1312:                {
        !          1313:                        if (srcParentID != dstParentID)
        !          1314:                                threadRecord.hfsThread.parentID = dstParentID;
        !          1315:                        if (isNewName)
        !          1316:                                CopyCatalogName((CatalogName *)&dstKey.hfs.nodeName,(CatalogName *) threadRecord.hfsThread.nodeName, isHFSPlus);
        !          1317: 
        !          1318:                        threadSize = sizeof(threadRecord.hfsThread);
        !          1319:                }
        !          1320: 
        !          1321:                result = DeleteBTreeRecord(volume->catalogRefNum, &threadKey);
        !          1322:                if (result != noErr) goto Exit_Delete;
        !          1323: 
        !          1324:                result = InsertBTreeRecord(volume->catalogRefNum, &threadKey, &threadRecord, threadSize, &threadHint);
        !          1325:                if (result != noErr) goto Exit_Delete;  //XXX exiting with a missing thread!
        !          1326:        }
        !          1327: 
        !          1328:        //--- we successfully added the new node so delete the old source CNode record
        !          1329: 
        !          1330:     if (! isOrigDeleted) {
        !          1331:         result = DeleteBTreeRecord(volume->catalogRefNum, &srcKey);
        !          1332:         if (result)
        !          1333:             {
        !          1334:             // uh oh, we could not delete the original
        !          1335:             // so we better get rid of the new node...
        !          1336: 
        !          1337:             (void) DeleteBTreeRecord(volume->catalogRefNum, &dstKey);
        !          1338: 
        !          1339:             //XXX also need to fix up the thread...
        !          1340: 
        !          1341:             return result;
        !          1342:             }
        !          1343:     }
        !          1344: 
        !          1345:        if (srcParentID != dstParentID)
        !          1346:        {
        !          1347:                result = UpdateFolderCount(volume, srcParentID, NULL, srcRecord.recordType, kNoHint, -1);
        !          1348:                result = UpdateFolderCount(volume, dstFolderParentID, dstFolderNamePtr, srcRecord.recordType, dstFolderHint, +1);
        !          1349:        }
        !          1350: 
        !          1351:        //--- make sure changes get flushed out
        !          1352:        VCB_LOCK(volume);
        !          1353:         volume->vcbFlags |= 0xFF00;            // Mark the VCB dirty
        !          1354:         volume->vcbLsMod = GetTimeUTC();       // update last modified date
        !          1355:        VCB_UNLOCK(volume);
        !          1356: 
        !          1357:        (void) FlushCatalog(volume);
        !          1358:        
        !          1359:        return result;
        !          1360: 
        !          1361: 
        !          1362: Exit_Delete:
        !          1363:        (void) DeleteBTreeRecord(volume->catalogRefNum, &dstKey);
        !          1364: 
        !          1365:        return result;
        !          1366: 
        !          1367: } // end MoveRenameCatalogNode
        !          1368: 
        !          1369: 
        !          1370: //_________________________________________________________________________________
        !          1371: //     Routine:        UpdateCatalogNode
        !          1372: //
        !          1373: //     Function:       Marks the Catalog BTree node identified by the given catalog hint
        !          1374: //                             as 'dirty'.
        !          1375: //
        !          1376: //_________________________________________________________________________________
        !          1377: 
        !          1378: 
        !          1379: OSErr
        !          1380: UpdateCatalogNode(ExtendedVCB *volume, HFSCatalogNodeID parentID, ConstUTF8Param name,
        !          1381:                                  UInt32 catalogHint, const CatalogNodeData *nodeData)
        !          1382: {
        !          1383:        CatalogKey              *key;
        !          1384:        CatalogRecord   *record;
        !          1385:        UInt32                  hint;
        !          1386:        UInt16                  recordSize;
        !          1387:        OSErr                   result;         
        !          1388:        CatalogKey              catalogKey;             // 518 bytes
        !          1389:        CatalogRecord   catalogRecord;  // 520 bytes
        !          1390:        Boolean                 isHFSPlus = volume->vcbSigWord == kHFSPlusSigWord;
        !          1391: 
        !          1392:        /* XXX no reason to have ptrs to local variables... */
        !          1393:        key        = &catalogKey;
        !          1394:        record = &catalogRecord;
        !          1395:        
        !          1396:        result = BuildCatalogKeyUTF8(volume, parentID, name, kUndefinedStrLen, key, NULL);
        !          1397:        ReturnIfError(result);
        !          1398: 
        !          1399:        //--- locate subject catalog node
        !          1400: 
        !          1401:        result = LocateCatalogNodeByKey(volume, catalogHint, key, record, &hint);
        !          1402: 
        !          1403:        // if we did not find it by name, then look for an embedded file ID in a mangled name
        !          1404:        if ( (result == cmNotFound) && isHFSPlus )
        !          1405:         result = LocateCatalogNodeByMangledName(volume, parentID, name, kUndefinedStrLen, key, record, &hint);
        !          1406: 
        !          1407:        if (result == btNotFound)
        !          1408:                result = cmNotFound;
        !          1409: 
        !          1410:        if (catalogHint != hint)
        !          1411:                PRINTIT(("UpdateCatalogNode: catalogHint does not match (in: %ld, out: %ld)\n", catalogHint, hint));
        !          1412:        ReturnIfError(result);
        !          1413: 
        !          1414:        // update user modifiable fields in the catalog node record...
        !          1415: 
        !          1416:        switch (record->recordType)
        !          1417:        {
        !          1418:                case kHFSFolderRecord:
        !          1419:                {
        !          1420:                  #if DEBUG_BUILD
        !          1421:                        if (nodeData->cn_type != kCatalogFolderNode)
        !          1422:                                DebugStr("\p UpdateCatalogNode: folder/file mismatch!");
        !          1423:                  #endif
        !          1424: 
        !          1425:                        record->hfsFolder.createDate = UTCToLocal(nodeData->cnd_createDate);
        !          1426:                        record->hfsFolder.modifyDate = UTCToLocal(nodeData->cnd_contentModDate);
        !          1427:                        record->hfsFolder.backupDate = UTCToLocal(nodeData->cnd_backupDate);
        !          1428: 
        !          1429:                        *(DInfo*) &record->hfsFolder.userInfo = *(DInfo*) &nodeData->cnd_finderInfo;
        !          1430:                        *(DXInfo*) &record->hfsFolder.finderInfo = *(DXInfo*) ((UInt32)&nodeData->cnd_finderInfo + 16);
        !          1431: 
        !          1432:                        recordSize = sizeof(HFSCatalogFolder);
        !          1433:                        break;
        !          1434:                }
        !          1435: 
        !          1436:                case kHFSFileRecord:
        !          1437:                {
        !          1438:                        UInt32  i;
        !          1439:                        
        !          1440:                  #if DEBUG_BUILD
        !          1441:                        if (nodeData->cnd_type != kCatalogFileNode)
        !          1442:                                DebugStr("UpdateCatalogNode: folder/file mismatch!");
        !          1443:                        if ((nodeData->nc_file.dataFork.totalBlocks > (0x7FFFFFFF/volume->blockSize)) ||
        !          1444:                            (nodeData->nc_file.resourceFork.totalBlocks > (0x7FFFFFFF/volume->blockSize)))
        !          1445:                                DebugStr("HFS file size is larger than 2Gig");
        !          1446:                  #endif
        !          1447: 
        !          1448:                        record->hfsFile.flags = (UInt8) nodeData->cnd_flags;
        !          1449:                        record->hfsFile.createDate = UTCToLocal(nodeData->cnd_createDate);
        !          1450:                        record->hfsFile.modifyDate = UTCToLocal(nodeData->cnd_contentModDate);
        !          1451:                        record->hfsFile.backupDate = UTCToLocal(nodeData->cnd_backupDate);
        !          1452: 
        !          1453:                        record->hfsFile.dataLogicalSize  = nodeData->cnd_datafork.logicalSize;
        !          1454:                        record->hfsFile.dataPhysicalSize = nodeData->cnd_datafork.totalBlocks * volume->blockSize;
        !          1455:                        record->hfsFile.rsrcLogicalSize  = nodeData->cnd_rsrcfork.logicalSize;
        !          1456:                        record->hfsFile.rsrcPhysicalSize = nodeData->cnd_rsrcfork.totalBlocks * volume->blockSize;
        !          1457: 
        !          1458:                        *(FInfo*) &record->hfsFile.userInfo = *(FInfo*) &nodeData->cnd_finderInfo;
        !          1459:                        *(FXInfo*) &record->hfsFile.finderInfo = *(FXInfo*) ((UInt32)&nodeData->cnd_finderInfo + 16);
        !          1460: 
        !          1461:                        // copy extent info
        !          1462:                        for (i = 0; i < kHFSExtentDensity; ++i)
        !          1463:                        {
        !          1464:                                record->hfsFile.dataExtents[i].startBlock =
        !          1465:                                    (UInt16) nodeData->cnd_datafork.extents[i].startBlock;
        !          1466:                                record->hfsFile.dataExtents[i].blockCount =
        !          1467:                                    (UInt16) nodeData->cnd_datafork.extents[i].blockCount;
        !          1468:                                record->hfsFile.rsrcExtents[i].startBlock =
        !          1469:                                    (UInt16) nodeData->cnd_rsrcfork.extents[i].startBlock;
        !          1470:                                record->hfsFile.rsrcExtents[i].blockCount =
        !          1471:                                    (UInt16) nodeData->cnd_rsrcfork.extents[i].blockCount;
        !          1472:                        }
        !          1473: 
        !          1474:                        recordSize = sizeof(HFSCatalogFile);
        !          1475:                        break;
        !          1476:                }
        !          1477: 
        !          1478:                case kHFSPlusFolderRecord:
        !          1479:                {
        !          1480:                        record->hfsPlusFolder.createDate = nodeData->cnd_createDate;
        !          1481:                        record->hfsPlusFolder.contentModDate = nodeData->cnd_contentModDate;
        !          1482:                        record->hfsPlusFolder.backupDate = nodeData->cnd_backupDate;
        !          1483:                        record->hfsPlusFolder.accessDate = nodeData->cnd_accessDate;
        !          1484:                        record->hfsPlusFolder.attributeModDate = nodeData->cnd_attributeModDate;
        !          1485:                        record->hfsPlusFolder.permissions.ownerID = nodeData->cnd_ownerID;
        !          1486:                        record->hfsPlusFolder.permissions.groupID = nodeData->cnd_groupID;
        !          1487:                        record->hfsPlusFolder.permissions.permissions = nodeData->cnd_permissions;
        !          1488:                        record->hfsPlusFolder.permissions.specialDevice = nodeData->cnd_specialDevice;
        !          1489: 
        !          1490:                        BlockMoveData(&nodeData->cnd_finderInfo, &record->hfsPlusFolder.userInfo, 32);
        !          1491: 
        !          1492:                        recordSize = sizeof(HFSPlusCatalogFolder);
        !          1493:                        break;
        !          1494:                }
        !          1495: 
        !          1496:                case kHFSPlusFileRecord:
        !          1497:                {
        !          1498:                        record->hfsPlusFile.flags = nodeData->cnd_flags;
        !          1499:                        record->hfsPlusFile.createDate = nodeData->cnd_createDate;
        !          1500:                        record->hfsPlusFile.contentModDate = nodeData->cnd_contentModDate;
        !          1501:                        record->hfsPlusFile.backupDate = nodeData->cnd_backupDate;
        !          1502:                        record->hfsPlusFile.accessDate = nodeData->cnd_accessDate;
        !          1503:                        record->hfsPlusFile.attributeModDate = nodeData->cnd_attributeModDate;
        !          1504:                        record->hfsPlusFile.permissions.ownerID = nodeData->cnd_ownerID;
        !          1505:                        record->hfsPlusFile.permissions.groupID = nodeData->cnd_groupID;
        !          1506:                        record->hfsPlusFile.permissions.permissions = nodeData->cnd_permissions;
        !          1507:                        record->hfsPlusFile.permissions.specialDevice = nodeData->cnd_specialDevice;
        !          1508: 
        !          1509:                        record->hfsPlusFile.dataFork.logicalSize = nodeData->cnd_datafork.logicalSize;
        !          1510:                        record->hfsPlusFile.dataFork.totalBlocks = nodeData->cnd_datafork.totalBlocks;
        !          1511:                        BlockMoveData(&nodeData->cnd_datafork.extents,
        !          1512:                            &record->hfsPlusFile.dataFork.extents, sizeof(HFSPlusExtentRecord));
        !          1513: 
        !          1514:                        record->hfsPlusFile.resourceFork.logicalSize = nodeData->cnd_rsrcfork.logicalSize;
        !          1515:                        record->hfsPlusFile.resourceFork.totalBlocks = nodeData->cnd_rsrcfork.totalBlocks;
        !          1516:                        BlockMoveData(&nodeData->cnd_rsrcfork.extents,
        !          1517:                            &record->hfsPlusFile.resourceFork.extents, sizeof(HFSPlusExtentRecord));
        !          1518:                        
        !          1519:                        BlockMoveData(&nodeData->cnd_finderInfo, &record->hfsPlusFile.userInfo, 32);
        !          1520: #if HFS_HARDLINKS
        !          1521:                        record->hfsPlusFile.linkCount = nodeData->cnd_linkCount;
        !          1522: #if DEBUG_BUILD
        !          1523:                        if (record->hfsPlusFile.userInfo.fdType == kHardLinkFileType &&
        !          1524:                            record->hfsPlusFile.userInfo.fdCreator == kHardLinkCreator) {
        !          1525:                            if (record->hfsPlusFile.dataFork.logicalSize != 0)
        !          1526:                                DebugStr("UpdateCatalogNode: link has data fork!");
        !          1527:                        }
        !          1528:                        if (record->hfsPlusFile.linkCount > 0) {
        !          1529:                            if (name != NULL)
        !          1530:                                DebugStr("UpdateCatalogNode: node has name!");
        !          1531:                            if (nodeData->cnd_nodeID != record->hfsPlusFile.fileID)
        !          1532:                                DebugStr("UpdateCatalogNode: fileID mismatch!");
        !          1533:                        }
        !          1534: #endif
        !          1535: #endif
        !          1536: 
        !          1537:                        recordSize = sizeof(HFSPlusCatalogFile);
        !          1538:                        break;
        !          1539:                }
        !          1540: 
        !          1541:                default:
        !          1542:                        return cmNotFound;
        !          1543:        }
        !          1544: 
        !          1545:        result = ReplaceBTreeRecord(volume->catalogRefNum, key, catalogHint, record, recordSize, &hint);
        !          1546:        
        !          1547:        if ( result == btNotFound )
        !          1548:        {
        !          1549:                result = cmNotFound;
        !          1550:        }
        !          1551:        else if ( result == noErr )
        !          1552:        {
        !          1553:                /* if we're just updating the accessDate then no need to change volume mod date */
        !          1554:                if (nodeData->cnd_contentModDate > volume->vcbLsMod ||
        !          1555:                        (isHFSPlus && nodeData->cnd_attributeModDate > volume->vcbLsMod))
        !          1556:                {
        !          1557:                        VCB_LOCK(volume);
        !          1558:                        volume->vcbFlags |= 0xFF00;             // Mark the VCB dirty
        !          1559:                        volume->vcbLsMod = GetTimeUTC();        // update last modified date
        !          1560:                        VCB_UNLOCK(volume);
        !          1561:                }
        !          1562: 
        !          1563:                result = FlushCatalog(volume);          // flush the catalog
        !          1564:        }
        !          1565: 
        !          1566:        return result;
        !          1567: }
        !          1568: 
        !          1569: 
        !          1570: //_________________________________________________________________________________
        !          1571: //     Routine:        CreateFileIDRef
        !          1572: //
        !          1573: //     Function:       Creates a file thread record for hfs file node
        !          1574: //
        !          1575: //_________________________________________________________________________________
        !          1576: 
        !          1577: OSErr
        !          1578: CreateFileIDRef(ExtendedVCB *volume, HFSCatalogNodeID parentID, ConstUTF8Param name, UInt32 hint, HFSCatalogNodeID *threadID)
        !          1579: {
        !          1580:        CatalogKey                      nodeKey;        // 518 bytes
        !          1581:        CatalogRecord           nodeData;       // 520 bytes
        !          1582:        HFSCatalogKey           threadKey;
        !          1583:        HFSCatalogThread        threadData;
        !          1584:        UInt32                          nodeHint;
        !          1585:        UInt32                          tempHint;
        !          1586:        OSErr                           result;
        !          1587:        Boolean                         isHFSPlus = (volume->vcbSigWord == kHFSPlusSigWord);
        !          1588:        
        !          1589:        *threadID = 0;
        !          1590:        
        !          1591:     result = BuildCatalogKeyUTF8(volume, parentID, name, kUndefinedStrLen, &nodeKey, NULL);
        !          1592:        ReturnIfError(result);
        !          1593: 
        !          1594:        //--- locate subject catalog node
        !          1595: 
        !          1596:        result = LocateCatalogNodeByKey(volume, hint, &nodeKey, &nodeData, &nodeHint);
        !          1597: 
        !          1598:        // if we did not find it by name, then look for an embedded file ID in a mangled name
        !          1599:        if ( (result == cmNotFound) && isHFSPlus )
        !          1600:         result = LocateCatalogNodeByMangledName(volume, parentID, name, kUndefinedStrLen, &nodeKey, &nodeData, &nodeHint);
        !          1601:        ReturnIfError(result);
        !          1602:        
        !          1603:     if (nodeData.recordType == kHFSPlusFileRecord)
        !          1604:        {
        !          1605:                *threadID = nodeData.hfsPlusFile.fileID;
        !          1606:                return noErr;   // already have one
        !          1607:        }
        !          1608:        
        !          1609:        if (nodeData.recordType != kHFSFileRecord)
        !          1610:        {
        !          1611:                return notAFileErr;
        !          1612:        }
        !          1613: 
        !          1614: 
        !          1615:        if (nodeData.hfsFile.flags & kHFSThreadExistsMask)
        !          1616:        {
        !          1617:                *threadID = nodeData.hfsFile.fileID;
        !          1618:                return noErr;   // already have one
        !          1619:        }
        !          1620: 
        !          1621:        result = VolumeWritable( volume );
        !          1622:        if ( result != noErr ) return result;
        !          1623: 
        !          1624:        //
        !          1625:        // need to insert a thread record
        !          1626:        //              
        !          1627:     BuildCatalogKey(nodeData.hfsFile.fileID, NULL, false, (CatalogKey *)&threadKey);
        !          1628:        
        !          1629:        ClearMemory(&threadData, sizeof(HFSCatalogThread));
        !          1630:     threadData.recordType = kHFSFileThreadRecord;
        !          1631:        threadData.parentID = nodeKey.hfs.parentID;     
        !          1632:        BlockMoveData(&nodeKey.hfs.nodeName, &threadData.nodeName, nodeKey.hfs.nodeName[0] + 1);
        !          1633: 
        !          1634:        result = InsertBTreeRecord(volume->catalogRefNum, &threadKey, &threadData, sizeof(HFSCatalogThread), &tempHint);
        !          1635:        if (result == btExists) result = noErr;         //XXX could return cmExists or fidExists
        !          1636:        ReturnIfError(result);
        !          1637: 
        !          1638:        //
        !          1639:        //      Finally, set the flag in the file record to say this file has a thread record.
        !          1640:        //
        !          1641:        nodeData.hfsFile.flags |= kHFSThreadExistsMask;
        !          1642:        result = ReplaceBTreeRecord(volume->catalogRefNum, &nodeKey, nodeHint, &nodeData, sizeof(HFSCatalogFile), &nodeHint );
        !          1643: 
        !          1644:        if (result == noErr) {
        !          1645:                (void) FlushCatalog(volume);
        !          1646:                *threadID = nodeData.hfsFile.fileID;
        !          1647:        }
        !          1648: 
        !          1649:        return result;
        !          1650: }
        !          1651: 
        !          1652: 
        !          1653: //_________________________________________________________________________________
        !          1654: //     Routine:        CompareCatalogKeys
        !          1655: //
        !          1656: //     Function:       Compares two catalog keys (a search key and a trial key).
        !          1657: //
        !          1658: //     Result:         +n  search key > trial key
        !          1659: //                              0  search key = trial key
        !          1660: //                             -n  search key < trial key
        !          1661: //_________________________________________________________________________________
        !          1662: 
        !          1663: SInt32
        !          1664: CompareCatalogKeys(HFSCatalogKey *searchKey, HFSCatalogKey *trialKey)
        !          1665: {
        !          1666:        HFSCatalogNodeID        searchParentID, trialParentID;
        !          1667:        SInt32  result;
        !          1668: 
        !          1669:        searchParentID = searchKey->parentID;
        !          1670:        trialParentID = trialKey->parentID;
        !          1671: 
        !          1672:        if ( searchParentID > trialParentID )   // parent dirID is unsigned
        !          1673:                result = 1;
        !          1674:        else if ( searchParentID < trialParentID )
        !          1675:                result = -1;
        !          1676:        else // parent dirID's are equal, compare names
        !          1677:        {
        !          1678:          #if ( ! FORDISKFIRSTAID )
        !          1679:                LogStartTime(kTraceRelString);
        !          1680: 
        !          1681:                result = FastRelString(searchKey->nodeName, trialKey->nodeName);
        !          1682: 
        !          1683:                LogEndTime(kTraceRelString, noErr);
        !          1684:          #else
        !          1685:                result = (SInt32) RelString_Glue(searchKey->nodeName, trialKey->nodeName);
        !          1686:          #endif
        !          1687:        }
        !          1688: 
        !          1689:        return result;
        !          1690: }
        !          1691: 
        !          1692: 
        !          1693: //_________________________________________________________________________________
        !          1694: //     Routine:        CompareExtendedCatalogKeys
        !          1695: //
        !          1696: //     Function:       Compares two large catalog keys (a search key and a trial key).
        !          1697: //
        !          1698: //     Result:         +n  search key > trial key
        !          1699: //                              0  search key = trial key
        !          1700: //                             -n  search key < trial key
        !          1701: //_________________________________________________________________________________
        !          1702: 
        !          1703: SInt32
        !          1704: CompareExtendedCatalogKeys(HFSPlusCatalogKey *searchKey, HFSPlusCatalogKey *trialKey)
        !          1705: {
        !          1706:        SInt32                  result;
        !          1707:        HFSCatalogNodeID        searchParentID, trialParentID;
        !          1708: 
        !          1709:        searchParentID = searchKey->parentID;
        !          1710:        trialParentID = trialKey->parentID;
        !          1711:        
        !          1712:        if ( searchParentID > trialParentID )   // parent node IDs are unsigned
        !          1713:        {
        !          1714:                result = 1;
        !          1715:        }
        !          1716:        else if ( searchParentID < trialParentID )
        !          1717:        {
        !          1718:                result = -1;
        !          1719:        }
        !          1720:        else // parent node ID's are equal, compare names
        !          1721:        {
        !          1722:                if ( searchKey->nodeName.length == 0 || trialKey->nodeName.length == 0 )
        !          1723:                        result = searchKey->nodeName.length - trialKey->nodeName.length;
        !          1724:                else
        !          1725:                        result = FastUnicodeCompare(&searchKey->nodeName.unicode[0], searchKey->nodeName.length,
        !          1726:                                                                                &trialKey->nodeName.unicode[0], trialKey->nodeName.length);
        !          1727:        }
        !          1728: 
        !          1729:        return result;
        !          1730: }
        !          1731: 

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.