Пример #1
0
	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;
		});
	}
Пример #2
0
	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;
	}
Пример #3
0
	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
}