|
|
1.1 root 1: /*
2: * Copyright (c) 1999 Apple Computer, Inc. All rights reserved.
3: *
4: * @APPLE_LICENSE_HEADER_START@
5: *
6: * Portions Copyright (c) 1999 Apple Computer, Inc. All Rights
7: * Reserved. This file contains Original Code and/or Modifications of
8: * Original Code as defined in and that are subject to the Apple Public
9: * Source License Version 1.1 (the "License"). You may not use this file
10: * except in compliance with the License. Please obtain a copy of the
11: * License at http://www.apple.com/publicsource and read it before using
12: * this file.
13: *
14: * The Original Code and all software distributed under the License are
15: * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
16: * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
17: * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
18: * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the
19: * License for the specific language governing rights and limitations
20: * under the License.
21: *
22: * @APPLE_LICENSE_HEADER_END@
23: */
24:
25: /*
26: File: CatalogUtilities.c
27:
28: Contains: Private Catalog Manager support routines.
29:
30: Version: HFS Plus 1.0
31:
32: Copyright: � 1997-1999 by Apple Computer, Inc., all rights reserved.
33:
34: File Ownership:
35:
36: DRI: Don Brady
37:
38: Other Contact: Mark Day
39:
40: Technology: xxx put technology here xxx
41:
42: Writers:
43:
44: (DSH) Deric Horn
45: (msd) Mark Day
46: (djb) Don Brady
47:
48: Change History (most recent first):
49: <Rhap> 1/8/99 djb Fixing LocateCatalogNodeByMangledName...
50: <Rhap> 1/7/99 djb In BuildCatalogKeyUTF8 check name length against NAME_MAX.
51: <Rhap> 12/7/98 djb Add ExtractTextEncoding routine to get text encodings.
52: <Rhap> 11/20/98 djb Add support for UTF-8 names.
53: <Rhap> 8/31/98 djb GetTimeLocal now takes an input.
54: <Rhap> 4/17/98 djb Add VCB locking.
55: <Rhap> 4/3/98 djb Removed last name conversion cache from LocateCatalogNodeWithRetry.
56: <Rhap> 4/2/98 djb InvalidateCatalogNodeCache and TrashCatalogNodeCache are not used in Rhapsody.
57: <Rhap> 03/31/98 djb Sync up with final HFSVolumes.h header file.
58:
59: <CS24> 1/29/98 DSH Add TrashCatalogNodeCache for TrashAllFSCaches API support.
60: <CS23> 12/15/97 djb Radar #2202860, In LocateCatalogNodeByMangledName remap
61: cmParentNotFound error code to cmNotFound.
62: <CS22> 12/10/97 DSH 2201501, Pin the leof and peof to multiple of allocation blocks
63: under 2 Gig.
64: <CS21> 12/9/97 DSH 2201501, Pin returned leof values to 2^31-1 (SInt32), instead of
65: 2^32-1
66: <CS20> 11/26/97 djb Radar #2005688, 2005461 - need to handle kTextMalformedInputErr.
67: <CS19> 11/25/97 djb Radar #2002357 (again) fix new bug introduced in <CS18>.
68: <CS18> 11/17/97 djb PrepareInputName routine now returns an error.
69: <CS17> 10/19/97 msd Bug 1684586. GetCatInfo and SetCatInfo use only contentModDate.
70: <CS16> 10/17/97 djb Add ConvertInputNameToUnicode for Catalog Create/Rename.
71: <CS15> 10/14/97 djb Fix LocateCatalogNode's MakeFSSpec optimization (radar #1683166)
72: <CS14> 10/13/97 djb Copy text encoding in CopyCatalogNodeData. Fix cut/paste error
73: in VolumeHasEncodings macro. When accessing encoding bitmap use
74: the MapEncodingToIndex and MapIndexToEncoding macros.
75: <CS13> 10/1/97 djb Remove old Catalog Iterator code...
76: <CS12> 9/8/97 msd Make sure a folder's modifyDate is set whenever its
77: contentModDate is set.
78: <CS11> 9/4/97 djb Add MakeFSSpec optimization.
79: <CS10> 9/4/97 msd In CatalogNodeData, change attributeModDate to modifyDate.
80: <CS9> 8/26/97 djb Back out <CS4> (UpdateFolderCount must maintain vcbNmFls for HFS
81: Plus volumes too).
82: <CS8> 8/14/97 djb Remove hard link support.
83: <CS7> 7/18/97 msd Include LowMemPriv.h.
84: <CS6> 7/16/97 DSH FilesInternal.i renamed FileMgrInternal.i to avoid name
85: collision
86: <CS5> 7/8/97 DSH Loading PrecompiledHeaders from define passed in on C line
87: <CS4> 6/27/97 msd UpdateFolderCount should update number of root files/folders for
88: HFS volumes, not HFS Plus.
89: <CS3> 6/24/97 djb LocateCatalogNodeWithRetry did not always set result code.
90: <CS2> 6/24/97 djb Add LocateCatalogNodeByMangledName routine
91: <CS1> 6/24/97 djb first checked in
92: */
93:
94: #if ( PRAGMA_LOAD_SUPPORTED )
95: #pragma load PrecompiledHeaders
96: #else
97: #if TARGET_OS_MAC
98: #include <Errors.h>
99: #include <Files.h>
100: #include <FSM.h>
101: #include <Memory.h>
102: #include <TextUtils.h>
103: #include <Types.h>
104: #include <LowMemPriv.h>
105: #else
106: #include "../headers/system/MacOSStubs.h"
107: #include <sys/syslimits.h>
108: #endif /* TARGET_OS_MAC */
109: #endif /* PRAGMA_LOAD_SUPPORTED */
110:
111:
112: #include "../headers/FileMgrInternal.h"
113: #include "../headers/BTreesInternal.h"
114: #include "../headers/HFSVolumes.h"
115: #include "../headers/system/HFSUnicodeWrappers.h"
116: #include "../headers/CatalogPrivate.h"
117: #include <string.h>
118:
119: static void ExtractTextEncoding (ItemCount length, ConstUniCharArrayPtr string, UInt32 * textEncoding);
120:
121: //�������������������������������������������������������������������������������
122: // Routine: LocateCatalogNode
123: //
124: // Function: Locates the catalog record for an existing folder or file
125: // CNode and returns pointers to the key and data records.
126: //
127: //�������������������������������������������������������������������������������
128:
129: OSErr
130: LocateCatalogNode(const ExtendedVCB *volume, HFSCatalogNodeID folderID, const CatalogName *name,
131: UInt32 hint, CatalogKey *keyPtr, CatalogRecord *dataPtr, UInt32 *newHint)
132: {
133: OSErr result;
134: CatalogName *nodeName = NULL; /* To ward off uninitialized use warnings from compiler */
135: HFSCatalogNodeID threadParentID;
136:
137:
138: result = LocateCatalogRecord(volume, folderID, name, hint, keyPtr, dataPtr, newHint);
139:
140: #if TARGET_OS_MAC
141: //
142: // MakeFSSpec Optimization...report a missing parent
143: //
144: if ( (result == cmNotFound) && (LMGetHFSFlags() & (1 << reportMissingParent)) )
145: {
146: HFSCatalogNodeID parentID;
147: UInt16 dataSize;
148: OSErr err;
149: Boolean isHFSPlus = (volume->vcbSigWord == kHFSPlusSigWord);
150:
151: result = cmParentNotFound; // assume directory is missing
152:
153: if ( (name != NULL) && (CatalogNameLength(name, isHFSPlus) > 0) )
154: {
155: // Look at the previous record's parent ID to determine if the
156: // parent of the CNode we were looking for actually exists
157:
158: err = GetBTreeRecord(volume->catalogRefNum, -1, keyPtr, dataPtr, &dataSize, newHint);
159: if (err == noErr)
160: {
161: if ( isHFSPlus )
162: parentID = keyPtr->hfsPlus.parentID;
163: else
164: parentID = keyPtr->hfs.parentID;
165:
166: if ( folderID == parentID ) // did parent exists?
167: result = cmNotFound; // yes, so report a missing file to MakeFSSpec
168: }
169: }
170: }
171: #endif /* TARGET_OS_MAC */
172:
173: ReturnIfError(result);
174:
175: // if we got a thread record, then go look up real record
176: switch ( dataPtr->recordType )
177: {
178: case kHFSFileThreadRecord:
179: case kHFSFolderThreadRecord:
180: threadParentID = dataPtr->hfsThread.parentID;
181: nodeName = (CatalogName *) &dataPtr->hfsThread.nodeName;
182: break;
183:
184: case kHFSPlusFileThreadRecord:
185: case kHFSPlusFolderThreadRecord:
186: threadParentID = dataPtr->hfsPlusThread.parentID;
187: nodeName = (CatalogName *) &dataPtr->hfsPlusThread.nodeName;
188: break;
189:
190: default:
191: threadParentID = 0;
192: break;
193: }
194:
195: if ( threadParentID ) // found a thread
196: result = LocateCatalogRecord(volume, threadParentID, nodeName, kNoHint, keyPtr, dataPtr, newHint);
197:
198: return result;
199: }
200:
201: //
202: // Routine: LocateCatalogNodeByKey
203: //
204: // Function: Locates the catalog record for an existing folder or file
205: // CNode and returns the key and data records.
206: //
207:
208: OSErr
209: LocateCatalogNodeByKey(const ExtendedVCB *volume, UInt32 hint, CatalogKey *keyPtr,
210: CatalogRecord *dataPtr, UInt32 *newHint)
211: {
212: OSErr result;
213: CatalogName *nodeName = NULL; /* To ward off uninitialized use warnings from compiler */
214: HFSCatalogNodeID threadParentID;
215: UInt16 tempSize;
216:
217:
218: result = SearchBTreeRecord(volume->catalogRefNum, keyPtr, hint, keyPtr,
219: dataPtr, &tempSize, newHint);
220: if (result == btNotFound)
221: result = cmNotFound;
222: ReturnIfError(result);
223:
224: // if we got a thread record, then go look up real record
225: switch ( dataPtr->recordType )
226: {
227: case kHFSFileThreadRecord:
228: case kHFSFolderThreadRecord:
229: threadParentID = dataPtr->hfsThread.parentID;
230: nodeName = (CatalogName *) &dataPtr->hfsThread.nodeName;
231: break;
232:
233: case kHFSPlusFileThreadRecord:
234: case kHFSPlusFolderThreadRecord:
235: threadParentID = dataPtr->hfsPlusThread.parentID;
236: nodeName = (CatalogName *) &dataPtr->hfsPlusThread.nodeName;
237: break;
238:
239: default:
240: threadParentID = 0;
241: break;
242: }
243:
244: if ( threadParentID ) // found a thread
245: result = LocateCatalogRecord(volume, threadParentID, nodeName, kNoHint, keyPtr, dataPtr, newHint);
246:
247: return result;
248: }
249:
250:
251: #if 0
252: //�������������������������������������������������������������������������������
253: // Routine: LocateCatalogNodeWithRetry
254: //
255: // Function: Locates the catalog record for an existing folder or file node.
256: // For HFS Plus volumes a retry is performed when a catalog node is
257: // not found and the volume contains more than one text encoding.
258: //
259: //�������������������������������������������������������������������������������
260:
261: #define VolumeHasEncodings(v) \
262: ( ((v)->encodingsBitmap.lo | (v)->encodingsBitmap.hi) != 0 )
263:
264: #define EncodingInstalled(i) \
265: ( (fsVars)->gConversionContext[(i)].toUnicode != 0 )
266:
267: #define EncodingUsedByVolume(v,i) \
268: ( i < 32 ? ((v)->encodingsBitmap.lo & (1 << (i))) : ((v)->encodingsBitmap.hi & (1 << (i-32))) )
269:
270:
271: OSErr
272: LocateCatalogNodeWithRetry (const ExtendedVCB *volume, HFSCatalogNodeID folderID, ConstStr31Param pascalName, CatalogName *unicodeName,
273: UInt32 hint, CatalogKey *keyPtr, CatalogRecord *dataPtr, UInt32 *newHint)
274: {
275: TextEncoding defaultEncoding;
276: TextEncoding encoding;
277: ItemCount encodingsToTry;
278: FSVarsRec *fsVars;
279: OSErr result = cmNotFound;
280:
281: fsVars = (FSVarsRec*) LMGetFSMVars(); // used by macros
282:
283: defaultEncoding = GetDefaultTextEncoding();
284: encodingsToTry = CountInstalledEncodings();
285:
286: // 1. Try finding file using default encoding (typical case)
287:
288: {
289: --encodingsToTry;
290: result = PrepareInputName(pascalName, true, defaultEncoding, unicodeName);
291: if (result == noErr)
292: result = LocateCatalogNode(volume, folderID, unicodeName, hint, keyPtr, dataPtr, newHint);
293: else
294: result = cmNotFound;
295:
296: if ( result != cmNotFound || encodingsToTry == 0)
297: return result;
298: }
299:
300: //
301: // XXX if the pascal string contains all 7-bit ascii then we don't need to do anymore retries
302: //
303:
304: // 2. Try finding file using Mac Roman (if not already tried above)
305:
306: if ( defaultEncoding != kTextEncodingMacRoman )
307: {
308: --encodingsToTry;
309: result = PrepareInputName(pascalName, true, kTextEncodingMacRoman, unicodeName);
310: if (result == noErr)
311: result = LocateCatalogNode(volume, folderID, unicodeName, hint, keyPtr, dataPtr, newHint);
312: else
313: result = cmNotFound;
314:
315: if ( result != cmNotFound || encodingsToTry == 0 )
316: return result;
317: }
318:
319: // 3. Try with encodings from disk (if any)
320:
321: if ( VolumeHasEncodings(volume) ) // any left to try?
322: {
323: UInt32 index;
324:
325: index = 0; // since we pre increment this will skip MacRoman (which was already tried above)
326:
327: while ( index < kMacBaseEncodingCount )
328: {
329: ++index;
330:
331: encoding = MapIndexToEncoding(index);
332:
333: if ( encoding == defaultEncoding )
334: continue; // we did this one already
335:
336: if ( EncodingInstalled(index) && EncodingUsedByVolume(volume, index) )
337: {
338: --encodingsToTry;
339: result = PrepareInputName(pascalName, true, encoding, unicodeName);
340: if (result == noErr)
341: result = LocateCatalogNode(volume, folderID, unicodeName, hint, keyPtr, dataPtr, newHint);
342: else
343: result = cmNotFound;
344:
345: if ( result != cmNotFound || encodingsToTry == 0 )
346: return result;
347: }
348: }
349: }
350:
351: // 4. Try any remaining encodings (if any)
352:
353: {
354: UInt32 index;
355:
356: index = 0; // since we pre increment this will skip MacRoman (which was already tried above)
357:
358: while ( (encodingsToTry > 0) && (index < kMacBaseEncodingCount) )
359: {
360: ++index;
361:
362: encoding = MapIndexToEncoding(index);
363:
364: if ( encoding == defaultEncoding )
365: continue; // we did this one already
366:
367: if ( EncodingInstalled(index) && EncodingUsedByVolume(volume, index) == false )
368: {
369: --encodingsToTry;
370: result = PrepareInputName(pascalName, true, encoding, unicodeName);
371: if (result == noErr)
372: result = LocateCatalogNode(volume, folderID, unicodeName, hint, keyPtr, dataPtr, newHint);
373: else
374: result = cmNotFound;
375:
376: if ( result != cmNotFound || encodingsToTry == 0 )
377: return result;
378: }
379: }
380: }
381:
382: return cmNotFound;
383: }
384: #endif
385:
386: //�������������������������������������������������������������������������������
387: // Routine: LocateCatalogNodeByMangledName
388: //
389: // Function: Locates the catalog record associated with a mangled name (if any)
390: //
391: //�������������������������������������������������������������������������������
392:
393: OSErr
394: LocateCatalogNodeByMangledName( const ExtendedVCB *volume, HFSCatalogNodeID folderID,
395: const unsigned char * name, CatalogKey *keyPtr,
396: CatalogRecord *dataPtr, UInt32 *hintPtr )
397: {
398: HFSCatalogNodeID fileID;
399: unsigned char nodeName[64];
400: OSErr result;
401: ByteCount actualDstLen;
402: ByteCount prefixlen;
403:
404:
405: if (name == NULL || name[0] == '\0')
406: return cmNotFound;
407:
408: fileID = GetEmbeddedFileID(name, &prefixlen);
409:
410: if ( fileID < kHFSFirstUserCatalogNodeID )
411: return cmNotFound;
412:
413: result = LocateCatalogNode(volume, fileID, NULL, kNoHint, keyPtr, dataPtr, hintPtr);
414: if ( result == cmParentNotFound ) // GetCatalogNode already handled cmParentNotFound case <CS23>
415: result = cmNotFound; // so remap <CS23>
416: ReturnIfError(result);
417:
418: // first make sure that the parents match
419: if ( folderID != keyPtr->hfsPlus.parentID )
420: return cmNotFound; // not the same folder so this is a false match
421:
422: result = ConvertUnicodeToUTF8( keyPtr->hfsPlus.nodeName.length * sizeof (UniChar),
423: keyPtr->hfsPlus.nodeName.unicode,
424: 64,
425: &actualDstLen,
426: nodeName);
427:
428: if ( (actualDstLen < prefixlen) || bcmp(nodeName, name, prefixlen) != 0)
429: return cmNotFound; // mangled names didn't match so this is a false match
430:
431: return noErr; // we found it
432: }
433:
434:
435: //�������������������������������������������������������������������������������
436: // Routine: LocateCatalogRecord
437: //
438: // Function: Locates the catalog record associated with folderID and name
439: //
440: //�������������������������������������������������������������������������������
441:
442: OSErr
443: LocateCatalogRecord(const ExtendedVCB *volume, HFSCatalogNodeID folderID, const CatalogName *name,
444: UInt32 hint, CatalogKey *keyPtr, CatalogRecord *dataPtr, UInt32 *newHint)
445: {
446: OSErr result;
447: CatalogKey tempKey; // 518 bytes
448: UInt16 tempSize;
449:
450: BuildCatalogKey(folderID, name, (volume->vcbSigWord == kHFSPlusSigWord), &tempKey);
451:
452: if ( name == NULL )
453: hint = kNoHint; // no CName given so clear the hint
454:
455: result = SearchBTreeRecord(volume->catalogRefNum, &tempKey, hint, keyPtr, dataPtr, &tempSize, newHint);
456:
457: return (result == btNotFound ? cmNotFound : result);
458: }
459:
460:
461: //�������������������������������������������������������������������������������
462: // Routine: LocateCatalogThread
463: //
464: // Function: Locates a catalog thread record in the catalog BTree file and
465: // returns a pointer to the data record.
466: //
467: //�������������������������������������������������������������������������������
468:
469: OSErr
470: LocateCatalogThread(const ExtendedVCB *volume, HFSCatalogNodeID nodeID, CatalogRecord *threadData, UInt16 *threadSize, UInt32 *threadHint)
471: {
472: CatalogKey threadKey; // 518 bytes
473: OSErr result;
474:
475: //--- build key record
476:
477: BuildCatalogKey(nodeID, NULL, (volume->vcbSigWord == kHFSPlusSigWord), &threadKey);
478:
479: //--- locate thread record in BTree
480:
481: result = SearchBTreeRecord( volume->catalogRefNum, &threadKey, kNoHint, &threadKey,
482: threadData, threadSize, threadHint);
483:
484: return (result == btNotFound ? cmNotFound : result);
485: }
486:
487:
488: /*
489: * Routine: BuildCatalogKey
490: *
491: * Function: Constructs a catalog key record (ckr) given the parent
492: * folder ID and CName. Works for both classic and extended
493: * HFS volumes.
494: *
495: */
496:
497: void
498: BuildCatalogKey(HFSCatalogNodeID parentID, const CatalogName *cName, Boolean isHFSPlus, CatalogKey *key)
499: {
500: if ( isHFSPlus )
501: {
502: key->hfsPlus.keyLength = kHFSPlusCatalogKeyMinimumLength; // initial key length (4 + 2)
503: key->hfsPlus.parentID = parentID; // set parent ID
504: key->hfsPlus.nodeName.length = 0; // null CName length
505: if ( cName != NULL )
506: {
507: CopyCatalogName(cName, (CatalogName *) &key->hfsPlus.nodeName, isHFSPlus);
508: key->hfsPlus.keyLength += sizeof(UniChar) * cName->ustr.length; // add CName size to key length
509: }
510: }
511: else
512: {
513: key->hfs.keyLength = kHFSCatalogKeyMinimumLength; // initial key length (1 + 4 + 1)
514: key->hfs.reserved = 0; // clear unused byte
515: key->hfs.parentID = parentID; // set parent ID
516: key->hfs.nodeName[0] = 0; // null CName length
517: if ( cName != NULL )
518: {
519: UpdateCatalogName(cName->pstr, key->hfs.nodeName);
520: key->hfs.keyLength += key->hfs.nodeName[0]; // add CName size to key length
521: }
522: }
523: }
524:
525: /*
526: * for HFS, only MacRoman is supported. If a non-MacRoman character is found, an error is returned
527: */
528: OSErr
529: BuildCatalogKeyUTF8(HFSCatalogNodeID parentID, const char *name,
530: Boolean isHFSPlus, CatalogKey *key, UInt32 *textEncoding)
531: {
532: OSErr err = 0;
533: UInt32 nameLength;
534:
535: if ( name != NULL )
536: nameLength = strlen(name);
537: else
538: nameLength = 0;
539:
540: if (nameLength > NAME_MAX)
541: err = bdNamErr; /* name is too long */
542:
543: if ( isHFSPlus ) {
544: ByteCount unicodeBytes = 0;
545:
546: key->hfsPlus.keyLength = kHFSPlusCatalogKeyMinimumLength; // initial key length (4 + 2)
547: key->hfsPlus.parentID = parentID; // set parent ID
548: key->hfsPlus.nodeName.length = 0; // null CName length
549: if ( nameLength > 0 ) {
550: err = ConvertUTF8ToUnicode(nameLength, name, sizeof(key->hfsPlus.nodeName.unicode),
551: &unicodeBytes, key->hfsPlus.nodeName.unicode);
552: key->hfsPlus.nodeName.length = unicodeBytes / sizeof(UniChar);
553: key->hfsPlus.keyLength += unicodeBytes;
554: }
555:
556: ExtractTextEncoding(key->hfsPlus.nodeName.length, key->hfsPlus.nodeName.unicode, textEncoding);
557: }
558: else {
559: key->hfs.keyLength = kHFSCatalogKeyMinimumLength; // initial key length (1 + 4 + 1)
560: key->hfs.reserved = 0; // clear unused byte
561: key->hfs.parentID = parentID; // set parent ID
562: key->hfs.nodeName[0] = 0; // null CName length
563: if ( nameLength > 0 ) {
564: err = ConvertUTF8ToMacRoman(nameLength, name, &key->hfs.nodeName[0]);
565: key->hfs.keyLength += key->hfs.nodeName[0]; // add CName size to key length
566: }
567: *textEncoding = 0;
568: }
569:
570: if (err) {
571: if (err == kTECOutputBufferFullStatus)
572: err = bdNamErr; /* name is too long */
573: else
574: err = paramErr; /* name has invalid characters */
575: }
576:
577: return err;
578: }
579:
580:
581: /*
582: * make a guess at the text encoding value that coresponds to the Unicode characters
583: */
584: static void
585: ExtractTextEncoding(ItemCount length, ConstUniCharArrayPtr string, UInt32 * textEncoding)
586: {
587: int i;
588: UniChar ch;
589:
590: *textEncoding = 0;
591:
592: for (i = 0; i < length; ++i) {
593: ch = string[i];
594: /* CJK codepoints are 0x3000 thru 0x9FFF */
595: if (ch >= 0x3000) {
596: if (ch < 0xa000) {
597: *textEncoding = kTextEncodingMacJapanese;
598: break;
599: }
600:
601: /* fullwidth character codepoints are 0xFF00 thru 0xFFEF */
602: if (ch >= 0xff00 && ch <= 0xffef) {
603: *textEncoding = kTextEncodingMacJapanese;
604: break;
605: }
606: }
607: }
608: }
609:
610:
611: //�������������������������������������������������������������������������������
612: // Routine: FlushCatalog
613: //
614: // Function: Flushes the catalog for a specified volume.
615: //
616: //�������������������������������������������������������������������������������
617:
618: OSErr
619: FlushCatalog(ExtendedVCB *volume)
620: {
621: FCB * fcb;
622: OSErr result;
623:
624: fcb = GetFileControlBlock(volume->catalogRefNum);
625: result = BTFlushPath(fcb);
626:
627: if (result == noErr)
628: {
629: //--- check if catalog's fcb is dirty...
630:
631: if ( fcb->fcbFlags & fcbModifiedMask )
632: {
633: VCB_LOCK(volume);
634: volume->vcbFlags |= 0xFF00; // Mark the VCB dirty
635: volume->vcbLsMod = GetTimeLocal(volume->vcbSigWord == kHFSSigWord); // update last modified date
636: VCB_UNLOCK(volume);
637:
638: result = FlushVolumeControlBlock(volume);
639: }
640: }
641:
642: return result;
643: }
644:
645:
646: //�������������������������������������������������������������������������������
647: // Routine: InvalidateCatalogNodeCache
648: //
649: // Function: Invalidates the Catalog node cache if volume and folderID match
650: //
651: //�������������������������������������������������������������������������������
652: #if TARGET_OS_MAC
653: void
654: InvalidateCatalogNodeCache(ExtendedVCB *volume, HFSCatalogNodeID parentID)
655: {
656: FSVarsRec *fsVars;
657:
658: fsVars = (FSVarsRec*) LMGetFSMVars();
659:
660: if ( (fsVars->gCatalogFSSpec.parID == parentID) && (volume->vcbVRefNum == fsVars->gCatalogFSSpec.vRefNum) )
661: {
662: fsVars->gCatalogFSSpec.parID = 0;
663: fsVars->gCatalogFSSpec.vRefNum = 0;
664: }
665: }
666: #endif
667:
668: //�������������������������������������������������������������������������������
669: // Routine: TrashCatalogNodeCache
670: //
671: // Function: Invalidates the Catalog node cache.
672: //
673: //�������������������������������������������������������������������������������
674: #if TARGET_OS_MAC
675: void
676: TrashCatalogNodeCache( void )
677: {
678: FSVarsRec *fsVars = (FSVarsRec*) LMGetFSMVars();
679:
680: fsVars->gCatalogFSSpec.parID = 0;
681: fsVars->gCatalogFSSpec.vRefNum = 0;
682: }
683: #endif
684:
685:
686: //�������������������������������������������������������������������������������
687: // Routine: UpdateCatalogName
688: //
689: // Function: Updates a CName.
690: //
691: //�������������������������������������������������������������������������������
692:
693: void
694: UpdateCatalogName(ConstStr31Param srcName, Str31 destName)
695: {
696: Size length = srcName[0];
697:
698: if (length > CMMaxCName)
699: length = CMMaxCName; // truncate to max
700:
701: destName[0] = length; // set length byte
702:
703: BlockMoveData(&srcName[1], &destName[1], length);
704: }
705:
706:
707: //�������������������������������������������������������������������������������
708: // Routine: AdjustVolumeCounts
709: //
710: // Function: Adjusts the folder and file counts in the VCB
711: //
712: //�������������������������������������������������������������������������������
713:
714: void
715: AdjustVolumeCounts(ExtendedVCB *volume, SInt16 type, SInt16 delta)
716: {
717: //�� also update extended VCB fields...
718:
719: VCB_LOCK(volume);
720:
721: if (type == kHFSFolderRecord || type == kHFSPlusFolderRecord)
722: volume->vcbDirCnt += delta; // adjust volume folder count, �� worry about overflow?
723: else
724: volume->vcbFilCnt += delta; // adjust volume file count
725:
726: volume->vcbFlags |= 0xFF00; // Mark the VCB dirty
727: volume->vcbLsMod = GetTimeLocal(volume->vcbSigWord == kHFSSigWord); // update last modified date
728:
729: VCB_UNLOCK(volume);
730: }
731:
732:
733: //�������������������������������������������������������������������������������
734:
735: void
736: UpdateVolumeEncodings(ExtendedVCB *volume, TextEncoding encoding)
737: {
738: UInt32 index;
739:
740: encoding &= 0x7F;
741:
742: index = MapEncodingToIndex(encoding);
743:
744: VCB_LOCK(volume);
745:
746: if ( index < 32 )
747: volume->encodingsBitmap.lo |= (1 << index);
748: else
749: volume->encodingsBitmap.hi |= (1 << (index - 32));
750:
751: VCB_UNLOCK(volume);
752:
753: // vcb should already be marked dirty
754: }
755:
756:
757: //�������������������������������������������������������������������������������
758:
759: OSErr
760: UpdateFolderCount( ExtendedVCB *volume, HFSCatalogNodeID parentID, const CatalogName *name, SInt16 newType,
761: UInt32 hint, SInt16 valenceDelta)
762: {
763: CatalogKey tempKey; // 518 bytes
764: CatalogRecord tempData; // 520 bytes
765: UInt32 tempHint;
766: HFSCatalogNodeID folderID;
767: UInt16 recordSize;
768: OSErr result;
769:
770: #if 0
771: result = SearchBTreeRecord(volume->catalogRefNum, parentKey, hint,
772: &tempKey, &tempData, &recordSize, &tempHint);
773: if (result)
774: return (result == btNotFound ? cmNotFound : result);
775: #else
776:
777: result = LocateCatalogNode(volume, parentID, name, hint, &tempKey, &tempData, &tempHint);
778: ReturnIfError(result);
779: #endif
780:
781: if ( volume->vcbSigWord == kHFSPlusSigWord ) // HFS Plus
782: {
783: UInt32 timeStamp;
784:
785: if ( DEBUG_BUILD && tempData.recordType != kHFSPlusFolderRecord )
786: DebugStr("\p UpdateFolder: found HFS folder on HFS+ volume!");
787:
788: timeStamp = GetTimeUTC();
789: tempData.hfsPlusFolder.valence += valenceDelta; // adjust valence
790: tempData.hfsPlusFolder.contentModDate = timeStamp; // set date/time last modified
791: folderID = tempData.hfsPlusFolder.folderID;
792: recordSize = sizeof(tempData.hfsPlusFolder);
793: }
794: else // classic HFS
795: {
796: if ( DEBUG_BUILD && tempData.recordType != kHFSFolderRecord )
797: DebugStr("\p UpdateFolder: found HFS+ folder on HFS volume!");
798:
799: tempData.hfsFolder.valence += valenceDelta; // adjust valence
800: tempData.hfsFolder.modifyDate = GetTimeLocal(true); // set date/time last modified
801: folderID = tempData.hfsFolder.folderID;
802: recordSize = sizeof(tempData.hfsFolder);
803: }
804:
805: result = ReplaceBTreeRecord(volume->catalogRefNum, &tempKey, tempHint,
806: &tempData, recordSize, &tempHint);
807: ReturnIfError(result);
808:
809: if ( folderID == kHFSRootFolderID )
810: {
811: if (newType == kHFSFolderRecord || newType == kHFSPlusFolderRecord)
812: {
813: VCB_LOCK(volume);
814: volume->vcbNmRtDirs += valenceDelta; // adjust root folder count (undefined for HFS Plus)
815: VCB_UNLOCK(volume);
816: }
817: else
818: {
819: VCB_LOCK(volume);
820: volume->vcbNmFls += valenceDelta; // adjust root file count (used by GetVolInfo)
821: VCB_UNLOCK(volume);
822: }
823: }
824:
825: //XXX also update extended VCB fields...
826:
827: return result;
828: }
829:
830:
831: //�������������������������������������������������������������������������������
832:
833: UInt16
834: GetCatalogRecordSize(const CatalogRecord *dataRecord)
835: {
836: switch (dataRecord->recordType)
837: {
838: case kHFSFileRecord:
839: return sizeof(HFSCatalogFile);
840:
841: case kHFSFolderRecord:
842: return sizeof(HFSCatalogFolder);
843:
844: case kHFSPlusFileRecord:
845: return sizeof(HFSPlusCatalogFile);
846:
847: case kHFSPlusFolderRecord:
848: return sizeof(HFSPlusCatalogFolder);
849:
850: case kHFSFolderThreadRecord:
851: case kHFSFileThreadRecord:
852: return sizeof(HFSCatalogThread);
853:
854: case kHFSPlusFolderThreadRecord:
855: case kHFSPlusFileThreadRecord:
856: return sizeof(HFSPlusCatalogThread);
857:
858: default:
859: return 0;
860: }
861: }
862:
863:
864: //�������������������������������������������������������������������������������
865:
866: void
867: CopyCatalogNodeData(const ExtendedVCB *volume, const CatalogRecord *dataPtr, CatalogNodeData *nodeData)
868: {
869: switch (dataPtr->recordType)
870: {
871: case kHFSFolderRecord:
872: {
873: nodeData->nodeType = kCatalogFolderNode;
874: nodeData->nodeFlags = dataPtr->hfsFolder.flags;
875: nodeData->nodeID = dataPtr->hfsFolder.folderID;
876: nodeData->createDate = dataPtr->hfsFolder.createDate;
877: nodeData->contentModDate = dataPtr->hfsFolder.modifyDate;
878: nodeData->backupDate = dataPtr->hfsFolder.backupDate;
879: nodeData->valence = dataPtr->hfsFolder.valence;
880:
881: BlockMoveData(&dataPtr->hfsFolder.userInfo, &nodeData->finderInfo, 32); // copy 32 bytes of finder data
882: break;
883: }
884:
885: case kHFSFileRecord:
886: {
887: UInt32 i;
888:
889: nodeData->nodeType = kCatalogFileNode;
890: nodeData->nodeFlags = dataPtr->hfsFile.flags;
891: nodeData->nodeID = dataPtr->hfsFile.fileID;
892: nodeData->createDate = dataPtr->hfsFile.createDate;
893: nodeData->contentModDate = dataPtr->hfsFile.modifyDate;
894: nodeData->backupDate = dataPtr->hfsFile.backupDate;
895: nodeData->valence = 0;
896:
897: BlockMoveData(&dataPtr->hfsFile.userInfo, &nodeData->finderInfo, 16); // copy finder data
898: BlockMoveData(&dataPtr->hfsFile.finderInfo, &nodeData->extFinderInfo, 16); // copy finder data
899:
900: nodeData->dataLogicalSize = dataPtr->hfsFile.dataLogicalSize;
901: nodeData->dataPhysicalSize = dataPtr->hfsFile.dataPhysicalSize;
902: nodeData->rsrcLogicalSize = dataPtr->hfsFile.rsrcLogicalSize;
903: nodeData->rsrcPhysicalSize = dataPtr->hfsFile.rsrcPhysicalSize;
904:
905: for (i = 0; i < kHFSExtentDensity; ++i)
906: {
907: nodeData->dataExtents[i].startBlock = (UInt32) (dataPtr->hfsFile.dataExtents[i].startBlock);
908: nodeData->dataExtents[i].blockCount = (UInt32) (dataPtr->hfsFile.dataExtents[i].blockCount);
909:
910: nodeData->rsrcExtents[i].startBlock = (UInt32) (dataPtr->hfsFile.rsrcExtents[i].startBlock);
911: nodeData->rsrcExtents[i].blockCount = (UInt32) (dataPtr->hfsFile.rsrcExtents[i].blockCount);
912: }
913: break;
914: }
915:
916: case kHFSPlusFolderRecord:
917: {
918: nodeData->nodeType = kCatalogFolderNode;
919: nodeData->nodeFlags = dataPtr->hfsPlusFolder.flags;
920: nodeData->nodeID = dataPtr->hfsPlusFolder.folderID;
921: nodeData->textEncoding = dataPtr->hfsPlusFolder.textEncoding;
922: nodeData->createDate = UTCToLocal(dataPtr->hfsPlusFolder.createDate);
923: nodeData->contentModDate = UTCToLocal(dataPtr->hfsPlusFolder.contentModDate);
924: nodeData->backupDate = UTCToLocal(dataPtr->hfsPlusFolder.backupDate);
925: if (dataPtr->hfsPlusFolder.valence > 0xffff)
926: nodeData->valence = 0xffff; // pass maximum 16-bit value
927: else
928: nodeData->valence = dataPtr->hfsPlusFolder.valence;
929:
930: BlockMoveData(&dataPtr->hfsPlusFolder.userInfo, &nodeData->finderInfo, 32); // copy finder data
931: #if TARGET_OS_RHAPSODY
932: nodeData->accessDate = UTCToLocal(dataPtr->hfsPlusFolder.accessDate);
933: nodeData->attributeModDate = UTCToLocal(dataPtr->hfsPlusFolder.attributeModDate);
934: nodeData->permissions = dataPtr->hfsPlusFolder.permissions;
935: #endif
936: break;
937: }
938:
939: case kHFSPlusFileRecord:
940: {
941: UInt32 largestFileSizeUnder2Gig;
942:
943: nodeData->nodeType = kCatalogFileNode;
944: nodeData->nodeFlags = dataPtr->hfsPlusFile.flags;
945: nodeData->nodeID = dataPtr->hfsPlusFile.fileID;
946: nodeData->textEncoding = dataPtr->hfsPlusFile.textEncoding;
947: nodeData->createDate = UTCToLocal(dataPtr->hfsPlusFile.createDate);
948: nodeData->contentModDate = UTCToLocal(dataPtr->hfsPlusFile.contentModDate);
949: nodeData->backupDate = UTCToLocal(dataPtr->hfsPlusFile.backupDate);
950: nodeData->valence = 0;
951:
952: BlockMoveData(&dataPtr->hfsPlusFile.userInfo, &nodeData->finderInfo, 32); // copy finder data
953:
954: // 2201501, Pin values to 2^31-1 (SInt32)
955: largestFileSizeUnder2Gig = (0x7FFFFFFF / volume->blockSize) * volume->blockSize;
956:
957: if ( (dataPtr->hfsPlusFile.dataFork.logicalSize.hi == 0) && (dataPtr->hfsPlusFile.dataFork.logicalSize.lo <= largestFileSizeUnder2Gig) )
958: {
959: nodeData->dataLogicalSize = dataPtr->hfsPlusFile.dataFork.logicalSize.lo;
960: nodeData->dataPhysicalSize = (dataPtr->hfsPlusFile.dataFork.totalBlocks * volume->blockSize);
961: }
962: else
963: {
964: nodeData->dataPhysicalSize = largestFileSizeUnder2Gig;
965: nodeData->dataLogicalSize = nodeData->dataPhysicalSize; // signal to Open that this file is too big
966: nodeData->valence |= kLargeDataForkMask; // overload the valence for files over 2Gig
967: }
968:
969: if ( (dataPtr->hfsPlusFile.resourceFork.logicalSize.hi == 0) && (dataPtr->hfsPlusFile.resourceFork.logicalSize.lo <= largestFileSizeUnder2Gig) )
970: {
971: nodeData->rsrcLogicalSize = dataPtr->hfsPlusFile.resourceFork.logicalSize.lo;
972: nodeData->rsrcPhysicalSize = (SInt32) ((SInt32)dataPtr->hfsPlusFile.resourceFork.totalBlocks * (SInt32)volume->blockSize);
973: }
974: else
975: {
976: nodeData->rsrcPhysicalSize = largestFileSizeUnder2Gig;
977: nodeData->rsrcLogicalSize = nodeData->rsrcPhysicalSize; // signal to Open that this file is too big
978: nodeData->valence |= kLargeRsrcForkMask; // overload the valence for files over 2Gig
979: }
980:
981: // copy data and rsrc extents
982: BlockMoveData(&dataPtr->hfsPlusFile.dataFork.extents, &nodeData->dataExtents, sizeof(HFSPlusExtentRecord));
983: BlockMoveData(&dataPtr->hfsPlusFile.resourceFork.extents, &nodeData->rsrcExtents, sizeof(HFSPlusExtentRecord));
984: #if TARGET_OS_RHAPSODY
985: nodeData->accessDate = UTCToLocal(dataPtr->hfsPlusFile.accessDate);
986: nodeData->attributeModDate = UTCToLocal(dataPtr->hfsPlusFile.attributeModDate);
987: nodeData->permissions = dataPtr->hfsPlusFile.permissions;
988: #endif
989: break;
990: }
991:
992: default:
993: nodeData->nodeType = '????'; // must have hit a thread record
994: }
995: }
996:
997:
998: //�������������������������������������������������������������������������������
999: #if 0
1000: OSErr
1001: PrepareInputName(ConstStr31Param name, Boolean unicode, TextEncoding encoding, CatalogName *catalogName)
1002: {
1003: OSErr result = noErr;
1004:
1005: if (name == NULL)
1006: {
1007: catalogName->ustr.length = 0; // set length byte (works for both unicode and pascal)
1008: }
1009: else
1010: {
1011: Size length = name[0];
1012:
1013: if (length > CMMaxCName)
1014: length = CMMaxCName; // truncate to max
1015:
1016: if ( length == 0 )
1017: {
1018: catalogName->ustr.length = 0;
1019: }
1020: else if (unicode)
1021: {
1022: Str31 truncatedName;
1023: StringPtr pName;
1024:
1025: if (name[0] <= CMMaxCName) //�� should be CMMaxCName
1026: {
1027: pName = (StringPtr)name;
1028: }
1029: else
1030: {
1031: BlockMoveData(&name[1], &truncatedName[1], CMMaxCName);
1032: truncatedName[0] = CMMaxCName;
1033: pName = truncatedName;
1034: }
1035:
1036: //���need to pass pascal string length (could be clipped above)
1037: result = ConvertHFSNameToUnicode ( pName, encoding, &catalogName->ustr );
1038: }
1039: else
1040: {
1041: BlockMoveData(&name[1], &catalogName->pstr[1], length);
1042: catalogName->pstr[0] = length; // set length byte (might be smaller than name[0]
1043: }
1044: }
1045:
1046: return result;
1047: }
1048: #endif
1049:
1050: //_______________________________________________________________________
1051:
1052: #if 0
1053: void
1054: ConvertInputNameToUnicode(ConstStr31Param name, TextEncoding encodingHint, TextEncoding *actualEncoding, CatalogName *catalogName)
1055: {
1056: Size length;
1057: OSErr result;
1058:
1059: catalogName->ustr.length = 0; // setup defaults...
1060: *actualEncoding = encodingHint;
1061:
1062: if (name == NULL)
1063: return; // catalogName is empty string
1064:
1065: length = name[0];
1066:
1067: if (length == 0 || length > CMMaxCName)
1068: return; // catalogName is empty string
1069:
1070: result = ConvertHFSNameToUnicode ( name, encodingHint, &catalogName->ustr );
1071:
1072: if (result != noErr)
1073: {
1074: // The converter didn't like our string, try again with roman encoding
1075: // This takes care of errors like: kTextMalformedInputErr
1076: // kTECPartialCharErr
1077: // kTECUnmappableElementErr
1078: // kTECIncompleteElementErr
1079: //
1080: if ( encodingHint != kTextEncodingMacRoman )
1081: {
1082: *actualEncoding = kTextEncodingMacRoman;
1083: result = ConvertHFSNameToUnicode ( name, kTextEncodingMacRoman, &catalogName->ustr );
1084:
1085: }
1086:
1087: if (DEBUG_BUILD && result != noErr)
1088: DebugStr("\p ConvertHFSNameToUnicode could not convert string!");
1089: }
1090: }
1091: #endif
1092:
1093: //_______________________________________________________________________
1094:
1095: void
1096: CopyCatalogName(const CatalogName *srcName, CatalogName *dstName, Boolean isHFSPLus)
1097: {
1098: UInt32 length;
1099:
1100: if ( srcName == NULL )
1101: {
1102: if ( dstName != NULL )
1103: dstName->ustr.length = 0; // set length byte to zero (works for both unicode and pascal)
1104: return;
1105: }
1106:
1107: if (isHFSPLus)
1108: length = sizeof(UniChar) * (srcName->ustr.length + 1);
1109: else
1110: length = sizeof(UInt8) + srcName->pstr[0];
1111:
1112: if ( length > 1 )
1113: BlockMoveData(srcName, dstName, length);
1114: else
1115: dstName->ustr.length = 0; // set length byte to zero (works for both unicode and pascal)
1116: }
1117:
1118: //_______________________________________________________________________
1119:
1120: UInt32
1121: CatalogNameLength(const CatalogName *name, Boolean isHFSPlus)
1122: {
1123: if (isHFSPlus)
1124: return name->ustr.length;
1125: else
1126: return name->pstr[0];
1127: }
1128:
1129:
1130:
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.