Skip to content
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
13ed4a3
uncommented u64 impl
Kivooeo Sep 6, 2025
1e772f9
check for empty heads after adding parent
lcnr Oct 2, 2025
36283bc
fix `RebaseReason::Ambiguity`
lcnr Oct 2, 2025
db3c9b6
When computing opaque types in the next solver, take an initial pass …
jackh726 Sep 30, 2025
9a5cef1
Cleanup opaque_type computation a bit
jackh726 Oct 1, 2025
c6c58e3
Remove UsageKind::HasError
jackh726 Oct 1, 2025
a3fbae5
Merge match and if
jackh726 Oct 1, 2025
283ad66
Fix comment, change first_pass to error_on_missing_defining_use, remo…
jackh726 Oct 3, 2025
8b17827
Remove unneeded get_hidden_type
jackh726 Oct 5, 2025
04c2724
constify basic Clone impls
npmccallum Sep 13, 2025
1e6b444
library: fs: Factor out a `file_time_to_timespec` function in prepara…
joshtriplett Oct 7, 2025
1bf555c
library: fs: Factor out the Apple file time to attrlist code for reuse
joshtriplett Oct 7, 2025
0355358
run zero-size assertion in `const {}`
cyrgani Oct 8, 2025
e3d2016
inline constants in generated `enum` `Encode` impls
cyrgani Oct 8, 2025
7225454
Implement fs api set_times and set_times_nofollow
chenyukang Oct 8, 2025
3d40fa6
Update library/std/src/fs.rs
chenyukang Oct 9, 2025
6308e76
Update library/std/src/fs.rs
chenyukang Oct 9, 2025
1c5c8ca
use proper unsupported
chenyukang Oct 9, 2025
46c6f0a
rebase #147504
chenyukang Oct 9, 2025
2438df7
support fs::set_times for wasi
chenyukang Oct 9, 2025
1dd5641
add doc alias for set_times_nofollow
chenyukang Oct 9, 2025
6fd1c2b
add doc alias for set_times
chenyukang Oct 9, 2025
901366a
fix c_char error in Android
chenyukang Oct 9, 2025
f8118d8
unsupported: Use `unsupported()` for `set_times`
joshtriplett Oct 9, 2025
d2f590a
unsupported: Use `unsupported()` for `set_times_nofollow`
joshtriplett Oct 9, 2025
6a12470
rename `DecodeMut` to `Decode`
cyrgani Oct 10, 2025
04da682
vexos: implement `pal::os::exit`
tropicaaal Oct 11, 2025
cc96d3f
Adjust the Arm targets in CI to reflect latest changes
thejpster Oct 11, 2025
64c023b
Add miscompiled test cases
dianqk Oct 12, 2025
9b91c3e
Add CFLAGS for armv8r-none-eabihf
thejpster Oct 12, 2025
fef16d2
Change armv7a-none-eabihf CFLAGS to assume only single-precision FPU
thejpster Oct 12, 2025
4f1b945
Avoid redundant UB check in RangeFrom slice indexing
saethlin Oct 12, 2025
11977b2
Hide vendoring and copyright in GHA group
Noratrieb Oct 13, 2025
d51f09e
Review comments. Move resolve_vars_if_possible into fn and add test m…
jackh726 Oct 12, 2025
2048b9c
GVN: Invalidate derefs at loop headers
dianqk Oct 12, 2025
87ac767
Rollup merge of #146277 - Kivooeo:u64-unlock, r=scottmcm
Zalathar Oct 14, 2025
d57f99d
Rollup merge of #146976 - npmccallum:clone, r=scottmcm
Zalathar Oct 14, 2025
bb460c2
Rollup merge of #147249 - jackh726:opaque-type-fallback, r=lcnr
Zalathar Oct 14, 2025
85ef06c
Rollup merge of #147266 - lcnr:fix-search_graph, r=BoxyUwU
Zalathar Oct 14, 2025
0adccf9
Rollup merge of #147468 - chenyukang:yukang-api-set-times, r=joshtrip…
Zalathar Oct 14, 2025
ebbb223
Rollup merge of #147497 - cyrgani:proc-macro-cleanups-3, r=petrochenkov
Zalathar Oct 14, 2025
e5d4915
Rollup merge of #147594 - vexide:vexos-exit-impl, r=joboet
Zalathar Oct 14, 2025
cccca62
Rollup merge of #147596 - thejpster:build-new-arm-tier2-targets, r=Ma…
Zalathar Oct 14, 2025
23c518d
Rollup merge of #147607 - dianqk:gvn-deref-loop, r=cjgillot
Zalathar Oct 14, 2025
d92dead
Rollup merge of #147620 - saethlin:RangeFrom-noubcheck, r=scottmcm
Zalathar Oct 14, 2025
304b76d
Rollup merge of #147647 - Noratrieb:bootstrap-groups, r=Zalathar
Zalathar Oct 14, 2025
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
Prev Previous commit
Next Next commit
Implement fs api set_times and set_times_nofollow
  • Loading branch information
