]>
Commit | Line | Data |
---|---|---|
de8ee011 A |
1 | /* Copyright © 2017-2018 Apple Inc. All rights reserved. |
2 | * | |
3 | * lf_hfs_attrlist.c | |
4 | * livefiles_hfs | |
5 | * | |
6 | * Created by Or Haimovich on 25/3/18. | |
7 | */ | |
8 | ||
9 | #include "lf_hfs_attrlist.h" | |
10 | #include "lf_hfs_locks.h" | |
11 | #include "lf_hfs.h" | |
12 | #include "lf_hfs_vfsutils.h" | |
13 | #include "lf_hfs_vnops.h" | |
14 | #include "lf_hfs_fileops_handler.h" | |
15 | #include "lf_hfs_logger.h" | |
16 | #include "lf_hfs_chash.h" | |
17 | ||
18 | static void SetAttrIntoStruct(UVFSDirEntryAttr* psAttrEntry, struct cat_attr* pAttr, struct cat_desc* psDesc, struct hfsmount* psHfsm, struct cat_fork* pDataFork) | |
19 | { | |
20 | psAttrEntry->dea_attrs.fa_validmask = VALID_OUT_ATTR_MASK; | |
21 | ||
22 | psAttrEntry->dea_attrs.fa_atime.tv_sec = pAttr->ca_atime; | |
23 | psAttrEntry->dea_attrs.fa_ctime.tv_sec = pAttr->ca_ctime; | |
24 | psAttrEntry->dea_attrs.fa_mtime.tv_sec = pAttr->ca_mtime; | |
25 | psAttrEntry->dea_attrs.fa_birthtime.tv_sec = pAttr->ca_btime; | |
26 | psAttrEntry->dea_attrs.fa_atime.tv_nsec = psAttrEntry->dea_attrs.fa_mtime.tv_nsec = psAttrEntry->dea_attrs.fa_ctime.tv_nsec = psAttrEntry->dea_attrs.fa_birthtime.tv_nsec = 0; | |
27 | psAttrEntry->dea_attrs.fa_fileid = psDesc->cd_cnid; | |
28 | psAttrEntry->dea_attrs.fa_parentid = psDesc->cd_parentcnid; | |
29 | psAttrEntry->dea_attrs.fa_mode = pAttr->ca_mode & ALL_UVFS_MODES; | |
30 | psAttrEntry->dea_attrs.fa_bsd_flags = pAttr->ca_bsdflags; | |
31 | ||
32 | if (VTTOUVFS(IFTOVT(pAttr->ca_mode)) == UVFS_FA_TYPE_DIR) | |
33 | { | |
34 | //If its a directory need to add . and .. to the direntries count | |
35 | psAttrEntry->dea_attrs.fa_nlink = 2 + pAttr->ca_entries; | |
36 | ||
37 | //If this is the root folder need to hide the journal files */ | |
38 | if (psHfsm->jnl && psDesc->cd_cnid == kHFSRootFolderID) | |
39 | { | |
40 | psAttrEntry->dea_attrs.fa_nlink -= 2; | |
41 | } | |
42 | psAttrEntry->dea_attrs.fa_size = (pAttr->ca_entries + 2) * AVERAGE_HFSDIRENTRY_SIZE ; | |
43 | } else { | |
44 | psAttrEntry->dea_attrs.fa_nlink = pAttr->ca_linkcount; | |
45 | psAttrEntry->dea_attrs.fa_size = pDataFork->cf_size; | |
46 | } | |
47 | ||
48 | psAttrEntry->dea_attrs.fa_allocsize = pDataFork->cf_blocks * HFSTOVCB(psHfsm)->blockSize; | |
49 | psAttrEntry->dea_attrs.fa_type = VTTOUVFS(IFTOVT(pAttr->ca_mode)); | |
50 | psAttrEntry->dea_attrs.fa_gid = pAttr->ca_gid; | |
51 | psAttrEntry->dea_attrs.fa_uid = pAttr->ca_uid ; | |
52 | } | |
53 | ||
54 | ||
55 | static void AddNewAttrEntry(ReadDirBuff_s* psReadDirBuffer, struct hfsmount* psHfsm, struct cat_desc* psDesc, struct cat_attr* pAttr, struct cat_fork* pDataFork, uint32_t* puUioSize, int iIndex, UVFSDirEntryAttr** ppsPrevAttrEntry) | |
56 | { | |
57 | UVFSDirEntryAttr* psAttrEntry = (UVFSDirEntryAttr*) (psReadDirBuffer->pvBuffer + READDIR_BUF_OFFSET(psReadDirBuffer)); | |
58 | ||
59 | SetAttrIntoStruct(psAttrEntry, pAttr, psDesc, psHfsm, pDataFork); | |
60 | ||
61 | psAttrEntry->dea_namelen = psDesc->cd_namelen; | |
62 | psAttrEntry->dea_nextrec = _UVFS_DIRENTRYATTR_RECLEN(UVFS_DIRENTRYATTR_NAMEOFF, psDesc->cd_namelen); | |
63 | psAttrEntry->dea_spare0 = 0; | |
64 | psAttrEntry->dea_nameoff = UVFS_DIRENTRYATTR_NAMEOFF; | |
65 | const u_int8_t * name = psDesc->cd_nameptr; | |
66 | memcpy( UVFS_DIRENTRYATTR_NAMEPTR(psAttrEntry), name, psDesc->cd_namelen ); | |
67 | UVFS_DIRENTRYATTR_NAMEPTR(psAttrEntry)[psAttrEntry->dea_namelen] = 0; | |
68 | ||
69 | *puUioSize = psAttrEntry->dea_nextrec; | |
70 | ||
71 | //Update prevEntry with cookie | |
72 | if (*ppsPrevAttrEntry != NULL) | |
73 | { | |
74 | (*ppsPrevAttrEntry)->dea_nextcookie = iIndex | ((u_int64_t)psDesc->cd_cnid << 32); | |
75 | } | |
76 | ||
77 | *ppsPrevAttrEntry = psAttrEntry; | |
78 | psReadDirBuffer->uBufferResid -= *puUioSize; | |
79 | ||
80 | return; | |
81 | } | |
82 | ||
83 | static bool CompareTimes(const struct timespec* psTimeA, const struct timespec* psTimeB) | |
84 | { | |
85 | //Returns true if a happened at or after b. | |
86 | if (psTimeA->tv_sec == psTimeB->tv_sec) | |
87 | return psTimeA->tv_nsec == psTimeB->tv_nsec; | |
88 | else | |
89 | return psTimeA->tv_sec > psTimeB->tv_sec; | |
90 | } | |
91 | ||
92 | static bool DirScanIsMatch(ScanDirRequest_s* psScanDirRequest, struct cat_desc* psDesc, struct cat_attr* pAttr, struct hfsmount* psHfsm, struct cat_fork* pDataFork) | |
93 | { | |
94 | bool bIsMatch = true; | |
95 | UVFSDirEntryAttr* psDirEntry = psScanDirRequest->psMatchingResult->smr_entry; | |
96 | const char* pcName = (const char *) psDesc->cd_nameptr; | |
97 | ||
98 | if (pcName && ((psDesc->cd_namelen == (sizeof(HFSPLUSMETADATAFOLDER) - 1) && | |
99 | memcmp(pcName, HFSPLUSMETADATAFOLDER, sizeof(HFSPLUSMETADATAFOLDER))) || | |
100 | (psDesc->cd_namelen == (sizeof(HFSPLUS_DIR_METADATA_FOLDER) - 1) && | |
101 | memcmp(pcName, HFSPLUS_DIR_METADATA_FOLDER, sizeof(HFSPLUS_DIR_METADATA_FOLDER))))) | |
102 | { | |
103 | // Skip over special dirs | |
104 | return false; | |
105 | } else if (pcName == NULL) | |
106 | { | |
107 | //XXXab: Should not happen anymore | |
108 | LFHFS_LOG(LEVEL_ERROR, "got NULL name during scandir: %#x!", psDesc->cd_cnid); | |
109 | return false; | |
110 | } | |
111 | ||
112 | bool bAllowHiddenFiles = (psScanDirRequest->psMatchingCriteria->smr_attribute_filter->fa_validmask & LI_FA_VALID_BSD_FLAGS) && | |
113 | (psScanDirRequest->psMatchingCriteria->smr_attribute_filter->fa_bsd_flags & UF_HIDDEN); | |
114 | ||
115 | ||
116 | // filter out hidden files | |
117 | if (bAllowHiddenFiles == false) { | |
118 | // Filter out files with BSD UF_HIDDEN flag set or filenames that begins with a dot | |
119 | if ( (pAttr->ca_flags & UF_HIDDEN) || (pcName[0] == '.') ) { | |
120 | return false; | |
121 | } | |
122 | // Filter out directories and files with kFinderInvisibleMask flag set | |
123 | if ( S_ISDIR(pAttr->ca_mode)) { | |
124 | if (pAttr->ca_finderdirinfo.frFlags & OSSwapHostToBigConstInt16(kFinderInvisibleMask)) { | |
125 | return false; | |
126 | } | |
127 | } else { // file | |
128 | if (pAttr->ca_finderfileinfo.fdFlags & OSSwapHostToBigConstInt16(kFinderInvisibleMask)) { | |
129 | return false; | |
130 | } | |
131 | } | |
132 | } | |
133 | ||
134 | // If need to verify name contains | |
135 | if (psScanDirRequest->psMatchingCriteria->smr_filename_contains != NULL) | |
136 | { | |
137 | //For each name in smr_filename_contains | |
138 | bool bAtLeastOneNameContainsMatched = false; | |
139 | char** ppcNameContainsStr = psScanDirRequest->psMatchingCriteria->smr_filename_contains; | |
140 | while ( (*ppcNameContainsStr) && (strlen(*ppcNameContainsStr) != 0) && !bAtLeastOneNameContainsMatched) | |
141 | { | |
142 | uint64_t uNameContainsLength = strlen(*ppcNameContainsStr); | |
143 | if (uNameContainsLength <= strlen(pcName)) | |
144 | { | |
145 | if(!hfs_strstr((const u_int8_t*) pcName, strlen(pcName), (const u_int8_t*) *ppcNameContainsStr, uNameContainsLength)) | |
146 | { | |
147 | bAtLeastOneNameContainsMatched |= true; | |
148 | } | |
149 | } | |
150 | ppcNameContainsStr++; | |
151 | } | |
152 | bIsMatch = bAtLeastOneNameContainsMatched; | |
153 | } | |
154 | ||
155 | if (!bIsMatch) goto check_if_directory; | |
156 | ||
157 | // If need to verify name appendix | |
158 | if (psScanDirRequest->psMatchingCriteria->smr_filename_ends_with != NULL) | |
159 | { | |
160 | //For each name in smr_filename_contains | |
161 | bool bAtLeastOneNameEndWithMatched = false; | |
162 | char** ppcNameEndsWithStr = psScanDirRequest->psMatchingCriteria->smr_filename_ends_with; | |
163 | while ( (*ppcNameEndsWithStr) && (strlen(*ppcNameEndsWithStr) != 0) && !bAtLeastOneNameEndWithMatched) | |
164 | { | |
165 | uint64_t uNameEndsWithLength = strlen(*ppcNameEndsWithStr); | |
166 | if (uNameEndsWithLength <= strlen(pcName)) | |
167 | { | |
168 | if ( !hfs_apendixcmp((const u_int8_t*) pcName, strlen(pcName),(const u_int8_t*) *ppcNameEndsWithStr, uNameEndsWithLength) ) | |
169 | { | |
170 | bAtLeastOneNameEndWithMatched |= true; | |
171 | } | |
172 | } | |
173 | ppcNameEndsWithStr++; | |
174 | } | |
175 | bIsMatch = bAtLeastOneNameEndWithMatched; | |
176 | } | |
177 | ||
178 | if (!bIsMatch) goto check_if_directory; | |
179 | ||
180 | //If need to validate any other param | |
181 | if (psScanDirRequest->psMatchingCriteria->smr_attribute_filter->fa_validmask != 0) | |
182 | { | |
183 | // If need to verify the file type | |
184 | if (psScanDirRequest->psMatchingCriteria->smr_attribute_filter->fa_validmask & UVFS_FA_VALID_TYPE) | |
185 | { | |
186 | uint32_t uEntryType = VTTOUVFS(IFTOVT(pAttr->ca_mode)); | |
187 | if ((psScanDirRequest->psMatchingCriteria->smr_attribute_filter->fa_type & uEntryType) != uEntryType) | |
188 | { | |
189 | bIsMatch = false; | |
190 | } | |
191 | } | |
192 | ||
193 | if (!bIsMatch) goto check_if_directory; | |
194 | ||
195 | // If need to verify the file mTime | |
196 | if (psScanDirRequest->psMatchingCriteria->smr_attribute_filter->fa_validmask & UVFS_FA_VALID_MTIME) | |
197 | { | |
198 | //Comapre if the Mtime of the found entry is after the search Mtime | |
199 | struct timespec mTime = {0}; | |
200 | mTime.tv_sec = pAttr->ca_mtime; | |
201 | if (!CompareTimes(&mTime, &psScanDirRequest->psMatchingCriteria->smr_attribute_filter->fa_mtime)) | |
202 | { | |
203 | bIsMatch = false; | |
204 | } | |
205 | } | |
206 | } | |
207 | ||
208 | if (bIsMatch) | |
209 | { | |
210 | psScanDirRequest->psMatchingResult->smr_result_type = SEARCH_RESULT_MATCH; | |
211 | } | |
212 | ||
213 | check_if_directory: | |
214 | //In case that one of the requested creteria wasn't fullfiled we need to check if this is a folder and return | |
215 | if (VTTOUVFS(IFTOVT(pAttr->ca_mode)) == UVFS_FA_TYPE_DIR) | |
216 | { | |
217 | psScanDirRequest->psMatchingResult->smr_result_type |= SEARCH_RESULT_PUSH; | |
218 | bIsMatch = true; | |
219 | } | |
220 | ||
221 | if (bIsMatch) | |
222 | { | |
223 | UVFSDirEntryAttr* psAttrEntry = psScanDirRequest->psMatchingResult->smr_entry; | |
224 | SetAttrIntoStruct(psAttrEntry, pAttr, psDesc, psHfsm, pDataFork); | |
225 | ||
226 | psDirEntry->dea_namelen = strlen( pcName ); | |
227 | psDirEntry->dea_spare0 = 0; | |
228 | psDirEntry->dea_nameoff = UVFS_DIRENTRYATTR_NAMEOFF; | |
229 | memcpy( UVFS_DIRENTRYATTR_NAMEPTR(psDirEntry), pcName, psDesc->cd_namelen); | |
230 | UVFS_DIRENTRYATTR_NAMEPTR(psDirEntry)[psDirEntry->dea_namelen] = 0; | |
231 | } | |
232 | return bIsMatch; | |
233 | } | |
234 | ||
235 | int | |
236 | hfs_scandir(struct vnode *dvp, ScanDirRequest_s* psScanDirRequest) | |
237 | { | |
238 | int error = 0; | |
239 | struct cat_entrylist *ce_list = NULL; | |
240 | struct cnode* dcp = VTOC(dvp); | |
241 | struct hfsmount* hfsmp = VTOHFS(dvp); | |
242 | uint64_t uCookie = psScanDirRequest->psMatchingCriteria->smr_start_cookie; | |
243 | int reachedeof = 0; | |
244 | ||
245 | /* | |
246 | * Take an exclusive directory lock since we manipulate the directory hints | |
247 | */ | |
248 | if ((error = hfs_lock(dcp, HFS_EXCLUSIVE_LOCK, HFS_LOCK_DEFAULT))) | |
249 | { | |
250 | return (error); | |
251 | } | |
252 | ||
253 | /* Initialize a catalog entry list - allocating 2 for eof reference. */ | |
254 | ce_list = hfs_mallocz(CE_LIST_SIZE(2)); | |
255 | ce_list->maxentries = 2; | |
256 | ||
257 | bool bContinueIterating = true; | |
258 | while (bContinueIterating) | |
259 | { | |
260 | /* | |
261 | * Populate the ce_list from the catalog file. | |
262 | */ | |
263 | int lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG, HFS_SHARED_LOCK); | |
264 | ||
265 | /* Extract directory index and tag (sequence number) from uio_offset */ | |
266 | int index = uCookie & HFS_INDEX_MASK; | |
267 | unsigned int tag = (unsigned int) uCookie & ~HFS_INDEX_MASK; | |
268 | ||
269 | /* Get a detached directory hint (cnode must be locked exclusive) */ | |
270 | directoryhint_t* dirhint = hfs_getdirhint(dcp, ((index - 1) & HFS_INDEX_MASK) | tag, TRUE); | |
271 | ||
272 | /* Hide tag from catalog layer. */ | |
273 | dirhint->dh_index &= HFS_INDEX_MASK; | |
274 | if (dirhint->dh_index == HFS_INDEX_MASK) | |
275 | { | |
276 | dirhint->dh_index = -1; | |
277 | } | |
278 | ||
279 | error = cat_getentriesattr(hfsmp, dirhint, ce_list, &reachedeof); | |
280 | /* Don't forget to release the descriptors later! */ | |
281 | ||
282 | hfs_systemfile_unlock(hfsmp, lockflags); | |
283 | ||
284 | if (error == ENOENT) | |
285 | { | |
286 | //In case of an empty directory need to set EOF and go to exit | |
287 | //We can get ENOENT with partial results so check if really no file was found | |
288 | error = 0; | |
289 | if (ce_list->realentries == 0) | |
290 | { | |
291 | psScanDirRequest->psMatchingResult->smr_entry->dea_nextcookie = UVFS_DIRCOOKIE_EOF; | |
292 | hfs_reldirhint(dcp, dirhint); | |
293 | goto exit; | |
294 | } | |
295 | } | |
296 | else if (error) | |
297 | { | |
298 | hfs_reldirhint(dcp, dirhint); | |
299 | goto exit; | |
300 | } | |
301 | ||
302 | dcp->c_touch_acctime = true; | |
303 | ||
304 | /* | |
305 | * Check for a FS corruption in the valence. We're holding the cnode lock | |
306 | * exclusive since we need to serialize the directory hints, so if we found | |
307 | * that the valence reported 0, but we actually found some items here, then | |
308 | * silently minimally self-heal and bump the valence to 1. | |
309 | */ | |
310 | if ((dcp->c_entries == 0) && (ce_list->realentries > 0)) | |
311 | { | |
312 | dcp->c_entries++; | |
313 | dcp->c_flag |= C_MODIFIED; | |
314 | LFHFS_LOG(LEVEL_DEBUG, "hfs_scandir: repairing valence to non-zero! \n"); | |
315 | ||
316 | /* force an update on dcp while we're still holding the lock. */ | |
317 | hfs_update(dvp, 0); | |
318 | } | |
319 | ||
320 | struct cnode *cp = NULL; | |
321 | struct cat_desc* psDesc = &ce_list->entry[0].ce_desc; | |
322 | struct cat_attr* psAttr = &ce_list->entry[0].ce_attr; | |
323 | struct hfsmount* psHfsmp = VTOHFS(dvp); | |
324 | struct cat_fork sDataFork; | |
325 | ||
326 | bzero(&sDataFork, sizeof(sDataFork)); | |
327 | sDataFork.cf_size = ce_list->entry[0].ce_datasize; | |
328 | sDataFork.cf_blocks = ce_list->entry[0].ce_datablks; | |
329 | ||
330 | struct vnode *vp = hfs_chash_getvnode(hfsmp, psAttr->ca_fileid, false, false, false); | |
331 | ||
332 | if (vp != NULL) | |
333 | { | |
334 | cp = VTOC(vp); | |
335 | /* Only use cnode's decriptor for non-hardlinks */ | |
336 | if (!(cp->c_flag & C_HARDLINK) && cp->c_desc.cd_nameptr != NULL) | |
337 | psDesc = &cp->c_desc; | |
338 | psAttr = &cp->c_attr; | |
339 | if (cp->c_datafork) | |
340 | { | |
341 | sDataFork.cf_size = cp->c_datafork->ff_size; | |
342 | sDataFork.cf_blocks = cp->c_datafork->ff_blocks; | |
343 | } | |
344 | } | |
345 | ||
346 | bool bIsAMatch = DirScanIsMatch(psScanDirRequest, psDesc, psAttr, psHfsmp, &sDataFork); | |
347 | ||
348 | if (vp != NULL) | |
349 | { | |
350 | /* All done with cnode. */ | |
351 | hfs_unlock(cp); | |
352 | cp = NULL; | |
353 | ||
354 | hfs_vnop_reclaim(vp); | |
355 | } | |
356 | ||
357 | /* If we skipped catalog entries for reserved files that should | |
358 | * not be listed in namespace, update the index accordingly. | |
359 | */ | |
360 | index++; | |
361 | if (ce_list->skipentries) | |
362 | { | |
363 | index += ce_list->skipentries; | |
364 | ce_list->skipentries = 0; | |
365 | } | |
366 | ||
367 | uCookie = reachedeof ? UVFS_DIRCOOKIE_EOF : (index | ((u_int64_t)ce_list->entry[1].ce_desc.cd_cnid << 32)); | |
368 | ||
369 | if (bIsAMatch) | |
370 | { | |
371 | bContinueIterating = false; | |
372 | psScanDirRequest->psMatchingResult->smr_entry->dea_nextcookie = uCookie; | |
373 | psScanDirRequest->psMatchingResult->smr_entry->dea_nextrec = 0; | |
374 | } | |
375 | ||
376 | if (reachedeof) | |
377 | hfs_reldirhint(dcp, dirhint); | |
378 | else | |
379 | hfs_insertdirhint(dcp, dirhint); | |
380 | ||
381 | // **** check if can move to exit ******* | |
382 | /* All done with the catalog descriptors. */ | |
383 | for (uint32_t i =0; i < ce_list->realentries; i++) | |
384 | { | |
385 | cat_releasedesc(&ce_list->entry[i].ce_desc); | |
386 | } | |
387 | ce_list->realentries = 0; | |
388 | } | |
389 | ||
390 | exit: | |
391 | //Drop the directory lock | |
392 | hfs_unlock(dcp); | |
393 | dcp = NULL; | |
394 | ||
395 | if (ce_list) | |
396 | { | |
397 | for (int i = 0; i < (int)ce_list->realentries; ++i) | |
398 | { | |
399 | cat_releasedesc(&ce_list->entry[i].ce_desc); | |
400 | } | |
401 | hfs_free(ce_list); | |
402 | } | |
403 | ||
404 | return (error); | |
405 | } | |
406 | ||
407 | /* | |
408 | * Common function for both hfs_vnop_readdirattr and hfs_vnop_getattrlistbulk. | |
409 | * This either fills in a vnode_attr structure or fills in an attrbute buffer | |
410 | * Currently the difference in behaviour required for the two vnops is keyed | |
411 | * on whether the passed in vnode_attr pointer is null or not. If the pointer | |
412 | * is null we fill in buffer passed and if it is not null we fill in the fields | |
413 | * of the vnode_attr structure. | |
414 | */ | |
415 | int | |
416 | hfs_readdirattr_internal(struct vnode *dvp, ReadDirBuff_s* psReadDirBuffer, int maxcount, uint32_t *newstate, int *eofflag, int *actualcount, uint64_t uCookie) | |
417 | { | |
418 | int error = 0; | |
419 | struct cat_desc *lastdescp = NULL; | |
420 | struct cat_entrylist *ce_list = NULL; | |
421 | UVFSDirEntryAttr* psPrevAttrEntry = NULL; | |
422 | ||
423 | int reachedeof = 0; | |
424 | *(actualcount) = *(eofflag) = 0; | |
425 | ||
426 | /* | |
427 | * Take an exclusive directory lock since we manipulate the directory hints | |
428 | */ | |
429 | if ((error = hfs_lock(VTOC(dvp), HFS_EXCLUSIVE_LOCK, HFS_LOCK_DEFAULT))) | |
430 | { | |
431 | return (error); | |
432 | } | |
433 | struct cnode* dcp = VTOC(dvp); | |
434 | struct hfsmount* hfsmp = VTOHFS(dvp); | |
435 | ||
436 | u_int32_t dirchg = dcp->c_dirchangecnt; | |
437 | ||
438 | /* Extract directory index and tag (sequence number) from uio_offset */ | |
439 | int index = uCookie & HFS_INDEX_MASK; | |
440 | unsigned int tag = (unsigned int) uCookie & ~HFS_INDEX_MASK; | |
441 | ||
442 | /* Get a detached directory hint (cnode must be locked exclusive) */ | |
443 | directoryhint_t* dirhint = hfs_getdirhint(dcp, ((index - 1) & HFS_INDEX_MASK) | tag, TRUE); | |
444 | ||
445 | /* Hide tag from catalog layer. */ | |
446 | dirhint->dh_index &= HFS_INDEX_MASK; | |
447 | if (dirhint->dh_index == HFS_INDEX_MASK) | |
448 | { | |
449 | dirhint->dh_index = -1; | |
450 | } | |
451 | ||
452 | /* | |
453 | * Obtain a list of catalog entries and pack their attributes until | |
454 | * the output buffer is full or maxcount entries have been packed. | |
455 | */ | |
456 | if (maxcount < 1) | |
457 | { | |
458 | error = EINVAL; | |
459 | goto exit2; | |
460 | } | |
461 | ||
462 | /* Initialize a catalog entry list. */ | |
463 | ce_list = hfs_mallocz(CE_LIST_SIZE(maxcount)); | |
464 | ce_list->maxentries = maxcount; | |
465 | ||
466 | /* | |
467 | * Populate the ce_list from the catalog file. | |
468 | */ | |
469 | int lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG, HFS_SHARED_LOCK); | |
470 | ||
471 | error = cat_getentriesattr(hfsmp, dirhint, ce_list, &reachedeof); | |
472 | /* Don't forget to release the descriptors later! */ | |
473 | ||
474 | hfs_systemfile_unlock(hfsmp, lockflags); | |
475 | ||
476 | if ((error == ENOENT) || (reachedeof != 0)) | |
477 | { | |
478 | *(eofflag) = true; | |
479 | error = 0; | |
480 | } | |
481 | if (error) | |
482 | { | |
483 | goto exit1; | |
484 | } | |
485 | ||
486 | dcp->c_touch_acctime = true; | |
487 | ||
488 | /* | |
489 | * Check for a FS corruption in the valence. We're holding the cnode lock | |
490 | * exclusive since we need to serialize the directory hints, so if we found | |
491 | * that the valence reported 0, but we actually found some items here, then | |
492 | * silently minimally self-heal and bump the valence to 1. | |
493 | */ | |
494 | if ((dcp->c_entries == 0) && (ce_list->realentries > 0)) | |
495 | { | |
496 | dcp->c_entries++; | |
497 | dcp->c_flag |= C_MODIFIED; | |
498 | LFHFS_LOG(LEVEL_DEBUG, "hfs_readdirattr_internal: repairing valence to non-zero! \n"); | |
499 | ||
500 | /* force an update on dcp while we're still holding the lock. */ | |
501 | hfs_update(dvp, 0); | |
502 | } | |
503 | ||
504 | /* | |
505 | * Drop the directory lock | |
506 | */ | |
507 | hfs_unlock(dcp); | |
508 | dcp = NULL; | |
509 | ||
510 | /* Process the catalog entries. */ | |
511 | for (int i = 0; i < (int)ce_list->realentries; ++i) | |
512 | { | |
513 | struct cnode *cp = NULL; | |
514 | struct vnode *vp = NULL; | |
515 | struct cat_desc * cdescp; | |
516 | struct cat_attr * cattrp; | |
517 | struct cat_fork c_datafork; | |
518 | struct cat_fork c_rsrcfork; | |
519 | ||
520 | bzero(&c_datafork, sizeof(c_datafork)); | |
521 | bzero(&c_rsrcfork, sizeof(c_rsrcfork)); | |
522 | cdescp = &ce_list->entry[i].ce_desc; | |
523 | cattrp = &ce_list->entry[i].ce_attr; | |
524 | c_datafork.cf_size = ce_list->entry[i].ce_datasize; | |
525 | c_datafork.cf_blocks = ce_list->entry[i].ce_datablks; | |
526 | c_rsrcfork.cf_size = ce_list->entry[i].ce_rsrcsize; | |
527 | c_rsrcfork.cf_blocks = ce_list->entry[i].ce_rsrcblks; | |
528 | ||
529 | vp = hfs_chash_getvnode(hfsmp, cattrp->ca_fileid, false, false, false); | |
530 | ||
531 | if (vp != NULL) | |
532 | { | |
533 | cp = VTOC(vp); | |
534 | /* Only use cnode's decriptor for non-hardlinks */ | |
535 | if (!(cp->c_flag & C_HARDLINK)) | |
536 | cdescp = &cp->c_desc; | |
537 | cattrp = &cp->c_attr; | |
538 | if (cp->c_datafork) | |
539 | { | |
540 | c_datafork.cf_size = cp->c_datafork->ff_size; | |
541 | c_datafork.cf_blocks = cp->c_datafork->ff_blocks; | |
542 | } | |
543 | if (cp->c_rsrcfork) | |
544 | { | |
545 | c_rsrcfork.cf_size = cp->c_rsrcfork->ff_size; | |
546 | c_rsrcfork.cf_blocks = cp->c_rsrcfork->ff_blocks; | |
547 | } | |
548 | /* All done with cnode. */ | |
549 | hfs_unlock(cp); | |
550 | cp = NULL; | |
551 | ||
552 | hfs_vnop_reclaim(vp); | |
553 | } | |
554 | ||
555 | u_int32_t currattrbufsize; | |
556 | AddNewAttrEntry(psReadDirBuffer, hfsmp, cdescp, cattrp, &c_datafork, &currattrbufsize, index, &psPrevAttrEntry); | |
557 | //Check if there was enough space to add the new entry | |
558 | if (currattrbufsize == 0) | |
559 | { | |
560 | break; | |
561 | } | |
562 | ||
563 | /* Save the last valid catalog entry */ | |
564 | lastdescp = &ce_list->entry[i].ce_desc; | |
565 | index++; | |
566 | *actualcount += 1; | |
567 | ||
568 | /* Termination checks */ | |
569 | if ((--maxcount <= 0) || (psReadDirBuffer->uBufferResid < _UVFS_DIRENTRYATTR_RECLEN(UVFS_DIRENTRYATTR_NAMEOFF, 128))) | |
570 | { | |
571 | break; | |
572 | } | |
573 | ||
574 | } /* for each catalog entry */ | |
575 | ||
576 | /* | |
577 | * If we couldn't fit all the entries requested in the user's buffer, | |
578 | * it's not EOF. | |
579 | */ | |
580 | if (*eofflag && (*actualcount < (int)ce_list->realentries)) | |
581 | *eofflag = 0; | |
582 | ||
583 | /* If we skipped catalog entries for reserved files that should | |
584 | * not be listed in namespace, update the index accordingly. | |
585 | */ | |
586 | if (ce_list->skipentries) | |
587 | { | |
588 | index += ce_list->skipentries; | |
589 | ce_list->skipentries = 0; | |
590 | } | |
591 | ||
592 | /* | |
593 | * If there are more entries then save the last name. | |
594 | * Key this behavior based on whether or not we observed EOFFLAG. | |
595 | * | |
596 | * Do not use the valence as a way to determine if we hit EOF, since | |
597 | * it can be wrong. Use the catalog's output only. | |
598 | */ | |
599 | if ((*(eofflag) == 0) && (lastdescp != NULL)) | |
600 | { | |
601 | /* Remember last entry */ | |
602 | if ((dirhint->dh_desc.cd_flags & CD_HASBUF) && (dirhint->dh_desc.cd_nameptr != NULL)) | |
603 | { | |
604 | dirhint->dh_desc.cd_flags &= ~CD_HASBUF; | |
605 | if (dirhint->dh_desc.cd_nameptr != NULL) | |
606 | hfs_free((void *) dirhint->dh_desc.cd_nameptr); | |
607 | dirhint->dh_desc.cd_nameptr = NULL; | |
608 | } | |
609 | if (lastdescp->cd_nameptr != NULL) | |
610 | { | |
611 | dirhint->dh_desc.cd_namelen = lastdescp->cd_namelen; | |
612 | dirhint->dh_desc.cd_nameptr = hfs_malloc(sizeof(char)*lastdescp->cd_namelen); | |
613 | if (dirhint->dh_desc.cd_nameptr == NULL) | |
614 | { | |
615 | error = ENOMEM; | |
616 | goto exit2; | |
617 | } | |
618 | memcpy((void *) dirhint->dh_desc.cd_nameptr,(void *) lastdescp->cd_nameptr,lastdescp->cd_namelen); | |
619 | dirhint->dh_desc.cd_flags |= CD_HASBUF; | |
620 | } | |
621 | else | |
622 | { | |
623 | dirhint->dh_desc.cd_namelen = 0; | |
624 | dirhint->dh_desc.cd_nameptr = NULL; | |
625 | } | |
626 | dirhint->dh_index = index - 1; | |
627 | dirhint->dh_desc.cd_cnid = lastdescp->cd_cnid; | |
628 | dirhint->dh_desc.cd_hint = lastdescp->cd_hint; | |
629 | dirhint->dh_desc.cd_encoding = lastdescp->cd_encoding; | |
630 | } | |
631 | ||
632 | /* All done with the catalog descriptors. */ | |
633 | for (int i = 0; i < (int)ce_list->realentries; ++i) | |
634 | { | |
635 | cat_releasedesc(&ce_list->entry[i].ce_desc); | |
636 | } | |
637 | ce_list->realentries = 0; | |
638 | ||
639 | (void) hfs_lock(VTOC(dvp), HFS_EXCLUSIVE_LOCK, HFS_LOCK_ALLOW_NOEXISTS); | |
640 | dcp = VTOC(dvp); | |
641 | ||
642 | exit1: | |
643 | /* Pack directory index and tag into uio_offset. */ | |
644 | while (tag == 0){ | |
645 | tag = (++dcp->c_dirhinttag) << HFS_INDEX_BITS; | |
646 | } | |
647 | ||
648 | if (psPrevAttrEntry != NULL) | |
649 | { | |
650 | //Need to update next cookie in the last entry | |
651 | psPrevAttrEntry->dea_nextcookie = *eofflag ? UVFS_DIRCOOKIE_EOF : index | tag; | |
652 | // Last entry in the buffer should always have dea_nextrec = 0 | |
653 | psPrevAttrEntry->dea_nextrec = 0; | |
654 | } | |
655 | dirhint->dh_index |= tag; | |
656 | ||
657 | exit2: | |
658 | if (newstate) | |
659 | *newstate = dirchg; | |
660 | ||
661 | /* | |
662 | * Drop directory hint on error or if there are no more entries, | |
663 | * only if EOF was seen. | |
664 | */ | |
665 | if (dirhint) | |
666 | { | |
667 | if ((error != 0) || *(eofflag)) | |
668 | { | |
669 | hfs_reldirhint(dcp, dirhint); | |
670 | } | |
671 | else | |
672 | { | |
673 | hfs_insertdirhint(dcp, dirhint); | |
674 | } | |
675 | } | |
676 | ||
677 | if (ce_list) | |
678 | hfs_free(ce_list); | |
679 | ||
680 | hfs_unlock(dcp); | |
681 | return (error); | |
682 | } |