Further correct the handling of long pathnames on Windows hosts.
authorTorbj?rn Svensson <torbjorn.svensson@st.com>
Mon, 28 Feb 2022 12:17:33 +0000 (12:17 +0000)
committerNick Clifton <nickc@redhat.com>
Mon, 28 Feb 2022 12:17:33 +0000 (12:17 +0000)
PR 25713
* bfdio.c (_bfd_real_fopen): Fix handling of parhs longer than 260
characters on Windows hosts.

bfd/ChangeLog
bfd/bfdio.c

index 3a003126062ba44182f4c7c716d100dfda53e284..7f0f25159b7750bc166043b3444d03edf67aa604 100644 (file)
@@ -1,3 +1,9 @@
+2022-02-28  Torbjörn Svensson  <torbjorn.svensson@st.com>
+
+       PR 25713
+       * bfdio.c (_bfd_real_fopen): Fix handling of parhs longer than 260
+       characters on Windows hosts.
+
 2022-02-28  Nick Clifton  <nickc@redhat.com>
 
        PR 28886
index 41b18b682ad045a330d20b24ab7100790e996b80..82310ffabc1d172402ea0a9cd915272bebd1d9dd 100644 (file)
@@ -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
 }
 
 /*