1 /* Core of implementation of fstat and stat for native Windows.
2 Copyright (C) 2017-2018 Free Software Foundation, Inc.
4 This program is free software: you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 3 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program. If not, see <https://www.gnu.org/licenses/>. */
17 /* Written by Bruno Haible. */
21 #if defined _WIN32 && ! defined __CYGWIN__
23 /* Ensure that <windows.h> defines FILE_ID_INFO. */
25 #define _WIN32_WINNT _WIN32_WINNT_WIN8
27 #include <sys/types.h>
41 /* Avoid warnings from gcc -Wcast-function-type. */
42 #define GetProcAddress \
43 (void *) GetProcAddress
45 #if _GL_WINDOWS_STAT_INODES == 2
46 /* GetFileInformationByHandleEx was introduced only in Windows Vista. */
47 typedef DWORD (WINAPI
* GetFileInformationByHandleExFuncType
) (HANDLE hFile
,
48 FILE_INFO_BY_HANDLE_CLASS fiClass
,
51 static GetFileInformationByHandleExFuncType GetFileInformationByHandleExFunc
= NULL
;
53 /* GetFinalPathNameByHandle was introduced only in Windows Vista. */
54 typedef DWORD (WINAPI
* GetFinalPathNameByHandleFuncType
) (HANDLE hFile
,
58 static GetFinalPathNameByHandleFuncType GetFinalPathNameByHandleFunc
= NULL
;
59 static BOOL initialized
= FALSE
;
64 HMODULE kernel32
= LoadLibrary ("kernel32.dll");
67 #if _GL_WINDOWS_STAT_INODES == 2
68 GetFileInformationByHandleExFunc
=
69 (GetFileInformationByHandleExFuncType
) GetProcAddress (kernel32
, "GetFileInformationByHandleEx");
71 GetFinalPathNameByHandleFunc
=
72 (GetFinalPathNameByHandleFuncType
) GetProcAddress (kernel32
, "GetFinalPathNameByHandleA");
77 /* Converts a FILETIME to GMT time since 1970-01-01 00:00:00. */
78 #if _GL_WINDOWS_STAT_TIMESPEC
80 _gl_convert_FILETIME_to_timespec (const FILETIME
*ft
)
82 struct timespec result
;
83 /* FILETIME: <https://msdn.microsoft.com/en-us/library/ms724284.aspx> */
84 unsigned long long since_1601
=
85 ((unsigned long long) ft
->dwHighDateTime
<< 32)
86 | (unsigned long long) ft
->dwLowDateTime
;
94 /* Between 1601-01-01 and 1970-01-01 there were 280 normal years and 89
95 leap years, in total 134774 days. */
96 unsigned long long since_1970
=
97 since_1601
- (unsigned long long) 134774 * (unsigned long long) 86400 * (unsigned long long) 10000000;
98 result
.tv_sec
= since_1970
/ (unsigned long long) 10000000;
99 result
.tv_nsec
= (unsigned long) (since_1970
% (unsigned long long) 10000000) * 100;
105 _gl_convert_FILETIME_to_POSIX (const FILETIME
*ft
)
107 /* FILETIME: <https://msdn.microsoft.com/en-us/library/ms724284.aspx> */
108 unsigned long long since_1601
=
109 ((unsigned long long) ft
->dwHighDateTime
<< 32)
110 | (unsigned long long) ft
->dwLowDateTime
;
115 /* Between 1601-01-01 and 1970-01-01 there were 280 normal years and 89
116 leap years, in total 134774 days. */
117 unsigned long long since_1970
=
118 since_1601
- (unsigned long long) 134774 * (unsigned long long) 86400 * (unsigned long long) 10000000;
119 return since_1970
/ (unsigned long long) 10000000;
124 /* Fill *BUF with information about the file designated by H.
125 PATH is the file name, if known, otherwise NULL.
126 Return 0 if successful, or -1 with errno set upon failure. */
128 _gl_fstat_by_handle (HANDLE h
, const char *path
, struct stat
*buf
)
131 <https://msdn.microsoft.com/en-us/library/aa364960.aspx> */
132 DWORD type
= GetFileType (h
);
133 if (type
== FILE_TYPE_DISK
)
138 /* st_mode can be determined through
140 <https://msdn.microsoft.com/en-us/library/aa364946.aspx>
141 <https://msdn.microsoft.com/en-us/library/aa365739.aspx>
143 GetFileInformationByHandle
144 <https://msdn.microsoft.com/en-us/library/aa364952.aspx>
145 <https://msdn.microsoft.com/en-us/library/aa363788.aspx>
147 GetFileInformationByHandleEx with argument FileBasicInfo
148 <https://msdn.microsoft.com/en-us/library/aa364953.aspx>
149 <https://msdn.microsoft.com/en-us/library/aa364217.aspx>
150 The latter requires -D_WIN32_WINNT=_WIN32_WINNT_VISTA or higher. */
151 BY_HANDLE_FILE_INFORMATION info
;
152 if (! GetFileInformationByHandle (h
, &info
))
155 /* Test for error conditions before starting to fill *buf. */
156 if (sizeof (buf
->st_size
) <= 4 && info
.nFileSizeHigh
> 0)
162 #if _GL_WINDOWS_STAT_INODES
163 /* st_ino can be determined through
164 GetFileInformationByHandle
165 <https://msdn.microsoft.com/en-us/library/aa364952.aspx>
166 <https://msdn.microsoft.com/en-us/library/aa363788.aspx>
167 as 64 bits, or through
168 GetFileInformationByHandleEx with argument FileIdInfo
169 <https://msdn.microsoft.com/en-us/library/aa364953.aspx>
170 <https://msdn.microsoft.com/en-us/library/hh802691.aspx>
172 The latter requires -D_WIN32_WINNT=_WIN32_WINNT_WIN8 or higher. */
173 /* Experiments show that GetFileInformationByHandleEx does not provide
174 much more information than GetFileInformationByHandle:
175 * The dwVolumeSerialNumber from GetFileInformationByHandle is equal
176 to the low 32 bits of the 64-bit VolumeSerialNumber from
177 GetFileInformationByHandleEx, and is apparently sufficient for
178 identifying the device.
179 * The nFileIndex from GetFileInformationByHandle is equal to the low
180 64 bits of the 128-bit FileId from GetFileInformationByHandleEx,
181 and the high 64 bits of this 128-bit FileId are zero.
182 * On a FAT file system, GetFileInformationByHandleEx fails with error
183 ERROR_INVALID_PARAMETER, whereas GetFileInformationByHandle
185 * On a CIFS/SMB file system, GetFileInformationByHandleEx fails with
186 error ERROR_INVALID_LEVEL, whereas GetFileInformationByHandle
188 # if _GL_WINDOWS_STAT_INODES == 2
189 if (GetFileInformationByHandleExFunc
!= NULL
)
192 if (GetFileInformationByHandleExFunc (h
, FileIdInfo
, &id
, sizeof (id
)))
194 buf
->st_dev
= id
.VolumeSerialNumber
;
195 verify (sizeof (ino_t
) == sizeof (id
.FileId
));
196 memcpy (&buf
->st_ino
, &id
.FileId
, sizeof (ino_t
));
201 switch (GetLastError ())
203 case ERROR_INVALID_PARAMETER
: /* older Windows version, or FAT */
204 case ERROR_INVALID_LEVEL
: /* CIFS/SMB file system */
212 /* Fallback for older Windows versions. */
213 buf
->st_dev
= info
.dwVolumeSerialNumber
;
214 buf
->st_ino
._gl_ino
[0] = ((ULONGLONG
) info
.nFileIndexHigh
<< 32) | (ULONGLONG
) info
.nFileIndexLow
;
215 buf
->st_ino
._gl_ino
[1] = 0;
217 # else /* _GL_WINDOWS_STAT_INODES == 1 */
218 buf
->st_dev
= info
.dwVolumeSerialNumber
;
219 buf
->st_ino
= ((ULONGLONG
) info
.nFileIndexHigh
<< 32) | (ULONGLONG
) info
.nFileIndexLow
;
222 /* st_ino is not wide enough for identifying a file on a device.
223 Without st_ino, st_dev is pointless. */
230 /* XXX How to handle FILE_ATTRIBUTE_REPARSE_POINT ? */
231 ((info
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
) ? _S_IFDIR
| S_IEXEC_UGO
: _S_IFREG
)
233 | ((info
.dwFileAttributes
& FILE_ATTRIBUTE_READONLY
) ? 0 : S_IWRITE_UGO
);
234 if (!(info
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
))
236 /* Determine whether the file is executable by looking at the file
238 If the file name is already known, use it. Otherwise, for
239 non-empty files, it can be determined through
240 GetFinalPathNameByHandle
241 <https://msdn.microsoft.com/en-us/library/aa364962.aspx>
243 GetFileInformationByHandleEx with argument FileNameInfo
244 <https://msdn.microsoft.com/en-us/library/aa364953.aspx>
245 <https://msdn.microsoft.com/en-us/library/aa364388.aspx>
246 Both require -D_WIN32_WINNT=_WIN32_WINNT_VISTA or higher. */
247 if (info
.nFileSizeHigh
> 0 || info
.nFileSizeLow
> 0)
249 char fpath
[PATH_MAX
];
251 || (GetFinalPathNameByHandleFunc
!= NULL
252 && GetFinalPathNameByHandleFunc (h
, fpath
, sizeof (fpath
), VOLUME_NAME_NONE
)
254 && (path
= fpath
, 1)))
256 const char *last_dot
= NULL
;
258 for (p
= path
; *p
!= '\0'; p
++)
261 if (last_dot
!= NULL
)
263 const char *suffix
= last_dot
+ 1;
264 if (_stricmp (suffix
, "exe") == 0
265 || _stricmp (suffix
, "bat") == 0
266 || _stricmp (suffix
, "cmd") == 0
267 || _stricmp (suffix
, "com") == 0)
272 /* Cannot determine file name. Pretend that it is executable. */
278 /* st_nlink can be determined through
279 GetFileInformationByHandle
280 <https://msdn.microsoft.com/en-us/library/aa364952.aspx>
281 <https://msdn.microsoft.com/en-us/library/aa363788.aspx>
283 GetFileInformationByHandleEx with argument FileStandardInfo
284 <https://msdn.microsoft.com/en-us/library/aa364953.aspx>
285 <https://msdn.microsoft.com/en-us/library/aa364401.aspx>
286 The latter requires -D_WIN32_WINNT=_WIN32_WINNT_VISTA or higher. */
287 buf
->st_nlink
= (info
.nNumberOfLinks
> SHRT_MAX
? SHRT_MAX
: info
.nNumberOfLinks
);
289 /* There's no easy way to map the Windows SID concept to an integer. */
293 /* st_rdev is irrelevant for normal files and directories. */
296 /* st_size can be determined through
298 <https://msdn.microsoft.com/en-us/library/aa364957.aspx>
301 <https://msdn.microsoft.com/en-us/library/aa364946.aspx>
302 <https://msdn.microsoft.com/en-us/library/aa365739.aspx>
304 GetFileInformationByHandle
305 <https://msdn.microsoft.com/en-us/library/aa364952.aspx>
306 <https://msdn.microsoft.com/en-us/library/aa363788.aspx>
308 GetFileInformationByHandleEx with argument FileStandardInfo
309 <https://msdn.microsoft.com/en-us/library/aa364953.aspx>
310 <https://msdn.microsoft.com/en-us/library/aa364401.aspx>
311 The latter requires -D_WIN32_WINNT=_WIN32_WINNT_VISTA or higher. */
312 if (sizeof (buf
->st_size
) <= 4)
313 /* Range check already done above. */
314 buf
->st_size
= info
.nFileSizeLow
;
316 buf
->st_size
= ((long long) info
.nFileSizeHigh
<< 32) | (long long) info
.nFileSizeLow
;
318 /* st_atime, st_mtime, st_ctime can be determined through
320 <https://msdn.microsoft.com/en-us/library/ms724320.aspx>
323 <https://msdn.microsoft.com/en-us/library/aa364946.aspx>
324 <https://msdn.microsoft.com/en-us/library/aa365739.aspx>
326 GetFileInformationByHandle
327 <https://msdn.microsoft.com/en-us/library/aa364952.aspx>
328 <https://msdn.microsoft.com/en-us/library/aa363788.aspx>
330 GetFileInformationByHandleEx with argument FileBasicInfo
331 <https://msdn.microsoft.com/en-us/library/aa364953.aspx>
332 <https://msdn.microsoft.com/en-us/library/aa364217.aspx>
333 The latter requires -D_WIN32_WINNT=_WIN32_WINNT_VISTA or higher. */
334 #if _GL_WINDOWS_STAT_TIMESPEC
335 buf
->st_atim
= _gl_convert_FILETIME_to_timespec (&info
.ftLastAccessTime
);
336 buf
->st_mtim
= _gl_convert_FILETIME_to_timespec (&info
.ftLastWriteTime
);
337 buf
->st_ctim
= _gl_convert_FILETIME_to_timespec (&info
.ftCreationTime
);
339 buf
->st_atime
= _gl_convert_FILETIME_to_POSIX (&info
.ftLastAccessTime
);
340 buf
->st_mtime
= _gl_convert_FILETIME_to_POSIX (&info
.ftLastWriteTime
);
341 buf
->st_ctime
= _gl_convert_FILETIME_to_POSIX (&info
.ftCreationTime
);
346 else if (type
== FILE_TYPE_CHAR
|| type
== FILE_TYPE_PIPE
)
349 #if _GL_WINDOWS_STAT_INODES == 2
350 buf
->st_ino
._gl_ino
[0] = buf
->st_ino
._gl_ino
[1] = 0;
354 buf
->st_mode
= (type
== FILE_TYPE_PIPE
? _S_IFIFO
: _S_IFCHR
);
359 if (type
== FILE_TYPE_PIPE
)
362 <https://msdn.microsoft.com/en-us/library/aa365779.aspx> */
363 DWORD bytes_available
;
364 if (PeekNamedPipe (h
, NULL
, 0, NULL
, &bytes_available
, NULL
))
365 buf
->st_size
= bytes_available
;
371 #if _GL_WINDOWS_STAT_TIMESPEC
372 buf
->st_atim
.tv_sec
= 0; buf
->st_atim
.tv_nsec
= 0;
373 buf
->st_mtim
.tv_sec
= 0; buf
->st_mtim
.tv_nsec
= 0;
374 buf
->st_ctim
.tv_sec
= 0; buf
->st_ctim
.tv_nsec
= 0;
390 DWORD error
= GetLastError ();
392 fprintf (stderr
, "_gl_fstat_by_handle error 0x%x\n", (unsigned int) error
);
396 case ERROR_ACCESS_DENIED
:
397 case ERROR_SHARING_VIOLATION
:
401 case ERROR_OUTOFMEMORY
:
405 case ERROR_WRITE_FAULT
:
406 case ERROR_READ_FAULT
:
407 case ERROR_GEN_FAILURE
:
421 /* This declaration is solely to ensure that after preprocessing
422 this file is never empty. */