2 * Copyright (c) 2000-2002 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@
22 #include <sys/param.h>
23 #include <sys/utfconv.h>
26 #include "../headers/FileMgrInternal.h"
27 #include "../headers/BTreesInternal.h"
28 #include "../headers/CatalogPrivate.h"
29 #include "../headers/HFSUnicodeWrappers.h"
33 //*******************************************************************************
34 // Routine: LocateCatalogNode
36 // Function: Locates the catalog record for an existing folder or file
37 // CNode and returns pointers to the key and data records.
39 //*******************************************************************************
42 LocateCatalogNode(const ExtendedVCB
*volume
, HFSCatalogNodeID folderID
, const CatalogName
*name
,
43 UInt32 hint
, CatalogKey
*keyPtr
, CatalogRecord
*dataPtr
, UInt32
*newHint
)
46 CatalogName
*nodeName
= NULL
; /* To ward off uninitialized use warnings from compiler */
47 HFSCatalogNodeID threadParentID
;
50 result
= LocateCatalogRecord(volume
, folderID
, name
, hint
, keyPtr
, dataPtr
, newHint
);
51 ReturnIfError(result
);
53 // if we got a thread record, then go look up real record
54 switch ( dataPtr
->recordType
)
56 case kHFSFileThreadRecord
:
57 case kHFSFolderThreadRecord
:
58 threadParentID
= dataPtr
->hfsThread
.parentID
;
59 nodeName
= (CatalogName
*) &dataPtr
->hfsThread
.nodeName
;
62 case kHFSPlusFileThreadRecord
:
63 case kHFSPlusFolderThreadRecord
:
64 threadParentID
= dataPtr
->hfsPlusThread
.parentID
;
65 nodeName
= (CatalogName
*) &dataPtr
->hfsPlusThread
.nodeName
;
73 if ( threadParentID
) // found a thread
74 result
= LocateCatalogRecord(volume
, threadParentID
, nodeName
, kNoHint
, keyPtr
, dataPtr
, newHint
);
80 // Routine: LocateCatalogNodeByKey
82 // Function: Locates the catalog record for an existing folder or file
83 // CNode and returns the key and data records.
87 LocateCatalogNodeByKey(const ExtendedVCB
*volume
, UInt32 hint
, CatalogKey
*keyPtr
,
88 CatalogRecord
*dataPtr
, UInt32
*newHint
)
91 CatalogName
*nodeName
= NULL
;
92 HFSCatalogNodeID threadParentID
;
94 FSBufferDescriptor btRecord
;
95 BTreeIterator searchIterator
= {0};
98 fcb
= GetFileControlBlock(volume
->catalogRefNum
);
100 btRecord
.bufferAddress
= dataPtr
;
101 btRecord
.itemCount
= 1;
102 btRecord
.itemSize
= sizeof(CatalogRecord
);
104 searchIterator
.hint
.nodeNum
= hint
;
106 bcopy(keyPtr
, &searchIterator
.key
, sizeof(CatalogKey
));
108 result
= BTSearchRecord( fcb
, &searchIterator
, &btRecord
, &tempSize
, &searchIterator
);
112 *newHint
= searchIterator
.hint
.nodeNum
;
114 BlockMoveData(&searchIterator
.key
, keyPtr
, sizeof(CatalogKey
));
117 if (result
== btNotFound
)
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
);
149 //*******************************************************************************
150 // Routine: LocateCatalogRecord
152 // Function: Locates the catalog record associated with folderID and name
154 //*******************************************************************************
157 LocateCatalogRecord(const ExtendedVCB
*volume
, HFSCatalogNodeID folderID
, const CatalogName
*name
,
158 UInt32 hint
, CatalogKey
*keyPtr
, CatalogRecord
*dataPtr
, UInt32
*newHint
)
161 CatalogKey tempKey
; // 518 bytes
164 BuildCatalogKey(folderID
, name
, (volume
->vcbSigWord
== kHFSPlusSigWord
), &tempKey
);
167 hint
= kNoHint
; // no CName given so clear the hint
169 result
= SearchBTreeRecord(volume
->catalogRefNum
, &tempKey
, hint
, keyPtr
, dataPtr
, &tempSize
, newHint
);
171 return (result
== btNotFound
? cmNotFound
: result
);
177 * Routine: BuildCatalogKey
179 * Function: Constructs a catalog key record (ckr) given the parent
180 * folder ID and CName. Works for both classic and extended
186 BuildCatalogKey(HFSCatalogNodeID parentID
, const CatalogName
*cName
, Boolean isHFSPlus
, CatalogKey
*key
)
190 key
->hfsPlus
.keyLength
= kHFSPlusCatalogKeyMinimumLength
; // initial key length (4 + 2)
191 key
->hfsPlus
.parentID
= parentID
; // set parent ID
192 key
->hfsPlus
.nodeName
.length
= 0; // null CName length
195 CopyCatalogName(cName
, (CatalogName
*) &key
->hfsPlus
.nodeName
, isHFSPlus
);
196 key
->hfsPlus
.keyLength
+= sizeof(UniChar
) * cName
->ustr
.length
; // add CName size to key length
201 key
->hfs
.keyLength
= kHFSCatalogKeyMinimumLength
; // initial key length (1 + 4 + 1)
202 key
->hfs
.reserved
= 0; // clear unused byte
203 key
->hfs
.parentID
= parentID
; // set parent ID
204 key
->hfs
.nodeName
[0] = 0; // null CName length
207 UpdateCatalogName(cName
->pstr
, key
->hfs
.nodeName
);
208 key
->hfs
.keyLength
+= key
->hfs
.nodeName
[0]; // add CName size to key length
214 BuildCatalogKeyUTF8(ExtendedVCB
*volume
, HFSCatalogNodeID parentID
, const char *name
, UInt32 nameLength
,
215 CatalogKey
*key
, UInt32
*textEncoding
)
221 else if (nameLength
== kUndefinedStrLen
)
222 nameLength
= strlen(name
);
224 if ( volume
->vcbSigWord
== kHFSPlusSigWord
) {
225 size_t unicodeBytes
= 0;
227 key
->hfsPlus
.keyLength
= kHFSPlusCatalogKeyMinimumLength
; // initial key length (4 + 2)
228 key
->hfsPlus
.parentID
= parentID
; // set parent ID
229 key
->hfsPlus
.nodeName
.length
= 0; // null CName length
230 if ( nameLength
> 0 ) {
231 err
= utf8_decodestr(name
, nameLength
, key
->hfsPlus
.nodeName
.unicode
,
232 &unicodeBytes
, sizeof(key
->hfsPlus
.nodeName
.unicode
), ':', UTF_DECOMPOSED
);
233 key
->hfsPlus
.nodeName
.length
= unicodeBytes
/ sizeof(UniChar
);
234 key
->hfsPlus
.keyLength
+= unicodeBytes
;
237 if (textEncoding
&& (*textEncoding
!= kTextEncodingMacUnicode
))
238 *textEncoding
= hfs_pickencoding(key
->hfsPlus
.nodeName
.unicode
,
239 key
->hfsPlus
.nodeName
.length
);
242 key
->hfs
.keyLength
= kHFSCatalogKeyMinimumLength
; // initial key length (1 + 4 + 1)
243 key
->hfs
.reserved
= 0; // clear unused byte
244 key
->hfs
.parentID
= parentID
; // set parent ID
245 key
->hfs
.nodeName
[0] = 0; // null CName length
246 if ( nameLength
> 0 ) {
247 err
= utf8_to_hfs(volume
, nameLength
, name
, &key
->hfs
.nodeName
[0]);
249 * Retry with MacRoman in case that's how it was exported.
250 * When textEncoding != NULL we know that this is a create
251 * or rename call and can skip the retry (ugly but it works).
253 if (err
&& (textEncoding
== NULL
))
254 err
= utf8_to_mac_roman(nameLength
, name
, &key
->hfs
.nodeName
[0]);
255 key
->hfs
.keyLength
+= key
->hfs
.nodeName
[0]; // add CName size to key length
262 if (err
== ENAMETOOLONG
)
263 err
= bdNamErr
; /* name is too long */
265 err
= paramErr
; /* name has invalid characters */
272 //*******************************************************************************
273 // Routine: FlushCatalog
275 // Function: Flushes the catalog for a specified volume.
277 //*******************************************************************************
280 FlushCatalog(ExtendedVCB
*volume
)
285 fcb
= GetFileControlBlock(volume
->catalogRefNum
);
286 result
= BTFlushPath(fcb
);
290 //--- check if catalog's fcb is dirty...
292 if ( 0 /*fcb->fcbFlags & fcbModifiedMask*/ )
295 volume
->vcbFlags
|= 0xFF00; // Mark the VCB dirty
296 volume
->vcbLsMod
= GetTimeUTC(); // update last modified date
299 // result = FlushVolumeControlBlock(volume);
307 //ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ
308 // Routine: UpdateCatalogName
310 // Function: Updates a CName.
312 //ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ
315 UpdateCatalogName(ConstStr31Param srcName
, Str31 destName
)
317 Size length
= srcName
[0];
319 if (length
> CMMaxCName
)
320 length
= CMMaxCName
; // truncate to max
322 destName
[0] = length
; // set length byte
324 BlockMoveData(&srcName
[1], &destName
[1], length
);
327 //_______________________________________________________________________
330 CopyCatalogName(const CatalogName
*srcName
, CatalogName
*dstName
, Boolean isHFSPLus
)
334 if ( srcName
== NULL
)
336 if ( dstName
!= NULL
)
337 dstName
->ustr
.length
= 0; // set length byte to zero (works for both unicode and pascal)
342 length
= sizeof(UniChar
) * (srcName
->ustr
.length
+ 1);
344 length
= sizeof(UInt8
) + srcName
->pstr
[0];
347 BlockMoveData(srcName
, dstName
, length
);
349 dstName
->ustr
.length
= 0; // set length byte to zero (works for both unicode and pascal)