|
|
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: CatSearch.c
27:
28: Contains: CatSearch (fast search of all files on a volume)
29:
30: Version: System 7.5.x (Tempo?)
31:
32: Copyright: � 1996-1997 by Apple Computer, Inc., all rights reserved.
33:
34: File Ownership:
35:
36: DRI: Mark Day
37:
38: Other Contact: Don Brady
39:
40: Technology: HFS+
41:
42: Writers:
43:
44: (JL) Jim Luther
45: (RNP) Roger Pantos
46: (DSH) Deric Horn
47: (djb) Don Brady
48: (msd) Mark Day
49:
50: Change History (most recent first):
51: <Rhap> 03/31/98 djb Sync up with final HFSVolumes.h header file.
52:
53: 4/14/98 DSH Now also compile some compare routines for Rhapsody for use in searchfs().
54: <CS18> 11/3/97 JL #2001483 - Return WDCBRecPtr from CatSearchC.
55: <CS17> 10/19/97 msd Bug 1684586. GetCatInfo and SetCatInfo use only contentModDate.
56: <CS16> 10/17/97 msd Conditionalize DebugStrs.
57: <CS15> 10/10/97 msd Bug 1683670. InitCatSearch was calling the wrong routine to
58: flush the volume; only the user files were getting flushed.
59: Added casts to Ptr for first parameter to UprText.
60: <CS14> 9/27/97 RNP Replace UprString() with UprText().
61: <CS13> 9/9/97 msd The PLstrcmp routine we link against has a bug with one
62: character strings, so just call our own PascalBinaryCompare
63: instead.
64: <CS12> 9/4/97 msd In CatalogNodeData, change attributeModDate to modifyDate.
65: <CS11> 7/28/97 msd Use fast new B-Tree scanner.
66: <CS10> 7/23/97 msd Radar #1668914. CatSearch should compare the file's backup date
67: with the search info's backup date range, not the create date
68: range. The mod date compare had the same problem.
69: <CS9> 7/16/97 DSH FilesInternal.i renamed FileMgrInternal.i to avoid name
70: collision
71: <CS8> 7/8/97 DSH Loading PrecompiledHeaders from define passed in on C line
72: <CS7> 6/24/97 djb Include "CatalogInternal.h"
73: <CS6> 6/20/97 msd Use contentModDate and attributeModDate fields instead of
74: modifyDate. Use CatalogNodeData as common record format. Got rid
75: of NEW_CATSEARCH define by removing old code.
76: <CS5> 6/17/97 msd Bug #1661443. Initialize cat position to parent dirID =
77: fsRtParID. When restoring a cat position, special case that
78: parent ID since there is no such thread record. If ioSearchTime
79: is -1, assume they meant infinite, and use 0 instead. The call
80: to ConvertUnicodeToHFSName in CheckCriteria was getting
81: parameters from the wrong places.
82: <4> 6/13/97 djb Change PrepareOutputName calls to ConvertUnicodeToHFSName calls.
83: <CS3> 6/9/97 msd Convert HFS Plus dates from UTC to local time in MakeCatDirRec
84: and MakeCatFilRec.
85: <CS2> 5/16/97 msd Fix some string type conversion problems in InsertMatch().
86: <CS1> 4/24/97 djb first checked in
87: <HFS22> 4/16/97 djb Always use new B-tree code.
88: <HFS21> 4/11/97 DSH use extended VCB fields catalogRefNum, and extentsRefNum.
89: <HFS20> 3/27/97 djb Unicode string lengths are now passed as a byte count.
90: <HFS19> 2/19/97 djb Get in sync with new B-tree definitions...
91: <HFS18> 2/6/97 msd When instrumentation is active, log a complete FSSpec when a
92: match is inserted into the buffer.
93: <HFS17> 2/5/97 msd On HFS+ disks, CheckCriteria needs to test against the HFS-style
94: copy of the catalog record rather than the HFS variants of the
95: CatalogRecord of the actual catalog data.
96: <HFS16> 1/29/97 msd InitCatSearch needs to preset the BTree position by searching
97: for a node before the directory record of the root so that the
98: root will be searched, too.
99: <HFS15> 1/29/97 msd CheckCriteria was ignoring the fsSBNegate bit when testing the
100: ioDirMask bit in the file attributes. Made sure that thread
101: records never match.
102: <HFS14> 1/17/97 msd Add trivial instrumentation.
103: <HFS13> 1/16/97 msd When using the new BTree manager, the old btcWCount field is
104: always zero, so use numUpdateNodes instead. However,
105: numUpdateNodes can be zero so use numUpdateNodes+1 instead; this
106: still allows the caller to set the writeCount in the
107: CatPositionRec to zero to indicate a new search.
108: <HFS12> 1/9/97 djb Get in sync with new HFSVolumesPriv.i..
109: <HFS11> 1/9/97 msd The CatPosition was not being updated during or after the call.
110: Also, InitCatSearch was going back to the root directory instead
111: of the parent directory specified in the CatPosition.
112: InitCatSearch should not use GetCatalogNode; it should use
113: SearchBTreeRecord instead since we want to be able to find
114: thread records and move relative to them.
115: <HFS10> 1/3/97 djb Integrated latest HFSVolumesPriv.h changes.
116: <HFS9> 12/23/96 msd Oops. Re-enable timers.
117: <HFS8> 12/23/96 msd Update to use new BTree interfaces. Conditionalized the code via
118: the NEW_CATSEARCH symbol. The code should compile both ways, but
119: should be cleaned up later. There is a DebugStr in case the
120: BTree returns an unexpected error. Performance is noticably
121: worse than the prior implementation.
122: <HFS7> 12/19/96 DSH All refs to VCB are now refs to ExtendedVCB
123: <HFS6> 12/18/96 msd Use PrepareOutputName when copying a found item's name into
124: FSSpec array.
125: <HFS5> 12/12/96 msd Use the Unicode wrapper functions.
126: <HFS4> 12/10/96 msd First version of HFS+ changes complete. The HFS+ catalog data is
127: converted to a CatalogRecord before comparison. Copied code from
128: Catalog.c for dealing with the Unicode names.
129: <HFS3> 12/10/96 msd Begin HFS+ changes. Check PRAGMA_LOAD_SUPPORTED before loading
130: precompiled headers.
131: <HFS2> 12/10/96 msd Add support for precompiled headers.
132: <HFS1> 12/9/96 msd first checked in
133:
134: */
135:
136: /*
137: Public routines:
138: CatSearchC
139: Searches the entire volume for files/folders matching given
140: criteria. It does this by reading every record in the Catalog
141: BTree; all non-index records are compared to the given criteria.
142: And files/folders that match have their FSSpec's returned
143: in a caller-supplied buffer. The caller may also provide a
144: buffer to use when reading the BTree; otherwise, a default
145: buffer within the File Manager is used.
146:
147: VerifyCatSearchPB
148: Validates the parameters in a CatSearch parameter block to
149: make sure the values are mutually consistent and in range.
150:
151: Internal routines:
152: ExtOffLineCheck
153: Make sure the given volume is both online and HFS (or HFS+).
154: If not, an appropriate error is returned.
155:
156: StartScan Initialize state for scan and return first record.
157: EndScan Cleanup state and return hint
158: ScanNextRecord Get the next record in the scan.
159: */
160:
161: #if ( PRAGMA_LOAD_SUPPORTED )
162: #pragma load PrecompiledHeaders
163: #else
164: #if TARGET_OS_MAC
165: #include <Timer.h>
166: #include <TextUtils.h>
167: #include <LowMemPriv.h>
168: #include <PLStringFuncs.h>
169: #else
170: #include "../headers/system/MacOSStubs.h"
171: #endif /* TARGET_OS_MAC */
172: #endif /* PRAGMA_LOAD_SUPPORTED */
173:
174:
175:
176: #include "../headers/HFSBtreesPriv.h"
177: #include "../headers/HFSVolumes.h"
178: #include "../headers/FileMgrInternal.h"
179: #include "../headers/system/HFSUnicodeWrappers.h"
180: #include "../headers/BTreesInternal.h"
181: #include "../headers/BTreesPrivate.h"
182: #include "../headers/BTreeScanner.h"
183: #include "../headers/CatalogPrivate.h"
184:
185: #if HFSInstrumentation
186: #include "../headers/system/Instrumentation.h"
187: #endif
188:
189:
190: #ifndef CATSEARCH_DEBUG
191: #define CATSEARCH_DEBUG 1
192: #endif
193:
194: #define BTREE_CB_TYPE BTreeControlBlock
195: #define BTREE_WRITE_COUNT(btcb) (btcb->numUpdateNodes+1)
196:
197: extern OSErr VerifyCatSearchPB( const CSParam *pb, UInt8 *outAttributes, UInt8 *outFlags );
198: extern OSErr CatSearchC( CSParam *pb, WDCBRecPtr *wdcb );
199: extern OSErr ExtOffLineCheck( ExtendedVCB *vcb );
200:
201: extern OSErr CheckExternalFileSystem( ExtendedVCB *vcb );
202: extern OSErr DeturmineVolume3( VolumeParam *iopb, short *nameLength, Boolean *nameSpecified, ExtendedVCB **vcb, WDCBRec **wdcb, unsigned char **pathName );
203: extern OSErr FlushVolume( ExtendedVCB *vcb );
204: extern OSErr FlushVolumeBuffers( ExtendedVCB *vcb );
205: extern OSErr ChkNode_glue( BTNodeDescriptor *node, BTREE_CB_TYPE *btcb );
206: extern void LocRec_glue( BTNodeDescriptor *node, BTREE_CB_TYPE *btcb, UInt32 recordNumber,
207: Ptr *key, Ptr *data, UInt32 *recordSize );
208:
209: //
210: //�� These need to be moved to an interface file
211: //
212: enum {
213: kBTreePrevious = -1,
214: kBTreeCurrent = 0,
215: kBTreeNext = 1
216:
217: };
218:
219: enum {
220: //
221: // Masks for bits in the file attributes
222: //
223: // kFileLockedMask = 1,
224: kMaskBatAttributes = ~(ioDirMask | kHFSFileLockedMask),
225:
226: //
227: // Masks for search bits
228: //
229: kMaskNotSearchInfo2 = ~(fsSBPartialName | fsSBFullName | fsSBNegate),
230: kMaskLengthBits = (fsSBFlLgLen | fsSBFlPyLen | fsSBFlRLgLen | fsSBFlRPyLen),
231:
232: //
233: // Masks for flags
234: //
235: kIncludeDirs = 1,
236: kIncludeFiles = 2,
237: kIncludeNames = 4,
238:
239: kMinimumBufferSize = 512
240: };
241:
242:
243: struct CatPosition {
244: UInt32 writeCount; // The BTree's write count (to see if the catalogwriteCount
245: // changed since the last search). If 0, the rest
246: // of the record is invalid, start from beginning.
247: UInt32 nextNode; // node number to resume search
248: UInt32 nextRecord; // record number to resume search
249: UInt32 recordsFound; // number of leaf records seen so far
250: };
251: typedef struct CatPosition CatPosition;
252:
253:
254: typedef BTScanState ScanState;
255:
256:
257: struct CatSearchState {
258: TMTask timer; // MUST be first field!
259: CSParam *pb;
260: Str31 targetName; // Upper case version of name being matched against
261: FSSpec *nextMatch;
262: long searchBits;
263: CatalogKey *currentKey; // Pointer to key within current record
264: CatalogRecord *currentData; // Pointer to data within current record
265: #if HFSInstrumentation
266: InstTraceClassRef trace;
267: InstEventTag eventTag;
268: InstDataDescriptorRef traceDescriptor;
269: #endif
270: short vRefNum; // Volume reference of volume being searched
271: Boolean isHFS; // True if volume is HFS
272: UInt8 attributes; // desired file attributes to match against
273: UInt8 flags;
274: Boolean timerCompleted; // Set to true by timer's completion routine
275: };
276: typedef struct CatSearchState CatSearchState;
277:
278: #if TARGET_OS_MAC
279: static OSErr InitCatSearch( ExtendedVCB *volume, CSParam *pb, ScanState *scanState );
280: static void UpdateCatPosition( const ExtendedVCB *volume, CatPosition *endingPosition, const ScanState *scanState );
281: static void InsertMatch(CatSearchState *searchState);
282: static Boolean CheckCriteria(ExtendedVCB *vcb, const CatSearchState *searchState);
283: Boolean CompareMasked(const UInt32 *thisValue, const UInt32 *compareData,
284: const UInt32 *compareMask, UInt32 count);
285: Boolean ComparePartialName(ConstStr31Param thisName, ConstStr31Param substring);
286: Boolean CompareFullName(ConstStr31Param thisName, ConstStr31Param testName);
287: #endif /* TARGET_OS_MAC */
288: Boolean CompareRange(UInt32 val, UInt32 low, UInt32 high);
289: #define CompareRange(val, low, high) (val >= low && val <= high)
290:
291: extern void CSTimerGlue(void);
292: extern void CatSearchTimerComplete(CatSearchState *state);
293:
294: UInt32 Low32BitsPinned(UInt64 x);
295: #define Low32BitsPinned(x) (x.hi ? 0xFFFFFFFF : x.lo)
296:
297:
298:
299: /*
300: ;__________________________________________________________________________________
301: ;
302: ; Routine: CMCatSearch
303: ;
304: ; Function: Locates files by search criteria
305: ;
306: ; Input: A0.L - parameter block pointer
307: ;
308: ; Register Usage (during call):
309: ; A0.L - scratch D0.L - scratch
310: ; A1.L - scratch D1.L - scratch
311: ; A2.L - scratch D2.L - scratch
312: ; A3.L - pointer to CatSearch State Record (CSSR) D3.L - scratch
313: ; A4.L - pointer to PScan State Record (PSR) D4.L - scratch
314: ; A5.L - pointer to user's param block D5.L - ioSearchBits
315: ; sp - standard file system stack
316: ; D7.L - ioActMatchCount
317: ;
318: ; Output: D0.W - result code (noErr, extFSErr, ioErr, nsvErr, catChanged, paramErr)
319: ; A0.L, A1.L - trash
320: ; D0.L, D1.L, D2.L - trash
321: ;__________________________________________________________________________________
322: */
323: #if TARGET_OS_MAC
324: OSErr CatSearchC( CSParam *pb, WDCBRecPtr *wdcb )
325: {
326: OSErr err;
327: ExtendedVCB *vcb;
328: short volNameLength;
329: Boolean volNameSpecified;
330: unsigned char *pathname;
331: UInt32 catalogRecordSize;
332: ScanState scanState;
333: CatSearchState searchState;
334: Boolean timerInstalled = false;
335: #if HFSInstrumentation
336: FSVarsRec *fsVars = (FSVarsRec *) LMGetFSMVars();
337: #endif
338:
339:
340: #if HFSInstrumentation
341: searchState.trace = (InstTraceClassRef) fsVars->later[0];
342: searchState.traceDescriptor = (InstDataDescriptorRef) fsVars->later[1];
343: searchState.eventTag = InstCreateEventTag();
344: InstLogTraceEvent( searchState.trace, searchState.eventTag, kInstStartEvent);
345: #endif
346:
347: //
348: // Start a timer for the call
349: //
350: searchState.timerCompleted = false;
351:
352: if (pb->ioSearchTime != 0 && pb->ioSearchTime != -1) {
353: searchState.timer.tmAddr = (TimerUPP) CSTimerGlue;
354: InsTime((QElem *) &searchState.timer);
355: PrimeTime((QElem *) &searchState.timer, pb->ioSearchTime);
356: timerInstalled = true;
357: }
358:
359: //
360: // Find the volume specified
361: //
362: err = DeturmineVolume3((VolumeParam *) pb, &volNameLength, &volNameSpecified, &vcb, wdcb, &pathname);
363: if (err != noErr) goto ErrorExit;
364:
365: //
366: // Make sure the volume is ours, and online.
367: //
368: err = ExtOffLineCheck(vcb);
369: if (err != noErr) goto ErrorExit;
370:
371: //
372: // Set up some information about the volume
373: //
374: searchState.vRefNum = vcb->vcbVRefNum;
375: if (vcb->vcbSigWord == kHFSSigWord)
376: searchState.isHFS = true;
377: else
378: searchState.isHFS = false;
379:
380: //
381: // Clear ioActMatchCount
382: //
383: pb->ioActMatchCount = 0;
384:
385: //
386: // Verify the parameter block
387: //
388: err = VerifyCatSearchPB(pb, &searchState.attributes, &searchState.flags);
389: if (err != noErr) goto ErrorExit;
390:
391: //
392: // If no matches requested, exit early
393: //
394: if (pb->ioReqMatchCount == 0) {
395: err = noErr;
396: goto ErrorExit;
397: }
398:
399: searchState.pb = pb;
400: searchState.searchBits = pb->ioSearchBits;
401: searchState.nextMatch = pb->ioMatchPtr;
402:
403: //
404: // If a name is part of the criteria, prepare it for matching. (We make it upper
405: // case and remove diacriticals.)
406: //
407: if (searchState.flags & kIncludeNames) {
408: StringPtr name;
409: name = pb->ioSearchInfo1->hFileInfo.ioNamePtr;
410: // If caller's name is longer than HFS names can be, then
411: // we can optimize.
412: if (name[0] > 31) {
413: // Nothing can possibly match the given name. If the caller asked
414: // us to negate the search, then we need to return everything.
415: if (searchState.searchBits & fsSBNegate) {
416: searchState.searchBits = 0; // causes everything to match
417: }
418: else {
419: err = eofErr; // nothing left to match
420: goto ErrorExit;
421: }
422: }
423: else {
424: //
425: // Make a copy of the supplied name and convert it to upper case
426: // for later comparison.
427: //
428: //�� There is a bug in the assembly CatSearch: it calls _FSUprString which
429: //�� is really just _UpperString. It passes the length of the string in D0.
430: //�� According to the interfaces, the second parameter (in D0) is a boolean
431: //�� to determine whether it should be diacritical sensitive (i.e. whether it
432: //���should leave diacriticals vs. stripping them). Apparently, this always
433: //�� evaluates to false (testing the high bit of the word?).
434: //
435: //�� I disagree with the above analysis. The $A054 trap, which _FSUprString is
436: //�� aliased to, is actually UprStringTrap(), implemented in :SuperMario:OS:SysUtil.a.
437: //�� It does in fact take a ptr to text in A0 and len in D0. UpperString(),
438: //���on the other hand, is glue in OSTrapsGlue.a which munges
439: //�� the input parameters (including the diac boolean) and then turns around and calls
440: //�� _UprString, with A0 pointing to text and D0 holding length. Same as assembly CatSearch.
441: //�� So _UprString (or _UpperString) != UpperString(), sadly enough.
442: //�� - RNP.
443: //
444: // Note: we still do this even for HFS+, since we want to compare the names that
445: // would actually be returned in the same way as HFS. That is name comparisons
446: // for HFS and HFS+ should behave identically if they returned the same name via
447: // GetCatInfo, even though HFS+ may be truncating or otherwise mangling the name
448: // to fit in 31 characters; we want to compare against the mangled name.
449: //
450: BlockMoveData(name, searchState.targetName, sizeof(Str31));
451: UprText( (Ptr)(searchState.targetName + 1), searchState.targetName[ 0]);
452: }
453: }
454:
455: //
456: // Set up for the search.
457: //
458: err = InitCatSearch(vcb, pb, &scanState);
459: #if DEBUG_BUILD
460: if (err > 0) DebugStr("\pCatSearch: Internal Error");
461: #endif
462:
463: //
464: // Now loop over the records
465: //
466: while (err == noErr) {
467: err = BTScanNextRecord(&scanState, searchState.timerCompleted, (void**)&searchState.currentKey, (void**)&searchState.currentData, &catalogRecordSize);
468: if (err == noErr) {
469: // Check for a match
470: if (CheckCriteria(vcb, &searchState)) { // Found a match?
471: err = BTScanCacheNode(&scanState); // Make sure current node is cached
472: InsertMatch(&searchState); // Return in to caller
473: if (++pb->ioActMatchCount == pb->ioReqMatchCount) // Got enough matches yet?
474: break;
475: }
476: }
477: }
478:
479: //
480: // See if we've scanned the entire Catalog BTree. If so, return eofErr.
481: // If we exited because of a timeout, then it's not an error.
482: //
483: if (err == btNotFound || err == fsBTEndOfIterationErr)
484: err = eofErr;
485: if (err == fsBTTimeOutErr)
486: err = noErr;
487: #if DEBUG_BUILD
488: if (err > 0) DebugStr("\pCatSearch: Internal Error");
489: #endif
490: //
491: // Update caller's CatPosition so they can resume the search
492: //
493: UpdateCatPosition(vcb, (CatPosition *) &pb->ioCatPosition, &scanState);
494:
495: //
496: // If we get here, we never actually started a scan, so all we have left
497: // is to remove the timer (if we installed one) and return the error code.
498: //
499: ErrorExit:
500: //
501: // Stop timer
502: //
503: if (timerInstalled)
504: RmvTime((QElem *) &searchState.timer);
505:
506: #if HFSInstrumentation
507: InstLogTraceEvent( searchState.trace, searchState.eventTag, kInstEndEvent);
508: #endif
509:
510: return err;
511: }
512:
513: #endif
514:
515: /*
516: ;__________________________________________________________________________________
517: ;
518: ; Routine: VerifyCSPB
519: ;
520: ; Function: Verify that a param block contains legal values
521: ;
522: ; Input: A0.L - parameter block pointer
523: ;
524: ; Register Usage (during call):
525: ; A0.L - pointer to user's param block D0.L - scratch
526: ; A1.L - pointer to ioSearchInfo1 D1.L - scratch
527: ; D2.B - copy of user's ioFlAttrib mask
528: ; D3.L - cssr.flags byte
529: ; A4.L - pointer to ioSearchInfo2
530: ; A5.L - pointer to user's param block D5.L - ioSearchBits
531: ;
532: ; Output:
533: ; d0.W - result code (noErr, paramErr)
534: ; d2.b - copy of user's ioFlAttrib mask, ioDirFlg masked
535: ; d3.b - cssr.flags with inclFiles, inclDirs and inclNames set correctly
536: ;
537: ; Here are the parameter checking rules for PBCatSearch: <1.3>
538: ; The labels at the left also appear in the code which checks that rule.
539: ;
540: ; You get a paramErr if
541: ; P1 ioMBufPtr is nil
542: ; P2 ioSearchInfo1 is nil
543: ; P3 (ioSearchBits includes not(fsSBPartialName or fsSBFullName or fsSBNegate)) and ioSearchInfo2 is nil;
544: ; i.e. A selected search bit requires the presence of ioSearchInfo2
545: ;
546: ; Then these,
547: ; ioSearchBits rules: You get a paramErr if
548: ; P4 fsSBPartialName
549: ; and (ioSearchInfo1->ioNamePtr = (nil or empty string))
550: ; P5 fsSBFullName (same as above)
551: ; P6 fsSBFlAttrib and some bit other than 0 (locked) or 4 (directory) returns paramErr
552: ; P7 search includes directories and
553: ; (ioSearchBits includes fsSBFlAttrib) and (ioSearchInfo2^.ioFlAttrib bit 0 = 1)
554: ; return paramErr
555: ; (i.e. "locked directory" makes no sense)
556: ; P8 fsSBDrNmFls and files returns paramErr
557: ; P9 fsSBFlLgLen and directories returns paramErr
558: ; P10 fsSBFlPyLen (same as above)
559: ; P11 fsSBFlRLgLen (same as above)
560: ; P12 fsSBFlRPyLen (same as above)
561: ;
562: ; A search includes (files only) if
563: ; ioSearchBits includes fsSBFlAttrib
564: ; - and -
565: ; ioSearchInfo1^.ioFlAttrib bit 4 = 0 i.e. explicitly looking for non-directories
566: ; - and -
567: ; ioSearchInfo2^.ioFlAttrib bit 4 = 1
568: ;
569: ; A search includes (directories only) if
570: ; ioSearchBits includes fsSBFlAttrib
571: ; - and -
572: ; ioSearchInfo1^.ioFlAttrib bit 4 = 1 i.e. explicitly looking for directories
573: ; - and -
574: ; ioSearchInfo2^.ioFlAttrib bit 4 = 1
575: ;
576: ; A search includes both if
577: ; ioSearchBits includes fsSBFlAttrib
578: ; - and -
579: ; ioSearchInfo2^.ioFlAttrib bit 4 = 0 i.e. don't care about the directory bit
580: ;
581: ; - OR -
582: ; ioSearchBits doesn't include fsSBFlAttrib i.e. don't care about attributes at all
583: ;__________________________________________________________________________________
584: */
585:
586: OSErr VerifyCatSearchPB(
587: const CSParam *pb, // user's parameter block
588: UInt8 *outAttributes, // ioFlAttrib, with ioDirFlg cleared
589: UInt8 *outFlags) // cssr.flags with inclFiles, inclDirs and inclNames set correctly
590: {
591: SInt8 attributes = 0; // local copy of output outAttributes
592: UInt8 flags; // local copy of output outFlags
593: long searchBits; // copy of pb->ioSearchBits
594: CInfoPBPtr searchInfo1; // copy of pb->ioSearchInfo1
595: CInfoPBPtr searchInfo2; // copy of pb->ioSearchInfo2
596:
597: //
598: // P1: User must supply a buffer to return the found FSSpec's
599: //
600: if (pb->ioMatchPtr == NULL)
601: goto ParamErrExit;
602:
603: //
604: // P2: Must supply values to compare against (ioSearchInfo1)
605: //
606: searchInfo1 = pb->ioSearchInfo1;
607: searchInfo2 = pb->ioSearchInfo2;
608: if (searchInfo1 == NULL)
609: goto ParamErrExit;
610:
611: searchBits = pb->ioSearchBits;
612:
613: //
614: // P3: See if we need any end-of-range or mask values (in ioSearchInfo2).
615: // If so, the pointer must be non-NULL.
616: //
617: if (((searchBits & kMaskNotSearchInfo2) != 0) && (searchInfo2 == NULL))
618: goto ParamErrExit;
619:
620: flags = 0;
621:
622: //
623: // P4-5: If we're searching by name (partial or full), then check the name passed.
624: //
625: if (searchBits & (fsSBPartialName | fsSBFullName)) {
626: StringPtr name = searchInfo1->hFileInfo.ioNamePtr;
627: if (name == NULL || name[0] == 0)
628: goto ParamErrExit;
629: flags |= kIncludeNames;
630: }
631:
632: //
633: // Determine whether we're looking for files, folders, or both.
634: //
635: if (searchBits & fsSBFlAttrib) {
636: attributes = searchInfo2->hFileInfo.ioFlAttrib; // get mask for attributes
637: if (attributes & kMaskBatAttributes) // P6: only locked and directory bits are allowed
638: goto ParamErrExit;
639: if (attributes & ioDirMask) // do they care whether object is a file or folder?
640: if (searchInfo1->hFileInfo.ioFlAttrib & ioDirMask)
641: flags |= kIncludeDirs; // wanted folders only
642: else
643: flags |= kIncludeFiles; // wanted files only
644: else
645: flags |= (kIncludeDirs | kIncludeFiles); // caller didn't care about files vs. folders
646: }
647: else
648: flags |= (kIncludeDirs | kIncludeFiles);
649:
650: attributes &= ~ioDirMask; // clear ioDirMask since it isn't actually on disk
651:
652: //
653: // P7: If they cared about file attributes, and search includes folders, then
654: // ioLockFlag must not be set (since it is meaningless for folders).
655: //
656: if ((flags & kIncludeDirs) && (searchBits & fsSBFlAttrib) && (attributes & kHFSFileLockedMask))
657: goto ParamErrExit;
658:
659: //
660: // P8: If search includes files, then # of files per folder must not be set.
661: //
662: if ((searchBits & fsSBDrNmFls) && (flags & kIncludeFiles))
663: goto ParamErrExit;
664:
665: //
666: // P9-P12: If search includes directories, then none of the fork length bits
667: // must be set.
668: //
669: if ((flags & kIncludeDirs) && (searchBits & kMaskLengthBits))
670: goto ParamErrExit;
671:
672: //
673: // When we get here, all the checks have passed. Return the output flags
674: // and file attributes.
675: //
676: *outAttributes = attributes;
677: *outFlags = flags;
678: return noErr;
679:
680: //
681: // If we get here, there was a problem with one of the parameters.
682: //
683: ParamErrExit:
684: return paramErr;
685: }
686:
687:
688:
689: //
690: // Copy the FSSpec for the current catalog node into the caller's match buffer
691: //
692: #if TARGET_OS_MAC
693: static void InsertMatch(CatSearchState *searchState)
694: {
695: CatalogKey *current;
696: FSSpec *match;
697:
698: // Get the key for the item we just found, and the place where we should
699: // store the item's FSSpec
700: current = searchState->currentKey;
701: match = searchState->nextMatch;
702:
703: // Copy vRefNum
704: match->vRefNum = searchState->vRefNum;
705:
706: if (searchState->isHFS) {
707: // Copy parentDirID
708: match->parID = current->hfs.parentID;
709:
710: // Copy the name as a PString
711: BlockMoveData(current->hfs.nodeName, &match->name[0], StrLength(current->hfs.nodeName) + 1);
712: }
713: else {
714: TextEncoding encoding;
715: HFSCatalogNodeID cnid;
716:
717: cnid = searchState->currentData->hfsPlusFile.fileID; // note: cnid at same offset for folders
718: encoding = searchState->currentData->hfsPlusFile.textEncoding; // note: textEncoding at same offset for folders
719:
720: // Copy parentDirID
721: match->parID = current->hfsPlus.parentID;
722:
723: // Copy the name as a PString
724: (void) ConvertUnicodeToHFSName( ¤t->hfsPlus.nodeName, encoding, cnid, &match->name[0]);
725: }
726:
727: #if HFSInstrumentation
728: // InstLogTraceEvent( searchState->trace, searchState->eventTag, kInstMiddleEvent);
729: {
730: struct {
731: long vRefNum;
732: long parID;
733: char name[32];
734: } x;
735: unsigned len;
736:
737: x.vRefNum = match->vRefNum;
738: x.parID = match->parID;
739:
740: len = match->name[0];
741: BlockMoveData(&match->name[1], &x.name[0], len);
742: x.name[len] = 0;
743:
744: InstLogTraceEventWithDataStructure( searchState->trace, searchState->eventTag, kInstMiddleEvent,
745: searchState->traceDescriptor, (UInt8 *) &x, sizeof(x));
746: }
747: #endif
748:
749: // Point to next FSSpec in caller's buffer
750: searchState->nextMatch++;
751: }
752: #endif /* TARGET_OS_MAC */
753:
754:
755:
756: //
757: // Determine whether the current catalog node matches the caller's criteria. Return
758: // true if the object matches.
759: //
760: #if TARGET_OS_MAC
761: static Boolean CheckCriteria(ExtendedVCB *vcb, const CatSearchState *searchState)
762: {
763: CatalogKey *key;
764: Boolean matched;
765: long searchBits;
766: CInfoPBRec *searchInfo1;
767: CInfoPBRec *searchInfo2;
768: CatalogNodeData catData;
769:
770: key = searchState->currentKey;
771:
772: // Change the catalog record data into a single common form
773: catData.nodeType = 0; // mark this record as not in use
774: CopyCatalogNodeData(vcb, searchState->currentData, &catData);
775:
776: switch (searchState->currentData->recordType) {
777: case kHFSFolderThreadRecord:
778: return false; // Never match a thread record!
779: break;
780: case kHFSPlusFolderThreadRecord:
781: return false; // Never match a thread record!
782: break;
783:
784: case kHFSFolderRecord:
785: case kHFSFileRecord:
786: case kHFSPlusFolderRecord:
787: case kHFSPlusFileRecord:
788: break;
789:
790: default:
791: return false; // Not a file or folder record, so can't search it
792: }
793:
794: searchBits = searchState->searchBits;
795: matched = true; // Assume we got a match
796:
797: // See if this type of object (file vs. folder) is one of the types they're trying
798: // to match.
799: if (catData.nodeType == kCatalogFolderNode && (searchState->flags & kIncludeDirs) == 0 ) {
800: // Found a folder, but they don't want folders.
801: matched = false;
802: goto TestDone;
803: }
804: if (catData.nodeType == kCatalogFileNode && (searchState->flags & kIncludeFiles) == 0 ) {
805: // Found a file, but they don't want files.
806: matched = false;
807: goto TestDone;
808: }
809:
810: searchInfo1 = searchState->pb->ioSearchInfo1;
811: searchInfo2 = searchState->pb->ioSearchInfo2;
812:
813: //
814: // First, attempt to match the name -- either partial or complete.
815: // If the volume is HFS+, then we need to make a Str31 version of the name
816: // to use for comparison.
817: //
818: if (searchBits & (fsSBPartialName | fsSBFullName)) {
819: Str31 keyName;
820: ConstStr31Param namePtr;
821:
822: // Make a pointer to a PString version of the name
823: if (searchState->isHFS)
824: namePtr = &key->hfs.nodeName[0];
825: else {
826: TextEncoding encoding;
827:
828: encoding = searchState->currentData->hfsPlusFile.textEncoding; // same offset for folders
829:
830: (void) ConvertUnicodeToHFSName(&key->hfsPlus.nodeName, encoding, catData.nodeID, keyName);
831: namePtr = keyName;
832: }
833:
834: // Check for a partial name match
835: if (searchBits & fsSBPartialName) {
836: matched = ComparePartialName(namePtr, searchState->targetName);
837: // Exit if no match, or we no there's nothing more to compare
838: if (matched == false || searchBits == fsSBPartialName)
839: goto TestDone;
840: }
841:
842: // Check for a full name match
843: if (searchBits & fsSBFullName) {
844: matched = CompareFullName(namePtr, searchState->targetName);
845: // Exit if no match, or we no there's nothing more to compare
846: if (matched == false || searchBits == fsSBFullName)
847: goto TestDone;
848: }
849: }
850:
851: //
852: // parent ID
853: //
854: if (searchBits & fsSBFlParID) {
855: HFSCatalogNodeID parentID;
856:
857: // Note: ioFlParID and ioDrParID have the same offset in search info, so no
858: // need to test the object type here. However, we do need to test for HFS
859: // vs. HFSPlus, since their key structures are different.
860:
861: if (searchState->isHFS)
862: parentID = key->hfs.parentID;
863: else
864: parentID = key->hfsPlus.parentID;
865:
866: matched = CompareRange(parentID, searchInfo1->hFileInfo.ioFlParID,
867: searchInfo2->hFileInfo.ioFlParID);
868: if (matched == false) goto TestDone;
869: }
870:
871: //
872: // File attributes
873: //
874: if (searchBits & fsSBFlAttrib) {
875: UInt32 temp;
876:
877: temp = (catData.nodeFlags ^ searchInfo1->hFileInfo.ioFlAttrib) & searchState->attributes;
878: if (temp != 0) {
879: matched = false;
880: goto TestDone;
881: }
882: }
883:
884: //
885: // Number of files in a directory
886: //
887: if (searchBits & fsSBDrNmFls) {
888: matched = CompareRange(catData.valence, searchInfo1->dirInfo.ioDrNmFls,
889: searchInfo2->dirInfo.ioDrNmFls);
890: if (matched == false) goto TestDone;
891: }
892:
893: //
894: // Finder Info
895: //
896: if (searchBits & fsSBFlFndrInfo) {
897: UInt32 *thisValue;
898:
899: thisValue = (UInt32 *) &catData.finderInfo[0];
900:
901: // Note: ioFlFndrInfo and ioDrUsrWds have the same offset in search info, so
902: // no need to test the object type here.
903: matched = CompareMasked(thisValue, (UInt32 *) &searchInfo1->hFileInfo.ioFlFndrInfo,
904: (UInt32 *) &searchInfo2->hFileInfo.ioFlFndrInfo,
905: sizeof(FInfo)/sizeof(UInt32));
906:
907: if (matched == false) goto TestDone;
908: }
909:
910: //
911: // Extended Finder Info
912: //
913: if (searchBits & fsSBFlXFndrInfo) {
914: UInt32 *thisValue;
915:
916: thisValue = (UInt32 *) &catData.extFinderInfo[0];
917:
918: // Note: ioFlXFndrInfo and ioDrFndrInfo have the same offset in search info, so
919: // no need to test the object type here.
920: matched = CompareMasked(thisValue, (UInt32 *) &searchInfo1->hFileInfo.ioFlXFndrInfo,
921: (UInt32 *) &searchInfo2->hFileInfo.ioFlXFndrInfo,
922: sizeof(FXInfo)/sizeof(UInt32));
923:
924: if (matched == false) goto TestDone;
925: }
926:
927: //
928: // File logical length (data fork)
929: //
930: if (searchBits & fsSBFlLgLen) {
931: matched = CompareRange(catData.dataLogicalSize, searchInfo1->hFileInfo.ioFlLgLen,
932: searchInfo2->hFileInfo.ioFlLgLen);
933: if (matched == false) goto TestDone;
934: }
935:
936: //
937: // File physical length (data fork)
938: //
939: if (searchBits & fsSBFlPyLen) {
940: matched = CompareRange(catData.dataPhysicalSize, searchInfo1->hFileInfo.ioFlPyLen,
941: searchInfo2->hFileInfo.ioFlPyLen);
942: if (matched == false) goto TestDone;
943: }
944:
945: //
946: // File logical length (resource fork)
947: //
948: if (searchBits & fsSBFlRLgLen) {
949: matched = CompareRange(catData.rsrcLogicalSize, searchInfo1->hFileInfo.ioFlRLgLen,
950: searchInfo2->hFileInfo.ioFlRLgLen);
951: if (matched == false) goto TestDone;
952: }
953:
954: //
955: // File physical length (resource fork)
956: //
957: if (searchBits & fsSBFlRPyLen) {
958: matched = CompareRange(catData.rsrcPhysicalSize, searchInfo1->hFileInfo.ioFlRPyLen,
959: searchInfo2->hFileInfo.ioFlRPyLen);
960: if (matched == false) goto TestDone;
961: }
962:
963: //
964: // Create date
965: //
966: if (searchBits & fsSBFlCrDat) {
967: matched = CompareRange(catData.createDate, searchInfo1->hFileInfo.ioFlCrDat,
968: searchInfo2->hFileInfo.ioFlCrDat);
969: if (matched == false) goto TestDone;
970: }
971:
972: //
973: // Mod date
974: //
975: if (searchBits & fsSBFlMdDat) {
976: matched = CompareRange(catData.contentModDate, searchInfo1->hFileInfo.ioFlMdDat,
977: searchInfo2->hFileInfo.ioFlMdDat);
978: if (matched == false) goto TestDone;
979: }
980:
981: //
982: // Backup date
983: //
984: if (searchBits & fsSBFlBkDat) {
985: matched = CompareRange(catData.backupDate, searchInfo1->hFileInfo.ioFlBkDat,
986: searchInfo2->hFileInfo.ioFlBkDat);
987: }
988:
989: TestDone:
990: //
991: // Finally, determine whether we need to negate the sense of the match (i.e. find
992: // all objects that DON'T match).
993: //
994: if (searchBits & fsSBNegate) {
995: matched = !matched;
996: }
997:
998: return matched;
999: }
1000: #endif /* TARGET_OS_MAC */
1001:
1002:
1003:
1004: Boolean CompareMasked(const UInt32 *thisValue, const UInt32 *compareData,
1005: const UInt32 *compareMask, UInt32 count)
1006: {
1007: Boolean matched;
1008: UInt32 i;
1009:
1010: matched = true; // Assume it will all match
1011:
1012: for (i=0; i<count; i++) {
1013: if (((*thisValue++ ^ *compareData++) & *compareMask++) != 0) {
1014: matched = false;
1015: break;
1016: }
1017: }
1018:
1019: return matched;
1020: }
1021:
1022:
1023: #if TARGET_OS_MAC
1024: Boolean CompareFullName(ConstStr31Param thisName, ConstStr31Param testName)
1025: {
1026: Str31 tempName;
1027:
1028: //
1029: // Make a quick check first. If the lengths differ, they can't possibly match.
1030: //
1031: if (thisName[0] != testName[0])
1032: return false;
1033:
1034: //
1035: // Now make an upper case copy of thisName.
1036: //
1037: //�� Would it be better to skip making upper case versions of the names
1038: //�� and just call EqualString(thisName, testName, false, false)? I think it
1039: //�� should give the same result.
1040: //
1041: BlockMoveData(thisName, tempName, sizeof(Str31));
1042: UprText( (Ptr)(tempName + 1), tempName[0]);
1043:
1044: //
1045: // Compare tempName and testName; they must be identical PStrings to match.
1046: //
1047: return PascalBinaryCompare(tempName, testName);
1048: }
1049: #endif /* TARGET_OS_MAC */
1050:
1051:
1052:
1053: #if TARGET_OS_MAC
1054: Boolean ComparePartialName(ConstStr31Param thisName, ConstStr31Param substring)
1055: {
1056: Boolean matched;
1057: Str31 tempName;
1058:
1059: //
1060: // Make a quick check first. If the substring is longer than this object's name,
1061: // it can't possibly match.
1062: //
1063: if (substring[0] > thisName[0])
1064: return false;
1065:
1066: //
1067: // Now make an upper case copy of thisName.
1068: //
1069: //�� Would it be better to skip making upper case versions of the names
1070: //�� and just call EqualString(thisName, testName, false, false)? I think it
1071: //�� should give the same result.
1072: //
1073: BlockMoveData(thisName, tempName, sizeof(Str31));
1074: UprText( (Ptr)(tempName + 1), tempName[0]);
1075:
1076: //
1077: // Now see if substring is a substring of tempName.
1078: //
1079: matched = (PLstrstr(tempName, substring) != NULL);
1080:
1081: return matched;
1082: }
1083: #endif /* TARGET_OS_MAC */
1084:
1085:
1086:
1087: //�� Same function appears in Volumes.c as CheckVolumeOffLine(), but the code is slightly different
1088: //�� we should delete one copy.
1089: #if TARGET_OS_MAC
1090: OSErr ExtOffLineCheck( ExtendedVCB *vcb )
1091: {
1092: OSErr err;
1093:
1094: err = CheckExternalFileSystem(vcb);
1095: if (err == noErr) {
1096: if (vcb->vcbDrvNum != 0)
1097: LMSetReqstVol((Ptr) vcb);
1098: else
1099: err = volOffLinErr;
1100: }
1101:
1102: return err;
1103: }
1104: #endif
1105:
1106:
1107: void CatSearchTimerComplete(CatSearchState *state)
1108: {
1109: state->timerCompleted = true;
1110: }
1111:
1112: #if TARGET_OS_MAC
1113: static OSErr InitCatSearch( ExtendedVCB *volume, CSParam *pb, ScanState *scanState )
1114: {
1115: OSErr err;
1116: FCB *fcb;
1117: BTREE_CB_TYPE *btcb;
1118: CatPosition *startingPosition;
1119: void *buffer;
1120: UInt32 bufferSize;
1121: FSVarsRec *fsVars;
1122:
1123: //
1124: // Set up pointers to the catalog's FCB and BTCB
1125: //
1126: fcb = GetFileControlBlock(volume->catalogRefNum);
1127: btcb = (BTREE_CB_TYPE *) fcb->fcbBTCBPtr;
1128:
1129: //
1130: // Flush all files on this volume so the BTrees will be up-to-date (since we only
1131: // read the BTree on disk, not what might be in the control blocks).
1132: //
1133: LMSetFlushOnly(0xFF);
1134: err = FlushVolume(volume);
1135: if (err != noErr) goto ErrorExit;
1136:
1137: //
1138: // Make sure the volume control files (especially the Catalog B-tree) gets flushed,
1139: // too, or else we'll be reading stale catalog data.
1140: //
1141: err = FlushVolumeBuffers(volume);
1142: if (err != noErr) goto ErrorExit;
1143:
1144: //
1145: // Use our internal optimization buffer unless the caller supplied a suitable one.
1146: //
1147: fsVars = (FSVarsRec *) LMGetFSMVars();
1148: buffer = pb->ioOptBuffer;
1149: bufferSize = pb->ioOptBufSize;
1150: if (buffer == NULL || bufferSize < fsVars->gAttributesBufferSize) {
1151: buffer = fsVars->gAttributesBuffer;
1152: bufferSize = fsVars->gAttributesBufferSize;
1153: }
1154:
1155: //
1156: // Set up the scan state to start a new search, or resume a previous one.
1157: //
1158: startingPosition = (CatPosition *) &pb->ioCatPosition;
1159: if (startingPosition->writeCount == 0) {
1160: //
1161: // Starting a new search
1162: //
1163: err = BTScanInitialize(fcb, 0, 0, 0, buffer, bufferSize, scanState);
1164: }
1165: else {
1166: //
1167: // Resuming a previous search. Make sure the catalog hasn't changed
1168: // since the previous search.
1169: //
1170: if (startingPosition->writeCount != BTREE_WRITE_COUNT(btcb)) {
1171: startingPosition->writeCount = BTREE_WRITE_COUNT(btcb); // return the new write count
1172: err = catChangedErr;
1173: goto ErrorExit;
1174: }
1175:
1176: err = BTScanInitialize(fcb, startingPosition->nextNode, startingPosition->nextRecord, startingPosition->recordsFound,
1177: buffer, bufferSize, scanState);
1178: }
1179:
1180: ErrorExit:
1181: return err;
1182: }
1183: #endif /* TARGET_OS_MAC */
1184:
1185: #if TARGET_OS_MAC
1186: static void UpdateCatPosition( const ExtendedVCB *volume, CatPosition *endingPosition, const ScanState *scanState )
1187: {
1188: FCB *fcb;
1189: BTREE_CB_TYPE *btcb;
1190:
1191: //
1192: // Set up pointers to the catalog's FCB and BTCB
1193: //
1194: fcb = GetFileControlBlock(volume->catalogRefNum);
1195: btcb = (BTREE_CB_TYPE *) fcb->fcbBTCBPtr;
1196:
1197: //
1198: // Update the caller's CatPosition for the next time
1199: //
1200: endingPosition->writeCount = BTREE_WRITE_COUNT(btcb);
1201: BTScanTerminate(scanState, &endingPosition->nextNode, &endingPosition->nextRecord, &endingPosition->recordsFound);
1202: }
1203: #endif /* TARGET_OS_MAC */
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.