]> git.saurik.com Git - apple/hfs.git/blob - livefiles_hfs_plugin/lf_hfs_attrlist.c
hfs-522.0.9.tar.gz
[apple/hfs.git] / livefiles_hfs_plugin / lf_hfs_attrlist.c
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 }