]>
Commit | Line | Data |
---|---|---|
1c79356b | 1 | /* |
2d21ac55 | 2 | * Copyright (c) 1997-2007 Apple Inc. All rights reserved. |
1c79356b | 3 | * |
2d21ac55 | 4 | * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ |
1c79356b | 5 | * |
2d21ac55 A |
6 | * This file contains Original Code and/or Modifications of Original Code |
7 | * as defined in and that are subject to the Apple Public Source License | |
8 | * Version 2.0 (the 'License'). You may not use this file except in | |
9 | * compliance with the License. The rights granted to you under the License | |
10 | * may not be used to create, or enable the creation or redistribution of, | |
11 | * unlawful or unlicensed copies of an Apple operating system, or to | |
12 | * circumvent, violate, or enable the circumvention or violation of, any | |
13 | * terms of an Apple operating system software license agreement. | |
8f6c56a5 | 14 | * |
2d21ac55 A |
15 | * Please obtain a copy of the License at |
16 | * http://www.opensource.apple.com/apsl/ and read it before using this file. | |
17 | * | |
18 | * The Original Code and all software distributed under the License are | |
19 | * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER | |
8f6c56a5 A |
20 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, |
21 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, | |
2d21ac55 A |
22 | * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. |
23 | * Please see the License for the specific language governing rights and | |
24 | * limitations under the License. | |
8f6c56a5 | 25 | * |
2d21ac55 | 26 | * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ |
1c79356b | 27 | * |
9bccf70c | 28 | * @(#)hfs_search.c |
1c79356b | 29 | */ |
2d21ac55 A |
30 | /* |
31 | * NOTICE: This file was modified by SPARTA, Inc. in 2005 to introduce | |
32 | * support for mandatory and extensible security protections. This notice | |
33 | * is included in support of clause 2.2 (b) of the Apple Public License, | |
34 | * Version 2.0. | |
35 | */ | |
1c79356b A |
36 | |
37 | #include <sys/param.h> | |
38 | #include <sys/systm.h> | |
39 | #include <sys/kernel.h> | |
40 | #include <sys/file.h> | |
1c79356b A |
41 | #include <sys/proc.h> |
42 | #include <sys/conf.h> | |
43 | #include <mach/machine/vm_types.h> | |
44 | #include <sys/vnode.h> | |
45 | #include <sys/malloc.h> | |
46 | #include <sys/signalvar.h> | |
47 | #include <sys/attr.h> | |
48 | #include <sys/utfconv.h> | |
91447636 A |
49 | #include <sys/kauth.h> |
50 | #include <sys/vnode_internal.h> | |
1c79356b | 51 | |
2d21ac55 A |
52 | #if CONFIG_MACF |
53 | #include <security/mac_framework.h> | |
54 | #endif | |
55 | ||
1c79356b A |
56 | #include "hfs.h" |
57 | #include "hfs_dbg.h" | |
9bccf70c A |
58 | #include "hfs_catalog.h" |
59 | #include "hfs_attrlist.h" | |
60 | #include "hfs_endian.h" | |
61 | ||
1c79356b | 62 | #include "hfscommon/headers/FileMgrInternal.h" |
1c79356b | 63 | #include "hfscommon/headers/HFSUnicodeWrappers.h" |
14353aa8 A |
64 | #include "hfscommon/headers/BTreesPrivate.h" |
65 | #include "hfscommon/headers/BTreeScanner.h" | |
91447636 | 66 | #include "hfscommon/headers/CatalogPrivate.h" |
1c79356b | 67 | |
9bccf70c A |
68 | /* Search criterea. */ |
69 | struct directoryInfoSpec | |
70 | { | |
91447636 | 71 | u_int32_t numFiles; |
9bccf70c A |
72 | }; |
73 | ||
74 | struct fileInfoSpec | |
75 | { | |
76 | off_t dataLogicalLength; | |
77 | off_t dataPhysicalLength; | |
78 | off_t resourceLogicalLength; | |
79 | off_t resourcePhysicalLength; | |
80 | }; | |
81 | ||
82 | struct searchinfospec | |
83 | { | |
84 | u_char name[kHFSPlusMaxFileNameBytes]; | |
85 | u_long nameLength; | |
86 | char attributes; // see IM:Files 2-100 | |
87 | u_long nodeID; | |
88 | u_long parentDirID; | |
89 | struct timespec creationDate; | |
90 | struct timespec modificationDate; | |
91 | struct timespec changeDate; | |
92 | struct timespec accessDate; | |
93 | struct timespec lastBackupDate; | |
2d21ac55 | 94 | u_int8_t finderInfo[32]; |
9bccf70c A |
95 | uid_t uid; |
96 | gid_t gid; | |
97 | mode_t mask; | |
98 | struct fileInfoSpec f; | |
99 | struct directoryInfoSpec d; | |
100 | }; | |
101 | typedef struct searchinfospec searchinfospec_t; | |
1c79356b | 102 | |
2d21ac55 | 103 | static void ResolveHardlink(struct hfsmount *hfsmp, HFSPlusCatalogFile *recp); |
1c79356b | 104 | |
1c79356b | 105 | |
91447636 | 106 | static int UnpackSearchAttributeBlock(struct hfsmount *hfsmp, struct attrlist *alist, |
9bccf70c A |
107 | searchinfospec_t *searchInfo, void *attributeBuffer); |
108 | ||
109 | static int CheckCriteria( ExtendedVCB *vcb, | |
110 | u_long searchBits, | |
111 | struct attrlist *attrList, | |
112 | CatalogRecord *rec, | |
113 | CatalogKey *key, | |
114 | searchinfospec_t *searchInfo1, | |
2d21ac55 | 115 | searchinfospec_t *searchInfo2 ); |
9bccf70c | 116 | |
13fec989 | 117 | static int CheckAccess(ExtendedVCB *vcb, u_long searchBits, CatalogKey *key, struct vfs_context *ctx); |
9bccf70c | 118 | |
91447636 | 119 | static int InsertMatch(struct hfsmount *hfsmp, uio_t a_uio, CatalogRecord *rec, |
1c79356b A |
120 | CatalogKey *key, struct attrlist *returnAttrList, |
121 | void *attributesBuffer, void *variableBuffer, | |
91447636 | 122 | u_long * nummatches ); |
1c79356b A |
123 | |
124 | static Boolean CompareRange(u_long val, u_long low, u_long high); | |
125 | static Boolean CompareWideRange(u_int64_t val, u_int64_t low, u_int64_t high); | |
126 | ||
127 | static Boolean CompareRange( u_long val, u_long low, u_long high ) | |
128 | { | |
129 | return( (val >= low) && (val <= high) ); | |
130 | } | |
131 | ||
132 | static Boolean CompareWideRange( u_int64_t val, u_int64_t low, u_int64_t high ) | |
133 | { | |
134 | return( (val >= low) && (val <= high) ); | |
135 | } | |
136 | //#define CompareRange(val, low, high) ((val >= low) && (val <= high)) | |
9bccf70c | 137 | |
1c79356b A |
138 | |
139 | /************************************************************************/ | |
9bccf70c | 140 | /* Entry for searchfs() */ |
1c79356b A |
141 | /************************************************************************/ |
142 | ||
143 | #define errSearchBufferFull 101 /* Internal search errors */ | |
144 | /* | |
145 | # | |
146 | #% searchfs vp L L L | |
147 | # | |
91447636 | 148 | vnop_searchfs { |
1c79356b A |
149 | IN struct vnode *vp; |
150 | IN off_t length; | |
151 | IN int flags; | |
91447636 | 152 | IN kauth_cred_t cred; |
1c79356b A |
153 | IN struct proc *p; |
154 | }; | |
155 | */ | |
156 | ||
55e303ae | 157 | __private_extern__ |
1c79356b | 158 | int |
91447636 A |
159 | hfs_vnop_search(ap) |
160 | struct vnop_searchfs_args *ap; /* | |
9bccf70c A |
161 | struct vnodeop_desc *a_desc; |
162 | struct vnode *a_vp; | |
163 | void *a_searchparams1; | |
164 | void *a_searchparams2; | |
165 | struct attrlist *a_searchattrs; | |
166 | u_long a_maxmatches; | |
167 | struct timeval *a_timelimit; | |
168 | struct attrlist *a_returnattrs; | |
169 | u_long *a_nummatches; | |
170 | u_long a_scriptcode; | |
171 | u_long a_options; | |
172 | struct uio *a_uio; | |
173 | struct searchstate *a_searchstate; | |
91447636 | 174 | vfs_context_t a_context; |
9bccf70c | 175 | */ |
1c79356b | 176 | { |
9bccf70c | 177 | ExtendedVCB *vcb = VTOVCB(ap->a_vp); |
91447636 | 178 | struct hfsmount *hfsmp; |
9bccf70c A |
179 | FCB * catalogFCB; |
180 | searchinfospec_t searchInfo1; | |
181 | searchinfospec_t searchInfo2; | |
182 | void *attributesBuffer; | |
183 | void *variableBuffer; | |
184 | u_long fixedBlockSize; | |
185 | u_long eachReturnBufferSize; | |
2d21ac55 | 186 | struct proc *p = current_proc(); |
9bccf70c A |
187 | int err = E_NONE; |
188 | int isHFSPlus; | |
189 | int timerExpired = false; | |
9bccf70c A |
190 | CatalogKey * myCurrentKeyPtr; |
191 | CatalogRecord * myCurrentDataPtr; | |
192 | CatPosition * myCatPositionPtr; | |
193 | BTScanState myBTScanState; | |
91447636 A |
194 | user_addr_t user_start = 0; |
195 | user_size_t user_len = 0; | |
55e303ae | 196 | int32_t searchTime; |
91447636 | 197 | int lockflags; |
1c79356b A |
198 | |
199 | /* XXX Parameter check a_searchattrs? */ | |
200 | ||
201 | *(ap->a_nummatches) = 0; | |
202 | ||
9bccf70c A |
203 | if (ap->a_options & ~SRCHFS_VALIDOPTIONSMASK) |
204 | return (EINVAL); | |
1c79356b | 205 | |
2d21ac55 A |
206 | /* |
207 | * Reject requests for unsupported attributes. | |
208 | */ | |
209 | if ((ap->a_returnattrs->commonattr & ~HFS_ATTR_CMN_VALID) || | |
210 | (ap->a_returnattrs->volattr != 0) || | |
211 | (ap->a_returnattrs->dirattr & ~HFS_ATTR_DIR_VALID) || | |
212 | (ap->a_returnattrs->fileattr & ~HFS_ATTR_FILE_VALID) || | |
213 | (ap->a_returnattrs->forkattr != 0)) { | |
214 | return (EINVAL); | |
215 | } | |
216 | ||
55e303ae A |
217 | /* SRCHFS_SKIPLINKS requires root access. |
218 | * This option cannot be used with either | |
219 | * the ATTR_CMN_NAME or ATTR_CMN_PAROBJID | |
220 | * attributes. | |
221 | */ | |
222 | if (ap->a_options & SRCHFS_SKIPLINKS) { | |
223 | attrgroup_t attrs; | |
224 | ||
225 | attrs = ap->a_searchattrs->commonattr | ap->a_returnattrs->commonattr; | |
226 | if (attrs & (ATTR_CMN_NAME | ATTR_CMN_PAROBJID)) | |
227 | return (EINVAL); | |
13fec989 | 228 | if ((err = vfs_context_suser(ap->a_context))) |
55e303ae A |
229 | return (err); |
230 | } | |
231 | ||
2d21ac55 A |
232 | // If both 32-bit and 64-bit parent ids or file ids are given |
233 | // then return an error. | |
234 | ||
235 | attrgroup_t test_attrs=ap->a_searchattrs->commonattr; | |
236 | ||
237 | if (((test_attrs & ATTR_CMN_OBJID) && (test_attrs & ATTR_CMN_FILEID)) || | |
238 | ((test_attrs & ATTR_CMN_PARENTID) && (test_attrs & ATTR_CMN_PAROBJID))) | |
239 | return (EINVAL); | |
240 | ||
241 | ||
91447636 | 242 | if (uio_resid(ap->a_uio) <= 0) |
1c79356b A |
243 | return (EINVAL); |
244 | ||
245 | isHFSPlus = (vcb->vcbSigWord == kHFSPlusSigWord); | |
91447636 | 246 | hfsmp = VTOHFS(ap->a_vp); |
55e303ae A |
247 | |
248 | searchTime = kMaxMicroSecsInKernel; | |
249 | if (ap->a_timelimit->tv_sec == 0 && | |
250 | ap->a_timelimit->tv_usec > 0 && | |
251 | ap->a_timelimit->tv_usec < kMaxMicroSecsInKernel) { | |
252 | searchTime = ap->a_timelimit->tv_usec; | |
253 | } | |
1c79356b A |
254 | |
255 | /* UnPack the search boundries, searchInfo1, searchInfo2 */ | |
91447636 | 256 | err = UnpackSearchAttributeBlock(hfsmp, ap->a_searchattrs, |
9bccf70c | 257 | &searchInfo1, ap->a_searchparams1); |
1c79356b | 258 | if (err) return err; |
91447636 | 259 | err = UnpackSearchAttributeBlock(hfsmp, ap->a_searchattrs, |
9bccf70c | 260 | &searchInfo2, ap->a_searchparams2); |
1c79356b A |
261 | if (err) return err; |
262 | ||
2d21ac55 A |
263 | //shadow search bits if 64-bit file/parent ids are used |
264 | if (ap->a_searchattrs->commonattr & ATTR_CMN_FILEID) | |
265 | ap->a_searchattrs->commonattr |= ATTR_CMN_OBJID; | |
266 | if (ap->a_searchattrs->commonattr & ATTR_CMN_PARENTID) | |
267 | ap->a_searchattrs->commonattr |= ATTR_CMN_PAROBJID; | |
268 | ||
269 | fixedBlockSize = sizeof(u_int32_t) + hfs_attrblksize(ap->a_returnattrs); /* u_int32_t for length word */ | |
91447636 | 270 | |
1c79356b | 271 | eachReturnBufferSize = fixedBlockSize; |
9bccf70c | 272 | |
1c79356b A |
273 | if ( ap->a_returnattrs->commonattr & ATTR_CMN_NAME ) /* XXX should be more robust! */ |
274 | eachReturnBufferSize += kHFSPlusMaxFileNameBytes + 1; | |
275 | ||
276 | MALLOC( attributesBuffer, void *, eachReturnBufferSize, M_TEMP, M_WAITOK ); | |
277 | variableBuffer = (void*)((char*) attributesBuffer + fixedBlockSize); | |
278 | ||
b4c24cb9 A |
279 | // XXXdbg - have to lock the user's buffer so we don't fault |
280 | // while holding the shared catalog file lock. see the comment | |
281 | // in hfs_readdir() for more details. | |
282 | // | |
91447636 A |
283 | if (hfsmp->jnl && uio_isuserspace(ap->a_uio)) { |
284 | user_start = uio_curriovbase(ap->a_uio); | |
285 | user_len = uio_curriovlen(ap->a_uio); | |
b4c24cb9 A |
286 | |
287 | if ((err = vslock(user_start, user_len)) != 0) { | |
91447636 | 288 | user_start = 0; |
b4c24cb9 A |
289 | goto ExitThisRoutine; |
290 | } | |
291 | } | |
292 | ||
91447636 | 293 | lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG, HFS_SHARED_LOCK); |
1c79356b | 294 | |
9bccf70c | 295 | catalogFCB = GetFileControlBlock(vcb->catalogRefNum); |
14353aa8 A |
296 | myCurrentKeyPtr = NULL; |
297 | myCurrentDataPtr = NULL; | |
298 | myCatPositionPtr = (CatPosition *)ap->a_searchstate; | |
1c79356b | 299 | |
14353aa8 A |
300 | if (ap->a_options & SRCHFS_START) { |
301 | /* Starting a new search. */ | |
9bccf70c | 302 | /* Make sure the on-disk Catalog file is current */ |
91447636 A |
303 | (void) hfs_fsync(vcb->catalogRefNum, MNT_WAIT, 0, p); |
304 | if (hfsmp->jnl) { | |
305 | hfs_systemfile_unlock(hfsmp, lockflags); | |
306 | journal_flush(hfsmp->jnl); | |
307 | lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG, HFS_SHARED_LOCK); | |
55e303ae A |
308 | } |
309 | ||
14353aa8 | 310 | ap->a_options &= ~SRCHFS_START; |
2d21ac55 | 311 | bzero((caddr_t)myCatPositionPtr, sizeof(*myCatPositionPtr)); |
14353aa8 | 312 | err = BTScanInitialize(catalogFCB, 0, 0, 0, kCatSearchBufferSize, &myBTScanState); |
2d21ac55 A |
313 | if (err) { |
314 | goto ExitThisRoutine; | |
d52fe63f | 315 | } |
14353aa8 A |
316 | } else { |
317 | /* Resuming a search. */ | |
318 | err = BTScanInitialize(catalogFCB, myCatPositionPtr->nextNode, | |
319 | myCatPositionPtr->nextRecord, | |
320 | myCatPositionPtr->recordsFound, | |
321 | kCatSearchBufferSize, | |
322 | &myBTScanState); | |
14353aa8 A |
323 | /* Make sure Catalog hasn't changed. */ |
324 | if (err == 0 | |
325 | && myCatPositionPtr->writeCount != myBTScanState.btcb->writeCount) { | |
326 | myCatPositionPtr->writeCount = myBTScanState.btcb->writeCount; | |
327 | err = EBUSY; /* catChangedErr */ | |
1c79356b | 328 | } |
14353aa8 | 329 | } |
91447636 | 330 | hfs_systemfile_unlock(hfsmp, lockflags); |
1c79356b | 331 | |
14353aa8 A |
332 | if (err) |
333 | goto ExitThisRoutine; | |
9bccf70c | 334 | |
14353aa8 A |
335 | /* |
336 | * Check all the catalog btree records... | |
337 | * return the attributes for matching items | |
338 | */ | |
339 | for (;;) { | |
340 | struct timeval myCurrentTime; | |
341 | struct timeval myElapsedTime; | |
1c79356b | 342 | |
14353aa8 A |
343 | err = BTScanNextRecord(&myBTScanState, timerExpired, |
344 | (void **)&myCurrentKeyPtr, (void **)&myCurrentDataPtr, | |
345 | NULL); | |
346 | if (err) | |
347 | break; | |
348 | ||
9bccf70c | 349 | /* Resolve any hardlinks */ |
91447636 | 350 | if (isHFSPlus && (ap->a_options & SRCHFS_SKIPLINKS) == 0) { |
2d21ac55 | 351 | ResolveHardlink(vcb, (HFSPlusCatalogFile *)myCurrentDataPtr); |
91447636 | 352 | } |
9bccf70c | 353 | if (CheckCriteria( vcb, ap->a_options, ap->a_searchattrs, myCurrentDataPtr, |
2d21ac55 | 354 | myCurrentKeyPtr, &searchInfo1, &searchInfo2 ) |
13fec989 | 355 | && CheckAccess(vcb, ap->a_options, myCurrentKeyPtr, ap->a_context)) { |
91447636 | 356 | err = InsertMatch(hfsmp, ap->a_uio, myCurrentDataPtr, |
9bccf70c | 357 | myCurrentKeyPtr, ap->a_returnattrs, |
91447636 | 358 | attributesBuffer, variableBuffer, ap->a_nummatches); |
14353aa8 A |
359 | if (err) { |
360 | /* | |
361 | * The last match didn't fit so come back | |
362 | * to this record on the next trip. | |
363 | */ | |
364 | --myBTScanState.recordsFound; | |
365 | --myBTScanState.recordNum; | |
1c79356b | 366 | break; |
14353aa8 | 367 | } |
9bccf70c | 368 | |
14353aa8 A |
369 | if (*(ap->a_nummatches) >= ap->a_maxmatches) |
370 | break; | |
371 | } | |
1c79356b | 372 | |
14353aa8 A |
373 | /* |
374 | * Check our elapsed time and bail if we've hit the max. | |
375 | * The idea here is to throttle the amount of time we | |
376 | * spend in the kernel. | |
377 | */ | |
91447636 | 378 | microuptime(&myCurrentTime); |
14353aa8 A |
379 | timersub(&myCurrentTime, &myBTScanState.startTime, &myElapsedTime); |
380 | /* Note: assumes kMaxMicroSecsInKernel is less than 1,000,000 */ | |
381 | if (myElapsedTime.tv_sec > 0 | |
55e303ae | 382 | || myElapsedTime.tv_usec >= searchTime) { |
14353aa8 | 383 | timerExpired = true; |
1c79356b A |
384 | } |
385 | } | |
9bccf70c | 386 | |
14353aa8 A |
387 | /* Update catalog position */ |
388 | myCatPositionPtr->writeCount = myBTScanState.btcb->writeCount; | |
1c79356b | 389 | |
9bccf70c A |
390 | BTScanTerminate(&myBTScanState, &myCatPositionPtr->nextNode, |
391 | &myCatPositionPtr->nextRecord, | |
392 | &myCatPositionPtr->recordsFound); | |
1c79356b A |
393 | |
394 | if ( err == E_NONE ) { | |
395 | err = EAGAIN; /* signal to the user to call searchfs again */ | |
396 | } else if ( err == errSearchBufferFull ) { | |
397 | if ( *(ap->a_nummatches) > 0 ) | |
398 | err = EAGAIN; | |
399 | else | |
400 | err = ENOBUFS; | |
401 | } else if ( err == btNotFound ) { | |
402 | err = E_NONE; /* the entire disk has been searched */ | |
14353aa8 A |
403 | } else if ( err == fsBTTimeOutErr ) { |
404 | err = EAGAIN; | |
1c79356b A |
405 | } |
406 | ||
407 | ExitThisRoutine: | |
91447636 | 408 | FREE( attributesBuffer, M_TEMP ); |
1c79356b | 409 | |
91447636 | 410 | if (hfsmp->jnl && user_start) { |
b4c24cb9 A |
411 | vsunlock(user_start, user_len, TRUE); |
412 | } | |
413 | ||
14353aa8 | 414 | return (MacToVFSError(err)); |
1c79356b A |
415 | } |
416 | ||
417 | ||
9bccf70c | 418 | static void |
2d21ac55 | 419 | ResolveHardlink(struct hfsmount *hfsmp, HFSPlusCatalogFile *recp) |
9bccf70c | 420 | { |
2d21ac55 A |
421 | u_int32_t type, creator; |
422 | int isdirlink = 0; | |
423 | int isfilelink = 0; | |
424 | time_t filecreatedate; | |
425 | ||
426 | if (recp->recordType != kHFSPlusFileRecord) { | |
427 | return; | |
428 | } | |
429 | type = SWAP_BE32(recp->userInfo.fdType); | |
430 | creator = SWAP_BE32(recp->userInfo.fdCreator); | |
431 | filecreatedate = to_bsd_time(recp->createDate); | |
432 | ||
433 | if ((type == kHardLinkFileType && creator == kHFSPlusCreator) && | |
434 | (filecreatedate == (time_t)hfsmp->vcbCrDate || | |
435 | filecreatedate == (time_t)hfsmp->hfs_metadata_createdate)) { | |
436 | isfilelink = 1; | |
437 | } else if ((type == kHFSAliasType && creator == kHFSAliasCreator) && | |
438 | (recp->flags & kHFSHasLinkChainMask) && | |
439 | (filecreatedate == (time_t)hfsmp->vcbCrDate || | |
440 | filecreatedate == (time_t)hfsmp->hfs_metadata_createdate)) { | |
441 | isdirlink = 1; | |
442 | } | |
443 | ||
444 | if (isfilelink || isdirlink) { | |
55e303ae | 445 | cnid_t saved_cnid; |
2d21ac55 | 446 | int lockflags; |
55e303ae A |
447 | |
448 | /* Export link's cnid (a unique value) instead of inode's cnid */ | |
449 | saved_cnid = recp->fileID; | |
2d21ac55 A |
450 | lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG, HFS_SHARED_LOCK); |
451 | ||
452 | (void) cat_resolvelink(hfsmp, recp->hl_linkReference, isdirlink, recp); | |
453 | ||
55e303ae | 454 | recp->fileID = saved_cnid; |
2d21ac55 | 455 | hfs_systemfile_unlock(hfsmp, lockflags); |
9bccf70c A |
456 | } |
457 | } | |
458 | ||
459 | ||
1c79356b | 460 | static Boolean |
2d21ac55 A |
461 | CompareMasked(const u_int32_t *thisValue, const u_int32_t *compareData, |
462 | const u_int32_t *compareMask, u_int32_t count) | |
1c79356b A |
463 | { |
464 | Boolean matched; | |
2d21ac55 | 465 | u_int32_t i; |
1c79356b A |
466 | |
467 | matched = true; /* Assume it will all match */ | |
468 | ||
469 | for (i=0; i<count; i++) { | |
470 | if (((*thisValue++ ^ *compareData++) & *compareMask++) != 0) { | |
471 | matched = false; | |
472 | break; | |
473 | } | |
474 | } | |
475 | ||
476 | return matched; | |
477 | } | |
478 | ||
479 | ||
480 | static Boolean | |
481 | ComparePartialUnicodeName (register ConstUniCharArrayPtr str, register ItemCount s_len, | |
482 | register ConstUniCharArrayPtr find, register ItemCount f_len ) | |
483 | { | |
484 | if (f_len == 0 || s_len == 0) | |
485 | return FALSE; | |
486 | ||
487 | do { | |
488 | if (s_len-- < f_len) | |
489 | return FALSE; | |
490 | } while (FastUnicodeCompare(str++, f_len, find, f_len) != 0); | |
491 | ||
492 | return TRUE; | |
493 | } | |
494 | ||
495 | ||
496 | static Boolean | |
497 | ComparePartialPascalName ( register ConstStr31Param str, register ConstStr31Param find ) | |
498 | { | |
499 | register u_char s_len = str[0]; | |
500 | register u_char f_len = find[0]; | |
501 | register u_char *tsp; | |
502 | Str31 tmpstr; | |
503 | ||
504 | if (f_len == 0 || s_len == 0) | |
505 | return FALSE; | |
506 | ||
507 | bcopy(str, tmpstr, s_len + 1); | |
508 | tsp = &tmpstr[0]; | |
509 | ||
510 | while (s_len-- >= f_len) { | |
511 | *tsp = f_len; | |
512 | ||
513 | if (FastRelString(tsp++, find) == 0) | |
514 | return TRUE; | |
515 | } | |
516 | ||
517 | return FALSE; | |
518 | } | |
519 | ||
520 | ||
55e303ae A |
521 | // |
522 | // Determine if a name is "inappropriate" where the definition | |
523 | // of "inappropriate" is up to higher level execs. Currently | |
524 | // that's limited to /System. | |
525 | // | |
526 | static int | |
2d21ac55 | 527 | is_inappropriate_name(const char *name, int len) |
55e303ae | 528 | { |
91447636 | 529 | const char *bad_names[] = { "System" }; |
55e303ae A |
530 | int bad_len[] = { 6 }; |
531 | int i; | |
532 | ||
91447636 | 533 | for(i=0; i < (int) (sizeof(bad_names) / sizeof(bad_names[0])); i++) { |
2d21ac55 | 534 | if (len == bad_len[i] && strncmp(name, bad_names[i], strlen(bad_names[i]) + 1) == 0) { |
55e303ae A |
535 | return 1; |
536 | } | |
537 | } | |
538 | ||
539 | // if we get here, no name matched | |
540 | return 0; | |
541 | } | |
542 | ||
543 | ||
544 | ||
1c79356b A |
545 | /* |
546 | * Check to see if caller has access rights to this item | |
547 | */ | |
9bccf70c | 548 | |
1c79356b | 549 | static int |
13fec989 | 550 | CheckAccess(ExtendedVCB *theVCBPtr, u_long searchBits, CatalogKey *theKeyPtr, struct vfs_context *ctx) |
1c79356b | 551 | { |
91447636 A |
552 | Boolean isHFSPlus; |
553 | int myErr; | |
554 | int myResult; | |
9bccf70c | 555 | HFSCatalogNodeID myNodeID; |
91447636 A |
556 | hfsmount_t * hfsmp; |
557 | struct FndrDirInfo *finfop; | |
558 | struct vnode * vp = NULL; | |
55e303ae | 559 | |
9bccf70c | 560 | myResult = 0; /* default to "no access" */ |
9bccf70c | 561 | |
13fec989 | 562 | if (!vfs_context_suser(ctx)) { |
9bccf70c A |
563 | myResult = 1; /* allow access */ |
564 | goto ExitThisRoutine; /* root always has access */ | |
565 | } | |
566 | ||
91447636 | 567 | hfsmp = VCBTOHFS( theVCBPtr ); |
9bccf70c A |
568 | isHFSPlus = ( theVCBPtr->vcbSigWord == kHFSPlusSigWord ); |
569 | if ( isHFSPlus ) | |
570 | myNodeID = theKeyPtr->hfsPlus.parentID; | |
571 | else | |
572 | myNodeID = theKeyPtr->hfs.parentID; | |
573 | ||
574 | while ( myNodeID >= kRootDirID ) { | |
91447636 A |
575 | cnode_t * cp; |
576 | ||
9bccf70c | 577 | /* now go get catalog data for this directory */ |
91447636 A |
578 | myErr = hfs_vget(hfsmp, myNodeID, &vp, 0); |
579 | if ( myErr ) { | |
9bccf70c | 580 | goto ExitThisRoutine; /* no access */ |
91447636 | 581 | } |
9bccf70c | 582 | |
91447636 A |
583 | cp = VTOC(vp); |
584 | finfop = (struct FndrDirInfo *)&cp->c_attr.ca_finderinfo[0]; | |
585 | ||
586 | if ( searchBits & SRCHFS_SKIPPACKAGES ) { | |
587 | if ( (SWAP_BE16(finfop->frFlags) & kHasBundle) | |
588 | || (cp->c_desc.cd_nameptr != NULL | |
2d21ac55 | 589 | && is_package_name((const char *)cp->c_desc.cd_nameptr, cp->c_desc.cd_namelen)) ) { |
91447636 A |
590 | myResult = 0; |
591 | goto ExitThisRoutine; | |
55e303ae A |
592 | } |
593 | } | |
594 | ||
91447636 A |
595 | if ( searchBits & SRCHFS_SKIPINAPPROPRIATE ) { |
596 | if ( cp->c_parentcnid == kRootDirID && cp->c_desc.cd_nameptr != NULL && | |
2d21ac55 | 597 | is_inappropriate_name((const char *)cp->c_desc.cd_nameptr, cp->c_desc.cd_namelen) ) { |
91447636 A |
598 | myResult = 0; |
599 | goto ExitThisRoutine; | |
55e303ae A |
600 | } |
601 | } | |
602 | ||
91447636 A |
603 | if ( (searchBits & SRCHFS_SKIPINVISIBLE) && |
604 | (SWAP_BE16(finfop->frFlags) & kIsInvisible) ) { | |
55e303ae A |
605 | myResult = 0; |
606 | goto ExitThisRoutine; | |
607 | } | |
608 | ||
91447636 A |
609 | myNodeID = cp->c_parentcnid; /* move up the hierarchy */ |
610 | hfs_unlock(VTOC(vp)); | |
2d21ac55 A |
611 | |
612 | #if CONFIG_MACF | |
613 | if (vp->v_type == VDIR) { | |
614 | myErr = mac_vnode_check_readdir(ctx, vp); | |
615 | } else { | |
616 | myErr = mac_vnode_check_stat(ctx, NOCRED, vp); | |
617 | } | |
618 | if (myErr) { | |
619 | vnode_put(vp); | |
620 | vp = NULL; | |
621 | goto ExitThisRoutine; | |
622 | } | |
623 | #endif /* MAC */ | |
624 | ||
b36670ce | 625 | if (vp->v_type == VDIR) { |
13fec989 | 626 | myErr = vnode_authorize(vp, NULL, (KAUTH_VNODE_SEARCH | KAUTH_VNODE_LIST_DIRECTORY), ctx); |
b36670ce | 627 | } else { |
13fec989 | 628 | myErr = vnode_authorize(vp, NULL, (KAUTH_VNODE_SEARCH), ctx); |
b36670ce | 629 | } |
91447636 A |
630 | vnode_put(vp); |
631 | vp = NULL; | |
632 | if ( myErr ) { | |
9bccf70c | 633 | goto ExitThisRoutine; /* no access */ |
91447636 | 634 | } |
9bccf70c | 635 | } |
9bccf70c A |
636 | myResult = 1; /* allow access */ |
637 | ||
638 | ExitThisRoutine: | |
91447636 A |
639 | if ( vp != NULL ) { |
640 | hfs_unlock(VTOC(vp)); | |
641 | vnode_put(vp); | |
642 | } | |
9bccf70c A |
643 | return ( myResult ); |
644 | ||
1c79356b A |
645 | } |
646 | ||
9bccf70c A |
647 | static int |
648 | CheckCriteria( ExtendedVCB *vcb, | |
649 | u_long searchBits, | |
650 | struct attrlist *attrList, | |
651 | CatalogRecord *rec, | |
d52fe63f | 652 | CatalogKey *key, |
9bccf70c | 653 | searchinfospec_t *searchInfo1, |
2d21ac55 | 654 | searchinfospec_t *searchInfo2 ) |
1c79356b A |
655 | { |
656 | Boolean matched, atleastone; | |
657 | Boolean isHFSPlus; | |
658 | attrgroup_t searchAttributes; | |
91447636 | 659 | struct cat_attr c_attr; |
9bccf70c A |
660 | struct cat_fork datafork; |
661 | struct cat_fork rsrcfork; | |
1c79356b | 662 | |
91447636 | 663 | bzero(&c_attr, sizeof(c_attr)); |
1c79356b A |
664 | isHFSPlus = (vcb->vcbSigWord == kHFSPlusSigWord); |
665 | ||
9bccf70c A |
666 | switch (rec->recordType) { |
667 | case kHFSFolderRecord: | |
668 | case kHFSPlusFolderRecord: | |
1c79356b A |
669 | if ( (searchBits & SRCHFS_MATCHDIRS) == 0 ) { /* If we are NOT searching folders */ |
670 | matched = false; | |
671 | goto TestDone; | |
672 | } | |
673 | break; | |
674 | ||
9bccf70c | 675 | case kHFSFileRecord: |
55e303ae A |
676 | if ( (searchBits & SRCHFS_MATCHFILES) == 0 ) { /* If we are NOT searching files */ |
677 | matched = false; | |
678 | goto TestDone; | |
679 | } | |
680 | break; | |
681 | ||
9bccf70c | 682 | case kHFSPlusFileRecord: |
55e303ae A |
683 | /* Check if hardlink links should be skipped. */ |
684 | if (searchBits & SRCHFS_SKIPLINKS) { | |
685 | cnid_t parid = key->hfsPlus.parentID; | |
686 | HFSPlusCatalogFile *filep = (HFSPlusCatalogFile *)rec; | |
687 | ||
688 | if ((SWAP_BE32(filep->userInfo.fdType) == kHardLinkFileType) && | |
689 | (SWAP_BE32(filep->userInfo.fdCreator) == kHFSPlusCreator)) { | |
2d21ac55 A |
690 | return (false); /* skip over file link records */ |
691 | } else if ((parid == vcb->hfs_private_desc[FILE_HARDLINKS].cd_cnid) && | |
55e303ae A |
692 | (filep->bsdInfo.special.linkCount == 0)) { |
693 | return (false); /* skip over unlinked files */ | |
2d21ac55 A |
694 | } else if ((SWAP_BE32(filep->userInfo.fdType) == kHFSAliasType) && |
695 | (SWAP_BE32(filep->userInfo.fdCreator) == kHFSAliasCreator) && | |
696 | (filep->flags & kHFSHasLinkChainMask)) { | |
697 | return (false); /* skip over dir link records */ | |
55e303ae | 698 | } |
2d21ac55 A |
699 | } else if (key->hfsPlus.parentID == vcb->hfs_private_desc[FILE_HARDLINKS].cd_cnid) { |
700 | return (false); /* skip over private files */ | |
701 | } else if (key->hfsPlus.parentID == vcb->hfs_private_desc[DIR_HARDLINKS].cd_cnid) { | |
55e303ae A |
702 | return (false); /* skip over private files */ |
703 | } | |
704 | ||
1c79356b A |
705 | if ( (searchBits & SRCHFS_MATCHFILES) == 0 ) { /* If we are NOT searching files */ |
706 | matched = false; | |
707 | goto TestDone; | |
708 | } | |
709 | break; | |
710 | ||
711 | default: /* Never match a thread record or any other type. */ | |
712 | return( false ); /* Not a file or folder record, so can't search it */ | |
713 | } | |
714 | ||
715 | matched = true; /* Assume we got a match */ | |
716 | atleastone = false; /* Dont insert unless we match at least one criteria */ | |
717 | ||
718 | /* First, attempt to match the name -- either partial or complete */ | |
719 | if ( attrList->commonattr & ATTR_CMN_NAME ) { | |
720 | if (isHFSPlus) { | |
721 | /* Check for partial/full HFS Plus name match */ | |
722 | ||
723 | if ( searchBits & SRCHFS_MATCHPARTIALNAMES ) { | |
724 | matched = ComparePartialUnicodeName(key->hfsPlus.nodeName.unicode, | |
725 | key->hfsPlus.nodeName.length, | |
726 | (UniChar*)searchInfo1->name, | |
727 | searchInfo1->nameLength ); | |
728 | } else /* full HFS Plus name match */ { | |
729 | matched = (FastUnicodeCompare(key->hfsPlus.nodeName.unicode, | |
730 | key->hfsPlus.nodeName.length, | |
731 | (UniChar*)searchInfo1->name, | |
732 | searchInfo1->nameLength ) == 0); | |
733 | } | |
734 | } else { | |
735 | /* Check for partial/full HFS name match */ | |
736 | ||
737 | if ( searchBits & SRCHFS_MATCHPARTIALNAMES ) | |
738 | matched = ComparePartialPascalName(key->hfs.nodeName, (u_char*)searchInfo1->name); | |
739 | else /* full HFS name match */ | |
740 | matched = (FastRelString(key->hfs.nodeName, (u_char*)searchInfo1->name) == 0); | |
741 | } | |
d52fe63f | 742 | |
1c79356b A |
743 | if ( matched == false || (searchBits & ~SRCHFS_MATCHPARTIALNAMES) == 0 ) |
744 | goto TestDone; /* no match, or nothing more to compare */ | |
745 | ||
746 | atleastone = true; | |
747 | } | |
9bccf70c A |
748 | |
749 | /* Convert catalog record into cat_attr format. */ | |
750 | cat_convertattr(VCBTOHFS(vcb), rec, &c_attr, &datafork, &rsrcfork); | |
1c79356b | 751 | |
55e303ae A |
752 | if (searchBits & SRCHFS_SKIPINVISIBLE) { |
753 | int flags; | |
754 | ||
755 | switch (rec->recordType) { | |
756 | case kHFSFolderRecord: | |
757 | case kHFSPlusFolderRecord: { | |
758 | struct FndrDirInfo *finder_info; | |
759 | ||
760 | finder_info = (struct FndrDirInfo *)&c_attr.ca_finderinfo[0]; | |
761 | flags = SWAP_BE16(finder_info->frFlags); | |
762 | break; | |
763 | } | |
764 | ||
765 | case kHFSFileRecord: | |
766 | case kHFSPlusFileRecord: { | |
767 | struct FndrFileInfo *finder_info; | |
768 | ||
769 | finder_info = (struct FndrFileInfo *)&c_attr.ca_finderinfo[0]; | |
770 | flags = SWAP_BE16(finder_info->fdFlags); | |
771 | break; | |
772 | } | |
773 | ||
774 | default: { | |
775 | flags = kIsInvisible; | |
776 | break; | |
777 | } | |
778 | } | |
779 | ||
780 | if (flags & kIsInvisible) { | |
781 | matched = false; | |
782 | goto TestDone; | |
783 | } | |
784 | } | |
785 | ||
786 | ||
787 | ||
1c79356b | 788 | /* Now that we have a record worth searching, see if it matches the search attributes */ |
9bccf70c A |
789 | if (rec->recordType == kHFSFileRecord || |
790 | rec->recordType == kHFSPlusFileRecord) { | |
14353aa8 | 791 | if ((attrList->fileattr & ~ATTR_FILE_VALIDMASK) != 0) { /* attr we do know about */ |
1c79356b A |
792 | matched = false; |
793 | goto TestDone; | |
794 | } | |
14353aa8 | 795 | else if ((attrList->fileattr & ATTR_FILE_VALIDMASK) != 0) { |
1c79356b A |
796 | searchAttributes = attrList->fileattr; |
797 | ||
798 | /* File logical length (data fork) */ | |
799 | if ( searchAttributes & ATTR_FILE_DATALENGTH ) { | |
800 | matched = CompareWideRange( | |
9bccf70c | 801 | datafork.cf_size, |
1c79356b A |
802 | searchInfo1->f.dataLogicalLength, |
803 | searchInfo2->f.dataLogicalLength); | |
804 | if (matched == false) goto TestDone; | |
805 | atleastone = true; | |
806 | } | |
807 | ||
808 | /* File physical length (data fork) */ | |
809 | if ( searchAttributes & ATTR_FILE_DATAALLOCSIZE ) { | |
810 | matched = CompareWideRange( | |
9bccf70c | 811 | (u_int64_t)datafork.cf_blocks * (u_int64_t)vcb->blockSize, |
1c79356b A |
812 | searchInfo1->f.dataPhysicalLength, |
813 | searchInfo2->f.dataPhysicalLength); | |
814 | if (matched == false) goto TestDone; | |
815 | atleastone = true; | |
816 | } | |
817 | ||
818 | /* File logical length (resource fork) */ | |
819 | if ( searchAttributes & ATTR_FILE_RSRCLENGTH ) { | |
820 | matched = CompareWideRange( | |
9bccf70c | 821 | rsrcfork.cf_size, |
1c79356b A |
822 | searchInfo1->f.resourceLogicalLength, |
823 | searchInfo2->f.resourceLogicalLength); | |
824 | if (matched == false) goto TestDone; | |
825 | atleastone = true; | |
826 | } | |
827 | ||
828 | /* File physical length (resource fork) */ | |
829 | if ( searchAttributes & ATTR_FILE_RSRCALLOCSIZE ) { | |
830 | matched = CompareWideRange( | |
9bccf70c | 831 | (u_int64_t)rsrcfork.cf_blocks * (u_int64_t)vcb->blockSize, |
1c79356b A |
832 | searchInfo1->f.resourcePhysicalLength, |
833 | searchInfo2->f.resourcePhysicalLength); | |
834 | if (matched == false) goto TestDone; | |
835 | atleastone = true; | |
836 | } | |
837 | } | |
838 | else { | |
9bccf70c | 839 | atleastone = true; /* to match SRCHFS_MATCHFILES */ |
1c79356b A |
840 | } |
841 | } | |
842 | /* | |
843 | * Check the directory attributes | |
844 | */ | |
9bccf70c A |
845 | else if (rec->recordType == kHFSFolderRecord || |
846 | rec->recordType == kHFSPlusFolderRecord) { | |
1c79356b A |
847 | if ((attrList->dirattr & ~ATTR_DIR_VALIDMASK) != 0) { /* attr we do know about */ |
848 | matched = false; | |
849 | goto TestDone; | |
850 | } | |
851 | else if ((attrList->dirattr & ATTR_DIR_VALIDMASK) != 0) { | |
852 | searchAttributes = attrList->dirattr; | |
853 | ||
854 | /* Directory valence */ | |
855 | if ( searchAttributes & ATTR_DIR_ENTRYCOUNT ) { | |
9bccf70c A |
856 | matched = CompareRange(c_attr.ca_entries, |
857 | searchInfo1->d.numFiles, | |
858 | searchInfo2->d.numFiles ); | |
1c79356b A |
859 | if (matched == false) goto TestDone; |
860 | atleastone = true; | |
861 | } | |
862 | } | |
863 | else { | |
864 | atleastone = true; /* to match SRCHFS_MATCHDIRS */ | |
865 | } | |
866 | } | |
867 | ||
868 | /* | |
869 | * Check the common attributes | |
870 | */ | |
871 | searchAttributes = attrList->commonattr; | |
872 | if ( (searchAttributes & ATTR_CMN_VALIDMASK) != 0 ) { | |
1c79356b A |
873 | /* node ID */ |
874 | if ( searchAttributes & ATTR_CMN_OBJID ) { | |
9bccf70c A |
875 | matched = CompareRange(c_attr.ca_fileid, |
876 | searchInfo1->nodeID, | |
877 | searchInfo2->nodeID ); | |
1c79356b A |
878 | if (matched == false) goto TestDone; |
879 | atleastone = true; | |
880 | } | |
881 | ||
882 | /* Parent ID */ | |
883 | if ( searchAttributes & ATTR_CMN_PAROBJID ) { | |
884 | HFSCatalogNodeID parentID; | |
885 | ||
886 | if (isHFSPlus) | |
887 | parentID = key->hfsPlus.parentID; | |
888 | else | |
889 | parentID = key->hfs.parentID; | |
890 | ||
9bccf70c A |
891 | matched = CompareRange(parentID, searchInfo1->parentDirID, |
892 | searchInfo2->parentDirID ); | |
1c79356b A |
893 | if (matched == false) goto TestDone; |
894 | atleastone = true; | |
895 | } | |
896 | ||
897 | /* Finder Info & Extended Finder Info where extFinderInfo is last 32 bytes */ | |
898 | if ( searchAttributes & ATTR_CMN_FNDRINFO ) { | |
2d21ac55 A |
899 | u_int32_t *thisValue; |
900 | thisValue = (u_int32_t *) &c_attr.ca_finderinfo; | |
1c79356b A |
901 | |
902 | /* | |
903 | * Note: ioFlFndrInfo and ioDrUsrWds have the same offset in search info, so | |
904 | * no need to test the object type here. | |
905 | */ | |
9bccf70c | 906 | matched = CompareMasked(thisValue, |
2d21ac55 A |
907 | (u_int32_t *)&searchInfo1->finderInfo, |
908 | (u_int32_t *) &searchInfo2->finderInfo, 8); | |
1c79356b A |
909 | if (matched == false) goto TestDone; |
910 | atleastone = true; | |
911 | } | |
912 | ||
913 | /* Create date */ | |
914 | if ( searchAttributes & ATTR_CMN_CRTIME ) { | |
9bccf70c A |
915 | matched = CompareRange(c_attr.ca_itime, |
916 | searchInfo1->creationDate.tv_sec, | |
917 | searchInfo2->creationDate.tv_sec); | |
1c79356b A |
918 | if (matched == false) goto TestDone; |
919 | atleastone = true; | |
920 | } | |
921 | ||
922 | /* Mod date */ | |
923 | if ( searchAttributes & ATTR_CMN_MODTIME ) { | |
9bccf70c A |
924 | matched = CompareRange(c_attr.ca_mtime, |
925 | searchInfo1->modificationDate.tv_sec, | |
926 | searchInfo2->modificationDate.tv_sec); | |
1c79356b A |
927 | if (matched == false) goto TestDone; |
928 | atleastone = true; | |
929 | } | |
930 | ||
931 | /* Change Time */ | |
932 | if ( searchAttributes & ATTR_CMN_CHGTIME ) { | |
9bccf70c A |
933 | matched = CompareRange(c_attr.ca_ctime, |
934 | searchInfo1->changeDate.tv_sec, | |
935 | searchInfo2->changeDate.tv_sec); | |
1c79356b A |
936 | if (matched == false) goto TestDone; |
937 | atleastone = true; | |
938 | } | |
939 | ||
9bccf70c A |
940 | /* Access date */ |
941 | if ( searchAttributes & ATTR_CMN_ACCTIME ) { | |
942 | matched = CompareRange(c_attr.ca_atime, | |
943 | searchInfo1->accessDate.tv_sec, | |
944 | searchInfo2->accessDate.tv_sec); | |
945 | if (matched == false) goto TestDone; | |
946 | atleastone = true; | |
947 | } | |
948 | ||
1c79356b A |
949 | /* Backup date */ |
950 | if ( searchAttributes & ATTR_CMN_BKUPTIME ) { | |
9bccf70c A |
951 | matched = CompareRange(c_attr.ca_btime, |
952 | searchInfo1->lastBackupDate.tv_sec, | |
953 | searchInfo2->lastBackupDate.tv_sec); | |
1c79356b A |
954 | if (matched == false) goto TestDone; |
955 | atleastone = true; | |
956 | } | |
957 | ||
958 | /* User ID */ | |
959 | if ( searchAttributes & ATTR_CMN_OWNERID ) { | |
9bccf70c A |
960 | matched = CompareRange(c_attr.ca_uid, |
961 | searchInfo1->uid, searchInfo2->uid); | |
1c79356b A |
962 | if (matched == false) goto TestDone; |
963 | atleastone = true; | |
964 | } | |
965 | ||
966 | /* Group ID */ | |
967 | if ( searchAttributes & ATTR_CMN_GRPID ) { | |
9bccf70c A |
968 | matched = CompareRange(c_attr.ca_gid, |
969 | searchInfo1->gid, searchInfo2->gid); | |
1c79356b A |
970 | if (matched == false) goto TestDone; |
971 | atleastone = true; | |
972 | } | |
973 | ||
974 | /* mode */ | |
975 | if ( searchAttributes & ATTR_CMN_ACCESSMASK ) { | |
2d21ac55 A |
976 | matched = CompareRange((u_int32_t)c_attr.ca_mode, |
977 | (u_int32_t)searchInfo1->mask, | |
978 | (u_int32_t)searchInfo2->mask); | |
1c79356b A |
979 | if (matched == false) goto TestDone; |
980 | atleastone = true; | |
981 | } | |
1c79356b A |
982 | } |
983 | ||
984 | /* If we got here w/o matching any, then set to false */ | |
985 | if (! atleastone) | |
986 | matched = false; | |
987 | ||
988 | TestDone: | |
989 | /* | |
990 | * Finally, determine whether we need to negate the sense of the match | |
991 | * (i.e. find all objects that DON'T match). | |
992 | */ | |
993 | if ( searchBits & SRCHFS_NEGATEPARAMS ) | |
994 | matched = !matched; | |
995 | ||
996 | return( matched ); | |
997 | } | |
998 | ||
999 | ||
1000 | /* | |
1001 | * Adds another record to the packed array for output | |
1002 | */ | |
1003 | static int | |
91447636 A |
1004 | InsertMatch(struct hfsmount *hfsmp, uio_t a_uio, CatalogRecord *rec, |
1005 | CatalogKey *key, struct attrlist *returnAttrList, | |
1006 | void *attributesBuffer, void *variableBuffer, u_long * nummatches) | |
1c79356b A |
1007 | { |
1008 | int err; | |
1009 | void *rovingAttributesBuffer; | |
1010 | void *rovingVariableBuffer; | |
1c79356b | 1011 | u_long packedBufferSize; |
9bccf70c | 1012 | struct attrblock attrblk; |
91447636 A |
1013 | struct cat_desc c_desc; |
1014 | struct cat_attr c_attr; | |
9bccf70c A |
1015 | struct cat_fork datafork; |
1016 | struct cat_fork rsrcfork; | |
1c79356b | 1017 | |
91447636 A |
1018 | bzero(&c_desc, sizeof(c_desc)); |
1019 | bzero(&c_attr, sizeof(c_attr)); | |
1c79356b A |
1020 | rovingAttributesBuffer = (char*)attributesBuffer + sizeof(u_long); /* Reserve space for length field */ |
1021 | rovingVariableBuffer = variableBuffer; | |
1c79356b | 1022 | |
9bccf70c | 1023 | /* Convert catalog record into cat_attr format. */ |
91447636 | 1024 | cat_convertattr(hfsmp, rec, &c_attr, &datafork, &rsrcfork); |
1c79356b | 1025 | |
2d21ac55 A |
1026 | /* Hide our private meta data directories */ |
1027 | if (c_attr.ca_fileid == hfsmp->hfs_private_desc[FILE_HARDLINKS].cd_cnid || | |
1028 | c_attr.ca_fileid == hfsmp->hfs_private_desc[DIR_HARDLINKS].cd_cnid) { | |
9bccf70c A |
1029 | err = 0; |
1030 | goto exit; | |
1031 | } | |
1c79356b | 1032 | |
b4c24cb9 | 1033 | /* Hide the private journal files */ |
91447636 A |
1034 | if (hfsmp->jnl && |
1035 | ((c_attr.ca_fileid == hfsmp->hfs_jnlfileid) || | |
1036 | (c_attr.ca_fileid == hfsmp->hfs_jnlinfoblkid))) { | |
b4c24cb9 A |
1037 | err = 0; |
1038 | goto exit; | |
1039 | } | |
1040 | ||
9bccf70c | 1041 | if (returnAttrList->commonattr & ATTR_CMN_NAME) { |
91447636 | 1042 | cat_convertkey(hfsmp, key, rec, &c_desc); |
9bccf70c A |
1043 | } else { |
1044 | c_desc.cd_cnid = c_attr.ca_fileid; | |
91447636 | 1045 | if (hfsmp->hfs_flags & HFS_STANDARD) |
9bccf70c | 1046 | c_desc.cd_parentcnid = key->hfs.parentID; |
91447636 A |
1047 | else |
1048 | c_desc.cd_parentcnid = key->hfsPlus.parentID; | |
9bccf70c | 1049 | } |
1c79356b | 1050 | |
9bccf70c A |
1051 | attrblk.ab_attrlist = returnAttrList; |
1052 | attrblk.ab_attrbufpp = &rovingAttributesBuffer; | |
1053 | attrblk.ab_varbufpp = &rovingVariableBuffer; | |
1054 | attrblk.ab_flags = 0; | |
1055 | attrblk.ab_blocksize = 0; | |
2d21ac55 | 1056 | attrblk.ab_context = vfs_context_current(); |
1c79356b | 1057 | |
91447636 | 1058 | hfs_packattrblk(&attrblk, hfsmp, NULL, &c_desc, &c_attr, &datafork, &rsrcfork, current_proc()); |
1c79356b A |
1059 | |
1060 | packedBufferSize = (char*)rovingVariableBuffer - (char*)attributesBuffer; | |
1061 | ||
91447636 | 1062 | if ( packedBufferSize > uio_resid(a_uio) ) |
1c79356b A |
1063 | return( errSearchBufferFull ); |
1064 | ||
1065 | (* nummatches)++; | |
1066 | ||
1067 | *((u_long *)attributesBuffer) = packedBufferSize; /* Store length of fixed + var block */ | |
1068 | ||
1069 | err = uiomove( (caddr_t)attributesBuffer, packedBufferSize, a_uio ); /* XXX should be packedBufferSize */ | |
9bccf70c A |
1070 | exit: |
1071 | cat_releasedesc(&c_desc); | |
1c79356b A |
1072 | |
1073 | return( err ); | |
1074 | } | |
1075 | ||
1076 | ||
1077 | static int | |
91447636 | 1078 | UnpackSearchAttributeBlock( struct hfsmount *hfsmp, struct attrlist *alist, searchinfospec_t *searchInfo, void *attributeBuffer ) |
1c79356b A |
1079 | { |
1080 | attrgroup_t a; | |
1081 | u_long bufferSize; | |
91447636 | 1082 | boolean_t is_64_bit; |
1c79356b A |
1083 | |
1084 | DBG_ASSERT(searchInfo != NULL); | |
91447636 A |
1085 | |
1086 | is_64_bit = proc_is64bit(current_proc()); | |
1c79356b | 1087 | |
2d21ac55 | 1088 | bufferSize = *((u_int32_t *)attributeBuffer); |
1c79356b A |
1089 | if (bufferSize == 0) |
1090 | return (EINVAL); /* XXX -DJB is a buffer size of zero ever valid for searchfs? */ | |
1091 | ||
2d21ac55 | 1092 | attributeBuffer = (u_int32_t *)attributeBuffer + 1; /* advance past the size */ |
1c79356b A |
1093 | |
1094 | /* | |
1095 | * UnPack common attributes | |
1096 | */ | |
1097 | a = alist->commonattr; | |
1098 | if ( a != 0 ) { | |
1099 | if ( a & ATTR_CMN_NAME ) { | |
91447636 A |
1100 | char *s; |
1101 | u_int32_t len; | |
1102 | ||
1103 | s = (char*) attributeBuffer + ((attrreference_t *) attributeBuffer)->attr_dataoffset; | |
1104 | len = ((attrreference_t *) attributeBuffer)->attr_length; | |
1c79356b A |
1105 | |
1106 | if (len > sizeof(searchInfo->name)) | |
1107 | return (EINVAL); | |
1108 | ||
91447636 A |
1109 | if (hfsmp->hfs_flags & HFS_STANDARD) { |
1110 | /* Convert name to pascal string to match HFS B-Tree names */ | |
1c79356b A |
1111 | |
1112 | if (len > 0) { | |
2d21ac55 | 1113 | if (utf8_to_hfs(HFSTOVCB(hfsmp), len-1, (u_char *)s, (u_char*)searchInfo->name) != 0) |
1c79356b A |
1114 | return (EINVAL); |
1115 | ||
91447636 | 1116 | searchInfo->nameLength = searchInfo->name[0]; |
1c79356b | 1117 | } else { |
91447636 | 1118 | searchInfo->name[0] = searchInfo->nameLength = 0; |
1c79356b | 1119 | } |
2d21ac55 | 1120 | attributeBuffer = (attrreference_t *)attributeBuffer + 1; |
1c79356b | 1121 | } else { |
91447636 A |
1122 | size_t ucslen; |
1123 | /* Convert name to Unicode to match HFS Plus B-Tree names */ | |
1c79356b A |
1124 | |
1125 | if (len > 0) { | |
2d21ac55 A |
1126 | if (utf8_decodestr((u_int8_t *)s, len-1, (UniChar*)searchInfo->name, &ucslen, |
1127 | sizeof(searchInfo->name), ':', UTF_DECOMPOSED | UTF_ESCAPE_ILLEGAL)) | |
1c79356b A |
1128 | return (EINVAL); |
1129 | ||
91447636 | 1130 | searchInfo->nameLength = ucslen / sizeof(UniChar); |
1c79356b | 1131 | } else { |
91447636 | 1132 | searchInfo->nameLength = 0; |
1c79356b | 1133 | } |
2d21ac55 | 1134 | attributeBuffer = (attrreference_t *)attributeBuffer + 1; |
1c79356b A |
1135 | } |
1136 | } | |
1137 | if ( a & ATTR_CMN_OBJID ) { | |
1138 | searchInfo->nodeID = ((fsobj_id_t *) attributeBuffer)->fid_objno; /* ignore fid_generation */ | |
2d21ac55 | 1139 | attributeBuffer = (fsobj_id_t *)attributeBuffer + 1; |
1c79356b A |
1140 | } |
1141 | if ( a & ATTR_CMN_PAROBJID ) { | |
2d21ac55 A |
1142 | searchInfo->parentDirID = ((fsobj_id_t *) attributeBuffer)->fid_objno; /* ignore fid_generation */ |
1143 | attributeBuffer = (fsobj_id_t *)attributeBuffer + 1; | |
1c79356b | 1144 | } |
2d21ac55 | 1145 | |
1c79356b | 1146 | if ( a & ATTR_CMN_CRTIME ) { |
91447636 A |
1147 | if (is_64_bit) { |
1148 | struct user_timespec tmp; | |
1149 | tmp = *((struct user_timespec *)attributeBuffer); | |
1150 | searchInfo->creationDate.tv_sec = (time_t)tmp.tv_sec; | |
1151 | searchInfo->creationDate.tv_nsec = tmp.tv_nsec; | |
2d21ac55 | 1152 | attributeBuffer = (struct user_timespec *)attributeBuffer + 1; |
91447636 A |
1153 | } |
1154 | else { | |
1155 | searchInfo->creationDate = *((struct timespec *)attributeBuffer); | |
2d21ac55 | 1156 | attributeBuffer = (struct timespec *)attributeBuffer + 1; |
91447636 | 1157 | } |
1c79356b A |
1158 | } |
1159 | if ( a & ATTR_CMN_MODTIME ) { | |
91447636 A |
1160 | if (is_64_bit) { |
1161 | struct user_timespec tmp; | |
1162 | tmp = *((struct user_timespec *)attributeBuffer); | |
1163 | searchInfo->modificationDate.tv_sec = (time_t)tmp.tv_sec; | |
1164 | searchInfo->modificationDate.tv_nsec = tmp.tv_nsec; | |
2d21ac55 | 1165 | attributeBuffer = (struct user_timespec *)attributeBuffer + 1; |
91447636 A |
1166 | } |
1167 | else { | |
1168 | searchInfo->modificationDate = *((struct timespec *)attributeBuffer); | |
2d21ac55 | 1169 | attributeBuffer = (struct timespec *)attributeBuffer + 1; |
91447636 | 1170 | } |
1c79356b A |
1171 | } |
1172 | if ( a & ATTR_CMN_CHGTIME ) { | |
91447636 A |
1173 | if (is_64_bit) { |
1174 | struct user_timespec tmp; | |
1175 | tmp = *((struct user_timespec *)attributeBuffer); | |
1176 | searchInfo->changeDate.tv_sec = (time_t)tmp.tv_sec; | |
1177 | searchInfo->changeDate.tv_nsec = tmp.tv_nsec; | |
2d21ac55 | 1178 | attributeBuffer = (struct user_timespec *)attributeBuffer + 1; |
91447636 A |
1179 | } |
1180 | else { | |
1181 | searchInfo->changeDate = *((struct timespec *)attributeBuffer); | |
2d21ac55 | 1182 | attributeBuffer = (struct timespec *)attributeBuffer + 1; |
91447636 | 1183 | } |
1c79356b | 1184 | } |
9bccf70c | 1185 | if ( a & ATTR_CMN_ACCTIME ) { |
91447636 A |
1186 | if (is_64_bit) { |
1187 | struct user_timespec tmp; | |
1188 | tmp = *((struct user_timespec *)attributeBuffer); | |
1189 | searchInfo->accessDate.tv_sec = (time_t)tmp.tv_sec; | |
1190 | searchInfo->accessDate.tv_nsec = tmp.tv_nsec; | |
2d21ac55 | 1191 | attributeBuffer = (struct user_timespec *)attributeBuffer + 1; |
91447636 A |
1192 | } |
1193 | else { | |
1194 | searchInfo->accessDate = *((struct timespec *)attributeBuffer); | |
2d21ac55 | 1195 | attributeBuffer = (struct timespec *)attributeBuffer + 1; |
91447636 | 1196 | } |
9bccf70c | 1197 | } |
1c79356b | 1198 | if ( a & ATTR_CMN_BKUPTIME ) { |
91447636 A |
1199 | if (is_64_bit) { |
1200 | struct user_timespec tmp; | |
1201 | tmp = *((struct user_timespec *)attributeBuffer); | |
1202 | searchInfo->lastBackupDate.tv_sec = (time_t)tmp.tv_sec; | |
1203 | searchInfo->lastBackupDate.tv_nsec = tmp.tv_nsec; | |
2d21ac55 | 1204 | attributeBuffer = (struct user_timespec *)attributeBuffer + 1; |
91447636 A |
1205 | } |
1206 | else { | |
1207 | searchInfo->lastBackupDate = *((struct timespec *)attributeBuffer); | |
2d21ac55 | 1208 | attributeBuffer = (struct timespec *)attributeBuffer + 1; |
91447636 | 1209 | } |
1c79356b A |
1210 | } |
1211 | if ( a & ATTR_CMN_FNDRINFO ) { | |
91447636 | 1212 | bcopy( attributeBuffer, searchInfo->finderInfo, sizeof(searchInfo->finderInfo) ); |
2d21ac55 | 1213 | attributeBuffer = (u_int8_t *)attributeBuffer + 32; |
1c79356b A |
1214 | } |
1215 | if ( a & ATTR_CMN_OWNERID ) { | |
1216 | searchInfo->uid = *((uid_t *)attributeBuffer); | |
2d21ac55 | 1217 | attributeBuffer = (uid_t *)attributeBuffer + 1; |
1c79356b A |
1218 | } |
1219 | if ( a & ATTR_CMN_GRPID ) { | |
1220 | searchInfo->gid = *((gid_t *)attributeBuffer); | |
2d21ac55 | 1221 | attributeBuffer = (gid_t *)attributeBuffer + 1; |
1c79356b A |
1222 | } |
1223 | if ( a & ATTR_CMN_ACCESSMASK ) { | |
1224 | searchInfo->mask = *((mode_t *)attributeBuffer); | |
2d21ac55 A |
1225 | attributeBuffer = (mode_t *)attributeBuffer + 1; |
1226 | } | |
1227 | if ( a & ATTR_CMN_FILEID ) { | |
1228 | searchInfo->nodeID = (u_int32_t)*((u_int64_t *) attributeBuffer); | |
1229 | attributeBuffer = (u_int64_t *)attributeBuffer + 1; | |
1230 | } | |
1231 | if ( a & ATTR_CMN_PARENTID ) { | |
1232 | searchInfo->parentDirID = (u_int32_t)*((u_int64_t *) attributeBuffer); | |
1233 | attributeBuffer = (u_int64_t *)attributeBuffer + 1; | |
1c79356b A |
1234 | } |
1235 | } | |
1236 | ||
1237 | a = alist->dirattr; | |
1238 | if ( a != 0 ) { | |
1239 | if ( a & ATTR_DIR_ENTRYCOUNT ) { | |
91447636 | 1240 | searchInfo->d.numFiles = *((u_int32_t *)attributeBuffer); |
2d21ac55 | 1241 | attributeBuffer = (u_int32_t *)attributeBuffer + 1; |
1c79356b A |
1242 | } |
1243 | } | |
1244 | ||
1245 | a = alist->fileattr; | |
1246 | if ( a != 0 ) { | |
1247 | if ( a & ATTR_FILE_DATALENGTH ) { | |
1248 | searchInfo->f.dataLogicalLength = *((off_t *)attributeBuffer); | |
2d21ac55 | 1249 | attributeBuffer = (off_t *)attributeBuffer + 1; |
1c79356b A |
1250 | } |
1251 | if ( a & ATTR_FILE_DATAALLOCSIZE ) { | |
1252 | searchInfo->f.dataPhysicalLength = *((off_t *)attributeBuffer); | |
2d21ac55 | 1253 | attributeBuffer = (off_t *)attributeBuffer + 1; |
1c79356b A |
1254 | } |
1255 | if ( a & ATTR_FILE_RSRCLENGTH ) { | |
1256 | searchInfo->f.resourceLogicalLength = *((off_t *)attributeBuffer); | |
2d21ac55 | 1257 | attributeBuffer = (off_t *)attributeBuffer + 1; |
1c79356b A |
1258 | } |
1259 | if ( a & ATTR_FILE_RSRCALLOCSIZE ) { | |
1260 | searchInfo->f.resourcePhysicalLength = *((off_t *)attributeBuffer); | |
2d21ac55 | 1261 | attributeBuffer = (off_t *)attributeBuffer + 1; |
1c79356b A |
1262 | } |
1263 | } | |
1264 | ||
1265 | return (0); | |
1266 | } | |
1267 |