diff --git a/src/uu/touch/src/touch.rs b/src/uu/touch/src/touch.rs index 102a60d06de..3c85d996ec5 100644 --- a/src/uu/touch/src/touch.rs +++ b/src/uu/touch/src/touch.rs @@ -17,7 +17,7 @@ use jiff::tz::TimeZone; use jiff::{Timestamp, ToSpan, Zoned}; use std::borrow::Cow; use std::ffi::{OsStr, OsString}; -use std::fs::{self, File}; +use std::fs; use std::io::{Error, ErrorKind}; use std::path::{Path, PathBuf}; use std::time::SystemTime; @@ -476,7 +476,13 @@ fn touch_file( return Ok(()); } - if let Err(e) = File::create(path) { + // Create empty file without truncation for inotify compatibility + // Use create_new to avoid O_TRUNC behavior that interferes with inotify + if let Err(e) = fs::OpenOptions::new() + .write(true) + .create_new(true) + .open(path) + { // we need to check if the path is the path to a directory (ends with a separator) // we can't use File::create to create a directory // we cannot use path.is_dir() because it calls fs::metadata which we already called diff --git a/tests/by-util/test_touch.rs b/tests/by-util/test_touch.rs index 29425d6aa6c..afbbf3493c0 100644 --- a/tests/by-util/test_touch.rs +++ b/tests/by-util/test_touch.rs @@ -1061,3 +1061,44 @@ fn test_touch_device_files() { .succeeds() .no_output(); } + +#[test] +#[cfg(unix)] +fn test_touch_inotify_compatibility() { + // Regression test for inotify file change detection issue + // Ensures touch creates files with GNU-compatible flags (without O_TRUNC) + // that don't interfere with inotify event generation + + let (at, mut ucmd) = at_and_ucmd!(); + let test_file = "inotify_test.txt"; + + // Create new file + ucmd.arg(test_file).succeeds().no_output(); + assert!(at.file_exists(test_file)); + + // Verify file creation uses correct flags by checking it doesn't truncate existing content + let initial_content = "test content"; + at.write(test_file, initial_content); + + // Touch existing file should not truncate content + new_ucmd!().arg(at.plus(test_file)).succeeds().no_output(); + assert_eq!(at.read(test_file), initial_content); + + // Verify timestamp changes are properly applied + let (atime_before, mtime_before) = get_file_times(&at, test_file); + + std::thread::sleep(std::time::Duration::from_secs(1)); + new_ucmd!().arg(at.plus(test_file)).succeeds().no_output(); + + let (atime_after, mtime_after) = get_file_times(&at, test_file); + + // Timestamps should be updated (inotify compatibility ensures proper event generation) + assert!( + mtime_after > mtime_before, + "Modification time should be updated for inotify compatibility" + ); + assert!( + atime_after >= atime_before, + "Access time should be updated or preserved" + ); +}