public inbox for pbs-devel@lists.proxmox.com
 help / color / mirror / Atom feed
* [PATCH proxmox-backup v3 00/15] fix 7642: avoid expensive uid/gid lookups for lock- and config-files
@ 2026-07-01 14:03 Christian Ebner
  2026-07-01 14:03 ` [PATCH proxmox-backup v3 01/15] bin: api: early init proxmox-product-config Christian Ebner
                   ` (14 more replies)
  0 siblings, 15 replies; 16+ messages in thread
From: Christian Ebner @ 2026-07-01 14:03 UTC (permalink / raw)
  To: pbs-devel

As reported by a user in the community forum [0] and the thereof
resulting bugzilla entry [1], phase 2 of garbage collection currently
produces avoidable syscalls and parsing overhead, reading
/etc/nsswitch.conf and /etc/passwd for each per-chunk file locking
call. This is however not limited to garbage collection, but rather
affects file locking in general. The backup_user() lookup was
identified as the culprit.

This patch series fixes this by switching to proxmox-product-config
helpers for config file operations and file locks instead, which does
use the user as parsed once on process init.

[0] https://forum.proxmox.com/threads/s3-gc-phase-2-nsswitch-passwd-overhead.183709/
[1] https://bugzilla.proxmox.com/show_bug.cgi?id=7642

Change since version 2:
- Extend previous patch series by refactoring also other callers of
  pbs_config::backup_user() and use proxmox-product-config helpers
  where possible

Change since version 1 (thanks @Fabian for feedback):
- Instead of fixing this for S3 file locking only, do it for all
  file locks using the open_backup_lockfile().
- Use proxmox-product-config helpers and init user as required
Note: Above changes significantly escalated the scope of the patches,
as they do now touch any config update as well and in the calling
process the init() must have been called once before.


proxmox-backup:

Christian Ebner (15):
  bin: api: early init proxmox-product-config
  bin: daily update: refactor to use proxmox-product-config
  pbs-config: use proxmox-product-config::replace_secret_config()
  pbs-config: use proxmox-product-config::replace_config()
  fix #7642: avoid expensive user lookups on file locking
  pbs-config: use proxmox-product-config helpers
  pbs-config: drop backup_group helper, use users gid instead
  pbs-datastore: use proxmox-product-config cached backup user
  pbs-datastore: use general helpers for file lock create options
  server: auth helpers: use proxmox-product-config create options
    helpers
  api: subscription: use proxmox-product-config create options
  tape: use proxmox-product-config helper for user lookup
  tape: use proxmox-product-config lock file create options
  tape: use proxmox-product-config to generate create options
  tree-wide: use proxmox-product-config::get_api_user for user lookup

 pbs-config/Cargo.toml                         |  1 +
 pbs-config/src/acl.rs                         |  5 +-
 pbs-config/src/config_version_cache.rs        |  2 +-
 pbs-config/src/datastore.rs                   |  5 +-
 pbs-config/src/domains.rs                     |  5 +-
 pbs-config/src/drive.rs                       |  5 +-
 pbs-config/src/encryption_keys.rs             |  9 +--
 pbs-config/src/lib.rs                         | 56 ++-----------------
 pbs-config/src/media_pool.rs                  |  5 +-
 pbs-config/src/metrics.rs                     |  3 +-
 pbs-config/src/node.rs                        |  3 +-
 pbs-config/src/notifications.rs               |  8 ++-
 pbs-config/src/prune.rs                       |  5 +-
 pbs-config/src/remote.rs                      |  3 +-
 pbs-config/src/s3.rs                          |  5 +-
 pbs-config/src/sync.rs                        |  5 +-
 pbs-config/src/tape_job.rs                    |  5 +-
 pbs-config/src/token_shadow.rs                |  8 +--
 pbs-config/src/traffic_control.rs             |  5 +-
 pbs-config/src/user.rs                        |  5 +-
 pbs-config/src/verify.rs                      |  5 +-
 pbs-datastore/Cargo.toml                      |  1 +
 pbs-datastore/src/chunk_store.rs              | 27 +++++----
 pbs-datastore/src/datastore.rs                | 17 +++---
 pbs-datastore/src/move_journal.rs             | 11 +---
 pbs-datastore/src/task_tracking.rs            |  7 +--
 src/api2/admin/datastore.rs                   |  2 +-
 src/api2/admin/s3.rs                          |  4 +-
 src/api2/config/datastore.rs                  |  2 +-
 src/api2/config/s3.rs                         |  2 +-
 src/api2/node/subscription.rs                 | 11 +---
 src/auth_helpers.rs                           | 27 ++-------
 src/bin/proxmox-backup-api.rs                 |  5 +-
 src/bin/proxmox-backup-proxy.rs               |  5 +-
 src/bin/proxmox-daily-update.rs               | 12 ++--
 src/bin/proxmox-tape.rs                       |  8 +++
 .../proxmox_backup_manager/subscription.rs    |  4 +-
 src/bin/sg-tape-cmd.rs                        |  5 +-
 src/config/mod.rs                             |  9 +--
 src/server/jobstate.rs                        |  4 +-
 src/server/metric_collection/mod.rs           |  2 +-
 src/server/metric_collection/pull_metrics.rs  |  2 +-
 src/server/metric_collection/rrd.rs           |  2 +-
 src/server/mod.rs                             |  6 +-
 src/server/notifications/mod.rs               |  4 +-
 src/tape/changer/mod.rs                       |  2 +-
 src/tape/drive/mod.rs                         | 11 +---
 src/tape/encryption_keys.rs                   |  5 +-
 src/tape/inventory.rs                         |  6 +-
 src/tape/media_catalog.rs                     | 14 ++---
 src/tape/media_catalog_cache.rs               | 10 +---
 src/tape/mod.rs                               |  8 +--
 src/tape/test/alloc_writable_media.rs         | 17 ++++++
 src/traffic_control_cache.rs                  |  2 +-
 54 files changed, 171 insertions(+), 236 deletions(-)


Summary over all repositories:
  54 files changed, 171 insertions(+), 236 deletions(-)

-- 
Generated by murpp 0.11.0




^ permalink raw reply	[flat|nested] 16+ messages in thread

* [PATCH proxmox-backup v3 01/15] bin: api: early init proxmox-product-config
  2026-07-01 14:03 [PATCH proxmox-backup v3 00/15] fix 7642: avoid expensive uid/gid lookups for lock- and config-files Christian Ebner
@ 2026-07-01 14:03 ` Christian Ebner
  2026-07-01 14:03 ` [PATCH proxmox-backup v3 02/15] bin: daily update: refactor to use proxmox-product-config Christian Ebner
                   ` (13 subsequent siblings)
  14 siblings, 0 replies; 16+ messages in thread
From: Christian Ebner @ 2026-07-01 14:03 UTC (permalink / raw)
  To: pbs-devel

In preparation for switching over config related helpers to the
proxmox-product-config. These helpers rely on the priv- and api-user
to already have been initialized.

Signed-off-by: Christian Ebner <c.ebner@proxmox.com>
---
 src/bin/proxmox-backup-api.rs | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/src/bin/proxmox-backup-api.rs b/src/bin/proxmox-backup-api.rs
index e49532810..710a70042 100644
--- a/src/bin/proxmox-backup-api.rs
+++ b/src/bin/proxmox-backup-api.rs
@@ -48,6 +48,9 @@ async fn run() -> Result<(), Error> {
         .tasklog_pbs()
         .init()?;
 
+    let backup_user = pbs_config::backup_user()?;
+    proxmox_product_config::init(backup_user.clone(), pbs_config::priv_user()?);
+
     config::create_configdir()?;
 
     config::update_self_signed_cert(false)?;
@@ -75,10 +78,8 @@ async fn run() -> Result<(), Error> {
 
     proxmox_backup::auth_helpers::setup_auth_context(true);
     proxmox_backup::server::notifications::init()?;
-    let backup_user = pbs_config::backup_user()?;
     let mut command_sock = proxmox_daemon::command_socket::CommandSocket::new(backup_user.gid);
 
-    proxmox_product_config::init(backup_user.clone(), pbs_config::priv_user()?);
     proxmox_acme_api::init(configdir!("/acme"), true)?;
 
     let dir_opts = CreateOptions::new()
-- 
2.47.3





^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [PATCH proxmox-backup v3 02/15] bin: daily update: refactor to use proxmox-product-config
  2026-07-01 14:03 [PATCH proxmox-backup v3 00/15] fix 7642: avoid expensive uid/gid lookups for lock- and config-files Christian Ebner
  2026-07-01 14:03 ` [PATCH proxmox-backup v3 01/15] bin: api: early init proxmox-product-config Christian Ebner
@ 2026-07-01 14:03 ` Christian Ebner
  2026-07-01 14:04 ` [PATCH proxmox-backup v3 03/15] pbs-config: use proxmox-product-config::replace_secret_config() Christian Ebner
                   ` (12 subsequent siblings)
  14 siblings, 0 replies; 16+ messages in thread
From: Christian Ebner @ 2026-07-01 14:03 UTC (permalink / raw)
  To: pbs-devel

Switch to use the generic proxmox-product-config helpers for the daily
update binary. This follows along the lines of what Proxmox Datacenter
Manager does.

The create option permissions for rest server worker task init are now
implicitly set to 0640 via the default_create_options() helper, the
worker task setting them as required on-demand.

Signed-off-by: Christian Ebner <c.ebner@proxmox.com>
---
 src/bin/proxmox-daily-update.rs | 12 +++++-------
 1 file changed, 5 insertions(+), 7 deletions(-)

diff --git a/src/bin/proxmox-daily-update.rs b/src/bin/proxmox-daily-update.rs
index 42ce62d16..597722497 100644
--- a/src/bin/proxmox-daily-update.rs
+++ b/src/bin/proxmox-daily-update.rs
@@ -4,7 +4,6 @@ use serde_json::json;
 use proxmox_notify::context::pbs::PBS_CONTEXT;
 use proxmox_router::{ApiHandler, RpcEnvironment, cli::*};
 use proxmox_subscription::SubscriptionStatus;
-use proxmox_sys::fs::CreateOptions;
 
 use pbs_buildcfg::configdir;
 use proxmox_backup::api2;
@@ -91,16 +90,15 @@ async fn check_acme_certificates(rpcenv: &mut dyn RpcEnvironment) -> Result<(),
 }
 
 async fn run(rpcenv: &mut dyn RpcEnvironment) -> Result<(), Error> {
-    let backup_user = pbs_config::backup_user()?;
-    let file_opts = CreateOptions::new()
-        .owner(backup_user.uid)
-        .group(backup_user.gid);
+    proxmox_product_config::init(pbs_config::backup_user()?, pbs_config::priv_user()?);
     proxmox_rest_server::init_worker_tasks(
         pbs_buildcfg::PROXMOX_BACKUP_LOG_DIR_M!().into(),
-        file_opts,
+        proxmox_product_config::default_create_options(),
     )?;
 
-    let mut command_sock = proxmox_daemon::command_socket::CommandSocket::new(backup_user.gid);
+    let mut command_sock = proxmox_daemon::command_socket::CommandSocket::new(
+        proxmox_product_config::get_api_user().gid,
+    );
     proxmox_rest_server::register_task_control_commands(&mut command_sock)?;
     command_sock.spawn(proxmox_rest_server::last_worker_future())?;
 
-- 
2.47.3





^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [PATCH proxmox-backup v3 03/15] pbs-config: use proxmox-product-config::replace_secret_config()
  2026-07-01 14:03 [PATCH proxmox-backup v3 00/15] fix 7642: avoid expensive uid/gid lookups for lock- and config-files Christian Ebner
  2026-07-01 14:03 ` [PATCH proxmox-backup v3 01/15] bin: api: early init proxmox-product-config Christian Ebner
  2026-07-01 14:03 ` [PATCH proxmox-backup v3 02/15] bin: daily update: refactor to use proxmox-product-config Christian Ebner
@ 2026-07-01 14:04 ` Christian Ebner
  2026-07-01 14:04 ` [PATCH proxmox-backup v3 04/15] pbs-config: use proxmox-product-config::replace_config() Christian Ebner
                   ` (11 subsequent siblings)
  14 siblings, 0 replies; 16+ messages in thread
From: Christian Ebner @ 2026-07-01 14:04 UTC (permalink / raw)
  To: pbs-devel

Instead of using the pbs-config local implementation, use the product
general implementation, dropping the local one instead.

Since proxmox-product-config::replace_secret_config() requires the
api- and priv-user to be initialized, any calling codepath must
guarantee to have run proxmox-product-config::init() once, so do
that for proxmox-tape as well, while proxy, api and manager are
already initializing it.

Signed-off-by: Christian Ebner <c.ebner@proxmox.com>
---
 pbs-config/Cargo.toml           |  1 +
 pbs-config/src/lib.rs           | 17 -----------------
 pbs-config/src/notifications.rs |  5 ++++-
 src/bin/proxmox-tape.rs         |  8 ++++++++
 src/tape/encryption_keys.rs     |  3 ++-
 5 files changed, 15 insertions(+), 19 deletions(-)

diff --git a/pbs-config/Cargo.toml b/pbs-config/Cargo.toml
index 04687cb59..d07b4de89 100644
--- a/pbs-config/Cargo.toml
+++ b/pbs-config/Cargo.toml
@@ -23,6 +23,7 @@ proxmox-http.workspace = true
 proxmox-lang.workspace = true
 proxmox-notify.workspace = true
 proxmox-router = { workspace = true, default-features = false }
