in sgx_tstd/src/sys/kernel_copy.rs [161:229]
fn copy(self) -> Result<u64> {
let (reader, writer) = (self.read, self.write);
let r_cfg = reader.properties();
let w_cfg = writer.properties();
// before direct operations on file descriptors ensure that all source and sink buffers are empty
let mut flush = || -> crate::io::Result<u64> {
let bytes = reader.drain_to(writer, u64::MAX)?;
// BufWriter buffered bytes have already been accounted for in earlier write() calls
writer.flush()?;
Ok(bytes)
};
let mut written = 0u64;
if let (CopyParams(input_meta, Some(readfd)), CopyParams(output_meta, Some(writefd))) =
(r_cfg, w_cfg)
{
written += flush()?;
let max_write = reader.min_limit();
if input_meta.copy_file_range_candidate() && output_meta.copy_file_range_candidate() {
let result = copy_regular_files(readfd, writefd, max_write);
result.update_take(reader);
match result {
CopyResult::Ended(bytes_copied) => return Ok(bytes_copied + written),
CopyResult::Error(e, _) => return Err(e),
CopyResult::Fallback(bytes) => written += bytes,
}
}
// on modern kernels sendfile can copy from any mmapable type (some but not all regular files and block devices)
// to any writable file descriptor. On older kernels the writer side can only be a socket.
// So we just try and fallback if needed.
// If current file offsets + write sizes overflow it may also fail, we do not try to fix that and instead
// fall back to the generic copy loop.
if input_meta.potential_sendfile_source() {
let result = sendfile_splice(SpliceMode::Sendfile, readfd, writefd, max_write);
result.update_take(reader);
match result {
CopyResult::Ended(bytes_copied) => return Ok(bytes_copied + written),
CopyResult::Error(e, _) => return Err(e),
CopyResult::Fallback(bytes) => written += bytes,
}
}
if input_meta.maybe_fifo() || output_meta.maybe_fifo() {
let result = sendfile_splice(SpliceMode::Splice, readfd, writefd, max_write);
result.update_take(reader);
match result {
CopyResult::Ended(bytes_copied) => return Ok(bytes_copied + written),
CopyResult::Error(e, _) => return Err(e),
CopyResult::Fallback(0) => { /* use the fallback below */ }
CopyResult::Fallback(_) => {
unreachable!("splice should not return > 0 bytes on the fallback path")
}
}
}
}
// fallback if none of the more specialized syscalls wants to work with these file descriptors
match generic_copy(reader, writer) {
Ok(bytes) => Ok(bytes + written),
err => err,
}
}