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