split the namespace out of BackupGroup/Dir api types
We decided to go this route because it'll most likely be safer in the API as we need to explicitly add namespaces support to the various API endpoints this way. For example, 'pull' should have 2 namespaces: local and remote, and the GroupFilter (which would otherwise contain exactly *one* namespace parameter) needs to be applied for both sides (to decide what to pull from the remote, and what to *remove* locally as cleanup). The *datastore* types still contain the namespace and have a `.backup_ns()` getter. Note that the datastore's `Display` implementations are no longer safe to use as a deserializable string. Additionally, some datastore based methods now have been exposed via the BackupGroup/BackupDir types to avoid a "round trip" in code. Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com> Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
This commit is contained in:
committed by
Thomas Lamprecht
parent
1baf9030ad
commit
133d718fe4
@ -59,6 +59,7 @@ use pbs_datastore::{
|
||||
use pbs_tools::json::required_string_param;
|
||||
use proxmox_rest_server::{formatter, WorkerTask};
|
||||
|
||||
use crate::api2::backup::optional_ns_param;
|
||||
use crate::api2::node::rrd::create_value_from_rrd;
|
||||
use crate::backup::{verify_all_backups, verify_backup_dir, verify_backup_group, verify_filter};
|
||||
|
||||
@ -66,29 +67,35 @@ use crate::server::jobstate::Job;
|
||||
|
||||
const GROUP_NOTES_FILE_NAME: &str = "notes";
|
||||
|
||||
fn get_group_note_path(store: &DataStore, group: &pbs_api_types::BackupGroup) -> PathBuf {
|
||||
let mut note_path = store.base_path();
|
||||
note_path.push(group.to_string());
|
||||
fn get_group_note_path(
|
||||
store: &DataStore,
|
||||
ns: &BackupNamespace,
|
||||
group: &pbs_api_types::BackupGroup,
|
||||
) -> PathBuf {
|
||||
let mut note_path = store.group_path(ns, group);
|
||||
note_path.push(GROUP_NOTES_FILE_NAME);
|
||||
note_path
|
||||
}
|
||||
|
||||
fn check_priv_or_backup_owner(
|
||||
// FIXME: We could probably switch to pbs-datastore::BackupGroup here to replace all of store,
|
||||
// ns and group.
|
||||
store: &DataStore,
|
||||
ns: &BackupNamespace,
|
||||
group: &pbs_api_types::BackupGroup,
|
||||
auth_id: &Authid,
|
||||
required_privs: u64,
|
||||
) -> Result<(), Error> {
|
||||
let user_info = CachedUserInfo::new()?;
|
||||
|
||||
let privs = if group.ns.is_root() {
|
||||
let privs = if ns.is_root() {
|
||||
user_info.lookup_privs(auth_id, &["datastore", store.name()])
|
||||
} else {
|
||||
user_info.lookup_privs(auth_id, &["datastore", store.name(), &group.ns.to_string()])
|
||||
user_info.lookup_privs(auth_id, &["datastore", store.name(), &ns.to_string()])
|
||||
};
|
||||
|
||||
if privs & required_privs == 0 {
|
||||
let owner = store.get_owner(group)?;
|
||||
let owner = store.get_owner(ns, group)?;
|
||||
check_backup_owner(&owner, auth_id)?;
|
||||
}
|
||||
Ok(())
|
||||
@ -212,10 +219,10 @@ pub fn list_groups(
|
||||
let list_all = (user_privs & PRIV_DATASTORE_AUDIT) != 0;
|
||||
|
||||
datastore
|
||||
.iter_backup_groups(backup_ns)? // FIXME: Namespaces and recursion parameters!
|
||||
.iter_backup_groups(backup_ns.clone())? // FIXME: Namespaces and recursion parameters!
|
||||
.try_fold(Vec::new(), |mut group_info, group| {
|
||||
let group = group?;
|
||||
let owner = match datastore.get_owner(group.as_ref()) {
|
||||
let owner = match datastore.get_owner(&backup_ns, group.as_ref()) {
|
||||
Ok(auth_id) => auth_id,
|
||||
Err(err) => {
|
||||
let id = &store;
|
||||
@ -248,7 +255,7 @@ pub fn list_groups(
|
||||
})
|
||||
.to_owned();
|
||||
|
||||
let note_path = get_group_note_path(&datastore, group.as_ref());
|
||||
let note_path = get_group_note_path(&datastore, &backup_ns, group.as_ref());
|
||||
let comment = file_read_firstline(¬e_path).ok();
|
||||
|
||||
group_info.push(GroupListItem {
|
||||
@ -268,6 +275,10 @@ pub fn list_groups(
|
||||
input: {
|
||||
properties: {
|
||||
store: { schema: DATASTORE_SCHEMA },
|
||||
"backup-ns": {
|
||||
type: BackupNamespace,
|
||||
optional: true,
|
||||
},
|
||||
group: {
|
||||
type: pbs_api_types::BackupGroup,
|
||||
flatten: true,
|
||||
@ -283,24 +294,33 @@ pub fn list_groups(
|
||||
/// Delete backup group including all snapshots.
|
||||
pub fn delete_group(
|
||||
store: String,
|
||||
backup_ns: Option<BackupNamespace>,
|
||||
group: pbs_api_types::BackupGroup,
|
||||
_info: &ApiMethod,
|
||||
rpcenv: &mut dyn RpcEnvironment,
|
||||
) -> Result<Value, Error> {
|
||||
let auth_id: Authid = rpcenv.get_auth_id().unwrap().parse()?;
|
||||
|
||||
let backup_ns = backup_ns.unwrap_or_default();
|
||||
|
||||
get_ns_privs_checked(
|
||||
&store,
|
||||
&group.ns,
|
||||
&backup_ns,
|
||||
&auth_id,
|
||||
PRIV_DATASTORE_MODIFY | PRIV_DATASTORE_PRUNE,
|
||||
)?;
|
||||
|
||||
let datastore = DataStore::lookup_datastore(&store, Some(Operation::Write))?;
|
||||
|
||||
check_priv_or_backup_owner(&datastore, &group, &auth_id, PRIV_DATASTORE_MODIFY)?;
|
||||
check_priv_or_backup_owner(
|
||||
&datastore,
|
||||
&backup_ns,
|
||||
&group,
|
||||
&auth_id,
|
||||
PRIV_DATASTORE_MODIFY,
|
||||
)?;
|
||||
|
||||
if !datastore.remove_backup_group(&group)? {
|
||||
if !datastore.remove_backup_group(&backup_ns, &group)? {
|
||||
bail!("group only partially deleted due to protected snapshots");
|
||||
}
|
||||
|
||||
@ -311,6 +331,10 @@ pub fn delete_group(
|
||||
input: {
|
||||
properties: {
|
||||
store: { schema: DATASTORE_SCHEMA },
|
||||
"backup-ns": {
|
||||
type: BackupNamespace,
|
||||
optional: true,
|
||||
},
|
||||
backup_dir: {
|
||||
type: pbs_api_types::BackupDir,
|
||||
flatten: true,
|
||||
@ -327,25 +351,29 @@ pub fn delete_group(
|
||||
/// List snapshot files.
|
||||
pub fn list_snapshot_files(
|
||||
store: String,
|
||||
backup_ns: Option<BackupNamespace>,
|
||||
backup_dir: pbs_api_types::BackupDir,
|
||||
_info: &ApiMethod,
|
||||
rpcenv: &mut dyn RpcEnvironment,
|
||||
) -> Result<Vec<BackupContent>, Error> {
|
||||
let auth_id: Authid = rpcenv.get_auth_id().unwrap().parse()?;
|
||||
|
||||
let backup_ns = backup_ns.unwrap_or_default();
|
||||
|
||||
get_ns_privs_checked(
|
||||
&store,
|
||||
&backup_dir.group.ns,
|
||||
&backup_ns,
|
||||
&auth_id,
|
||||
PRIV_DATASTORE_AUDIT | PRIV_DATASTORE_READ | PRIV_DATASTORE_BACKUP,
|
||||
)?;
|
||||
|
||||
let datastore = DataStore::lookup_datastore(&store, Some(Operation::Read))?;
|
||||
|
||||
let snapshot = datastore.backup_dir(backup_dir)?;
|
||||
let snapshot = datastore.backup_dir(backup_ns, backup_dir)?;
|
||||
|
||||
check_priv_or_backup_owner(
|
||||
&datastore,
|
||||
snapshot.backup_ns(),
|
||||
snapshot.as_ref(),
|
||||
&auth_id,
|
||||
PRIV_DATASTORE_AUDIT | PRIV_DATASTORE_READ,
|
||||
@ -362,6 +390,10 @@ pub fn list_snapshot_files(
|
||||
input: {
|
||||
properties: {
|
||||
store: { schema: DATASTORE_SCHEMA },
|
||||
"backup-ns": {
|
||||
type: BackupNamespace,
|
||||
optional: true,
|
||||
},
|
||||
backup_dir: {
|
||||
type: pbs_api_types::BackupDir,
|
||||
flatten: true,
|
||||
@ -377,30 +409,34 @@ pub fn list_snapshot_files(
|
||||
/// Delete backup snapshot.
|
||||
pub fn delete_snapshot(
|
||||
store: String,
|
||||
backup_ns: Option<BackupNamespace>,
|
||||
backup_dir: pbs_api_types::BackupDir,
|
||||
_info: &ApiMethod,
|
||||
rpcenv: &mut dyn RpcEnvironment,
|
||||
) -> Result<Value, Error> {
|
||||
let auth_id: Authid = rpcenv.get_auth_id().unwrap().parse()?;
|
||||
|
||||
let backup_ns = backup_ns.unwrap_or_default();
|
||||
|
||||
get_ns_privs_checked(
|
||||
&store,
|
||||
&backup_dir.group.ns,
|
||||
&backup_ns,
|
||||
&auth_id,
|
||||
PRIV_DATASTORE_MODIFY | PRIV_DATASTORE_PRUNE,
|
||||
)?;
|
||||
|
||||
let datastore = DataStore::lookup_datastore(&store, Some(Operation::Write))?;
|
||||
let snapshot = datastore.backup_dir(backup_dir)?;
|
||||
let snapshot = datastore.backup_dir(backup_ns, backup_dir)?;
|
||||
|
||||
check_priv_or_backup_owner(
|
||||
&datastore,
|
||||
snapshot.backup_ns(),
|
||||
snapshot.as_ref(),
|
||||
&auth_id,
|
||||
PRIV_DATASTORE_MODIFY,
|
||||
)?;
|
||||
|
||||
datastore.remove_backup_dir(snapshot.as_ref(), false)?;
|
||||
snapshot.destroy(false)?;
|
||||
|
||||
Ok(Value::Null)
|
||||
}
|
||||
@ -549,7 +585,7 @@ pub fn list_snapshots(
|
||||
};
|
||||
|
||||
groups.iter().try_fold(Vec::new(), |mut snapshots, group| {
|
||||
let owner = match datastore.get_owner(group.as_ref()) {
|
||||
let owner = match group.get_owner() {
|
||||
Ok(auth_id) => auth_id,
|
||||
Err(err) => {
|
||||
eprintln!(
|
||||
@ -583,7 +619,8 @@ fn get_snapshots_count(
|
||||
store
|
||||
.iter_backup_groups_ok(Default::default())? // FIXME: Recurse!
|
||||
.filter(|group| {
|
||||
let owner = match store.get_owner(group.as_ref()) {
|
||||
// FIXME: namespace:
|
||||
let owner = match store.get_owner(&BackupNamespace::root(), group.as_ref()) {
|
||||
Ok(owner) => owner,
|
||||
Err(err) => {
|
||||
let id = store.name();
|
||||
@ -763,7 +800,13 @@ pub fn verify(
|
||||
let dir =
|
||||
datastore.backup_dir_from_parts(backup_ns, backup_type, backup_id, backup_time)?;
|
||||
|
||||
check_priv_or_backup_owner(&datastore, dir.as_ref(), &auth_id, PRIV_DATASTORE_VERIFY)?;
|
||||
check_priv_or_backup_owner(
|
||||
&datastore,
|
||||
dir.backup_ns(),
|
||||
dir.as_ref(),
|
||||
&auth_id,
|
||||
PRIV_DATASTORE_VERIFY,
|
||||
)?;
|
||||
|
||||
backup_dir = Some(dir);
|
||||
worker_type = "verify_snapshot";
|
||||
@ -776,11 +819,17 @@ pub fn verify(
|
||||
backup_type,
|
||||
backup_id
|
||||
);
|
||||
let group = pbs_api_types::BackupGroup::from((backup_ns, backup_type, backup_id));
|
||||
let group = pbs_api_types::BackupGroup::from((backup_type, backup_id));
|
||||
|
||||
check_priv_or_backup_owner(&datastore, &group, &auth_id, PRIV_DATASTORE_VERIFY)?;
|
||||
check_priv_or_backup_owner(
|
||||
&datastore,
|
||||
&backup_ns,
|
||||
&group,
|
||||
&auth_id,
|
||||
PRIV_DATASTORE_VERIFY,
|
||||
)?;
|
||||
|
||||
backup_group = Some(datastore.backup_group(group));
|
||||
backup_group = Some(datastore.backup_group(backup_ns, group));
|
||||
worker_type = "verify_group";
|
||||
}
|
||||
(None, None, None) => {
|
||||
@ -851,6 +900,10 @@ pub fn verify(
|
||||
#[api(
|
||||
input: {
|
||||
properties: {
|
||||
"backup-ns": {
|
||||
type: BackupNamespace,
|
||||
optional: true,
|
||||
},
|
||||
group: {
|
||||
type: pbs_api_types::BackupGroup,
|
||||
flatten: true,
|
||||
@ -879,6 +932,7 @@ pub fn verify(
|
||||
)]
|
||||
/// Prune a group on the datastore
|
||||
pub fn prune(
|
||||
backup_ns: Option<BackupNamespace>,
|
||||
group: pbs_api_types::BackupGroup,
|
||||
dry_run: bool,
|
||||
prune_options: PruneOptions,
|
||||
@ -887,18 +941,27 @@ pub fn prune(
|
||||
rpcenv: &mut dyn RpcEnvironment,
|
||||
) -> Result<Value, Error> {
|
||||
let auth_id: Authid = rpcenv.get_auth_id().unwrap().parse()?;
|
||||
|
||||
let backup_ns = backup_ns.unwrap_or_default();
|
||||
|
||||
get_ns_privs_checked(
|
||||
&store,
|
||||
&group.ns,
|
||||
&backup_ns,
|
||||
&auth_id,
|
||||
PRIV_DATASTORE_MODIFY | PRIV_DATASTORE_PRUNE,
|
||||
)?;
|
||||
|
||||
let datastore = DataStore::lookup_datastore(&store, Some(Operation::Write))?;
|
||||
|
||||
let group = datastore.backup_group(group);
|
||||
let group = datastore.backup_group(backup_ns, group);
|
||||
|
||||
check_priv_or_backup_owner(&datastore, group.as_ref(), &auth_id, PRIV_DATASTORE_MODIFY)?;
|
||||
check_priv_or_backup_owner(
|
||||
&datastore,
|
||||
group.backup_ns(),
|
||||
group.as_ref(),
|
||||
&auth_id,
|
||||
PRIV_DATASTORE_MODIFY,
|
||||
)?;
|
||||
|
||||
let worker_id = format!("{}:{}", store, group);
|
||||
|
||||
@ -962,20 +1025,16 @@ pub fn prune(
|
||||
|
||||
task_log!(worker, "{}", msg);
|
||||
|
||||
let mut result = json!({
|
||||
prune_result.push(json!({
|
||||
"backup-type": group.ty,
|
||||
"backup-id": group.id,
|
||||
"backup-time": backup_time,
|
||||
"keep": keep,
|
||||
"protected": mark.protected(),
|
||||
});
|
||||
if !group.ns.is_root() {
|
||||
result["backup-ns"] = serde_json::to_value(&group.ns)?;
|
||||
}
|
||||
prune_result.push(result);
|
||||
}));
|
||||
|
||||
if !(dry_run || keep) {
|
||||
if let Err(err) = datastore.remove_backup_dir(info.backup_dir.as_ref(), false) {
|
||||
if let Err(err) = info.backup_dir.destroy(false) {
|
||||
task_warn!(
|
||||
worker,
|
||||
"failed to remove dir {:?}: {}",
|
||||
@ -1231,20 +1290,22 @@ pub fn download_file(
|
||||
async move {
|
||||
let auth_id: Authid = rpcenv.get_auth_id().unwrap().parse()?;
|
||||
let store = required_string_param(¶m, "store")?;
|
||||
let backup_ns = optional_ns_param(¶m)?;
|
||||
let backup_dir: pbs_api_types::BackupDir = Deserialize::deserialize(¶m)?;
|
||||
get_ns_privs_checked(
|
||||
&store,
|
||||
&backup_dir.group.ns,
|
||||
&backup_ns,
|
||||
&auth_id,
|
||||
PRIV_DATASTORE_READ | PRIV_DATASTORE_BACKUP,
|
||||
)?;
|
||||
let datastore = DataStore::lookup_datastore(store, Some(Operation::Read))?;
|
||||
let backup_dir = datastore.backup_dir(backup_dir)?;
|
||||
let backup_dir = datastore.backup_dir(backup_ns, backup_dir)?;
|
||||
|
||||
let file_name = required_string_param(¶m, "file-name")?.to_owned();
|
||||
|
||||
check_priv_or_backup_owner(
|
||||
&datastore,
|
||||
backup_dir.backup_ns(),
|
||||
backup_dir.as_ref(),
|
||||
&auth_id,
|
||||
PRIV_DATASTORE_READ,
|
||||
@ -1315,20 +1376,22 @@ pub fn download_file_decoded(
|
||||
async move {
|
||||
let auth_id: Authid = rpcenv.get_auth_id().unwrap().parse()?;
|
||||
let store = required_string_param(¶m, "store")?;
|
||||
let backup_ns = optional_ns_param(¶m)?;
|
||||
let backup_dir: pbs_api_types::BackupDir = Deserialize::deserialize(¶m)?;
|
||||
get_ns_privs_checked(
|
||||
&store,
|
||||
&backup_dir.group.ns,
|
||||
&backup_ns,
|
||||
&auth_id,
|
||||
PRIV_DATASTORE_READ | PRIV_DATASTORE_BACKUP,
|
||||
)?;
|
||||
let datastore = DataStore::lookup_datastore(store, Some(Operation::Read))?;
|
||||
let backup_dir = datastore.backup_dir(backup_dir)?;
|
||||
let backup_dir = datastore.backup_dir(backup_ns, backup_dir)?;
|
||||
|
||||
let file_name = required_string_param(¶m, "file-name")?.to_owned();
|
||||
|
||||
check_priv_or_backup_owner(
|
||||
&datastore,
|
||||
backup_dir.backup_ns(),
|
||||
backup_dir.as_ref(),
|
||||
&auth_id,
|
||||
PRIV_DATASTORE_READ,
|
||||
@ -1445,23 +1508,18 @@ pub fn upload_backup_log(
|
||||
async move {
|
||||
let auth_id: Authid = rpcenv.get_auth_id().unwrap().parse()?;
|
||||
let store = required_string_param(¶m, "store")?;
|
||||
let backup_ns = optional_ns_param(¶m)?;
|
||||
let backup_dir: pbs_api_types::BackupDir = Deserialize::deserialize(¶m)?;
|
||||
get_ns_privs_checked(
|
||||
&store,
|
||||
&backup_dir.group.ns,
|
||||
&auth_id,
|
||||
PRIV_DATASTORE_BACKUP,
|
||||
)?;
|
||||
get_ns_privs_checked(&store, &backup_ns, &auth_id, PRIV_DATASTORE_BACKUP)?;
|
||||
let datastore = DataStore::lookup_datastore(store, Some(Operation::Write))?;
|
||||
let backup_dir = datastore.backup_dir(backup_dir)?;
|
||||
let backup_dir = datastore.backup_dir(backup_ns, backup_dir)?;
|
||||
|
||||
let file_name = CLIENT_LOG_BLOB_NAME;
|
||||
|
||||
let owner = datastore.get_owner(backup_dir.as_ref())?;
|
||||
let owner = backup_dir.get_owner()?;
|
||||
check_backup_owner(&owner, &auth_id)?;
|
||||
|
||||
let mut path = datastore.base_path();
|
||||
path.push(backup_dir.relative_path());
|
||||
let mut path = backup_dir.full_path();
|
||||
path.push(&file_name);
|
||||
|
||||
if path.exists() {
|
||||
@ -1493,6 +1551,10 @@ pub fn upload_backup_log(
|
||||
input: {
|
||||
properties: {
|
||||
store: { schema: DATASTORE_SCHEMA },
|
||||
"backup-ns": {
|
||||
type: BackupNamespace,
|
||||
optional: true,
|
||||
},
|
||||
backup_dir: {
|
||||
type: pbs_api_types::BackupDir,
|
||||
flatten: true,
|
||||
@ -1512,23 +1574,26 @@ pub fn upload_backup_log(
|
||||
/// Get the entries of the given path of the catalog
|
||||
pub fn catalog(
|
||||
store: String,
|
||||
backup_ns: Option<BackupNamespace>,
|
||||
backup_dir: pbs_api_types::BackupDir,
|
||||
filepath: String,
|
||||
rpcenv: &mut dyn RpcEnvironment,
|
||||
) -> Result<Vec<ArchiveEntry>, Error> {
|
||||
let auth_id: Authid = rpcenv.get_auth_id().unwrap().parse()?;
|
||||
let backup_ns = backup_ns.unwrap_or_default();
|
||||
get_ns_privs_checked(
|
||||
&store,
|
||||
&backup_dir.group.ns,
|
||||
&backup_ns,
|
||||
&auth_id,
|
||||
PRIV_DATASTORE_READ | PRIV_DATASTORE_BACKUP,
|
||||
)?;
|
||||
|
||||
let datastore = DataStore::lookup_datastore(&store, Some(Operation::Read))?;
|
||||
let backup_dir = datastore.backup_dir(backup_dir)?;
|
||||
let backup_dir = datastore.backup_dir(backup_ns, backup_dir)?;
|
||||
|
||||
check_priv_or_backup_owner(
|
||||
&datastore,
|
||||
backup_dir.backup_ns(),
|
||||
backup_dir.as_ref(),
|
||||
&auth_id,
|
||||
PRIV_DATASTORE_READ,
|
||||
@ -1600,15 +1665,16 @@ pub fn pxar_file_download(
|
||||
async move {
|
||||
let auth_id: Authid = rpcenv.get_auth_id().unwrap().parse()?;
|
||||
let store = required_string_param(¶m, "store")?;
|
||||
let backup_ns = optional_ns_param(¶m)?;
|
||||
let backup_dir: pbs_api_types::BackupDir = Deserialize::deserialize(¶m)?;
|
||||
get_ns_privs_checked(
|
||||
&store,
|
||||
&backup_dir.group.ns,
|
||||
&backup_ns,
|
||||
&auth_id,
|
||||
PRIV_DATASTORE_READ | PRIV_DATASTORE_BACKUP,
|
||||
)?;
|
||||
let datastore = DataStore::lookup_datastore(&store, Some(Operation::Read))?;
|
||||
let backup_dir = datastore.backup_dir(backup_dir)?;
|
||||
let backup_dir = datastore.backup_dir(backup_ns, backup_dir)?;
|
||||
|
||||
let filepath = required_string_param(¶m, "filepath")?.to_owned();
|
||||
|
||||
@ -1616,6 +1682,7 @@ pub fn pxar_file_download(
|
||||
|
||||
check_priv_or_backup_owner(
|
||||
&datastore,
|
||||
backup_dir.backup_ns(),
|
||||
backup_dir.as_ref(),
|
||||
&auth_id,
|
||||
PRIV_DATASTORE_READ,
|
||||
@ -1786,6 +1853,10 @@ pub fn get_active_operations(store: String, _param: Value) -> Result<Value, Erro
|
||||
input: {
|
||||
properties: {
|
||||
store: { schema: DATASTORE_SCHEMA },
|
||||
"backup-ns": {
|
||||
type: BackupNamespace,
|
||||
optional: true,
|
||||
},
|
||||
backup_group: {
|
||||
type: pbs_api_types::BackupGroup,
|
||||
flatten: true,
|
||||
@ -1801,21 +1872,29 @@ pub fn get_active_operations(store: String, _param: Value) -> Result<Value, Erro
|
||||
/// Get "notes" for a backup group
|
||||
pub fn get_group_notes(
|
||||
store: String,
|
||||
backup_ns: Option<BackupNamespace>,
|
||||
backup_group: pbs_api_types::BackupGroup,
|
||||
rpcenv: &mut dyn RpcEnvironment,
|
||||
) -> Result<String, Error> {
|
||||
let auth_id: Authid = rpcenv.get_auth_id().unwrap().parse()?;
|
||||
let backup_ns = backup_ns.unwrap_or_default();
|
||||
get_ns_privs_checked(
|
||||
&store,
|
||||
&backup_group.ns,
|
||||
&backup_ns,
|
||||
&auth_id,
|
||||
PRIV_DATASTORE_AUDIT | PRIV_DATASTORE_BACKUP,
|
||||
)?;
|
||||
let datastore = DataStore::lookup_datastore(&store, Some(Operation::Read))?;
|
||||
|
||||
check_priv_or_backup_owner(&datastore, &backup_group, &auth_id, PRIV_DATASTORE_AUDIT)?;
|
||||
check_priv_or_backup_owner(
|
||||
&datastore,
|
||||
&backup_ns,
|
||||
&backup_group,
|
||||
&auth_id,
|
||||
PRIV_DATASTORE_AUDIT,
|
||||
)?;
|
||||
|
||||
let note_path = get_group_note_path(&datastore, &backup_group);
|
||||
let note_path = get_group_note_path(&datastore, &backup_ns, &backup_group);
|
||||
Ok(file_read_optional_string(note_path)?.unwrap_or_else(|| "".to_owned()))
|
||||
}
|
||||
|
||||
@ -1823,6 +1902,10 @@ pub fn get_group_notes(
|
||||
input: {
|
||||
properties: {
|
||||
store: { schema: DATASTORE_SCHEMA },
|
||||
"backup-ns": {
|
||||
type: BackupNamespace,
|
||||
optional: true,
|
||||
},
|
||||
backup_group: {
|
||||
type: pbs_api_types::BackupGroup,
|
||||
flatten: true,
|
||||
@ -1841,22 +1924,30 @@ pub fn get_group_notes(
|
||||
/// Set "notes" for a backup group
|
||||
pub fn set_group_notes(
|
||||
store: String,
|
||||
backup_ns: Option<BackupNamespace>,
|
||||
backup_group: pbs_api_types::BackupGroup,
|
||||
notes: String,
|
||||
rpcenv: &mut dyn RpcEnvironment,
|
||||
) -> Result<(), Error> {
|
||||
let auth_id: Authid = rpcenv.get_auth_id().unwrap().parse()?;
|
||||
let backup_ns = backup_ns.unwrap_or_default();
|
||||
get_ns_privs_checked(
|
||||
&store,
|
||||
&backup_group.ns,
|
||||
&backup_ns,
|
||||
&auth_id,
|
||||
PRIV_DATASTORE_MODIFY | PRIV_DATASTORE_BACKUP,
|
||||
)?;
|
||||
let datastore = DataStore::lookup_datastore(&store, Some(Operation::Write))?;
|
||||
|
||||
check_priv_or_backup_owner(&datastore, &backup_group, &auth_id, PRIV_DATASTORE_MODIFY)?;
|
||||
check_priv_or_backup_owner(
|
||||
&datastore,
|
||||
&backup_ns,
|
||||
&backup_group,
|
||||
&auth_id,
|
||||
PRIV_DATASTORE_MODIFY,
|
||||
)?;
|
||||
|
||||
let note_path = get_group_note_path(&datastore, &backup_group);
|
||||
let note_path = get_group_note_path(&datastore, &backup_ns, &backup_group);
|
||||
replace_file(note_path, notes.as_bytes(), CreateOptions::new(), false)?;
|
||||
|
||||
Ok(())
|
||||
@ -1866,6 +1957,10 @@ pub fn set_group_notes(
|
||||
input: {
|
||||
properties: {
|
||||
store: { schema: DATASTORE_SCHEMA },
|
||||
"backup-ns": {
|
||||
type: BackupNamespace,
|
||||
optional: true,
|
||||
},
|
||||
backup_dir: {
|
||||
type: pbs_api_types::BackupDir,
|
||||
flatten: true,
|
||||
@ -1881,28 +1976,31 @@ pub fn set_group_notes(
|
||||
/// Get "notes" for a specific backup
|
||||
pub fn get_notes(
|
||||
store: String,
|
||||
backup_ns: Option<BackupNamespace>,
|
||||
backup_dir: pbs_api_types::BackupDir,
|
||||
rpcenv: &mut dyn RpcEnvironment,
|
||||
) -> Result<String, Error> {
|
||||
let auth_id: Authid = rpcenv.get_auth_id().unwrap().parse()?;
|
||||
let backup_ns = backup_ns.unwrap_or_default();
|
||||
get_ns_privs_checked(
|
||||
&store,
|
||||
&backup_dir.group.ns,
|
||||
&backup_ns,
|
||||
&auth_id,
|
||||
PRIV_DATASTORE_AUDIT | PRIV_DATASTORE_BACKUP,
|
||||
)?;
|
||||
let datastore = DataStore::lookup_datastore(&store, Some(Operation::Read))?;
|
||||
|
||||
let backup_dir = datastore.backup_dir(backup_dir)?;
|
||||
let backup_dir = datastore.backup_dir(backup_ns, backup_dir)?;
|
||||
|
||||
check_priv_or_backup_owner(
|
||||
&datastore,
|
||||
backup_dir.backup_ns(),
|
||||
backup_dir.as_ref(),
|
||||
&auth_id,
|
||||
PRIV_DATASTORE_AUDIT,
|
||||
)?;
|
||||
|
||||
let (manifest, _) = datastore.load_manifest(&backup_dir)?;
|
||||
let (manifest, _) = backup_dir.load_manifest()?;
|
||||
|
||||
let notes = manifest.unprotected["notes"].as_str().unwrap_or("");
|
||||
|
||||
@ -1913,6 +2011,10 @@ pub fn get_notes(
|
||||
input: {
|
||||
properties: {
|
||||
store: { schema: DATASTORE_SCHEMA },
|
||||
"backup-ns": {
|
||||
type: BackupNamespace,
|
||||
optional: true,
|
||||
},
|
||||
backup_dir: {
|
||||
type: pbs_api_types::BackupDir,
|
||||
flatten: true,
|
||||
@ -1931,30 +2033,33 @@ pub fn get_notes(
|
||||
/// Set "notes" for a specific backup
|
||||
pub fn set_notes(
|
||||
store: String,
|
||||
backup_ns: Option<BackupNamespace>,
|
||||
backup_dir: pbs_api_types::BackupDir,
|
||||
notes: String,
|
||||
rpcenv: &mut dyn RpcEnvironment,
|
||||
) -> Result<(), Error> {
|
||||
let auth_id: Authid = rpcenv.get_auth_id().unwrap().parse()?;
|
||||
let backup_ns = backup_ns.unwrap_or_default();
|
||||
get_ns_privs_checked(
|
||||
&store,
|
||||
&backup_dir.group.ns,
|
||||
&backup_ns,
|
||||
&auth_id,
|
||||
PRIV_DATASTORE_MODIFY | PRIV_DATASTORE_BACKUP,
|
||||
)?;
|
||||
let datastore = DataStore::lookup_datastore(&store, Some(Operation::Write))?;
|
||||
|
||||
let backup_dir = datastore.backup_dir(backup_dir)?;
|
||||
let backup_dir = datastore.backup_dir(backup_ns, backup_dir)?;
|
||||
|
||||
check_priv_or_backup_owner(
|
||||
&datastore,
|
||||
backup_dir.backup_ns(),
|
||||
backup_dir.as_ref(),
|
||||
&auth_id,
|
||||
PRIV_DATASTORE_MODIFY,
|
||||
)?;
|
||||
|
||||
datastore
|
||||
.update_manifest(&backup_dir, |manifest| {
|
||||
backup_dir
|
||||
.update_manifest(|manifest| {
|
||||
manifest.unprotected["notes"] = notes.into();
|
||||
})
|
||||
.map_err(|err| format_err!("unable to update manifest blob - {}", err))?;
|
||||
@ -1966,6 +2071,10 @@ pub fn set_notes(
|
||||
input: {
|
||||
properties: {
|
||||
store: { schema: DATASTORE_SCHEMA },
|
||||
"backup-ns": {
|
||||
type: BackupNamespace,
|
||||
optional: true,
|
||||
},
|
||||
backup_dir: {
|
||||
type: pbs_api_types::BackupDir,
|
||||
flatten: true,
|
||||
@ -1981,22 +2090,25 @@ pub fn set_notes(
|
||||
/// Query protection for a specific backup
|
||||
pub fn get_protection(
|
||||
store: String,
|
||||
backup_ns: Option<BackupNamespace>,
|
||||
backup_dir: pbs_api_types::BackupDir,
|
||||
rpcenv: &mut dyn RpcEnvironment,
|
||||
) -> Result<bool, Error> {
|
||||
let auth_id: Authid = rpcenv.get_auth_id().unwrap().parse()?;
|
||||
let backup_ns = backup_ns.unwrap_or_default();
|
||||
get_ns_privs_checked(
|
||||
&store,
|
||||
&backup_dir.group.ns,
|
||||
&backup_ns,
|
||||
&auth_id,
|
||||
PRIV_DATASTORE_AUDIT | PRIV_DATASTORE_BACKUP,
|
||||
)?;
|
||||
let datastore = DataStore::lookup_datastore(&store, Some(Operation::Read))?;
|
||||
|
||||
let backup_dir = datastore.backup_dir(backup_dir)?;
|
||||
let backup_dir = datastore.backup_dir(backup_ns, backup_dir)?;
|
||||
|
||||
check_priv_or_backup_owner(
|
||||
&datastore,
|
||||
backup_dir.backup_ns(),
|
||||
backup_dir.as_ref(),
|
||||
&auth_id,
|
||||
PRIV_DATASTORE_AUDIT,
|
||||
@ -2009,6 +2121,10 @@ pub fn get_protection(
|
||||
input: {
|
||||
properties: {
|
||||
store: { schema: DATASTORE_SCHEMA },
|
||||
"backup-ns": {
|
||||
type: BackupNamespace,
|
||||
optional: true,
|
||||
},
|
||||
backup_dir: {
|
||||
type: pbs_api_types::BackupDir,
|
||||
flatten: true,
|
||||
@ -2027,23 +2143,26 @@ pub fn get_protection(
|
||||
/// En- or disable protection for a specific backup
|
||||
pub fn set_protection(
|
||||
store: String,
|
||||
backup_ns: Option<BackupNamespace>,
|
||||
backup_dir: pbs_api_types::BackupDir,
|
||||
protected: bool,
|
||||
rpcenv: &mut dyn RpcEnvironment,
|
||||
) -> Result<(), Error> {
|
||||
let auth_id: Authid = rpcenv.get_auth_id().unwrap().parse()?;
|
||||
let backup_ns = backup_ns.unwrap_or_default();
|
||||
get_ns_privs_checked(
|
||||
&store,
|
||||
&backup_dir.group.ns,
|
||||
&backup_ns,
|
||||
&auth_id,
|
||||
PRIV_DATASTORE_MODIFY | PRIV_DATASTORE_BACKUP,
|
||||
)?;
|
||||
let datastore = DataStore::lookup_datastore(&store, Some(Operation::Write))?;
|
||||
|
||||
let backup_dir = datastore.backup_dir(backup_dir)?;
|
||||
let backup_dir = datastore.backup_dir(backup_ns, backup_dir)?;
|
||||
|
||||
check_priv_or_backup_owner(
|
||||
&datastore,
|
||||
backup_dir.backup_ns(),
|
||||
backup_dir.as_ref(),
|
||||
&auth_id,
|
||||
PRIV_DATASTORE_MODIFY,
|
||||
@ -2056,6 +2175,10 @@ pub fn set_protection(
|
||||
input: {
|
||||
properties: {
|
||||
store: { schema: DATASTORE_SCHEMA },
|
||||
"backup-ns": {
|
||||
type: BackupNamespace,
|
||||
optional: true,
|
||||
},
|
||||
backup_group: {
|
||||
type: pbs_api_types::BackupGroup,
|
||||
flatten: true,
|
||||
@ -2074,6 +2197,7 @@ pub fn set_protection(
|
||||
/// Change owner of a backup group
|
||||
pub fn set_backup_owner(
|
||||
store: String,
|
||||
backup_ns: Option<BackupNamespace>,
|
||||
backup_group: pbs_api_types::BackupGroup,
|
||||
new_owner: Authid,
|
||||
rpcenv: &mut dyn RpcEnvironment,
|
||||
@ -2081,13 +2205,14 @@ pub fn set_backup_owner(
|
||||
let datastore = DataStore::lookup_datastore(&store, Some(Operation::Write))?;
|
||||
|
||||
let auth_id: Authid = rpcenv.get_auth_id().unwrap().parse()?;
|
||||
let privs = get_ns_privs(&store, &backup_group.ns, &auth_id)?;
|
||||
let backup_group = datastore.backup_group(backup_group);
|
||||
let backup_ns = backup_ns.unwrap_or_default();
|
||||
let privs = get_ns_privs(&store, &backup_ns, &auth_id)?;
|
||||
let backup_group = datastore.backup_group(backup_ns, backup_group);
|
||||
|
||||
let allowed = if (privs & PRIV_DATASTORE_MODIFY) != 0 {
|
||||
true // High-privilege user/token
|
||||
} else if (privs & PRIV_DATASTORE_BACKUP) != 0 {
|
||||
let owner = datastore.get_owner(backup_group.as_ref())?;
|
||||
let owner = backup_group.get_owner()?;
|
||||
|
||||
match (owner.is_token(), new_owner.is_token()) {
|
||||
(true, true) => {
|
||||
@ -2137,7 +2262,7 @@ pub fn set_backup_owner(
|
||||
);
|
||||
}
|
||||
|
||||
datastore.set_owner(backup_group.as_ref(), &new_owner, true)?;
|
||||
backup_group.set_owner(&new_owner, true)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
@ -614,7 +614,7 @@ impl BackupEnvironment {
|
||||
.map_err(|err| format_err!("unable to update manifest blob - {}", err))?;
|
||||
|
||||
if let Some(base) = &self.last_backup {
|
||||
let path = self.datastore.snapshot_path(base.backup_dir.as_ref());
|
||||
let path = base.backup_dir.full_path();
|
||||
if !path.exists() {
|
||||
bail!(
|
||||
"base snapshot {} was removed during backup, cannot finish as chunks might be missing",
|
||||
@ -710,8 +710,11 @@ impl BackupEnvironment {
|
||||
let mut state = self.state.lock().unwrap();
|
||||
state.finished = true;
|
||||
|
||||
self.datastore
|
||||
.remove_backup_dir(self.backup_dir.as_ref(), true)?;
|
||||
self.datastore.remove_backup_dir(
|
||||
self.backup_dir.backup_ns(),
|
||||
self.backup_dir.as_ref(),
|
||||
true,
|
||||
)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
@ -17,9 +17,9 @@ use proxmox_schema::*;
|
||||
use proxmox_sys::sortable;
|
||||
|
||||
use pbs_api_types::{
|
||||
Authid, BackupType, Operation, SnapshotVerifyState, VerifyState, BACKUP_ARCHIVE_NAME_SCHEMA,
|
||||
BACKUP_ID_SCHEMA, BACKUP_NAMESPACE_SCHEMA, BACKUP_TIME_SCHEMA, BACKUP_TYPE_SCHEMA,
|
||||
CHUNK_DIGEST_SCHEMA, DATASTORE_SCHEMA, PRIV_DATASTORE_BACKUP,
|
||||
Authid, BackupNamespace, BackupType, Operation, SnapshotVerifyState, VerifyState,
|
||||
BACKUP_ARCHIVE_NAME_SCHEMA, BACKUP_ID_SCHEMA, BACKUP_NAMESPACE_SCHEMA, BACKUP_TIME_SCHEMA,
|
||||
BACKUP_TYPE_SCHEMA, CHUNK_DIGEST_SCHEMA, DATASTORE_SCHEMA, PRIV_DATASTORE_BACKUP,
|
||||
};
|
||||
use pbs_config::CachedUserInfo;
|
||||
use pbs_datastore::index::IndexFile;
|
||||
@ -58,6 +58,14 @@ pub const API_METHOD_UPGRADE_BACKUP: ApiMethod = ApiMethod::new(
|
||||
&Permission::Anybody
|
||||
);
|
||||
|
||||
pub(crate) fn optional_ns_param(param: &Value) -> Result<BackupNamespace, Error> {
|
||||
match param.get("backup-ns") {
|
||||
Some(Value::String(ns)) => ns.parse(),
|
||||
None => Ok(BackupNamespace::root()),
|
||||
_ => bail!("invalid backup-ns parameter"),
|
||||
}
|
||||
}
|
||||
|
||||
fn upgrade_to_backup_protocol(
|
||||
parts: Parts,
|
||||
req_body: Body,
|
||||
@ -72,9 +80,9 @@ fn upgrade_to_backup_protocol(
|
||||
let auth_id: Authid = rpcenv.get_auth_id().unwrap().parse()?;
|
||||
|
||||
let store = required_string_param(¶m, "store")?.to_owned();
|
||||
let backup_ns = optional_ns_param(¶m)?;
|
||||
let backup_dir_arg = pbs_api_types::BackupDir::deserialize(¶m)?;
|
||||
|
||||
let backup_ns = &backup_dir_arg.group.ns;
|
||||
let user_info = CachedUserInfo::new()?;
|
||||
|
||||
let privs = if backup_ns.is_root() {
|
||||
@ -105,7 +113,7 @@ fn upgrade_to_backup_protocol(
|
||||
);
|
||||
}
|
||||
|
||||
if !datastore.ns_path(&backup_ns).exists() {
|
||||
if !datastore.namespace_path(&backup_ns).exists() {
|
||||
proxmox_router::http_bail!(NOT_FOUND, "namespace not found");
|
||||
}
|
||||
|
||||
@ -113,7 +121,7 @@ fn upgrade_to_backup_protocol(
|
||||
|
||||
let env_type = rpcenv.env_type();
|
||||
|
||||
let backup_group = datastore.backup_group(backup_dir_arg.group.clone());
|
||||
let backup_group = datastore.backup_group(backup_ns, backup_dir_arg.group.clone());
|
||||
|
||||
let worker_type = if backup_group.backup_type() == BackupType::Host
|
||||
&& backup_group.backup_id() == "benchmark"
|
||||
@ -130,8 +138,11 @@ fn upgrade_to_backup_protocol(
|
||||
};
|
||||
|
||||
// lock backup group to only allow one backup per group at a time
|
||||
let (owner, _group_guard) =
|
||||
datastore.create_locked_backup_group(backup_group.as_ref(), &auth_id)?;
|
||||
let (owner, _group_guard) = datastore.create_locked_backup_group(
|
||||
backup_group.backup_ns(),
|
||||
backup_group.as_ref(),
|
||||
&auth_id,
|
||||
)?;
|
||||
|
||||
// permission check
|
||||
let correct_owner =
|
||||
@ -169,7 +180,7 @@ fn upgrade_to_backup_protocol(
|
||||
}
|
||||
|
||||
// lock last snapshot to prevent forgetting/pruning it during backup
|
||||
let full_path = datastore.snapshot_path(last.backup_dir.as_ref());
|
||||
let full_path = last.backup_dir.full_path();
|
||||
Some(lock_dir_noblock_shared(
|
||||
&full_path,
|
||||
"snapshot",
|
||||
@ -179,7 +190,8 @@ fn upgrade_to_backup_protocol(
|
||||
None
|
||||
};
|
||||
|
||||
let (path, is_new, snap_guard) = datastore.create_locked_backup_dir(backup_dir.as_ref())?;
|
||||
let (path, is_new, snap_guard) =
|
||||
datastore.create_locked_backup_dir(backup_dir.backup_ns(), backup_dir.as_ref())?;
|
||||
if !is_new {
|
||||
bail!("backup directory already exists.");
|
||||
}
|
||||
@ -818,7 +830,7 @@ fn download_previous(
|
||||
None => bail!("no valid previous backup"),
|
||||
};
|
||||
|
||||
let mut path = env.datastore.snapshot_path(last_backup.backup_dir.as_ref());
|
||||
let mut path = last_backup.backup_dir.full_path();
|
||||
path.push(&archive_name);
|
||||
|
||||
{
|
||||
|
@ -29,6 +29,7 @@ use pbs_tools::json::required_string_param;
|
||||
use proxmox_rest_server::{H2Service, WorkerTask};
|
||||
use proxmox_sys::fs::lock_dir_noblock_shared;
|
||||
|
||||
use crate::api2::backup::optional_ns_param;
|
||||
use crate::api2::helpers;
|
||||
|
||||
mod environment;
|
||||
@ -91,6 +92,7 @@ fn upgrade_to_backup_reader_protocol(
|
||||
|
||||
let datastore = DataStore::lookup_datastore(&store, Some(Operation::Read))?;
|
||||
|
||||
let backup_ns = optional_ns_param(¶m)?;
|
||||
let backup_dir = pbs_api_types::BackupDir::deserialize(¶m)?;
|
||||
|
||||
let protocols = parts
|
||||
@ -112,9 +114,9 @@ fn upgrade_to_backup_reader_protocol(
|
||||
|
||||
let env_type = rpcenv.env_type();
|
||||
|
||||
let backup_dir = datastore.backup_dir(backup_dir)?;
|
||||
let backup_dir = datastore.backup_dir(backup_ns, backup_dir)?;
|
||||
if !priv_read {
|
||||
let owner = datastore.get_owner(backup_dir.as_ref())?;
|
||||
let owner = backup_dir.get_owner()?;
|
||||
let correct_owner = owner == auth_id
|
||||
|| (owner.is_token() && Authid::from(owner.user().clone()) == auth_id);
|
||||
if !correct_owner {
|
||||
|
@ -17,7 +17,7 @@ use pbs_api_types::{
|
||||
|
||||
use pbs_config::CachedUserInfo;
|
||||
use pbs_datastore::backup_info::{BackupDir, BackupGroup, BackupInfo};
|
||||
use pbs_datastore::{DataStore, SnapshotReader, StoreProgress};
|
||||
use pbs_datastore::{DataStore, StoreProgress};
|
||||
use proxmox_rest_server::WorkerTask;
|
||||
|
||||
use crate::{
|
||||
@ -577,7 +577,7 @@ pub fn backup_snapshot(
|
||||
) -> Result<bool, Error> {
|
||||
task_log!(worker, "backup snapshot {}", snapshot);
|
||||
|
||||
let snapshot_reader = match SnapshotReader::new(datastore.clone(), (&snapshot).into()) {
|
||||
let snapshot_reader = match snapshot.locked_reader() {
|
||||
Ok(reader) => reader,
|
||||
Err(err) => {
|
||||
// ignore missing snapshots and continue
|
||||
|
@ -17,9 +17,9 @@ use proxmox_sys::{task_log, task_warn, WorkerTaskContext};
|
||||
use proxmox_uuid::Uuid;
|
||||
|
||||
use pbs_api_types::{
|
||||
Authid, CryptMode, Operation, Userid, DATASTORE_MAP_ARRAY_SCHEMA, DATASTORE_MAP_LIST_SCHEMA,
|
||||
DRIVE_NAME_SCHEMA, PRIV_DATASTORE_BACKUP, PRIV_DATASTORE_MODIFY, PRIV_TAPE_READ,
|
||||
TAPE_RESTORE_SNAPSHOT_SCHEMA, UPID_SCHEMA,
|
||||
Authid, BackupNamespace, CryptMode, Operation, Userid, DATASTORE_MAP_ARRAY_SCHEMA,
|
||||
DATASTORE_MAP_LIST_SCHEMA, DRIVE_NAME_SCHEMA, PRIV_DATASTORE_BACKUP, PRIV_DATASTORE_MODIFY,
|
||||
PRIV_TAPE_READ, TAPE_RESTORE_SNAPSHOT_SCHEMA, UPID_SCHEMA,
|
||||
};
|
||||
use pbs_config::CachedUserInfo;
|
||||
use pbs_datastore::dynamic_index::DynamicIndexReader;
|
||||
@ -401,6 +401,10 @@ fn restore_list_worker(
|
||||
restore_owner: &Authid,
|
||||
email: Option<String>,
|
||||
) -> Result<(), Error> {
|
||||
// FIXME: Namespace needs to come from somewhere, `snapshots` is just a snapshot string list
|
||||
// here.
|
||||
let ns = BackupNamespace::root();
|
||||
|
||||
let base_path: PathBuf = format!("{}/{}", RESTORE_TMP_DIR, media_set_uuid).into();
|
||||
std::fs::create_dir_all(&base_path)?;
|
||||
|
||||
@ -430,7 +434,7 @@ fn restore_list_worker(
|
||||
})?;
|
||||
|
||||
let (owner, _group_lock) =
|
||||
datastore.create_locked_backup_group(backup_dir.as_ref(), restore_owner)?;
|
||||
datastore.create_locked_backup_group(&ns, backup_dir.as_ref(), restore_owner)?;
|
||||
if restore_owner != &owner {
|
||||
// only the owner is allowed to create additional snapshots
|
||||
task_warn!(
|
||||
@ -458,7 +462,8 @@ fn restore_list_worker(
|
||||
continue;
|
||||
};
|
||||
|
||||
let (_rel_path, is_new, snap_lock) = datastore.create_locked_backup_dir(&backup_dir)?;
|
||||
let (_rel_path, is_new, snap_lock) =
|
||||
datastore.create_locked_backup_dir(&ns, &backup_dir)?;
|
||||
|
||||
if !is_new {
|
||||
task_log!(
|
||||
@ -586,7 +591,7 @@ fn restore_list_worker(
|
||||
tmp_path.push(&source_datastore);
|
||||
tmp_path.push(snapshot);
|
||||
|
||||
let path = datastore.snapshot_path(&backup_dir);
|
||||
let path = datastore.snapshot_path(&ns, &backup_dir);
|
||||
|
||||
for entry in std::fs::read_dir(tmp_path)? {
|
||||
let entry = entry?;
|
||||
@ -1036,12 +1041,17 @@ fn restore_archive<'a>(
|
||||
snapshot
|
||||
);
|
||||
|
||||
// FIXME: Namespace
|
||||
let backup_ns = BackupNamespace::root();
|
||||
let backup_dir: pbs_api_types::BackupDir = snapshot.parse()?;
|
||||
|
||||
if let Some((store_map, authid)) = target.as_ref() {
|
||||
if let Some(datastore) = store_map.get_datastore(&datastore_name) {
|
||||
let (owner, _group_lock) =
|
||||
datastore.create_locked_backup_group(backup_dir.as_ref(), authid)?;
|
||||
let (owner, _group_lock) = datastore.create_locked_backup_group(
|
||||
&backup_ns,
|
||||
backup_dir.as_ref(),
|
||||
authid,
|
||||
)?;
|
||||
if *authid != &owner {
|
||||
// only the owner is allowed to create additional snapshots
|
||||
bail!(
|
||||
@ -1053,7 +1063,7 @@ fn restore_archive<'a>(
|
||||
}
|
||||
|
||||
let (rel_path, is_new, _snap_lock) =
|
||||
datastore.create_locked_backup_dir(backup_dir.as_ref())?;
|
||||
datastore.create_locked_backup_dir(&backup_ns, backup_dir.as_ref())?;
|
||||
let mut path = datastore.base_path();
|
||||
path.push(rel_path);
|
||||
|
||||
|
@ -8,7 +8,9 @@ use anyhow::{bail, format_err, Error};
|
||||
|
||||
use proxmox_sys::{task_log, WorkerTaskContext};
|
||||
|
||||
use pbs_api_types::{Authid, BackupType, CryptMode, SnapshotVerifyState, VerifyState, UPID};
|
||||
use pbs_api_types::{
|
||||
Authid, BackupNamespace, BackupType, CryptMode, SnapshotVerifyState, VerifyState, UPID,
|
||||
};
|
||||
use pbs_datastore::backup_info::{BackupDir, BackupGroup, BackupInfo};
|
||||
use pbs_datastore::index::IndexFile;
|
||||
use pbs_datastore::manifest::{archive_type, ArchiveType, BackupManifest, FileInfo};
|
||||
@ -324,7 +326,7 @@ pub fn verify_backup_dir(
|
||||
filter: Option<&dyn Fn(&BackupManifest) -> bool>,
|
||||
) -> Result<bool, Error> {
|
||||
let snap_lock = lock_dir_noblock_shared(
|
||||
&verify_worker.datastore.snapshot_path(backup_dir.as_ref()),
|
||||
&backup_dir.full_path(),
|
||||
"snapshot",
|
||||
"locked by another operation",
|
||||
);
|
||||
@ -510,7 +512,13 @@ pub fn verify_all_backups(
|
||||
}
|
||||
|
||||
let filter_by_owner = |group: &BackupGroup| {
|
||||
match (verify_worker.datastore.get_owner(group.as_ref()), &owner) {
|
||||
match (
|
||||
// FIXME: with recursion the namespace needs to come from the iterator...
|
||||
verify_worker
|
||||
.datastore
|
||||
.get_owner(&BackupNamespace::root(), group.as_ref()),
|
||||
&owner,
|
||||
) {
|
||||
(Ok(ref group_owner), Some(owner)) => {
|
||||
group_owner == owner
|
||||
|| (group_owner.is_token()
|
||||
|
@ -45,11 +45,12 @@ pub fn prune_datastore(
|
||||
let has_privs = privs & PRIV_DATASTORE_MODIFY != 0;
|
||||
|
||||
// FIXME: Namespace recursion!
|
||||
for group in datastore.iter_backup_groups(ns)? {
|
||||
for group in datastore.iter_backup_groups(ns.clone())? {
|
||||
let ns_recursed = &ns; // remove_backup_dir might need the inner one
|
||||
let group = group?;
|
||||
let list = group.list_backups()?;
|
||||
|
||||
if !has_privs && !datastore.owns_backup(group.as_ref(), &auth_id)? {
|
||||
if !has_privs && !datastore.owns_backup(&ns_recursed, group.as_ref(), &auth_id)? {
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -75,7 +76,9 @@ pub fn prune_datastore(
|
||||
info.backup_dir.backup_time_string()
|
||||
);
|
||||
if !keep && !dry_run {
|
||||
if let Err(err) = datastore.remove_backup_dir(info.backup_dir.as_ref(), false) {
|
||||
if let Err(err) =
|
||||
datastore.remove_backup_dir(ns_recursed, info.backup_dir.as_ref(), false)
|
||||
{
|
||||
task_warn!(
|
||||
worker,
|
||||
"failed to remove dir {:?}: {}",
|
||||
|
@ -15,7 +15,8 @@ use proxmox_router::HttpError;
|
||||
use proxmox_sys::task_log;
|
||||
|
||||
use pbs_api_types::{
|
||||
Authid, GroupFilter, GroupListItem, Operation, RateLimitConfig, Remote, SnapshotListItem,
|
||||
Authid, BackupNamespace, GroupFilter, GroupListItem, Operation, RateLimitConfig, Remote,
|
||||
SnapshotListItem,
|
||||
};
|
||||
|
||||
use pbs_client::{
|
||||
@ -504,7 +505,9 @@ async fn pull_snapshot_from(
|
||||
snapshot: &pbs_api_types::BackupDir,
|
||||
downloaded_chunks: Arc<Mutex<HashSet<[u8; 32]>>>,
|
||||
) -> Result<(), Error> {
|
||||
let (_path, is_new, _snap_lock) = tgt_store.create_locked_backup_dir(snapshot)?;
|
||||
// FIXME: Namespace support requires source AND target namespace
|
||||
let ns = BackupNamespace::root();
|
||||
let (_path, is_new, _snap_lock) = tgt_store.create_locked_backup_dir(&ns, snapshot)?;
|
||||
|
||||
let snapshot_path = snapshot.to_string();
|
||||
if is_new {
|
||||
@ -519,7 +522,7 @@ async fn pull_snapshot_from(
|
||||
)
|
||||
.await
|
||||
{
|
||||
if let Err(cleanup_err) = tgt_store.remove_backup_dir(snapshot, true) {
|
||||
if let Err(cleanup_err) = tgt_store.remove_backup_dir(&ns, snapshot, true) {
|
||||
task_log!(worker, "cleanup error - {}", cleanup_err);
|
||||
}
|
||||
return Err(err);
|
||||
@ -604,6 +607,9 @@ async fn pull_group(
|
||||
group: &pbs_api_types::BackupGroup,
|
||||
progress: &mut StoreProgress,
|
||||
) -> Result<(), Error> {
|
||||
// FIXME: Namespace support
|
||||
let ns = BackupNamespace::root();
|
||||
|
||||
let path = format!(
|
||||
"api2/json/admin/datastore/{}/snapshots",
|
||||
params.source.store()
|
||||
@ -623,7 +629,7 @@ async fn pull_group(
|
||||
|
||||
let fingerprint = client.fingerprint();
|
||||
|
||||
let last_sync = params.store.last_successful_backup(group)?;
|
||||
let last_sync = params.store.last_successful_backup(&ns, group)?;
|
||||
|
||||
let mut remote_snapshots = std::collections::HashSet::new();
|
||||
|
||||
@ -674,8 +680,15 @@ async fn pull_group(
|
||||
options,
|
||||
)?;
|
||||
|
||||
let reader =
|
||||
BackupReader::start(new_client, None, params.source.store(), &snapshot, true).await?;
|
||||
let reader = BackupReader::start(
|
||||
new_client,
|
||||
None,
|
||||
params.source.store(),
|
||||
&ns,
|
||||
&snapshot,
|
||||
true,
|
||||
)
|
||||
.await?;
|
||||
|
||||
let result = pull_snapshot_from(
|
||||
worker,
|
||||
@ -693,7 +706,7 @@ async fn pull_group(
|
||||
}
|
||||
|
||||
if params.remove_vanished {
|
||||
let group = params.store.backup_group(group.clone());
|
||||
let group = params.store.backup_group(ns.clone(), group.clone());
|
||||
let local_list = group.list_backups()?;
|
||||
for info in local_list {
|
||||
let backup_time = info.backup_dir.backup_time();
|
||||
@ -715,7 +728,7 @@ async fn pull_group(
|
||||
);
|
||||
params
|
||||
.store
|
||||
.remove_backup_dir(info.backup_dir.as_ref(), false)?;
|
||||
.remove_backup_dir(&ns, info.backup_dir.as_ref(), false)?;
|
||||
}
|
||||
}
|
||||
|
||||
@ -744,6 +757,10 @@ pub async fn pull_store(
|
||||
client: &HttpClient,
|
||||
params: &PullParameters,
|
||||
) -> Result<(), Error> {
|
||||
// FIXME: Namespace support requires source AND target namespace
|
||||
let ns = BackupNamespace::root();
|
||||
let local_ns = BackupNamespace::root();
|
||||
|
||||
// explicit create shared lock to prevent GC on newly created chunks
|
||||
let _shared_store_lock = params.store.try_shared_chunk_store_lock()?;
|
||||
|
||||
@ -806,22 +823,23 @@ pub async fn pull_store(
|
||||
progress.done_snapshots = 0;
|
||||
progress.group_snapshots = 0;
|
||||
|
||||
let (owner, _lock_guard) = match params
|
||||
.store
|
||||
.create_locked_backup_group(&group, ¶ms.owner)
|
||||
{
|
||||
Ok(result) => result,
|
||||
Err(err) => {
|
||||
task_log!(
|
||||
worker,
|
||||
"sync group {} failed - group lock failed: {}",
|
||||
&group,
|
||||
err
|
||||
);
|
||||
errors = true; // do not stop here, instead continue
|
||||
continue;
|
||||
}
|
||||
};
|
||||
let (owner, _lock_guard) =
|
||||
match params
|
||||
.store
|
||||
.create_locked_backup_group(&ns, &group, ¶ms.owner)
|
||||
{
|
||||
Ok(result) => result,
|
||||
Err(err) => {
|
||||
task_log!(
|
||||
worker,
|
||||
"sync group {} failed - group lock failed: {}",
|
||||
&group,
|
||||
err
|
||||
);
|
||||
errors = true; // do not stop here, instead continue
|
||||
continue;
|
||||
}
|
||||
};
|
||||
|
||||
// permission check
|
||||
if params.owner != owner {
|
||||
@ -848,7 +866,7 @@ pub async fn pull_store(
|
||||
if new_groups.contains(local_group.as_ref()) {
|
||||
continue;
|
||||
}
|
||||
let owner = params.store.get_owner(&local_group.group())?;
|
||||
let owner = params.store.get_owner(&local_ns, &local_group.group())?;
|
||||
if check_backup_owner(&owner, ¶ms.owner).is_err() {
|
||||
continue;
|
||||
}
|
||||
@ -863,7 +881,7 @@ pub async fn pull_store(
|
||||
local_group.backup_type(),
|
||||
local_group.backup_id()
|
||||
);
|
||||
match params.store.remove_backup_group(local_group.as_ref()) {
|
||||
match params.store.remove_backup_group(&ns, local_group.as_ref()) {
|
||||
Ok(true) => {}
|
||||
Ok(false) => {
|
||||
task_log!(
|
||||
|
Reference in New Issue
Block a user