make datastore BackupGroup/Dir ctors private
And use the api-types for their contents. These are supposed to be instances for a datastore, the pure specifications are the ones in pbs_api_types which should be preferred in crates like clients which do not need to deal with the datastore directly. Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
This commit is contained in:
@ -1,40 +1,21 @@
|
||||
use std::os::unix::io::RawFd;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::str::FromStr;
|
||||
|
||||
use anyhow::{bail, format_err, Error};
|
||||
use anyhow::{bail, Error};
|
||||
|
||||
use pbs_api_types::{
|
||||
BackupType, GroupFilter, BACKUP_DATE_REGEX, BACKUP_FILE_REGEX, GROUP_PATH_REGEX,
|
||||
SNAPSHOT_PATH_REGEX,
|
||||
};
|
||||
use pbs_api_types::{BackupType, GroupFilter, BACKUP_DATE_REGEX, BACKUP_FILE_REGEX};
|
||||
|
||||
use super::manifest::MANIFEST_BLOB_NAME;
|
||||
|
||||
/// BackupGroup is a directory containing a list of BackupDir
|
||||
#[derive(Debug, Eq, PartialEq, Hash, Clone)]
|
||||
pub struct BackupGroup {
|
||||
/// Type of backup
|
||||
backup_type: BackupType,
|
||||
/// Unique (for this type) ID
|
||||
backup_id: String,
|
||||
group: pbs_api_types::BackupGroup,
|
||||
}
|
||||
|
||||
impl std::cmp::Ord for BackupGroup {
|
||||
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
|
||||
let type_order = self.backup_type.cmp(&other.backup_type);
|
||||
if type_order != std::cmp::Ordering::Equal {
|
||||
return type_order;
|
||||
}
|
||||
// try to compare IDs numerically
|
||||
let id_self = self.backup_id.parse::<u64>();
|
||||
let id_other = other.backup_id.parse::<u64>();
|
||||
match (id_self, id_other) {
|
||||
(Ok(id_self), Ok(id_other)) => id_self.cmp(&id_other),
|
||||
(Ok(_), Err(_)) => std::cmp::Ordering::Less,
|
||||
(Err(_), Ok(_)) => std::cmp::Ordering::Greater,
|
||||
_ => self.backup_id.cmp(&other.backup_id),
|
||||
}
|
||||
self.group.cmp(&other.group)
|
||||
}
|
||||
}
|
||||
|
||||
@ -45,29 +26,22 @@ impl std::cmp::PartialOrd for BackupGroup {
|
||||
}
|
||||
|
||||
impl BackupGroup {
|
||||
pub fn new<T: Into<String>>(backup_type: BackupType, backup_id: T) -> Self {
|
||||
pub(crate) fn new<T: Into<String>>(backup_type: BackupType, backup_id: T) -> Self {
|
||||
Self {
|
||||
backup_type,
|
||||
backup_id: backup_id.into(),
|
||||
group: (backup_type, backup_id.into()).into(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn backup_type(&self) -> BackupType {
|
||||
self.backup_type
|
||||
self.group.ty
|
||||
}
|
||||
|
||||
pub fn backup_id(&self) -> &str {
|
||||
&self.backup_id
|
||||
&self.group.id
|
||||
}
|
||||
|
||||
pub fn relative_group_path(&self) -> PathBuf {
|
||||
let mut relative_path = PathBuf::new();
|
||||
|
||||
relative_path.push(self.backup_type.as_str());
|
||||
|
||||
relative_path.push(&self.backup_id);
|
||||
|
||||
relative_path
|
||||
self.group.to_string().into()
|
||||
}
|
||||
|
||||
pub fn list_backups(&self, base_path: &Path) -> Result<Vec<BackupInfo>, Error> {
|
||||
@ -85,8 +59,7 @@ impl BackupGroup {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let backup_dir =
|
||||
BackupDir::with_rfc3339(self.backup_type, &self.backup_id, backup_time)?;
|
||||
let backup_dir = self.backup_dir_with_rfc3339(backup_time)?;
|
||||
let files = list_backup_files(l2_fd, backup_time)?;
|
||||
|
||||
let protected = backup_dir.is_protected(base_path.to_owned());
|
||||
@ -171,26 +144,37 @@ impl BackupGroup {
|
||||
}
|
||||
|
||||
pub fn matches(&self, filter: &GroupFilter) -> bool {
|
||||
match filter {
|
||||
GroupFilter::Group(backup_group) => match BackupGroup::from_str(backup_group) {
|
||||
Ok(group) => &group == self,
|
||||
Err(_) => false, // shouldn't happen if value is schema-checked
|
||||
},
|
||||
GroupFilter::BackupType(backup_type) => self.backup_type().as_str() == backup_type,
|
||||
GroupFilter::Regex(regex) => regex.is_match(&self.to_string()),
|
||||
}
|
||||
self.group.matches(filter)
|
||||
}
|
||||
|
||||
pub fn backup_dir(&self, time: i64) -> Result<BackupDir, Error> {
|
||||
BackupDir::with_group(self.clone(), time)
|
||||
}
|
||||
|
||||
pub fn backup_dir_with_rfc3339<T: Into<String>>(
|
||||
&self,
|
||||
time_string: T,
|
||||
) -> Result<BackupDir, Error> {
|
||||
BackupDir::with_rfc3339(self.clone(), time_string.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<pbs_api_types::BackupGroup> for BackupGroup {
|
||||
#[inline]
|
||||
fn as_ref(&self) -> &pbs_api_types::BackupGroup {
|
||||
&self.group
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&BackupGroup> for pbs_api_types::BackupGroup {
|
||||
fn from(group: &BackupGroup) -> pbs_api_types::BackupGroup {
|
||||
(group.backup_type, group.backup_id.clone()).into()
|
||||
group.group.clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<BackupGroup> for pbs_api_types::BackupGroup {
|
||||
fn from(group: BackupGroup) -> pbs_api_types::BackupGroup {
|
||||
(group.backup_type, group.backup_id).into()
|
||||
group.group
|
||||
}
|
||||
}
|
||||
|
||||
@ -202,21 +186,19 @@ impl std::fmt::Display for BackupGroup {
|
||||
}
|
||||
}
|
||||
|
||||
impl std::str::FromStr for BackupGroup {
|
||||
type Err = Error;
|
||||
impl From<BackupDir> for BackupGroup {
|
||||
fn from(dir: BackupDir) -> BackupGroup {
|
||||
BackupGroup {
|
||||
group: dir.dir.group,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Parse a backup group path
|
||||
///
|
||||
/// This parses strings like `vm/100".
|
||||
fn from_str(path: &str) -> Result<Self, Self::Err> {
|
||||
let cap = GROUP_PATH_REGEX
|
||||
.captures(path)
|
||||
.ok_or_else(|| format_err!("unable to parse backup group path '{}'", path))?;
|
||||
|
||||
Ok(Self {
|
||||
backup_type: cap.get(1).unwrap().as_str().parse()?,
|
||||
backup_id: cap.get(2).unwrap().as_str().to_owned(),
|
||||
})
|
||||
impl From<&BackupDir> for BackupGroup {
|
||||
fn from(dir: &BackupDir) -> BackupGroup {
|
||||
BackupGroup {
|
||||
group: dir.dir.group.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -225,57 +207,53 @@ impl std::str::FromStr for BackupGroup {
|
||||
/// We also call this a backup snaphost.
|
||||
#[derive(Debug, Eq, PartialEq, Clone)]
|
||||
pub struct BackupDir {
|
||||
/// Backup group
|
||||
group: BackupGroup,
|
||||
/// Backup timestamp
|
||||
backup_time: i64,
|
||||
dir: pbs_api_types::BackupDir,
|
||||
// backup_time as rfc3339
|
||||
backup_time_string: String,
|
||||
}
|
||||
|
||||
impl BackupDir {
|
||||
pub fn new<T>(backup_type: BackupType, backup_id: T, backup_time: i64) -> Result<Self, Error>
|
||||
where
|
||||
T: Into<String>,
|
||||
{
|
||||
let group = BackupGroup::new(backup_type, backup_id.into());
|
||||
BackupDir::with_group(group, backup_time)
|
||||
/// Temporarily used for tests.
|
||||
#[doc(hidden)]
|
||||
pub fn new_test(dir: pbs_api_types::BackupDir) -> Self {
|
||||
Self {
|
||||
backup_time_string: Self::backup_time_to_string(dir.time).unwrap(),
|
||||
dir,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn with_rfc3339<T, U>(
|
||||
backup_type: BackupType,
|
||||
backup_id: T,
|
||||
backup_time_string: U,
|
||||
) -> Result<Self, Error>
|
||||
where
|
||||
T: Into<String>,
|
||||
U: Into<String>,
|
||||
{
|
||||
let backup_time_string = backup_time_string.into();
|
||||
let backup_time = proxmox_time::parse_rfc3339(&backup_time_string)?;
|
||||
let group = BackupGroup::new(backup_type, backup_id.into());
|
||||
Ok(Self {
|
||||
group,
|
||||
backup_time,
|
||||
backup_time_string,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn with_group(group: BackupGroup, backup_time: i64) -> Result<Self, Error> {
|
||||
pub(crate) fn with_group(group: BackupGroup, backup_time: i64) -> Result<Self, Error> {
|
||||
let backup_time_string = Self::backup_time_to_string(backup_time)?;
|
||||
Ok(Self {
|
||||
group,
|
||||
backup_time,
|
||||
dir: (group.group, backup_time).into(),
|
||||
backup_time_string,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn group(&self) -> &BackupGroup {
|
||||
&self.group
|
||||
pub(crate) fn with_rfc3339(
|
||||
group: BackupGroup,
|
||||
backup_time_string: String,
|
||||
) -> Result<Self, Error> {
|
||||
let backup_time = proxmox_time::parse_rfc3339(&backup_time_string)?;
|
||||
Ok(Self {
|
||||
dir: (group.group, backup_time).into(),
|
||||
backup_time_string,
|
||||
})
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn backup_type(&self) -> BackupType {
|
||||
self.dir.group.ty
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn backup_id(&self) -> &str {
|
||||
&self.dir.group.id
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn backup_time(&self) -> i64 {
|
||||
self.backup_time
|
||||
self.dir.time
|
||||
}
|
||||
|
||||
pub fn backup_time_string(&self) -> &str {
|
||||
@ -283,11 +261,15 @@ impl BackupDir {
|
||||
}
|
||||
|
||||
pub fn relative_path(&self) -> PathBuf {
|
||||
let mut relative_path = self.group.relative_group_path();
|
||||
format!("{}/{}", self.dir.group, self.backup_time_string).into()
|
||||
}
|
||||
|
||||
relative_path.push(self.backup_time_string.clone());
|
||||
|
||||
relative_path
|
||||
/// Returns the absolute path for backup_dir, using the cached formatted time string.
|
||||
pub fn full_path(&self, mut base_path: PathBuf) -> PathBuf {
|
||||
base_path.push(self.dir.group.ty.as_str());
|
||||
base_path.push(&self.dir.group.id);
|
||||
base_path.push(&self.backup_time_string);
|
||||
base_path
|
||||
}
|
||||
|
||||
pub fn protected_file(&self, mut path: PathBuf) -> PathBuf {
|
||||
@ -307,46 +289,45 @@ impl BackupDir {
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<pbs_api_types::BackupDir> for BackupDir {
|
||||
fn as_ref(&self) -> &pbs_api_types::BackupDir {
|
||||
&self.dir
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<pbs_api_types::BackupGroup> for BackupDir {
|
||||
fn as_ref(&self) -> &pbs_api_types::BackupGroup {
|
||||
&self.dir.group
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&BackupDir> for pbs_api_types::BackupGroup {
|
||||
fn from(dir: &BackupDir) -> pbs_api_types::BackupGroup {
|
||||
dir.dir.group.clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<BackupDir> for pbs_api_types::BackupGroup {
|
||||
fn from(dir: BackupDir) -> pbs_api_types::BackupGroup {
|
||||
dir.dir.group.into()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&BackupDir> for pbs_api_types::BackupDir {
|
||||
fn from(dir: &BackupDir) -> pbs_api_types::BackupDir {
|
||||
(
|
||||
pbs_api_types::BackupGroup::from(dir.group.clone()),
|
||||
dir.backup_time,
|
||||
)
|
||||
.into()
|
||||
dir.dir.clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<BackupDir> for pbs_api_types::BackupDir {
|
||||
fn from(dir: BackupDir) -> pbs_api_types::BackupDir {
|
||||
(pbs_api_types::BackupGroup::from(dir.group), dir.backup_time).into()
|
||||
}
|
||||
}
|
||||
|
||||
impl std::str::FromStr for BackupDir {
|
||||
type Err = Error;
|
||||
|
||||
/// Parse a snapshot path
|
||||
///
|
||||
/// This parses strings like `host/elsa/2020-06-15T05:18:33Z".
|
||||
fn from_str(path: &str) -> Result<Self, Self::Err> {
|
||||
let cap = SNAPSHOT_PATH_REGEX
|
||||
.captures(path)
|
||||
.ok_or_else(|| format_err!("unable to parse backup snapshot path '{}'", path))?;
|
||||
|
||||
BackupDir::with_rfc3339(
|
||||
cap.get(1).unwrap().as_str().parse()?,
|
||||
cap.get(2).unwrap().as_str(),
|
||||
cap.get(3).unwrap().as_str(),
|
||||
)
|
||||
dir.dir
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for BackupDir {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
let backup_type = self.group.backup_type();
|
||||
let id = self.group.backup_id();
|
||||
write!(f, "{}/{}/{}", backup_type, id, self.backup_time_string)
|
||||
write!(f, "{}/{}", self.dir.group, self.backup_time_string)
|
||||
}
|
||||
}
|
||||
|
||||
@ -379,10 +360,10 @@ impl BackupInfo {
|
||||
pub fn sort_list(list: &mut Vec<BackupInfo>, ascendending: bool) {
|
||||
if ascendending {
|
||||
// oldest first
|
||||
list.sort_unstable_by(|a, b| a.backup_dir.backup_time.cmp(&b.backup_dir.backup_time));
|
||||
list.sort_unstable_by(|a, b| a.backup_dir.dir.time.cmp(&b.backup_dir.dir.time));
|
||||
} else {
|
||||
// newest first
|
||||
list.sort_unstable_by(|a, b| b.backup_dir.backup_time.cmp(&a.backup_dir.backup_time));
|
||||
list.sort_unstable_by(|a, b| b.backup_dir.dir.time.cmp(&a.backup_dir.dir.time));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -19,7 +19,8 @@ use proxmox_sys::{task_log, task_warn};
|
||||
|
||||
use pbs_api_types::{
|
||||
Authid, BackupType, ChunkOrder, DataStoreConfig, DatastoreTuning, GarbageCollectionStatus,
|
||||
HumanByte, Operation, BACKUP_DATE_REGEX, BACKUP_ID_REGEX, UPID,
|
||||
HumanByte, Operation, BACKUP_DATE_REGEX, BACKUP_ID_REGEX, GROUP_PATH_REGEX,
|
||||
SNAPSHOT_PATH_REGEX, UPID,
|
||||
};
|
||||
use pbs_config::{open_backup_lockfile, BackupLockGuard, ConfigVersionCache};
|
||||
|
||||
@ -302,11 +303,19 @@ impl DataStore {
|
||||
/// Removes all files not mentioned in the manifest.
|
||||
pub fn cleanup_backup_dir(
|
||||
&self,
|
||||
backup_dir: &BackupDir,
|
||||
backup_dir: impl AsRef<pbs_api_types::BackupDir>,
|
||||
manifest: &BackupManifest,
|
||||
) -> Result<(), Error> {
|
||||
self.cleanup_backup_dir_do(backup_dir.as_ref(), manifest)
|
||||
}
|
||||
|
||||
fn cleanup_backup_dir_do(
|
||||
&self,
|
||||
backup_dir: &pbs_api_types::BackupDir,
|
||||
manifest: &BackupManifest,
|
||||
) -> Result<(), Error> {
|
||||
let mut full_path = self.base_path();
|
||||
full_path.push(backup_dir.relative_path());
|
||||
full_path.push(backup_dir.to_string());
|
||||
|
||||
let mut wanted_files = HashSet::new();
|
||||
wanted_files.insert(MANIFEST_BLOB_NAME.to_string());
|
||||
@ -339,23 +348,28 @@ impl DataStore {
|
||||
}
|
||||
|
||||
/// Returns the absolute path for a backup_group
|
||||
pub fn group_path(&self, backup_group: &BackupGroup) -> PathBuf {
|
||||
pub fn group_path(&self, backup_group: &pbs_api_types::BackupGroup) -> PathBuf {
|
||||
let mut full_path = self.base_path();
|
||||
full_path.push(backup_group.relative_group_path());
|
||||
full_path.push(backup_group.to_string());
|
||||
full_path
|
||||
}
|
||||
|
||||
/// Returns the absolute path for backup_dir
|
||||
pub fn snapshot_path(&self, backup_dir: &BackupDir) -> PathBuf {
|
||||
pub fn snapshot_path(&self, backup_dir: &pbs_api_types::BackupDir) -> PathBuf {
|
||||
let mut full_path = self.base_path();
|
||||
full_path.push(backup_dir.relative_path());
|
||||
full_path.push(backup_dir.to_string());
|
||||
full_path
|
||||
}
|
||||
|
||||
/// Remove a complete backup group including all snapshots, returns true
|
||||
/// if all snapshots were removed, and false if some were protected
|
||||
pub fn remove_backup_group(&self, backup_group: &BackupGroup) -> Result<bool, Error> {
|
||||
let full_path = self.group_path(backup_group);
|
||||
pub fn remove_backup_group(
|
||||
&self,
|
||||
backup_group: &pbs_api_types::BackupGroup,
|
||||
) -> Result<bool, Error> {
|
||||
let backup_group = self.backup_group_from_spec(backup_group.clone());
|
||||
|
||||
let full_path = self.group_path(backup_group.as_ref());
|
||||
|
||||
let _guard = proxmox_sys::fs::lock_dir_noblock(
|
||||
&full_path,
|
||||
@ -373,7 +387,7 @@ impl DataStore {
|
||||
removed_all = false;
|
||||
continue;
|
||||
}
|
||||
self.remove_backup_dir(&snap.backup_dir, false)?;
|
||||
self.remove_backup_dir(snap.backup_dir.as_ref(), false)?;
|
||||
}
|
||||
|
||||
if removed_all {
|
||||
@ -391,13 +405,19 @@ impl DataStore {
|
||||
}
|
||||
|
||||
/// Remove a backup directory including all content
|
||||
pub fn remove_backup_dir(&self, backup_dir: &BackupDir, force: bool) -> Result<(), Error> {
|
||||
let full_path = self.snapshot_path(backup_dir);
|
||||
pub fn remove_backup_dir(
|
||||
&self,
|
||||
backup_dir: &pbs_api_types::BackupDir,
|
||||
force: bool,
|
||||
) -> Result<(), Error> {
|
||||
let backup_dir = self.backup_dir_from_spec(backup_dir.clone())?;
|
||||
|
||||
let full_path = backup_dir.full_path(self.base_path());
|
||||
|
||||
let (_guard, _manifest_guard);
|
||||
if !force {
|
||||
_guard = lock_dir_noblock(&full_path, "snapshot", "possibly running or in use")?;
|
||||
_manifest_guard = self.lock_manifest(backup_dir)?;
|
||||
_manifest_guard = self.lock_manifest(&backup_dir)?;
|
||||
}
|
||||
|
||||
if backup_dir.is_protected(self.base_path()) {
|
||||
@ -410,7 +430,7 @@ impl DataStore {
|
||||
})?;
|
||||
|
||||
// the manifest does not exists anymore, we do not need to keep the lock
|
||||
if let Ok(path) = self.manifest_lock_path(backup_dir) {
|
||||
if let Ok(path) = self.manifest_lock_path(&backup_dir) {
|
||||
// ignore errors
|
||||
let _ = std::fs::remove_file(path);
|
||||
}
|
||||
@ -421,7 +441,12 @@ impl DataStore {
|
||||
/// Returns the time of the last successful backup
|
||||
///
|
||||
/// Or None if there is no backup in the group (or the group dir does not exist).
|
||||
pub fn last_successful_backup(&self, backup_group: &BackupGroup) -> Result<Option<i64>, Error> {
|
||||
pub fn last_successful_backup(
|
||||
&self,
|
||||
backup_group: &pbs_api_types::BackupGroup,
|
||||
) -> Result<Option<i64>, Error> {
|
||||
let backup_group = self.backup_group_from_spec(backup_group.clone());
|
||||
|
||||
let base_path = self.base_path();
|
||||
let mut group_path = base_path.clone();
|
||||
group_path.push(backup_group.relative_group_path());
|
||||
@ -436,15 +461,19 @@ impl DataStore {
|
||||
/// Returns the backup owner.
|
||||
///
|
||||
/// The backup owner is the entity who first created the backup group.
|
||||
pub fn get_owner(&self, backup_group: &BackupGroup) -> Result<Authid, Error> {
|
||||
pub fn get_owner(&self, backup_group: &pbs_api_types::BackupGroup) -> Result<Authid, Error> {
|
||||
let mut full_path = self.base_path();
|
||||
full_path.push(backup_group.relative_group_path());
|
||||
full_path.push(backup_group.to_string());
|
||||
full_path.push("owner");
|
||||
let owner = proxmox_sys::fs::file_read_firstline(full_path)?;
|
||||
owner.trim_end().parse() // remove trailing newline
|
||||
}
|
||||
|
||||
pub fn owns_backup(&self, backup_group: &BackupGroup, auth_id: &Authid) -> Result<bool, Error> {
|
||||
pub fn owns_backup(
|
||||
&self,
|
||||
backup_group: &pbs_api_types::BackupGroup,
|
||||
auth_id: &Authid,
|
||||
) -> Result<bool, Error> {
|
||||
let owner = self.get_owner(backup_group)?;
|
||||
|
||||
Ok(check_backup_owner(&owner, auth_id).is_ok())
|
||||
@ -453,12 +482,12 @@ impl DataStore {
|
||||
/// Set the backup owner.
|
||||
pub fn set_owner(
|
||||
&self,
|
||||
backup_group: &BackupGroup,
|
||||
backup_group: &pbs_api_types::BackupGroup,
|
||||
auth_id: &Authid,
|
||||
force: bool,
|
||||
) -> Result<(), Error> {
|
||||
let mut path = self.base_path();
|
||||
path.push(backup_group.relative_group_path());
|
||||
path.push(backup_group.to_string());
|
||||
path.push("owner");
|
||||
|
||||
let mut open_options = std::fs::OpenOptions::new();
|
||||
@ -489,15 +518,15 @@ impl DataStore {
|
||||
/// This also acquires an exclusive lock on the directory and returns the lock guard.
|
||||
pub fn create_locked_backup_group(
|
||||
&self,
|
||||
backup_group: &BackupGroup,
|
||||
backup_group: &pbs_api_types::BackupGroup,
|
||||
auth_id: &Authid,
|
||||
) -> Result<(Authid, DirLockGuard), Error> {
|
||||
// create intermediate path first:
|
||||
let mut full_path = self.base_path();
|
||||
full_path.push(backup_group.backup_type().as_str());
|
||||
full_path.push(backup_group.ty.as_str());
|
||||
std::fs::create_dir_all(&full_path)?;
|
||||
|
||||
full_path.push(backup_group.backup_id());
|
||||
full_path.push(&backup_group.id);
|
||||
|
||||
// create the last component now
|
||||
match std::fs::create_dir(&full_path) {
|
||||
@ -529,9 +558,9 @@ impl DataStore {
|
||||
/// The BackupGroup directory needs to exist.
|
||||
pub fn create_locked_backup_dir(
|
||||
&self,
|
||||
backup_dir: &BackupDir,
|
||||
backup_dir: &pbs_api_types::BackupDir,
|
||||
) -> Result<(PathBuf, bool, DirLockGuard), Error> {
|
||||
let relative_path = backup_dir.relative_path();
|
||||
let relative_path = PathBuf::from(backup_dir.to_string());
|
||||
let mut full_path = self.base_path();
|
||||
full_path.push(&relative_path);
|
||||
|
||||
@ -699,7 +728,7 @@ impl DataStore {
|
||||
if let Some(backup_dir_path) = img.parent() {
|
||||
let backup_dir_path = backup_dir_path.strip_prefix(self.base_path())?;
|
||||
if let Some(backup_dir_str) = backup_dir_path.to_str() {
|
||||
if BackupDir::from_str(backup_dir_str).is_err() {
|
||||
if pbs_api_types::BackupDir::from_str(backup_dir_str).is_err() {
|
||||
strange_paths_count += 1;
|
||||
}
|
||||
}
|
||||
@ -933,8 +962,8 @@ impl DataStore {
|
||||
let mut path = format!(
|
||||
"/run/proxmox-backup/locks/{}/{}/{}",
|
||||
self.name(),
|
||||
backup_dir.group().backup_type(),
|
||||
backup_dir.group().backup_id(),
|
||||
backup_dir.backup_type(),
|
||||
backup_dir.backup_id(),
|
||||
);
|
||||
std::fs::create_dir_all(&path)?;
|
||||
use std::fmt::Write;
|
||||
@ -994,7 +1023,7 @@ impl DataStore {
|
||||
|
||||
/// Updates the protection status of the specified snapshot.
|
||||
pub fn update_protection(&self, backup_dir: &BackupDir, protection: bool) -> Result<(), Error> {
|
||||
let full_path = self.snapshot_path(backup_dir);
|
||||
let full_path = backup_dir.full_path(self.base_path());
|
||||
|
||||
let _guard = lock_dir_noblock(&full_path, "snapshot", "possibly running or in use")?;
|
||||
|
||||
@ -1063,6 +1092,70 @@ impl DataStore {
|
||||
|
||||
Ok(chunk_list)
|
||||
}
|
||||
|
||||
pub fn backup_group_from_spec(&self, group: pbs_api_types::BackupGroup) -> BackupGroup {
|
||||
BackupGroup::new(group.ty, group.id)
|
||||
}
|
||||
|
||||
pub fn backup_dir_from_spec(&self, dir: pbs_api_types::BackupDir) -> Result<BackupDir, Error> {
|
||||
BackupDir::with_group(self.backup_group_from_spec(dir.group), dir.time)
|
||||
}
|
||||
|
||||
pub fn backup_dir_from_parts<T>(
|
||||
&self,
|
||||
ty: BackupType,
|
||||
id: T,
|
||||
time: i64,
|
||||
) -> Result<BackupDir, Error>
|
||||
where
|
||||
T: Into<String>,
|
||||
{
|
||||
self.backup_dir_from_spec((ty, id.into(), time).into())
|
||||
}
|
||||
|
||||
pub fn backup_group<T>(&self, ty: BackupType, id: T) -> BackupGroup
|
||||
where
|
||||
T: Into<String>,
|
||||
{
|
||||
BackupGroup::new(ty, id.into())
|
||||
}
|
||||
|
||||
pub fn backup_group_from_path(&self, path: &str) -> Result<BackupGroup, Error> {
|
||||
let cap = GROUP_PATH_REGEX
|
||||
.captures(path)
|
||||
.ok_or_else(|| format_err!("unable to parse backup group path '{}'", path))?;
|
||||
|
||||
Ok(self.backup_group(
|
||||
cap.get(1).unwrap().as_str().parse()?,
|
||||
cap.get(2).unwrap().as_str().to_owned(),
|
||||
))
|
||||
}
|
||||
|
||||
pub fn backup_dir(&self, group: BackupGroup, time: i64) -> Result<BackupDir, Error> {
|
||||
BackupDir::with_group(group, time)
|
||||
}
|
||||
|
||||
pub fn backup_dir_with_rfc3339<T: Into<String>>(
|
||||
&self,
|
||||
group: BackupGroup,
|
||||
time_string: T,
|
||||
) -> Result<BackupDir, Error> {
|
||||
BackupDir::with_rfc3339(group, time_string.into())
|
||||
}
|
||||
|
||||
pub fn backup_dir_from_path(&self, path: &str) -> Result<BackupDir, Error> {
|
||||
let cap = SNAPSHOT_PATH_REGEX
|
||||
.captures(path)
|
||||
.ok_or_else(|| format_err!("unable to parse backup snapshot path '{}'", path))?;
|
||||
|
||||
BackupDir::with_rfc3339(
|
||||
BackupGroup::new(
|
||||
cap.get(1).unwrap().as_str().parse()?,
|
||||
cap.get(2).unwrap().as_str().to_owned(),
|
||||
),
|
||||
cap.get(3).unwrap().as_str().to_owned(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// A iterator for all BackupDir's (Snapshots) in a BackupGroup
|
||||
|
@ -9,8 +9,6 @@ use serde_json::{json, Value};
|
||||
use pbs_api_types::{BackupType, CryptMode, Fingerprint};
|
||||
use pbs_tools::crypt_config::CryptConfig;
|
||||
|
||||
use crate::BackupDir;
|
||||
|
||||
pub const MANIFEST_BLOB_NAME: &str = "index.json.blob";
|
||||
pub const MANIFEST_LOCK_NAME: &str = ".index.json.lck";
|
||||
pub const CLIENT_LOG_BLOB_NAME: &str = "client.log.blob";
|
||||
@ -85,11 +83,11 @@ pub fn archive_type<P: AsRef<Path>>(archive_name: P) -> Result<ArchiveType, Erro
|
||||
}
|
||||
|
||||
impl BackupManifest {
|
||||
pub fn new(snapshot: BackupDir) -> Self {
|
||||
pub fn new(snapshot: pbs_api_types::BackupDir) -> Self {
|
||||
Self {
|
||||
backup_type: snapshot.group().backup_type(),
|
||||
backup_id: snapshot.group().backup_id().into(),
|
||||
backup_time: snapshot.backup_time(),
|
||||
backup_type: snapshot.group.ty,
|
||||
backup_id: snapshot.group.id.into(),
|
||||
backup_time: snapshot.time,
|
||||
files: Vec::new(),
|
||||
unprotected: json!({}),
|
||||
signature: None,
|
||||
@ -284,9 +282,7 @@ fn test_manifest_signature() -> Result<(), Error> {
|
||||
|
||||
let crypt_config = CryptConfig::new(testkey)?;
|
||||
|
||||
let snapshot: BackupDir = "host/elsa/2020-06-26T13:56:05Z".parse()?;
|
||||
|
||||
let mut manifest = BackupManifest::new(snapshot);
|
||||
let mut manifest = BackupManifest::new("host/elsa/2020-06-26T13:56:05Z".parse()?);
|
||||
|
||||
manifest.add_file("test1.img.fidx".into(), 200, [1u8; 32], CryptMode::Encrypt)?;
|
||||
manifest.add_file("abc.blob".into(), 200, [2u8; 32], CryptMode::None)?;
|
||||
|
@ -28,8 +28,13 @@ pub struct SnapshotReader {
|
||||
|
||||
impl SnapshotReader {
|
||||
/// Lock snapshot, reads the manifest and returns a new instance
|
||||
pub fn new(datastore: Arc<DataStore>, snapshot: BackupDir) -> Result<Self, Error> {
|
||||
let snapshot_path = datastore.snapshot_path(&snapshot);
|
||||
pub fn new(
|
||||
datastore: Arc<DataStore>,
|
||||
snapshot: pbs_api_types::BackupDir,
|
||||
) -> Result<Self, Error> {
|
||||
let snapshot = datastore.backup_dir_from_spec(snapshot)?;
|
||||
|
||||
let snapshot_path = snapshot.full_path(datastore.base_path());
|
||||
|
||||
let locked_dir =
|
||||
lock_dir_noblock_shared(&snapshot_path, "snapshot", "locked by another operation")?;
|
||||
|
Reference in New Issue
Block a user