use crate::{
encryption::EncryptionProvider,
file::fsync_directory,
fs::{Fs, SyncMode},
manifest_blocks::writer::ManifestArchiveWriter,
runtime_config::RuntimeConfig,
version::Version,
};
use alloc::sync::Arc;
use crate::path::Path;
pub const SNAPSHOT_TEMPORARY_SUFFIX: &str = ".tmp";
pub fn persist_version(
folder: &Path,
version: &Version,
comparator_name: &str,
fs: &dyn Fs,
runtime: Arc<RuntimeConfig>,
encryption: Option<Arc<dyn EncryptionProvider>>,
sync_mode: SyncMode,
) -> crate::Result<()> {
if comparator_name.len() > crate::comparator::MAX_COMPARATOR_NAME_BYTES {
return Err(crate::Error::from(crate::io::Error::new(
crate::io::ErrorKind::InvalidInput,
format!(
"comparator name is {} bytes (max {})",
comparator_name.len(),
crate::comparator::MAX_COMPARATOR_NAME_BYTES,
),
)));
}
log::trace!(
"Persisting version {} in {}",
version.id(),
folder.display(),
);
let path = folder.join(format!("v{}", version.id()));
let temporary = folder.join(format!("v{}{SNAPSHOT_TEMPORARY_SUFFIX}", version.id()));
let log = super::edit_log::log_path(folder, version.id());
if super::edit_log::log_size(fs, &log)? > 0 {
return Err(crate::Error::from(crate::io::Error::new(
crate::io::ErrorKind::AlreadyExists,
format!(
"manifest snapshot {} is a live generation; reopen the tree",
path.display()
),
)));
}
remove_leftover(fs, &temporary)?;
let mut writer = ManifestArchiveWriter::create(&temporary, fs, runtime, encryption, sync_mode)?;
version.encode_into(&mut writer, comparator_name)?;
writer.finish()?;
super::edit_log::create_log(fs, folder, version.id(), sync_mode)?;
fs.rename(&temporary, &path)?;
if let Err(e) = fsync_directory(folder, fs, sync_mode) {
return Err(if withdraw(folder, &path, fs, sync_mode) {
e.into()
} else {
crate::Error::InstallOutcomeUnknown(alloc::boxed::Box::new(e.into()))
});
}
Ok(())
}
fn withdraw(folder: &Path, snapshot: &Path, fs: &dyn Fs, sync_mode: SyncMode) -> bool {
if let Err(e) = fs.remove_file(snapshot) {
log::warn!(
"persist: could not withdraw {} after a failed install: {e}",
snapshot.display()
);
return false;
}
if let Err(e) = fsync_directory(folder, fs, sync_mode) {
log::warn!(
"persist: the withdrawal of {} is not durable: {e}",
snapshot.display()
);
return false;
}
true
}
fn remove_leftover(fs: &dyn Fs, path: &Path) -> crate::Result<()> {
match fs.remove_file(path) {
Ok(()) => {
log::warn!(
"removed {}, left by an earlier failed manifest install",
path.display()
);
Ok(())
}
Err(e) if e.kind() == crate::io::ErrorKind::NotFound => Ok(()),
Err(e) => Err(e.into()),
}
}
#[cfg(test)]
mod tests;