Fix incorrect st_nlink inheritance from directory in Windows stream scanner

This commit is contained in:
Andrea Mazzoleni 2026-09-07 15:31:39 +02:00
parent c9fbc058ef
commit b113accaad
3 changed files with 18 additions and 8 deletions

View file

@ -1577,10 +1577,13 @@ static int scan_sub(struct snapraid_scan* scan, int level, int is_diff, char* pa
#if HAVE_LSTAT_SYNC
/*
* If the st_ino field is missing, takes care to fill it using the extended lstat()
* this can happen only in Windows
* In Windows fast directory enumeration, st_ino and st_nlink may be
* missing (0). If a field is needed, lstat_sync() must be called to
* retrieve it. Here only st_ino is required for file identity and
* hardlink tracking, while st_nlink is not; we call lstat_sync() only
* when st_ino is missing to avoid expensive file opens.
*/
if (st->st_ino == INODE_INVALID || st->st_nlink == 0) {
if (st->st_ino == INODE_INVALID) {
if (lstat_sync(path_next, st, 0) != 0) {
/* LCOV_EXCL_START */
log_tag("%s:%u:%s:%s: Stat error. %s.\n", es(errno), 0, disk->name, esc_tag(path_next), strerror(errno));

View file

@ -547,7 +547,12 @@ static int windows_stream2stat(const BY_HANDLE_FILE_INFORMATION* info, const FIL
st->st_ino = stream->FileId.QuadPart;
st->st_nlink = info->nNumberOfLinks;
/*
* FILE_ID_BOTH_DIR_INFO doesn't provide the hardlink count of the entry.
* The BY_HANDLE_FILE_INFORMATION refers to the directory itself, so its
* nNumberOfLinks cannot be used for the child.
*/
st->st_nlink = 0;
st->st_dev = info->dwVolumeSerialNumber;

View file

@ -409,11 +409,13 @@ int windows_closedir(windows_dir* dirstream);
* Specifically this happens if we are using GetFileInformationByHandleEx()
* to read the directory stream.
*
* The st_ino field may be 0 if it's not possible to read it in a fast way.
* Specifically this happens if we are using FindFirst/FindNext to enumerate
* the directory.
* The st_ino and st_nlink fields may be 0 if the fast directory enumeration
* API does not provide them. Specifically, st_ino is 0 with FindFirst/FindNext,
* while st_nlink is 0 with both directory streams and FindFirst/FindNext.
*
* In such cases, call lstat_sync() to fill the missing fields.
* If a missing field is actually needed by the caller, call lstat_sync()
* to retrieve it from the file handle. Otherwise, avoid calling lstat_sync()
* to preserve fast directory scanning performance.
*/
void windows_dirent_lstat(const struct windows_dirent* dd, struct windows_stat* st);