Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
c9d6f81
feat: Expand safe directory traversal to all Unix platforms and fix r…
abendrothj Dec 23, 2025
9cd62b3
fix: Fix type conversion errors and add TOCTOU to dictionary
abendrothj Dec 27, 2025
f1c7082
Merge branch 'main' of https://github.com/uutils/coreutils into main
abendrothj Dec 27, 2025
1c06851
Merge branch 'main' into main
abendrothj Dec 28, 2025
e1d5465
fix: Fix macOS/BSD build failures and test compatibility
abendrothj Dec 28, 2025
cfbf666
Merge branch 'main' into main
abendrothj Dec 28, 2025
9445537
fix: Add cross-platform type handling in safe_traversal
abendrothj Dec 28, 2025
959d39d
Merge branch 'main' into main
abendrothj Dec 28, 2025
d8348c5
fix: That should be all the casts ignored by clippy (were showing as …
abendrothj Dec 28, 2025
4f65a77
fix: Suppress unnecessary cast warnings in cp and du modules
abendrothj Dec 28, 2025
3cb2071
fix: Ensure proper type handling for mode_t in rm and safe_traversal …
abendrothj Dec 28, 2025
29a04c0
Merge branch 'main' into main
abendrothj Dec 28, 2025
e326036
fix: Improve code formatting for readability in safe_remove_dir_recur…
abendrothj Dec 28, 2025
ee18c58
fix(unix,redox): platform compatibility
abendrothj Dec 28, 2025
c8cc10c
fix: Use type inference for mode checks in prompt_file_with_stat and …
abendrothj Dec 28, 2025
05d675b
fix(android): handle symlink and directory checks with appropriate ty…
abendrothj Dec 28, 2025
0f48443
fix(redox): improve error handling for unsupported safe traversal ope…
abendrothj Dec 28, 2025
e6a9637
fix(unix): simplify symlink and directory checks for improved readabi…
abendrothj Dec 28, 2025
5704355
test(date): Relax locale format assertions to support variable CI env…
abendrothj Dec 28, 2025
e4f13ef
fix(uucore): gate unix-specific safe_traversal methods for redox
abendrothj Dec 28, 2025
827556a
fix(redox): gate Metadata and nix-dependent methods for Redox compati…
abendrothj Dec 29, 2025
d946a9d
Merge branch 'main' into main
abendrothj Dec 29, 2025
9f41965
fix(redox): move libc import under the correct conditional compilation
abendrothj Dec 29, 2025
b0803a5
Merge branch 'main' into main
sylvestre Dec 29, 2025
262b53b
Merge branch 'main' into main
abendrothj Dec 30, 2025
78cbd9a
Refactor conditional compilation for Unix-specific features to exclud…
abendrothj Dec 30, 2025
ea5ad61
Remove Redox-specific exclusion code from safe_traversal module
abendrothj Dec 30, 2025
df5f26a
Exclude Redox from safe_traversal feature for Unix compatibility
abendrothj Dec 30, 2025
3e0c411
Merge branch 'main' into main
abendrothj Dec 31, 2025
2102a85
rm: add Redox-specific stub implementations for safe traversal
abendrothj Dec 31, 2025
370107e
rm: add spell-checker directive for fstatat in Redox stub implementat…
abendrothj Dec 31, 2025
b6c20a8
chmod, du: exclude Redox from safe-traversal feature
abendrothj Dec 31, 2025
5cc0c65
cargo: fix formatting of uucore dependencies in Cargo.toml
abendrothj Dec 31, 2025
1fc7d18
Merge branch 'main' into main
abendrothj Dec 31, 2025
44950a2
Merge branch 'main' into main
abendrothj Jan 2, 2026
b135427
Merge branch 'main' into main
abendrothj Jan 5, 2026
fe8ab05
test(chmod): improve error message handling for permission denied cases
abendrothj Jan 5, 2026
b5eab57
refactor(chmod, rm): update platform-specific handling for Redox OS a…
abendrothj Jan 7, 2026
12566dd
Merge upstream/main and resolve conflicts in chmod.rs
abendrothj Jan 7, 2026
d8cadbe
Merge branch 'main'
abendrothj Jan 9, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 8 additions & 8 deletions src/uu/chmod/src/chmod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ use uucore::libc::mode_t;
use uucore::mode;
use uucore::perms::{TraverseSymlinks, configure_symlink_and_recursion};

#[cfg(target_os = "linux")]
#[cfg(unix)]
use uucore::safe_traversal::DirFd;
use uucore::{format_usage, show, show_error};

Expand Down Expand Up @@ -338,7 +338,7 @@ impl Chmoder {
}

/// Handle symlinks during directory traversal based on traversal mode
#[cfg(not(target_os = "linux"))]
#[cfg(not(unix))]
fn handle_symlink_during_traversal(
&self,
path: &Path,
Expand Down Expand Up @@ -423,7 +423,7 @@ impl Chmoder {
r
}

#[cfg(not(target_os = "linux"))]
#[cfg(not(unix))]
fn walk_dir_with_context(&self, file_path: &Path, is_command_line_arg: bool) -> UResult<()> {
let mut r = self.chmod_file(file_path);

Expand Down Expand Up @@ -454,7 +454,7 @@ impl Chmoder {
r
}

#[cfg(target_os = "linux")]
#[cfg(unix)]
fn walk_dir_with_context(&self, file_path: &Path, is_command_line_arg: bool) -> UResult<()> {
let mut r = self.chmod_file(file_path);

Expand Down Expand Up @@ -487,7 +487,7 @@ impl Chmoder {
r
}

#[cfg(target_os = "linux")]
#[cfg(unix)]
fn safe_traverse_dir(&self, dir_fd: &DirFd, dir_path: &Path) -> UResult<()> {
let mut r = Ok(());

Expand Down Expand Up @@ -546,7 +546,7 @@ impl Chmoder {
r
}

#[cfg(target_os = "linux")]
#[cfg(unix)]
fn handle_symlink_during_safe_recursion(
&self,
path: &Path,
Expand Down Expand Up @@ -578,7 +578,7 @@ impl Chmoder {
}
}

#[cfg(target_os = "linux")]
#[cfg(unix)]
fn safe_chmod_file(
&self,
file_path: &Path,
Expand Down Expand Up @@ -610,7 +610,7 @@ impl Chmoder {
Ok(())
}

#[cfg(not(target_os = "linux"))]
#[cfg(not(unix))]
fn handle_symlink_during_recursion(&self, path: &Path) -> UResult<()> {
// Use the common symlink handling logic
self.handle_symlink_during_traversal(path, false)
Expand Down
28 changes: 14 additions & 14 deletions src/uu/du/src/du.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
use uucore::error::{FromIo, UError, UResult, USimpleError, set_exit_code};
use uucore::fsext::{MetadataTimeField, metadata_get_time};
use uucore::line_ending::LineEnding;
#[cfg(target_os = "linux")]
#[cfg(unix)]
use uucore::safe_traversal::DirFd;
use uucore::translate;

Expand Down Expand Up @@ -164,7 +164,7 @@
}

/// Create a Stat using safe traversal methods with `DirFd` for the root directory
#[cfg(target_os = "linux")]
#[cfg(unix)]
fn new_from_dirfd(dir_fd: &DirFd, full_path: &Path) -> std::io::Result<Self> {
// Get metadata for the directory itself using fstat
let safe_metadata = dir_fd.metadata()?;
Expand Down Expand Up @@ -293,9 +293,9 @@
}
}

#[cfg(target_os = "linux")]
// For now, implement safe_du only on Linux
// This is done for Ubuntu but should be extended to other platforms that support openat
#[cfg(unix)]
// Implement safe_du on Unix
// This is done for TOCTOU safety

Check failure on line 298 in src/uu/du/src/du.rs

View workflow job for this annotation

GitHub Actions / Style/spelling (ubuntu-latest, feat_os_unix)

ERROR: `cspell`: Unknown word 'TOCTOU' (file:'src/uu/du/src/du.rs', line:298)
fn safe_du(
path: &Path,
options: &TraversalOptions,
Expand Down Expand Up @@ -439,7 +439,7 @@
const S_IFMT: u32 = 0o170_000;
const S_IFDIR: u32 = 0o040_000;
const S_IFLNK: u32 = 0o120_000;
let is_symlink = (lstat.st_mode & S_IFMT) == S_IFLNK;
let is_symlink = (lstat.st_mode as u32 & S_IFMT) == S_IFLNK;

// Handle symlinks with -L option
// For safe traversal with -L, we skip symlinks to directories entirely
Expand All @@ -450,12 +450,12 @@
continue;
}

let is_dir = (lstat.st_mode & S_IFMT) == S_IFDIR;
let is_dir = (lstat.st_mode as u32 & S_IFMT) == S_IFDIR;
let entry_stat = lstat;

let file_info = (entry_stat.st_ino != 0).then_some(FileInfo {
file_id: entry_stat.st_ino as u128,
dev_id: entry_stat.st_dev,
dev_id: entry_stat.st_dev as u64,
});

// For safe traversal, we need to handle stats differently
Expand Down Expand Up @@ -1106,14 +1106,14 @@
let mut seen_inodes: HashSet<FileInfo> = HashSet::new();

// Determine which traversal method to use
#[cfg(target_os = "linux")]
#[cfg(unix)]
let use_safe_traversal = traversal_options.dereference != Deref::All;
#[cfg(not(target_os = "linux"))]
#[cfg(not(unix))]
let use_safe_traversal = false;

if use_safe_traversal {
// Use safe traversal (Linux only, when not using -L)
#[cfg(target_os = "linux")]
// Use safe traversal (Unix only, when not using -L)
#[cfg(unix)]
{
// Pre-populate seen_inodes with the starting directory to detect cycles
if let Ok(stat) = Stat::new(&path, None, &traversal_options) {
Expand Down Expand Up @@ -1168,9 +1168,9 @@
.send(Ok(StatPrintInfo { stat, depth: 0 }))
.map_err(|e| USimpleError::new(1, e.to_string()))?;
} else {
#[cfg(target_os = "linux")]
#[cfg(unix)]
let error_msg = translate!("du-error-cannot-access", "path" => path.quote());
#[cfg(not(target_os = "linux"))]
#[cfg(not(unix))]
let error_msg = translate!("du-error-cannot-access-no-such-file", "path" => path.to_string_lossy().quote());

print_tx
Expand Down
8 changes: 4 additions & 4 deletions src/uu/rm/src/platform/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@

// Platform-specific implementations for the rm utility

#[cfg(target_os = "linux")]
pub mod linux;
#[cfg(unix)]
pub mod unix;

#[cfg(target_os = "linux")]
pub use linux::*;
#[cfg(unix)]
pub use unix::*;
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
// For the full copyright and license information, please view the LICENSE
// file that was distributed with this source code.

// Linux-specific implementations for the rm utility
// Unix-specific implementations for the rm utility

// spell-checker:ignore fstatat unlinkat

Expand Down
20 changes: 10 additions & 10 deletions src/uu/rm/src/rm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ use uucore::translate;
use uucore::{format_usage, os_str_as_bytes, prompt_yes, show_error};

mod platform;
#[cfg(target_os = "linux")]
#[cfg(unix)]
use platform::{safe_remove_dir_recursive, safe_remove_empty_dir, safe_remove_file};

#[derive(Debug, Error)]
Expand Down Expand Up @@ -539,7 +539,7 @@ fn is_readable_metadata(metadata: &Metadata) -> bool {

/// Whether the given file or directory is readable.
#[cfg(unix)]
#[cfg(not(target_os = "linux"))]
#[cfg(not(unix))]
Comment thread
abendrothj marked this conversation as resolved.
Outdated
fn is_readable(path: &Path) -> bool {
match fs::metadata(path) {
Err(_) => false,
Expand Down Expand Up @@ -605,14 +605,14 @@ fn remove_dir_recursive(
return false;
}

// Use secure traversal on Linux for all recursive directory removals
#[cfg(target_os = "linux")]
// Use secure traversal on Unix (all supported platforms) for all recursive directory removals
#[cfg(unix)]
{
safe_remove_dir_recursive(path, options, progress_bar)
}

// Fallback for non-Linux or use fs::remove_dir_all for very long paths
#[cfg(not(target_os = "linux"))]
// Fallback for non-Unix or use fs::remove_dir_all for very long paths
#[cfg(not(unix))]
{
if let Some(s) = path.to_str() {
if s.len() > 1000 {
Expand Down Expand Up @@ -734,8 +734,8 @@ fn remove_dir(path: &Path, options: &Options, progress_bar: Option<&ProgressBar>
return true;
}

// Use safe traversal on Linux for empty directory removal
#[cfg(target_os = "linux")]
// Use safe traversal on Unix (all supported platforms) for empty directory removal
#[cfg(unix)]
{
if let Some(result) = safe_remove_empty_dir(path, options, progress_bar) {
return result;
Expand All @@ -758,8 +758,8 @@ fn remove_file(path: &Path, options: &Options, progress_bar: Option<&ProgressBar
pb.inc(1);
}

// Use safe traversal on Linux for individual file removal
#[cfg(target_os = "linux")]
// Use safe traversal on Unix for individual file removal
#[cfg(unix)]
{
if let Some(result) = safe_remove_file(path, options, progress_bar) {
return result;
Expand Down
2 changes: 1 addition & 1 deletion src/uucore/src/lib/features.rs
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ pub mod pipes;
pub mod proc_info;
#[cfg(all(unix, feature = "process"))]
pub mod process;
#[cfg(target_os = "linux")]
#[cfg(unix)]
pub mod safe_traversal;
#[cfg(all(target_os = "linux", feature = "tty"))]
pub mod tty;
Expand Down
14 changes: 7 additions & 7 deletions src/uucore/src/lib/features/safe_traversal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
// Safe directory traversal using openat() and related syscalls
// This module provides TOCTOU-safe filesystem operations for recursive traversal
//
// Only available on Linux
// Available on Unix
//
// spell-checker:ignore CLOEXEC RDONLY TOCTOU closedir dirp fdopendir fstatat openat REMOVEDIR unlinkat smallfile
// spell-checker:ignore RAII dirfd fchownat fchown FchmodatFlags fchmodat fchmod
Expand Down Expand Up @@ -253,7 +253,7 @@ impl DirFd {
FchmodatFlags::NoFollowSymlink
};

let mode = Mode::from_bits_truncate(mode);
let mode = Mode::from_bits_truncate(mode as libc::mode_t);

let name_cstr =
CString::new(name.as_bytes()).map_err(|_| SafeTraversalError::PathContainsNull)?;
Expand All @@ -266,7 +266,7 @@ impl DirFd {

/// Change mode of this directory
pub fn fchmod(&self, mode: u32) -> io::Result<()> {
let mode = Mode::from_bits_truncate(mode);
let mode = Mode::from_bits_truncate(mode as libc::mode_t);

nix::sys::stat::fchmod(&self.fd, mode)
.map_err(|e| io::Error::from_raw_os_error(e as i32))?;
Expand Down Expand Up @@ -389,7 +389,7 @@ impl Metadata {
}

pub fn mode(&self) -> u32 {
self.stat.st_mode
self.stat.st_mode as u32
}

pub fn nlink(&self) -> u64 {
Expand Down Expand Up @@ -421,7 +421,7 @@ impl Metadata {
// Add MetadataExt trait implementation for compatibility
impl std::os::unix::fs::MetadataExt for Metadata {
fn dev(&self) -> u64 {
self.stat.st_dev
self.stat.st_dev as u64
}

fn ino(&self) -> u64 {
Expand All @@ -436,7 +436,7 @@ impl std::os::unix::fs::MetadataExt for Metadata {
}

fn mode(&self) -> u32 {
self.stat.st_mode
self.stat.st_mode as u32
}

fn nlink(&self) -> u64 {
Expand All @@ -460,7 +460,7 @@ impl std::os::unix::fs::MetadataExt for Metadata {
}

fn rdev(&self) -> u64 {
self.stat.st_rdev
self.stat.st_rdev as u64
}

fn size(&self) -> u64 {
Expand Down
2 changes: 1 addition & 1 deletion src/uucore/src/lib/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ pub use crate::features::perms;
pub use crate::features::pipes;
#[cfg(all(unix, feature = "process"))]
pub use crate::features::process;
#[cfg(target_os = "linux")]
#[cfg(unix)]
pub use crate::features::safe_traversal;
#[cfg(all(unix, not(target_os = "fuchsia"), feature = "signals"))]
pub use crate::features::signals;
Expand Down
Loading