2 * Copyright (c) 2000 Apple Computer, Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
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.
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
20 * @APPLE_LICENSE_HEADER_END@
25 Contains: Catalog Manager Implementation
29 Copyright: © 1996-2000 by Apple Computer, Inc., all rights reserved.
35 Other Contact: Mark Day
37 Technology: xxx put technology here xxx
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.
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
79 <CS25> 9/9/97 DSH Added RelString_Glue to avoid having to link DFAEngine with
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
97 <CS14> 7/16/97 DSH FilesInternal.i renamed FileMgrInternal.i to avoid name
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
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
149 <HFS4> 12/12/96 DSH Removed static function declarations for functions used by
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
154 <HFS1> 10/29/96 djb first checked in
158 #pragma segment Catalog
160 #include <sys/param.h>
161 #include <sys/utfconv.h>
163 #include "../../hfs_endian.h"
165 #include "../headers/FileMgrInternal.h"
166 #include "../headers/BTreesInternal.h"
167 #include "../headers/CatalogPrivate.h"
168 #include "../headers/HFSUnicodeWrappers.h"
169 #include "../headers/HFSInstrumentation.h"
174 extern SInt32
FastRelString( ConstStr255Param str1
, ConstStr255Param str2
);
176 extern SInt16
RelString_Glue(StringPtr pStr1
, StringPtr pStr2
);
181 static OSErr
IterateCatalogNode(ExtendedVCB
*volume
, CatalogIterator
*catalogIterator
,
182 UInt16 index
, CatalogNodeData
*nodeData
,
183 HFSCatalogNodeID
*nodeID
, SInt16
*nodeType
);
185 void InitCatalogThreadRecord(ExtendedVCB
*volume
, UInt32 nodeType
, CatalogKey
*nodeKey
,
186 CatalogRecord
*record
, UInt32
*threadSize
);
188 void InitCatalogRecord(ExtendedVCB
*volume
, UInt32 nodeType
, UInt32 textEncoding
,
189 CatalogRecord
*record
, UInt32
*recordSize
, HFSCatalogNodeID catalogNodeID
);
192 #include <sys/systm.h>
193 #define PRINTIT(A) kprintf A;
196 #endif /* HFS_DIAGNOSTIC */
198 //_________________________________________________________________________________
201 // CreateCatalogNode - Creates a new folder or file CNode.
202 // DeleteCatalogNode - Deletes an existing folder or file CNode.
203 // GetCatalogNode - Locates an existing folder or file CNode.
204 // GetCatalogOffspringFile - Gets an offspring file record from a folder.
205 // GetCatalogOffspring - Gets an offspring record from a folder.
206 // MoveRenameCatalogNode - Moves/Renames an existing folder or file CNode.
207 // UpdateCatalogNode - Marks a Catalog BTree node as 'dirty'.
208 // CreateFileIDRef - Creates a file thread record for hfs file node
209 // CompareCatalogKeys - Compares two catalog keys.
211 //_________________________________________________________________________________
214 //_________________________________________________________________________________
216 // About date/time values:
218 // Date/time values stored in control blocks and generic structures (such as
219 // CatalogNodeData) are always stored in local time. Values stored in HFS volume
220 // format structures (such as B-tree records) are also stored in local time.
221 // Values stored in HFS Plus format structures are stored in UTC.
222 //_________________________________________________________________________________
228 //_________________________________________________________________________________
229 // Routine: CreateCatalogNode
231 // Function: Creates a new folder or file CNode. A new folder or file
232 // record is added to the catalog BTree. If a folder CNode is
233 // being created, a new thread record is also added.
235 //_________________________________________________________________________________
238 CreateCatalogNode ( ExtendedVCB
*volume
, HFSCatalogNodeID parentID
, ConstUTF8Param name
,
239 UInt32 nodeType
, HFSCatalogNodeID
*catalogNodeID
, UInt32
*catalogHint
)
242 CatalogRecord nodeData
; // 520 bytes
244 CatalogRecord parentThreadData
; // 520 bytes
245 HFSCatalogNodeID parentsParentID
;
246 CatalogName
*parentNamePtr
;
251 Boolean isHFSPlus
= (volume
->vcbSigWord
== kHFSPlusSigWord
);
253 FSBufferDescriptor btRecord
;
254 BTreeIterator iterator
;
255 BTreeIterator threadIter
;
256 HFSCatalogNodeID nextCNID
;
258 if (nodeType
!= kCatalogFolderNode
&& nodeType
!= kCatalogFileNode
)
261 fcb
= GetFileControlBlock(volume
->catalogRefNum
);
262 nodeKey
= (CatalogKey
*) &iterator
.key
;
264 //--- make sure parent exists (by locating the parent's thread record)
266 result
= LocateCatalogThread(volume
, parentID
, &parentThreadData
, &tempSize
, &tempHint
);
267 ReturnIfError(result
);
269 TrashCatalogIterator(volume
, parentID
); // invalidate any iterators for this parentID
271 // save copy of parent's parentID and name.
275 if (parentThreadData
.recordType
!= kHFSPlusFolderThreadRecord
)
278 parentsParentID
= parentThreadData
.hfsPlusThread
.parentID
;
279 parentNamePtr
= (CatalogName
*) &parentThreadData
.hfsPlusThread
.nodeName
;
283 if (parentThreadData
.recordType
!= kHFSFolderThreadRecord
)
286 parentsParentID
= parentThreadData
.hfsThread
.parentID
;
287 parentNamePtr
= (CatalogName
*) &parentThreadData
.hfsThread
.nodeName
;
290 // invalidate cache for parent since its about to change
291 InvalidateCatalogNodeCache(volume
, parentsParentID
);
293 //--- build key for new catalog node
294 result
= BuildCatalogKeyUTF8(volume
, parentID
, name
, kUndefinedStrLen
, nodeKey
, &textEncoding
);
295 ReturnIfError(result
);
297 /* make sure it doesn't exist */
298 result
= BTSearchRecord(fcb
, &iterator
, kInvalidMRUCacheKey
, NULL
, NULL
, &iterator
);
299 if (result
!= btNotFound
)
302 nextCNID
= volume
->vcbNxtCNID
;
303 if (!isHFSPlus
&& nextCNID
== 0xFFFFFFFF)
306 //--- build thread record for new CNode
307 if (isHFSPlus
|| nodeType
== kCatalogFolderNode
)
309 CatalogRecord threadData
; // 520 bytes
312 btRecord
.bufferAddress
= &threadData
;
313 btRecord
.itemSize
= threadSize
;
314 btRecord
.itemCount
= 1;
315 InitCatalogThreadRecord(volume
, nodeType
, nodeKey
, &threadData
, &threadSize
);
317 BuildCatalogKey(nextCNID
, NULL
, isHFSPlus
, (CatalogKey
*) &threadIter
.key
);
318 result
= BTInsertRecord(fcb
, &threadIter
, &btRecord
, threadSize
);
319 if (result
== btExists
&& isHFSPlus
)
322 * Allow CNIDs on HFS Plus volumes to wrap around
325 if (nextCNID
< kHFSFirstUserCatalogNodeID
)
327 volume
->vcbAtrb
|= kHFSCatalogNodeIDsReusedMask
;
328 volume
->vcbFlags
|= 0xFF00;
329 nextCNID
= kHFSFirstUserCatalogNodeID
;
333 ReturnIfError(result
);
336 //--- initialize catalog data record (for folder or file)
337 btRecord
.bufferAddress
= &nodeData
;
338 btRecord
.itemSize
= nodeDataSize
;
339 btRecord
.itemCount
= 1;
340 InitCatalogRecord(volume
, nodeType
, textEncoding
, &nodeData
, &nodeDataSize
, nextCNID
);
342 //--- add new folder/file record to catalog BTree
343 result
= BTInsertRecord(fcb
, &iterator
, &btRecord
, nodeDataSize
);
346 if (result
== btExists
)
349 if (isHFSPlus
|| nodeType
== kCatalogFolderNode
)
350 (void) BTDeleteRecord(fcb
, &threadIter
);
356 * Return the CNID actually used. Update the volume's next ID.
358 *catalogNodeID
= nextCNID
;
359 if (++nextCNID
< kHFSFirstUserCatalogNodeID
)
361 volume
->vcbAtrb
|= kHFSCatalogNodeIDsReusedMask
;
362 volume
->vcbFlags
|= 0xFF00;
363 nextCNID
= kHFSFirstUserCatalogNodeID
;
365 volume
->vcbNxtCNID
= nextCNID
;
367 //--- update counters...
369 result
= UpdateFolderCount( volume
, parentsParentID
, parentNamePtr
, nodeData
.recordType
, kNoHint
, +1);
370 ReturnIfError(result
); /* XXX what about cleanup ??? */
372 AdjustVolumeCounts(volume
, nodeData
.recordType
, +1);
374 result
= FlushCatalog(volume
);
378 } // end CreateCatalogNode
382 * initialize catalog data record (for folder or file)
384 void InitCatalogRecord(ExtendedVCB
*volume
, UInt32 nodeType
, UInt32 textEncoding
, CatalogRecord
*record
, UInt32
*recordSize
, HFSCatalogNodeID nodeID
)
388 ClearMemory(record
, sizeof(CatalogRecord
)); // first clear the record
390 if (volume
->vcbSigWord
== kHFSPlusSigWord
)
392 timeStamp
= GetTimeUTC(); // get current date/time (universal)
394 UpdateVolumeEncodings(volume
, textEncoding
);
396 if (nodeType
== kCatalogFolderNode
)
398 record
->recordType
= kHFSPlusFolderRecord
;
399 record
->hfsPlusFolder
.folderID
= nodeID
;
400 record
->hfsPlusFolder
.createDate
= timeStamp
;
401 record
->hfsPlusFolder
.contentModDate
= timeStamp
;
402 record
->hfsPlusFolder
.accessDate
= timeStamp
;
403 record
->hfsPlusFolder
.attributeModDate
= timeStamp
;
404 record
->hfsPlusFolder
.textEncoding
= textEncoding
;
405 *recordSize
= sizeof(HFSPlusCatalogFolder
);
406 // threadType = kHFSPlusFolderThreadRecord;
408 else if (nodeType
== kCatalogFileNode
)
410 record
->recordType
= kHFSPlusFileRecord
;
411 record
->hfsPlusFile
.fileID
= nodeID
;
412 record
->hfsPlusFile
.createDate
= timeStamp
;
413 record
->hfsPlusFile
.contentModDate
= timeStamp
;
414 record
->hfsPlusFile
.accessDate
= timeStamp
;
415 record
->hfsPlusFile
.attributeModDate
= timeStamp
;
416 record
->hfsPlusFile
.flags
|= kHFSThreadExistsMask
;
417 record
->hfsPlusFile
.textEncoding
= textEncoding
;
418 *recordSize
= sizeof(HFSPlusCatalogFile
);
419 // threadType = kHFSPlusFileThreadRecord;
422 else /* standard hfs */
424 timeStamp
= GetTimeLocal(true); // get current local date/time
426 if (nodeType
== kCatalogFolderNode
)
428 record
->recordType
= kHFSFolderRecord
;
429 record
->hfsFolder
.folderID
= nodeID
;
430 record
->hfsFolder
.createDate
= timeStamp
;
431 record
->hfsFolder
.modifyDate
= timeStamp
;
432 *recordSize
= sizeof(HFSCatalogFolder
);
433 // threadType = kHFSFolderThreadRecord;
435 else if (nodeType
== kCatalogFileNode
)
437 record
->recordType
= kHFSFileRecord
;
438 record
->hfsFile
.fileID
= nodeID
;
439 record
->hfsFile
.createDate
= timeStamp
;
440 record
->hfsFile
.modifyDate
= timeStamp
;
441 *recordSize
= sizeof(HFSCatalogFile
);
447 void InitCatalogThreadRecord(ExtendedVCB
*volume
, UInt32 nodeType
, CatalogKey
*nodeKey
,
448 CatalogRecord
*record
, UInt32
*threadSize
)
450 ClearMemory(record
, sizeof(CatalogRecord
) ); // first clear the record
452 if (volume
->vcbSigWord
== kHFSPlusSigWord
)
454 if (nodeType
== kCatalogFolderNode
)
455 record
->recordType
= kHFSPlusFolderThreadRecord
;
457 record
->recordType
= kHFSPlusFileThreadRecord
;
458 record
->hfsPlusThread
.parentID
= nodeKey
->hfsPlus
.parentID
;
459 *threadSize
= sizeof(record
->hfsPlusThread
);
461 // HFS Plus has varaible sized threads so adjust to actual length
462 *threadSize
-= ( sizeof(record
->hfsPlusThread
.nodeName
.unicode
) -
463 (nodeKey
->hfsPlus
.nodeName
.length
* sizeof(UniChar
)) );
464 BlockMoveData(&nodeKey
->hfsPlus
.nodeName
, &record
->hfsPlusThread
.nodeName
,
465 sizeof(UniChar
) * (nodeKey
->hfsPlus
.nodeName
.length
+ 1));
469 if (nodeType
== kCatalogFolderNode
)
470 record
->recordType
= kHFSFolderThreadRecord
;
472 record
->recordType
= kHFSFileThreadRecord
;
473 record
->hfsThread
.parentID
= nodeKey
->hfs
.parentID
;
474 *threadSize
= sizeof(record
->hfsThread
);
475 BlockMoveData(&nodeKey
->hfs
.nodeName
, &record
->hfsThread
.nodeName
,
476 nodeKey
->hfs
.nodeName
[0] + 1);
481 //_________________________________________________________________________________
482 // Routine: DeleteCatalogNode
484 // Function: Deletes an existing folder or file CNode. The thread record
485 // is also deleted for directories and files that have thread
488 // The valence for a folder must be zero before it can be deleted.
489 // The rootfolder cannot be deleted.
491 //_________________________________________________________________________________
494 DeleteCatalogNode(ExtendedVCB
*volume
, HFSCatalogNodeID parentID
, ConstUTF8Param name
, UInt32 hint
)
496 CatalogKey key
; // 518 bytes
497 CatalogRecord data
; // 520 bytes
499 HFSCatalogNodeID nodeID
;
500 HFSCatalogNodeID nodeParentID
;
503 Boolean isHFSPlus
= (volume
->vcbSigWord
== kHFSPlusSigWord
);
505 //--- locate subject catalog node
507 result
= BuildCatalogKeyUTF8(volume
, parentID
, name
, kUndefinedStrLen
, &key
, NULL
);
508 ReturnIfError(result
);
510 result
= LocateCatalogNodeByKey(volume
, hint
, &key
, &data
, &nodeHint
);
512 // if we did not find it by name, then look for an embedded file ID in a mangled name
513 if ( (result
== cmNotFound
) && isHFSPlus
)
514 result
= LocateCatalogNodeByMangledName(volume
, parentID
, name
, kUndefinedStrLen
, &key
, &data
, &nodeHint
);
515 ReturnIfError(result
);
517 nodeParentID
= isHFSPlus
? key
.hfsPlus
.parentID
: key
.hfs
.parentID
; // establish real parent cnid
518 nodeType
= data
.recordType
; // establish cnode type
523 case kHFSFolderRecord
:
524 if (data
.hfsFolder
.valence
!= 0) // is it empty?
527 nodeID
= data
.hfsFolder
.folderID
;
530 case kHFSPlusFolderRecord
:
531 if (data
.hfsPlusFolder
.valence
!= 0) // is it empty?
534 nodeID
= data
.hfsPlusFolder
.folderID
;
538 if (data
.hfsFile
.flags
& kHFSThreadExistsMask
)
539 nodeID
= data
.hfsFile
.fileID
;
542 case kHFSPlusFileRecord
:
543 nodeID
= data
.hfsPlusFile
.fileID
; // note: HFS Plus files always have a thread
551 if (nodeID
== fsRtDirID
) // is this the root folder?
552 return cmRootCN
; // sorry, you can't delete the root!
554 TrashCatalogIterator(volume
, nodeParentID
); // invalidate any iterators for this parentID
555 InvalidateCatalogNodeCache(volume
, nodeParentID
); // and invalidate node cache
557 //--- delete catalog records for CNode and file threads if they exist
559 result
= DeleteBTreeRecord(volume
->catalogRefNum
, &key
);
560 ReturnIfError(result
);
564 CatalogKey threadKey
; // 518 bytes
566 BuildCatalogKey(nodeID
, NULL
, isHFSPlus
, &threadKey
);
568 (void) DeleteBTreeRecord(volume
->catalogRefNum
, &threadKey
); // ignore errors for thread deletes
571 //--- update counters...
573 result
= UpdateFolderCount(volume
, nodeParentID
, NULL
, nodeType
, kNoHint
, -1);
574 ReturnIfError(result
);
576 AdjustVolumeCounts(volume
, nodeType
, -1); // all done with this file or folder
578 result
= FlushCatalog(volume
);
582 } // end DeleteCatalogNode
585 //_________________________________________________________________________________
586 // Routine: GetCatalogNode
588 // Function: Locates an existing folder or file CNode and pointer to the CNode data record.
590 //_________________________________________________________________________________
593 GetCatalogNode( ExtendedVCB
*volume
, HFSCatalogNodeID parentID
, ConstUTF8Param name
, UInt32 nameLen
, UInt32 hint
,
594 CatalogNodeData
*nodeData
, UInt32
*newHint
)
597 CatalogRecord
*record
;
598 BTreeIterator searchIterator
;
599 FSBufferDescriptor btRecord
;
601 UInt32 heuristicHint
;
604 OSErr result
= noErr
;
606 Boolean isHFSPlus
= (volume
->vcbSigWord
== kHFSPlusSigWord
);
609 btRecord
.bufferAddress
= nodeData
;
610 btRecord
.itemSize
= sizeof(CatalogNodeData
);
612 btRecord
.bufferAddress
= &nodeData
->cnd_extra
;
613 btRecord
.itemSize
= sizeof(HFSCatalogFile
);
616 btRecord
.itemCount
= 1;
617 record
= (CatalogRecord
*) btRecord
.bufferAddress
;
618 key
= (CatalogKey
*) &searchIterator
.key
;
620 if (name
&& nameLen
== kUndefinedStrLen
)
621 nameLen
= strlen(name
);
623 result
= BuildCatalogKeyUTF8(volume
, parentID
, name
, nameLen
, key
, NULL
);
624 ReturnIfError(result
);
626 fcb
= GetFileControlBlock(volume
->catalogRefNum
);
627 searchIterator
.hint
.nodeNum
= *newHint
;
628 searchIterator
.hint
.index
= 0;
631 * We pass a 2nd hint/guess into BTSearchRecord. The heuristicHint
632 * is a mapping of dirID and nodeNumber, in hopes that the current
633 * search will be in the same node as the last search with the same
636 if (name
!= NULL
&& GetMRUCacheBlock(parentID
, volume
->hintCachePtr
, (Ptr
*)&cachedHint
) == 0)
637 heuristicHint
= *cachedHint
;
639 heuristicHint
= kInvalidMRUCacheKey
;
641 result
= BTSearchRecord(fcb
, &searchIterator
, heuristicHint
, &btRecord
, &dataSize
, &searchIterator
);
642 if (result
== btNotFound
)
645 if (name
!= NULL
&& result
== noErr
)
646 InsertMRUCacheBlock(volume
->hintCachePtr
, parentID
, (Ptr
) &(searchIterator
.hint
.nodeNum
));
648 if (result
== noErr
) {
649 CatalogName
*nodeName
= NULL
;
650 HFSCatalogNodeID threadParentID
;
652 /* if we got a thread record, then go look up real record */
653 switch (record
->recordType
) {
655 case kHFSFileThreadRecord
:
656 case kHFSFolderThreadRecord
:
657 threadParentID
= record
->hfsThread
.parentID
;
658 nodeName
= (CatalogName
*) &record
->hfsThread
.nodeName
;
661 case kHFSPlusFileThreadRecord
:
662 case kHFSPlusFolderThreadRecord
:
663 threadParentID
= record
->hfsPlusThread
.parentID
;
664 nodeName
= (CatalogName
*) &record
->hfsPlusThread
.nodeName
;
669 *newHint
= searchIterator
.hint
.nodeNum
;
672 if (threadParentID
) {
673 BuildCatalogKey(threadParentID
, nodeName
, isHFSPlus
, key
);
674 searchIterator
.hint
.nodeNum
= kNoHint
;
675 searchIterator
.hint
.index
= 0;
677 result
= BTSearchRecord(fcb
, &searchIterator
, kInvalidMRUCacheKey
, &btRecord
, &dataSize
, &searchIterator
);
678 if (result
== btNotFound
)
681 *newHint
= searchIterator
.hint
.nodeNum
;
686 * If we did not find it by name, then look for an embedded
687 * file ID in a mangled name.
689 if ( result
== cmNotFound
&& isHFSPlus
)
690 result
= LocateCatalogNodeByMangledName(volume
, parentID
, name
, nameLen
,
691 key
, record
, newHint
);
694 * In Mac OS X there can also be HFS filenames that
695 * could not be encoded using the default encoding.
696 * In which case they were encoded as MacRoman.
698 if (result
== cmNotFound
&& !isHFSPlus
) {
701 utf8_to_mac_roman(nameLen
, name
, hfsName
);
702 result
= LocateCatalogNode(volume
, parentID
, (CatalogName
*)hfsName
,
703 0, key
, record
, newHint
);
705 ReturnIfError(result
);
707 nodeData
->cnm_parID
= isHFSPlus
? key
->hfsPlus
.parentID
: key
->hfs
.parentID
;
709 if (nodeData
->cnm_flags
& kCatNameNoCopyName
) {
711 if (record
->recordType
== kHFSFolderRecord
|| record
->recordType
== kHFSFileRecord
)
712 CopyCatalogNodeData(volume
, record
, nodeData
);
718 nodeData
->cnm_nameptr
= nodeData
->cnm_namespace
;
720 result
= utf8_encodestr(key
->hfsPlus
.nodeName
.unicode
,
721 key
->hfsPlus
.nodeName
.length
* sizeof(UniChar
),
722 nodeData
->cnm_namespace
, (size_t *)&utf8len
,
723 MAXHFSVNODELEN
+ 1, ':', 0);
725 /* Need to allocate buffer large enough */
726 if (result
== ENAMETOOLONG
) {
727 utf8len
= utf8_encodelen(key
->hfsPlus
.nodeName
.unicode
,
728 key
->hfsPlus
.nodeName
.length
* sizeof(UniChar
),
730 nodeData
->cnm_nameptr
= NewPtr(utf8len
+ 1);
731 nodeData
->cnm_flags
|= kCatNameIsAllocated
;
732 result
= utf8_encodestr(key
->hfsPlus
.nodeName
.unicode
,
733 key
->hfsPlus
.nodeName
.length
* sizeof(UniChar
),
734 nodeData
->cnm_nameptr
, (size_t *)&utf8len
,
735 utf8len
+ 1, ':', 0);
738 else { // classic HFS
740 /* convert data to HFS Plus format */
741 if (record
->recordType
== kHFSFolderRecord
|| record
->recordType
== kHFSFileRecord
) {
742 CopyCatalogNodeData(volume
, record
, nodeData
);
743 result
= hfs_to_utf8(volume
, key
->hfs
.nodeName
, MAXHFSVNODELEN
+ 1,
744 &utf8len
, nodeData
->cnm_namespace
);
745 /* Need to allocate buffer large enough */
746 if (result
== ENAMETOOLONG
) {
747 nodeData
->cnm_nameptr
= NewPtr(utf8len
+ 1);
748 nodeData
->cnm_flags
|= kCatNameIsAllocated
;
749 result
= hfs_to_utf8(volume
, key
->hfs
.nodeName
, utf8len
+ 1,
750 &utf8len
, nodeData
->cnm_nameptr
);
753 * When an HFS name cannot be encoded with the current
754 * volume encoding we use MacRoman as a fallback.
756 result
= mac_roman_to_utf8(key
->hfs
.nodeName
, MAXHFSVNODELEN
+ 1,
757 &utf8len
, nodeData
->cnm_namespace
);
763 nodeData
->cnm_length
= utf8len
;
764 if (result
&& (nodeData
->cnm_flags
& kCatNameIsAllocated
))
766 DisposePtr(nodeData
->cnm_nameptr
);
767 nodeData
->cnm_flags
&= ~kCatNameIsAllocated
;
768 nodeData
->cnm_nameptr
= 0; /* Just to be clean */
774 if ( nodeData
->cnd_nodeID
> volume
->vcbNxtCNID
|| nodeData
->cnd_nodeID
== 0)
775 DebugStr("\pGetCatalogNode bad file ID found!");
780 } // end GetCatalogNode
784 GetDirEntrySize(BTreeIterator
*bip
, ExtendedVCB
* vol
)
789 UInt8 name
[kdirentMaxNameBytes
+ 1];
792 ckp
= (CatalogKey
*) &bip
->key
;
794 if (vol
->vcbSigWord
== kHFSPlusSigWord
) {
795 cnp
= (CatalogName
*) &ckp
->hfsPlus
.nodeName
;
796 utf8chars
= utf8_encodelen(cnp
->ustr
.unicode
,
797 cnp
->ustr
.length
* sizeof(UniChar
), ':', 0);
798 if (utf8chars
> kdirentMaxNameBytes
)
799 utf8chars
= kdirentMaxNameBytes
;
800 utf8chars
++; /* account for NULL termination */
802 cnp
= (CatalogName
*) ckp
->hfs
.nodeName
;
803 result
= hfs_to_utf8(vol
, cnp
->pstr
, kdirentMaxNameBytes
+ 1,
805 /*XXX ignoring error */
808 return DIRENTRY_SIZE(utf8chars
);
811 * NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE
813 * This is assuming maxinum size of a name is 255 (kdirentMaxNameBytes), which is incorrect.
814 * Any caller of this has to make sure names > 255 are mangled!!!!!!!!
818 PositionIterator(CatalogIterator
*cip
, UInt32 offset
, BTreeIterator
*bip
, UInt16
*op
)
820 #define CAT_START_OFFSET (2 * sizeof(struct hfsdotentry))
825 /* are we past the end of a directory? */
826 if (cip
->folderID
!= cip
->parentID
)
830 fcb
= GetFileControlBlock(vol
->catalogRefNum
);
832 /* make a btree iterator from catalog iterator */
833 UpdateBtreeIterator(cip
, bip
);
835 if (cip
->currentOffset
== offset
) {
836 *op
= kBTreeCurrentRecord
;
838 } else if (cip
->nextOffset
== offset
) {
839 *op
= kBTreeNextRecord
;
841 } else { /* start from beginning */
842 UInt32 heuristicHint
;
845 *op
= kBTreeNextRecord
;
848 * We pass a 2nd hint/guess into BTSearchRecord. The heuristicHint
849 * is a mapping of dirID and nodeNumber, in hopes that the current
850 * search will be in the same node as the last search with the same
853 result
= GetMRUCacheBlock( cip
->folderID
, vol
->hintCachePtr
, (Ptr
*)&cachedHint
);
854 heuristicHint
= (result
== noErr
) ? *cachedHint
: kInvalidMRUCacheKey
;
856 /* Position iterator at the folder's thread record */
857 result
= BTSearchRecord(fcb
, bip
, heuristicHint
, NULL
, NULL
, bip
);
861 InsertMRUCacheBlock( vol
->hintCachePtr
, cip
->folderID
, (Ptr
) &bip
->hint
.nodeNum
);
863 /* find offset (note: n^2 / 2) */
864 if (offset
> CAT_START_OFFSET
) {
865 HFSCatalogNodeID pid
, *idp
;
866 UInt32 curOffset
, nextOffset
;
868 /* get first record (ie offset 24) */
869 result
= BTIterateRecord( fcb
, kBTreeNextRecord
, bip
, NULL
, NULL
);
873 if (vol
->vcbSigWord
== kHFSPlusSigWord
)
874 idp
= &((CatalogKey
*) &bip
->key
)->hfsPlus
.parentID
;
876 idp
= &((CatalogKey
*) &bip
->key
)->hfs
.parentID
;
880 curOffset
= CAT_START_OFFSET
;
881 nextOffset
= GetDirEntrySize(bip
, vol
);
883 while (nextOffset
< offset
) {
884 result
= BTIterateRecord( fcb
, kBTreeNextRecord
, bip
, NULL
, NULL
);
888 /* check for parent change */
890 result
= cmNotFound
; /* offset past end of directory */
894 curOffset
= nextOffset
;
895 nextOffset
+= GetDirEntrySize(bip
, vol
);
898 if (nextOffset
!= offset
) {
903 UpdateCatalogIterator(bip
, cip
);
904 cip
->currentOffset
= curOffset
;
905 cip
->nextOffset
= nextOffset
;
910 if (result
== btNotFound
)
915 } /* end PositionIterator */
918 //_________________________________________________________________________________
919 // Routine: GetCatalogOffspring
921 // Function: Gets an offspring record from a specified folder. The folder
922 // is identified by it's folderID. The desired offspring CNode is
923 // indicated by the value of the offspring index (1 = 1st offspring
924 // CNode, 2 = 2nd offspring CNode, etc.).
926 //_________________________________________________________________________________
929 GetCatalogOffspring(ExtendedVCB
*volume
, HFSCatalogNodeID folderID
, UInt16 index
,
930 CatalogNodeData
*nodeData
,
931 HFSCatalogNodeID
*nodeID
, SInt16
*nodeType
)
933 CatalogIterator
* catalogIterator
;
941 * return cmNotFound for index 32767, to prevent overflowing
942 * the index into negative numbers.
944 if ( index
== 32767 )
947 // get best catalog iterator...
948 catalogIterator
= oGetCatalogIterator(volume
, folderID
, index
);
950 result
= IterateCatalogNode(volume
, catalogIterator
, index
,
951 nodeData
, nodeID
, nodeType
);
953 (void) ReleaseCatalogIterator(catalogIterator
);
957 } // end GetCatalogOffspring
960 //_________________________________________________________________________________
961 // Routine: IterateCatalogNode
963 // Function: Gets an offspring record from a specified folder. The folder
964 // is identified by it's folderID. The desired offspring CNode is
965 // indicated by the value of the offspring index (1 = 1st offspring
966 // CNode, 2 = 2nd offspring CNode, etc.).
968 //_________________________________________________________________________________
971 IterateCatalogNode( ExtendedVCB
*volume
, CatalogIterator
*catalogIterator
, UInt16 index
,
972 CatalogNodeData
*nodeData
, HFSCatalogNodeID
*nodeID
,
975 HFSCatalogNodeID offspringParentID
;
976 CatalogKey
* offspringKey
;
977 CatalogName
* offspringName
;
978 BTreeIterator btreeIterator
;
979 FSBufferDescriptor btRecord
;
980 CatalogRecord
* record
;
981 UInt8 databuf
[32]; /* space for partial record */
983 SInt16 selectionIndex
;
991 isHFSPlus
= (volume
->vcbSigWord
== kHFSPlusSigWord
);
992 fcb
= GetFileControlBlock(volume
->catalogRefNum
);
994 // make a btree iterator from catalog iterator
995 UpdateBtreeIterator(catalogIterator
, &btreeIterator
);
997 /* if client doesn't want data (ie readdir), just get type and id */
998 if (nodeData
== NULL
) {
999 /* data buf has space to cover all type/id offsets */
1000 btRecord
.bufferAddress
= databuf
;
1001 btRecord
.itemSize
= sizeof(databuf
);
1002 } else if (isHFSPlus
) {
1003 btRecord
.bufferAddress
= nodeData
;
1004 btRecord
.itemSize
= sizeof(CatalogNodeData
);
1006 btRecord
.bufferAddress
= &nodeData
->cnd_extra
;
1007 btRecord
.itemSize
= sizeof(HFSCatalogFile
);
1009 btRecord
.itemCount
= 1;
1011 //--- if neccessary position the iterator at the thread record for the specified folder
1013 if ( catalogIterator
->currentIndex
== 0 ) // is this a new iterator?
1015 UInt32 heuristicHint
;
1018 // We pass a 2nd hint/guess into BTSearchRecord. The heuristicHint is a mapping of
1019 // dirID and nodeNumber, in hopes that the current search will be in the same node
1020 // as the last search with the same parentID.
1021 result
= GetMRUCacheBlock( catalogIterator
->folderID
, volume
->hintCachePtr
, (Ptr
*)&cachedHint
);
1022 heuristicHint
= (result
== noErr
) ? *cachedHint
: kInvalidMRUCacheKey
;
1024 result
= BTSearchRecord( fcb
, &btreeIterator
, heuristicHint
, &btRecord
, &tempSize
, &btreeIterator
);
1025 ExitOnError(result
);
1027 UpdateCatalogIterator(&btreeIterator
, catalogIterator
); // update btree hint and key
1029 InsertMRUCacheBlock( volume
->hintCachePtr
, catalogIterator
->folderID
, (Ptr
) &btreeIterator
.hint
.nodeNum
);
1032 //--- get offspring record (relative to catalogIterator's position)
1034 selectionIndex
= index
- catalogIterator
->currentIndex
;
1036 // now we have to map index into next/prev operations...
1037 if (selectionIndex
== 1)
1039 operation
= kBTreeNextRecord
;
1041 else if (selectionIndex
== -1)
1043 operation
= kBTreePrevRecord
;
1045 else if (selectionIndex
== 0)
1047 operation
= kBTreeCurrentRecord
;
1049 else if (selectionIndex
> 1)
1053 for (i
= 1; i
< selectionIndex
; ++i
)
1055 result
= BTIterateRecord( fcb
, kBTreeNextRecord
, &btreeIterator
, &btRecord
, &tempSize
);
1056 ExitOnError(result
);
1058 operation
= kBTreeNextRecord
;
1060 else // (selectionIndex < -1)
1064 for (i
= -1; i
> selectionIndex
; --i
)
1066 result
= BTIterateRecord( fcb
, kBTreePrevRecord
, &btreeIterator
, &btRecord
, &tempSize
);
1067 ExitOnError(result
);
1069 operation
= kBTreePrevRecord
;
1072 result
= BTIterateRecord( fcb
, operation
, &btreeIterator
, &btRecord
, &tempSize
);
1073 ExitOnError(result
);
1075 offspringKey
= (CatalogKey
*) &btreeIterator
.key
;
1079 offspringParentID
= offspringKey
->hfsPlus
.parentID
;
1080 offspringName
= (CatalogName
*) &offspringKey
->hfsPlus
.nodeName
;
1084 offspringParentID
= offspringKey
->hfs
.parentID
;
1085 offspringName
= (CatalogName
*) offspringKey
->hfs
.nodeName
;
1088 if (offspringParentID
!= catalogIterator
->folderID
) // different parent?
1090 AgeCatalogIterator(catalogIterator
); // we reached the end, so don't hog the cache!
1092 result
= cmNotFound
; // must be done with this folder
1096 UpdateCatalogIterator(&btreeIterator
, catalogIterator
); // update btree hint and key
1097 catalogIterator
->currentIndex
= index
; // update the offspring index marker
1099 record
= (CatalogRecord
*) btRecord
.bufferAddress
;
1101 if (nodeData
== NULL
) { /* Just copy the id and type...not name */
1104 *nodeType
= record
->recordType
;
1105 *nodeID
= record
->hfsPlusFolder
.folderID
;
1110 if (record
->recordType
== kHFSFileRecord
) {
1111 *nodeType
= kCatalogFileNode
;
1112 *nodeID
= record
->hfsFile
.fileID
;
1113 } else if (record
->recordType
== kHFSFolderRecord
) {
1114 *nodeType
= kCatalogFolderNode
;
1115 *nodeID
= record
->hfsFolder
.folderID
;
1117 result
= cmNotFound
;
1120 nodeData
->cnm_parID
= isHFSPlus
? offspringKey
->hfsPlus
.parentID
: offspringKey
->hfs
.parentID
;
1121 nodeData
->cnm_nameptr
= nodeData
->cnm_namespace
;
1124 result
= utf8_encodestr(offspringName
->ustr
.unicode
,
1125 offspringName
->ustr
.length
* sizeof(UniChar
),
1126 nodeData
->cnm_namespace
, (size_t *)&utf8len
,
1127 MAXHFSVNODELEN
+ 1, ':', 0);
1129 /* Need to allocate buffer large enough */
1130 if (result
== ENAMETOOLONG
) {
1131 utf8len
= utf8_encodelen(offspringName
->ustr
.unicode
,
1132 offspringName
->ustr
.length
* sizeof(UniChar
),
1134 nodeData
->cnm_nameptr
= NewPtr(utf8len
+ 1);
1135 nodeData
->cnm_flags
|= kCatNameIsAllocated
;
1136 result
= utf8_encodestr(offspringName
->ustr
.unicode
,
1137 offspringName
->ustr
.length
* sizeof(UniChar
),
1138 nodeData
->cnm_nameptr
, (size_t *)&utf8len
,
1139 utf8len
+ 1, ':', 0);
1144 if (record
->recordType
== kHFSFolderRecord
|| record
->recordType
== kHFSFileRecord
) {
1146 CopyCatalogNodeData(volume
, record
, nodeData
);
1147 result
= hfs_to_utf8(volume
, offspringName
->pstr
, MAXHFSVNODELEN
+ 1, &utf8len
, nodeData
->cnm_namespace
);
1148 if (result
== ENAMETOOLONG
) { /* Need to allocate buffer large enough */
1149 nodeData
->cnm_nameptr
= NewPtr(utf8len
+1);
1150 nodeData
->cnm_flags
|= kCatNameIsAllocated
;
1151 result
= hfs_to_utf8(volume
, offspringName
->pstr
, utf8len
+ 1, &utf8len
, nodeData
->cnm_nameptr
);
1155 result
= cmNotFound
;
1164 if ( result
== btNotFound
)
1165 result
= cmNotFound
;
1169 } // end IterateCatalogNode
1172 //_________________________________________________________________________________
1173 // Routine: MoveRenameCatalogNode
1175 // Function: Moves and/or rename an existing folder or file CNode.
1176 // Note that when moving a folder, all decendants (its offspring,
1177 // their offspring, etc.) are also moved.
1179 // Assumes srcHint contains a text encoding that was set by a GetCatalogNode call
1180 //_________________________________________________________________________________
1183 MoveRenameCatalogNode(ExtendedVCB
*volume
, HFSCatalogNodeID srcParentID
, ConstUTF8Param srcName
,
1184 UInt32 srcHint
, HFSCatalogNodeID dstParentID
, ConstUTF8Param dstName
, UInt32
*newHint
)
1186 CatalogKey srcKey
; // 518 bytes
1187 CatalogRecord srcRecord
; // 520 bytes
1188 CatalogKey dstKey
; // 518 bytes
1189 CatalogKey dstFolderKey
; // 518 bytes
1190 HFSCatalogNodeID dstFolderParentID
= 0;
1191 UInt32 dstFolderHint
;
1192 CatalogName
*dstFolderNamePtr
= NULL
;
1193 CatalogRecord tmpRecord
; // 520 bytes
1194 HFSCatalogNodeID threadID
;
1195 UInt32 textEncoding
;
1198 Boolean isHFSPlus
= (volume
->vcbSigWord
== kHFSPlusSigWord
);
1199 Boolean isOrigDeleted
= false;
1204 result
= BuildCatalogKeyUTF8(volume
, srcParentID
, srcName
, kUndefinedStrLen
, &srcKey
, &textEncoding
);
1205 ReturnIfError(result
);
1207 /* XXX can strlen and bcmp handle NULL pointers? */
1209 srcNameLen
= strlen(srcName
);
1210 dstNameLen
= strlen(dstName
);
1212 //--- check if names match
1214 if ((srcNameLen
== dstNameLen
) && (bcmp(srcName
, dstName
, srcNameLen
) == 0))
1219 dstKey
.hfsPlus
.parentID
= dstParentID
; // set parent ID
1222 dstKey
.hfs
.parentID
= dstParentID
; // set parent ID
1225 else /* names are different */
1228 result
= BuildCatalogKeyUTF8(volume
, dstParentID
, dstName
, kUndefinedStrLen
, &dstKey
, &textEncoding
);
1229 ReturnIfError(result
);
1232 //--- make sure source record exists
1234 result
= LocateCatalogNodeByKey(volume
, srcHint
, &srcKey
, &srcRecord
, &srcHint
);
1236 // if we did not find it by name, then look for an embedded file ID in a mangled name
1237 if ( (result
== cmNotFound
) && isHFSPlus
)
1238 result
= LocateCatalogNodeByMangledName(volume
, srcParentID
, srcName
, kUndefinedStrLen
, &srcKey
, &srcRecord
, &srcHint
);
1239 ReturnIfError(result
);
1241 srcParentID
= (isHFSPlus
? srcKey
.hfsPlus
.parentID
: srcKey
.hfs
.parentID
);
1243 // if we're moving then do some additional preflighting...
1245 if (srcParentID
!= dstParentID
)
1247 //--- make sure destination folder exists
1249 result
= LocateCatalogNode(volume
, dstParentID
, NULL
, kNoHint
, &dstFolderKey
, &tmpRecord
, &dstFolderHint
);
1250 ReturnIfError(result
);
1252 if (tmpRecord
.recordType
== kHFSPlusFolderRecord
)
1254 dstParentID
= tmpRecord
.hfsPlusFolder
.folderID
;
1255 dstFolderParentID
= dstFolderKey
.hfsPlus
.parentID
;
1256 dstFolderNamePtr
= (CatalogName
*) &dstFolderKey
.hfsPlus
.nodeName
;
1258 else if (tmpRecord
.recordType
== kHFSFolderRecord
)
1260 dstParentID
= tmpRecord
.hfsFolder
.folderID
;
1261 dstFolderParentID
= dstFolderKey
.hfs
.parentID
;
1262 dstFolderNamePtr
= (CatalogName
*) &dstFolderKey
.hfs
.nodeName
;
1269 //--- if source is a folder, make sure its a proper move
1271 if (srcRecord
.recordType
== kHFSPlusFolderRecord
|| srcRecord
.recordType
== kHFSFolderRecord
)
1273 HFSCatalogNodeID srcFolderID
;
1274 HFSCatalogNodeID ancestorParentID
;
1275 CatalogKey tempKey
; // 518 bytes
1280 srcFolderID
= srcRecord
.hfsPlusFolder
.folderID
;
1281 ancestorParentID
= dstFolderKey
.hfsPlus
.parentID
;
1285 srcFolderID
= srcRecord
.hfsFolder
.folderID
;
1286 ancestorParentID
= dstFolderKey
.hfs
.parentID
;
1289 if ( srcFolderID
== fsRtDirID
|| // source == root?
1290 srcFolderID
== dstParentID
|| // source == destination?
1291 srcFolderID
== ancestorParentID
) // source == destination's parent?
1296 while (ancestorParentID
> fsRtDirID
) // loop until we reach the root folder
1298 // locate next folder up the tree...
1299 result
= LocateCatalogNode(volume
, ancestorParentID
, NULL
, kNoHint
, &tempKey
, &tmpRecord
, &tempHint
);
1300 ReturnIfError(result
);
1302 ancestorParentID
= isHFSPlus
? tempKey
.hfsPlus
.parentID
: tempKey
.hfs
.parentID
;
1304 if (srcFolderID
== ancestorParentID
) // source = destination ancestor?
1309 TrashCatalogIterator(volume
, dstParentID
); // invalidate any iterators for destination parentID
1311 else /* (srcParentID == dstParentID) */
1315 *newHint
= srcHint
; // they match, so we're all done!
1320 TrashCatalogIterator(volume
, srcParentID
); // invalidate any iterators for source's parentID
1321 InvalidateCatalogNodeCache(volume
, srcParentID
); // invalidate node cache since parent changed
1323 if (isNewName
&& isHFSPlus
)
1325 // update textEncoding hint (works for folders and files)
1326 srcRecord
.hfsPlusFolder
.textEncoding
= textEncoding
;
1328 UpdateVolumeEncodings(volume
, textEncoding
);
1331 //--- insert source CNode record in BTree with new key (a new parent id and/or new name)
1333 result
= InsertBTreeRecord(volume
->catalogRefNum
, &dstKey
, &srcRecord
, GetCatalogRecordSize(&srcRecord
), newHint
);
1335 if (result
== btExists
)
1339 /* XXX what about the case: move id1,foo to id2,FOO ?? */
1340 if (srcParentID
!= dstParentID
|| isNewName
== false)
1343 //--- new CNode name already exists in the same folder, locate the existing one
1344 result
= SearchBTreeRecord(volume
->catalogRefNum
, &dstKey
, srcHint
,
1345 &dstFolderKey
, &tmpRecord
, &dataSize
, newHint
);
1347 if (result
== btNotFound
)
1348 result
= cmNotFound
;
1349 ReturnIfError(result
);
1351 //--- check if its the same CNode (same name but different upper/lower case)
1353 if (srcRecord
.recordType
!= tmpRecord
.recordType
)
1356 switch (srcRecord
.recordType
)
1358 case kHFSPlusFileRecord
: /* HFS Plus records share same cnid location */
1359 case kHFSPlusFolderRecord
:
1360 if (srcRecord
.hfsPlusFolder
.folderID
!= tmpRecord
.hfsPlusFolder
.folderID
)
1364 case kHFSFolderRecord
:
1365 if (srcRecord
.hfsFolder
.folderID
!= tmpRecord
.hfsFolder
.folderID
)
1369 case kHFSFileRecord
:
1370 if (srcRecord
.hfsFile
.fileID
!= tmpRecord
.hfsFile
.fileID
)
1378 //--- same name but different case, so delete old and insert with new name...
1380 result
= DeleteBTreeRecord(volume
->catalogRefNum
, &srcKey
);
1381 ReturnIfError(result
);
1382 isOrigDeleted
= true; // So we dont delete it again down below
1384 result
= InsertBTreeRecord(volume
->catalogRefNum
, &dstKey
, &srcRecord
, dataSize
, newHint
);
1386 ReturnIfError(result
);
1389 // from this point on we need to cleanup (ie delete the new record) if we encounter errors!
1392 //--- update thread record for node (if it exists)
1394 switch (srcRecord
.recordType
)
1396 case kHFSPlusFileRecord
:
1397 case kHFSPlusFolderRecord
:
1398 threadID
= srcRecord
.hfsPlusFolder
.folderID
;
1401 case kHFSFolderRecord
:
1402 threadID
= srcRecord
.hfsFolder
.folderID
;
1405 case kHFSFileRecord
:
1406 if (srcRecord
.hfsFile
.flags
& kHFSThreadExistsMask
)
1408 threadID
= srcRecord
.hfsFile
.fileID
;
1411 /* fall through if no thread... */
1420 CatalogKey threadKey
; // 518 bytes
1421 CatalogRecord threadRecord
; // 520 bytes
1424 result
= LocateCatalogRecord(volume
, threadID
, NULL
, kNoHint
, &threadKey
, &threadRecord
, &threadHint
);
1425 if (result
!= noErr
) goto Exit_Delete
;
1429 if (srcParentID
!= dstParentID
)
1430 threadRecord
.hfsPlusThread
.parentID
= dstParentID
;
1432 CopyCatalogName((CatalogName
*)&dstKey
.hfsPlus
.nodeName
, (CatalogName
*) &threadRecord
.hfsPlusThread
.nodeName
, isHFSPlus
);
1434 threadSize
= sizeof(threadRecord
.hfsPlusThread
);
1435 // HFS Plus has varaible sized threads so adjust to actual length
1436 threadSize
-= ( sizeof(threadRecord
.hfsPlusThread
.nodeName
.unicode
) - (threadRecord
.hfsPlusThread
.nodeName
.length
* sizeof(UniChar
)) );
1440 if (srcParentID
!= dstParentID
)
1441 threadRecord
.hfsThread
.parentID
= dstParentID
;
1443 CopyCatalogName((CatalogName
*)&dstKey
.hfs
.nodeName
,(CatalogName
*) threadRecord
.hfsThread
.nodeName
, isHFSPlus
);
1445 threadSize
= sizeof(threadRecord
.hfsThread
);
1448 result
= DeleteBTreeRecord(volume
->catalogRefNum
, &threadKey
);
1449 if (result
!= noErr
) goto Exit_Delete
;
1451 result
= InsertBTreeRecord(volume
->catalogRefNum
, &threadKey
, &threadRecord
, threadSize
, &threadHint
);
1452 if (result
!= noErr
) goto Exit_Delete
; //XXX exiting with a missing thread!
1455 //--- we successfully added the new node so delete the old source CNode record
1457 if (! isOrigDeleted
) {
1458 result
= DeleteBTreeRecord(volume
->catalogRefNum
, &srcKey
);
1461 // uh oh, we could not delete the original
1462 // so we better get rid of the new node...
1464 (void) DeleteBTreeRecord(volume
->catalogRefNum
, &dstKey
);
1466 //XXX also need to fix up the thread...
1472 if (srcParentID
!= dstParentID
)
1474 result
= UpdateFolderCount(volume
, srcParentID
, NULL
, srcRecord
.recordType
, kNoHint
, -1);
1475 result
= UpdateFolderCount(volume
, dstFolderParentID
, dstFolderNamePtr
, srcRecord
.recordType
, dstFolderHint
, +1);
1478 //--- make sure changes get flushed out
1480 volume
->vcbFlags
|= 0xFF00; // Mark the VCB dirty
1481 volume
->vcbLsMod
= GetTimeUTC(); // update last modified date
1484 (void) FlushCatalog(volume
);
1490 (void) DeleteBTreeRecord(volume
->catalogRefNum
, &dstKey
);
1494 } // end MoveRenameCatalogNode
1497 //_________________________________________________________________________________
1498 // Routine: UpdateCatalogNode
1500 // Function: Marks the Catalog BTree node identified by the given catalog hint
1503 //_________________________________________________________________________________
1507 UpdateCatalogNode(ExtendedVCB
*volume
, HFSCatalogNodeID parentID
, ConstUTF8Param name
,
1508 UInt32 catalogHint
, const CatalogNodeData
*nodeData
)
1511 CatalogRecord
*record
;
1515 CatalogKey catalogKey
; // 518 bytes
1516 CatalogRecord catalogRecord
; // 520 bytes
1517 Boolean isHFSPlus
= volume
->vcbSigWord
== kHFSPlusSigWord
;
1519 /* XXX no reason to have ptrs to local variables... */
1521 record
= &catalogRecord
;
1523 result
= BuildCatalogKeyUTF8(volume
, parentID
, name
, kUndefinedStrLen
, key
, NULL
);
1524 ReturnIfError(result
);
1526 //--- locate subject catalog node
1528 result
= LocateCatalogNodeByKey(volume
, catalogHint
, key
, record
, &hint
);
1530 // if we did not find it by name, then look for an embedded file ID in a mangled name
1531 if ( (result
== cmNotFound
) && isHFSPlus
)
1532 result
= LocateCatalogNodeByMangledName(volume
, parentID
, name
, kUndefinedStrLen
, key
, record
, &hint
);
1534 if (result
== btNotFound
)
1535 result
= cmNotFound
;
1537 if (catalogHint
!= hint
)
1538 PRINTIT(("UpdateCatalogNode: catalogHint does not match (in: %ld, out: %ld)\n", catalogHint
, hint
));
1539 ReturnIfError(result
);
1541 // update user modifiable fields in the catalog node record...
1543 switch (record
->recordType
)
1545 case kHFSFolderRecord
:
1548 if (nodeData
->cnd_type
!= kCatalogFolderNode
)
1549 DebugStr("\p UpdateCatalogNode: folder/file mismatch!");
1552 record
->hfsFolder
.createDate
= UTCToLocal(nodeData
->cnd_createDate
);
1553 record
->hfsFolder
.modifyDate
= UTCToLocal(nodeData
->cnd_contentModDate
);
1554 record
->hfsFolder
.backupDate
= UTCToLocal(nodeData
->cnd_backupDate
);
1556 *(DInfo
*) &record
->hfsFolder
.userInfo
= *(DInfo
*) &nodeData
->cnd_finderInfo
;
1557 *(DXInfo
*) &record
->hfsFolder
.finderInfo
= *(DXInfo
*) ((UInt32
)&nodeData
->cnd_finderInfo
+ 16);
1559 recordSize
= sizeof(HFSCatalogFolder
);
1563 case kHFSFileRecord
:
1568 if (nodeData
->cnd_type
!= kCatalogFileNode
)
1569 DebugStr("UpdateCatalogNode: folder/file mismatch!");
1570 if ((nodeData
->cnd_datafork
.totalBlocks
> (0x7FFFFFFF/volume
->blockSize
)) ||
1571 (nodeData
->cnd_rsrcfork
.totalBlocks
> (0x7FFFFFFF/volume
->blockSize
)))
1572 DebugStr("HFS file size is larger than 2Gig");
1575 record
->hfsFile
.flags
= (UInt8
) nodeData
->cnd_flags
;
1576 record
->hfsFile
.createDate
= UTCToLocal(nodeData
->cnd_createDate
);
1577 record
->hfsFile
.modifyDate
= UTCToLocal(nodeData
->cnd_contentModDate
);
1578 record
->hfsFile
.backupDate
= UTCToLocal(nodeData
->cnd_backupDate
);
1580 record
->hfsFile
.dataLogicalSize
= nodeData
->cnd_datafork
.logicalSize
;
1581 record
->hfsFile
.dataPhysicalSize
= nodeData
->cnd_datafork
.totalBlocks
* volume
->blockSize
;
1582 record
->hfsFile
.rsrcLogicalSize
= nodeData
->cnd_rsrcfork
.logicalSize
;
1583 record
->hfsFile
.rsrcPhysicalSize
= nodeData
->cnd_rsrcfork
.totalBlocks
* volume
->blockSize
;
1585 *(FInfo
*) &record
->hfsFile
.userInfo
= *(FInfo
*) &nodeData
->cnd_finderInfo
;
1586 *(FXInfo
*) &record
->hfsFile
.finderInfo
= *(FXInfo
*) ((UInt32
)&nodeData
->cnd_finderInfo
+ 16);
1589 for (i
= 0; i
< kHFSExtentDensity
; ++i
)
1591 record
->hfsFile
.dataExtents
[i
].startBlock
=
1592 (UInt16
) nodeData
->cnd_datafork
.extents
[i
].startBlock
;
1593 record
->hfsFile
.dataExtents
[i
].blockCount
=
1594 (UInt16
) nodeData
->cnd_datafork
.extents
[i
].blockCount
;
1595 record
->hfsFile
.rsrcExtents
[i
].startBlock
=
1596 (UInt16
) nodeData
->cnd_rsrcfork
.extents
[i
].startBlock
;
1597 record
->hfsFile
.rsrcExtents
[i
].blockCount
=
1598 (UInt16
) nodeData
->cnd_rsrcfork
.extents
[i
].blockCount
;
1601 recordSize
= sizeof(HFSCatalogFile
);
1605 case kHFSPlusFolderRecord
:
1607 record
->hfsPlusFolder
.createDate
= nodeData
->cnd_createDate
;
1608 record
->hfsPlusFolder
.contentModDate
= nodeData
->cnd_contentModDate
;
1609 record
->hfsPlusFolder
.backupDate
= nodeData
->cnd_backupDate
;
1610 record
->hfsPlusFolder
.accessDate
= nodeData
->cnd_accessDate
;
1611 record
->hfsPlusFolder
.attributeModDate
= nodeData
->cnd_attributeModDate
;
1612 record
->hfsPlusFolder
.bsdInfo
.ownerID
= nodeData
->cnd_ownerID
;
1613 record
->hfsPlusFolder
.bsdInfo
.groupID
= nodeData
->cnd_groupID
;
1614 record
->hfsPlusFolder
.bsdInfo
.ownerFlags
= nodeData
->cnd_ownerFlags
;
1615 record
->hfsPlusFolder
.bsdInfo
.adminFlags
= nodeData
->cnd_adminFlags
;
1616 record
->hfsPlusFolder
.bsdInfo
.fileMode
= nodeData
->cnd_mode
;
1617 record
->hfsPlusFolder
.textEncoding
= nodeData
->cnd_textEncoding
;
1619 BlockMoveData(&nodeData
->cnd_finderInfo
, &record
->hfsPlusFolder
.userInfo
, 32);
1621 recordSize
= sizeof(HFSPlusCatalogFolder
);
1625 case kHFSPlusFileRecord
:
1627 record
->hfsPlusFile
.flags
= nodeData
->cnd_flags
;
1628 record
->hfsPlusFile
.createDate
= nodeData
->cnd_createDate
;
1629 record
->hfsPlusFile
.contentModDate
= nodeData
->cnd_contentModDate
;
1630 record
->hfsPlusFile
.backupDate
= nodeData
->cnd_backupDate
;
1631 record
->hfsPlusFile
.accessDate
= nodeData
->cnd_accessDate
;
1632 record
->hfsPlusFile
.attributeModDate
= nodeData
->cnd_attributeModDate
;
1633 record
->hfsPlusFile
.bsdInfo
.ownerID
= nodeData
->cnd_ownerID
;
1634 record
->hfsPlusFile
.bsdInfo
.groupID
= nodeData
->cnd_groupID
;
1635 record
->hfsPlusFile
.bsdInfo
.ownerFlags
= nodeData
->cnd_ownerFlags
;
1636 record
->hfsPlusFile
.bsdInfo
.adminFlags
= nodeData
->cnd_adminFlags
;
1637 record
->hfsPlusFile
.bsdInfo
.fileMode
= nodeData
->cnd_mode
;
1638 /* get special value (iNodeNum, linkCount or rawDevice) */
1639 record
->hfsPlusFile
.bsdInfo
.special
.rawDevice
= nodeData
->cnd_rawDevice
;
1640 record
->hfsPlusFile
.textEncoding
= nodeData
->cnd_textEncoding
;
1642 record
->hfsPlusFile
.dataFork
.logicalSize
= nodeData
->cnd_datafork
.logicalSize
;
1643 record
->hfsPlusFile
.dataFork
.totalBlocks
= nodeData
->cnd_datafork
.totalBlocks
;
1644 BlockMoveData(&nodeData
->cnd_datafork
.extents
,
1645 &record
->hfsPlusFile
.dataFork
.extents
, sizeof(HFSPlusExtentRecord
));
1647 record
->hfsPlusFile
.resourceFork
.logicalSize
= nodeData
->cnd_rsrcfork
.logicalSize
;
1648 record
->hfsPlusFile
.resourceFork
.totalBlocks
= nodeData
->cnd_rsrcfork
.totalBlocks
;
1649 BlockMoveData(&nodeData
->cnd_rsrcfork
.extents
,
1650 &record
->hfsPlusFile
.resourceFork
.extents
, sizeof(HFSPlusExtentRecord
));
1652 BlockMoveData(&nodeData
->cnd_finderInfo
, &record
->hfsPlusFile
.userInfo
, 32);
1654 #if HFS_HARDLINKS && DEBUG_BUILD
1655 /* Must swap opaque finder data */
1656 if (SWAP_BE32 (record
->hfsPlusFile
.userInfo
.fdType
) == kHardLinkFileType
&&
1657 SWAP_BE32 (record
->hfsPlusFile
.userInfo
.fdCreator
) == kHardLinkCreator
) {
1658 if (record
->hfsPlusFile
.dataFork
.logicalSize
!= 0)
1659 DebugStr("UpdateCatalogNode: link has data fork!");
1662 recordSize
= sizeof(HFSPlusCatalogFile
);
1670 result
= ReplaceBTreeRecord(volume
->catalogRefNum
, key
, catalogHint
, record
, recordSize
, &hint
);
1672 if ( result
== btNotFound
)
1674 result
= cmNotFound
;
1676 else if ( result
== noErr
)
1678 /* if we're just updating the accessDate then no need to change volume mod date */
1679 if (nodeData
->cnd_contentModDate
> volume
->vcbLsMod
||
1680 (isHFSPlus
&& nodeData
->cnd_attributeModDate
> volume
->vcbLsMod
))
1683 volume
->vcbFlags
|= 0xFF00; // Mark the VCB dirty
1684 volume
->vcbLsMod
= GetTimeUTC(); // update last modified date
1688 result
= FlushCatalog(volume
); // flush the catalog
1695 //_________________________________________________________________________________
1696 // Routine: CreateFileIDRef
1698 // Function: Creates a file thread record for hfs file node
1700 //_________________________________________________________________________________
1703 CreateFileIDRef(ExtendedVCB
*volume
, HFSCatalogNodeID parentID
, ConstUTF8Param name
, UInt32 hint
, HFSCatalogNodeID
*threadID
)
1705 CatalogKey nodeKey
; // 518 bytes
1706 CatalogRecord nodeData
; // 520 bytes
1707 HFSCatalogKey threadKey
;
1708 HFSCatalogThread threadData
;
1712 Boolean isHFSPlus
= (volume
->vcbSigWord
== kHFSPlusSigWord
);
1716 result
= BuildCatalogKeyUTF8(volume
, parentID
, name
, kUndefinedStrLen
, &nodeKey
, NULL
);
1717 ReturnIfError(result
);
1719 //--- locate subject catalog node
1721 result
= LocateCatalogNodeByKey(volume
, hint
, &nodeKey
, &nodeData
, &nodeHint
);
1723 // if we did not find it by name, then look for an embedded file ID in a mangled name
1724 if ( (result
== cmNotFound
) && isHFSPlus
)
1725 result
= LocateCatalogNodeByMangledName(volume
, parentID
, name
, kUndefinedStrLen
, &nodeKey
, &nodeData
, &nodeHint
);
1726 ReturnIfError(result
);
1728 if (nodeData
.recordType
== kHFSPlusFileRecord
)
1730 *threadID
= nodeData
.hfsPlusFile
.fileID
;
1731 return noErr
; // already have one
1734 if (nodeData
.recordType
!= kHFSFileRecord
)
1740 if (nodeData
.hfsFile
.flags
& kHFSThreadExistsMask
)
1742 *threadID
= nodeData
.hfsFile
.fileID
;
1743 return noErr
; // already have one
1746 result
= VolumeWritable( volume
);
1747 if ( result
!= noErr
) return result
;
1750 // need to insert a thread record
1752 BuildCatalogKey(nodeData
.hfsFile
.fileID
, NULL
, false, (CatalogKey
*)&threadKey
);
1754 ClearMemory(&threadData
, sizeof(HFSCatalogThread
));
1755 threadData
.recordType
= kHFSFileThreadRecord
;
1756 threadData
.parentID
= nodeKey
.hfs
.parentID
;
1757 BlockMoveData(&nodeKey
.hfs
.nodeName
, &threadData
.nodeName
, nodeKey
.hfs
.nodeName
[0] + 1);
1759 result
= InsertBTreeRecord(volume
->catalogRefNum
, &threadKey
, &threadData
, sizeof(HFSCatalogThread
), &tempHint
);
1760 if (result
== btExists
) result
= noErr
; //XXX could return cmExists or fidExists
1761 ReturnIfError(result
);
1764 // Finally, set the flag in the file record to say this file has a thread record.
1766 nodeData
.hfsFile
.flags
|= kHFSThreadExistsMask
;
1767 result
= ReplaceBTreeRecord(volume
->catalogRefNum
, &nodeKey
, nodeHint
, &nodeData
, sizeof(HFSCatalogFile
), &nodeHint
);
1769 if (result
== noErr
) {
1770 (void) FlushCatalog(volume
);
1771 *threadID
= nodeData
.hfsFile
.fileID
;
1778 //_________________________________________________________________________________
1779 // Routine: CompareCatalogKeys
1781 // Function: Compares two catalog keys (a search key and a trial key).
1783 // Result: +n search key > trial key
1784 // 0 search key = trial key
1785 // -n search key < trial key
1786 //_________________________________________________________________________________
1789 CompareCatalogKeys(HFSCatalogKey
*searchKey
, HFSCatalogKey
*trialKey
)
1791 HFSCatalogNodeID searchParentID
, trialParentID
;
1794 searchParentID
= searchKey
->parentID
;
1795 trialParentID
= trialKey
->parentID
;
1797 if ( searchParentID
> trialParentID
) // parent dirID is unsigned
1799 else if ( searchParentID
< trialParentID
)
1801 else // parent dirID's are equal, compare names
1803 #if ( ! FORDISKFIRSTAID )
1804 LogStartTime(kTraceRelString
);
1806 result
= FastRelString(searchKey
->nodeName
, trialKey
->nodeName
);
1808 LogEndTime(kTraceRelString
, noErr
);
1810 result
= (SInt32
) RelString_Glue(searchKey
->nodeName
, trialKey
->nodeName
);
1818 //_________________________________________________________________________________
1819 // Routine: CompareExtendedCatalogKeys
1821 // Function: Compares two large catalog keys (a search key and a trial key).
1823 // Result: +n search key > trial key
1824 // 0 search key = trial key
1825 // -n search key < trial key
1826 //_________________________________________________________________________________
1829 CompareExtendedCatalogKeys(HFSPlusCatalogKey
*searchKey
, HFSPlusCatalogKey
*trialKey
)
1832 HFSCatalogNodeID searchParentID
, trialParentID
;
1834 searchParentID
= searchKey
->parentID
;
1835 trialParentID
= trialKey
->parentID
;
1837 if ( searchParentID
> trialParentID
) // parent node IDs are unsigned
1841 else if ( searchParentID
< trialParentID
)
1845 else // parent node ID's are equal, compare names
1847 if ( searchKey
->nodeName
.length
== 0 || trialKey
->nodeName
.length
== 0 )
1848 result
= searchKey
->nodeName
.length
- trialKey
->nodeName
.length
;
1850 result
= FastUnicodeCompare(&searchKey
->nodeName
.unicode
[0], searchKey
->nodeName
.length
,
1851 &trialKey
->nodeName
.unicode
[0], trialKey
->nodeName
.length
);