windows: fix CreateFile() calls to support long filenames

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
This commit is contained in:
Viktor Szakats
2025-12-19 23:26:10 +01:00
parent a468e605eb
commit 969351bb1e
6 changed files with 75 additions and 30 deletions

View File

@@ -353,6 +353,9 @@ This is the full list of functions generally banned.
atoi
atol
calloc
CreateFile
CreateFileA
CreateFileW
fclose
fdopen
fopen

View 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;

View File

@@ -35,6 +35,15 @@
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);

View 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;
}

View File

@@ -70,6 +70,9 @@ my %banfunc = (
"atoi" => 1,
"atol" => 1,
"calloc" => 1,
"CreateFile" => 1,
"CreateFileA" => 1,
"CreateFileW" => 1,
"fclose" => 1,
"fdopen" => 1,
"fopen" => 1,

View 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;