diff --git a/src/uu/cp/src/platform/linux.rs b/src/uu/cp/src/platform/linux.rs
index f0b2af752..7d97813dd 100644
--- a/src/uu/cp/src/platform/linux.rs
+++ b/src/uu/cp/src/platform/linux.rs
@@ -3,13 +3,16 @@
// * For the full copyright and license information, please view the LICENSE
// * file that was distributed with this source code.
// spell-checker:ignore ficlone reflink ftruncate pwrite fiemap
-use std::fs::File;
+use std::fs::{File, OpenOptions};
use std::io::Read;
+use std::os::unix::fs::OpenOptionsExt;
use std::os::unix::io::AsRawFd;
use std::path::Path;
use quick_error::ResultExt;
+use uucore::mode::get_umask;
+
use crate::{CopyResult, ReflinkMode, SparseMode};
// From /usr/include/linux/fs.h:
@@ -27,9 +30,6 @@ enum CloneFallback {
/// Raise an error.
Error,
- /// Use [`std::io::copy`].
- IOCopy,
-
/// Use [`std::fs::copy`].
FSCopy,
}
@@ -42,8 +42,8 @@ fn clone
(source: P, dest: P, fallback: CloneFallback) -> std::io::Result<()>
where
P: AsRef,
{
- let mut src_file = File::open(&source)?;
- let mut dst_file = File::create(&dest)?;
+ let src_file = File::open(&source)?;
+ let dst_file = File::create(&dest)?;
let src_fd = src_file.as_raw_fd();
let dst_fd = dst_file.as_raw_fd();
let result = unsafe { libc::ioctl(dst_fd, FICLONE!(), src_fd) };
@@ -52,7 +52,6 @@ where
}
match fallback {
CloneFallback::Error => Err(std::io::Error::last_os_error()),
- CloneFallback::IOCopy => std::io::copy(&mut src_file, &mut dst_file).map(|_| ()),
CloneFallback::FSCopy => std::fs::copy(source, dest).map(|_| ()),
}
}
@@ -98,6 +97,41 @@ where
Ok(())
}
+/// Copy the contents of the given source FIFO to the given file.
+fn copy_fifo_contents(source: P, dest: P) -> std::io::Result
+where
+ P: AsRef,
+{
+ // For some reason,
+ //
+ // cp --preserve=ownership --copy-contents fifo fifo2
+ //
+ // causes `fifo2` to be created with limited permissions (mode 622
+ // or maybe 600 it seems), and then after `fifo` is closed, the
+ // permissions get updated to match those of `fifo`. This doesn't
+ // make much sense to me but the behavior appears in
+ // `tests/cp/file-perm-race.sh`.
+ //
+ // So it seems that if `--preserve=ownership` is true then what we
+ // need to do is create the destination file with limited
+ // permissions, copy the contents, then update the permissions. If
+ // `--preserve=ownership` is not true, however, then we can just
+ // match the mode of the source file.
+ //
+ // TODO Update the code below to respect the case where
+ // `--preserve=ownership` is not true.
+ let mut src_file = File::open(&source)?;
+ let mode = 0o622 & !get_umask();
+ let mut dst_file = OpenOptions::new()
+ .create(true)
+ .write(true)
+ .mode(mode)
+ .open(&dest)?;
+ let num_bytes_copied = std::io::copy(&mut src_file, &mut dst_file)?;
+ dst_file.set_permissions(src_file.metadata()?.permissions())?;
+ Ok(num_bytes_copied)
+}
+
/// Copies `source` to `dest` using copy-on-write if possible.
///
/// The `source_is_fifo` flag must be set to `true` if and only if
@@ -119,7 +153,7 @@ pub(crate) fn copy_on_write(
(ReflinkMode::Auto, _) => {
if source_is_fifo {
- clone(source, dest, CloneFallback::IOCopy)
+ copy_fifo_contents(source, dest).map(|_| ())
} else {
clone(source, dest, CloneFallback::FSCopy)
}