Skip to main content

lb_fs/
cache.rs

1use crate::{fs_impl::Drive, utils::file_id};
2use lb_rs::model::file::File;
3use lb_rs::model::work_unit::WorkUnit;
4use nfs3_server::nfs3_types::nfs3::{fattr3, ftype3, nfstime3};
5use std::time::{Duration, SystemTime};
6use tracing::info;
7
8pub struct FileEntry {
9    pub file: File,
10    pub fattr: fattr3,
11}
12
13impl FileEntry {
14    pub fn from_file(file: File, size: u64) -> Self {
15        let ftype = if file.is_folder() { ftype3::NF3DIR } else { ftype3::NF3REG };
16
17        // todo this deserves some scrutiny and cross platform testing
18        let mode = if file.is_folder() { 0o755 } else { 0o644 };
19
20        let fileid = file_id(&file);
21        // interestingly a number of key read operations rely on this being correct
22        let size = if file.is_folder() { 0 } else { size };
23
24        let atime = Self::ts_from_u64(0);
25        let mtime = Self::ts_from_u64(file.last_modified);
26        let ctime = Self::ts_from_u64(file.last_modified);
27
28        let fattr = fattr3 {
29            type_: ftype,
30            mode,
31            nlink: 1, // hard links to this file
32            uid: 501, // todo: evaluate owner field? not resolved by this lib
33            gid: 20,  // group id
34            size,
35            used: size,               // ?
36            rdev: Default::default(), // ?
37            fsid: Default::default(), // file system id
38            fileid,
39            atime,
40            mtime,
41            ctime,
42        };
43
44        Self { file, fattr }
45    }
46
47    pub fn ts_from_u64(version: u64) -> nfstime3 {
48        let time = Duration::from_millis(version);
49        nfstime3 { seconds: time.as_secs() as u32, nseconds: time.subsec_nanos() }
50    }
51
52    pub fn now() -> nfstime3 {
53        SystemTime::now()
54            .try_into()
55            .expect("failed to get current time")
56    }
57}
58
59impl Drive {
60    // todo: probably need a variant of this that is more suitable post sync cache updates
61    pub async fn prepare_caches(&self) {
62        info!("performing startup sync");
63        self.lb.sync(Self::progress()).await.unwrap();
64
65        info!("preparing cache, are you release build?");
66        let sizes = self.lb.get_uncompressed_usage_breakdown().await.unwrap();
67        let files = self.lb.list_metadatas().await.unwrap();
68
69        let mut data = self.data.lock().await;
70        for file in files {
71            let id = file.id;
72            let entry =
73                FileEntry::from_file(file, sizes.get(&id).copied().unwrap_or_default() as u64);
74            data.insert(entry.file.id.into(), entry);
75        }
76        info!("cache ready");
77    }
78
79    pub async fn sync(&self) {
80        let status = self.lb.sync(None).await.unwrap();
81        let mut data = self.data.lock().await;
82
83        for unit in status.work_units {
84            if let WorkUnit::ServerChange(dirty_id) = unit {
85                let file = self.lb.get_file_by_id(dirty_id).await.unwrap();
86                let size = if file.is_document() {
87                    self.lb.read_document(dirty_id, false).await.unwrap().len()
88                } else {
89                    0
90                };
91
92                let mut entry = FileEntry::from_file(file, size as u64);
93
94                let now = FileEntry::now();
95
96                entry.fattr.mtime = now;
97                entry.fattr.ctime = now;
98
99                data.insert(entry.file.id.into(), entry);
100            }
101        }
102    }
103}