use NtSetInformationFile to truncate files in unbuffered mode (to avoid having to close and re-open them)

This commit is contained in:
Arvid Norberg 2011-11-02 09:02:28 +00:00
parent 99a209e3c9
commit 32dfc469c3
1 changed files with 74 additions and 17 deletions

View File

@ -1082,7 +1082,16 @@ namespace libtorrent
size += i->iov_len;
}
error_code code;
if (eof) TORRENT_ASSERT(file_offset + size >= get_size(code));
if (eof)
{
size_type fsize = get_size(code);
if (code) printf("get_size: %s\n", code.message().c_str());
if (file_offset + size < fsize)
{
printf("offset: %d size: %d get_size: %d\n", int(file_offset), int(size), int(fsize));
TORRENT_ASSERT(false);
}
}
}
#endif
@ -1388,31 +1397,79 @@ namespace libtorrent
if (file_size > 0)
{
HANDLE f = CreateFile_(m_path.c_str(), GENERIC_WRITE
, FILE_SHARE_READ | FILE_SHARE_WRITE, 0, OPEN_EXISTING
, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS, 0);
#define FileEndOfFileInformation 20
#ifndef NT_SUCCESS
#define NT_SUCCESS(x) (!((x) & 0x80000000))
#endif
// for NtSetInformationFile, see:
// http://undocumented.ntinternals.net/UserMode/Undocumented%20Functions/NT%20Objects/File/NtSetInformationFile.html
if (f == INVALID_HANDLE_VALUE)
typedef DWORD _NTSTATUS;
typedef _NTSTATUS (NTAPI * NtSetInformationFile_t)(HANDLE file, PULONG_PTR iosb, PVOID data, ULONG len, ULONG file_info_class);
static NtSetInformationFile_t NtSetInformationFile = 0;
static bool failed_ntdll = false;
if (NtSetInformationFile == 0 && !failed_ntdll)
{
ec.assign(GetLastError(), get_system_category());
return -1;
HMODULE nt = LoadLibraryA("ntdll");
if (nt)
{
NtSetInformationFile = (NtSetInformationFile_t)GetProcAddress(nt, "NtSetInformationFile");
if (NtSetInformationFile == 0) failed_ntdll = true;
}
else failed_ntdll = true;
}
LARGE_INTEGER offs;
offs.QuadPart = file_size;
if (SetFilePointerEx(f, offs, &offs, FILE_BEGIN) == FALSE)
if (failed_ntdll || NtSetInformationFile == 0)
{
// if we fail to load NtSetInformationFile from ntdll, fall
// back to the old re-open-file-and-truncate trick
// TODO: this codepath will always fail unless we close this
// file first, and then re-open it again. Doing that though,
// opens up for race conditions where other applications may
// acquire an exclusive lock to the file, at which case we're screwed.
HANDLE f = CreateFile_(m_path.c_str(), GENERIC_WRITE
, FILE_SHARE_READ | FILE_SHARE_WRITE, 0, OPEN_EXISTING
, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS, 0);
if (f == INVALID_HANDLE_VALUE)
{
ec.assign(GetLastError(), get_system_category());
return -1;
}
LARGE_INTEGER offs;
offs.QuadPart = file_size;
if (SetFilePointerEx(f, offs, &offs, FILE_BEGIN) == FALSE)
{
CloseHandle(f);
ec.assign(GetLastError(), get_system_category());
return -1;
}
if (::SetEndOfFile(f) == FALSE)
{
ec.assign(GetLastError(), get_system_category());
CloseHandle(f);
return -1;
}
CloseHandle(f);
ec.assign(GetLastError(), get_system_category());
return -1;
}
if (::SetEndOfFile(f) == FALSE)
else
{
ec.assign(GetLastError(), get_system_category());
CloseHandle(f);
return -1;
ULONG_PTR Iosb[2];
LARGE_INTEGER fsize;
fsize.QuadPart = file_size;
_NTSTATUS st = NtSetInformationFile(native_handle()
, Iosb, &fsize, sizeof(fsize), FileEndOfFileInformation);
if (!NT_SUCCESS(st))
{
ec.assign(INVALID_SET_FILE_POINTER, get_system_category());
}
}
CloseHandle(f);
}
return ret;