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