Skip to main content

shadow_crypt_core/
file.rs

1//! Version-independent file types.
2//!
3//! [`PlaintextFile`] is the decrypted *output* shape shared by every format
4//! version. It carries no format-specific information, so sharing it does not
5//! couple the version modules to each other (unlike format-defining code,
6//! which is duplicated per version on purpose).
7
8use std::time::SystemTime;
9
10use crate::memory::{SecureBytes, SecureString};
11
12/// What an encrypted file's content stream contains.
13#[derive(Debug, Clone, Copy, PartialEq, Eq)]
14pub enum ContentKind {
15    /// The content is the bytes of a single file.
16    File,
17    /// The content is a [`crate::archive`] stream holding a directory tree.
18    Archive,
19}
20
21/// Metadata of a plaintext file, stored encrypted alongside the content.
22///
23/// Which fields a format version actually preserves varies: v1/v2 store only
24/// the filename, v3 stores everything. Absent fields are `None`.
25#[derive(Debug, Clone)]
26pub struct FileMetadata {
27    filename: SecureString,
28    mtime: Option<SystemTime>,
29    mode: Option<u32>, // Unix permission bits
30    kind: ContentKind,
31}
32
33impl FileMetadata {
34    pub fn new(filename: SecureString, mtime: Option<SystemTime>, mode: Option<u32>) -> Self {
35        Self {
36            filename,
37            mtime,
38            mode,
39            kind: ContentKind::File,
40        }
41    }
42
43    /// Marks this metadata as describing an archive (directory tree) rather
44    /// than a single file. For archives, `filename` is the directory name.
45    pub fn into_archive(mut self) -> Self {
46        self.kind = ContentKind::Archive;
47        self
48    }
49
50    pub fn filename(&self) -> &SecureString {
51        &self.filename
52    }
53    pub fn mtime(&self) -> Option<SystemTime> {
54        self.mtime
55    }
56    pub fn mode(&self) -> Option<u32> {
57        self.mode
58    }
59    pub fn kind(&self) -> ContentKind {
60        self.kind
61    }
62}
63
64/// Represents a plaintext file with filename and content
65#[derive(Debug)]
66pub struct PlaintextFile {
67    filename: SecureString, // Decrypted filename
68    content: SecureBytes,   // Decrypted file content
69}
70
71impl PlaintextFile {
72    pub fn new(filename: SecureString, content: SecureBytes) -> Self {
73        Self { filename, content }
74    }
75    pub fn filename(&self) -> &SecureString {
76        &self.filename
77    }
78    pub fn content(&self) -> &SecureBytes {
79        &self.content
80    }
81}
82
83#[cfg(test)]
84mod tests {
85    use super::*;
86
87    #[test]
88    fn plaintext_file_accessors() {
89        let file = PlaintextFile::new(
90            SecureString::new("a.txt".to_string()),
91            SecureBytes::new(vec![1, 2, 3]),
92        );
93        assert_eq!(file.filename().as_str(), "a.txt");
94        assert_eq!(file.content().as_slice(), &[1, 2, 3]);
95    }
96}