fix(photos): serialize save to enforce quota under concurrency; avoid empty-dir leak
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
3af4893a81
commit
9d93a33bae
@ -21,20 +21,28 @@ pub enum SaveError {
|
||||
}
|
||||
|
||||
/// 按用户隔离的图片仓库。内部只是一个根目录路径,`Clone` 廉价。
|
||||
/// `lock` 串行化 save 的 “计数 → 写入” 临界区,杜绝并发超额(TOCTOU)。
|
||||
#[derive(Clone)]
|
||||
pub struct PhotoStore {
|
||||
base: PathBuf,
|
||||
lock: std::sync::Arc<std::sync::Mutex<()>>,
|
||||
}
|
||||
|
||||
impl PhotoStore {
|
||||
/// 根目录取 `PHOTO_DIR`,默认 `photo-wall`(相对进程 cwd)。
|
||||
pub fn from_env() -> Self {
|
||||
let base = std::env::var("PHOTO_DIR").unwrap_or_else(|_| "photo-wall".to_string());
|
||||
Self { base: base.into() }
|
||||
Self {
|
||||
base: base.into(),
|
||||
lock: std::sync::Arc::new(std::sync::Mutex::new(())),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new(base: impl Into<PathBuf>) -> Self {
|
||||
Self { base: base.into() }
|
||||
Self {
|
||||
base: base.into(),
|
||||
lock: std::sync::Arc::new(std::sync::Mutex::new(())),
|
||||
}
|
||||
}
|
||||
|
||||
fn user_dir(&self, user: &str) -> Option<PathBuf> {
|
||||
@ -48,10 +56,15 @@ impl PhotoStore {
|
||||
pub fn save(&self, user: &str, data_url: &str) -> Result<String, SaveError> {
|
||||
let dir = self.user_dir(user).ok_or(SaveError::BadUser)?;
|
||||
let (ext, bytes) = parse_data_url(data_url)?;
|
||||
std::fs::create_dir_all(&dir).map_err(|_| SaveError::Io)?;
|
||||
// 串行化 “计数 → 写入”:防止单用户并发上传同时通过额度检查而超额。
|
||||
// 中毒锁仅守护一个 (),恢复内部值继续即可,无需 panic。
|
||||
let _guard = self.lock.lock().unwrap_or_else(|e| e.into_inner());
|
||||
// 先查额度(`list_in` 对不存在的目录返回空 → 0),满了直接返回,
|
||||
// 避免给已满用户白白创建空目录。
|
||||
if list_in(&dir).len() >= MAX_PHOTOS {
|
||||
return Err(SaveError::Full);
|
||||
}
|
||||
std::fs::create_dir_all(&dir).map_err(|_| SaveError::Io)?;
|
||||
let id = format!("{}.{}", new_stem(), ext);
|
||||
std::fs::write(dir.join(&id), &bytes).map_err(|_| SaveError::Io)?;
|
||||
Ok(id)
|
||||
|
||||
Loading…
Reference in New Issue
Block a user