]> git.ipfire.org Git - thirdparty/curl.git/commitdiff
windows: fix `CreateFile()` calls to support long filenames
authorViktor Szakats <commit@vsz.me>
Fri, 19 Dec 2025 22:26:10 +0000 (23:26 +0100)
committerViktor Szakats <commit@vsz.me>
Sat, 20 Dec 2025 13:16:42 +0000 (14:16 +0100)
It makes them work in Schannel's CA bundle loader, and curl tool's
set/get file timestamp operations (e.g. in `-R`/`--remote-time`). Also
to match file open operations, that already support long filenames.

E.g. when using `--remote-time`, fixing:
```
Warning: Failed to set filetime 1741363917 on outfile: CreateFile failed:
Warning: GetLastError 0x00000003
```

The special long filename logic is necessary to support Windows releases
prior to Windows 10 v1607. With the latter, it's possible to opt-in to
this behavior via a manifest setting. Note that Windows itself also needs
to opt-in to support this. Finally note that curl itself needs passing
`--globoff` to let long filenames through, pending #20044 and #20046.

Refs:
https://learn.microsoft.com/windows/win32/api/fileapi/nf-fileapi-createfilea
https://learn.microsoft.com/windows/win32/fileio/maximum-file-path-limitation

Ref: #8361
Inspired by: #19286
Inspired-by: Mathesh V
Closes #19286
Closes #20040

docs/internals/CODE_STYLE.md
lib/curlx/fopen.c
lib/curlx/fopen.h
lib/vtls/schannel_verify.c
scripts/checksrc.pl
src/tool_filetime.c

index d8f97594f5bf96561cc1281d42f544ced409af32..9a2709885a8abd8d714b0b2c588d6c0fad56ab50 100644 (file)
@@ -353,6 +353,9 @@ This is the full list of functions generally banned.
     atoi
     atol
     calloc
+    CreateFile
+    CreateFileA
+    CreateFileW
     fclose
     fdopen
     fopen
index 5de9a13a795097361d7ba8e8919892b5113e1b01..c1399135b37283c9b75a6579925ea64d66f05f0c 100644 (file)
@@ -43,6 +43,8 @@ int curlx_fseek(void *stream, curl_off_t offset, int whence)
 
 #include <share.h>  /* for _SH_DENYNO */
 
