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
,
243 CatalogRecord nodeData
; // 520 bytes
245 CatalogRecord parentThreadData
; // 520 bytes
246 HFSCatalogNodeID parentsParentID
;
247 CatalogName
*parentNamePtr
;
252 Boolean isHFSPlus
= (volume
->vcbSigWord
== kHFSPlusSigWord
);
254 FSBufferDescriptor btRecord
;
255 BTreeIterator iterator
;
256 BTreeIterator threadIter
;
257 HFSCatalogNodeID nextCNID
;
259 if (nodeType
!= kCatalogFolderNode
&& nodeType
!= kCatalogFileNode
)
262 fcb
= GetFileControlBlock(volume
->catalogRefNum
);
263 nodeKey
= (CatalogKey
*) &iterator
.key
;
265 //--- make sure parent exists (by locating the parent's thread record)
267 result
= LocateCatalogThread(volume
, parentID
, &parentThreadData
, &tempSize
, &tempHint
);
268 ReturnIfError(result
);
270 TrashCatalogIterator(volume
, parentID
); // invalidate any iterators for this parentID
272 // save copy of parent's parentID and name.
276 if (parentThreadData
.recordType
!= kHFSPlusFolderThreadRecord
)
279 parentsParentID
= parentThreadData
.hfsPlusThread
.parentID
;
280 parentNamePtr
= (CatalogName
*) &parentThreadData
.hfsPlusThread
.nodeName
;
284 if (parentThreadData
.recordType
!= kHFSFolderThreadRecord
)
287 parentsParentID
= parentThreadData
.hfsThread
.parentID
;
288 parentNamePtr
= (CatalogName
*) &parentThreadData
.hfsThread
.nodeName
;
291 // invalidate cache for parent since its about to change
292 InvalidateCatalogNodeCache(volume
, parentsParentID
);
294 //--- build key for new catalog node
295 textEncoding
= teHint
;
296 result
= BuildCatalogKeyUTF8(volume
, parentID
, name
, kUndefinedStrLen
, nodeKey
, &textEncoding
);
297 ReturnIfError(result
);
299 /* make sure it doesn't exist */
300 result
= BTSearchRecord(fcb
, &iterator
, kInvalidMRUCacheKey
, NULL
, NULL
, &iterator
);
301 if (result
!= btNotFound
)
304 nextCNID
= volume
->vcbNxtCNID
;
305 if (!isHFSPlus
&& nextCNID
== 0xFFFFFFFF)
308 //--- build thread record for new CNode
309 if (isHFSPlus
|| nodeType
== kCatalogFolderNode
)
311 CatalogRecord threadData
; // 520 bytes
314 btRecord
.bufferAddress
= &threadData
;
315 btRecord
.itemSize
= threadSize
;
316 btRecord
.itemCount
= 1;
317 InitCatalogThreadRecord(volume
, nodeType
, nodeKey
, &threadData
, &threadSize
);
319 BuildCatalogKey(nextCNID
, NULL
, isHFSPlus
, (CatalogKey
*) &threadIter
.key
);
320 result
= BTInsertRecord(fcb
, &threadIter
, &btRecord
, threadSize
);
321 if (result
== btExists
&& isHFSPlus
)
324 * Allow CNIDs on HFS Plus volumes to wrap around
327 if (nextCNID
< kHFSFirstUserCatalogNodeID
)
329 volume
->vcbAtrb
|= kHFSCatalogNodeIDsReusedMask
;
330 volume
->vcbFlags
|= 0xFF00;
331 nextCNID
= kHFSFirstUserCatalogNodeID
;
335 ReturnIfError(result
);
338 //--- initialize catalog data record (for folder or file)
339 btRecord
.bufferAddress
= &nodeData
;
340 btRecord
.itemSize
= nodeDataSize
;
341 btRecord
.itemCount
= 1;
342 InitCatalogRecord(volume
, nodeType
, textEncoding
, &nodeData
, &nodeDataSize
, nextCNID
);
344 //--- add new folder/file record to catalog BTree
345 result
= BTInsertRecord(fcb
, &iterator
, &btRecord
, nodeDataSize
);
348 if (result
== btExists
)
351 if (isHFSPlus
|| nodeType
== kCatalogFolderNode
)
352 (void) BTDeleteRecord(fcb
, &threadIter
);
358 * Return the CNID actually used. Update the volume's next ID.
360 *catalogNodeID
= nextCNID
;
361 if (++nextCNID
< kHFSFirstUserCatalogNodeID
)
363 volume
->vcbAtrb
|= kHFSCatalogNodeIDsReusedMask
;
364 volume
->vcbFlags
|= 0xFF00;
365 nextCNID
= kHFSFirstUserCatalogNodeID
;
367 volume
->vcbNxtCNID
= nextCNID
;
369 //--- update counters...
371 result
= UpdateFolderCount( volume
, parentsParentID
, parentNamePtr
, nodeData
.recordType
, kNoHint
, +1);
372 ReturnIfError(result
); /* XXX what about cleanup ??? */
374 AdjustVolumeCounts(volume
, nodeData
.recordType
, +1);
376 result
= FlushCatalog(volume
);
380 } // end CreateCatalogNode
384 * initialize catalog data record (for folder or file)
386 void InitCatalogRecord(ExtendedVCB
*volume
, UInt32 nodeType
, UInt32 textEncoding
, CatalogRecord
*record
, UInt32
*recordSize
, HFSCatalogNodeID nodeID
)
390 ClearMemory(record
, sizeof(CatalogRecord
)); // first clear the record
392 if (volume
->vcbSigWord
== kHFSPlusSigWord
)
394 timeStamp
= GetTimeUTC(); // get current date/time (universal)
396 UpdateVolumeEncodings(volume
, textEncoding
);
398 if (nodeType
== kCatalogFolderNode
)
400 record
->recordType
= kHFSPlusFolderRecord
;
401 record
->hfsPlusFolder
.folderID
= nodeID
;
402 record
->hfsPlusFolder
.createDate
= timeStamp
;
403 record
->hfsPlusFolder
.contentModDate
= timeStamp
;
404 record
->hfsPlusFolder
.accessDate
= timeStamp
;
405 record
->hfsPlusFolder
.attributeModDate
= timeStamp
;
406 record
->hfsPlusFolder
.textEncoding
= textEncoding
;
407 *recordSize
= sizeof(HFSPlusCatalogFolder
);
408 // threadType = kHFSPlusFolderThreadRecord;
410 else if (nodeType
== kCatalogFileNode
)
412 record
->recordType
= kHFSPlusFileRecord
;
413 record
->hfsPlusFile
.fileID
= nodeID
;
414 record
->hfsPlusFile
.createDate
= timeStamp
;
415 record
->hfsPlusFile
.contentModDate
= timeStamp
;
416 record
->hfsPlusFile
.accessDate
= timeStamp
;
417 record
->hfsPlusFile
.attributeModDate
= timeStamp
;
418 record
->hfsPlusFile
.flags
|= kHFSThreadExistsMask
;
419 record
->hfsPlusFile
.textEncoding
= textEncoding
;
420 *recordSize
= sizeof(HFSPlusCatalogFile
);
421 // threadType = kHFSPlusFileThreadRecord;
424 else /* standard hfs */
426 timeStamp
= GetTimeLocal(true); // get current local date/time
428 if (nodeType
== kCatalogFolderNode
)
430 record
->recordType
= kHFSFolderRecord
;
431 record
->hfsFolder
.folderID
= nodeID
;
432 record
->hfsFolder
.createDate
= timeStamp
;
433 record
->hfsFolder
.modifyDate
= timeStamp
;
434 *recordSize
= sizeof(HFSCatalogFolder
);
435 // threadType = kHFSFolderThreadRecord;
437 else if (nodeType
== kCatalogFileNode
)
439 record
->recordType
= kHFSFileRecord
;
440 record
->hfsFile
.fileID
= nodeID
;
441 record
->hfsFile
.createDate
= timeStamp
;
442 record
->hfsFile
.modifyDate
= timeStamp
;
443 *recordSize
= sizeof(HFSCatalogFile
);
449 void InitCatalogThreadRecord(ExtendedVCB
*volume
, UInt32 nodeType
, CatalogKey
*nodeKey
,
450 CatalogRecord
*record
, UInt32
*threadSize
)
452 ClearMemory(record
, sizeof(CatalogRecord
) ); // first clear the record
454 if (volume
->vcbSigWord
== kHFSPlusSigWord
)
456 if (nodeType
== kCatalogFolderNode
)
457 record
->recordType
= kHFSPlusFolderThreadRecord
;
459 record
->recordType
= kHFSPlusFileThreadRecord
;
460 record
->hfsPlusThread
.parentID
= nodeKey
->hfsPlus
.parentID
;
461 *threadSize
= sizeof(record
->hfsPlusThread
);
463 // HFS Plus has varaible sized threads so adjust to actual length
464 *threadSize
-= ( sizeof(record
->hfsPlusThread
.nodeName
.unicode
) -
465 (nodeKey
->hfsPlus
.nodeName
.length
* sizeof(UniChar
)) );
466 BlockMoveData(&nodeKey
->hfsPlus
.nodeName
, &record
->hfsPlusThread
.nodeName
,
467 sizeof(UniChar
) * (nodeKey
->hfsPlus
.nodeName
.length
+ 1));
471 if (nodeType
== kCatalogFolderNode
)
472 record
->recordType
= kHFSFolderThreadRecord
;
474 record
->recordType
= kHFSFileThreadRecord
;
475 record
->hfsThread
.parentID
= nodeKey
->hfs
.parentID
;
476 *threadSize
= sizeof(record
->hfsThread
);
477 BlockMoveData(&nodeKey
->hfs
.nodeName
, &record
->hfsThread
.nodeName
,
478 nodeKey
->hfs
.nodeName
[0] + 1);
483 //_________________________________________________________________________________
484 // Routine: DeleteCatalogNode
486 // Function: Deletes an existing folder or file CNode. The thread record
487 // is also deleted for directories and files that have thread
490 // The valence for a folder must be zero before it can be deleted.
491 // The rootfolder cannot be deleted.
493 //_________________________________________________________________________________
496 DeleteCatalogNode(ExtendedVCB
*volume
, HFSCatalogNodeID parentID
, ConstUTF8Param name
, UInt32 hint
)
498 CatalogKey key
; // 518 bytes
499 CatalogRecord data
; // 520 bytes
501 HFSCatalogNodeID nodeID
;
502 HFSCatalogNodeID nodeParentID
;
505 Boolean isHFSPlus
= (volume
->vcbSigWord
== kHFSPlusSigWord
);
507 //--- locate subject catalog node
509 result
= BuildCatalogKeyUTF8(volume
, parentID
, name
, kUndefinedStrLen
, &key
, NULL
);
510 ReturnIfError(result
);
512 result
= LocateCatalogNodeByKey(volume
, hint
, &key
, &data
, &nodeHint
);
514 // if we did not find it by name, then look for an embedded file ID in a mangled name
515 if ( (result
== cmNotFound
) && isHFSPlus
)
516 result
= LocateCatalogNodeByMangledName(volume
, parentID
, name
, kUndefinedStrLen
, &key
, &data
, &nodeHint
);
519 * In Mac OS X there can also be HFS filenames that
520 * could not be encoded using the default encoding.
522 if (result
== cmNotFound
&& !isHFSPlus
&& VCBTOHFS(volume
)->hfs_encoding
!= 0) {
525 utf8_to_mac_roman(strlen(name
), name
, hfsName
);
526 result
= LocateCatalogNode(volume
, parentID
, (CatalogName
*)hfsName
,
527 0, &key
, &data
, &nodeHint
);
529 ReturnIfError(result
);
531 nodeParentID
= isHFSPlus
? key
.hfsPlus
.parentID
: key
.hfs
.parentID
; // establish real parent cnid
532 nodeType
= data
.recordType
; // establish cnode type
537 case kHFSFolderRecord
:
538 if (data
.hfsFolder
.valence
!= 0) // is it empty?
541 nodeID
= data
.hfsFolder
.folderID
;
544 case kHFSPlusFolderRecord
:
545 if (data
.hfsPlusFolder
.valence
!= 0) // is it empty?
548 nodeID
= data
.hfsPlusFolder
.folderID
;
552 if (data
.hfsFile
.flags
& kHFSThreadExistsMask
)
553 nodeID
= data
.hfsFile
.fileID
;
556 case kHFSPlusFileRecord
:
557 nodeID
= data
.hfsPlusFile
.fileID
; // note: HFS Plus files always have a thread
565 if (nodeID
== fsRtDirID
) // is this the root folder?
566 return cmRootCN
; // sorry, you can't delete the root!
568 TrashCatalogIterator(volume
, nodeParentID
); // invalidate any iterators for this parentID
569 InvalidateCatalogNodeCache(volume
, nodeParentID
); // and invalidate node cache
571 //--- delete catalog records for CNode and file threads if they exist
573 result
= DeleteBTreeRecord(volume
->catalogRefNum
, &key
);
574 ReturnIfError(result
);
578 CatalogKey threadKey
; // 518 bytes
580 BuildCatalogKey(nodeID
, NULL
, isHFSPlus
, &threadKey
);
582 (void) DeleteBTreeRecord(volume
->catalogRefNum
, &threadKey
); // ignore errors for thread deletes
585 //--- update counters...
587 result
= UpdateFolderCount(volume
, nodeParentID
, NULL
, nodeType
, kNoHint
, -1);
588 ReturnIfError(result
);
590 AdjustVolumeCounts(volume
, nodeType
, -1); // all done with this file or folder
592 result
= FlushCatalog(volume
);
596 } // end DeleteCatalogNode
599 //_________________________________________________________________________________
600 // Routine: GetCatalogNode
602 // Function: Locates an existing folder or file CNode and pointer to the CNode data record.
604 //_________________________________________________________________________________
607 GetCatalogNode( ExtendedVCB
*volume
, HFSCatalogNodeID parentID
, ConstUTF8Param name
, UInt32 nameLen
, UInt32 hint
,
608 CatalogNodeData
*nodeData
, UInt32
*newHint
)
611 CatalogRecord
*record
;
612 BTreeIterator searchIterator
;
613 FSBufferDescriptor btRecord
;
615 UInt32 heuristicHint
;
618 OSErr result
= noErr
;
620 Boolean isHFSPlus
= (volume
->vcbSigWord
== kHFSPlusSigWord
);
623 btRecord
.bufferAddress
= nodeData
;
624 btRecord
.itemSize
= sizeof(CatalogNodeData
);
626 btRecord
.bufferAddress
= &nodeData
->cnd_extra
;
627 btRecord
.itemSize
= sizeof(HFSCatalogFile
);
630 btRecord
.itemCount
= 1;
631 record
= (CatalogRecord
*) btRecord
.bufferAddress
;
632 key
= (CatalogKey
*) &searchIterator
.key
;
634 if (name
&& nameLen
== kUndefinedStrLen
)
635 nameLen
= strlen(name
);
637 result
= BuildCatalogKeyUTF8(volume
, parentID
, name
, nameLen
, key
, NULL
);
638 ReturnIfError(result
);
640 fcb
= GetFileControlBlock(volume
->catalogRefNum
);
641 searchIterator
.hint
.nodeNum
= *newHint
;
642 searchIterator
.hint
.index
= 0;
645 * We pass a 2nd hint/guess into BTSearchRecord. The heuristicHint
646 * is a mapping of dirID and nodeNumber, in hopes that the current
647 * search will be in the same node as the last search with the same
650 if (name
!= NULL
&& GetMRUCacheBlock(parentID
, volume
->hintCachePtr
, (Ptr
*)&cachedHint
) == 0)
651 heuristicHint
= *cachedHint
;
653 heuristicHint
= kInvalidMRUCacheKey
;
655 result
= BTSearchRecord(fcb
, &searchIterator
, heuristicHint
, &btRecord
, &dataSize
, &searchIterator
);
656 if (result
== btNotFound
)
659 if (name
!= NULL
&& result
== noErr
)
660 InsertMRUCacheBlock(volume
->hintCachePtr
, parentID
, (Ptr
) &(searchIterator
.hint
.nodeNum
));
662 if (result
== noErr
) {
663 CatalogName
*nodeName
= NULL
;
664 HFSCatalogNodeID threadParentID
;
666 /* if we got a thread record, then go look up real record */
667 switch (record
->recordType
) {
669 case kHFSFileThreadRecord
:
670 case kHFSFolderThreadRecord
:
671 threadParentID
= record
->hfsThread
.parentID
;
672 nodeName
= (CatalogName
*) &record
->hfsThread
.nodeName
;
675 case kHFSPlusFileThreadRecord
:
676 case kHFSPlusFolderThreadRecord
:
677 threadParentID
= record
->hfsPlusThread
.parentID
;
678 nodeName
= (CatalogName
*) &record
->hfsPlusThread
.nodeName
;
683 *newHint
= searchIterator
.hint
.nodeNum
;
686 if (threadParentID
) {
687 BuildCatalogKey(threadParentID
, nodeName
, isHFSPlus
, key
);
688 searchIterator
.hint
.nodeNum
= kNoHint
;
689 searchIterator
.hint
.index
= 0;
691 result
= BTSearchRecord(fcb
, &searchIterator
, kInvalidMRUCacheKey
, &btRecord
, &dataSize
, &searchIterator
);
692 if (result
== btNotFound
)
695 *newHint
= searchIterator
.hint
.nodeNum
;
700 * If we did not find it by name, then look for an embedded
701 * file ID in a mangled name.
703 if ( result
== cmNotFound
&& isHFSPlus
)
704 result
= LocateCatalogNodeByMangledName(volume
, parentID
, name
, nameLen
,
705 key
, record
, newHint
);
708 * In Mac OS X there can also be HFS filenames that
709 * could not be encoded using the default encoding.
710 * In which case they were encoded as MacRoman.
712 if (result
== cmNotFound
&& !isHFSPlus
&& VCBTOHFS(volume
)->hfs_encoding
!= 0) {
715 utf8_to_mac_roman(nameLen
, name
, hfsName
);
716 result
= LocateCatalogNode(volume
, parentID
, (CatalogName
*)hfsName
,
717 0, key
, record
, newHint
);
719 ReturnIfError(result
);
721 nodeData
->cnm_parID
= isHFSPlus
? key
->hfsPlus
.parentID
: key
->hfs
.parentID
;
723 if (nodeData
->cnm_flags
& kCatNameNoCopyName
) {
725 if (record
->recordType
== kHFSFolderRecord
|| record
->recordType
== kHFSFileRecord
)
726 CopyCatalogNodeData(volume
, record
, nodeData
);
732 nodeData
->cnm_nameptr
= nodeData
->cnm_namespace
;
734 result
= utf8_encodestr(key
->hfsPlus
.nodeName
.unicode
,
735 key
->hfsPlus
.nodeName
.length
* sizeof(UniChar
),
736 nodeData
->cnm_namespace
, (size_t *)&utf8len
,
737 MAXHFSVNODELEN
+ 1, ':', 0);
739 /* Need to allocate buffer large enough */
740 if (result
== ENAMETOOLONG
) {
741 utf8len
= utf8_encodelen(key
->hfsPlus
.nodeName
.unicode
,
742 key
->hfsPlus
.nodeName
.length
* sizeof(UniChar
),
744 nodeData
->cnm_nameptr
= NewPtr(utf8len
+ 1);
745 nodeData
->cnm_flags
|= kCatNameIsAllocated
;
746 result
= utf8_encodestr(key
->hfsPlus
.nodeName
.unicode
,
747 key
->hfsPlus
.nodeName
.length
* sizeof(UniChar
),
748 nodeData
->cnm_nameptr
, (size_t *)&utf8len
,
749 utf8len
+ 1, ':', 0);
752 else { // classic HFS
754 /* convert data to HFS Plus format */
755 if (record
->recordType
== kHFSFolderRecord
|| record
->recordType
== kHFSFileRecord
) {
756 CopyCatalogNodeData(volume
, record
, nodeData
);
757 result
= hfs_to_utf8(volume
, key
->hfs
.nodeName
, MAXHFSVNODELEN
+ 1,
758 &utf8len
, nodeData
->cnm_namespace
);
759 /* Need to allocate buffer large enough */
760 if (result
== ENAMETOOLONG
) {
761 nodeData
->cnm_nameptr
= NewPtr(utf8len
+ 1);
762 nodeData
->cnm_flags
|= kCatNameIsAllocated
;
763 result
= hfs_to_utf8(volume
, key
->hfs
.nodeName
, utf8len
+ 1,
764 &utf8len
, nodeData
->cnm_nameptr
);
767 * When an HFS name cannot be encoded with the current
768 * volume encoding we use MacRoman as a fallback.
770 result
= mac_roman_to_utf8(key
->hfs
.nodeName
, MAXHFSVNODELEN
+ 1,
771 &utf8len
, nodeData
->cnm_namespace
);
777 nodeData
->cnm_length
= utf8len
;
778 if (result
&& (nodeData
->cnm_flags
& kCatNameIsAllocated
))
780 DisposePtr(nodeData
->cnm_nameptr
);
781 nodeData
->cnm_flags
&= ~kCatNameIsAllocated
;
782 nodeData
->cnm_nameptr
= 0; /* Just to be clean */
788 if ( nodeData
->cnd_nodeID
> volume
->vcbNxtCNID
|| nodeData
->cnd_nodeID
== 0)
789 DebugStr("\pGetCatalogNode bad file ID found!");
794 } // end GetCatalogNode
798 GetDirEntrySize(BTreeIterator
*bip
, ExtendedVCB
* vol
)
803 UInt8 name
[kdirentMaxNameBytes
+ 1];
806 ckp
= (CatalogKey
*) &bip
->key
;
808 if (vol
->vcbSigWord
== kHFSPlusSigWord
) {
809 cnp
= (CatalogName
*) &ckp
->hfsPlus
.nodeName
;
810 utf8chars
= utf8_encodelen(cnp
->ustr
.unicode
,
811 cnp
->ustr
.length
* sizeof(UniChar
), ':', 0);
812 if (utf8chars
> kdirentMaxNameBytes
)
813 utf8chars
= kdirentMaxNameBytes
;
815 cnp
= (CatalogName
*) ckp
->hfs
.nodeName
;
816 result
= hfs_to_utf8(vol
, cnp
->pstr
, kdirentMaxNameBytes
+ 1,
820 * When an HFS name cannot be encoded with the current
821 * volume encoding we use MacRoman as a fallback.
823 result
= mac_roman_to_utf8(cnp
->pstr
, MAXHFSVNODELEN
+ 1,
828 return DIRENTRY_SIZE(utf8chars
);
831 * NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE
833 * This is assuming maxinum size of a name is 255 (kdirentMaxNameBytes), which is incorrect.
834 * Any caller of this has to make sure names > 255 are mangled!!!!!!!!
838 PositionIterator(CatalogIterator
*cip
, UInt32 offset
, BTreeIterator
*bip
, UInt16
*op
)
840 #define CAT_START_OFFSET (2 * sizeof(struct hfsdotentry))
845 /* are we past the end of a directory? */
846 if (cip
->folderID
!= cip
->parentID
)
850 fcb
= GetFileControlBlock(vol
->catalogRefNum
);
852 /* make a btree iterator from catalog iterator */
853 UpdateBtreeIterator(cip
, bip
);
855 if (cip
->currentOffset
== offset
) {
856 *op
= kBTreeCurrentRecord
;
858 } else if (cip
->nextOffset
== offset
) {
859 *op
= kBTreeNextRecord
;
861 } else { /* start from beginning */
862 UInt32 heuristicHint
;
865 *op
= kBTreeNextRecord
;
868 * We pass a 2nd hint/guess into BTSearchRecord. The heuristicHint
869 * is a mapping of dirID and nodeNumber, in hopes that the current
870 * search will be in the same node as the last search with the same
873 result
= GetMRUCacheBlock( cip
->folderID
, vol
->hintCachePtr
, (Ptr
*)&cachedHint
);
874 heuristicHint
= (result
== noErr
) ? *cachedHint
: kInvalidMRUCacheKey
;
876 /* Position iterator at the folder's thread record */
877 result
= BTSearchRecord(fcb
, bip
, heuristicHint
, NULL
, NULL
, bip
);
881 InsertMRUCacheBlock( vol
->hintCachePtr
, cip
->folderID
, (Ptr
) &bip
->hint
.nodeNum
);
883 /* find offset (note: n^2 / 2) */
884 if (offset
> CAT_START_OFFSET
) {
885 HFSCatalogNodeID pid
, *idp
;
886 UInt32 curOffset
, nextOffset
;
888 /* get first record (ie offset 24) */
889 result
= BTIterateRecord( fcb
, kBTreeNextRecord
, bip
, NULL
, NULL
);
893 if (vol
->vcbSigWord
== kHFSPlusSigWord
)
894 idp
= &((CatalogKey
*) &bip
->key
)->hfsPlus
.parentID
;
896 idp
= &((CatalogKey
*) &bip
->key
)->hfs
.parentID
;
900 curOffset
= CAT_START_OFFSET
;
901 nextOffset
= CAT_START_OFFSET
+ GetDirEntrySize(bip
, vol
);
903 while (nextOffset
< offset
) {
904 result
= BTIterateRecord( fcb
, kBTreeNextRecord
, bip
, NULL
, NULL
);
908 /* check for parent change */
910 result
= cmNotFound
; /* offset past end of directory */
914 curOffset
= nextOffset
;
915 nextOffset
+= GetDirEntrySize(bip
, vol
);
918 if (nextOffset
!= offset
) {
923 UpdateCatalogIterator(bip
, cip
);
924 cip
->currentOffset
= curOffset
;
925 cip
->nextOffset
= nextOffset
;
930 if (result
== btNotFound
)
935 } /* end PositionIterator */
938 //_________________________________________________________________________________
939 // Routine: GetCatalogOffspring
941 // Function: Gets an offspring record from a specified folder. The folder
942 // is identified by it's folderID. The desired offspring CNode is
943 // indicated by the value of the offspring index (1 = 1st offspring
944 // CNode, 2 = 2nd offspring CNode, etc.).
946 //_________________________________________________________________________________
949 GetCatalogOffspring(ExtendedVCB
*volume
, HFSCatalogNodeID folderID
, UInt16 index
,
950 CatalogNodeData
*nodeData
,
951 HFSCatalogNodeID
*nodeID
, SInt16
*nodeType
)
953 CatalogIterator
* catalogIterator
;
961 * return cmNotFound for index 32767, to prevent overflowing
962 * the index into negative numbers.
964 if ( index
== 32767 )
967 // get best catalog iterator...
968 catalogIterator
= oGetCatalogIterator(volume
, folderID
, index
);
970 result
= IterateCatalogNode(volume
, catalogIterator
, index
,
971 nodeData
, nodeID
, nodeType
);
973 (void) ReleaseCatalogIterator(catalogIterator
);
977 } // end GetCatalogOffspring
980 //_________________________________________________________________________________
981 // Routine: IterateCatalogNode
983 // Function: Gets an offspring record from a specified folder. The folder
984 // is identified by it's folderID. The desired offspring CNode is
985 // indicated by the value of the offspring index (1 = 1st offspring
986 // CNode, 2 = 2nd offspring CNode, etc.).
988 //_________________________________________________________________________________
991 IterateCatalogNode( ExtendedVCB
*volume
, CatalogIterator
*catalogIterator
, UInt16 index
,
992 CatalogNodeData
*nodeData
, HFSCatalogNodeID
*nodeID
,
995 HFSCatalogNodeID offspringParentID
;
996 CatalogKey
* offspringKey
;
997 CatalogName
* offspringName
;
998 BTreeIterator btreeIterator
;
999 FSBufferDescriptor btRecord
;
1000 CatalogRecord
* record
;
1001 UInt8 databuf
[32]; /* space for partial record */
1003 SInt16 selectionIndex
;
1011 isHFSPlus
= (volume
->vcbSigWord
== kHFSPlusSigWord
);
1012 fcb
= GetFileControlBlock(volume
->catalogRefNum
);
1014 // make a btree iterator from catalog iterator
1015 UpdateBtreeIterator(catalogIterator
, &btreeIterator
);
1017 /* if client doesn't want data (ie readdir), just get type and id */
1018 if (nodeData
== NULL
) {
1019 /* data buf has space to cover all type/id offsets */
1020 btRecord
.bufferAddress
= databuf
;
1021 btRecord
.itemSize
= sizeof(databuf
);
1022 } else if (isHFSPlus
) {
1023 btRecord
.bufferAddress
= nodeData
;
1024 btRecord
.itemSize
= sizeof(CatalogNodeData
);
1026 btRecord
.bufferAddress
= &nodeData
->cnd_extra
;
1027 btRecord
.itemSize
= sizeof(HFSCatalogFile
);
1029 btRecord
.itemCount
= 1;
1031 //--- if neccessary position the iterator at the thread record for the specified folder
1033 if ( catalogIterator
->currentIndex
== 0 ) // is this a new iterator?
1035 UInt32 heuristicHint
;
1038 // We pass a 2nd hint/guess into BTSearchRecord. The heuristicHint is a mapping of
1039 // dirID and nodeNumber, in hopes that the current search will be in the same node
1040 // as the last search with the same parentID.
1041 result
= GetMRUCacheBlock( catalogIterator
->folderID
, volume
->hintCachePtr
, (Ptr
*)&cachedHint
);
1042 heuristicHint
= (result
== noErr
) ? *cachedHint
: kInvalidMRUCacheKey
;
1044 result
= BTSearchRecord( fcb
, &btreeIterator
, heuristicHint
, &btRecord
, &tempSize
, &btreeIterator
);
1045 ExitOnError(result
);
1047 UpdateCatalogIterator(&btreeIterator
, catalogIterator
); // update btree hint and key
1049 InsertMRUCacheBlock( volume
->hintCachePtr
, catalogIterator
->folderID
, (Ptr
) &btreeIterator
.hint
.nodeNum
);
1052 //--- get offspring record (relative to catalogIterator's position)
1054 selectionIndex
= index
- catalogIterator
->currentIndex
;
1056 // now we have to map index into next/prev operations...
1057 if (selectionIndex
== 1)
1059 operation
= kBTreeNextRecord
;
1061 else if (selectionIndex
== -1)
1063 operation
= kBTreePrevRecord
;
1065 else if (selectionIndex
== 0)
1067 operation
= kBTreeCurrentRecord
;
1069 else if (selectionIndex
> 1)
1073 for (i
= 1; i
< selectionIndex
; ++i
)
1075 result
= BTIterateRecord( fcb
, kBTreeNextRecord
, &btreeIterator
, &btRecord
, &tempSize
);
1076 ExitOnError(result
);
1078 operation
= kBTreeNextRecord
;
1080 else // (selectionIndex < -1)
1084 for (i
= -1; i
> selectionIndex
; --i
)
1086 result
= BTIterateRecord( fcb
, kBTreePrevRecord
, &btreeIterator
, &btRecord
, &tempSize
);
1087 ExitOnError(result
);
1089 operation
= kBTreePrevRecord
;
1092 result
= BTIterateRecord( fcb
, operation
, &btreeIterator
, &btRecord
, &tempSize
);
1093 ExitOnError(result
);
1095 offspringKey
= (CatalogKey
*) &btreeIterator
.key
;
1099 offspringParentID
= offspringKey
->hfsPlus
.parentID
;
1100 offspringName
= (CatalogName
*) &offspringKey
->hfsPlus
.nodeName
;
1104 offspringParentID
= offspringKey
->hfs
.parentID
;
1105 offspringName
= (CatalogName
*) offspringKey
->hfs
.nodeName
;
1108 if (offspringParentID
!= catalogIterator
->folderID
) // different parent?
1110 AgeCatalogIterator(catalogIterator
); // we reached the end, so don't hog the cache!
1112 result
= cmNotFound
; // must be done with this folder
1116 UpdateCatalogIterator(&btreeIterator
, catalogIterator
); // update btree hint and key
1117 catalogIterator
->currentIndex
= index
; // update the offspring index marker
1119 record
= (CatalogRecord
*) btRecord
.bufferAddress
;
1121 if (nodeData
== NULL
) { /* Just copy the id and type...not name */
1124 *nodeType
= record
->recordType
;
1125 *nodeID
= record
->hfsPlusFolder
.folderID
;
1130 if (record
->recordType
== kHFSFileRecord
) {
1131 *nodeType
= kCatalogFileNode
;
1132 *nodeID
= record
->hfsFile
.fileID
;
1133 } else if (record
->recordType
== kHFSFolderRecord
) {
1134 *nodeType
= kCatalogFolderNode
;
1135 *nodeID
= record
->hfsFolder
.folderID
;
1137 result
= cmNotFound
;
1140 nodeData
->cnm_parID
= isHFSPlus
? offspringKey
->hfsPlus
.parentID
: offspringKey
->hfs
.parentID
;
1141 nodeData
->cnm_nameptr
= nodeData
->cnm_namespace
;
1144 result
= utf8_encodestr(offspringName
->ustr
.unicode
,
1145 offspringName
->ustr
.length
* sizeof(UniChar
),
1146 nodeData
->cnm_namespace
, (size_t *)&utf8len
,
1147 MAXHFSVNODELEN
+ 1, ':', 0);
1149 /* Need to allocate buffer large enough */
1150 if (result
== ENAMETOOLONG
) {
1151 utf8len
= utf8_encodelen(offspringName
->ustr
.unicode
,
1152 offspringName
->ustr
.length
* sizeof(UniChar
),
1154 nodeData
->cnm_nameptr
= NewPtr(utf8len
+ 1);
1155 nodeData
->cnm_flags
|= kCatNameIsAllocated
;
1156 result
= utf8_encodestr(offspringName
->ustr
.unicode
,
1157 offspringName
->ustr
.length
* sizeof(UniChar
),
1158 nodeData
->cnm_nameptr
, (size_t *)&utf8len
,
1159 utf8len
+ 1, ':', 0);
1164 if (record
->recordType
== kHFSFolderRecord
|| record
->recordType
== kHFSFileRecord
) {
1166 CopyCatalogNodeData(volume
, record
, nodeData
);
1167 result
= hfs_to_utf8(volume
, offspringName
->pstr
, MAXHFSVNODELEN
+ 1, &utf8len
, nodeData
->cnm_namespace
);
1168 if (result
== ENAMETOOLONG
) { /* Need to allocate buffer large enough */
1169 nodeData
->cnm_nameptr
= NewPtr(utf8len
+1);
1170 nodeData
->cnm_flags
|= kCatNameIsAllocated
;
1171 result
= hfs_to_utf8(volume
, offspringName
->pstr
, utf8len
+ 1, &utf8len
, nodeData
->cnm_nameptr
);
1172 } else if (result
) {
1174 * When an HFS name cannot be encoded with the current
1175 * volume encoding we use MacRoman as a fallback.
1177 result
= mac_roman_to_utf8(offspringName
->pstr
, MAXHFSVNODELEN
+ 1,
1178 &utf8len
, nodeData
->cnm_namespace
);
1182 result
= cmNotFound
;
1191 if ( result
== btNotFound
)
1192 result
= cmNotFound
;
1196 } // end IterateCatalogNode
1199 //_________________________________________________________________________________
1200 // Routine: MoveRenameCatalogNode
1202 // Function: Moves and/or rename an existing folder or file CNode.
1203 // Note that when moving a folder, all decendants (its offspring,
1204 // their offspring, etc.) are also moved.
1206 // Assumes srcHint contains a text encoding that was set by a GetCatalogNode call
1207 //_________________________________________________________________________________
1210 MoveRenameCatalogNode(ExtendedVCB
*volume
, HFSCatalogNodeID srcParentID
, ConstUTF8Param srcName
,
1211 UInt32 srcHint
, HFSCatalogNodeID dstParentID
, ConstUTF8Param dstName
,
1212 UInt32
*newHint
, UInt32 teHint
)
1214 CatalogKey srcKey
; // 518 bytes
1215 CatalogRecord srcRecord
; // 520 bytes
1216 CatalogKey dstKey
; // 518 bytes
1217 CatalogKey dstFolderKey
; // 518 bytes
1218 HFSCatalogNodeID dstFolderParentID
= 0;
1219 UInt32 dstFolderHint
;
1220 CatalogName
*dstFolderNamePtr
= NULL
;
1221 CatalogRecord tmpRecord
; // 520 bytes
1222 HFSCatalogNodeID threadID
;
1223 UInt32 textEncoding
;
1226 Boolean isHFSPlus
= (volume
->vcbSigWord
== kHFSPlusSigWord
);
1227 Boolean isOrigDeleted
= false;
1231 textEncoding
= teHint
;
1232 result
= BuildCatalogKeyUTF8(volume
, srcParentID
, srcName
, kUndefinedStrLen
, &srcKey
, &textEncoding
);
1233 ReturnIfError(result
);
1235 /* XXX can strlen and bcmp handle NULL pointers? */
1237 srcNameLen
= strlen(srcName
);
1238 dstNameLen
= strlen(dstName
);
1240 //--- check if names match
1242 if ((srcNameLen
== dstNameLen
) && (bcmp(srcName
, dstName
, srcNameLen
) == 0))
1247 dstKey
.hfsPlus
.parentID
= dstParentID
; // set parent ID
1250 dstKey
.hfs
.parentID
= dstParentID
; // set parent ID
1253 else /* names are different */
1256 result
= BuildCatalogKeyUTF8(volume
, dstParentID
, dstName
, kUndefinedStrLen
, &dstKey
, &textEncoding
);
1257 ReturnIfError(result
);
1260 //--- make sure source record exists
1262 result
= LocateCatalogNodeByKey(volume
, srcHint
, &srcKey
, &srcRecord
, &srcHint
);
1264 // if we did not find it by name, then look for an embedded file ID in a mangled name
1265 if ( (result
== cmNotFound
) && isHFSPlus
)
1266 result
= LocateCatalogNodeByMangledName(volume
, srcParentID
, srcName
, kUndefinedStrLen
, &srcKey
, &srcRecord
, &srcHint
);
1268 * In Mac OS X there can also be HFS filenames that
1269 * could not be encoded using the default encoding.
1271 if (result
== cmNotFound
&& !isHFSPlus
&& VCBTOHFS(volume
)->hfs_encoding
!= 0) {
1274 utf8_to_mac_roman(strlen(srcName
), srcName
, hfsName
);
1275 result
= LocateCatalogNode(volume
, srcParentID
, (CatalogName
*)hfsName
,
1276 0, &srcKey
, &srcRecord
, &srcHint
);
1278 ReturnIfError(result
);
1280 srcParentID
= (isHFSPlus
? srcKey
.hfsPlus
.parentID
: srcKey
.hfs
.parentID
);
1282 // if we're moving then do some additional preflighting...
1284 if (srcParentID
!= dstParentID
)
1286 //--- make sure destination folder exists
1288 result
= LocateCatalogNode(volume
, dstParentID
, NULL
, kNoHint
, &dstFolderKey
, &tmpRecord
, &dstFolderHint
);
1289 ReturnIfError(result
);
1291 if (tmpRecord
.recordType
== kHFSPlusFolderRecord
)
1293 dstParentID
= tmpRecord
.hfsPlusFolder
.folderID
;
1294 dstFolderParentID
= dstFolderKey
.hfsPlus
.parentID
;
1295 dstFolderNamePtr
= (CatalogName
*) &dstFolderKey
.hfsPlus
.nodeName
;
1297 else if (tmpRecord
.recordType
== kHFSFolderRecord
)
1299 dstParentID
= tmpRecord
.hfsFolder
.folderID
;
1300 dstFolderParentID
= dstFolderKey
.hfs
.parentID
;
1301 dstFolderNamePtr
= (CatalogName
*) &dstFolderKey
.hfs
.nodeName
;
1308 //--- if source is a folder, make sure its a proper move
1310 if (srcRecord
.recordType
== kHFSPlusFolderRecord
|| srcRecord
.recordType
== kHFSFolderRecord
)
1312 HFSCatalogNodeID srcFolderID
;
1313 HFSCatalogNodeID ancestorParentID
;
1314 CatalogKey tempKey
; // 518 bytes
1319 srcFolderID
= srcRecord
.hfsPlusFolder
.folderID
;
1320 ancestorParentID
= dstFolderKey
.hfsPlus
.parentID
;
1324 srcFolderID
= srcRecord
.hfsFolder
.folderID
;
1325 ancestorParentID
= dstFolderKey
.hfs
.parentID
;
1328 if ( srcFolderID
== fsRtDirID
|| // source == root?
1329 srcFolderID
== dstParentID
|| // source == destination?
1330 srcFolderID
== ancestorParentID
) // source == destination's parent?
1335 while (ancestorParentID
> fsRtDirID
) // loop until we reach the root folder
1337 // locate next folder up the tree...
1338 result
= LocateCatalogNode(volume
, ancestorParentID
, NULL
, kNoHint
, &tempKey
, &tmpRecord
, &tempHint
);
1339 ReturnIfError(result
);
1341 ancestorParentID
= isHFSPlus
? tempKey
.hfsPlus
.parentID
: tempKey
.hfs
.parentID
;
1343 if (srcFolderID
== ancestorParentID
) // source = destination ancestor?
1348 TrashCatalogIterator(volume
, dstParentID
); // invalidate any iterators for destination parentID
1350 else /* (srcParentID == dstParentID) */
1354 *newHint
= srcHint
; // they match, so we're all done!
1359 TrashCatalogIterator(volume
, srcParentID
); // invalidate any iterators for source's parentID
1360 InvalidateCatalogNodeCache(volume
, srcParentID
); // invalidate node cache since parent changed
1362 if (isNewName
&& isHFSPlus
)
1364 // update textEncoding hint (works for folders and files)
1365 srcRecord
.hfsPlusFolder
.textEncoding
= textEncoding
;
1367 UpdateVolumeEncodings(volume
, textEncoding
);
1370 //--- insert source CNode record in BTree with new key (a new parent id and/or new name)
1372 result
= InsertBTreeRecord(volume
->catalogRefNum
, &dstKey
, &srcRecord
, GetCatalogRecordSize(&srcRecord
), newHint
);
1374 if (result
== btExists
)
1378 /* XXX what about the case: move id1,foo to id2,FOO ?? */
1379 if (srcParentID
!= dstParentID
|| isNewName
== false)
1382 //--- new CNode name already exists in the same folder, locate the existing one
1383 result
= SearchBTreeRecord(volume
->catalogRefNum
, &dstKey
, srcHint
,
1384 &dstFolderKey
, &tmpRecord
, &dataSize
, newHint
);
1386 if (result
== btNotFound
)
1387 result
= cmNotFound
;
1388 ReturnIfError(result
);
1390 //--- check if its the same CNode (same name but different upper/lower case)
1392 if (srcRecord
.recordType
!= tmpRecord
.recordType
)
1395 switch (srcRecord
.recordType
)
1397 case kHFSPlusFileRecord
: /* HFS Plus records share same cnid location */
1398 case kHFSPlusFolderRecord
:
1399 if (srcRecord
.hfsPlusFolder
.folderID
!= tmpRecord
.hfsPlusFolder
.folderID
)
1403 case kHFSFolderRecord
:
1404 if (srcRecord
.hfsFolder
.folderID
!= tmpRecord
.hfsFolder
.folderID
)
1408 case kHFSFileRecord
:
1409 if (srcRecord
.hfsFile
.fileID
!= tmpRecord
.hfsFile
.fileID
)
1417 //--- same name but different case, so delete old and insert with new name...
1419 result
= DeleteBTreeRecord(volume
->catalogRefNum
, &srcKey
);
1420 ReturnIfError(result
);
1421 isOrigDeleted
= true; // So we dont delete it again down below
1423 result
= InsertBTreeRecord(volume
->catalogRefNum
, &dstKey
, &srcRecord
, dataSize
, newHint
);
1425 ReturnIfError(result
);
1428 // from this point on we need to cleanup (ie delete the new record) if we encounter errors!
1431 //--- update thread record for node (if it exists)
1433 switch (srcRecord
.recordType
)
1435 case kHFSPlusFileRecord
:
1436 case kHFSPlusFolderRecord
:
1437 threadID
= srcRecord
.hfsPlusFolder
.folderID
;
1440 case kHFSFolderRecord
:
1441 threadID
= srcRecord
.hfsFolder
.folderID
;
1444 case kHFSFileRecord
:
1445 if (srcRecord
.hfsFile
.flags
& kHFSThreadExistsMask
)
1447 threadID
= srcRecord
.hfsFile
.fileID
;
1450 /* fall through if no thread... */
1459 CatalogKey threadKey
; // 518 bytes
1460 CatalogRecord threadRecord
; // 520 bytes
1463 result
= LocateCatalogRecord(volume
, threadID
, NULL
, kNoHint
, &threadKey
, &threadRecord
, &threadHint
);
1464 if (result
!= noErr
) goto Exit_Delete
;
1468 if (srcParentID
!= dstParentID
)
1469 threadRecord
.hfsPlusThread
.parentID
= dstParentID
;
1471 CopyCatalogName((CatalogName
*)&dstKey
.hfsPlus
.nodeName
, (CatalogName
*) &threadRecord
.hfsPlusThread
.nodeName
, isHFSPlus
);
1473 threadSize
= sizeof(threadRecord
.hfsPlusThread
);
1474 // HFS Plus has varaible sized threads so adjust to actual length
1475 threadSize
-= ( sizeof(threadRecord
.hfsPlusThread
.nodeName
.unicode
) - (threadRecord
.hfsPlusThread
.nodeName
.length
* sizeof(UniChar
)) );
1479 if (srcParentID
!= dstParentID
)
1480 threadRecord
.hfsThread
.parentID
= dstParentID
;
1482 CopyCatalogName((CatalogName
*)&dstKey
.hfs
.nodeName
,(CatalogName
*) threadRecord
.hfsThread
.nodeName
, isHFSPlus
);
1484 threadSize
= sizeof(threadRecord
.hfsThread
);
1487 result
= DeleteBTreeRecord(volume
->catalogRefNum
, &threadKey
);
1488 if (result
!= noErr
) goto Exit_Delete
;
1490 result
= InsertBTreeRecord(volume
->catalogRefNum
, &threadKey
, &threadRecord
, threadSize
, &threadHint
);
1491 if (result
!= noErr
) goto Exit_Delete
; //XXX exiting with a missing thread!
1494 //--- we successfully added the new node so delete the old source CNode record
1496 if (! isOrigDeleted
) {
1497 result
= DeleteBTreeRecord(volume
->catalogRefNum
, &srcKey
);
1500 // uh oh, we could not delete the original
1501 // so we better get rid of the new node...
1503 (void) DeleteBTreeRecord(volume
->catalogRefNum
, &dstKey
);
1505 //XXX also need to fix up the thread...
1511 if (srcParentID
!= dstParentID
)
1513 result
= UpdateFolderCount(volume
, srcParentID
, NULL
, srcRecord
.recordType
, kNoHint
, -1);
1514 result
= UpdateFolderCount(volume
, dstFolderParentID
, dstFolderNamePtr
, srcRecord
.recordType
, dstFolderHint
, +1);
1517 //--- make sure changes get flushed out
1519 volume
->vcbFlags
|= 0xFF00; // Mark the VCB dirty
1520 volume
->vcbLsMod
= GetTimeUTC(); // update last modified date
1523 (void) FlushCatalog(volume
);
1529 (void) DeleteBTreeRecord(volume
->catalogRefNum
, &dstKey
);
1533 } // end MoveRenameCatalogNode
1536 //_________________________________________________________________________________
1537 // Routine: UpdateCatalogNode
1539 // Function: Marks the Catalog BTree node identified by the given catalog hint
1542 //_________________________________________________________________________________
1546 UpdateCatalogNode(ExtendedVCB
*volume
, HFSCatalogNodeID parentID
, ConstUTF8Param name
,
1547 UInt32 catalogHint
, const CatalogNodeData
*nodeData
)
1550 CatalogRecord
*record
;
1554 CatalogKey catalogKey
; // 518 bytes
1555 CatalogRecord catalogRecord
; // 520 bytes
1556 Boolean isHFSPlus
= volume
->vcbSigWord
== kHFSPlusSigWord
;
1558 /* XXX no reason to have ptrs to local variables... */
1560 record
= &catalogRecord
;
1562 result
= BuildCatalogKeyUTF8(volume
, parentID
, name
, kUndefinedStrLen
, key
, NULL
);
1563 ReturnIfError(result
);
1565 //--- locate subject catalog node
1567 result
= LocateCatalogNodeByKey(volume
, catalogHint
, key
, record
, &hint
);
1569 // if we did not find it by name, then look for an embedded file ID in a mangled name
1570 if ( (result
== cmNotFound
) && isHFSPlus
)
1571 result
= LocateCatalogNodeByMangledName(volume
, parentID
, name
, kUndefinedStrLen
, key
, record
, &hint
);
1574 * In Mac OS X there can also be HFS filenames that
1575 * could not be encoded using the default encoding.
1577 if (result
== cmNotFound
&& !isHFSPlus
&& VCBTOHFS(volume
)->hfs_encoding
!= 0) {
1580 utf8_to_mac_roman(strlen(name
), name
, hfsName
);
1581 result
= LocateCatalogNode(volume
, parentID
, (CatalogName
*)hfsName
,
1582 0, key
, record
, &hint
);
1584 if (result
== btNotFound
)
1585 result
= cmNotFound
;
1587 if (catalogHint
!= hint
)
1588 PRINTIT(("UpdateCatalogNode: catalogHint does not match (in: %ld, out: %ld)\n", catalogHint
, hint
));
1589 ReturnIfError(result
);
1591 // update user modifiable fields in the catalog node record...
1593 switch (record
->recordType
)
1595 case kHFSFolderRecord
:
1598 if (nodeData
->cnd_type
!= kCatalogFolderNode
)
1599 DebugStr("\p UpdateCatalogNode: folder/file mismatch!");
1602 record
->hfsFolder
.createDate
= UTCToLocal(nodeData
->cnd_createDate
);
1603 record
->hfsFolder
.modifyDate
= UTCToLocal(nodeData
->cnd_contentModDate
);
1604 record
->hfsFolder
.backupDate
= UTCToLocal(nodeData
->cnd_backupDate
);
1606 *(DInfo
*) &record
->hfsFolder
.userInfo
= *(DInfo
*) &nodeData
->cnd_finderInfo
;
1607 *(DXInfo
*) &record
->hfsFolder
.finderInfo
= *(DXInfo
*) ((UInt32
)&nodeData
->cnd_finderInfo
+ 16);
1609 recordSize
= sizeof(HFSCatalogFolder
);
1613 case kHFSFileRecord
:
1618 if (nodeData
->cnd_type
!= kCatalogFileNode
)
1619 DebugStr("UpdateCatalogNode: folder/file mismatch!");
1620 if ((nodeData
->cnd_datafork
.totalBlocks
> (0x7FFFFFFF/volume
->blockSize
)) ||
1621 (nodeData
->cnd_rsrcfork
.totalBlocks
> (0x7FFFFFFF/volume
->blockSize
)))
1622 DebugStr("HFS file size is larger than 2Gig");
1625 record
->hfsFile
.flags
= (UInt8
) nodeData
->cnd_flags
;
1626 record
->hfsFile
.createDate
= UTCToLocal(nodeData
->cnd_createDate
);
1627 record
->hfsFile
.modifyDate
= UTCToLocal(nodeData
->cnd_contentModDate
);
1628 record
->hfsFile
.backupDate
= UTCToLocal(nodeData
->cnd_backupDate
);
1630 record
->hfsFile
.dataLogicalSize
= nodeData
->cnd_datafork
.logicalSize
;
1631 record
->hfsFile
.dataPhysicalSize
= nodeData
->cnd_datafork
.totalBlocks
* volume
->blockSize
;
1632 record
->hfsFile
.rsrcLogicalSize
= nodeData
->cnd_rsrcfork
.logicalSize
;
1633 record
->hfsFile
.rsrcPhysicalSize
= nodeData
->cnd_rsrcfork
.totalBlocks
* volume
->blockSize
;
1635 *(FInfo
*) &record
->hfsFile
.userInfo
= *(FInfo
*) &nodeData
->cnd_finderInfo
;
1636 *(FXInfo
*) &record
->hfsFile
.finderInfo
= *(FXInfo
*) ((UInt32
)&nodeData
->cnd_finderInfo
+ 16);
1639 for (i
= 0; i
< kHFSExtentDensity
; ++i
)
1641 record
->hfsFile
.dataExtents
[i
].startBlock
=
1642 (UInt16
) nodeData
->cnd_datafork
.extents
[i
].startBlock
;
1643 record
->hfsFile
.dataExtents
[i
].blockCount
=
1644 (UInt16
) nodeData
->cnd_datafork
.extents
[i
].blockCount
;
1645 record
->hfsFile
.rsrcExtents
[i
].startBlock
=
1646 (UInt16
) nodeData
->cnd_rsrcfork
.extents
[i
].startBlock
;
1647 record
->hfsFile
.rsrcExtents
[i
].blockCount
=
1648 (UInt16
) nodeData
->cnd_rsrcfork
.extents
[i
].blockCount
;
1651 recordSize
= sizeof(HFSCatalogFile
);
1655 case kHFSPlusFolderRecord
:
1657 record
->hfsPlusFolder
.createDate
= nodeData
->cnd_createDate
;
1658 record
->hfsPlusFolder
.contentModDate
= nodeData
->cnd_contentModDate
;
1659 record
->hfsPlusFolder
.backupDate
= nodeData
->cnd_backupDate
;
1660 record
->hfsPlusFolder
.accessDate
= nodeData
->cnd_accessDate
;
1661 record
->hfsPlusFolder
.attributeModDate
= nodeData
->cnd_attributeModDate
;
1662 record
->hfsPlusFolder
.bsdInfo
.ownerID
= nodeData
->cnd_ownerID
;
1663 record
->hfsPlusFolder
.bsdInfo
.groupID
= nodeData
->cnd_groupID
;
1664 record
->hfsPlusFolder
.bsdInfo
.ownerFlags
= nodeData
->cnd_ownerFlags
;
1665 record
->hfsPlusFolder
.bsdInfo
.adminFlags
= nodeData
->cnd_adminFlags
;
1666 record
->hfsPlusFolder
.bsdInfo
.fileMode
= nodeData
->cnd_mode
;
1667 record
->hfsPlusFolder
.textEncoding
= nodeData
->cnd_textEncoding
;
1669 BlockMoveData(&nodeData
->cnd_finderInfo
, &record
->hfsPlusFolder
.userInfo
, 32);
1671 recordSize
= sizeof(HFSPlusCatalogFolder
);
1675 case kHFSPlusFileRecord
:
1677 record
->hfsPlusFile
.flags
= nodeData
->cnd_flags
;
1678 record
->hfsPlusFile
.createDate
= nodeData
->cnd_createDate
;
1679 record
->hfsPlusFile
.contentModDate
= nodeData
->cnd_contentModDate
;
1680 record
->hfsPlusFile
.backupDate
= nodeData
->cnd_backupDate
;
1681 record
->hfsPlusFile
.accessDate
= nodeData
->cnd_accessDate
;
1682 record
->hfsPlusFile
.attributeModDate
= nodeData
->cnd_attributeModDate
;
1683 record
->hfsPlusFile
.bsdInfo
.ownerID
= nodeData
->cnd_ownerID
;
1684 record
->hfsPlusFile
.bsdInfo
.groupID
= nodeData
->cnd_groupID
;
1685 record
->hfsPlusFile
.bsdInfo
.ownerFlags
= nodeData
->cnd_ownerFlags
;
1686 record
->hfsPlusFile
.bsdInfo
.adminFlags
= nodeData
->cnd_adminFlags
;
1687 record
->hfsPlusFile
.bsdInfo
.fileMode
= nodeData
->cnd_mode
;
1688 /* get special value (iNodeNum, linkCount or rawDevice) */
1689 record
->hfsPlusFile
.bsdInfo
.special
.rawDevice
= nodeData
->cnd_rawDevice
;
1690 record
->hfsPlusFile
.textEncoding
= nodeData
->cnd_textEncoding
;
1692 record
->hfsPlusFile
.dataFork
.logicalSize
= nodeData
->cnd_datafork
.logicalSize
;
1693 record
->hfsPlusFile
.dataFork
.totalBlocks
= nodeData
->cnd_datafork
.totalBlocks
;
1694 BlockMoveData(&nodeData
->cnd_datafork
.extents
,
1695 &record
->hfsPlusFile
.dataFork
.extents
, sizeof(HFSPlusExtentRecord
));
1697 record
->hfsPlusFile
.resourceFork
.logicalSize
= nodeData
->cnd_rsrcfork
.logicalSize
;
1698 record
->hfsPlusFile
.resourceFork
.totalBlocks
= nodeData
->cnd_rsrcfork
.totalBlocks
;
1699 BlockMoveData(&nodeData
->cnd_rsrcfork
.extents
,
1700 &record
->hfsPlusFile
.resourceFork
.extents
, sizeof(HFSPlusExtentRecord
));
1702 BlockMoveData(&nodeData
->cnd_finderInfo
, &record
->hfsPlusFile
.userInfo
, 32);
1704 #if HFS_HARDLINKS && DEBUG_BUILD
1705 /* Must swap opaque finder data */
1706 if (SWAP_BE32 (record
->hfsPlusFile
.userInfo
.fdType
) == kHardLinkFileType
&&
1707 SWAP_BE32 (record
->hfsPlusFile
.userInfo
.fdCreator
) == kHardLinkCreator
) {
1708 if (record
->hfsPlusFile
.dataFork
.logicalSize
!= 0)
1709 DebugStr("UpdateCatalogNode: link has data fork!");
1712 recordSize
= sizeof(HFSPlusCatalogFile
);
1720 result
= ReplaceBTreeRecord(volume
->catalogRefNum
, key
, catalogHint
, record
, recordSize
, &hint
);
1722 if ( result
== btNotFound
)
1724 result
= cmNotFound
;
1726 else if ( result
== noErr
)
1728 /* if we're just updating the accessDate then no need to change volume mod date */
1729 if (nodeData
->cnd_contentModDate
> volume
->vcbLsMod
||
1730 (isHFSPlus
&& nodeData
->cnd_attributeModDate
> volume
->vcbLsMod
))
1733 volume
->vcbFlags
|= 0xFF00; // Mark the VCB dirty
1734 volume
->vcbLsMod
= GetTimeUTC(); // update last modified date
1738 result
= FlushCatalog(volume
); // flush the catalog
1745 //_________________________________________________________________________________
1746 // Routine: CreateFileIDRef
1748 // Function: Creates a file thread record for hfs file node
1750 //_________________________________________________________________________________
1753 CreateFileIDRef(ExtendedVCB
*volume
, HFSCatalogNodeID parentID
, ConstUTF8Param name
, UInt32 hint
, HFSCatalogNodeID
*threadID
)
1755 CatalogKey nodeKey
; // 518 bytes
1756 CatalogRecord nodeData
; // 520 bytes
1757 HFSCatalogKey threadKey
;
1758 HFSCatalogThread threadData
;
1762 Boolean isHFSPlus
= (volume
->vcbSigWord
== kHFSPlusSigWord
);
1766 result
= BuildCatalogKeyUTF8(volume
, parentID
, name
, kUndefinedStrLen
, &nodeKey
, NULL
);
1767 ReturnIfError(result
);
1769 //--- locate subject catalog node
1771 result
= LocateCatalogNodeByKey(volume
, hint
, &nodeKey
, &nodeData
, &nodeHint
);
1773 // if we did not find it by name, then look for an embedded file ID in a mangled name
1774 if ( (result
== cmNotFound
) && isHFSPlus
)
1775 result
= LocateCatalogNodeByMangledName(volume
, parentID
, name
, kUndefinedStrLen
, &nodeKey
, &nodeData
, &nodeHint
);
1777 * In Mac OS X there can also be HFS filenames that
1778 * could not be encoded using the default encoding.
1780 if ((result
== cmNotFound
) && !isHFSPlus
&& VCBTOHFS(volume
)->hfs_encoding
!= 0) {
1783 utf8_to_mac_roman(strlen(name
), name
, hfsName
);
1784 result
= LocateCatalogNode(volume
, parentID
, (CatalogName
*)hfsName
,
1785 0, &nodeKey
, &nodeData
, &nodeHint
);
1787 ReturnIfError(result
);
1789 if (nodeData
.recordType
== kHFSPlusFileRecord
)
1791 *threadID
= nodeData
.hfsPlusFile
.fileID
;
1792 return noErr
; // already have one
1795 if (nodeData
.recordType
!= kHFSFileRecord
)
1801 if (nodeData
.hfsFile
.flags
& kHFSThreadExistsMask
)
1803 *threadID
= nodeData
.hfsFile
.fileID
;
1804 return noErr
; // already have one
1807 result
= VolumeWritable( volume
);
1808 if ( result
!= noErr
) return result
;
1811 // need to insert a thread record
1813 BuildCatalogKey(nodeData
.hfsFile
.fileID
, NULL
, false, (CatalogKey
*)&threadKey
);
1815 ClearMemory(&threadData
, sizeof(HFSCatalogThread
));
1816 threadData
.recordType
= kHFSFileThreadRecord
;
1817 threadData
.parentID
= nodeKey
.hfs
.parentID
;
1818 BlockMoveData(&nodeKey
.hfs
.nodeName
, &threadData
.nodeName
, nodeKey
.hfs
.nodeName
[0] + 1);
1820 result
= InsertBTreeRecord(volume
->catalogRefNum
, &threadKey
, &threadData
, sizeof(HFSCatalogThread
), &tempHint
);
1821 if (result
== btExists
) result
= noErr
; //XXX could return cmExists or fidExists
1822 ReturnIfError(result
);
1825 // Finally, set the flag in the file record to say this file has a thread record.
1827 nodeData
.hfsFile
.flags
|= kHFSThreadExistsMask
;
1828 result
= ReplaceBTreeRecord(volume
->catalogRefNum
, &nodeKey
, nodeHint
, &nodeData
, sizeof(HFSCatalogFile
), &nodeHint
);
1830 if (result
== noErr
) {
1831 (void) FlushCatalog(volume
);
1832 *threadID
= nodeData
.hfsFile
.fileID
;
1839 //_________________________________________________________________________________
1840 // Routine: CompareCatalogKeys
1842 // Function: Compares two catalog keys (a search key and a trial key).
1844 // Result: +n search key > trial key
1845 // 0 search key = trial key
1846 // -n search key < trial key
1847 //_________________________________________________________________________________
1850 CompareCatalogKeys(HFSCatalogKey
*searchKey
, HFSCatalogKey
*trialKey
)
1852 HFSCatalogNodeID searchParentID
, trialParentID
;
1855 searchParentID
= searchKey
->parentID
;
1856 trialParentID
= trialKey
->parentID
;
1858 if ( searchParentID
> trialParentID
) // parent dirID is unsigned
1860 else if ( searchParentID
< trialParentID
)
1862 else // parent dirID's are equal, compare names
1864 #if ( ! FORDISKFIRSTAID )
1865 LogStartTime(kTraceRelString
);
1867 result
= FastRelString(searchKey
->nodeName
, trialKey
->nodeName
);
1869 LogEndTime(kTraceRelString
, noErr
);
1871 result
= (SInt32
) RelString_Glue(searchKey
->nodeName
, trialKey
->nodeName
);
1879 //_________________________________________________________________________________
1880 // Routine: CompareExtendedCatalogKeys
1882 // Function: Compares two large catalog keys (a search key and a trial key).
1884 // Result: +n search key > trial key
1885 // 0 search key = trial key
1886 // -n search key < trial key
1887 //_________________________________________________________________________________
1890 CompareExtendedCatalogKeys(HFSPlusCatalogKey
*searchKey
, HFSPlusCatalogKey
*trialKey
)
1893 HFSCatalogNodeID searchParentID
, trialParentID
;
1895 searchParentID
= searchKey
->parentID
;
1896 trialParentID
= trialKey
->parentID
;
1898 if ( searchParentID
> trialParentID
) // parent node IDs are unsigned
1902 else if ( searchParentID
< trialParentID
)
1906 else // parent node ID's are equal, compare names
1908 if ( searchKey
->nodeName
.length
== 0 || trialKey
->nodeName
.length
== 0 )
1909 result
= searchKey
->nodeName
.length
- trialKey
->nodeName
.length
;
1911 result
= FastUnicodeCompare(&searchKey
->nodeName
.unicode
[0], searchKey
->nodeName
.length
,
1912 &trialKey
->nodeName
.unicode
[0], trialKey
->nodeName
.length
);