]> git.saurik.com Git - apple/xnu.git/blob - bsd/hfs/hfscommon/Catalog/CatalogUtilities.c
a966d7ab4aa973b1a0df3e67b7c40322a5ee74d7
[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 #include <sys/stat.h>
93
94 #include "../headers/FileMgrInternal.h"
95 #include "../headers/BTreesInternal.h"
96 #include "../headers/CatalogPrivate.h"
97 #include "../headers/HFSUnicodeWrappers.h"
98 #include <string.h>
99
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 OSErr
476 BuildCatalogKeyUTF8(ExtendedVCB *volume, HFSCatalogNodeID parentID, const char *name, UInt32 nameLength,
477 CatalogKey *key, UInt32 *textEncoding)
478 {
479 OSErr err = 0;
480
481 if ( name == NULL)
482 nameLength = 0;
483 else if (nameLength == kUndefinedStrLen)
484 nameLength = strlen(name);
485
486 if ( volume->vcbSigWord == kHFSPlusSigWord ) {
487 size_t unicodeBytes = 0;
488
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;
497 }
498
499 if (textEncoding && (*textEncoding != kTextEncodingMacUnicode))
500 *textEncoding = hfs_pickencoding(key->hfsPlus.nodeName.unicode,
501 key->hfsPlus.nodeName.length);
502 }
503 else {
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]);
510 /*
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).
514 */
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
518 }
519 if (textEncoding)
520 *textEncoding = 0;
521 }
522
523 if (err) {
524 if (err == ENAMETOOLONG)
525 err = bdNamErr; /* name is too long */
526 else
527 err = paramErr; /* name has invalid characters */
528 }
529
530 return err;
531 }
532
533
534 //*******************************************************************************
535 // Routine: FlushCatalog
536 //
537 // Function: Flushes the catalog for a specified volume.
538 //
539 //*******************************************************************************
540
541 OSErr
542 FlushCatalog(ExtendedVCB *volume)
543 {
544 FCB * fcb;
545 OSErr result;
546
547 fcb = GetFileControlBlock(volume->catalogRefNum);
548 result = BTFlushPath(fcb);
549
550 if (result == noErr)
551 {
552 //--- check if catalog's fcb is dirty...
553
554 if ( fcb->fcbFlags & fcbModifiedMask )
555 {
556 VCB_LOCK(volume);
557 volume->vcbFlags |= 0xFF00; // Mark the VCB dirty
558 volume->vcbLsMod = GetTimeUTC(); // update last modified date
559 VCB_UNLOCK(volume);
560
561 result = FlushVolumeControlBlock(volume);
562 }
563 }
564
565 return result;
566 }
567
568
569 //ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ
570 // Routine: UpdateCatalogName
571 //
572 // Function: Updates a CName.
573 //
574 //ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ
575
576 void
577 UpdateCatalogName(ConstStr31Param srcName, Str31 destName)
578 {
579 Size length = srcName[0];
580
581 if (length > CMMaxCName)
582 length = CMMaxCName; // truncate to max
583
584 destName[0] = length; // set length byte
585
586 BlockMoveData(&srcName[1], &destName[1], length);
587 }
588
589
590 //*******************************************************************************
591 // Routine: AdjustVolumeCounts
592 //
593 // Function: Adjusts the folder and file counts in the VCB
594 //
595 //*******************************************************************************
596
597 void
598 AdjustVolumeCounts(ExtendedVCB *volume, SInt16 type, SInt16 delta)
599 {
600 //\80\80 also update extended VCB fields...
601
602 VCB_LOCK(volume);
603
604 if (type == kHFSFolderRecord || type == kHFSPlusFolderRecord)
605 volume->vcbDirCnt += delta; // adjust volume folder count, \80\80 worry about overflow?
606 else
607 volume->vcbFilCnt += delta; // adjust volume file count
608
609 volume->vcbFlags |= 0xFF00; // Mark the VCB dirty
610 volume->vcbLsMod = GetTimeUTC(); // update last modified date
611
612 VCB_UNLOCK(volume);
613 }
614
615
616 //*******************************************************************************
617
618 void
619 UpdateVolumeEncodings(ExtendedVCB *volume, TextEncoding encoding)
620 {
621 UInt32 index;
622
623 encoding &= 0x7F;
624
625 index = MapEncodingToIndex(encoding);
626
627 VCB_LOCK(volume);
628
629 volume->encodingsBitmap |= (1 << index);
630
631 VCB_UNLOCK(volume);
632
633 // vcb should already be marked dirty
634 }
635
636
637 //*******************************************************************************
638
639 OSErr
640 UpdateFolderCount( ExtendedVCB *volume, HFSCatalogNodeID parentID, const CatalogName *name, SInt16 newType,
641 UInt32 hint, SInt16 valenceDelta)
642 {
643 CatalogKey tempKey; // 518 bytes
644 CatalogRecord tempData; // 520 bytes
645 UInt32 tempHint;
646 HFSCatalogNodeID folderID;
647 UInt16 recordSize;
648 OSErr result;
649
650 #if 0
651 result = SearchBTreeRecord(volume->catalogRefNum, parentKey, hint,
652 &tempKey, &tempData, &recordSize, &tempHint);
653 if (result)
654 return (result == btNotFound ? cmNotFound : result);
655 #else
656
657 result = LocateCatalogNode(volume, parentID, name, hint, &tempKey, &tempData, &tempHint);
658 ReturnIfError(result);
659 #endif
660
661 if ( volume->vcbSigWord == kHFSPlusSigWord ) // HFS Plus
662 {
663 UInt32 timeStamp;
664
665 if ( DEBUG_BUILD && tempData.recordType != kHFSPlusFolderRecord )
666 DebugStr("\p UpdateFolder: found HFS folder on HFS+ volume!");
667
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;
674 else
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);
679 }
680 else // classic HFS
681 {
682 if ( DEBUG_BUILD && tempData.recordType != kHFSFolderRecord )
683 DebugStr("\p UpdateFolder: found HFS+ folder on HFS volume!");
684
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;
690 else
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);
695 }
696
697 result = ReplaceBTreeRecord(volume->catalogRefNum, &tempKey, tempHint,
698 &tempData, recordSize, &tempHint);
699 ReturnIfError(result);
700
701 if ( folderID == kHFSRootFolderID )
702 {
703 if (newType == kHFSFolderRecord || newType == kHFSPlusFolderRecord)
704 {
705 VCB_LOCK(volume);
706 volume->vcbNmRtDirs += valenceDelta; // adjust root folder count (undefined for HFS Plus)
707 VCB_UNLOCK(volume);
708 }
709 else
710 {
711 VCB_LOCK(volume);
712 volume->vcbNmFls += valenceDelta; // adjust root file count (used by GetVolInfo)
713 VCB_UNLOCK(volume);
714 }
715 }
716
717 //XXX also update extended VCB fields...
718
719 return result;
720 }
721
722
723 //*******************************************************************************
724
725 UInt16
726 GetCatalogRecordSize(const CatalogRecord *dataRecord)
727 {
728 switch (dataRecord->recordType)
729 {
730 case kHFSFileRecord:
731 return sizeof(HFSCatalogFile);
732
733 case kHFSFolderRecord:
734 return sizeof(HFSCatalogFolder);
735
736 case kHFSPlusFileRecord:
737 return sizeof(HFSPlusCatalogFile);
738
739 case kHFSPlusFolderRecord:
740 return sizeof(HFSPlusCatalogFolder);
741
742 case kHFSFolderThreadRecord:
743 case kHFSFileThreadRecord:
744 return sizeof(HFSCatalogThread);
745
746 case kHFSPlusFolderThreadRecord:
747 case kHFSPlusFileThreadRecord:
748 return sizeof(HFSPlusCatalogThread);
749
750 default:
751 return 0;
752 }
753 }
754
755
756 //*******************************************************************************
757
758 void
759 CopyCatalogNodeData(const ExtendedVCB *volume, const CatalogRecord *dataPtr, CatalogNodeData *nodeData)
760 {
761 /* convert classic hfs records to hfs plus format */
762
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;
778
779 BlockMoveData(&dataPtr->hfsFolder.userInfo, &nodeData->cnd_finderInfo, 32);
780 } else if (dataPtr->recordType == kHFSFileRecord) {
781 UInt32 i;
782
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;
797
798 BlockMoveData(&dataPtr->hfsFile.userInfo, &nodeData->cnd_finderInfo, 16);
799 BlockMoveData(&dataPtr->hfsFile.finderInfo, (void*)((UInt32)&nodeData->cnd_finderInfo + 16), 16);
800
801 nodeData->cnd_datafork.logicalSize = dataPtr->hfsFile.dataLogicalSize;
802 nodeData->cnd_datafork.totalBlocks =
803 dataPtr->hfsFile.dataPhysicalSize / volume->blockSize;
804
805 nodeData->cnd_rsrcfork.logicalSize = dataPtr->hfsFile.rsrcLogicalSize;
806 nodeData->cnd_rsrcfork.totalBlocks =
807 dataPtr->hfsFile.rsrcPhysicalSize / volume->blockSize;
808
809 for (i = 0; i < kHFSExtentDensity; ++i) {
810 nodeData->cnd_datafork.extents[i].startBlock =
811 (UInt32) (dataPtr->hfsFile.dataExtents[i].startBlock);
812
813 nodeData->cnd_datafork.extents[i].blockCount =
814 (UInt32) (dataPtr->hfsFile.dataExtents[i].blockCount);
815
816 nodeData->cnd_rsrcfork.extents[i].startBlock =
817 (UInt32) (dataPtr->hfsFile.rsrcExtents[i].startBlock);
818
819 nodeData->cnd_rsrcfork.extents[i].blockCount =
820 (UInt32) (dataPtr->hfsFile.rsrcExtents[i].blockCount);
821 }
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;
827 }
828 } else {
829 nodeData->cnd_type = 0;
830 }
831 }
832
833
834 //_______________________________________________________________________
835
836 void
837 CopyCatalogName(const CatalogName *srcName, CatalogName *dstName, Boolean isHFSPLus)
838 {
839 UInt32 length;
840
841 if ( srcName == NULL )
842 {
843 if ( dstName != NULL )
844 dstName->ustr.length = 0; // set length byte to zero (works for both unicode and pascal)
845 return;
846 }
847
848 if (isHFSPLus)
849 length = sizeof(UniChar) * (srcName->ustr.length + 1);
850 else
851 length = sizeof(UInt8) + srcName->pstr[0];
852
853 if ( length > 1 )
854 BlockMoveData(srcName, dstName, length);
855 else
856 dstName->ustr.length = 0; // set length byte to zero (works for both unicode and pascal)
857 }
858
859 //_______________________________________________________________________
860
861 UInt32
862 CatalogNameLength(const CatalogName *name, Boolean isHFSPlus)
863 {
864 if (isHFSPlus)
865 return name->ustr.length;
866 else
867 return name->pstr[0];
868 }
869
870
871