1 /* Copyright © 2017-2018 Apple Inc. All rights reserved.
6 * Created by Or Haimovich on 25/3/18.
9 #include "lf_hfs_attrlist.h"
10 #include "lf_hfs_locks.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"
18 static void SetAttrIntoStruct(UVFSDirEntryAttr
* psAttrEntry
, struct cat_attr
* pAttr
, struct cat_desc
* psDesc
, struct hfsmount
* psHfsm
, struct cat_fork
* pDataFork
)
20 psAttrEntry
->dea_attrs
.fa_validmask
= VALID_OUT_ATTR_MASK
;
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
;
32 if (VTTOUVFS(IFTOVT(pAttr
->ca_mode
)) == UVFS_FA_TYPE_DIR
)
34 //If its a directory need to add . and .. to the direntries count
35 psAttrEntry
->dea_attrs
.fa_nlink
= 2 + pAttr
->ca_entries
;
37 //If this is the root folder need to hide the journal files */
38 if (psHfsm
->jnl
&& psDesc
->cd_cnid
== kHFSRootFolderID
)
40 psAttrEntry
->dea_attrs
.fa_nlink
-= 2;
42 psAttrEntry
->dea_attrs
.fa_size
= (pAttr
->ca_entries
+ 2) * AVERAGE_HFSDIRENTRY_SIZE
;
44 psAttrEntry
->dea_attrs
.fa_nlink
= pAttr
->ca_linkcount
;
45 psAttrEntry
->dea_attrs
.fa_size
= pDataFork
->cf_size
;
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
;
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
)
57 UVFSDirEntryAttr
* psAttrEntry
= (UVFSDirEntryAttr
*) (psReadDirBuffer
->pvBuffer
+ READDIR_BUF_OFFSET(psReadDirBuffer
));
59 SetAttrIntoStruct(psAttrEntry
, pAttr
, psDesc
, psHfsm
, pDataFork
);
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;
69 *puUioSize
= psAttrEntry
->dea_nextrec
;
71 //Update prevEntry with cookie
72 if (*ppsPrevAttrEntry
!= NULL
)
74 (*ppsPrevAttrEntry
)->dea_nextcookie
= iIndex
| ((u_int64_t
)psDesc
->cd_cnid
<< 32);
77 *ppsPrevAttrEntry
= psAttrEntry
;
78 psReadDirBuffer
->uBufferResid
-= *puUioSize
;
83 static bool CompareTimes(const struct timespec
* psTimeA
, const struct timespec
* psTimeB
)
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
;
89 return psTimeA
->tv_sec
> psTimeB
->tv_sec
;
92 static bool DirScanIsMatch(ScanDirRequest_s
* psScanDirRequest
, struct cat_desc
* psDesc
, struct cat_attr
* pAttr
, struct hfsmount
* psHfsm
, struct cat_fork
* pDataFork
)
95 UVFSDirEntryAttr
* psDirEntry
= psScanDirRequest
->psMatchingResult
->smr_entry
;
96 const char* pcName
= (const char *) psDesc
->cd_nameptr
;
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
)))))
103 // Skip over special dirs
105 } else if (pcName
== NULL
)
107 //XXXab: Should not happen anymore
108 LFHFS_LOG(LEVEL_ERROR
, "got NULL name during scandir: %#x!", psDesc
->cd_cnid
);
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
);
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] == '.') ) {
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
)) {
128 if (pAttr
->ca_finderfileinfo
.fdFlags
& OSSwapHostToBigConstInt16(kFinderInvisibleMask
)) {
134 // If need to verify name contains
135 if (psScanDirRequest
->psMatchingCriteria
->smr_filename_contains
!= NULL
)
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
)
142 uint64_t uNameContainsLength
= strlen(*ppcNameContainsStr
);
143 if (uNameContainsLength
<= strlen(pcName
))
145 if(!hfs_strstr((const u_int8_t
*) pcName
, strlen(pcName
), (const u_int8_t
*) *ppcNameContainsStr
, uNameContainsLength
))
147 bAtLeastOneNameContainsMatched
|= true;
150 ppcNameContainsStr
++;
152 bIsMatch
= bAtLeastOneNameContainsMatched
;
155 if (!bIsMatch
) goto check_if_directory
;
157 // If need to verify name appendix
158 if (psScanDirRequest
->psMatchingCriteria
->smr_filename_ends_with
!= NULL
)
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
)
165 uint64_t uNameEndsWithLength
= strlen(*ppcNameEndsWithStr
);
166 if (uNameEndsWithLength
<= strlen(pcName
))
168 if ( !hfs_apendixcmp((const u_int8_t
*) pcName
, strlen(pcName
),(const u_int8_t
*) *ppcNameEndsWithStr
, uNameEndsWithLength
) )
170 bAtLeastOneNameEndWithMatched
|= true;
173 ppcNameEndsWithStr
++;
175 bIsMatch
= bAtLeastOneNameEndWithMatched
;
178 if (!bIsMatch
) goto check_if_directory
;
180 //If need to validate any other param
181 if (psScanDirRequest
->psMatchingCriteria
->smr_attribute_filter
->fa_validmask
!= 0)
183 // If need to verify the file type
184 if (psScanDirRequest
->psMatchingCriteria
->smr_attribute_filter
->fa_validmask
& UVFS_FA_VALID_TYPE
)
186 uint32_t uEntryType
= VTTOUVFS(IFTOVT(pAttr
->ca_mode
));
187 if ((psScanDirRequest
->psMatchingCriteria
->smr_attribute_filter
->fa_type
& uEntryType
) != uEntryType
)
193 if (!bIsMatch
) goto check_if_directory
;
195 // If need to verify the file mTime
196 if (psScanDirRequest
->psMatchingCriteria
->smr_attribute_filter
->fa_validmask
& UVFS_FA_VALID_MTIME
)
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
))
210 psScanDirRequest
->psMatchingResult
->smr_result_type
= SEARCH_RESULT_MATCH
;
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
)
217 psScanDirRequest
->psMatchingResult
->smr_result_type
|= SEARCH_RESULT_PUSH
;
223 UVFSDirEntryAttr
* psAttrEntry
= psScanDirRequest
->psMatchingResult
->smr_entry
;
224 SetAttrIntoStruct(psAttrEntry
, pAttr
, psDesc
, psHfsm
, pDataFork
);
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;
236 hfs_scandir(struct vnode
*dvp
, ScanDirRequest_s
* psScanDirRequest
)
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
;
246 * Take an exclusive directory lock since we manipulate the directory hints
248 if ((error
= hfs_lock(dcp
, HFS_EXCLUSIVE_LOCK
, HFS_LOCK_DEFAULT
)))
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;
257 bool bContinueIterating
= true;
258 while (bContinueIterating
)
261 * Populate the ce_list from the catalog file.
263 int lockflags
= hfs_systemfile_lock(hfsmp
, SFL_CATALOG
, HFS_SHARED_LOCK
);
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
;
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
);
272 /* Hide tag from catalog layer. */
273 dirhint
->dh_index
&= HFS_INDEX_MASK
;
274 if (dirhint
->dh_index
== HFS_INDEX_MASK
)
276 dirhint
->dh_index
= -1;
279 error
= cat_getentriesattr(hfsmp
, dirhint
, ce_list
, &reachedeof
);
280 /* Don't forget to release the descriptors later! */
282 hfs_systemfile_unlock(hfsmp
, lockflags
);
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
289 if (ce_list
->realentries
== 0)
291 psScanDirRequest
->psMatchingResult
->smr_entry
->dea_nextcookie
= UVFS_DIRCOOKIE_EOF
;
292 hfs_reldirhint(dcp
, dirhint
);
298 hfs_reldirhint(dcp
, dirhint
);
302 dcp
->c_touch_acctime
= true;
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.
310 if ((dcp
->c_entries
== 0) && (ce_list
->realentries
> 0))
313 dcp
->c_flag
|= C_MODIFIED
;
314 LFHFS_LOG(LEVEL_DEBUG
, "hfs_scandir: repairing valence to non-zero! \n");
316 /* force an update on dcp while we're still holding the lock. */
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
;
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
;
330 struct vnode
*vp
= hfs_chash_getvnode(hfsmp
, psAttr
->ca_fileid
, false, false, false);
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
;
341 sDataFork
.cf_size
= cp
->c_datafork
->ff_size
;
342 sDataFork
.cf_blocks
= cp
->c_datafork
->ff_blocks
;
346 bool bIsAMatch
= DirScanIsMatch(psScanDirRequest
, psDesc
, psAttr
, psHfsmp
, &sDataFork
);
350 /* All done with cnode. */
354 hfs_vnop_reclaim(vp
);
357 /* If we skipped catalog entries for reserved files that should
358 * not be listed in namespace, update the index accordingly.
361 if (ce_list
->skipentries
)
363 index
+= ce_list
->skipentries
;
364 ce_list
->skipentries
= 0;
367 uCookie
= reachedeof
? UVFS_DIRCOOKIE_EOF
: (index
| ((u_int64_t
)ce_list
->entry
[1].ce_desc
.cd_cnid
<< 32));
371 bContinueIterating
= false;
372 psScanDirRequest
->psMatchingResult
->smr_entry
->dea_nextcookie
= uCookie
;
373 psScanDirRequest
->psMatchingResult
->smr_entry
->dea_nextrec
= 0;
377 hfs_reldirhint(dcp
, dirhint
);
379 hfs_insertdirhint(dcp
, dirhint
);
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
++)
385 cat_releasedesc(&ce_list
->entry
[i
].ce_desc
);
387 ce_list
->realentries
= 0;
391 //Drop the directory lock
397 for (int i
= 0; i
< (int)ce_list
->realentries
; ++i
)
399 cat_releasedesc(&ce_list
->entry
[i
].ce_desc
);
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.
416 hfs_readdirattr_internal(struct vnode
*dvp
, ReadDirBuff_s
* psReadDirBuffer
, int maxcount
, uint32_t *newstate
, int *eofflag
, int *actualcount
, uint64_t uCookie
)
419 struct cat_desc
*lastdescp
= NULL
;
420 struct cat_entrylist
*ce_list
= NULL
;
421 UVFSDirEntryAttr
* psPrevAttrEntry
= NULL
;
424 *(actualcount
) = *(eofflag
) = 0;
427 * Take an exclusive directory lock since we manipulate the directory hints
429 if ((error
= hfs_lock(VTOC(dvp
), HFS_EXCLUSIVE_LOCK
, HFS_LOCK_DEFAULT
)))
433 struct cnode
* dcp
= VTOC(dvp
);
434 struct hfsmount
* hfsmp
= VTOHFS(dvp
);
436 u_int32_t dirchg
= dcp
->c_dirchangecnt
;
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
;
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
);
445 /* Hide tag from catalog layer. */
446 dirhint
->dh_index
&= HFS_INDEX_MASK
;
447 if (dirhint
->dh_index
== HFS_INDEX_MASK
)
449 dirhint
->dh_index
= -1;
453 * Obtain a list of catalog entries and pack their attributes until
454 * the output buffer is full or maxcount entries have been packed.
462 /* Initialize a catalog entry list. */
463 ce_list
= hfs_mallocz(CE_LIST_SIZE(maxcount
));
464 ce_list
->maxentries
= maxcount
;
467 * Populate the ce_list from the catalog file.
469 int lockflags
= hfs_systemfile_lock(hfsmp
, SFL_CATALOG
, HFS_SHARED_LOCK
);
471 error
= cat_getentriesattr(hfsmp
, dirhint
, ce_list
, &reachedeof
);
472 /* Don't forget to release the descriptors later! */
474 hfs_systemfile_unlock(hfsmp
, lockflags
);
476 if ((error
== ENOENT
) || (reachedeof
!= 0))
486 dcp
->c_touch_acctime
= true;
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.
494 if ((dcp
->c_entries
== 0) && (ce_list
->realentries
> 0))
497 dcp
->c_flag
|= C_MODIFIED
;
498 LFHFS_LOG(LEVEL_DEBUG
, "hfs_readdirattr_internal: repairing valence to non-zero! \n");
500 /* force an update on dcp while we're still holding the lock. */
505 * Drop the directory lock
510 /* Process the catalog entries. */
511 for (int i
= 0; i
< (int)ce_list
->realentries
; ++i
)
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
;
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
;
529 vp
= hfs_chash_getvnode(hfsmp
, cattrp
->ca_fileid
, false, false, false);
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
;
540 c_datafork
.cf_size
= cp
->c_datafork
->ff_size
;
541 c_datafork
.cf_blocks
= cp
->c_datafork
->ff_blocks
;
545 c_rsrcfork
.cf_size
= cp
->c_rsrcfork
->ff_size
;
546 c_rsrcfork
.cf_blocks
= cp
->c_rsrcfork
->ff_blocks
;
548 /* All done with cnode. */
552 hfs_vnop_reclaim(vp
);
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)
563 /* Save the last valid catalog entry */
564 lastdescp
= &ce_list
->entry
[i
].ce_desc
;
568 /* Termination checks */
569 if ((--maxcount
<= 0) || (psReadDirBuffer
->uBufferResid
< _UVFS_DIRENTRYATTR_RECLEN(UVFS_DIRENTRYATTR_NAMEOFF
, 128)))
574 } /* for each catalog entry */
577 * If we couldn't fit all the entries requested in the user's buffer,
580 if (*eofflag
&& (*actualcount
< (int)ce_list
->realentries
))
583 /* If we skipped catalog entries for reserved files that should
584 * not be listed in namespace, update the index accordingly.
586 if (ce_list
->skipentries
)
588 index
+= ce_list
->skipentries
;
589 ce_list
->skipentries
= 0;
593 * If there are more entries then save the last name.
594 * Key this behavior based on whether or not we observed EOFFLAG.
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.
599 if ((*(eofflag
) == 0) && (lastdescp
!= NULL
))
601 /* Remember last entry */
602 if ((dirhint
->dh_desc
.cd_flags
& CD_HASBUF
) && (dirhint
->dh_desc
.cd_nameptr
!= NULL
))
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
;
609 if (lastdescp
->cd_nameptr
!= NULL
)
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
)
618 memcpy((void *) dirhint
->dh_desc
.cd_nameptr
,(void *) lastdescp
->cd_nameptr
,lastdescp
->cd_namelen
);
619 dirhint
->dh_desc
.cd_flags
|= CD_HASBUF
;
623 dirhint
->dh_desc
.cd_namelen
= 0;
624 dirhint
->dh_desc
.cd_nameptr
= NULL
;
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
;
632 /* All done with the catalog descriptors. */
633 for (int i
= 0; i
< (int)ce_list
->realentries
; ++i
)
635 cat_releasedesc(&ce_list
->entry
[i
].ce_desc
);
637 ce_list
->realentries
= 0;
639 (void) hfs_lock(VTOC(dvp
), HFS_EXCLUSIVE_LOCK
, HFS_LOCK_ALLOW_NOEXISTS
);
643 /* Pack directory index and tag into uio_offset. */
645 tag
= (++dcp
->c_dirhinttag
) << HFS_INDEX_BITS
;
648 if (psPrevAttrEntry
!= NULL
)
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;
655 dirhint
->dh_index
|= tag
;
662 * Drop directory hint on error or if there are no more entries,
663 * only if EOF was seen.
667 if ((error
!= 0) || *(eofflag
))
669 hfs_reldirhint(dcp
, dirhint
);
673 hfs_insertdirhint(dcp
, dirhint
);