diff --git a/ChangeLog b/ChangeLog index 50b5c499d..a984d591e 100644 --- a/ChangeLog +++ b/ChangeLog @@ -41,6 +41,7 @@ * fix uTP edge case where udp socket buffer fills up * fix nagle implementation in uTP + * fix use of SetFileValidData on windows * fix crash when using full allocation storage mode * improve error_code and error_category support in python bindings * fix python binding for external_ip_alert diff --git a/src/file.cpp b/src/file.cpp index 6d2632c8d..a6634c9cf 100644 --- a/src/file.cpp +++ b/src/file.cpp @@ -1946,6 +1946,108 @@ typedef struct _FILE_ALLOCATED_RANGE_BUFFER { return 0; } +#ifdef TORRENT_WINDOWS + bool get_manage_volume_privs() + { + typedef BOOL (WINAPI *OpenProcessToken_t)( + HANDLE ProcessHandle, + DWORD DesiredAccess, + PHANDLE TokenHandle); + + typedef BOOL (WINAPI *LookupPrivilegeValue_t)( + LPCSTR lpSystemName, + LPCSTR lpName, + PLUID lpLuid); + + typedef BOOL (WINAPI *AdjustTokenPrivileges_t)( + HANDLE TokenHandle, + BOOL DisableAllPrivileges, + PTOKEN_PRIVILEGES NewState, + DWORD BufferLength, + PTOKEN_PRIVILEGES PreviousState, + PDWORD ReturnLength); + + static OpenProcessToken_t pOpenProcessToken = NULL; + static LookupPrivilegeValue_t pLookupPrivilegeValue = NULL; + static AdjustTokenPrivileges_t pAdjustTokenPrivileges = NULL; + static bool failed_advapi = false; + + if (pOpenProcessToken == NULL && !failed_advapi) + { + HMODULE advapi = LoadLibraryA("advapi32"); + if (advapi == NULL) + { + failed_advapi = true; + return false; + } + pOpenProcessToken = (OpenProcessToken_t)GetProcAddress(advapi, "OpenProcessToken"); + pLookupPrivilegeValue = (LookupPrivilegeValue_t)GetProcAddress(advapi, "LookupPrivilegeValueA"); + pAdjustTokenPrivileges = (AdjustTokenPrivileges_t)GetProcAddress(advapi, "AdjustTokenPrivileges"); + if (pOpenProcessToken == NULL + || pLookupPrivilegeValue == NULL + || pAdjustTokenPrivileges == NULL) + { + failed_advapi = true; + return false; + } + } + + HANDLE token; + if (!pOpenProcessToken(GetCurrentProcess() + , TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &token)) + return false; + + TOKEN_PRIVILEGES privs; + if (!pLookupPrivilegeValue(NULL, "SeManageVolumePrivilege" + , &privs.Privileges[0].Luid)) + { + CloseHandle(token); + return false; + } + + privs.PrivilegeCount = 1; + privs.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; + + bool ret = pAdjustTokenPrivileges(token, FALSE, &privs, 0, NULL, NULL) + && GetLastError() == ERROR_SUCCESS; + + CloseHandle(token); + + return ret; + } + + void set_file_valid_data(HANDLE f, boost::int64_t size) + { + static bool has_privs = get_manage_volume_privs(); + + typedef BOOL (WINAPI *SetFileValidData_t)(HANDLE, LONGLONG); + static SetFileValidData_t pSetFileValidData = NULL; + static bool failed_kernel32 = false; + + if (pSetFileValidData == NULL && !failed_kernel32) + { + HMODULE k32 = LoadLibraryA("kernel32"); + if (k32 == NULL) + { + failed_kernel32 = true; + return; + } + pSetFileValidData = (SetFileValidData_t)GetProcAddress(k32, "SetFileValidData"); + if (pSetFileValidData == NULL) + { + failed_kernel32 = true; + return; + } + } + + TORRENT_ASSERT(pSetFileValidData); + + // we don't necessarily expect to have enough + // privilege to do this, so ignore errors. + pSetFileValidData(f, size); + } +#endif + bool file::set_size(size_type s, error_code& ec) { TORRENT_ASSERT(is_open()); @@ -1995,6 +2097,10 @@ typedef struct _FILE_ALLOCATED_RANGE_BUFFER { ec.assign(INVALID_SET_FILE_POINTER, get_system_category()); return false; } + + if ((m_open_mode & sparse) == 0) + set_file_valid_data(m_file_handle, s); + return true; } @@ -2037,10 +2143,8 @@ typedef struct _FILE_ALLOCATED_RANGE_BUFFER { #else typedef DWORD (WINAPI *GetCompressedFileSize_t)(LPCSTR lpFileName, LPDWORD lpFileSizeHigh); #endif - typedef BOOL (WINAPI *SetFileValidData_t)(HANDLE hFile, LONGLONG ValidDataLength); static GetCompressedFileSize_t GetCompressedFileSize_ = NULL; - static SetFileValidData_t SetFileValidData = NULL; static bool failed_kernel32 = false; @@ -2054,11 +2158,6 @@ typedef struct _FILE_ALLOCATED_RANGE_BUFFER { #else GetCompressedFileSize_ = (GetCompressedFileSize_t)GetProcAddress(kernel32, "GetCompressedFileSizeA"); #endif - SetFileValidData = (SetFileValidData_t)GetProcAddress(kernel32, "SetFileValidData"); - if ((GetCompressedFileSize_ == NULL) || (SetFileValidData == NULL)) - { - failed_kernel32 = true; - } } else { @@ -2066,7 +2165,8 @@ typedef struct _FILE_ALLOCATED_RANGE_BUFFER { } } - if (!failed_kernel32 && GetCompressedFileSize_ && SetFileValidData) + offs.QuadPart = 0; + if (GetCompressedFileSize_) { // only allocate the space if the file // is not fully allocated @@ -2078,13 +2178,14 @@ typedef struct _FILE_ALLOCATED_RANGE_BUFFER { ec.assign(GetLastError(), get_system_category()); if (ec) return false; } - if (offs.QuadPart != s) - { - // if the user has permissions, avoid filling - // the file with zeroes, but just fill it with - // garbage instead - SetFileValidData(m_file_handle, offs.QuadPart); - } + } + + if (offs.QuadPart != s) + { + // if the user has permissions, avoid filling + // the file with zeroes, but just fill it with + // garbage instead + set_file_valid_data(m_file_handle, s); } } #else // NON-WINDOWS