|
|
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: Catalog.c
27:
28: Contains: Catalog Manager Implementation
29:
30: Version: HFS Plus 1.0
31:
32: Copyright: � 1996-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: (msd) Mark Day
45: (DSH) Deric Horn
46: (djb) Don Brady
47:
48: Change History (most recent first):
49: <Rhap> 2/2/99 djb Fix CreateFileIDRef to copy entire name when creating thread record.
50: <Rhap> 1/7/99 djb Use a max bytes of 256 in calls to ConvertUnicodeToUTF8.
51: <Rhap> 12/9/98 djb UpdateCatalogNode only updates vcbLsMod if contentModDate changes.
52: <Rhap> 11/5/98 djb Add support for UTF-8 names.
53: <Rhap> 8/31/98 djb GetTimeLocal now takes an input.
54: <Rhap> 7/8/98 ser Added accessDate and AttributeModDate init. to create routine.
55: <Rhap> 6/5/98 djb Added CreateFileIDRef routine.
56: <Rhap> 6/3/98 djb Merge MoveCatalogRecord and RenameCatalogRecord into one routine.
57: <Rhap> 4/17/98 djb Add VCB locking.
58: <Rhap> 4/6/98 djb Catalog iterators now need to be released.
59: <Rhap> 4/6/98 djb Removed CreateVolumeCatalogCache and DisposeVolumeCatalogCache (obsolete).
60: <Rhap> 3/31/98 djb Make UpdateCatalogNode interface thread-safe.
61: <Rhap> 3/31/98 djb Sync up with final HFSVolumes.h header file.
62: <Rhap> 3/17/98 djb Fixed CreateCatalogNode interface to take kCatalogFolderNode and
63: kCatalogFileNode as type input.
64:
65: <CS36> 12/10/97 DSH 2201501, UpdateCatalogNode to only update CatalogRecords which
66: are under 2 Gig by checking the overloaded valence field.
67: <CS35> 11/20/97 djb Radar #2002357. Fixing retry mechanism.
68: <CS34> 11/17/97 djb PrepareInputName routine now returns an error.
69: <CS33> 11/13/97 djb Radar #1683572. Add new GetCatalogOffspringFile routine for
70: PBGetFileInfo calls (support used to be in HFSPathnameCalls.a).
71: <CS32> 11/7/97 msd Change calls to the wrapper routine CompareUnicodeNames() to use
72: the underlying routine FastUnicodeCompare() instead.
73: <CS31> 10/19/97 msd Bug 1684586. GetCatInfo and SetCatInfo use only contentModDate.
74: <CS30> 10/17/97 djb Change Catalog Create/Rename to use ConvertInputNameToUnicode.
75: <CS29> 10/13/97 djb Update volumeNameEncodingHint when changing volume name. Change
76: name of GetSystemTextEncoding to GetDefaultTextEncoding.
77: <CS28> 10/1/97 djb Add new catalog iterators and node cache to improve performance.
78: <CS27> 9/12/97 msd In CreateCatalogNode, make sure parent is a folder, not a file.
79: <CS26> 9/10/97 msd In RenameCatalogNodeUnicode, remove HFS-only code and make sure
80: the conversion context is set up and marked in the volume's
81: bitmap.
82: <CS25> 9/9/97 DSH Added RelString_Glue to avoid having to link DFAEngine with
83: Interface.o
84: <CS24> 9/8/97 msd Make sure a folder's modifyDate is set whenever its
85: contentModDate is set. In UpdateCatalogNode, make sure the
86: modifyDate is greater or equal to contentModDate; do a DebugStr
87: only for debug builds.
88: <CS23> 9/7/97 djb Make some DebuStrs DEBUG_BUILD only.
89: <CS22> 9/4/97 djb Add more Catalog Iterators, Instrument RelString.
90: <CS21> 9/4/97 msd Remove call to PropertyDeleteObject.
91: <CS20> 8/18/97 DSH Use RelString instead of FastRelString in DFA to avoid loading
92: branch island instead of table.
93: <CS19> 8/14/97 djb Remove hard link support. Switch over to FastRelString.
94: <CS18> 8/8/97 djb Fixed bugs in LinkCatalogNode.
95: <CS17> 8/5/97 djb Don't restore vcbNxtCNID if thread exists (radar #1670614).
96: <CS16> 7/25/97 DSH Pass heuristicHint to BTSearchRecord from GetCatalogOffspring.
97: <CS15> 7/18/97 msd Include LowMemPriv.h. In LinkCatalogNode, now sets the
98: kInsertedFileThread2 flag correctly; should only affect error
99: recovery code.
100: <CS14> 7/16/97 DSH FilesInternal.i renamed FileMgrInternal.i to avoid name
101: collision
102: <CS13> 7/8/97 DSH Loading PrecompiledHeaders from define passed in on C line
103: <CS12> 6/27/97 msd Add PBLongRename SPI. Added RenameCatalogNodeUnicode call, which
104: takes Unicode names for HFS Plus volumes. Removed calls to
105: Attributes module when creating, renaming or moving nodes.
106: <CS11> 6/24/97 djb Validate the mangled name matches in
107: LocateCatalogNodeByMangledName.
108: <CS10> 6/24/97 djb Add hard link support.
109: <CS9> 6/20/97 msd Use contentModDate and attributeModDate fields instead of
110: modifyDate. Made CopyCatalogNodeData public.
111: <CS8> 6/18/97 djb Add routines LocateCatalogNodeWithRetry & UpdateVolumeEncodings.
112: Add mangled name retry to DeleteCatalogNode, MoveCatalogNode and
113: RenameCatalogNode.
114: <CS7> 6/13/97 djb Major changes for longname support and multiple scripts.
115: <CS6> 6/9/97 msd Instead of calling GetDateTime, call GetTimeUTC or GetTimeLocal.
116: Dates on an HFS Plus volume need to be converted to/from UTC.
117: <CS5> 6/4/97 djb Set textEncoding hint in Rename and Create. TrashCatalogIterator
118: was not always called with the correct folder ID.
119: <CS4> 5/21/97 djb Turn off recursive iterators.
120: <CS3> 5/19/97 djb Add support for B-tree iterators to GetCatalogOffspring.
121: <CS2> 5/9/97 djb Get in sync with FilesInternal.i.
122: <CS1> 4/24/97 djb First checked into Common System Project.
123: <HFS26> 4/11/97 DSH Use extended VCB fields catalogRefNum, and extentsRefNum.
124: <HFS25> 4/4/97 djb Get in sync with volume format changes.
125: <HFS24> 3/31/97 djb Additional HFS Plus optimization added to GetCatalogNode.
126: <HFS23> 3/28/97 djb Add Optimization to GetCatalogNode.
127: <HFS22> 3/27/97 djb Unicode conversion routines now use byte counts.
128: <HFS21> 3/17/97 DSH Casting to compile with SC, GetRecordSize ->
129: GetCatalogRecordSize, moved some prototypes to extern.
130: <HFS20> 3/5/97 msd Add calls to Property Manager when catalog entries are created,
131: deleted, moved, renamed.
132: <HFS19> 2/19/97 djb HFS Plus catalog keys no longer have a pad word.
133: <HFS18> 1/24/97 DSH (djb) GetCatalogOffSpring() fix volume->vcbDirIDM = 0
134: <HFS17> 1/23/97 DSH Truncate name to CMMaxCName characters in PrepareInputName().
135: <HFS16> 1/14/97 djb Fixed RenameCatalogNode for case when just a cnid is passed.
136: <HFS15> 1/13/97 djb Added support for varaible sized thread records in HFS+.
137: <HFS14> 1/11/97 DSH Moving PrepareInputName() declaration fo FilesInternal.h
138: <HFS13> 1/10/97 djb CopyCatalogNodeData was trashing the resource extents on HFS+.
139: <HFS12> 1/10/97 djb CopyCatalogNodeData was trashing dataLogicalSize on HFS+ disks.
140: <HFS11> 1/9/97 djb Get in sync with new HFSVolumesPriv.i.
141: <HFS10> 1/6/97 djb Added name length checking to CompareExtendedCatalogKeys. Fixed
142: GetCatalogOffspring - it was not correctly passing the HFS+ flag
143: to PrepareOutputName. Fixed BuildKey for HFS+ keys.
144: <HFS9> 1/3/97 djb Fixed termination bug in GetCatalogOffspring. Added support for
145: large keys. Integrated latest HFSVolumesPriv.h changes.
146: <HFS8> 12/19/96 DSH Changed call from C_FlushMDB to HFS+ savy
147: FlushVolumeControlBlock()
148: <HFS7> 12/19/96 djb Add new B-tree manager...
149: <HFS6> 12/13/96 djb Fixing bugs for HFS+. Switch to HFSUnicodeWrappers routines.
150: <HFS5> 12/12/96 djb Changed the SPI for GetCatalogNode, GetCatalogOffspring, and
151: UpdateCatalogNode.
152: <HFS4> 12/12/96 DSH Removed static function declarations for functions used by
153: FileIDServices.c.
154: <HFS3> 11/11/96 djb Added support for HFS+ Unicode names. Major changes throughout.
155: <HFS2> 11/4/96 djb Added FSSpec output to GetCatalogNode and GetCatalogOffspring
156: routines.
157: <HFS1> 10/29/96 djb first checked in
158:
159: */
160:
161: #pragma segment Catalog
162:
163: #if ( PRAGMA_LOAD_SUPPORTED )
164: #pragma load PrecompiledHeaders
165: #else
166: #if TARGET_OS_MAC
167: #include <Errors.h>
168: #include <Files.h>
169: #include <FSM.h>
170: #include <Memory.h>
171: #include <TextUtils.h>
172: #include <Types.h>
173: #include <LowMemPriv.h>
174: #else
175: #include "../headers/system/MacOSTypes.h"
176: #include "../headers/system/MacOSStubs.h"
177: #endif /* TARGET_OS_MAC */
178: #endif /* PRAGMA_LOAD_SUPPORTED */
179:
180: #include "../headers/FileMgrInternal.h"
181: #include "../headers/BTreesInternal.h"
182: #include "../headers/HFSVolumes.h"
183: #include "../headers/system/HFSUnicodeWrappers.h"
184: #include "../headers/system/HFSInstrumentation.h"
185: #include "../headers/CatalogPrivate.h"
186:
187:
188: // External routines
189:
190: extern SInt32 FastRelString( ConstStr255Param str1, ConstStr255Param str2 );
191:
192: extern SInt16 RelString_Glue(StringPtr pStr1, StringPtr pStr2);
193:
194:
195: // Internal routines
196:
197: static OSErr IterateCatalogNode( ExtendedVCB *volume, CatalogIterator *catalogIterator, UInt16 index,
198: FSSpec *nodeSpec, CatalogNodeData *nodeData, UInt32 *hint );
199:
200: void InitCatalogThreadRecord(ExtendedVCB *volume, UInt32 nodeType, CatalogKey *nodeKey,
201: CatalogRecord *record, UInt32 *threadSize);
202:
203: void InitCatalogRecord(ExtendedVCB *volume, UInt32 nodeType, UInt32 textEncoding,
204: CatalogRecord *record, UInt32 *recordSize, HFSCatalogNodeID *catalogNodeID);
205:
206: #if TARGET_OS_RHAPSODY && DIAGNOSTIC
207: #include <sys/systm.h>
208: #define PRINTIT(A) kprintf A;
209: #else
210: #define PRINTIT(A)
211: #endif
212:
213: //_________________________________________________________________________________
214: // Exported Routines
215: //
216: // CreateCatalogNode - Creates a new folder or file CNode.
217: // DeleteCatalogNode - Deletes an existing folder or file CNode.
218: // GetCatalogNode - Locates an existing folder or file CNode.
219: // GetCatalogOffspringFile - Gets an offspring file record from a folder.
220: // GetCatalogOffspring - Gets an offspring record from a folder.
221: // MoveRenameCatalogNode - Moves/Renames an existing folder or file CNode.
222: // UpdateCatalogNode - Marks a Catalog BTree node as 'dirty'.
223: // CreateFileIDRef - Creates a file thread record for hfs file node
224: // CompareCatalogKeys - Compares two catalog keys.
225: //
226: //_________________________________________________________________________________
227:
228:
229: //_________________________________________________________________________________
230: //
231: // About date/time values:
232: //
233: // Date/time values stored in control blocks and generic structures (such as
234: // CatalogNodeData) are always stored in local time. Values stored in HFS volume
235: // format structures (such as B-tree records) are also stored in local time.
236: // Values stored in HFS Plus format structures are stored in UTC.
237: //_________________________________________________________________________________
238:
239:
240: // Implementation
241:
242:
243: //_________________________________________________________________________________
244: // Routine: CreateCatalogNode
245: //
246: // Function: Creates a new folder or file CNode. A new folder or file
247: // record is added to the catalog BTree. If a folder CNode is
248: // being created, a new thread record is also added.
249: //
250: //_________________________________________________________________________________
251:
252: OSErr
253: CreateCatalogNode ( ExtendedVCB *volume, HFSCatalogNodeID parentID, ConstUTF8Param name,
254: UInt32 nodeType, HFSCatalogNodeID *catalogNodeID, UInt32 *catalogHint)
255: {
256: CatalogKey nodeKey; // 518 bytes
257: CatalogRecord nodeData; // 520 bytes
258: UInt32 nodeDataSize;
259: CatalogRecord parentThreadData; // 520 bytes
260: HFSCatalogNodeID parentsParentID;
261: CatalogName *parentNamePtr;
262: UInt32 tempHint;
263: UInt32 textEncoding;
264: UInt16 tempSize;
265: OSErr result;
266: Boolean isHFSPlus = (volume->vcbSigWord == kHFSPlusSigWord);
267:
268: if (nodeType != kCatalogFolderNode && nodeType != kCatalogFileNode)
269: return paramErr;
270:
271: //--- make sure parent exists (by locating the parent's thread record)
272:
273: result = LocateCatalogThread(volume, parentID, &parentThreadData, &tempSize, &tempHint);
274: ReturnIfError(result);
275:
276: TrashCatalogIterator(volume, parentID); // invalidate any iterators for this parentID
277:
278: // save copy of parent's parentID and name.
279:
280: if (isHFSPlus)
281: {
282: if (parentThreadData.recordType != kHFSPlusFolderThreadRecord)
283: return dirNFErr;
284:
285: parentsParentID = parentThreadData.hfsPlusThread.parentID;
286: parentNamePtr = (CatalogName*) &parentThreadData.hfsPlusThread.nodeName;
287: }
288: else
289: {
290: if (parentThreadData.recordType != kHFSFolderThreadRecord)
291: return dirNFErr;
292:
293: parentsParentID = parentThreadData.hfsThread.parentID;
294: parentNamePtr = (CatalogName*) &parentThreadData.hfsThread.nodeName;
295: }
296:
297: // invalidate cache for parent since its about to change
298: InvalidateCatalogNodeCache(volume, parentsParentID);
299:
300: //--- build key for new catalog node
301: result = BuildCatalogKeyUTF8(parentID, name, isHFSPlus, &nodeKey, &textEncoding);
302: ReturnIfError(result);
303:
304: //--- initialize catalog data record (for folder or file)
305: InitCatalogRecord(volume, nodeType, textEncoding, &nodeData, &nodeDataSize, catalogNodeID);
306:
307: //--- add new folder/file record to catalog BTree
308: result = InsertBTreeRecord(volume->catalogRefNum, &nodeKey, &nodeData, nodeDataSize, catalogHint);
309: if (result)
310: {
311: if (result == btExists)
312: result = cmExists;
313: return result;
314: }
315:
316: //--- build thread record for new CNode
317: if (isHFSPlus || nodeType == kCatalogFolderNode)
318: {
319: HFSCatalogKey threadKey; // use the smaller key since name is null
320: CatalogRecord threadData; // 520 bytes
321: UInt32 threadSize;
322:
323: BuildCatalogKey(*catalogNodeID, NULL, isHFSPlus, (CatalogKey*) &threadKey);
324:
325: InitCatalogThreadRecord(volume, nodeType, &nodeKey, &threadData, &threadSize);
326:
327: //--- add thread record to catalog BTree
328:
329: result = InsertBTreeRecord(volume->catalogRefNum, &threadKey, &threadData, threadSize, &tempHint);
330:
331: //--- couldn't add thread record, delete newly created folder record and exit
332: if (result)
333: {
334: (void) DeleteBTreeRecord(volume->catalogRefNum, &nodeKey);
335:
336: if ( result == btExists ) // <CS17>
337: result = cmExists; // remap to a catalog error
338:
339: return result;
340: }
341: }
342:
343: //--- update counters...
344:
345: result = UpdateFolderCount( volume, parentsParentID, parentNamePtr, nodeData.recordType, kNoHint, +1);
346: ReturnIfError(result); /* XXX what about cleanup ??? */
347:
348: AdjustVolumeCounts(volume, nodeData.recordType, +1);
349:
350: result = FlushCatalog(volume);
351:
352: return result;
353:
354: } // end CreateCatalogNode
355:
356:
357: /*
358: * initialize catalog data record (for folder or file)
359: */
360: void InitCatalogRecord(ExtendedVCB *volume, UInt32 nodeType, UInt32 textEncoding, CatalogRecord *record, UInt32 *recordSize, HFSCatalogNodeID *catalogNodeID)
361: {
362: HFSCatalogNodeID nodeID;
363: UInt32 timeStamp;
364:
365: ClearMemory(record, sizeof(CatalogRecord)); // first clear the record
366:
367: VCB_LOCK(volume);
368: nodeID = volume->vcbNxtCNID++; // get CNID and bump next available CNode ID
369: VCB_UNLOCK(volume);
370: *catalogNodeID = nodeID; // make sure it gets passed back
371:
372: if (volume->vcbSigWord == kHFSPlusSigWord)
373: {
374: timeStamp = GetTimeUTC(); // get current date/time (universal)
375:
376: UpdateVolumeEncodings(volume, textEncoding);
377:
378: if (nodeType == kCatalogFolderNode )
379: {
380: record->recordType = kHFSPlusFolderRecord;
381: record->hfsPlusFolder.folderID = nodeID;
382: record->hfsPlusFolder.createDate = timeStamp;
383: record->hfsPlusFolder.contentModDate = timeStamp;
384: record->hfsPlusFolder.accessDate = timeStamp;
385: record->hfsPlusFolder.attributeModDate = timeStamp;
386: record->hfsPlusFolder.textEncoding = textEncoding;
387: *recordSize = sizeof(HFSPlusCatalogFolder);
388: // threadType = kHFSPlusFolderThreadRecord;
389: }
390: else if (nodeType == kCatalogFileNode )
391: {
392: record->recordType = kHFSPlusFileRecord;
393: record->hfsPlusFile.fileID = nodeID;
394: record->hfsPlusFile.createDate = timeStamp;
395: record->hfsPlusFile.contentModDate = timeStamp;
396: record->hfsPlusFile.accessDate = timeStamp;
397: record->hfsPlusFile.attributeModDate = timeStamp;
398: record->hfsPlusFile.flags |= kHFSThreadExistsMask;
399: record->hfsPlusFile.textEncoding = textEncoding;
400: *recordSize = sizeof(HFSPlusCatalogFile);
401: // threadType = kHFSPlusFileThreadRecord;
402: }
403: }
404: else /* standard hfs */
405: {
406: timeStamp = GetTimeLocal(true); // get current local date/time
407:
408: if (nodeType == kCatalogFolderNode )
409: {
410: record->recordType = kHFSFolderRecord;
411: record->hfsFolder.folderID = nodeID;
412: record->hfsFolder.createDate = timeStamp;
413: record->hfsFolder.modifyDate = timeStamp;
414: *recordSize = sizeof(HFSCatalogFolder);
415: // threadType = kHFSFolderThreadRecord;
416: }
417: else if (nodeType == kCatalogFileNode )
418: {
419: record->recordType = kHFSFileRecord;
420: record->hfsFile.fileID = nodeID;
421: record->hfsFile.createDate = timeStamp;
422: record->hfsFile.modifyDate = timeStamp;
423: *recordSize = sizeof(HFSCatalogFile);
424: }
425: }
426: }
427:
428:
429: void InitCatalogThreadRecord(ExtendedVCB *volume, UInt32 nodeType, CatalogKey *nodeKey,
430: CatalogRecord *record, UInt32 *threadSize)
431: {
432: ClearMemory(record, sizeof(CatalogRecord) ); // first clear the record
433:
434: if (volume->vcbSigWord == kHFSPlusSigWord)
435: {
436: if (nodeType == kCatalogFolderNode)
437: record->recordType = kHFSPlusFolderThreadRecord;
438: else
439: record->recordType = kHFSPlusFileThreadRecord;
440: record->hfsPlusThread.parentID = nodeKey->hfsPlus.parentID;
441: *threadSize = sizeof(record->hfsPlusThread);
442:
443: // HFS Plus has varaible sized threads so adjust to actual length
444: *threadSize -= ( sizeof(record->hfsPlusThread.nodeName.unicode) -
445: (nodeKey->hfsPlus.nodeName.length * sizeof(UniChar)) );
446: BlockMoveData(&nodeKey->hfsPlus.nodeName, &record->hfsPlusThread.nodeName,
447: sizeof(UniChar) * (nodeKey->hfsPlus.nodeName.length + 1));
448: }
449: else // classic HFS
450: {
451: if (nodeType == kCatalogFolderNode)
452: record->recordType = kHFSFolderThreadRecord;
453: else
454: record->recordType = kHFSFileThreadRecord;
455: record->hfsThread.parentID = nodeKey->hfs.parentID;
456: *threadSize = sizeof(record->hfsThread);
457: BlockMoveData(&nodeKey->hfs.nodeName, &record->hfsThread.nodeName,
458: nodeKey->hfs.nodeName[0] + 1);
459: }
460: }
461:
462:
463: //_________________________________________________________________________________
464: // Routine: DeleteCatalogNode
465: //
466: // Function: Deletes an existing folder or file CNode. The thread record
467: // is also deleted for directories and files that have thread
468: // records.
469: //
470: // The valence for a folder must be zero before it can be deleted.
471: // The rootfolder cannot be deleted.
472: //
473: //_________________________________________________________________________________
474:
475: OSErr
476: DeleteCatalogNode(ExtendedVCB *volume, HFSCatalogNodeID parentID, ConstUTF8Param name, UInt32 hint)
477: {
478: CatalogKey key; // 518 bytes
479: CatalogRecord data; // 520 bytes
480: UInt32 nodeHint;
481: HFSCatalogNodeID nodeID;
482: HFSCatalogNodeID nodeParentID;
483: UInt32 textEncoding;
484: UInt16 nodeType;
485: OSErr result;
486: Boolean isHFSPlus = (volume->vcbSigWord == kHFSPlusSigWord);
487:
488: //--- locate subject catalog node
489:
490: result = BuildCatalogKeyUTF8(parentID, name, isHFSPlus, &key, &textEncoding);
491: ReturnIfError(result);
492:
493: result = LocateCatalogNodeByKey(volume, hint, &key, &data, &nodeHint);
494:
495: // if we did not find it by name, then look for an embedded file ID in a mangled name
496: if ( (result == cmNotFound) && isHFSPlus )
497: result = LocateCatalogNodeByMangledName(volume, parentID, name, &key, &data, &nodeHint);
498: ReturnIfError(result);
499:
500: nodeParentID = isHFSPlus ? key.hfsPlus.parentID : key.hfs.parentID; // establish real parent cnid
501: nodeType = data.recordType; // establish cnode type
502: nodeID = 0;
503:
504: switch (nodeType)
505: {
506: case kHFSFolderRecord:
507: if (data.hfsFolder.valence != 0) // is it empty?
508: return cmNotEmpty;
509:
510: nodeID = data.hfsFolder.folderID;
511: break;
512:
513: case kHFSPlusFolderRecord:
514: if (data.hfsPlusFolder.valence != 0) // is it empty?
515: return cmNotEmpty;
516:
517: nodeID = data.hfsPlusFolder.folderID;
518: break;
519:
520: case kHFSFileRecord:
521: if (data.hfsFile.flags & kHFSThreadExistsMask)
522: nodeID = data.hfsFile.fileID;
523: break;
524:
525: case kHFSPlusFileRecord:
526: nodeID = data.hfsPlusFile.fileID; // note: HFS Plus files always have a thread
527: break;
528:
529: default:
530: return cmNotFound;
531: }
532:
533:
534: if (nodeID == fsRtDirID) // is this the root folder?
535: return cmRootCN; // sorry, you can't delete the root!
536:
537: TrashCatalogIterator(volume, nodeParentID); // invalidate any iterators for this parentID
538: InvalidateCatalogNodeCache(volume, nodeParentID); // and invalidate node cache
539:
540: //--- delete catalog records for CNode and file threads if they exist
541:
542: result = DeleteBTreeRecord(volume->catalogRefNum, &key);
543: ReturnIfError(result);
544:
545: if ( nodeID )
546: {
547: CatalogKey threadKey; // 518 bytes
548:
549: BuildCatalogKey(nodeID, NULL, isHFSPlus, &threadKey);
550:
551: (void) DeleteBTreeRecord(volume->catalogRefNum, &threadKey); // ignore errors for thread deletes
552: }
553:
554: //--- update counters...
555:
556: result = UpdateFolderCount(volume, nodeParentID, NULL, nodeType, kNoHint, -1);
557: ReturnIfError(result);
558:
559: AdjustVolumeCounts(volume, nodeType, -1); // all done with this file or folder
560:
561: result = FlushCatalog(volume);
562:
563: return result;
564:
565: } // end DeleteCatalogNode
566:
567:
568: //_________________________________________________________________________________
569: // Routine: GetCatalogNode
570: //
571: // Function: Locates an existing folder or file CNode and returns an FSSpec for
572: // the CNode and a pointer to the CNode data record.
573: //
574: //_________________________________________________________________________________
575:
576: OSErr
577: GetCatalogNode( ExtendedVCB *volume, HFSCatalogNodeID parentID, ConstUTF8Param name, UInt32 hint,
578: FSSpec *nodeSpec, CatalogNodeData *nodeData, UInt32 *newHint)
579: {
580: CatalogKey *key;
581: CatalogRecord *record;
582: OSErr result = noErr;
583: Boolean isHFSPlus = (volume->vcbSigWord == kHFSPlusSigWord);
584: UInt32 textEncoding;
585: CatalogRecord catalogRecord; // 520 bytes
586: CatalogKey catalogKey; // 518 bytes
587: ByteCount actualDstLen;
588:
589: record = &catalogRecord;
590: key = &catalogKey;
591:
592: //--- Locate folder/file BTree record for the CNode
593: result = BuildCatalogKeyUTF8(parentID, name, isHFSPlus, key, &textEncoding);
594: ReturnIfError(result);
595: result = LocateCatalogNodeByKey(volume, hint, key, record, newHint);
596:
597: // if we did not find it by name, then look for an embedded file ID in a mangled name
598: if ( result == cmNotFound && isHFSPlus)
599: result = LocateCatalogNodeByMangledName(volume, parentID, name, key, record, newHint);
600: ReturnIfError(result);
601:
602: //--- Fill out the FSSpec (output)
603:
604: nodeSpec->vRefNum = volume->vcbVRefNum;
605: nodeSpec->parID = isHFSPlus ? key->hfsPlus.parentID : key->hfs.parentID;
606:
607: if ( isHFSPlus )
608: {
609: // if ( UnicodeBinaryCompare(&key->hfsPlus.nodeName, &nodeName.ustr) )
610: // {
611: // // HFS Plus Optimization: since nodeName == key->ckrCName just copy the original name!
612: // // Omit _expensive_ conversion back to ascii
613: //
614: // BlockMoveData(name, nodeSpec->name, name[0] + 1);
615: // }
616: // else
617: {
618: result = ConvertUnicodeToUTF8(key->hfsPlus.nodeName.length * sizeof(UniChar),
619: key->hfsPlus.nodeName.unicode,
620: NAME_MAX + 1, /* 255 + termination byte */
621: &actualDstLen,
622: nodeSpec->name);
623: }
624: }
625: else // classic HFS
626: {
627: result = ConvertMacRomanToUTF8(key->hfs.nodeName, NAME_MAX + 1, &actualDstLen, nodeSpec->name);
628: }
629:
630: //--- Fill out universal data record...
631: CopyCatalogNodeData(volume, record, nodeData);
632:
633: #if DEBUG_BUILD
634: if ( nodeData->nodeType != '????' )
635: if ( nodeData->nodeID > volume->vcbNxtCNID || nodeData->nodeID == 0)
636: DebugStr("\pGetCatalogOffspring: bad file ID found!");
637: #endif
638:
639: return result;
640:
641: } // end GetCatalogNode
642:
643:
644:
645: //_________________________________________________________________________________
646: // Routine: GetCatalogOffspringFile
647: //
648: // Function: Gets an offspring file from a specified folder. The folder
649: // is identified by it's folderID. The desired offspring file is
650: // indicated by the value of the file index (1 = 1st file offspring,
651: // 2 = 2nd file offspring, etc.).
652: //
653: //_________________________________________________________________________________
654:
655: OSErr
656: GetCatalogOffspringFile( ExtendedVCB *volume, HFSCatalogNodeID folderID, UInt16 fileIndex,
657: FSSpec *nodeSpec, CatalogNodeData *nodeData, UInt32 *hint )
658: {
659: CatalogIterator * catalogIterator;
660: UInt16 index;
661: UInt16 fileCounter;
662: OSErr result = noErr;
663:
664:
665: if ( folderID == 0 )
666: return cmNotFound;
667:
668: // get the best catalog iterator...
669: catalogIterator = GetCatalogIterator(volume, folderID, fileIndex, kIterateFilesOnly);
670:
671: index = catalogIterator->currentIndex; // Get last search's folder offset
672:
673: // make sure we're not looking for a file beyond this one...
674: if ( index == 0 || catalogIterator->fileIndex > fileIndex )
675: {
676: index = 1; // Start with the first item in the folder
677: fileCounter = fileIndex; // Need to look at all files up to and including fileIndex
678: }
679: else
680: {
681: fileCounter = fileIndex - catalogIterator->fileIndex + 1;
682:
683: if (fileCounter == 2) // if difference is one then no need to read previous file
684: {
685: ++index; // go to next entry
686: fileCounter = 1; // were looking for 1 file (the next one we find)
687: }
688: }
689:
690: while ( fileCounter > 0 )
691: {
692: result = IterateCatalogNode(volume, catalogIterator, index++, nodeSpec, nodeData, hint);
693:
694: if (result != noErr)
695: break;
696:
697: if ( nodeData->nodeType == kCatalogFileNode )
698: --fileCounter; // one down
699: }
700:
701: if ( result == noErr )
702: catalogIterator->fileIndex = fileIndex; // update current file index for next time
703:
704: (void) ReleaseCatalogIterator(catalogIterator);
705:
706: return result;
707:
708: } // end GetCatalogOffspringFile
709:
710:
711: //_________________________________________________________________________________
712: // Routine: GetCatalogOffspring
713: //
714: // Function: Gets an offspring record from a specified folder. The folder
715: // is identified by it's folderID. The desired offspring CNode is
716: // indicated by the value of the offspring index (1 = 1st offspring
717: // CNode, 2 = 2nd offspring CNode, etc.).
718: //
719: //_________________________________________________________________________________
720:
721: OSErr
722: GetCatalogOffspring( ExtendedVCB *volume, HFSCatalogNodeID folderID, UInt16 index,
723: FSSpec *nodeSpec, CatalogNodeData *nodeData, UInt32 *hint )
724: {
725: CatalogIterator * catalogIterator;
726: OSErr result;
727:
728:
729: if ( folderID == 0 )
730: return cmNotFound;
731:
732: // get best catalog iterator...
733: catalogIterator = GetCatalogIterator(volume, folderID, index, kIterateAll);
734:
735: result = IterateCatalogNode(volume, catalogIterator, index, nodeSpec, nodeData, hint);
736:
737: (void) ReleaseCatalogIterator(catalogIterator);
738:
739: return result;
740:
741: } // end GetCatalogOffspring
742:
743:
744: //_________________________________________________________________________________
745: // Routine: IterateCatalogNode
746: //
747: // Function: Gets an offspring record from a specified folder. The folder
748: // is identified by it's folderID. The desired offspring CNode is
749: // indicated by the value of the offspring index (1 = 1st offspring
750: // CNode, 2 = 2nd offspring CNode, etc.).
751: //
752: //_________________________________________________________________________________
753:
754: static OSErr
755: IterateCatalogNode( ExtendedVCB *volume, CatalogIterator *catalogIterator, UInt16 index,
756: FSSpec *nodeSpec, CatalogNodeData *nodeData, UInt32 *hint )
757: {
758: HFSCatalogNodeID offspringParentID;
759: CatalogKey * offspringKey;
760: CatalogRecord * offspringData;
761: CatalogName * offspringName;
762: BTreeIterator btreeIterator;
763: FSBufferDescriptor btRecord;
764: FCB * fcb;
765: SInt16 selectionIndex;
766: UInt16 tempSize;
767: UInt16 operation;
768: OSErr result;
769: Boolean isHFSPlus;
770: CatalogRecord catalogRecord; // 520 bytes
771: ByteCount actualDstLen;
772:
773: offspringData = &catalogRecord;
774:
775: isHFSPlus = (volume->vcbSigWord == kHFSPlusSigWord);
776: fcb = GetFileControlBlock(volume->catalogRefNum);
777:
778: // make a btree iterator from catalog iterator
779: UpdateBtreeIterator(catalogIterator, &btreeIterator);
780:
781: btRecord.bufferAddress = offspringData;
782: btRecord.itemCount = 1;
783: btRecord.itemSize = sizeof(CatalogRecord);
784:
785: //--- if neccessary position the iterator at the thread record for the specified folder
786:
787: if ( catalogIterator->currentIndex == 0 ) // is this a new iterator?
788: {
789: UInt32 heuristicHint;
790: UInt32 *cachedHint;
791:
792: // We pass a 2nd hint/guess into BTSearchRecord. The heuristicHint is a mapping of
793: // dirID and nodeNumber, in hopes that the current search will be in the same node
794: // as the last search with the same parentID.
795: result = GetMRUCacheBlock( catalogIterator->folderID, volume->hintCachePtr, (Ptr *)&cachedHint );
796: heuristicHint = (result == noErr) ? *cachedHint : kInvalidMRUCacheKey;
797:
798: result = BTSearchRecord( fcb, &btreeIterator, heuristicHint, &btRecord, &tempSize, &btreeIterator );
799: ExitOnError(result);
800:
801: UpdateCatalogIterator(&btreeIterator, catalogIterator); // update btree hint and key
802:
803: InsertMRUCacheBlock( volume->hintCachePtr, catalogIterator->folderID, (Ptr) &btreeIterator.hint.nodeNum );
804: }
805:
806: //--- get offspring record (relative to catalogIterator's position)
807:
808: selectionIndex = index - catalogIterator->currentIndex;
809:
810: // now we have to map index into next/prev operations...
811: if (selectionIndex == 1)
812: {
813: operation = kBTreeNextRecord;
814: }
815: else if (selectionIndex == -1)
816: {
817: operation = kBTreePrevRecord;
818: }
819: else if (selectionIndex == 0)
820: {
821: operation = kBTreeCurrentRecord;
822: }
823: else if (selectionIndex > 1)
824: {
825: UInt32 i;
826:
827: for (i = 1; i < selectionIndex; ++i)
828: {
829: result = BTIterateRecord( fcb, kBTreeNextRecord, &btreeIterator, &btRecord, &tempSize );
830: ExitOnError(result);
831: }
832: operation = kBTreeNextRecord;
833: }
834: else // (selectionIndex < -1)
835: {
836: SInt32 i;
837:
838: for (i = -1; i > selectionIndex; --i)
839: {
840: result = BTIterateRecord( fcb, kBTreePrevRecord, &btreeIterator, &btRecord, &tempSize );
841: ExitOnError(result);
842: }
843: operation = kBTreePrevRecord;
844: }
845:
846: result = BTIterateRecord( fcb, operation, &btreeIterator, &btRecord, &tempSize );
847: ExitOnError(result);
848:
849: offspringKey = (CatalogKey*) &btreeIterator.key;
850:
851: if (isHFSPlus)
852: {
853: offspringParentID = offspringKey->hfsPlus.parentID;
854: offspringName = (CatalogName*) &offspringKey->hfsPlus.nodeName;
855: }
856: else
857: {
858: offspringParentID = offspringKey->hfs.parentID;
859: offspringName = (CatalogName*) offspringKey->hfs.nodeName;
860: }
861:
862: if (offspringParentID != catalogIterator->folderID) // different parent?
863: {
864: AgeCatalogIterator(catalogIterator); // we reached the end, so don't hog the cache!
865:
866: result = cmNotFound; // must be done with this folder
867: goto ErrorExit;
868: }
869:
870: UpdateCatalogIterator(&btreeIterator, catalogIterator); // update btree hint and key
871: catalogIterator->currentIndex = index; // update the offspring index marker
872:
873: *hint = btreeIterator.hint.nodeNum; // return an old-style hint
874:
875: //--- Fill out the FSSpec...
876:
877: nodeSpec->vRefNum = volume->vcbVRefNum;
878: nodeSpec->parID = offspringParentID;
879:
880: if (isHFSPlus)
881: {
882: result = ConvertUnicodeToUTF8(offspringName->ustr.length * sizeof(UniChar),
883: offspringName->ustr.unicode,
884: NAME_MAX + 1, /* 255 + termination byte */
885: &actualDstLen,
886: nodeSpec->name);
887: }
888: else /* hfs name */
889: {
890: result = ConvertMacRomanToUTF8(offspringName->pstr, NAME_MAX + 1, &actualDstLen, nodeSpec->name);
891: }
892:
893: //--- Fill out universal data record...
894: CopyCatalogNodeData(volume, offspringData, nodeData);
895:
896:
897: #if DEBUG_BUILD
898: if ( nodeData->nodeType != '????' )
899: if ( nodeData->nodeID > volume->vcbNxtCNID || nodeData->nodeID == 0)
900: DebugStr("\pGetCatalogOffspring: bad file ID found!");
901: #endif
902:
903: return result;
904:
905: ErrorExit:
906:
907: if ( result == btNotFound )
908: result = cmNotFound;
909:
910: return result;
911:
912: } // end IterateCatalogNode
913:
914:
915: //_________________________________________________________________________________
916: // Routine: MoveRenameCatalogNode
917: //
918: // Function: Moves and/or rename an existing folder or file CNode.
919: // Note that when moving a folder, all decendants (its offspring,
920: // their offspring, etc.) are also moved.
921: //
922: // Assumes srcHint contains a text encoding that was set by a GetCatalogNode call
923: //_________________________________________________________________________________
924:
925: OSErr
926: MoveRenameCatalogNode(ExtendedVCB *volume, HFSCatalogNodeID srcParentID, ConstUTF8Param srcName,
927: UInt32 srcHint, HFSCatalogNodeID dstParentID, ConstUTF8Param dstName, UInt32 *newHint)
928: {
929: CatalogKey srcKey; // 518 bytes
930: CatalogRecord srcRecord; // 520 bytes
931: CatalogKey dstKey; // 518 bytes
932: CatalogKey dstFolderKey; // 518 bytes
933: HFSCatalogNodeID dstFolderParentID = 0;
934: UInt32 dstFolderHint;
935: CatalogName *dstFolderNamePtr = NULL;
936: CatalogRecord tmpRecord; // 520 bytes
937: HFSCatalogNodeID threadID;
938: UInt32 textEncoding;
939: OSErr result;
940: Boolean isNewName;
941: Boolean isHFSPlus = (volume->vcbSigWord == kHFSPlusSigWord);
942: Boolean isOrigDeleted = false;
943: short srcNameLen;
944: short dstNameLen;
945:
946:
947: result = BuildCatalogKeyUTF8(srcParentID, srcName, isHFSPlus, &srcKey, &textEncoding);
948: ReturnIfError(result);
949:
950: /* XXX can strlen and bcmp handle NULL pointers? */
951:
952: srcNameLen = strlen(srcName);
953: dstNameLen = strlen(dstName);
954:
955: //--- check if names match
956:
957: if ((srcNameLen == dstNameLen) && (bcmp(srcName, dstName, srcNameLen) == 0))
958: {
959: isNewName = false;
960: dstKey = srcKey;
961: if ( isHFSPlus ) {
962: dstKey.hfsPlus.parentID = dstParentID; // set parent ID
963: }
964: else {
965: dstKey.hfs.parentID = dstParentID; // set parent ID
966: }
967: }
968: else /* names are different */
969: {
970: isNewName = true;
971: result = BuildCatalogKeyUTF8(dstParentID, dstName, isHFSPlus, &dstKey, &textEncoding);
972: ReturnIfError(result);
973: }
974:
975: //--- make sure source record exists
976:
977: result = LocateCatalogNodeByKey(volume, srcHint, &srcKey, &srcRecord, &srcHint);
978:
979: // if we did not find it by name, then look for an embedded file ID in a mangled name
980: if ( (result == cmNotFound) && isHFSPlus )
981: result = LocateCatalogNodeByMangledName(volume, srcParentID, srcName, &srcKey, &srcRecord, &srcHint);
982: ReturnIfError(result);
983:
984: srcParentID = (isHFSPlus ? srcKey.hfsPlus.parentID : srcKey.hfs.parentID);
985:
986: // if we're moving then do some additional preflighting...
987:
988: if (srcParentID != dstParentID)
989: {
990: //--- make sure destination folder exists
991:
992: result = LocateCatalogNode(volume, dstParentID, NULL, kNoHint, &dstFolderKey, &tmpRecord, &dstFolderHint);
993: ReturnIfError(result);
994:
995: if (tmpRecord.recordType == kHFSPlusFolderRecord)
996: {
997: dstParentID = tmpRecord.hfsPlusFolder.folderID;
998: dstFolderParentID = dstFolderKey.hfsPlus.parentID;
999: dstFolderNamePtr = (CatalogName*) &dstFolderKey.hfsPlus.nodeName;
1000: }
1001: else if (tmpRecord.recordType == kHFSFolderRecord)
1002: {
1003: dstParentID = tmpRecord.hfsFolder.folderID;
1004: dstFolderParentID = dstFolderKey.hfs.parentID;
1005: dstFolderNamePtr = (CatalogName*) &dstFolderKey.hfs.nodeName;
1006: }
1007: else
1008: {
1009: return badMovErr;
1010: }
1011:
1012: //--- if source is a folder, make sure its a proper move
1013:
1014: if (srcRecord.recordType == kHFSPlusFolderRecord || srcRecord.recordType == kHFSFolderRecord)
1015: {
1016: HFSCatalogNodeID srcFolderID;
1017: HFSCatalogNodeID ancestorParentID;
1018: CatalogKey tempKey; // 518 bytes
1019: UInt32 tempHint;
1020:
1021: if (isHFSPlus)
1022: {
1023: srcFolderID = srcRecord.hfsPlusFolder.folderID;
1024: ancestorParentID = dstFolderKey.hfsPlus.parentID;
1025: }
1026: else
1027: {
1028: srcFolderID = srcRecord.hfsFolder.folderID;
1029: ancestorParentID = dstFolderKey.hfs.parentID;
1030: }
1031:
1032: if ( srcFolderID == fsRtDirID || // source == root?
1033: srcFolderID == dstParentID || // source == destination?
1034: srcFolderID == ancestorParentID ) // source == destination's parent?
1035: {
1036: return badMovErr;
1037: }
1038:
1039: while (ancestorParentID > fsRtDirID) // loop until we reach the root folder
1040: {
1041: // locate next folder up the tree...
1042: result = LocateCatalogNode(volume, ancestorParentID, NULL, kNoHint, &tempKey, &tmpRecord, &tempHint);
1043: ReturnIfError(result);
1044:
1045: ancestorParentID = isHFSPlus ? tempKey.hfsPlus.parentID : tempKey.hfs.parentID;
1046:
1047: if (srcFolderID == ancestorParentID) // source = destination ancestor?
1048: return badMovErr;
1049: }
1050: }
1051:
1052: TrashCatalogIterator(volume, dstParentID); // invalidate any iterators for destination parentID
1053: }
1054: else /* (srcParentID == dstParentID) */
1055: {
1056: if ( !isNewName )
1057: {
1058: *newHint = srcHint; // they match, so we're all done!
1059: return noErr;
1060: }
1061: }
1062:
1063: TrashCatalogIterator(volume, srcParentID); // invalidate any iterators for source's parentID
1064: InvalidateCatalogNodeCache(volume, srcParentID); // invalidate node cache since parent changed
1065:
1066: if (isNewName && isHFSPlus)
1067: {
1068: // update textEncoding hint (works for folders and files)
1069: srcRecord.hfsPlusFolder.textEncoding = textEncoding;
1070:
1071: UpdateVolumeEncodings(volume, textEncoding);
1072: }
1073:
1074: //--- insert source CNode record in BTree with new key (a new parent id and/or new name)
1075:
1076: result = InsertBTreeRecord(volume->catalogRefNum, &dstKey, &srcRecord, GetCatalogRecordSize(&srcRecord), newHint);
1077:
1078: if (result == btExists)
1079: {
1080: UInt16 dataSize;
1081:
1082: /* XXX what about the case: move id1,foo to id2,FOO ?? */
1083: if (srcParentID != dstParentID || isNewName == false)
1084: return cmExists;
1085:
1086: //--- new CNode name already exists in the same folder, locate the existing one
1087: result = SearchBTreeRecord(volume->catalogRefNum, &dstKey, srcHint,
1088: &dstFolderKey, &tmpRecord, &dataSize, newHint);
1089:
1090: if (result == btNotFound)
1091: result = cmNotFound;
1092: ReturnIfError(result);
1093:
1094: //--- check if its the same CNode (same name but different upper/lower case)
1095:
1096: if (srcRecord.recordType != tmpRecord.recordType)
1097: return cmExists;
1098:
1099: switch (srcRecord.recordType)
1100: {
1101: case kHFSPlusFileRecord: /* HFS Plus records share same cnid location */
1102: case kHFSPlusFolderRecord:
1103: if (srcRecord.hfsPlusFolder.folderID != tmpRecord.hfsPlusFolder.folderID)
1104: return cmExists;
1105: break;
1106:
1107: case kHFSFolderRecord:
1108: if (srcRecord.hfsFolder.folderID != tmpRecord.hfsFolder.folderID)
1109: return cmExists;
1110: break;
1111:
1112: case kHFSFileRecord:
1113: if (srcRecord.hfsFile.fileID != tmpRecord.hfsFile.fileID)
1114: return cmExists;
1115: break;
1116:
1117: default:
1118: return cmExists;
1119: }
1120:
1121: //--- same name but different case, so delete old and insert with new name...
1122:
1123: result = DeleteBTreeRecord(volume->catalogRefNum, &srcKey);
1124: ReturnIfError(result);
1125: isOrigDeleted = true; // So we dont delete it again down below
1126:
1127: result = InsertBTreeRecord(volume->catalogRefNum, &dstKey, &srcRecord, dataSize, newHint);
1128: }
1129: ReturnIfError(result);
1130:
1131: //
1132: // from this point on we need to cleanup (ie delete the new record) if we encounter errors!
1133: //
1134:
1135: //--- update thread record for node (if it exists)
1136:
1137: switch (srcRecord.recordType)
1138: {
1139: case kHFSPlusFileRecord:
1140: case kHFSPlusFolderRecord:
1141: threadID = srcRecord.hfsPlusFolder.folderID;
1142: break;
1143:
1144: case kHFSFolderRecord:
1145: threadID = srcRecord.hfsFolder.folderID;
1146: break;
1147:
1148: case kHFSFileRecord:
1149: if (srcRecord.hfsFile.flags & kHFSThreadExistsMask)
1150: {
1151: threadID = srcRecord.hfsFile.fileID;
1152: break;
1153: }
1154: /* fall through if no thread... */
1155:
1156: default:
1157: threadID = 0;
1158: }
1159:
1160: if (threadID)
1161: {
1162: UInt32 threadHint;
1163: CatalogKey threadKey; // 518 bytes
1164: CatalogRecord threadRecord; // 520 bytes
1165: UInt16 threadSize;
1166:
1167: result = LocateCatalogRecord(volume, threadID, NULL, kNoHint, &threadKey, &threadRecord, &threadHint);
1168: if (result != noErr) goto Exit_Delete;
1169:
1170: if (isHFSPlus)
1171: {
1172: if (srcParentID != dstParentID)
1173: threadRecord.hfsPlusThread.parentID = dstParentID;
1174: if (isNewName)
1175: CopyCatalogName((CatalogName *)&dstKey.hfsPlus.nodeName, (CatalogName *) &threadRecord.hfsPlusThread.nodeName, isHFSPlus);
1176:
1177: threadSize = sizeof(threadRecord.hfsPlusThread);
1178: // HFS Plus has varaible sized threads so adjust to actual length
1179: threadSize -= ( sizeof(threadRecord.hfsPlusThread.nodeName.unicode) - (threadRecord.hfsPlusThread.nodeName.length * sizeof(UniChar)) );
1180: }
1181: else
1182: {
1183: if (srcParentID != dstParentID)
1184: threadRecord.hfsThread.parentID = dstParentID;
1185: if (isNewName)
1186: CopyCatalogName((CatalogName *)&dstKey.hfs.nodeName,(CatalogName *) threadRecord.hfsThread.nodeName, isHFSPlus);
1187:
1188: threadSize = sizeof(threadRecord.hfsThread);
1189: }
1190:
1191: result = DeleteBTreeRecord(volume->catalogRefNum, &threadKey);
1192: if (result != noErr) goto Exit_Delete;
1193:
1194: result = InsertBTreeRecord(volume->catalogRefNum, &threadKey, &threadRecord, threadSize, &threadHint);
1195: if (result != noErr) goto Exit_Delete; //XXX exiting with a missing thread!
1196: }
1197:
1198: //--- we successfully added the new node so delete the old source CNode record
1199:
1200: if (! isOrigDeleted) {
1201: result = DeleteBTreeRecord(volume->catalogRefNum, &srcKey);
1202: if (result)
1203: {
1204: // uh oh, we could not delete the original
1205: // so we better get rid of the new node...
1206:
1207: (void) DeleteBTreeRecord(volume->catalogRefNum, &dstKey);
1208:
1209: //XXX also need to fix up the thread...
1210:
1211: return result;
1212: }
1213: }
1214:
1215: if (srcParentID != dstParentID)
1216: {
1217: result = UpdateFolderCount(volume, srcParentID, NULL, srcRecord.recordType, kNoHint, -1);
1218: result = UpdateFolderCount(volume, dstFolderParentID, dstFolderNamePtr, srcRecord.recordType, dstFolderHint, +1);
1219: }
1220:
1221: //--- make sure changes get flushed out
1222: VCB_LOCK(volume);
1223: volume->vcbFlags |= 0xFF00; // Mark the VCB dirty
1224: volume->vcbLsMod = GetTimeLocal(!isHFSPlus); // update last modified date
1225: VCB_UNLOCK(volume);
1226:
1227: (void) FlushCatalog(volume);
1228:
1229: return result;
1230:
1231:
1232: Exit_Delete:
1233: (void) DeleteBTreeRecord(volume->catalogRefNum, &dstKey);
1234:
1235: return result;
1236:
1237: } // end MoveRenameCatalogNode
1238:
1239:
1240: //_________________________________________________________________________________
1241: // Routine: UpdateCatalogNode
1242: //
1243: // Function: Marks the Catalog BTree node identified by the given catalog hint
1244: // as 'dirty'.
1245: //
1246: //_________________________________________________________________________________
1247:
1248:
1249: OSErr
1250: UpdateCatalogNode(ExtendedVCB *volume, HFSCatalogNodeID parentID, ConstUTF8Param name,
1251: UInt32 catalogHint, const CatalogNodeData *nodeData)
1252: {
1253: CatalogKey *key;
1254: CatalogRecord *record;
1255: UInt32 hint;
1256: UInt16 recordSize;
1257: OSErr result;
1258: TextEncoding textEncoding;
1259: CatalogKey catalogKey; // 518 bytes
1260: CatalogRecord catalogRecord; // 520 bytes
1261: Boolean isHFSPlus = volume->vcbSigWord == kHFSPlusSigWord;
1262:
1263: /* XXX no reason to have ptrs to local variables... */
1264: key = &catalogKey;
1265: record = &catalogRecord;
1266:
1267: result = BuildCatalogKeyUTF8(parentID, name, isHFSPlus, key, &textEncoding);
1268: ReturnIfError(result);
1269:
1270: //--- locate subject catalog node
1271:
1272: result = LocateCatalogNodeByKey(volume, catalogHint, key, record, &hint);
1273:
1274: // if we did not find it by name, then look for an embedded file ID in a mangled name
1275: if ( (result == cmNotFound) && isHFSPlus )
1276: result = LocateCatalogNodeByMangledName(volume, parentID, name, key, record, &hint);
1277:
1278: if (result == btNotFound)
1279: result = cmNotFound;
1280:
1281: if (catalogHint != hint)
1282: PRINTIT(("UpdateCatalogNode: catalogHint does not match (in: %ld, out: %ld)\n", catalogHint, hint));
1283: ReturnIfError(result);
1284:
1285: // update user modifiable fields in the catalog node record...
1286:
1287: switch (record->recordType)
1288: {
1289: case kHFSFolderRecord:
1290: {
1291: #if DEBUG_BUILD
1292: if (nodeData->nodeType != kCatalogFolderNode)
1293: DebugStr("\p UpdateCatalogNode: folder/file mismatch!");
1294: #endif
1295:
1296: record->hfsFolder.createDate = nodeData->createDate;
1297: record->hfsFolder.modifyDate = nodeData->contentModDate;
1298: record->hfsFolder.backupDate = nodeData->backupDate;
1299:
1300: *(DInfo*) &record->hfsFolder.userInfo = *(DInfo*) &nodeData->finderInfo;
1301: *(DXInfo*) &record->hfsFolder.finderInfo = *(DXInfo*) &nodeData->extFinderInfo;
1302:
1303: recordSize = sizeof(HFSCatalogFolder);
1304: break;
1305: }
1306:
1307: case kHFSFileRecord:
1308: {
1309: UInt32 i;
1310:
1311: #if DEBUG_BUILD
1312: if (nodeData->nodeType != kCatalogFileNode)
1313: DebugStr("\p UpdateCatalogNode: folder/file mismatch!");
1314: #endif
1315:
1316: record->hfsFile.flags = (UInt8) nodeData->nodeFlags;
1317: record->hfsFile.createDate = nodeData->createDate;
1318: record->hfsFile.modifyDate = nodeData->contentModDate;
1319: record->hfsFile.backupDate = nodeData->backupDate;
1320:
1321: record->hfsFile.dataLogicalSize = nodeData->dataLogicalSize;
1322: record->hfsFile.dataPhysicalSize = nodeData->dataPhysicalSize;
1323: record->hfsFile.rsrcLogicalSize = nodeData->rsrcLogicalSize;
1324: record->hfsFile.rsrcPhysicalSize = nodeData->rsrcPhysicalSize;
1325:
1326: *(FInfo*) &record->hfsFile.userInfo = *(FInfo*) &nodeData->finderInfo;
1327: *(FXInfo*) &record->hfsFile.finderInfo = *(FXInfo*) &nodeData->extFinderInfo;
1328:
1329: // copy extent info
1330: for (i = 0; i < kHFSExtentDensity; ++i)
1331: {
1332: record->hfsFile.dataExtents[i].startBlock = (UInt16) nodeData->dataExtents[i].startBlock;
1333: record->hfsFile.dataExtents[i].blockCount = (UInt16) nodeData->dataExtents[i].blockCount;
1334:
1335: record->hfsFile.rsrcExtents[i].startBlock = (UInt16) nodeData->rsrcExtents[i].startBlock;
1336: record->hfsFile.rsrcExtents[i].blockCount = (UInt16) nodeData->rsrcExtents[i].blockCount;
1337: }
1338:
1339: recordSize = sizeof(HFSCatalogFile);
1340: break;
1341: }
1342:
1343: case kHFSPlusFolderRecord:
1344: {
1345: record->hfsPlusFolder.createDate = LocalToUTC(nodeData->createDate);
1346: record->hfsPlusFolder.contentModDate = LocalToUTC(nodeData->contentModDate);
1347: record->hfsPlusFolder.backupDate = LocalToUTC(nodeData->backupDate);
1348: #if TARGET_OS_RHAPSODY
1349: record->hfsPlusFolder.accessDate = LocalToUTC(nodeData->accessDate);
1350: record->hfsPlusFolder.attributeModDate = LocalToUTC(nodeData->attributeModDate);
1351: record->hfsPlusFolder.permissions = nodeData->permissions;
1352: #endif
1353:
1354: BlockMoveData(&nodeData->finderInfo, &record->hfsPlusFolder.userInfo, 32); // copy all of finder data
1355:
1356: recordSize = sizeof(HFSPlusCatalogFolder);
1357: break;
1358: }
1359:
1360: case kHFSPlusFileRecord:
1361: {
1362: record->hfsPlusFile.flags = nodeData->nodeFlags;
1363: record->hfsPlusFile.createDate = LocalToUTC(nodeData->createDate);
1364: record->hfsPlusFile.contentModDate = LocalToUTC(nodeData->contentModDate);
1365: record->hfsPlusFile.backupDate = LocalToUTC(nodeData->backupDate);
1366: #if TARGET_OS_RHAPSODY
1367: record->hfsPlusFile.accessDate = LocalToUTC(nodeData->accessDate);
1368: record->hfsPlusFile.attributeModDate = LocalToUTC(nodeData->attributeModDate);
1369: record->hfsPlusFile.permissions = nodeData->permissions;
1370: #endif
1371:
1372: record->hfsPlusFile.dataFork.logicalSize.lo = nodeData->dataLogicalSize;
1373: record->hfsPlusFile.dataFork.totalBlocks = nodeData->dataPhysicalSize / volume->blockSize;
1374: BlockMoveData(&nodeData->dataExtents, &record->hfsPlusFile.dataFork.extents, sizeof(HFSPlusExtentRecord));
1375:
1376: record->hfsPlusFile.resourceFork.logicalSize.lo = nodeData->rsrcLogicalSize;
1377: record->hfsPlusFile.resourceFork.totalBlocks = nodeData->rsrcPhysicalSize / volume->blockSize;
1378: BlockMoveData(&nodeData->rsrcExtents, &record->hfsPlusFile.resourceFork.extents, sizeof(HFSPlusExtentRecord));
1379:
1380: BlockMoveData(&nodeData->finderInfo, &record->hfsPlusFile.userInfo, 32); // copy all of 32 bytes of Finder data
1381:
1382: recordSize = sizeof(HFSPlusCatalogFile);
1383: break;
1384: }
1385:
1386: default:
1387: return cmNotFound;
1388: }
1389:
1390: result = ReplaceBTreeRecord(volume->catalogRefNum, key, catalogHint, record, recordSize, &hint);
1391:
1392: if ( result == btNotFound )
1393: {
1394: result = cmNotFound;
1395: }
1396: else if ( result == noErr )
1397: {
1398: /* if we're just updating the accessDate then no need to change volume mod date */
1399: if (nodeData->contentModDate > volume->vcbLsMod ||
1400: (isHFSPlus && nodeData->attributeModDate > volume->vcbLsMod))
1401: {
1402: VCB_LOCK(volume);
1403: volume->vcbFlags |= 0xFF00; // Mark the VCB dirty
1404: volume->vcbLsMod = GetTimeLocal(!isHFSPlus); // update last modified date
1405: VCB_UNLOCK(volume);
1406: }
1407:
1408: result = FlushCatalog(volume); // flush the catalog
1409: }
1410:
1411: return result;
1412: }
1413:
1414:
1415: //_________________________________________________________________________________
1416: // Routine: CreateFileIDRef
1417: //
1418: // Function: Creates a file thread record for hfs file node
1419: //
1420: //_________________________________________________________________________________
1421:
1422: OSErr
1423: CreateFileIDRef(ExtendedVCB *volume, HFSCatalogNodeID parentID, ConstUTF8Param name, UInt32 hint, HFSCatalogNodeID *threadID)
1424: {
1425: CatalogKey nodeKey; // 518 bytes
1426: CatalogRecord nodeData; // 520 bytes
1427: HFSCatalogKey threadKey;
1428: HFSCatalogThread threadData;
1429: UInt32 nodeHint;
1430: UInt32 tempHint;
1431: OSErr result;
1432: Boolean isHFSPlus = (volume->vcbSigWord == kHFSPlusSigWord);
1433: UInt32 textEncoding;
1434:
1435: *threadID = 0;
1436:
1437: result = BuildCatalogKeyUTF8(parentID, name, isHFSPlus, &nodeKey, &textEncoding);
1438: ReturnIfError(result);
1439:
1440: //--- locate subject catalog node
1441:
1442: result = LocateCatalogNodeByKey(volume, hint, &nodeKey, &nodeData, &nodeHint);
1443:
1444: // if we did not find it by name, then look for an embedded file ID in a mangled name
1445: if ( (result == cmNotFound) && isHFSPlus )
1446: result = LocateCatalogNodeByMangledName(volume, parentID, name, &nodeKey, &nodeData, &nodeHint);
1447: ReturnIfError(result);
1448:
1449: if (nodeData.recordType == kHFSPlusFileRecord)
1450: {
1451: *threadID = nodeData.hfsPlusFile.fileID;
1452: return noErr; // already have one
1453: }
1454:
1455: if (nodeData.recordType != kHFSFileRecord)
1456: {
1457: return notAFileErr;
1458: }
1459:
1460:
1461: if (nodeData.hfsFile.flags & kHFSThreadExistsMask)
1462: {
1463: *threadID = nodeData.hfsFile.fileID;
1464: return noErr; // already have one
1465: }
1466:
1467: result = VolumeWritable( volume );
1468: if ( result != noErr ) return result;
1469:
1470: //
1471: // need to insert a thread record
1472: //
1473: BuildCatalogKey(nodeData.hfsFile.fileID, NULL, false, (CatalogKey *)&threadKey);
1474:
1475: ClearMemory(&threadData, sizeof(HFSCatalogThread));
1476: threadData.recordType = kHFSFileThreadRecord;
1477: threadData.parentID = nodeKey.hfs.parentID;
1478: BlockMoveData(&nodeKey.hfs.nodeName, &threadData.nodeName, nodeKey.hfs.nodeName[0] + 1);
1479:
1480: result = InsertBTreeRecord(volume->catalogRefNum, &threadKey, &threadData, sizeof(HFSCatalogThread), &tempHint);
1481: if (result == btExists) result = noErr; //XXX could return cmExists or fidExists
1482: ReturnIfError(result);
1483:
1484: //
1485: // Finally, set the flag in the file record to say this file has a thread record.
1486: //
1487: nodeData.hfsFile.flags |= kHFSThreadExistsMask;
1488: result = ReplaceBTreeRecord(volume->catalogRefNum, &nodeKey, nodeHint, &nodeData, sizeof(HFSCatalogFile), &nodeHint );
1489:
1490: if (result == noErr) {
1491: (void) FlushCatalog(volume);
1492: *threadID = nodeData.hfsFile.fileID;
1493: }
1494:
1495: return result;
1496: }
1497:
1498:
1499: //_________________________________________________________________________________
1500: // Routine: CompareCatalogKeys
1501: //
1502: // Function: Compares two catalog keys (a search key and a trial key).
1503: //
1504: // Result: +n search key > trial key
1505: // 0 search key = trial key
1506: // -n search key < trial key
1507: //_________________________________________________________________________________
1508:
1509: SInt32
1510: CompareCatalogKeys(HFSCatalogKey *searchKey, HFSCatalogKey *trialKey)
1511: {
1512: HFSCatalogNodeID searchParentID, trialParentID;
1513: SInt32 result;
1514:
1515: searchParentID = searchKey->parentID;
1516: trialParentID = trialKey->parentID;
1517:
1518: if ( searchParentID > trialParentID ) // parent dirID is unsigned
1519: result = 1;
1520: else if ( searchParentID < trialParentID )
1521: result = -1;
1522: else // parent dirID's are equal, compare names
1523: {
1524: #if ( ! FORDISKFIRSTAID )
1525: LogStartTime(kTraceRelString);
1526:
1527: result = FastRelString(searchKey->nodeName, trialKey->nodeName);
1528:
1529: LogEndTime(kTraceRelString, noErr);
1530: #else
1531: result = (SInt32) RelString_Glue(searchKey->nodeName, trialKey->nodeName);
1532: #endif
1533: }
1534:
1535: return result;
1536: }
1537:
1538:
1539: //_________________________________________________________________________________
1540: // Routine: CompareExtendedCatalogKeys
1541: //
1542: // Function: Compares two large catalog keys (a search key and a trial key).
1543: //
1544: // Result: +n search key > trial key
1545: // 0 search key = trial key
1546: // -n search key < trial key
1547: //_________________________________________________________________________________
1548:
1549: SInt32
1550: CompareExtendedCatalogKeys(HFSPlusCatalogKey *searchKey, HFSPlusCatalogKey *trialKey)
1551: {
1552: SInt32 result;
1553: HFSCatalogNodeID searchParentID, trialParentID;
1554:
1555: searchParentID = searchKey->parentID;
1556: trialParentID = trialKey->parentID;
1557:
1558: if ( searchParentID > trialParentID ) // parent node IDs are unsigned
1559: {
1560: result = 1;
1561: }
1562: else if ( searchParentID < trialParentID )
1563: {
1564: result = -1;
1565: }
1566: else // parent node ID's are equal, compare names
1567: {
1568: if ( searchKey->nodeName.length == 0 || trialKey->nodeName.length == 0 )
1569: result = searchKey->nodeName.length - trialKey->nodeName.length;
1570: else
1571: result = FastUnicodeCompare(&searchKey->nodeName.unicode[0], searchKey->nodeName.length,
1572: &trialKey->nodeName.unicode[0], trialKey->nodeName.length);
1573: }
1574:
1575: return result;
1576: }
1577:
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.