filetime/unix/
utimes.rs

1use crate::FileTime;
2use std::ffi::CString;
3use std::fs;
4use std::io;
5use std::os::unix::prelude::*;
6use std::path::Path;
7
8#[allow(dead_code)]
9pub fn set_file_times(p: &Path, atime: FileTime, mtime: FileTime) -> io::Result<()> {
10    set_times(p, Some(atime), Some(mtime), false)
11}
12
13#[allow(dead_code)]
14pub fn set_file_mtime(p: &Path, mtime: FileTime) -> io::Result<()> {
15    set_times(p, None, Some(mtime), false)
16}
17
18#[allow(dead_code)]
19pub fn set_file_atime(p: &Path, atime: FileTime) -> io::Result<()> {
20    set_times(p, Some(atime), None, false)
21}
22
23#[cfg(not(target_env = "uclibc"))]
24#[allow(dead_code)]
25pub fn set_file_handle_times(
26    f: &fs::File,
27    atime: Option<FileTime>,
28    mtime: Option<FileTime>,
29) -> io::Result<()> {
30    let (atime, mtime) = match get_times(atime, mtime, || f.metadata())? {
31        Some(pair) => pair,
32        None => return Ok(()),
33    };
34    let times = [to_timeval(&atime), to_timeval(&mtime)];
35    let rc = unsafe { libc::futimes(f.as_raw_fd(), times.as_ptr()) };
36    return if rc == 0 {
37        Ok(())
38    } else {
39        Err(io::Error::last_os_error())
40    };
41}
42
43#[cfg(target_env = "uclibc")]
44#[allow(dead_code)]
45pub fn set_file_handle_times(
46    f: &fs::File,
47    atime: Option<FileTime>,
48    mtime: Option<FileTime>,
49) -> io::Result<()> {
50    let (atime, mtime) = match get_times(atime, mtime, || f.metadata())? {
51        Some(pair) => pair,
52        None => return Ok(()),
53    };
54    let times = [to_timespec(&atime), to_timespec(&mtime)];
55    let rc = unsafe { libc::futimens(f.as_raw_fd(), times.as_ptr()) };
56    return if rc == 0 {
57        Ok(())
58    } else {
59        Err(io::Error::last_os_error())
60    };
61}
62
63fn get_times(
64    atime: Option<FileTime>,
65    mtime: Option<FileTime>,
66    current: impl FnOnce() -> io::Result<fs::Metadata>,
67) -> io::Result<Option<(FileTime, FileTime)>> {
68    let pair = match (atime, mtime) {
69        (Some(a), Some(b)) => (a, b),
70        (None, None) => return Ok(None),
71        (Some(a), None) => {
72            let meta = current()?;
73            (a, FileTime::from_last_modification_time(&meta))
74        }
75        (None, Some(b)) => {
76            let meta = current()?;
77            (FileTime::from_last_access_time(&meta), b)
78        }
79    };
80    Ok(Some(pair))
81}
82
83#[allow(dead_code)]
84pub fn set_symlink_file_times(p: &Path, atime: FileTime, mtime: FileTime) -> io::Result<()> {
85    set_times(p, Some(atime), Some(mtime), true)
86}
87
88pub fn set_times(
89    p: &Path,
90    atime: Option<FileTime>,
91    mtime: Option<FileTime>,
92    symlink: bool,
93) -> io::Result<()> {
94    let (atime, mtime) = match get_times(atime, mtime, || p.metadata())? {
95        Some(pair) => pair,
96        None => return Ok(()),
97    };
98    let p = CString::new(p.as_os_str().as_bytes())?;
99    let times = [to_timeval(&atime), to_timeval(&mtime)];
100    let rc = unsafe {
101        if symlink {
102            libc::lutimes(p.as_ptr(), times.as_ptr())
103        } else {
104            libc::utimes(p.as_ptr(), times.as_ptr())
105        }
106    };
107    return if rc == 0 {
108        Ok(())
109    } else {
110        Err(io::Error::last_os_error())
111    };
112}
113
114fn to_timeval(ft: &FileTime) -> libc::timeval {
115    libc::timeval {
116        tv_sec: ft.seconds() as libc::time_t,
117        tv_usec: (ft.nanoseconds() / 1000) as libc::suseconds_t,
118    }
119}
120
121fn to_timespec(ft: &FileTime) -> libc::timespec {
122    libc::timespec {
123        tv_sec: ft.seconds() as libc::time_t,
124        #[cfg(all(target_arch = "x86_64", target_pointer_width = "32"))]
125        tv_nsec: (ft.nanoseconds()) as i64,
126        #[cfg(not(all(target_arch = "x86_64", target_pointer_width = "32")))]
127        tv_nsec: (ft.nanoseconds()) as libc::c_long,
128    }
129}