aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--NEWS7
-rw-r--r--src/Makefile.am2
-rw-r--r--src/estream.c125
-rw-r--r--src/gpgrt-int.h1
-rw-r--r--src/protos.h30
-rw-r--r--src/sysutils.c50
-rw-r--r--src/w32-gettext.c39
7 files changed, 232 insertions, 22 deletions
diff --git a/NEWS b/NEWS
index 20026db..dcd316a 100644
--- a/NEWS
+++ b/NEWS
@@ -1,6 +1,9 @@
Noteworthy changes in version 1.39 (unreleased) [C29/A29/R1]
-----------------------------------------------
+ * On Windows gpgrt_fopen can now handle UTF-8 names.
+
+ * On Windows gpgrt_chdir and gpgrt_mkdir can now handle UTF-8 names.
* Interface changes relative to the 1.38 release:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -10,8 +13,8 @@ Noteworthy changes in version 1.39 (unreleased) [C29/A29/R1]
Noteworthy changes in version 1.38 (2020-05-29) [C29/A29/R0]
-----------------------------------------------
- * New option parser features to implement system wide configuration
- files.
+ * New option parser with features to implement system wide
+ configuration files.
* New functions to build file names.
diff --git a/src/Makefile.am b/src/Makefile.am
index b2bffd2..fc3acc3 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -194,7 +194,7 @@ libgpg_error_la_LDFLAGS = \
@LIBGPG_ERROR_LT_CURRENT@:@LIBGPG_ERROR_LT_REVISION@:@LIBGPG_ERROR_LT_AGE@
libgpg_error_la_SOURCES = gettext.h $(arch_sources) \
- gpgrt-int.h init.c init.h version.c lock.h thread.h \
+ gpgrt-int.h protos.h init.c init.h version.c lock.h thread.h \
estream.c estream-printf.c estream-printf.h \
strsource.c strerror.c code-to-errno.c code-from-errno.c \
visibility.c visibility.h \
diff --git a/src/estream.c b/src/estream.c
index 06ebdaa..0d37c1a 100644
--- a/src/estream.c
+++ b/src/estream.c
@@ -238,7 +238,8 @@ mem_free (void *p)
/*
* A Windows helper function to map a W32 API error code to a standard
- * system error code.
+ * system error code. That actually belong into sysutils but to allow
+ * standalone use of estream we keep it here.
*/
#ifdef HAVE_W32_SYSTEM
static int
@@ -256,11 +257,19 @@ map_w32_to_errno (DWORD w32_err)
return ENOENT;
case ERROR_ACCESS_DENIED:
- return EPERM;
+ return EPERM; /* ReactOS uses EACCES ("Permission denied") and
+ * is likely right because they used an
+ * undocumented function to associate the error
+ * codes. However we have always used EPERM
+ * ("Operation not permitted", e.g. function is
+ * required to be called by root) and we better
+ * stick to that to avoid surprising bugs. */
case ERROR_INVALID_HANDLE:
+ return EBADF;
+
case ERROR_INVALID_BLOCK:
- return EINVAL;
+ return ENOMEM;
case ERROR_NOT_ENOUGH_MEMORY:
return ENOMEM;
@@ -268,10 +277,90 @@ map_w32_to_errno (DWORD w32_err)
case ERROR_NO_DATA:
return EPIPE;
+ case ERROR_ALREADY_EXISTS:
+ return EEXIST;
+
+ /* This mapping has been taken from reactOS. */
+ case ERROR_TOO_MANY_OPEN_FILES: return EMFILE;
+ case ERROR_ARENA_TRASHED: return ENOMEM;
+ case ERROR_BAD_ENVIRONMENT: return E2BIG;
+ case ERROR_BAD_FORMAT: return ENOEXEC;
+ case ERROR_INVALID_DRIVE: return ENOENT;
+ case ERROR_CURRENT_DIRECTORY: return EACCES;
+ case ERROR_NOT_SAME_DEVICE: return EXDEV;
+ case ERROR_NO_MORE_FILES: return ENOENT;
+ case ERROR_WRITE_PROTECT: return EACCES;
+ case ERROR_BAD_UNIT: return EACCES;
+ case ERROR_NOT_READY: return EACCES;
+ case ERROR_BAD_COMMAND: return EACCES;
+ case ERROR_CRC: return EACCES;
+ case ERROR_BAD_LENGTH: return EACCES;
+ case ERROR_SEEK: return EACCES;
+ case ERROR_NOT_DOS_DISK: return EACCES;
+ case ERROR_SECTOR_NOT_FOUND: return EACCES;
+ case ERROR_OUT_OF_PAPER: return EACCES;
+ case ERROR_WRITE_FAULT: return EACCES;
+ case ERROR_READ_FAULT: return EACCES;
+ case ERROR_GEN_FAILURE: return EACCES;
+ case ERROR_SHARING_VIOLATION: return EACCES;
+ case ERROR_LOCK_VIOLATION: return EACCES;
+ case ERROR_WRONG_DISK: return EACCES;
+ case ERROR_SHARING_BUFFER_EXCEEDED: return EACCES;
+ case ERROR_BAD_NETPATH: return ENOENT;
+ case ERROR_NETWORK_ACCESS_DENIED: return EACCES;
+ case ERROR_BAD_NET_NAME: return ENOENT;
+ case ERROR_FILE_EXISTS: return EEXIST;
+ case ERROR_CANNOT_MAKE: return EACCES;
+ case ERROR_FAIL_I24: return EACCES;
+ case ERROR_NO_PROC_SLOTS: return EAGAIN;
+ case ERROR_DRIVE_LOCKED: return EACCES;
+ case ERROR_BROKEN_PIPE: return EPIPE;
+ case ERROR_DISK_FULL: return ENOSPC;
+ case ERROR_INVALID_TARGET_HANDLE: return EBADF;
+ case ERROR_WAIT_NO_CHILDREN: return ECHILD;
+ case ERROR_CHILD_NOT_COMPLETE: return ECHILD;
+ case ERROR_DIRECT_ACCESS_HANDLE: return EBADF;
+ case ERROR_SEEK_ON_DEVICE: return EACCES;
+ case ERROR_DIR_NOT_EMPTY: return ENOTEMPTY;
+ case ERROR_NOT_LOCKED: return EACCES;
+ case ERROR_BAD_PATHNAME: return ENOENT;
+ case ERROR_MAX_THRDS_REACHED: return EAGAIN;
+ case ERROR_LOCK_FAILED: return EACCES;
+ case ERROR_INVALID_STARTING_CODESEG: return ENOEXEC;
+ case ERROR_INVALID_STACKSEG: return ENOEXEC;
+ case ERROR_INVALID_MODULETYPE: return ENOEXEC;
+ case ERROR_INVALID_EXE_SIGNATURE: return ENOEXEC;
+ case ERROR_EXE_MARKED_INVALID: return ENOEXEC;
+ case ERROR_BAD_EXE_FORMAT: return ENOEXEC;
+ case ERROR_ITERATED_DATA_EXCEEDS_64k: return ENOEXEC;
+ case ERROR_INVALID_MINALLOCSIZE: return ENOEXEC;
+ case ERROR_DYNLINK_FROM_INVALID_RING: return ENOEXEC;
+ case ERROR_IOPL_NOT_ENABLED: return ENOEXEC;
+ case ERROR_INVALID_SEGDPL: return ENOEXEC;
+ case ERROR_AUTODATASEG_EXCEEDS_64k: return ENOEXEC;
+ case ERROR_RING2SEG_MUST_BE_MOVABLE: return ENOEXEC;
+ case ERROR_RELOC_CHAIN_XEEDS_SEGLIM: return ENOEXEC;
+ case ERROR_INFLOOP_IN_RELOC_CHAIN: return ENOEXEC;
+ case ERROR_FILENAME_EXCED_RANGE: return ENOENT;
+ case ERROR_NESTING_NOT_ALLOWED: return EAGAIN;
+ case ERROR_NOT_ENOUGH_QUOTA: return ENOMEM;
+
default:
return EIO;
}
}
+
+/* Wrapper to be used by other modules to set ERRNO from the Windows
+ * error. EC may be -1 to get the last error. */
+void
+_gpgrt_w32_set_errno (int ec)
+{
+ if (ec == -1)
+ ec = GetLastError ();
+ _set_errno (map_w32_to_errno (ec));
+}
+
+
#endif /*HAVE_W32_SYSTEM*/
/*
@@ -1578,6 +1667,18 @@ static struct cookie_io_functions_s estream_functions_fp =
* operations ares handled by file descriptor based I/O.
*/
+#ifdef HAVE_W32_SYSTEM
+static int
+any8bitchar (const char *string)
+{
+ if (string)
+ for ( ; *string; string++)
+ if ((*string & 0x80))
+ return 1;
+ return 0;
+}
+#endif /*HAVE_W32_SYSTEM*/
+
/* Create function for objects identified by a file name. */
static int
func_file_create (void **cookie, int *filedes,
@@ -1596,7 +1697,25 @@ func_file_create (void **cookie, int *filedes,
goto out;
}
+#ifdef HAVE_W32_SYSTEM
+ if (any8bitchar (path))
+ {
+ wchar_t *wpath;
+
+ wpath = _gpgrt_utf8_to_wchar (path);
+ if (!wpath)
+ fd = -1;
+ else
+ {
+ fd = _wopen (wpath, modeflags, cmode);
+ _gpgrt_free_wchar (wpath);
+ }
+ }
+ else /* Avoid unnecessary conversion. */
+ fd = open (path, modeflags, cmode);
+#else
fd = open (path, modeflags, cmode);
+#endif
if (fd == -1)
{
err = -1;
diff --git a/src/gpgrt-int.h b/src/gpgrt-int.h
index 913b9ba..58b156f 100644
--- a/src/gpgrt-int.h
+++ b/src/gpgrt-int.h
@@ -23,6 +23,7 @@
#include "gpg-error.h"
#include "visibility.h"
+#include "protos.h"
/*
* Internal i18n macros.
diff --git a/src/protos.h b/src/protos.h
new file mode 100644
index 0000000..9a8d57a
--- /dev/null
+++ b/src/protos.h
@@ -0,0 +1,30 @@
+/* protos.h - Miscellaneous prototypes
+ * Copyright (C) 2020 g10 Code GmbH
+ *
+ * This file is part of libgpg-error.
+ *
+ * libgpg-error is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * libgpg-error is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, see <https://www.gnu.org/licenses/>.
+ * SPDX-License-Identifier: LGPL-2.1+
+ */
+
+#ifndef _GPGRT_PROTOS_H
+#define _GPGRT_PROTOS_H
+
+/*-- w32-gettext.c --*/
+wchar_t *_gpgrt_utf8_to_wchar (const char *string);
+void _gpgrt_free_wchar (wchar_t *wstring);
+void _gpgrt_w32_set_errno (int ec);
+
+
+#endif /*_GPGRT_PROTOS_H*/
diff --git a/src/sysutils.c b/src/sysutils.c
index bc446fb..eb5249a 100644
--- a/src/sysutils.c
+++ b/src/sysutils.c
@@ -269,29 +269,34 @@ modestr_to_mode (const char *modestr)
* write allowed, execution allowed with the first group for the user,
* the second for the group and the third for all others. If the
* string is shorter than above the missing mode characters are meant
- * to be not set. */
+ * to be not set.
+ *
+ * Note that in addition to returning an gpg-error error code ERRNO is
+ * also set by this function.
+ */
gpg_err_code_t
_gpgrt_mkdir (const char *name, const char *modestr)
{
-#ifdef HAVE_W32CE_SYSTEM
+#ifdef HAVE_W32_SYSTEM
wchar_t *wname;
+ gpg_err_code_t ec;
(void)modestr;
- wname = utf8_to_wchar (name);
+ /* Note: Fixme: We should set appropriate permissions. */
+ wname = _gpgrt_utf8_to_wchar (name);
if (!wname)
return _gpg_err_code_from_syserror ();
if (!CreateDirectoryW (wname, NULL))
{
- xfree (wname);
- return _gpg_err_code_from_syserror ();
+ _gpgrt_w32_set_errno (-1);
+ ec = _gpg_err_code_from_syserror ();
}
- xfree (wname);
- return 0;
+ else
+ ec = 0;
+ _gpgrt_free_wchar (wname);
+ return ec;
#elif MKDIR_TAKES_ONE_ARG
(void)modestr;
- /* Note: In the case of W32 we better use CreateDirectory and try to
- set appropriate permissions. However using mkdir is easier
- because this sets ERRNO. */
if (mkdir (name))
return _gpg_err_code_from_syserror ();
return 0;
@@ -304,13 +309,34 @@ _gpgrt_mkdir (const char *name, const char *modestr)
/* A simple wrapper around chdir. NAME is expected to be utf8
- * encoded. */
+ * encoded.
+ * Note that in addition to returning an gpg-error error code ERRNO is
+ * also set by this function. */
gpg_err_code_t
_gpgrt_chdir (const char *name)
{
+#ifdef HAVE_W32_SYSTEM
+ wchar_t *wname;
+ gpg_err_code_t ec;
+
+ wname = _gpgrt_utf8_to_wchar (name);
+ if (!wname)
+ return _gpg_err_code_from_syserror ();
+ if (!SetCurrentDirectoryW (wname))
+ {
+ _gpgrt_w32_set_errno (-1);
+ ec = _gpg_err_code_from_syserror ();
+ }
+ else
+ ec = 0;
+ _gpgrt_free_wchar (wname);
+ return ec;
+
+#else /*!HAVE_W32_SYSTEM*/
if (chdir (name))
return _gpg_err_code_from_syserror ();
return 0;
+#endif /*!HAVE_W32_SYSTEM*/
}
@@ -322,6 +348,7 @@ _gpgrt_getcwd (void)
char *buffer;
size_t size = 100;
+ /* FIXME: We need to support utf8 */
for (;;)
{
buffer = xtrymalloc (size+1);
@@ -389,6 +416,7 @@ _gpgrt_getusername (void)
char tmp[1];
DWORD size = 1;
+ /* FIXME: We need to support utf8 */
GetUserNameA (tmp, &size);
result = _gpgrt_malloc (size);
if (result && !GetUserNameA (result, &size))
diff --git a/src/w32-gettext.c b/src/w32-gettext.c
index 11e4f3d..a734ef2 100644
--- a/src/w32-gettext.c
+++ b/src/w32-gettext.c
@@ -52,6 +52,7 @@
#include "init.h"
#include "gpg-error.h"
+#include "protos.h"
/* Override values initialized by gpgrt_w32_override_locale. If NAME
* is not the empty string LANGID will be used. */
@@ -1354,15 +1355,23 @@ load_domain (const char *filename)
/* Return a malloced wide char string from an UTF-8 encoded input
string STRING. Caller must free this value. On failure returns
NULL. The result of calling this function with STRING set to NULL
- is not defined. */
+ is not defined. If LENGTH is zero and RETLEN NULL the fucntion
+ assumes that STRING is a nul-terminated string and returns a
+ (wchar_t)0-terminated string. */
static wchar_t *
utf8_to_wchar (const char *string, size_t length, size_t *retlen)
{
int n;
wchar_t *result;
size_t nbytes;
+ int cbmultibyte;
+
+ if (!length && !retlen)
+ cbmultibyte = -1;
+ else
+ cbmultibyte = length;
- n = MultiByteToWideChar (CP_UTF8, 0, string, length, NULL, 0);
+ n = MultiByteToWideChar (CP_UTF8, 0, string, cbmultibyte, NULL, 0);
if (n < 0 || (n+1) <= 0)
return NULL;
@@ -1376,17 +1385,38 @@ utf8_to_wchar (const char *string, size_t length, size_t *retlen)
if (!result)
return NULL;
- n = MultiByteToWideChar (CP_UTF8, 0, string, length, result, n);
+ n = MultiByteToWideChar (CP_UTF8, 0, string, cbmultibyte, result, n);
if (n < 0)
{
jnlib_free (result);
return NULL;
}
- *retlen = n;
+ if (retlen)
+ *retlen = n;
return result;
}
+/* Convert an UTF8 string to a WCHAR string. Caller should use
+ * _gpgrt_free_wchar to release the result. */
+wchar_t *
+_gpgrt_utf8_to_wchar (const char *string)
+{
+ return utf8_to_wchar (string, 0, NULL);
+}
+
+
+/* We provide a dedicated release function to be sure that we don't
+ * use a somehow mapped free function but the one which matches the
+ * used alloc. */
+void
+_gpgrt_free_wchar (wchar_t *wstring)
+{
+ if (wstring)
+ jnlib_free (wstring);
+}
+
+
/* Return a malloced string encoded in the native console codepage
from the wide char input string STRING.
Caller must free this value. On failure returns NULL.
@@ -1445,7 +1475,6 @@ utf8_to_native (const char *string, size_t length, size_t *retlen)
}
-
/* Specify that the DOMAINNAME message catalog will be found
in DIRNAME rather than in the system locale data base. */