Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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
10 changes: 8 additions & 2 deletions src/uu/touch/src/touch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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
Expand Down
41 changes: 41 additions & 0 deletions tests/by-util/test_touch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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"
);
}
Loading