use anyhow::{bail, Error};
use proxmox::api::{api, Router, RpcEnvironment, Permission};
-use proxmox::tools::fs::open_file_locked;
use crate::api2::types::*;
use crate::config::acl;
use crate::config::acl::{Role, PRIV_SYS_AUDIT, PRIV_PERMISSIONS_MODIFY};
use crate::config::cached_user_info::CachedUserInfo;
+use crate::backup::open_backup_lockfile;
fn extract_acl_node_data(
node: &acl::AclTreeNode,
};
}
- let _lock = open_file_locked(acl::ACL_CFG_LOCKFILE, std::time::Duration::new(10, 0), true)?;
+ let _lock = open_backup_lockfile(acl::ACL_CFG_LOCKFILE, None, true)?;
let (mut tree, expected_digest) = acl::config()?;
use proxmox::api::{api, Permission, RpcEnvironment};
use proxmox::{list_subdirs_api_method};
use proxmox::{identity, sortable};
-use proxmox::tools::fs::open_file_locked;
use proxmox_openid::{OpenIdAuthenticator, OpenIdConfig};
use crate::config::domains::{OpenIdUserAttribute, OpenIdRealmConfig};
use crate::config::cached_user_info::CachedUserInfo;
+use crate::backup::open_backup_lockfile;
+
use crate::api2::types::*;
use crate::auth_helpers::*;
if !user_info.is_active_user_id(&user_id) {
if config.autocreate.unwrap_or(false) {
use crate::config::user;
- let _lock = open_file_locked(user::USER_CFG_LOCKFILE, std::time::Duration::new(10, 0), true)?;
+ let _lock = open_backup_lockfile(user::USER_CFG_LOCKFILE, None, true)?;
let user = user::User {
userid: user_id.clone(),
comment: None,
use proxmox::api::{api, ApiMethod, Router, RpcEnvironment, Permission};
use proxmox::api::router::SubdirMap;
use proxmox::api::schema::{Schema, StringSchema};
-use proxmox::tools::fs::open_file_locked;
use pbs_api_types::{
PASSWORD_FORMAT, PROXMOX_CONFIG_DIGEST_SCHEMA, SINGLE_LINE_COMMENT_SCHEMA, Authid,
use crate::config::token_shadow;
use crate::config::acl::{PRIV_SYS_AUDIT, PRIV_PERMISSIONS_MODIFY};
use crate::config::cached_user_info::CachedUserInfo;
+use crate::backup::open_backup_lockfile;
pub const PBS_PASSWORD_SCHEMA: Schema = StringSchema::new("User Password.")
.format(&PASSWORD_FORMAT)
rpcenv: &mut dyn RpcEnvironment
) -> Result<(), Error> {
- let _lock = open_file_locked(user::USER_CFG_LOCKFILE, std::time::Duration::new(10, 0), true)?;
+ let _lock = open_backup_lockfile(user::USER_CFG_LOCKFILE, None, true)?;
let user: user::User = serde_json::from_value(param)?;
rpcenv: &mut dyn RpcEnvironment,
) -> Result<(), Error> {
- let _lock = open_file_locked(user::USER_CFG_LOCKFILE, std::time::Duration::new(10, 0), true)?;
+ let _lock = open_backup_lockfile(user::USER_CFG_LOCKFILE, None, true)?;
let (mut config, expected_digest) = user::config()?;
pub fn delete_user(userid: Userid, digest: Option<String>) -> Result<(), Error> {
let _tfa_lock = crate::config::tfa::write_lock()?;
- let _lock = open_file_locked(user::USER_CFG_LOCKFILE, std::time::Duration::new(10, 0), true)?;
+ let _lock = open_backup_lockfile(user::USER_CFG_LOCKFILE, None, true)?;
let (mut config, expected_digest) = user::config()?;
digest: Option<String>,
) -> Result<Value, Error> {
- let _lock = open_file_locked(user::USER_CFG_LOCKFILE, std::time::Duration::new(10, 0), true)?;
+ let _lock = open_backup_lockfile(user::USER_CFG_LOCKFILE, None, true)?;
let (mut config, expected_digest) = user::config()?;
digest: Option<String>,
) -> Result<(), Error> {
- let _lock = open_file_locked(user::USER_CFG_LOCKFILE, std::time::Duration::new(10, 0), true)?;
+ let _lock = open_backup_lockfile(user::USER_CFG_LOCKFILE, None, true)?;
let (mut config, expected_digest) = user::config()?;
digest: Option<String>,
) -> Result<(), Error> {
- let _lock = open_file_locked(user::USER_CFG_LOCKFILE, std::time::Duration::new(10, 0), true)?;
+ let _lock = open_backup_lockfile(user::USER_CFG_LOCKFILE, None, true)?;
let (mut config, expected_digest) = user::config()?;
}
pub(crate) fn do_create_datastore(
- _lock: std::fs::File,
+ _lock: BackupLockGuard,
mut config: SectionConfigData,
datastore: DataStoreConfig,
worker: Option<&dyn TaskState>,
use proxmox::api::{api, ApiMethod, Router, RpcEnvironment, Permission};
use proxmox::http_err;
-use proxmox::tools::fs::open_file_locked;
use pbs_client::{HttpClient, HttpClientOptions};
use crate::config::cached_user_info::CachedUserInfo;
use crate::config::remote;
use crate::config::acl::{PRIV_REMOTE_AUDIT, PRIV_REMOTE_MODIFY};
+use crate::backup::open_backup_lockfile;
#[api(
input: {
/// Create new remote.
pub fn create_remote(password: String, param: Value) -> Result<(), Error> {
- let _lock = open_file_locked(remote::REMOTE_CFG_LOCKFILE, std::time::Duration::new(10, 0), true)?;
+ let _lock = open_backup_lockfile(remote::REMOTE_CFG_LOCKFILE, None, true)?;
let mut data = param;
data["password"] = Value::from(base64::encode(password.as_bytes()));
digest: Option<String>,
) -> Result<(), Error> {
- let _lock = open_file_locked(remote::REMOTE_CFG_LOCKFILE, std::time::Duration::new(10, 0), true)?;
+ let _lock = open_backup_lockfile(remote::REMOTE_CFG_LOCKFILE, None, true)?;
let (mut config, expected_digest) = remote::config()?;
}
}
- let _lock = open_file_locked(remote::REMOTE_CFG_LOCKFILE, std::time::Duration::new(10, 0), true)?;
+ let _lock = open_backup_lockfile(remote::REMOTE_CFG_LOCKFILE, None, true)?;
let (mut config, expected_digest) = remote::config()?;
use ::serde::{Deserialize, Serialize};
use proxmox::api::{api, Permission, Router, RpcEnvironment};
-use proxmox::tools::fs::open_file_locked;
use crate::api2::types::*;
use crate::config::cached_user_info::CachedUserInfo;
use crate::config::sync::{self, SyncJobConfig};
+use crate::backup::open_backup_lockfile;
pub fn check_sync_job_read_access(
user_info: &CachedUserInfo,
let auth_id: Authid = rpcenv.get_auth_id().unwrap().parse()?;
let user_info = CachedUserInfo::new()?;
- let _lock = open_file_locked(sync::SYNC_CFG_LOCKFILE, std::time::Duration::new(10, 0), true)?;
+ let _lock = open_backup_lockfile(sync::SYNC_CFG_LOCKFILE, None, true)?;
let sync_job: sync::SyncJobConfig = serde_json::from_value(param)?;
if !check_sync_job_modify_access(&user_info, &auth_id, &sync_job) {
let auth_id: Authid = rpcenv.get_auth_id().unwrap().parse()?;
let user_info = CachedUserInfo::new()?;
- let _lock = open_file_locked(sync::SYNC_CFG_LOCKFILE, std::time::Duration::new(10, 0), true)?;
+ let _lock = open_backup_lockfile(sync::SYNC_CFG_LOCKFILE, None, true)?;
// pass/compare digest
let (mut config, expected_digest) = sync::config()?;
let auth_id: Authid = rpcenv.get_auth_id().unwrap().parse()?;
let user_info = CachedUserInfo::new()?;
- let _lock = open_file_locked(sync::SYNC_CFG_LOCKFILE, std::time::Duration::new(10, 0), true)?;
+ let _lock = open_backup_lockfile(sync::SYNC_CFG_LOCKFILE, None, true)?;
let (mut config, expected_digest) = sync::config()?;
use ::serde::{Deserialize, Serialize};
use proxmox::api::{api, Router, RpcEnvironment, Permission};
-use proxmox::tools::fs::open_file_locked;
use crate::{
api2::types::{
MEDIA_POOL_NAME_SCHEMA,
SYNC_SCHEDULE_SCHEMA,
},
+ backup::open_backup_lockfile,
config::{
self,
cached_user_info::CachedUserInfo,
job: TapeBackupJobConfig,
_rpcenv: &mut dyn RpcEnvironment,
) -> Result<(), Error> {
-
- let _lock = open_file_locked(TAPE_JOB_CFG_LOCKFILE, std::time::Duration::new(10, 0), true)?;
+ let _lock = open_backup_lockfile(TAPE_JOB_CFG_LOCKFILE, None, true)?;
let (mut config, _digest) = config::tape_job::config()?;
delete: Option<Vec<DeletableProperty>>,
digest: Option<String>,
) -> Result<(), Error> {
- let _lock = open_file_locked(TAPE_JOB_CFG_LOCKFILE, std::time::Duration::new(10, 0), true)?;
+ let _lock = open_backup_lockfile(TAPE_JOB_CFG_LOCKFILE, None, true)?;
let (mut config, expected_digest) = config::tape_job::config()?;
digest: Option<String>,
_rpcenv: &mut dyn RpcEnvironment,
) -> Result<(), Error> {
- let _lock = open_file_locked(TAPE_JOB_CFG_LOCKFILE, std::time::Duration::new(10, 0), true)?;
+ let _lock = open_backup_lockfile(TAPE_JOB_CFG_LOCKFILE, None, true)?;
let (mut config, expected_digest) = config::tape_job::config()?;
RpcEnvironment,
Permission,
},
- tools::fs::open_file_locked,
};
use pbs_datastore::{KeyInfo, Kdf};
PASSWORD_HINT_SCHEMA,
},
backup::{
+ open_backup_lockfile,
KeyConfig,
Fingerprint,
},
bail!("Please specify a key derivation function (none is not allowed here).");
}
- let _lock = open_file_locked(
- TAPE_KEYS_LOCKFILE,
- std::time::Duration::new(10, 0),
- true,
- )?;
+ let _lock = open_backup_lockfile(TAPE_KEYS_LOCKFILE, None, true)?;
let (mut config_map, expected_digest) = load_key_configs()?;
digest: Option<String>,
_rpcenv: &mut dyn RpcEnvironment,
) -> Result<(), Error> {
-
- let _lock = open_file_locked(
- TAPE_KEYS_LOCKFILE,
- std::time::Duration::new(10, 0),
- true,
- )?;
+ let _lock = open_backup_lockfile(TAPE_KEYS_LOCKFILE, None, true)?;
let (mut config_map, expected_digest) = load_key_configs()?;
let (mut key_map, _) = load_keys()?;
use ::serde::{Deserialize, Serialize};
use proxmox::api::{api, Permission, Router, RpcEnvironment};
-use proxmox::tools::fs::open_file_locked;
use crate::api2::types::*;
};
use crate::config::cached_user_info::CachedUserInfo;
-
use crate::config::verify::{self, VerificationJobConfig};
+use crate::backup::open_backup_lockfile;
#[api(
input: {
user_info.check_privs(&auth_id, &["datastore", &verification_job.store], PRIV_DATASTORE_VERIFY, false)?;
- let _lock = open_file_locked(verify::VERIFICATION_CFG_LOCKFILE, std::time::Duration::new(10, 0), true)?;
+ let _lock = open_backup_lockfile(verify::VERIFICATION_CFG_LOCKFILE, None, true)?;
let (mut config, _digest) = verify::config()?;
let auth_id: Authid = rpcenv.get_auth_id().unwrap().parse()?;
let user_info = CachedUserInfo::new()?;
- let _lock = open_file_locked(verify::VERIFICATION_CFG_LOCKFILE, std::time::Duration::new(10, 0), true)?;
+ let _lock = open_backup_lockfile(verify::VERIFICATION_CFG_LOCKFILE, None, true)?;
// pass/compare digest
let (mut config, expected_digest) = verify::config()?;
let auth_id: Authid = rpcenv.get_auth_id().unwrap().parse()?;
let user_info = CachedUserInfo::new()?;
- let _lock = open_file_locked(verify::VERIFICATION_CFG_LOCKFILE, std::time::Duration::new(10, 0), true)?;
+ let _lock = open_backup_lockfile(verify::VERIFICATION_CFG_LOCKFILE, None, true)?;
let (mut config, expected_digest) = verify::config()?;
use proxmox::api::{api, Permission, RpcEnvironment, RpcEnvironmentType};
use proxmox::api::section_config::SectionConfigData;
use proxmox::api::router::Router;
-use proxmox::tools::fs::open_file_locked;
use crate::config::acl::{PRIV_SYS_AUDIT, PRIV_SYS_MODIFY};
use crate::tools::disks::{
use crate::api2::types::*;
use crate::config::datastore::{self, DataStoreConfig};
+use crate::backup::open_backup_lockfile;
#[api(
properties: {
systemd::start_unit(&mount_unit_name)?;
if add_datastore {
- let lock = open_file_locked(datastore::DATASTORE_CFG_LOCKFILE, std::time::Duration::new(10, 0), true)?;
+ let lock = open_backup_lockfile(datastore::DATASTORE_CFG_LOCKFILE, None, true)?;
let datastore: DataStoreConfig =
serde_json::from_value(json!({ "name": name, "path": mount_point }))?;
use proxmox::api::{api, ApiMethod, Router, RpcEnvironment, Permission};
use proxmox::api::schema::parse_property_string;
-use proxmox::tools::fs::open_file_locked;
use crate::config::network::{self, NetworkConfig};
use crate::config::acl::{PRIV_SYS_AUDIT, PRIV_SYS_MODIFY};
use crate::api2::types::*;
use crate::server::{WorkerTask};
+use crate::backup::open_backup_lockfile;
fn split_interface_list(list: &str) -> Result<Vec<String>, Error> {
let value = parse_property_string(&list, &NETWORK_INTERFACE_ARRAY_SCHEMA)?;
let interface_type = pbs_tools::json::required_string_param(¶m, "type")?;
let interface_type: NetworkInterfaceType = serde_json::from_value(interface_type.into())?;
- let _lock = open_file_locked(network::NETWORK_LOCKFILE, std::time::Duration::new(10, 0), true)?;
+ let _lock = open_backup_lockfile(network::NETWORK_LOCKFILE, None, true)?;
let (mut config, _digest) = network::config()?;
param: Value,
) -> Result<(), Error> {
- let _lock = open_file_locked(network::NETWORK_LOCKFILE, std::time::Duration::new(10, 0), true)?;
+ let _lock = open_backup_lockfile(network::NETWORK_LOCKFILE, None, true)?;
let (mut config, expected_digest) = network::config()?;
)]
/// Remove network interface configuration.
pub fn delete_interface(iface: String, digest: Option<String>) -> Result<(), Error> {
-
- let _lock = open_file_locked(network::NETWORK_LOCKFILE, std::time::Duration::new(10, 0), true)?;
+ let _lock = open_backup_lockfile(network::NETWORK_LOCKFILE, None, true)?;
let (mut config, expected_digest) = network::config()?;
use std::convert::TryFrom;
use std::str::FromStr;
use std::time::Duration;
-use std::fs::File;
use anyhow::{bail, format_err, Error};
use lazy_static::lazy_static;
-use proxmox::tools::fs::{replace_file, file_read_optional_string, CreateOptions, open_file_locked};
+use proxmox::tools::fs::{replace_file, file_read_optional_string, CreateOptions};
use pbs_api_types::upid::UPID;
use pbs_api_types::{Authid, GarbageCollectionStatus};
use crate::config::datastore::{self, DataStoreConfig};
use crate::tools;
+use crate::backup::{open_backup_lockfile, BackupLockGuard};
+
lazy_static! {
static ref DATASTORE_MAP: Mutex<HashMap<String, Arc<DataStore>>> = Mutex::new(HashMap::new());
fn lock_manifest(
&self,
backup_dir: &BackupDir,
- ) -> Result<File, Error> {
+ ) -> Result<BackupLockGuard, Error> {
let path = self.manifest_lock_path(backup_dir)?;
// update_manifest should never take a long time, so if someone else has
// the lock we can simply block a bit and should get it soon
- open_file_locked(&path, Duration::from_secs(5), true)
+ open_backup_lockfile(&path, Some(Duration::from_secs(5)), true)
.map_err(|err| {
format_err!(
"unable to acquire manifest lock {:?} - {}", &path, err
mod cached_chunk_reader;
pub use cached_chunk_reader::*;
+
+pub struct BackupLockGuard(std::fs::File);
+
+/// Open or create a lock file owned by user "backup" and lock it.
+///
+/// Owner/Group of the file is set to backup/backup.
+/// File mode is 0660.
+/// Default timeout is 10 seconds.
+///
+/// Note: This method needs to be called by user "root" or "backup".
+pub fn open_backup_lockfile<P: AsRef<std::path::Path>>(
+ path: P,
+ timeout: Option<std::time::Duration>,
+ exclusive: bool,
+) -> Result<BackupLockGuard, Error> {
+ let user = backup_user()?;
+ let options = proxmox::tools::fs::CreateOptions::new()
+ .perm(nix::sys::stat::Mode::from_bits_truncate(0o660))
+ .owner(user.uid)
+ .group(user.gid);
+
+ let timeout = timeout.unwrap_or(std::time::Duration::new(10, 0));
+
+ let file = proxmox::tools::fs::open_file_locked(&path, timeout, exclusive, options)?;
+ Ok(BackupLockGuard(file))
+}
use proxmox::tools::{fs::replace_file, fs::CreateOptions};
use crate::api2::types::PROXMOX_SAFE_ID_FORMAT;
+use crate::backup::{open_backup_lockfile, BackupLockGuard};
pub const PLUGIN_ID_SCHEMA: Schema = StringSchema::new("ACME Challenge Plugin ID.")
.format(&PROXMOX_SAFE_ID_FORMAT)
const ACME_PLUGIN_CFG_FILENAME: &str = pbs_buildcfg::configdir!("/acme/plugins.cfg");
const ACME_PLUGIN_CFG_LOCKFILE: &str = pbs_buildcfg::configdir!("/acme/.plugins.lck");
-const LOCK_TIMEOUT: std::time::Duration = std::time::Duration::from_secs(10);
-pub fn lock() -> Result<std::fs::File, Error> {
+pub fn lock() -> Result<BackupLockGuard, Error> {
super::make_acme_dir()?;
- proxmox::tools::fs::open_file_locked(ACME_PLUGIN_CFG_LOCKFILE, LOCK_TIMEOUT, true)
+ open_backup_lockfile(ACME_PLUGIN_CFG_LOCKFILE, None, true)
}
pub fn config() -> Result<(PluginData, [u8; 32]), Error> {
};
use proxmox::tools::fs::{
- open_file_locked,
replace_file,
CreateOptions,
};
use crate::api2::types::*;
+use crate::backup::{open_backup_lockfile, BackupLockGuard};
lazy_static! {
pub static ref CONFIG: SectionConfig = init();
pub const DATASTORE_CFG_LOCKFILE: &str = "/etc/proxmox-backup/.datastore.lck";
/// Get exclusive lock
-pub fn lock_config() -> Result<std::fs::File, Error> {
- open_file_locked(DATASTORE_CFG_LOCKFILE, std::time::Duration::new(10, 0), true)
+pub fn lock_config() -> Result<BackupLockGuard, Error> {
+ open_backup_lockfile(DATASTORE_CFG_LOCKFILE, None, true)
}
pub fn config() -> Result<(SectionConfigData, [u8;32]), Error> {
};
use proxmox::tools::fs::{
- open_file_locked,
replace_file,
CreateOptions,
};
use crate::api2::types::*;
+use crate::backup::{open_backup_lockfile, BackupLockGuard};
lazy_static! {
pub static ref CONFIG: SectionConfig = init();
pub const DOMAINS_CFG_LOCKFILE: &str = "/etc/proxmox-backup/.domains.lck";
/// Get exclusive lock
-pub fn lock_config() -> Result<std::fs::File, Error> {
- open_file_locked(DOMAINS_CFG_LOCKFILE, std::time::Duration::new(10, 0), true)
+pub fn lock_config() -> Result<BackupLockGuard, Error> {
+ open_backup_lockfile(DOMAINS_CFG_LOCKFILE, None, true)
}
pub fn config() -> Result<(SectionConfigData, [u8;32]), Error> {
},
},
tools::fs::{
- open_file_locked,
replace_file,
CreateOptions,
},
};
use crate::{
+ backup::{open_backup_lockfile, BackupLockGuard},
api2::types::{
DRIVE_NAME_SCHEMA,
VirtualTapeDrive,
pub const DRIVE_CFG_LOCKFILE: &str = "/etc/proxmox-backup/.tape.lck";
/// Get exclusive lock
-pub fn lock() -> Result<std::fs::File, Error> {
- open_file_locked(DRIVE_CFG_LOCKFILE, std::time::Duration::new(10, 0), true)
+pub fn lock() -> Result<BackupLockGuard, Error> {
+ open_backup_lockfile(DRIVE_CFG_LOCKFILE, None, true)
}
/// Read and parse the configuration file
}
},
tools::fs::{
- open_file_locked,
replace_file,
CreateOptions,
},
};
use crate::{
+ backup::{open_backup_lockfile, BackupLockGuard},
api2::types::{
MEDIA_POOL_NAME_SCHEMA,
MediaPoolConfig,
/// Get exclusive lock
-pub fn lock() -> Result<std::fs::File, Error> {
- open_file_locked(MEDIA_POOL_CFG_LOCKFILE, std::time::Duration::new(10, 0), true)
+pub fn lock() -> Result<BackupLockGuard, Error> {
+ open_backup_lockfile(MEDIA_POOL_CFG_LOCKFILE, None, true)
}
/// Read and parse the configuration file
use std::collections::HashSet;
-use std::fs::File;
-use std::time::Duration;
use anyhow::{bail, Error};
use nix::sys::stat::Mode;
use pbs_buildcfg::configdir;
+use crate::backup::{open_backup_lockfile, BackupLockGuard};
use crate::acme::AcmeClient;
use crate::api2::types::{
AcmeAccountName, AcmeDomain, ACME_DOMAIN_PROPERTY_SCHEMA, HTTP_PROXY_SCHEMA,
const CONF_FILE: &str = configdir!("/node.cfg");
const LOCK_FILE: &str = configdir!("/.node.lck");
-const LOCK_TIMEOUT: Duration = Duration::from_secs(10);
-pub fn lock() -> Result<File, Error> {
- proxmox::tools::fs::open_file_locked(LOCK_FILE, LOCK_TIMEOUT, true)
+pub fn lock() -> Result<BackupLockGuard, Error> {
+ open_backup_lockfile(LOCK_FILE, None, true)
}
/// Read the Node Config.
use proxmox::tools::fs::{
file_read_optional_string,
replace_file,
- open_file_locked,
CreateOptions,
};
use crate::{
backup::{
+ open_backup_lockfile,
Fingerprint,
KeyConfig,
},
/// Get the lock, load both files, insert the new key, store files.
pub fn insert_key(key: [u8;32], key_config: KeyConfig, force: bool) -> Result<(), Error> {
- let _lock = open_file_locked(
- TAPE_KEYS_LOCKFILE,
- std::time::Duration::new(10, 0),
- true,
- )?;
+ let _lock = open_backup_lockfile(TAPE_KEYS_LOCKFILE, None, true)?;
let (mut key_map, _) = load_keys()?;
let (mut config_map, _) = load_key_configs()?;
use std::os::unix::fs::OpenOptionsExt;
use std::os::unix::io::AsRawFd;
use std::path::PathBuf;
-use std::time::Duration;
use anyhow::{bail, format_err, Error};
use nix::sys::stat::Mode;
use pbs_buildcfg::configdir;
use crate::api2::types::Userid;
+use crate::backup::{open_backup_lockfile, BackupLockGuard};
/// Mapping of userid to TFA entry.
pub type TfaUsers = HashMap<Userid, TfaUserData>;
const CONF_FILE: &str = configdir!("/tfa.json");
const LOCK_FILE: &str = configdir!("/tfa.json.lock");
-const LOCK_TIMEOUT: Duration = Duration::from_secs(5);
const CHALLENGE_DATA_PATH: &str = pbs_buildcfg::rundir!("/tfa/challenges");
/// U2F registration challenges time out after 2 minutes.
const CHALLENGE_TIMEOUT: i64 = 2 * 60;
-pub fn read_lock() -> Result<File, Error> {
- proxmox::tools::fs::open_file_locked(LOCK_FILE, LOCK_TIMEOUT, false)
+pub fn read_lock() -> Result<BackupLockGuard, Error> {
+ open_backup_lockfile(LOCK_FILE, None, false)
}
-pub fn write_lock() -> Result<File, Error> {
- proxmox::tools::fs::open_file_locked(LOCK_FILE, LOCK_TIMEOUT, true)
+pub fn write_lock() -> Result<BackupLockGuard, Error> {
+ open_backup_lockfile(LOCK_FILE, None, true)
}
/// Read the TFA entries.
use std::collections::HashMap;
-use std::time::Duration;
use anyhow::{bail, format_err, Error};
use serde::{Serialize, Deserialize};
use serde_json::{from_value, Value};
-use proxmox::tools::fs::{open_file_locked, CreateOptions};
+use proxmox::tools::fs::CreateOptions;
use crate::api2::types::Authid;
use crate::auth;
+use crate::backup::open_backup_lockfile;
const LOCK_FILE: &str = pbs_buildcfg::configdir!("/token.shadow.lock");
const CONF_FILE: &str = pbs_buildcfg::configdir!("/token.shadow");
-const LOCK_TIMEOUT: Duration = Duration::from_secs(5);
#[derive(Serialize, Deserialize)]
#[serde(rename_all="kebab-case")]
bail!("not an API token ID");
}
- let _guard = open_file_locked(LOCK_FILE, LOCK_TIMEOUT, true)?;
+ let _guard = open_backup_lockfile(LOCK_FILE, None, true)?;
let mut data = read_file()?;
let hashed_secret = auth::encrypt_pw(secret)?;
bail!("not an API token ID");
}
- let _guard = open_file_locked(LOCK_FILE, LOCK_TIMEOUT, true)?;
+ let _guard = open_backup_lockfile(LOCK_FILE, None, true)?;
let mut data = read_file()?;
data.remove(tokenid);
//! # }
//!
//! ```
-use std::fs::File;
use std::path::{Path, PathBuf};
-use std::time::Duration;
use anyhow::{bail, format_err, Error};
use proxmox::tools::fs::{
- create_path, file_read_optional_string, open_file_locked, replace_file, CreateOptions,
+ create_path, file_read_optional_string, replace_file, CreateOptions,
};
use serde::{Deserialize, Serialize};
use crate::{
- tools::systemd::time::{
+ backup::{open_backup_lockfile, BackupLockGuard},
+ tools::systemd::time::{
parse_calendar_event,
compute_next_event,
},
jobname: String,
/// The State of the job
pub state: JobState,
- _lock: File,
+ _lock: BackupLockGuard,
}
const JOB_STATE_BASEDIR: &str = "/var/lib/proxmox-backup/jobstates";
path
}
-fn get_lock<P>(path: P) -> Result<File, Error>
+fn get_lock<P>(path: P) -> Result<BackupLockGuard, Error>
where
P: AsRef<Path>,
{
let mut path = path.as_ref().to_path_buf();
path.set_extension("lck");
- let lock = open_file_locked(&path, Duration::new(10, 0), true)?;
- let backup_user = crate::backup::backup_user()?;
- nix::unistd::chown(&path, Some(backup_user.uid), Some(backup_user.gid))?;
- Ok(lock)
+ open_backup_lockfile(&path, None, true)
}
/// Removes the statefile of a job, this is useful if we delete a job
use proxmox::sys::linux::procfs;
use proxmox::try_block;
-use proxmox::tools::fs::{create_path, open_file_locked, replace_file, CreateOptions};
+use proxmox::tools::fs::{create_path, replace_file, CreateOptions};
use super::{UPID, UPIDExt};
use crate::tools::logrotate::{LogRotate, LogRotateFiles};
use crate::tools::{FileLogger, FileLogOptions};
use crate::api2::types::{Authid, TaskStateType};
+use crate::backup::{open_backup_lockfile, BackupLockGuard};
macro_rules! taskdir {
($subdir:expr) => (concat!(pbs_buildcfg::PROXMOX_BACKUP_LOG_DIR_M!(), "/tasks", $subdir))
pub state: Option<TaskState>, // endtime, status
}
-fn lock_task_list_files(exclusive: bool) -> Result<std::fs::File, Error> {
- let backup_user = crate::backup::backup_user()?;
-
- let lock = open_file_locked(PROXMOX_BACKUP_TASK_LOCK_FN, std::time::Duration::new(10, 0), exclusive)?;
- nix::unistd::chown(PROXMOX_BACKUP_TASK_LOCK_FN, Some(backup_user.uid), Some(backup_user.gid))?;
-
- Ok(lock)
+fn lock_task_list_files(exclusive: bool) -> Result<BackupLockGuard, Error> {
+ open_backup_lockfile(PROXMOX_BACKUP_TASK_LOCK_FN, None, exclusive)
}
/// checks if the Task Archive is bigger that 'size_threshold' bytes, and
list: VecDeque<TaskListInfo>,
end: bool,
archive: Option<LogRotateFiles>,
- lock: Option<File>,
+ lock: Option<BackupLockGuard>,
}
impl TaskListInfoIterator {
mod lto;
pub use lto::*;
-use std::os::unix::io::AsRawFd;
use std::path::PathBuf;
use anyhow::{bail, format_err, Error};
use ::serde::{Deserialize};
use serde_json::Value;
+use nix::fcntl::OFlag;
+use nix::sys::stat::Mode;
use proxmox::{
tools::{
Uuid,
io::ReadExt,
fs::{
- fchown,
+ lock_file,
+ atomic_open_or_create_file,
file_read_optional_string,
replace_file,
CreateOptions,
pub struct DeviceLockGuard(std::fs::File);
-// Acquires an exclusive lock on `device_path`
-//
// Uses systemd escape_unit to compute a file name from `device_path`, the try
// to lock `/var/lock/<name>`.
-fn lock_device_path(device_path: &str) -> Result<DeviceLockGuard, TapeLockError> {
-
+fn open_device_lock(device_path: &str) -> Result<std::fs::File, Error> {
let lock_name = crate::tools::systemd::escape_unit(device_path, true);
let mut path = std::path::PathBuf::from(crate::tape::DRIVE_LOCK_DIR);
path.push(lock_name);
+ let user = crate::backup::backup_user()?;
+ let options = CreateOptions::new()
+ .perm(Mode::from_bits_truncate(0o660))
+ .owner(user.uid)
+ .group(user.gid);
+
+ atomic_open_or_create_file(
+ path,
+ OFlag::O_RDWR | OFlag::O_CLOEXEC | OFlag::O_APPEND,
+ &[],
+ options,
+ )
+}
+
+// Acquires an exclusive lock on `device_path`
+//
+fn lock_device_path(device_path: &str) -> Result<DeviceLockGuard, TapeLockError> {
+ let mut file = open_device_lock(device_path)?;
let timeout = std::time::Duration::new(10, 0);
- let mut file = std::fs::OpenOptions::new().create(true).append(true).open(path)?;
- if let Err(err) = proxmox::tools::fs::lock_file(&mut file, true, Some(timeout)) {
+ if let Err(err) = lock_file(&mut file, true, Some(timeout)) {
if err.kind() == std::io::ErrorKind::Interrupted {
return Err(TapeLockError::TimeOut);
} else {
}
}
- let backup_user = crate::backup::backup_user()?;
- fchown(file.as_raw_fd(), Some(backup_user.uid), Some(backup_user.gid))?;
-
Ok(DeviceLockGuard(file))
}
// non-blocking, and returning if the file is locked or not
fn test_device_path_lock(device_path: &str) -> Result<bool, Error> {
- let lock_name = crate::tools::systemd::escape_unit(device_path, true);
-
- let mut path = std::path::PathBuf::from(crate::tape::DRIVE_LOCK_DIR);
- path.push(lock_name);
+ let mut file = open_device_lock(device_path)?;
let timeout = std::time::Duration::new(0, 0);
- let mut file = std::fs::OpenOptions::new().create(true).append(true).open(path)?;
- match proxmox::tools::fs::lock_file(&mut file, true, Some(timeout)) {
+ match lock_file(&mut file, true, Some(timeout)) {
// file was not locked, continue
Ok(()) => {},
// file was locked, return true
Err(err) => bail!("{}", err),
}
- let backup_user = crate::backup::backup_user()?;
- fchown(file.as_raw_fd(), Some(backup_user.uid), Some(backup_user.gid))?;
-
Ok(false)
}
let mut lock_path = std::path::PathBuf::from(&self.path);
lock_path.push(".drive.lck");
+ let options = CreateOptions::new();
let timeout = std::time::Duration::new(10, 0);
- let lock = proxmox::tools::fs::open_file_locked(&lock_path, timeout, true)?;
+ let lock = proxmox::tools::fs::open_file_locked(&lock_path, timeout, true, options)?;
Ok(VirtualTapeHandle {
_lock: lock,
use std::collections::{HashMap, BTreeMap};
use std::path::{Path, PathBuf};
-use std::os::unix::io::AsRawFd;
-use std::fs::File;
use std::time::Duration;
use anyhow::{bail, Error};
use proxmox::tools::{
Uuid,
fs::{
- open_file_locked,
replace_file,
- fchown,
file_get_json,
CreateOptions,
},
MediaStatus,
MediaLocation,
},
+ backup::{open_backup_lockfile, BackupLockGuard},
tape::{
TAPE_STATUS_DIR,
MediaSet,
}
/// Lock the database
- fn lock(&self) -> Result<std::fs::File, Error> {
- let file = open_file_locked(&self.lockfile_path, std::time::Duration::new(10, 0), true)?;
- if cfg!(test) {
- // We cannot use chown inside test environment (no permissions)
- return Ok(file);
- }
-
- let backup_user = crate::backup::backup_user()?;
- fchown(file.as_raw_fd(), Some(backup_user.uid), Some(backup_user.gid))?;
-
- Ok(file)
+ fn lock(&self) -> Result<BackupLockGuard, Error> {
+ open_backup_lockfile(&self.lockfile_path, None, true)
}
fn load_media_db(path: &Path) -> Result<BTreeMap<Uuid, MediaStateEntry>, Error> {
}
/// Lock a media pool
-pub fn lock_media_pool(base_path: &Path, name: &str) -> Result<File, Error> {
+pub fn lock_media_pool(base_path: &Path, name: &str) -> Result<BackupLockGuard, Error> {
let mut path = base_path.to_owned();
path.push(format!(".pool-{}", name));
path.set_extension("lck");
- let timeout = std::time::Duration::new(10, 0);
- let lock = proxmox::tools::fs::open_file_locked(&path, timeout, true)?;
-
- if cfg!(test) {
- // We cannot use chown inside test environment (no permissions)
- return Ok(lock);
- }
-
- let backup_user = crate::backup::backup_user()?;
- fchown(lock.as_raw_fd(), Some(backup_user.uid), Some(backup_user.gid))?;
-
- Ok(lock)
+ open_backup_lockfile(&path, None, true)
}
/// Lock for media not assigned to any pool
-pub fn lock_unassigned_media_pool(base_path: &Path) -> Result<File, Error> {
+pub fn lock_unassigned_media_pool(base_path: &Path) -> Result<BackupLockGuard, Error> {
// lock artificial "__UNASSIGNED__" pool to avoid races
lock_media_pool(base_path, "__UNASSIGNED__")
}
base_path: &Path,
media_set_uuid: &Uuid,
timeout: Option<Duration>,
-) -> Result<File, Error> {
+) -> Result<BackupLockGuard, Error> {
let mut path = base_path.to_owned();
path.push(format!(".media-set-{}", media_set_uuid));
path.set_extension("lck");
- let timeout = timeout.unwrap_or(Duration::new(10, 0));
- let file = open_file_locked(&path, timeout, true)?;
- if cfg!(test) {
- // We cannot use chown inside test environment (no permissions)
- return Ok(file);
- }
-
- let backup_user = crate::backup::backup_user()?;
- fchown(file.as_raw_fd(), Some(backup_user.uid), Some(backup_user.gid))?;
-
- Ok(file)
+ open_backup_lockfile(&path, timeout, true)
}
// shell completion helper
//!
use std::path::{PathBuf, Path};
-use std::fs::File;
use anyhow::{bail, Error};
use ::serde::{Deserialize, Serialize};
use proxmox::tools::Uuid;
use crate::{
- backup::Fingerprint,
+ backup::{Fingerprint, BackupLockGuard},
api2::types::{
MediaStatus,
MediaLocation,
inventory: Inventory,
current_media_set: MediaSet,
- current_media_set_lock: Option<File>,
+ current_media_set_lock: Option<BackupLockGuard>,
}
impl MediaPool {
//! Memory based communication channel between proxy & daemon for things such as cache
//! invalidation.
-use std::ffi::CString;
-use std::io;
use std::os::unix::io::AsRawFd;
use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::Arc;
-use anyhow::{bail, format_err, Error};
-use nix::errno::Errno;
+use anyhow::Error;
use nix::fcntl::OFlag;
use nix::sys::mman::{MapFlags, ProtFlags};
use nix::sys::stat::Mode;
use once_cell::sync::OnceCell;
-use proxmox::sys::error::SysError;
-use proxmox::tools::fd::Fd;
+use proxmox::tools::fs::CreateOptions;
use proxmox::tools::mmap::Mmap;
/// In-memory communication channel.
static INSTANCE: OnceCell<Arc<Memcom>> = OnceCell::new();
const MEMCOM_FILE_PATH: &str = pbs_buildcfg::rundir!("/proxmox-backup-memcom");
+const EMPTY_PAGE: [u8; 4096] = [0u8; 4096];
impl Memcom {
/// Open the memory based communication channel singleton.
// Actual work of `new`:
fn open() -> Result<Arc<Self>, Error> {
- let fd = match open_existing() {
- Ok(fd) => fd,
- Err(err) if err.not_found() => create_new()?,
- Err(err) => bail!("failed to open {} - {}", MEMCOM_FILE_PATH, err),
- };
+ let user = crate::backup::backup_user()?;
+ let options = CreateOptions::new()
+ .perm(Mode::from_bits_truncate(0o660))
+ .owner(user.uid)
+ .group(user.gid);
+
+ let file = proxmox::tools::fs::atomic_open_or_create_file(
+ MEMCOM_FILE_PATH,
+ OFlag::O_RDWR | OFlag::O_CLOEXEC,
+ &EMPTY_PAGE, options)?;
let mmap = unsafe {
Mmap::<u8>::map_fd(
- fd.as_raw_fd(),
+ file.as_raw_fd(),
0,
4096,
ProtFlags::PROT_READ | ProtFlags::PROT_WRITE,
.fetch_add(1, Ordering::AcqRel);
}
}
-
-/// The fast path opens an existing file.
-fn open_existing() -> Result<Fd, nix::Error> {
- Fd::open(
- MEMCOM_FILE_PATH,
- OFlag::O_RDWR | OFlag::O_CLOEXEC,
- Mode::empty(),
- )
-}
-
-/// Since we need to initialize the file, we also need a solid slow path where we create the file.
-/// In order to make sure the next user's `open()` vs `mmap()` race against our `truncate()` call,
-/// we create it in a temporary location and rotate it in place.
-fn create_new() -> Result<Fd, Error> {
- // create a temporary file:
- let temp_file_name = format!("{}.{}", MEMCOM_FILE_PATH, unsafe { libc::getpid() });
- let fd = Fd::open(
- temp_file_name.as_str(),
- OFlag::O_CREAT | OFlag::O_EXCL | OFlag::O_RDWR | OFlag::O_CLOEXEC,
- Mode::from_bits_truncate(0o660),
- )
- .map_err(|err| {
- format_err!(
- "failed to create new in-memory communication file at {} - {}",
- temp_file_name,
- err
- )
- })?;
-
- // let it be a page in size, it'll be initialized to zero by the kernel
- nix::unistd::ftruncate(fd.as_raw_fd(), 4096)
- .map_err(|err| format_err!("failed to set size of {} - {}", temp_file_name, err))?;
-
- // if this is the pbs-daemon (running as root) rather than the proxy (running as backup user),
- // make sure the backup user can access the file:
- if let Ok(backup_user) = crate::backup::backup_user() {
- match nix::unistd::fchown(fd.as_raw_fd(), None, Some(backup_user.gid)) {
- Ok(()) => (),
- Err(err) if err.is_errno(Errno::EPERM) => {
- // we're not the daemon (root), so the file is already owned by the backup user
- }
- Err(err) => bail!(
- "failed to set group to 'backup' for {} - {}",
- temp_file_name,
- err
- ),
- }
- }
-
- // rotate the file into place, but use `RENAME_NOREPLACE`, so in case 2 processes race against
- // the initialization, the first one wins!
- // TODO: nicer `renameat2()` wrapper in `proxmox::sys`?
- let c_file_name = CString::new(temp_file_name.as_bytes()).unwrap();
- let new_path = CString::new(MEMCOM_FILE_PATH).unwrap();
- let rc = unsafe {
- libc::renameat2(
- -1,
- c_file_name.as_ptr(),
- -1,
- new_path.as_ptr(),
- libc::RENAME_NOREPLACE,
- )
- };
- if rc == 0 {
- return Ok(fd);
- }
- let err = io::Error::last_os_error();
-
- // if another process has already raced ahead and created the file, let's just open theirs
- // instead:
- if err.kind() == io::ErrorKind::AlreadyExists {
- // someone beat us to it:
- drop(fd);
- return open_existing().map_err(Error::from);
- }
-
- // for any other errors, just bail out
- bail!(
- "failed to move file at {} into place at {} - {}",
- temp_file_name,
- MEMCOM_FILE_PATH,
- err
- );
-}