diff options
Diffstat (limited to 'src/util.rs')
-rw-r--r-- | src/util.rs | 51 |
1 files changed, 50 insertions, 1 deletions
diff --git a/src/util.rs b/src/util.rs index e1f82b3..1f69523 100644 --- a/src/util.rs +++ b/src/util.rs @@ -1,6 +1,10 @@ use crate::error::SiteError; -use log::{debug, trace}; +use libc::{timespec, utimensat, AT_FDCWD, AT_SYMLINK_NOFOLLOW}; +use log::{debug, error, trace}; +use std::ffi::CString; +use std::os::unix::ffi::OsStrExt; use std::path::{Component, Path, PathBuf}; +use std::time::{SystemTime, UNIX_EPOCH}; pub fn canonicalize(path: &Path) -> Result<PathBuf, SiteError> { path.canonicalize() @@ -16,6 +20,36 @@ pub fn mkdir(path: &Path) -> Result<(), SiteError> { pub fn copy(src: &Path, dest: &Path) -> Result<(), SiteError> { trace!("copying: {} -> {}", src.display(), dest.display()); std::fs::copy(src, dest).map_err(|e| SiteError::CopyFile(src.into(), dest.into(), e))?; + let mtime = get_mtime(src)?; + set_mtime(dest, mtime)?; + Ok(()) +} + +pub fn get_mtime(src: &Path) -> Result<SystemTime, SiteError> { + let metadata = std::fs::metadata(src).map_err(|e| SiteError::FileMetadata(src.into(), e))?; + let mtime = metadata + .modified() + .map_err(|e| SiteError::FileMtime(src.into(), e))?; + Ok(mtime) +} + +pub fn set_mtime(filename: &Path, mtime: SystemTime) -> Result<(), SiteError> { + let mtime = timespec(mtime)?; + let times = [mtime, mtime]; + let times: *const timespec = ×[0]; + + let pathbuf = filename.to_path_buf(); + let path = path_to_cstring(filename); + + // We have to use unsafe here to be able call the libc functions + // below. + unsafe { + if utimensat(AT_FDCWD, path.as_ptr(), times, AT_SYMLINK_NOFOLLOW) == -1 { + let error = std::io::Error::last_os_error(); + error!("utimensat failed on {:?}", path); + return Err(SiteError::Utimensat(pathbuf, error)); + } + } Ok(()) } @@ -76,6 +110,21 @@ pub fn make_path_absolute(path: &Path) -> PathBuf { Path::new("/").join(&path) } +fn timespec(time: SystemTime) -> Result<timespec, SiteError> { + let dur = time + .duration_since(UNIX_EPOCH) + .map_err(SiteError::UnixTime)?; + let tv_sec = dur.as_secs() as libc::time_t; + let tv_nsec = dur.subsec_nanos() as libc::c_long; + Ok(timespec { tv_sec, tv_nsec }) +} + +fn path_to_cstring(path: &Path) -> CString { + let path = path.as_os_str(); + let path = path.as_bytes(); + CString::new(path).unwrap() +} + #[cfg(test)] mod test { use super::{ |