Skip to content

Commit

Permalink
Merge pull request #335 from Narayanbhat166/support_max_upload_file_s…
Browse files Browse the repository at this point in the history
…ize_config

* feat: add a check in the store file function to check whether the upload dir is full

* chore: cargo clippy

* feat: add test cases

* refactor: rename config variable

* Revert "refactor: rename config variable"

This reverts commit 2328473.

* test(fixtures): update fixture for max upload size

* refactor(server): vendor get_size function from fx_extra

* refactor(paste): update error message

* fix(lints): apply clippy suggestions

---------

Co-authored-by: Orhun Parmaksız <[email protected]>
  • Loading branch information
orhun authored Dec 11, 2024
2 parents 1bbc764 + aad5f77 commit c5f7377
Show file tree
Hide file tree
Showing 6 changed files with 75 additions and 2 deletions.
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 9 additions & 0 deletions fixtures/test-server-upload-dir-limit/config.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
[server]
address = "127.0.0.1:8000"
max_content_length = "10KB"
max_upload_dir_size = "20KB"
upload_path = "./upload"

[paste]
default_extension = "txt"
duplicate_files = true
19 changes: 19 additions & 0 deletions fixtures/test-server-upload-dir-limit/test.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
#!/usr/bin/env bash

setup() {
truncate -s 9KB bigfile1 bigfile2 bigfile3
}

run_test() {
result=$(curl -s -F "file=@bigfile1" localhost:8000)
result=$(curl -s -F "file=@bigfile2" localhost:8000)
curl -s "$result"

result=$(curl -s -F "file=@bigfile3" localhost:8000)
test "upload directory size limit exceeded" = "$result"
}

teardown() {
rm bigfile*
rm -r upload
}
2 changes: 2 additions & 0 deletions src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ pub struct ServerConfig {
pub max_content_length: Byte,
/// Storage path.
pub upload_path: PathBuf,
/// Maximum upload directory size.
pub max_upload_dir_size: Option<Byte>,
/// Request timeout.
#[serde(default, with = "humantime_serde")]
pub timeout: Option<Duration>,
Expand Down
20 changes: 19 additions & 1 deletion src/paste.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,15 @@ use crate::header::ContentDisposition;
use crate::util;
use actix_web::{error, Error};
use awc::Client;
use std::convert::{TryFrom, TryInto};
use std::fs::{self, File};
use std::io::{Error as IoError, ErrorKind as IoErrorKind, Result as IoResult, Write};
use std::path::{Path, PathBuf};
use std::str;
use std::sync::RwLock;
use std::{
convert::{TryFrom, TryInto},
ops::Add,
};
use url::Url;

/// Type of the data to store.
Expand Down Expand Up @@ -109,6 +112,21 @@ impl Paste {
}
}
}

if let Some(max_dir_size) = config.server.max_upload_dir_size {
let file_size = u64::try_from(self.data.len()).unwrap_or_default();
let upload_dir = self.type_.get_path(&config.server.upload_path)?;
let current_size_of_upload_dir = util::get_dir_size(&upload_dir).map_err(|e| {
error::ErrorInternalServerError(format!("could not get directory size: {e}"))
})?;
let expected_size_of_upload_dir = current_size_of_upload_dir.add(file_size);
if expected_size_of_upload_dir > max_dir_size {
return Err(error::ErrorInsufficientStorage(
"upload directory size limit exceeded",
));
}
}

let mut file_name = match PathBuf::from(file_name)
.file_name()
.and_then(|v| v.to_str())
Expand Down
25 changes: 25 additions & 0 deletions src/util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,31 @@ pub fn safe_path_join<B: AsRef<Path>, P: AsRef<Path>>(base: B, part: P) -> IoRes
Ok(new_path)
}

/// Returns the size of the directory at the given path.
///
/// This function is recursive, and will calculate the size of all files and directories.
/// If a symlink is encountered, the size of the symlink itself is counted, not its target.
///
/// Adopted from <https://docs.rs/fs_extra/latest/src/fs_extra/dir.rs.html>
pub fn get_dir_size(path: &Path) -> IoResult<u64> {
let path_metadata = path.symlink_metadata()?;
let mut size_in_bytes = 0;
if path_metadata.is_dir() {
for entry in std::fs::read_dir(path)? {
let entry = entry?;
let entry_metadata = entry.metadata()?;
if entry_metadata.is_dir() {
size_in_bytes += get_dir_size(&entry.path())?;
} else {
size_in_bytes += entry_metadata.len();
}
}
} else {
size_in_bytes = path_metadata.len();
}
Ok(size_in_bytes)
}

#[cfg(test)]
mod tests {
use super::*;
Expand Down

0 comments on commit c5f7377

Please sign in to comment.