From b757c61621696a059c0cd4e8e5ec243c31413342 Mon Sep 17 00:00:00 2001 From: Markus Frank Date: Thu, 30 Nov 2023 11:37:24 +0100 Subject: [PATCH] api: datastore create: allow re-using existing dirs if empty & not a mountpoint When formatting and creating a filesystem on a disk it's important that the target directory in `/mnt/datastore/` either doesn't exist yet, or is empty and not a mountpoint of an existing FS. As that way we ensure that no data is lost, or gets hidden, on creating a new datastore. Our current check was a bit stricter than required, it always bailed if the target directory existed, even if it was a plain & empty directory on the root file-system. So adapt the check and also check whether an existing target directory is empty and not already mounted, as then it can be used just fine. Signed-off-by: Markus Frank Tested-by: Christian Ebner [ TL: reword subject and commit message to include more details ] Signed-off-by: Thomas Lamprecht --- src/api2/node/disks/directory.rs | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/src/api2/node/disks/directory.rs b/src/api2/node/disks/directory.rs index 5e1cb124..9f1112a9 100644 --- a/src/api2/node/disks/directory.rs +++ b/src/api2/node/disks/directory.rs @@ -1,6 +1,7 @@ use ::serde::{Deserialize, Serialize}; use anyhow::{bail, Error}; use serde_json::json; +use std::os::linux::fs::MetadataExt; use proxmox_router::{Permission, Router, RpcEnvironment, RpcEnvironmentType}; use proxmox_schema::api; @@ -155,13 +156,21 @@ pub fn create_datastore_disk( let mount_point = format!("{}{}", BASE_MOUNT_DIR, &name); - // check if the default path does exist already and bail if it does + // check if the default path exists already. + // bail if it is not empty or another filesystem mounted on top let default_path = std::path::PathBuf::from(&mount_point); match std::fs::metadata(&default_path) { Err(_) => {} // path does not exist - Ok(_) => { - bail!("path {:?} already exists", default_path); + Ok(stat) => { + let basedir_dev = std::fs::metadata(BASE_MOUNT_DIR)?.st_dev(); + if stat.st_dev() != basedir_dev { + bail!("path {default_path:?} already exists and is mountpoint"); + } + let is_empty = default_path.read_dir()?.next().is_none(); + if !is_empty { + bail!("path {default_path:?} already exists and is not empty"); + } } } -- 2.39.5