示例#1
0
BOOL My_WriteFileGather()
{
	HANDLE hFile=NULL;
	FILE_SEGMENT_ELEMENT aSegmentArray[50];
	DWORD nNumberOfBytesToWrite=NULL;
	LPDWORD lpReserved=NULL;
	LPOVERLAPPED lpOverlapped=NULL;
	BOOL returnVal_Real = NULL;
	BOOL returnVal_Intercepted = NULL;

	DWORD error_Real = 0;
	DWORD error_Intercepted = 0;
	disableInterception();
	returnVal_Real = WriteFileGather (hFile,aSegmentArray,nNumberOfBytesToWrite,lpReserved,lpOverlapped);
	error_Real = GetLastError();
	enableInterception();
	returnVal_Intercepted = WriteFileGather (hFile,aSegmentArray,nNumberOfBytesToWrite,lpReserved,lpOverlapped);
	error_Intercepted = GetLastError();
	return ((returnVal_Real == returnVal_Intercepted) && (error_Real == error_Intercepted));
}
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
}