]> git.saurik.com Git - apple/xnu.git/blob - bsd/hfs/hfscommon/Catalog/CatalogUtilities.c
15ff358a96cd6fac6ba4308b4cd42df7d4306767
[apple/xnu.git] / bsd / hfs / hfscommon / Catalog / CatalogUtilities.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: CatalogUtilities.c
24
25 Contains: Private Catalog Manager support routines.
26
27 Version: HFS Plus 1.0
28
29 Copyright: © 1997-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 (DSH) Deric Horn
42 (msd) Mark Day
43 (djb) Don Brady
44
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.
55
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
60 under 2 Gig.
61 <CS21> 12/9/97 DSH 2201501, Pin returned leof values to 2^31-1 (SInt32), instead of
62 2^32-1
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
78 Plus volumes too).
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
82 collision
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
89 */
90 #include <sys/param.h>
91 #include <sys/utfconv.h>
92
93 #include "../headers/FileMgrInternal.h"
94 #include "../headers/BTreesInternal.h"
95 #include "../headers/CatalogPrivate.h"
96 #include "../headers/HFSUnicodeWrappers.h"
97 #include <string.h>
98
99 static void ExtractTextEncoding (ItemCount length, ConstUniCharArrayPtr string, UInt32 * textEncoding);
100
101 //*******************************************************************************
102 // Routine: LocateCatalogNode
103 //
104 // Function: Locates the catalog record for an existing folder or file
105 // CNode and returns pointers to the key and data records.
106 //
107 //*******************************************************************************
108
109 OSErr
110 LocateCatalogNode(const ExtendedVCB *volume, HFSCatalogNodeID folderID, const CatalogName *name,
111 UInt32 hint, CatalogKey *keyPtr, CatalogRecord *dataPtr, UInt32 *newHint)
112 {
113 OSErr result;
114 CatalogName *nodeName = NULL; /* To ward off uninitialized use warnings from compiler */
115 HFSCatalogNodeID threadParentID;
116
117
118 result = LocateCatalogRecord(volume, folderID, name, hint, keyPtr, dataPtr, newHint);
119 ReturnIfError(result);
120
121 // if we got a thread record, then go look up real record
122 switch ( dataPtr->recordType )
123 {
124 case kHFSFileThreadRecord:
125 case kHFSFolderThreadRecord:
126 threadParentID = dataPtr->hfsThread.parentID;
127 nodeName = (CatalogName *) &dataPtr->hfsThread.nodeName;
128 break;
129
130 case kHFSPlusFileThreadRecord:
131 case kHFSPlusFolderThreadRecord:
132 threadParentID = dataPtr->hfsPlusThread.parentID;
133 nodeName = (CatalogName *) &dataPtr->hfsPlusThread.nodeName;
134 break;
135
136 default:
137 threadParentID = 0;
138 break;
139 }
140
141 if ( threadParentID ) // found a thread
142 result = LocateCatalogRecord(volume, threadParentID, nodeName, kNoHint, keyPtr, dataPtr, newHint);
143
144 return result;
145 }
146
147 //
148 // Routine: LocateCatalogNodeByKey
149 //
150 // Function: Locates the catalog record for an existing folder or file
151 // CNode and returns the key and data records.
152 //
153
154 OSErr
155 LocateCatalogNodeByKey(const ExtendedVCB *volume, UInt32 hint, CatalogKey *keyPtr,
156 CatalogRecord *dataPtr, UInt32 *newHint)
157 {
158 OSErr result;
159 CatalogName *nodeName = NULL; /* To ward off uninitialized use warnings from compiler */
160 HFSCatalogNodeID threadParentID;
161 UInt16 tempSize;
162
163
164 result = SearchBTreeRecord(volume->catalogRefNum, keyPtr, hint, keyPtr,
165 dataPtr, &tempSize, newHint);
166 if (result == btNotFound)
167 result = cmNotFound;
168 ReturnIfError(result);
169
170 // if we got a thread record, then go look up real record
171 switch ( dataPtr->recordType )
172 {
173 case kHFSFileThreadRecord:
174 case kHFSFolderThreadRecord:
175 threadParentID = dataPtr->hfsThread.parentID;
176 nodeName = (CatalogName *) &dataPtr->hfsThread.nodeName;
177 break;
178
179 case kHFSPlusFileThreadRecord:
180 case kHFSPlusFolderThreadRecord:
181 threadParentID = dataPtr->hfsPlusThread.parentID;
182 nodeName = (CatalogName *) &dataPtr->hfsPlusThread.nodeName;
183 break;
184
185 default:
186 threadParentID = 0;
187 break;
188 }
189
190 if ( threadParentID ) // found a thread
191 result = LocateCatalogRecord(volume, threadParentID, nodeName, kNoHint, keyPtr, dataPtr, newHint);
192
193 return result;
194 }
195
196
197 #if 0
198 //*******************************************************************************
199 // Routine: LocateCatalogNodeWithRetry
200 //
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.
204 //
205 //ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ
206
207 #define VolumeHasEncodings(v) \
208 ( ((v)->encodingsBitmap != 0 )
209
210 #define EncodingInstalled(i) \
211 ( (fsVars)->gConversionContext[(i)].toUnicode != 0 )
212
213 #define EncodingUsedByVolume(v,i) \
214 ( ((v)->encodingsBitmap & (1 << (i))) )
215
216
217 OSErr
218 LocateCatalogNodeWithRetry (const ExtendedVCB *volume, HFSCatalogNodeID folderID, ConstStr31Param pascalName, CatalogName *unicodeName,
219 UInt32 hint, CatalogKey *keyPtr, CatalogRecord *dataPtr, UInt32 *newHint)
220 {
221 TextEncoding defaultEncoding;
222 TextEncoding encoding;
223 ItemCount encodingsToTry;
224 FSVarsRec *fsVars;
225 OSErr result = cmNotFound;
226
227 fsVars = (FSVarsRec*) LMGetFSMVars(); // used by macros
228
229 defaultEncoding = GetDefaultTextEncoding();
230 encodingsToTry = CountInstalledEncodings();
231
232 // 1. Try finding file using default encoding (typical case)
233
234 {
235 --encodingsToTry;
236 result = PrepareInputName(pascalName, true, defaultEncoding, unicodeName);
237 if (result == noErr)
238 result = LocateCatalogNode(volume, folderID, unicodeName, hint, keyPtr, dataPtr, newHint);
239 else
240 result = cmNotFound;
241
242 if ( result != cmNotFound || encodingsToTry == 0)
243 return result;
244 }
245
246 //
247 // XXX if the pascal string contains all 7-bit ascii then we don't need to do anymore retries
248 //
249
250 // 2. Try finding file using Mac Roman (if not already tried above)
251
252 if ( defaultEncoding != kTextEncodingMacRoman )
253 {
254 --encodingsToTry;
255 result = PrepareInputName(pascalName, true, kTextEncodingMacRoman, unicodeName);
256 if (result == noErr)
257 result = LocateCatalogNode(volume, folderID, unicodeName, hint, keyPtr, dataPtr, newHint);
258 else
259 result = cmNotFound;
260
261 if ( result != cmNotFound || encodingsToTry == 0 )
262 return result;
263 }
264
265 // 3. Try with encodings from disk (if any)
266
267 if ( VolumeHasEncodings(volume) ) // any left to try?
268 {
269 UInt32 index;
270
271 index = 0; // since we pre increment this will skip MacRoman (which was already tried above)
272
273 while ( index < kMacBaseEncodingCount )
274 {
275 ++index;
276
277 encoding = MapIndexToEncoding(index);
278
279 if ( encoding == defaultEncoding )
280 continue; // we did this one already
281
282 if ( EncodingInstalled(index) && EncodingUsedByVolume(volume, index) )
283 {
284 --encodingsToTry;
285 result = PrepareInputName(pascalName, true, encoding, unicodeName);
286 if (result == noErr)
287 result = LocateCatalogNode(volume, folderID, unicodeName, hint, keyPtr, dataPtr, newHint);
288 else
289 result = cmNotFound;
290
291 if ( result != cmNotFound || encodingsToTry == 0 )
292 return result;
293 }
294 }
295 }
296
297 // 4. Try any remaining encodings (if any)
298
299 {
300 UInt32 index;
301
302 index = 0; // since we pre increment this will skip MacRoman (which was already tried above)
303
304 while ( (encodingsToTry > 0) && (index < kMacBaseEncodingCount) )
305 {
306 ++index;
307
308 encoding = MapIndexToEncoding(index);
309
310 if ( encoding == defaultEncoding )
311 continue; // we did this one already
312
313 if ( EncodingInstalled(index) && EncodingUsedByVolume(volume, index) == false )
314 {
315 --encodingsToTry;
316 result = PrepareInputName(pascalName, true, encoding, unicodeName);
317 if (result == noErr)
318 result = LocateCatalogNode(volume, folderID, unicodeName, hint, keyPtr, dataPtr, newHint);
319 else
320 result = cmNotFound;
321
322 if ( result != cmNotFound || encodingsToTry == 0 )
323 return result;
324 }
325 }
326 }
327
328 return cmNotFound;
329 }
330 #endif
331
332 //*******************************************************************************
333 // Routine: LocateCatalogNodeByMangledName
334 //
335 // Function: Locates the catalog record associated with a mangled name (if any)
336 //
337 //*******************************************************************************
338 #define kMaxCompareLen 64 /* If it compares this far...lets believe it */
339
340 OSErr
341 LocateCatalogNodeByMangledName( const ExtendedVCB *volume, HFSCatalogNodeID folderID,
342 const unsigned char * name, UInt32 length, CatalogKey *keyPtr,
343 CatalogRecord *dataPtr, UInt32 *hintPtr )
344 {
345 HFSCatalogNodeID fileID;
346 unsigned char nodeName[kMaxCompareLen+1];
347 OSErr result;
348 size_t actualDstLen;
349 ByteCount prefixlen;
350
351
352 if (name == NULL || name[0] == '\0')
353 return cmNotFound;
354
355 fileID = GetEmbeddedFileID(name, length, &prefixlen);
356
357 if ( fileID < kHFSFirstUserCatalogNodeID )
358 return cmNotFound;
359
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);
364
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
368
369 (void) utf8_encodestr(keyPtr->hfsPlus.nodeName.unicode,
370 keyPtr->hfsPlus.nodeName.length * sizeof (UniChar),
371 nodeName, &actualDstLen, kMaxCompareLen+1, ':', 0);
372
373 prefixlen = min(prefixlen, kMaxCompareLen);
374
375 if ((prefixlen - actualDstLen) < 6)
376 prefixlen = actualDstLen; /* To take into account UTF8 rounding */
377
378 if ( (actualDstLen < prefixlen) || bcmp(nodeName, name, prefixlen-6) != 0)
379 return cmNotFound; // mangled names didn't match so this is a false match
380
381 return noErr; // we found it
382 }
383
384
385 //*******************************************************************************
386 // Routine: LocateCatalogRecord
387 //
388 // Function: Locates the catalog record associated with folderID and name
389 //
390 //*******************************************************************************
391
392 OSErr
393 LocateCatalogRecord(const ExtendedVCB *volume, HFSCatalogNodeID folderID, const CatalogName *name,
394 UInt32 hint, CatalogKey *keyPtr, CatalogRecord *dataPtr, UInt32 *newHint)
395 {
396 OSErr result;
397 CatalogKey tempKey; // 518 bytes
398 UInt16 tempSize;
399
400 BuildCatalogKey(folderID, name, (volume->vcbSigWord == kHFSPlusSigWord), &tempKey);
401
402 if ( name == NULL )
403 hint = kNoHint; // no CName given so clear the hint
404
405 result = SearchBTreeRecord(volume->catalogRefNum, &tempKey, hint, keyPtr, dataPtr, &tempSize, newHint);
406
407 return (result == btNotFound ? cmNotFound : result);
408 }
409
410
411 //*******************************************************************************
412 // Routine: LocateCatalogThread
413 //
414 // Function: Locates a catalog thread record in the catalog BTree file and
415 // returns a pointer to the data record.
416 //
417 //*******************************************************************************
418
419 OSErr
420 LocateCatalogThread(const ExtendedVCB *volume, HFSCatalogNodeID nodeID, CatalogRecord *threadData, UInt16 *threadSize, UInt32 *threadHint)
421 {
422 CatalogKey threadKey; // 518 bytes
423 OSErr result;
424
425 //--- build key record
426
427 BuildCatalogKey(nodeID, NULL, (volume->vcbSigWord == kHFSPlusSigWord), &threadKey);
428
429 //--- locate thread record in BTree
430
431 result = SearchBTreeRecord( volume->catalogRefNum, &threadKey, kNoHint, &threadKey,
432 threadData, threadSize, threadHint);
433
434 return (result == btNotFound ? cmNotFound : result);
435 }
436
437
438 /*
439 * Routine: BuildCatalogKey
440 *
441 * Function: Constructs a catalog key record (ckr) given the parent
442 * folder ID and CName. Works for both classic and extended
443 * HFS volumes.
444 *
445 */
446
447 void
448 BuildCatalogKey(HFSCatalogNodeID parentID, const CatalogName *cName, Boolean isHFSPlus, CatalogKey *key)
449 {
450 if ( isHFSPlus )
451 {
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
455 if ( cName != NULL )
456 {
457 CopyCatalogName(cName, (CatalogName *) &key->hfsPlus.nodeName, isHFSPlus);
458 key->hfsPlus.keyLength += sizeof(UniChar) * cName->ustr.length; // add CName size to key length
459 }
460 }
461 else
462 {
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
467 if ( cName != NULL )
468 {
469 UpdateCatalogName(cName->pstr, key->hfs.nodeName);
470 key->hfs.keyLength += key->hfs.nodeName[0]; // add CName size to key length
471 }
472 }
473 }
474
475 /*
476 * for HFS, only MacRoman is supported. If a non-MacRoman character is found, an error is returned
477 */
478 OSErr
479 BuildCatalogKeyUTF8(ExtendedVCB *volume, HFSCatalogNodeID parentID, const char *name, UInt32 nameLength,
480 CatalogKey *key, UInt32 *textEncoding)
481 {
482 OSErr err = 0;
483
484 if ( name == NULL)
485 nameLength = 0;
486 else if (nameLength == kUndefinedStrLen)
487 nameLength = strlen(name);
488
489 if ( volume->vcbSigWord == kHFSPlusSigWord ) {
490 size_t unicodeBytes = 0;
491
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;
500 }
501
502 if (textEncoding)
503 ExtractTextEncoding(key->hfsPlus.nodeName.length, key->hfsPlus.nodeName.unicode, textEncoding);
504 }
505 else {
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]);
512 /*
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).
516 */
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
520 }
521 if (textEncoding)
522 *textEncoding = 0;
523 }
524
525 if (err) {
526 if (err == ENAMETOOLONG)
527 err = bdNamErr; /* name is too long */
528 else
529 err = paramErr; /* name has invalid characters */
530 }
531
532 return err;
533 }
534
535
536 /*
537 * make a guess at the text encoding value that coresponds to the Unicode characters
538 */
539 static void
540 ExtractTextEncoding(ItemCount length, ConstUniCharArrayPtr string, UInt32 * textEncoding)
541 {
542 int i;
543 UniChar ch;
544
545 *textEncoding = 0;
546
547 for (i = 0; i < length; ++i) {
548 ch = string[i];
549 /* CJK codepoints are 0x3000 thru 0x9FFF */
550 if (ch >= 0x3000) {
551 if (ch < 0xa000) {
552 *textEncoding = kTextEncodingMacJapanese;
553 break;
554 }
555
556 /* fullwidth character codepoints are 0xFF00 thru 0xFFEF */
557 if (ch >= 0xff00 && ch <= 0xffef) {
558 *textEncoding = kTextEncodingMacJapanese;
559 break;
560 }
561 }
562 }
563 }
564
565
566 //*******************************************************************************
567 // Routine: FlushCatalog
568 //
569 // Function: Flushes the catalog for a specified volume.
570 //
571 //*******************************************************************************
572
573 OSErr
574 FlushCatalog(ExtendedVCB *volume)
575 {
576 FCB * fcb;
577 OSErr result;
578
579 fcb = GetFileControlBlock(volume->catalogRefNum);
580 result = BTFlushPath(fcb);
581
582 if (result == noErr)
583 {
584 //--- check if catalog's fcb is dirty...
585
586 if ( fcb->fcbFlags & fcbModifiedMask )
587 {
588 VCB_LOCK(volume);
589 volume->vcbFlags |= 0xFF00; // Mark the VCB dirty
590 volume->vcbLsMod = GetTimeUTC(); // update last modified date
591 VCB_UNLOCK(volume);
592
593 result = FlushVolumeControlBlock(volume);
594 }
595 }
596
597 return result;
598 }
599
600
601 //ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ
602 // Routine: UpdateCatalogName
603 //
604 // Function: Updates a CName.
605 //
606 //ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ
607
608 void
609 UpdateCatalogName(ConstStr31Param srcName, Str31 destName)
610 {
611 Size length = srcName[0];
612
613 if (length > CMMaxCName)
614 length = CMMaxCName; // truncate to max
615
616 destName[0] = length; // set length byte
617
618 BlockMoveData(&srcName[1], &destName[1], length);
619 }
620
621
622 //*******************************************************************************
623 // Routine: AdjustVolumeCounts
624 //
625 // Function: Adjusts the folder and file counts in the VCB
626 //
627 //*******************************************************************************
628
629 void
630 AdjustVolumeCounts(ExtendedVCB *volume, SInt16 type, SInt16 delta)
631 {
632 //\80\80 also update extended VCB fields...
633
634 VCB_LOCK(volume);
635
636 if (type == kHFSFolderRecord || type == kHFSPlusFolderRecord)
637 volume->vcbDirCnt += delta; // adjust volume folder count, \80\80 worry about overflow?
638 else
639 volume->vcbFilCnt += delta; // adjust volume file count
640
641 volume->vcbFlags |= 0xFF00; // Mark the VCB dirty
642 volume->vcbLsMod = GetTimeUTC(); // update last modified date
643
644 VCB_UNLOCK(volume);
645 }
646
647
648 //*******************************************************************************
649
650 void
651 UpdateVolumeEncodings(ExtendedVCB *volume, TextEncoding encoding)
652 {
653 UInt32 index;
654
655 encoding &= 0x7F;
656
657 index = MapEncodingToIndex(encoding);
658
659 VCB_LOCK(volume);
660
661 volume->encodingsBitmap |= (1 << index);
662
663 VCB_UNLOCK(volume);
664
665 // vcb should already be marked dirty
666 }
667
668
669 //*******************************************************************************
670
671 OSErr
672 UpdateFolderCount( ExtendedVCB *volume, HFSCatalogNodeID parentID, const CatalogName *name, SInt16 newType,
673 UInt32 hint, SInt16 valenceDelta)
674 {
675 CatalogKey tempKey; // 518 bytes
676 CatalogRecord tempData; // 520 bytes
677 UInt32 tempHint;
678 HFSCatalogNodeID folderID;
679 UInt16 recordSize;
680 OSErr result;
681
682 #if 0
683 result = SearchBTreeRecord(volume->catalogRefNum, parentKey, hint,
684 &tempKey, &tempData, &recordSize, &tempHint);
685 if (result)
686 return (result == btNotFound ? cmNotFound : result);
687 #else
688
689 result = LocateCatalogNode(volume, parentID, name, hint, &tempKey, &tempData, &tempHint);
690 ReturnIfError(result);
691 #endif
692
693 if ( volume->vcbSigWord == kHFSPlusSigWord ) // HFS Plus
694 {
695 UInt32 timeStamp;
696
697 if ( DEBUG_BUILD && tempData.recordType != kHFSPlusFolderRecord )
698 DebugStr("\p UpdateFolder: found HFS folder on HFS+ volume!");
699
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;
706 else
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);
711 }
712 else // classic HFS
713 {
714 if ( DEBUG_BUILD && tempData.recordType != kHFSFolderRecord )
715 DebugStr("\p UpdateFolder: found HFS+ folder on HFS volume!");
716
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;
722 else
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);
727 }
728
729 result = ReplaceBTreeRecord(volume->catalogRefNum, &tempKey, tempHint,
730 &tempData, recordSize, &tempHint);
731 ReturnIfError(result);
732
733 if ( folderID == kHFSRootFolderID )
734 {
735 if (newType == kHFSFolderRecord || newType == kHFSPlusFolderRecord)
736 {
737 VCB_LOCK(volume);
738 volume->vcbNmRtDirs += valenceDelta; // adjust root folder count (undefined for HFS Plus)
739 VCB_UNLOCK(volume);
740 }
741 else
742 {
743 VCB_LOCK(volume);
744 volume->vcbNmFls += valenceDelta; // adjust root file count (used by GetVolInfo)
745 VCB_UNLOCK(volume);
746 }
747 }
748
749 //XXX also update extended VCB fields...
750
751 return result;
752 }
753
754
755 //*******************************************************************************
756
757 UInt16
758 GetCatalogRecordSize(const CatalogRecord *dataRecord)
759 {
760 switch (dataRecord->recordType)
761 {
762 case kHFSFileRecord:
763 return sizeof(HFSCatalogFile);
764
765 case kHFSFolderRecord:
766 return sizeof(HFSCatalogFolder);
767
768 case kHFSPlusFileRecord:
769 return sizeof(HFSPlusCatalogFile);
770
771 case kHFSPlusFolderRecord:
772 return sizeof(HFSPlusCatalogFolder);
773
774 case kHFSFolderThreadRecord:
775 case kHFSFileThreadRecord:
776 return sizeof(HFSCatalogThread);
777
778 case kHFSPlusFolderThreadRecord:
779 case kHFSPlusFileThreadRecord:
780 return sizeof(HFSPlusCatalogThread);
781
782 default:
783 return 0;
784 }
785 }
786
787
788 //*******************************************************************************
789
790 void
791 CopyCatalogNodeData(const ExtendedVCB *volume, const CatalogRecord *dataPtr, CatalogNodeData *nodeData)
792 {
793 /* convert classic hfs records to hfs plus format */
794
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;
803
804 BlockMoveData(&dataPtr->hfsFolder.userInfo, &nodeData->cnd_finderInfo, 32);
805 } else if (dataPtr->recordType == kHFSFileRecord) {
806 UInt32 i;
807
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;
815
816 BlockMoveData(&dataPtr->hfsFile.userInfo, &nodeData->cnd_finderInfo, 16);
817 BlockMoveData(&dataPtr->hfsFile.finderInfo, (void*)((UInt32)&nodeData->cnd_finderInfo + 16), 16);
818
819 nodeData->cnd_datafork.logicalSize = dataPtr->hfsFile.dataLogicalSize;
820 nodeData->cnd_datafork.totalBlocks =
821 dataPtr->hfsFile.dataPhysicalSize / volume->blockSize;
822
823 nodeData->cnd_rsrcfork.logicalSize = dataPtr->hfsFile.rsrcLogicalSize;
824 nodeData->cnd_rsrcfork.totalBlocks =
825 dataPtr->hfsFile.rsrcPhysicalSize / volume->blockSize;
826
827 for (i = 0; i < kHFSExtentDensity; ++i) {
828 nodeData->cnd_datafork.extents[i].startBlock =
829 (UInt32) (dataPtr->hfsFile.dataExtents[i].startBlock);
830
831 nodeData->cnd_datafork.extents[i].blockCount =
832 (UInt32) (dataPtr->hfsFile.dataExtents[i].blockCount);
833
834 nodeData->cnd_rsrcfork.extents[i].startBlock =
835 (UInt32) (dataPtr->hfsFile.rsrcExtents[i].startBlock);
836
837 nodeData->cnd_rsrcfork.extents[i].blockCount =
838 (UInt32) (dataPtr->hfsFile.rsrcExtents[i].blockCount);
839 }
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;
845 }
846 } else {
847 nodeData->cnd_type = 0;
848 }
849 }
850
851
852 //_______________________________________________________________________
853
854 void
855 CopyCatalogName(const CatalogName *srcName, CatalogName *dstName, Boolean isHFSPLus)
856 {
857 UInt32 length;
858
859 if ( srcName == NULL )
860 {
861 if ( dstName != NULL )
862 dstName->ustr.length = 0; // set length byte to zero (works for both unicode and pascal)
863 return;
864 }
865
866 if (isHFSPLus)
867 length = sizeof(UniChar) * (srcName->ustr.length + 1);
868 else
869 length = sizeof(UInt8) + srcName->pstr[0];
870
871 if ( length > 1 )
872 BlockMoveData(srcName, dstName, length);
873 else
874 dstName->ustr.length = 0; // set length byte to zero (works for both unicode and pascal)
875 }
876
877 //_______________________________________________________________________
878
879 UInt32
880 CatalogNameLength(const CatalogName *name, Boolean isHFSPlus)
881 {
882 if (isHFSPlus)
883 return name->ustr.length;
884 else
885 return name->pstr[0];
886 }
887
888
889