diff --git a/src/file.cpp b/src/file.cpp index 9748c63e6..da4660032 100644 --- a/src/file.cpp +++ b/src/file.cpp @@ -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) diff --git a/src/storage.cpp b/src/storage.cpp index 19a72b1e4..aedafa969 100644 --- a/src/storage.cpp +++ b/src/storage.cpp @@ -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; diff --git a/test/test_storage.cpp b/test/test_storage.cpp index 3c134c33b..a38c1e4b8 100644 --- a/test/test_storage.cpp +++ b/test/test_storage.cpp @@ -487,11 +487,13 @@ void run_storage_tests(boost::intrusive_ptr 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));