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