blob: 0344ab7f913a1be6fdb7202bc4dbd0062d649608 [file] [log] [blame]
From a137bb56674eca0b8d2592e870a1d2595a9eb934 Mon Sep 17 00:00:00 2001
From: Jeremy Tan <jtanx@outlook.com>
Date: Sat, 15 Jun 2019 14:40:52 +1000
Subject: [PATCH] Update g_fopen, g_open and g_creat to open with
FILE_SHARE_DELETE sharing access
Very loosely based on the patches in
https://gitlab.gnome.org/GNOME/glib/issues/539
but with much more robust file mode parsing and error handling.
Implements most of the definition as provided on msdn for fopen.
If charcter conversion is requested (via _O_U8TEXT, _O_U16TEXT, _O_WTEXT or any
of the 'ccs=utf8/utf-16le/unicode'), g_fopen and g_open fall back to use
_wfopen and _wopen respectively, as there is no easy way to replicate the
expected behaviour of those modes, particularly around BOM handling.
---
glib/gstdio.c | 304 ++++++++++++++++++++++++++++++++++++++++++++------
1 file changed, 267 insertions(+), 37 deletions(-)
diff --git a/glib/gstdio.c b/glib/gstdio.c
index 653c8a3a1..c34440424 100644
--- a/glib/gstdio.c
+++ b/glib/gstdio.c
@@ -1035,21 +1035,146 @@ g_open (const gchar *filename,
int mode)
{
#ifdef G_OS_WIN32
- wchar_t *wfilename = g_utf8_to_utf16 (filename, -1, NULL, NULL, NULL);
+ HANDLE file_handle;
+ DWORD disposition;
+ DWORD desired_access;
+ DWORD flags_and_attributes;
+ DWORD last_error;
+ SECURITY_ATTRIBUTES security_attributes;
+ wchar_t *wfilename;
+ int r;
int retval;
int save_errno;
-
+
+ wfilename = g_utf8_to_utf16 (filename, -1, NULL, NULL, NULL);
if (wfilename == NULL)
{
errno = EINVAL;
return -1;
}
- retval = _wopen (wfilename, flags, mode);
- save_errno = errno;
+ /* If any of these flags are specified, fall back to _wopen */
+ if (flags & (_O_U8TEXT | _O_U16TEXT | _O_WTEXT))
+ {
+ retval = _wopen (wfilename, flags, mode);
+ save_errno = errno;
+
+ g_free (wfilename);
+ errno = save_errno;
+ return retval;
+ }
+
+ /* Set up the access mode; exactly one of these must be specified */
+ switch (flags & (_O_RDONLY | _O_WRONLY | _O_RDWR))
+ {
+ case _O_RDONLY:
+ desired_access = GENERIC_READ;
+ break;
+ case _O_WRONLY:
+ desired_access = GENERIC_WRITE;
+ break;
+ case _O_RDWR:
+ desired_access = GENERIC_READ | GENERIC_WRITE;
+ break;
+ default:
+ g_free (wfilename);
+ errno = EINVAL;
+ return -1;
+ }
+
+ /* Parse the creation disposition */
+ switch (flags & (_O_CREAT | _O_EXCL | _O_TRUNC))
+ {
+ case _O_CREAT:
+ disposition = OPEN_ALWAYS;
+ break;
+ case _O_CREAT | _O_TRUNC:
+ disposition = CREATE_ALWAYS;
+ break;
+ case _O_CREAT | _O_EXCL:
+ case _O_CREAT | _O_TRUNC | _O_EXCL:
+ disposition = CREATE_NEW;
+ break;
+ case _O_TRUNC:
+ case _O_TRUNC | _O_EXCL:
+ disposition = TRUNCATE_EXISTING;
+ break;
+ default:
+ disposition = OPEN_EXISTING;
+ }
+ if (!(desired_access & GENERIC_WRITE) && disposition == TRUNCATE_EXISTING)
+ {
+ /* Must have GENERIC_WRITE to be able to truncate */
+ g_free (wfilename);
+ errno = EINVAL;
+ return -1;
+ }
+
+ /* Set up the security descriptor */
+ security_attributes.nLength = sizeof(security_attributes);
+ security_attributes.lpSecurityDescriptor = NULL;
+ security_attributes.bInheritHandle = (flags & _O_NOINHERIT) != _O_NOINHERIT;
+
+ flags_and_attributes = 0;
+ if ((flags & _O_CREAT) && !(mode & _S_IWRITE))
+ flags_and_attributes |= FILE_ATTRIBUTE_READONLY;
+ if (flags & _O_TEMPORARY)
+ {
+ flags_and_attributes |= FILE_FLAG_DELETE_ON_CLOSE;
+ desired_access |= DELETE;
+ }
+ if (flags & _O_SHORT_LIVED)
+ flags_and_attributes |= FILE_ATTRIBUTE_TEMPORARY;
+ if (flags & _O_SEQUENTIAL)
+ flags_and_attributes |= FILE_FLAG_SEQUENTIAL_SCAN;
+ if (flags & _O_RANDOM)
+ flags_and_attributes |= FILE_FLAG_RANDOM_ACCESS;
+
+ file_handle = CreateFileW (wfilename,
+ desired_access,
+ FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE,
+ &security_attributes,
+ disposition,
+ flags_and_attributes,
+ NULL);
+ last_error = GetLastError ();
g_free (wfilename);
+ if (file_handle == INVALID_HANDLE_VALUE)
+ {
+ errno = w32_error_to_errno (last_error);
+ return -1;
+ }
+
+ retval = _open_osfhandle ((intptr_t)file_handle, flags);
+ save_errno = errno;
+
+ if (retval == -1)
+ {
+ CloseHandle (file_handle);
+ errno = save_errno;
+ return -1;
+ }
+
+ flags &= _O_BINARY | _O_TEXT;
+ if (!flags)
+ /* No explicit specification; try the global mode */
+ flags = _fmode & (_O_BINARY | _O_TEXT);
+
+ if (flags & _O_BINARY)
+ r = _setmode (retval, _O_BINARY);
+ else
+ r = _setmode (retval, _O_TEXT);
+
+ if (r == -1)
+ {
+ save_errno = errno;
+ close (retval);
+ errno = save_errno;
+ return -1;
+ }
+
errno = save_errno;
return retval;
#else
@@ -1098,23 +1223,7 @@ g_creat (const gchar *filename,
int mode)
{
#ifdef G_OS_WIN32
- wchar_t *wfilename = g_utf8_to_utf16 (filename, -1, NULL, NULL, NULL);
- int retval;
- int save_errno;
-
- if (wfilename == NULL)
- {
- errno = EINVAL;
- return -1;
- }
-
- retval = _wcreat (wfilename, mode);
- save_errno = errno;
-
- g_free (wfilename);
-
- errno = save_errno;
- return retval;
+ return g_open (filename, _O_WRONLY|_O_CREAT|_O_TRUNC, mode);
#else
return creat (filename, mode);
#endif
@@ -1550,35 +1659,155 @@ g_fopen (const gchar *filename,
const gchar *mode)
{
#ifdef G_OS_WIN32
- wchar_t *wfilename = g_utf8_to_utf16 (filename, -1, NULL, NULL, NULL);
- wchar_t *wmode;
- FILE *retval;
+ const gchar *orig_mode = mode;
+ char filtered_mode[5];
+ char *filtered_mode_iter;
+ gboolean has_ccs;
+ gboolean has_commit;
+ int fd;
+ int flags;
int save_errno;
+ FILE *retval;
- if (wfilename == NULL)
+ if (filename == NULL || mode == NULL)
+ goto err_einval;
+
+ filtered_mode_iter = filtered_mode;
+
+ while (*mode == ' ') ++mode;
+
+ switch (*mode)
{
- errno = EINVAL;
- return NULL;
+ case 'r':
+ flags = _O_RDONLY;
+ break;
+ case 'w':
+ flags = _O_WRONLY | _O_CREAT | _O_TRUNC;
+ break;
+ case 'a':
+ flags = _O_WRONLY | _O_CREAT | _O_APPEND;
+ break;
+ default:
+ goto err_einval;
}
- wmode = g_utf8_to_utf16 (mode, -1, NULL, NULL, NULL);
+ *filtered_mode_iter++ = *mode++;
- if (wmode == NULL)
+#define CHECK_AND_ADD(chk, add) \
+ do { \
+ if (flags & (chk)) \
+ goto err_einval; \
+ flags |= (add); \
+ } while (0)
+
+ for (has_ccs = FALSE, has_commit = FALSE; *mode && !has_ccs; ++mode)
+ {
+ switch (*mode)
+ {
+ case '+':
+ CHECK_AND_ADD (_O_RDWR, _O_RDWR);
+ flags &= ~(_O_RDONLY | _O_WRONLY);
+ if ((filtered_mode_iter - filtered_mode) > 1)
+ {
+ *filtered_mode_iter++ = filtered_mode[1];
+ filtered_mode[1] = '+';
+ }
+ else
+ *filtered_mode_iter++ = '+';
+ break;
+ case 't':
+ CHECK_AND_ADD (_O_TEXT | _O_BINARY, _O_TEXT);
+ *filtered_mode_iter++ = 't';
+ break;
+ case 'b':
+ CHECK_AND_ADD (_O_TEXT | _O_BINARY, _O_BINARY);
+ *filtered_mode_iter++ = 'b';
+ break;
+ case 'N':
+ CHECK_AND_ADD (_O_NOINHERIT, _O_NOINHERIT);
+ break;
+ case 'S':
+ CHECK_AND_ADD (_O_SEQUENTIAL, _O_SEQUENTIAL);
+ break;
+ case 'R':
+ CHECK_AND_ADD (_O_RANDOM, _O_RANDOM);
+ break;
+ case 'T':
+ CHECK_AND_ADD (_O_SHORT_LIVED, _O_SHORT_LIVED);
+ break;
+ case 'D':
+ CHECK_AND_ADD (_O_TEMPORARY, _O_TEMPORARY);
+ break;
+ case 'x':
+ CHECK_AND_ADD (_O_EXCL, _O_EXCL);
+ break;
+
+ case 'c':
+ case 'n':
+ if (has_commit)
+ goto err_einval;
+ has_commit = TRUE;
+ *filtered_mode_iter++ = *mode;
+ /* fallthrough */
+ case ' ':
+ break;
+
+ case ',':
+ has_ccs = TRUE;
+ break;
+
+ default:
+ goto err_einval;
+ }
+ }
+
+#undef CHECK_AND_ADD
+
+ *filtered_mode_iter = 0;
+ while (*mode == ' ') ++mode;
+
+ /*
+ * If a ccs is specified, fall back to using _wfopen, as
+ * there's no easy way to handle this
+ */
+ if (has_ccs)
{
+ wchar_t *wfilename = g_utf8_to_utf16 (filename, -1, NULL, NULL, NULL);
+ wchar_t *wmode = g_utf8_to_utf16 (orig_mode, -1, NULL, NULL, NULL);
+
+ _g_win32_fix_mode (wmode);
+ retval = _wfopen (wfilename, wmode);
+ save_errno = errno;
+
+ g_free (wmode);
g_free (wfilename);
- errno = EINVAL;
- return NULL;
+
+ errno = save_errno;
+ return retval;
}
- _g_win32_fix_mode (wmode);
- retval = _wfopen (wfilename, wmode);
- save_errno = errno;
+ if (*mode)
+ goto err_einval;
- g_free (wfilename);
- g_free (wmode);
+ fd = g_open (filename, flags, (_S_IREAD | _S_IWRITE));
+
+ if (fd == -1)
+ /* 'errno' will have already been set by 'g_open()' */
+ return NULL;
+
+ retval = _fdopen (fd, filtered_mode);
+ if (retval == NULL)
+ {
+ save_errno = errno;
+ close (fd);
+ errno = save_errno;
+ }
- errno = save_errno;
return retval;
+
+err_einval:
+ errno = EINVAL;
+ return NULL;
#else
return fopen (filename, mode);
#endif
@@ -1619,6 +1848,7 @@ g_freopen (const gchar *filename,
}
wmode = g_utf8_to_utf16 (mode, -1, NULL, NULL, NULL);
+ _g_win32_fix_mode (wmode);
if (wmode == NULL)
{
--
2.22.0