RedoxFS 0.5.0

Significant re-design to use copy-on-write and hashes for all data
This commit is contained in:
Jeremy Soller
2021-12-29 16:11:54 -07:00
committed by Jeremy Soller
parent c98e63af87
commit 731b97262b
27 changed files with 3837 additions and 1602 deletions
+161 -34
View File
@@ -1,7 +1,13 @@
use core::ops::{Deref, DerefMut};
use core::{fmt, mem, slice};
use simple_endian::*;
use {BLOCK_SIZE, SIGNATURE, VERSION};
use aes::{Aes128, BlockDecrypt, BlockEncrypt};
use uuid::Uuid;
use crate::{AllocList, BlockPtr, KeySlot, Tree, BLOCK_SIZE, SIGNATURE, VERSION};
pub const HEADER_RING: u64 = 256;
/// The header of the filesystem
#[derive(Clone, Copy)]
@@ -10,48 +16,142 @@ pub struct Header {
/// Signature, should be SIGNATURE
pub signature: [u8; 8],
/// Version, should be VERSION
pub version: u64,
pub version: u64le,
/// Disk ID, a 128-bit unique identifier
pub uuid: [u8; 16],
/// Disk size, in number of BLOCK_SIZE sectors
pub size: u64,
/// Block of root node
pub root: u64,
/// Block of free space node
pub free: u64,
pub size: u64le,
/// Generation of header
pub generation: u64le,
/// Block of first tree node
pub tree: BlockPtr<Tree>,
/// Block of last alloc node
pub alloc: BlockPtr<AllocList>,
/// Key slots
pub key_slots: [KeySlot; 64],
/// Padding
pub padding: [u8; BLOCK_SIZE as usize - 56],
pub padding: [u8; BLOCK_SIZE as usize - 2152],
/// encrypted hash of header data without hash, set to hash and padded if disk is not encrypted
pub encrypted_hash: [u8; 16],
/// hash of header data without hash
pub hash: u64le,
}
impl Header {
pub fn default() -> Header {
Header {
signature: [0; 8],
version: 0,
uuid: [0; 16],
size: 0,
root: 0,
free: 0,
padding: [0; BLOCK_SIZE as usize - 56],
}
}
#[cfg(feature = "std")]
pub fn new(size: u64, root: u64, free: u64) -> Header {
let uuid = uuid::Uuid::new_v4();
Header {
pub fn new(size: u64) -> Header {
let uuid = Uuid::new_v4();
let mut header = Header {
signature: *SIGNATURE,
version: VERSION,
version: VERSION.into(),
uuid: *uuid.as_bytes(),
size: size,
root: root,
free: free,
padding: [0; BLOCK_SIZE as usize - 56],
}
size: size.into(),
..Default::default()
};
header.update_hash(None);
header
}
pub fn valid(&self) -> bool {
&self.signature == SIGNATURE && self.version == VERSION
if &self.signature != SIGNATURE {
// Signature does not match
return false;
}
if { self.version }.to_native() != VERSION {
// Version does not match
return false;
}
if { self.hash }.to_native() != self.create_hash() {
// Hash does not match
return false;
}
// All tests passed, header is valid
true
}
pub fn uuid(&self) -> [u8; 16] {
self.uuid
}
pub fn size(&self) -> u64 {
{ self.size }.to_native()
}
pub fn generation(&self) -> u64 {
{ self.generation }.to_native()
}
fn create_hash(&self) -> u64 {
// Calculate part of header to hash (everything before the hashes)
let end = mem::size_of_val(self)
- mem::size_of_val(&{ self.hash })
- mem::size_of_val(&{ self.encrypted_hash });
seahash::hash(&self[..end])
}
fn create_encrypted_hash(&self, aes_opt: Option<&Aes128>) -> [u8; 16] {
let mut encrypted_hash = [0; 16];
for (i, b) in { self.hash }.to_native().to_le_bytes().iter().enumerate() {
encrypted_hash[i] = *b;
}
if let Some(aes) = aes_opt {
let mut block = aes::Block::from(encrypted_hash);
aes.encrypt_block(&mut block);
encrypted_hash = block.into();
}
encrypted_hash
}
pub fn encrypted(&self) -> bool {
(self.encrypted_hash) != self.create_encrypted_hash(None)
}
pub fn aes(&self, password: &[u8]) -> Option<Aes128> {
let hash = self.create_encrypted_hash(None);
for slot in self.key_slots.iter() {
//TODO: handle errors
let aes = slot.key(password).unwrap().into_aes();
let mut block = aes::Block::from(self.encrypted_hash);
aes.decrypt_block(&mut block);
if block == aes::Block::from(hash) {
return Some(aes);
}
}
None
}
fn update_hash(&mut self, aes_opt: Option<&Aes128>) {
self.hash = self.create_hash().into();
// Make sure to do this second, it relies on the hash being up to date
self.encrypted_hash = self.create_encrypted_hash(aes_opt);
}
pub fn update(&mut self, aes_opt: Option<&Aes128>) -> u64 {
let mut generation = self.generation();
generation += 1;
self.generation = generation.into();
self.update_hash(aes_opt);
generation
}
}
impl Default for Header {
fn default() -> Self {
Self {
signature: [0; 8],
version: 0.into(),
uuid: [0; 16],
size: 0.into(),
generation: 0.into(),
tree: BlockPtr::<Tree>::default(),
alloc: BlockPtr::<AllocList>::default(),
key_slots: [KeySlot::default(); 64],
padding: [0; BLOCK_SIZE as usize - 2152],
encrypted_hash: [0; 16],
hash: 0.into(),
}
}
}
@@ -61,15 +161,19 @@ impl fmt::Debug for Header {
let version = self.version;
let uuid = self.uuid;
let size = self.size;
let root = self.root;
let free = self.free;
let generation = self.generation;
let tree = self.tree;
let alloc = self.alloc;
let hash = self.hash;
f.debug_struct("Header")
.field("signature", &signature)
.field("version", &version)
.field("uuid", &uuid)
.field("size", &size)
.field("root", &root)
.field("free", &free)
.field("generation", &generation)
.field("tree", &tree)
.field("alloc", &alloc)
.field("hash", &hash)
.finish()
}
}
@@ -93,7 +197,30 @@ impl DerefMut for Header {
}
}
#[test]
fn header_not_valid_test() {
assert_eq!(Header::default().valid(), false);
}
#[test]
fn header_size_test() {
assert_eq!(mem::size_of::<Header>(), BLOCK_SIZE as usize);
}
#[test]
fn header_hash_test() {
let mut header = Header::default();
assert_eq!(header.create_hash(), 0xe81ffcb86026ff96);
header.update_hash(None);
assert_eq!({ header.hash }.to_native(), 0xe81ffcb86026ff96);
assert_eq!(
header.encrypted_hash,
[0x96, 0xff, 0x26, 0x60, 0xb8, 0xfc, 0x1f, 0xe8, 0, 0, 0, 0, 0, 0, 0, 0]
);
}
#[cfg(feature = "std")]
#[test]
fn header_valid_test() {
assert_eq!(Header::new(0).valid(), true);
}