1use crate::{
2 cache::FileEntry,
3 utils::{fmt, get_string},
4};
5use async_trait::async_trait;
6use lb_rs::{model::file_metadata::FileType, Lb, Uuid};
7use nfsserve::{
8 nfs::{
9 fattr3, fileid3, filename3, nfspath3, nfsstat3, nfsstring, sattr3, set_atime, set_gid3,
10 set_mode3, set_mtime, set_size3, set_uid3,
11 },
12 vfs::{DirEntry, NFSFileSystem, ReadDirResult, VFSCapabilities},
13};
14use std::{collections::HashMap, sync::Arc};
15use tokio::sync::Mutex;
16use tracing::{info, instrument, warn};
17
18#[derive(Clone)]
19pub struct Drive {
20 pub lb: Lb,
21
22 pub root: Uuid,
24
25 pub data: Arc<Mutex<HashMap<fileid3, FileEntry>>>,
34}
35
36#[async_trait]
37impl NFSFileSystem for Drive {
38 #[instrument(skip(self))]
39 fn root_dir(&self) -> fileid3 {
40 let root = self.root;
41 let half = root.as_u64_pair().0;
42
43 info!("ret={root}");
44 half
45 }
46
47 fn capabilities(&self) -> VFSCapabilities {
48 VFSCapabilities::ReadWrite
49 }
50
51 #[instrument(skip(self), fields(id = fmt(id), buffer = buffer.len()))]
52 async fn write(&self, id: fileid3, offset: u64, buffer: &[u8]) -> Result<fattr3, nfsstat3> {
53 let offset = offset as usize;
54
55 let mut data = self.data.lock().await;
56 let entry = data.get_mut(&id).unwrap();
57 let id = entry.file.id;
58
59 let mut doc = self.lb.read_document(id, false).await.unwrap();
60 let mut expanded = false;
61 if offset + buffer.len() > doc.len() {
62 doc.resize(offset + buffer.len(), 0);
63 doc[offset..].copy_from_slice(buffer);
64 expanded = true;
65 } else {
66 for (idx, datum) in buffer.iter().enumerate() {
67 doc[offset + idx] = *datum;
68 }
69 }
70 let doc_size = doc.len();
71 self.lb.write_document(id, &doc).await.unwrap();
72
73 entry.fattr.size = doc_size as u64;
74
75 info!("expanded={expanded}, fattr.size = {}", doc_size);
76
77 Ok(entry.fattr)
78 }
79
80 #[instrument(skip(self), fields(dirid = fmt(dirid), filename = get_string(filename)))]
82 async fn create(
83 &self, dirid: fileid3, filename: &filename3, attr: sattr3,
84 ) -> Result<(fileid3, fattr3), nfsstat3> {
85 let filename = get_string(filename);
86 let parent = self.data.lock().await.get(&dirid).unwrap().file.id;
87 let file = self
88 .lb
89 .create_file(&filename, &parent, FileType::Document)
90 .await
91 .unwrap();
92
93 let entry = FileEntry::from_file(file, 0);
94 let id = entry.fattr.fileid;
95 self.data.lock().await.insert(entry.fattr.fileid, entry);
96
97 let file = self.setattr(id, attr).await.unwrap();
98
99 info!("({}, size={})", fmt(file.fileid), file.size);
100 Ok((id, file))
101 }
102
103 #[instrument(skip(self), fields(dirid = fmt(dirid), filename = get_string(filename)))]
104 async fn create_exclusive(
105 &self, dirid: fileid3, filename: &filename3,
106 ) -> Result<fileid3, nfsstat3> {
107 let filename = get_string(filename);
108 let dirid = self.data.lock().await.get(&dirid).unwrap().file.id;
109 let children = self.lb.get_children(&dirid).await.unwrap();
110 for child in children {
111 if child.name == filename {
112 warn!("exists already");
113 return Err(nfsstat3::NFS3ERR_EXIST);
114 }
115 }
116
117 let file = self
118 .lb
119 .create_file(&filename, &dirid, FileType::Document)
120 .await
121 .unwrap();
122
123 let entry = FileEntry::from_file(file, 0);
124 let id = entry.fattr.fileid;
125 info!("({}, size={})", fmt(id), entry.fattr.size);
126 self.data.lock().await.insert(entry.fattr.fileid, entry);
127
128 return Ok(id);
129 }
130
131 #[instrument(skip(self), fields(dirid = fmt(dirid), filename = get_string(filename)))]
132 async fn lookup(&self, dirid: fileid3, filename: &filename3) -> Result<fileid3, nfsstat3> {
133 let dir = self.data.lock().await.get(&dirid).unwrap().file.clone();
134
135 if dir.is_document() {
136 info!("NOTDIR");
137 return Err(nfsstat3::NFS3ERR_NOTDIR);
138 }
139
140 if filename[..] == [b'.'] {
142 info!(". == {}", fmt(dirid));
143 return Ok(dirid);
144 }
145
146 if filename[..] == [b'.', b'.'] {
148 info!(".. == {}", dir.parent);
149 return Ok(dir.parent.as_u64_pair().0);
150 }
151
152 let children = self.lb.get_children(&dir.id).await.unwrap();
153 let file_name = String::from_utf8(filename.0.clone()).unwrap();
154
155 for child in children {
156 if file_name == child.name {
157 info!("{}", child.id);
158 return Ok(child.id.as_u64_pair().0);
159 }
160 }
161
162 info!("NOENT");
163 Err(nfsstat3::NFS3ERR_NOENT)
164 }
165
166 #[instrument(skip(self), fields(id = fmt(id)))]
167 async fn getattr(&self, id: fileid3) -> Result<fattr3, nfsstat3> {
168 let file = self.data.lock().await.get(&id).unwrap().fattr;
169 info!("fattr = {:?}", file);
170 Ok(file)
171 }
172
173 #[instrument(skip(self), fields(id = fmt(id)))]
174 async fn setattr(&self, id: fileid3, setattr: sattr3) -> Result<fattr3, nfsstat3> {
175 let mut data = self.data.lock().await;
176 let now = FileEntry::now();
177 let entry = data.get_mut(&id).unwrap();
178
179 match setattr.size {
180 set_size3::Void => {}
181 set_size3::size(new) => {
182 if entry.fattr.size != new {
183 let mut doc = self.lb.read_document(entry.file.id, false).await.unwrap();
184 doc.resize(new as usize, 0);
185 self.lb.write_document(entry.file.id, &doc).await.unwrap();
186 entry.fattr.mtime = FileEntry::ts_from_u64(now);
187 entry.fattr.ctime = FileEntry::ts_from_u64(now);
188 }
189 }
190 }
191
192 match setattr.atime {
193 set_atime::DONT_CHANGE => {}
194 set_atime::SET_TO_SERVER_TIME => {
195 entry.fattr.atime = FileEntry::ts_from_u64(now);
196 }
197 set_atime::SET_TO_CLIENT_TIME(ts) => {
198 entry.fattr.atime = ts;
199 }
200 }
201
202 match setattr.mtime {
203 set_mtime::DONT_CHANGE => {}
204 set_mtime::SET_TO_SERVER_TIME => {
205 entry.fattr.mtime = FileEntry::ts_from_u64(now);
206 entry.fattr.ctime = FileEntry::ts_from_u64(now);
207 }
208 set_mtime::SET_TO_CLIENT_TIME(ts) => {
209 entry.fattr.mtime = ts;
210 entry.fattr.ctime = ts;
211 }
212 }
213
214 match setattr.uid {
215 set_uid3::Void => {}
216 set_uid3::uid(uid) => {
217 entry.fattr.uid = uid;
218 entry.fattr.ctime = FileEntry::ts_from_u64(now);
219 }
220 }
221
222 match setattr.gid {
223 set_gid3::Void => {}
224 set_gid3::gid(gid) => {
225 entry.fattr.gid = gid;
226 entry.fattr.ctime = FileEntry::ts_from_u64(now);
227 }
228 }
229
230 match setattr.mode {
231 set_mode3::Void => {}
232 set_mode3::mode(mode) => {
233 entry.fattr.mode = mode;
234 entry.fattr.ctime = FileEntry::ts_from_u64(now);
235 }
236 }
237
238 info!("fattr = {:?}", entry.fattr);
239
240 return Ok(entry.fattr);
241 }
242
243 #[instrument(skip(self), fields(id = fmt(id), offset, count))]
244 async fn read(
245 &self, id: fileid3, offset: u64, count: u32,
246 ) -> Result<(Vec<u8>, bool), nfsstat3> {
247 let offset = offset as usize;
248 let count = count as usize;
249 let id = self.data.lock().await.get(&id).unwrap().file.id;
250
251 let doc = self.lb.read_document(id, false).await.unwrap();
252
253 if offset >= doc.len() {
254 info!("[] EOF");
255 return Ok((vec![], true));
256 }
257
258 if offset + count >= doc.len() {
259 info!("|{}| EOF", doc[offset..].len());
260 return Ok((doc[offset..].to_vec(), true));
261 }
262
263 info!("|{}|", count);
264 return Ok((doc[offset..offset + count].to_vec(), false));
265 }
266
267 #[instrument(skip(self), fields(dirid = fmt(dirid), start_after, max_entries))]
269 async fn readdir(
270 &self, dirid: fileid3, start_after: fileid3, max_entries: usize,
271 ) -> Result<ReadDirResult, nfsstat3> {
272 let data = self.data.lock().await;
273 let dirid = data.get(&dirid).unwrap().file.id;
274 let mut children = self.lb.get_children(&dirid).await.unwrap();
275
276 children.sort_by(|a, b| a.id.cmp(&b.id));
277
278 let mut start_index = 0;
280 if start_after > 0 {
281 for (idx, child) in children.iter().enumerate() {
282 if child.id.as_u64_pair().0 == start_after {
283 start_index = idx + 1;
284 }
285 }
286 }
287
288 let mut ret = ReadDirResult::default();
289
290 if start_index >= children.len() {
291 ret.end = true;
292 info!("[] done");
293 return Ok(ret);
294 }
295
296 let end_index = if start_index + max_entries >= children.len() {
297 ret.end = true;
298 children.len()
299 } else {
300 start_index + max_entries
301 };
302
303 for child in &children[start_index..end_index] {
304 let fileid = child.id.as_u64_pair().0;
305 let name = nfsstring(child.name.clone().into_bytes());
306 let attr = data.get(&fileid).unwrap().fattr;
307
308 ret.entries.push(DirEntry { fileid, name, attr });
309 }
310
311 info!("|{}| done={}", ret.entries.len(), ret.end);
312
313 Ok(ret)
314 }
315
316 #[instrument(skip(self), fields(dirid = fmt(dirid), filename = get_string(filename)))]
320 #[allow(unused)]
321 async fn remove(&self, dirid: fileid3, filename: &filename3) -> Result<(), nfsstat3> {
322 let mut data = self.data.lock().await;
323 let dirid = data.get(&dirid).unwrap().file.id;
324
325 let children = self.lb.get_children(&dirid).await.unwrap();
326 let file_name = String::from_utf8(filename.0.clone()).unwrap();
327
328 for child in children {
329 if file_name == child.name {
330 info!("deleted");
331 self.lb.delete(&child.id).await;
332 data.remove(&child.id.as_u64_pair().0);
333 return Ok(());
334 }
335 }
336
337 info!("NOENT");
338 return Err(nfsstat3::NFS3ERR_NOENT);
339 }
340
341 #[instrument(skip(self), fields(from_dirid = fmt(from_dirid), from_filename = get_string(from_filename), to_dirid = fmt(to_dirid), to_filename = get_string(to_filename)))]
343 #[allow(unused)]
344 async fn rename(
345 &self, from_dirid: fileid3, from_filename: &filename3, to_dirid: fileid3,
346 to_filename: &filename3,
347 ) -> Result<(), nfsstat3> {
348 let mut data = self.data.lock().await;
349
350 let from_filename = String::from_utf8(from_filename.0.clone()).unwrap();
351 let to_filename = String::from_utf8(to_filename.0.clone()).unwrap();
352
353 let from_dirid = data.get(&from_dirid).unwrap().file.id;
354 let to_dirid = data.get(&to_dirid).unwrap().file.id;
355
356 let src_children = self.lb.get_children(&from_dirid).await.unwrap();
357
358 let mut from_id = None;
359 let mut to_id = None;
360 for child in src_children {
361 if child.name == from_filename {
362 from_id = Some(child.id);
363 }
364
365 if to_dirid == from_dirid && child.name == to_filename {
366 to_id = Some(child.id);
367 }
368 }
369
370 if to_dirid != from_dirid {
371 let dst_children = self.lb.get_children(&to_dirid).await.unwrap();
372 for child in dst_children {
373 if child.name == to_filename {
374 to_id = Some(child.id);
375 }
376 }
377 }
378
379 let from_id = from_id.unwrap();
380
381 match to_id {
382 Some(id) => {
384 info!("overwrite {from_id} -> {id}");
385 let from_doc = self.lb.read_document(from_id, false).await.unwrap();
386 info!("|{}|", from_doc.len());
387 let doc_len = from_doc.len() as u64;
388 self.lb.write_document(id, &from_doc).await.unwrap();
389 self.lb.delete(&from_id).await.unwrap();
390
391 let mut entry = data.get_mut(&id.as_u64_pair().0).unwrap();
392 entry.fattr.size = doc_len;
393
394 data.remove(&from_id.as_u64_pair().0);
395 }
396
397 None => {
399 if from_dirid != to_dirid {
400 info!("move {} -> {}\t", from_id, to_dirid);
401 self.lb.move_file(&from_id, &to_dirid).await.unwrap();
402 }
403
404 if from_filename != to_filename {
405 info!("rename {} -> {}\t", from_id, to_filename);
406 self.lb.rename_file(&from_id, &to_filename).await.unwrap();
407 }
408
409 let mut entry = data.get_mut(&from_id.as_u64_pair().0).unwrap();
410
411 let file = self.lb.get_file_by_id(from_id).await.unwrap();
412 entry.file = file;
413
414 info!("ok");
415 }
416 }
417
418 return Ok(());
419 }
420
421 #[instrument(skip(self), fields(dirid = fmt(dirid), dirname = get_string(dirname)))]
422 #[allow(unused)]
423 async fn mkdir(
424 &self, dirid: fileid3, dirname: &filename3,
425 ) -> Result<(fileid3, fattr3), nfsstat3> {
426 let filename = get_string(dirname);
427 let parent = self.data.lock().await.get(&dirid).unwrap().file.id;
428 let file = self
429 .lb
430 .create_file(&filename, &parent, FileType::Folder)
431 .await
432 .unwrap();
433
434 let entry = FileEntry::from_file(file, 0);
435 let id = entry.fattr.fileid;
436 let fattr = entry.fattr;
437 self.data.lock().await.insert(entry.fattr.fileid, entry);
438
439 info!("({}, fattr={:?})", fmt(id), fattr);
440 Ok((id, fattr))
441 }
442
443 async fn symlink(
444 &self, _dirid: fileid3, _linkname: &filename3, _symlink: &nfspath3, _attr: &sattr3,
445 ) -> Result<(fileid3, fattr3), nfsstat3> {
446 info!("symlink NOTSUPP");
447 return Err(nfsstat3::NFS3ERR_NOTSUPP);
448 }
449 async fn readlink(&self, _id: fileid3) -> Result<nfspath3, nfsstat3> {
450 info!("readklink NOTSUPP");
451 return Err(nfsstat3::NFS3ERR_NOTSUPP);
452 }
453}