chenyukang committed Oct 9, 2025
commit 722545427f46d670228c345a03ab9c0c9cf74241
74 changes: 74 additions & 0 deletions library/std/src/fs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -387,6 +387,80 @@ pub fn write<P: AsRef<Path>, C: AsRef<[u8]>>(path: P, contents: C) -> io::Result
inner(path.as_ref(), contents.as_ref())
}

/// Changes the timestamps of the file or directory at the specified path.
///
/// This function will attempt to set the access and modification times
/// to the times specified. If the path refers to a symbolic link, this function
/// will follow the link and change the timestamps of the target file.
///
/// # Platform-specific behavior
///
/// This function currently corresponds to the `utimensat` function on Unix platforms
/// and the `SetFileTime` function on Windows.
///
/// # Errors
///
/// This function will return an error if the user lacks permission to change timestamps on the
/// target file or symlink. It may also return an error if the OS does not support it.
///
/// # Examples
///
/// ```no_run
/// #![feature(fs_set_times)]
/// use std::fs::{self, FileTimes};
/// use std::time::SystemTime;
///
/// fn main() -> std::io::Result<()> {
/// let now = SystemTime::now();
/// let times = FileTimes::new()
/// .set_accessed(now)
/// .set_modified(now);
/// fs::set_times("foo.txt", times)?;
/// Ok(())
/// }
/// ```
#[unstable(feature = "fs_set_times", issue = "147455")]
pub fn set_times<P: AsRef<Path>>(path: P, times: FileTimes) -> io::Result<()> {
fs_imp::set_times(path.as_ref(), times.0)
}

/// Changes the timestamps of the file or symlink at the specified path.
///
/// This function will attempt to set the access and modification times
/// to the times specified. Differ from `set_times`, if the path refers to a symbolic link,
/// this function will change the timestamps of the symlink itself, not the target file.
///
/// # Platform-specific behavior
///
/// This function currently corresponds to the `utimensat` function with `AT_SYMLINK_NOFOLLOW`
/// on Unix platforms and the `SetFileTime` function on Windows after opening the symlink.
///
/// # Errors
///
/// This function will return an error if the user lacks permission to change timestamps on the
/// target file or symlink. It may also return an error if the OS does not support it.
///
/// # Examples
///
/// ```no_run
/// #![feature(fs_set_times)]
/// use std::fs::{self, FileTimes};
/// use std::time::SystemTime;
///
/// fn main() -> std::io::Result<()> {
/// let now = SystemTime::now();
/// let times = FileTimes::new()
/// .set_accessed(now)
/// .set_modified(now);
/// fs::set_times_nofollow("symlink.txt", times)?;
/// Ok(())
/// }
/// ```
#[unstable(feature = "fs_set_times", issue = "147455")]
pub fn set_times_nofollow<P: AsRef<Path>>(path: P, times: FileTimes) -> io::Result<()> {
fs_imp::set_times_nofollow(path.as_ref(), times.0)
}

#[stable(feature = "file_lock", since = "1.89.0")]
impl error::Error for TryLockError {}

Expand Down
219 changes: 219 additions & 0 deletions library/std/src/fs/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2226,3 +2226,222 @@ fn test_open_options_invalid_combinations() {
assert_eq!(err.kind(), ErrorKind::InvalidInput);
assert_eq!(err.to_string(), "must specify at least one of read, write, or append access");
}

#[test]
fn test_fs_set_times() {
#[cfg(target_vendor = "apple")]
use crate::os::darwin::fs::FileTimesExt;
#[cfg(windows)]
use crate::os::windows::fs::FileTimesExt;

let tmp = tmpdir();
let path = tmp.join("foo");
File::create(&path).unwrap();

let mut times = FileTimes::new();
let accessed = SystemTime::UNIX_EPOCH + Duration::from_secs(12345);
let modified = SystemTime::UNIX_EPOCH + Duration::from_secs(54321);
times = times.set_accessed(accessed).set_modified(modified);

#[cfg(any(windows, target_vendor = "apple"))]
let created = SystemTime::UNIX_EPOCH + Duration::from_secs(32123);
#[cfg(any(windows, target_vendor = "apple"))]
{
times = times.set_created(created);
}

match fs::set_times(&path, times) {
// Allow unsupported errors on platforms which don't support setting times.
#[cfg(not(any(
windows,
all(
unix,
not(any(
target_os = "android",
target_os = "redox",
target_os = "espidf",
target_os = "horizon"
))
)
)))]
Err(e) if e.kind() == ErrorKind::Unsupported => return,
Err(e) => panic!("error setting file times: {e:?}"),
Ok(_) => {}
}

