summaryrefslogtreecommitdiff
path: root/utils/fs/fs.c
diff options
context:
space:
mode:
authorTamar Christina <tamar@zhox.com>2019-09-08 23:11:19 +0100
committerTamar Christina <tamar@zhox.com>2019-10-20 16:21:10 +0100
commit4b431f334018eaef2cf36de3316025c68c922915 (patch)
tree7f85d5a1a6bc6d39e9a8c51fe33904ff61461a7e /utils/fs/fs.c
parentc4c9904b324736dc5d190a91418e8d8f564d4104 (diff)
downloadhaskell-4b431f334018eaef2cf36de3316025c68c922915.tar.gz
Windows: Update tarballs to GCC 9.2 and remove MAX_PATH limit.
Diffstat (limited to 'utils/fs/fs.c')
-rw-r--r--utils/fs/fs.c299
1 files changed, 266 insertions, 33 deletions
diff --git a/utils/fs/fs.c b/utils/fs/fs.c
index 6644705ffe..4a761e5f9e 100644
--- a/utils/fs/fs.c
+++ b/utils/fs/fs.c
@@ -1,6 +1,6 @@
/* -----------------------------------------------------------------------------
*
- * (c) Tamar Christina 2018
+ * (c) Tamar Christina 2018-2019
*
* Windows I/O routines for file opening.
*
@@ -24,16 +24,20 @@
#include <sys/stat.h>
#include <sys/types.h>
#include <share.h>
+#include <errno.h>
/* This function converts Windows paths between namespaces. More specifically
It converts an explorer style path into a NT or Win32 namespace.
- This has several caveats but they are caviats that are native to Windows and
+ This has several caveats but they are caveats that are native to Windows and
not POSIX. See
https://msdn.microsoft.com/en-us/library/windows/desktop/aa365247.aspx.
Anything else such as raw device paths we leave untouched. The main benefit
of doing any of this is that we can break the MAX_PATH restriction and also
- access raw handles that we couldn't before. */
-static wchar_t* __hs_create_device_name (const wchar_t* filename) {
+ access raw handles that we couldn't before.
+
+ The resulting string is dynamically allocated and so callers are expected to
+ free this string. */
+wchar_t* FS(create_device_name) (const wchar_t* filename) {
const wchar_t* win32_dev_namespace = L"\\\\.\\";
const wchar_t* win32_file_namespace = L"\\\\?\\";
const wchar_t* nt_device_namespace = L"\\Device\\";
@@ -57,13 +61,31 @@ static wchar_t* __hs_create_device_name (const wchar_t* filename) {
result[i] = L'\\';
}
+ /* We need to expand dos short paths as well. */
+ DWORD nResult = GetLongPathNameW (result, NULL, 0) + 1;
+ wchar_t* temp = NULL;
+ if (nResult > 1)
+ {
+ temp = _wcsdup (result);
+ free (result);
+ result = malloc (nResult * sizeof (wchar_t));
+ if (GetLongPathNameW (temp, result, nResult) == 0)
+ {
+ result = memcpy (result, temp, wcslen (temp));
+ goto cleanup;
+ }
+ free (temp);
+ }
+
/* Now resolve any . and .. in the path or subsequent API calls may fail since
Win32 will no longer resolve them. */
- DWORD nResult = GetFullPathNameW (result, 0, NULL, NULL) + 1;
- wchar_t *temp = _wcsdup (result);
+ nResult = GetFullPathNameW (result, 0, NULL, NULL) + 1;
+ temp = _wcsdup (result);
+ free (result);
result = malloc (nResult * sizeof (wchar_t));
if (GetFullPathNameW (temp, nResult, result, NULL) == 0)
{
+ result = memcpy (result, temp, wcslen (temp));
goto cleanup;
}
@@ -84,6 +106,7 @@ static wchar_t* __hs_create_device_name (const wchar_t* filename) {
/* Create new string. */
int bLen = wcslen (result) + wcslen (ns) + 1;
temp = _wcsdup (result);
+ free (result);
result = malloc (bLen * sizeof (wchar_t));
if (swprintf (result, bLen, L"%ls%ls", ns, temp) <= 0)
{
@@ -100,23 +123,64 @@ cleanup:
return NULL;
}
-#define HAS_FLAG(a,b) ((a & b) == b)
+static int setErrNoFromWin32Error (void);
+/* Sets errno to the right error value and returns -1 to indicate the failure.
+ This function should only be called when the creation of the fd actually
+ failed and you want to return -1 for the fd. */
+static
+int setErrNoFromWin32Error () {
+ switch (GetLastError()) {
+ case ERROR_SUCCESS:
+ errno = 0;
+ break;
+ case ERROR_ACCESS_DENIED:
+ case ERROR_FILE_READ_ONLY:
+ errno = EACCES;
+ break;
+ case ERROR_FILE_NOT_FOUND:
+ case ERROR_PATH_NOT_FOUND:
+ errno = ENOENT;
+ break;
+ case ERROR_FILE_EXISTS:
+ errno = EEXIST;
+ break;
+ case ERROR_NOT_ENOUGH_MEMORY:
+ case ERROR_OUTOFMEMORY:
+ errno = ENOMEM;
+ break;
+ case ERROR_INVALID_HANDLE:
+ errno = EBADF;
+ break;
+ case ERROR_INVALID_FUNCTION:
+ errno = EFAULT;
+ break;
+ default:
+ errno = EINVAL;
+ break;
+ }
+ return -1;
+}
+
+
+#define HAS_FLAG(a,b) (((a) & (b)) == (b))
int FS(swopen) (const wchar_t* filename, int oflag, int shflag, int pmode)
{
/* Construct access mode. */
+ /* https://docs.microsoft.com/en-us/windows/win32/fileio/file-access-rights-constants */
DWORD dwDesiredAccess = 0;
if (HAS_FLAG (oflag, _O_RDONLY))
- dwDesiredAccess |= GENERIC_READ | FILE_READ_DATA | FILE_READ_ATTRIBUTES;
+ dwDesiredAccess |= GENERIC_READ;
if (HAS_FLAG (oflag, _O_RDWR))
- dwDesiredAccess |= GENERIC_WRITE | GENERIC_READ | FILE_READ_DATA |
- FILE_WRITE_DATA | FILE_READ_ATTRIBUTES |
- FILE_WRITE_ATTRIBUTES;
- if (HAS_FLAG (oflag, _O_WRONLY))
- dwDesiredAccess|= GENERIC_WRITE | FILE_WRITE_DATA | FILE_WRITE_ATTRIBUTES;
+ dwDesiredAccess |= GENERIC_WRITE | GENERIC_READ;
+ if (HAS_FLAG (oflag, _O_WRONLY))
+ dwDesiredAccess |= GENERIC_WRITE;
+ if (HAS_FLAG (oflag, _O_APPEND))
+ dwDesiredAccess |= FILE_APPEND_DATA;
/* Construct shared mode. */
- DWORD dwShareMode = FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE;
+ /* https://docs.microsoft.com/en-us/windows/win32/fileio/file-attribute-constants */
+ DWORD dwShareMode = FILE_SHARE_READ | FILE_SHARE_WRITE;
if (HAS_FLAG (shflag, _SH_DENYRW))
dwShareMode &= ~(FILE_SHARE_READ | FILE_SHARE_WRITE);
if (HAS_FLAG (shflag, _SH_DENYWR))
@@ -138,21 +202,33 @@ int FS(swopen) (const wchar_t* filename, int oflag, int shflag, int pmode)
}
/* Create file disposition. */
- DWORD dwCreationDisposition = OPEN_EXISTING;
- if (HAS_FLAG (oflag, _O_CREAT))
- dwCreationDisposition = OPEN_ALWAYS;
+ /* https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilea */
+ DWORD dwCreationDisposition = 0;
if (HAS_FLAG (oflag, (_O_CREAT | _O_EXCL)))
- dwCreationDisposition = CREATE_NEW;
- if (HAS_FLAG (oflag, _O_TRUNC) && !HAS_FLAG (oflag, _O_CREAT))
- dwCreationDisposition = TRUNCATE_EXISTING;
+ dwCreationDisposition |= CREATE_NEW;
+ else if (HAS_FLAG (oflag, _O_TRUNC | _O_CREAT))
+ dwCreationDisposition |= CREATE_ALWAYS;
+ else if (HAS_FLAG (oflag, _O_TRUNC) && !HAS_FLAG (oflag, O_RDONLY))
+ dwCreationDisposition |= TRUNCATE_EXISTING;
+ else if (HAS_FLAG (oflag, _O_APPEND | _O_CREAT))
+ dwCreationDisposition |= OPEN_ALWAYS;
+ else if (HAS_FLAG (oflag, _O_APPEND))
+ dwCreationDisposition |= OPEN_EXISTING;
+ else if (HAS_FLAG (oflag, _O_CREAT))
+ dwCreationDisposition |= OPEN_ALWAYS;
+ else
+ dwCreationDisposition |= OPEN_EXISTING;
/* Set file access attributes. */
DWORD dwFlagsAndAttributes = FILE_ATTRIBUTE_NORMAL;
if (HAS_FLAG (oflag, _O_RDONLY))
dwFlagsAndAttributes |= 0; /* No special attribute. */
- if (HAS_FLAG (oflag, (_O_CREAT | _O_TEMPORARY)))
- dwFlagsAndAttributes |= FILE_FLAG_DELETE_ON_CLOSE;
- if (HAS_FLAG (oflag, (_O_CREAT | _O_SHORT_LIVED)))
+ if (HAS_FLAG (oflag, _O_TEMPORARY))
+ {
+ dwFlagsAndAttributes |= FILE_FLAG_DELETE_ON_CLOSE;
+ dwShareMode |= FILE_SHARE_DELETE;
+ }
+ if (HAS_FLAG (oflag, _O_SHORT_LIVED))
dwFlagsAndAttributes |= FILE_ATTRIBUTE_TEMPORARY;
if (HAS_FLAG (oflag, _O_RANDOM))
dwFlagsAndAttributes |= FILE_FLAG_RANDOM_ACCESS;
@@ -162,6 +238,11 @@ int FS(swopen) (const wchar_t* filename, int oflag, int shflag, int pmode)
if (dwFlagsAndAttributes != FILE_ATTRIBUTE_NORMAL)
dwFlagsAndAttributes &= ~FILE_ATTRIBUTE_NORMAL;
+ /* Ensure we have shared read for files which are opened read-only. */
+ if (HAS_FLAG (dwCreationDisposition, OPEN_EXISTING)
+ && ((dwDesiredAccess & (GENERIC_WRITE|GENERIC_READ)) == GENERIC_READ))
+ dwShareMode |= FILE_SHARE_READ;
+
/* Set security attributes. */
SECURITY_ATTRIBUTES securityAttributes;
ZeroMemory (&securityAttributes, sizeof(SECURITY_ATTRIBUTES));
@@ -169,41 +250,39 @@ int FS(swopen) (const wchar_t* filename, int oflag, int shflag, int pmode)
securityAttributes.lpSecurityDescriptor = NULL;
securityAttributes.nLength = sizeof(SECURITY_ATTRIBUTES);
- wchar_t* _filename = __hs_create_device_name (filename);
+ wchar_t* _filename = FS(create_device_name) (filename);
if (!_filename)
return -1;
HANDLE hResult
= CreateFileW (_filename, dwDesiredAccess, dwShareMode, &securityAttributes,
dwCreationDisposition, dwFlagsAndAttributes, NULL);
+
free (_filename);
if (INVALID_HANDLE_VALUE == hResult)
- return -1;
+ return setErrNoFromWin32Error ();
/* Now we have a Windows handle, we have to convert it to an FD and apply
the remaining flags. */
const int flag_mask = _O_APPEND | _O_RDONLY | _O_TEXT | _O_WTEXT;
int fd = _open_osfhandle ((intptr_t)hResult, oflag & flag_mask);
if (-1 == fd)
- return -1;
+ return setErrNoFromWin32Error ();
/* Finally we can change the mode to the requested one. */
const int mode_mask = _O_TEXT | _O_BINARY | _O_U16TEXT | _O_U8TEXT | _O_WTEXT;
if ((oflag & mode_mask) && (-1 == _setmode (fd, oflag & mode_mask)))
- return -1;
+ return setErrNoFromWin32Error ();
return fd;
}
-FILE *FS(fwopen) (const wchar_t* filename, const wchar_t* mode)
+int FS(translate_mode) (const wchar_t* mode)
{
- int shflag = 0;
- int pmode = 0;
- int oflag = 0;
-
+ int oflag = 0;
int len = wcslen (mode);
int i;
- #define IS_EXT(X) ((i < (len - 1)) && mode[i] == X)
+ #define IS_EXT(X) ((i < (len - 1)) && mode[i+1] == X)
for (i = 0; i < len; i++)
{
@@ -261,7 +340,19 @@ FILE *FS(fwopen) (const wchar_t* filename, const wchar_t* mode)
}
#undef IS_EXT
+ return oflag;
+}
+
+FILE *FS(fwopen) (const wchar_t* filename, const wchar_t* mode)
+{
+ int shflag = 0;
+ int pmode = 0;
+ int oflag = FS(translate_mode) (mode);
+
int fd = FS(swopen) (filename, oflag, shflag, pmode);
+ if (fd < 0)
+ return NULL;
+
FILE* file = _wfdopen (fd, mode);
return file;
}
@@ -281,8 +372,150 @@ FILE *FS(fopen) (const char* filename, const char* mode)
FILE *result = FS(fwopen) (w_filename, w_mode);
free (w_filename);
free (w_mode);
+
+ return result;
+}
+
+int FS(sopen) (const char* filename, int oflag, int shflag, int pmode)
+{
+ size_t len = mbstowcs (NULL, filename, 0);
+ wchar_t *w_filename = malloc (sizeof (wchar_t) * (len + 1));
+ mbstowcs (w_filename, filename, len);
+ w_filename[len] = L'\0';
+
+ int result = FS(swopen) (w_filename, oflag, shflag, pmode);
+ free (w_filename);
+
+ return result;
+}
+
+int FS(_stat) (const char *path, struct _stat *buffer)
+{
+ size_t len = mbstowcs (NULL, path, 0);
+ wchar_t *w_path = malloc (sizeof (wchar_t) * (len + 1));
+ mbstowcs (w_path, path, len);
+ w_path[len] = L'\0';
+
+ int result = FS(_wstat) (w_path, buffer);
+ free (w_path);
+
return result;
}
+
+int FS(_stat64) (const char *path, struct __stat64 *buffer)
+{
+ size_t len = mbstowcs (NULL, path, 0);
+ wchar_t *w_path = malloc (sizeof (wchar_t) * (len + 1));
+ mbstowcs (w_path, path, len);
+ w_path[len] = L'\0';
+
+ int result = FS(_wstat64) (w_path, buffer);
+ free (w_path);
+
+ return result;
+}
+
+static __time64_t ftToPosix(FILETIME ft)
+{
+ /* takes the last modified date. */
+ LARGE_INTEGER date, adjust;
+ date.HighPart = ft.dwHighDateTime;
+ date.LowPart = ft.dwLowDateTime;
+
+ /* 100-nanoseconds = milliseconds * 10000. */
+ /* A UNIX timestamp contains the number of seconds from Jan 1, 1970, while the
+ FILETIME documentation says: Contains a 64-bit value representing the
+ number of 100-nanosecond intervals since January 1, 1601 (UTC).
+
+ Between Jan 1, 1601 and Jan 1, 1970 there are 11644473600 seconds */
+ adjust.QuadPart = 11644473600000 * 10000;
+
+ /* removes the diff between 1970 and 1601. */
+ date.QuadPart -= adjust.QuadPart;
+
+ /* converts back from 100-nanoseconds to seconds. */
+ return (__time64_t)date.QuadPart / 10000000;
+}
+
+int FS(_wstat) (const wchar_t *path, struct _stat *buffer)
+{
+ ZeroMemory (buffer, sizeof (struct _stat));
+ wchar_t* _path = FS(create_device_name) (path);
+ if (!_path)
+ return -1;
+
+ /* Construct shared mode. */
+ DWORD dwShareMode = FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE;
+ DWORD dwDesiredAccess = FILE_READ_ATTRIBUTES;
+ DWORD dwFlagsAndAttributes = FILE_FLAG_BACKUP_SEMANTICS;
+ DWORD dwCreationDisposition = OPEN_EXISTING;
+
+ SECURITY_ATTRIBUTES securityAttributes;
+ ZeroMemory (&securityAttributes, sizeof(SECURITY_ATTRIBUTES));
+ securityAttributes.bInheritHandle = false;
+ securityAttributes.lpSecurityDescriptor = NULL;
+ securityAttributes.nLength = sizeof(SECURITY_ATTRIBUTES);
+
+ HANDLE hResult
+ = CreateFileW (_path, dwDesiredAccess, dwShareMode, &securityAttributes,
+ dwCreationDisposition, dwFlagsAndAttributes, NULL);
+
+ if (INVALID_HANDLE_VALUE == hResult)
+ {
+ free (_path);
+ return setErrNoFromWin32Error ();
+ }
+
+ WIN32_FILE_ATTRIBUTE_DATA finfo;
+ ZeroMemory (&finfo, sizeof (WIN32_FILE_ATTRIBUTE_DATA));
+ if(!GetFileAttributesExW (_path, GetFileExInfoStandard, &finfo))
+ {
+ free (_path);
+ CloseHandle (hResult);
+ return setErrNoFromWin32Error ();
+ }
+
+ unsigned short mode = _S_IREAD;
+
+ if (finfo.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
+ mode |= (_S_IFDIR | _S_IEXEC);
+ else
+ {
+ mode |= _S_IFREG;
+ DWORD type;
+ if (GetBinaryTypeW (_path, &type))
+ mode |= _S_IEXEC;
+ }
+
+ if (!(finfo.dwFileAttributes & FILE_ATTRIBUTE_READONLY))
+ mode |= _S_IWRITE;
+
+ buffer->st_mode = mode;
+ buffer->st_nlink = 1;
+ buffer->st_size = ((uint64_t)finfo.nFileSizeHigh << 32) + finfo.nFileSizeLow;
+ buffer->st_atime = ftToPosix (finfo.ftLastAccessTime);
+ buffer->st_mtime = buffer->st_ctime = ftToPosix (finfo.ftLastWriteTime);
+ free (_path);
+ CloseHandle (hResult);
+ return 0;
+}
+
+int FS(_wstat64) (const wchar_t *path, struct __stat64 *buffer)
+{
+ struct _stat buf;
+ ZeroMemory (buffer, sizeof (struct __stat64));
+
+ int result = FS(_wstat) (path, &buf);
+
+ buffer->st_mode = buf.st_mode;
+ buffer->st_nlink = 1;
+ buffer->st_size = buf.st_size;
+ buffer->st_atime = buf.st_atime;
+ buffer->st_mtime = buf.st_mtime;
+
+ return result;
+}
+
#else
FILE *FS(fopen) (const char* filename, const char* mode)
{