From cb7da2a640c405e0658c135b3ab2ac5be2fdc53a Mon Sep 17 00:00:00 2001 From: Torbj?rn Svensson Date: Mon, 28 Feb 2022 12:17:33 +0000 Subject: [PATCH] Further correct the handling of long pathnames on Windows hosts. PR 25713 * bfdio.c (_bfd_real_fopen): Fix handling of parhs longer than 260 characters on Windows hosts. --- bfd/ChangeLog | 6 ++++ bfd/bfdio.c | 78 +++++++++++++++++++++++++-------------------------- 2 files changed, 45 insertions(+), 39 deletions(-) diff --git a/bfd/ChangeLog b/bfd/ChangeLog index 3a003126062..7f0f25159b7 100644 --- a/bfd/ChangeLog +++ b/bfd/ChangeLog @@ -1,3 +1,9 @@ +2022-02-28 Torbjörn Svensson + + PR 25713 + * bfdio.c (_bfd_real_fopen): Fix handling of parhs longer than 260 + characters on Windows hosts. + 2022-02-28 Nick Clifton PR 28886 diff --git a/bfd/bfdio.c b/bfd/bfdio.c index 41b18b682ad..82310ffabc1 100644 --- a/bfd/bfdio.c +++ b/bfd/bfdio.c @@ -116,55 +116,55 @@ _bfd_real_fopen (const char *filename, const char *modes) } #elif defined (_WIN32) - size_t filelen; + /* PR 25713: Handle extra long path names possibly containing '..' and '.'. */ - /* PR 25713: Handle extra long path names. - For relative paths, convert them to absolute, in case that version is too long. */ - if (! IS_ABSOLUTE_PATH (filename) && (strstr (filename, ".o") != NULL)) - { - char cwd[1024]; + wchar_t **lpFilePart = {NULL}; + const wchar_t prefix[] = L"\\\\?\\"; + const wchar_t ccs[] = L", ccs=UNICODE"; + const size_t partPathLen = strlen(filename) + 1; - getcwd (cwd, sizeof (cwd)); - filelen = strlen (cwd) + 1; - strncat (cwd, "\\", sizeof (cwd) - filelen); - ++ filelen; - strncat (cwd, filename, sizeof (cwd) - filelen); + /* Converting the partial path from ascii to unicode. + 1) get the length: Calling with lpWideCharStr set to null returns the length. + 2) convert the string: Calling with cbMultiByte set to -1 includes the terminating null. */ + size_t partPathWSize = MultiByteToWideChar (CP_UTF8, 0, partPathOrig, -1, NULL, 0); + wchar_t *partPath = calloc (partPathWSize, sizeof(wchar_t)); - filename = cwd; - } + MultiByteToWideChar (CP_UTF8, 0, partPathOrig, -1, partPath, partPathWSize); - filelen = strlen (filename) + 1; + /* Convert any UNIX style path separators into the DOS i.e. backslash separator. */ + size_t ix; + for (ix = 0; ix < partPathLen; ix++) + if (IS_UNIX_DIR_SEPARATOR(filename[ix])) + partPath[ix] = '\\'; - if (filelen > MAX_PATH - 1) - { - FILE * file; - char * fullpath; - int i; - - fullpath = (char *) malloc (filelen + 8); - - /* Add a Microsoft recommended prefix that - will allow the extra-long path to work. */ - strcpy (fullpath, "\\\\?\\"); - strcat (fullpath, filename); - - /* Convert any UNIX style path separators into the DOS form. */ - for (i = 0; fullpath[i]; i++) - { - if (IS_UNIX_DIR_SEPARATOR (fullpath[i])) - fullpath[i] = '\\'; - } - - file = close_on_exec (fopen (fullpath, modes)); - free (fullpath); - return file; - } + /* Getting the full path from the provided partial path. + 1) get the length: + 2) resolve the path. */ + long fullPathWSize = GetFullPathNameW (partPath, 0, NULL, lpFilePart); + wchar_t *fullPath = calloc (fullPathWSize + sizeof(prefix) + 1, sizeof(wchar_t)); + + wcscpy (fullPath, prefix); + int prefixLen = sizeof(prefix) / sizeof(wchar_t); + wchar_t* fullPathOffset = fullPath + prefixLen - 1; + GetFullPathNameW (partPath, fullPathWSize, fullPathOffset, lpFilePart); + free (partPath); + + /* It is non-standard for modes to exceed 16 characters. */ + wchar_t modesW[16 + sizeof(ccs)]; + MultiByteToWideChar (CP_UTF8, 0, modes, -1, modesW, sizeof(modesW)); + wcscat (modesW, ccs); + + FILE* file = _wfopen (fullPath, mdesW); + free (fullPath); + + return close_on_exec (file); #elif defined (HAVE_FOPEN64) return close_on_exec (fopen64 (filename, modes)); -#endif +#else return close_on_exec (fopen (filename, modes)); +#endif } /* -- 2.30.2