Example #1
0
File: async.c Project: L0op/qemu
static gboolean
aio_ctx_prepare(GSource *source, gint    *timeout)
{
    AioContext *ctx = (AioContext *) source;

    /* We assume there is no timeout already supplied */
    *timeout = qemu_timeout_ns_to_ms(aio_compute_timeout(ctx));

    if (aio_prepare(ctx)) {
        *timeout = 0;
    }

    return *timeout == 0;
}
int MtpFfsCompatHandle::sendFile(mtp_file_range mfr) {
    uint64_t file_length = mfr.length;
    uint32_t given_length = std::min(static_cast<uint64_t>(MAX_MTP_FILE_SIZE),
            file_length + sizeof(mtp_data_header));
    uint64_t offset = mfr.offset;
    int packet_size = getPacketSize(mBulkIn);

    // If file_length is larger than a size_t, truncating would produce the wrong comparison.
    // Instead, promote the left side to 64 bits, then truncate the small result.
    int init_read_len = std::min(
            static_cast<uint64_t>(packet_size - sizeof(mtp_data_header)), file_length);

    unsigned char *data = mIobuf[0].bufs.data();
    unsigned char *data2 = mIobuf[1].bufs.data();

    posix_fadvise(mfr.fd, 0, 0, POSIX_FADV_SEQUENTIAL | POSIX_FADV_NOREUSE);

    struct aiocb aio;
    aio.aio_fildes = mfr.fd;
    struct aiocb *aiol[] = {&aio};
    int ret, length;
    int error = 0;
    bool read = false;
    bool write = false;

    // Send the header data
    mtp_data_header *header = reinterpret_cast<mtp_data_header*>(data);
    header->length = htole32(given_length);
    header->type = htole16(2); /* data packet */
    header->command = htole16(mfr.command);
    header->transaction_id = htole32(mfr.transaction_id);

    // Some hosts don't support header/data separation even though MTP allows it
    // Handle by filling first packet with initial file data
    if (TEMP_FAILURE_RETRY(pread(mfr.fd, reinterpret_cast<char*>(data) +
                    sizeof(mtp_data_header), init_read_len, offset))
            != init_read_len) return -1;
    if (writeHandle(mBulkIn, data, sizeof(mtp_data_header) + init_read_len) == -1) return -1;
    file_length -= init_read_len;
    offset += init_read_len;
    ret = init_read_len + sizeof(mtp_data_header);

    // Break down the file into pieces that fit in buffers
    while (file_length > 0) {
        if (read) {
            // Wait for the previous read to finish
            aio_suspend(aiol, 1, nullptr);
            ret = aio_return(&aio);
            if (ret == -1) {
                errno = aio_error(&aio);
                return -1;
            }
            if (static_cast<size_t>(ret) < aio.aio_nbytes) {
                errno = EIO;
                return -1;
            }

            file_length -= ret;
            offset += ret;
            std::swap(data, data2);
            read = false;
            write = true;
        }

        if (error == -1) {
            return -1;
        }

        if (file_length > 0) {
            length = std::min(static_cast<uint64_t>(MAX_FILE_CHUNK_SIZE), file_length);
            // Queue up another read
            aio_prepare(&aio, data, length, offset);
            aio_read(&aio);
            read = true;
        }

        if (write) {
            if (writeHandle(mBulkIn, data2, ret) == -1) {
                error = -1;
            }
            write = false;
        }
    }

    if (ret % packet_size == 0) {
        // If the last packet wasn't short, send a final empty packet
        if (TEMP_FAILURE_RETRY(::write(mBulkIn, data, 0)) != 0) {
            return -1;
        }
    }

    return 0;
}
int MtpFfsCompatHandle::receiveFile(mtp_file_range mfr, bool zero_packet) {
    // When receiving files, the incoming length is given in 32 bits.
    // A >4G file is given as 0xFFFFFFFF
    uint32_t file_length = mfr.length;
    uint64_t offset = mfr.offset;
    int packet_size = getPacketSize(mBulkOut);

    unsigned char *data = mIobuf[0].bufs.data();
    unsigned char *data2 = mIobuf[1].bufs.data();

    struct aiocb aio;
    aio.aio_fildes = mfr.fd;
    aio.aio_buf = nullptr;
    struct aiocb *aiol[] = {&aio};
    int ret = -1;
    size_t length;
    bool read = false;
    bool write = false;

    posix_fadvise(mfr.fd, 0, 0, POSIX_FADV_SEQUENTIAL | POSIX_FADV_NOREUSE);

    // Break down the file into pieces that fit in buffers
    while (file_length > 0 || write) {
        if (file_length > 0) {
            length = std::min(static_cast<uint32_t>(MAX_FILE_CHUNK_SIZE), file_length);

            // Read data from USB, handle errors after waiting for write thread.
            ret = readHandle(mBulkOut, data, length);

            if (file_length != MAX_MTP_FILE_SIZE && ret < static_cast<int>(length)) {
                ret = -1;
                errno = EIO;
            }
            read = true;
        }

        if (write) {
            // get the return status of the last write request
            aio_suspend(aiol, 1, nullptr);

            int written = aio_return(&aio);
            if (written == -1) {
                errno = aio_error(&aio);
                return -1;
            }
            if (static_cast<size_t>(written) < aio.aio_nbytes) {
                errno = EIO;
                return -1;
            }
            write = false;
        }

        // If there was an error reading above
        if (ret == -1) {
            return -1;
        }

        if (read) {
            if (file_length == MAX_MTP_FILE_SIZE) {
                // For larger files, receive until a short packet is received.
                if (static_cast<size_t>(ret) < length) {
                    file_length = 0;
                }
            } else {
                file_length -= ret;
            }
            // Enqueue a new write request
            aio_prepare(&aio, data, length, offset);
            aio_write(&aio);

            offset += ret;
            std::swap(data, data2);

            write = true;
            read = false;
        }
    }
    // Receive an empty packet if size is a multiple of the endpoint size.
    if (ret % packet_size == 0 || zero_packet) {
        if (TEMP_FAILURE_RETRY(::read(mBulkOut, data, packet_size)) != 0) {
            return -1;
        }
    }
    return 0;
}