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