snix_castore/fs/
inodes.rs

1//! This module contains all the data structures used to track information
2//! about inodes, which present snix-castore nodes in a filesystem.
3use std::time::Duration;
4
5use crate::{B3Digest, Node, path::PathComponent};
6
7#[derive(Clone, Debug)]
8pub enum InodeData {
9    Regular(B3Digest, u64, bool),  // digest, size, executable
10    Symlink(bytes::Bytes),         // target
11    Directory(DirectoryInodeData), // either [DirectoryInodeData:Sparse] or [DirectoryInodeData:Populated]
12}
13
14/// This encodes the two different states of [InodeData::Directory].
15/// Either the data still is sparse (we only saw a [crate::Node::Directory],
16/// but didn't fetch the [crate::Directory] struct yet, or we processed a
17/// lookup and did fetch the data.
18#[derive(Clone, Debug)]
19pub enum DirectoryInodeData {
20    Sparse(B3Digest, u64),                                // digest, size
21    Populated(B3Digest, Vec<(u64, PathComponent, Node)>), // [(child_inode, name, node)]
22}
23
24impl InodeData {
25    /// Constructs a new InodeData by consuming a [Node].
26    pub fn from_node(node: &Node) -> Self {
27        match node {
28            Node::Directory { digest, size } => {
29                Self::Directory(DirectoryInodeData::Sparse(digest.clone(), *size))
30            }
31            Node::File {
32                digest,
33                size,
34                executable,
35            } => Self::Regular(digest.clone(), *size, *executable),
36            Node::Symlink { target } => Self::Symlink(target.clone().into()),
37        }
38    }
39
40    pub fn as_fuse_file_attr(&self, inode: u64) -> fuse_backend_rs::abi::fuse_abi::Attr {
41        fuse_backend_rs::abi::fuse_abi::Attr {
42            ino: inode,
43            // FUTUREWORK: play with this numbers, as it affects read sizes for client applications.
44            blocks: 1024,
45            size: match self {
46                InodeData::Regular(_, size, _) => *size,
47                InodeData::Symlink(target) => target.len() as u64,
48                InodeData::Directory(DirectoryInodeData::Sparse(_, size)) => *size,
49                InodeData::Directory(DirectoryInodeData::Populated(_, children)) => {
50                    children.len() as u64
51                }
52            },
53            mode: self.as_fuse_type() | self.mode(),
54            ..Default::default()
55        }
56    }
57
58    fn mode(&self) -> u32 {
59        match self {
60            InodeData::Regular(_, _, false) | InodeData::Symlink(_) => 0o444,
61            InodeData::Regular(_, _, true) | InodeData::Directory(_) => 0o555,
62        }
63    }
64
65    pub fn as_fuse_entry(&self, inode: u64) -> fuse_backend_rs::api::filesystem::Entry {
66        fuse_backend_rs::api::filesystem::Entry {
67            inode,
68            attr: self.as_fuse_file_attr(inode).into(),
69            attr_timeout: Duration::MAX,
70            entry_timeout: Duration::MAX,
71            ..Default::default()
72        }
73    }
74
75    /// Returns the u32 fuse type
76    pub fn as_fuse_type(&self) -> u32 {
77        #[allow(clippy::let_and_return)]
78        let ty = match self {
79            InodeData::Regular(_, _, _) => libc::S_IFREG,
80            InodeData::Symlink(_) => libc::S_IFLNK,
81            InodeData::Directory(_) => libc::S_IFDIR,
82        };
83        // libc::S_IFDIR is u32 on Linux and u16 on MacOS
84        #[cfg(target_os = "macos")]
85        let ty = ty as u32;
86
87        ty
88    }
89}