extern crate libc;
#[cfg(unix)] use std::os::unix::prelude::*;
#[cfg(windows)] use std::os::windows::prelude::*;
use std::fmt;
use std::fs;
use std::io;
use std::path::Path;
#[derive(Eq, PartialEq, Ord, PartialOrd, Debug, Copy, Clone, Hash)]
pub struct FileTime {
seconds: u64,
nanos: u32,
}
impl FileTime {
pub fn zero() -> FileTime {
FileTime { seconds: 0, nanos: 0 }
}
pub fn from_seconds_since_1970(seconds: u64, nanos: u32) -> FileTime {
FileTime {
seconds: seconds + if cfg!(windows) {11644473600} else {0},
nanos: nanos,
}
}
pub fn from_last_modification_time(meta: &fs::Metadata) -> FileTime {
#[cfg(unix)]
fn imp(meta: &fs::Metadata) -> FileTime {
FileTime::from_os_repr(meta.mtime() as u64, meta.mtime_nsec() as u32)
}
#[cfg(windows)]
fn imp(meta: &fs::Metadata) -> FileTime {
FileTime::from_os_repr(meta.last_write_time())
}
imp(meta)
}
pub fn from_last_access_time(meta: &fs::Metadata) -> FileTime {
#[cfg(unix)]
fn imp(meta: &fs::Metadata) -> FileTime {
FileTime::from_os_repr(meta.atime() as u64, meta.atime_nsec() as u32)
}
#[cfg(windows)]
fn imp(meta: &fs::Metadata) -> FileTime {
FileTime::from_os_repr(meta.last_access_time())
}
imp(meta)
}
pub fn from_creation_time(meta: &fs::Metadata) -> Option<FileTime> {
macro_rules! birthtim {
($(($e:expr, $i:ident)),*) => {
#[cfg(any($(target_os = $e),*))]
fn imp(meta: &fs::Metadata) -> Option<FileTime> {
$(
#[cfg(target_os = $e)]
use std::os::$i::fs::MetadataExt;
)*
let raw = meta.as_raw_stat();
Some(FileTime::from_os_repr(raw.st_birthtime as u64,
raw.st_birthtime_nsec as u32))
}
#[cfg(all(not(windows),
$(not(target_os = $e)),*))]
fn imp(_meta: &fs::Metadata) -> Option<FileTime> {
None
}
}
}
birthtim! {
("bitrig", bitrig),
("freebsd", freebsd),
("ios", ios),
("macos", macos),
("openbsd", openbsd)
}
#[cfg(windows)]
fn imp(meta: &fs::Metadata) -> Option<FileTime> {
Some(FileTime::from_os_repr(meta.last_access_time()))
}
imp(meta)
}
#[cfg(windows)]
fn from_os_repr(time: u64) -> FileTime {
FileTime {
seconds: time / (1_000_000_000 / 100),
nanos: ((time % (1_000_000_000 / 100)) * 100) as u32,
}
}
#[cfg(unix)]
fn from_os_repr(seconds: u64, nanos: u32) -> FileTime {
FileTime { seconds: seconds, nanos: nanos }
}
pub fn seconds(&self) -> u64 { self.seconds }
pub fn seconds_relative_to_1970(&self) -> u64 {
self.seconds - if cfg!(windows) {11644473600} else {0}
}
pub fn nanoseconds(&self) -> u32 { self.nanos }
}
impl fmt::Display for FileTime {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}.{:09}s", self.seconds, self.nanos)
}
}
pub fn set_file_times<P>(p: P, atime: FileTime, mtime: FileTime)
-> io::Result<()> where P: AsRef<Path> {
set_file_times_(p.as_ref(), atime, mtime)
}
#[cfg(unix)]
fn set_file_times_(p: &Path, atime: FileTime, mtime: FileTime) -> io::Result<()> {
use std::ffi::CString;
use libc::{timeval, time_t, suseconds_t, utimes};
let times = [to_timeval(&atime), to_timeval(&mtime)];
let p = try!(CString::new(p.as_os_str().as_bytes()));
return unsafe {
if utimes(p.as_ptr() as *const _, times.as_ptr()) == 0 {
Ok(())
} else {
Err(io::Error::last_os_error())
}
};
fn to_timeval(ft: &FileTime) -> timeval {
timeval {
tv_sec: ft.seconds() as time_t,
tv_usec: (ft.nanoseconds() / 1000) as suseconds_t,
}
}
}
#[cfg(windows)]
#[allow(bad_style)]
fn set_file_times_(p: &Path, atime: FileTime, mtime: FileTime) -> io::Result<()> {
use std::fs::OpenOptions;
type BOOL = i32;
type HANDLE = *mut u8;
type DWORD = u32;
#[repr(C)]
struct FILETIME {
dwLowDateTime: u32,
dwHighDateTime: u32,
}
extern "system" {
fn SetFileTime(hFile: HANDLE,
lpCreationTime: *const FILETIME,
lpLastAccessTime: *const FILETIME,
lpLastWriteTime: *const FILETIME) -> BOOL;
}
let f = try!(OpenOptions::new().write(true).open(p));
let atime = to_filetime(&atime);
let mtime = to_filetime(&mtime);
return unsafe {
let ret = SetFileTime(f.as_raw_handle() as *mut _,
0 as *const _,
&atime, &mtime);
if ret != 0 {
Ok(())
} else {
Err(io::Error::last_os_error())
}
};
fn to_filetime(ft: &FileTime) -> FILETIME {
let intervals = ft.seconds() * (1_000_000_000 / 100) +
((ft.nanoseconds() as u64) / 100);
FILETIME {
dwLowDateTime: intervals as DWORD,
dwHighDateTime: (intervals >> 32) as DWORD,
}
}
}
#[cfg(test)]
mod tests {
extern crate tempdir;
use std::fs::{self, File};
use self::tempdir::TempDir;
use super::{FileTime, set_file_times};
#[test]
fn set_file_times_test() {
let td = TempDir::new("filetime").unwrap();
let path = td.path().join("foo.txt");
File::create(&path).unwrap();
let metadata = fs::metadata(&path).unwrap();
let mtime = FileTime::from_last_modification_time(&metadata);
let atime = FileTime::from_last_access_time(&metadata);
set_file_times(&path, atime, mtime).unwrap();
let new_mtime = FileTime::from_seconds_since_1970(10_000, 0);
set_file_times(&path, atime, new_mtime).unwrap();
let metadata = fs::metadata(&path).unwrap();
let mtime = FileTime::from_last_modification_time(&metadata);
assert_eq!(mtime, new_mtime);
}
}