]>
Commit | Line | Data |
---|---|---|
1c79356b A |
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 | /* @(#)hfs_search.c | |
23 | * | |
24 | * (c) 1997-2000 Apple Computer, Inc. All Rights Reserved | |
25 | * | |
26 | * | |
27 | * MODIFICATION HISTORY: | |
28 | * 04-May-1999 Don Brady Split off from hfs_vnodeops.c. | |
29 | */ | |
30 | ||
31 | #include <sys/param.h> | |
32 | #include <sys/systm.h> | |
33 | #include <sys/kernel.h> | |
34 | #include <sys/file.h> | |
35 | #include <sys/buf.h> | |
36 | #include <sys/proc.h> | |
37 | #include <sys/conf.h> | |
38 | #include <mach/machine/vm_types.h> | |
39 | #include <sys/vnode.h> | |
40 | #include <sys/malloc.h> | |
41 | #include <sys/signalvar.h> | |
42 | #include <sys/attr.h> | |
43 | #include <sys/utfconv.h> | |
44 | ||
45 | #include "hfs.h" | |
46 | #include "hfs_dbg.h" | |
47 | #include "hfscommon/headers/FileMgrInternal.h" | |
48 | #include "hfscommon/headers/CatalogPrivate.h" | |
49 | #include "hfscommon/headers/HFSUnicodeWrappers.h" | |
50 | ||
51 | ||
52 | /* Private description used in hfs_search */ | |
53 | /* | |
54 | * ============ W A R N I N G ! ============ | |
55 | * DO NOT INCREASE THE SIZE OF THIS STRUCT! | |
56 | * It must match the size of the opaque | |
57 | * searchstate struct (in sys/attr.h). | |
58 | */ | |
59 | struct SearchState { | |
60 | long searchBits; | |
61 | BTreeIterator btreeIterator; | |
62 | }; | |
63 | typedef struct SearchState SearchState; | |
64 | ||
65 | ||
66 | static int UnpackSearchAttributeBlock(struct vnode *vp, struct attrlist *alist, searchinfospec_t *searchInfo, void *attributeBuffer); | |
67 | ||
68 | Boolean CheckCriteria(ExtendedVCB *vcb, const SearchState *searchState, | |
69 | u_long searchBits, struct attrlist *attrList, | |
70 | CatalogNodeData *cnp, CatalogKey *key, | |
71 | searchinfospec_t *searchInfo1, searchinfospec_t *searchInfo2); | |
72 | ||
73 | static int CheckAccess(CatalogNodeData *cnp, CatalogKey *key, struct proc *p); | |
74 | ||
75 | static int InsertMatch(struct vnode *vp, struct uio *a_uio, CatalogNodeData *cnp, | |
76 | CatalogKey *key, struct attrlist *returnAttrList, | |
77 | void *attributesBuffer, void *variableBuffer, | |
78 | u_long bufferSize, u_long * nummatches ); | |
79 | ||
80 | static Boolean CompareRange(u_long val, u_long low, u_long high); | |
81 | static Boolean CompareWideRange(u_int64_t val, u_int64_t low, u_int64_t high); | |
82 | ||
83 | static Boolean CompareRange( u_long val, u_long low, u_long high ) | |
84 | { | |
85 | return( (val >= low) && (val <= high) ); | |
86 | } | |
87 | ||
88 | static Boolean CompareWideRange( u_int64_t val, u_int64_t low, u_int64_t high ) | |
89 | { | |
90 | return( (val >= low) && (val <= high) ); | |
91 | } | |
92 | //#define CompareRange(val, low, high) ((val >= low) && (val <= high)) | |
93 | ||
94 | ||
95 | ||
96 | /************************************************************************/ | |
97 | /* Entry for searchfs() */ | |
98 | /************************************************************************/ | |
99 | ||
100 | #define errSearchBufferFull 101 /* Internal search errors */ | |
101 | /* | |
102 | # | |
103 | #% searchfs vp L L L | |
104 | # | |
105 | vop_searchfs { | |
106 | IN struct vnode *vp; | |
107 | IN off_t length; | |
108 | IN int flags; | |
109 | IN struct ucred *cred; | |
110 | IN struct proc *p; | |
111 | }; | |
112 | */ | |
113 | ||
114 | int | |
115 | hfs_search( ap ) | |
116 | struct vop_searchfs_args *ap; /* | |
117 | struct vnodeop_desc *a_desc; | |
118 | struct vnode *a_vp; | |
119 | void *a_searchparams1; | |
120 | void *a_searchparams2; | |
121 | struct attrlist *a_searchattrs; | |
122 | u_long a_maxmatches; | |
123 | struct timeval *a_timelimit; | |
124 | struct attrlist *a_returnattrs; | |
125 | u_long *a_nummatches; | |
126 | u_long a_scriptcode; | |
127 | u_long a_options; | |
128 | struct uio *a_uio; | |
129 | struct searchstate *a_searchstate; | |
130 | */ | |
131 | { | |
132 | CatalogNodeData cnode; | |
133 | BTreeKey *key; | |
134 | FSBufferDescriptor btRecord; | |
135 | FCB* catalogFCB; | |
136 | SearchState *searchState; | |
137 | searchinfospec_t searchInfo1; | |
138 | searchinfospec_t searchInfo2; | |
139 | void *attributesBuffer; | |
140 | void *variableBuffer; | |
141 | short recordSize; | |
142 | short operation; | |
143 | u_long fixedBlockSize; | |
144 | u_long eachReturnBufferSize; | |
145 | struct proc *p = current_proc(); | |
146 | u_long nodesToCheck = 30; /* After we search 30 nodes we must give up time */ | |
147 | u_long lastNodeNum = 0XFFFFFFFF; | |
148 | ExtendedVCB *vcb = VTOVCB(ap->a_vp); | |
149 | int err = E_NONE; | |
150 | int isHFSPlus; | |
151 | ||
152 | /* XXX Parameter check a_searchattrs? */ | |
153 | ||
154 | *(ap->a_nummatches) = 0; | |
155 | ||
156 | if ( ap->a_options & ~SRCHFS_VALIDOPTIONSMASK ) | |
157 | return( EINVAL ); | |
158 | ||
159 | if (ap->a_uio->uio_resid <= 0) | |
160 | return (EINVAL); | |
161 | ||
162 | isHFSPlus = (vcb->vcbSigWord == kHFSPlusSigWord); | |
163 | searchState = (SearchState *)ap->a_searchstate; | |
164 | ||
165 | /* | |
166 | * Check if this is the first time we are being called. | |
167 | * If it is, allocate SearchState and we'll move it to the users space on exit | |
168 | */ | |
169 | if ( ap->a_options & SRCHFS_START ) { | |
170 | bzero( (caddr_t)searchState, sizeof(SearchState) ); | |
171 | operation = kBTreeFirstRecord; | |
172 | ap->a_options &= ~SRCHFS_START; | |
173 | } else { | |
174 | operation = kBTreeCurrentRecord; | |
175 | } | |
176 | ||
177 | /* UnPack the search boundries, searchInfo1, searchInfo2 */ | |
178 | err = UnpackSearchAttributeBlock( ap->a_vp, ap->a_searchattrs, &searchInfo1, ap->a_searchparams1 ); | |
179 | if (err) return err; | |
180 | err = UnpackSearchAttributeBlock( ap->a_vp, ap->a_searchattrs, &searchInfo2, ap->a_searchparams2 ); | |
181 | if (err) return err; | |
182 | ||
183 | btRecord.itemCount = 1; | |
184 | if (isHFSPlus) { | |
185 | btRecord.itemSize = sizeof(cnode); | |
186 | btRecord.bufferAddress = &cnode; | |
187 | } else { | |
188 | btRecord.itemSize = sizeof(HFSCatalogFile); | |
189 | btRecord.bufferAddress = &cnode.cnd_extra; | |
190 | } | |
191 | catalogFCB = VTOFCB( vcb->catalogRefNum ); | |
192 | key = (BTreeKey*) &(searchState->btreeIterator.key); | |
193 | fixedBlockSize = sizeof(u_long) + AttributeBlockSize( ap->a_returnattrs ); /* u_long for length longword */ | |
194 | eachReturnBufferSize = fixedBlockSize; | |
195 | ||
196 | if ( ap->a_returnattrs->commonattr & ATTR_CMN_NAME ) /* XXX should be more robust! */ | |
197 | eachReturnBufferSize += kHFSPlusMaxFileNameBytes + 1; | |
198 | ||
199 | MALLOC( attributesBuffer, void *, eachReturnBufferSize, M_TEMP, M_WAITOK ); | |
200 | variableBuffer = (void*)((char*) attributesBuffer + fixedBlockSize); | |
201 | ||
202 | /* Lock catalog b-tree */ | |
203 | err = hfs_metafilelocking( VTOHFS(ap->a_vp), kHFSCatalogFileID, LK_SHARED, p ); | |
204 | if ( err != E_NONE ) { | |
205 | goto ExitThisRoutine; | |
206 | }; | |
207 | ||
208 | /* | |
209 | * Iterate over all the catalog btree records | |
210 | */ | |
211 | ||
212 | err = BTIterateRecord( catalogFCB, operation, &(searchState->btreeIterator), &btRecord, &recordSize ); | |
213 | ||
214 | while( err == E_NONE ) { | |
215 | if (!isHFSPlus) | |
216 | CopyCatalogNodeData(vcb, (CatalogRecord*)&cnode.cnd_extra, &cnode); | |
217 | ||
218 | if ( CheckCriteria( vcb, searchState, ap->a_options, ap->a_searchattrs, &cnode, | |
219 | (CatalogKey *)key, &searchInfo1, &searchInfo2 ) && | |
220 | CheckAccess(&cnode, (CatalogKey *)key, ap->a_uio->uio_procp)) { | |
221 | err = InsertMatch(ap->a_vp, ap->a_uio, &cnode, (CatalogKey *)key, | |
222 | ap->a_returnattrs, attributesBuffer, variableBuffer, | |
223 | eachReturnBufferSize, ap->a_nummatches); | |
224 | if ( err != E_NONE ) | |
225 | break; | |
226 | } | |
227 | ||
228 | err = BTIterateRecord( catalogFCB, kBTreeNextRecord, &(searchState->btreeIterator), &btRecord, &recordSize ); | |
229 | ||
230 | if ( *(ap->a_nummatches) >= ap->a_maxmatches ) | |
231 | break; | |
232 | ||
233 | if ( searchState->btreeIterator.hint.nodeNum != lastNodeNum ) { | |
234 | lastNodeNum = searchState->btreeIterator.hint.nodeNum; | |
235 | if ( --nodesToCheck == 0 ) | |
236 | break; /* We must leave the kernel to give up time */ | |
237 | } | |
238 | } | |
239 | ||
240 | /* Unlock catalog b-tree */ | |
241 | (void) hfs_metafilelocking( VTOHFS(ap->a_vp), kHFSCatalogFileID, LK_RELEASE, p ); | |
242 | ||
243 | ||
244 | if ( err == E_NONE ) { | |
245 | err = EAGAIN; /* signal to the user to call searchfs again */ | |
246 | } else if ( err == errSearchBufferFull ) { | |
247 | if ( *(ap->a_nummatches) > 0 ) | |
248 | err = EAGAIN; | |
249 | else | |
250 | err = ENOBUFS; | |
251 | } else if ( err == btNotFound ) { | |
252 | err = E_NONE; /* the entire disk has been searched */ | |
253 | } | |
254 | ||
255 | ExitThisRoutine: | |
256 | FREE( attributesBuffer, M_TEMP ); | |
257 | ||
258 | return( err ); | |
259 | } | |
260 | ||
261 | ||
262 | static Boolean | |
263 | CompareMasked(const UInt32 *thisValue, const UInt32 *compareData, | |
264 | const UInt32 *compareMask, UInt32 count) | |
265 | { | |
266 | Boolean matched; | |
267 | UInt32 i; | |
268 | ||
269 | matched = true; /* Assume it will all match */ | |
270 | ||
271 | for (i=0; i<count; i++) { | |
272 | if (((*thisValue++ ^ *compareData++) & *compareMask++) != 0) { | |
273 | matched = false; | |
274 | break; | |
275 | } | |
276 | } | |
277 | ||
278 | return matched; | |
279 | } | |
280 | ||
281 | ||
282 | static Boolean | |
283 | ComparePartialUnicodeName (register ConstUniCharArrayPtr str, register ItemCount s_len, | |
284 | register ConstUniCharArrayPtr find, register ItemCount f_len ) | |
285 | { | |
286 | if (f_len == 0 || s_len == 0) | |
287 | return FALSE; | |
288 | ||
289 | do { | |
290 | if (s_len-- < f_len) | |
291 | return FALSE; | |
292 | } while (FastUnicodeCompare(str++, f_len, find, f_len) != 0); | |
293 | ||
294 | return TRUE; | |
295 | } | |
296 | ||
297 | ||
298 | static Boolean | |
299 | ComparePartialPascalName ( register ConstStr31Param str, register ConstStr31Param find ) | |
300 | { | |
301 | register u_char s_len = str[0]; | |
302 | register u_char f_len = find[0]; | |
303 | register u_char *tsp; | |
304 | Str31 tmpstr; | |
305 | ||
306 | if (f_len == 0 || s_len == 0) | |
307 | return FALSE; | |
308 | ||
309 | bcopy(str, tmpstr, s_len + 1); | |
310 | tsp = &tmpstr[0]; | |
311 | ||
312 | while (s_len-- >= f_len) { | |
313 | *tsp = f_len; | |
314 | ||
315 | if (FastRelString(tsp++, find) == 0) | |
316 | return TRUE; | |
317 | } | |
318 | ||
319 | return FALSE; | |
320 | } | |
321 | ||
322 | ||
323 | /* | |
324 | * Check to see if caller has access rights to this item | |
325 | */ | |
326 | static int | |
327 | CheckAccess(CatalogNodeData *cnp, CatalogKey *key, struct proc *p) | |
328 | { | |
329 | return (1); | |
330 | } | |
331 | ||
332 | Boolean | |
333 | CheckCriteria( ExtendedVCB *vcb, const SearchState *searchState, u_long searchBits, | |
334 | struct attrlist *attrList, CatalogNodeData *cnp, CatalogKey *key, | |
335 | searchinfospec_t *searchInfo1, searchinfospec_t *searchInfo2 ) | |
336 | { | |
337 | Boolean matched, atleastone; | |
338 | Boolean isHFSPlus; | |
339 | attrgroup_t searchAttributes; | |
340 | ||
341 | isHFSPlus = (vcb->vcbSigWord == kHFSPlusSigWord); | |
342 | ||
343 | switch (cnp->cnd_type) { | |
344 | case kCatalogFolderNode: | |
345 | if ( (searchBits & SRCHFS_MATCHDIRS) == 0 ) { /* If we are NOT searching folders */ | |
346 | matched = false; | |
347 | goto TestDone; | |
348 | } | |
349 | break; | |
350 | ||
351 | case kCatalogFileNode: | |
352 | if ( (searchBits & SRCHFS_MATCHFILES) == 0 ) { /* If we are NOT searching files */ | |
353 | matched = false; | |
354 | goto TestDone; | |
355 | } | |
356 | break; | |
357 | ||
358 | default: /* Never match a thread record or any other type. */ | |
359 | return( false ); /* Not a file or folder record, so can't search it */ | |
360 | } | |
361 | ||
362 | matched = true; /* Assume we got a match */ | |
363 | atleastone = false; /* Dont insert unless we match at least one criteria */ | |
364 | ||
365 | /* First, attempt to match the name -- either partial or complete */ | |
366 | if ( attrList->commonattr & ATTR_CMN_NAME ) { | |
367 | if (isHFSPlus) { | |
368 | /* Check for partial/full HFS Plus name match */ | |
369 | ||
370 | if ( searchBits & SRCHFS_MATCHPARTIALNAMES ) { | |
371 | matched = ComparePartialUnicodeName(key->hfsPlus.nodeName.unicode, | |
372 | key->hfsPlus.nodeName.length, | |
373 | (UniChar*)searchInfo1->name, | |
374 | searchInfo1->nameLength ); | |
375 | } else /* full HFS Plus name match */ { | |
376 | matched = (FastUnicodeCompare(key->hfsPlus.nodeName.unicode, | |
377 | key->hfsPlus.nodeName.length, | |
378 | (UniChar*)searchInfo1->name, | |
379 | searchInfo1->nameLength ) == 0); | |
380 | } | |
381 | } else { | |
382 | /* Check for partial/full HFS name match */ | |
383 | ||
384 | if ( searchBits & SRCHFS_MATCHPARTIALNAMES ) | |
385 | matched = ComparePartialPascalName(key->hfs.nodeName, (u_char*)searchInfo1->name); | |
386 | else /* full HFS name match */ | |
387 | matched = (FastRelString(key->hfs.nodeName, (u_char*)searchInfo1->name) == 0); | |
388 | } | |
389 | ||
390 | if ( matched == false || (searchBits & ~SRCHFS_MATCHPARTIALNAMES) == 0 ) | |
391 | goto TestDone; /* no match, or nothing more to compare */ | |
392 | ||
393 | atleastone = true; | |
394 | } | |
395 | ||
396 | /* Now that we have a record worth searching, see if it matches the search attributes */ | |
397 | if (cnp->cnd_type == kCatalogFileNode) { | |
398 | if ((attrList->dirattr & ~ATTR_FILE_VALIDMASK) != 0) { /* attr we do know about */ | |
399 | matched = false; | |
400 | goto TestDone; | |
401 | } | |
402 | else if ((attrList->dirattr & ATTR_FILE_VALIDMASK) != 0) { | |
403 | searchAttributes = attrList->fileattr; | |
404 | ||
405 | /* File logical length (data fork) */ | |
406 | if ( searchAttributes & ATTR_FILE_DATALENGTH ) { | |
407 | matched = CompareWideRange( | |
408 | cnp->cnd_datafork.logicalSize, | |
409 | searchInfo1->f.dataLogicalLength, | |
410 | searchInfo2->f.dataLogicalLength); | |
411 | if (matched == false) goto TestDone; | |
412 | atleastone = true; | |
413 | } | |
414 | ||
415 | /* File physical length (data fork) */ | |
416 | if ( searchAttributes & ATTR_FILE_DATAALLOCSIZE ) { | |
417 | matched = CompareWideRange( | |
418 | cnp->cnd_datafork.totalBlocks * vcb->blockSize, | |
419 | searchInfo1->f.dataPhysicalLength, | |
420 | searchInfo2->f.dataPhysicalLength); | |
421 | if (matched == false) goto TestDone; | |
422 | atleastone = true; | |
423 | } | |
424 | ||
425 | /* File logical length (resource fork) */ | |
426 | if ( searchAttributes & ATTR_FILE_RSRCLENGTH ) { | |
427 | matched = CompareWideRange( | |
428 | cnp->cnd_rsrcfork.logicalSize, | |
429 | searchInfo1->f.resourceLogicalLength, | |
430 | searchInfo2->f.resourceLogicalLength); | |
431 | if (matched == false) goto TestDone; | |
432 | atleastone = true; | |
433 | } | |
434 | ||
435 | /* File physical length (resource fork) */ | |
436 | if ( searchAttributes & ATTR_FILE_RSRCALLOCSIZE ) { | |
437 | matched = CompareWideRange( | |
438 | cnp->cnd_rsrcfork.totalBlocks * vcb->blockSize, | |
439 | searchInfo1->f.resourcePhysicalLength, | |
440 | searchInfo2->f.resourcePhysicalLength); | |
441 | if (matched == false) goto TestDone; | |
442 | atleastone = true; | |
443 | } | |
444 | } | |
445 | else { | |
446 | atleastone = true; /* to match SRCHFS_MATCHDIRS */ | |
447 | } | |
448 | } | |
449 | /* | |
450 | * Check the directory attributes | |
451 | */ | |
452 | else if (cnp->cnd_type == kCatalogFolderNode) { | |
453 | if ((attrList->dirattr & ~ATTR_DIR_VALIDMASK) != 0) { /* attr we do know about */ | |
454 | matched = false; | |
455 | goto TestDone; | |
456 | } | |
457 | else if ((attrList->dirattr & ATTR_DIR_VALIDMASK) != 0) { | |
458 | searchAttributes = attrList->dirattr; | |
459 | ||
460 | /* Directory valence */ | |
461 | if ( searchAttributes & ATTR_DIR_ENTRYCOUNT ) { | |
462 | matched = CompareRange(cnp->cnd_valence, searchInfo1->d.numFiles, searchInfo2->d.numFiles ); | |
463 | if (matched == false) goto TestDone; | |
464 | atleastone = true; | |
465 | } | |
466 | } | |
467 | else { | |
468 | atleastone = true; /* to match SRCHFS_MATCHDIRS */ | |
469 | } | |
470 | } | |
471 | ||
472 | /* | |
473 | * Check the common attributes | |
474 | */ | |
475 | searchAttributes = attrList->commonattr; | |
476 | if ( (searchAttributes & ATTR_CMN_VALIDMASK) != 0 ) { | |
477 | ||
478 | /* node ID */ | |
479 | if ( searchAttributes & ATTR_CMN_OBJID ) { | |
480 | matched = CompareRange( cnp->cnd_nodeID, searchInfo1->nodeID, searchInfo2->nodeID ); | |
481 | if (matched == false) goto TestDone; | |
482 | atleastone = true; | |
483 | } | |
484 | ||
485 | /* Parent ID */ | |
486 | if ( searchAttributes & ATTR_CMN_PAROBJID ) { | |
487 | HFSCatalogNodeID parentID; | |
488 | ||
489 | if (isHFSPlus) | |
490 | parentID = key->hfsPlus.parentID; | |
491 | else | |
492 | parentID = key->hfs.parentID; | |
493 | ||
494 | matched = CompareRange( parentID, searchInfo1->parentDirID, searchInfo2->parentDirID ); | |
495 | if (matched == false) goto TestDone; | |
496 | atleastone = true; | |
497 | } | |
498 | ||
499 | /* Finder Info & Extended Finder Info where extFinderInfo is last 32 bytes */ | |
500 | if ( searchAttributes & ATTR_CMN_FNDRINFO ) { | |
501 | UInt32 *thisValue; | |
502 | thisValue = (UInt32 *) &cnp->cnd_finderInfo; | |
503 | ||
504 | /* | |
505 | * Note: ioFlFndrInfo and ioDrUsrWds have the same offset in search info, so | |
506 | * no need to test the object type here. | |
507 | */ | |
508 | matched = CompareMasked( thisValue, (UInt32 *) &searchInfo1->finderInfo, | |
509 | (UInt32 *) &searchInfo2->finderInfo, 8 ); /* 8 * UInt32 */ | |
510 | if (matched == false) goto TestDone; | |
511 | atleastone = true; | |
512 | } | |
513 | ||
514 | /* Create date */ | |
515 | if ( searchAttributes & ATTR_CMN_CRTIME ) { | |
516 | matched = CompareRange(to_bsd_time(cnp->cnd_createDate), | |
517 | searchInfo1->creationDate.tv_sec, searchInfo2->creationDate.tv_sec ); | |
518 | if (matched == false) goto TestDone; | |
519 | atleastone = true; | |
520 | } | |
521 | ||
522 | /* Mod date */ | |
523 | if ( searchAttributes & ATTR_CMN_MODTIME ) { | |
524 | matched = CompareRange(to_bsd_time(cnp->cnd_contentModDate), | |
525 | searchInfo1->modificationDate.tv_sec, searchInfo2->modificationDate.tv_sec ); | |
526 | if (matched == false) goto TestDone; | |
527 | atleastone = true; | |
528 | } | |
529 | ||
530 | /* Change Time */ | |
531 | if ( searchAttributes & ATTR_CMN_CHGTIME ) { | |
532 | matched = CompareRange(to_bsd_time(cnp->cnd_attributeModDate), | |
533 | searchInfo1->changeDate.tv_sec, searchInfo2->changeDate.tv_sec ); | |
534 | if (matched == false) goto TestDone; | |
535 | atleastone = true; | |
536 | } | |
537 | ||
538 | /* Backup date */ | |
539 | if ( searchAttributes & ATTR_CMN_BKUPTIME ) { | |
540 | matched = CompareRange(to_bsd_time(cnp->cnd_backupDate), | |
541 | searchInfo1->lastBackupDate.tv_sec, searchInfo2->lastBackupDate.tv_sec ); | |
542 | if (matched == false) goto TestDone; | |
543 | atleastone = true; | |
544 | } | |
545 | ||
546 | /* User ID */ | |
547 | if ( searchAttributes & ATTR_CMN_OWNERID ) { | |
548 | matched = CompareRange( cnp->cnd_ownerID, searchInfo1->uid, searchInfo2->uid ); | |
549 | if (matched == false) goto TestDone; | |
550 | atleastone = true; | |
551 | } | |
552 | ||
553 | /* Group ID */ | |
554 | if ( searchAttributes & ATTR_CMN_GRPID ) { | |
555 | matched = CompareRange( cnp->cnd_groupID, searchInfo1->gid, searchInfo2->gid ); | |
556 | if (matched == false) goto TestDone; | |
557 | atleastone = true; | |
558 | } | |
559 | ||
560 | /* mode */ | |
561 | if ( searchAttributes & ATTR_CMN_ACCESSMASK ) { | |
562 | matched = CompareRange( (u_long)cnp->cnd_mode, | |
563 | (u_long)searchInfo1->mask, (u_long)searchInfo2->mask ); | |
564 | if (matched == false) goto TestDone; | |
565 | atleastone = true; | |
566 | } | |
567 | ||
568 | } | |
569 | ||
570 | /* If we got here w/o matching any, then set to false */ | |
571 | if (! atleastone) | |
572 | matched = false; | |
573 | ||
574 | TestDone: | |
575 | /* | |
576 | * Finally, determine whether we need to negate the sense of the match | |
577 | * (i.e. find all objects that DON'T match). | |
578 | */ | |
579 | if ( searchBits & SRCHFS_NEGATEPARAMS ) | |
580 | matched = !matched; | |
581 | ||
582 | return( matched ); | |
583 | } | |
584 | ||
585 | ||
586 | /* | |
587 | * Adds another record to the packed array for output | |
588 | */ | |
589 | static int | |
590 | InsertMatch( struct vnode *root_vp, struct uio *a_uio, CatalogNodeData *cnp, | |
591 | CatalogKey *key, struct attrlist *returnAttrList, void *attributesBuffer, | |
592 | void *variableBuffer, u_long bufferSize, u_long * nummatches ) | |
593 | { | |
594 | int err; | |
595 | void *rovingAttributesBuffer; | |
596 | void *rovingVariableBuffer; | |
597 | struct hfsCatalogInfo catalogInfo; | |
598 | u_long packedBufferSize; | |
599 | ExtendedVCB *vcb = VTOVCB(root_vp); | |
600 | Boolean isHFSPlus = vcb->vcbSigWord == kHFSPlusSigWord; | |
601 | u_long privateDir = VTOHFS(root_vp)->hfs_private_metadata_dir; | |
602 | ||
603 | rovingAttributesBuffer = (char*)attributesBuffer + sizeof(u_long); /* Reserve space for length field */ | |
604 | rovingVariableBuffer = variableBuffer; | |
605 | ||
606 | INIT_CATALOGDATA(&catalogInfo.nodeData, 0); | |
765c9de3 | 607 | catalogInfo.nodeData.cnd_iNodeNumCopy = 0; |
1c79356b A |
608 | |
609 | /* The packing call below expects a struct hfsCatalogInfo */ | |
610 | bcopy(cnp, &catalogInfo.nodeData, (cnp->cnd_type == kCatalogFileNode) ? | |
611 | sizeof(HFSPlusCatalogFile) : sizeof(HFSPlusCatalogFolder)); | |
612 | ||
613 | catalogInfo.nodeData.cnm_parID = isHFSPlus ? key->hfsPlus.parentID : key->hfs.parentID; | |
614 | ||
615 | /* hide open files that have been deleted */ | |
616 | if ((privateDir != 0) && (catalogInfo.nodeData.cnm_parID == privateDir)) | |
617 | return (0); | |
618 | ||
619 | /* hide our private meta data directory */ | |
620 | if ((privateDir != 0) && (catalogInfo.nodeData.cnd_nodeID == privateDir)) | |
621 | return (0); | |
622 | ||
623 | if ( returnAttrList->commonattr & ATTR_CMN_NAME ) { | |
624 | size_t utf8len = 0; | |
625 | ||
626 | catalogInfo.nodeData.cnm_nameptr = catalogInfo.nodeData.cnm_namespace; | |
627 | ||
628 | /* Return result in UTF-8 */ | |
629 | if ( isHFSPlus ) { | |
630 | err = utf8_encodestr(key->hfsPlus.nodeName.unicode, | |
631 | key->hfsPlus.nodeName.length * sizeof(UniChar), | |
632 | catalogInfo.nodeData.cnm_namespace, | |
633 | &utf8len, | |
634 | MAXHFSVNODELEN + 1, ':', 0); | |
635 | if (err == ENAMETOOLONG) { | |
636 | utf8len = utf8_encodelen(key->hfsPlus.nodeName.unicode, | |
637 | key->hfsPlus.nodeName.length * sizeof(UniChar), ':', 0); | |
638 | MALLOC(catalogInfo.nodeData.cnm_nameptr, char *, utf8len+1, M_TEMP, M_WAITOK); | |
639 | catalogInfo.nodeData.cnm_flags |= kCatNameIsAllocated; | |
640 | err = utf8_encodestr(key->hfsPlus.nodeName.unicode, | |
641 | key->hfsPlus.nodeName.length * sizeof(UniChar), | |
642 | catalogInfo.nodeData.cnm_nameptr, | |
643 | &utf8len, | |
644 | utf8len + 1, ':', 0); | |
645 | } | |
646 | } else { | |
647 | err = hfs_to_utf8(vcb, | |
648 | key->hfs.nodeName, | |
649 | MAXHFSVNODELEN + 1, | |
650 | (ByteCount*) &utf8len, | |
651 | catalogInfo.nodeData.cnm_namespace); | |
652 | if (err == ENAMETOOLONG) { | |
653 | MALLOC(catalogInfo.nodeData.cnm_nameptr, char *, utf8len+1, M_TEMP, M_WAITOK); | |
654 | catalogInfo.nodeData.cnm_flags |= kCatNameIsAllocated; | |
655 | err = hfs_to_utf8(vcb, | |
656 | key->hfs.nodeName, | |
657 | utf8len + 1, | |
658 | (ByteCount*) &utf8len, | |
659 | catalogInfo.nodeData.cnm_nameptr); | |
660 | } else if (err) { | |
661 | /* | |
662 | * When an HFS name cannot be encoded with the current | |
663 | * volume encoding we use MacRoman as a fallback. | |
664 | */ | |
665 | err = mac_roman_to_utf8(key->hfs.nodeName, MAXHFSVNODELEN + 1, | |
666 | (ByteCount*) &utf8len, | |
667 | catalogInfo.nodeData.cnm_namespace); | |
668 | } | |
669 | } | |
670 | catalogInfo.nodeData.cnm_length = utf8len; | |
671 | if (err && (catalogInfo.nodeData.cnm_flags & kCatNameIsAllocated)) | |
672 | { | |
673 | DisposePtr(catalogInfo.nodeData.cnm_nameptr); | |
674 | catalogInfo.nodeData.cnm_flags &= ~kCatNameIsAllocated; | |
675 | catalogInfo.nodeData.cnm_nameptr = catalogInfo.nodeData.cnm_namespace; | |
676 | catalogInfo.nodeData.cnm_namespace[0] = 0; | |
677 | } | |
678 | } | |
679 | ||
680 | PackCatalogInfoAttributeBlock( returnAttrList,root_vp, &catalogInfo, &rovingAttributesBuffer, &rovingVariableBuffer ); | |
681 | ||
682 | CLEAN_CATALOGDATA(&catalogInfo.nodeData); | |
683 | ||
684 | packedBufferSize = (char*)rovingVariableBuffer - (char*)attributesBuffer; | |
685 | ||
686 | if ( packedBufferSize > a_uio->uio_resid ) | |
687 | return( errSearchBufferFull ); | |
688 | ||
689 | (* nummatches)++; | |
690 | ||
691 | *((u_long *)attributesBuffer) = packedBufferSize; /* Store length of fixed + var block */ | |
692 | ||
693 | err = uiomove( (caddr_t)attributesBuffer, packedBufferSize, a_uio ); /* XXX should be packedBufferSize */ | |
694 | ||
695 | return( err ); | |
696 | } | |
697 | ||
698 | ||
699 | static int | |
700 | UnpackSearchAttributeBlock( struct vnode *vp, struct attrlist *alist, searchinfospec_t *searchInfo, void *attributeBuffer ) | |
701 | { | |
702 | attrgroup_t a; | |
703 | u_long bufferSize; | |
704 | ||
705 | DBG_ASSERT(searchInfo != NULL); | |
706 | ||
707 | bufferSize = *((u_long *)attributeBuffer); | |
708 | if (bufferSize == 0) | |
709 | return (EINVAL); /* XXX -DJB is a buffer size of zero ever valid for searchfs? */ | |
710 | ||
711 | ++((u_long *)attributeBuffer); /* advance past the size */ | |
712 | ||
713 | /* | |
714 | * UnPack common attributes | |
715 | */ | |
716 | a = alist->commonattr; | |
717 | if ( a != 0 ) { | |
718 | if ( a & ATTR_CMN_NAME ) { | |
719 | char *s = (char*) attributeBuffer + ((attrreference_t *) attributeBuffer)->attr_dataoffset; | |
720 | size_t len = ((attrreference_t *) attributeBuffer)->attr_length; | |
721 | ||
722 | if (len > sizeof(searchInfo->name)) | |
723 | return (EINVAL); | |
724 | ||
725 | if (VTOVCB(vp)->vcbSigWord == kHFSPlusSigWord) { | |
726 | size_t ucslen; | |
727 | /* Convert name to Unicode to match HFS Plus B-Tree names */ | |
728 | ||
729 | if (len > 0) { | |
730 | if (utf8_decodestr(s, len-1, (UniChar*)searchInfo->name, &ucslen, | |
731 | sizeof(searchInfo->name), ':', UTF_DECOMPOSED)) | |
732 | return (EINVAL); | |
733 | ||
734 | searchInfo->nameLength = ucslen / sizeof(UniChar); | |
735 | } else { | |
736 | searchInfo->nameLength = 0; | |
737 | } | |
738 | ++((attrreference_t *)attributeBuffer); | |
739 | ||
740 | } else { | |
741 | /* Convert name to pascal string to match HFS B-Tree names */ | |
742 | ||
743 | if (len > 0) { | |
744 | if (utf8_to_hfs(VTOVCB(vp), len-1, s, (u_char*)searchInfo->name) != 0) | |
745 | return (EINVAL); | |
746 | ||
747 | searchInfo->nameLength = searchInfo->name[0]; | |
748 | } else { | |
749 | searchInfo->name[0] = searchInfo->nameLength = 0; | |
750 | } | |
751 | ++((attrreference_t *)attributeBuffer); | |
752 | } | |
753 | } | |
754 | if ( a & ATTR_CMN_OBJID ) { | |
755 | searchInfo->nodeID = ((fsobj_id_t *) attributeBuffer)->fid_objno; /* ignore fid_generation */ | |
756 | ++((fsobj_id_t *)attributeBuffer); | |
757 | } | |
758 | if ( a & ATTR_CMN_PAROBJID ) { | |
759 | searchInfo->parentDirID = ((fsobj_id_t *) attributeBuffer)->fid_objno; /* ignore fid_generation */ | |
760 | ++((fsobj_id_t *)attributeBuffer); | |
761 | } | |
762 | if ( a & ATTR_CMN_CRTIME ) { | |
763 | searchInfo->creationDate = *((struct timespec *)attributeBuffer); | |
764 | ++((struct timespec *)attributeBuffer); | |
765 | } | |
766 | if ( a & ATTR_CMN_MODTIME ) { | |
767 | searchInfo->modificationDate = *((struct timespec *)attributeBuffer); | |
768 | ++((struct timespec *)attributeBuffer); | |
769 | } | |
770 | if ( a & ATTR_CMN_CHGTIME ) { | |
771 | searchInfo->changeDate = *((struct timespec *)attributeBuffer); | |
772 | ++((struct timespec *)attributeBuffer); | |
773 | } | |
774 | if ( a & ATTR_CMN_BKUPTIME ) { | |
775 | searchInfo->lastBackupDate = *((struct timespec *)attributeBuffer); | |
776 | ++((struct timespec *)attributeBuffer); | |
777 | } | |
778 | if ( a & ATTR_CMN_FNDRINFO ) { | |
779 | bcopy( attributeBuffer, searchInfo->finderInfo, sizeof(u_long) * 8 ); | |
780 | (u_long *)attributeBuffer += 8; | |
781 | } | |
782 | if ( a & ATTR_CMN_BKUPTIME ) { | |
783 | searchInfo->lastBackupDate = *((struct timespec *)attributeBuffer); | |
784 | ++((struct timespec *)attributeBuffer); | |
785 | } | |
786 | if ( a & ATTR_CMN_OWNERID ) { | |
787 | searchInfo->uid = *((uid_t *)attributeBuffer); | |
788 | ++((uid_t *)attributeBuffer); | |
789 | } | |
790 | if ( a & ATTR_CMN_GRPID ) { | |
791 | searchInfo->gid = *((gid_t *)attributeBuffer); | |
792 | ++((gid_t *)attributeBuffer); | |
793 | } | |
794 | if ( a & ATTR_CMN_ACCESSMASK ) { | |
795 | searchInfo->mask = *((mode_t *)attributeBuffer); | |
796 | ++((mode_t *)attributeBuffer); | |
797 | } | |
798 | } | |
799 | ||
800 | a = alist->dirattr; | |
801 | if ( a != 0 ) { | |
802 | if ( a & ATTR_DIR_ENTRYCOUNT ) { | |
803 | searchInfo->d.numFiles = *((u_long *)attributeBuffer); | |
804 | ++((u_long *)attributeBuffer); | |
805 | } | |
806 | } | |
807 | ||
808 | a = alist->fileattr; | |
809 | if ( a != 0 ) { | |
810 | if ( a & ATTR_FILE_DATALENGTH ) { | |
811 | searchInfo->f.dataLogicalLength = *((off_t *)attributeBuffer); | |
812 | ++((off_t *)attributeBuffer); | |
813 | } | |
814 | if ( a & ATTR_FILE_DATAALLOCSIZE ) { | |
815 | searchInfo->f.dataPhysicalLength = *((off_t *)attributeBuffer); | |
816 | ++((off_t *)attributeBuffer); | |
817 | } | |
818 | if ( a & ATTR_FILE_RSRCLENGTH ) { | |
819 | searchInfo->f.resourceLogicalLength = *((off_t *)attributeBuffer); | |
820 | ++((off_t *)attributeBuffer); | |
821 | } | |
822 | if ( a & ATTR_FILE_RSRCALLOCSIZE ) { | |
823 | searchInfo->f.resourcePhysicalLength = *((off_t *)attributeBuffer); | |
824 | ++((off_t *)attributeBuffer); | |
825 | } | |
826 | } | |
827 | ||
828 | return (0); | |
829 | } | |
830 | ||
831 |