* [pbs-devel] [PATCH proxmox-backup v2 1/2] fix #5439: allow to reuse existing datastore
@ 2024-06-12 13:22 Gabriel Goller
2024-06-12 13:22 ` [pbs-devel] [PATCH proxmox-backup v2 2/2] web: disallow datastore in root, add reuse-datastore flag Gabriel Goller
2024-07-10 13:28 ` [pbs-devel] [PATCH proxmox-backup v2 1/2] fix #5439: allow to reuse existing datastore Fabian Grünbichler
0 siblings, 2 replies; 10+ messages in thread
From: Gabriel Goller @ 2024-06-12 13:22 UTC (permalink / raw)
To: pbs-devel
Disallow creating datastores in non-empty directories. Allow adding
existing datastores via a 'reuse-datastore' checkmark. This only checks
if all the necessary directories (.chunks + subdirectories and .lock)
are existing and have the correct permissions, then opens the
datastore.
Signed-off-by: Gabriel Goller <g.goller@proxmox.com>
---
v2, thanks @Fabian:
- also check on frontend for root
- forbid datastore creation if dir not empty
- add reuse-datastore option
- verify chunkstore directories permissions and owners
pbs-datastore/src/chunk_store.rs | 49 ++++++++++++++++++++++++++++-
src/api2/config/datastore.rs | 54 +++++++++++++++++++++++++-------
src/api2/node/disks/directory.rs | 1 +
src/api2/node/disks/zfs.rs | 1 +
4 files changed, 93 insertions(+), 12 deletions(-)
diff --git a/pbs-datastore/src/chunk_store.rs b/pbs-datastore/src/chunk_store.rs
index 9f6289c9..077bc316 100644
--- a/pbs-datastore/src/chunk_store.rs
+++ b/pbs-datastore/src/chunk_store.rs
@@ -164,7 +164,7 @@ impl ChunkStore {
/// Note that this must be used with care, as it's dangerous to create two instances on the
/// same base path, as closing the underlying ProcessLocker drops all locks from this process
/// on the lockfile (even if separate FDs)
- pub(crate) fn open<P: Into<PathBuf>>(
+ pub fn open<P: Into<PathBuf>>(
name: &str,
base: P,
sync_level: DatastoreFSyncLevel,
@@ -563,6 +563,53 @@ impl ChunkStore {
// unwrap: only `None` in unit tests
ProcessLocker::try_exclusive_lock(self.locker.clone().unwrap())
}
+
+ /// Checks permissions and owner of passed path.
+ fn check_permissions<T: AsRef<Path>>(path: T, file_mode: u32) -> Result<(), Error> {
+ match nix::sys::stat::stat(path.as_ref()) {
+ Ok(stat) => {
+ if stat.st_uid != u32::from(pbs_config::backup_user()?.uid)
+ || stat.st_gid != u32::from(pbs_config::backup_group()?.gid)
+ || stat.st_mode & 0o700 != file_mode
+ {
+ bail!(
+ "unable to open existing chunk store path {:?} - permissions or owner not correct",
+ path.as_ref(),
+ );
+ }
+ }
+ Err(err) => {
+ bail!(
+ "unable to open existing chunk store path {:?} - {err}",
+ path.as_ref(),
+ );
+ }
+ }
+ Ok(())
+ }
+
+ /// Verify vital files in datastore. Checks the owner and permissions of: the chunkstore, it's
+ /// subdirectories and the lock file.
+ pub fn verify_chunkstore<T: AsRef<Path>>(path: T) -> Result<(), Error> {
+ // Check datastore root path perm/owner
+ ChunkStore::check_permissions(path.as_ref(), 0o700)?;
+
+ let chunk_dir = Self::chunk_dir(path.as_ref());
+ // Check datastore .chunks path perm/owner
+ ChunkStore::check_permissions(&chunk_dir, 0o700)?;
+
+ // Check all .chunks subdirectories
+ for i in 0..64 * 1024 {
+ let mut l1path = chunk_dir.clone();
+ l1path.push(format!("{:04x}", i));
+ ChunkStore::check_permissions(&l1path, 0o700)?;
+ }
+
+ // Check .lock file
+ let lockfile_path = Self::lockfile_path(path.as_ref());
+ ChunkStore::check_permissions(lockfile_path, 0o600)?;
+ Ok(())
+ }
}
#[test]
diff --git a/src/api2/config/datastore.rs b/src/api2/config/datastore.rs
index 6b742acb..f1e80b2d 100644
--- a/src/api2/config/datastore.rs
+++ b/src/api2/config/datastore.rs
@@ -1,7 +1,7 @@
use std::path::PathBuf;
use ::serde::{Deserialize, Serialize};
-use anyhow::Error;
+use anyhow::{bail, Error};
use hex::FromHex;
use serde_json::Value;
@@ -70,23 +70,48 @@ pub(crate) fn do_create_datastore(
_lock: BackupLockGuard,
mut config: SectionConfigData,
datastore: DataStoreConfig,
+ reuse_datastore: bool,
worker: Option<&dyn WorkerTaskContext>,
) -> Result<(), Error> {
let path: PathBuf = datastore.path.clone().into();
+ if path.parent().is_none() {
+ bail!("cannot create datastore in root path");
+ }
+
+ let datastore_empty = std::fs::read_dir(path.clone())?.all(|dir| {
+ dir.map_or(false, |file| {
+ file.file_name().to_str().map_or(false, |name| {
+ (name.starts_with('.')
+ && !(name == ".chunks" || name == ".lock" || name == ".gc-status"))
+ || name.starts_with("lost+found")
+ })
+ })
+ });
+
let tuning: DatastoreTuning = serde_json::from_value(
DatastoreTuning::API_SCHEMA
.parse_property_string(datastore.tuning.as_deref().unwrap_or(""))?,
)?;
- let backup_user = pbs_config::backup_user()?;
- let _store = ChunkStore::create(
- &datastore.name,
- path,
- backup_user.uid,
- backup_user.gid,
- worker,
- tuning.sync_level.unwrap_or_default(),
- )?;
+
+ if !datastore_empty && !reuse_datastore {
+ bail!("Directory is not empty!");
+ } else if !datastore_empty && reuse_datastore {
+ ChunkStore::verify_chunkstore(&path)?;
+
+ let _store =
+ ChunkStore::open(&datastore.name, path, tuning.sync_level.unwrap_or_default())?;
+ } else {
+ let backup_user = pbs_config::backup_user()?;
+ let _store = ChunkStore::create(
+ &datastore.name,
+ path,
+ backup_user.uid,
+ backup_user.gid,
+ worker,
+ tuning.sync_level.unwrap_or_default(),
+ )?;
+ }
config.set_data(&datastore.name, "datastore", &datastore)?;
@@ -103,6 +128,12 @@ pub(crate) fn do_create_datastore(
type: DataStoreConfig,
flatten: true,
},
+ "reuse-datastore": {
+ type: Boolean,
+ optional: true,
+ default: false,
+ description: "Re-use existing datastore directory."
+ }
},
},
access: {
@@ -112,6 +143,7 @@ pub(crate) fn do_create_datastore(
/// Create new datastore config.
pub fn create_datastore(
config: DataStoreConfig,
+ reuse_datastore: bool,
rpcenv: &mut dyn RpcEnvironment,
) -> Result<String, Error> {
let lock = pbs_config::datastore::lock_config()?;
@@ -156,7 +188,7 @@ pub fn create_datastore(
auth_id.to_string(),
to_stdout,
move |worker| {
- do_create_datastore(lock, section_config, config, Some(&worker))?;
+ do_create_datastore(lock, section_config, config, reuse_datastore, Some(&worker))?;
if let Some(prune_job_config) = prune_job_config {
do_create_prune_job(prune_job_config, Some(&worker))
diff --git a/src/api2/node/disks/directory.rs b/src/api2/node/disks/directory.rs
index 9f1112a9..25cfe784 100644
--- a/src/api2/node/disks/directory.rs
+++ b/src/api2/node/disks/directory.rs
@@ -217,6 +217,7 @@ pub fn create_datastore_disk(
lock,
config,
datastore,
+ false,
Some(&worker),
)?;
}
diff --git a/src/api2/node/disks/zfs.rs b/src/api2/node/disks/zfs.rs
index 673dc1bf..ad2129fc 100644
--- a/src/api2/node/disks/zfs.rs
+++ b/src/api2/node/disks/zfs.rs
@@ -323,6 +323,7 @@ pub fn create_zpool(
lock,
config,
datastore,
+ false,
Some(&worker),
)?;
}
--
2.43.0
_______________________________________________
pbs-devel mailing list
pbs-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pbs-devel
^ permalink raw reply [flat|nested] 10+ messages in thread
* [pbs-devel] [PATCH proxmox-backup v2 2/2] web: disallow datastore in root, add reuse-datastore flag
2024-06-12 13:22 [pbs-devel] [PATCH proxmox-backup v2 1/2] fix #5439: allow to reuse existing datastore Gabriel Goller
@ 2024-06-12 13:22 ` Gabriel Goller
2024-07-22 6:45 ` Thomas Lamprecht
2024-07-10 13:28 ` [pbs-devel] [PATCH proxmox-backup v2 1/2] fix #5439: allow to reuse existing datastore Fabian Grünbichler
1 sibling, 1 reply; 10+ messages in thread
From: Gabriel Goller @ 2024-06-12 13:22 UTC (permalink / raw)
To: pbs-devel
Disallows creating a datastore in root on the frontend side, by
filtering the '/' path. Add reuse-flag to permit us to open existing
datastores.
Signed-off-by: Gabriel Goller <g.goller@proxmox.com>
---
www/window/DataStoreEdit.js | 11 +++++++++++
1 file changed, 11 insertions(+)
diff --git a/www/window/DataStoreEdit.js b/www/window/DataStoreEdit.js
index b6115460..2565688e 100644
--- a/www/window/DataStoreEdit.js
+++ b/www/window/DataStoreEdit.js
@@ -61,6 +61,17 @@ Ext.define('PBS.DataStoreEdit', {
allowBlank: false,
fieldLabel: gettext('Backing Path'),
emptyText: gettext('An absolute path'),
+ validator: function(val) {
+ if (val.trim() === '/') {
+ return false;
+ }
+ return true;
+ },
+ },
+ {
+ xtype: 'checkbox',
+ name: 'reuse-datastore',
+ fieldLabel: gettext('Reuse existing datastore'),
},
],
column2: [
--
2.43.0
_______________________________________________
pbs-devel mailing list
pbs-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pbs-devel
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [pbs-devel] [PATCH proxmox-backup v2 1/2] fix #5439: allow to reuse existing datastore
2024-06-12 13:22 [pbs-devel] [PATCH proxmox-backup v2 1/2] fix #5439: allow to reuse existing datastore Gabriel Goller
2024-06-12 13:22 ` [pbs-devel] [PATCH proxmox-backup v2 2/2] web: disallow datastore in root, add reuse-datastore flag Gabriel Goller
@ 2024-07-10 13:28 ` Fabian Grünbichler
2024-07-12 11:57 ` Gabriel Goller
1 sibling, 1 reply; 10+ messages in thread
From: Fabian Grünbichler @ 2024-07-10 13:28 UTC (permalink / raw)
To: Proxmox Backup Server development discussion
On June 12, 2024 3:22 pm, Gabriel Goller wrote:
> Disallow creating datastores in non-empty directories. Allow adding
> existing datastores via a 'reuse-datastore' checkmark. This only checks
> if all the necessary directories (.chunks + subdirectories and .lock)
> are existing and have the correct permissions, then opens the
> datastore.
>
> Signed-off-by: Gabriel Goller <g.goller@proxmox.com>
> ---
>
> v2, thanks @Fabian:
> - also check on frontend for root
> - forbid datastore creation if dir not empty
> - add reuse-datastore option
> - verify chunkstore directories permissions and owners
>
> pbs-datastore/src/chunk_store.rs | 49 ++++++++++++++++++++++++++++-
> src/api2/config/datastore.rs | 54 +++++++++++++++++++++++++-------
> src/api2/node/disks/directory.rs | 1 +
> src/api2/node/disks/zfs.rs | 1 +
> 4 files changed, 93 insertions(+), 12 deletions(-)
>
> diff --git a/pbs-datastore/src/chunk_store.rs b/pbs-datastore/src/chunk_store.rs
> index 9f6289c9..077bc316 100644
> --- a/pbs-datastore/src/chunk_store.rs
> +++ b/pbs-datastore/src/chunk_store.rs
> @@ -164,7 +164,7 @@ impl ChunkStore {
> /// Note that this must be used with care, as it's dangerous to create two instances on the
> /// same base path, as closing the underlying ProcessLocker drops all locks from this process
> /// on the lockfile (even if separate FDs)
given this ^
> - pub(crate) fn open<P: Into<PathBuf>>(
> + pub fn open<P: Into<PathBuf>>(
wouldn't it make more sense to split out the path checks and call them
both in open and in verify_chunkstore below
> name: &str,
> base: P,
> sync_level: DatastoreFSyncLevel,
> @@ -563,6 +563,53 @@ impl ChunkStore {
> // unwrap: only `None` in unit tests
> ProcessLocker::try_exclusive_lock(self.locker.clone().unwrap())
> }
> +
> + /// Checks permissions and owner of passed path.
> + fn check_permissions<T: AsRef<Path>>(path: T, file_mode: u32) -> Result<(), Error> {
> + match nix::sys::stat::stat(path.as_ref()) {
> + Ok(stat) => {
> + if stat.st_uid != u32::from(pbs_config::backup_user()?.uid)
> + || stat.st_gid != u32::from(pbs_config::backup_group()?.gid)
> + || stat.st_mode & 0o700 != file_mode
> + {
> + bail!(
> + "unable to open existing chunk store path {:?} - permissions or owner not correct",
> + path.as_ref(),
> + );
> + }
> + }
> + Err(err) => {
> + bail!(
> + "unable to open existing chunk store path {:?} - {err}",
> + path.as_ref(),
> + );
> + }
> + }
> + Ok(())
> + }
> +
> + /// Verify vital files in datastore. Checks the owner and permissions of: the chunkstore, it's
> + /// subdirectories and the lock file.
> + pub fn verify_chunkstore<T: AsRef<Path>>(path: T) -> Result<(), Error> {
> + // Check datastore root path perm/owner
> + ChunkStore::check_permissions(path.as_ref(), 0o700)?;
> +
> + let chunk_dir = Self::chunk_dir(path.as_ref());
> + // Check datastore .chunks path perm/owner
> + ChunkStore::check_permissions(&chunk_dir, 0o700)?;
> +
> + // Check all .chunks subdirectories
> + for i in 0..64 * 1024 {
> + let mut l1path = chunk_dir.clone();
> + l1path.push(format!("{:04x}", i));
> + ChunkStore::check_permissions(&l1path, 0o700)?;
> + }
> +
> + // Check .lock file
> + let lockfile_path = Self::lockfile_path(path.as_ref());
> + ChunkStore::check_permissions(lockfile_path, 0o600)?;
> + Ok(())
> + }
> }
>
> #[test]
> diff --git a/src/api2/config/datastore.rs b/src/api2/config/datastore.rs
> index 6b742acb..f1e80b2d 100644
> --- a/src/api2/config/datastore.rs
> +++ b/src/api2/config/datastore.rs
> @@ -1,7 +1,7 @@
> use std::path::PathBuf;
>
> use ::serde::{Deserialize, Serialize};
> -use anyhow::Error;
> +use anyhow::{bail, Error};
> use hex::FromHex;
> use serde_json::Value;
>
> @@ -70,23 +70,48 @@ pub(crate) fn do_create_datastore(
> _lock: BackupLockGuard,
> mut config: SectionConfigData,
> datastore: DataStoreConfig,
> + reuse_datastore: bool,
> worker: Option<&dyn WorkerTaskContext>,
> ) -> Result<(), Error> {
> let path: PathBuf = datastore.path.clone().into();
>
> + if path.parent().is_none() {
> + bail!("cannot create datastore in root path");
> + }
> +
> + let datastore_empty = std::fs::read_dir(path.clone())?.all(|dir| {
doesn't this read_dir fail if `path` doesn't exist yet? prior to this
change, creating a datastore would also create the datastore dir itself
(and any parents!).
> + dir.map_or(false, |file| {
> + file.file_name().to_str().map_or(false, |name| {
> + (name.starts_with('.')
> + && !(name == ".chunks" || name == ".lock" || name == ".gc-status"))
> + || name.starts_with("lost+found")
I think the last check there could be == as well?
the indentation here is kinda whacky.. maybe it would be enough to
consider `.chunks` to mark a dir as non-empty?
> + })
> + })
>+ });
should we do this always? or restructure the ifs below and just do it
for the non-reusing create path?
> +
> let tuning: DatastoreTuning = serde_json::from_value(
> DatastoreTuning::API_SCHEMA
> .parse_property_string(datastore.tuning.as_deref().unwrap_or(""))?,
> )?;
> - let backup_user = pbs_config::backup_user()?;
> - let _store = ChunkStore::create(
> - &datastore.name,
> - path,
> - backup_user.uid,
> - backup_user.gid,
> - worker,
> - tuning.sync_level.unwrap_or_default(),
> - )?;
> +
> + if !datastore_empty && !reuse_datastore {
> + bail!("Directory is not empty!");
> + } else if !datastore_empty && reuse_datastore {
> + ChunkStore::verify_chunkstore(&path)?;
> +
> + let _store =
> + ChunkStore::open(&datastore.name, path, tuning.sync_level.unwrap_or_default())?;
(continued from first comment above) and leave out this call here?
> + } else {
> + let backup_user = pbs_config::backup_user()?;
> + let _store = ChunkStore::create(
> + &datastore.name,
> + path,
> + backup_user.uid,
> + backup_user.gid,
> + worker,
> + tuning.sync_level.unwrap_or_default(),
> + )?;
> + }
>
> config.set_data(&datastore.name, "datastore", &datastore)?;
>
> @@ -103,6 +128,12 @@ pub(crate) fn do_create_datastore(
> type: DataStoreConfig,
> flatten: true,
> },
> + "reuse-datastore": {
> + type: Boolean,
> + optional: true,
> + default: false,
> + description: "Re-use existing datastore directory."
> + }
> },
> },
> access: {
> @@ -112,6 +143,7 @@ pub(crate) fn do_create_datastore(
> /// Create new datastore config.
> pub fn create_datastore(
> config: DataStoreConfig,
> + reuse_datastore: bool,
> rpcenv: &mut dyn RpcEnvironment,
> ) -> Result<String, Error> {
> let lock = pbs_config::datastore::lock_config()?;
> @@ -156,7 +188,7 @@ pub fn create_datastore(
> auth_id.to_string(),
> to_stdout,
> move |worker| {
> - do_create_datastore(lock, section_config, config, Some(&worker))?;
> + do_create_datastore(lock, section_config, config, reuse_datastore, Some(&worker))?;
>
> if let Some(prune_job_config) = prune_job_config {
> do_create_prune_job(prune_job_config, Some(&worker))
> diff --git a/src/api2/node/disks/directory.rs b/src/api2/node/disks/directory.rs
> index 9f1112a9..25cfe784 100644
> --- a/src/api2/node/disks/directory.rs
> +++ b/src/api2/node/disks/directory.rs
> @@ -217,6 +217,7 @@ pub fn create_datastore_disk(
> lock,
> config,
> datastore,
> + false,
> Some(&worker),
> )?;
> }
> diff --git a/src/api2/node/disks/zfs.rs b/src/api2/node/disks/zfs.rs
> index 673dc1bf..ad2129fc 100644
> --- a/src/api2/node/disks/zfs.rs
> +++ b/src/api2/node/disks/zfs.rs
> @@ -323,6 +323,7 @@ pub fn create_zpool(
> lock,
> config,
> datastore,
> + false,
> Some(&worker),
> )?;
> }
> --
> 2.43.0
>
>
>
> _______________________________________________
> pbs-devel mailing list
> pbs-devel@lists.proxmox.com
> https://lists.proxmox.com/cgi-bin/mailman/listinfo/pbs-devel
>
>
>
_______________________________________________
pbs-devel mailing list
pbs-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pbs-devel
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [pbs-devel] [PATCH proxmox-backup v2 1/2] fix #5439: allow to reuse existing datastore
2024-07-10 13:28 ` [pbs-devel] [PATCH proxmox-backup v2 1/2] fix #5439: allow to reuse existing datastore Fabian Grünbichler
@ 2024-07-12 11:57 ` Gabriel Goller
2024-07-15 11:23 ` Fabian Grünbichler
0 siblings, 1 reply; 10+ messages in thread
From: Gabriel Goller @ 2024-07-12 11:57 UTC (permalink / raw)
To: Proxmox Backup Server development discussion
On 10.07.2024 15:28, Fabian Grünbichler wrote:
>On June 12, 2024 3:22 pm, Gabriel Goller wrote:
>> Disallow creating datastores in non-empty directories. Allow adding
>> existing datastores via a 'reuse-datastore' checkmark. This only checks
>> if all the necessary directories (.chunks + subdirectories and .lock)
>> are existing and have the correct permissions, then opens the
>> datastore.
>>
>> Signed-off-by: Gabriel Goller <g.goller@proxmox.com>
>> ---
>>
>> v2, thanks @Fabian:
>> - also check on frontend for root
>> - forbid datastore creation if dir not empty
>> - add reuse-datastore option
>> - verify chunkstore directories permissions and owners
>>
>> pbs-datastore/src/chunk_store.rs | 49 ++++++++++++++++++++++++++++-
>> src/api2/config/datastore.rs | 54 +++++++++++++++++++++++++-------
>> src/api2/node/disks/directory.rs | 1 +
>> src/api2/node/disks/zfs.rs | 1 +
>> 4 files changed, 93 insertions(+), 12 deletions(-)
>>
>> diff --git a/pbs-datastore/src/chunk_store.rs b/pbs-datastore/src/chunk_store.rs
>> index 9f6289c9..077bc316 100644
>> --- a/pbs-datastore/src/chunk_store.rs
>> +++ b/pbs-datastore/src/chunk_store.rs
>> @@ -164,7 +164,7 @@ impl ChunkStore {
>> /// Note that this must be used with care, as it's dangerous to create two instances on the
>> /// same base path, as closing the underlying ProcessLocker drops all locks from this process
>> /// on the lockfile (even if separate FDs)
>
>given this ^
>
>> - pub(crate) fn open<P: Into<PathBuf>>(
>> + pub fn open<P: Into<PathBuf>>(
>
>wouldn't it make more sense to split out the path checks and call them
>both in open and in verify_chunkstore below
Why call them both in open and verify_chunkstore? create_datastore
always calls the verify method before open, so we could move this to
the verify function? I think I don't understand this correctly...
>> name: &str,
>> base: P,
>> sync_level: DatastoreFSyncLevel,
>> @@ -563,6 +563,53 @@ impl ChunkStore {
>> // unwrap: only `None` in unit tests
>> ProcessLocker::try_exclusive_lock(self.locker.clone().unwrap())
>> }
>> +
>> + /// Checks permissions and owner of passed path.
>> + fn check_permissions<T: AsRef<Path>>(path: T, file_mode: u32) -> Result<(), Error> {
>> + match nix::sys::stat::stat(path.as_ref()) {
>> + Ok(stat) => {
>> + if stat.st_uid != u32::from(pbs_config::backup_user()?.uid)
>> + || stat.st_gid != u32::from(pbs_config::backup_group()?.gid)
>> + || stat.st_mode & 0o700 != file_mode
>> + {
>> + bail!(
>> + "unable to open existing chunk store path {:?} - permissions or owner not correct",
>> + path.as_ref(),
>> + );
>> + }
>> + }
>> + Err(err) => {
>> + bail!(
>> + "unable to open existing chunk store path {:?} - {err}",
>> + path.as_ref(),
>> + );
>> + }
>> + }
>> + Ok(())
>> + }
>> +
>> + /// Verify vital files in datastore. Checks the owner and permissions of: the chunkstore, it's
>> + /// subdirectories and the lock file.
>> + pub fn verify_chunkstore<T: AsRef<Path>>(path: T) -> Result<(), Error> {
>> + // Check datastore root path perm/owner
>> + ChunkStore::check_permissions(path.as_ref(), 0o700)?;
>> +
>> + let chunk_dir = Self::chunk_dir(path.as_ref());
>> + // Check datastore .chunks path perm/owner
>> + ChunkStore::check_permissions(&chunk_dir, 0o700)?;
>> +
>> + // Check all .chunks subdirectories
>> + for i in 0..64 * 1024 {
>> + let mut l1path = chunk_dir.clone();
>> + l1path.push(format!("{:04x}", i));
>> + ChunkStore::check_permissions(&l1path, 0o700)?;
>> + }
>> +
>> + // Check .lock file
>> + let lockfile_path = Self::lockfile_path(path.as_ref());
>> + ChunkStore::check_permissions(lockfile_path, 0o600)?;
>> + Ok(())
>> + }
>> }
>>
>> #[test]
>> diff --git a/src/api2/config/datastore.rs b/src/api2/config/datastore.rs
>> index 6b742acb..f1e80b2d 100644
>> --- a/src/api2/config/datastore.rs
>> +++ b/src/api2/config/datastore.rs
>> @@ -1,7 +1,7 @@
>> use std::path::PathBuf;
>>
>> use ::serde::{Deserialize, Serialize};
>> -use anyhow::Error;
>> +use anyhow::{bail, Error};
>> use hex::FromHex;
>> use serde_json::Value;
>>
>> @@ -70,23 +70,48 @@ pub(crate) fn do_create_datastore(
>> _lock: BackupLockGuard,
>> mut config: SectionConfigData,
>> datastore: DataStoreConfig,
>> + reuse_datastore: bool,
>> worker: Option<&dyn WorkerTaskContext>,
>> ) -> Result<(), Error> {
>> let path: PathBuf = datastore.path.clone().into();
>>
>> + if path.parent().is_none() {
>> + bail!("cannot create datastore in root path");
>> + }
>> +
>> + let datastore_empty = std::fs::read_dir(path.clone())?.all(|dir| {
>
>doesn't this read_dir fail if `path` doesn't exist yet? prior to this
>change, creating a datastore would also create the datastore dir itself
>(and any parents!).
Yes, correct. Changed it to another map_or.
>> + dir.map_or(false, |file| {
>> + file.file_name().to_str().map_or(false, |name| {
>> + (name.starts_with('.')
>> + && !(name == ".chunks" || name == ".lock" || name == ".gc-status"))
>> + || name.starts_with("lost+found")
>
>I think the last check there could be == as well?
Yep.
>the indentation here is kinda whacky.. maybe it would be enough to
>consider `.chunks` to mark a dir as non-empty?
Hmm, yeah this would make everything simpler.
>> + })
>> + })
>>+ });
>
>should we do this always? or restructure the ifs below and just do it
>for the non-reusing create path?
Oh, yeah that would work. We already check the existance of `.chunks`
with the verify method (though implicitly) so that should work.
>> +
>> let tuning: DatastoreTuning = serde_json::from_value(
>> DatastoreTuning::API_SCHEMA
>> .parse_property_string(datastore.tuning.as_deref().unwrap_or(""))?,
>> )?;
>> - let backup_user = pbs_config::backup_user()?;
>> - let _store = ChunkStore::create(
>> - &datastore.name,
>> - path,
>> - backup_user.uid,
>> - backup_user.gid,
>> - worker,
>> - tuning.sync_level.unwrap_or_default(),
>> - )?;
>> +
>> + if !datastore_empty && !reuse_datastore {
>> + bail!("Directory is not empty!");
>> + } else if !datastore_empty && reuse_datastore {
>> + ChunkStore::verify_chunkstore(&path)?;
>> +
>> + let _store =
>> + ChunkStore::open(&datastore.name, path, tuning.sync_level.unwrap_or_default())?;
>
>(continued from first comment above) and leave out this call here?
>
>> + } else {
>> + let backup_user = pbs_config::backup_user()?;
>> + let _store = ChunkStore::create(
>> + &datastore.name,
>> + path,
>> + backup_user.uid,
>> + backup_user.gid,
>> + worker,
>> + tuning.sync_level.unwrap_or_default(),
>> + )?;
>> + }
>>
>> config.set_data(&datastore.name, "datastore", &datastore)?;
>>
>> @@ -103,6 +128,12 @@ pub(crate) fn do_create_datastore(
>> type: DataStoreConfig,
>> flatten: true,
>> },
>> + "reuse-datastore": {
>> + type: Boolean,
>> + optional: true,
>> + default: false,
>> + description: "Re-use existing datastore directory."
>> + }
>> },
>> },
>> access: {
>> @@ -112,6 +143,7 @@ pub(crate) fn do_create_datastore(
>> /// Create new datastore config.
>> pub fn create_datastore(
>> config: DataStoreConfig,
>> + reuse_datastore: bool,
>> rpcenv: &mut dyn RpcEnvironment,
>> ) -> Result<String, Error> {
>> let lock = pbs_config::datastore::lock_config()?;
>> @@ -156,7 +188,7 @@ pub fn create_datastore(
>> auth_id.to_string(),
>> to_stdout,
>> move |worker| {
>> - do_create_datastore(lock, section_config, config, Some(&worker))?;
>> + do_create_datastore(lock, section_config, config, reuse_datastore, Some(&worker))?;
>>
>> if let Some(prune_job_config) = prune_job_config {
>> do_create_prune_job(prune_job_config, Some(&worker))
>> diff --git a/src/api2/node/disks/directory.rs b/src/api2/node/disks/directory.rs
>> index 9f1112a9..25cfe784 100644
>> --- a/src/api2/node/disks/directory.rs
>> +++ b/src/api2/node/disks/directory.rs
>> @@ -217,6 +217,7 @@ pub fn create_datastore_disk(
>> lock,
>> config,
>> datastore,
>> + false,
>> Some(&worker),
>> )?;
>> }
>> diff --git a/src/api2/node/disks/zfs.rs b/src/api2/node/disks/zfs.rs
>> index 673dc1bf..ad2129fc 100644
>> --- a/src/api2/node/disks/zfs.rs
>> +++ b/src/api2/node/disks/zfs.rs
>> @@ -323,6 +323,7 @@ pub fn create_zpool(
>> lock,
>> config,
>> datastore,
>> + false,
>> Some(&worker),
>> )?;
>> }
>> --
>> 2.43.0
>>
>>
>>
>> _______________________________________________
>> pbs-devel mailing list
>> pbs-devel@lists.proxmox.com
>> https://lists.proxmox.com/cgi-bin/mailman/listinfo/pbs-devel
>>
>>
>>
>
>
>_______________________________________________
>pbs-devel mailing list
>pbs-devel@lists.proxmox.com
>https://lists.proxmox.com/cgi-bin/mailman/listinfo/pbs-devel
>
>
_______________________________________________
pbs-devel mailing list
pbs-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pbs-devel
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [pbs-devel] [PATCH proxmox-backup v2 1/2] fix #5439: allow to reuse existing datastore
2024-07-12 11:57 ` Gabriel Goller
@ 2024-07-15 11:23 ` Fabian Grünbichler
2024-07-18 9:27 ` Gabriel Goller
0 siblings, 1 reply; 10+ messages in thread
From: Fabian Grünbichler @ 2024-07-15 11:23 UTC (permalink / raw)
To: Proxmox Backup Server development discussion
On July 12, 2024 1:57 pm, Gabriel Goller wrote:
> On 10.07.2024 15:28, Fabian Grünbichler wrote:
>>On June 12, 2024 3:22 pm, Gabriel Goller wrote:
>>> Disallow creating datastores in non-empty directories. Allow adding
>>> existing datastores via a 'reuse-datastore' checkmark. This only checks
>>> if all the necessary directories (.chunks + subdirectories and .lock)
>>> are existing and have the correct permissions, then opens the
>>> datastore.
>>>
>>> Signed-off-by: Gabriel Goller <g.goller@proxmox.com>
>>> ---
>>>
>>> v2, thanks @Fabian:
>>> - also check on frontend for root
>>> - forbid datastore creation if dir not empty
>>> - add reuse-datastore option
>>> - verify chunkstore directories permissions and owners
>>>
>>> pbs-datastore/src/chunk_store.rs | 49 ++++++++++++++++++++++++++++-
>>> src/api2/config/datastore.rs | 54 +++++++++++++++++++++++++-------
>>> src/api2/node/disks/directory.rs | 1 +
>>> src/api2/node/disks/zfs.rs | 1 +
>>> 4 files changed, 93 insertions(+), 12 deletions(-)
>>>
>>> diff --git a/pbs-datastore/src/chunk_store.rs b/pbs-datastore/src/chunk_store.rs
>>> index 9f6289c9..077bc316 100644
>>> --- a/pbs-datastore/src/chunk_store.rs
>>> +++ b/pbs-datastore/src/chunk_store.rs
>>> @@ -164,7 +164,7 @@ impl ChunkStore {
>>> /// Note that this must be used with care, as it's dangerous to create two instances on the
>>> /// same base path, as closing the underlying ProcessLocker drops all locks from this process
>>> /// on the lockfile (even if separate FDs)
>>
>>given this ^
this part here was important ;)
>>
>>> - pub(crate) fn open<P: Into<PathBuf>>(
>>> + pub fn open<P: Into<PathBuf>>(
>>
>>wouldn't it make more sense to split out the path checks and call them
>>both in open and in verify_chunkstore below
>
> Why call them both in open and verify_chunkstore? create_datastore
> always calls the verify method before open, so we could move this to
> the verify function? I think I don't understand this correctly...
see below..
>>> name: &str,
>>> base: P,
>>> sync_level: DatastoreFSyncLevel,
>>> @@ -563,6 +563,53 @@ impl ChunkStore {
>>> // unwrap: only `None` in unit tests
>>> ProcessLocker::try_exclusive_lock(self.locker.clone().unwrap())
>>> }
>
>>> +
>>> let tuning: DatastoreTuning = serde_json::from_value(
>>> DatastoreTuning::API_SCHEMA
>>> .parse_property_string(datastore.tuning.as_deref().unwrap_or(""))?,
>>> )?;
>>> - let backup_user = pbs_config::backup_user()?;
>>> - let _store = ChunkStore::create(
>>> - &datastore.name,
>>> - path,
>>> - backup_user.uid,
>>> - backup_user.gid,
>>> - worker,
>>> - tuning.sync_level.unwrap_or_default(),
>>> - )?;
>>> +
>>> + if !datastore_empty && !reuse_datastore {
>>> + bail!("Directory is not empty!");
>>> + } else if !datastore_empty && reuse_datastore {
>>> + ChunkStore::verify_chunkstore(&path)?;
>>> +
>>> + let _store =
>>> + ChunkStore::open(&datastore.name, path, tuning.sync_level.unwrap_or_default())?;
>>
>>(continued from first comment above) and leave out this call here?
this was the continuation ;) if we drop the open call here (to avoid
side-effects via the process locker), then we need the path checks in
verify *and* open. hope that makes sense now :)
>>
>>> + } else {
>>> + let backup_user = pbs_config::backup_user()?;
>>> + let _store = ChunkStore::create(
>>> + &datastore.name,
>>> + path,
>>> + backup_user.uid,
>>> + backup_user.gid,
>>> + worker,
>>> + tuning.sync_level.unwrap_or_default(),
>>> + )?;
>>> + }
_______________________________________________
pbs-devel mailing list
pbs-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pbs-devel
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [pbs-devel] [PATCH proxmox-backup v2 1/2] fix #5439: allow to reuse existing datastore
2024-07-15 11:23 ` Fabian Grünbichler
@ 2024-07-18 9:27 ` Gabriel Goller
2024-07-18 11:05 ` Fabian Grünbichler
0 siblings, 1 reply; 10+ messages in thread
From: Gabriel Goller @ 2024-07-18 9:27 UTC (permalink / raw)
To: Proxmox Backup Server development discussion
On 15.07.2024 13:23, Fabian Grünbichler wrote:
>On July 12, 2024 1:57 pm, Gabriel Goller wrote:
>> On 10.07.2024 15:28, Fabian Grünbichler wrote:
>>>On June 12, 2024 3:22 pm, Gabriel Goller wrote:
>>>> Disallow creating datastores in non-empty directories. Allow adding
>>>> existing datastores via a 'reuse-datastore' checkmark. This only checks
>>>> if all the necessary directories (.chunks + subdirectories and .lock)
>>>> are existing and have the correct permissions, then opens the
>>>> datastore.
>>>>
>>>> Signed-off-by: Gabriel Goller <g.goller@proxmox.com>
>>>> ---
>>>>
>>>> v2, thanks @Fabian:
>>>> - also check on frontend for root
>>>> - forbid datastore creation if dir not empty
>>>> - add reuse-datastore option
>>>> - verify chunkstore directories permissions and owners
>>>>
>>>> pbs-datastore/src/chunk_store.rs | 49 ++++++++++++++++++++++++++++-
>>>> src/api2/config/datastore.rs | 54 +++++++++++++++++++++++++-------
>>>> src/api2/node/disks/directory.rs | 1 +
>>>> src/api2/node/disks/zfs.rs | 1 +
>>>> 4 files changed, 93 insertions(+), 12 deletions(-)
>>>>
>>>> diff --git a/pbs-datastore/src/chunk_store.rs b/pbs-datastore/src/chunk_store.rs
>>>> index 9f6289c9..077bc316 100644
>>>> --- a/pbs-datastore/src/chunk_store.rs
>>>> +++ b/pbs-datastore/src/chunk_store.rs
>>>> @@ -164,7 +164,7 @@ impl ChunkStore {
>>>> /// Note that this must be used with care, as it's dangerous to create two instances on the
>>>> /// same base path, as closing the underlying ProcessLocker drops all locks from this process
>>>> /// on the lockfile (even if separate FDs)
>>>
>>>given this ^
>
>this part here was important ;)
>
>>>
>>>> - pub(crate) fn open<P: Into<PathBuf>>(
>>>> + pub fn open<P: Into<PathBuf>>(
>>>
>>>wouldn't it make more sense to split out the path checks and call them
>>>both in open and in verify_chunkstore below
>>
>> Why call them both in open and verify_chunkstore? create_datastore
>> always calls the verify method before open, so we could move this to
>> the verify function? I think I don't understand this correctly...
>
>see below..
>
>>>> name: &str,
>>>> base: P,
>>>> sync_level: DatastoreFSyncLevel,
>>>> @@ -563,6 +563,53 @@ impl ChunkStore {
>>>> // unwrap: only `None` in unit tests
>>>> ProcessLocker::try_exclusive_lock(self.locker.clone().unwrap())
>>>> }
>>
>>>> +
>>>> let tuning: DatastoreTuning = serde_json::from_value(
>>>> DatastoreTuning::API_SCHEMA
>>>> .parse_property_string(datastore.tuning.as_deref().unwrap_or(""))?,
>>>> )?;
>>>> - let backup_user = pbs_config::backup_user()?;
>>>> - let _store = ChunkStore::create(
>>>> - &datastore.name,
>>>> - path,
>>>> - backup_user.uid,
>>>> - backup_user.gid,
>>>> - worker,
>>>> - tuning.sync_level.unwrap_or_default(),
>>>> - )?;
>>>> +
>>>> + if !datastore_empty && !reuse_datastore {
>>>> + bail!("Directory is not empty!");
>>>> + } else if !datastore_empty && reuse_datastore {
>>>> + ChunkStore::verify_chunkstore(&path)?;
>>>> +
>>>> + let _store =
>>>> + ChunkStore::open(&datastore.name, path, tuning.sync_level.unwrap_or_default())?;
>>>
>>>(continued from first comment above) and leave out this call here?
>
>this was the continuation ;) if we drop the open call here (to avoid
>side-effects via the process locker), then we need the path checks in
>verify *and* open. hope that makes sense now :)
>
Ooh, I think I got it...
So basically we could just call `ChunkStore::verify_chunkstore` in
`ChunkStore::open` and drop the `open` call above, right?
>>>
>>>> + } else {
>>>> + let backup_user = pbs_config::backup_user()?;
>>>> + let _store = ChunkStore::create(
>>>> + &datastore.name,
>>>> + path,
>>>> + backup_user.uid,
>>>> + backup_user.gid,
>>>> + worker,
>>>> + tuning.sync_level.unwrap_or_default(),
>>>> + )?;
>>>> + }
>
>
>_______________________________________________
>pbs-devel mailing list
>pbs-devel@lists.proxmox.com
>https://lists.proxmox.com/cgi-bin/mailman/listinfo/pbs-devel
_______________________________________________
pbs-devel mailing list
pbs-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pbs-devel
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [pbs-devel] [PATCH proxmox-backup v2 1/2] fix #5439: allow to reuse existing datastore
2024-07-18 9:27 ` Gabriel Goller
@ 2024-07-18 11:05 ` Fabian Grünbichler
2024-07-18 12:18 ` Gabriel Goller
0 siblings, 1 reply; 10+ messages in thread
From: Fabian Grünbichler @ 2024-07-18 11:05 UTC (permalink / raw)
To: Proxmox Backup Server development discussion
On July 18, 2024 11:27 am, Gabriel Goller wrote:
> On 15.07.2024 13:23, Fabian Grünbichler wrote:
>>On July 12, 2024 1:57 pm, Gabriel Goller wrote:
>>> On 10.07.2024 15:28, Fabian Grünbichler wrote:
>>>>On June 12, 2024 3:22 pm, Gabriel Goller wrote:
>>>>> Disallow creating datastores in non-empty directories. Allow adding
>>>>> existing datastores via a 'reuse-datastore' checkmark. This only checks
>>>>> if all the necessary directories (.chunks + subdirectories and .lock)
>>>>> are existing and have the correct permissions, then opens the
>>>>> datastore.
>>>>>
>>>>> Signed-off-by: Gabriel Goller <g.goller@proxmox.com>
>>>>> ---
>>>>>
>>>>> v2, thanks @Fabian:
>>>>> - also check on frontend for root
>>>>> - forbid datastore creation if dir not empty
>>>>> - add reuse-datastore option
>>>>> - verify chunkstore directories permissions and owners
>>>>>
>>>>> pbs-datastore/src/chunk_store.rs | 49 ++++++++++++++++++++++++++++-
>>>>> src/api2/config/datastore.rs | 54 +++++++++++++++++++++++++-------
>>>>> src/api2/node/disks/directory.rs | 1 +
>>>>> src/api2/node/disks/zfs.rs | 1 +
>>>>> 4 files changed, 93 insertions(+), 12 deletions(-)
>>>>>
>>>>> diff --git a/pbs-datastore/src/chunk_store.rs b/pbs-datastore/src/chunk_store.rs
>>>>> index 9f6289c9..077bc316 100644
>>>>> --- a/pbs-datastore/src/chunk_store.rs
>>>>> +++ b/pbs-datastore/src/chunk_store.rs
>>>>> @@ -164,7 +164,7 @@ impl ChunkStore {
>>>>> /// Note that this must be used with care, as it's dangerous to create two instances on the
>>>>> /// same base path, as closing the underlying ProcessLocker drops all locks from this process
>>>>> /// on the lockfile (even if separate FDs)
>>>>
>>>>given this ^
>>
>>this part here was important ;)
>>
>>>>
>>>>> - pub(crate) fn open<P: Into<PathBuf>>(
>>>>> + pub fn open<P: Into<PathBuf>>(
>>>>
>>>>wouldn't it make more sense to split out the path checks and call them
>>>>both in open and in verify_chunkstore below
>>>
>>> Why call them both in open and verify_chunkstore? create_datastore
>>> always calls the verify method before open, so we could move this to
>>> the verify function? I think I don't understand this correctly...
>>
>>see below..
>>
>>>>> name: &str,
>>>>> base: P,
>>>>> sync_level: DatastoreFSyncLevel,
>>>>> @@ -563,6 +563,53 @@ impl ChunkStore {
>>>>> // unwrap: only `None` in unit tests
>>>>> ProcessLocker::try_exclusive_lock(self.locker.clone().unwrap())
>>>>> }
>>>
>>>>> +
>>>>> let tuning: DatastoreTuning = serde_json::from_value(
>>>>> DatastoreTuning::API_SCHEMA
>>>>> .parse_property_string(datastore.tuning.as_deref().unwrap_or(""))?,
>>>>> )?;
>>>>> - let backup_user = pbs_config::backup_user()?;
>>>>> - let _store = ChunkStore::create(
>>>>> - &datastore.name,
>>>>> - path,
>>>>> - backup_user.uid,
>>>>> - backup_user.gid,
>>>>> - worker,
>>>>> - tuning.sync_level.unwrap_or_default(),
>>>>> - )?;
>>>>> +
>>>>> + if !datastore_empty && !reuse_datastore {
>>>>> + bail!("Directory is not empty!");
>>>>> + } else if !datastore_empty && reuse_datastore {
>>>>> + ChunkStore::verify_chunkstore(&path)?;
>>>>> +
>>>>> + let _store =
>>>>> + ChunkStore::open(&datastore.name, path, tuning.sync_level.unwrap_or_default())?;
>>>>
>>>>(continued from first comment above) and leave out this call here?
>>
>>this was the continuation ;) if we drop the open call here (to avoid
>>side-effects via the process locker), then we need the path checks in
>>verify *and* open. hope that makes sense now :)
>>
>
> Ooh, I think I got it...
> So basically we could just call `ChunkStore::verify_chunkstore` in
> `ChunkStore::open` and drop the `open` call above, right?
no, what I meant is:
in open, split out the checks there (that base is absolute, and that the
chunk dir is accessible) into a new helper fn. then call that new helper
fn in open (so that the behaviour of open stays the same).
then, also call this helper in your patch here - it doesn't really give
much extra benefit atm, but it makes it less easy to miss that this
should be done in both places when adding new checks.
I don't think we want to do the full verification on each open, but when
creating a datastore config entry with an existing datastore, we want to
do both the extensive checks and those we'd do when opening it.
of course another option would be to have a single helper, and a
parameter that controls whether the extensive checks should be done or
not. even harder to miss that there are two related sets of checks then
;)
>>>>
>>>>> + } else {
>>>>> + let backup_user = pbs_config::backup_user()?;
>>>>> + let _store = ChunkStore::create(
>>>>> + &datastore.name,
>>>>> + path,
>>>>> + backup_user.uid,
>>>>> + backup_user.gid,
>>>>> + worker,
>>>>> + tuning.sync_level.unwrap_or_default(),
>>>>> + )?;
>>>>> + }
>>
>>
>>_______________________________________________
>>pbs-devel mailing list
>>pbs-devel@lists.proxmox.com
>>https://lists.proxmox.com/cgi-bin/mailman/listinfo/pbs-devel
>
>
> _______________________________________________
> pbs-devel mailing list
> pbs-devel@lists.proxmox.com
> https://lists.proxmox.com/cgi-bin/mailman/listinfo/pbs-devel
>
_______________________________________________
pbs-devel mailing list
pbs-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pbs-devel
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [pbs-devel] [PATCH proxmox-backup v2 1/2] fix #5439: allow to reuse existing datastore
2024-07-18 11:05 ` Fabian Grünbichler
@ 2024-07-18 12:18 ` Gabriel Goller
0 siblings, 0 replies; 10+ messages in thread
From: Gabriel Goller @ 2024-07-18 12:18 UTC (permalink / raw)
To: Proxmox Backup Server development discussion
>>
>> Ooh, I think I got it...
>> So basically we could just call `ChunkStore::verify_chunkstore` in
>> `ChunkStore::open` and drop the `open` call above, right?
>
>no, what I meant is:
>
>in open, split out the checks there (that base is absolute, and that the
>chunk dir is accessible) into a new helper fn. then call that new helper
>fn in open (so that the behaviour of open stays the same).
>
>then, also call this helper in your patch here - it doesn't really give
>much extra benefit atm, but it makes it less easy to miss that this
>should be done in both places when adding new checks.
>
>I don't think we want to do the full verification on each open, but when
>creating a datastore config entry with an existing datastore, we want to
>do both the extensive checks and those we'd do when opening it.
>
>of course another option would be to have a single helper, and a
>parameter that controls whether the extensive checks should be done or
>not. even harder to miss that there are two related sets of checks then
>;)
>
Got it! Didn't have my coffee yet, sorry about that :)
Will submit a new version soon!
_______________________________________________
pbs-devel mailing list
pbs-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pbs-devel
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [pbs-devel] [PATCH proxmox-backup v2 2/2] web: disallow datastore in root, add reuse-datastore flag
2024-06-12 13:22 ` [pbs-devel] [PATCH proxmox-backup v2 2/2] web: disallow datastore in root, add reuse-datastore flag Gabriel Goller
@ 2024-07-22 6:45 ` Thomas Lamprecht
2024-07-30 9:10 ` Gabriel Goller
0 siblings, 1 reply; 10+ messages in thread
From: Thomas Lamprecht @ 2024-07-22 6:45 UTC (permalink / raw)
To: Proxmox Backup Server development discussion, Gabriel Goller
Am 12/06/2024 um 15:22 schrieb Gabriel Goller:
> Disallows creating a datastore in root on the frontend side, by
> filtering the '/' path. Add reuse-flag to permit us to open existing
> datastores.
>
> Signed-off-by: Gabriel Goller <g.goller@proxmox.com>
> ---
> www/window/DataStoreEdit.js | 11 +++++++++++
> 1 file changed, 11 insertions(+)
>
> diff --git a/www/window/DataStoreEdit.js b/www/window/DataStoreEdit.js
> index b6115460..2565688e 100644
> --- a/www/window/DataStoreEdit.js
> +++ b/www/window/DataStoreEdit.js
> @@ -61,6 +61,17 @@ Ext.define('PBS.DataStoreEdit', {
> allowBlank: false,
> fieldLabel: gettext('Backing Path'),
> emptyText: gettext('An absolute path'),
> + validator: function(val) {
> + if (val.trim() === '/') {
> + return false;
> + }
> + return true;
above could be reduced to:
validator: val => val?.trim() !== '/',
Note also the null check, not 100% sure from top of my head, but when
going from some value to deleting the field content (CTRL + A; DEL)
the validator might be called with a null/undefined value, or did you
check and ensure that it's then always an empty string?
Besides that, this seems quite unrelated to the "allow re-adding an
existing datastore" part and should be its own patch, which could
be applied up-front.
Oh, and does the backend disallow this already, or is this intended
as "unsafe" frontend only check for pure UX reason (can be fine, but
I would at least note that explicitly in the commit message).
> + },
> + },
> + {
> + xtype: 'checkbox',
> + name: 'reuse-datastore',
> + fieldLabel: gettext('Reuse existing datastore'),
hmm, not sure w.r.t. always showing a checkbox for some relatively niche
use case by default.
Maybe this is better split-off into either an advanced section or by
opening the dialogue through some new button (e.g. by adding a top bar
in the all-datastore summary page and add an "Add Datastore" split-
button there with the "Re-Add Existing Datastore" as menu button there
(like the shutdown + reboot/stop for guests in PVE).
> },
> ],
> column2: [
_______________________________________________
pbs-devel mailing list
pbs-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pbs-devel
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [pbs-devel] [PATCH proxmox-backup v2 2/2] web: disallow datastore in root, add reuse-datastore flag
2024-07-22 6:45 ` Thomas Lamprecht
@ 2024-07-30 9:10 ` Gabriel Goller
0 siblings, 0 replies; 10+ messages in thread
From: Gabriel Goller @ 2024-07-30 9:10 UTC (permalink / raw)
To: Thomas Lamprecht; +Cc: Proxmox Backup Server development discussion
On 22.07.2024 08:45, Thomas Lamprecht wrote:
>Am 12/06/2024 um 15:22 schrieb Gabriel Goller:
>> Disallows creating a datastore in root on the frontend side, by
>> filtering the '/' path. Add reuse-flag to permit us to open existing
>> datastores.
>>
>> Signed-off-by: Gabriel Goller <g.goller@proxmox.com>
>> ---
>> www/window/DataStoreEdit.js | 11 +++++++++++
>> 1 file changed, 11 insertions(+)
>>
>> diff --git a/www/window/DataStoreEdit.js b/www/window/DataStoreEdit.js
>> index b6115460..2565688e 100644
>> --- a/www/window/DataStoreEdit.js
>> +++ b/www/window/DataStoreEdit.js
>> @@ -61,6 +61,17 @@ Ext.define('PBS.DataStoreEdit', {
>> allowBlank: false,
>> fieldLabel: gettext('Backing Path'),
>> emptyText: gettext('An absolute path'),
>> + validator: function(val) {
>> + if (val.trim() === '/') {
>> + return false;
>> + }
>> + return true;
>
>above could be reduced to:
>
>validator: val => val?.trim() !== '/',
>
>
>Note also the null check, not 100% sure from top of my head, but when
>going from some value to deleting the field content (CTRL + A; DEL)
>the validator might be called with a null/undefined value, or did you
>check and ensure that it's then always an empty string?
Hmm I could not reproduce this, probably because we have the `allowBlank:
false` above? Either way, your line is cleaner, so I will update it :).
>Besides that, this seems quite unrelated to the "allow re-adding an
>existing datastore" part and should be its own patch, which could
>be applied up-front.
>Oh, and does the backend disallow this already, or is this intended
>as "unsafe" frontend only check for pure UX reason (can be fine, but
>I would at least note that explicitly in the commit message).
This is checked on the backend as well.
(src/api2/config/datastore.rs:77, with this series applied.)
Yes, the series started out as "disallow datastore creation in root" and
now transformed into "allow opening a existing datastore" (with all the
correct checks). I could factor out the "root-path-check" (frontend and
backend) and submit a separate series for that though?
>
>
>> + },
>> + },
>> + {
>> + xtype: 'checkbox',
>> + name: 'reuse-datastore',
>> + fieldLabel: gettext('Reuse existing datastore'),
>
>hmm, not sure w.r.t. always showing a checkbox for some relatively niche
>use case by default.
>Maybe this is better split-off into either an advanced section or by
>opening the dialogue through some new button (e.g. by adding a top bar
>in the all-datastore summary page and add an "Add Datastore" split-
>button there with the "Re-Add Existing Datastore" as menu button there
>(like the shutdown + reboot/stop for guests in PVE).
I'd agree with an "Advanced" button to toggle this option. Will include
it in the next version!
A split button in the datastore overview is IMO a bit hard to find, I
don't think the overview is used that much.
_______________________________________________
pbs-devel mailing list
pbs-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pbs-devel
^ permalink raw reply [flat|nested] 10+ messages in thread
end of thread, other threads:[~2024-07-30 9:10 UTC | newest]
Thread overview: 10+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2024-06-12 13:22 [pbs-devel] [PATCH proxmox-backup v2 1/2] fix #5439: allow to reuse existing datastore Gabriel Goller
2024-06-12 13:22 ` [pbs-devel] [PATCH proxmox-backup v2 2/2] web: disallow datastore in root, add reuse-datastore flag Gabriel Goller
2024-07-22 6:45 ` Thomas Lamprecht
2024-07-30 9:10 ` Gabriel Goller
2024-07-10 13:28 ` [pbs-devel] [PATCH proxmox-backup v2 1/2] fix #5439: allow to reuse existing datastore Fabian Grünbichler
2024-07-12 11:57 ` Gabriel Goller
2024-07-15 11:23 ` Fabian Grünbichler
2024-07-18 9:27 ` Gabriel Goller
2024-07-18 11:05 ` Fabian Grünbichler
2024-07-18 12:18 ` Gabriel Goller
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.
Service provided by Proxmox Server Solutions GmbH | Privacy | Legal