lb_fs/
cache.rs

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