let metadata = fs::metadata(&path).unwrap();
assert_eq!(metadata.accessed().unwrap(), accessed);
assert_eq!(metadata.modified().unwrap(), modified);
#[cfg(any(windows, target_vendor = "apple"))]
{
assert_eq!(metadata.created().unwrap(), created);
}
}

#[test]
fn test_fs_set_times_follows_symlink() {
#[cfg(target_vendor = "apple")]
use crate::os::darwin::fs::FileTimesExt;
#[cfg(windows)]
use crate::os::windows::fs::FileTimesExt;

let tmp = tmpdir();

// Create a target file
let target = tmp.join("target");
File::create(&target).unwrap();

// Create a symlink to the target
#[cfg(unix)]
let link = tmp.join("link");
#[cfg(unix)]
crate::os::unix::fs::symlink(&target, &link).unwrap();

#[cfg(windows)]
let link = tmp.join("link.txt");
#[cfg(windows)]
crate::os::windows::fs::symlink_file(&target, &link).unwrap();

// Get the symlink's own modified time BEFORE calling set_times (to compare later)
// We don't check accessed time because reading metadata may update atime on some platforms.
let link_metadata_before = fs::symlink_metadata(&link).unwrap();
let link_modified_before = link_metadata_before.modified().unwrap();

let mut times = FileTimes::new();
let accessed = SystemTime::UNIX_EPOCH + Duration::from_secs(12345);
let modified = SystemTime::UNIX_EPOCH + Duration::from_secs(54321);
times = times.set_accessed(accessed).set_modified(modified);

#[cfg(any(windows, target_vendor = "apple"))]
let created = SystemTime::UNIX_EPOCH + Duration::from_secs(32123);
#[cfg(any(windows, target_vendor = "apple"))]
{
times = times.set_created(created);
}

// Call fs::set_times on the symlink - it should follow the link and modify the target
match fs::set_times(&link, times) {
// Allow unsupported errors on platforms which don't support setting times.
#[cfg(not(any(
windows,
all(
unix,
not(any(
target_os = "android",
target_os = "redox",
target_os = "espidf",
target_os = "horizon"
))
)
)))]
Err(e) if e.kind() == ErrorKind::Unsupported => return,
Err(e) => panic!("error setting file times through symlink: {e:?}"),
Ok(_) => {}
}

// Verify that the TARGET file's times were changed (following the symlink)
let target_metadata = fs::metadata(&target).unwrap();
assert_eq!(
target_metadata.accessed().unwrap(),
accessed,
"target file accessed time should match"
);
assert_eq!(
target_metadata.modified().unwrap(),
modified,
"target file modified time should match"
);
#[cfg(any(windows, target_vendor = "apple"))]
{
assert_eq!(
target_metadata.created().unwrap(),
created,
"target file created time should match"
);
}

// Also verify through the symlink (fs::metadata follows symlinks)
let link_followed_metadata = fs::metadata(&link).unwrap();
assert_eq!(link_followed_metadata.accessed().unwrap(), accessed);
assert_eq!(link_followed_metadata.modified().unwrap(), modified);

// Verify that the SYMLINK ITSELF was NOT modified
// Note: We only check modified time, not accessed time, because reading the symlink
// metadata may update its atime on some platforms (e.g., Linux).
let link_metadata_after = fs::symlink_metadata(&link).unwrap();
assert_eq!(
link_metadata_after.modified().unwrap(),
link_modified_before,
"symlink's own modified time should not change"
);
}

