From 7fb95a2a569ca6d68dad4cef5b7b299e9fe68e2b Mon Sep 17 00:00:00 2001 From: Ron Yorston Date: Sun, 15 May 2022 08:52:26 +0100 Subject: win32: try to get link count for directories On Unix the link count of a directory reflects the number of subdirectories it contains. Enhance readdir(3) to return file types and use this to count subdirectories when stat(2) is called for a directory. As with other features that might slow down stat(2) this is controlled by the build-time setting FEATURE_EXTRA_FILE_DATA. (Commit d82db8e9a 'win32: make stat(2) fetch additional metadata'). (GitHub issue #254) --- win32/dirent.c | 11 +++++++++++ win32/dirent.h | 3 +++ win32/mingw.c | 23 ++++++++++++++++++++++- 3 files changed, 36 insertions(+), 1 deletion(-) diff --git a/win32/dirent.c b/win32/dirent.c index b7de19391..b5b379209 100644 --- a/win32/dirent.c +++ b/win32/dirent.c @@ -10,6 +10,17 @@ static inline void finddata2dirent(struct dirent *ent, WIN32_FIND_DATAA *fdata) { /* copy file name from WIN32_FIND_DATA to dirent */ strcpy(ent->d_name, fdata->cFileName); + +#if ENABLE_FEATURE_EXTRA_FILE_DATA + if ((fdata->dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) && + (fdata->dwReserved0 == IO_REPARSE_TAG_SYMLINK || + fdata->dwReserved0 == IO_REPARSE_TAG_MOUNT_POINT)) + ent->d_type = DT_LNK; + else if (fdata->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) + ent->d_type = DT_DIR; + else + ent->d_type = DT_REG; +#endif } DIR *opendir(const char *name) diff --git a/win32/dirent.h b/win32/dirent.h index b38d0d133..214f80547 100644 --- a/win32/dirent.h +++ b/win32/dirent.h @@ -9,6 +9,9 @@ typedef struct DIR DIR; #define DT_LNK 3 struct dirent { +#if ENABLE_FEATURE_EXTRA_FILE_DATA + unsigned char d_type; +#endif char d_name[PATH_MAX]; // file name }; diff --git a/win32/mingw.c b/win32/mingw.c index 539aa48d2..c35b1d6af 100644 --- a/win32/mingw.c +++ b/win32/mingw.c @@ -588,6 +588,26 @@ static int is_symlink(const char *pathname) return 0; } +#if ENABLE_FEATURE_EXTRA_FILE_DATA +static int count_subdirs(const char *pathname) +{ + int count = 0; + DIR *dirp = opendir(pathname); + struct dirent *dp; + + if (dirp) { + while ((dp = readdir(dirp)) != NULL) { + if (dp->d_type == DT_DIR) + count++; + } + closedir(dirp); + } else { + count = 2; + } + return count; +} +#endif + /* If follow is true then act like stat() and report on the link * target. Otherwise report on the link itself. */ @@ -656,7 +676,8 @@ static int do_lstat(int follow, const char *file_name, struct mingw_stat *buf) buf->st_dev = hdata.dwVolumeSerialNumber; buf->st_ino = hdata.nFileIndexLow | (((ino_t)hdata.nFileIndexHigh)<<32); - buf->st_nlink = S_ISDIR(buf->st_mode) ? 2 : + buf->st_nlink = S_ISDIR(buf->st_mode) ? + count_subdirs(file_name) : hdata.nNumberOfLinks; } buf->st_uid = buf->st_gid = file_owner(fh); -- cgit v1.2.3-55-g6feb