int posix_storage::readv(session_settings const& , span<iovec_t const> bufs , piece_index_t const piece, int const offset , storage_error& error) { return readwritev(files(), bufs, piece, offset, error , [this](file_index_t const file_index , std::int64_t const file_offset , span<iovec_t const> vec, storage_error& ec) { if (files().pad_file_at(file_index)) { // reading from a pad file yields zeroes clear_bufs(vec); return bufs_size(vec); } FILE* const f = open_file(file_index, open_mode::read_only , file_offset, ec); if (ec.ec) return -1; // set this unconditionally in case the upper layer would like to treat // short reads as errors ec.operation = operation_t::file_read; int ret = 0; for (auto buf : vec) { int const r = static_cast<int>(fread(buf.data(), 1 , static_cast<std::size_t>(buf.size()), f)); if (r == 0) { ec.ec.assign(errno, generic_category()); break; } ret += r; // the file may be shorter than we think if (r < buf.size()) break; } fclose(f); // we either get an error or 0 or more bytes read TORRENT_ASSERT(ec.ec || ret > 0); TORRENT_ASSERT(ret <= bufs_size(vec)); if (ec.ec) { ec.file(file_index); return -1; } return ret; }); }
int piece_manager::write_impl( file::iovec_t* bufs , int piece_index , int offset , int num_bufs) { TORRENT_ASSERT(bufs); TORRENT_ASSERT(offset >= 0); TORRENT_ASSERT(num_bufs > 0); TORRENT_ASSERT(piece_index >= 0 && piece_index < m_files.num_pieces()); int size = bufs_size(bufs, num_bufs); file::iovec_t* iov = TORRENT_ALLOCA(file::iovec_t, num_bufs); std::copy(bufs, bufs + num_bufs, iov); m_last_piece = piece_index; int slot = allocate_slot_for_piece(piece_index); int ret = m_storage->writev(bufs, slot, offset, num_bufs); // only save the partial hash if the write succeeds if (ret != size) return ret; if (m_storage->settings().disable_hash_checks) return ret; return ret; }
int posix_storage::writev(session_settings const& , span<iovec_t const> bufs , piece_index_t const piece, int const offset , storage_error& error) { return readwritev(files(), bufs, piece, offset, error , [this](file_index_t const file_index , std::int64_t const file_offset , span<iovec_t const> vec, storage_error& ec) { if (files().pad_file_at(file_index)) { // writing to a pad-file is a no-op return bufs_size(vec); } FILE* const f = open_file(file_index, open_mode::write , file_offset, ec); if (ec.ec) return -1; // set this unconditionally in case the upper layer would like to treat // short reads as errors ec.operation = operation_t::file_write; int ret = 0; for (auto buf : vec) { auto const r = static_cast<int>(fwrite(buf.data(), 1 , static_cast<std::size_t>(buf.size()), f)); if (r != buf.size()) { ec.ec.assign(errno, generic_category()); break; } ret += r; } fclose(f); // invalidate our stat cache for this file, since // we're writing to it m_stat_cache.set_dirty(file_index); if (ec) { ec.file(file_index); return -1; } return ret; }); }
size_type file::writev(size_type file_offset, iovec_t const* bufs, int num_bufs, error_code& ec) { TORRENT_ASSERT((m_open_mode & rw_mask) == write_only || (m_open_mode & rw_mask) == read_write); TORRENT_ASSERT(bufs); TORRENT_ASSERT(num_bufs > 0); TORRENT_ASSERT(is_open()); #if defined TORRENT_WINDOWS || defined TORRENT_LINUX || defined TORRENT_DEBUG // make sure m_page_size is initialized init_file(); #endif #ifdef TORRENT_DEBUG if (m_open_mode & no_buffer) { bool eof = false; int size = 0; // when opened in no_buffer mode, the file_offset must // be aligned to pos_alignment() TORRENT_ASSERT((file_offset & (pos_alignment()-1)) == 0); for (file::iovec_t const* i = bufs, *end(bufs + num_bufs); i < end; ++i) { TORRENT_ASSERT((uintptr_t(i->iov_base) & (buf_alignment()-1)) == 0); // every buffer must be a multiple of the page size // except for the last one TORRENT_ASSERT((i->iov_len & (size_alignment()-1)) == 0 || i == end-1); if ((i->iov_len & (size_alignment()-1)) != 0) eof = true; size += i->iov_len; } error_code code; if (eof) TORRENT_ASSERT(file_offset + size >= get_size(code)); } #endif #ifdef TORRENT_WINDOWS DWORD ret = 0; // since the ReadFileScatter requires the file to be opened // with no buffering, and no buffering requires page aligned // buffers, open the file in non-buffered mode in case the // buffer is not aligned. Most of the times the buffer should // be aligned though if ((m_open_mode & no_buffer) == 0) { // this means the buffer base or the buffer size is not aligned // to the page size. Use a regular file for this operation. LARGE_INTEGER offs; offs.QuadPart = file_offset; if (SetFilePointerEx(m_file_handle, offs, &offs, FILE_BEGIN) == FALSE) { ec = error_code(GetLastError(), get_system_category()); return -1; } for (file::iovec_t const* i = bufs, *end(bufs + num_bufs); i < end; ++i) { DWORD intermediate = 0; if (WriteFile(m_file_handle, (char const*)i->iov_base , (DWORD)i->iov_len, &intermediate, 0) == FALSE) { ec = error_code(GetLastError(), get_system_category()); return -1; } ret += intermediate; } return ret; } int size = bufs_size(bufs, num_bufs); // number of pages for the write. round up int num_pages = (size + m_page_size - 1) / m_page_size; // allocate array of FILE_SEGMENT_ELEMENT for WriteFileGather FILE_SEGMENT_ELEMENT* segment_array = TORRENT_ALLOCA(FILE_SEGMENT_ELEMENT, num_pages + 1); FILE_SEGMENT_ELEMENT* cur_seg = segment_array; for (file::iovec_t const* i = bufs, *end(bufs + num_bufs); i < end; ++i) { for (int k = 0; k < i->iov_len; k += m_page_size) { cur_seg->Buffer = PtrToPtr64((((char*)i->iov_base) + k)); ++cur_seg; } } // terminate the array cur_seg->Buffer = 0; OVERLAPPED ol; ol.Internal = 0; ol.InternalHigh = 0; ol.OffsetHigh = file_offset >> 32; ol.Offset = file_offset & 0xffffffff; 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 file_size = file_offset + size; size = num_pages * m_page_size; } if (WriteFileGather(m_file_handle, segment_array, size, 0, &ol) == 0) { if (GetLastError() != ERROR_IO_PENDING) { TORRENT_ASSERT(GetLastError() != ERROR_BAD_ARGUMENTS); ec = error_code(GetLastError(), get_system_category()); CloseHandle(ol.hEvent); return -1; } DWORD tmp; if (GetOverlappedResult(m_file_handle, &ol, &tmp, true) == 0) { ec = error_code(GetLastError(), get_system_category()); CloseHandle(ol.hEvent); return -1; } if (tmp < ret) ret = tmp; } CloseHandle(ol.hEvent); if (file_size > 0) { #if TORRENT_USE_WPATH #define CreateFile_ CreateFileW #else #define CreateFile_ CreateFileA #endif 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 = error_code(GetLastError(), get_system_category()); return -1; } LARGE_INTEGER offs; offs.QuadPart = file_size; if (SetFilePointerEx(f, offs, &offs, FILE_BEGIN) == FALSE) { CloseHandle(f); ec = error_code(GetLastError(), get_system_category()); return -1; } if (::SetEndOfFile(f) == FALSE) { ec = error_code(GetLastError(), get_system_category()); CloseHandle(f); return -1; } CloseHandle(f); } return ret; #else size_type ret = lseek(m_fd, file_offset, SEEK_SET); if (ret < 0) { ec = error_code(errno, get_posix_category()); return -1; } #if TORRENT_USE_WRITEV #ifdef TORRENT_LINUX bool aligned = false; int size = 0; // if we're not opened in no-buffer mode, we don't need alignment if ((m_open_mode & no_buffer) == 0) aligned = true; if (!aligned) { size = bufs_size(bufs, num_bufs); if ((size & (size_alignment()-1)) == 0) aligned = true; } if (aligned) #endif { ret = ::writev(m_fd, bufs, num_bufs); if (ret < 0) { ec = error_code(errno, get_posix_category()); return -1; } return ret; } #ifdef TORRENT_LINUX file::iovec_t* temp_bufs = TORRENT_ALLOCA(file::iovec_t, num_bufs); memcpy(temp_bufs, bufs, sizeof(file::iovec_t) * num_bufs); iovec_t& last = temp_bufs[num_bufs-1]; last.iov_len = (last.iov_len & ~(size_alignment()-1)) + size_alignment(); ret = ::writev(m_fd, temp_bufs, num_bufs); if (ret < 0) { ec = error_code(errno, get_posix_category()); return -1; } if (ftruncate(m_fd, file_offset + size) < 0) { ec = error_code(errno, get_posix_category()); return -1; } return (std::min)(ret, size_type(size)); #endif // TORRENT_LINUX #else // TORRENT_USE_WRITEV ret = 0; for (file::iovec_t const* i = bufs, *end(bufs + num_bufs); i < end; ++i) { int tmp = write(m_fd, i->iov_base, i->iov_len); if (tmp < 0) { ec = error_code(errno, get_posix_category()); return -1; } ret += tmp; if (tmp < i->iov_len) break; } return ret; #endif // TORRENT_USE_WRITEV #endif // TORRENT_WINDOWS }