diff options
Diffstat (limited to 'src/sysutils.c')
-rw-r--r-- | src/sysutils.c | 149 |
1 files changed, 118 insertions, 31 deletions
diff --git a/src/sysutils.c b/src/sysutils.c index 8f70a66..bc3a68d 100644 --- a/src/sysutils.c +++ b/src/sysutils.c @@ -39,20 +39,6 @@ #include "gpgrt-int.h" -#ifdef HAVE_W32_SYSTEM -/* Return true if STRING has any 8 bit character. */ -static int -any8bitchar (const char *string) -{ - if (string) - for ( ; *string; string++) - if ((*string & 0x80)) - return 1; - return 0; -} -#endif /*HAVE_W32_SYSTEM*/ - - /* Return true if FD is valid. */ int _gpgrt_fd_valid_p (int fd) @@ -238,6 +224,94 @@ _gpgrt_setenv (const char *name, const char *value, int overwrite) } +#ifdef HAVE_W32_SYSTEM +/* Convert an UTF-8 encode file name to wchar. If the file name is + * close to the limit of MAXPATH the API functions will fail. The + * method to overcome this API limitation is to use a prefix which + * bypasses the checking by CreateFile. This also required to first + * convert the name to an absolute file name. */ +wchar_t * +_gpgrt_fname_to_wchar (const char *fname) +{ + wchar_t *wname; + wchar_t *wfullpath = NULL; + int success = 0; + + wname = _gpgrt_utf8_to_wchar (fname); + if (!wname) + return NULL; + + if (!strncmp (fname, "\\\\?\\", 4)) + success = 1; /* Already translated. */ + else if (wcslen (wname) > 230) + { + int wlen = 1024; + int extralen; + DWORD res; + wchar_t *w; + + try_again: + wfullpath = xtrymalloc (wlen * sizeof *wfullpath); + if (!wfullpath) + goto leave; + + if (*fname == '\\' && fname[1] == '\\' && fname[2]) + { + wcscpy (wfullpath, L"\\\\?\\UNC\\"); + extralen = 8; + } + else + { + wcscpy (wfullpath, L"\\\\?\\"); + extralen = 4; + } + res = GetFullPathNameW (wname, wlen-extralen, wfullpath+extralen, NULL); + if (!res) + { + _gpgrt_w32_set_errno (-1); + goto leave; + } + else if (res >= wlen - extralen) + { + /* Truncated - increase to the desired length. */ + if (wlen > 1024) + { + /* We should never get to here. */ + errno = ENAMETOOLONG; + goto leave; + } + /* GetFullPathNameW indicated the required buffer length. */ + _gpgrt_free_wchar (wfullpath); + wfullpath = NULL; + wlen = res + extralen; + goto try_again; + } + _gpgrt_free_wchar (wname); + wname = wfullpath; + wfullpath = NULL; + /* Need to make sure that all slashes are mapped. */ + for (w = wname; *w; w++) + if (*w == L'/') + *w = L'\\'; + success = 1; + } + else + success = 1; + + leave: + _gpgrt_free_wchar (wfullpath); + if (!success) + { + _gpgrt_free_wchar (wname); + wname = NULL; + } + return wname; +} + +#endif /*HAVE_W32_SYSTEM*/ + + + #ifndef HAVE_W32_SYSTEM static mode_t modestr_to_mode (const char *modestr) @@ -296,9 +370,10 @@ _gpgrt_mkdir (const char *name, const char *modestr) (void)modestr; /* Note: Fixme: We should set appropriate permissions. */ - wname = _gpgrt_utf8_to_wchar (name); + wname = _gpgrt_fname_to_wchar (name); if (!wname) return _gpg_err_code_from_syserror (); + if (!CreateDirectoryW (wname, NULL)) { _gpgrt_w32_set_errno (-1); @@ -306,8 +381,10 @@ _gpgrt_mkdir (const char *name, const char *modestr) } else ec = 0; + _gpgrt_free_wchar (wname); return ec; + #elif MKDIR_TAKES_ONE_ARG (void)modestr; if (mkdir (name)) @@ -322,9 +399,8 @@ _gpgrt_mkdir (const char *name, const char *modestr) /* A simple wrapper around chdir. NAME is expected to be utf8 - * encoded. - * Note that in addition to returning an gpg-error error code ERRNO is - * also set by this function. */ + * 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) { @@ -332,6 +408,8 @@ _gpgrt_chdir (const char *name) wchar_t *wname; gpg_err_code_t ec; + /* Note that the \\?\ trick does not work with SetCurrentDirectoryW + * Thus we use the plain conversion function. */ wname = _gpgrt_utf8_to_wchar (name); if (!wname) return _gpg_err_code_from_syserror (); @@ -376,6 +454,8 @@ _gpgrt_getcwd (void) } else if (wlen > MAX_PATH) { + /* FWIW: I tried to use GetFullPathNameW (L".") but found no way + * to execute a test program at a too long cwd. */ _gpg_err_set_errno (ENAMETOOLONG); return NULL; } @@ -417,28 +497,35 @@ _gpgrt_access (const char *fname, int mode) gpg_err_code_t ec; #ifdef HAVE_W32_SYSTEM - if (any8bitchar (fname)) - { - wchar_t *wfname; + wchar_t *wfname; + DWORD attribs; - wfname = _gpgrt_utf8_to_wchar (fname); - if (!wfname) - ec = _gpg_err_code_from_syserror (); - else + wfname = _gpgrt_fname_to_wchar (fname); + if (!wfname) + return _gpg_err_code_from_syserror (); + + attribs = GetFileAttributesW (wfname); + if (attribs == (DWORD)(-1)) + ec = _gpgrt_w32_get_last_err_code (); + else + { + if ((mode & W_OK) && (attribs & FILE_ATTRIBUTE_READONLY)) { - ec = _waccess (wfname, mode)? _gpg_err_code_from_syserror () : 0; - _gpgrt_free_wchar (wfname); + _gpg_err_set_errno (EACCES); + ec = _gpg_err_code_from_syserror (); } + else + ec = 0; } - else -#endif /*HAVE_W32_SYSTEM*/ - ec = access (fname, mode)? _gpg_err_code_from_syserror () : 0; + _gpgrt_free_wchar (wfname); +#else /* Unix */ + ec = access (fname, mode)? _gpg_err_code_from_syserror () : 0; +#endif /* Unix */ return ec; } - /* Get the standard home directory for user NAME. If NAME is NULL the * directory for the current user is returned. Caller must release * the returned string. */ |