diff options
-rw-r--r-- | NEWS | 7 | ||||
-rw-r--r-- | src/Makefile.am | 2 | ||||
-rw-r--r-- | src/estream.c | 125 | ||||
-rw-r--r-- | src/gpgrt-int.h | 1 | ||||
-rw-r--r-- | src/protos.h | 30 | ||||
-rw-r--r-- | src/sysutils.c | 50 | ||||
-rw-r--r-- | src/w32-gettext.c | 39 |
7 files changed, 232 insertions, 22 deletions
@@ -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. */ |