Skip to content

Commit c964fb0

Browse files
authored
Merge pull request #82 from auyer/adaptative-tmp-download-location
Check space available in tmp dir before downloading
2 parents 3e098d3 + 5c56559 commit c964fb0

File tree

7 files changed

+156
-11
lines changed

7 files changed

+156
-11
lines changed

Cargo.lock

Lines changed: 20 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

fuzz/Cargo.lock

Lines changed: 20 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

libprotonup/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ astral-tokio-tar = "0.6"
3535
async-compression = { version = "0.4", features = ['gzip', 'xz', 'zstd', 'tokio'] }
3636
tempfile = "3.27"
3737
regex = "1.12"
38+
fs4 = { version = "0.13", features = ["sync"] }
3839

3940
[dev-dependencies]
4041
tar = "0.4"

libprotonup/src/constants.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@ pub const DEFAULT_LUTRIS_TOOL: &str = "WineGE";
55

66
pub const USER_AGENT: &str = "protoup-rs";
77

8+
pub const MIN_TEMP_SPACE_BYTES: u64 = 1_073_741_824; // 1GB
9+
pub const FALLBACK_TEMP_DIR: &str = ".local/state/protonup-rs/tmp";
10+
811
// pub const CONFIG_FILE: &str = "~/.config/protonup/config.ini";
912
// use const_format::formatcp;
1013
// pub const USER_AGENT: &'static str = formatcp!("{}/v{}", USER_AGENT, VERSION);

libprotonup/src/downloads.rs

Lines changed: 8 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -262,17 +262,14 @@ pub struct Download {
262262
impl Download {
263263
// output_dir checks if the file is supported and returns the standardized file name
264264
pub fn download_dir(&self) -> Result<PathBuf> {
265-
let mut output_dir = tempfile::tempdir()
266-
.expect("Failed to create tempdir")
267-
.keep();
268-
269-
match files::check_supported_extension(&self.download_url) {
270-
Ok(ext) => {
271-
output_dir.push(format!("{}.{}", &self.version, ext));
272-
Ok(output_dir)
273-
}
274-
Err(err) => Err(err),
275-
}
265+
crate::utils::create_download_temp_dir(&self.version, &self.download_url).with_context(
266+
|| {
267+
format!(
268+
"Failed to create temp download directory for {}",
269+
self.version
270+
)
271+
},
272+
)
276273
}
277274
}
278275

libprotonup/src/utils.rs

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,96 @@
1+
use std::fs;
12
use std::path::{Path, PathBuf};
3+
use tempfile::TempDir;
4+
5+
use crate::constants;
6+
7+
/// Builds the fallback temp directory path from the constant.
8+
/// The FALLBACK_TEMP_DIR constant contains the full relative path (e.g., ".local/state/protonup-rs/tmp")
9+
/// This function splits it by '/' and joins each component to the base directory.
10+
fn build_fallback_dir(base_dir: &Path) -> PathBuf {
11+
constants::FALLBACK_TEMP_DIR
12+
.split('/')
13+
.fold(base_dir.to_path_buf(), |acc, component| acc.join(component))
14+
}
15+
16+
/// Checks available disk space and returns an appropriate temp directory.
17+
/// If /tmp has less than 1GB available, returns a fallback directory under
18+
/// .local/state/protonup-rs/tmp. Creates the fallback directory if it doesn't exist.
19+
pub fn get_temp_dir() -> std::io::Result<PathBuf> {
20+
// Check available space on tmp dir
21+
let tmp_path = TempDir::with_prefix("protonup-rs-").map(|dir| dir.keep());
22+
23+
if let Ok(temp_dir) = tmp_path {
24+
let available_space = fs4::available_space(&temp_dir).unwrap_or(0);
25+
26+
if available_space >= constants::MIN_TEMP_SPACE_BYTES {
27+
// Enough space, use standard tempdir
28+
return Ok(temp_dir);
29+
}
30+
}
31+
32+
// Not enough space, or failed creating the temp dir, use fallback directory
33+
let fallback_dir = match dirs::state_dir() {
34+
Some(state_dir) => build_fallback_dir(&state_dir),
35+
None => {
36+
// Fallback to home directory if state_dir is not available
37+
let home_dir = dirs::home_dir().unwrap_or_else(|| PathBuf::from("."));
38+
build_fallback_dir(&home_dir)
39+
}
40+
};
41+
42+
// Create the fallback directory if it doesn't exist
43+
fs::create_dir_all(&fallback_dir)?;
44+
45+
Ok(fallback_dir)
46+
}
47+
48+
/// Creates a temporary download directory for a specific version.
49+
/// Handles disk space checking and creates the necessary directory structure.
50+
/// Returns the full path where the download file should be saved.
51+
pub fn create_download_temp_dir(version: &str, download_url: &str) -> std::io::Result<PathBuf> {
52+
let temp_dir = get_temp_dir()?;
53+
54+
let download_dir = temp_dir.join(format!("{}.{}", version, "download"));
55+
56+
// Create the version-specific subdirectory
57+
fs::create_dir_all(&download_dir)?;
58+
59+
// Determine the file extension from the download URL
60+
let ext = crate::files::check_supported_extension(download_url)
61+
.map_err(|e| std::io::Error::new(std::io::ErrorKind::InvalidInput, e.to_string()))?;
62+
63+
let download_path = download_dir.join(format!("{}.{}", version, ext));
64+
65+
Ok(download_path)
66+
}
67+
68+
/// Cleans up the fallback temp directory contents.
69+
/// This should be called on application exit to remove temporary files.
70+
pub fn cleanup_fallback_temp_dir() -> std::io::Result<()> {
71+
let fallback_dir = match dirs::state_dir() {
72+
Some(state_dir) => build_fallback_dir(&state_dir),
73+
None => {
74+
let home_dir = dirs::home_dir().unwrap_or_else(|| PathBuf::from("."));
75+
build_fallback_dir(&home_dir)
76+
}
77+
};
78+
79+
if fallback_dir.exists() {
80+
// Remove all contents of the fallback directory
81+
for entry in fs::read_dir(&fallback_dir)? {
82+
let entry = entry?;
83+
let path = entry.path();
84+
if path.is_dir() {
85+
fs::remove_dir_all(&path)?;
86+
} else {
87+
fs::remove_file(&path)?;
88+
}
89+
}
90+
}
91+
92+
Ok(())
93+
}
294

395
pub fn expand_tilde<P: AsRef<Path>>(path_user_input: P) -> Option<PathBuf> {
496
let p = path_user_input.as_ref();

protonup-rs/src/main.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,15 @@ mod manage_apps;
1515

1616
use manage_apps::manage_apps_routine;
1717

18+
/// Guard struct that cleans up temp directory when dropped
19+
struct TempDirCleanupGuard;
20+
21+
impl Drop for TempDirCleanupGuard {
22+
fn drop(&mut self) {
23+
let _ = libprotonup::utils::cleanup_fallback_temp_dir();
24+
}
25+
}
26+
1827
#[derive(Debug, Parser)]
1928
#[command(
2029
about = "Protonup-rs Install and Manage Proton/Wine and other Game Runtimes.\n\nRun without arguments to start the interactive TUI mode, or use the options:"
@@ -82,6 +91,9 @@ impl fmt::Display for InitialMenu {
8291

8392
#[tokio::main]
8493
async fn main() {
94+
// Register temp directory cleanup guard
95+
let _cleanup_guard = TempDirCleanupGuard;
96+
8597
let Opt {
8698
quick_download,
8799
force,

0 commit comments

Comments
 (0)