From fdd0b1d9086611df0cddbcf8135f0c758c6dccf9 Mon Sep 17 00:00:00 2001 From: Christopher Dryden Date: Tue, 6 Jan 2026 21:24:32 +0000 Subject: [PATCH 1/3] cp: use lstat for destination check to support LD_PRELOAD tests --- src/uu/cp/src/cp.rs | 3 +-- src/uucore/src/lib/features/fs.rs | 10 ++++++++++ util/fetch-gnu.sh | 1 + 3 files changed, 12 insertions(+), 2 deletions(-) diff --git a/src/uu/cp/src/cp.rs b/src/uu/cp/src/cp.rs index 3048f38b711..1a8f4040d6f 100644 --- a/src/uu/cp/src/cp.rs +++ b/src/uu/cp/src/cp.rs @@ -1390,8 +1390,7 @@ pub fn copy(sources: &[PathBuf], target: &Path, options: &Options) -> CopyResult let dest = construct_dest_path(source, target, target_type, options) .unwrap_or_else(|_| target.to_path_buf()); - if fs::metadata(&dest).is_ok() - && !fs::symlink_metadata(&dest)?.file_type().is_symlink() + if FileInformation::from_path(&dest, false).is_ok_and(|info| !info.is_symlink()) // if both `source` and `dest` are symlinks, it should be considered as an overwrite. || fs::metadata(source).is_ok() && fs::symlink_metadata(source)?.file_type().is_symlink() diff --git a/src/uucore/src/lib/features/fs.rs b/src/uucore/src/lib/features/fs.rs index bebfd1821cf..90f5f1a6481 100644 --- a/src/uucore/src/lib/features/fs.rs +++ b/src/uucore/src/lib/features/fs.rs @@ -166,6 +166,16 @@ impl FileInformation { #[cfg(any(target_os = "netbsd", not(target_pointer_width = "64")))] return self.0.st_ino.into(); } + + #[cfg(unix)] + pub fn is_symlink(&self) -> bool { + (self.0.st_mode as mode_t & S_IFMT) == S_IFLNK + } + + #[cfg(windows)] + pub fn is_symlink(&self) -> bool { + (self.0.file_attributes() & windows_sys::Win32::Storage::FileSystem::FILE_ATTRIBUTE_REPARSE_POINT) != 0 + } } #[cfg(unix)] diff --git a/util/fetch-gnu.sh b/util/fetch-gnu.sh index 92e88ed75c6..34bb3fb9c5e 100755 --- a/util/fetch-gnu.sh +++ b/util/fetch-gnu.sh @@ -7,6 +7,7 @@ curl -L "${repo}/releases/download/v${ver}/coreutils-${ver}.tar.xz" | tar --stri curl -L ${repo}/raw/refs/heads/master/tests/mv/hardlink-case.sh > tests/mv/hardlink-case.sh curl -L ${repo}/raw/refs/heads/master/tests/mkdir/writable-under-readonly.sh > tests/mkdir/writable-under-readonly.sh curl -L ${repo}/raw/refs/heads/master/tests/cp/cp-mv-enotsup-xattr.sh > tests/cp/cp-mv-enotsup-xattr.sh #spell-checker:disable-line +curl -L ${repo}/raw/refs/heads/master/tests/cp/nfs-removal-race.sh > tests/cp/nfs-removal-race.sh curl -L ${repo}/raw/refs/heads/master/tests/csplit/csplit-io-err.sh > tests/csplit/csplit-io-err.sh # Avoid incorrect PASS curl -L ${repo}/raw/refs/heads/master/tests/runcon/runcon-compute.sh > tests/runcon/runcon-compute.sh From b583af4381f1da27e8e68043ed545f56b6112a5d Mon Sep 17 00:00:00 2001 From: Christopher Dryden Date: Tue, 6 Jan 2026 21:37:41 +0000 Subject: [PATCH 2/3] fs: fix Windows is_symlink type mismatch and add explanatory comment --- src/uucore/src/lib/features/fs.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/uucore/src/lib/features/fs.rs b/src/uucore/src/lib/features/fs.rs index 90f5f1a6481..0d1abb76868 100644 --- a/src/uucore/src/lib/features/fs.rs +++ b/src/uucore/src/lib/features/fs.rs @@ -167,6 +167,9 @@ impl FileInformation { return self.0.st_ino.into(); } + // The concept of symlinks doesn't map exactly to Windows (which has reparse + // points, junctions, etc.), so having our own method lets us control what + // we consider a symlink across platforms. #[cfg(unix)] pub fn is_symlink(&self) -> bool { (self.0.st_mode as mode_t & S_IFMT) == S_IFLNK @@ -174,7 +177,8 @@ impl FileInformation { #[cfg(windows)] pub fn is_symlink(&self) -> bool { - (self.0.file_attributes() & windows_sys::Win32::Storage::FileSystem::FILE_ATTRIBUTE_REPARSE_POINT) != 0 + use windows_sys::Win32::Storage::FileSystem::FILE_ATTRIBUTE_REPARSE_POINT; + self.0.file_attributes() & u64::from(FILE_ATTRIBUTE_REPARSE_POINT) != 0 } } From 6a3d9021d9583cf0dce2cb0f4bd365b065b0fe7f Mon Sep 17 00:00:00 2001 From: Christopher Dryden Date: Tue, 6 Jan 2026 22:03:35 +0000 Subject: [PATCH 3/3] cp: use stat for dest existence check to support LD_PRELOAD --- src/uu/cp/src/cp.rs | 3 ++- src/uucore/src/lib/features/fs.rs | 14 -------------- 2 files changed, 2 insertions(+), 15 deletions(-) diff --git a/src/uu/cp/src/cp.rs b/src/uu/cp/src/cp.rs index 1a8f4040d6f..cd84caa36af 100644 --- a/src/uu/cp/src/cp.rs +++ b/src/uu/cp/src/cp.rs @@ -1390,7 +1390,8 @@ pub fn copy(sources: &[PathBuf], target: &Path, options: &Options) -> CopyResult let dest = construct_dest_path(source, target, target_type, options) .unwrap_or_else(|_| target.to_path_buf()); - if FileInformation::from_path(&dest, false).is_ok_and(|info| !info.is_symlink()) + if FileInformation::from_path(&dest, true).is_ok() + && !fs::symlink_metadata(&dest).is_ok_and(|m| m.file_type().is_symlink()) // if both `source` and `dest` are symlinks, it should be considered as an overwrite. || fs::metadata(source).is_ok() && fs::symlink_metadata(source)?.file_type().is_symlink() diff --git a/src/uucore/src/lib/features/fs.rs b/src/uucore/src/lib/features/fs.rs index 0d1abb76868..bebfd1821cf 100644 --- a/src/uucore/src/lib/features/fs.rs +++ b/src/uucore/src/lib/features/fs.rs @@ -166,20 +166,6 @@ impl FileInformation { #[cfg(any(target_os = "netbsd", not(target_pointer_width = "64")))] return self.0.st_ino.into(); } - - // The concept of symlinks doesn't map exactly to Windows (which has reparse - // points, junctions, etc.), so having our own method lets us control what - // we consider a symlink across platforms. - #[cfg(unix)] - pub fn is_symlink(&self) -> bool { - (self.0.st_mode as mode_t & S_IFMT) == S_IFLNK - } - - #[cfg(windows)] - pub fn is_symlink(&self) -> bool { - use windows_sys::Win32::Storage::FileSystem::FILE_ATTRIBUTE_REPARSE_POINT; - self.0.file_attributes() & u64::from(FILE_ATTRIBUTE_REPARSE_POINT) != 0 - } } #[cfg(unix)]