| #include <windows.h> |
| #include <time.h> |
| #include <fcntl.h> |
| #include <io.h> |
| #include <sys/stat.h> |
| #include <stdio.h> |
| #include <errno.h> |
| |
| #include <shared.h> |
| |
| typedef struct { |
| DWORD dwFileAttributes; |
| FILETIME ftLastWriteTime; |
| FILETIME ftCreationTime; |
| FILETIME ftLastAccessTime; |
| DWORD nFileSizeLow; |
| } stat_file_info_t; |
| |
| #define COPY_MEMBER(DEST, SRC, MEMBER) \ |
| do { \ |
| (DEST)->MEMBER = (SRC)->MEMBER; \ |
| } while(0) |
| |
| #define TO_STAT_FILE_INFO(DEST, SRC) \ |
| do { \ |
| COPY_MEMBER((DEST), (SRC), dwFileAttributes); \ |
| COPY_MEMBER((DEST), (SRC), ftLastWriteTime); \ |
| COPY_MEMBER((DEST), (SRC), ftCreationTime); \ |
| COPY_MEMBER((DEST), (SRC), ftLastAccessTime); \ |
| COPY_MEMBER((DEST), (SRC), nFileSizeLow); \ |
| } while(0) |
| |
| static int __stat_by_file_info(stat_file_info_t *fi, struct stat *st, int exec) { |
| int permission = _S_IREAD; |
| |
| memset (st, 0, sizeof (*st)); |
| |
| st->st_size = fi->nFileSizeLow; |
| st->st_mode = _S_IFREG; |
| |
| if((fi->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0) { |
| st->st_mode = _S_IFDIR | _S_IEXEC; |
| } else if(exec) permission |= _S_IEXEC; |
| |
| if((fi->dwFileAttributes & FILE_ATTRIBUTE_READONLY) == 0) { |
| permission |= _S_IWRITE; |
| } |
| |
| st->st_mode |= permission | (permission >> 3) | (permission >> 6); |
| |
| st->st_nlink = 1; /* always 1? */ |
| st->st_rdev = 1; /* Where to get drive info? */ |
| st->st_ino = 0; /* always 0 on Windows */ |
| |
| st->st_mtime = __filetime_to_time_t(fi->ftLastWriteTime); |
| st->st_ctime = __filetime_to_time_t(fi->ftCreationTime); |
| st->st_atime = __filetime_to_time_t(fi->ftLastAccessTime); |
| |
| /* Looks like the code below is never triggered. |
| Windows CE always only keeps the LastWriteTime, and |
| copies it to the CreationTime and LastAccessTime fields. */ |
| if(st->st_ctime == 0) st->st_ctime = st->st_mtime; |
| if(st->st_atime == 0) { |
| /* On XP, at least, the st_atime is always set to the same as |
| the st_mtime, except the hour/min/sec == 00:00:00. */ |
| SYSTEMTIME s; |
| FILETIME f = fi->ftLastWriteTime; |
| FileTimeToSystemTime(&f, &s); |
| s.wHour = 0; s.wMinute = 0; |
| s.wSecond = 0; s.wMilliseconds = 0; |
| SystemTimeToFileTime(&s, &f); |
| st->st_atime = __filetime_to_time_t(f); |
| /* st->st_atime = st->st_mtime; */ |
| } |
| return 0; |
| } |
| |
| int _fstat(int fd, struct _stat *st) { |
| BY_HANDLE_FILE_INFORMATION_NEW fi; |
| stat_file_info_t sfi; |
| |
| if(!st) { |
| errno = EFAULT; |
| return -1; |
| } |
| |
| if(!GetFileInformationByHandle((HANDLE)fd, (BY_HANDLE_FILE_INFORMATION *)&fi)) return -1; |
| TO_STAT_FILE_INFO(&sfi, &fi); |
| return __stat_by_file_info(&sfi, (struct stat *)st, 1); |
| } |
| |
| int fstat(int fd, struct stat *st) { |
| return _fstat(fd, (struct _stat *)st); |
| } |
| |
| int _stat(const char *path, struct _stat *st) { |
| WIN32_FIND_DATAW fd; |
| HANDLE h; |
| int ret; |
| stat_file_info_t sfi; |
| wchar_t wpath[MAX_PATH + 1]; |
| size_t len, wlen; |
| int request_dir = 0; |
| |
| if(!path || !st) { |
| errno = EFAULT; |
| return -1; |
| } |
| |
| len = strlen(path); |
| if(!len) { |
| errno = ENOENT; |
| return -1; |
| } |
| if(len > MAX_PATH) { |
| errno = ENAMETOOLONG; |
| return -1; |
| } |
| wlen = mbstowcs(wpath, path, len + 1); |
| if(path[len - 1] == '/') { |
| request_dir = 1; |
| if(len > 1 && wlen > 0) wpath[wlen - 1] = 0; |
| } |
| if((h = FindFirstFileW(wpath, &fd)) == INVALID_HANDLE_VALUE) { |
| DWORD dwError = GetLastError(); |
| if(dwError == ERROR_NO_MORE_FILES) { |
| /* Convert error to something more sensible. */ |
| SetLastError(ERROR_FILE_NOT_FOUND); |
| } |
| return -1; |
| } |
| |
| TO_STAT_FILE_INFO(&sfi, &fd); |
| if(request_dir && !(sfi.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) { |
| errno = ENOTDIR; |
| return -1; |
| } |
| |
| ret = __stat_by_file_info(&sfi, (struct stat *)st, 1); |
| FindClose(h); |
| return ret; |
| } |
| |
| int stat(const char *path, struct stat *st) { |
| return _stat(path, (struct _stat *)st); |
| } |