blob: a526930d8beb6a7a2b23339d03ab94875188407b [file] [log] [blame] [raw]
#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);
if(__stat_by_file_info(&sfi, (struct stat *)st, 1) < 0) return -1;
st->st_nlink = fi.nNumberOfLinks;
st->st_dev = fi.dwVolumeSerialNumber;
return 0;
}
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);
errno = ENOENT;
}
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);
}