+#include "multibyte.h"
+
 #ifdef CURLDEBUG
 /*
  * Use system allocators to avoid infinite recursion when called by curl's
@@ -248,6 +250,49 @@ cleanup:
   return *out ? true : false;
 }
 
+#ifndef CURL_WINDOWS_UWP
+HANDLE curlx_CreateFile(const char *filename,
+                        DWORD dwDesiredAccess,
+                        DWORD dwShareMode,
+                        LPSECURITY_ATTRIBUTES lpSecurityAttributes,
+                        DWORD dwCreationDisposition,
+                        DWORD dwFlagsAndAttributes,
+                        HANDLE hTemplateFile)
+{
+  HANDLE handle = INVALID_HANDLE_VALUE;
+
+#ifdef UNICODE
+  TCHAR *filename_t = curlx_convert_UTF8_to_wchar(filename);
+#else
+  const TCHAR *filename_t = filename;
+#endif
+
+  if(filename_t) {
+    TCHAR *fixed = NULL;
+    const TCHAR *target = NULL;
+
+    if(fix_excessive_path(filename_t, &fixed))
+      target = fixed;
+    else
+      target = filename_t;
+    /* !checksrc! disable BANNEDFUNC 1 */
+    handle = CreateFile(target,
+                        dwDesiredAccess,
+                        dwShareMode,
+                        lpSecurityAttributes,
+                        dwCreationDisposition,
+                        dwFlagsAndAttributes,
+                        hTemplateFile);
+    CURLX_FREE(fixed);
+#ifdef UNICODE
+    curlx_free(filename_t);
+#endif
+  }
+
+  return handle;
+}
+#endif /* !CURL_WINDOWS_UWP */
+
 int curlx_win32_open(const char *filename, int oflag, ...)
 {
   int pmode = 0;
index fb3277a17e7da6229a61076af5568bbf3cd036ae..bc1081200731ca3ce8d1a3985564fc34774afabc 100644 (file)
 int curlx_fseek(void *stream, curl_off_t offset, int whence);
 
 #ifdef _WIN32
+#ifndef CURL_WINDOWS_UWP
+HANDLE curlx_CreateFile(const char *filename,
+                        DWORD dwDesiredAccess,
+                        DWORD dwShareMode,
+                        LPSECURITY_ATTRIBUTES lpSecurityAttributes,
+                        DWORD dwCreationDisposition,
+                        DWORD dwFlagsAndAttributes,
+                        HANDLE hTemplateFile);
+#endif /* !CURL_WINDOWS_UWP */
 FILE *curlx_win32_fopen(const char *filename, const char *mode);
 FILE *curlx_win32_freopen(const char *filename, const char *mode, FILE *fh);
 int curlx_win32_stat(const char *path, struct_stat *buffer);
index 4f939ea4cb7bd13b63676bd44ea0a3a7493cd2e6..bb8a30919b614419d71e2c1dd87cd483c4fd3e73 100644 (file)
@@ -39,6 +39,7 @@
 #include "schannel.h"
 #include "schannel_int.h"
 
+#include "../curlx/fopen.h"
 #include "../curlx/inet_pton.h"
 #include "vtls.h"
 #include "vtls_int.h"
@@ -254,34 +255,24 @@ static CURLcode add_certs_file_to_store(HCERTSTORE trust_store,
                                         struct Curl_easy *data)
 {
   CURLcode result;
-  HANDLE ca_file_handle = INVALID_HANDLE_VALUE;
+  HANDLE ca_file_handle;
   LARGE_INTEGER file_size;
   char *ca_file_buffer = NULL;
-  TCHAR *ca_file_tstr = NULL;
   size_t ca_file_bufsize = 0;
   DWORD total_bytes_read = 0;
 
-  ca_file_tstr = curlx_convert_UTF8_to_tchar(ca_file);
-  if(!ca_file_tstr) {
-    char buffer[WINAPI_ERROR_LEN];
-    failf(data, "schannel: invalid path name for CA file '%s': %s", ca_file,
-          curlx_winapi_strerror(GetLastError(), buffer, sizeof(buffer)));
-    result = CURLE_SSL_CACERT_BADFILE;
-    goto cleanup;
-  }
-
   /*
    * Read the CA file completely into memory before parsing it. This
    * optimizes for the common case where the CA file will be relatively
    * small ( < 1 MiB ).
    */
-  ca_file_handle = CreateFile(ca_file_tstr,
-                              GENERIC_READ,
-                              FILE_SHARE_READ,
-                              NULL,
-                              OPEN_EXISTING,
-                              FILE_ATTRIBUTE_NORMAL,
-                              NULL);
+  ca_file_handle = curlx_CreateFile(ca_file,
+                                    GENERIC_READ,
+                                    FILE_SHARE_READ,
+                                    NULL,
+                                    OPEN_EXISTING,
+                                    FILE_ATTRIBUTE_NORMAL,
+                                    NULL);
   if(ca_file_handle == INVALID_HANDLE_VALUE) {
     char buffer[WINAPI_ERROR_LEN];
     failf(data, "schannel: failed to open CA file '%s': %s", ca_file,
@@ -347,7 +338,6 @@ cleanup:
     CloseHandle(ca_file_handle);
   }
   Curl_safefree(ca_file_buffer);
-  curlx_free(ca_file_tstr);
 
   return result;
 }
index 54436b8358eafce17be61eb96735edc18809a13e..978959008fcad997c7d2d668f59805cf118474dc 100755 (executable)
@@ -70,6 +70,9 @@ my %banfunc = (
     "atoi" => 1,
     "atol" => 1,
     "calloc" => 1,
+    "CreateFile" => 1,
+    "CreateFileA" => 1,
+    "CreateFileW" => 1,
     "fclose" => 1,
     "fdopen" => 1,
     "fopen" => 1,
index a959af9937229007c74880ba78d1e7d0368eaef0..afe308f8ad68532e5a38253a82b1cfec665a3615 100644 (file)
@@ -41,12 +41,9 @@ int getfiletime(const char *filename, curl_off_t *stamp)
    access to a 64-bit type we can bypass stat and get the times directly. */
 #if defined(_WIN32) && !defined(CURL_WINDOWS_UWP)
   HANDLE hfile;
-  TCHAR *tchar_filename = curlx_convert_UTF8_to_tchar(filename);
-
-  hfile = CreateFile(tchar_filename, FILE_READ_ATTRIBUTES,
-                     (FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE),
-                     NULL, OPEN_EXISTING, 0, NULL);
-  curlx_free(tchar_filename);
+  hfile = curlx_CreateFile(filename, FILE_READ_ATTRIBUTES,
+                           FILE_SHARE_READ | FILE_SHARE_WRITE |
+                           FILE_SHARE_DELETE, NULL, OPEN_EXISTING, 0, NULL);
   if(hfile != INVALID_HANDLE_VALUE) {
     FILETIME ft;
     if(GetFileTime(hfile, NULL, NULL, &ft)) {
@@ -93,7 +90,6 @@ void setfiletime(curl_off_t filetime, const char *filename)
    access to a 64-bit type we can bypass utime and set the times directly. */
 #if defined(_WIN32) && !defined(CURL_WINDOWS_UWP)
   HANDLE hfile;
-  TCHAR *tchar_filename = curlx_convert_UTF8_to_tchar(filename);
 
   /* 910670515199 is the maximum Unix filetime that can be used as a Windows
      FILETIME without overflow: 30827-12-31T23:59:59. */
@@ -108,10 +104,9 @@ void setfiletime(curl_off_t filetime, const char *filename)
     warnf("Capping set filetime to minimum to avoid overflow");
   }
 
-  hfile = CreateFile(tchar_filename, FILE_WRITE_ATTRIBUTES,
-                     (FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE),
-                     NULL, OPEN_EXISTING, 0, NULL);
-  curlx_free(tchar_filename);
+  hfile = curlx_CreateFile(filename, FILE_WRITE_ATTRIBUTES,
+                           FILE_SHARE_READ | FILE_SHARE_WRITE |
+                           FILE_SHARE_DELETE, NULL, OPEN_EXISTING, 0, NULL);
   if(hfile != INVALID_HANDLE_VALUE) {
     curl_off_t converted = ((curl_off_t)filetime * 10000000) +
       116444736000000000;