diff --git a/cmdline/scan.c b/cmdline/scan.c index 4de55a5d..e27f8026 100644 --- a/cmdline/scan.c +++ b/cmdline/scan.c @@ -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)); diff --git a/os/mingw.c b/os/mingw.c index 30ad2f34..c28a19fa 100644 --- a/os/mingw.c +++ b/os/mingw.c @@ -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; diff --git a/os/mingw.h b/os/mingw.h index f90861d1..2e128ead 100644 --- a/os/mingw.h +++ b/os/mingw.h @@ -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);