1use crate::cache::FileEntry;
2use crate::file_handle::UuidFileHandle;
3use crate::utils::{file_id, get_string};
4use lb_rs::model::file::File;
5use lb_rs::model::file_metadata::FileType;
6use lb_rs::{Lb, Uuid};
7use nfs3_server::nfs3_types::nfs3::{
8 Nfs3Option, fattr3, filename3, nfspath3, nfsstat3, sattr3, set_atime, set_mtime,
9};
10use nfs3_server::vfs::{
11 DirEntry, DirEntryPlus, NfsFileSystem, NfsReadFileSystem, ReadDirIterator, ReadDirPlusIterator,
12};
13use std::collections::HashMap;
14use std::iter::Iterator as StdIterator;
15use std::sync::Arc;
16use tokio::sync::Mutex;
17use tracing::{info, instrument, warn};
18
19type EntriesMap = Arc<Mutex<HashMap<UuidFileHandle, FileEntry>>>;
20
21#[derive(Clone)]
22pub struct Drive {
23 pub lb: Lb,
24
25 pub root: Uuid,
27
28 pub data: EntriesMap,
37}
38
39impl Drive {
40 async fn load_children(
52 &self, dirid: &UuidFileHandle, cookie: u64,
53 ) -> impl StdIterator<Item = File> + 'static {
54 let mut children = self.lb.get_children(dirid.as_uuid()).await.unwrap();
55
56 children.sort_by(|a, b| a.id.cmp(&b.id));
57
58 let mut start_index = 0;
59 if cookie > 0 {
60 start_index = children
61 .iter()
62 .position(|child| file_id(child) > cookie)
63 .unwrap_or_else(|| {
64 warn!("cookie {cookie} not found");
65 children.len()
66 });
67 }
68
69 children.into_iter().skip(start_index)
70 }
71}
72
73impl NfsReadFileSystem for Drive {
74 type Handle = UuidFileHandle;
75
76 #[instrument(skip(self))]
77 fn root_dir(&self) -> Self::Handle {
78 self.root.into()
79 }
80
81 #[instrument(skip(self), fields(dirid = dirid.to_string(), filename = get_string(filename)))]
82 async fn lookup(
83 &self, dirid: &Self::Handle, filename: &filename3<'_>,
84 ) -> Result<Self::Handle, nfsstat3> {
85 let dir = self
86 .data
87 .lock()
88 .await
89 .get(dirid)
90 .ok_or(nfsstat3::NFS3ERR_STALE)?
91 .file
92 .clone();
93
94 if dir.is_document() {
95 info!("NOTDIR");
96 return Err(nfsstat3::NFS3ERR_NOTDIR);
97 }
98
99 if filename.as_ref() == [b'.'] {
101 info!(". == {dirid}");
102 return Ok(*dirid);
103 }
104
105 if filename.as_ref() == [b'.', b'.'] {
107 info!(".. == {}", dir.parent);
108 return Ok(dir.parent.into());
109 }
110
111 let children = self.lb.get_children(&dir.id).await.unwrap();
113 let file_name = get_string(filename);
114
115 for child in children {
116 if file_name == child.name {
117 info!("{}", child.id);
118 return Ok(child.id.into());
119 }
120 }
121
122 info!("NOENT");
123 Err(nfsstat3::NFS3ERR_NOENT)
124 }
125
126 #[instrument(skip(self), fields(id = id.to_string()))]
127 async fn getattr(&self, id: &Self::Handle) -> Result<fattr3, nfsstat3> {
128 let file = self
129 .data
130 .lock()
131 .await
132 .get(id)
133 .ok_or(nfsstat3::NFS3ERR_STALE)?
134 .fattr
135 .clone();
136 info!("fattr = {:?}", file);
137 Ok(file)
138 }
139
140 #[instrument(skip(self), fields(id = id.to_string(), offset, count))]
141 async fn read(
142 &self, id: &Self::Handle, offset: u64, count: u32,
143 ) -> Result<(Vec<u8>, bool), nfsstat3> {
144 let offset = offset as usize;
145 let count = count as usize;
146
147 let doc = self.lb.read_document(*id.as_uuid(), false).await.unwrap();
148
149 if offset >= doc.len() {
150 info!("[] EOF");
151 return Ok((vec![], true));
152 }
153
154 if offset + count >= doc.len() {
155 info!("|{}| EOF", doc[offset..].len());
156 return Ok((doc[offset..].to_vec(), true));
157 }
158
159 info!("|{}|", count);
160 return Ok((doc[offset..offset + count].to_vec(), false));
161 }
162
163 #[instrument(skip(self), fields(dirid = dirid.to_string(), start_after = cookie))]
164 async fn readdir(
165 &self, dirid: &Self::Handle, cookie: u64,
166 ) -> Result<impl nfs3_server::vfs::ReadDirIterator, nfsstat3> {
167 let iter = self.load_children(dirid, cookie).await;
168 Ok(Iterator { inner: iter })
169 }
170
171 #[instrument(skip(self), fields(dirid = dirid.to_string(), start_after = cookie))]
172 async fn readdirplus(
173 &self, dirid: &Self::Handle, cookie: u64,
174 ) -> Result<impl ReadDirPlusIterator<UuidFileHandle>, nfsstat3> {
175 let iter = self.load_children(dirid, cookie).await;
176 let data = self.data.lock().await;
177
178 let iter = iter
179 .map(move |file| {
180 let id: UuidFileHandle = file.id.into();
181 let name = file.name.as_bytes().to_vec().into();
182 let fattr = data.get(&id).map(|entry| entry.fattr.clone());
183 DirEntryPlus {
184 fileid: id.fileid(),
185 name,
186 cookie: id.fileid(),
187 name_attributes: fattr,
188 name_handle: Some(id),
189 }
190 })
191 .collect::<Vec<_>>()
192 .into_iter();
193 Ok(IteratorPlus { inner: iter })
194 }
195
196 async fn readlink(&self, _id: &Self::Handle) -> Result<nfspath3<'_>, nfsstat3> {
197 info!("readlink NOTSUPP");
198 Err(nfsstat3::NFS3ERR_NOTSUPP)
199 }
200}
201
202impl NfsFileSystem for Drive {
203 #[instrument(skip(self), fields(id = id.to_string()))]
204 async fn setattr(&self, id: &Self::Handle, setattr: sattr3) -> Result<fattr3, nfsstat3> {
205 let mut data = self.data.lock().await;
206 let now = FileEntry::now();
207 let entry = data.get_mut(id).unwrap();
208
209 if let Nfs3Option::Some(new) = setattr.size
210 && entry.fattr.size != new
211 {
212 let mut doc = self.lb.read_document(*id.as_uuid(), false).await.unwrap();
213 doc.resize(new as usize, 0);
214 self.lb.write_document(*id.as_uuid(), &doc).await.unwrap();
215 entry.fattr.mtime = now;
216 entry.fattr.ctime = now;
217 }
218
219 match setattr.atime {
220 set_atime::DONT_CHANGE => {}
221 set_atime::SET_TO_SERVER_TIME => {
222 entry.fattr.atime = now;
223 }
224 set_atime::SET_TO_CLIENT_TIME(ts) => {
225 entry.fattr.atime = ts;
226 }
227 }
228
229 match setattr.mtime {
230 set_mtime::DONT_CHANGE => {}
231 set_mtime::SET_TO_SERVER_TIME => {
232 entry.fattr.mtime = now;
233 entry.fattr.ctime = now;
234 }
235 set_mtime::SET_TO_CLIENT_TIME(ts) => {
236 entry.fattr.mtime = ts;
237 entry.fattr.ctime = ts;
238 }
239 }
240
241 if let Nfs3Option::Some(uid) = setattr.uid {
242 entry.fattr.uid = uid;
243 entry.fattr.ctime = now;
244 }
245
246 if let Nfs3Option::Some(gid) = setattr.gid {
247 entry.fattr.gid = gid;
248 entry.fattr.ctime = now;
249 }
250
251 if let Nfs3Option::Some(mode) = setattr.mode {
252 entry.fattr.mode = mode;
253 entry.fattr.ctime = now;
254 }
255
256 info!("fattr = {:?}", entry.fattr);
257 Ok(entry.fattr.clone())
258 }
259
260 #[instrument(skip(self), fields(id = id.to_string(), buffer = buffer.len()))]
261 async fn write(
262 &self, id: &Self::Handle, offset: u64, buffer: &[u8],
263 ) -> Result<fattr3, nfsstat3> {
264 let offset = offset as usize;
265
266 let mut data = self.data.lock().await;
267 let entry = data.get_mut(id).unwrap();
268
269 let mut doc = self.lb.read_document(*id.as_uuid(), false).await.unwrap();
270 let mut expanded = false;
271 if offset + buffer.len() > doc.len() {
272 doc.resize(offset + buffer.len(), 0);
273 doc[offset..].copy_from_slice(buffer);
274 expanded = true;
275 } else {
276 for (idx, datum) in buffer.iter().enumerate() {
277 doc[offset + idx] = *datum;
278 }
279 }
280 let doc_size = doc.len();
281 self.lb.write_document(*id.as_uuid(), &doc).await.unwrap();
282
283 entry.fattr.size = doc_size as u64;
284
285 info!("expanded={expanded}, fattr.size = {}", doc_size);
286
287 Ok(entry.fattr.clone())
288 }
289
290 #[instrument(skip(self), fields(dirid = dirid.to_string(), filename = get_string(filename)))]
292 async fn create(
293 &self, dirid: &Self::Handle, filename: &filename3<'_>, attr: sattr3,
294 ) -> Result<(Self::Handle, fattr3), nfsstat3> {
295 let filename = get_string(filename);
296 let file = self
297 .lb
298 .create_file(&filename, dirid.as_uuid(), FileType::Document)
299 .await
300 .unwrap();
301
302 let id = file.id.into();
303 let entry = FileEntry::from_file(file, 0);
304 self.data.lock().await.insert(id, entry);
305
306 let file = self.setattr(&id, attr).await.unwrap();
307
308 info!("({id}, size={})", file.size);
309 Ok((id, file))
310 }
311
312 #[instrument(skip(self), fields(dirid = dirid.to_string(), filename = get_string(filename)))]
313 async fn create_exclusive(
314 &self, dirid: &Self::Handle, filename: &filename3<'_>,
315 createverf: nfs3_server::nfs3_types::nfs3::createverf3,
316 ) -> Result<Self::Handle, nfsstat3> {
317 let filename = get_string(filename);
318 let children = self.lb.get_children(dirid.as_uuid()).await.unwrap();
319 for child in children {
320 if child.name == filename {
321 warn!("exists already");
322 return Err(nfsstat3::NFS3ERR_EXIST);
323 }
324 }
325
326 let file = self
327 .lb
328 .create_file(&filename, dirid.as_uuid(), FileType::Document)
329 .await
330 .unwrap();
331
332 let id = file.id.into();
333 let entry = FileEntry::from_file(file, 0);
334 info!("({id}, size={})", entry.fattr.size);
335 self.data.lock().await.insert(id, entry);
336
337 Ok(id)
338 }
339
340 #[instrument(skip(self), fields(dirid = dirid.to_string(), dirname = get_string(dirname)))]
341 async fn mkdir(
342 &self, dirid: &Self::Handle, dirname: &filename3<'_>,
343 ) -> Result<(Self::Handle, fattr3), nfsstat3> {
344 let filename = get_string(dirname);
345 let file = self
346 .lb
347 .create_file(&filename, dirid.as_uuid(), FileType::Folder)
348 .await
349 .unwrap();
350
351 let id = file.id.into();
352 let entry = FileEntry::from_file(file, 0);
353 let fattr = entry.fattr.clone();
354 self.data.lock().await.insert(id, entry);
355
356 info!("({id}, fattr={fattr:?})");
357 Ok((id, fattr))
358 }
359
360 #[instrument(skip(self), fields(dirid = dirid.to_string(), filename = get_string(filename)))]
364 async fn remove(&self, dirid: &Self::Handle, filename: &filename3<'_>) -> Result<(), nfsstat3> {
365 let mut data = self.data.lock().await;
366
367 let children = self.lb.get_children(dirid.as_uuid()).await.unwrap();
368 let file_name = get_string(filename);
369
370 for child in children {
371 if file_name == child.name {
372 info!("deleted");
373 let _ = self.lb.delete(&child.id).await; data.remove(&child.id.into());
375 return Ok(());
376 }
377 }
378
379 info!("NOENT");
380 Err(nfsstat3::NFS3ERR_NOENT)
381 }
382
383 #[instrument(skip(self), fields(from_dirid = from_dirid.to_string(), from_filename = get_string(from_filename), to_dirid = to_dirid.to_string(), to_filename = get_string(to_filename)))]
385 async fn rename<'a>(
386 &self, from_dirid: &Self::Handle, from_filename: &filename3<'a>, to_dirid: &Self::Handle,
387 to_filename: &filename3<'a>,
388 ) -> Result<(), nfsstat3> {
389 let mut data = self.data.lock().await;
390
391 let from_filename = get_string(from_filename);
392 let to_filename = get_string(to_filename);
393
394 let src_children = self.lb.get_children(from_dirid.as_uuid()).await.unwrap();
395
396 let mut from_id = None;
397 let mut to_id = None;
398 for child in src_children {
399 if child.name == from_filename {
400 from_id = Some(child.id);
401 }
402
403 if to_dirid == from_dirid && child.name == to_filename {
404 to_id = Some(child.id);
405 }
406 }
407
408 if to_dirid != from_dirid {
409 let dst_children = self.lb.get_children(to_dirid.as_uuid()).await.unwrap();
410 for child in dst_children {
411 if child.name == to_filename {
412 to_id = Some(child.id);
413 }
414 }
415 }
416
417 let from_id = from_id.unwrap();
418
419 match to_id {
420 Some(id) => {
422 info!("overwrite {from_id} -> {id}");
423 let from_doc = self.lb.read_document(from_id, false).await.unwrap();
424 info!("|{}|", from_doc.len());
425 let doc_len = from_doc.len() as u64;
426 self.lb.write_document(id, &from_doc).await.unwrap();
427 self.lb.delete(&from_id).await.unwrap();
428
429 let entry = data.get_mut(&id.into()).unwrap();
430 entry.fattr.size = doc_len;
431
432 data.remove(&from_id.into());
433 }
434
435 None => {
437 if from_dirid != to_dirid {
438 info!("move {} -> {}\t", from_id, to_dirid);
439 self.lb
440 .move_file(&from_id, to_dirid.as_uuid())
441 .await
442 .unwrap();
443 }
444
445 if from_filename != to_filename {
446 info!("rename {} -> {}\t", from_id, to_filename);
447 self.lb.rename_file(&from_id, &to_filename).await.unwrap();
448 }
449
450 let entry = data.get_mut(&from_id.into()).unwrap();
451
452 let file = self.lb.get_file_by_id(from_id).await.unwrap();
453 entry.file = file;
454
455 info!("ok");
456 }
457 }
458
459 Ok(())
460 }
461
462 async fn symlink<'a>(
463 &self, _dirid: &Self::Handle, _linkname: &filename3<'a>, _symlink: &nfspath3<'a>,
464 _attr: &sattr3,
465 ) -> Result<(Self::Handle, fattr3), nfsstat3> {
466 info!("symlink NOTSUPP");
467 Err(nfsstat3::NFS3ERR_NOTSUPP)
468 }
469}
470
471pub struct Iterator<I>
472where
473 I: StdIterator<Item = File> + Send + Sync + 'static,
474{
475 inner: I,
476}
477
478impl<I> ReadDirIterator for Iterator<I>
479where
480 I: StdIterator<Item = File> + Send + Sync + 'static,
481{
482 async fn next(&mut self) -> nfs3_server::vfs::NextResult<DirEntry> {
483 match self.inner.next() {
484 Some(entry) => nfs3_server::vfs::NextResult::Ok(DirEntry {
485 fileid: file_id(&entry),
486 name: entry.name.as_bytes().to_vec().into(),
487 cookie: 0,
488 }),
489 None => nfs3_server::vfs::NextResult::Eof,
490 }
491 }
492}
493
494pub struct IteratorPlus<I>
495where
496 I: StdIterator<Item = DirEntryPlus<UuidFileHandle>> + Send + Sync + 'static,
497{
498 inner: I,
499}
500
501impl<I> ReadDirPlusIterator<UuidFileHandle> for IteratorPlus<I>
502where
503 I: StdIterator<Item = DirEntryPlus<UuidFileHandle>> + Send + Sync + 'static,
504{
505 async fn next(&mut self) -> nfs3_server::vfs::NextResult<DirEntryPlus<UuidFileHandle>> {
506 match self.inner.next() {
507 Some(entry) => nfs3_server::vfs::NextResult::Ok(entry),
508 None => nfs3_server::vfs::NextResult::Eof,
509 }
510 }
511}