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; }