perform copy_file_range until EOF is reached instead of basing things on file size
This solves several problems - race conditions where a file is truncated while copying from it. if we blindly trusted the file size this would lead to an infinite loop - proc files appearing empty to copy_file_range but not to read/write https://github.com/coreutils/coreutils/commit/4b04a0c - copy_file_range returning 0 for some filesystems (overlay? bind mounts?) inside docker, again leading to an infinite loop
This commit is contained in:
parent
f0783632d3
commit
4ddedd5214
1 changed files with 12 additions and 3 deletions
|
@ -1140,14 +1140,14 @@ pub fn copy(from: &Path, to: &Path) -> io::Result<u64> {
|
|||
}
|
||||
|
||||
let (mut reader, reader_metadata) = open_from(from)?;
|
||||
let len = reader_metadata.len();
|
||||
let max_len = u64::MAX;
|
||||
let (mut writer, _) = open_to_and_set_permissions(to, reader_metadata)?;
|
||||
|
||||
let has_copy_file_range = HAS_COPY_FILE_RANGE.load(Ordering::Relaxed);
|
||||
let mut written = 0u64;
|
||||
while written < len {
|
||||
while written < max_len {
|
||||
let copy_result = if has_copy_file_range {
|
||||
let bytes_to_copy = cmp::min(len - written, usize::MAX as u64) as usize;
|
||||
let bytes_to_copy = cmp::min(max_len - written, usize::MAX as u64) as usize;
|
||||
let copy_result = unsafe {
|
||||
// We actually don't have to adjust the offsets,
|
||||
// because copy_file_range adjusts the file offset automatically
|
||||
|
@ -1173,6 +1173,15 @@ pub fn copy(from: &Path, to: &Path) -> io::Result<u64> {
|
|||
Err(io::Error::from_raw_os_error(libc::ENOSYS))
|
||||
};
|
||||
match copy_result {
|
||||
Ok(0) if written == 0 => {
|
||||
// fallback to work around several kernel bugs where copy_file_range will fail to
|
||||
// copy any bytes and return 0 instead of an error if
|
||||
// - reading virtual files from the proc filesystem which appear to have 0 size
|
||||
// but are not empty. noted in coreutils to affect kernels at least up to 5.6.19.
|
||||
// - copying from an overlay filesystem in docker. reported to occur on fedora 32.
|
||||
return io::copy(&mut reader, &mut writer);
|
||||
}
|
||||
Ok(0) => return Ok(written), // reached EOF
|
||||
Ok(ret) => written += ret as u64,
|
||||
Err(err) => {
|
||||
match err.raw_os_error() {
|
||||
|
|
Loading…
Add table
Reference in a new issue