use std::fs::File;
use std::io::Read;
use std::path::Path;
use crate::{Error, READ_BUFFER_SIZE, Result};
use super::PreserveMetadata;
pub(crate) fn apply_metadata(
path: &Path,
options: &PreserveMetadata,
modification_time: Option<u64>,
creation_time: Option<u64>,
attributes: Option<u32>,
) {
use filetime::FileTime;
const FILETIME_UNIX_DIFF: u64 = 116444736000000000;
let filetime_to_unix = |ft: u64| -> Option<FileTime> {
if ft >= FILETIME_UNIX_DIFF {
let unix_100ns = ft - FILETIME_UNIX_DIFF;
let secs = unix_100ns / 10_000_000;
let nanos = ((unix_100ns % 10_000_000) * 100) as u32;
Some(FileTime::from_unix_time(secs as i64, nanos))
} else {
None
}
};
if options.modification_time {
if let Some(mtime) = modification_time.and_then(filetime_to_unix) {
if let Err(e) = filetime::set_file_mtime(path, mtime) {
log::warn!(
"Failed to set modification time on '{}': {}",
path.display(),
e
);
}
}
}
#[cfg(any(windows, target_os = "macos"))]
if options.creation_time {
if let Some(ctime) = creation_time.and_then(filetime_to_unix) {
log::debug!(
"Creation time preservation requested but not yet implemented for '{}': {:?}",
path.display(),
ctime
);
}
}
#[cfg(not(any(windows, target_os = "macos")))]
let _ = creation_time;
if options.attributes {
if let Some(attrs) = attributes {
apply_file_attributes(path, attrs);
}
}
}
#[cfg(unix)]
pub(crate) fn apply_file_attributes(path: &Path, attrs: u32) {
use crate::ownership::decode_unix_mode;
use std::os::unix::fs::PermissionsExt;
if let Some(mode) = decode_unix_mode(attrs) {
if let Err(e) = std::fs::set_permissions(path, std::fs::Permissions::from_mode(mode)) {
log::warn!("Failed to set permissions on '{}': {}", path.display(), e);
}
}
}
#[cfg(windows)]
pub(crate) fn apply_file_attributes(path: &Path, attrs: u32) {
let windows_attrs = attrs & 0xFFFF;
if windows_attrs & 0x01 != 0 {
if let Ok(metadata) = std::fs::metadata(path) {
let mut perms = metadata.permissions();
perms.set_readonly(true);
if let Err(e) = std::fs::set_permissions(path, perms) {
log::warn!(
"Failed to set read-only attribute on '{}': {}",
path.display(),
e
);
}
}
}
}
#[cfg(not(any(unix, windows)))]
pub(crate) fn apply_file_attributes(_path: &Path, _attrs: u32) {
}
pub(crate) fn calculate_file_crc(path: &Path) -> Result<u32> {
let mut file = File::open(path).map_err(Error::Io)?;
let mut hasher = crc32fast::Hasher::new();
let mut buf = [0u8; READ_BUFFER_SIZE];
loop {
let n = file.read(&mut buf).map_err(Error::Io)?;
if n == 0 {
break;
}
hasher.update(&buf[..n]);
}
Ok(hasher.finalize())
}