Fix: storage dir creation logic and permissions issues ( #120, #121)

This commit is contained in:
rustmailer
2026-01-21 00:10:07 +08:00
parent df1a6f8c5b
commit fcd19b1c9f
4 changed files with 83 additions and 24 deletions
+27 -2
View File
@@ -76,6 +76,7 @@ use tokio::{
sync::{mpsc, Mutex},
task,
};
use tracing::info;
pub static ENVELOPE_INDEX_MANAGER: LazyLock<EnvelopeIndexManager> =
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)
}
}
+14 -22
View File
@@ -16,6 +16,7 @@
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
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<String>,
/// 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())
})
)]
+41
View File
@@ -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(())
}
+1
View File
@@ -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;