summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBruno Haible <bruno@clisp.org>2017-05-14 14:20:01 +0200
committerBruno Haible <bruno@clisp.org>2017-05-14 17:38:24 +0200
commit8123b614e616eaa951d842f10730ba7b914f75b3 (patch)
tree73e656f9915d2cf1a033e90641ef136d9420f273
parentd75a6cfc4125de5b0ed14ca7d08e93ca98d5b16c (diff)
downloadgnulib-8123b614e616eaa951d842f10730ba7b914f75b3.tar.gz
windows-stat-inodes: New module.
* m4/windows-stat-inodes.m4: New file. * m4/sys_types_h.m4 (gl_SYS_TYPES_H): Set WINDOWS_STAT_INODES. * modules/sys_types (Makefile.am): Substitute WINDOWS_STAT_INODES. * lib/sys_types.in.h [WINDOWS_STAT_INODES]: Override dev_t and ino_t. (_GL_WINDOWS_STAT_INODES): New macro. * lib/stat-w32.c: Set _WIN32_WINNT. Include <string.h>, verify.h. (GetFileInformationByHandleExFunc): New variable. (initialize): Initialize it. (_gl_fstat_by_handle) [_GL_WINDOWS_STAT_INODES]: Initialize st_dev and st_ino appropriately. * lib/stat.c (rpl_stat): Use the directory entry based approach only as a fallback, because it does not provide st_dev and st_ino values. * modules/fstat (Depends-on): Add 'verify'. * modules/windows-stat-inodes: New file. * doc/windows-stat-inodes.texi: New file. * doc/gnulib.texi: Include it. * doc/posix-headers/sys_stat.texi: Mention the new module.
-rw-r--r--ChangeLog21
-rw-r--r--doc/gnulib.texi3
-rw-r--r--doc/posix-headers/sys_stat.texi10
-rw-r--r--doc/windows-stat-inodes.texi14
-rw-r--r--lib/stat-w32.c85
-rw-r--r--lib/stat.c266
-rw-r--r--lib/sys_types.in.h42
-rw-r--r--m4/sys_types_h.m410
-rw-r--r--m4/windows-stat-inodes.m419
-rw-r--r--modules/fstat1
-rw-r--r--modules/sys_types1
-rw-r--r--modules/windows-stat-inodes27
12 files changed, 374 insertions, 125 deletions
diff --git a/ChangeLog b/ChangeLog
index 3e78f773fc..488d25894c 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,26 @@
2017-05-14 Bruno Haible <bruno@clisp.org>
+ windows-stat-inodes: New module.
+ * m4/windows-stat-inodes.m4: New file.
+ * m4/sys_types_h.m4 (gl_SYS_TYPES_H): Set WINDOWS_STAT_INODES.
+ * modules/sys_types (Makefile.am): Substitute WINDOWS_STAT_INODES.
+ * lib/sys_types.in.h [WINDOWS_STAT_INODES]: Override dev_t and ino_t.
+ (_GL_WINDOWS_STAT_INODES): New macro.
+ * lib/stat-w32.c: Set _WIN32_WINNT. Include <string.h>, verify.h.
+ (GetFileInformationByHandleExFunc): New variable.
+ (initialize): Initialize it.
+ (_gl_fstat_by_handle) [_GL_WINDOWS_STAT_INODES]: Initialize st_dev and
+ st_ino appropriately.
+ * lib/stat.c (rpl_stat): Use the directory entry based approach only as
+ a fallback, because it does not provide st_dev and st_ino values.
+ * modules/fstat (Depends-on): Add 'verify'.
+ * modules/windows-stat-inodes: New file.
+ * doc/windows-stat-inodes.texi: New file.
+ * doc/gnulib.texi: Include it.
+ * doc/posix-headers/sys_stat.texi: Mention the new module.
+
+2017-05-14 Bruno Haible <bruno@clisp.org>
+
stat-time tests: Workaround for native Windows.
* tests/test-stat-time.c: Include <stdio.h>, <time.h>.
(filename_stamp1, filename_testfile, filename_stamp2, filename_stamp3):
diff --git a/doc/gnulib.texi b/doc/gnulib.texi
index 5cadf468ea..3eb7a1db8b 100644
--- a/doc/gnulib.texi
+++ b/doc/gnulib.texi
@@ -6324,6 +6324,7 @@ to POSIX that it can be treated like any other Unix-like platform.
@menu
* Libtool and Windows::
* Large File Support::
+* Inode numbers on Windows::
* Precise file timestamps on Windows::
* Avoiding the year 2038 problem::
* Windows sockets::
@@ -6334,6 +6335,8 @@ to POSIX that it can be treated like any other Unix-like platform.
@include largefile.texi
+@include windows-stat-inodes.texi
+
@include windows-stat-timespec.texi
@include year2038.texi
diff --git a/doc/posix-headers/sys_stat.texi b/doc/posix-headers/sys_stat.texi
index 4c176aa7cd..42a27ba0d5 100644
--- a/doc/posix-headers/sys_stat.texi
+++ b/doc/posix-headers/sys_stat.texi
@@ -5,7 +5,7 @@ POSIX specification:@* @url{http://www.opengroup.org/onlinepubs/9699919799/based
Gnulib module: sys_stat
-Portability problems fixed by Gnulib:
+Portability problems fixed by Gnulib module @code{sys_stat}:
@itemize
@item
The type @code{mode_t} is not defined on some platforms:
@@ -31,14 +31,18 @@ On some platforms, @code{struct stat} does not include @code{st_atim},
@samp{stat-time} for accessors to portably get at subsecond resolution.
@end itemize
+Portability problems fixed by Gnulib module @code{sys_stat}, together with module @code{windows-stat-inodes}:
+@itemize
+@item
+On Windows platforms (excluding Cygwin), @code{st_ino} is always 0.
+@end itemize
+
Portability problems not fixed by Gnulib:
@itemize
@item
The macro @code{S_IFBLK} is missing on some platforms:
MSVC 9.
@item
-On Windows platforms (excluding Cygwin), @code{st_ino} is always 0.
-@item
On OpenVMS, @code{st_ino} is an array of three @code{ino_t} values,
not a single value.
@item
diff --git a/doc/windows-stat-inodes.texi b/doc/windows-stat-inodes.texi
new file mode 100644
index 0000000000..f3c05815e9
--- /dev/null
+++ b/doc/windows-stat-inodes.texi
@@ -0,0 +1,14 @@
+@node Inode numbers on Windows
+@section Inode numbers on Windows
+
+The module @samp{windows-stat-inodes} ensures that,
+on native Windows platforms, @code{struct stat} contains
+@code{st_dev}, @code{st_ino} fields that are able to distinguish
+different inodes.
+
+Note: Such values can only be provided for most files on the
+file system. For a few files (such as inaccessible files),
+@code{st_dev} and @code{st_ino} are set to 0. Therefore,
+you should test whether @code{st_dev != 0 && st_ino != 0},
+before going to make inferences based on the file identity
+based on @code{st_dev} and @code{st_ino}.
diff --git a/lib/stat-w32.c b/lib/stat-w32.c
index 515311d834..b4c762c315 100644
--- a/lib/stat-w32.c
+++ b/lib/stat-w32.c
@@ -20,10 +20,15 @@
#if (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__
+/* Ensure that <windows.h> defines FILE_ID_INFO. */
+#undef _WIN32_WINNT
+#define _WIN32_WINNT _WIN32_WINNT_WIN8
+
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <limits.h>
+#include <string.h>
#include <unistd.h>
#include <windows.h>
@@ -31,7 +36,16 @@
#include "stat-w32.h"
#include "pathmax.h"
+#include "verify.h"
+#if _GL_WINDOWS_STAT_INODES == 2
+/* GetFileInformationByHandleEx was introduced only in Windows Vista. */
+typedef DWORD (WINAPI * GetFileInformationByHandleExFuncType) (HANDLE hFile,
+ FILE_INFO_BY_HANDLE_CLASS fiClass,
+ LPVOID lpBuffer,
+ DWORD dwBufferSize);
+static GetFileInformationByHandleExFuncType GetFileInformationByHandleExFunc = NULL;
+#endif
/* GetFinalPathNameByHandle was introduced only in Windows Vista. */
typedef DWORD (WINAPI * GetFinalPathNameByHandleFuncType) (HANDLE hFile,
LPTSTR lpFilePath,
@@ -46,8 +60,12 @@ initialize (void)
HMODULE kernel32 = LoadLibrary ("kernel32.dll");
if (kernel32 != NULL)
{
+#if _GL_WINDOWS_STAT_INODES == 2
+ GetFileInformationByHandleExFunc =
+ (GetFileInformationByHandleExFuncType) GetProcAddress (kernel32, "GetFileInformationByHandleEx");
+#endif
GetFinalPathNameByHandleFunc =
- (GetFinalPathNameByHandleFuncType) GetProcAddress (kernel32, "GetFinalPathNameByHandleA");
+ (GetFinalPathNameByHandleFuncType) GetProcAddress (kernel32, "GetFinalPathNameByHandleA");
}
initialized = TRUE;
}
@@ -137,10 +155,71 @@ _gl_fstat_by_handle (HANDLE h, const char *path, struct stat *buf)
return -1;
}
+#if _GL_WINDOWS_STAT_INODES
+ /* st_ino can be determined through
+ GetFileInformationByHandle
+ <https://msdn.microsoft.com/en-us/library/aa364952.aspx>
+ <https://msdn.microsoft.com/en-us/library/aa363788.aspx>
+ as 64 bits, or through
+ GetFileInformationByHandleEx with argument FileIdInfo
+ <https://msdn.microsoft.com/en-us/library/aa364953.aspx>
+ <https://msdn.microsoft.com/en-us/library/hh802691.aspx>
+ as 128 bits.
+ The latter requires -D_WIN32_WINNT=_WIN32_WINNT_WIN8 or higher. */
+ /* Experiments show that GetFileInformationByHandleEx does not provide
+ much more information than GetFileInformationByHandle:
+ * The dwVolumeSerialNumber from GetFileInformationByHandle is equal
+ to the low 32 bits of the 64-bit VolumeSerialNumber from
+ GetFileInformationByHandleEx, and is apparently sufficient for
+ identifying the device.
+ * The nFileIndex from GetFileInformationByHandle is equal to the low
+ 64 bits of the 128-bit FileId from GetFileInformationByHandleEx,
+ and the high 64 bits of this 128-bit FileId are zero.
+ * On a FAT file system, GetFileInformationByHandleEx fails with error
+ ERROR_INVALID_PARAMETER, whereas GetFileInformationByHandle
+ succeeds.
+ * On a CIFS/SMB file system, GetFileInformationByHandleEx fails with
+ error ERROR_INVALID_LEVEL, whereas GetFileInformationByHandle
+ succeeds. */
+# if _GL_WINDOWS_STAT_INODES == 2
+ if (GetFileInformationByHandleExFunc != NULL)
+ {
+ FILE_ID_INFO id;
+ if (GetFileInformationByHandleExFunc (h, FileIdInfo, &id, sizeof (id)))
+ {
+ buf->st_dev = id.VolumeSerialNumber;
+ verify (sizeof (ino_t) == sizeof (id.FileId));
+ memcpy (&buf->st_ino, &id.FileId, sizeof (ino_t));
+ goto ino_done;
+ }
+ else
+ {
+ switch (GetLastError ())
+ {
+ case ERROR_INVALID_PARAMETER: /* older Windows version, or FAT */
+ case ERROR_INVALID_LEVEL: /* CIFS/SMB file system */
+ goto fallback;
+ default:
+ goto failed;
+ }
+ }
+ }
+ fallback: ;
+ /* Fallback for older Windows versions. */
+ buf->st_dev = info.dwVolumeSerialNumber;
+ buf->st_ino._gl_ino[0] = ((ULONGLONG) info.nFileIndexHigh << 32) | (ULONGLONG) info.nFileIndexLow;
+ buf->st_ino._gl_ino[1] = 0;
+ ino_done: ;
+# else /* _GL_WINDOWS_STAT_INODES == 1 */
+ buf->st_dev = info.dwVolumeSerialNumber;
+ buf->st_ino = ((ULONGLONG) info.nFileIndexHigh << 32) | (ULONGLONG) info.nFileIndexLow;
+# endif
+#else
/* st_ino is not wide enough for identifying a file on a device.
Without st_ino, st_dev is pointless. */
buf->st_dev = 0;
buf->st_ino = 0;
+#endif
/* st_mode. */
unsigned int mode =
@@ -263,7 +342,11 @@ _gl_fstat_by_handle (HANDLE h, const char *path, struct stat *buf)
else if (type == FILE_TYPE_CHAR || type == FILE_TYPE_PIPE)
{
buf->st_dev = 0;
+#if _GL_WINDOWS_STAT_INODES == 2
+ buf->st_ino._gl_ino[0] = buf->st_ino._gl_ino[1] = 0;
+#else
buf->st_ino = 0;
+#endif
buf->st_mode = (type == FILE_TYPE_PIPE ? _S_IFIFO : _S_IFCHR);
buf->st_nlink = 1;
buf->st_uid = 0;
diff --git a/lib/stat.c b/lib/stat.c
index 199e216cc5..a73a3445cd 100644
--- a/lib/stat.c
+++ b/lib/stat.c
@@ -176,134 +176,160 @@ rpl_stat (char const *name, struct stat *buf)
UNC root directories (e.g. '\\server\share').
The second approach fails for some system files (e.g. 'C:\pagefile.sys'
and 'C:\hiberfil.sys'): ERROR_SHARING_VIOLATION.
- So we use the first approach for nearly all files, and the second one
- only for root and UNC root directories. */
+ The second approach gives more information (in particular, correct
+ st_dev, st_ino, st_nlink fields).
+ So we use the second approach and, as a fallback except for root and
+ UNC root directories, also the first approach. */
{
int ret;
- if (!((rlen == drive_prefix_len + 1 && ISSLASH (rname[drive_prefix_len]))
- || is_unc_root (rname)))
- {
- /* Approach based on the directory entry. */
-
- if (strchr (rname, '?') != NULL || strchr (rname, '*') != NULL)
- {
- /* Other Windows API functions would fail with error
- ERROR_INVALID_NAME. */
- if (malloca_rname != NULL)
- freea (malloca_rname);
- errno = ENOENT;
- return -1;
- }
-
- /* Get the details about the directory entry. */
- WIN32_FIND_DATA info;
- HANDLE h = FindFirstFile (rname, &info);
- if (h == INVALID_HANDLE_VALUE)
- goto failed;
-
- /* Test for error conditions before starting to fill *buf. */
- if (sizeof (buf->st_size) <= 4 && info.nFileSizeHigh > 0)
- {
- FindClose (h);
- if (malloca_rname != NULL)
- freea (malloca_rname);
- errno = EOVERFLOW;
- return -1;
- }
-
- /* st_ino is not wide enough for identifying a file on a device.
- Without st_ino, st_dev is pointless. */
- buf->st_dev = 0;
- buf->st_ino = 0;
-
- /* st_mode. */
- unsigned int mode =
- /* XXX How to handle FILE_ATTRIBUTE_REPARSE_POINT ? */
- ((info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) ? _S_IFDIR | S_IEXEC_UGO : _S_IFREG)
- | S_IREAD_UGO
- | ((info.dwFileAttributes & FILE_ATTRIBUTE_READONLY) ? 0 : S_IWRITE_UGO);
- if (!(info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
- {
- /* Determine whether the file is executable by looking at the file
- name suffix. */
- if (info.nFileSizeHigh > 0 || info.nFileSizeLow > 0)
- {
- const char *last_dot = NULL;
- const char *p;
- for (p = info.cFileName; *p != '\0'; p++)
- if (*p == '.')
- last_dot = p;
- if (last_dot != NULL)
- {
- const char *suffix = last_dot + 1;
- if (_stricmp (suffix, "exe") == 0
- || _stricmp (suffix, "bat") == 0
- || _stricmp (suffix, "cmd") == 0
- || _stricmp (suffix, "com") == 0)
- mode |= S_IEXEC_UGO;
- }
- }
- }
- buf->st_mode = mode;
-
- /* st_nlink. Ignore hard links here. */
- buf->st_nlink = 1;
-
- /* There's no easy way to map the Windows SID concept to an integer. */
- buf->st_uid = 0;
- buf->st_gid = 0;
-
- /* st_rdev is irrelevant for normal files and directories. */
- buf->st_rdev = 0;
-
- /* st_size. */
- if (sizeof (buf->st_size) <= 4)
- /* Range check already done above. */
- buf->st_size = info.nFileSizeLow;
- else
- buf->st_size = ((long long) info.nFileSizeHigh << 32) | (long long) info.nFileSizeLow;
-
- /* st_atime, st_mtime, st_ctime. */
+
+ {
+ /* Approach based on the file. */
+
+ /* Open a handle to the file.
+ CreateFile
+ <https://msdn.microsoft.com/en-us/library/aa363858.aspx>
+ <https://msdn.microsoft.com/en-us/library/aa363874.aspx> */
+ HANDLE h =
+ CreateFile (rname,
+ FILE_READ_ATTRIBUTES,
+ FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+ NULL,
+ OPEN_EXISTING,
+ /* FILE_FLAG_POSIX_SEMANTICS (treat file names that differ only
+ in case as different) makes sense only when applied to *all*
+ filesystem operations. */
+ FILE_FLAG_BACKUP_SEMANTICS /* | FILE_FLAG_POSIX_SEMANTICS */,
+ NULL);
+ if (h != INVALID_HANDLE_VALUE)
+ {
+ ret = _gl_fstat_by_handle (h, rname, buf);
+ CloseHandle (h);
+ goto done;
+ }
+ }
+
+ /* Test for root and UNC root directories. */
+ if ((rlen == drive_prefix_len + 1 && ISSLASH (rname[drive_prefix_len]))
+ || is_unc_root (rname))
+ goto failed;
+
+ /* Fallback. */
+ {
+ /* Approach based on the directory entry. */
+
+ if (strchr (rname, '?') != NULL || strchr (rname, '*') != NULL)
+ {
+ /* Other Windows API functions would fail with error
+ ERROR_INVALID_NAME. */
+ if (malloca_rname != NULL)
+ freea (malloca_rname);
+ errno = ENOENT;
+ return -1;
+ }
+
+ /* Get the details about the directory entry. This can be done through
+ FindFirstFile
+ <https://msdn.microsoft.com/en-us/library/aa364418.aspx>
+ <https://msdn.microsoft.com/en-us/library/aa365740.aspx>
+ or through
+ FindFirstFileEx with argument FindExInfoBasic
+ <https://msdn.microsoft.com/en-us/library/aa364419.aspx>
+ <https://msdn.microsoft.com/en-us/library/aa364415.aspx>
+ <https://msdn.microsoft.com/en-us/library/aa365740.aspx> */
+ WIN32_FIND_DATA info;
+ HANDLE h = FindFirstFile (rname, &info);
+ if (h == INVALID_HANDLE_VALUE)
+ goto failed;
+
+ /* Test for error conditions before starting to fill *buf. */
+ if (sizeof (buf->st_size) <= 4 && info.nFileSizeHigh > 0)
+ {
+ FindClose (h);
+ if (malloca_rname != NULL)
+ freea (malloca_rname);
+ errno = EOVERFLOW;
+ return -1;
+ }
+
+# if _GL_WINDOWS_STAT_INODES
+ buf->st_dev = 0;
+# if _GL_WINDOWS_STAT_INODES == 2
+ buf->st_ino._gl_ino[0] = buf->st_ino._gl_ino[1] = 0;
+# else /* _GL_WINDOWS_STAT_INODES == 1 */
+ buf->st_ino = 0;
+# endif
+# else
+ /* st_ino is not wide enough for identifying a file on a device.
+ Without st_ino, st_dev is pointless. */
+ buf->st_dev = 0;
+ buf->st_ino = 0;
+# endif
+
+ /* st_mode. */
+ unsigned int mode =
+ /* XXX How to handle FILE_ATTRIBUTE_REPARSE_POINT ? */
+ ((info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) ? _S_IFDIR | S_IEXEC_UGO : _S_IFREG)
+ | S_IREAD_UGO
+ | ((info.dwFileAttributes & FILE_ATTRIBUTE_READONLY) ? 0 : S_IWRITE_UGO);
+ if (!(info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
+ {
+ /* Determine whether the file is executable by looking at the file
+ name suffix. */
+ if (info.nFileSizeHigh > 0 || info.nFileSizeLow > 0)
+ {
+ const char *last_dot = NULL;
+ const char *p;
+ for (p = info.cFileName; *p != '\0'; p++)
+ if (*p == '.')
+ last_dot = p;
+ if (last_dot != NULL)
+ {
+ const char *suffix = last_dot + 1;
+ if (_stricmp (suffix, "exe") == 0
+ || _stricmp (suffix, "bat") == 0
+ || _stricmp (suffix, "cmd") == 0
+ || _stricmp (suffix, "com") == 0)
+ mode |= S_IEXEC_UGO;
+ }
+ }
+ }
+ buf->st_mode = mode;
+
+ /* st_nlink. Ignore hard links here. */
+ buf->st_nlink = 1;
+
+ /* There's no easy way to map the Windows SID concept to an integer. */
+ buf->st_uid = 0;
+ buf->st_gid = 0;
+
+ /* st_rdev is irrelevant for normal files and directories. */
+ buf->st_rdev = 0;
+
+ /* st_size. */
+ if (sizeof (buf->st_size) <= 4)
+ /* Range check already done above. */
+ buf->st_size = info.nFileSizeLow;
+ else
+ buf->st_size = ((long long) info.nFileSizeHigh << 32) | (long long) info.nFileSizeLow;
+
+ /* st_atime, st_mtime, st_ctime. */
# if _GL_WINDOWS_STAT_TIMESPEC
- buf->st_atim = _gl_convert_FILETIME_to_timespec (&info.ftLastAccessTime);
- buf->st_mtim = _gl_convert_FILETIME_to_timespec (&info.ftLastWriteTime);
- buf->st_ctim = _gl_convert_FILETIME_to_timespec (&info.ftCreationTime);
+ buf->st_atim = _gl_convert_FILETIME_to_timespec (&info.ftLastAccessTime);
+ buf->st_mtim = _gl_convert_FILETIME_to_timespec (&info.ftLastWriteTime);
+ buf->st_ctim = _gl_convert_FILETIME_to_timespec (&info.ftCreationTime);
# else
- buf->st_atime = _gl_convert_FILETIME_to_POSIX (&info.ftLastAccessTime);
- buf->st_mtime = _gl_convert_FILETIME_to_POSIX (&info.ftLastWriteTime);
- buf->st_ctime = _gl_convert_FILETIME_to_POSIX (&info.ftCreationTime);
+ buf->st_atime = _gl_convert_FILETIME_to_POSIX (&info.ftLastAccessTime);
+ buf->st_mtime = _gl_convert_FILETIME_to_POSIX (&info.ftLastWriteTime);
+ buf->st_ctime = _gl_convert_FILETIME_to_POSIX (&info.ftCreationTime);
# endif
- FindClose (h);
+ FindClose (h);
- ret = 0;
- }
- else
- {
- /* Approach based on the file. */
-
- /* Open a handle to the file.
- CreateFile
- <https://msdn.microsoft.com/en-us/library/aa363858.aspx>
- <https://msdn.microsoft.com/en-us/library/aa363874.aspx> */
- HANDLE h =
- CreateFile (rname,
- FILE_READ_ATTRIBUTES,
- FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
- NULL,
- OPEN_EXISTING,
- /* FILE_FLAG_POSIX_SEMANTICS (treat file names that differ only
- in case as different) makes sense only when applied to *all*
- filesystem operations. */
- FILE_FLAG_BACKUP_SEMANTICS /* | FILE_FLAG_POSIX_SEMANTICS */,
- NULL);
- if (h == INVALID_HANDLE_VALUE)
- goto failed;
-
- ret = _gl_fstat_by_handle (h, rname, buf);
- CloseHandle (h);
- }
+ ret = 0;
+ }
+ done:
if (ret >= 0 && check_dir && !S_ISDIR (buf->st_mode))
{
errno = ENOTDIR;
diff --git a/lib/sys_types.in.h b/lib/sys_types.in.h
index c5fea83a01..fd11fdc098 100644
--- a/lib/sys_types.in.h
+++ b/lib/sys_types.in.h
@@ -42,6 +42,48 @@
# define _GL_WINDOWS_64_BIT_OFF_T 1
#endif
+/* Override dev_t and ino_t if distinguishable inodes support is requested
+ on native Windows. */
+#if @WINDOWS_STAT_INODES@
+
+# if @WINDOWS_STAT_INODES@ == 2
+/* Experimental, not useful in Windows 10. */
+
+/* Define dev_t to a 64-bit type. */
+# if !defined GNULIB_defined_dev_t
+typedef unsigned long long int rpl_dev_t;
+# undef dev_t
+# define dev_t rpl_dev_t
+# define GNULIB_defined_dev_t 1
+# endif
+
+/* Define ino_t to a 128-bit type. */
+# if !defined GNULIB_defined_ino_t
+/* MSVC does not have a 128-bit integer type.
+ GCC has a 128-bit integer type __int128, but only on 64-bit targets. */
+typedef struct { unsigned long long int _gl_ino[2]; } rpl_ino_t;
+# undef ino_t
+# define ino_t rpl_ino_t
+# define GNULIB_defined_ino_t 1
+# endif
+
+# else /* @WINDOWS_STAT_INODES@ == 1 */
+
+/* Define ino_t to a 64-bit type. */
+# if !defined GNULIB_defined_ino_t
+typedef unsigned long long int rpl_ino_t;
+# undef ino_t
+# define ino_t rpl_ino_t
+# define GNULIB_defined_ino_t 1
+# endif
+
+# endif
+
+/* Indicator, for gnulib internal purposes. */
+# define _GL_WINDOWS_STAT_INODES @WINDOWS_STAT_INODES@
+
+#endif
+
/* MSVC 9 defines size_t in <stddef.h>, not in <sys/types.h>. */
/* But avoid namespace pollution on glibc systems. */
#if ((defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__) \
diff --git a/m4/sys_types_h.m4 b/m4/sys_types_h.m4
index 2eb4e9e44e..e5906709f6 100644
--- a/m4/sys_types_h.m4
+++ b/m4/sys_types_h.m4
@@ -1,4 +1,4 @@
-# sys_types_h.m4 serial 6
+# sys_types_h.m4 serial 7
dnl Copyright (C) 2011-2017 Free Software Foundation, Inc.
dnl This file is free software; the Free Software Foundation
dnl gives unlimited permission to copy and/or distribute it,
@@ -17,6 +17,14 @@ AC_DEFUN_ONCE([gl_SYS_TYPES_H],
dnl Whether to override the 'off_t' type.
AC_REQUIRE([gl_TYPE_OFF_T])
+
+ dnl Whether to override the 'dev_t' and 'ino_t' types.
+ m4_ifdef([gl_WINDOWS_STAT_INODES], [
+ AC_REQUIRE([gl_WINDOWS_STAT_INODES])
+ ], [
+ WINDOWS_STAT_INODES=0
+ ])
+ AC_SUBST([WINDOWS_STAT_INODES])
])
AC_DEFUN([gl_SYS_TYPES_H_DEFAULTS],
diff --git a/m4/windows-stat-inodes.m4 b/m4/windows-stat-inodes.m4
new file mode 100644
index 0000000000..d3739a3978
--- /dev/null
+++ b/m4/windows-stat-inodes.m4
@@ -0,0 +1,19 @@
+# windows-stat-inodes.m4 serial 1
+dnl Copyright (C) 2017 Free Software Foundation, Inc.
+dnl This file is free software; the Free Software Foundation
+dnl gives unlimited permission to copy and/or distribute it,
+dnl with or without modifications, as long as this notice is preserved.
+
+dnl Enable inode identification in 'struct stat' on native Windows platforms.
+dnl Set WINDOWS_STAT_INODES to
+dnl - 0 -> keep the default (dev_t = 32-bit, ino_t = 16-bit),
+dnl - 1 -> override types normally (dev_t = 32-bit, ino_t = 64-bit),
+dnl - 2 -> override types in an extended way (dev_t = 64-bit, ino_t = 128-bit).
+AC_DEFUN([gl_WINDOWS_STAT_INODES],
+[
+ AC_REQUIRE([AC_CANONICAL_HOST])
+ case "$host_os" in
+ mingw*) WINDOWS_STAT_INODES=1 ;;
+ *) WINDOWS_STAT_INODES=0 ;;
+ esac
+])
diff --git a/modules/fstat b/modules/fstat
index 076b5f7dbb..7a62c6a72c 100644
--- a/modules/fstat
+++ b/modules/fstat
@@ -12,6 +12,7 @@ sys_stat
largefile
pathmax [test $REPLACE_FSTAT = 1]
unistd [test $REPLACE_FSTAT = 1]
+verify [test $REPLACE_FSTAT = 1]
msvc-nothrow [test $REPLACE_FSTAT = 1]
configure.ac:
diff --git a/modules/sys_types b/modules/sys_types
index 2e9c427867..81a11aa4f3 100644
--- a/modules/sys_types
+++ b/modules/sys_types
@@ -29,6 +29,7 @@ sys/types.h: sys_types.in.h $(top_builddir)/config.status
-e 's|@''PRAGMA_COLUMNS''@|@PRAGMA_COLUMNS@|g' \
-e 's|@''NEXT_SYS_TYPES_H''@|$(NEXT_SYS_TYPES_H)|g' \
-e 's|@''WINDOWS_64_BIT_OFF_T''@|$(WINDOWS_64_BIT_OFF_T)|g' \
+ -e 's|@''WINDOWS_STAT_INODES''@|$(WINDOWS_STAT_INODES)|g' \
< $(srcdir)/sys_types.in.h; \
} > $@-t && \
mv $@-t $@
diff --git a/modules/windows-stat-inodes b/modules/windows-stat-inodes
new file mode 100644
index 0000000000..06079374bc
--- /dev/null
+++ b/modules/windows-stat-inodes
@@ -0,0 +1,27 @@
+Description:
+On native Windows platforms, ensure that 'struct stat' contains values
+in the st_dev, st_ino fields that are able to distinguish different inodes.
+
+Comment:
+This module should not be used as a dependency from a test module,
+otherwise when this module occurs as a tests-related module, it will
+have side effects on the compilation of the main modules in lib/.
+
+Files:
+m4/windows-stat-inodes.m4
+
+Depends-on:
+windows-stat-override
+
+configure.ac:
+AC_REQUIRE([gl_WINDOWS_STAT_INODES])
+
+Makefile.am:
+
+Include:
+
+License:
+LGPLv2+
+
+Maintainer:
+Bruno Haible