+proxmox-product-config.workspace = true
 proxmox-s3-client.workspace = true
 proxmox-schema.workspace = true
 proxmox-section-config.workspace = true
diff --git a/pbs-config/src/lib.rs b/pbs-config/src/lib.rs
index a194d70ac..1d32d23e2 100644
--- a/pbs-config/src/lib.rs
+++ b/pbs-config/src/lib.rs
@@ -139,23 +139,6 @@ pub fn replace_backup_config<P: AsRef<std::path::Path>>(path: P, data: &[u8]) ->
     Ok(())
 }
 
-/// Atomically write data to file owned by "root:root" with permission "0600"
-///
-/// Only the superuser can read and write those files.
-pub fn replace_secret_config<P: AsRef<std::path::Path>>(path: P, data: &[u8]) -> Result<(), Error> {
-    let mode = nix::sys::stat::Mode::from_bits_truncate(0o0600);
-    // set the correct owner/group/permissions while saving file
-    // owner(rw) = root, group(r)= root
-    let options = proxmox_sys::fs::CreateOptions::new()
-        .perm(mode)
-        .owner(nix::unistd::ROOT)
-        .group(nix::unistd::Gid::from_raw(0));
-
-    proxmox_sys::fs::replace_file(path, data, options, true)?;
-
-    Ok(())
-}
-
 /// Detect modified configuration files
 ///
 /// This function fails with a reasonable error message if checksums do not match.
