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>
93 #include "../headers/FileMgrInternal.h"
94 #include "../headers/BTreesInternal.h"
95 #include "../headers/CatalogPrivate.h"
96 #include "../headers/HFSUnicodeWrappers.h"
99 static void ExtractTextEncoding (ItemCount length
, ConstUniCharArrayPtr string
, UInt32
* textEncoding
);
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 * for HFS, only MacRoman is supported. If a non-MacRoman character is found, an error is returned
479 BuildCatalogKeyUTF8(ExtendedVCB
*volume
, HFSCatalogNodeID parentID
, const char *name
, UInt32 nameLength
,
480 CatalogKey
*key
, UInt32
*textEncoding
)
486 else if (nameLength
== kUndefinedStrLen
)
487 nameLength
= strlen(name
);
489 if ( volume
->vcbSigWord
== kHFSPlusSigWord
) {
490 size_t unicodeBytes
= 0;
492 key
->hfsPlus
.keyLength
= kHFSPlusCatalogKeyMinimumLength
; // initial key length (4 + 2)
493 key
->hfsPlus
.parentID
= parentID
; // set parent ID
494 key
->hfsPlus
.nodeName
.length
= 0; // null CName length
495 if ( nameLength
> 0 ) {
496 err
= utf8_decodestr(name
, nameLength
, key
->hfsPlus
.nodeName
.unicode
,
497 &unicodeBytes
, sizeof(key
->hfsPlus
.nodeName
.unicode
), ':', UTF_DECOMPOSED
);
498 key
->hfsPlus
.nodeName
.length
= unicodeBytes
/ sizeof(UniChar
);
499 key
->hfsPlus
.keyLength
+= unicodeBytes
;
503 ExtractTextEncoding(key
->hfsPlus
.nodeName
.length
, key
->hfsPlus
.nodeName
.unicode
, textEncoding
);
506 key
->hfs
.keyLength
= kHFSCatalogKeyMinimumLength
; // initial key length (1 + 4 + 1)
507 key
->hfs
.reserved
= 0; // clear unused byte
508 key
->hfs
.parentID
= parentID
; // set parent ID
509 key
->hfs
.nodeName
[0] = 0; // null CName length
510 if ( nameLength
> 0 ) {
511 err
= utf8_to_hfs(volume
, nameLength
, name
, &key
->hfs
.nodeName
[0]);
513 * Retry with MacRoman in case that's how it was exported.
514 * When textEncoding != NULL we know that this is a create
515 * or rename call and can skip the retry (ugly but it works).
517 if (err
&& (textEncoding
== NULL
))
518 err
= utf8_to_mac_roman(nameLength
, name
, &key
->hfs
.nodeName
[0]);
519 key
->hfs
.keyLength
+= key
->hfs
.nodeName
[0]; // add CName size to key length
526 if (err
== ENAMETOOLONG
)
527 err
= bdNamErr
; /* name is too long */
529 err
= paramErr
; /* name has invalid characters */
537 * make a guess at the text encoding value that coresponds to the Unicode characters
540 ExtractTextEncoding(ItemCount length
, ConstUniCharArrayPtr string
, UInt32
* textEncoding
)
547 for (i
= 0; i
< length
; ++i
) {
549 /* CJK codepoints are 0x3000 thru 0x9FFF */
552 *textEncoding
= kTextEncodingMacJapanese
;
556 /* fullwidth character codepoints are 0xFF00 thru 0xFFEF */
557 if (ch
>= 0xff00 && ch
<= 0xffef) {
558 *textEncoding
= kTextEncodingMacJapanese
;
566 //*******************************************************************************
567 // Routine: FlushCatalog
569 // Function: Flushes the catalog for a specified volume.
571 //*******************************************************************************
574 FlushCatalog(ExtendedVCB
*volume
)
579 fcb
= GetFileControlBlock(volume
->catalogRefNum
);
580 result
= BTFlushPath(fcb
);
584 //--- check if catalog's fcb is dirty...
586 if ( fcb
->fcbFlags
& fcbModifiedMask
)
589 volume
->vcbFlags
|= 0xFF00; // Mark the VCB dirty
590 volume
->vcbLsMod
= GetTimeUTC(); // update last modified date
593 result
= FlushVolumeControlBlock(volume
);
601 //ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ
602 // Routine: UpdateCatalogName
604 // Function: Updates a CName.
606 //ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ
609 UpdateCatalogName(ConstStr31Param srcName
, Str31 destName
)
611 Size length
= srcName
[0];
613 if (length
> CMMaxCName
)
614 length
= CMMaxCName
; // truncate to max
616 destName
[0] = length
; // set length byte
618 BlockMoveData(&srcName
[1], &destName
[1], length
);
622 //*******************************************************************************
623 // Routine: AdjustVolumeCounts
625 // Function: Adjusts the folder and file counts in the VCB
627 //*******************************************************************************
630 AdjustVolumeCounts(ExtendedVCB
*volume
, SInt16 type
, SInt16 delta
)
632 //\80\80 also update extended VCB fields...
636 if (type
== kHFSFolderRecord
|| type
== kHFSPlusFolderRecord
)
637 volume
->vcbDirCnt
+= delta
; // adjust volume folder count, \80\80 worry about overflow?
639 volume
->vcbFilCnt
+= delta
; // adjust volume file count
641 volume
->vcbFlags
|= 0xFF00; // Mark the VCB dirty
642 volume
->vcbLsMod
= GetTimeUTC(); // update last modified date
648 //*******************************************************************************
651 UpdateVolumeEncodings(ExtendedVCB
*volume
, TextEncoding encoding
)
657 index
= MapEncodingToIndex(encoding
);
661 volume
->encodingsBitmap
|= (1 << index
);
665 // vcb should already be marked dirty
669 //*******************************************************************************
672 UpdateFolderCount( ExtendedVCB
*volume
, HFSCatalogNodeID parentID
, const CatalogName
*name
, SInt16 newType
,
673 UInt32 hint
, SInt16 valenceDelta
)
675 CatalogKey tempKey
; // 518 bytes
676 CatalogRecord tempData
; // 520 bytes
678 HFSCatalogNodeID folderID
;
683 result
= SearchBTreeRecord(volume
->catalogRefNum
, parentKey
, hint
,
684 &tempKey
, &tempData
, &recordSize
, &tempHint
);
686 return (result
== btNotFound
? cmNotFound
: result
);
689 result
= LocateCatalogNode(volume
, parentID
, name
, hint
, &tempKey
, &tempData
, &tempHint
);
690 ReturnIfError(result
);
693 if ( volume
->vcbSigWord
== kHFSPlusSigWord
) // HFS Plus
697 if ( DEBUG_BUILD
&& tempData
.recordType
!= kHFSPlusFolderRecord
)
698 DebugStr("\p UpdateFolder: found HFS folder on HFS+ volume!");
700 timeStamp
= GetTimeUTC();
701 /* adjust valence, but don't go negative */
702 if (valenceDelta
> 0)
703 tempData
.hfsPlusFolder
.valence
+= valenceDelta
;
704 else if (tempData
.hfsPlusFolder
.valence
!= 0)
705 tempData
.hfsPlusFolder
.valence
+= valenceDelta
;
707 volume
->vcbFlags
|= kHFS_DamagedVolume
;
708 tempData
.hfsPlusFolder
.contentModDate
= timeStamp
; // set date/time last modified
709 folderID
= tempData
.hfsPlusFolder
.folderID
;
710 recordSize
= sizeof(tempData
.hfsPlusFolder
);
714 if ( DEBUG_BUILD
&& tempData
.recordType
!= kHFSFolderRecord
)
715 DebugStr("\p UpdateFolder: found HFS+ folder on HFS volume!");
717 /* adjust valence, but don't go negative */
718 if (valenceDelta
> 0)
719 tempData
.hfsFolder
.valence
+= valenceDelta
;
720 else if (tempData
.hfsFolder
.valence
!= 0)
721 tempData
.hfsFolder
.valence
+= valenceDelta
;
723 volume
->vcbFlags
|= kHFS_DamagedVolume
;
724 tempData
.hfsFolder
.modifyDate
= GetTimeLocal(true); // set date/time last modified
725 folderID
= tempData
.hfsFolder
.folderID
;
726 recordSize
= sizeof(tempData
.hfsFolder
);
729 result
= ReplaceBTreeRecord(volume
->catalogRefNum
, &tempKey
, tempHint
,
730 &tempData
, recordSize
, &tempHint
);
731 ReturnIfError(result
);
733 if ( folderID
== kHFSRootFolderID
)
735 if (newType
== kHFSFolderRecord
|| newType
== kHFSPlusFolderRecord
)
738 volume
->vcbNmRtDirs
+= valenceDelta
; // adjust root folder count (undefined for HFS Plus)
744 volume
->vcbNmFls
+= valenceDelta
; // adjust root file count (used by GetVolInfo)
749 //XXX also update extended VCB fields...
755 //*******************************************************************************
758 GetCatalogRecordSize(const CatalogRecord
*dataRecord
)
760 switch (dataRecord
->recordType
)
763 return sizeof(HFSCatalogFile
);
765 case kHFSFolderRecord
:
766 return sizeof(HFSCatalogFolder
);
768 case kHFSPlusFileRecord
:
769 return sizeof(HFSPlusCatalogFile
);
771 case kHFSPlusFolderRecord
:
772 return sizeof(HFSPlusCatalogFolder
);
774 case kHFSFolderThreadRecord
:
775 case kHFSFileThreadRecord
:
776 return sizeof(HFSCatalogThread
);
778 case kHFSPlusFolderThreadRecord
:
779 case kHFSPlusFileThreadRecord
:
780 return sizeof(HFSPlusCatalogThread
);
788 //*******************************************************************************
791 CopyCatalogNodeData(const ExtendedVCB
*volume
, const CatalogRecord
*dataPtr
, CatalogNodeData
*nodeData
)
793 /* convert classic hfs records to hfs plus format */
795 if (dataPtr
->recordType
== kHFSFolderRecord
) {
796 nodeData
->cnd_type
= kCatalogFolderNode
;
797 nodeData
->cnd_flags
= dataPtr
->hfsFolder
.flags
;
798 nodeData
->cnd_nodeID
= dataPtr
->hfsFolder
.folderID
;
799 nodeData
->cnd_createDate
= LocalToUTC(dataPtr
->hfsFolder
.createDate
);
800 nodeData
->cnd_contentModDate
= LocalToUTC(dataPtr
->hfsFolder
.modifyDate
);
801 nodeData
->cnd_backupDate
= LocalToUTC(dataPtr
->hfsFolder
.backupDate
);
802 nodeData
->cnd_valence
= dataPtr
->hfsFolder
.valence
;
804 BlockMoveData(&dataPtr
->hfsFolder
.userInfo
, &nodeData
->cnd_finderInfo
, 32);
805 } else if (dataPtr
->recordType
== kHFSFileRecord
) {
808 nodeData
->cnd_type
= kCatalogFileNode
;
809 nodeData
->cnd_flags
= dataPtr
->hfsFile
.flags
;
810 nodeData
->cnd_nodeID
= dataPtr
->hfsFile
.fileID
;
811 nodeData
->cnd_createDate
= LocalToUTC(dataPtr
->hfsFile
.createDate
);
812 nodeData
->cnd_contentModDate
= LocalToUTC(dataPtr
->hfsFile
.modifyDate
);
813 nodeData
->cnd_backupDate
= LocalToUTC(dataPtr
->hfsFile
.backupDate
);
814 nodeData
->cnd_linkCount
= 0;
816 BlockMoveData(&dataPtr
->hfsFile
.userInfo
, &nodeData
->cnd_finderInfo
, 16);
817 BlockMoveData(&dataPtr
->hfsFile
.finderInfo
, (void*)((UInt32
)&nodeData
->cnd_finderInfo
+ 16), 16);
819 nodeData
->cnd_datafork
.logicalSize
= dataPtr
->hfsFile
.dataLogicalSize
;
820 nodeData
->cnd_datafork
.totalBlocks
=
821 dataPtr
->hfsFile
.dataPhysicalSize
/ volume
->blockSize
;
823 nodeData
->cnd_rsrcfork
.logicalSize
= dataPtr
->hfsFile
.rsrcLogicalSize
;
824 nodeData
->cnd_rsrcfork
.totalBlocks
=
825 dataPtr
->hfsFile
.rsrcPhysicalSize
/ volume
->blockSize
;
827 for (i
= 0; i
< kHFSExtentDensity
; ++i
) {
828 nodeData
->cnd_datafork
.extents
[i
].startBlock
=
829 (UInt32
) (dataPtr
->hfsFile
.dataExtents
[i
].startBlock
);
831 nodeData
->cnd_datafork
.extents
[i
].blockCount
=
832 (UInt32
) (dataPtr
->hfsFile
.dataExtents
[i
].blockCount
);
834 nodeData
->cnd_rsrcfork
.extents
[i
].startBlock
=
835 (UInt32
) (dataPtr
->hfsFile
.rsrcExtents
[i
].startBlock
);
837 nodeData
->cnd_rsrcfork
.extents
[i
].blockCount
=
838 (UInt32
) (dataPtr
->hfsFile
.rsrcExtents
[i
].blockCount
);
840 for (i
= kHFSExtentDensity
; i
< kHFSPlusExtentDensity
; ++i
) {
841 nodeData
->cnd_datafork
.extents
[i
].startBlock
= 0;
842 nodeData
->cnd_datafork
.extents
[i
].blockCount
= 0;
843 nodeData
->cnd_rsrcfork
.extents
[i
].startBlock
= 0;
844 nodeData
->cnd_rsrcfork
.extents
[i
].blockCount
= 0;
847 nodeData
->cnd_type
= 0;
852 //_______________________________________________________________________
855 CopyCatalogName(const CatalogName
*srcName
, CatalogName
*dstName
, Boolean isHFSPLus
)
859 if ( srcName
== NULL
)
861 if ( dstName
!= NULL
)
862 dstName
->ustr
.length
= 0; // set length byte to zero (works for both unicode and pascal)
867 length
= sizeof(UniChar
) * (srcName
->ustr
.length
+ 1);
869 length
= sizeof(UInt8
) + srcName
->pstr
[0];
872 BlockMoveData(srcName
, dstName
, length
);
874 dstName
->ustr
.length
= 0; // set length byte to zero (works for both unicode and pascal)
877 //_______________________________________________________________________
880 CatalogNameLength(const CatalogName
*name
, Boolean isHFSPlus
)
883 return name
->ustr
.length
;
885 return name
->pstr
[0];