From 2f6b64cee4218ca3d445d04be2d9ed68dff80da9 Mon Sep 17 00:00:00 2001 From: ccmsi Date: Tue, 15 Sep 2026 21:33:48 -0400 Subject: [PATCH 1/2] sync: fsync() file operands when no -d/-f flags (GNU MODE_FILE compat) With file operands and no --data/-d or --file-system/-f flag, GNU sync enters MODE_FILE and fsync()s each operand. uutils instead falls through to a global sync(), silently ignoring the operands and blocking on every filesystem behind initramfs/dracut. Add an fsync(2) path (Linux/Android) mirroring do_fdatasync, and a regression test using /proc/self/mem which rejects fsync() with EINVAL (a global sync() would wrongly exit 0). Fixes: #14591 --- src/uu/sync/src/sync.rs | 13 +++++++++++++ tests/by-util/test_sync.rs | 13 +++++++++++++ 2 files changed, 26 insertions(+) diff --git a/src/uu/sync/src/sync.rs b/src/uu/sync/src/sync.rs index d6137345056..8693e7ae2ac 100644 --- a/src/uu/sync/src/sync.rs +++ b/src/uu/sync/src/sync.rs @@ -86,6 +86,11 @@ mod platform { pub fn do_fdatasync(files: &[String]) -> UResult<()> { do_sync_with(files, rustix::fs::fdatasync) } + + #[cfg(any(target_os = "linux", target_os = "android"))] + pub fn do_fsync(files: &[String]) -> UResult<()> { + do_sync_with(files, rustix::fs::fsync) + } } #[cfg(windows)] @@ -254,6 +259,9 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> { } else if matches.get_flag(options::DATA) { #[cfg(any(target_os = "linux", target_os = "android"))] fdatasync(&files)?; + } else if !files.is_empty() { + #[cfg(any(target_os = "linux", target_os = "android"))] + fsync(&files)?; } else { sync()?; } @@ -303,3 +311,8 @@ fn syncfs(files: &[String]) -> UResult<()> { fn fdatasync(files: &[String]) -> UResult<()> { platform::do_fdatasync(files) } + +#[cfg(any(target_os = "linux", target_os = "android"))] +fn fsync(files: &[String]) -> UResult<()> { + platform::do_fsync(files) +} diff --git a/tests/by-util/test_sync.rs b/tests/by-util/test_sync.rs index 8516aac9d77..0f775e83f66 100644 --- a/tests/by-util/test_sync.rs +++ b/tests/by-util/test_sync.rs @@ -143,6 +143,19 @@ fn test_sync_fdatasync_error_handling() { .stderr_contains("error opening"); } +#[cfg(any(target_os = "linux", target_os = "android"))] +#[test] +fn test_sync_file_operands_fsync_each_file() { + // Regression test: `sync FILE` (no -d/-f flags) must fsync each file + // like GNU coreutils, not fall back to a global sync() that ignores + // the operands. /proc/self/mem rejects fsync() with EINVAL, so this + // must fail; a global sync() would wrongly exit 0. + new_ucmd!() + .arg("/proc/self/mem") + .fails_with_code(1) + .stderr_contains("error syncing"); +} + #[cfg(target_vendor = "apple")] #[test] fn test_sync_syncfs_error_handling_macos() { From 466fb2b815140f12972f9d0e3ec77c43be04103b Mon Sep 17 00:00:00 2001 From: Cam <6634309+lostallmymoney@users.noreply.github.com> Date: Wed, 16 Sep 2026 15:06:56 -0400 Subject: [PATCH 2/2] Update tests/by-util/test_sync.rs Co-authored-by: oech3 <79379754+oech3@users.noreply.github.com> --- tests/by-util/test_sync.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/by-util/test_sync.rs b/tests/by-util/test_sync.rs index 0f775e83f66..eafb2a558ed 100644 --- a/tests/by-util/test_sync.rs +++ b/tests/by-util/test_sync.rs @@ -151,7 +151,7 @@ fn test_sync_file_operands_fsync_each_file() { // the operands. /proc/self/mem rejects fsync() with EINVAL, so this // must fail; a global sync() would wrongly exit 0. new_ucmd!() - .arg("/proc/self/mem") + .arg("/dev/null") .fails_with_code(1) .stderr_contains("error syncing"); }