use crate::store::stats::ParentDirSyncEvidence;
use crate::store::{StoreError, SyncMode};
use std::fs::File;
use std::path::Path;
pub(crate) fn persist_temp_with_parent_sync(
named_temp: tempfile::NamedTempFile,
final_path: &Path,
_admission: ParentDirSyncAdmission,
) -> std::io::Result<()> {
{
let handle = named_temp.as_file();
handle.sync_all()?;
}
named_temp
.persist(final_path)
.map_err(|error| error.error)?;
sync_parent_dir_io(final_path)?;
Ok(())
}
pub(crate) fn sync_parent_dir(path: &Path) -> Result<(), StoreError> {
let evidence = crate::store::platform::evidence::parent_dir_sync_evidence();
let _admission = admit_parent_dir_sync(evidence)?;
sync_parent_dir_io(path).map_err(StoreError::Io)
}
fn sync_parent_dir_io(path: &Path) -> std::io::Result<()> {
#[cfg(unix)]
{
if let Some(parent) = path.parent() {
let dir = File::open(parent)?;
dir.sync_all()?;
}
}
#[cfg(not(unix))]
{
let _path_is_intentionally_unused_on_non_unix = path;
}
Ok(())
}
pub(crate) fn sync_file_with_mode(file: &File, mode: &SyncMode) -> Result<(), StoreError> {
match mode {
SyncMode::SyncAll => file.sync_all().map_err(StoreError::Io),
SyncMode::SyncData => file.sync_data().map_err(StoreError::Io),
}
}
pub(crate) fn sync_file_all_io(file: &File) -> std::io::Result<()> {
file.sync_all()
}
#[derive(Clone, Copy, Debug)]
pub(crate) struct ParentDirSyncAdmission {
_private: (),
}
pub(crate) fn admit_parent_dir_sync(
evidence: ParentDirSyncEvidence,
) -> Result<ParentDirSyncAdmission, StoreError> {
match evidence {
ParentDirSyncEvidence::UnixFsync | ParentDirSyncEvidence::RenameOnly => {}
ParentDirSyncEvidence::Unknown | ParentDirSyncEvidence::ProbeFailed => {
return Err(StoreError::PlatformAdmissionFailed {
capability: "parent directory sync",
reason: format!("parent directory sync evidence {evidence:?} is not admissible"),
});
}
};
Ok(ParentDirSyncAdmission { _private: () })
}
pub(crate) fn admit_current_parent_dir_sync() -> Result<ParentDirSyncAdmission, StoreError> {
admit_parent_dir_sync(crate::store::platform::evidence::parent_dir_sync_evidence())
}
#[cfg(all(test, unix))]
mod tests {
use super::*;
use std::error::Error;
#[test]
fn sync_file_with_mode_surfaces_platform_sync_errors() -> Result<(), Box<dyn Error>> {
let file = File::open("/dev/null")?;
assert!(
matches!(
sync_file_with_mode(&file, &SyncMode::SyncAll),
Err(StoreError::Io(_))
),
"PROPERTY: sync_file_with_mode must map platform sync errors to StoreError::Io, not report success"
);
Ok(())
}
}
#[cfg(test)]
mod admission_tests {
use super::*;
#[test]
fn parent_dir_sync_admission_accepts_only_reported_sync_modes() {
for evidence in [
ParentDirSyncEvidence::UnixFsync,
ParentDirSyncEvidence::RenameOnly,
] {
assert!(
admit_parent_dir_sync(evidence).is_ok(),
"PROPERTY: parent-dir sync evidence {evidence:?} must be admissible"
);
}
for evidence in [
ParentDirSyncEvidence::Unknown,
ParentDirSyncEvidence::ProbeFailed,
] {
assert!(
matches!(
admit_parent_dir_sync(evidence),
Err(StoreError::PlatformAdmissionFailed {
capability: "parent directory sync",
..
})
),
"PROPERTY: parent-dir sync evidence {evidence:?} must reject with a parent-dir admission failure"
);
}
}
}