[pbs-devel] [PATCH pbs-client v5] fix #3699: client: prefer xdg cache directory for tmp files
Maximiliano Sandoval
m.sandoval at proxmox.com
Wed Jul 17 12:18:35 CEST 2024
Adds a helper to create temporal files in XDG_CACHE_HOME. If we cannot
create a file there, we fallback to /tmp as before.
Note that the temporary files stored by the client might grow
arbitrarily in size, making XDG_RUNTIME_DIR a less desirable option.
Citing the Arch wiki [1]:
> Should not store large files as it may be mounted as a tmpfs.
[1] https://wiki.archlinux.org/title/XDG_Base_Directory
Signed-off-by: Maximiliano Sandoval <m.sandoval at proxmox.com>
---
Differences from v4:
- Use backticks in documentation for `/tmp` and `XDG_CACHE_HOME`
Differences from v3:
- Link create_tmp_file in docs
- Improve commit message
Differences from v2:
- Files are created at XDG_CACHE_HOME directly
- If opening fails, we try opening at /tmp
- We check if the directory exists as part of the error handling of open()
pbs-client/src/backup_reader.rs | 31 ++++++++--------------------
pbs-client/src/backup_writer.rs | 13 ++----------
pbs-client/src/tools/mod.rs | 26 +++++++++++++++++++++++
proxmox-backup-client/src/catalog.rs | 19 +++--------------
4 files changed, 40 insertions(+), 49 deletions(-)
diff --git a/pbs-client/src/backup_reader.rs b/pbs-client/src/backup_reader.rs
index 36d8ebcf..4706abc7 100644
--- a/pbs-client/src/backup_reader.rs
+++ b/pbs-client/src/backup_reader.rs
@@ -1,7 +1,6 @@
use anyhow::{format_err, Error};
use std::fs::File;
use std::io::{Seek, SeekFrom, Write};
-use std::os::unix::fs::OpenOptionsExt;
use std::sync::Arc;
use futures::future::AbortHandle;
@@ -141,18 +140,14 @@ impl BackupReader {
/// Download a .blob file
///
- /// This creates a temporary file in /tmp (using O_TMPFILE). The data is verified using
- /// the provided manifest.
+ /// This creates a temporary file (See [`crate::tools::create_tmp_file`] for
+ /// details). The data is verified using the provided manifest.
pub async fn download_blob(
&self,
manifest: &BackupManifest,
name: &str,
) -> Result<DataBlobReader<'_, File>, Error> {
- let mut tmpfile = std::fs::OpenOptions::new()
- .write(true)
- .read(true)
- .custom_flags(libc::O_TMPFILE)
- .open("/tmp")?;
+ let mut tmpfile = crate::tools::create_tmp_file()?;
self.download(name, &mut tmpfile).await?;
@@ -167,18 +162,14 @@ impl BackupReader {
/// Download dynamic index file
///
- /// This creates a temporary file in /tmp (using O_TMPFILE). The index is verified using
- /// the provided manifest.
+ /// This creates a temporary file (See [`crate::tools::create_tmp_file`] for
+ /// details). The index is verified using the provided manifest.
pub async fn download_dynamic_index(
&self,
manifest: &BackupManifest,
name: &str,
) -> Result<DynamicIndexReader, Error> {
- let mut tmpfile = std::fs::OpenOptions::new()
- .write(true)
- .read(true)
- .custom_flags(libc::O_TMPFILE)
- .open("/tmp")?;
+ let mut tmpfile = crate::tools::create_tmp_file()?;
self.download(name, &mut tmpfile).await?;
@@ -194,18 +185,14 @@ impl BackupReader {
/// Download fixed index file
///
- /// This creates a temporary file in /tmp (using O_TMPFILE). The index is verified using
- /// the provided manifest.
+ /// This creates a temporary file (See [`crate::tools::create_tmp_file`] for
+ /// details). The index is verified using the provided manifest.
pub async fn download_fixed_index(
&self,
manifest: &BackupManifest,
name: &str,
) -> Result<FixedIndexReader, Error> {
- let mut tmpfile = std::fs::OpenOptions::new()
- .write(true)
- .read(true)
- .custom_flags(libc::O_TMPFILE)
- .open("/tmp")?;
+ let mut tmpfile = crate::tools::create_tmp_file()?;
self.download(name, &mut tmpfile).await?;
diff --git a/pbs-client/src/backup_writer.rs b/pbs-client/src/backup_writer.rs
index 813c8d60..d63c09b5 100644
--- a/pbs-client/src/backup_writer.rs
+++ b/pbs-client/src/backup_writer.rs
@@ -1,6 +1,5 @@
use std::collections::HashSet;
use std::future::Future;
-use std::os::unix::fs::OpenOptionsExt;
use std::sync::atomic::{AtomicU64, AtomicUsize, Ordering};
use std::sync::{Arc, Mutex};
@@ -531,11 +530,7 @@ impl BackupWriter {
manifest: &BackupManifest,
known_chunks: Arc<Mutex<HashSet<[u8; 32]>>>,
) -> Result<FixedIndexReader, Error> {
- let mut tmpfile = std::fs::OpenOptions::new()
- .write(true)
- .read(true)
- .custom_flags(libc::O_TMPFILE)
- .open("/tmp")?;
+ let mut tmpfile = crate::tools::create_tmp_file()?;
let param = json!({ "archive-name": archive_name });
self.h2
@@ -570,11 +565,7 @@ impl BackupWriter {
manifest: &BackupManifest,
known_chunks: Arc<Mutex<HashSet<[u8; 32]>>>,
) -> Result<DynamicIndexReader, Error> {
- let mut tmpfile = std::fs::OpenOptions::new()
- .write(true)
- .read(true)
- .custom_flags(libc::O_TMPFILE)
- .open("/tmp")?;
+ let mut tmpfile = crate::tools::create_tmp_file()?;
let param = json!({ "archive-name": archive_name });
self.h2
diff --git a/pbs-client/src/tools/mod.rs b/pbs-client/src/tools/mod.rs
index 90fac696..772cc126 100644
--- a/pbs-client/src/tools/mod.rs
+++ b/pbs-client/src/tools/mod.rs
@@ -5,9 +5,11 @@ use std::ffi::OsStr;
use std::fs::File;
use std::io::{BufRead, BufReader};
use std::os::unix::ffi::OsStrExt;
+use std::os::unix::fs::OpenOptionsExt;
use std::os::unix::io::FromRawFd;
use std::path::PathBuf;
use std::process::Command;
+use std::sync::OnceLock;
use anyhow::{bail, format_err, Context, Error};
use serde_json::{json, Value};
@@ -736,3 +738,27 @@ pub async fn pxar_metadata_catalog_lookup<T: Clone + ReadAt>(
Ok(entries)
}
+
+/// Creates a temporary file (with `O_TMPFILE`) in `XDG_CACHE_HOME`. If we
+/// cannot create the file there it will be created in `/tmp` instead.
+pub fn create_tmp_file() -> std::io::Result<std::fs::File> {
+ static TMP_PATH: OnceLock<std::path::PathBuf> = OnceLock::new();
+ let tmp_path = TMP_PATH.get_or_init(|| {
+ xdg::BaseDirectories::new()
+ .map(|base| base.get_cache_home())
+ .unwrap_or_else(|_| std::path::PathBuf::from("/tmp"))
+ });
+
+ let mut open_opts_binding = std::fs::OpenOptions::new();
+ let builder = open_opts_binding
+ .write(true)
+ .read(true)
+ .custom_flags(libc::O_TMPFILE);
+ builder.open(tmp_path).or_else(|err| {
+ if tmp_path != std::path::Path::new("/tmp") {
+ builder.open("/tmp")
+ } else {
+ Err(err)
+ }
+ })
+}
diff --git a/proxmox-backup-client/src/catalog.rs b/proxmox-backup-client/src/catalog.rs
index 276457c1..0a374c01 100644
--- a/proxmox-backup-client/src/catalog.rs
+++ b/proxmox-backup-client/src/catalog.rs
@@ -1,5 +1,4 @@
use std::io::{Seek, SeekFrom};
-use std::os::unix::fs::OpenOptionsExt;
use std::sync::Arc;
use anyhow::{bail, format_err, Error};
@@ -106,11 +105,7 @@ async fn dump_catalog(param: Value) -> Result<Value, Error> {
let mut reader = BufferedDynamicReader::new(index, chunk_reader);
- let mut catalogfile = std::fs::OpenOptions::new()
- .write(true)
- .read(true)
- .custom_flags(libc::O_TMPFILE)
- .open("/tmp")?;
+ let mut catalogfile = pbs_client::tools::create_tmp_file()?;
std::io::copy(&mut reader, &mut catalogfile)
.map_err(|err| format_err!("unable to download catalog - {}", err))?;
@@ -198,11 +193,7 @@ async fn catalog_shell(param: Value) -> Result<(), Error> {
)
.await?;
- let mut tmpfile = std::fs::OpenOptions::new()
- .write(true)
- .read(true)
- .custom_flags(libc::O_TMPFILE)
- .open("/tmp")?;
+ let mut tmpfile = pbs_client::tools::create_tmp_file()?;
let (manifest, _) = client.download_manifest().await?;
manifest.check_fingerprint(crypt_config.as_ref().map(Arc::as_ref))?;
@@ -233,11 +224,7 @@ async fn catalog_shell(param: Value) -> Result<(), Error> {
most_used,
);
let mut reader = BufferedDynamicReader::new(index, chunk_reader);
- let mut catalogfile = std::fs::OpenOptions::new()
- .write(true)
- .read(true)
- .custom_flags(libc::O_TMPFILE)
- .open("/tmp")?;
+ let mut catalogfile = pbs_client::tools::create_tmp_file()?;
std::io::copy(&mut reader, &mut catalogfile)
.map_err(|err| format_err!("unable to download catalog - {}", err))?;
--
2.39.2
More information about the pbs-devel
mailing list