use std::fs::{self, File, OpenOptions};
use std::io::{self, Write};
use std::os::unix::fs::{OpenOptionsExt, PermissionsExt};
use std::path::Path;
pub fn bytes_to_hex(bytes: &[u8]) -> String {
bytes.iter().map(|b| format!("{:02x}", b)).collect()
}
pub fn create_secure_file<P: AsRef<Path>>(path: P) -> io::Result<File> {
OpenOptions::new()
.create(true)
.write(true)
.truncate(true)
.mode(0o600)
.open(path.as_ref())
}
#[allow(dead_code)]
pub fn write_secure_file<P: AsRef<Path>>(path: P, data: &[u8]) -> io::Result<()> {
let mut file = create_secure_file(path)?;
file.write_all(data)
}
pub fn create_secure_dir_all<P: AsRef<Path>>(path: P) -> io::Result<()> {
let path = path.as_ref();
if path.exists() {
fs::set_permissions(path, fs::Permissions::from_mode(0o700))?;
return Ok(());
}
fs::create_dir_all(path)?;
fs::set_permissions(path, fs::Permissions::from_mode(0o700))?;
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
use std::fs;
use tempfile::tempdir;
#[test]
fn test_bytes_to_hex() {
assert_eq!(bytes_to_hex(&[]), "");
assert_eq!(bytes_to_hex(&[0x00]), "00");
assert_eq!(bytes_to_hex(&[0xff]), "ff");
assert_eq!(bytes_to_hex(&[0x01, 0x23, 0x45, 0x67]), "01234567");
assert_eq!(bytes_to_hex(&[0xab, 0xcd, 0xef]), "abcdef");
}
#[test]
fn test_create_secure_file() {
let dir = tempdir().expect("Failed to create temp dir");
let file_path = dir.path().join("test_file");
let file = create_secure_file(&file_path).expect("Failed to create secure file");
drop(file);
assert!(file_path.exists());
let metadata = fs::metadata(&file_path).expect("Failed to get metadata");
let mode = metadata.permissions().mode();
assert_eq!(mode & 0o777, 0o600, "File should have 0o600 permissions");
}
#[test]
fn test_write_secure_file() {
let dir = tempdir().expect("Failed to create temp dir");
let file_path = dir.path().join("test_file");
write_secure_file(&file_path, b"test data").expect("Failed to write secure file");
assert!(file_path.exists());
let metadata = fs::metadata(&file_path).expect("Failed to get metadata");
let mode = metadata.permissions().mode();
assert_eq!(mode & 0o777, 0o600, "File should have 0o600 permissions");
let contents = fs::read(&file_path).expect("Failed to read file");
assert_eq!(contents, b"test data");
}
#[test]
fn test_create_secure_dir_all() {
let dir = tempdir().expect("Failed to create temp dir");
let nested_path = dir.path().join("a/b/c");
create_secure_dir_all(&nested_path).expect("Failed to create secure directories");
assert!(nested_path.exists());
let metadata = fs::metadata(&nested_path).expect("Failed to get metadata");
assert!(metadata.is_dir());
let mode = metadata.permissions().mode();
assert_eq!(
mode & 0o777,
0o700,
"Directory a/b/c should have 0o700 permissions"
);
}
#[test]
fn test_create_secure_dir_all_existing() {
let dir = tempdir().expect("Failed to create temp dir");
let path = dir.path().join("existing_dir");
fs::create_dir_all(&path).expect("Failed to create directory");
fs::set_permissions(&path, fs::Permissions::from_mode(0o755))
.expect("Failed to set permissions");
create_secure_dir_all(&path).expect("Failed to update permissions");
let metadata = fs::metadata(&path).expect("Failed to get metadata");
let mode = metadata.permissions().mode();
assert_eq!(
mode & 0o777,
0o700,
"Directory should have 0o700 permissions"
);
}
}