]> git.saurik.com Git - apple/xnu.git/blob - bsd/hfs/hfs_search.c
xnu-201.5.tar.gz
[apple/xnu.git] / bsd / hfs / hfs_search.c
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);
607 catalogInfo.nodeData.cnd_iNodeNumCopy = 0;
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