]> git.saurik.com Git - apple/xnu.git/blob - bsd/hfs/hfscommon/Catalog/Catalog.c
xnu-201.5.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 UInt32 teHint)
241 {
242 CatalogKey *nodeKey;
243 CatalogRecord nodeData; // 520 bytes
244 UInt32 nodeDataSize;
245 CatalogRecord parentThreadData; // 520 bytes
246 HFSCatalogNodeID parentsParentID;
247 CatalogName *parentNamePtr;
248 UInt32 tempHint;
249 UInt32 textEncoding;
250 UInt16 tempSize;
251 OSErr result;
252 Boolean isHFSPlus = (volume->vcbSigWord == kHFSPlusSigWord);
253 FCB *fcb;
254 FSBufferDescriptor btRecord;
255 BTreeIterator iterator;
256 BTreeIterator threadIter;
257 HFSCatalogNodeID nextCNID;
258
259 if (nodeType != kCatalogFolderNode && nodeType != kCatalogFileNode)
260 return paramErr;
261
262 fcb = GetFileControlBlock(volume->catalogRefNum);
263 nodeKey = (CatalogKey *) &iterator.key;
264
265 //--- make sure parent exists (by locating the parent's thread record)
266
267 result = LocateCatalogThread(volume, parentID, &parentThreadData, &tempSize, &tempHint);
268 ReturnIfError(result);
269
270 TrashCatalogIterator(volume, parentID); // invalidate any iterators for this parentID
271
272 // save copy of parent's parentID and name.
273
274 if (isHFSPlus)
275 {
276 if (parentThreadData.recordType != kHFSPlusFolderThreadRecord)
277 return dirNFErr;
278
279 parentsParentID = parentThreadData.hfsPlusThread.parentID;
280 parentNamePtr = (CatalogName*) &parentThreadData.hfsPlusThread.nodeName;
281 }
282 else
283 {
284 if (parentThreadData.recordType != kHFSFolderThreadRecord)
285 return dirNFErr;
286
287 parentsParentID = parentThreadData.hfsThread.parentID;
288 parentNamePtr = (CatalogName*) &parentThreadData.hfsThread.nodeName;
289 }
290
291 // invalidate cache for parent since its about to change
292 InvalidateCatalogNodeCache(volume, parentsParentID);
293
294 //--- build key for new catalog node
295 textEncoding = teHint;
296 result = BuildCatalogKeyUTF8(volume, parentID, name, kUndefinedStrLen, nodeKey, &textEncoding);
297 ReturnIfError(result);
298
299 /* make sure it doesn't exist */
300 result = BTSearchRecord(fcb, &iterator, kInvalidMRUCacheKey, NULL, NULL, &iterator);
301 if (result != btNotFound)
302 return (cmExists);
303
304 nextCNID = volume->vcbNxtCNID;
305 if (!isHFSPlus && nextCNID == 0xFFFFFFFF)
306 return (dskFulErr);
307
308 //--- build thread record for new CNode
309 if (isHFSPlus || nodeType == kCatalogFolderNode)
310 {
311 CatalogRecord threadData; // 520 bytes
312 UInt32 threadSize;
313
314 btRecord.bufferAddress = &threadData;
315 btRecord.itemSize = threadSize;
316 btRecord.itemCount = 1;
317 InitCatalogThreadRecord(volume, nodeType, nodeKey, &threadData, &threadSize);
318 TryNextID:
319 BuildCatalogKey(nextCNID, NULL, isHFSPlus, (CatalogKey*) &threadIter.key);
320 result = BTInsertRecord(fcb, &threadIter, &btRecord, threadSize);
321 if (result == btExists && isHFSPlus)
322 {
323 /*
324 * Allow CNIDs on HFS Plus volumes to wrap around
325 */
326 ++nextCNID;
327 if (nextCNID < kHFSFirstUserCatalogNodeID)
328 {
329 volume->vcbAtrb |= kHFSCatalogNodeIDsReusedMask;
330 volume->vcbFlags |= 0xFF00;
331 nextCNID = kHFSFirstUserCatalogNodeID;
332 }
333 goto TryNextID;
334 }
335 ReturnIfError(result);
336 }
337
338 //--- initialize catalog data record (for folder or file)
339 btRecord.bufferAddress = &nodeData;
340 btRecord.itemSize = nodeDataSize;
341 btRecord.itemCount = 1;
342 InitCatalogRecord(volume, nodeType, textEncoding, &nodeData, &nodeDataSize, nextCNID);
343
344 //--- add new folder/file record to catalog BTree
345 result = BTInsertRecord(fcb, &iterator, &btRecord, nodeDataSize );
346 if (result)
347 {
348 if (result == btExists)
349 result = cmExists;
350
351 if (isHFSPlus || nodeType == kCatalogFolderNode)
352 (void) BTDeleteRecord(fcb, &threadIter);
353
354 return result;
355 }
356
357 /*
358 * Return the CNID actually used. Update the volume's next ID.
359 */
360 *catalogNodeID = nextCNID;
361 if (++nextCNID < kHFSFirstUserCatalogNodeID)
362 {
363 volume->vcbAtrb |= kHFSCatalogNodeIDsReusedMask;
364 volume->vcbFlags |= 0xFF00;
365 nextCNID = kHFSFirstUserCatalogNodeID;
366 }
367 volume->vcbNxtCNID = nextCNID;
368
369 //--- update counters...
370
371 result = UpdateFolderCount( volume, parentsParentID, parentNamePtr, nodeData.recordType, kNoHint, +1);
372 ReturnIfError(result); /* XXX what about cleanup ??? */
373
374 AdjustVolumeCounts(volume, nodeData.recordType, +1);
375
376 result = FlushCatalog(volume);
377
378 return result;
379
380 } // end CreateCatalogNode
381
382
383 /*
384 * initialize catalog data record (for folder or file)
385 */
386 void InitCatalogRecord(ExtendedVCB *volume, UInt32 nodeType, UInt32 textEncoding, CatalogRecord *record, UInt32 *recordSize, HFSCatalogNodeID nodeID)
387 {
388 UInt32 timeStamp;
389
390 ClearMemory(record, sizeof(CatalogRecord)); // first clear the record
391
392 if (volume->vcbSigWord == kHFSPlusSigWord)
393 {
394 timeStamp = GetTimeUTC(); // get current date/time (universal)
395
396 UpdateVolumeEncodings(volume, textEncoding);
397
398 if (nodeType == kCatalogFolderNode )
399 {
400 record->recordType = kHFSPlusFolderRecord;
401 record->hfsPlusFolder.folderID = nodeID;
402 record->hfsPlusFolder.createDate = timeStamp;
403 record->hfsPlusFolder.contentModDate = timeStamp;
404 record->hfsPlusFolder.accessDate = timeStamp;
405 record->hfsPlusFolder.attributeModDate = timeStamp;
406 record->hfsPlusFolder.textEncoding = textEncoding;
407 *recordSize = sizeof(HFSPlusCatalogFolder);
408 // threadType = kHFSPlusFolderThreadRecord;
409 }
410 else if (nodeType == kCatalogFileNode )
411 {
412 record->recordType = kHFSPlusFileRecord;
413 record->hfsPlusFile.fileID = nodeID;
414 record->hfsPlusFile.createDate = timeStamp;
415 record->hfsPlusFile.contentModDate = timeStamp;
416 record->hfsPlusFile.accessDate = timeStamp;
417 record->hfsPlusFile.attributeModDate = timeStamp;
418 record->hfsPlusFile.flags |= kHFSThreadExistsMask;
419 record->hfsPlusFile.textEncoding = textEncoding;
420 *recordSize = sizeof(HFSPlusCatalogFile);
421 // threadType = kHFSPlusFileThreadRecord;
422 }
423 }
424 else /* standard hfs */
425 {
426 timeStamp = GetTimeLocal(true); // get current local date/time
427
428 if (nodeType == kCatalogFolderNode )
429 {
430 record->recordType = kHFSFolderRecord;
431 record->hfsFolder.folderID = nodeID;
432 record->hfsFolder.createDate = timeStamp;
433 record->hfsFolder.modifyDate = timeStamp;
434 *recordSize = sizeof(HFSCatalogFolder);
435 // threadType = kHFSFolderThreadRecord;
436 }
437 else if (nodeType == kCatalogFileNode )
438 {
439 record->recordType = kHFSFileRecord;
440 record->hfsFile.fileID = nodeID;
441 record->hfsFile.createDate = timeStamp;
442 record->hfsFile.modifyDate = timeStamp;
443 *recordSize = sizeof(HFSCatalogFile);
444 }
445 }
446 }
447
448
449 void InitCatalogThreadRecord(ExtendedVCB *volume, UInt32 nodeType, CatalogKey *nodeKey,
450 CatalogRecord *record, UInt32 *threadSize)
451 {
452 ClearMemory(record, sizeof(CatalogRecord) ); // first clear the record
453
454 if (volume->vcbSigWord == kHFSPlusSigWord)
455 {
456 if (nodeType == kCatalogFolderNode)
457 record->recordType = kHFSPlusFolderThreadRecord;
458 else
459 record->recordType = kHFSPlusFileThreadRecord;
460 record->hfsPlusThread.parentID = nodeKey->hfsPlus.parentID;
461 *threadSize = sizeof(record->hfsPlusThread);
462
463 // HFS Plus has varaible sized threads so adjust to actual length
464 *threadSize -= ( sizeof(record->hfsPlusThread.nodeName.unicode) -
465 (nodeKey->hfsPlus.nodeName.length * sizeof(UniChar)) );
466 BlockMoveData(&nodeKey->hfsPlus.nodeName, &record->hfsPlusThread.nodeName,
467 sizeof(UniChar) * (nodeKey->hfsPlus.nodeName.length + 1));
468 }
469 else // classic HFS
470 {
471 if (nodeType == kCatalogFolderNode)
472 record->recordType = kHFSFolderThreadRecord;
473 else
474 record->recordType = kHFSFileThreadRecord;
475 record->hfsThread.parentID = nodeKey->hfs.parentID;
476 *threadSize = sizeof(record->hfsThread);
477 BlockMoveData(&nodeKey->hfs.nodeName, &record->hfsThread.nodeName,
478 nodeKey->hfs.nodeName[0] + 1);
479 }
480 }
481
482
483 //_________________________________________________________________________________
484 // Routine: DeleteCatalogNode
485 //
486 // Function: Deletes an existing folder or file CNode. The thread record
487 // is also deleted for directories and files that have thread
488 // records.
489 //
490 // The valence for a folder must be zero before it can be deleted.
491 // The rootfolder cannot be deleted.
492 //
493 //_________________________________________________________________________________
494
495 OSErr
496 DeleteCatalogNode(ExtendedVCB *volume, HFSCatalogNodeID parentID, ConstUTF8Param name, UInt32 hint)
497 {
498 CatalogKey key; // 518 bytes
499 CatalogRecord data; // 520 bytes
500 UInt32 nodeHint;
501 HFSCatalogNodeID nodeID;
502 HFSCatalogNodeID nodeParentID;
503 UInt16 nodeType;
504 OSErr result;
505 Boolean isHFSPlus = (volume->vcbSigWord == kHFSPlusSigWord);
506
507 //--- locate subject catalog node
508
509 result = BuildCatalogKeyUTF8(volume, parentID, name, kUndefinedStrLen, &key, NULL);
510 ReturnIfError(result);
511
512 result = LocateCatalogNodeByKey(volume, hint, &key, &data, &nodeHint);
513
514 // if we did not find it by name, then look for an embedded file ID in a mangled name
515 if ( (result == cmNotFound) && isHFSPlus )
516 result = LocateCatalogNodeByMangledName(volume, parentID, name, kUndefinedStrLen, &key, &data, &nodeHint);
517
518 /*
519 * In Mac OS X there can also be HFS filenames that
520 * could not be encoded using the default encoding.
521 */
522 if (result == cmNotFound && !isHFSPlus && VCBTOHFS(volume)->hfs_encoding != 0) {
523 Str31 hfsName;
524
525 utf8_to_mac_roman(strlen(name), name, hfsName);
526 result = LocateCatalogNode(volume, parentID, (CatalogName*)hfsName,
527 0, &key, &data, &nodeHint);
528 }
529 ReturnIfError(result);
530
531 nodeParentID = isHFSPlus ? key.hfsPlus.parentID : key.hfs.parentID; // establish real parent cnid
532 nodeType = data.recordType; // establish cnode type
533 nodeID = 0;
534
535 switch (nodeType)
536 {
537 case kHFSFolderRecord:
538 if (data.hfsFolder.valence != 0) // is it empty?
539 return cmNotEmpty;
540
541 nodeID = data.hfsFolder.folderID;
542 break;
543
544 case kHFSPlusFolderRecord:
545 if (data.hfsPlusFolder.valence != 0) // is it empty?
546 return cmNotEmpty;
547
548 nodeID = data.hfsPlusFolder.folderID;
549 break;
550
551 case kHFSFileRecord:
552 if (data.hfsFile.flags & kHFSThreadExistsMask)
553 nodeID = data.hfsFile.fileID;
554 break;
555
556 case kHFSPlusFileRecord:
557 nodeID = data.hfsPlusFile.fileID; // note: HFS Plus files always have a thread
558 break;
559
560 default:
561 return cmNotFound;
562 }
563
564
565 if (nodeID == fsRtDirID) // is this the root folder?
566 return cmRootCN; // sorry, you can't delete the root!
567
568 TrashCatalogIterator(volume, nodeParentID); // invalidate any iterators for this parentID
569 InvalidateCatalogNodeCache(volume, nodeParentID); // and invalidate node cache
570
571 //--- delete catalog records for CNode and file threads if they exist
572
573 result = DeleteBTreeRecord(volume->catalogRefNum, &key);
574 ReturnIfError(result);
575
576 if ( nodeID )
577 {
578 CatalogKey threadKey; // 518 bytes
579
580 BuildCatalogKey(nodeID, NULL, isHFSPlus, &threadKey);
581
582 (void) DeleteBTreeRecord(volume->catalogRefNum, &threadKey); // ignore errors for thread deletes
583 }
584
585 //--- update counters...
586
587 result = UpdateFolderCount(volume, nodeParentID, NULL, nodeType, kNoHint, -1);
588 ReturnIfError(result);
589
590 AdjustVolumeCounts(volume, nodeType, -1); // all done with this file or folder
591
592 result = FlushCatalog(volume);
593
594 return result;
595
596 } // end DeleteCatalogNode
597
598
599 //_________________________________________________________________________________
600 // Routine: GetCatalogNode
601 //
602 // Function: Locates an existing folder or file CNode and pointer to the CNode data record.
603 //
604 //_________________________________________________________________________________
605
606 OSErr
607 GetCatalogNode( ExtendedVCB *volume, HFSCatalogNodeID parentID, ConstUTF8Param name, UInt32 nameLen, UInt32 hint,
608 CatalogNodeData *nodeData, UInt32 *newHint)
609 {
610 CatalogKey *key;
611 CatalogRecord *record;
612 BTreeIterator searchIterator;
613 FSBufferDescriptor btRecord;
614 ByteCount utf8len;
615 UInt32 heuristicHint;
616 UInt32 *cachedHint;
617 FCB *fcb;
618 OSErr result = noErr;
619 UInt16 dataSize;
620 Boolean isHFSPlus = (volume->vcbSigWord == kHFSPlusSigWord);
621
622 if (isHFSPlus) {
623 btRecord.bufferAddress = nodeData;
624 btRecord.itemSize = sizeof(CatalogNodeData);
625 } else {
626 btRecord.bufferAddress = &nodeData->cnd_extra;
627 btRecord.itemSize = sizeof(HFSCatalogFile);
628 }
629
630 btRecord.itemCount = 1;
631 record = (CatalogRecord *) btRecord.bufferAddress;
632 key = (CatalogKey *) &searchIterator.key;
633
634 if (name && nameLen == kUndefinedStrLen)
635 nameLen = strlen(name);
636
637 result = BuildCatalogKeyUTF8(volume, parentID, name, nameLen, key, NULL);
638 ReturnIfError(result);
639
640 fcb = GetFileControlBlock(volume->catalogRefNum);
641 searchIterator.hint.nodeNum = *newHint;
642 searchIterator.hint.index = 0;
643
644 /*
645 * We pass a 2nd hint/guess into BTSearchRecord. The heuristicHint
646 * is a mapping of dirID and nodeNumber, in hopes that the current
647 * search will be in the same node as the last search with the same
648 * parentID.
649 */
650 if (name != NULL && GetMRUCacheBlock(parentID, volume->hintCachePtr, (Ptr *)&cachedHint) == 0)
651 heuristicHint = *cachedHint;
652 else
653 heuristicHint = kInvalidMRUCacheKey;
654
655 result = BTSearchRecord(fcb, &searchIterator, heuristicHint, &btRecord, &dataSize, &searchIterator);
656 if (result == btNotFound)
657 result = cmNotFound;
658
659 if (name != NULL && result == noErr)
660 InsertMRUCacheBlock(volume->hintCachePtr, parentID, (Ptr) &(searchIterator.hint.nodeNum));
661
662 if (result == noErr) {
663 CatalogName *nodeName = NULL;
664 HFSCatalogNodeID threadParentID;
665
666 /* if we got a thread record, then go look up real record */
667 switch (record->recordType) {
668
669 case kHFSFileThreadRecord:
670 case kHFSFolderThreadRecord:
671 threadParentID = record->hfsThread.parentID;
672 nodeName = (CatalogName *) &record->hfsThread.nodeName;
673 break;
674
675 case kHFSPlusFileThreadRecord:
676 case kHFSPlusFolderThreadRecord:
677 threadParentID = record->hfsPlusThread.parentID;
678 nodeName = (CatalogName *) &record->hfsPlusThread.nodeName;
679 break;
680
681 default:
682 threadParentID = 0;
683 *newHint = searchIterator.hint.nodeNum;
684 break;
685 }
686 if (threadParentID) {
687 BuildCatalogKey(threadParentID, nodeName, isHFSPlus, key);
688 searchIterator.hint.nodeNum = kNoHint;
689 searchIterator.hint.index = 0;
690
691 result = BTSearchRecord(fcb, &searchIterator, kInvalidMRUCacheKey, &btRecord, &dataSize, &searchIterator);
692 if (result == btNotFound)
693 result = cmNotFound;
694 if (result == noErr)
695 *newHint = searchIterator.hint.nodeNum;
696 }
697 }
698
699 /*
700 * If we did not find it by name, then look for an embedded
701 * file ID in a mangled name.
702 */
703 if ( result == cmNotFound && isHFSPlus)
704 result = LocateCatalogNodeByMangledName(volume, parentID, name, nameLen,
705 key, record, newHint);
706
707 /*
708 * In Mac OS X there can also be HFS filenames that
709 * could not be encoded using the default encoding.
710 * In which case they were encoded as MacRoman.
711 */
712 if (result == cmNotFound && !isHFSPlus && VCBTOHFS(volume)->hfs_encoding != 0) {
713 Str31 hfsName;
714
715 utf8_to_mac_roman(nameLen, name, hfsName);
716 result = LocateCatalogNode(volume, parentID, (CatalogName*)hfsName,
717 0, key, record, newHint);
718 }
719 ReturnIfError(result);
720
721 nodeData->cnm_parID = isHFSPlus ? key->hfsPlus.parentID : key->hfs.parentID;
722
723 if (nodeData->cnm_flags & kCatNameNoCopyName) {
724 if (! isHFSPlus) {
725 if (record->recordType == kHFSFolderRecord || record->recordType == kHFSFileRecord)
726 CopyCatalogNodeData(volume, record, nodeData);
727 else
728 result = cmNotFound;
729 }
730 }
731 else {
732 nodeData->cnm_nameptr = nodeData->cnm_namespace;
733 if ( isHFSPlus ) {
734 result = utf8_encodestr(key->hfsPlus.nodeName.unicode,
735 key->hfsPlus.nodeName.length * sizeof(UniChar),
736 nodeData->cnm_namespace, (size_t *)&utf8len,
737 MAXHFSVNODELEN + 1, ':', 0);
738
739 /* Need to allocate buffer large enough */
740 if (result == ENAMETOOLONG) {
741 utf8len = utf8_encodelen(key->hfsPlus.nodeName.unicode,
742 key->hfsPlus.nodeName.length * sizeof(UniChar),
743 ':', 0);
744 nodeData->cnm_nameptr = NewPtr(utf8len + 1);
745 nodeData->cnm_flags |= kCatNameIsAllocated;
746 result = utf8_encodestr(key->hfsPlus.nodeName.unicode,
747 key->hfsPlus.nodeName.length * sizeof(UniChar),
748 nodeData->cnm_nameptr, (size_t *)&utf8len,
749 utf8len + 1, ':', 0);
750 }
751 }
752 else { // classic HFS
753
754 /* convert data to HFS Plus format */
755 if (record->recordType == kHFSFolderRecord || record->recordType == kHFSFileRecord) {
756 CopyCatalogNodeData(volume, record, nodeData);
757 result = hfs_to_utf8(volume, key->hfs.nodeName, MAXHFSVNODELEN + 1,
758 &utf8len, nodeData->cnm_namespace);
759 /* Need to allocate buffer large enough */
760 if (result == ENAMETOOLONG) {
761 nodeData->cnm_nameptr = NewPtr(utf8len + 1);
762 nodeData->cnm_flags |= kCatNameIsAllocated;
763 result = hfs_to_utf8(volume, key->hfs.nodeName, utf8len + 1,
764 &utf8len, nodeData->cnm_nameptr);
765 } else if (result) {
766 /*
767 * When an HFS name cannot be encoded with the current
768 * volume encoding we use MacRoman as a fallback.
769 */
770 result = mac_roman_to_utf8(key->hfs.nodeName, MAXHFSVNODELEN + 1,
771 &utf8len, nodeData->cnm_namespace);
772 }
773 } else
774 result = cmNotFound;
775 }
776
777 nodeData->cnm_length = utf8len;
778 if (result && (nodeData->cnm_flags & kCatNameIsAllocated))
779 {
780 DisposePtr(nodeData->cnm_nameptr);
781 nodeData->cnm_flags &= ~kCatNameIsAllocated;
782 nodeData->cnm_nameptr = 0; /* Just to be clean */
783 }
784 }
785
786
787 #if DEBUG_BUILD
788 if ( nodeData->cnd_nodeID > volume->vcbNxtCNID || nodeData->cnd_nodeID == 0)
789 DebugStr("\pGetCatalogNode bad file ID found!");
790 #endif
791
792 return result;
793
794 } // end GetCatalogNode
795
796
797 UInt32
798 GetDirEntrySize(BTreeIterator *bip, ExtendedVCB * vol)
799 {
800 CatalogKey * ckp;
801 CatalogName * cnp;
802 ByteCount utf8chars;
803 UInt8 name[kdirentMaxNameBytes + 1];
804 OSErr result;
805
806 ckp = (CatalogKey*) &bip->key;
807
808 if (vol->vcbSigWord == kHFSPlusSigWord) {
809 cnp = (CatalogName*) &ckp->hfsPlus.nodeName;
810 utf8chars = utf8_encodelen(cnp->ustr.unicode,
811 cnp->ustr.length * sizeof(UniChar), ':', 0);
812 if (utf8chars > kdirentMaxNameBytes)
813 utf8chars = kdirentMaxNameBytes;
814 } else { /* hfs */
815 cnp = (CatalogName*) ckp->hfs.nodeName;
816 result = hfs_to_utf8(vol, cnp->pstr, kdirentMaxNameBytes + 1,
817 &utf8chars, name);
818 if (result) {
819 /*
820 * When an HFS name cannot be encoded with the current
821 * volume encoding we use MacRoman as a fallback.
822 */
823 result = mac_roman_to_utf8(cnp->pstr, MAXHFSVNODELEN + 1,
824 &utf8chars, name);
825 }
826 }
827
828 return DIRENTRY_SIZE(utf8chars);
829 }
830 /*
831 * NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE
832 *
833 * This is assuming maxinum size of a name is 255 (kdirentMaxNameBytes), which is incorrect.
834 * Any caller of this has to make sure names > 255 are mangled!!!!!!!!
835 */
836
837 OSErr
838 PositionIterator(CatalogIterator *cip, UInt32 offset, BTreeIterator *bip, UInt16 *op)
839 {
840 #define CAT_START_OFFSET (2 * sizeof(struct hfsdotentry))
841 ExtendedVCB * vol;
842 FCB * fcb;
843 OSErr result = 0;
844
845 /* are we past the end of a directory? */
846 if (cip->folderID != cip->parentID)
847 return(cmNotFound);
848
849 vol = cip->volume;
850 fcb = GetFileControlBlock(vol->catalogRefNum);
851
852 /* make a btree iterator from catalog iterator */
853 UpdateBtreeIterator(cip, bip);
854
855 if (cip->currentOffset == offset) {
856 *op = kBTreeCurrentRecord;
857
858 } else if (cip->nextOffset == offset) {
859 *op = kBTreeNextRecord;
860
861 } else { /* start from beginning */
862 UInt32 heuristicHint;
863 UInt32 *cachedHint;
864
865 *op = kBTreeNextRecord;
866
867 /*
868 * We pass a 2nd hint/guess into BTSearchRecord. The heuristicHint
869 * is a mapping of dirID and nodeNumber, in hopes that the current
870 * search will be in the same node as the last search with the same
871 * parentID.
872 */
873 result = GetMRUCacheBlock( cip->folderID, vol->hintCachePtr, (Ptr *)&cachedHint );
874 heuristicHint = (result == noErr) ? *cachedHint : kInvalidMRUCacheKey;
875
876 /* Position iterator at the folder's thread record */
877 result = BTSearchRecord(fcb, bip, heuristicHint, NULL, NULL, bip);
878 if (result)
879 goto exit;
880
881 InsertMRUCacheBlock( vol->hintCachePtr, cip->folderID, (Ptr) &bip->hint.nodeNum );
882
883 /* find offset (note: n^2 / 2) */
884 if (offset > CAT_START_OFFSET) {
885 HFSCatalogNodeID pid, *idp;
886 UInt32 curOffset, nextOffset;
887
888 /* get first record (ie offset 24) */
889 result = BTIterateRecord( fcb, kBTreeNextRecord, bip, NULL, NULL );
890 if (result)
891 goto exit;
892
893 if (vol->vcbSigWord == kHFSPlusSigWord)
894 idp = &((CatalogKey*) &bip->key)->hfsPlus.parentID;
895 else
896 idp = &((CatalogKey*) &bip->key)->hfs.parentID;
897
898 pid = *idp;
899
900 curOffset = CAT_START_OFFSET;
901 nextOffset = CAT_START_OFFSET + GetDirEntrySize(bip, vol);
902
903 while (nextOffset < offset) {
904 result = BTIterateRecord( fcb, kBTreeNextRecord, bip, NULL, NULL );
905 if (result)
906 goto exit;
907
908 /* check for parent change */
909 if (pid != *idp) {
910 result = cmNotFound; /* offset past end of directory */
911 goto exit;
912 }
913
914 curOffset = nextOffset;
915 nextOffset += GetDirEntrySize(bip, vol);
916 };
917
918 if (nextOffset != offset) {
919 result = cmNotFound;
920 goto exit;
921 }
922
923 UpdateCatalogIterator(bip, cip);
924 cip->currentOffset = curOffset;
925 cip->nextOffset = nextOffset;
926 }
927 }
928
929 exit:
930 if (result == btNotFound)
931 result = cmNotFound;
932
933 return result;
934
935 } /* end PositionIterator */
936
937
938 //_________________________________________________________________________________
939 // Routine: GetCatalogOffspring
940 //
941 // Function: Gets an offspring record from a specified folder. The folder
942 // is identified by it's folderID. The desired offspring CNode is
943 // indicated by the value of the offspring index (1 = 1st offspring
944 // CNode, 2 = 2nd offspring CNode, etc.).
945 //
946 //_________________________________________________________________________________
947
948 OSErr
949 GetCatalogOffspring(ExtendedVCB *volume, HFSCatalogNodeID folderID, UInt16 index,
950 CatalogNodeData *nodeData,
951 HFSCatalogNodeID *nodeID, SInt16 *nodeType)
952 {
953 CatalogIterator * catalogIterator;
954 OSErr result;
955
956
957 if ( folderID == 0 )
958 return cmNotFound;
959
960 /*
961 * return cmNotFound for index 32767, to prevent overflowing
962 * the index into negative numbers.
963 */
964 if ( index == 32767 )
965 return cmNotFound;
966
967 // get best catalog iterator...
968 catalogIterator = oGetCatalogIterator(volume, folderID, index);
969
970 result = IterateCatalogNode(volume, catalogIterator, index,
971 nodeData, nodeID, nodeType);
972
973 (void) ReleaseCatalogIterator(catalogIterator);
974
975 return result;
976
977 } // end GetCatalogOffspring
978
979
980 //_________________________________________________________________________________
981 // Routine: IterateCatalogNode
982 //
983 // Function: Gets an offspring record from a specified folder. The folder
984 // is identified by it's folderID. The desired offspring CNode is
985 // indicated by the value of the offspring index (1 = 1st offspring
986 // CNode, 2 = 2nd offspring CNode, etc.).
987 //
988 //_________________________________________________________________________________
989
990 static OSErr
991 IterateCatalogNode( ExtendedVCB *volume, CatalogIterator *catalogIterator, UInt16 index,
992 CatalogNodeData *nodeData, HFSCatalogNodeID *nodeID,
993 SInt16 *nodeType )
994 {
995 HFSCatalogNodeID offspringParentID;
996 CatalogKey * offspringKey;
997 CatalogName * offspringName;
998 BTreeIterator btreeIterator;
999 FSBufferDescriptor btRecord;
1000 CatalogRecord * record;
1001 UInt8 databuf[32]; /* space for partial record */
1002 FCB * fcb;
1003 SInt16 selectionIndex;
1004 UInt16 tempSize;
1005 UInt16 operation;
1006 OSErr result;
1007 Boolean isHFSPlus;
1008 ByteCount utf8len;
1009
1010
1011 isHFSPlus = (volume->vcbSigWord == kHFSPlusSigWord);
1012 fcb = GetFileControlBlock(volume->catalogRefNum);
1013
1014 // make a btree iterator from catalog iterator
1015 UpdateBtreeIterator(catalogIterator, &btreeIterator);
1016
1017 /* if client doesn't want data (ie readdir), just get type and id */
1018 if (nodeData == NULL) {
1019 /* data buf has space to cover all type/id offsets */
1020 btRecord.bufferAddress = databuf;
1021 btRecord.itemSize = sizeof(databuf);
1022 } else if (isHFSPlus) {
1023 btRecord.bufferAddress = nodeData;
1024 btRecord.itemSize = sizeof(CatalogNodeData);
1025 } else {
1026 btRecord.bufferAddress = &nodeData->cnd_extra;
1027 btRecord.itemSize = sizeof(HFSCatalogFile);
1028 }
1029 btRecord.itemCount = 1;
1030
1031 //--- if neccessary position the iterator at the thread record for the specified folder
1032
1033 if ( catalogIterator->currentIndex == 0 ) // is this a new iterator?
1034 {
1035 UInt32 heuristicHint;
1036 UInt32 *cachedHint;
1037
1038 // We pass a 2nd hint/guess into BTSearchRecord. The heuristicHint is a mapping of
1039 // dirID and nodeNumber, in hopes that the current search will be in the same node
1040 // as the last search with the same parentID.
1041 result = GetMRUCacheBlock( catalogIterator->folderID, volume->hintCachePtr, (Ptr *)&cachedHint );
1042 heuristicHint = (result == noErr) ? *cachedHint : kInvalidMRUCacheKey;
1043
1044 result = BTSearchRecord( fcb, &btreeIterator, heuristicHint, &btRecord, &tempSize, &btreeIterator );
1045 ExitOnError(result);
1046
1047 UpdateCatalogIterator(&btreeIterator, catalogIterator); // update btree hint and key
1048
1049 InsertMRUCacheBlock( volume->hintCachePtr, catalogIterator->folderID, (Ptr) &btreeIterator.hint.nodeNum );
1050 }
1051
1052 //--- get offspring record (relative to catalogIterator's position)
1053
1054 selectionIndex = index - catalogIterator->currentIndex;
1055
1056 // now we have to map index into next/prev operations...
1057 if (selectionIndex == 1)
1058 {
1059 operation = kBTreeNextRecord;
1060 }
1061 else if (selectionIndex == -1)
1062 {
1063 operation = kBTreePrevRecord;
1064 }
1065 else if (selectionIndex == 0)
1066 {
1067 operation = kBTreeCurrentRecord;
1068 }
1069 else if (selectionIndex > 1)
1070 {
1071 UInt32 i;
1072
1073 for (i = 1; i < selectionIndex; ++i)
1074 {
1075 result = BTIterateRecord( fcb, kBTreeNextRecord, &btreeIterator, &btRecord, &tempSize );
1076 ExitOnError(result);
1077 }
1078 operation = kBTreeNextRecord;
1079 }
1080 else // (selectionIndex < -1)
1081 {
1082 SInt32 i;
1083
1084 for (i = -1; i > selectionIndex; --i)
1085 {
1086 result = BTIterateRecord( fcb, kBTreePrevRecord, &btreeIterator, &btRecord, &tempSize );
1087 ExitOnError(result);
1088 }
1089 operation = kBTreePrevRecord;
1090 }
1091
1092 result = BTIterateRecord( fcb, operation, &btreeIterator, &btRecord, &tempSize );
1093 ExitOnError(result);
1094
1095 offspringKey = (CatalogKey*) &btreeIterator.key;
1096
1097 if (isHFSPlus)
1098 {
1099 offspringParentID = offspringKey->hfsPlus.parentID;
1100 offspringName = (CatalogName*) &offspringKey->hfsPlus.nodeName;
1101 }
1102 else
1103 {
1104 offspringParentID = offspringKey->hfs.parentID;
1105 offspringName = (CatalogName*) offspringKey->hfs.nodeName;
1106 }
1107
1108 if (offspringParentID != catalogIterator->folderID) // different parent?
1109 {
1110 AgeCatalogIterator(catalogIterator); // we reached the end, so don't hog the cache!
1111
1112 result = cmNotFound; // must be done with this folder
1113 goto ErrorExit;
1114 }
1115
1116 UpdateCatalogIterator(&btreeIterator, catalogIterator); // update btree hint and key
1117 catalogIterator->currentIndex = index; // update the offspring index marker
1118
1119 record = (CatalogRecord *) btRecord.bufferAddress;
1120
1121 if (nodeData == NULL) { /* Just copy the id and type...not name */
1122 if (isHFSPlus)
1123 {
1124 *nodeType = record->recordType;
1125 *nodeID = record->hfsPlusFolder.folderID;
1126 }
1127 else /* hfs name */
1128 {
1129
1130 if (record->recordType == kHFSFileRecord) {
1131 *nodeType = kCatalogFileNode;
1132 *nodeID = record->hfsFile.fileID;
1133 } else if (record->recordType == kHFSFolderRecord) {
1134 *nodeType = kCatalogFolderNode;
1135 *nodeID = record->hfsFolder.folderID;
1136 } else
1137 result = cmNotFound;
1138 }
1139 } else {
1140 nodeData->cnm_parID = isHFSPlus ? offspringKey->hfsPlus.parentID : offspringKey->hfs.parentID;
1141 nodeData->cnm_nameptr = nodeData->cnm_namespace;
1142 if (isHFSPlus)
1143 {
1144 result = utf8_encodestr(offspringName->ustr.unicode,
1145 offspringName->ustr.length * sizeof(UniChar),
1146 nodeData->cnm_namespace, (size_t *)&utf8len,
1147 MAXHFSVNODELEN + 1, ':', 0);
1148
1149 /* Need to allocate buffer large enough */
1150 if (result == ENAMETOOLONG) {
1151 utf8len = utf8_encodelen(offspringName->ustr.unicode,
1152 offspringName->ustr.length * sizeof(UniChar),
1153 ':', 0);
1154 nodeData->cnm_nameptr = NewPtr(utf8len + 1);
1155 nodeData->cnm_flags |= kCatNameIsAllocated;
1156 result = utf8_encodestr(offspringName->ustr.unicode,
1157 offspringName->ustr.length * sizeof(UniChar),
1158 nodeData->cnm_nameptr, (size_t *)&utf8len,
1159 utf8len + 1, ':', 0);
1160 }
1161 }
1162 else /* hfs name */
1163 {
1164 if (record->recordType == kHFSFolderRecord || record->recordType == kHFSFileRecord) {
1165
1166 CopyCatalogNodeData(volume, record, nodeData);
1167 result = hfs_to_utf8(volume, offspringName->pstr, MAXHFSVNODELEN + 1, &utf8len, nodeData->cnm_namespace);
1168 if (result == ENAMETOOLONG) { /* Need to allocate buffer large enough */
1169 nodeData->cnm_nameptr = NewPtr(utf8len+1);
1170 nodeData->cnm_flags |= kCatNameIsAllocated;
1171 result = hfs_to_utf8(volume, offspringName->pstr, utf8len + 1, &utf8len, nodeData->cnm_nameptr);
1172 } else if (result) {
1173 /*
1174 * When an HFS name cannot be encoded with the current
1175 * volume encoding we use MacRoman as a fallback.
1176 */
1177 result = mac_roman_to_utf8(offspringName->pstr, MAXHFSVNODELEN + 1,
1178 &utf8len, nodeData->cnm_namespace);
1179 }
1180
1181 } else {
1182 result = cmNotFound;
1183 }
1184 }
1185 }
1186
1187 return result;
1188
1189 ErrorExit:
1190
1191 if ( result == btNotFound )
1192 result = cmNotFound;
1193
1194 return result;
1195
1196 } // end IterateCatalogNode
1197
1198
1199 //_________________________________________________________________________________
1200 // Routine: MoveRenameCatalogNode
1201 //
1202 // Function: Moves and/or rename an existing folder or file CNode.
1203 // Note that when moving a folder, all decendants (its offspring,
1204 // their offspring, etc.) are also moved.
1205 //
1206 // Assumes srcHint contains a text encoding that was set by a GetCatalogNode call
1207 //_________________________________________________________________________________
1208
1209 OSErr
1210 MoveRenameCatalogNode(ExtendedVCB *volume, HFSCatalogNodeID srcParentID, ConstUTF8Param srcName,
1211 UInt32 srcHint, HFSCatalogNodeID dstParentID, ConstUTF8Param dstName,
1212 UInt32 *newHint, UInt32 teHint)
1213 {
1214 CatalogKey srcKey; // 518 bytes
1215 CatalogRecord srcRecord; // 520 bytes
1216 CatalogKey dstKey; // 518 bytes
1217 CatalogKey dstFolderKey; // 518 bytes
1218 HFSCatalogNodeID dstFolderParentID = 0;
1219 UInt32 dstFolderHint;
1220 CatalogName *dstFolderNamePtr = NULL;
1221 CatalogRecord tmpRecord; // 520 bytes
1222 HFSCatalogNodeID threadID;
1223 UInt32 textEncoding;
1224 OSErr result;
1225 Boolean isNewName;
1226 Boolean isHFSPlus = (volume->vcbSigWord == kHFSPlusSigWord);
1227 Boolean isOrigDeleted = false;
1228 short srcNameLen;
1229 short dstNameLen;
1230
1231 textEncoding = teHint;
1232 result = BuildCatalogKeyUTF8(volume, srcParentID, srcName, kUndefinedStrLen, &srcKey, &textEncoding);
1233 ReturnIfError(result);
1234
1235 /* XXX can strlen and bcmp handle NULL pointers? */
1236
1237 srcNameLen = strlen(srcName);
1238 dstNameLen = strlen(dstName);
1239
1240 //--- check if names match
1241
1242 if ((srcNameLen == dstNameLen) && (bcmp(srcName, dstName, srcNameLen) == 0))
1243 {
1244 isNewName = false;
1245 dstKey = srcKey;
1246 if ( isHFSPlus ) {
1247 dstKey.hfsPlus.parentID = dstParentID; // set parent ID
1248 }
1249 else {
1250 dstKey.hfs.parentID = dstParentID; // set parent ID
1251 }
1252 }
1253 else /* names are different */
1254 {
1255 isNewName = true;
1256 result = BuildCatalogKeyUTF8(volume, dstParentID, dstName, kUndefinedStrLen, &dstKey, &textEncoding);
1257 ReturnIfError(result);
1258 }
1259
1260 //--- make sure source record exists
1261
1262 result = LocateCatalogNodeByKey(volume, srcHint, &srcKey, &srcRecord, &srcHint);
1263
1264 // if we did not find it by name, then look for an embedded file ID in a mangled name
1265 if ( (result == cmNotFound) && isHFSPlus )
1266 result = LocateCatalogNodeByMangledName(volume, srcParentID, srcName, kUndefinedStrLen, &srcKey, &srcRecord, &srcHint);
1267 /*
1268 * In Mac OS X there can also be HFS filenames that
1269 * could not be encoded using the default encoding.
1270 */
1271 if (result == cmNotFound && !isHFSPlus && VCBTOHFS(volume)->hfs_encoding != 0) {
1272 Str31 hfsName;
1273
1274 utf8_to_mac_roman(strlen(srcName), srcName, hfsName);
1275 result = LocateCatalogNode(volume, srcParentID, (CatalogName*)hfsName,
1276 0, &srcKey, &srcRecord, &srcHint);
1277 }
1278 ReturnIfError(result);
1279
1280 srcParentID = (isHFSPlus ? srcKey.hfsPlus.parentID : srcKey.hfs.parentID);
1281
1282 // if we're moving then do some additional preflighting...
1283
1284 if (srcParentID != dstParentID)
1285 {
1286 //--- make sure destination folder exists
1287
1288 result = LocateCatalogNode(volume, dstParentID, NULL, kNoHint, &dstFolderKey, &tmpRecord, &dstFolderHint);
1289 ReturnIfError(result);
1290
1291 if (tmpRecord.recordType == kHFSPlusFolderRecord)
1292 {
1293 dstParentID = tmpRecord.hfsPlusFolder.folderID;
1294 dstFolderParentID = dstFolderKey.hfsPlus.parentID;
1295 dstFolderNamePtr = (CatalogName*) &dstFolderKey.hfsPlus.nodeName;
1296 }
1297 else if (tmpRecord.recordType == kHFSFolderRecord)
1298 {
1299 dstParentID = tmpRecord.hfsFolder.folderID;
1300 dstFolderParentID = dstFolderKey.hfs.parentID;
1301 dstFolderNamePtr = (CatalogName*) &dstFolderKey.hfs.nodeName;
1302 }
1303 else
1304 {
1305 return badMovErr;
1306 }
1307
1308 //--- if source is a folder, make sure its a proper move
1309
1310 if (srcRecord.recordType == kHFSPlusFolderRecord || srcRecord.recordType == kHFSFolderRecord)
1311 {
1312 HFSCatalogNodeID srcFolderID;
1313 HFSCatalogNodeID ancestorParentID;
1314 CatalogKey tempKey; // 518 bytes
1315 UInt32 tempHint;
1316
1317 if (isHFSPlus)
1318 {
1319 srcFolderID = srcRecord.hfsPlusFolder.folderID;
1320 ancestorParentID = dstFolderKey.hfsPlus.parentID;
1321 }
1322 else
1323 {
1324 srcFolderID = srcRecord.hfsFolder.folderID;
1325 ancestorParentID = dstFolderKey.hfs.parentID;
1326 }
1327
1328 if ( srcFolderID == fsRtDirID || // source == root?
1329 srcFolderID == dstParentID || // source == destination?
1330 srcFolderID == ancestorParentID ) // source == destination's parent?
1331 {
1332 return badMovErr;
1333 }
1334
1335 while (ancestorParentID > fsRtDirID) // loop until we reach the root folder
1336 {
1337 // locate next folder up the tree...
1338 result = LocateCatalogNode(volume, ancestorParentID, NULL, kNoHint, &tempKey, &tmpRecord, &tempHint);
1339 ReturnIfError(result);
1340
1341 ancestorParentID = isHFSPlus ? tempKey.hfsPlus.parentID : tempKey.hfs.parentID;
1342
1343 if (srcFolderID == ancestorParentID) // source = destination ancestor?
1344 return badMovErr;
1345 }
1346 }
1347
1348 TrashCatalogIterator(volume, dstParentID); // invalidate any iterators for destination parentID
1349 }
1350 else /* (srcParentID == dstParentID) */
1351 {
1352 if ( !isNewName )
1353 {
1354 *newHint = srcHint; // they match, so we're all done!
1355 return noErr;
1356 }
1357 }
1358
1359 TrashCatalogIterator(volume, srcParentID); // invalidate any iterators for source's parentID
1360 InvalidateCatalogNodeCache(volume, srcParentID); // invalidate node cache since parent changed
1361
1362 if (isNewName && isHFSPlus)
1363 {
1364 // update textEncoding hint (works for folders and files)
1365 srcRecord.hfsPlusFolder.textEncoding = textEncoding;
1366
1367 UpdateVolumeEncodings(volume, textEncoding);
1368 }
1369
1370 //--- insert source CNode record in BTree with new key (a new parent id and/or new name)
1371
1372 result = InsertBTreeRecord(volume->catalogRefNum, &dstKey, &srcRecord, GetCatalogRecordSize(&srcRecord), newHint);
1373
1374 if (result == btExists)
1375 {
1376 UInt16 dataSize;
1377
1378 /* XXX what about the case: move id1,foo to id2,FOO ?? */
1379 if (srcParentID != dstParentID || isNewName == false)
1380 return cmExists;
1381
1382 //--- new CNode name already exists in the same folder, locate the existing one
1383 result = SearchBTreeRecord(volume->catalogRefNum, &dstKey, srcHint,
1384 &dstFolderKey, &tmpRecord, &dataSize, newHint);
1385
1386 if (result == btNotFound)
1387 result = cmNotFound;
1388 ReturnIfError(result);
1389
1390 //--- check if its the same CNode (same name but different upper/lower case)
1391
1392 if (srcRecord.recordType != tmpRecord.recordType)
1393 return cmExists;
1394
1395 switch (srcRecord.recordType)
1396 {
1397 case kHFSPlusFileRecord: /* HFS Plus records share same cnid location */
1398 case kHFSPlusFolderRecord:
1399 if (srcRecord.hfsPlusFolder.folderID != tmpRecord.hfsPlusFolder.folderID)
1400 return cmExists;
1401 break;
1402
1403 case kHFSFolderRecord:
1404 if (srcRecord.hfsFolder.folderID != tmpRecord.hfsFolder.folderID)
1405 return cmExists;
1406 break;
1407
1408 case kHFSFileRecord:
1409 if (srcRecord.hfsFile.fileID != tmpRecord.hfsFile.fileID)
1410 return cmExists;
1411 break;
1412
1413 default:
1414 return cmExists;
1415 }
1416
1417 //--- same name but different case, so delete old and insert with new name...
1418
1419 result = DeleteBTreeRecord(volume->catalogRefNum, &srcKey);
1420 ReturnIfError(result);
1421 isOrigDeleted = true; // So we dont delete it again down below
1422
1423 result = InsertBTreeRecord(volume->catalogRefNum, &dstKey, &srcRecord, dataSize, newHint);
1424 }
1425 ReturnIfError(result);
1426
1427 //
1428 // from this point on we need to cleanup (ie delete the new record) if we encounter errors!
1429 //
1430
1431 //--- update thread record for node (if it exists)
1432
1433 switch (srcRecord.recordType)
1434 {
1435 case kHFSPlusFileRecord:
1436 case kHFSPlusFolderRecord:
1437 threadID = srcRecord.hfsPlusFolder.folderID;
1438 break;
1439
1440 case kHFSFolderRecord:
1441 threadID = srcRecord.hfsFolder.folderID;
1442 break;
1443
1444 case kHFSFileRecord:
1445 if (srcRecord.hfsFile.flags & kHFSThreadExistsMask)
1446 {
1447 threadID = srcRecord.hfsFile.fileID;
1448 break;
1449 }
1450 /* fall through if no thread... */
1451
1452 default:
1453 threadID = 0;
1454 }
1455
1456 if (threadID)
1457 {
1458 UInt32 threadHint;
1459 CatalogKey threadKey; // 518 bytes
1460 CatalogRecord threadRecord; // 520 bytes
1461 UInt16 threadSize;
1462
1463 result = LocateCatalogRecord(volume, threadID, NULL, kNoHint, &threadKey, &threadRecord, &threadHint);
1464 if (result != noErr) goto Exit_Delete;
1465
1466 if (isHFSPlus)
1467 {
1468 if (srcParentID != dstParentID)
1469 threadRecord.hfsPlusThread.parentID = dstParentID;
1470 if (isNewName)
1471 CopyCatalogName((CatalogName *)&dstKey.hfsPlus.nodeName, (CatalogName *) &threadRecord.hfsPlusThread.nodeName, isHFSPlus);
1472
1473 threadSize = sizeof(threadRecord.hfsPlusThread);
1474 // HFS Plus has varaible sized threads so adjust to actual length
1475 threadSize -= ( sizeof(threadRecord.hfsPlusThread.nodeName.unicode) - (threadRecord.hfsPlusThread.nodeName.length * sizeof(UniChar)) );
1476 }
1477 else
1478 {
1479 if (srcParentID != dstParentID)
1480 threadRecord.hfsThread.parentID = dstParentID;
1481 if (isNewName)
1482 CopyCatalogName((CatalogName *)&dstKey.hfs.nodeName,(CatalogName *) threadRecord.hfsThread.nodeName, isHFSPlus);
1483
1484 threadSize = sizeof(threadRecord.hfsThread);
1485 }
1486
1487 result = DeleteBTreeRecord(volume->catalogRefNum, &threadKey);
1488 if (result != noErr) goto Exit_Delete;
1489
1490 result = InsertBTreeRecord(volume->catalogRefNum, &threadKey, &threadRecord, threadSize, &threadHint);
1491 if (result != noErr) goto Exit_Delete; //XXX exiting with a missing thread!
1492 }
1493
1494 //--- we successfully added the new node so delete the old source CNode record
1495
1496 if (! isOrigDeleted) {
1497 result = DeleteBTreeRecord(volume->catalogRefNum, &srcKey);
1498 if (result)
1499 {
1500 // uh oh, we could not delete the original
1501 // so we better get rid of the new node...
1502
1503 (void) DeleteBTreeRecord(volume->catalogRefNum, &dstKey);
1504
1505 //XXX also need to fix up the thread...
1506
1507 return result;
1508 }
1509 }
1510
1511 if (srcParentID != dstParentID)
1512 {
1513 result = UpdateFolderCount(volume, srcParentID, NULL, srcRecord.recordType, kNoHint, -1);
1514 result = UpdateFolderCount(volume, dstFolderParentID, dstFolderNamePtr, srcRecord.recordType, dstFolderHint, +1);
1515 }
1516
1517 //--- make sure changes get flushed out
1518 VCB_LOCK(volume);
1519 volume->vcbFlags |= 0xFF00; // Mark the VCB dirty
1520 volume->vcbLsMod = GetTimeUTC(); // update last modified date
1521 VCB_UNLOCK(volume);
1522
1523 (void) FlushCatalog(volume);
1524
1525 return result;
1526
1527
1528 Exit_Delete:
1529 (void) DeleteBTreeRecord(volume->catalogRefNum, &dstKey);
1530
1531 return result;
1532
1533 } // end MoveRenameCatalogNode
1534
1535
1536 //_________________________________________________________________________________
1537 // Routine: UpdateCatalogNode
1538 //
1539 // Function: Marks the Catalog BTree node identified by the given catalog hint
1540 // as 'dirty'.
1541 //
1542 //_________________________________________________________________________________
1543
1544
1545 OSErr
1546 UpdateCatalogNode(ExtendedVCB *volume, HFSCatalogNodeID parentID, ConstUTF8Param name,
1547 UInt32 catalogHint, const CatalogNodeData *nodeData)
1548 {
1549 CatalogKey *key;
1550 CatalogRecord *record;
1551 UInt32 hint;
1552 UInt16 recordSize;
1553 OSErr result;
1554 CatalogKey catalogKey; // 518 bytes
1555 CatalogRecord catalogRecord; // 520 bytes
1556 Boolean isHFSPlus = volume->vcbSigWord == kHFSPlusSigWord;
1557
1558 /* XXX no reason to have ptrs to local variables... */
1559 key = &catalogKey;
1560 record = &catalogRecord;
1561
1562 result = BuildCatalogKeyUTF8(volume, parentID, name, kUndefinedStrLen, key, NULL);
1563 ReturnIfError(result);
1564
1565 //--- locate subject catalog node
1566
1567 result = LocateCatalogNodeByKey(volume, catalogHint, key, record, &hint);
1568
1569 // if we did not find it by name, then look for an embedded file ID in a mangled name
1570 if ( (result == cmNotFound) && isHFSPlus )
1571 result = LocateCatalogNodeByMangledName(volume, parentID, name, kUndefinedStrLen, key, record, &hint);
1572
1573 /*
1574 * In Mac OS X there can also be HFS filenames that
1575 * could not be encoded using the default encoding.
1576 */
1577 if (result == cmNotFound && !isHFSPlus && VCBTOHFS(volume)->hfs_encoding != 0) {
1578 Str31 hfsName;
1579
1580 utf8_to_mac_roman(strlen(name), name, hfsName);
1581 result = LocateCatalogNode(volume, parentID, (CatalogName*)hfsName,
1582 0, key, record, &hint);
1583 }
1584 if (result == btNotFound)
1585 result = cmNotFound;
1586
1587 if (catalogHint != hint)
1588 PRINTIT(("UpdateCatalogNode: catalogHint does not match (in: %ld, out: %ld)\n", catalogHint, hint));
1589 ReturnIfError(result);
1590
1591 // update user modifiable fields in the catalog node record...
1592
1593 switch (record->recordType)
1594 {
1595 case kHFSFolderRecord:
1596 {
1597 #if DEBUG_BUILD
1598 if (nodeData->cnd_type != kCatalogFolderNode)
1599 DebugStr("\p UpdateCatalogNode: folder/file mismatch!");
1600 #endif
1601
1602 record->hfsFolder.createDate = UTCToLocal(nodeData->cnd_createDate);
1603 record->hfsFolder.modifyDate = UTCToLocal(nodeData->cnd_contentModDate);
1604 record->hfsFolder.backupDate = UTCToLocal(nodeData->cnd_backupDate);
1605
1606 *(DInfo*) &record->hfsFolder.userInfo = *(DInfo*) &nodeData->cnd_finderInfo;
1607 *(DXInfo*) &record->hfsFolder.finderInfo = *(DXInfo*) ((UInt32)&nodeData->cnd_finderInfo + 16);
1608
1609 recordSize = sizeof(HFSCatalogFolder);
1610 break;
1611 }
1612
1613 case kHFSFileRecord:
1614 {
1615 UInt32 i;
1616
1617 #if DEBUG_BUILD
1618 if (nodeData->cnd_type != kCatalogFileNode)
1619 DebugStr("UpdateCatalogNode: folder/file mismatch!");
1620 if ((nodeData->cnd_datafork.totalBlocks > (0x7FFFFFFF/volume->blockSize)) ||
1621 (nodeData->cnd_rsrcfork.totalBlocks > (0x7FFFFFFF/volume->blockSize)))
1622 DebugStr("HFS file size is larger than 2Gig");
1623 #endif
1624
1625 record->hfsFile.flags = (UInt8) nodeData->cnd_flags;
1626 record->hfsFile.createDate = UTCToLocal(nodeData->cnd_createDate);
1627 record->hfsFile.modifyDate = UTCToLocal(nodeData->cnd_contentModDate);
1628 record->hfsFile.backupDate = UTCToLocal(nodeData->cnd_backupDate);
1629
1630 record->hfsFile.dataLogicalSize = nodeData->cnd_datafork.logicalSize;
1631 record->hfsFile.dataPhysicalSize = nodeData->cnd_datafork.totalBlocks * volume->blockSize;
1632 record->hfsFile.rsrcLogicalSize = nodeData->cnd_rsrcfork.logicalSize;
1633 record->hfsFile.rsrcPhysicalSize = nodeData->cnd_rsrcfork.totalBlocks * volume->blockSize;
1634
1635 *(FInfo*) &record->hfsFile.userInfo = *(FInfo*) &nodeData->cnd_finderInfo;
1636 *(FXInfo*) &record->hfsFile.finderInfo = *(FXInfo*) ((UInt32)&nodeData->cnd_finderInfo + 16);
1637
1638 // copy extent info
1639 for (i = 0; i < kHFSExtentDensity; ++i)
1640 {
1641 record->hfsFile.dataExtents[i].startBlock =
1642 (UInt16) nodeData->cnd_datafork.extents[i].startBlock;
1643 record->hfsFile.dataExtents[i].blockCount =
1644 (UInt16) nodeData->cnd_datafork.extents[i].blockCount;
1645 record->hfsFile.rsrcExtents[i].startBlock =
1646 (UInt16) nodeData->cnd_rsrcfork.extents[i].startBlock;
1647 record->hfsFile.rsrcExtents[i].blockCount =
1648 (UInt16) nodeData->cnd_rsrcfork.extents[i].blockCount;
1649 }
1650
1651 recordSize = sizeof(HFSCatalogFile);
1652 break;
1653 }
1654
1655 case kHFSPlusFolderRecord:
1656 {
1657 record->hfsPlusFolder.createDate = nodeData->cnd_createDate;
1658 record->hfsPlusFolder.contentModDate = nodeData->cnd_contentModDate;
1659 record->hfsPlusFolder.backupDate = nodeData->cnd_backupDate;
1660 record->hfsPlusFolder.accessDate = nodeData->cnd_accessDate;
1661 record->hfsPlusFolder.attributeModDate = nodeData->cnd_attributeModDate;
1662 record->hfsPlusFolder.bsdInfo.ownerID = nodeData->cnd_ownerID;
1663 record->hfsPlusFolder.bsdInfo.groupID = nodeData->cnd_groupID;
1664 record->hfsPlusFolder.bsdInfo.ownerFlags = nodeData->cnd_ownerFlags;
1665 record->hfsPlusFolder.bsdInfo.adminFlags = nodeData->cnd_adminFlags;
1666 record->hfsPlusFolder.bsdInfo.fileMode = nodeData->cnd_mode;
1667 record->hfsPlusFolder.textEncoding = nodeData->cnd_textEncoding;
1668
1669 BlockMoveData(&nodeData->cnd_finderInfo, &record->hfsPlusFolder.userInfo, 32);
1670
1671 recordSize = sizeof(HFSPlusCatalogFolder);
1672 break;
1673 }
1674
1675 case kHFSPlusFileRecord:
1676 {
1677 record->hfsPlusFile.flags = nodeData->cnd_flags;
1678 record->hfsPlusFile.createDate = nodeData->cnd_createDate;
1679 record->hfsPlusFile.contentModDate = nodeData->cnd_contentModDate;
1680 record->hfsPlusFile.backupDate = nodeData->cnd_backupDate;
1681 record->hfsPlusFile.accessDate = nodeData->cnd_accessDate;
1682 record->hfsPlusFile.attributeModDate = nodeData->cnd_attributeModDate;
1683 record->hfsPlusFile.bsdInfo.ownerID = nodeData->cnd_ownerID;
1684 record->hfsPlusFile.bsdInfo.groupID = nodeData->cnd_groupID;
1685 record->hfsPlusFile.bsdInfo.ownerFlags = nodeData->cnd_ownerFlags;
1686 record->hfsPlusFile.bsdInfo.adminFlags = nodeData->cnd_adminFlags;
1687 record->hfsPlusFile.bsdInfo.fileMode = nodeData->cnd_mode;
1688 /* get special value (iNodeNum, linkCount or rawDevice) */
1689 record->hfsPlusFile.bsdInfo.special.rawDevice = nodeData->cnd_rawDevice;
1690 record->hfsPlusFile.textEncoding = nodeData->cnd_textEncoding;
1691
1692 record->hfsPlusFile.dataFork.logicalSize = nodeData->cnd_datafork.logicalSize;
1693 record->hfsPlusFile.dataFork.totalBlocks = nodeData->cnd_datafork.totalBlocks;
1694 BlockMoveData(&nodeData->cnd_datafork.extents,
1695 &record->hfsPlusFile.dataFork.extents, sizeof(HFSPlusExtentRecord));
1696
1697 record->hfsPlusFile.resourceFork.logicalSize = nodeData->cnd_rsrcfork.logicalSize;
1698 record->hfsPlusFile.resourceFork.totalBlocks = nodeData->cnd_rsrcfork.totalBlocks;
1699 BlockMoveData(&nodeData->cnd_rsrcfork.extents,
1700 &record->hfsPlusFile.resourceFork.extents, sizeof(HFSPlusExtentRecord));
1701
1702 BlockMoveData(&nodeData->cnd_finderInfo, &record->hfsPlusFile.userInfo, 32);
1703
1704 #if HFS_HARDLINKS && DEBUG_BUILD
1705 /* Must swap opaque finder data */
1706 if (SWAP_BE32 (record->hfsPlusFile.userInfo.fdType) == kHardLinkFileType &&
1707 SWAP_BE32 (record->hfsPlusFile.userInfo.fdCreator) == kHardLinkCreator) {
1708 if (record->hfsPlusFile.dataFork.logicalSize != 0)
1709 DebugStr("UpdateCatalogNode: link has data fork!");
1710 }
1711 #endif
1712 recordSize = sizeof(HFSPlusCatalogFile);
1713 break;
1714 }
1715
1716 default:
1717 return cmNotFound;
1718 }
1719
1720 result = ReplaceBTreeRecord(volume->catalogRefNum, key, catalogHint, record, recordSize, &hint);
1721
1722 if ( result == btNotFound )
1723 {
1724 result = cmNotFound;
1725 }
1726 else if ( result == noErr )
1727 {
1728 /* if we're just updating the accessDate then no need to change volume mod date */
1729 if (nodeData->cnd_contentModDate > volume->vcbLsMod ||
1730 (isHFSPlus && nodeData->cnd_attributeModDate > volume->vcbLsMod))
1731 {
1732 VCB_LOCK(volume);
1733 volume->vcbFlags |= 0xFF00; // Mark the VCB dirty
1734 volume->vcbLsMod = GetTimeUTC(); // update last modified date
1735 VCB_UNLOCK(volume);
1736 }
1737
1738 result = FlushCatalog(volume); // flush the catalog
1739 }
1740
1741 return result;
1742 }
1743
1744
1745 //_________________________________________________________________________________
1746 // Routine: CreateFileIDRef
1747 //
1748 // Function: Creates a file thread record for hfs file node
1749 //
1750 //_________________________________________________________________________________
1751
1752 OSErr
1753 CreateFileIDRef(ExtendedVCB *volume, HFSCatalogNodeID parentID, ConstUTF8Param name, UInt32 hint, HFSCatalogNodeID *threadID)
1754 {
1755 CatalogKey nodeKey; // 518 bytes
1756 CatalogRecord nodeData; // 520 bytes
1757 HFSCatalogKey threadKey;
1758 HFSCatalogThread threadData;
1759 UInt32 nodeHint;
1760 UInt32 tempHint;
1761 OSErr result;
1762 Boolean isHFSPlus = (volume->vcbSigWord == kHFSPlusSigWord);
1763
1764 *threadID = 0;
1765
1766 result = BuildCatalogKeyUTF8(volume, parentID, name, kUndefinedStrLen, &nodeKey, NULL);
1767 ReturnIfError(result);
1768
1769 //--- locate subject catalog node
1770
1771 result = LocateCatalogNodeByKey(volume, hint, &nodeKey, &nodeData, &nodeHint);
1772
1773 // if we did not find it by name, then look for an embedded file ID in a mangled name
1774 if ( (result == cmNotFound) && isHFSPlus )
1775 result = LocateCatalogNodeByMangledName(volume, parentID, name, kUndefinedStrLen, &nodeKey, &nodeData, &nodeHint);
1776 /*
1777 * In Mac OS X there can also be HFS filenames that
1778 * could not be encoded using the default encoding.
1779 */
1780 if ((result == cmNotFound) && !isHFSPlus && VCBTOHFS(volume)->hfs_encoding != 0) {
1781 Str31 hfsName;
1782
1783 utf8_to_mac_roman(strlen(name), name, hfsName);
1784 result = LocateCatalogNode(volume, parentID, (CatalogName*)hfsName,
1785 0, &nodeKey, &nodeData, &nodeHint);
1786 }
1787 ReturnIfError(result);
1788
1789 if (nodeData.recordType == kHFSPlusFileRecord)
1790 {
1791 *threadID = nodeData.hfsPlusFile.fileID;
1792 return noErr; // already have one
1793 }
1794
1795 if (nodeData.recordType != kHFSFileRecord)
1796 {
1797 return notAFileErr;
1798 }
1799
1800
1801 if (nodeData.hfsFile.flags & kHFSThreadExistsMask)
1802 {
1803 *threadID = nodeData.hfsFile.fileID;
1804 return noErr; // already have one
1805 }
1806
1807 result = VolumeWritable( volume );
1808 if ( result != noErr ) return result;
1809
1810 //
1811 // need to insert a thread record
1812 //
1813 BuildCatalogKey(nodeData.hfsFile.fileID, NULL, false, (CatalogKey *)&threadKey);
1814
1815 ClearMemory(&threadData, sizeof(HFSCatalogThread));
1816 threadData.recordType = kHFSFileThreadRecord;
1817 threadData.parentID = nodeKey.hfs.parentID;
1818 BlockMoveData(&nodeKey.hfs.nodeName, &threadData.nodeName, nodeKey.hfs.nodeName[0] + 1);
1819
1820 result = InsertBTreeRecord(volume->catalogRefNum, &threadKey, &threadData, sizeof(HFSCatalogThread), &tempHint);
1821 if (result == btExists) result = noErr; //XXX could return cmExists or fidExists
1822 ReturnIfError(result);
1823
1824 //
1825 // Finally, set the flag in the file record to say this file has a thread record.
1826 //
1827 nodeData.hfsFile.flags |= kHFSThreadExistsMask;
1828 result = ReplaceBTreeRecord(volume->catalogRefNum, &nodeKey, nodeHint, &nodeData, sizeof(HFSCatalogFile), &nodeHint );
1829
1830 if (result == noErr) {
1831 (void) FlushCatalog(volume);
1832 *threadID = nodeData.hfsFile.fileID;
1833 }
1834
1835 return result;
1836 }
1837
1838
1839 //_________________________________________________________________________________
1840 // Routine: CompareCatalogKeys
1841 //
1842 // Function: Compares two catalog keys (a search key and a trial key).
1843 //
1844 // Result: +n search key > trial key
1845 // 0 search key = trial key
1846 // -n search key < trial key
1847 //_________________________________________________________________________________
1848
1849 SInt32
1850 CompareCatalogKeys(HFSCatalogKey *searchKey, HFSCatalogKey *trialKey)
1851 {
1852 HFSCatalogNodeID searchParentID, trialParentID;
1853 SInt32 result;
1854
1855 searchParentID = searchKey->parentID;
1856 trialParentID = trialKey->parentID;
1857
1858 if ( searchParentID > trialParentID ) // parent dirID is unsigned
1859 result = 1;
1860 else if ( searchParentID < trialParentID )
1861 result = -1;
1862 else // parent dirID's are equal, compare names
1863 {
1864 #if ( ! FORDISKFIRSTAID )
1865 LogStartTime(kTraceRelString);
1866
1867 result = FastRelString(searchKey->nodeName, trialKey->nodeName);
1868
1869 LogEndTime(kTraceRelString, noErr);
1870 #else
1871 result = (SInt32) RelString_Glue(searchKey->nodeName, trialKey->nodeName);
1872 #endif
1873 }
1874
1875 return result;
1876 }
1877
1878
1879 //_________________________________________________________________________________
1880 // Routine: CompareExtendedCatalogKeys
1881 //
1882 // Function: Compares two large catalog keys (a search key and a trial key).
1883 //
1884 // Result: +n search key > trial key
1885 // 0 search key = trial key
1886 // -n search key < trial key
1887 //_________________________________________________________________________________
1888
1889 SInt32
1890 CompareExtendedCatalogKeys(HFSPlusCatalogKey *searchKey, HFSPlusCatalogKey *trialKey)
1891 {
1892 SInt32 result;
1893 HFSCatalogNodeID searchParentID, trialParentID;
1894
1895 searchParentID = searchKey->parentID;
1896 trialParentID = trialKey->parentID;
1897
1898 if ( searchParentID > trialParentID ) // parent node IDs are unsigned
1899 {
1900 result = 1;
1901 }
1902 else if ( searchParentID < trialParentID )
1903 {
1904 result = -1;
1905 }
1906 else // parent node ID's are equal, compare names
1907 {
1908 if ( searchKey->nodeName.length == 0 || trialKey->nodeName.length == 0 )
1909 result = searchKey->nodeName.length - trialKey->nodeName.length;
1910 else
1911 result = FastUnicodeCompare(&searchKey->nodeName.unicode[0], searchKey->nodeName.length,
1912 &trialKey->nodeName.unicode[0], trialKey->nodeName.length);
1913 }
1914
1915 return result;
1916 }
1917