summaryrefslogtreecommitdiff
path: root/win32
diff options
context:
space:
mode:
Diffstat (limited to 'win32')
-rw-r--r--win32/win32.c397
1 files changed, 188 insertions, 209 deletions
diff --git a/win32/win32.c b/win32/win32.c
index 162ef62de0..829bdfbc60 100644
--- a/win32/win32.c
+++ b/win32/win32.c
@@ -10,16 +10,14 @@
#define PERLIO_NOT_STDIO 0
#define WIN32_LEAN_AND_MEAN
#define WIN32IO_IS_STDIO
+/* for CreateSymbolicLinkA() etc */
+#define _WIN32_WINNT 0x0601
#include <tchar.h>
#ifdef __GNUC__
# define Win32_Winsock
#endif
-#ifndef _WIN32_WINNT
-# define _WIN32_WINNT 0x0500 /* needed for CreateHardlink() etc. */
-#endif
-
#include <windows.h>
#ifndef HWND_MESSAGE
@@ -164,6 +162,8 @@ static HWND get_hwnd_delay(pTHX, long child, DWORD tries);
static void win32_csighandler(int sig);
#endif
+static void translate_to_errno(void);
+
START_EXTERN_C
HANDLE w32_perldll_handle = INVALID_HANDLE_VALUE;
char w32_module_name[MAX_PATH+1];
@@ -180,6 +180,22 @@ static HKEY HKCU_Perl_hnd;
static HKEY HKLM_Perl_hnd;
#endif
+/* the time_t epoch start time as a filetime expressed as a large integer */
+static ULARGE_INTEGER time_t_epoch_base_filetime;
+
+static const SYSTEMTIME time_t_epoch_base_systemtime = {
+ 1970, /* wYear */
+ 1, /* wMonth */
+ 0, /* wDayOfWeek */
+ 1, /* wDay */
+ 0, /* wHour */
+ 0, /* wMinute */
+ 0, /* wSecond */
+ 0 /* wMilliseconds */
+};
+
+#define FILETIME_CHUNKS_PER_SECOND (10000000UL)
+
#ifdef SET_INVALID_PARAMETER_HANDLER
static BOOL silent_invalid_parameter_handler = FALSE;
@@ -1455,143 +1471,136 @@ win32_kill(int pid, int sig)
return -1;
}
+PERL_STATIC_INLINE
+time_t
+translate_ft_to_time_t(FILETIME ft) {
+ /* Based on Win32::UTCTime.
+ Older CRTs (including MSVCRT used for gcc builds) product
+ strange behaviour when the specified time and the current time
+ differ on whether DST was in effect, this code doesnt have that
+ problem.
+ */
+ ULARGE_INTEGER u;
+ u.LowPart = ft.dwLowDateTime;
+ u.HighPart = ft.dwHighDateTime;
+ return (u.QuadPart - time_t_epoch_base_filetime.QuadPart) / FILETIME_CHUNKS_PER_SECOND;
+}
+
+static int
+win32_stat_low(HANDLE handle, const char *path, STRLEN len, Stat_t *sbuf) {
+ DWORD type = GetFileType(handle);
+ BY_HANDLE_FILE_INFORMATION bhi;
+
+ Zero(sbuf, 1, Stat_t);
+
+ type &= ~FILE_TYPE_REMOTE;
+
+ switch (type) {
+ case FILE_TYPE_DISK:
+ if (GetFileInformationByHandle(handle, &bhi)) {
+ sbuf->st_dev = bhi.dwVolumeSerialNumber;
+ sbuf->st_ino = bhi.nFileIndexHigh;
+ sbuf->st_ino <<= 32;
+ sbuf->st_ino |= bhi.nFileIndexLow;
+ sbuf->st_nlink = bhi.nNumberOfLinks;
+ sbuf->st_uid = 0;
+ sbuf->st_gid = 0;
+ /* ucrt sets this to the drive letter for
+ stat(), lets not reproduce that mistake */
+ sbuf->st_rdev = 0;
+ sbuf->st_size = bhi.nFileSizeHigh;
+ sbuf->st_size <<= 32;
+ sbuf->st_size |= bhi.nFileSizeLow;
+
+ sbuf->st_atime = translate_ft_to_time_t(bhi.ftLastAccessTime);
+ sbuf->st_mtime = translate_ft_to_time_t(bhi.ftLastWriteTime);
+ sbuf->st_ctime = translate_ft_to_time_t(bhi.ftCreationTime);
+
+ if (bhi.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
+ sbuf->st_mode = _S_IFDIR | _S_IREAD | _S_IEXEC;
+ /* duplicate the logic from the end of the old win32_stat() */
+ if (!(bhi.dwFileAttributes & FILE_ATTRIBUTE_READONLY)) {
+ sbuf->st_mode |= S_IWRITE;
+ }
+ }
+ else {
+ char path_buf[MAX_PATH+1];
+ sbuf->st_mode = _S_IFREG;
+
+ if (!path) {
+ len = GetFinalPathNameByHandleA(handle, path_buf, sizeof(path_buf), 0);
+ /* < to ensure there's space for the \0 */
+ if (len && len < sizeof(path_buf)) {
+ path = path_buf;
+ }
+ }
+
+ if (path && len > 4 &&
+ (_stricmp(path + len - 4, ".exe") == 0 ||
+ _stricmp(path + len - 4, ".bat") == 0 ||
+ _stricmp(path + len - 4, ".cmd") == 0 ||
+ _stricmp(path + len - 4, ".com") == 0)) {
+ sbuf->st_mode |= _S_IEXEC;
+ }
+ if (!(bhi.dwFileAttributes & FILE_ATTRIBUTE_READONLY)) {
+ sbuf->st_mode |= _S_IWRITE;
+ }
+ sbuf->st_mode |= _S_IREAD;
+ }
+ }
+ else {
+ translate_to_errno();
+ return -1;
+ }
+ break;
+
+ case FILE_TYPE_CHAR:
+ case FILE_TYPE_PIPE:
+ sbuf->st_mode = (type == FILE_TYPE_CHAR) ? _S_IFCHR : _S_IFIFO;
+ if (handle == GetStdHandle(STD_INPUT_HANDLE) ||
+ handle == GetStdHandle(STD_OUTPUT_HANDLE) ||
+ handle == GetStdHandle(STD_ERROR_HANDLE)) {
+ sbuf->st_mode |= _S_IWRITE | _S_IREAD;
+ }
+ break;
+
+ default:
+ return -1;
+ }
+
+ /* owner == user == group */
+ sbuf->st_mode |= (sbuf->st_mode & 0700) >> 3;
+ sbuf->st_mode |= (sbuf->st_mode & 0700) >> 6;
+
+ return 0;
+}
+
DllExport int
win32_stat(const char *path, Stat_t *sbuf)
{
- char buffer[MAX_PATH+1];
- int l = strlen(path);
+ size_t l = strlen(path);
dTHX;
- int res;
- int nlink = 1;
- unsigned __int64 ino = 0;
- DWORD vol = 0;
BOOL expect_dir = FALSE;
- struct _stati64 st;
-
- if (l > 1) {
- switch(path[l - 1]) {
- /* FindFirstFile() and stat() are buggy with a trailing
- * slashes, except for the root directory of a drive */
- case '\\':
- case '/':
- if (l > sizeof(buffer)) {
- errno = ENAMETOOLONG;
- return -1;
- }
- --l;
- strncpy(buffer, path, l);
- /* remove additional trailing slashes */
- while (l > 1 && (buffer[l-1] == '/' || buffer[l-1] == '\\'))
- --l;
- /* add back slash if we otherwise end up with just a drive letter */
- if (l == 2 && isALPHA(buffer[0]) && buffer[1] == ':')
- buffer[l++] = '\\';
- buffer[l] = '\0';
- path = buffer;
- expect_dir = TRUE;
- break;
-
- /* FindFirstFile() is buggy with "x:", so add a dot :-( */
- case ':':
- if (l == 2 && isALPHA(path[0])) {
- buffer[0] = path[0];
- buffer[1] = ':';
- buffer[2] = '.';
- buffer[3] = '\0';
- l = 3;
- path = buffer;
- }
- break;
- }
- }
+ int result;
+ HANDLE handle;
path = PerlDir_mapA(path);
l = strlen(path);
- if (!w32_sloppystat) {
- /* We must open & close the file once; otherwise file attribute changes */
- /* might not yet have propagated to "other" hard links of the same file. */
- /* This also gives us an opportunity to determine the number of links. */
- HANDLE handle = CreateFileA(path, 0, 0, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
- if (handle != INVALID_HANDLE_VALUE) {
- BY_HANDLE_FILE_INFORMATION bhi;
- if (GetFileInformationByHandle(handle, &bhi)) {
- nlink = bhi.nNumberOfLinks;
- ino = bhi.nFileIndexHigh;
- ino <<= 32;
- ino |= bhi.nFileIndexLow;
- vol = bhi.dwVolumeSerialNumber;
- }
- CloseHandle(handle);
- }
- else {
- DWORD err = GetLastError();
- /* very common case, skip CRT stat and its also failing syscalls */
- if(err == ERROR_FILE_NOT_FOUND) {
- errno = ENOENT;
- return -1;
- }
- }
- }
-
- /* path will be mapped correctly above */
- res = _stati64(path, sbuf);
- sbuf->st_dev = vol;
- sbuf->st_ino = ino;
- sbuf->st_mode = st.st_mode;
- sbuf->st_nlink = nlink;
- sbuf->st_uid = st.st_uid;
- sbuf->st_gid = st.st_gid;
- sbuf->st_rdev = st.st_rdev;
- sbuf->st_size = st.st_size;
- sbuf->st_atime = st.st_atime;
- sbuf->st_mtime = st.st_mtime;
- sbuf->st_ctime = st.st_ctime;
-
- if (res < 0) {
- /* CRT is buggy on sharenames, so make sure it really isn't.
- * XXX using GetFileAttributesEx() will enable us to set
- * sbuf->st_*time (but note that's not available on the
- * Windows of 1995) */
- DWORD r = GetFileAttributesA(path);
- if (r != 0xffffffff && (r & FILE_ATTRIBUTE_DIRECTORY)) {
- /* sbuf may still contain old garbage since stat() failed */
- Zero(sbuf, 1, Stat_t);
- sbuf->st_mode = S_IFDIR | S_IREAD;
- errno = 0;
- if (!(r & FILE_ATTRIBUTE_READONLY))
- sbuf->st_mode |= S_IWRITE | S_IEXEC;
- return 0;
- }
+ handle =
+ CreateFileA(path, FILE_READ_ATTRIBUTES,
+ FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
+ NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
+ if (handle != INVALID_HANDLE_VALUE) {
+ result = win32_stat_low(handle, path, l, sbuf);
+ CloseHandle(handle);
}
else {
- if (l == 3 && isALPHA(path[0]) && path[1] == ':'
- && (path[2] == '\\' || path[2] == '/'))
- {
- /* The drive can be inaccessible, some _stat()s are buggy */
- if (!GetVolumeInformationA(path,NULL,0,NULL,NULL,NULL,NULL,0)) {
- errno = ENOENT;
- return -1;
- }
- }
- if (expect_dir && !S_ISDIR(sbuf->st_mode)) {
- errno = ENOTDIR;
- return -1;
- }
- if (S_ISDIR(sbuf->st_mode)) {
- /* Ensure the "write" bit is switched off in the mode for
- * directories with the read-only attribute set. Some compilers
- * switch it on for directories, which is technically correct
- * (directories are indeed always writable unless denied by DACLs),
- * but we want stat() and -w to reflect the state of the read-only
- * attribute for symmetry with chmod(). */
- DWORD r = GetFileAttributesA(path);
- if (r != 0xffffffff && (r & FILE_ATTRIBUTE_READONLY)) {
- sbuf->st_mode &= ~S_IWRITE;
- }
- }
+ translate_to_errno();
+ result = -1;
}
- return res;
+
+ return result;
}
static void
@@ -1600,9 +1609,6 @@ translate_to_errno(void)
/* This isn't perfect, eg. Win32 returns ERROR_ACCESS_DENIED for
both permissions errors and if the source is a directory, while
POSIX wants EACCES and EPERM respectively.
-
- Determined by experimentation on Windows 7 x64 SP1, since MS
- don't document what error codes are returned.
*/
switch (GetLastError()) {
case ERROR_BAD_NET_NAME:
@@ -1618,9 +1624,11 @@ translate_to_errno(void)
errno = EEXIST;
break;
case ERROR_ACCESS_DENIED:
- case ERROR_PRIVILEGE_NOT_HELD:
errno = EACCES;
break;
+ case ERROR_PRIVILEGE_NOT_HELD:
+ errno = EPERM;
+ break;
case ERROR_NOT_SAME_DEVICE:
errno = EXDEV;
break;
@@ -1776,7 +1784,6 @@ DllExport int
win32_lstat(const char *path, Stat_t *sbuf)
{
HANDLE f;
- int fd;
int result;
DWORD attr = GetFileAttributes(path); /* doesn't follow symlinks */
@@ -1801,12 +1808,13 @@ win32_lstat(const char *path, Stat_t *sbuf)
return win32_stat(path, sbuf);
}
- fd = win32_open_osfhandle((intptr_t)f, 0);
- result = win32_fstat(fd, sbuf);
+ result = win32_stat_low(f, NULL, 0, sbuf);
+ CloseHandle(f);
+
if (result != -1){
sbuf->st_mode = (sbuf->st_mode & ~_S_IFMT) | _S_IFLNK;
}
- close(fd);
+
return result;
}
@@ -2162,27 +2170,17 @@ win32_times(struct tms *timebuf)
return process_time_so_far;
}
-/* fix utime() so it works on directories in NT */
static BOOL
filetime_from_time(PFILETIME pFileTime, time_t Time)
{
- struct tm *pTM = localtime(&Time);
- SYSTEMTIME SystemTime;
- FILETIME LocalTime;
+ ULARGE_INTEGER u;
+ u.QuadPart = Time;
+ u.QuadPart = u.QuadPart * FILETIME_CHUNKS_PER_SECOND + time_t_epoch_base_filetime.QuadPart;
- if (pTM == NULL)
- return FALSE;
-
- SystemTime.wYear = pTM->tm_year + 1900;
- SystemTime.wMonth = pTM->tm_mon + 1;
- SystemTime.wDay = pTM->tm_mday;
- SystemTime.wHour = pTM->tm_hour;
- SystemTime.wMinute = pTM->tm_min;
- SystemTime.wSecond = pTM->tm_sec;
- SystemTime.wMilliseconds = 0;
+ pFileTime->dwLowDateTime = u.LowPart;
+ pFileTime->dwHighDateTime = u.HighPart;
- return SystemTimeToFileTime(&SystemTime, &LocalTime) &&
- LocalFileTimeToFileTime(&LocalTime, pFileTime);
+ return TRUE;
}
DllExport int
@@ -2220,38 +2218,38 @@ win32_utime(const char *filename, struct utimbuf *times)
{
dTHX;
HANDLE handle;
- FILETIME ftCreate;
FILETIME ftAccess;
FILETIME ftWrite;
struct utimbuf TimeBuffer;
- int rc;
+ int rc = -1;
filename = PerlDir_mapA(filename);
- rc = utime(filename, times);
-
- /* EACCES: path specifies directory or readonly file */
- if (rc == 0 || errno != EACCES)
- return rc;
-
- if (times == NULL) {
- times = &TimeBuffer;
- time(&times->actime);
- times->modtime = times->actime;
- }
-
/* This will (and should) still fail on readonly files */
handle = CreateFileA(filename, GENERIC_READ | GENERIC_WRITE,
- FILE_SHARE_READ | FILE_SHARE_DELETE, NULL,
+ FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
- if (handle == INVALID_HANDLE_VALUE)
- return rc;
+ if (handle == INVALID_HANDLE_VALUE) {
+ translate_to_errno();
+ return -1;
+ }
- if (GetFileTime(handle, &ftCreate, &ftAccess, &ftWrite) &&
- filetime_from_time(&ftAccess, times->actime) &&
- filetime_from_time(&ftWrite, times->modtime) &&
- SetFileTime(handle, &ftCreate, &ftAccess, &ftWrite))
- {
- rc = 0;
+ if (times == NULL) {
+ times = &TimeBuffer;
+ time(&times->actime);
+ times->modtime = times->actime;
+ }
+
+ if (filetime_from_time(&ftAccess, times->actime) &&
+ filetime_from_time(&ftWrite, times->modtime)) {
+ if (SetFileTime(handle, NULL, &ftAccess, &ftWrite)) {
+ rc = 0;
+ }
+ else {
+ translate_to_errno();
+ }
+ }
+ else {
+ errno = EINVAL; /* bad time? */
}
CloseHandle(handle);
@@ -3195,39 +3193,9 @@ win32_abort(void)
DllExport int
win32_fstat(int fd, Stat_t *sbufptr)
{
- int result;
- struct _stati64 st;
- dTHX;
- result = _fstati64(fd, &st);
- if (result == 0) {
- sbufptr->st_mode = st.st_mode;
- sbufptr->st_uid = st.st_uid;
- sbufptr->st_gid = st.st_gid;
- sbufptr->st_rdev = st.st_rdev;
- sbufptr->st_size = st.st_size;
- sbufptr->st_atime = st.st_atime;
- sbufptr->st_mtime = st.st_mtime;
- sbufptr->st_ctime = st.st_ctime;
-
- if (w32_sloppystat) {
- sbufptr->st_nlink = st.st_nlink;
- sbufptr->st_dev = st.st_dev;
- sbufptr->st_ino = st.st_ino;
- }
- else {
- HANDLE handle = (HANDLE)win32_get_osfhandle(fd);
- BY_HANDLE_FILE_INFORMATION bhi;
- if (GetFileInformationByHandle(handle, &bhi)) {
- sbufptr->st_nlink = bhi.nNumberOfLinks;
- sbufptr->st_ino = bhi.nFileIndexHigh;
- sbufptr->st_ino <<= 32;
- sbufptr->st_ino |= bhi.nFileIndexLow;
- sbufptr->st_dev = bhi.dwVolumeSerialNumber;
- }
- }
- }
-
- return result;
+ HANDLE handle = (HANDLE)win32_get_osfhandle(fd);
+
+ return win32_stat_low(handle, NULL, 0, sbufptr);
}
DllExport int
@@ -4818,6 +4786,17 @@ Perl_win32_init(int *argcp, char ***argvp)
}
}
#endif
+
+ {
+ FILETIME ft;
+ if (!SystemTimeToFileTime(&time_t_epoch_base_systemtime,
+ &ft)) {
+ fprintf(stderr, "panic: cannot convert base system time to filetime\n"); /* no interp */
+ exit(1);
+ }
+ time_t_epoch_base_filetime.LowPart = ft.dwLowDateTime;
+ time_t_epoch_base_filetime.HighPart = ft.dwHighDateTime;
+ }
}
void