#[test]
fn test_fs_set_times_nofollow() {
#[cfg(target_vendor = "apple")]
use crate::os::darwin::fs::FileTimesExt;
#[cfg(windows)]
use crate::os::windows::fs::FileTimesExt;

let tmp = tmpdir();

// Create a target file and a symlink to it
let target = tmp.join("target");
File::create(&target).unwrap();

#[cfg(unix)]
let link = tmp.join("link");
#[cfg(unix)]
crate::os::unix::fs::symlink(&target, &link).unwrap();

#[cfg(windows)]
let link = tmp.join("link.txt");
#[cfg(windows)]
crate::os::windows::fs::symlink_file(&target, &link).unwrap();

let mut times = FileTimes::new();
let accessed = SystemTime::UNIX_EPOCH + Duration::from_secs(11111);
let modified = SystemTime::UNIX_EPOCH + Duration::from_secs(22222);
times = times.set_accessed(accessed).set_modified(modified);

#[cfg(any(windows, target_vendor = "apple"))]
let created = SystemTime::UNIX_EPOCH + Duration::from_secs(33333);
#[cfg(any(windows, target_vendor = "apple"))]
{
times = times.set_created(created);
}

// Set times on the symlink itself (not following it)
match fs::set_times_nofollow(&link, times) {
// Allow unsupported errors on platforms which don't support setting times.
#[cfg(not(any(
windows,
all(
unix,
not(any(
target_os = "android",
target_os = "redox",
target_os = "espidf",
target_os = "horizon"
))
)
)))]
Err(e) if e.kind() == ErrorKind::Unsupported => return,
Err(e) => panic!("error setting symlink times: {e:?}"),
Ok(_) => {}
}

// Read symlink metadata (without following)
let metadata = fs::symlink_metadata(&link).unwrap();
assert_eq!(metadata.accessed().unwrap(), accessed);
assert_eq!(metadata.modified().unwrap(), modified);
#[cfg(any(windows, target_vendor = "apple"))]
{
assert_eq!(metadata.created().unwrap(), created);
}

// Verify that the target file's times were NOT changed
let target_metadata = fs::metadata(&target).unwrap();
assert_ne!(target_metadata.accessed().unwrap(), accessed);
assert_ne!(target_metadata.modified().unwrap(), modified);
}
8 changes: 8 additions & 0 deletions library/std/src/sys/fs/hermit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -566,6 +566,14 @@ pub fn set_perm(_p: &Path, _perm: FilePermissions) -> io::Result<()> {
Err(Error::from_raw_os_error(22))
}

pub fn set_times(_p: &Path, _times: FileTimes) -> io::Result<()> {
Err(Error::from_raw_os_error(22))
}

pub fn set_times_nofollow(_p: &Path, _times: FileTimes) -> io::Result<()> {
Err(Error::from_raw_os_error(22))
}

pub fn rmdir(path: &Path) -> io::Result<()> {
run_path_with_cstr(path, &|path| cvt(unsafe { hermit_abi::rmdir(path.as_ptr()) }).map(|_| ()))
}
Expand Down
8 changes: 8 additions & 0 deletions library/std/src/sys/fs/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -161,3 +161,11 @@ pub fn exists(path: &Path) -> io::Result<bool> {
#[cfg(windows)]
with_native_path(path, &imp::exists)
}

pub fn set_times(path: &Path, times: FileTimes) -> io::Result<()> {
with_native_path(path, &|path| imp::set_times(path, times.clone()))
}

pub fn set_times_nofollow(path: &Path, times: FileTimes) -> io::Result<()> {
with_native_path(path, &|path| imp::set_times_nofollow(path, times.clone()))
}
11 changes: 11 additions & 0 deletions library/std/src/sys/fs/solid.rs
Original file line number Diff line number Diff line change
Expand Up @@ -538,6 +538,17 @@ pub fn set_perm(p: &Path, perm: FilePermissions) -> io::Result<()> {
Ok(())
}

pub fn set_times(_p: &Path, _times: FileTimes) -> io::Result<()> {
Err(io::const_error!(io::ErrorKind::Unsupported, "setting file times not supported",))
}

pub fn set_times_nofollow(_p: &Path, _times: FileTimes) -> io::Result<()> {
Err(io::const_error!(
io::ErrorKind::Unsupported,
"setting file times on symlinks not supported",
))
}

pub fn rmdir(p: &Path) -> io::Result<()> {
if stat(p)?.file_type().is_dir() {
error::SolidError::err_if_negative(unsafe { abi::SOLID_FS_Unlink(cstr(p)?.as_ptr()) })
Expand Down
8 changes: 8 additions & 0 deletions library/std/src/sys/fs/uefi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -333,6 +333,14 @@ pub fn set_perm(_p: &Path, _perm: FilePermissions) -> io::Result<()> {
unsupported()
}

pub fn set_times(_p: &Path, _times: FileTimes) -> io::Result<()> {
unsupported()
}

pub fn set_times_nofollow(_p: &Path, _times: FileTimes) -> io::Result<()> {
unsupported()
}

pub fn rmdir(_p: &Path) -> io::Result<()> {
unsupported()
}
Expand Down
Loading