]> git.saurik.com Git - apple/xnu.git/blob - bsd/hfs/hfscommon/Catalog/Catalog.c
xnu-124.7.tar.gz
[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 } else { /* hfs */
801 cnp = (CatalogName*) ckp->hfs.nodeName;
802 result = hfs_to_utf8(vol, cnp->pstr, kdirentMaxNameBytes + 1,
803 &utf8chars, name);
804 /*XXX ignoring error */
805 }
806
807 return DIRENTRY_SIZE(utf8chars);
808 }
809 /*
810 * NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE
811 *
812 * This is assuming maxinum size of a name is 255 (kdirentMaxNameBytes), which is incorrect.
813 * Any caller of this has to make sure names > 255 are mangled!!!!!!!!
814 */
815
816 OSErr
817 PositionIterator(CatalogIterator *cip, UInt32 offset, BTreeIterator *bip, UInt16 *op)
818 {
819 #define CAT_START_OFFSET 0
820 ExtendedVCB * vol;
821 FCB * fcb;
822 OSErr result = 0;
823
824 /* are we past the end of a directory? */
825 if (cip->folderID != cip->parentID)
826 return(cmNotFound);
827
828 vol = cip->volume;
829 fcb = GetFileControlBlock(vol->catalogRefNum);
830
831 /* make a btree iterator from catalog iterator */
832 UpdateBtreeIterator(cip, bip);
833
834 if (cip->currentOffset == offset) {
835 *op = kBTreeCurrentRecord;
836
837 } else if (cip->nextOffset == offset) {
838 *op = kBTreeNextRecord;
839
840 } else { /* start from beginning */
841 UInt32 heuristicHint;
842 UInt32 *cachedHint;
843
844 *op = kBTreeNextRecord;
845
846 /*
847 * We pass a 2nd hint/guess into BTSearchRecord. The heuristicHint
848 * is a mapping of dirID and nodeNumber, in hopes that the current
849 * search will be in the same node as the last search with the same
850 * parentID.
851 */
852 result = GetMRUCacheBlock( cip->folderID, vol->hintCachePtr, (Ptr *)&cachedHint );
853 heuristicHint = (result == noErr) ? *cachedHint : kInvalidMRUCacheKey;
854
855 /* Position iterator at the folder's thread record */
856 result = BTSearchRecord(fcb, bip, heuristicHint, NULL, NULL, bip);
857 if (result)
858 goto exit;
859
860 InsertMRUCacheBlock( vol->hintCachePtr, cip->folderID, (Ptr) &bip->hint.nodeNum );
861
862 /* find offset (note: n^2 / 2) */
863 if (offset > CAT_START_OFFSET) {
864 HFSCatalogNodeID pid, *idp;
865 UInt32 curOffset, nextOffset;
866
867 /* get first record (ie offset 24) */
868 result = BTIterateRecord( fcb, kBTreeNextRecord, bip, NULL, NULL );
869 if (result)
870 goto exit;
871
872 if (vol->vcbSigWord == kHFSPlusSigWord)
873 idp = &((CatalogKey*) &bip->key)->hfsPlus.parentID;
874 else
875 idp = &((CatalogKey*) &bip->key)->hfs.parentID;
876
877 pid = *idp;
878
879 curOffset = CAT_START_OFFSET;
880 nextOffset = GetDirEntrySize(bip, vol);
881
882 while (nextOffset < offset) {
883 result = BTIterateRecord( fcb, kBTreeNextRecord, bip, NULL, NULL );
884 if (result)
885 goto exit;
886
887 /* check for parent change */
888 if (pid != *idp) {
889 result = cmNotFound; /* offset past end of directory */
890 goto exit;
891 }
892
893 curOffset = nextOffset;
894 nextOffset += GetDirEntrySize(bip, vol);
895 };
896
897 if (nextOffset != offset) {
898 result = cmNotFound;
899 goto exit;
900 }
901
902 UpdateCatalogIterator(bip, cip);
903 cip->currentOffset = curOffset;
904 cip->nextOffset = nextOffset;
905 }
906 }
907
908 exit:
909 if (result == btNotFound)
910 result = cmNotFound;
911
912 return result;
913
914 } /* end PositionIterator */
915
916
917 //_________________________________________________________________________________
918 // Routine: GetCatalogOffspring
919 //
920 // Function: Gets an offspring record from a specified folder. The folder
921 // is identified by it's folderID. The desired offspring CNode is
922 // indicated by the value of the offspring index (1 = 1st offspring
923 // CNode, 2 = 2nd offspring CNode, etc.).
924 //
925 //_________________________________________________________________________________
926
927 OSErr
928 GetCatalogOffspring(ExtendedVCB *volume, HFSCatalogNodeID folderID, UInt16 index,
929 CatalogNodeData *nodeData,
930 HFSCatalogNodeID *nodeID, SInt16 *nodeType)
931 {
932 CatalogIterator * catalogIterator;
933 OSErr result;
934
935
936 if ( folderID == 0 )
937 return cmNotFound;
938
939 /*
940 * return cmNotFound for index 32767, to prevent overflowing
941 * the index into negative numbers.
942 */
943 if ( index == 32767 )
944 return cmNotFound;
945
946 // get best catalog iterator...
947 catalogIterator = oGetCatalogIterator(volume, folderID, index);
948
949 result = IterateCatalogNode(volume, catalogIterator, index,
950 nodeData, nodeID, nodeType);
951
952 (void) ReleaseCatalogIterator(catalogIterator);
953
954 return result;
955
956 } // end GetCatalogOffspring
957
958
959 //_________________________________________________________________________________
960 // Routine: IterateCatalogNode
961 //
962 // Function: Gets an offspring record from a specified folder. The folder
963 // is identified by it's folderID. The desired offspring CNode is
964 // indicated by the value of the offspring index (1 = 1st offspring
965 // CNode, 2 = 2nd offspring CNode, etc.).
966 //
967 //_________________________________________________________________________________
968
969 static OSErr
970 IterateCatalogNode( ExtendedVCB *volume, CatalogIterator *catalogIterator, UInt16 index,
971 CatalogNodeData *nodeData, HFSCatalogNodeID *nodeID,
972 SInt16 *nodeType )
973 {
974 HFSCatalogNodeID offspringParentID;
975 CatalogKey * offspringKey;
976 CatalogName * offspringName;
977 BTreeIterator btreeIterator;
978 FSBufferDescriptor btRecord;
979 CatalogRecord * record;
980 UInt8 databuf[32]; /* space for partial record */
981 FCB * fcb;
982 SInt16 selectionIndex;
983 UInt16 tempSize;
984 UInt16 operation;
985 OSErr result;
986 Boolean isHFSPlus;
987 ByteCount utf8len;
988
989
990 isHFSPlus = (volume->vcbSigWord == kHFSPlusSigWord);
991 fcb = GetFileControlBlock(volume->catalogRefNum);
992
993 // make a btree iterator from catalog iterator
994 UpdateBtreeIterator(catalogIterator, &btreeIterator);
995
996 /* if client doesn't want data (ie readdir), just get type and id */
997 if (nodeData == NULL) {
998 /* data buf has space to cover all type/id offsets */
999 btRecord.bufferAddress = databuf;
1000 btRecord.itemSize = sizeof(databuf);
1001 } else if (isHFSPlus) {
1002 btRecord.bufferAddress = nodeData;
1003 btRecord.itemSize = sizeof(CatalogNodeData);
1004 } else {
1005 btRecord.bufferAddress = &nodeData->cnd_extra;
1006 btRecord.itemSize = sizeof(HFSCatalogFile);
1007 }
1008 btRecord.itemCount = 1;
1009
1010 //--- if neccessary position the iterator at the thread record for the specified folder
1011
1012 if ( catalogIterator->currentIndex == 0 ) // is this a new iterator?
1013 {
1014 UInt32 heuristicHint;
1015 UInt32 *cachedHint;
1016
1017 // We pass a 2nd hint/guess into BTSearchRecord. The heuristicHint is a mapping of
1018 // dirID and nodeNumber, in hopes that the current search will be in the same node
1019 // as the last search with the same parentID.
1020 result = GetMRUCacheBlock( catalogIterator->folderID, volume->hintCachePtr, (Ptr *)&cachedHint );
1021 heuristicHint = (result == noErr) ? *cachedHint : kInvalidMRUCacheKey;
1022
1023 result = BTSearchRecord( fcb, &btreeIterator, heuristicHint, &btRecord, &tempSize, &btreeIterator );
1024 ExitOnError(result);
1025
1026 UpdateCatalogIterator(&btreeIterator, catalogIterator); // update btree hint and key
1027
1028 InsertMRUCacheBlock( volume->hintCachePtr, catalogIterator->folderID, (Ptr) &btreeIterator.hint.nodeNum );
1029 }
1030
1031 //--- get offspring record (relative to catalogIterator's position)
1032
1033 selectionIndex = index - catalogIterator->currentIndex;
1034
1035 // now we have to map index into next/prev operations...
1036 if (selectionIndex == 1)
1037 {
1038 operation = kBTreeNextRecord;
1039 }
1040 else if (selectionIndex == -1)
1041 {
1042 operation = kBTreePrevRecord;
1043 }
1044 else if (selectionIndex == 0)
1045 {
1046 operation = kBTreeCurrentRecord;
1047 }
1048 else if (selectionIndex > 1)
1049 {
1050 UInt32 i;
1051
1052 for (i = 1; i < selectionIndex; ++i)
1053 {
1054 result = BTIterateRecord( fcb, kBTreeNextRecord, &btreeIterator, &btRecord, &tempSize );
1055 ExitOnError(result);
1056 }
1057 operation = kBTreeNextRecord;
1058 }
1059 else // (selectionIndex < -1)
1060 {
1061 SInt32 i;
1062
1063 for (i = -1; i > selectionIndex; --i)
1064 {
1065 result = BTIterateRecord( fcb, kBTreePrevRecord, &btreeIterator, &btRecord, &tempSize );
1066 ExitOnError(result);
1067 }
1068 operation = kBTreePrevRecord;
1069 }
1070
1071 result = BTIterateRecord( fcb, operation, &btreeIterator, &btRecord, &tempSize );
1072 ExitOnError(result);
1073
1074 offspringKey = (CatalogKey*) &btreeIterator.key;
1075
1076 if (isHFSPlus)
1077 {
1078 offspringParentID = offspringKey->hfsPlus.parentID;
1079 offspringName = (CatalogName*) &offspringKey->hfsPlus.nodeName;
1080 }
1081 else
1082 {
1083 offspringParentID = offspringKey->hfs.parentID;
1084 offspringName = (CatalogName*) offspringKey->hfs.nodeName;
1085 }
1086
1087 if (offspringParentID != catalogIterator->folderID) // different parent?
1088 {
1089 AgeCatalogIterator(catalogIterator); // we reached the end, so don't hog the cache!
1090
1091 result = cmNotFound; // must be done with this folder
1092 goto ErrorExit;
1093 }
1094
1095 UpdateCatalogIterator(&btreeIterator, catalogIterator); // update btree hint and key
1096 catalogIterator->currentIndex = index; // update the offspring index marker
1097
1098 record = (CatalogRecord *) btRecord.bufferAddress;
1099
1100 if (nodeData == NULL) { /* Just copy the id and type...not name */
1101 if (isHFSPlus)
1102 {
1103 *nodeType = record->recordType;
1104 *nodeID = record->hfsPlusFolder.folderID;
1105 }
1106 else /* hfs name */
1107 {
1108
1109 if (record->recordType == kHFSFileRecord) {
1110 *nodeType = kCatalogFileNode;
1111 *nodeID = record->hfsFile.fileID;
1112 } else if (record->recordType == kHFSFolderRecord) {
1113 *nodeType = kCatalogFolderNode;
1114 *nodeID = record->hfsFolder.folderID;
1115 } else
1116 result = cmNotFound;
1117 }
1118 } else {
1119 nodeData->cnm_parID = isHFSPlus ? offspringKey->hfsPlus.parentID : offspringKey->hfs.parentID;
1120 nodeData->cnm_nameptr = nodeData->cnm_namespace;
1121 if (isHFSPlus)
1122 {
1123 result = utf8_encodestr(offspringName->ustr.unicode,
1124 offspringName->ustr.length * sizeof(UniChar),
1125 nodeData->cnm_namespace, (size_t *)&utf8len,
1126 MAXHFSVNODELEN + 1, ':', 0);
1127
1128 /* Need to allocate buffer large enough */
1129 if (result == ENAMETOOLONG) {
1130 utf8len = utf8_encodelen(offspringName->ustr.unicode,
1131 offspringName->ustr.length * sizeof(UniChar),
1132 ':', 0);
1133 nodeData->cnm_nameptr = NewPtr(utf8len + 1);
1134 nodeData->cnm_flags |= kCatNameIsAllocated;
1135 result = utf8_encodestr(offspringName->ustr.unicode,
1136 offspringName->ustr.length * sizeof(UniChar),
1137 nodeData->cnm_nameptr, (size_t *)&utf8len,
1138 utf8len + 1, ':', 0);
1139 }
1140 }
1141 else /* hfs name */
1142 {
1143 if (record->recordType == kHFSFolderRecord || record->recordType == kHFSFileRecord) {
1144
1145 CopyCatalogNodeData(volume, record, nodeData);
1146 result = hfs_to_utf8(volume, offspringName->pstr, MAXHFSVNODELEN + 1, &utf8len, nodeData->cnm_namespace);
1147 if (result == ENAMETOOLONG) { /* Need to allocate buffer large enough */
1148 nodeData->cnm_nameptr = NewPtr(utf8len+1);
1149 nodeData->cnm_flags |= kCatNameIsAllocated;
1150 result = hfs_to_utf8(volume, offspringName->pstr, utf8len + 1, &utf8len, nodeData->cnm_nameptr);
1151 }
1152
1153 } else {
1154 result = cmNotFound;
1155 }
1156 }
1157 }
1158
1159 return result;
1160
1161 ErrorExit:
1162
1163 if ( result == btNotFound )
1164 result = cmNotFound;
1165
1166 return result;
1167
1168 } // end IterateCatalogNode
1169
1170
1171 //_________________________________________________________________________________
1172 // Routine: MoveRenameCatalogNode
1173 //
1174 // Function: Moves and/or rename an existing folder or file CNode.
1175 // Note that when moving a folder, all decendants (its offspring,
1176 // their offspring, etc.) are also moved.
1177 //
1178 // Assumes srcHint contains a text encoding that was set by a GetCatalogNode call
1179 //_________________________________________________________________________________
1180
1181 OSErr
1182 MoveRenameCatalogNode(ExtendedVCB *volume, HFSCatalogNodeID srcParentID, ConstUTF8Param srcName,
1183 UInt32 srcHint, HFSCatalogNodeID dstParentID, ConstUTF8Param dstName, UInt32 *newHint)
1184 {
1185 CatalogKey srcKey; // 518 bytes
1186 CatalogRecord srcRecord; // 520 bytes
1187 CatalogKey dstKey; // 518 bytes
1188 CatalogKey dstFolderKey; // 518 bytes
1189 HFSCatalogNodeID dstFolderParentID = 0;
1190 UInt32 dstFolderHint;
1191 CatalogName *dstFolderNamePtr = NULL;
1192 CatalogRecord tmpRecord; // 520 bytes
1193 HFSCatalogNodeID threadID;
1194 UInt32 textEncoding;
1195 OSErr result;
1196 Boolean isNewName;
1197 Boolean isHFSPlus = (volume->vcbSigWord == kHFSPlusSigWord);
1198 Boolean isOrigDeleted = false;
1199 short srcNameLen;
1200 short dstNameLen;
1201
1202
1203 result = BuildCatalogKeyUTF8(volume, srcParentID, srcName, kUndefinedStrLen, &srcKey, &textEncoding);
1204 ReturnIfError(result);
1205
1206 /* XXX can strlen and bcmp handle NULL pointers? */
1207
1208 srcNameLen = strlen(srcName);
1209 dstNameLen = strlen(dstName);
1210
1211 //--- check if names match
1212
1213 if ((srcNameLen == dstNameLen) && (bcmp(srcName, dstName, srcNameLen) == 0))
1214 {
1215 isNewName = false;
1216 dstKey = srcKey;
1217 if ( isHFSPlus ) {
1218 dstKey.hfsPlus.parentID = dstParentID; // set parent ID
1219 }
1220 else {
1221 dstKey.hfs.parentID = dstParentID; // set parent ID
1222 }
1223 }
1224 else /* names are different */
1225 {
1226 isNewName = true;
1227 result = BuildCatalogKeyUTF8(volume, dstParentID, dstName, kUndefinedStrLen, &dstKey, &textEncoding);
1228 ReturnIfError(result);
1229 }
1230
1231 //--- make sure source record exists
1232
1233 result = LocateCatalogNodeByKey(volume, srcHint, &srcKey, &srcRecord, &srcHint);
1234
1235 // if we did not find it by name, then look for an embedded file ID in a mangled name
1236 if ( (result == cmNotFound) && isHFSPlus )
1237 result = LocateCatalogNodeByMangledName(volume, srcParentID, srcName, kUndefinedStrLen, &srcKey, &srcRecord, &srcHint);
1238 ReturnIfError(result);
1239
1240 srcParentID = (isHFSPlus ? srcKey.hfsPlus.parentID : srcKey.hfs.parentID);
1241
1242 // if we're moving then do some additional preflighting...
1243
1244 if (srcParentID != dstParentID)
1245 {
1246 //--- make sure destination folder exists
1247
1248 result = LocateCatalogNode(volume, dstParentID, NULL, kNoHint, &dstFolderKey, &tmpRecord, &dstFolderHint);
1249 ReturnIfError(result);
1250
1251 if (tmpRecord.recordType == kHFSPlusFolderRecord)
1252 {
1253 dstParentID = tmpRecord.hfsPlusFolder.folderID;
1254 dstFolderParentID = dstFolderKey.hfsPlus.parentID;
1255 dstFolderNamePtr = (CatalogName*) &dstFolderKey.hfsPlus.nodeName;
1256 }
1257 else if (tmpRecord.recordType == kHFSFolderRecord)
1258 {
1259 dstParentID = tmpRecord.hfsFolder.folderID;
1260 dstFolderParentID = dstFolderKey.hfs.parentID;
1261 dstFolderNamePtr = (CatalogName*) &dstFolderKey.hfs.nodeName;
1262 }
1263 else
1264 {
1265 return badMovErr;
1266 }
1267
1268 //--- if source is a folder, make sure its a proper move
1269
1270 if (srcRecord.recordType == kHFSPlusFolderRecord || srcRecord.recordType == kHFSFolderRecord)
1271 {
1272 HFSCatalogNodeID srcFolderID;
1273 HFSCatalogNodeID ancestorParentID;
1274 CatalogKey tempKey; // 518 bytes
1275 UInt32 tempHint;
1276
1277 if (isHFSPlus)
1278 {
1279 srcFolderID = srcRecord.hfsPlusFolder.folderID;
1280 ancestorParentID = dstFolderKey.hfsPlus.parentID;
1281 }
1282 else
1283 {
1284 srcFolderID = srcRecord.hfsFolder.folderID;
1285 ancestorParentID = dstFolderKey.hfs.parentID;
1286 }
1287
1288 if ( srcFolderID == fsRtDirID || // source == root?
1289 srcFolderID == dstParentID || // source == destination?
1290 srcFolderID == ancestorParentID ) // source == destination's parent?
1291 {
1292 return badMovErr;
1293 }
1294
1295 while (ancestorParentID > fsRtDirID) // loop until we reach the root folder
1296 {
1297 // locate next folder up the tree...
1298 result = LocateCatalogNode(volume, ancestorParentID, NULL, kNoHint, &tempKey, &tmpRecord, &tempHint);
1299 ReturnIfError(result);
1300
1301 ancestorParentID = isHFSPlus ? tempKey.hfsPlus.parentID : tempKey.hfs.parentID;
1302
1303 if (srcFolderID == ancestorParentID) // source = destination ancestor?
1304 return badMovErr;
1305 }
1306 }
1307
1308 TrashCatalogIterator(volume, dstParentID); // invalidate any iterators for destination parentID
1309 }
1310 else /* (srcParentID == dstParentID) */
1311 {
1312 if ( !isNewName )
1313 {
1314 *newHint = srcHint; // they match, so we're all done!
1315 return noErr;
1316 }
1317 }
1318
1319 TrashCatalogIterator(volume, srcParentID); // invalidate any iterators for source's parentID
1320 InvalidateCatalogNodeCache(volume, srcParentID); // invalidate node cache since parent changed
1321
1322 if (isNewName && isHFSPlus)
1323 {
1324 // update textEncoding hint (works for folders and files)
1325 srcRecord.hfsPlusFolder.textEncoding = textEncoding;
1326
1327 UpdateVolumeEncodings(volume, textEncoding);
1328 }
1329
1330 //--- insert source CNode record in BTree with new key (a new parent id and/or new name)
1331
1332 result = InsertBTreeRecord(volume->catalogRefNum, &dstKey, &srcRecord, GetCatalogRecordSize(&srcRecord), newHint);
1333
1334 if (result == btExists)
1335 {
1336 UInt16 dataSize;
1337
1338 /* XXX what about the case: move id1,foo to id2,FOO ?? */
1339 if (srcParentID != dstParentID || isNewName == false)
1340 return cmExists;
1341
1342 //--- new CNode name already exists in the same folder, locate the existing one
1343 result = SearchBTreeRecord(volume->catalogRefNum, &dstKey, srcHint,
1344 &dstFolderKey, &tmpRecord, &dataSize, newHint);
1345
1346 if (result == btNotFound)
1347 result = cmNotFound;
1348 ReturnIfError(result);
1349
1350 //--- check if its the same CNode (same name but different upper/lower case)
1351
1352 if (srcRecord.recordType != tmpRecord.recordType)
1353 return cmExists;
1354
1355 switch (srcRecord.recordType)
1356 {
1357 case kHFSPlusFileRecord: /* HFS Plus records share same cnid location */
1358 case kHFSPlusFolderRecord:
1359 if (srcRecord.hfsPlusFolder.folderID != tmpRecord.hfsPlusFolder.folderID)
1360 return cmExists;
1361 break;
1362
1363 case kHFSFolderRecord:
1364 if (srcRecord.hfsFolder.folderID != tmpRecord.hfsFolder.folderID)
1365 return cmExists;
1366 break;
1367
1368 case kHFSFileRecord:
1369 if (srcRecord.hfsFile.fileID != tmpRecord.hfsFile.fileID)
1370 return cmExists;
1371 break;
1372
1373 default:
1374 return cmExists;
1375 }
1376
1377 //--- same name but different case, so delete old and insert with new name...
1378
1379 result = DeleteBTreeRecord(volume->catalogRefNum, &srcKey);
1380 ReturnIfError(result);
1381 isOrigDeleted = true; // So we dont delete it again down below
1382
1383 result = InsertBTreeRecord(volume->catalogRefNum, &dstKey, &srcRecord, dataSize, newHint);
1384 }
1385 ReturnIfError(result);
1386
1387 //
1388 // from this point on we need to cleanup (ie delete the new record) if we encounter errors!
1389 //
1390
1391 //--- update thread record for node (if it exists)
1392
1393 switch (srcRecord.recordType)
1394 {
1395 case kHFSPlusFileRecord:
1396 case kHFSPlusFolderRecord:
1397 threadID = srcRecord.hfsPlusFolder.folderID;
1398 break;
1399
1400 case kHFSFolderRecord:
1401 threadID = srcRecord.hfsFolder.folderID;
1402 break;
1403
1404 case kHFSFileRecord:
1405 if (srcRecord.hfsFile.flags & kHFSThreadExistsMask)
1406 {
1407 threadID = srcRecord.hfsFile.fileID;
1408 break;
1409 }
1410 /* fall through if no thread... */
1411
1412 default:
1413 threadID = 0;
1414 }
1415
1416 if (threadID)
1417 {
1418 UInt32 threadHint;
1419 CatalogKey threadKey; // 518 bytes
1420 CatalogRecord threadRecord; // 520 bytes
1421 UInt16 threadSize;
1422
1423 result = LocateCatalogRecord(volume, threadID, NULL, kNoHint, &threadKey, &threadRecord, &threadHint);
1424 if (result != noErr) goto Exit_Delete;
1425
1426 if (isHFSPlus)
1427 {
1428 if (srcParentID != dstParentID)
1429 threadRecord.hfsPlusThread.parentID = dstParentID;
1430 if (isNewName)
1431 CopyCatalogName((CatalogName *)&dstKey.hfsPlus.nodeName, (CatalogName *) &threadRecord.hfsPlusThread.nodeName, isHFSPlus);
1432
1433 threadSize = sizeof(threadRecord.hfsPlusThread);
1434 // HFS Plus has varaible sized threads so adjust to actual length
1435 threadSize -= ( sizeof(threadRecord.hfsPlusThread.nodeName.unicode) - (threadRecord.hfsPlusThread.nodeName.length * sizeof(UniChar)) );
1436 }
1437 else
1438 {
1439 if (srcParentID != dstParentID)
1440 threadRecord.hfsThread.parentID = dstParentID;
1441 if (isNewName)
1442 CopyCatalogName((CatalogName *)&dstKey.hfs.nodeName,(CatalogName *) threadRecord.hfsThread.nodeName, isHFSPlus);
1443
1444 threadSize = sizeof(threadRecord.hfsThread);
1445 }
1446
1447 result = DeleteBTreeRecord(volume->catalogRefNum, &threadKey);
1448 if (result != noErr) goto Exit_Delete;
1449
1450 result = InsertBTreeRecord(volume->catalogRefNum, &threadKey, &threadRecord, threadSize, &threadHint);
1451 if (result != noErr) goto Exit_Delete; //XXX exiting with a missing thread!
1452 }
1453
1454 //--- we successfully added the new node so delete the old source CNode record
1455
1456 if (! isOrigDeleted) {
1457 result = DeleteBTreeRecord(volume->catalogRefNum, &srcKey);
1458 if (result)
1459 {
1460 // uh oh, we could not delete the original
1461 // so we better get rid of the new node...
1462
1463 (void) DeleteBTreeRecord(volume->catalogRefNum, &dstKey);
1464
1465 //XXX also need to fix up the thread...
1466
1467 return result;
1468 }
1469 }
1470
1471 if (srcParentID != dstParentID)
1472 {
1473 result = UpdateFolderCount(volume, srcParentID, NULL, srcRecord.recordType, kNoHint, -1);
1474 result = UpdateFolderCount(volume, dstFolderParentID, dstFolderNamePtr, srcRecord.recordType, dstFolderHint, +1);
1475 }
1476
1477 //--- make sure changes get flushed out
1478 VCB_LOCK(volume);
1479 volume->vcbFlags |= 0xFF00; // Mark the VCB dirty
1480 volume->vcbLsMod = GetTimeUTC(); // update last modified date
1481 VCB_UNLOCK(volume);
1482
1483 (void) FlushCatalog(volume);
1484
1485 return result;
1486
1487
1488 Exit_Delete:
1489 (void) DeleteBTreeRecord(volume->catalogRefNum, &dstKey);
1490
1491 return result;
1492
1493 } // end MoveRenameCatalogNode
1494
1495
1496 //_________________________________________________________________________________
1497 // Routine: UpdateCatalogNode
1498 //
1499 // Function: Marks the Catalog BTree node identified by the given catalog hint
1500 // as 'dirty'.
1501 //
1502 //_________________________________________________________________________________
1503
1504
1505 OSErr
1506 UpdateCatalogNode(ExtendedVCB *volume, HFSCatalogNodeID parentID, ConstUTF8Param name,
1507 UInt32 catalogHint, const CatalogNodeData *nodeData)
1508 {
1509 CatalogKey *key;
1510 CatalogRecord *record;
1511 UInt32 hint;
1512 UInt16 recordSize;
1513 OSErr result;
1514 CatalogKey catalogKey; // 518 bytes
1515 CatalogRecord catalogRecord; // 520 bytes
1516 Boolean isHFSPlus = volume->vcbSigWord == kHFSPlusSigWord;
1517
1518 /* XXX no reason to have ptrs to local variables... */
1519 key = &catalogKey;
1520 record = &catalogRecord;
1521
1522 result = BuildCatalogKeyUTF8(volume, parentID, name, kUndefinedStrLen, key, NULL);
1523 ReturnIfError(result);
1524
1525 //--- locate subject catalog node
1526
1527 result = LocateCatalogNodeByKey(volume, catalogHint, key, record, &hint);
1528
1529 // if we did not find it by name, then look for an embedded file ID in a mangled name
1530 if ( (result == cmNotFound) && isHFSPlus )
1531 result = LocateCatalogNodeByMangledName(volume, parentID, name, kUndefinedStrLen, key, record, &hint);
1532
1533 if (result == btNotFound)
1534 result = cmNotFound;
1535
1536 if (catalogHint != hint)
1537 PRINTIT(("UpdateCatalogNode: catalogHint does not match (in: %ld, out: %ld)\n", catalogHint, hint));
1538 ReturnIfError(result);
1539
1540 // update user modifiable fields in the catalog node record...
1541
1542 switch (record->recordType)
1543 {
1544 case kHFSFolderRecord:
1545 {
1546 #if DEBUG_BUILD
1547 if (nodeData->cnd_type != kCatalogFolderNode)
1548 DebugStr("\p UpdateCatalogNode: folder/file mismatch!");
1549 #endif
1550
1551 record->hfsFolder.createDate = UTCToLocal(nodeData->cnd_createDate);
1552 record->hfsFolder.modifyDate = UTCToLocal(nodeData->cnd_contentModDate);
1553 record->hfsFolder.backupDate = UTCToLocal(nodeData->cnd_backupDate);
1554
1555 *(DInfo*) &record->hfsFolder.userInfo = *(DInfo*) &nodeData->cnd_finderInfo;
1556 *(DXInfo*) &record->hfsFolder.finderInfo = *(DXInfo*) ((UInt32)&nodeData->cnd_finderInfo + 16);
1557
1558 recordSize = sizeof(HFSCatalogFolder);
1559 break;
1560 }
1561
1562 case kHFSFileRecord:
1563 {
1564 UInt32 i;
1565
1566 #if DEBUG_BUILD
1567 if (nodeData->cnd_type != kCatalogFileNode)
1568 DebugStr("UpdateCatalogNode: folder/file mismatch!");
1569 if ((nodeData->cnd_datafork.totalBlocks > (0x7FFFFFFF/volume->blockSize)) ||
1570 (nodeData->cnd_rsrcfork.totalBlocks > (0x7FFFFFFF/volume->blockSize)))
1571 DebugStr("HFS file size is larger than 2Gig");
1572 #endif
1573
1574 record->hfsFile.flags = (UInt8) nodeData->cnd_flags;
1575 record->hfsFile.createDate = UTCToLocal(nodeData->cnd_createDate);
1576 record->hfsFile.modifyDate = UTCToLocal(nodeData->cnd_contentModDate);
1577 record->hfsFile.backupDate = UTCToLocal(nodeData->cnd_backupDate);
1578
1579 record->hfsFile.dataLogicalSize = nodeData->cnd_datafork.logicalSize;
1580 record->hfsFile.dataPhysicalSize = nodeData->cnd_datafork.totalBlocks * volume->blockSize;
1581 record->hfsFile.rsrcLogicalSize = nodeData->cnd_rsrcfork.logicalSize;
1582 record->hfsFile.rsrcPhysicalSize = nodeData->cnd_rsrcfork.totalBlocks * volume->blockSize;
1583
1584 *(FInfo*) &record->hfsFile.userInfo = *(FInfo*) &nodeData->cnd_finderInfo;
1585 *(FXInfo*) &record->hfsFile.finderInfo = *(FXInfo*) ((UInt32)&nodeData->cnd_finderInfo + 16);
1586
1587 // copy extent info
1588 for (i = 0; i < kHFSExtentDensity; ++i)
1589 {
1590 record->hfsFile.dataExtents[i].startBlock =
1591 (UInt16) nodeData->cnd_datafork.extents[i].startBlock;
1592 record->hfsFile.dataExtents[i].blockCount =
1593 (UInt16) nodeData->cnd_datafork.extents[i].blockCount;
1594 record->hfsFile.rsrcExtents[i].startBlock =
1595 (UInt16) nodeData->cnd_rsrcfork.extents[i].startBlock;
1596 record->hfsFile.rsrcExtents[i].blockCount =
1597 (UInt16) nodeData->cnd_rsrcfork.extents[i].blockCount;
1598 }
1599
1600 recordSize = sizeof(HFSCatalogFile);
1601 break;
1602 }
1603
1604 case kHFSPlusFolderRecord:
1605 {
1606 record->hfsPlusFolder.createDate = nodeData->cnd_createDate;
1607 record->hfsPlusFolder.contentModDate = nodeData->cnd_contentModDate;
1608 record->hfsPlusFolder.backupDate = nodeData->cnd_backupDate;
1609 record->hfsPlusFolder.accessDate = nodeData->cnd_accessDate;
1610 record->hfsPlusFolder.attributeModDate = nodeData->cnd_attributeModDate;
1611 record->hfsPlusFolder.bsdInfo.ownerID = nodeData->cnd_ownerID;
1612 record->hfsPlusFolder.bsdInfo.groupID = nodeData->cnd_groupID;
1613 record->hfsPlusFolder.bsdInfo.ownerFlags = nodeData->cnd_ownerFlags;
1614 record->hfsPlusFolder.bsdInfo.adminFlags = nodeData->cnd_adminFlags;
1615 record->hfsPlusFolder.bsdInfo.fileMode = nodeData->cnd_mode;
1616 record->hfsPlusFolder.textEncoding = nodeData->cnd_textEncoding;
1617
1618 BlockMoveData(&nodeData->cnd_finderInfo, &record->hfsPlusFolder.userInfo, 32);
1619
1620 recordSize = sizeof(HFSPlusCatalogFolder);
1621 break;
1622 }
1623
1624 case kHFSPlusFileRecord:
1625 {
1626 record->hfsPlusFile.flags = nodeData->cnd_flags;
1627 record->hfsPlusFile.createDate = nodeData->cnd_createDate;
1628 record->hfsPlusFile.contentModDate = nodeData->cnd_contentModDate;
1629 record->hfsPlusFile.backupDate = nodeData->cnd_backupDate;
1630 record->hfsPlusFile.accessDate = nodeData->cnd_accessDate;
1631 record->hfsPlusFile.attributeModDate = nodeData->cnd_attributeModDate;
1632 record->hfsPlusFile.bsdInfo.ownerID = nodeData->cnd_ownerID;
1633 record->hfsPlusFile.bsdInfo.groupID = nodeData->cnd_groupID;
1634 record->hfsPlusFile.bsdInfo.ownerFlags = nodeData->cnd_ownerFlags;
1635 record->hfsPlusFile.bsdInfo.adminFlags = nodeData->cnd_adminFlags;
1636 record->hfsPlusFile.bsdInfo.fileMode = nodeData->cnd_mode;
1637 /* get special value (iNodeNum, linkCount or rawDevice) */
1638 record->hfsPlusFile.bsdInfo.special.rawDevice = nodeData->cnd_rawDevice;
1639 record->hfsPlusFile.textEncoding = nodeData->cnd_textEncoding;
1640
1641 record->hfsPlusFile.dataFork.logicalSize = nodeData->cnd_datafork.logicalSize;
1642 record->hfsPlusFile.dataFork.totalBlocks = nodeData->cnd_datafork.totalBlocks;
1643 BlockMoveData(&nodeData->cnd_datafork.extents,
1644 &record->hfsPlusFile.dataFork.extents, sizeof(HFSPlusExtentRecord));
1645
1646 record->hfsPlusFile.resourceFork.logicalSize = nodeData->cnd_rsrcfork.logicalSize;
1647 record->hfsPlusFile.resourceFork.totalBlocks = nodeData->cnd_rsrcfork.totalBlocks;
1648 BlockMoveData(&nodeData->cnd_rsrcfork.extents,
1649 &record->hfsPlusFile.resourceFork.extents, sizeof(HFSPlusExtentRecord));
1650
1651 BlockMoveData(&nodeData->cnd_finderInfo, &record->hfsPlusFile.userInfo, 32);
1652
1653 #if HFS_HARDLINKS && DEBUG_BUILD
1654 /* Must swap opaque finder data */
1655 if (SWAP_BE32 (record->hfsPlusFile.userInfo.fdType) == kHardLinkFileType &&
1656 SWAP_BE32 (record->hfsPlusFile.userInfo.fdCreator) == kHardLinkCreator) {
1657 if (record->hfsPlusFile.dataFork.logicalSize != 0)
1658 DebugStr("UpdateCatalogNode: link has data fork!");
1659 }
1660 #endif
1661 recordSize = sizeof(HFSPlusCatalogFile);
1662 break;
1663 }
1664
1665 default:
1666 return cmNotFound;
1667 }
1668
1669 result = ReplaceBTreeRecord(volume->catalogRefNum, key, catalogHint, record, recordSize, &hint);
1670
1671 if ( result == btNotFound )
1672 {
1673 result = cmNotFound;
1674 }
1675 else if ( result == noErr )
1676 {
1677 /* if we're just updating the accessDate then no need to change volume mod date */
1678 if (nodeData->cnd_contentModDate > volume->vcbLsMod ||
1679 (isHFSPlus && nodeData->cnd_attributeModDate > volume->vcbLsMod))
1680 {
1681 VCB_LOCK(volume);
1682 volume->vcbFlags |= 0xFF00; // Mark the VCB dirty
1683 volume->vcbLsMod = GetTimeUTC(); // update last modified date
1684 VCB_UNLOCK(volume);
1685 }
1686
1687 result = FlushCatalog(volume); // flush the catalog
1688 }
1689
1690 return result;
1691 }
1692
1693
1694 //_________________________________________________________________________________
1695 // Routine: CreateFileIDRef
1696 //
1697 // Function: Creates a file thread record for hfs file node
1698 //
1699 //_________________________________________________________________________________
1700
1701 OSErr
1702 CreateFileIDRef(ExtendedVCB *volume, HFSCatalogNodeID parentID, ConstUTF8Param name, UInt32 hint, HFSCatalogNodeID *threadID)
1703 {
1704 CatalogKey nodeKey; // 518 bytes
1705 CatalogRecord nodeData; // 520 bytes
1706 HFSCatalogKey threadKey;
1707 HFSCatalogThread threadData;
1708 UInt32 nodeHint;
1709 UInt32 tempHint;
1710 OSErr result;
1711 Boolean isHFSPlus = (volume->vcbSigWord == kHFSPlusSigWord);
1712
1713 *threadID = 0;
1714
1715 result = BuildCatalogKeyUTF8(volume, parentID, name, kUndefinedStrLen, &nodeKey, NULL);
1716 ReturnIfError(result);
1717
1718 //--- locate subject catalog node
1719
1720 result = LocateCatalogNodeByKey(volume, hint, &nodeKey, &nodeData, &nodeHint);
1721
1722 // if we did not find it by name, then look for an embedded file ID in a mangled name
1723 if ( (result == cmNotFound) && isHFSPlus )
1724 result = LocateCatalogNodeByMangledName(volume, parentID, name, kUndefinedStrLen, &nodeKey, &nodeData, &nodeHint);
1725 ReturnIfError(result);
1726
1727 if (nodeData.recordType == kHFSPlusFileRecord)
1728 {
1729 *threadID = nodeData.hfsPlusFile.fileID;
1730 return noErr; // already have one
1731 }
1732
1733 if (nodeData.recordType != kHFSFileRecord)
1734 {
1735 return notAFileErr;
1736 }
1737
1738
1739 if (nodeData.hfsFile.flags & kHFSThreadExistsMask)
1740 {
1741 *threadID = nodeData.hfsFile.fileID;
1742 return noErr; // already have one
1743 }
1744
1745 result = VolumeWritable( volume );
1746 if ( result != noErr ) return result;
1747
1748 //
1749 // need to insert a thread record
1750 //
1751 BuildCatalogKey(nodeData.hfsFile.fileID, NULL, false, (CatalogKey *)&threadKey);
1752
1753 ClearMemory(&threadData, sizeof(HFSCatalogThread));
1754 threadData.recordType = kHFSFileThreadRecord;
1755 threadData.parentID = nodeKey.hfs.parentID;
1756 BlockMoveData(&nodeKey.hfs.nodeName, &threadData.nodeName, nodeKey.hfs.nodeName[0] + 1);
1757
1758 result = InsertBTreeRecord(volume->catalogRefNum, &threadKey, &threadData, sizeof(HFSCatalogThread), &tempHint);
1759 if (result == btExists) result = noErr; //XXX could return cmExists or fidExists
1760 ReturnIfError(result);
1761
1762 //
1763 // Finally, set the flag in the file record to say this file has a thread record.
1764 //
1765 nodeData.hfsFile.flags |= kHFSThreadExistsMask;
1766 result = ReplaceBTreeRecord(volume->catalogRefNum, &nodeKey, nodeHint, &nodeData, sizeof(HFSCatalogFile), &nodeHint );
1767
1768 if (result == noErr) {
1769 (void) FlushCatalog(volume);
1770 *threadID = nodeData.hfsFile.fileID;
1771 }
1772
1773 return result;
1774 }
1775
1776
1777 //_________________________________________________________________________________
1778 // Routine: CompareCatalogKeys
1779 //
1780 // Function: Compares two catalog keys (a search key and a trial key).
1781 //
1782 // Result: +n search key > trial key
1783 // 0 search key = trial key
1784 // -n search key < trial key
1785 //_________________________________________________________________________________
1786
1787 SInt32
1788 CompareCatalogKeys(HFSCatalogKey *searchKey, HFSCatalogKey *trialKey)
1789 {
1790 HFSCatalogNodeID searchParentID, trialParentID;
1791 SInt32 result;
1792
1793 searchParentID = searchKey->parentID;
1794 trialParentID = trialKey->parentID;
1795
1796 if ( searchParentID > trialParentID ) // parent dirID is unsigned
1797 result = 1;
1798 else if ( searchParentID < trialParentID )
1799 result = -1;
1800 else // parent dirID's are equal, compare names
1801 {
1802 #if ( ! FORDISKFIRSTAID )
1803 LogStartTime(kTraceRelString);
1804
1805 result = FastRelString(searchKey->nodeName, trialKey->nodeName);
1806
1807 LogEndTime(kTraceRelString, noErr);
1808 #else
1809 result = (SInt32) RelString_Glue(searchKey->nodeName, trialKey->nodeName);
1810 #endif
1811 }
1812
1813 return result;
1814 }
1815
1816
1817 //_________________________________________________________________________________
1818 // Routine: CompareExtendedCatalogKeys
1819 //
1820 // Function: Compares two large catalog keys (a search key and a trial key).
1821 //
1822 // Result: +n search key > trial key
1823 // 0 search key = trial key
1824 // -n search key < trial key
1825 //_________________________________________________________________________________
1826
1827 SInt32
1828 CompareExtendedCatalogKeys(HFSPlusCatalogKey *searchKey, HFSPlusCatalogKey *trialKey)
1829 {
1830 SInt32 result;
1831 HFSCatalogNodeID searchParentID, trialParentID;
1832
1833 searchParentID = searchKey->parentID;
1834 trialParentID = trialKey->parentID;
1835
1836 if ( searchParentID > trialParentID ) // parent node IDs are unsigned
1837 {
1838 result = 1;
1839 }
1840 else if ( searchParentID < trialParentID )
1841 {
1842 result = -1;
1843 }
1844 else // parent node ID's are equal, compare names
1845 {
1846 if ( searchKey->nodeName.length == 0 || trialKey->nodeName.length == 0 )
1847 result = searchKey->nodeName.length - trialKey->nodeName.length;
1848 else
1849 result = FastUnicodeCompare(&searchKey->nodeName.unicode[0], searchKey->nodeName.length,
1850 &trialKey->nodeName.unicode[0], trialKey->nodeName.length);
1851 }
1852
1853 return result;
1854 }
1855