use std::fs;
use std::path::{Path, PathBuf};
use std::sync::{Arc, Mutex};
use rpki::uri;
use serde::{Deserialize, Serialize};
use crate::commons::file;
use crate::commons::KrillResult;
use crate::commons::error::{Error, KrillIoError};
use crate::constants::REPOSITORY_RSYNC_DIR;
use super::rrdp::SnapshotData;
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct RsyncdStore {
base_uri: uri::Rsync,
rsync_dir: PathBuf,
#[serde(
skip_serializing,
skip_deserializing,
default = "Default::default"
)]
lock: Arc<Mutex<()>>,
}
impl RsyncdStore {
pub fn new(base_uri: uri::Rsync, repo_dir: &Path) -> Self {
let mut rsync_dir = repo_dir.to_path_buf();
rsync_dir.push(REPOSITORY_RSYNC_DIR);
RsyncdStore {
base_uri,
rsync_dir,
lock: Default::default(),
}
}
pub fn base_uri(&self) -> &uri::Rsync {
&self.base_uri
}
pub fn write(
&self,
serial: u64,
snapshot: &SnapshotData,
) -> KrillResult<()> {
let _lock = self.lock.lock().map_err(|_| {
Error::custom("Could not get write lock for rsync repo")
})?;
let mut new_dir = self.rsync_dir.clone();
new_dir.push(format!("tmp-{serial}"));
fs::create_dir_all(&new_dir).map_err(|e| {
KrillIoError::new(
format!(
"Could not create dir(s) '{}' for publishing rsync",
new_dir.display()
),
e,
)
})?;
for current in snapshot.publishers_current_objects().values() {
for (uri_key, base64) in current.iter() {
let uri = uri::Rsync::try_from(uri_key)?;
let rel = uri.relative_to(&self.base_uri).ok_or_else(|| {
Error::publishing_outside_jail(&uri, &self.base_uri)
})?;
let mut path = new_dir.clone();
path.push(rel);
file::save(&base64.to_bytes(), &path)?;
}
}
let mut current_dir = self.rsync_dir.clone();
current_dir.push("current");
let mut old_dir = self.rsync_dir.clone();
old_dir.push("old");
if current_dir.exists() {
fs::rename(¤t_dir, &old_dir).map_err(|e| {
KrillIoError::new(
format!(
"Could not rename current rsync dir from \
'{}' to '{}' while publishing",
current_dir.display(),
old_dir.display()
),
e,
)
})?;
}
fs::rename(&new_dir, ¤t_dir).map_err(|e| {
KrillIoError::new(
format!(
"Could not rename new rsync dir from \
'{}' to '{}' while publishing",
new_dir.display(),
current_dir.display()
),
e,
)
})?;
if old_dir.exists() {
fs::remove_dir_all(&old_dir).map_err(|e| {
KrillIoError::new(
format!(
"Could not remove up old rsync dir '{}' \
while publishing",
old_dir.display()
),
e,
)
})?;
}
Ok(())
}
pub fn clear(&self) {
let _ = fs::remove_dir_all(&self.rsync_dir);
}
}