summaryrefslogtreecommitdiff
path: root/src/util.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/util.rs')
-rw-r--r--src/util.rs51
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 = &times[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::{