2 * Copyright (c) 2000 Apple Computer, Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * The contents of this file constitute Original Code as defined in and
7 * are subject to the Apple Public Source License Version 1.1 (the
8 * "License"). You may not use this file except in compliance with the
9 * License. Please obtain a copy of the License at
10 * http://www.apple.com/publicsource and read it before using this file.
12 * This Original Code and all software distributed under the License are
13 * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
14 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
15 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the
17 * License for the specific language governing rights and limitations
20 * @APPLE_LICENSE_HEADER_END@
23 File: CatalogUtilities.c
25 Contains: Private Catalog Manager support routines.
29 Copyright: © 1997-2000 by Apple Computer, Inc., all rights reserved.
35 Other Contact: Mark Day
37 Technology: xxx put technology here xxx
45 Change History (most recent first):
46 <MacOSX> 1/8/99 djb Fixing LocateCatalogNodeByMangledName...
47 <MacOSX> 1/7/99 djb In BuildCatalogKeyUTF8 check name length against NAME_MAX.
48 <MacOSX> 12/7/98 djb Add ExtractTextEncoding routine to get text encodings.
49 <MacOSX> 11/20/98 djb Add support for UTF-8 names.
50 <MacOSX> 8/31/98 djb GetTimeLocal now takes an input.
51 <MacOSX> 4/17/98 djb Add VCB locking.
52 <MacOSX> 4/3/98 djb Removed last name conversion cache from LocateCatalogNodeWithRetry.
53 <MacOSX> 4/2/98 djb InvalidateCatalogNodeCache and TrashCatalogNodeCache are not used in MacOS X.
54 <MacOSX> 03/31/98 djb Sync up with final HFSVolumes.h header file.
56 <CS24> 1/29/98 DSH Add TrashCatalogNodeCache for TrashAllFSCaches API support.
57 <CS23> 12/15/97 djb Radar #2202860, In LocateCatalogNodeByMangledName remap
58 cmParentNotFound error code to cmNotFound.
59 <CS22> 12/10/97 DSH 2201501, Pin the leof and peof to multiple of allocation blocks
61 <CS21> 12/9/97 DSH 2201501, Pin returned leof values to 2^31-1 (SInt32), instead of
63 <CS20> 11/26/97 djb Radar #2005688, 2005461 - need to handle kTextMalformedInputErr.
64 <CS19> 11/25/97 djb Radar #2002357 (again) fix new bug introduced in <CS18>.
65 <CS18> 11/17/97 djb PrepareInputName routine now returns an error.
66 <CS17> 10/19/97 msd Bug 1684586. GetCatInfo and SetCatInfo use only contentModDate.
67 <CS16> 10/17/97 djb Add ConvertInputNameToUnicode for Catalog Create/Rename.
68 <CS15> 10/14/97 djb Fix LocateCatalogNode's MakeFSSpec optimization (radar #1683166)
69 <CS14> 10/13/97 djb Copy text encoding in CopyCatalogNodeData. Fix cut/paste error
70 in VolumeHasEncodings macro. When accessing encoding bitmap use
71 the MapEncodingToIndex and MapIndexToEncoding macros.
72 <CS13> 10/1/97 djb Remove old Catalog Iterator code...
73 <CS12> 9/8/97 msd Make sure a folder's modifyDate is set whenever its
74 contentModDate is set.
75 <CS11> 9/4/97 djb Add MakeFSSpec optimization.
76 <CS10> 9/4/97 msd In CatalogNodeData, change attributeModDate to modifyDate.
77 <CS9> 8/26/97 djb Back out <CS4> (UpdateFolderCount must maintain vcbNmFls for HFS
79 <CS8> 8/14/97 djb Remove hard link support.
80 <CS7> 7/18/97 msd Include LowMemPriv.h.
81 <CS6> 7/16/97 DSH FilesInternal.i renamed FileMgrInternal.i to avoid name
83 <CS5> 7/8/97 DSH Loading PrecompiledHeaders from define passed in on C line
84 <CS4> 6/27/97 msd UpdateFolderCount should update number of root files/folders for
85 HFS volumes, not HFS Plus.
86 <CS3> 6/24/97 djb LocateCatalogNodeWithRetry did not always set result code.
87 <CS2> 6/24/97 djb Add LocateCatalogNodeByMangledName routine
88 <CS1> 6/24/97 djb first checked in
90 #include <sys/param.h>
91 #include <sys/utfconv.h>
94 #include "../headers/FileMgrInternal.h"
95 #include "../headers/BTreesInternal.h"
96 #include "../headers/CatalogPrivate.h"
97 #include "../headers/HFSUnicodeWrappers.h"
101 //*******************************************************************************
102 // Routine: LocateCatalogNode
104 // Function: Locates the catalog record for an existing folder or file
105 // CNode and returns pointers to the key and data records.
107 //*******************************************************************************
110 LocateCatalogNode(const ExtendedVCB
*volume
, HFSCatalogNodeID folderID
, const CatalogName
*name
,
111 UInt32 hint
, CatalogKey
*keyPtr
, CatalogRecord
*dataPtr
, UInt32
*newHint
)
114 CatalogName
*nodeName
= NULL
; /* To ward off uninitialized use warnings from compiler */
115 HFSCatalogNodeID threadParentID
;
118 result
= LocateCatalogRecord(volume
, folderID
, name
, hint
, keyPtr
, dataPtr
, newHint
);
119 ReturnIfError(result
);
121 // if we got a thread record, then go look up real record
122 switch ( dataPtr
->recordType
)
124 case kHFSFileThreadRecord
:
125 case kHFSFolderThreadRecord
:
126 threadParentID
= dataPtr
->hfsThread
.parentID
;
127 nodeName
= (CatalogName
*) &dataPtr
->hfsThread
.nodeName
;
130 case kHFSPlusFileThreadRecord
:
131 case kHFSPlusFolderThreadRecord
:
132 threadParentID
= dataPtr
->hfsPlusThread
.parentID
;
133 nodeName
= (CatalogName
*) &dataPtr
->hfsPlusThread
.nodeName
;
141 if ( threadParentID
) // found a thread
142 result
= LocateCatalogRecord(volume
, threadParentID
, nodeName
, kNoHint
, keyPtr
, dataPtr
, newHint
);
148 // Routine: LocateCatalogNodeByKey
150 // Function: Locates the catalog record for an existing folder or file
151 // CNode and returns the key and data records.
155 LocateCatalogNodeByKey(const ExtendedVCB
*volume
, UInt32 hint
, CatalogKey
*keyPtr
,
156 CatalogRecord
*dataPtr
, UInt32
*newHint
)
159 CatalogName
*nodeName
= NULL
; /* To ward off uninitialized use warnings from compiler */
160 HFSCatalogNodeID threadParentID
;
164 result
= SearchBTreeRecord(volume
->catalogRefNum
, keyPtr
, hint
, keyPtr
,
165 dataPtr
, &tempSize
, newHint
);
166 if (result
== btNotFound
)
168 ReturnIfError(result
);
170 // if we got a thread record, then go look up real record
171 switch ( dataPtr
->recordType
)
173 case kHFSFileThreadRecord
:
174 case kHFSFolderThreadRecord
:
175 threadParentID
= dataPtr
->hfsThread
.parentID
;
176 nodeName
= (CatalogName
*) &dataPtr
->hfsThread
.nodeName
;
179 case kHFSPlusFileThreadRecord
:
180 case kHFSPlusFolderThreadRecord
:
181 threadParentID
= dataPtr
->hfsPlusThread
.parentID
;
182 nodeName
= (CatalogName
*) &dataPtr
->hfsPlusThread
.nodeName
;
190 if ( threadParentID
) // found a thread
191 result
= LocateCatalogRecord(volume
, threadParentID
, nodeName
, kNoHint
, keyPtr
, dataPtr
, newHint
);
198 //*******************************************************************************
199 // Routine: LocateCatalogNodeWithRetry
201 // Function: Locates the catalog record for an existing folder or file node.
202 // For HFS Plus volumes a retry is performed when a catalog node is
203 // not found and the volume contains more than one text encoding.
205 //ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ
207 #define VolumeHasEncodings(v) \
208 ( ((v)->encodingsBitmap != 0 )
210 #define EncodingInstalled(i) \
211 ( (fsVars)->gConversionContext[(i)].toUnicode != 0 )
213 #define EncodingUsedByVolume(v,i) \
214 ( ((v)->encodingsBitmap & (1 << (i))) )
218 LocateCatalogNodeWithRetry (const ExtendedVCB
*volume
, HFSCatalogNodeID folderID
, ConstStr31Param pascalName
, CatalogName
*unicodeName
,
219 UInt32 hint
, CatalogKey
*keyPtr
, CatalogRecord
*dataPtr
, UInt32
*newHint
)
221 TextEncoding defaultEncoding
;
222 TextEncoding encoding
;
223 ItemCount encodingsToTry
;
225 OSErr result
= cmNotFound
;
227 fsVars
= (FSVarsRec
*) LMGetFSMVars(); // used by macros
229 defaultEncoding
= GetDefaultTextEncoding();
230 encodingsToTry
= CountInstalledEncodings();
232 // 1. Try finding file using default encoding (typical case)
236 result
= PrepareInputName(pascalName
, true, defaultEncoding
, unicodeName
);
238 result
= LocateCatalogNode(volume
, folderID
, unicodeName
, hint
, keyPtr
, dataPtr
, newHint
);
242 if ( result
!= cmNotFound
|| encodingsToTry
== 0)
247 // XXX if the pascal string contains all 7-bit ascii then we don't need to do anymore retries
250 // 2. Try finding file using Mac Roman (if not already tried above)
252 if ( defaultEncoding
!= kTextEncodingMacRoman
)
255 result
= PrepareInputName(pascalName
, true, kTextEncodingMacRoman
, unicodeName
);
257 result
= LocateCatalogNode(volume
, folderID
, unicodeName
, hint
, keyPtr
, dataPtr
, newHint
);
261 if ( result
!= cmNotFound
|| encodingsToTry
== 0 )
265 // 3. Try with encodings from disk (if any)
267 if ( VolumeHasEncodings(volume
) ) // any left to try?
271 index
= 0; // since we pre increment this will skip MacRoman (which was already tried above)
273 while ( index
< kMacBaseEncodingCount
)
277 encoding
= MapIndexToEncoding(index
);
279 if ( encoding
== defaultEncoding
)
280 continue; // we did this one already
282 if ( EncodingInstalled(index
) && EncodingUsedByVolume(volume
, index
) )
285 result
= PrepareInputName(pascalName
, true, encoding
, unicodeName
);
287 result
= LocateCatalogNode(volume
, folderID
, unicodeName
, hint
, keyPtr
, dataPtr
, newHint
);
291 if ( result
!= cmNotFound
|| encodingsToTry
== 0 )
297 // 4. Try any remaining encodings (if any)
302 index
= 0; // since we pre increment this will skip MacRoman (which was already tried above)
304 while ( (encodingsToTry
> 0) && (index
< kMacBaseEncodingCount
) )
308 encoding
= MapIndexToEncoding(index
);
310 if ( encoding
== defaultEncoding
)
311 continue; // we did this one already
313 if ( EncodingInstalled(index
) && EncodingUsedByVolume(volume
, index
) == false )
316 result
= PrepareInputName(pascalName
, true, encoding
, unicodeName
);
318 result
= LocateCatalogNode(volume
, folderID
, unicodeName
, hint
, keyPtr
, dataPtr
, newHint
);
322 if ( result
!= cmNotFound
|| encodingsToTry
== 0 )
332 //*******************************************************************************
333 // Routine: LocateCatalogNodeByMangledName
335 // Function: Locates the catalog record associated with a mangled name (if any)
337 //*******************************************************************************
338 #define kMaxCompareLen 64 /* If it compares this far...lets believe it */
341 LocateCatalogNodeByMangledName( const ExtendedVCB
*volume
, HFSCatalogNodeID folderID
,
342 const unsigned char * name
, UInt32 length
, CatalogKey
*keyPtr
,
343 CatalogRecord
*dataPtr
, UInt32
*hintPtr
)
345 HFSCatalogNodeID fileID
;
346 unsigned char nodeName
[kMaxCompareLen
+1];
352 if (name
== NULL
|| name
[0] == '\0')
355 fileID
= GetEmbeddedFileID(name
, length
, &prefixlen
);
357 if ( fileID
< kHFSFirstUserCatalogNodeID
)
360 result
= LocateCatalogNode(volume
, fileID
, NULL
, kNoHint
, keyPtr
, dataPtr
, hintPtr
);
361 if ( result
== cmParentNotFound
) // GetCatalogNode already handled cmParentNotFound case <CS23>
362 result
= cmNotFound
; // so remap <CS23>
363 ReturnIfError(result
);
365 // first make sure that the parents match
366 if ( folderID
!= keyPtr
->hfsPlus
.parentID
)
367 return cmNotFound
; // not the same folder so this is a false match
369 (void) utf8_encodestr(keyPtr
->hfsPlus
.nodeName
.unicode
,
370 keyPtr
->hfsPlus
.nodeName
.length
* sizeof (UniChar
),
371 nodeName
, &actualDstLen
, kMaxCompareLen
+1, ':', 0);
373 prefixlen
= min(prefixlen
, kMaxCompareLen
);
375 if ((prefixlen
- actualDstLen
) < 6)
376 prefixlen
= actualDstLen
; /* To take into account UTF8 rounding */
378 if ( (actualDstLen
< prefixlen
) || bcmp(nodeName
, name
, prefixlen
-6) != 0)
379 return cmNotFound
; // mangled names didn't match so this is a false match
381 return noErr
; // we found it
385 //*******************************************************************************
386 // Routine: LocateCatalogRecord
388 // Function: Locates the catalog record associated with folderID and name
390 //*******************************************************************************
393 LocateCatalogRecord(const ExtendedVCB
*volume
, HFSCatalogNodeID folderID
, const CatalogName
*name
,
394 UInt32 hint
, CatalogKey
*keyPtr
, CatalogRecord
*dataPtr
, UInt32
*newHint
)
397 CatalogKey tempKey
; // 518 bytes
400 BuildCatalogKey(folderID
, name
, (volume
->vcbSigWord
== kHFSPlusSigWord
), &tempKey
);
403 hint
= kNoHint
; // no CName given so clear the hint
405 result
= SearchBTreeRecord(volume
->catalogRefNum
, &tempKey
, hint
, keyPtr
, dataPtr
, &tempSize
, newHint
);
407 return (result
== btNotFound
? cmNotFound
: result
);
411 //*******************************************************************************
412 // Routine: LocateCatalogThread
414 // Function: Locates a catalog thread record in the catalog BTree file and
415 // returns a pointer to the data record.
417 //*******************************************************************************
420 LocateCatalogThread(const ExtendedVCB
*volume
, HFSCatalogNodeID nodeID
, CatalogRecord
*threadData
, UInt16
*threadSize
, UInt32
*threadHint
)
422 CatalogKey threadKey
; // 518 bytes
425 //--- build key record
427 BuildCatalogKey(nodeID
, NULL
, (volume
->vcbSigWord
== kHFSPlusSigWord
), &threadKey
);
429 //--- locate thread record in BTree
431 result
= SearchBTreeRecord( volume
->catalogRefNum
, &threadKey
, kNoHint
, &threadKey
,
432 threadData
, threadSize
, threadHint
);
434 return (result
== btNotFound
? cmNotFound
: result
);
439 * Routine: BuildCatalogKey
441 * Function: Constructs a catalog key record (ckr) given the parent
442 * folder ID and CName. Works for both classic and extended
448 BuildCatalogKey(HFSCatalogNodeID parentID
, const CatalogName
*cName
, Boolean isHFSPlus
, CatalogKey
*key
)
452 key
->hfsPlus
.keyLength
= kHFSPlusCatalogKeyMinimumLength
; // initial key length (4 + 2)
453 key
->hfsPlus
.parentID
= parentID
; // set parent ID
454 key
->hfsPlus
.nodeName
.length
= 0; // null CName length
457 CopyCatalogName(cName
, (CatalogName
*) &key
->hfsPlus
.nodeName
, isHFSPlus
);
458 key
->hfsPlus
.keyLength
+= sizeof(UniChar
) * cName
->ustr
.length
; // add CName size to key length
463 key
->hfs
.keyLength
= kHFSCatalogKeyMinimumLength
; // initial key length (1 + 4 + 1)
464 key
->hfs
.reserved
= 0; // clear unused byte
465 key
->hfs
.parentID
= parentID
; // set parent ID
466 key
->hfs
.nodeName
[0] = 0; // null CName length
469 UpdateCatalogName(cName
->pstr
, key
->hfs
.nodeName
);
470 key
->hfs
.keyLength
+= key
->hfs
.nodeName
[0]; // add CName size to key length
476 BuildCatalogKeyUTF8(ExtendedVCB
*volume
, HFSCatalogNodeID parentID
, const char *name
, UInt32 nameLength
,
477 CatalogKey
*key
, UInt32
*textEncoding
)
483 else if (nameLength
== kUndefinedStrLen
)
484 nameLength
= strlen(name
);
486 if ( volume
->vcbSigWord
== kHFSPlusSigWord
) {
487 size_t unicodeBytes
= 0;
489 key
->hfsPlus
.keyLength
= kHFSPlusCatalogKeyMinimumLength
; // initial key length (4 + 2)
490 key
->hfsPlus
.parentID
= parentID
; // set parent ID
491 key
->hfsPlus
.nodeName
.length
= 0; // null CName length
492 if ( nameLength
> 0 ) {
493 err
= utf8_decodestr(name
, nameLength
, key
->hfsPlus
.nodeName
.unicode
,
494 &unicodeBytes
, sizeof(key
->hfsPlus
.nodeName
.unicode
), ':', UTF_DECOMPOSED
);
495 key
->hfsPlus
.nodeName
.length
= unicodeBytes
/ sizeof(UniChar
);
496 key
->hfsPlus
.keyLength
+= unicodeBytes
;
499 if (textEncoding
&& (*textEncoding
!= kTextEncodingMacUnicode
))
500 *textEncoding
= hfs_pickencoding(key
->hfsPlus
.nodeName
.unicode
,
501 key
->hfsPlus
.nodeName
.length
);
504 key
->hfs
.keyLength
= kHFSCatalogKeyMinimumLength
; // initial key length (1 + 4 + 1)
505 key
->hfs
.reserved
= 0; // clear unused byte
506 key
->hfs
.parentID
= parentID
; // set parent ID
507 key
->hfs
.nodeName
[0] = 0; // null CName length
508 if ( nameLength
> 0 ) {
509 err
= utf8_to_hfs(volume
, nameLength
, name
, &key
->hfs
.nodeName
[0]);
511 * Retry with MacRoman in case that's how it was exported.
512 * When textEncoding != NULL we know that this is a create
513 * or rename call and can skip the retry (ugly but it works).
515 if (err
&& (textEncoding
== NULL
))
516 err
= utf8_to_mac_roman(nameLength
, name
, &key
->hfs
.nodeName
[0]);
517 key
->hfs
.keyLength
+= key
->hfs
.nodeName
[0]; // add CName size to key length
524 if (err
== ENAMETOOLONG
)
525 err
= bdNamErr
; /* name is too long */
527 err
= paramErr
; /* name has invalid characters */
534 //*******************************************************************************
535 // Routine: FlushCatalog
537 // Function: Flushes the catalog for a specified volume.
539 //*******************************************************************************
542 FlushCatalog(ExtendedVCB
*volume
)
547 fcb
= GetFileControlBlock(volume
->catalogRefNum
);
548 result
= BTFlushPath(fcb
);
552 //--- check if catalog's fcb is dirty...
554 if ( fcb
->fcbFlags
& fcbModifiedMask
)
557 volume
->vcbFlags
|= 0xFF00; // Mark the VCB dirty
558 volume
->vcbLsMod
= GetTimeUTC(); // update last modified date
561 result
= FlushVolumeControlBlock(volume
);
569 //ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ
570 // Routine: UpdateCatalogName
572 // Function: Updates a CName.
574 //ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ
577 UpdateCatalogName(ConstStr31Param srcName
, Str31 destName
)
579 Size length
= srcName
[0];
581 if (length
> CMMaxCName
)
582 length
= CMMaxCName
; // truncate to max
584 destName
[0] = length
; // set length byte
586 BlockMoveData(&srcName
[1], &destName
[1], length
);
590 //*******************************************************************************
591 // Routine: AdjustVolumeCounts
593 // Function: Adjusts the folder and file counts in the VCB
595 //*******************************************************************************
598 AdjustVolumeCounts(ExtendedVCB
*volume
, SInt16 type
, SInt16 delta
)
600 //\80\80 also update extended VCB fields...
604 if (type
== kHFSFolderRecord
|| type
== kHFSPlusFolderRecord
)
605 volume
->vcbDirCnt
+= delta
; // adjust volume folder count, \80\80 worry about overflow?
607 volume
->vcbFilCnt
+= delta
; // adjust volume file count
609 volume
->vcbFlags
|= 0xFF00; // Mark the VCB dirty
610 volume
->vcbLsMod
= GetTimeUTC(); // update last modified date
616 //*******************************************************************************
619 UpdateVolumeEncodings(ExtendedVCB
*volume
, TextEncoding encoding
)
625 index
= MapEncodingToIndex(encoding
);
629 volume
->encodingsBitmap
|= (1 << index
);
633 // vcb should already be marked dirty
637 //*******************************************************************************
640 UpdateFolderCount( ExtendedVCB
*volume
, HFSCatalogNodeID parentID
, const CatalogName
*name
, SInt16 newType
,
641 UInt32 hint
, SInt16 valenceDelta
)
643 CatalogKey tempKey
; // 518 bytes
644 CatalogRecord tempData
; // 520 bytes
646 HFSCatalogNodeID folderID
;
651 result
= SearchBTreeRecord(volume
->catalogRefNum
, parentKey
, hint
,
652 &tempKey
, &tempData
, &recordSize
, &tempHint
);
654 return (result
== btNotFound
? cmNotFound
: result
);
657 result
= LocateCatalogNode(volume
, parentID
, name
, hint
, &tempKey
, &tempData
, &tempHint
);
658 ReturnIfError(result
);
661 if ( volume
->vcbSigWord
== kHFSPlusSigWord
) // HFS Plus
665 if ( DEBUG_BUILD
&& tempData
.recordType
!= kHFSPlusFolderRecord
)
666 DebugStr("\p UpdateFolder: found HFS folder on HFS+ volume!");
668 timeStamp
= GetTimeUTC();
669 /* adjust valence, but don't go negative */
670 if (valenceDelta
> 0)
671 tempData
.hfsPlusFolder
.valence
+= valenceDelta
;
672 else if (tempData
.hfsPlusFolder
.valence
!= 0)
673 tempData
.hfsPlusFolder
.valence
+= valenceDelta
;
675 volume
->vcbFlags
|= kHFS_DamagedVolume
;
676 tempData
.hfsPlusFolder
.contentModDate
= timeStamp
; // set date/time last modified
677 folderID
= tempData
.hfsPlusFolder
.folderID
;
678 recordSize
= sizeof(tempData
.hfsPlusFolder
);
682 if ( DEBUG_BUILD
&& tempData
.recordType
!= kHFSFolderRecord
)
683 DebugStr("\p UpdateFolder: found HFS+ folder on HFS volume!");
685 /* adjust valence, but don't go negative */
686 if (valenceDelta
> 0)
687 tempData
.hfsFolder
.valence
+= valenceDelta
;
688 else if (tempData
.hfsFolder
.valence
!= 0)
689 tempData
.hfsFolder
.valence
+= valenceDelta
;
691 volume
->vcbFlags
|= kHFS_DamagedVolume
;
692 tempData
.hfsFolder
.modifyDate
= GetTimeLocal(true); // set date/time last modified
693 folderID
= tempData
.hfsFolder
.folderID
;
694 recordSize
= sizeof(tempData
.hfsFolder
);
697 result
= ReplaceBTreeRecord(volume
->catalogRefNum
, &tempKey
, tempHint
,
698 &tempData
, recordSize
, &tempHint
);
699 ReturnIfError(result
);
701 if ( folderID
== kHFSRootFolderID
)
703 if (newType
== kHFSFolderRecord
|| newType
== kHFSPlusFolderRecord
)
706 volume
->vcbNmRtDirs
+= valenceDelta
; // adjust root folder count (undefined for HFS Plus)
712 volume
->vcbNmFls
+= valenceDelta
; // adjust root file count (used by GetVolInfo)
717 //XXX also update extended VCB fields...
723 //*******************************************************************************
726 GetCatalogRecordSize(const CatalogRecord
*dataRecord
)
728 switch (dataRecord
->recordType
)
731 return sizeof(HFSCatalogFile
);
733 case kHFSFolderRecord
:
734 return sizeof(HFSCatalogFolder
);
736 case kHFSPlusFileRecord
:
737 return sizeof(HFSPlusCatalogFile
);
739 case kHFSPlusFolderRecord
:
740 return sizeof(HFSPlusCatalogFolder
);
742 case kHFSFolderThreadRecord
:
743 case kHFSFileThreadRecord
:
744 return sizeof(HFSCatalogThread
);
746 case kHFSPlusFolderThreadRecord
:
747 case kHFSPlusFileThreadRecord
:
748 return sizeof(HFSPlusCatalogThread
);
756 //*******************************************************************************
759 CopyCatalogNodeData(const ExtendedVCB
*volume
, const CatalogRecord
*dataPtr
, CatalogNodeData
*nodeData
)
761 /* convert classic hfs records to hfs plus format */
763 if (dataPtr
->recordType
== kHFSFolderRecord
) {
764 nodeData
->cnd_type
= kCatalogFolderNode
;
765 nodeData
->cnd_flags
= dataPtr
->hfsFolder
.flags
;
766 nodeData
->cnd_nodeID
= dataPtr
->hfsFolder
.folderID
;
767 nodeData
->cnd_createDate
= LocalToUTC(dataPtr
->hfsFolder
.createDate
);
768 nodeData
->cnd_contentModDate
= LocalToUTC(dataPtr
->hfsFolder
.modifyDate
);
769 nodeData
->cnd_attributeModDate
= LocalToUTC(dataPtr
->hfsFolder
.modifyDate
);
770 nodeData
->cnd_accessDate
= LocalToUTC(dataPtr
->hfsFolder
.modifyDate
);
771 nodeData
->cnd_backupDate
= LocalToUTC(dataPtr
->hfsFolder
.backupDate
);
772 nodeData
->cnd_ownerID
= VCBTOHFS(volume
)->hfs_uid
;
773 nodeData
->cnd_groupID
= VCBTOHFS(volume
)->hfs_gid
;
774 nodeData
->cnd_adminFlags
= 0;
775 nodeData
->cnd_ownerFlags
= 0; /* HFS directories cannot be locked */
776 nodeData
->cnd_mode
= IFDIR
| (ACCESSPERMS
& VCBTOHFS(volume
)->hfs_dir_mask
);
777 nodeData
->cnd_valence
= dataPtr
->hfsFolder
.valence
;
779 BlockMoveData(&dataPtr
->hfsFolder
.userInfo
, &nodeData
->cnd_finderInfo
, 32);
780 } else if (dataPtr
->recordType
== kHFSFileRecord
) {
783 nodeData
->cnd_type
= kCatalogFileNode
;
784 nodeData
->cnd_flags
= dataPtr
->hfsFile
.flags
;
785 nodeData
->cnd_nodeID
= dataPtr
->hfsFile
.fileID
;
786 nodeData
->cnd_createDate
= LocalToUTC(dataPtr
->hfsFile
.createDate
);
787 nodeData
->cnd_contentModDate
= LocalToUTC(dataPtr
->hfsFile
.modifyDate
);
788 nodeData
->cnd_attributeModDate
= LocalToUTC(dataPtr
->hfsFolder
.modifyDate
);
789 nodeData
->cnd_accessDate
= LocalToUTC(dataPtr
->hfsFolder
.modifyDate
);
790 nodeData
->cnd_backupDate
= LocalToUTC(dataPtr
->hfsFile
.backupDate
);
791 nodeData
->cnd_ownerID
= VCBTOHFS(volume
)->hfs_uid
;
792 nodeData
->cnd_groupID
= VCBTOHFS(volume
)->hfs_gid
;
793 nodeData
->cnd_adminFlags
= 0;
794 nodeData
->cnd_ownerFlags
= (nodeData
->cnd_flags
& kHFSFileLockedMask
) ? UF_IMMUTABLE
: 0;
795 nodeData
->cnd_mode
= IFREG
| (ACCESSPERMS
& VCBTOHFS(volume
)->hfs_file_mask
);
796 nodeData
->cnd_linkCount
= 0;
798 BlockMoveData(&dataPtr
->hfsFile
.userInfo
, &nodeData
->cnd_finderInfo
, 16);
799 BlockMoveData(&dataPtr
->hfsFile
.finderInfo
, (void*)((UInt32
)&nodeData
->cnd_finderInfo
+ 16), 16);
801 nodeData
->cnd_datafork
.logicalSize
= dataPtr
->hfsFile
.dataLogicalSize
;
802 nodeData
->cnd_datafork
.totalBlocks
=
803 dataPtr
->hfsFile
.dataPhysicalSize
/ volume
->blockSize
;
805 nodeData
->cnd_rsrcfork
.logicalSize
= dataPtr
->hfsFile
.rsrcLogicalSize
;
806 nodeData
->cnd_rsrcfork
.totalBlocks
=
807 dataPtr
->hfsFile
.rsrcPhysicalSize
/ volume
->blockSize
;
809 for (i
= 0; i
< kHFSExtentDensity
; ++i
) {
810 nodeData
->cnd_datafork
.extents
[i
].startBlock
=
811 (UInt32
) (dataPtr
->hfsFile
.dataExtents
[i
].startBlock
);
813 nodeData
->cnd_datafork
.extents
[i
].blockCount
=
814 (UInt32
) (dataPtr
->hfsFile
.dataExtents
[i
].blockCount
);
816 nodeData
->cnd_rsrcfork
.extents
[i
].startBlock
=
817 (UInt32
) (dataPtr
->hfsFile
.rsrcExtents
[i
].startBlock
);
819 nodeData
->cnd_rsrcfork
.extents
[i
].blockCount
=
820 (UInt32
) (dataPtr
->hfsFile
.rsrcExtents
[i
].blockCount
);
822 for (i
= kHFSExtentDensity
; i
< kHFSPlusExtentDensity
; ++i
) {
823 nodeData
->cnd_datafork
.extents
[i
].startBlock
= 0;
824 nodeData
->cnd_datafork
.extents
[i
].blockCount
= 0;
825 nodeData
->cnd_rsrcfork
.extents
[i
].startBlock
= 0;
826 nodeData
->cnd_rsrcfork
.extents
[i
].blockCount
= 0;
829 nodeData
->cnd_type
= 0;
834 //_______________________________________________________________________
837 CopyCatalogName(const CatalogName
*srcName
, CatalogName
*dstName
, Boolean isHFSPLus
)
841 if ( srcName
== NULL
)
843 if ( dstName
!= NULL
)
844 dstName
->ustr
.length
= 0; // set length byte to zero (works for both unicode and pascal)
849 length
= sizeof(UniChar
) * (srcName
->ustr
.length
+ 1);
851 length
= sizeof(UInt8
) + srcName
->pstr
[0];
854 BlockMoveData(srcName
, dstName
, length
);
856 dstName
->ustr
.length
= 0; // set length byte to zero (works for both unicode and pascal)
859 //_______________________________________________________________________
862 CatalogNameLength(const CatalogName
*name
, Boolean isHFSPlus
)
865 return name
->ustr
.length
;
867 return name
->pstr
[0];