aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorWerner Koch <[email protected]>2025-04-22 14:56:56 +0000
committerWerner Koch <[email protected]>2025-04-22 15:04:07 +0000
commit65114f24e13f835a289f791ed7a5e408b2843d59 (patch)
tree60b66e9337682cddd59885e23d4f02de4de72e79
parentMark the initializations with __nonstring__ attribute. (diff)
downloadlibgpg-error-65114f24e13f835a289f791ed7a5e408b2843d59.tar.gz
libgpg-error-65114f24e13f835a289f791ed7a5e408b2843d59.zip
w32: More changes to the extended length path handling.
* src/sysutils.c (has_path_separator): New. (_gpgrt_fname_to_wchar): Rewrite to add the prefix only when needed. ---- GetFullPathNameW has no MAX_PATH limit and thus can be used with any input. We now prefix the resulting absolute path after the call to GFPN. This allows to check whether the CWD is an UNC and thus use the right prefix. We also don't run the entire thing for just a drive letter etc and further skip the prefix if the absolute path is less than MAX_PATH. This gives better backward compatibility. There is also now a GPGRT_DISABLE_EXTLENPATH=-2 which does not disable the extended path handling but enables debug output. To actually disable the feature a positive value must be used. Updates-commit 28ae4ee194ec56e98123d3aceda130161dfd2df8 GnuPG-bug-id: 5754
-rw-r--r--src/sysutils.c122
1 files changed, 90 insertions, 32 deletions
diff --git a/src/sysutils.c b/src/sysutils.c
index f016c10..5517b3d 100644
--- a/src/sysutils.c
+++ b/src/sysutils.c
@@ -26,6 +26,7 @@
#include <errno.h>
#ifdef HAVE_W32_SYSTEM
# include <windows.h>
+# include <wchar.h>
#endif
#ifdef HAVE_STAT
# include <sys/stat.h>
@@ -253,73 +254,99 @@ _gpgrt_setenv (const char *name, const char *value, int overwrite)
#ifdef HAVE_W32_SYSTEM
+/* Checks whether fname has a path separator, that is a slash or
+ * backslash somewhere. However, it skips leading slashes with drive
+ * letters (backslashes and slashes are considered the same):
+ * x:\foo -> false
+ * x:foo -> false
+ * x: -> false
+ * x:\ -> false
+ * foo -> false
+ * foo\bar -> true
+ * x:\foo\bar -> true
+ * x:foo\bar -> true
+ * \\server -> true
+ */
+static int
+has_path_separator (const char *fname)
+{
+ if (fname[0] == '/' || fname[0] == '\\')
+ return 1;
+ else if (fname[0] && fname[1] == ':')
+ return (fname[2] && strpbrk (fname+3, "/\\"));
+ else
+ return !!strpbrk (fname, "/\\");
+}
+
/* 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 requires to first
* convert the name to an absolute file name. To avoid looking up the
- * fill name in all cases we do the \\?\ prefixing only if FNAME is
+ * full name in all cases we do the \\?\ prefixing only if FNAME is
* longer than 60 byes or if it has any path separator. */
wchar_t *
_gpgrt_fname_to_wchar (const char *fname)
{
static int no_extlenpath;
- wchar_t *wname;
+ wchar_t *wname, *w;
wchar_t *wfullpath = NULL;
int success = 0;
if (!no_extlenpath)
{
+ /* Note that a value of -2 enables debugging of extlenpath. */
const char *s = getenv ("GPGRT_DISABLE_EXTLENPATH");
- if (s && atoi (s))
+ int i = atoi (s? s:"0");
+
+ if (i > 0)
no_extlenpath = 1;
else
- no_extlenpath = -1;
+ no_extlenpath = (i == -2)? -2 : -1;
}
wname = _gpgrt_utf8_to_wchar (fname);
if (!wname)
return NULL;
+ /* We use only backslashes in the wchar version of fname. */
+ for (w = wname; *w; w++)
+ if (*w == L'/')
+ *w = L'\\';
if (no_extlenpath > 0)
success = 1; /* Extended length path support has been disabled. */
else if (!strncmp (fname, "\\\\?\\", 4) || !strncmp (fname, "//?/", 4))
success = 1; /* Already translated. */
- else if (strpbrk (fname, "/\\") || wcslen (wname) > 60)
+ else if (has_path_separator (fname) || wcslen (wname) > 60)
{
int wlen = 1024;
- int extralen;
DWORD res;
- wchar_t *w;
try_again:
- wfullpath = xtrymalloc (wlen * sizeof *wfullpath);
+ /* We need some extra characters (backslashes are actually used):
+ * Either "//?/" = 4 or "//?/UNC" = 7 and to be safe one more
+ * for the end of string */
+ wfullpath = xtrymalloc ((wlen+8) * sizeof *wfullpath);
if (!wfullpath)
goto leave;
- if (((*fname == '\\' && fname[1] == '\\')
- ||(*fname == '/' && fname[1] == '/')) && fname[2])
- {
- /* Note that the GFPN call below will append the UNC name
- * and thus two leading backslashes. However, for an
- * extended length path only one backslash is expected after
- * the prefix. We handle this by replacing the first
- * backslash with the C from UNC after the GFPN call. */
- wcscpy (wfullpath, L"\\\\?\\UN");
- extralen = 6;
- }
- else
+ if (no_extlenpath == -2)
{
- wcscpy (wfullpath, L"\\\\?\\");
- extralen = 4;
+ char *tmpn = _gpgrt_wchar_to_utf8 (wname, -1);
+ _gpgrt_log_debug ("%s:%d: checking '%s'\n",
+ __func__, __LINE__, tmpn);
+ xfree (tmpn);
}
- res = GetFullPathNameW (wname, wlen-extralen, wfullpath+extralen, NULL);
+ /* Note that GFPN is basically a string function without a
+ * MAX_PATH limitation. cf https://googleprojectzero.blogspot.com
+ * /2016/02/the-definitive-guide-on-win32-to-nt.html */
+ res = GetFullPathNameW (wname, wlen, wfullpath, NULL);
if (!res)
{
_gpgrt_w32_set_errno (-1);
goto leave;
}
- else if (res >= wlen - extralen)
+ else if (res >= wlen)
{
/* Truncated - increase to the desired length. */
if (wlen > 1024)
@@ -331,21 +358,46 @@ _gpgrt_fname_to_wchar (const char *fname)
/* GetFullPathNameW indicated the required buffer length. */
_gpgrt_free_wchar (wfullpath);
wfullpath = NULL;
- wlen = res + extralen;
+ wlen = res;
goto try_again;
}
_gpgrt_free_wchar (wname);
wname = wfullpath;
wfullpath = NULL;
+ if (no_extlenpath == -2)
+ {
+ char *tmpn = _gpgrt_wchar_to_utf8 (wname, -1);
+ _gpgrt_log_debug ("%s:%d: absfname '%s' (len=%d)\n",
+ __func__, __LINE__, tmpn, (int)res);
+ xfree (tmpn);
+ }
- if (extralen == 6)
- wname[6] = L'C'; /* Replace first backslash - see above. */
+ if (res < MAX_PATH - 5)
+ ; /* No need for extended length path (-5 is kind of arbitrary) */
+ else if (*wname == L'\\' && wname[1] == L'\\' && wname[2])
+ {
+ /* For an UNC extended length path only one backslash is
+ * expected after the prefix. This we overwrite the first
+ * slash with the 'C'. Thus a shift by 6 and not 7. */
+ wmemmove (wname+6, wname, res+1);
+ wname[0] = L'\\';
+ wname[1] = L'\\';
+ wname[2] = L'?';
+ wname[3] = L'\\';
+ wname[4] = L'U';
+ wname[5] = L'N';
+ wname[6] = L'C'; /* (Overwrites the first slash.) */
- /* Need to make sure that all slashes are mapped. */
- for (w = wname; *w; w++)
- if (*w == L'/')
- *w = L'\\';
- success = 1;
+ }
+ else
+ {
+ wmemmove (wname+4, wname, res+1);
+ wname[0] = L'\\';
+ wname[1] = L'\\';
+ wname[2] = L'?';
+ wname[3] = L'\\';
+ }
+ success = 2;
}
else
success = 1;
@@ -357,6 +409,12 @@ _gpgrt_fname_to_wchar (const char *fname)
_gpgrt_free_wchar (wname);
wname = NULL;
}
+ if (wname && success == 2 && no_extlenpath == -2)
+ {
+ char *tmpn = _gpgrt_wchar_to_utf8 (wname, -1);
+ _gpgrt_log_debug ("%s:%d: using '%s'\n", __func__, __LINE__, tmpn);
+ xfree (tmpn);
+ }
return wname;
}