1#![crate_name = "remotefs_memory"]
2#![crate_type = "lib"]
3
4#![doc(html_playground_url = "https://play.rust-lang.org")]
43#![doc(
44 html_favicon_url = "https://raw.githubusercontent.com/remotefs-rs/remotefs-rs/main/assets/logo-128.png"
45)]
46#![doc(
47 html_logo_url = "https://raw.githubusercontent.com/remotefs-rs/remotefs-rs/main/assets/logo.png"
48)]
49
50#[macro_use]
51extern crate log;
52
53mod inode;
54#[cfg(test)]
55mod test;
56
57use std::io::{Cursor, Read, Seek, Write};
58use std::path::{Path, PathBuf};
59use std::time::SystemTime;
60
61pub use orange_trees::{node, Node, Tree};
62use remotefs::fs::stream::{StreamWriter, WriteAndSeek};
63use remotefs::fs::{FileType, Metadata, ReadStream, UnixPex, Welcome, WriteStream};
64use remotefs::{File, RemoteError, RemoteErrorType, RemoteFs, RemoteResult};
65
66pub use self::inode::Inode;
67
68pub type FsTree = Tree<PathBuf, Inode>;
70
71pub struct MemoryFs {
84 tree: FsTree,
85 wrkdir: PathBuf,
86 connected: bool,
87 get_uid: Box<dyn Fn() -> u32 + Send + Sync>,
89 get_gid: Box<dyn Fn() -> u32 + Send + Sync>,
91}
92
93#[derive(Debug, Clone)]
94struct WriteHandle {
95 path: PathBuf,
96 data: Cursor<Vec<u8>>,
97 mode: WriteMode,
98}
99
100#[derive(Debug, Clone, Copy, PartialEq, Eq)]
101enum WriteMode {
102 Append,
103 Create,
104}
105
106impl Write for WriteHandle {
107 fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
108 self.data.write(buf)
109 }
110
111 fn flush(&mut self) -> std::io::Result<()> {
112 Ok(())
113 }
114}
115
116impl Seek for WriteHandle {
117 fn seek(&mut self, pos: std::io::SeekFrom) -> std::io::Result<u64> {
118 self.data.seek(pos)
119 }
120}
121
122impl WriteAndSeek for WriteHandle {}
123
124impl MemoryFs {
125 pub fn new(tree: FsTree) -> Self {
127 Self {
128 tree,
129 wrkdir: PathBuf::from("/"),
130 connected: false,
131 get_uid: Box::new(|| 0),
132 get_gid: Box::new(|| 0),
133 }
134 }
135
136 pub fn with_get_uid<F>(mut self, get_uid: F) -> Self
138 where
139 F: Fn() -> u32 + Send + Sync + 'static,
140 {
141 self.get_uid = Box::new(get_uid);
142 self
143 }
144
145 pub fn with_get_gid<F>(mut self, get_gid: F) -> Self
147 where
148 F: Fn() -> u32 + Send + Sync + 'static,
149 {
150 self.get_gid = Box::new(get_gid);
151 self
152 }
153
154 fn absolutize(&self, path: &Path) -> PathBuf {
155 if path.is_absolute() {
156 path.to_path_buf()
157 } else {
158 self.wrkdir.join(path)
159 }
160 }
161
162 fn downcast_write_handle(handle: WriteStream) -> Box<WriteHandle> {
164 match handle.stream {
165 StreamWriter::Write(w) => {
166 let raw: *mut dyn Write = Box::into_raw(w);
167 unsafe { Box::from_raw(raw as *mut WriteHandle) }
168 }
169 StreamWriter::WriteAndSeek(w) => {
170 let raw: *mut dyn WriteAndSeek = Box::into_raw(w);
171 unsafe { Box::from_raw(raw as *mut WriteHandle) }
172 }
173 }
174 }
175}
176
177impl RemoteFs for MemoryFs {
178 fn connect(&mut self) -> RemoteResult<Welcome> {
179 debug!("connect()");
180 self.connected = true;
181 Ok(Welcome::default())
182 }
183
184 fn disconnect(&mut self) -> RemoteResult<()> {
185 debug!("disconnect()");
186 self.connected = false;
187 Ok(())
188 }
189
190 fn is_connected(&mut self) -> bool {
191 debug!("is_connected() -> {}", self.connected);
192 self.connected
193 }
194
195 fn pwd(&mut self) -> RemoteResult<PathBuf> {
196 if !self.connected {
197 return Err(RemoteError::new(RemoteErrorType::NotConnected));
198 }
199 debug!("pwd() -> {:?}", self.wrkdir);
200
201 Ok(self.wrkdir.clone())
202 }
203
204 fn change_dir(&mut self, dir: &Path) -> RemoteResult<PathBuf> {
205 if !self.connected {
206 return Err(RemoteError::new(RemoteErrorType::NotConnected));
207 }
208
209 let dir = self.absolutize(dir);
210
211 debug!("change_dir({:?})", dir);
212
213 let inode = self
215 .tree
216 .root()
217 .query(&dir)
218 .ok_or_else(|| RemoteError::new(RemoteErrorType::NoSuchFileOrDirectory))?
219 .value()
220 .clone();
221
222 match inode.metadata().file_type {
223 FileType::Directory => {
224 self.wrkdir = dir.clone();
225 Ok(self.wrkdir.clone())
226 }
227 FileType::Symlink if inode.metadata().symlink.is_some() => {
228 self.change_dir(inode.metadata().symlink.as_ref().unwrap())
229 }
230 FileType::Symlink | FileType::File => Err(RemoteError::new(RemoteErrorType::BadFile)),
231 }
232 }
233
234 fn list_dir(&mut self, path: &Path) -> RemoteResult<Vec<File>> {
235 if !self.connected {
236 return Err(RemoteError::new(RemoteErrorType::NotConnected));
237 }
238
239 let path = self.absolutize(path);
240 debug!("list_dir({:?})", path);
241
242 let node = self
244 .tree
245 .root()
246 .query(&path)
247 .ok_or_else(|| RemoteError::new(RemoteErrorType::NoSuchFileOrDirectory))?;
248
249 let mut files = vec![];
250
251 for child in node.children() {
252 let path = child.id().clone();
253 let metadata = child.value().metadata().clone();
254 debug!("list_dir() -> {path:?}, {metadata:?}");
255
256 files.push(File { path, metadata })
257 }
258
259 Ok(files)
260 }
261
262 fn stat(&mut self, path: &Path) -> RemoteResult<File> {
263 if !self.connected {
264 return Err(RemoteError::new(RemoteErrorType::NotConnected));
265 }
266
267 let path = self.absolutize(path);
268 debug!("stat({:?})", path);
269
270 let node = self
271 .tree
272 .root()
273 .query(&path)
274 .ok_or_else(|| RemoteError::new(RemoteErrorType::NoSuchFileOrDirectory))?;
275
276 let path = node.id().clone();
277 let metadata = node.value().metadata().clone();
278
279 debug!("stat({path:?}) -> {metadata:?}");
280
281 Ok(File { path, metadata })
282 }
283
284 fn setstat(&mut self, path: &Path, metadata: Metadata) -> RemoteResult<()> {
285 if !self.connected {
286 return Err(RemoteError::new(RemoteErrorType::NotConnected));
287 }
288
289 let path = self.absolutize(path);
290 debug!("setstat({:?}, {:?})", path, metadata);
291
292 let node = self
293 .tree
294 .root_mut()
295 .query_mut(&path)
296 .ok_or_else(|| RemoteError::new(RemoteErrorType::NoSuchFileOrDirectory))?;
297
298 node.set_value(Inode {
299 metadata,
300 content: node.value().content.clone(),
301 });
302
303 Ok(())
304 }
305
306 fn exists(&mut self, path: &Path) -> RemoteResult<bool> {
307 if !self.connected {
308 return Err(RemoteError::new(RemoteErrorType::NotConnected));
309 }
310
311 let path = self.absolutize(path);
312 debug!("exists({:?})", path);
313
314 Ok(self.tree.root().query(&path).is_some())
315 }
316
317 fn remove_file(&mut self, path: &Path) -> RemoteResult<()> {
318 if !self.connected {
319 return Err(RemoteError::new(RemoteErrorType::NotConnected));
320 }
321
322 let path = self.absolutize(path);
323 debug!("remove_file({:?})", path);
324
325 let node = self
327 .tree
328 .root_mut()
329 .query_mut(&path)
330 .ok_or_else(|| RemoteError::new(RemoteErrorType::NoSuchFileOrDirectory))?;
331
332 if !node.is_leaf() || node.value().metadata().file_type == FileType::Directory {
334 return Err(RemoteError::new(RemoteErrorType::CouldNotRemoveFile));
335 }
336
337 let parent = self.tree.root_mut().parent_mut(&path).unwrap();
338 parent.remove_child(&path);
339
340 Ok(())
341 }
342
343 fn remove_dir(&mut self, path: &Path) -> RemoteResult<()> {
344 if !self.connected {
345 return Err(RemoteError::new(RemoteErrorType::NotConnected));
346 }
347
348 let path = self.absolutize(path);
349 debug!("remove_dir({:?})", path);
350
351 let node = self
353 .tree
354 .root_mut()
355 .query_mut(&path)
356 .ok_or_else(|| RemoteError::new(RemoteErrorType::NoSuchFileOrDirectory))?;
357 if !node.is_leaf() {
359 debug!("Directory {path:?} is not empty");
360 return Err(RemoteError::new(RemoteErrorType::DirectoryNotEmpty));
361 }
362 if node.value().metadata().file_type != FileType::Directory {
363 debug!("{path:?} is not a directory");
364 return Err(RemoteError::new(RemoteErrorType::CouldNotRemoveFile));
365 }
366
367 let parent = self.tree.root_mut().parent_mut(&path).unwrap();
368 parent.remove_child(&path);
369 debug!("removed {:?}", path);
370
371 Ok(())
372 }
373
374 fn remove_dir_all(&mut self, path: &Path) -> RemoteResult<()> {
375 if !self.connected {
376 return Err(RemoteError::new(RemoteErrorType::NotConnected));
377 }
378
379 let path = self.absolutize(path);
380 debug!("remove_dir_all({:?})", path);
381
382 let parent = self
383 .tree
384 .root_mut()
385 .parent_mut(&path)
386 .ok_or_else(|| RemoteError::new(RemoteErrorType::NoSuchFileOrDirectory))?;
387
388 if !parent.children().iter().any(|child| *child.id() == path) {
389 return Err(RemoteError::new(RemoteErrorType::NoSuchFileOrDirectory));
390 }
391 parent.remove_child(&path);
392 debug!("removed {:?}", path);
393
394 Ok(())
395 }
396
397 fn create_dir(&mut self, path: &Path, mode: UnixPex) -> RemoteResult<()> {
398 if !self.connected {
399 return Err(RemoteError::new(RemoteErrorType::NotConnected));
400 }
401
402 let path = self.absolutize(path);
403 debug!("create_dir({:?})", path);
404 let parent = path
405 .parent()
406 .unwrap_or_else(|| Path::new("/"))
407 .to_path_buf();
408
409 let dir = Inode::dir((self.get_uid)(), (self.get_gid)(), mode);
410
411 let parent = self
412 .tree
413 .root_mut()
414 .query_mut(&parent)
415 .ok_or_else(|| RemoteError::new(RemoteErrorType::NoSuchFileOrDirectory))?;
416
417 if parent.children().iter().any(|child| *child.id() == path) {
419 debug!("Directory {path:?} already exists");
420 return Err(RemoteError::new(RemoteErrorType::DirectoryAlreadyExists));
421 }
422
423 parent.add_child(Node::new(path.clone(), dir));
425 debug!("created directory {path:?}");
426
427 Ok(())
428 }
429
430 fn symlink(&mut self, path: &Path, target: &Path) -> RemoteResult<()> {
431 if !self.connected {
432 return Err(RemoteError::new(RemoteErrorType::NotConnected));
433 }
434 let path = self.absolutize(path);
435 let target = self.absolutize(target);
436 debug!("symlink({:?}, {:?})", path, target);
437 if self.tree.root().query(&target).is_none() {
439 debug!("target {target:?} does not exist");
440 return Err(RemoteError::new(RemoteErrorType::NoSuchFileOrDirectory));
441 }
442
443 let parent = path
444 .parent()
445 .unwrap_or_else(|| Path::new("/"))
446 .to_path_buf();
447
448 let symlink = Inode::symlink((self.get_uid)(), (self.get_gid)(), target.to_path_buf());
449
450 let parent = self
451 .tree
452 .root_mut()
453 .query_mut(&parent)
454 .ok_or_else(|| RemoteError::new(RemoteErrorType::NoSuchFileOrDirectory))?;
455
456 if parent.children().iter().any(|child| *child.id() == path) {
458 debug!("symbolic link {path:?} already exists");
459 return Err(RemoteError::new(RemoteErrorType::FileCreateDenied));
460 }
461
462 parent.add_child(Node::new(path.clone(), symlink));
464 debug!("symlink {path:?} -> {target:?}");
465
466 Ok(())
467 }
468
469 fn copy(&mut self, src: &Path, dest: &Path) -> RemoteResult<()> {
470 if !self.connected {
471 return Err(RemoteError::new(RemoteErrorType::NotConnected));
472 }
473 let src = self.absolutize(src);
474 let dest = self.absolutize(dest);
475 debug!("copy({:?}, {:?})", src, dest);
476
477 let dest_parent = dest
478 .parent()
479 .unwrap_or_else(|| Path::new("/"))
480 .to_path_buf();
481
482 let dest_inode = self
483 .tree
484 .root()
485 .query(&src)
486 .ok_or_else(|| RemoteError::new(RemoteErrorType::NoSuchFileOrDirectory))?
487 .value()
488 .clone();
489
490 let dest_parent = self
491 .tree
492 .root_mut()
493 .query_mut(&dest_parent)
494 .ok_or_else(|| RemoteError::new(RemoteErrorType::NoSuchFileOrDirectory))?;
495
496 debug!("copied {src:?} to {dest:?}");
497 dest_parent.add_child(Node::new(dest, dest_inode));
498
499 Ok(())
500 }
501
502 fn mov(&mut self, src: &Path, dest: &Path) -> RemoteResult<()> {
503 if !self.connected {
504 return Err(RemoteError::new(RemoteErrorType::NotConnected));
505 }
506 let src = self.absolutize(src);
507 let dest = self.absolutize(dest);
508 debug!("mov({:?}, {:?})", src, dest);
509
510 let dest_parent = dest
511 .parent()
512 .unwrap_or_else(|| Path::new("/"))
513 .to_path_buf();
514
515 let dest_inode = self
516 .tree
517 .root()
518 .query(&src)
519 .ok_or_else(|| RemoteError::new(RemoteErrorType::NoSuchFileOrDirectory))?
520 .value()
521 .clone();
522
523 let dest_parent = self
524 .tree
525 .root_mut()
526 .query_mut(&dest_parent)
527 .ok_or_else(|| RemoteError::new(RemoteErrorType::NoSuchFileOrDirectory))?;
528
529 dest_parent.add_child(Node::new(dest.clone(), dest_inode));
530
531 let src_parent = self
533 .tree
534 .root_mut()
535 .parent_mut(&src)
536 .ok_or_else(|| RemoteError::new(RemoteErrorType::NoSuchFileOrDirectory))?;
537
538 src_parent.remove_child(&src);
539 debug!("moved {src:?} to {dest:?}");
540
541 Ok(())
542 }
543
544 fn exec(&mut self, _cmd: &str) -> RemoteResult<(u32, String)> {
545 Err(RemoteError::new(RemoteErrorType::UnsupportedFeature))
546 }
547
548 fn append(&mut self, path: &Path, metadata: &Metadata) -> RemoteResult<WriteStream> {
549 if !self.connected {
550 return Err(RemoteError::new(RemoteErrorType::NotConnected));
551 }
552 let path = self.absolutize(path);
553 debug!("append({:?},{:?})", path, metadata);
554 let parent = path
555 .parent()
556 .unwrap_or_else(|| Path::new("/"))
557 .to_path_buf();
558
559 let parent = self
560 .tree
561 .root_mut()
562 .query_mut(&parent)
563 .ok_or_else(|| RemoteError::new(RemoteErrorType::NoSuchFileOrDirectory))?;
564
565 let content = match parent.query(&path) {
567 Some(node) => node.value().content.clone(),
568 None => None,
569 };
570
571 let file = Inode::file(
572 metadata.uid.unwrap_or((self.get_uid)()),
573 metadata.gid.unwrap_or((self.get_gid)()),
574 metadata.mode.unwrap_or_else(|| UnixPex::from(0o755)),
575 content.clone().unwrap_or_default(),
576 );
577
578 parent.add_child(Node::new(path.clone(), file));
580
581 debug!("file {path:?} opened for append");
583 let handle = WriteHandle {
584 path,
585 data: Cursor::new(content.unwrap_or_default()),
586 mode: WriteMode::Append,
587 };
588
589 let stream = Box::new(handle) as Box<dyn WriteAndSeek + Send>;
590
591 Ok(WriteStream {
592 stream: StreamWriter::WriteAndSeek(stream),
593 })
594 }
595
596 fn create(&mut self, path: &Path, metadata: &Metadata) -> RemoteResult<WriteStream> {
597 if !self.connected {
598 return Err(RemoteError::new(RemoteErrorType::NotConnected));
599 }
600 let path = self.absolutize(path);
601 debug!("create({:?},{:?})", path, metadata);
602 let parent = path
603 .parent()
604 .unwrap_or_else(|| Path::new("/"))
605 .to_path_buf();
606
607 let parent = self
608 .tree
609 .root_mut()
610 .query_mut(&parent)
611 .ok_or_else(|| RemoteError::new(RemoteErrorType::NoSuchFileOrDirectory))?;
612
613 let file = Inode::file(
614 metadata.uid.unwrap_or((self.get_uid)()),
615 metadata.gid.unwrap_or((self.get_gid)()),
616 metadata.mode.unwrap_or_else(|| UnixPex::from(0o755)),
617 vec![],
618 );
619
620 parent.add_child(Node::new(path.clone(), file));
622 debug!("{:?} created", path);
623
624 let handle = WriteHandle {
626 path,
627 data: Cursor::new(vec![]),
628 mode: WriteMode::Create,
629 };
630
631 let stream = Box::new(handle) as Box<dyn WriteAndSeek + Send>;
632
633 Ok(WriteStream {
634 stream: StreamWriter::WriteAndSeek(stream),
635 })
636 }
637
638 fn open(&mut self, path: &Path) -> RemoteResult<ReadStream> {
639 if !self.connected {
640 return Err(RemoteError::new(RemoteErrorType::NotConnected));
641 }
642 let path = self.absolutize(path);
643 debug!("open({:?})", path);
644
645 let node = self
646 .tree
647 .root()
648 .query(&path)
649 .ok_or_else(|| RemoteError::new(RemoteErrorType::NoSuchFileOrDirectory))?;
650
651 debug!("{:?} opened", path);
652
653 let stream = Cursor::new(node.value().content.as_ref().cloned().unwrap_or_default());
654 let stream = Box::new(stream) as Box<dyn Read + Send>;
655
656 Ok(ReadStream::from(stream))
657 }
658
659 fn on_written(&mut self, writable: WriteStream) -> RemoteResult<()> {
660 let handle = Self::downcast_write_handle(writable);
661 debug!("on_written({:?}, {:?})", handle.path, handle.mode);
662
663 let node = self
665 .tree
666 .root_mut()
667 .query_mut(&handle.path)
668 .ok_or_else(|| RemoteError::new(RemoteErrorType::NoSuchFileOrDirectory))?;
669
670 let mut value = node.value().clone();
671
672 value.content = match handle.mode {
673 WriteMode::Append => {
674 let mut content = value.content.as_ref().cloned().unwrap_or_default();
675 content.extend_from_slice(handle.data.get_ref());
676 Some(content)
677 }
678 WriteMode::Create => Some(handle.data.get_ref().to_vec()),
679 };
680 value.metadata.size = match handle.mode {
681 WriteMode::Append => {
682 let mut size = value.metadata.size;
683 size += handle.data.get_ref().len() as u64;
684 size
685 }
686 WriteMode::Create => handle.data.get_ref().len() as u64,
687 };
688 value.metadata.modified = Some(SystemTime::now());
689
690 debug!("{:?} written {:?}", handle.path, value);
691 node.set_value(value);
692
693 Ok(())
694 }
695}