From fcd19b1c9faae155c7ce2665320ec9730a56d066 Mon Sep 17 00:00:00 2001 From: rustmailer Date: Wed, 21 Jan 2026 00:10:07 +0800 Subject: [PATCH] Fix: storage dir creation logic and permissions issues ( #120, #121) --- src/modules/indexer/manager.rs | 29 ++++++++++++++++++++++-- src/modules/settings/cli.rs | 36 ++++++++++++----------------- src/modules/settings/io.rs | 41 ++++++++++++++++++++++++++++++++++ src/modules/settings/mod.rs | 1 + 4 files changed, 83 insertions(+), 24 deletions(-) create mode 100644 src/modules/settings/io.rs diff --git a/src/modules/indexer/manager.rs b/src/modules/indexer/manager.rs index d019e67..d293ffa 100644 --- a/src/modules/indexer/manager.rs +++ b/src/modules/indexer/manager.rs @@ -76,6 +76,7 @@ use tokio::{ sync::{mpsc, Mutex}, task, }; +use tracing::info; pub static ENVELOPE_INDEX_MANAGER: LazyLock = LazyLock::new(EnvelopeIndexManager::new); @@ -188,13 +189,23 @@ impl EnvelopeIndexManager { } fn open_or_create_index(index_dir: &PathBuf) -> Index { - if !index_dir.exists() { + let need_create = !index_dir.exists() + || index_dir + .read_dir() + .map(|mut d| d.next().is_none()) + .unwrap_or(true); + if need_create { + info!( + "Email index not found or empty, creating new index at {}", + index_dir.display() + ); std::fs::create_dir_all(&index_dir).unwrap_or_else(|e| { panic!("Failed to create index directory {:?}: {}", index_dir, e) }); Index::create_in_dir(&index_dir, SchemaTools::envelope_schema()) .unwrap_or_else(|e| panic!("Failed to create index in {:?}: {}", index_dir, e)) } else { + info!("Opening existing email index at {}", index_dir.display()); open(&index_dir) } } @@ -1331,7 +1342,17 @@ impl EmlIndexManager { } fn open_or_create_index(index_dir: &PathBuf) -> Index { - if !index_dir.exists() { + let need_create = !index_dir.exists() + || index_dir + .read_dir() + .map(|mut d| d.next().is_none()) + .unwrap_or(true); + + if need_create { + info!( + "Email data storage not found or empty, creating new mail storage at {}", + index_dir.display() + ); std::fs::create_dir_all(&index_dir).unwrap_or_else(|e| { panic!("Failed to create index directory {:?}: {}", index_dir, e) }); @@ -1347,6 +1368,10 @@ impl EmlIndexManager { .create_in_dir(&index_dir) .unwrap_or_else(|e| panic!("Failed to create index in {:?}: {}", index_dir, e)) } else { + info!( + "Opening existing email data storage at {}", + index_dir.display() + ); open(&index_dir) } } diff --git a/src/modules/settings/cli.rs b/src/modules/settings/cli.rs index 6d2cad7..d1b6ca3 100644 --- a/src/modules/settings/cli.rs +++ b/src/modules/settings/cli.rs @@ -16,6 +16,7 @@ // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . +use crate::modules::settings::io::check_dir_read_write; use clap::{builder::ValueParser, Parser, ValueEnum}; use std::{collections::HashSet, env, fmt, path::PathBuf, sync::LazyLock}; @@ -64,7 +65,7 @@ pub struct Settings { )] pub bichon_bind_ip: Option, - /// RustMail public URL (default: "http://localhost:15630") + /// bichon public URL (default: "http://localhost:15630") #[clap( long, default_value = "http://localhost:15630", @@ -160,15 +161,12 @@ pub struct Settings { help = "Set the file path for bichon database", value_parser = ValueParser::new(|s: &str| { let path = PathBuf::from(s); + if !path.is_absolute() { - return Err("Path must be an absolute directory path".to_string()); - } - if !path.exists() { - return Err(format!("Path {:?} does not exist", path)); - } - if !path.is_dir() { - return Err(format!("Path {:?} is not a directory", path)); + return Err("'bichon_root_dir' must be an absolute directory path".to_string()); } + + check_dir_read_write(&path)?; Ok(s.to_string()) }) )] @@ -179,15 +177,12 @@ pub struct Settings { help = "Set the file path for email index directory", value_parser = ValueParser::new(|s: &str| { let path = PathBuf::from(s); + if !path.is_absolute() { - return Err("Path must be an absolute directory path".to_string()); - } - if !path.exists() { - return Err(format!("Path {:?} does not exist", path)); - } - if !path.is_dir() { - return Err(format!("Path {:?} is not a directory", path)); + return Err("'bichon_index_dir' must be an absolute directory path".to_string()); } + + check_dir_read_write(&path)?; Ok(s.to_string()) }) )] @@ -198,15 +193,12 @@ pub struct Settings { help = "Set the file path for email data directory", value_parser = ValueParser::new(|s: &str| { let path = PathBuf::from(s); + if !path.is_absolute() { - return Err("Path must be an absolute directory path".to_string()); - } - if !path.exists() { - return Err(format!("Path {:?} does not exist", path)); - } - if !path.is_dir() { - return Err(format!("Path {:?} is not a directory", path)); + return Err("'bichon_data_dir' must be an absolute directory path".to_string()); } + + check_dir_read_write(&path)?; Ok(s.to_string()) }) )] diff --git a/src/modules/settings/io.rs b/src/modules/settings/io.rs new file mode 100644 index 0000000..34352ee --- /dev/null +++ b/src/modules/settings/io.rs @@ -0,0 +1,41 @@ +use std::fs::{self, OpenOptions}; +use std::io::{Read, Write}; +use std::path::Path; + +pub fn check_dir_read_write(path: &Path) -> Result<(), String> { + if !path.exists() { + fs::create_dir_all(path) + .map_err(|e| format!("Cannot create directory {:?}: {}", path, e))?; + } + + if !path.is_dir() { + return Err(format!("{:?} is not a directory", path)); + } + + let test_file = path.join(".bichon_perm_test"); + + { + let mut f = OpenOptions::new() + .create(true) + .write(true) + .open(&test_file) + .map_err(|e| format!("Directory {:?} is not writable: {}", path, e))?; + + f.write_all(b"test") + .map_err(|e| format!("Directory {:?} is not writable: {}", path, e))?; + } + + { + let mut buf = Vec::new(); + let mut f = OpenOptions::new() + .read(true) + .open(&test_file) + .map_err(|e| format!("Directory {:?} is not readable: {}", path, e))?; + + f.read_to_end(&mut buf) + .map_err(|e| format!("Directory {:?} is not readable: {}", path, e))?; + } + let _ = fs::remove_file(&test_file); + + Ok(()) +} diff --git a/src/modules/settings/mod.rs b/src/modules/settings/mod.rs index 2fe55d5..1e5cb77 100644 --- a/src/modules/settings/mod.rs +++ b/src/modules/settings/mod.rs @@ -23,6 +23,7 @@ use crate::modules::settings::cli::Settings; pub mod cli; pub mod dir; +pub mod io; pub mod proxy; pub mod system;