diff --git a/pbs-config/src/notifications.rs b/pbs-config/src/notifications.rs
index 3ee019f23..cbdbcee7a 100644
--- a/pbs-config/src/notifications.rs
+++ b/pbs-config/src/notifications.rs
@@ -35,7 +35,10 @@ pub fn config() -> Result<Config, Error> {
 pub fn save_config(config: Config) -> Result<(), Error> {
     let (cfg, priv_cfg) = config.write()?;
     crate::replace_backup_config(NOTIFICATION_CONFIG_PATH, cfg.as_bytes())?;
-    crate::replace_secret_config(NOTIFICATION_PRIV_CONFIG_PATH, priv_cfg.as_bytes())?;
+    proxmox_product_config::replace_secret_config(
+        NOTIFICATION_PRIV_CONFIG_PATH,
+        priv_cfg.as_bytes(),
+    )?;
 
     Ok(())
 }
diff --git a/src/bin/proxmox-tape.rs b/src/bin/proxmox-tape.rs
index 1d26b31e8..8e52bc5d0 100644
--- a/src/bin/proxmox-tape.rs
+++ b/src/bin/proxmox-tape.rs
@@ -1116,5 +1116,13 @@ fn main() {
     let mut rpcenv = CliEnvironment::new();
     rpcenv.set_auth_id(Some(String::from("root@pam")));
 
+    if let Err(err) = proxmox_lang::try_block!({
+        proxmox_product_config::init(pbs_config::backup_user()?, pbs_config::priv_user()?);
+        Ok::<(), Error>(())
+    }) {
+        eprintln!("Failed on product config init: {err}");
+        std::process::exit(-1);
+    }
+
     proxmox_async::runtime::main(run_async_cli_command(cmd_def, rpcenv));
 }
diff --git a/src/tape/encryption_keys.rs b/src/tape/encryption_keys.rs
index 42e4931a5..1af7decaa 100644
--- a/src/tape/encryption_keys.rs
+++ b/src/tape/encryption_keys.rs
@@ -18,8 +18,9 @@ use serde::{Deserialize, Serialize};
 use proxmox_sys::fs::file_read_optional_string;
 
 use pbs_api_types::Fingerprint;
-use pbs_config::{open_backup_lockfile, replace_backup_config, replace_secret_config};
+use pbs_config::{open_backup_lockfile, replace_backup_config};
 use pbs_key_config::KeyConfig;
+use proxmox_product_config::replace_secret_config;
 
 mod hex_key {
     use hex::FromHex;
-- 
2.47.3





^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [PATCH proxmox-backup v3 04/15] pbs-config: use proxmox-product-config::replace_config()
  2026-07-01 14:03 [PATCH proxmox-backup v3 00/15] fix 7642: avoid expensive uid/gid lookups for lock- and config-files Christian Ebner
                   ` (2 preceding siblings ...)
  2026-07-01 14:04 ` [PATCH proxmox-backup v3 03/15] pbs-config: use proxmox-product-config::replace_secret_config() Christian Ebner
@ 2026-07-01 14:04 ` Christian Ebner
  2026-07-01 14:04 ` [PATCH proxmox-backup v3 05/15] fix #7642: avoid expensive user lookups on file locking Christian Ebner
                   ` (10 subsequent siblings)
  14 siblings, 0 replies; 16+ messages in thread
From: Christian Ebner @ 2026-07-01 14:04 UTC (permalink / raw)
  To: pbs-devel

Instead of using the pbs-config local implementation, use the product
general implementation, dropping the local one instead.

Since proxmox-product-config::replace_config() requires the api- and
priv-user to be initialized a-priori, any calling codepath must
guarantee to have run proxmox-product-config::init() once.

Signed-off-by: Christian Ebner <c.ebner@proxmox.com>
---
 pbs-config/src/acl.rs             |  5 +++--
 pbs-config/src/datastore.rs       |  5 +++--
 pbs-config/src/domains.rs         |  5 +++--
 pbs-config/src/drive.rs           |  5 +++--
 pbs-config/src/encryption_keys.rs |  7 ++++---
 pbs-config/src/lib.rs             | 18 ------------------
 pbs-config/src/media_pool.rs      |  5 +++--
 pbs-config/src/metrics.rs         |  3 ++-
 pbs-config/src/node.rs            |  3 ++-
 pbs-config/src/notifications.rs   |  3 ++-
 pbs-config/src/prune.rs           |  5 +++--
 pbs-config/src/remote.rs          |  3 ++-
 pbs-config/src/s3.rs              |  5 +++--
 pbs-config/src/sync.rs            |  5 +++--
 pbs-config/src/tape_job.rs        |  5 +++--
 pbs-config/src/traffic_control.rs |  5 +++--
 pbs-config/src/user.rs            |  5 +++--
 pbs-config/src/verify.rs          |  5 +++--
 src/config/mod.rs                 |  5 +++--
 src/tape/encryption_keys.rs       |  6 +++---
 20 files changed, 54 insertions(+), 54 deletions(-)

diff --git a/pbs-config/src/acl.rs b/pbs-config/src/acl.rs
index ea858cf4d..d70c6b164 100644
--- a/pbs-config/src/acl.rs
+++ b/pbs-config/src/acl.rs
@@ -6,11 +6,12 @@ use std::sync::{Arc, LazyLock, RwLock};
 
 use anyhow::{Error, bail};
 
+use proxmox_product_config::replace_config;
 use proxmox_schema::{ApiStringFormat, ApiType, Schema, StringSchema};
 
 use pbs_api_types::{Authid, ROLE_NAME_NO_ACCESS, Role, Userid};
 
-use crate::{BackupLockGuard, open_backup_lockfile, replace_backup_config};
+use crate::{BackupLockGuard, open_backup_lockfile};
 
 /// Map of pre-defined [Roles](Role) to their associated
 /// [privileges](pbs_api_types::PRIVILEGES) combination and description.
@@ -767,7 +768,7 @@ pub fn save_config(acl: &AclTree) -> Result<(), Error> {
 
     acl.write_config(&mut raw)?;
 
-    replace_backup_config(ACL_CFG_FILENAME, &raw)
+    replace_config(ACL_CFG_FILENAME, &raw)
 }
 
 #[cfg(test)]
diff --git a/pbs-config/src/datastore.rs b/pbs-config/src/datastore.rs
index 0fb8d99c7..32a5a8019 100644
--- a/pbs-config/src/datastore.rs
+++ b/pbs-config/src/datastore.rs
@@ -3,12 +3,13 @@ use std::sync::LazyLock;
 
 use anyhow::Error;
 
+use proxmox_product_config::replace_config;
 use proxmox_schema::{AllOfSchema, ApiType};
 use proxmox_section_config::{SectionConfig, SectionConfigData, SectionConfigPlugin};
 
 use pbs_api_types::{DATASTORE_SCHEMA, DataStoreConfig, DatastoreBackendConfig, DatastoreTuning};
 
-use crate::{BackupLockGuard, ConfigVersionCache, open_backup_lockfile, replace_backup_config};
+use crate::{BackupLockGuard, ConfigVersionCache, open_backup_lockfile};
 
 pub static CONFIG: LazyLock<SectionConfig> = LazyLock::new(init);
 
@@ -45,7 +46,7 @@ pub fn config() -> Result<(SectionConfigData, [u8; 32]), Error> {
 
 pub fn save_config(config: &SectionConfigData) -> Result<(), Error> {
     let raw = CONFIG.write(DATASTORE_CFG_FILENAME, config)?;
-    replace_backup_config(DATASTORE_CFG_FILENAME, raw.as_bytes())?;
+    replace_config(DATASTORE_CFG_FILENAME, raw.as_bytes())?;
 
     // used in pbs-datastore
     let version_cache = ConfigVersionCache::new()?;
diff --git a/pbs-config/src/domains.rs b/pbs-config/src/domains.rs
index 054904dcb..0b0eaea53 100644
--- a/pbs-config/src/domains.rs
+++ b/pbs-config/src/domains.rs
@@ -4,10 +4,11 @@ use std::sync::LazyLock;
 use anyhow::Error;
 
 use pbs_buildcfg::configdir;
+use proxmox_product_config::replace_config;
 use proxmox_schema::{ApiType, ObjectSchema};
 use proxmox_section_config::{SectionConfig, SectionConfigData, SectionConfigPlugin};
 
-use crate::{BackupLockGuard, open_backup_lockfile, replace_backup_config};
+use crate::{BackupLockGuard, open_backup_lockfile};
 use pbs_api_types::{
     AdRealmConfig, LdapRealmConfig, OpenIdRealmConfig, PamRealmConfig, PbsRealmConfig,
     REALM_ID_SCHEMA,
@@ -75,7 +76,7 @@ pub fn config() -> Result<(SectionConfigData, [u8; 32]), Error> {
 
 pub fn save_config(config: &SectionConfigData) -> Result<(), Error> {
     let raw = CONFIG.write(DOMAINS_CFG_FILENAME, config)?;
-    replace_backup_config(DOMAINS_CFG_FILENAME, raw.as_bytes())
+    replace_config(DOMAINS_CFG_FILENAME, raw.as_bytes())
 }
 
 /// Unsets the default login realm for users by deleting the `default` property
diff --git a/pbs-config/src/drive.rs b/pbs-config/src/drive.rs
index 4a782e9cb..a016dc6b9 100644
--- a/pbs-config/src/drive.rs
+++ b/pbs-config/src/drive.rs
@@ -16,10 +16,11 @@ use std::sync::LazyLock;
 
 use anyhow::{Error, bail};
 
+use proxmox_product_config::replace_config;
 use proxmox_schema::*;
 use proxmox_section_config::{SectionConfig, SectionConfigData, SectionConfigPlugin};
 
-use crate::{BackupLockGuard, open_backup_lockfile, replace_backup_config};
+use crate::{BackupLockGuard, open_backup_lockfile};
 
 use pbs_api_types::{DRIVE_NAME_SCHEMA, LtoTapeDrive, ScsiTapeChanger, VirtualTapeDrive};
 
@@ -77,7 +78,7 @@ pub fn config() -> Result<(SectionConfigData, [u8; 32]), Error> {
 /// Save the configuration file
 pub fn save_config(config: &SectionConfigData) -> Result<(), Error> {
     let raw = CONFIG.write(DRIVE_CFG_FILENAME, config)?;
-    replace_backup_config(DRIVE_CFG_FILENAME, raw.as_bytes())
+    replace_config(DRIVE_CFG_FILENAME, raw.as_bytes())
 }
 
 /// Check if the specified drive name exists in the config.
diff --git a/pbs-config/src/encryption_keys.rs b/pbs-config/src/encryption_keys.rs
index 98316c753..3f8870254 100644
--- a/pbs-config/src/encryption_keys.rs
+++ b/pbs-config/src/encryption_keys.rs
@@ -6,6 +6,7 @@ use nix::{sys::stat::Mode, unistd::Uid};
 use serde::Deserialize;
 
 use pbs_api_types::{CRYPT_KEY_ID_SCHEMA, CryptKey, KeyInfo};
+use proxmox_product_config::replace_config;
 use proxmox_schema::ApiType;
 use proxmox_section_config::{SectionConfig, SectionConfigData, SectionConfigPlugin};
 use proxmox_sys::fs::CreateOptions;
@@ -13,7 +14,7 @@ use proxmox_sys::fs::CreateOptions;
 use pbs_buildcfg::configdir;
 use pbs_key_config::KeyConfig;
 
-use crate::{BackupLockGuard, open_backup_lockfile, replace_backup_config};
+use crate::{BackupLockGuard, open_backup_lockfile};
 
 pub static CONFIG: LazyLock<SectionConfig> = LazyLock::new(init);
 
@@ -57,7 +58,7 @@ pub fn config() -> Result<(SectionConfigData, [u8; 32]), Error> {
 /// Save given key configuration to file.
 pub fn save_config(config: &SectionConfigData) -> Result<(), Error> {
     let raw = CONFIG.write(ENCRYPTION_KEYS_CFG_FILENAME, config)?;
-    replace_backup_config(ENCRYPTION_KEYS_CFG_FILENAME, raw.as_bytes())
+    replace_config(ENCRYPTION_KEYS_CFG_FILENAME, raw.as_bytes())
 }
 
 /// Shell completion helper to complete encryption key id's as found in the config.
@@ -189,7 +190,7 @@ pub fn delete_key(id: &str, mut config: SectionConfigData) -> Result<bool, Error
 
             let raw = CONFIG.write(ENCRYPTION_KEYS_CFG_FILENAME, &config)?;
             // drops config lock
-            replace_backup_config(ENCRYPTION_KEYS_CFG_FILENAME, raw.as_bytes())?;
+            replace_config(ENCRYPTION_KEYS_CFG_FILENAME, raw.as_bytes())?;
 
             std::fs::remove_file(key_path)?;
             return Ok(true);
diff --git a/pbs-config/src/lib.rs b/pbs-config/src/lib.rs
index 1d32d23e2..b9d5a4d20 100644
--- a/pbs-config/src/lib.rs
+++ b/pbs-config/src/lib.rs
@@ -121,24 +121,6 @@ pub fn open_backup_lockfile<P: AsRef<std::path::Path>>(
     })
 }
 
-/// Atomically write data to file owned by "root:backup" with permission "0640"
-///
-/// Only the superuser can write those files, but group 'backup' can read them.
-pub fn replace_backup_config<P: AsRef<std::path::Path>>(path: P, data: &[u8]) -> Result<(), Error> {
-    let backup_user = backup_user()?;
-    let mode = nix::sys::stat::Mode::from_bits_truncate(0o0640);
-    // set the correct owner/group/permissions while saving file
-    // owner(rw) = root, group(r)= backup
-    let options = proxmox_sys::fs::CreateOptions::new()
-        .perm(mode)
-        .owner(nix::unistd::ROOT)
-        .group(backup_user.gid);
-
-    proxmox_sys::fs::replace_file(path, data, options, true)?;
-
-    Ok(())
-}
-
 /// Detect modified configuration files
 ///
 /// This function fails with a reasonable error message if checksums do not match.
diff --git a/pbs-config/src/media_pool.rs b/pbs-config/src/media_pool.rs
index 105be14fb..da799d1d9 100644
--- a/pbs-config/src/media_pool.rs
+++ b/pbs-config/src/media_pool.rs
@@ -11,12 +11,13 @@ use std::sync::LazyLock;
 
 use anyhow::Error;
 
+use proxmox_product_config::replace_config;
 use proxmox_schema::*;
 use proxmox_section_config::{SectionConfig, SectionConfigData, SectionConfigPlugin};
 
 use pbs_api_types::{MEDIA_POOL_NAME_SCHEMA, MediaPoolConfig};
 
-use crate::{BackupLockGuard, open_backup_lockfile, replace_backup_config};
+use crate::{BackupLockGuard, open_backup_lockfile};
 
 /// Static [`SectionConfig`] to access parser/writer functions.
 pub static CONFIG: LazyLock<SectionConfig> = LazyLock::new(init);
@@ -57,7 +58,7 @@ pub fn config() -> Result<(SectionConfigData, [u8; 32]), Error> {
 /// Save the configuration file
 pub fn save_config(config: &SectionConfigData) -> Result<(), Error> {
     let raw = CONFIG.write(MEDIA_POOL_CFG_FILENAME, config)?;
-    replace_backup_config(MEDIA_POOL_CFG_FILENAME, raw.as_bytes())
+    replace_config(MEDIA_POOL_CFG_FILENAME, raw.as_bytes())
 }
 
 // shell completion helper
diff --git a/pbs-config/src/metrics.rs b/pbs-config/src/metrics.rs
index 06ae518f1..e6c3a5fc7 100644
--- a/pbs-config/src/metrics.rs
+++ b/pbs-config/src/metrics.rs
@@ -3,6 +3,7 @@ use std::sync::LazyLock;
 
 use anyhow::Error;
 
+use proxmox_product_config::replace_config;
 use proxmox_schema::*;
 use proxmox_section_config::{SectionConfig, SectionConfigData, SectionConfigPlugin};
 
@@ -55,7 +56,7 @@ pub fn config() -> Result<(SectionConfigData, [u8; 32]), Error> {
 
 pub fn save_config(config: &SectionConfigData) -> Result<(), Error> {
     let raw = CONFIG.write(METRIC_SERVER_CFG_FILENAME, config)?;
-    crate::replace_backup_config(METRIC_SERVER_CFG_FILENAME, raw.as_bytes())
+    replace_config(METRIC_SERVER_CFG_FILENAME, raw.as_bytes())
 }
 
 // shell completion helper
diff --git a/pbs-config/src/node.rs b/pbs-config/src/node.rs
index dae98d778..b297c60b7 100644
--- a/pbs-config/src/node.rs
+++ b/pbs-config/src/node.rs
@@ -5,6 +5,7 @@ use openssl::ssl::{SslAcceptor, SslMethod};
 
 use pbs_api_types::NodeConfig;
 use proxmox_http::ProxyConfig;
+use proxmox_product_config::replace_config;
 use proxmox_schema::ApiType;
 
 use pbs_buildcfg::configdir;
@@ -46,7 +47,7 @@ pub fn save_config(config: &NodeConfig) -> Result<(), Error> {
     }
 
     let raw = crate::key_value::to_bytes(config, &NodeConfig::API_SCHEMA)?;
-    crate::replace_backup_config(CONF_FILE, &raw)
+    replace_config(CONF_FILE, &raw)
 }
 
 pub fn node_http_proxy_config() -> Result<Option<ProxyConfig>, Error> {
diff --git a/pbs-config/src/notifications.rs b/pbs-config/src/notifications.rs
index cbdbcee7a..fe72b0218 100644
--- a/pbs-config/src/notifications.rs
+++ b/pbs-config/src/notifications.rs
@@ -1,6 +1,7 @@
 use anyhow::Error;
 
 use proxmox_notify::Config;
+use proxmox_product_config::replace_config;
 
 use pbs_buildcfg::configdir;
 
@@ -34,7 +35,7 @@ pub fn config() -> Result<Config, Error> {
 /// Save notification config.
 pub fn save_config(config: Config) -> Result<(), Error> {
     let (cfg, priv_cfg) = config.write()?;
-    crate::replace_backup_config(NOTIFICATION_CONFIG_PATH, cfg.as_bytes())?;
+    replace_config(NOTIFICATION_CONFIG_PATH, cfg.as_bytes())?;
     proxmox_product_config::replace_secret_config(
         NOTIFICATION_PRIV_CONFIG_PATH,
         priv_cfg.as_bytes(),
diff --git a/pbs-config/src/prune.rs b/pbs-config/src/prune.rs
index 1e51653eb..17b3a525f 100644
--- a/pbs-config/src/prune.rs
+++ b/pbs-config/src/prune.rs
@@ -3,12 +3,13 @@ use std::collections::HashMap;
 use anyhow::Error;
 use std::sync::LazyLock;
 
+use proxmox_product_config::replace_config;
 use proxmox_schema::*;
 use proxmox_section_config::{SectionConfig, SectionConfigData, SectionConfigPlugin};
 
 use pbs_api_types::{JOB_ID_SCHEMA, PruneJobConfig};
 
-use crate::{BackupLockGuard, open_backup_lockfile, replace_backup_config};
+use crate::{BackupLockGuard, open_backup_lockfile};
 
 pub static CONFIG: LazyLock<SectionConfig> = LazyLock::new(init);
 
@@ -43,7 +44,7 @@ pub fn config() -> Result<(SectionConfigData, [u8; 32]), Error> {
 
 pub fn save_config(config: &SectionConfigData) -> Result<(), Error> {
     let raw = CONFIG.write(PRUNE_CFG_FILENAME, config)?;
-    replace_backup_config(PRUNE_CFG_FILENAME, raw.as_bytes())
+    replace_config(PRUNE_CFG_FILENAME, raw.as_bytes())
 }
 
 // shell completion helper
diff --git a/pbs-config/src/remote.rs b/pbs-config/src/remote.rs
index 3eac1d978..e96c06864 100644
--- a/pbs-config/src/remote.rs
+++ b/pbs-config/src/remote.rs
@@ -3,6 +3,7 @@ use std::sync::LazyLock;
 
 use anyhow::Error;
 
+use proxmox_product_config::replace_config;
 use proxmox_schema::*;
 use proxmox_section_config::{SectionConfig, SectionConfigData, SectionConfigPlugin};
 
@@ -45,7 +46,7 @@ pub fn config() -> Result<(SectionConfigData, [u8; 32]), Error> {
 
 pub fn save_config(config: &SectionConfigData) -> Result<(), Error> {
     let raw = CONFIG.write(REMOTE_CFG_FILENAME, config)?;
-    crate::replace_backup_config(REMOTE_CFG_FILENAME, raw.as_bytes())
+    replace_config(REMOTE_CFG_FILENAME, raw.as_bytes())
 }
 
 // shell completion helper
diff --git a/pbs-config/src/s3.rs b/pbs-config/src/s3.rs
index 6e31cf685..790e497d7 100644
--- a/pbs-config/src/s3.rs
+++ b/pbs-config/src/s3.rs
@@ -3,13 +3,14 @@ use std::sync::LazyLock;
 
 use anyhow::Error;
 
+use proxmox_product_config::replace_config;
 use proxmox_s3_client::{S3_CLIENT_ID_SCHEMA, S3ClientConf};
 use proxmox_schema::*;
 use proxmox_section_config::{SectionConfig, SectionConfigData, SectionConfigPlugin};
 
 use pbs_buildcfg::configdir;
 
-use crate::{BackupLockGuard, open_backup_lockfile, replace_backup_config};
+use crate::{BackupLockGuard, open_backup_lockfile};
 
 pub static CONFIG: LazyLock<SectionConfig> = LazyLock::new(init);
 
@@ -47,7 +48,7 @@ pub fn config() -> Result<(SectionConfigData, [u8; 32]), Error> {
 /// Save given s3 client configuration to file.
 pub fn save_config(config: &SectionConfigData) -> Result<(), Error> {
     let raw = CONFIG.write(S3_CFG_FILENAME, config)?;
-    replace_backup_config(S3_CFG_FILENAME, raw.as_bytes())?;
+    replace_config(S3_CFG_FILENAME, raw.as_bytes())?;
     Ok(())
 }
 
diff --git a/pbs-config/src/sync.rs b/pbs-config/src/sync.rs
index 8f154b970..59033a98d 100644
--- a/pbs-config/src/sync.rs
+++ b/pbs-config/src/sync.rs
@@ -3,12 +3,13 @@ use std::sync::LazyLock;
 
 use anyhow::Error;
 
+use proxmox_product_config::replace_config;
 use proxmox_schema::{ApiType, Schema};
 use proxmox_section_config::{SectionConfig, SectionConfigData, SectionConfigPlugin};
 
 use pbs_api_types::{JOB_ID_SCHEMA, SyncJobConfig};
 
-use crate::{BackupLockGuard, open_backup_lockfile, replace_backup_config};
+use crate::{BackupLockGuard, open_backup_lockfile};
 
 pub static CONFIG: LazyLock<SectionConfig> = LazyLock::new(init);
 
@@ -45,7 +46,7 @@ pub fn config() -> Result<(SectionConfigData, [u8; 32]), Error> {
 
 pub fn save_config(config: &SectionConfigData) -> Result<(), Error> {
     let raw = CONFIG.write(SYNC_CFG_FILENAME, config)?;
-    replace_backup_config(SYNC_CFG_FILENAME, raw.as_bytes())
+    replace_config(SYNC_CFG_FILENAME, raw.as_bytes())
 }
 
 // shell completion helper
diff --git a/pbs-config/src/tape_job.rs b/pbs-config/src/tape_job.rs
index dada1c83c..deaacfde9 100644
--- a/pbs-config/src/tape_job.rs
+++ b/pbs-config/src/tape_job.rs
@@ -2,12 +2,13 @@ use anyhow::Error;
 use std::collections::HashMap;
 use std::sync::LazyLock;
 
+use proxmox_product_config::replace_config;
 use proxmox_schema::{ApiType, Schema};
 use proxmox_section_config::{SectionConfig, SectionConfigData, SectionConfigPlugin};
 
 use pbs_api_types::{JOB_ID_SCHEMA, TapeBackupJobConfig};
 
-use crate::{BackupLockGuard, open_backup_lockfile, replace_backup_config};
+use crate::{BackupLockGuard, open_backup_lockfile};
 
 pub static CONFIG: LazyLock<SectionConfig> = LazyLock::new(init);
 
@@ -44,7 +45,7 @@ pub fn config() -> Result<(SectionConfigData, [u8; 32]), Error> {
 
 pub fn save_config(config: &SectionConfigData) -> Result<(), Error> {
     let raw = CONFIG.write(TAPE_JOB_CFG_FILENAME, config)?;
-    replace_backup_config(TAPE_JOB_CFG_FILENAME, raw.as_bytes())
+    replace_config(TAPE_JOB_CFG_FILENAME, raw.as_bytes())
 }
 
 // shell completion helper
diff --git a/pbs-config/src/traffic_control.rs b/pbs-config/src/traffic_control.rs
index ed4a0ae96..c6d5fd6b0 100644
--- a/pbs-config/src/traffic_control.rs
+++ b/pbs-config/src/traffic_control.rs
@@ -8,10 +8,11 @@ use proxmox_schema::{ApiType, Schema};
 
 use pbs_api_types::{TRAFFIC_CONTROL_ID_SCHEMA, TrafficControlRule};
 
+use proxmox_product_config::replace_config;
 use proxmox_section_config::{SectionConfig, SectionConfigData, SectionConfigPlugin};
 
 use crate::ConfigVersionCache;
-use crate::{BackupLockGuard, open_backup_lockfile, replace_backup_config};
+use crate::{BackupLockGuard, open_backup_lockfile};
 
 /// Static [`SectionConfig`] to access parser/writer functions.
 pub static CONFIG: LazyLock<SectionConfig> = LazyLock::new(init);
@@ -52,7 +53,7 @@ pub fn config() -> Result<(SectionConfigData, [u8; 32]), Error> {
 /// Save the configuration file
 pub fn save_config(config: &SectionConfigData) -> Result<(), Error> {
     let raw = CONFIG.write(TRAFFIC_CONTROL_CFG_FILENAME, config)?;
-    replace_backup_config(TRAFFIC_CONTROL_CFG_FILENAME, raw.as_bytes())?;
+    replace_config(TRAFFIC_CONTROL_CFG_FILENAME, raw.as_bytes())?;
 
     // increase traffic control version
     // We use this in TrafficControlCache
diff --git a/pbs-config/src/user.rs b/pbs-config/src/user.rs
index f80440650..6996fa455 100644
--- a/pbs-config/src/user.rs
+++ b/pbs-config/src/user.rs
@@ -3,6 +3,7 @@ use std::sync::{Arc, LazyLock, RwLock};
 
 use anyhow::{Error, bail};
 
+use proxmox_product_config::replace_config;
 use proxmox_schema::*;
 use proxmox_section_config::{SectionConfig, SectionConfigData, SectionConfigPlugin};
 
@@ -10,7 +11,7 @@ use pbs_api_types::{ApiToken, Authid, User, Userid};
 
 use crate::ConfigVersionCache;
 
-use crate::{BackupLockGuard, open_backup_lockfile, replace_backup_config};
+use crate::{BackupLockGuard, open_backup_lockfile};
 
 pub static CONFIG: LazyLock<SectionConfig> = LazyLock::new(init);
 
@@ -122,7 +123,7 @@ pub fn cached_config() -> Result<Arc<SectionConfigData>, Error> {
 
 pub fn save_config(config: &SectionConfigData) -> Result<(), Error> {
     let raw = CONFIG.write(USER_CFG_FILENAME, config)?;
-    replace_backup_config(USER_CFG_FILENAME, raw.as_bytes())?;
+    replace_config(USER_CFG_FILENAME, raw.as_bytes())?;
 
     // increase user version
     // We use this in CachedUserInfo
diff --git a/pbs-config/src/verify.rs b/pbs-config/src/verify.rs
index 3295f2bb4..4bc70dd49 100644
--- a/pbs-config/src/verify.rs
+++ b/pbs-config/src/verify.rs
@@ -3,12 +3,13 @@ use std::sync::LazyLock;
 
 use anyhow::Error;
 
+use proxmox_product_config::replace_config;
 use proxmox_schema::*;
 use proxmox_section_config::{SectionConfig, SectionConfigData, SectionConfigPlugin};
 
 use pbs_api_types::{JOB_ID_SCHEMA, VerificationJobConfig};
 
-use crate::{BackupLockGuard, open_backup_lockfile, replace_backup_config};
+use crate::{BackupLockGuard, open_backup_lockfile};
 
 pub static CONFIG: LazyLock<SectionConfig> = LazyLock::new(init);
 
@@ -48,7 +49,7 @@ pub fn config() -> Result<(SectionConfigData, [u8; 32]), Error> {
 
 pub fn save_config(config: &SectionConfigData) -> Result<(), Error> {
     let raw = CONFIG.write(VERIFICATION_CFG_FILENAME, config)?;
-    replace_backup_config(VERIFICATION_CFG_FILENAME, raw.as_bytes())
+    replace_config(VERIFICATION_CFG_FILENAME, raw.as_bytes())
 }
 
 // shell completion helper
diff --git a/src/config/mod.rs b/src/config/mod.rs
index 8b7f3dd5b..0d795562f 100644
--- a/src/config/mod.rs
+++ b/src/config/mod.rs
@@ -11,6 +11,7 @@ use openssl::x509::X509Builder;
 use std::path::Path;
 
 use proxmox_lang::try_block;
+use proxmox_product_config::replace_config;
 
 use pbs_api_types::{PamRealmConfig, PbsRealmConfig};
 use pbs_buildcfg::{self, configdir};
@@ -186,9 +187,9 @@ pub(crate) fn set_proxy_certificate(cert_pem: &[u8], key_pem: &[u8]) -> Result<(
     let cert_path = Path::new(configdir!("/proxy.pem"));
 
     create_configdir()?;
-    pbs_config::replace_backup_config(key_path, key_pem)
+    replace_config(key_path, key_pem)
         .map_err(|err| format_err!("error writing certificate private key - {}", err))?;
-    pbs_config::replace_backup_config(cert_path, cert_pem)
+    replace_config(cert_path, cert_pem)
         .map_err(|err| format_err!("error writing certificate file - {}", err))?;
 
     Ok(())
diff --git a/src/tape/encryption_keys.rs b/src/tape/encryption_keys.rs
index 1af7decaa..2e36aad93 100644
--- a/src/tape/encryption_keys.rs
+++ b/src/tape/encryption_keys.rs
@@ -18,9 +18,9 @@ use serde::{Deserialize, Serialize};
 use proxmox_sys::fs::file_read_optional_string;
 
 use pbs_api_types::Fingerprint;
-use pbs_config::{open_backup_lockfile, replace_backup_config};
+use pbs_config::open_backup_lockfile;
 use pbs_key_config::KeyConfig;
-use proxmox_product_config::replace_secret_config;
+use proxmox_product_config::{replace_config, replace_secret_config};
 
 mod hex_key {
     use hex::FromHex;
@@ -149,7 +149,7 @@ pub fn save_key_configs(map: HashMap<Fingerprint, KeyConfig>) -> Result<(), Erro
     }
 
     let raw = serde_json::to_string_pretty(&list)?;
-    replace_backup_config(TAPE_KEY_CONFIG_FILENAME, raw.as_bytes())
+    replace_config(TAPE_KEY_CONFIG_FILENAME, raw.as_bytes())
 }
 
 /// Insert a new key
-- 
2.47.3





^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [PATCH proxmox-backup v3 05/15] fix #7642: avoid expensive user lookups on file locking
  2026-07-01 14:03 [PATCH proxmox-backup v3 00/15] fix 7642: avoid expensive uid/gid lookups for lock- and config-files Christian Ebner
                   ` (3 preceding siblings ...)
  2026-07-01 14:04 ` [PATCH proxmox-backup v3 04/15] pbs-config: use proxmox-product-config::replace_config() Christian Ebner
@ 2026-07-01 14:04 ` Christian Ebner
  2026-07-01 14:04 ` [PATCH proxmox-backup v3 06/15] pbs-config: use proxmox-product-config helpers Christian Ebner
                   ` (9 subsequent siblings)
  14 siblings, 0 replies; 16+ messages in thread
From: Christian Ebner @ 2026-07-01 14:04 UTC (permalink / raw)
  To: pbs-devel

Currently each time a backup lock file is being acquired, the backup
user is looked in order to instantiate the create options used if the
lock file is not pre-existing, which requires knowledge of the uid
and gid. backup_user() internally however calls User::from_name(),
which itself uses getpwnam_r(3) [0] and therefore can involve
nsswitch.conf and /etc/passwd parsing, including several syscalls to
do so (see backtrace in linked issue).

Ideally proxmox_product_config::open_api_lockfile() would be used as
drop in replacement for open_backup_lockfile(), which is however not
possible until the currently required legacy locking fallback
implementation remains in place (to be dropped with PBS 5).
Therefore, only switch to
proxmox_product_config::lockfile_create_options() for the time being.
By this the api user as read on init is used for all file lock
operations using this helper.

[0] https://docs.rs/nix/0.29.0/nix/unistd/struct.User.html#method.from_name

Fixes: https://bugzilla.proxmox.com/show_bug.cgi?id=7642
Signed-off-by: Christian Ebner <c.ebner@proxmox.com>
---
 pbs-config/src/lib.rs | 9 ++++-----
 1 file changed, 4 insertions(+), 5 deletions(-)

diff --git a/pbs-config/src/lib.rs b/pbs-config/src/lib.rs
index b9d5a4d20..73a8a87fa 100644
--- a/pbs-config/src/lib.rs
+++ b/pbs-config/src/lib.rs
@@ -4,6 +4,7 @@ use anyhow::{Error, bail, format_err};
 use hex::FromHex;
 use nix::unistd::{Gid, Group, Uid, User};
 
+use proxmox_product_config::lockfile_create_options;
 use proxmox_sys::fs::DirLockGuard;
 
 pub use pbs_buildcfg::{BACKUP_GROUP_NAME, BACKUP_USER_NAME};
@@ -106,11 +107,9 @@ pub fn open_backup_lockfile<P: AsRef<std::path::Path>>(
     timeout: Option<std::time::Duration>,
     exclusive: bool,
 ) -> Result<BackupLockGuard, Error> {
-    let user = backup_user()?;
-    let options = proxmox_sys::fs::CreateOptions::new()
-        .perm(nix::sys::stat::Mode::from_bits_truncate(0o660))
-        .owner(user.uid)
-        .group(user.gid);
+    // TODO: Replace whole helper with proxmox_product_config::open_api_lockfile() when
+    // dropping _legacy_dir in in PBS5.
+    let options = lockfile_create_options();
 
     let timeout = timeout.unwrap_or(std::time::Duration::new(10, 0));
 
-- 
2.47.3





^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [PATCH proxmox-backup v3 06/15] pbs-config: use proxmox-product-config helpers
  2026-07-01 14:03 [PATCH proxmox-backup v3 00/15] fix 7642: avoid expensive uid/gid lookups for lock- and config-files Christian Ebner
                   ` (4 preceding siblings ...)
  2026-07-01 14:04 ` [PATCH proxmox-backup v3 05/15] fix #7642: avoid expensive user lookups on file locking Christian Ebner
@ 2026-07-01 14:04 ` Christian Ebner
  2026-07-01 14:04 ` [PATCH proxmox-backup v3 07/15] pbs-config: drop backup_group helper, use users gid instead Christian Ebner
                   ` (8 subsequent siblings)
  14 siblings, 0 replies; 16+ messages in thread
From: Christian Ebner @ 2026-07-01 14:04 UTC (permalink / raw)
  To: pbs-devel

Avoids expensive user lookups via pbs-config::backup_user(),
which internally calls User::from_name() and uses getpwnam_r(3) [0],
therefore can involve nsswitch.conf and /etc/passwd parsing
requiring several syscalls to do so.

[0] https://docs.rs/nix/0.29.0/nix/unistd/struct.User.html#method.from_name

Signed-off-by: Christian Ebner <c.ebner@proxmox.com>
---
 pbs-config/src/config_version_cache.rs | 2 +-
 pbs-config/src/encryption_keys.rs      | 2 +-
 pbs-config/src/token_shadow.rs         | 8 +-------
 3 files changed, 3 insertions(+), 9 deletions(-)

diff --git a/pbs-config/src/config_version_cache.rs b/pbs-config/src/config_version_cache.rs
index a282da746..5053f6dc6 100644
--- a/pbs-config/src/config_version_cache.rs
+++ b/pbs-config/src/config_version_cache.rs
@@ -92,7 +92,7 @@ impl ConfigVersionCache {
 
     // Actual work of `new`:
     fn open() -> Result<Arc<Self>, Error> {
-        let user = crate::backup_user()?;
+        let user = proxmox_product_config::get_api_user();
 
         let dir_opts = CreateOptions::new()
             .perm(Mode::from_bits_truncate(0o770))
diff --git a/pbs-config/src/encryption_keys.rs b/pbs-config/src/encryption_keys.rs
index 3f8870254..367ac1e12 100644
--- a/pbs-config/src/encryption_keys.rs
+++ b/pbs-config/src/encryption_keys.rs
@@ -107,7 +107,7 @@ pub fn store_key(id: &str, key: &KeyConfig) -> Result<(), Error> {
         bail!("key with id '{id}' already exists.");
     }
 
-    let backup_user = crate::backup_user()?;
+    let backup_user = proxmox_product_config::get_api_user();
     let dir_options = CreateOptions::new()
         .perm(Mode::from_bits_truncate(0o0750))
         .owner(Uid::from_raw(0))
diff --git a/pbs-config/src/token_shadow.rs b/pbs-config/src/token_shadow.rs
index 871218411..d931cc6fa 100644
--- a/pbs-config/src/token_shadow.rs
+++ b/pbs-config/src/token_shadow.rs
@@ -9,7 +9,6 @@ use parking_lot::RwLock;
 use serde::{Deserialize, Serialize};
 use serde_json::{Value, from_value};
 
-use proxmox_sys::fs::CreateOptions;
 use proxmox_time::epoch_i64;
 
 use pbs_api_types::Authid;
@@ -59,12 +58,7 @@ fn read_file() -> Result<HashMap<Authid, String>, Error> {
 }
 
 fn write_file(data: HashMap<Authid, String>) -> Result<(), Error> {
-    let backup_user = crate::backup_user()?;
-    let options = CreateOptions::new()
-        .perm(nix::sys::stat::Mode::from_bits_truncate(0o0640))
-        .owner(backup_user.uid)
-        .group(backup_user.gid);
-
+    let options = proxmox_product_config::default_create_options();
     let json = serde_json::to_vec(&data)?;
     proxmox_sys::fs::replace_file(CONF_FILE, &json, options, true)
 }
-- 
2.47.3





^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [PATCH proxmox-backup v3 07/15] pbs-config: drop backup_group helper, use users gid instead
  2026-07-01 14:03 [PATCH proxmox-backup v3 00/15] fix 7642: avoid expensive uid/gid lookups for lock- and config-files Christian Ebner
                   ` (5 preceding siblings ...)
  2026-07-01 14:04 ` [PATCH proxmox-backup v3 06/15] pbs-config: use proxmox-product-config helpers Christian Ebner
@ 2026-07-01 14:04 ` Christian Ebner
  2026-07-01 14:04 ` [PATCH proxmox-backup v3 08/15] pbs-datastore: use proxmox-product-config cached backup user Christian Ebner
                   ` (7 subsequent siblings)
  14 siblings, 0 replies; 16+ messages in thread
From: Christian Ebner @ 2026-07-01 14:04 UTC (permalink / raw)
  To: pbs-devel

Use the gid as reported for the backup user, which matches the
`backup` group anyways. By this it is possible to use the user
provided via proxmox-product-config::get_api_user() for chunk store
operations instead, avoiding expensive user/group lookups.

Signed-off-by: Christian Ebner <c.ebner@proxmox.com>
---
 pbs-config/src/lib.rs            | 12 +-----------
 pbs-datastore/Cargo.toml         |  1 +
 pbs-datastore/src/chunk_store.rs | 27 +++++++++++++--------------
 src/bin/proxmox-backup-proxy.rs  |  5 +++--
 src/bin/sg-tape-cmd.rs           |  5 +++--
 5 files changed, 21 insertions(+), 29 deletions(-)

diff --git a/pbs-config/src/lib.rs b/pbs-config/src/lib.rs
index 73a8a87fa..e2b74f8a8 100644
--- a/pbs-config/src/lib.rs
+++ b/pbs-config/src/lib.rs
@@ -2,7 +2,7 @@ use std::os::unix::prelude::AsRawFd;
 
 use anyhow::{Error, bail, format_err};
 use hex::FromHex;
-use nix::unistd::{Gid, Group, Uid, User};
+use nix::unistd::{Uid, User};
 
 use proxmox_product_config::lockfile_create_options;
 use proxmox_sys::fs::DirLockGuard;
@@ -44,16 +44,6 @@ pub fn backup_user() -> Result<nix::unistd::User, Error> {
     }
 }
 
-/// Return Group info for the 'backup' group (``getgrnam(3)``)
-pub fn backup_group() -> Result<nix::unistd::Group, Error> {
-    if cfg!(test) {
-        Ok(Group::from_gid(Gid::current())?.expect("current group does not exist"))
-    } else {
-        Group::from_name(BACKUP_GROUP_NAME)?
-            .ok_or_else(|| format_err!("Unable to lookup '{}' group.", BACKUP_GROUP_NAME))
-    }
-}
-
 /// Return User info for root
 pub fn priv_user() -> Result<nix::unistd::User, Error> {
     if cfg!(test) {
diff --git a/pbs-datastore/Cargo.toml b/pbs-datastore/Cargo.toml
index 09991e530..b51438dc6 100644
--- a/pbs-datastore/Cargo.toml
+++ b/pbs-datastore/Cargo.toml
@@ -38,6 +38,7 @@ proxmox-http.workspace = true
 proxmox-human-byte.workspace = true
 proxmox-io.workspace = true
 proxmox-lang.workspace=true
+proxmox-product-config.workspace = true
 proxmox-s3-client = { workspace = true, features = [ "impl" ] }
 proxmox-schema = { workspace = true, features = [ "api-macro" ] }
 proxmox-section-config.workspace = true
diff --git a/pbs-datastore/src/chunk_store.rs b/pbs-datastore/src/chunk_store.rs
index a936f5034..09a0242fc 100644
--- a/pbs-datastore/src/chunk_store.rs
+++ b/pbs-datastore/src/chunk_store.rs
@@ -756,12 +756,11 @@ impl ChunkStore {
             .parent()
             .ok_or_else(|| format_err!("unable to get chunk dir"))?;
 
-        let mut create_options = CreateOptions::new();
-        if nix::unistd::Uid::effective().is_root() {
-            let uid = pbs_config::backup_user()?.uid;
-            let gid = pbs_config::backup_group()?.gid;
-            create_options = create_options.owner(uid).group(gid);
-        }
+        let create_options = if nix::unistd::Uid::effective().is_root() {
+            proxmox_product_config::default_create_options()
+        } else {
+            CreateOptions::new()
+        };
         proxmox_sys::fs::replace_file(
             &chunk_path,
             raw_data,
@@ -813,12 +812,11 @@ impl ChunkStore {
 
     /// Helper to generate new empty marker file
     fn create_marker_file(path: &Path) -> Result<(), Error> {
-        let mut create_options = CreateOptions::new();
-        if nix::unistd::Uid::effective().is_root() {
-            let uid = pbs_config::backup_user()?.uid;
-            let gid = pbs_config::backup_group()?.gid;
-            create_options = create_options.owner(uid).group(gid);
-        }
+        let create_options = if nix::unistd::Uid::effective().is_root() {
+            proxmox_product_config::default_create_options()
+        } else {
+            CreateOptions::new()
+        };
         proxmox_sys::fs::replace_file(path, &[], create_options, false)
     }
 
@@ -904,8 +902,9 @@ impl ChunkStore {
     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)
+                let backup_user = proxmox_product_config::get_api_user();
+                if stat.st_uid != u32::from(backup_user.uid)
+                    || stat.st_gid != u32::from(backup_user.gid)
                     || stat.st_mode & 0o777 != file_mode
                 {
                     bail!(
diff --git a/src/bin/proxmox-backup-proxy.rs b/src/bin/proxmox-backup-proxy.rs
index b372f779e..37af76ade 100644
--- a/src/bin/proxmox-backup-proxy.rs
+++ b/src/bin/proxmox-backup-proxy.rs
@@ -60,8 +60,9 @@ fn main() -> Result<(), Error> {
 
     proxmox_backup::tools::setup_safe_path_env();
 
-    let backup_uid = pbs_config::backup_user()?.uid;
-    let backup_gid = pbs_config::backup_group()?.gid;
+    let backup_user = pbs_config::backup_user()?;
+    let backup_uid = backup_user.uid;
+    let backup_gid = backup_user.gid;
     let running_uid = nix::unistd::Uid::effective();
     let running_gid = nix::unistd::Gid::effective();
 
diff --git a/src/bin/sg-tape-cmd.rs b/src/bin/sg-tape-cmd.rs
index 9ff73a4a4..7a365160b 100644
--- a/src/bin/sg-tape-cmd.rs
+++ b/src/bin/sg-tape-cmd.rs
@@ -129,8 +129,9 @@ fn main() -> Result<(), Error> {
         .init()?;
 
     // check if we are user root or backup
-    let backup_uid = pbs_config::backup_user()?.uid;
-    let backup_gid = pbs_config::backup_group()?.gid;
+    let backup_user = pbs_config::backup_user()?;
+    let backup_uid = backup_user.uid;
+    let backup_gid = backup_user.gid;
     let running_uid = nix::unistd::Uid::current();
     let running_gid = nix::unistd::Gid::current();
 
-- 
2.47.3





^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [PATCH proxmox-backup v3 08/15] pbs-datastore: use proxmox-product-config cached backup user
  2026-07-01 14:03 [PATCH proxmox-backup v3 00/15] fix 7642: avoid expensive uid/gid lookups for lock- and config-files Christian Ebner
                   ` (6 preceding siblings ...)
  2026-07-01 14:04 ` [PATCH proxmox-backup v3 07/15] pbs-config: drop backup_group helper, use users gid instead Christian Ebner
@ 2026-07-01 14:04 ` Christian Ebner
  2026-07-01 14:04 ` [PATCH proxmox-backup v3 09/15] pbs-datastore: use general helpers for file lock create options Christian Ebner
                   ` (6 subsequent siblings)
  14 siblings, 0 replies; 16+ messages in thread
From: Christian Ebner @ 2026-07-01 14:04 UTC (permalink / raw)
  To: pbs-devel

Instead of looking it up, use the globally cached backup user info
provided by proxmox-product-config. The user is not allowed to change
change anyways during the runtime of the process, as otherwise all
sorts of permission issues can arise.

Signed-off-by: Christian Ebner <c.ebner@proxmox.com>
---
 pbs-datastore/src/datastore.rs | 17 ++++++++++-------
 1 file changed, 10 insertions(+), 7 deletions(-)

diff --git a/pbs-datastore/src/datastore.rs b/pbs-datastore/src/datastore.rs
index e2d1ae67c..06adbece5 100644
--- a/pbs-datastore/src/datastore.rs
+++ b/pbs-datastore/src/datastore.rs
@@ -488,14 +488,15 @@ impl DataStore {
 
                 let (config, _config_digest) = pbs_config::s3::config()?;
                 let config: S3ClientConf = config.lookup(S3_CFG_TYPE_ID, s3_client_id)?;
+                let backup_user = proxmox_product_config::get_api_user();
                 let rate_limiter_options = S3RateLimiterOptions {
                     id: s3_client_id.to_string(),
-                    user: pbs_config::backup_user()?,
+                    user: backup_user.clone(),
                     base_path: S3_CLIENT_RATE_LIMITER_BASE_PATH.into(),
                 };
                 let request_counter_config = S3RequestCounterConfig {
                     id: format!("{s3_client_id}-{bucket}-{}", self.name()),
-                    user: pbs_config::backup_user()?,
+                    user: backup_user.clone(),
                     base_path: S3_CLIENT_REQUEST_COUNTER_BASE_PATH.into(),
                 };
 
@@ -819,8 +820,9 @@ impl DataStore {
                 .ok_or(format_err!("missing s3 bucket"))?,
             config.name,
         );
+        let backup_user = proxmox_product_config::get_api_user();
         let request_counters =
-            SharedRequestCounters::open_shared_memory_mapped(path, pbs_config::backup_user()?)?;
+            SharedRequestCounters::open_shared_memory_mapped(path, backup_user.clone())?;
         Ok(request_counters)
     }
 
@@ -2653,7 +2655,7 @@ impl DataStore {
             let mut path = self.base_path();
             path.push(".gc-status");
 
-            let backup_user = pbs_config::backup_user()?;
+            let backup_user = proxmox_product_config::get_api_user();
             let mode = nix::sys::stat::Mode::from_bits_truncate(0o0644);
             // set the correct owner/group/permissions while saving file
             // owner(rw) = backup, group(r)= backup
@@ -3183,7 +3185,7 @@ impl DataStore {
     // Fetch the contents (metadata, no chunks) of the datastore from the S3 object store to the
     // provided temporaray directory
     async fn fetch_tmp_contents(&self, tmp_base: &Path, s3_client: &S3Client) -> Result<(), Error> {
-        let backup_user = pbs_config::backup_user().context("failed to get backup user")?;
+        let backup_user = proxmox_product_config::get_api_user();
         let mode = nix::sys::stat::Mode::from_bits_truncate(0o0644);
         let file_create_options = CreateOptions::new()
             .perm(mode)
@@ -3336,14 +3338,15 @@ impl DataStore {
         let client_config: S3ClientConf = config
             .lookup(S3_CFG_TYPE_ID, s3_client_id)
             .with_context(|| format!("no '{s3_client_id}' in config"))?;
+        let backup_user = proxmox_product_config::get_api_user();
         let rate_limiter_options = S3RateLimiterOptions {
             id: s3_client_id.to_string(),
-            user: pbs_config::backup_user()?,
+            user: backup_user.clone(),
             base_path: S3_CLIENT_RATE_LIMITER_BASE_PATH.into(),
         };
         let request_counter_config = S3RequestCounterConfig {
             id: format!("{s3_client_id}-{bucket}-{}", datastore_config.name),
-            user: pbs_config::backup_user()?,
+            user: backup_user.clone(),
             base_path: S3_CLIENT_REQUEST_COUNTER_BASE_PATH.into(),
         };
 
-- 
2.47.3





^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [PATCH proxmox-backup v3 09/15] pbs-datastore: use general helpers for file lock create options
  2026-07-01 14:03 [PATCH proxmox-backup v3 00/15] fix 7642: avoid expensive uid/gid lookups for lock- and config-files Christian Ebner
                   ` (7 preceding siblings ...)
  2026-07-01 14:04 ` [PATCH proxmox-backup v3 08/15] pbs-datastore: use proxmox-product-config cached backup user Christian Ebner
@ 2026-07-01 14:04 ` Christian Ebner
  2026-07-01 14:04 ` [PATCH proxmox-backup v3 10/15] server: auth helpers: use proxmox-product-config create options helpers Christian Ebner
                   ` (5 subsequent siblings)
  14 siblings, 0 replies; 16+ messages in thread
From: Christian Ebner @ 2026-07-01 14:04 UTC (permalink / raw)
  To: pbs-devel

Instead of using local implementations, use the helpers provided by
the proxmox-product-config crate for consistency. By this the backup
user lookup is avoided as well, using the globally cached information
instead.

Signed-off-by: Christian Ebner <c.ebner@proxmox.com>
---
 pbs-datastore/src/move_journal.rs  | 11 ++---------
 pbs-datastore/src/task_tracking.rs |  7 +------
 2 files changed, 3 insertions(+), 15 deletions(-)

diff --git a/pbs-datastore/src/move_journal.rs b/pbs-datastore/src/move_journal.rs
index a31001541..a678115d4 100644
--- a/pbs-datastore/src/move_journal.rs
+++ b/pbs-datastore/src/move_journal.rs
@@ -48,11 +48,8 @@ use std::path::{Path, PathBuf};
 use std::time::Duration;
 
 use anyhow::{Context, Error, bail, format_err};
-use nix::sys::stat::Mode;
 
-use proxmox_sys::fs::{CreateOptions, open_file_locked};
-
-use pbs_config::backup_user;
+use proxmox_sys::fs::open_file_locked;
 
 use crate::backup_info::DATASTORE_LOCKS_DIR;
 
@@ -78,11 +75,7 @@ fn ensure_parent(path: &Path) -> Result<(), Error> {
 
 fn open_locked_exclusive(path: &Path, timeout: Duration) -> Result<File, Error> {
     ensure_parent(path)?;
-    let user = backup_user()?;
-    let options = CreateOptions::new()
-        .perm(Mode::from_bits_truncate(0o660))
-        .owner(user.uid)
-        .group(user.gid);
+    let options = proxmox_product_config::lockfile_create_options();
     open_file_locked(path, timeout, true, options)
         .with_context(|| format!("failed to acquire exclusive move-journal lock at {path:?}"))
 }
diff --git a/pbs-datastore/src/task_tracking.rs b/pbs-datastore/src/task_tracking.rs
index d4cc076aa..3d7bf3e44 100644
--- a/pbs-datastore/src/task_tracking.rs
+++ b/pbs-datastore/src/task_tracking.rs
@@ -35,14 +35,9 @@ struct TaskOperations {
 }
 
 fn open_lock_file(name: &str) -> Result<(std::fs::File, CreateOptions), Error> {
-    let user = pbs_config::backup_user()?;
-
     let lock_path = PathBuf::from(format!("{}/{}.lock", crate::ACTIVE_OPERATIONS_DIR, name));
 
-    let options = CreateOptions::new()
-        .group(user.gid)
-        .owner(user.uid)
-        .perm(nix::sys::stat::Mode::from_bits_truncate(0o660));
+    let options = proxmox_product_config::lockfile_create_options();
 
     let timeout = std::time::Duration::new(10, 0);
 
-- 
2.47.3





^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [PATCH proxmox-backup v3 10/15] server: auth helpers: use proxmox-product-config create options helpers
  2026-07-01 14:03 [PATCH proxmox-backup v3 00/15] fix 7642: avoid expensive uid/gid lookups for lock- and config-files Christian Ebner
                   ` (8 preceding siblings ...)
  2026-07-01 14:04 ` [PATCH proxmox-backup v3 09/15] pbs-datastore: use general helpers for file lock create options Christian Ebner
@ 2026-07-01 14:04 ` Christian Ebner
  2026-07-01 14:04 ` [PATCH proxmox-backup v3 11/15] api: subscription: use proxmox-product-config create options Christian Ebner
                   ` (4 subsequent siblings)
  14 siblings, 0 replies; 16+ messages in thread
From: Christian Ebner @ 2026-07-01 14:04 UTC (permalink / raw)
  To: pbs-devel

Instead of re-defining them again use the global helper providing
them.

Signed-off-by: Christian Ebner <c.ebner@proxmox.com>
---
 src/auth_helpers.rs | 27 ++++-----------------------
 1 file changed, 4 insertions(+), 23 deletions(-)

diff --git a/src/auth_helpers.rs b/src/auth_helpers.rs
index 64222e7cc..8145027de 100644
--- a/src/auth_helpers.rs
+++ b/src/auth_helpers.rs
@@ -23,16 +23,10 @@ pub fn generate_csrf_key() -> Result<(), Error> {
 
     let key = HMACKey::generate()?.to_base64()?;
 
-    use nix::sys::stat::Mode;
-    let backup_user = pbs_config::backup_user()?;
-
     replace_file(
         &path,
         key.as_bytes(),
-        CreateOptions::new()
-            .perm(Mode::from_bits_truncate(0o0640))
-            .owner(nix::unistd::ROOT)
-            .group(backup_user.gid),
+        proxmox_product_config::privileged_create_options(),
         true,
     )?;
 
@@ -60,15 +54,10 @@ pub fn generate_auth_key() -> Result<(), Error> {
         true,
     )?;
 
-    let backup_user = pbs_config::backup_user()?;
-
     replace_file(
         &public_path,
         &key.public_key_to_pem()?,
-        CreateOptions::new()
-            .perm(Mode::from_bits_truncate(0o0640))
-            .owner(nix::unistd::ROOT)
-            .group(backup_user.gid),
+        proxmox_product_config::privileged_create_options(),
         true,
     )?;
 
@@ -122,11 +111,7 @@ pub fn store_ldap_bind_password(
     let mut data = proxmox_sys::fs::file_get_json(LDAP_PASSWORDS_FILENAME, Some(json!({})))?;
     data[realm] = password.into();
 
-    let mode = nix::sys::stat::Mode::from_bits_truncate(0o0600);
-    let options = proxmox_sys::fs::CreateOptions::new()
-        .perm(mode)
-        .owner(nix::unistd::ROOT)
-        .group(nix::unistd::Gid::from_raw(0));
+    let options = proxmox_product_config::secret_create_options();
 
     let data = serde_json::to_vec_pretty(&data)?;
     proxmox_sys::fs::replace_file(LDAP_PASSWORDS_FILENAME, &data, options, true)?;
@@ -142,11 +127,7 @@ pub fn remove_ldap_bind_password(realm: &str, _domain_lock: &BackupLockGuard) ->
         map.remove(realm);
     }
 
-    let mode = nix::sys::stat::Mode::from_bits_truncate(0o0600);
-    let options = proxmox_sys::fs::CreateOptions::new()
-        .perm(mode)
-        .owner(nix::unistd::ROOT)
-        .group(nix::unistd::Gid::from_raw(0));
+    let options = proxmox_product_config::secret_create_options();
 
     let data = serde_json::to_vec_pretty(&data)?;
     proxmox_sys::fs::replace_file(LDAP_PASSWORDS_FILENAME, &data, options, true)?;
-- 
2.47.3





^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [PATCH proxmox-backup v3 11/15] api: subscription: use proxmox-product-config create options
  2026-07-01 14:03 [PATCH proxmox-backup v3 00/15] fix 7642: avoid expensive uid/gid lookups for lock- and config-files Christian Ebner
                   ` (9 preceding siblings ...)
  2026-07-01 14:04 ` [PATCH proxmox-backup v3 10/15] server: auth helpers: use proxmox-product-config create options helpers Christian Ebner
@ 2026-07-01 14:04 ` Christian Ebner
  2026-07-01 14:04 ` [PATCH proxmox-backup v3 12/15] tape: use proxmox-product-config helper for user lookup Christian Ebner
                   ` (3 subsequent siblings)
  14 siblings, 0 replies; 16+ messages in thread
From: Christian Ebner @ 2026-07-01 14:04 UTC (permalink / raw)
  To: pbs-devel

Instead of re-defining them, use the create options helper for
secret files as defined in proxmox-product-config crate.

Signed-off-by: Christian Ebner <c.ebner@proxmox.com>
---
 src/api2/node/subscription.rs                  | 11 +----------
 src/bin/proxmox_backup_manager/subscription.rs |  4 ++--
 2 files changed, 3 insertions(+), 12 deletions(-)

diff --git a/src/api2/node/subscription.rs b/src/api2/node/subscription.rs
index 033a6e4c0..639a1d1dd 100644
--- a/src/api2/node/subscription.rs
+++ b/src/api2/node/subscription.rs
@@ -22,15 +22,6 @@ const PRODUCT_URL: &str = "https://www.proxmox.com/en/proxmox-backup-server/pric
 const APT_AUTH_FN: &str = "/etc/apt/auth.conf.d/pbs.conf";
 const APT_AUTH_URL: &str = "enterprise.proxmox.com/debian/pbs";
 
-pub fn subscription_file_opts() -> Result<CreateOptions, Error> {
-    let backup_user = pbs_config::backup_user()?;
-    let mode = nix::sys::stat::Mode::from_bits_truncate(0o0640);
-    Ok(CreateOptions::new()
-        .perm(mode)
-        .owner(nix::unistd::ROOT)
-        .group(backup_user.gid))
-}
-
 fn apt_auth_file_opts() -> CreateOptions {
     let mode = nix::sys::stat::Mode::from_bits_truncate(0o0600);
     CreateOptions::new().perm(mode).owner(nix::unistd::ROOT)
@@ -58,7 +49,7 @@ fn check_and_write_subscription(key: String, server_id: String) -> Result<(), Er
 
     proxmox_subscription::files::write_subscription(
         PROXMOX_BACKUP_SUBSCRIPTION_FN,
-        subscription_file_opts()?,
+        proxmox_product_config::privileged_create_options(),
         &info,
     )
     .map_err(|e| format_err!("Error writing updated subscription status - {}", e))?;
diff --git a/src/bin/proxmox_backup_manager/subscription.rs b/src/bin/proxmox_backup_manager/subscription.rs
index f04949bf3..dffc39066 100644
--- a/src/bin/proxmox_backup_manager/subscription.rs
+++ b/src/bin/proxmox_backup_manager/subscription.rs
@@ -5,7 +5,7 @@ use proxmox_router::{ApiHandler, RpcEnvironment, cli::*};
 use proxmox_schema::api;
 use proxmox_subscription::{ProductType, SubscriptionInfo};
 
-use proxmox_backup::api2::{self, node::subscription::subscription_file_opts};
+use proxmox_backup::api2;
 
 use pbs_buildcfg::PROXMOX_BACKUP_SUBSCRIPTION_FN;
 
@@ -62,7 +62,7 @@ pub fn set_offline_subscription_key(data: String) -> Result<(), Error> {
     info.check_server_id();
     proxmox_subscription::files::write_subscription(
         PROXMOX_BACKUP_SUBSCRIPTION_FN,
-        subscription_file_opts()?,
+        proxmox_product_config::privileged_create_options(),
         &info,
     )?;
     Ok(())
-- 
2.47.3





^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [PATCH proxmox-backup v3 12/15] tape: use proxmox-product-config helper for user lookup
  2026-07-01 14:03 [PATCH proxmox-backup v3 00/15] fix 7642: avoid expensive uid/gid lookups for lock- and config-files Christian Ebner
                   ` (10 preceding siblings ...)
  2026-07-01 14:04 ` [PATCH proxmox-backup v3 11/15] api: subscription: use proxmox-product-config create options Christian Ebner
@ 2026-07-01 14:04 ` Christian Ebner
  2026-07-01 14:04 ` [PATCH proxmox-backup v3 13/15] tape: use proxmox-product-config lock file create options Christian Ebner
                   ` (2 subsequent siblings)
  14 siblings, 0 replies; 16+ messages in thread
From: Christian Ebner @ 2026-07-01 14:04 UTC (permalink / raw)
  To: pbs-devel

By this the globally cached user information is used instead of doing
a lookup to be consistent with the rest of the codebase.

Tests are adapted to call the init exactly once by an init callback
for a LazyLock. This is thread safe, so tests can be run in parallel
without interference.

Signed-off-by: Christian Ebner <c.ebner@proxmox.com>
---
 src/tape/media_catalog.rs             | 14 ++++----------
 src/tape/mod.rs                       |  8 ++++----
 src/tape/test/alloc_writable_media.rs | 17 +++++++++++++++++
 3 files changed, 25 insertions(+), 14 deletions(-)

diff --git a/src/tape/media_catalog.rs b/src/tape/media_catalog.rs
index 41c1dcad0..4fcf45dc3 100644
--- a/src/tape/media_catalog.rs
+++ b/src/tape/media_catalog.rs
@@ -10,7 +10,7 @@ use endian_trait::Endian;
 use proxmox_sys::fs::read_subdir;
 
 use proxmox_io::{ReadExt, WriteExt};
-use proxmox_sys::fs::{CreateOptions, create_path, fchown};
+use proxmox_sys::fs::{create_path, fchown};
 use proxmox_uuid::Uuid;
 
 use pbs_api_types::{BackupDir, BackupNamespace, parse_ns_and_snapshot, print_ns_and_snapshot};
@@ -168,13 +168,7 @@ impl MediaCatalog {
     }
 
     fn create_basedir<P: AsRef<Path>>(base_path: P) -> Result<(), Error> {
-        let backup_user = pbs_config::backup_user()?;
-        let mode = nix::sys::stat::Mode::from_bits_truncate(0o0640);
-        let opts = CreateOptions::new()
-            .perm(mode)
-            .owner(backup_user.uid)
-            .group(backup_user.gid);
-
+        let opts = proxmox_product_config::default_create_options();
         create_path(base_path, None, Some(opts))
             .map_err(|err: Error| format_err!("unable to create media catalog dir - {}", err))?;
         Ok(())
@@ -200,7 +194,7 @@ impl MediaCatalog {
                 .create(create)
                 .open(&path)?;
 
-            let backup_user = pbs_config::backup_user()?;
+            let backup_user = proxmox_product_config::get_api_user();
             fchown(
                 file.as_raw_fd(),
                 Some(backup_user.uid),
@@ -261,7 +255,7 @@ impl MediaCatalog {
             return Ok(file);
         }
 
-        let backup_user = pbs_config::backup_user()?;
+        let backup_user = proxmox_product_config::get_api_user();
         fchown(
             file.as_raw_fd(),
             Some(backup_user.uid),
diff --git a/src/tape/mod.rs b/src/tape/mod.rs
index eba692035..c1f04595f 100644
--- a/src/tape/mod.rs
+++ b/src/tape/mod.rs
@@ -57,7 +57,7 @@ pub const COMMIT_BLOCK_SIZE: usize = 128 * 1024 * 1024 * 1024; // 128 GiB
 
 /// Create tape status dir with correct permission
 pub fn create_tape_status_dir() -> Result<(), Error> {
-    let backup_user = pbs_config::backup_user()?;
+    let backup_user = proxmox_product_config::get_api_user();
     let mode = nix::sys::stat::Mode::from_bits_truncate(0o0750);
     let options = CreateOptions::new()
         .perm(mode)
@@ -76,7 +76,7 @@ pub fn create_tape_status_dir() -> Result<(), Error> {
 
 /// Create drive lock dir with correct permission
 pub fn create_drive_lock_dir() -> Result<(), Error> {
-    let backup_user = pbs_config::backup_user()?;
+    let backup_user = proxmox_product_config::get_api_user();
     let mode = nix::sys::stat::Mode::from_bits_truncate(0o0750);
     let options = CreateOptions::new()
         .perm(mode)
@@ -95,7 +95,7 @@ pub fn create_drive_lock_dir() -> Result<(), Error> {
 
 /// Create drive state dir with correct permission
 pub fn create_drive_state_dir() -> Result<(), Error> {
-    let backup_user = pbs_config::backup_user()?;
+    let backup_user = proxmox_product_config::get_api_user();
     let mode = nix::sys::stat::Mode::from_bits_truncate(0o0750);
     let options = CreateOptions::new()
         .perm(mode)
@@ -114,7 +114,7 @@ pub fn create_drive_state_dir() -> Result<(), Error> {
 
 /// Create changer state cache dir with correct permission
 pub fn create_changer_state_dir() -> Result<(), Error> {
-    let backup_user = pbs_config::backup_user()?;
+    let backup_user = proxmox_product_config::get_api_user();
     let mode = nix::sys::stat::Mode::from_bits_truncate(0o0750);
     let options = CreateOptions::new()
         .perm(mode)
diff --git a/src/tape/test/alloc_writable_media.rs b/src/tape/test/alloc_writable_media.rs
index ff9a63489..fc1bb88ce 100644
--- a/src/tape/test/alloc_writable_media.rs
+++ b/src/tape/test/alloc_writable_media.rs
@@ -4,11 +4,20 @@
 
 use anyhow::Error;
 use std::path::PathBuf;
+use std::sync::LazyLock;
 
 use pbs_api_types::{MediaSetPolicy, RetentionPolicy};
 
 use crate::tape::{Inventory, MediaPool};
 
+static PROXMOX_PRODUCT_CONFIG_INIT_FOR_TESTS: LazyLock<bool> = LazyLock::new(|| {
+    proxmox_product_config::init(
+        pbs_config::backup_user().unwrap(),
+        pbs_config::priv_user().unwrap(),
+    );
+    true
+});
+
 fn create_testdir(name: &str) -> Result<PathBuf, Error> {
     let mut testdir: PathBuf = String::from("./target/testout").into();
     testdir.push(std::module_path!());
@@ -22,6 +31,8 @@ fn create_testdir(name: &str) -> Result<PathBuf, Error> {
 
 #[test]
 fn test_alloc_writable_media_1() -> Result<(), Error> {
+    assert_eq!(LazyLock::force(&PROXMOX_PRODUCT_CONFIG_INIT_FOR_TESTS), &true);
+
     let testdir = create_testdir("test_alloc_writable_media_1")?;
 
     let mut ctime = 0;
@@ -48,6 +59,8 @@ fn test_alloc_writable_media_1() -> Result<(), Error> {
 
 #[test]
 fn test_alloc_writable_media_2() -> Result<(), Error> {
+    assert_eq!(LazyLock::force(&PROXMOX_PRODUCT_CONFIG_INIT_FOR_TESTS), &true);
+
     let testdir = create_testdir("test_alloc_writable_media_2")?;
 
     let mut inventory = Inventory::load(&testdir)?;
@@ -85,6 +98,8 @@ fn test_alloc_writable_media_2() -> Result<(), Error> {
 
 #[test]
 fn test_alloc_writable_media_3() -> Result<(), Error> {
+    assert_eq!(LazyLock::force(&PROXMOX_PRODUCT_CONFIG_INIT_FOR_TESTS), &true);
+
     let testdir = create_testdir("test_alloc_writable_media_3")?;
 
     let mut inventory = Inventory::load(&testdir)?;
@@ -133,6 +148,8 @@ fn test_alloc_writable_media_3() -> Result<(), Error> {
 
 #[test]
 fn test_alloc_writable_media_4() -> Result<(), Error> {
+    assert_eq!(LazyLock::force(&PROXMOX_PRODUCT_CONFIG_INIT_FOR_TESTS), &true);
+
     let testdir = create_testdir("test_alloc_writable_media_4")?;
 
     let mut inventory = Inventory::load(&testdir)?;
-- 
2.47.3





^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [PATCH proxmox-backup v3 13/15] tape: use proxmox-product-config lock file create options
  2026-07-01 14:03 [PATCH proxmox-backup v3 00/15] fix 7642: avoid expensive uid/gid lookups for lock- and config-files Christian Ebner
                   ` (11 preceding siblings ...)
  2026-07-01 14:04 ` [PATCH proxmox-backup v3 12/15] tape: use proxmox-product-config helper for user lookup Christian Ebner
@ 2026-07-01 14:04 ` Christian Ebner
  2026-07-01 14:04 ` [PATCH proxmox-backup v3 14/15] tape: use proxmox-product-config to generate " Christian Ebner
  2026-07-01 14:04 ` [PATCH proxmox-backup v3 15/15] tree-wide: use proxmox-product-config::get_api_user for user lookup Christian Ebner
  14 siblings, 0 replies; 16+ messages in thread
From: Christian Ebner @ 2026-07-01 14:04 UTC (permalink / raw)
  To: pbs-devel

Instead of redefining them, use the global helper defined in
proxmox-product-config. Requires the crates init function to be
called exactly once before using in any callpath.

Signed-off-by: Christian Ebner <c.ebner@proxmox.com>
---
 src/tape/drive/mod.rs | 9 +--------
 1 file changed, 1 insertion(+), 8 deletions(-)

diff --git a/src/tape/drive/mod.rs b/src/tape/drive/mod.rs
index 66182a63d..b717aa71d 100644
--- a/src/tape/drive/mod.rs
+++ b/src/tape/drive/mod.rs
@@ -4,7 +4,6 @@ use std::path::PathBuf;
 
 use anyhow::{Error, bail, format_err};
 use nix::fcntl::OFlag;
-use nix::sys::stat::Mode;
 use serde::Deserialize;
 use serde_json::Value;
 use tracing::info;
@@ -604,17 +603,11 @@ fn open_device_lock(device_path: &str) -> Result<std::fs::File, Error> {
     let mut path = std::path::PathBuf::from(crate::tape::DRIVE_LOCK_DIR);
     path.push(lock_name);
 
-    let user = pbs_config::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,
+        proxmox_product_config::lockfile_create_options(),
         false,
     )
 }
-- 
2.47.3





^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [PATCH proxmox-backup v3 14/15] tape: use proxmox-product-config to generate create options
  2026-07-01 14:03 [PATCH proxmox-backup v3 00/15] fix 7642: avoid expensive uid/gid lookups for lock- and config-files Christian Ebner
                   ` (12 preceding siblings ...)
  2026-07-01 14:04 ` [PATCH proxmox-backup v3 13/15] tape: use proxmox-product-config lock file create options Christian Ebner
@ 2026-07-01 14:04 ` Christian Ebner
  2026-07-01 14:04 ` [PATCH proxmox-backup v3 15/15] tree-wide: use proxmox-product-config::get_api_user for user lookup Christian Ebner
  14 siblings, 0 replies; 16+ messages in thread
From: Christian Ebner @ 2026-07-01 14:04 UTC (permalink / raw)
  To: pbs-devel

Instead of redefining them, use the global helper defined in
proxmox-product-config. Requires the crates init function to be
called exactly once before using in any callpath.

Signed-off-by: Christian Ebner <c.ebner@proxmox.com>
---
 src/tape/inventory.rs           |  6 +-----
 src/tape/media_catalog_cache.rs | 10 +---------
 2 files changed, 2 insertions(+), 14 deletions(-)

diff --git a/src/tape/inventory.rs b/src/tape/inventory.rs
index 6c54738fb..12cca02c6 100644
--- a/src/tape/inventory.rs
+++ b/src/tape/inventory.rs
@@ -177,11 +177,7 @@ impl Inventory {
             // We cannot use chown inside test environment (no permissions)
             CreateOptions::new().perm(mode)
         } else {
-            let backup_user = pbs_config::backup_user()?;
-            CreateOptions::new()
-                .perm(mode)
-                .owner(backup_user.uid)
-                .group(backup_user.gid)
+            proxmox_product_config::default_create_options()
         };
 
         replace_file(&self.inventory_path, raw.as_bytes(), options, true)?;
diff --git a/src/tape/media_catalog_cache.rs b/src/tape/media_catalog_cache.rs
index 131d31d66..bb821109d 100644
--- a/src/tape/media_catalog_cache.rs
+++ b/src/tape/media_catalog_cache.rs
@@ -3,8 +3,6 @@ use std::path::Path;
 
 use anyhow::{Error, bail, format_err};
 
-use proxmox_sys::fs::CreateOptions;
-
 use crate::tape::{MediaCatalog, MediaId};
 
 /// Returns a list of (store, snapshot) for a given MediaId
@@ -93,13 +91,7 @@ fn write_snapshot_cache<P: AsRef<Path>>(
         }
     }
 
-    let backup_user = pbs_config::backup_user()?;
-    let mode = nix::sys::stat::Mode::from_bits_truncate(0o0640);
-    let options = CreateOptions::new()
-        .perm(mode)
-        .owner(backup_user.uid)
-        .group(backup_user.gid);
-
+    let options = proxmox_product_config::default_create_options();
     proxmox_sys::fs::replace_file(cache_path, data.as_bytes(), options, false)?;
 
     Ok(list)
-- 
2.47.3





^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [PATCH proxmox-backup v3 15/15] tree-wide: use proxmox-product-config::get_api_user for user lookup
  2026-07-01 14:03 [PATCH proxmox-backup v3 00/15] fix 7642: avoid expensive uid/gid lookups for lock- and config-files Christian Ebner
                   ` (13 preceding siblings ...)
  2026-07-01 14:04 ` [PATCH proxmox-backup v3 14/15] tape: use proxmox-product-config to generate " Christian Ebner
@ 2026-07-01 14:04 ` Christian Ebner
  14 siblings, 0 replies; 16+ messages in thread
From: Christian Ebner @ 2026-07-01 14:04 UTC (permalink / raw)
  To: pbs-devel

Use the global helper defined in proxmox-product-config over
pbs-config::backup_user(), which internally calls User::from_name()
using getpwnam_r(3) [0] and therefore can involve nsswitch.conf and
/etc/passwd parsing, including several syscalls to do so.
None of these call sites are performance critical, but using the
globally cached user info is favorable for consistency.

Requires proxmox-product-config::init() to be called exactly once
before using in any callpath of a binary.

Signed-off-by: Christian Ebner <c.ebner@proxmox.com>
---
 src/api2/admin/datastore.rs                  | 2 +-
 src/api2/admin/s3.rs                         | 4 ++--
 src/api2/config/datastore.rs                 | 2 +-
 src/api2/config/s3.rs                        | 2 +-
 src/config/mod.rs                            | 4 ++--
 src/server/jobstate.rs                       | 4 ++--
 src/server/metric_collection/mod.rs          | 2 +-
 src/server/metric_collection/pull_metrics.rs | 2 +-
 src/server/metric_collection/rrd.rs          | 2 +-
 src/server/mod.rs                            | 6 +++---
 src/server/notifications/mod.rs              | 4 ++--
 src/tape/changer/mod.rs                      | 2 +-
 src/tape/drive/mod.rs                        | 2 +-
 src/traffic_control_cache.rs                 | 2 +-
 14 files changed, 20 insertions(+), 20 deletions(-)

diff --git a/src/api2/admin/datastore.rs b/src/api2/admin/datastore.rs
index 39e8a1ce8..bc2b2436e 100644
--- a/src/api2/admin/datastore.rs
+++ b/src/api2/admin/datastore.rs
@@ -2442,7 +2442,7 @@ fn setup_mounted_device(datastore: &DataStoreConfig, tmp_mount_path: &str) -> Re
         "{tmp_mount_path}/{}",
         datastore.path.trim_start_matches('/')
     );
-    let backup_user = pbs_config::backup_user()?;
+    let backup_user = proxmox_product_config::get_api_user();
     let options = CreateOptions::new()
         .owner(backup_user.uid)
         .group(backup_user.gid);
diff --git a/src/api2/admin/s3.rs b/src/api2/admin/s3.rs
index c5bdcc01f..70ae4f46b 100644
--- a/src/api2/admin/s3.rs
+++ b/src/api2/admin/s3.rs
@@ -59,7 +59,7 @@ pub async fn check(
     };
     let request_counter_config = S3RequestCounterConfig {
         id: request_counter_id,
-        user: pbs_config::backup_user()?,
+        user: proxmox_product_config::get_api_user().clone(),
         base_path: S3_CLIENT_REQUEST_COUNTER_BASE_PATH.into(),
     };
 
@@ -147,7 +147,7 @@ pub async fn reset_counters(
         bail!("Cannot find s3 counters file '{path:?}'");
     }
 
-    let user = pbs_config::backup_user()?;
+    let user = proxmox_product_config::get_api_user().clone();
     let request_counters = SharedRequestCounters::open_shared_memory_mapped(path, user)
         .context("failed to open shared request counters")?;
     request_counters.reset(Ordering::Release);
diff --git a/src/api2/config/datastore.rs b/src/api2/config/datastore.rs
index 7c65470f9..e7028480c 100644
--- a/src/api2/config/datastore.rs
+++ b/src/api2/config/datastore.rs
@@ -196,7 +196,7 @@ pub(crate) fn do_create_datastore(
                 message: None,
             }))?;
         }
-        let backup_user = pbs_config::backup_user()?;
+        let backup_user = proxmox_product_config::get_api_user();
         ChunkStore::create(
             &datastore.name,
             path.clone(),
diff --git a/src/api2/config/s3.rs b/src/api2/config/s3.rs
index fc0d39e7d..fb4657281 100644
--- a/src/api2/config/s3.rs
+++ b/src/api2/config/s3.rs
@@ -346,7 +346,7 @@ pub async fn list_buckets(
     let empty_prefix = String::new();
     let request_counter_config = S3RequestCounterConfig {
         id,
-        user: pbs_config::backup_user()?,
+        user: proxmox_product_config::get_api_user().clone(),
         base_path: S3_CLIENT_REQUEST_COUNTER_BASE_PATH.into(),
     };
     let options = S3ClientOptions::from_config(
diff --git a/src/config/mod.rs b/src/config/mod.rs
index 0d795562f..cf084325d 100644
--- a/src/config/mod.rs
+++ b/src/config/mod.rs
@@ -26,7 +26,7 @@ pub mod tfa;
 pub fn check_configdir_permissions() -> Result<(), Error> {
     let cfgdir = pbs_buildcfg::CONFIGDIR;
 
-    let backup_user = pbs_config::backup_user()?;
+    let backup_user = proxmox_product_config::get_api_user();
     let backup_uid = backup_user.uid.as_raw();
     let backup_gid = backup_user.gid.as_raw();
 
@@ -71,7 +71,7 @@ pub fn create_configdir() -> Result<(), Error> {
         ),
     }
 
-    let backup_user = pbs_config::backup_user()?;
+    let backup_user = proxmox_product_config::get_api_user();
 
     nix::unistd::chown(cfgdir, Some(backup_user.uid), Some(backup_user.gid)).map_err(|err| {
         format_err!(
diff --git a/src/server/jobstate.rs b/src/server/jobstate.rs
index d2336ea62..7add06779 100644
--- a/src/server/jobstate.rs
+++ b/src/server/jobstate.rs
@@ -87,7 +87,7 @@ const JOB_STATE_BASEDIR: &str = concat!(PROXMOX_BACKUP_STATE_DIR_M!(), "/jobstat
 
 /// Create jobstate stat dir with correct permission
 pub fn create_jobstate_dir() -> Result<(), Error> {
-    let backup_user = pbs_config::backup_user()?;
+    let backup_user = proxmox_product_config::get_api_user();
 
     let opts = CreateOptions::new()
         .owner(backup_user.uid)
@@ -323,7 +323,7 @@ impl Job {
         let serialized = serde_json::to_string(&self.state)?;
         let path = get_path(&self.jobtype, &self.jobname);
 
-        let backup_user = pbs_config::backup_user()?;
+        let backup_user = proxmox_product_config::get_api_user();
         let mode = nix::sys::stat::Mode::from_bits_truncate(0o0644);
         // set the correct owner/group/permissions while saving file
         // owner(rw) = backup, group(r)= backup
diff --git a/src/server/metric_collection/mod.rs b/src/server/metric_collection/mod.rs
index 18625b1a5..bbbd1a8ed 100644
--- a/src/server/metric_collection/mod.rs
+++ b/src/server/metric_collection/mod.rs
@@ -250,7 +250,7 @@ fn collect_s3_stats(
     let s3_stats = match counters.entry(path.clone()) {
         Entry::Occupied(o) => load_s3_statistics(o.get()),
         Entry::Vacant(v) => {
-            let user = pbs_config::backup_user()?;
+            let user = proxmox_product_config::get_api_user().clone();
             let counters = SharedRequestCounters::open_shared_memory_mapped(path, user)?;
             let s3_stats = load_s3_statistics(&counters);
             v.insert(counters);
diff --git a/src/server/metric_collection/pull_metrics.rs b/src/server/metric_collection/pull_metrics.rs
index 5287517e2..6b2ea3cc5 100644
--- a/src/server/metric_collection/pull_metrics.rs
+++ b/src/server/metric_collection/pull_metrics.rs
@@ -23,7 +23,7 @@ static METRIC_CACHE: OnceLock<SharedCache> = OnceLock::new();
 
 /// Initialize the metric cache.
 pub(super) fn init() -> Result<(), Error> {
-    let backup_user = pbs_config::backup_user()?;
+    let backup_user = proxmox_product_config::get_api_user();
     let file_opts = CreateOptions::new()
         .owner(backup_user.uid)
         .group(backup_user.gid)
diff --git a/src/server/metric_collection/rrd.rs b/src/server/metric_collection/rrd.rs
index da79a835c..11699ebe2 100644
--- a/src/server/metric_collection/rrd.rs
+++ b/src/server/metric_collection/rrd.rs
@@ -34,7 +34,7 @@ fn get_cache() -> Result<&'static Cache, Error> {
 ///
 /// Note: Only a single process must do this (proxmox-backup-proxy)
 pub(super) fn init() -> Result<&'static Cache, Error> {
-    let backup_user = pbs_config::backup_user()?;
+    let backup_user = proxmox_product_config::get_api_user();
 
     let file_options = CreateOptions::new()
         .owner(backup_user.uid)
diff --git a/src/server/mod.rs b/src/server/mod.rs
index 979f5357e..b89f657c8 100644
--- a/src/server/mod.rs
+++ b/src/server/mod.rs
@@ -63,7 +63,7 @@ pub(crate) async fn notify_datastore_removed() -> Result<(), Error> {
 /// This exists to fixate the permissions for the run *base* directory while allowing intermediate
 /// directories after it to have different permissions.
 pub fn create_run_dir() -> Result<(), Error> {
-    let backup_user = pbs_config::backup_user()?;
+    let backup_user = proxmox_product_config::get_api_user();
     let opts = CreateOptions::new()
         .owner(backup_user.uid)
         .group(backup_user.gid);
@@ -72,7 +72,7 @@ pub fn create_run_dir() -> Result<(), Error> {
 }
 
 pub fn create_state_dir() -> Result<(), Error> {
-    let backup_user = pbs_config::backup_user()?;
+    let backup_user = proxmox_product_config::get_api_user();
     let opts = CreateOptions::new()
         .owner(backup_user.uid)
         .group(backup_user.gid);
@@ -86,7 +86,7 @@ pub fn create_state_dir() -> Result<(), Error> {
 
 /// Create active operations dir with correct permission.
 pub fn create_active_operations_dir() -> Result<(), Error> {
-    let backup_user = pbs_config::backup_user()?;
+    let backup_user = proxmox_product_config::get_api_user();
     let mode = nix::sys::stat::Mode::from_bits_truncate(0o0750);
     let options = CreateOptions::new()
         .perm(mode)
diff --git a/src/server/notifications/mod.rs b/src/server/notifications/mod.rs
index 0f772ddd9..181f12cbc 100644
--- a/src/server/notifications/mod.rs
+++ b/src/server/notifications/mod.rs
@@ -38,7 +38,7 @@ pub fn init() -> Result<(), Error> {
 /// Create the directory which will be used to temporarily store notifications
 /// which were sent from an unprivileged process.
 pub fn create_spool_dir() -> Result<(), Error> {
-    let backup_user = pbs_config::backup_user()?;
+    let backup_user = proxmox_product_config::get_api_user();
     let opts = CreateOptions::new()
         .owner(backup_user.uid)
         .group(backup_user.gid);
@@ -118,7 +118,7 @@ fn send_notification(notification: Notification) -> Result<(), Error> {
         let ser = serde_json::to_vec(&notification)?;
         let path = Path::new(SPOOL_DIR).join(format!("{id}.json", id = notification.id()));
 
-        let backup_user = pbs_config::backup_user()?;
+        let backup_user = proxmox_product_config::get_api_user();
         let opts = CreateOptions::new()
             .owner(backup_user.uid)
             .group(backup_user.gid);
diff --git a/src/tape/changer/mod.rs b/src/tape/changer/mod.rs
index 1593182ad..1c5e6dbf5 100644
--- a/src/tape/changer/mod.rs
+++ b/src/tape/changer/mod.rs
@@ -353,7 +353,7 @@ fn save_changer_state_cache(changer: &str, state: &MtxStatus) -> Result<(), Erro
 
     let state = serde_json::to_string_pretty(state)?;
 
-    let backup_user = pbs_config::backup_user()?;
+    let backup_user = proxmox_product_config::get_api_user();
     let mode = nix::sys::stat::Mode::from_bits_truncate(0o0644);
     let options = CreateOptions::new()
         .perm(mode)
diff --git a/src/tape/drive/mod.rs b/src/tape/drive/mod.rs
index b717aa71d..051977240 100644
--- a/src/tape/drive/mod.rs
+++ b/src/tape/drive/mod.rs
@@ -549,7 +549,7 @@ pub fn set_tape_device_state(drive: &str, state: &str) -> Result<(), Error> {
     let mut path = PathBuf::from(crate::tape::DRIVE_STATE_DIR);
     path.push(drive);
 
-    let backup_user = pbs_config::backup_user()?;
+    let backup_user = proxmox_product_config::get_api_user();
     let mode = nix::sys::stat::Mode::from_bits_truncate(0o0644);
     let options = CreateOptions::new()
         .perm(mode)
diff --git a/src/traffic_control_cache.rs b/src/traffic_control_cache.rs
index c2074af9d..02c99173e 100644
--- a/src/traffic_control_cache.rs
+++ b/src/traffic_control_cache.rs
@@ -114,7 +114,7 @@ fn create_limiter(
     burst: u64,
 ) -> Result<SharedRateLimit, Error> {
     if use_shared_memory {
-        let user = pbs_config::backup_user()?;
+        let user = proxmox_product_config::get_api_user().clone();
         let base_path = pbs_buildcfg::rundir!("/shmem/tbf");
         let limiter = SharedRateLimiter::mmap_shmem(name, rate, burst, user, base_path)?;
         Ok(Arc::new(limiter))
-- 
2.47.3





^ permalink raw reply related	[flat|nested] 16+ messages in thread

end of thread, other threads:[~2026-07-01 14:05 UTC | newest]

Thread overview: 16+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-07-01 14:03 [PATCH proxmox-backup v3 00/15] fix 7642: avoid expensive uid/gid lookups for lock- and config-files Christian Ebner
2026-07-01 14:03 ` [PATCH proxmox-backup v3 01/15] bin: api: early init proxmox-product-config Christian Ebner
2026-07-01 14:03 ` [PATCH proxmox-backup v3 02/15] bin: daily update: refactor to use proxmox-product-config Christian Ebner
2026-07-01 14:04 ` [PATCH proxmox-backup v3 03/15] pbs-config: use proxmox-product-config::replace_secret_config() Christian Ebner
2026-07-01 14:04 ` [PATCH proxmox-backup v3 04/15] pbs-config: use proxmox-product-config::replace_config() Christian Ebner
2026-07-01 14:04 ` [PATCH proxmox-backup v3 05/15] fix #7642: avoid expensive user lookups on file locking Christian Ebner
2026-07-01 14:04 ` [PATCH proxmox-backup v3 06/15] pbs-config: use proxmox-product-config helpers Christian Ebner
2026-07-01 14:04 ` [PATCH proxmox-backup v3 07/15] pbs-config: drop backup_group helper, use users gid instead Christian Ebner
2026-07-01 14:04 ` [PATCH proxmox-backup v3 08/15] pbs-datastore: use proxmox-product-config cached backup user Christian Ebner
2026-07-01 14:04 ` [PATCH proxmox-backup v3 09/15] pbs-datastore: use general helpers for file lock create options Christian Ebner
2026-07-01 14:04 ` [PATCH proxmox-backup v3 10/15] server: auth helpers: use proxmox-product-config create options helpers Christian Ebner
2026-07-01 14:04 ` [PATCH proxmox-backup v3 11/15] api: subscription: use proxmox-product-config create options Christian Ebner
2026-07-01 14:04 ` [PATCH proxmox-backup v3 12/15] tape: use proxmox-product-config helper for user lookup Christian Ebner
2026-07-01 14:04 ` [PATCH proxmox-backup v3 13/15] tape: use proxmox-product-config lock file create options Christian Ebner
2026-07-01 14:04 ` [PATCH proxmox-backup v3 14/15] tape: use proxmox-product-config to generate " Christian Ebner
2026-07-01 14:04 ` [PATCH proxmox-backup v3 15/15] tree-wide: use proxmox-product-config::get_api_user for user lookup Christian Ebner

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
Service provided by Proxmox Server Solutions GmbH | Privacy | Legal