24#include <wx/filename.h>
44#if !defined( __MINGW32__ )
68#define FileFullDirectoryInformation ( (FILE_INFORMATION_CLASS) 2 )
75#if defined( _MSC_VER ) || !defined( USE_MSYS2_FALLBACK )
78 HANDLE hFile = INVALID_HANDLE_VALUE;
79 hFile = CreateFileW( aPath.wc_str(),
84 FILE_FLAG_SEQUENTIAL_SCAN,
87 if (hFile == INVALID_HANDLE_VALUE)
92 int fd = _open_osfhandle(
reinterpret_cast<intptr_t
>( hFile ), 0 );
101 FILE* fp = _fdopen( fd, aMode.c_str() );
112 return wxFopen( aPath, aMode );
122 const SECURITY_INFORMATION secInfo = DACL_SECURITY_INFORMATION;
130 GetFileSecurityW( aSrc.wc_str(), secInfo,
nullptr, 0, &dwSize );
132 if( dwSize == 0 || GetLastError() != ERROR_INSUFFICIENT_BUFFER )
135 std::vector<BYTE> sdBuffer( dwSize );
136 PSECURITY_DESCRIPTOR pSD =
static_cast<PSECURITY_DESCRIPTOR
>( sdBuffer.data() );
138 return GetFileSecurityW( aSrc.wc_str(), secInfo, pSD, dwSize, &dwSize )
139 && SetFileSecurityW( aDest.wc_str(), secInfo, pSD );
144 DWORD attrs = GetFileAttributesW( aFilePath.wc_str() );
146 if( attrs == INVALID_FILE_ATTRIBUTES )
152 DWORD attrsToRemove = FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_HIDDEN;
154 if( attrs & attrsToRemove )
156 attrs &= ~attrsToRemove;
157 return SetFileAttributesW( aFilePath.wc_str(), attrs ) != 0;
165 TARGET_ATTRS snapshot;
166 DWORD attrs = GetFileAttributesW( aPath.wc_str() );
168 if( attrs == INVALID_FILE_ATTRIBUTES )
174 snapshot.value =
static_cast<std::uint32_t
>( attrs )
175 & ( FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_HIDDEN );
176 snapshot.captured =
true;
183 if( !aAttrs.captured )
186 DWORD current = GetFileAttributesW( aPath.wc_str() );
188 if( current == INVALID_FILE_ATTRIBUTES )
191 DWORD merged = current |
static_cast<DWORD
>( aAttrs.value );
193 if( merged == current )
196 return SetFileAttributesW( aPath.wc_str(), merged ) != 0;
201 const wxString& aMode, wxString* aTempPathOut,
206 for(
unsigned attempt = 0; attempt < 32; ++attempt )
209 HANDLE h = CreateFileW( candidate.wc_str(), GENERIC_WRITE, 0,
nullptr, CREATE_NEW,
210 FILE_ATTRIBUTE_NORMAL,
nullptr );
212 if( h != INVALID_HANDLE_VALUE )
215 const int translation = aMode.Contains( wxT(
"b" ) ) ? _O_BINARY : _O_TEXT;
216 int fd = _open_osfhandle(
reinterpret_cast<intptr_t
>( h ), _O_WRONLY | translation );
221 DeleteFileW( candidate.wc_str() );
225 *aError = wxString::Format( wxT(
"_open_osfhandle failed for '%s'" ),
232 FILE* fp = _wfdopen( fd, aMode.wc_str() );
237 DeleteFileW( candidate.wc_str() );
240 *aError = wxString::Format( wxT(
"_wfdopen failed for '%s'" ), candidate );
246 *aTempPathOut = candidate;
251 DWORD err = GetLastError();
253 if( err != ERROR_FILE_EXISTS && err != ERROR_ALREADY_EXISTS )
256 *aError = wxString::Format( wxT(
"CreateFile failed for '%s' (Win32 %lu)" ),
264 *aError = wxT(
"Exhausted temp-file retry budget" );
274 WIN32_FIND_DATAW findData;
275 HANDLE find = FindFirstFileW( aPath.wc_str(), &findData );
277 if(
find == INVALID_HANDLE_VALUE )
282 if( !( findData.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT )
283 || findData.dwReserved0 != IO_REPARSE_TAG_SYMLINK )
288 HANDLE file = CreateFileW( aPath.wc_str(), 0, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
289 nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS,
nullptr );
291 if( file == INVALID_HANDLE_VALUE )
294 std::vector<wchar_t> buffer( 32768 );
295 DWORD length = GetFinalPathNameByHandleW( file, buffer.data(),
static_cast<DWORD
>( buffer.size() ),
296 FILE_NAME_NORMALIZED | VOLUME_NAME_DOS );
299 if( length == 0 || length >= buffer.size() )
302 wxString resolved( buffer.data(), length );
304 if( resolved.StartsWith( wxT(
"\\\\?\\UNC\\" ) ) )
305 resolved = wxT(
"\\\\" ) + resolved.Mid( 8 );
306 else if( resolved.StartsWith( wxT(
"\\\\?\\" ) ) )
307 resolved = resolved.Mid( 4 );
315 const DWORD attributes = GetFileAttributesW( aFileName.fn_str() );
317 return attributes != INVALID_FILE_ATTRIBUTES
318 && ( attributes & FILE_ATTRIBUTE_HIDDEN ) != 0;
328 if( aFilename.GetVolume().Length() == 1 )
330 aFilename.SetVolume(
"\\\\?\\" + aFilename.GetVolume() +
":" );
331 else if( aFilename.GetVolume().Length() > 1
332 && aFilename.GetVolume().StartsWith( wxT(
"\\\\" ) )
333 && !aFilename.GetVolume().StartsWith( wxT(
"\\\\?" ) ) )
338 aFilename.SetVolume(
"\\\\?\\UNC" + aFilename.GetVolume().Mid( 1 ) );
339 else if( aFilename.GetVolume().StartsWith( wxT(
"\\\\?" ) )
340 && aFilename.GetDirs().size() >= 2
341 && aFilename.GetDirs()[0] ==
"UNC" )
347 aFilename.SetVolume(
"\\\\?\\UNC\\" + aFilename.GetDirs()[1] );
348 aFilename.RemoveDir( 0 );
349 aFilename.RemoveDir( 0 );
356 long long timestamp = 0;
361 static PFN_NtQueryDirectoryFile pNtQueryDirectoryFile =
nullptr;
363 if( !pNtQueryDirectoryFile )
365 HMODULE ntdll = GetModuleHandleW(
L"ntdll.dll" );
369 pNtQueryDirectoryFile =
370 (PFN_NtQueryDirectoryFile) GetProcAddress( ntdll,
"NtQueryDirectoryFile" );
374 if( !pNtQueryDirectoryFile )
377 std::wstring dirPath( aDirPath.t_str() );
379 if( !dirPath.empty() && dirPath.back() !=
L'\\' )
385 if( dirPath.size() >= 2 && dirPath[0] ==
L'\\' && dirPath[1] ==
L'\\' )
387 if( dirPath.size() >= 4 && dirPath[2] ==
L'?' && dirPath[3] ==
L'\\' )
395 ntPath =
L"\\\\?\\UNC\\" + dirPath.substr( 2 );
401 ntPath =
L"\\\\?\\" + dirPath;
404 HANDLE hDir = CreateFileW( ntPath.c_str(), FILE_LIST_DIRECTORY,
405 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
nullptr,
406 OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS,
nullptr );
408 if( hDir == INVALID_HANDLE_VALUE )
411 std::wstring pattern( aFilespec.t_str() );
414 alignas(
sizeof( LONGLONG ) )
char buffer[64 * 1024];
416 IO_STATUS_BLOCK iosb;
418 bool firstQuery =
true;
422 status = pNtQueryDirectoryFile( hDir,
nullptr,
nullptr,
nullptr, &iosb, buffer,
424 nullptr, firstQuery ? TRUE : FALSE );
439 && PathMatchSpecW( fileName.c_str(), pattern.c_str() ) )
464 if( std::fflush( aFp ) != 0 )
467 int fd = _fileno( aFp );
472 HANDLE h =
reinterpret_cast<HANDLE>( _get_osfhandle( fd ) );
474 if( h == INVALID_HANDLE_VALUE )
477 return FlushFileBuffers( h ) != 0;
499 for(
int attempt = 0; attempt < 10; ++attempt )
501 if( MoveFileExW( aSrc.wc_str(), aDst.wc_str(),
502 MOVEFILE_REPLACE_EXISTING | MOVEFILE_WRITE_THROUGH ) )
505 lastError = GetLastError();
507 if( lastError != ERROR_SHARING_VIOLATION && lastError != ERROR_ACCESS_DENIED
508 && lastError != ERROR_LOCK_VIOLATION )
516 if( ReplaceFileW( aDst.wc_str(), aSrc.wc_str(),
nullptr, REPLACEFILE_WRITE_THROUGH,
nullptr,
520 DWORD fallbackError = GetLastError();
524 wchar_t* msg =
nullptr;
525 FormatMessageW( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM
526 | FORMAT_MESSAGE_IGNORE_INSERTS,
527 nullptr, fallbackError ? fallbackError : lastError,
528 MAKELANGID( LANG_NEUTRAL, SUBLANG_DEFAULT ),
reinterpret_cast<LPWSTR
>( &msg ),
533 *aError = wxString( msg );
538 *aError = wxString::Format( wxT(
"Win32 error %lu" ), fallbackError ? fallbackError
549 m_fileHandle = CreateFileW( aFileName.wc_str(), GENERIC_READ, FILE_SHARE_READ,
550 nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL,
nullptr );
552 if( m_fileHandle == INVALID_HANDLE_VALUE )
554 m_fileHandle =
nullptr;
555 throw std::runtime_error( std::string(
"Cannot open file: " )
556 + aFileName.ToStdString() );
559 LARGE_INTEGER fileSize;
561 if( !GetFileSizeEx( m_fileHandle, &fileSize ) )
563 CloseHandle( m_fileHandle );
564 m_fileHandle =
nullptr;
565 throw std::runtime_error( std::string(
"Cannot determine file size: " )
566 + aFileName.ToStdString() );
569 m_size =
static_cast<size_t>( fileSize.QuadPart );
573 CloseHandle( m_fileHandle );
574 m_fileHandle =
nullptr;
578 m_mapHandle = CreateFileMappingW( m_fileHandle,
nullptr, PAGE_READONLY, 0, 0,
nullptr );
582 CloseHandle( m_fileHandle );
583 m_fileHandle =
nullptr;
588 void* ptr = MapViewOfFile( m_mapHandle, FILE_MAP_READ, 0, 0, 0 );
592 CloseHandle( m_mapHandle );
593 m_mapHandle =
nullptr;
594 CloseHandle( m_fileHandle );
595 m_fileHandle =
nullptr;
600 m_data =
static_cast<const uint8_t*
>( ptr );
606 if( m_data && m_mapHandle )
607 UnmapViewOfFile( m_data );
610 CloseHandle( m_mapHandle );
613 CloseHandle( m_fileHandle );
628 auto openFile = [&]( DWORD aAccess, DWORD aDisposition )
630 return CreateFileW( aPath.wc_str(), aAccess,
631 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
nullptr,
632 aDisposition, FILE_ATTRIBUTE_NORMAL,
nullptr );
635 HANDLE handle = openFile( GENERIC_READ | GENERIC_WRITE, CREATE_NEW );
637 aCreated = handle != INVALID_HANDLE_VALUE;
640 handle = openFile( GENERIC_READ | GENERIC_WRITE, OPEN_EXISTING );
642 if( handle == INVALID_HANDLE_VALUE )
645 handle = openFile( GENERIC_READ, OPEN_EXISTING );
647 if( handle != INVALID_HANDLE_VALUE )
650 m_state = STATE::UNSUPPORTED;
658 OVERLAPPED overlapped = {};
661 if( LockFileEx( handle, LOCKFILE_EXCLUSIVE_LOCK | LOCKFILE_FAIL_IMMEDIATELY, 0, 1, 0,
664 m_state = STATE::HELD;
666 else if( GetLastError() == ERROR_LOCK_VIOLATION || GetLastError() == ERROR_IO_PENDING )
668 m_state = STATE::BUSY;
672 m_state = STATE::UNSUPPORTED;
683 aHeldByAnother =
false;
685 HANDLE handle = CreateFileW( aPath.wc_str(), GENERIC_READ,
686 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
nullptr,
687 OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL,
nullptr );
689 if( handle == INVALID_HANDLE_VALUE )
694 OVERLAPPED overlapped = {};
698 if( LockFileEx( handle, LOCKFILE_EXCLUSIVE_LOCK | LOCKFILE_FAIL_IMMEDIATELY, 0, 1, 0,
701 UnlockFileEx( handle, 0, 1, 0, &overlapped );
703 else if( GetLastError() == ERROR_LOCK_VIOLATION || GetLastError() == ERROR_IO_PENDING )
705 aHeldByAnother =
true;
714 return m_handle !=
nullptr;
723 LARGE_INTEGER zero = {};
725 if( !SetFilePointerEx( m_handle, zero,
nullptr, FILE_BEGIN ) )
733 while( ReadFile( m_handle, buffer,
sizeof( buffer ), &read,
nullptr ) && read > 0 )
734 aContents.append( buffer, read );
745 LARGE_INTEGER zero = {};
747 if( !SetFilePointerEx( m_handle, zero,
nullptr, FILE_BEGIN ) || !SetEndOfFile( m_handle ) )
752 if( !WriteFile( m_handle, aContents.data(),
static_cast<DWORD
>( aContents.size() ), &written,
758 return written == aContents.size();
767 CloseHandle( m_handle );
771 m_state = STATE::NONE;
static constexpr DWORD LOCK_BYTE_OFFSET
typedef FILE_INFORMATION_CLASS
struct _FILE_FULL_DIR_INFORMATION FILE_FULL_DIR_INFORMATION
typedef NTSTATUS(NTAPI *PFN_NtQueryDirectoryFile)(HANDLE
struct _FILE_FULL_DIR_INFORMATION * PFILE_FULL_DIR_INFORMATION
#define FileFullDirectoryInformation