1 /********************************************************************/
2 /* */
3 /* stat_win.c Define functions used by os_stat macros. */
4 /* Copyright (C) 2015 - 2016 Thomas Mertes */
5 /* */
6 /* This file is part of the Seed7 Runtime Library. */
7 /* */
8 /* The Seed7 Runtime Library is free software; you can */
9 /* redistribute it and/or modify it under the terms of the GNU */
10 /* Lesser General Public License as published by the Free Software */
11 /* Foundation; either version 2.1 of the License, or (at your */
12 /* option) any later version. */
13 /* */
14 /* The Seed7 Runtime Library is distributed in the hope that it */
15 /* will be useful, but WITHOUT ANY WARRANTY; without even the */
16 /* implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR */
17 /* PURPOSE. See the GNU Lesser General Public License for more */
18 /* details. */
19 /* */
20 /* You should have received a copy of the GNU Lesser General */
21 /* Public License along with this program; if not, write to the */
22 /* Free Software Foundation, Inc., 51 Franklin Street, */
23 /* Fifth Floor, Boston, MA 02110-1301, USA. */
24 /* */
25 /* Module: Seed7 Runtime Library */
26 /* File: seed7/src/stat_win.c */
27 /* Changes: 2015 - 2016 Thomas Mertes */
28 /* Content: Define functions used by os_stat macros. */
29 /* */
30 /********************************************************************/
31
32 #define LOG_FUNCTIONS 0
33 #define VERBOSE_EXCEPTIONS 0
34
35 #include "version.h"
36
37 #include "stdio.h"
38 #include "string.h"
39 #include "sys/stat.h"
40 #include "sys/types.h"
41 #include "windows.h"
42 #include "io.h"
43 #ifdef OS_STRI_WCHAR
44 #include "wchar.h"
45 #endif
46 #include "time.h"
47 #include "errno.h"
48
49 #include "common.h"
50 #include "os_decls.h"
51 #include "striutl.h"
52 #include "fil_rtl.h"
53 #include "tim_drv.h"
54
55 #include "stat_drv.h"
56
57
58 /* Seconds between 1601-01-01 and 1970-01-01 */
59 #define SECONDS_1601_1970 INT64_SUFFIX(11644473600)
60 #define WINDOWS_TICK UINT64_SUFFIX(10000000)
61
62 #ifdef DEFINE_OS_STAT_ORIG_PROTOTYPE
63 #ifdef C_PLUS_PLUS
64 extern "C" int __cdecl os_stat_orig (const_os_striType path, os_stat_struct *statBuf);
65 #else
66 extern int __cdecl os_stat_orig (const_os_striType path, os_stat_struct *statBuf);
67 #endif
68 #endif
69
70
71
72 /**
73 * Convert a Windows FILETIME to a Unix time_t time.
74 * A Windows FILETIME is measured in 100 nanosecond units from
75 * 1601-01-01 00:00:00. Actually FILETIME is a structure holding
76 * holding the high and low bits of a 64-bit value.
77 * A Unix time_t time is measured in seconds since
78 * 1970-01-01 00:00:00. If time_t is 32-bit or 64-bit depends
79 * on the implementation of the C runtime library.
80 */
fileTime2UnixTime(const FILETIME * fileTime)81 static time_t fileTime2UnixTime (const FILETIME *fileTime)
82
83 {
84 uint64Type nanosecs100; /* Time since 1 Jan 1601 in 100ns units. */
85 int64Type unixTime;
86
87 /* fileTime2UnixTime */
88 nanosecs100 = ((uint64Type) fileTime->dwHighDateTime << 32) | fileTime->dwLowDateTime;
89 unixTime = (int64Type) (nanosecs100 / WINDOWS_TICK) - SECONDS_1601_1970;
90 if (!inTimeTRange(unixTime)) {
91 unixTime = 0;
92 } /* if */
93 return (time_t) unixTime;
94 } /* fileTime2UnixTime */
95
96
97
98 #ifdef os_stat_orig
99 /**
100 * Undo the time adjustments done by a windows stat() function.
101 * The windows stat() functions (_wstati64, ...) return adjusted
102 * access, modification and status change times of a file. E.g.:
103 * During summer time a modification time from summer is returned
104 * unchanged. During winter time the same modification time is
105 * returned, without daylight saving adjustment, in winter time.
106 * So for the same file you get different modification times in
107 * summer and in winter. The same applies for a modification time
108 * from winter. This adjustments of Daylight Saving Time can be
109 * switched on and off in the Time Zone settings of Windows. But
110 * getting access, modification and status change times of a file
111 * should neither depend on the time when the request is done, nor
112 * on some strange settings. This function corrects this Daylight
113 * Saving Timetime adjustments.
114 */
correctAdjustedUnixTime(time_t aTime)115 static time_t correctAdjustedUnixTime (time_t aTime)
116
117 {
118 #if defined USE_LOCALTIME_R || defined USE_LOCALTIME_S
119 struct tm tmResult;
120 #endif
121 struct tm *localTime;
122 SYSTEMTIME systemTime;
123 FILETIME localFileTime;
124 FILETIME fileTime;
125
126 /* correctAdjustedUnixTime */
127 #if defined USE_LOCALTIME_R
128 localTime = localtime_r(&aTime, &tmResult);
129 #elif defined USE_LOCALTIME_S
130 if (localtime_s(&tmResult, &aTime) != 0) {
131 localTime = NULL;
132 } else {
133 localTime = &tmResult;
134 } /* if */
135 #else
136 localTime = localtime(&aTime);
137 #endif
138 systemTime.wYear = (WORD) (localTime->tm_year + 1900);
139 systemTime.wMonth = (WORD) (localTime->tm_mon + 1);
140 systemTime.wDayOfWeek = (WORD) localTime->tm_wday;
141 systemTime.wDay = (WORD) localTime->tm_mday;
142 systemTime.wHour = (WORD) localTime->tm_hour;
143 systemTime.wMinute = (WORD) localTime->tm_min;
144 systemTime.wSecond = (WORD) localTime->tm_sec;
145 systemTime.wMilliseconds = 0;
146 if (SystemTimeToFileTime(&systemTime, &localFileTime) == 0 ||
147 LocalFileTimeToFileTime(&localFileTime, &fileTime) == 0) {
148 return 0;
149 } /* if */
150 return fileTime2UnixTime(&fileTime);
151 } /* correctAdjustedUnixTime */
152 #endif
153
154
155
156 /**
157 * Determine a Unix file mode from Windows attributes and file name.
158 */
fileAttr2UnixMode(DWORD attr,const wchar_t * path)159 static short unsigned int fileAttr2UnixMode (DWORD attr, const wchar_t *path)
160
161 {
162 unsigned int mode;
163
164 /* fileAttr2UnixMode */
165 logFunction(printf("fileAttr2UnixMode(" FMT_X32 ", \"%ls\")\n",
166 attr, path != NULL ? path : L"**NULL**"););
167 mode = S_IRUSR | S_IRGRP | S_IROTH;
168 if ((attr & FILE_ATTRIBUTE_READONLY) == 0) {
169 mode |= S_IWUSR | S_IWGRP | S_IWOTH;
170 } /* if */
171 if ((attr & FILE_ATTRIBUTE_DIRECTORY) != 0) {
172 mode |= S_IFDIR | S_IXUSR | S_IXGRP | S_IXOTH;
173 } else {
174 mode |= S_IFREG;
175 } /* if */
176 if (path != NULL && (mode & S_IFREG)) {
177 memSizeType len = wcslen(path);
178 if (len >= 4) {
179 const WCHAR *end = &path[len - 4];
180 if (_wcsicmp(end, L".bat") == 0 ||
181 _wcsicmp(end, L".cmd") == 0 ||
182 _wcsicmp(end, L".com") == 0 ||
183 _wcsicmp(end, L".exe") == 0) {
184 mode |= S_IXUSR | S_IXGRP | S_IXOTH;
185 } /* if */
186 } /* if */
187 } /* if */
188 logFunction(printf("fileAttr2UnixMode --> 0%o\n", mode););
189 return (short unsigned int) mode;
190 } /* fileAttr2UnixMode */
191
192
193
194 #ifdef DEFINE_WSTATI64_EXT
195 /**
196 * Stat() function for a wide character path and a 64 bit file size.
197 * The windows stat() functions (_wstati64, ...) have a lot of issues:
198 * - They don't work for extended length paths (paths longer than MAX_PATH).
199 * - For devices they set all times to 1980-01-01 00:00:00.
200 * - They adjust times (In summer and winter different times are returned).
201 * Wstati64Ext avoids this problems.
202 */
wstati64Ext(const wchar_t * path,os_stat_struct * statBuf)203 int wstati64Ext (const wchar_t *path, os_stat_struct *statBuf)
204
205 {
206 WIN32_FILE_ATTRIBUTE_DATA fileInfo;
207 int result = 0;
208
209 /* wstati64Ext */
210 logFunction(printf("wstati64Ext(\"%ls\", *)", path);
211 fflush(stdout););
212 if (likely(GetFileAttributesExW(path, GetFileExInfoStandard, &fileInfo) != 0)) {
213 /* The function os_stat_orig() fails with ENOENT, if the path is */
214 /* longer than MAX_PATH. So GetFileAttributesExW(), which works with */
215 /* an extended length path, is used. */
216 memset(statBuf, 0, sizeof(os_stat_struct));
217 statBuf->st_nlink = 1;
218 statBuf->st_mode = fileAttr2UnixMode(fileInfo.dwFileAttributes, path);
219 /* For devices os_stat_orig() sets all times to 1980-01-01 00:00:00. */
220 /* For daylight saving time os_stat_orig() returns adjusted times. */
221 /* To get correct times the times from GetFileAttributesExW() are */
222 /* used instead of the times from os_stat_orig(). */
223 statBuf->st_atime = fileTime2UnixTime(&fileInfo.ftLastAccessTime);
224 statBuf->st_mtime = fileTime2UnixTime(&fileInfo.ftLastWriteTime);
225 statBuf->st_ctime = fileTime2UnixTime(&fileInfo.ftCreationTime);
226 if (!S_ISDIR(statBuf->st_mode)) {
227 statBuf->st_size = ((int64Type) fileInfo.nFileSizeHigh << 32) |
228 fileInfo.nFileSizeLow;
229 } /* if */
230 if (path[PREFIX_LEN] >= 'a' && path[PREFIX_LEN] <= 'z') {
231 statBuf->st_dev = (wchar_t) (path[PREFIX_LEN] - 'a');
232 } else if (path[PREFIX_LEN] >= 'A' && path[PREFIX_LEN] <= 'Z') {
233 statBuf->st_dev = (wchar_t) (path[PREFIX_LEN] - 'A');
234 } /* if */
235 statBuf->st_rdev = statBuf->st_dev;
236 } else {
237 /* GetFileAttributesExW fails with ERROR_SHARING_VIOLATION, if the */
238 /* file is currently in use, by some other program. This happens */
239 /* e.g. with c:\swapfile.sys or c:\hiberfil.sys. Interestingly */
240 /* os_stat_orig() succeeds for these files. */
241 #ifdef os_stat_orig
242 DWORD lastError = GetLastError();
243 logMessage(printf("wstati64Ext: GetFileAttributesExW(\"%ls\", *) failed:\n"
244 "GetLastError=" FMT_U32 "\n",
245 path, (uint32Type) lastError););
246 if (lastError == ERROR_FILE_NOT_FOUND || lastError == ERROR_PATH_NOT_FOUND) {
247 logError(printf("wstati64Ext: GetFileAttributesExW(\"%ls\", *) failed:\n"
248 "GetLastError=" FMT_U32 "\n",
249 path, (uint32Type) GetLastError()););
250 errno = ENOENT;
251 result = -1;
252 } else if (os_stat_orig(&path[USE_EXTENDED_LENGTH_PATH * PREFIX_LEN], statBuf) == 0) {
253 /* Os_stat_orig does not work with an extended length path. */
254 /* printf("os_stat_orig(\"%ls\", *) succeeded.\n",
255 &path[USE_EXTENDED_LENGTH_PATH * PREFIX_LEN]); */
256 /* Undo the effect of time adjustments done by os_stat_orig(). */
257 statBuf->st_atime = correctAdjustedUnixTime(statBuf->st_atime);
258 statBuf->st_mtime = correctAdjustedUnixTime(statBuf->st_mtime);
259 statBuf->st_ctime = correctAdjustedUnixTime(statBuf->st_ctime);
260 } else {
261 logError(printf("wstati64Ext: os_stat_orig(\"%ls\", *) failed:\n"
262 "errno=%d\nerror: %s\n",
263 &path[USE_EXTENDED_LENGTH_PATH * PREFIX_LEN],
264 errno, strerror(errno)););
265 result = -1;
266 } /* if */
267 #else
268 logError(printf("wstati64Ext: GetFileAttributesExW(\"%ls\", *) failed:\n"
269 "GetLastError=" FMT_U32 "\n",
270 path, (uint32Type) GetLastError()););
271 errno = ENOENT;
272 result = -1;
273 #endif
274 } /* if */
275 logFunctionResult(printf("%d\n", result););
276 return result;
277 } /* wstati64Ext */
278 #endif
279
280
281
282 #ifdef DEFINE_FSTATI64_EXT
fstati64Ext(int fd,os_fstat_struct * statBuf)283 int fstati64Ext (int fd, os_fstat_struct *statBuf)
284
285 {
286 HANDLE fileHandle;
287 BY_HANDLE_FILE_INFORMATION fileInfo;
288 #ifdef os_fstat_orig
289 #ifdef os_fstat_struct_orig
290 os_fstat_struct_orig tmp;
291 #else
292 struct stat tmp;
293 #endif
294 #endif
295 int result;
296
297 /* fstati64Ext */
298 logFunction(printf("fstati64Ext(%d, *)", fd);
299 fflush(stdout););
300 fileHandle = (HANDLE) _get_osfhandle(fd);
301 #ifdef os_fstat_orig
302 result = os_fstat_orig(fd, &tmp);
303 if (result == 0) {
304 tmp.st_mode &= ~(S_IWGRP | S_IWOTH);
305 statBuf->st_dev = tmp.st_dev;
306 statBuf->st_ino = tmp.st_ino;
307 statBuf->st_mode = tmp.st_mode;
308 statBuf->st_nlink = tmp.st_nlink;
309 statBuf->st_uid = tmp.st_uid;
310 statBuf->st_gid = tmp.st_gid;
311 statBuf->st_rdev = tmp.st_rdev;
312 statBuf->st_size = (int64Type) tmp.st_size;
313 statBuf->st_atime = tmp.st_atime;
314 statBuf->st_mtime = tmp.st_mtime;
315 statBuf->st_ctime = tmp.st_ctime;
316 if (likely(GetFileInformationByHandle(fileHandle, &fileInfo) != 0)) {
317 if (!(fileInfo.dwFileAttributes & FILE_ATTRIBUTE_READONLY)) {
318 statBuf->st_mode |= S_IWUSR;
319 } /* if */
320 statBuf->st_size = ((int64Type) fileInfo.nFileSizeHigh << 32) |
321 fileInfo.nFileSizeLow;
322 } else {
323 logError(printf("fstati64Ext(%d, *): "
324 "GetFileInformationByHandle(\"" FMT_U_MEM "\", *) failed.\n"
325 "GetLastError=" FMT_U32 "\n",
326 fd, (memSizeType) fileHandle, (uint32Type) GetLastError()););
327 errno = EACCES;
328 result = -1;
329 } /* if */
330 } else
331 #endif
332 if (likely(GetFileInformationByHandle(fileHandle, &fileInfo) != 0)) {
333 memset(statBuf, 0, sizeof(os_fstat_struct));
334 statBuf->st_dev = 0; /* Not assigned */
335 statBuf->st_mode = fileAttr2UnixMode(fileInfo.dwFileAttributes, NULL);
336 statBuf->st_nlink = fileInfo.nNumberOfLinks;
337 statBuf->st_rdev = 0; /* Not assigned */
338 statBuf->st_size = ((int64Type) fileInfo.nFileSizeHigh << 32) |
339 fileInfo.nFileSizeLow;
340 statBuf->st_atime = fileTime2UnixTime(&fileInfo.ftLastAccessTime);
341 statBuf->st_mtime = fileTime2UnixTime(&fileInfo.ftLastWriteTime);
342 statBuf->st_ctime = fileTime2UnixTime(&fileInfo.ftCreationTime);
343 } else {
344 logError(printf("fstati64Ext(%d, *): "
345 "GetFileInformationByHandle(\"" FMT_U_MEM "\", *) failed:\n"
346 "GetLastError=" FMT_U32 "\n",
347 fd, (memSizeType) fileHandle, (uint32Type) GetLastError()););
348 errno = ENOENT;
349 result = -1;
350 } /* if */
351 logFunctionResult(printf("%d\n", result););
352 return result;
353 } /* fstati64Ext */
354 #endif
355