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..eafb2a558ed 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("/dev/null") + .fails_with_code(1) + .stderr_contains("error syncing"); +} + #[cfg(target_vendor = "apple")] #[test] fn test_sync_syncfs_error_handling_macos() {