windows storage fixes

This commit is contained in:
Arvid Norberg 2011-11-13 04:12:56 +00:00
parent 344e6c8a3b
commit 4d49d0f20d
3 changed files with 60 additions and 91 deletions

View File

@ -1168,7 +1168,8 @@ namespace libtorrent
CloseHandle(ol.hEvent);
return -1;
}
if (GetOverlappedResult(m_file_handle, &ol, &ret, true) == 0)
DWORD num_read;
if (GetOverlappedResult(m_file_handle, &ol, &num_read, true) == 0)
{
if (GetLastError() != ERROR_HANDLE_EOF)
{
@ -1177,6 +1178,7 @@ namespace libtorrent
return -1;
}
}
if (num_read < ret) ret = num_read;
}
CloseHandle(ol.hEvent);
return ret;
@ -1358,17 +1360,12 @@ namespace libtorrent
ol.hEvent = CreateEvent(0, true, false, 0);
ret += size;
// if file_size is > 0, the file will be opened in unbuffered
// mode after the write completes, and truncate the file to
// file_size.
size_type file_size = 0;
if ((size & (m_page_size-1)) != 0)
{
// if size is not an even multiple, this must be the tail
// of the file. Write the whole page and then open a new
// file without FILE_FLAG_NO_BUFFERING and set the
// file size to file_offset + size
// of the file.
file_size = file_offset + size;
size = num_pages * m_page_size;
@ -1384,95 +1381,17 @@ namespace libtorrent
CloseHandle(ol.hEvent);
return -1;
}
DWORD tmp;
if (GetOverlappedResult(m_file_handle, &ol, &tmp, true) == 0)
DWORD num_written;
if (GetOverlappedResult(m_file_handle, &ol, &num_written, true) == 0)
{
ec.assign(GetLastError(), get_system_category());
CloseHandle(ol.hEvent);
return -1;
}
if (tmp < ret) ret = tmp;
if (num_written < ret) ret = num_written;
}
CloseHandle(ol.hEvent);
if (file_size > 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
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)
{
HMODULE nt = LoadLibraryA("ntdll");
if (nt)
{
NtSetInformationFile = (NtSetInformationFile_t)GetProcAddress(nt, "NtSetInformationFile");
if (NtSetInformationFile == 0) failed_ntdll = true;
}
else failed_ntdll = true;
}
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);
}
else
{
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());
return -1;
}
}
}
if (file_size > 0) set_size(file_size, ec);
return ret;
#else
size_type ret = lseek(m_fd, file_offset, SEEK_SET);
@ -1637,6 +1556,53 @@ namespace libtorrent
TORRENT_ASSERT(s >= 0);
#ifdef TORRENT_WINDOWS
if ((m_open_mode & no_buffer) && (s & (size_alignment()-1)) != 0)
{
// the file is opened in unbuffered mode, and the size is not
// aligned to the required cluster size. Use NtSetInformationFile
#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
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)
{
HMODULE nt = LoadLibraryA("ntdll");
if (nt)
{
NtSetInformationFile = (NtSetInformationFile_t)GetProcAddress(nt, "NtSetInformationFile");
if (NtSetInformationFile == 0) failed_ntdll = true;
}
else failed_ntdll = true;
}
if (!failed_ntdll && NtSetInformationFile)
{
ULONG_PTR Iosb[2];
LARGE_INTEGER fsize;
fsize.QuadPart = s;
_NTSTATUS st = NtSetInformationFile(m_file_handle
, Iosb, &fsize, sizeof(fsize), FileEndOfFileInformation);
if (!NT_SUCCESS(st))
{
ec.assign(INVALID_SET_FILE_POINTER, get_system_category());
return false;
}
return true;
}
}
LARGE_INTEGER offs;
LARGE_INTEGER cur_size;
if (GetFileSizeEx(m_file_handle, &cur_size) == FALSE)

View File

@ -1250,8 +1250,8 @@ ret:
bytes_transferred = (int)(this->*op.unaligned_op)(file_handle, adjusted_offset
, tmp_bufs, num_tmp_bufs, ec);
if (op.mode == file::read_write
&& adjusted_offset + bytes_transferred == file_iter->size
&& file_handle->pos_alignment() > 0)
&& adjusted_offset + bytes_transferred >= file_iter->size
&& (file_handle->pos_alignment() > 0 || file_handle->size_alignment() > 0))
{
// we were writing, and we just wrote the last block of the file
// we likely wrote a bit too much, since we're restricted to
@ -1266,6 +1266,7 @@ ret:
{
bytes_transferred = (int)((*file_handle).*op.regular_op)(adjusted_offset
, tmp_bufs, num_tmp_bufs, ec);
TORRENT_ASSERT(bytes_transferred <= bufs_size(tmp_bufs, num_tmp_bufs));
}
file_offset = 0;

View File

@ -487,11 +487,13 @@ void run_storage_tests(boost::intrusive_ptr<torrent_info> info
// test unaligned read (where the bytes are not aligned)
ret = s->read(piece, 0, 3, piece_size-9);
TEST_CHECK(ret == piece_size - 9);
if (ret != piece_size - 9) print_error(ret, s);
TEST_CHECK(std::equal(piece, piece + piece_size-9, piece1+3));
// verify piece 1
ret = s->read(piece, 0, 0, piece_size);
TEST_CHECK(ret == piece_size);
if (ret != piece_size) print_error(ret, s);
TEST_CHECK(std::equal(piece, piece + piece_size, piece1));