Skip to main content

doublecrypt_core/
model.rs

1use serde::{Deserialize, Serialize};
2
3/// Default block size: 64 KiB.
4pub const DEFAULT_BLOCK_SIZE: usize = 65536;
5
6/// Maximum filename length in bytes.
7pub const MAX_NAME_LEN: usize = 255;
8
9/// Reserved block IDs.
10pub const BLOCK_STORAGE_HEADER: u64 = 0;
11pub const BLOCK_ROOT_POINTER_A: u64 = 1;
12pub const BLOCK_ROOT_POINTER_B: u64 = 2;
13
14/// First allocatable block ID (after reserved blocks).
15pub const FIRST_DATA_BLOCK: u64 = 3;
16
17// ── Object types ──
18
19/// Identifies the kind of logical object stored in a block.
20#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
21#[repr(u8)]
22pub enum ObjectKind {
23    Superblock = 1,
24    RootPointer = 2,
25    Inode = 3,
26    DirectoryPage = 4,
27    ExtentMap = 5,
28    FileDataChunk = 6,
29}
30
31/// A reference to a logical object stored at a particular block.
32#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Hash)]
33pub struct ObjectRef {
34    /// Block ID where the object is stored.
35    pub block_id: u64,
36}
37
38impl ObjectRef {
39    pub fn new(block_id: u64) -> Self {
40        Self { block_id }
41    }
42
43    pub fn null() -> Self {
44        Self { block_id: u64::MAX }
45    }
46
47    pub fn is_null(&self) -> bool {
48        self.block_id == u64::MAX
49    }
50}
51
52// ── Storage header ──
53
54/// Written to block 0. Identifies the filesystem format.
55#[derive(Debug, Clone, Serialize, Deserialize)]
56pub struct StorageHeader {
57    pub magic: [u8; 8],
58    pub version: u32,
59    pub block_size: u32,
60    pub total_blocks: u64,
61}
62
63impl StorageHeader {
64    pub const MAGIC: [u8; 8] = *b"DBLCRYPT";
65
66    pub fn new(block_size: u32, total_blocks: u64) -> Self {
67        Self {
68            magic: Self::MAGIC,
69            version: 1,
70            block_size,
71            total_blocks,
72        }
73    }
74
75    pub fn is_valid(&self) -> bool {
76        self.magic == Self::MAGIC && self.version == 1
77    }
78}
79
80// ── Root pointer ──
81
82/// Stored in block 1 (A) and block 2 (B). Points to the current superblock.
83/// We alternate between A and B to get atomic commits.
84#[derive(Debug, Clone, Serialize, Deserialize)]
85pub struct RootPointer {
86    /// Monotonic generation counter. Higher = newer.
87    pub generation: u64,
88    /// Block ID of the current superblock object.
89    pub superblock_ref: ObjectRef,
90    /// BLAKE3 checksum of the serialized superblock for integrity.
91    pub checksum: [u8; 32],
92}
93
94// ── Superblock ──
95
96/// The root metadata object for the filesystem.
97#[derive(Debug, Clone, Serialize, Deserialize)]
98pub struct Superblock {
99    pub generation: u64,
100    /// Reference to the root directory inode.
101    pub root_inode_ref: ObjectRef,
102}
103
104// ── Inode ──
105
106/// The type of a filesystem entry.
107#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
108#[repr(u8)]
109pub enum InodeKind {
110    File = 1,
111    Directory = 2,
112}
113
114/// Unique inode identifier. Monotonically allocated.
115pub type InodeId = u64;
116
117/// Metadata for a single filesystem object (file or directory).
118#[derive(Debug, Clone, Serialize, Deserialize)]
119pub struct Inode {
120    pub id: InodeId,
121    pub kind: InodeKind,
122    /// For files: total size in bytes. For directories: 0.
123    pub size: u64,
124    /// For directories: reference to a DirectoryPage object.
125    /// For files: ObjectRef::null().
126    pub directory_page_ref: ObjectRef,
127    /// For files: reference to the ExtentMap object.
128    /// For directories: ObjectRef::null().
129    pub extent_map_ref: ObjectRef,
130    /// Unix timestamp – seconds since epoch. Placeholder for now.
131    pub created_at: u64,
132    pub modified_at: u64,
133}
134
135// ── Directory ──
136
137/// A single entry in a directory.
138#[derive(Debug, Clone, Serialize, Deserialize)]
139pub struct DirectoryEntry {
140    /// The name of this entry (file or subdirectory).
141    pub name: String,
142    /// Reference to the inode for this entry.
143    pub inode_ref: ObjectRef,
144    /// Inode ID (for quick lookup without loading the full inode).
145    pub inode_id: InodeId,
146    /// Kind hint so we can distinguish files/dirs without loading inode.
147    pub kind: InodeKind,
148}
149
150/// A single page of directory entries. V1 uses one page per directory.
151#[derive(Debug, Clone, Serialize, Deserialize)]
152pub struct DirectoryPage {
153    pub entries: Vec<DirectoryEntry>,
154}
155
156impl DirectoryPage {
157    pub fn new() -> Self {
158        Self {
159            entries: Vec::new(),
160        }
161    }
162}
163
164// ── Extent map ──
165
166/// Maps a logical file chunk index to a block containing the encrypted data chunk.
167#[derive(Debug, Clone, Serialize, Deserialize)]
168pub struct ExtentEntry {
169    /// Chunk index (0-based).
170    pub chunk_index: u64,
171    /// Reference to the block containing the encrypted file data chunk.
172    pub data_ref: ObjectRef,
173    /// Size of the plaintext data in this chunk.
174    pub plaintext_len: u32,
175}
176
177/// Per-file map of chunks. V1 uses one extent map per file.
178#[derive(Debug, Clone, Serialize, Deserialize)]
179pub struct ExtentMap {
180    pub entries: Vec<ExtentEntry>,
181}
182
183impl ExtentMap {
184    pub fn new() -> Self {
185        Self {
186            entries: Vec::new(),
187        }
188    }
189}
190
191// ── Encrypted envelope ──
192
193/// The on-disk (on-block) format: a serialized+encrypted logical object.
194/// This is what actually gets written into a block.
195#[derive(Debug, Clone, Serialize, Deserialize)]
196pub struct EncryptedObject {
197    pub kind: ObjectKind,
198    pub version: u32,
199    /// 12-byte nonce for ChaCha20-Poly1305.
200    pub nonce: [u8; 12],
201    /// Ciphertext (includes Poly1305 tag appended by AEAD).
202    pub ciphertext: Vec<u8>,
203}
204
205// ── Logical object ──
206
207/// A decrypted logical object with its kind tag and plaintext payload.
208#[derive(Debug, Clone)]
209pub struct LogicalObject {
210    pub kind: ObjectKind,
211    pub payload: Vec<u8>,
212}