use std::fs;
#[cfg(unix)]
use std::fs::OpenOptions;
use std::io;
use std::path::Path;
#[cfg(unix)]
use std::path::PathBuf;
pub(super) fn create_data_dir(dir: &Path) -> io::Result<()> {
if dir.as_os_str().is_empty() || dir.is_dir() {
return Ok(());
}
if let Some(parent) = dir.parent() {
fs::create_dir_all(parent)?;
}
match make_private_dir(dir) {
Ok(()) => Ok(()),
Err(e) if e.kind() == io::ErrorKind::AlreadyExists && dir.is_dir() => Ok(()),
Err(e) => Err(e),
}
}
#[cfg(unix)]
fn make_private_dir(dir: &Path) -> io::Result<()> {
use std::os::unix::fs::DirBuilderExt;
fs::DirBuilder::new().mode(0o700).create(dir)
}
#[cfg(not(unix))]
fn make_private_dir(dir: &Path) -> io::Result<()> {
fs::create_dir(dir)
}
#[cfg(unix)]
pub(super) fn prepare_db_files(db: &Path) {
use std::os::unix::fs::OpenOptionsExt;
let _ = OpenOptions::new()
.write(true)
.create_new(true)
.mode(0o600)
.open(db);
tighten(db);
for suffix in SIDECAR_SUFFIXES {
tighten(&sidecar(db, suffix));
}
}
#[cfg(not(unix))]
pub(super) fn prepare_db_files(_db: &Path) {}
#[cfg(unix)]
const SIDECAR_SUFFIXES: [&str; 3] = ["-wal", "-shm", "-journal"];
#[cfg(unix)]
fn sidecar(db: &Path, suffix: &str) -> PathBuf {
let mut name = db.as_os_str().to_owned();
name.push(suffix);
PathBuf::from(name)
}
#[cfg(unix)]
fn tighten(path: &Path) {
let euid = unsafe { libc::geteuid() };
tighten_as(path, euid);
}
#[cfg(unix)]
pub(super) fn tighten_as(path: &Path, euid: u32) {
use std::os::unix::fs::{MetadataExt, OpenOptionsExt, PermissionsExt};
let Ok(file) = OpenOptions::new()
.read(true)
.custom_flags(libc::O_NOFOLLOW | libc::O_NONBLOCK)
.open(path)
else {
return;
};
let Ok(meta) = file.metadata() else {
return;
};
if !meta.file_type().is_file() || meta.uid() != euid {
return;
}
let mode = meta.mode() & 0o777;
if mode & 0o077 == 0 {
return;
}
let owner = mode & 0o700;
let target = if owner & 0o600 == 0o600 { 0o600 } else { owner };
let _ = file.set_permissions(fs::Permissions::from_mode(target));
}