warnings: fix compilation with old autoconf
[gnulib/ericb.git] / lib / stat-w32.c
blob237e2aad877d1c29dc7047fcecbe972296c96620
1 /* Core of implementation of fstat and stat for native Windows.
2 Copyright (C) 2017 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 <http://www.gnu.org/licenses/>. */
17 /* Written by Bruno Haible. */
19 #include <config.h>
21 #if (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__
23 /* Ensure that <windows.h> defines FILE_ID_INFO. */
24 #undef _WIN32_WINNT
25 #define _WIN32_WINNT _WIN32_WINNT_WIN8
27 #include <sys/types.h>
28 #include <sys/stat.h>
29 #include <errno.h>
30 #include <limits.h>
31 #include <string.h>
32 #include <unistd.h>
33 #include <windows.h>
35 /* Specification. */
36 #include "stat-w32.h"
38 #include "pathmax.h"
39 #include "verify.h"
41 #if _GL_WINDOWS_STAT_INODES == 2
42 /* GetFileInformationByHandleEx was introduced only in Windows Vista. */
43 typedef DWORD (WINAPI * GetFileInformationByHandleExFuncType) (HANDLE hFile,
44 FILE_INFO_BY_HANDLE_CLASS fiClass,
45 LPVOID lpBuffer,
46 DWORD dwBufferSize);
47 static GetFileInformationByHandleExFuncType GetFileInformationByHandleExFunc = NULL;
48 #endif
49 /* GetFinalPathNameByHandle was introduced only in Windows Vista. */
50 typedef DWORD (WINAPI * GetFinalPathNameByHandleFuncType) (HANDLE hFile,
51 LPTSTR lpFilePath,
52 DWORD lenFilePath,
53 DWORD dwFlags);
54 static GetFinalPathNameByHandleFuncType GetFinalPathNameByHandleFunc = NULL;
55 static BOOL initialized = FALSE;
57 static void
58 initialize (void)
60 HMODULE kernel32 = LoadLibrary ("kernel32.dll");
61 if (kernel32 != NULL)
63 #if _GL_WINDOWS_STAT_INODES == 2
64 GetFileInformationByHandleExFunc =
65 (GetFileInformationByHandleExFuncType) GetProcAddress (kernel32, "GetFileInformationByHandleEx");
66 #endif
67 GetFinalPathNameByHandleFunc =
68 (GetFinalPathNameByHandleFuncType) GetProcAddress (kernel32, "GetFinalPathNameByHandleA");
70 initialized = TRUE;
73 /* Converts a FILETIME to GMT time since 1970-01-01 00:00:00. */
74 #if _GL_WINDOWS_STAT_TIMESPEC
75 struct timespec
76 _gl_convert_FILETIME_to_timespec (const FILETIME *ft)
78 struct timespec result;
79 /* FILETIME: <https://msdn.microsoft.com/en-us/library/ms724284.aspx> */
80 unsigned long long since_1601 =
81 ((unsigned long long) ft->dwHighDateTime << 32)
82 | (unsigned long long) ft->dwLowDateTime;
83 if (since_1601 == 0)
85 result.tv_sec = 0;
86 result.tv_nsec = 0;
88 else
90 /* Between 1601-01-01 and 1970-01-01 there were 280 normal years and 89
91 leap years, in total 134774 days. */
92 unsigned long long since_1970 =
93 since_1601 - (unsigned long long) 134774 * (unsigned long long) 86400 * (unsigned long long) 10000000;
94 result.tv_sec = since_1970 / (unsigned long long) 10000000;
95 result.tv_nsec = (unsigned long) (since_1970 % (unsigned long long) 10000000) * 100;
97 return result;
99 #else
100 time_t
101 _gl_convert_FILETIME_to_POSIX (const FILETIME *ft)
103 /* FILETIME: <https://msdn.microsoft.com/en-us/library/ms724284.aspx> */
104 unsigned long long since_1601 =
105 ((unsigned long long) ft->dwHighDateTime << 32)
106 | (unsigned long long) ft->dwLowDateTime;
107 if (since_1601 == 0)
108 return 0;
109 else
111 /* Between 1601-01-01 and 1970-01-01 there were 280 normal years and 89
112 leap years, in total 134774 days. */
113 unsigned long long since_1970 =
114 since_1601 - (unsigned long long) 134774 * (unsigned long long) 86400 * (unsigned long long) 10000000;
115 return since_1970 / (unsigned long long) 10000000;
118 #endif
120 /* Fill *BUF with information about the file designated by H.
121 PATH is the file name, if known, otherwise NULL.
122 Return 0 if successful, or -1 with errno set upon failure. */
124 _gl_fstat_by_handle (HANDLE h, const char *path, struct stat *buf)
126 /* GetFileType
127 <https://msdn.microsoft.com/en-us/library/aa364960.aspx> */
128 DWORD type = GetFileType (h);
129 if (type == FILE_TYPE_DISK)
131 if (!initialized)
132 initialize ();
134 /* st_mode can be determined through
135 GetFileAttributesEx
136 <https://msdn.microsoft.com/en-us/library/aa364946.aspx>
137 <https://msdn.microsoft.com/en-us/library/aa365739.aspx>
138 or through
139 GetFileInformationByHandle
140 <https://msdn.microsoft.com/en-us/library/aa364952.aspx>
141 <https://msdn.microsoft.com/en-us/library/aa363788.aspx>
142 or through
143 GetFileInformationByHandleEx with argument FileBasicInfo
144 <https://msdn.microsoft.com/en-us/library/aa364953.aspx>
145 <https://msdn.microsoft.com/en-us/library/aa364217.aspx>
146 The latter requires -D_WIN32_WINNT=_WIN32_WINNT_VISTA or higher. */
147 BY_HANDLE_FILE_INFORMATION info;
148 if (! GetFileInformationByHandle (h, &info))
149 goto failed;
151 /* Test for error conditions before starting to fill *buf. */
152 if (sizeof (buf->st_size) <= 4 && info.nFileSizeHigh > 0)
154 errno = EOVERFLOW;
155 return -1;
158 #if _GL_WINDOWS_STAT_INODES
159 /* st_ino can be determined through
160 GetFileInformationByHandle
161 <https://msdn.microsoft.com/en-us/library/aa364952.aspx>
162 <https://msdn.microsoft.com/en-us/library/aa363788.aspx>
163 as 64 bits, or through
164 GetFileInformationByHandleEx with argument FileIdInfo
165 <https://msdn.microsoft.com/en-us/library/aa364953.aspx>
166 <https://msdn.microsoft.com/en-us/library/hh802691.aspx>
167 as 128 bits.
168 The latter requires -D_WIN32_WINNT=_WIN32_WINNT_WIN8 or higher. */
169 /* Experiments show that GetFileInformationByHandleEx does not provide
170 much more information than GetFileInformationByHandle:
171 * The dwVolumeSerialNumber from GetFileInformationByHandle is equal
172 to the low 32 bits of the 64-bit VolumeSerialNumber from
173 GetFileInformationByHandleEx, and is apparently sufficient for
174 identifying the device.
175 * The nFileIndex from GetFileInformationByHandle is equal to the low
176 64 bits of the 128-bit FileId from GetFileInformationByHandleEx,
177 and the high 64 bits of this 128-bit FileId are zero.
178 * On a FAT file system, GetFileInformationByHandleEx fails with error
179 ERROR_INVALID_PARAMETER, whereas GetFileInformationByHandle
180 succeeds.
181 * On a CIFS/SMB file system, GetFileInformationByHandleEx fails with
182 error ERROR_INVALID_LEVEL, whereas GetFileInformationByHandle
183 succeeds. */
184 # if _GL_WINDOWS_STAT_INODES == 2
185 if (GetFileInformationByHandleExFunc != NULL)
187 FILE_ID_INFO id;
188 if (GetFileInformationByHandleExFunc (h, FileIdInfo, &id, sizeof (id)))
190 buf->st_dev = id.VolumeSerialNumber;
191 verify (sizeof (ino_t) == sizeof (id.FileId));
192 memcpy (&buf->st_ino, &id.FileId, sizeof (ino_t));
193 goto ino_done;
195 else
197 switch (GetLastError ())
199 case ERROR_INVALID_PARAMETER: /* older Windows version, or FAT */
200 case ERROR_INVALID_LEVEL: /* CIFS/SMB file system */
201 goto fallback;
202 default:
203 goto failed;
207 fallback: ;
208 /* Fallback for older Windows versions. */
209 buf->st_dev = info.dwVolumeSerialNumber;
210 buf->st_ino._gl_ino[0] = ((ULONGLONG) info.nFileIndexHigh << 32) | (ULONGLONG) info.nFileIndexLow;
211 buf->st_ino._gl_ino[1] = 0;
212 ino_done: ;
213 # else /* _GL_WINDOWS_STAT_INODES == 1 */
214 buf->st_dev = info.dwVolumeSerialNumber;
215 buf->st_ino = ((ULONGLONG) info.nFileIndexHigh << 32) | (ULONGLONG) info.nFileIndexLow;
216 # endif
217 #else
218 /* st_ino is not wide enough for identifying a file on a device.
219 Without st_ino, st_dev is pointless. */
220 buf->st_dev = 0;
221 buf->st_ino = 0;
222 #endif
224 /* st_mode. */
225 unsigned int mode =
226 /* XXX How to handle FILE_ATTRIBUTE_REPARSE_POINT ? */
227 ((info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) ? _S_IFDIR | S_IEXEC_UGO : _S_IFREG)
228 | S_IREAD_UGO
229 | ((info.dwFileAttributes & FILE_ATTRIBUTE_READONLY) ? 0 : S_IWRITE_UGO);
230 if (!(info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
232 /* Determine whether the file is executable by looking at the file
233 name suffix.
234 If the file name is already known, use it. Otherwise, for
235 non-empty files, it can be determined through
236 GetFinalPathNameByHandle
237 <https://msdn.microsoft.com/en-us/library/aa364962.aspx>
238 or through
239 GetFileInformationByHandleEx with argument FileNameInfo
240 <https://msdn.microsoft.com/en-us/library/aa364953.aspx>
241 <https://msdn.microsoft.com/en-us/library/aa364388.aspx>
242 Both require -D_WIN32_WINNT=_WIN32_WINNT_VISTA or higher. */
243 if (info.nFileSizeHigh > 0 || info.nFileSizeLow > 0)
245 char fpath[PATH_MAX];
246 if (path != NULL
247 || (GetFinalPathNameByHandleFunc != NULL
248 && GetFinalPathNameByHandleFunc (h, fpath, sizeof (fpath), VOLUME_NAME_NONE)
249 < sizeof (fpath)
250 && (path = fpath, 1)))
252 const char *last_dot = NULL;
253 const char *p;
254 for (p = path; *p != '\0'; p++)
255 if (*p == '.')
256 last_dot = p;
257 if (last_dot != NULL)
259 const char *suffix = last_dot + 1;
260 if (_stricmp (suffix, "exe") == 0
261 || _stricmp (suffix, "bat") == 0
262 || _stricmp (suffix, "cmd") == 0
263 || _stricmp (suffix, "com") == 0)
264 mode |= S_IEXEC_UGO;
267 else
268 /* Cannot determine file name. Pretend that it is executable. */
269 mode |= S_IEXEC_UGO;
272 buf->st_mode = mode;
274 /* st_nlink can be determined through
275 GetFileInformationByHandle
276 <https://msdn.microsoft.com/en-us/library/aa364952.aspx>
277 <https://msdn.microsoft.com/en-us/library/aa363788.aspx>
278 or through
279 GetFileInformationByHandleEx with argument FileStandardInfo
280 <https://msdn.microsoft.com/en-us/library/aa364953.aspx>
281 <https://msdn.microsoft.com/en-us/library/aa364401.aspx>
282 The latter requires -D_WIN32_WINNT=_WIN32_WINNT_VISTA or higher. */
283 buf->st_nlink = (info.nNumberOfLinks > SHRT_MAX ? SHRT_MAX : info.nNumberOfLinks);
285 /* There's no easy way to map the Windows SID concept to an integer. */
286 buf->st_uid = 0;
287 buf->st_gid = 0;
289 /* st_rdev is irrelevant for normal files and directories. */
290 buf->st_rdev = 0;
292 /* st_size can be determined through
293 GetFileSizeEx
294 <https://msdn.microsoft.com/en-us/library/aa364957.aspx>
295 or through
296 GetFileAttributesEx
297 <https://msdn.microsoft.com/en-us/library/aa364946.aspx>
298 <https://msdn.microsoft.com/en-us/library/aa365739.aspx>
299 or through
300 GetFileInformationByHandle
301 <https://msdn.microsoft.com/en-us/library/aa364952.aspx>
302 <https://msdn.microsoft.com/en-us/library/aa363788.aspx>
303 or through
304 GetFileInformationByHandleEx with argument FileStandardInfo
305 <https://msdn.microsoft.com/en-us/library/aa364953.aspx>
306 <https://msdn.microsoft.com/en-us/library/aa364401.aspx>
307 The latter requires -D_WIN32_WINNT=_WIN32_WINNT_VISTA or higher. */
308 if (sizeof (buf->st_size) <= 4)
309 /* Range check already done above. */
310 buf->st_size = info.nFileSizeLow;
311 else
312 buf->st_size = ((long long) info.nFileSizeHigh << 32) | (long long) info.nFileSizeLow;
314 /* st_atime, st_mtime, st_ctime can be determined through
315 GetFileTime
316 <https://msdn.microsoft.com/en-us/library/ms724320.aspx>
317 or through
318 GetFileAttributesEx
319 <https://msdn.microsoft.com/en-us/library/aa364946.aspx>
320 <https://msdn.microsoft.com/en-us/library/aa365739.aspx>
321 or through
322 GetFileInformationByHandle
323 <https://msdn.microsoft.com/en-us/library/aa364952.aspx>
324 <https://msdn.microsoft.com/en-us/library/aa363788.aspx>
325 or through
326 GetFileInformationByHandleEx with argument FileBasicInfo
327 <https://msdn.microsoft.com/en-us/library/aa364953.aspx>
328 <https://msdn.microsoft.com/en-us/library/aa364217.aspx>
329 The latter requires -D_WIN32_WINNT=_WIN32_WINNT_VISTA or higher. */
330 #if _GL_WINDOWS_STAT_TIMESPEC
331 buf->st_atim = _gl_convert_FILETIME_to_timespec (&info.ftLastAccessTime);
332 buf->st_mtim = _gl_convert_FILETIME_to_timespec (&info.ftLastWriteTime);
333 buf->st_ctim = _gl_convert_FILETIME_to_timespec (&info.ftCreationTime);
334 #else
335 buf->st_atime = _gl_convert_FILETIME_to_POSIX (&info.ftLastAccessTime);
336 buf->st_mtime = _gl_convert_FILETIME_to_POSIX (&info.ftLastWriteTime);
337 buf->st_ctime = _gl_convert_FILETIME_to_POSIX (&info.ftCreationTime);
338 #endif
340 return 0;
342 else if (type == FILE_TYPE_CHAR || type == FILE_TYPE_PIPE)
344 buf->st_dev = 0;
345 #if _GL_WINDOWS_STAT_INODES == 2
346 buf->st_ino._gl_ino[0] = buf->st_ino._gl_ino[1] = 0;
347 #else
348 buf->st_ino = 0;
349 #endif
350 buf->st_mode = (type == FILE_TYPE_PIPE ? _S_IFIFO : _S_IFCHR);
351 buf->st_nlink = 1;
352 buf->st_uid = 0;
353 buf->st_gid = 0;
354 buf->st_rdev = 0;
355 if (type == FILE_TYPE_PIPE)
357 /* PeekNamedPipe
358 <https://msdn.microsoft.com/en-us/library/aa365779.aspx> */
359 DWORD bytes_available;
360 if (PeekNamedPipe (h, NULL, 0, NULL, &bytes_available, NULL))
361 buf->st_size = bytes_available;
362 else
363 buf->st_size = 0;
365 else
366 buf->st_size = 0;
367 #if _GL_WINDOWS_STAT_TIMESPEC
368 buf->st_atim.tv_sec = 0; buf->st_atim.tv_nsec = 0;
369 buf->st_mtim.tv_sec = 0; buf->st_mtim.tv_nsec = 0;
370 buf->st_ctim.tv_sec = 0; buf->st_ctim.tv_nsec = 0;
371 #else
372 buf->st_atime = 0;
373 buf->st_mtime = 0;
374 buf->st_ctime = 0;
375 #endif
376 return 0;
378 else
380 errno = ENOENT;
381 return -1;
384 failed:
386 DWORD error = GetLastError ();
387 #if 0
388 fprintf (stderr, "_gl_fstat_by_handle error 0x%x\n", (unsigned int) error);
389 #endif
390 switch (error)
392 case ERROR_ACCESS_DENIED:
393 case ERROR_SHARING_VIOLATION:
394 errno = EACCES;
395 break;
397 case ERROR_OUTOFMEMORY:
398 errno = ENOMEM;
399 break;
401 case ERROR_WRITE_FAULT:
402 case ERROR_READ_FAULT:
403 case ERROR_GEN_FAILURE:
404 errno = EIO;
405 break;
407 default:
408 errno = EINVAL;
409 break;
411 return -1;
415 #else
417 /* This declaration is solely to ensure that after preprocessing
418 this file is never empty. */
419 typedef int dummy;
421 #endif