1use super::filesystem::InodeResolution;
2use super::*;
3use crate::{FileType, FsError, Metadata, OpenOptionsConfig, Result, VirtualFile};
4use shared_buffer::OwnedBuffer;
5use std::path::Path;
6use tracing::*;
7
8impl FileSystem {
9 pub fn insert_ro_file(&self, path: &Path, contents: OwnedBuffer) -> Result<()> {
12 let _ = crate::FileSystem::remove_file(self, path);
13 let (inode_of_parent, maybe_inode_of_file, name_of_file) = self.insert_inode(path)?;
14
15 let inode_of_parent = match inode_of_parent {
16 InodeResolution::Found(a) => a,
17 InodeResolution::Redirect(..) => {
18 return Err(FsError::InvalidInput);
19 }
20 };
21
22 match maybe_inode_of_file {
23 Some(_inode_of_file) => return Err(FsError::AlreadyExists),
25
26 None => {
28 let mut fs = self.inner.write().map_err(|_| FsError::Lock)?;
30
31 let file = ReadOnlyFile::new(contents);
32 let file_len = file.len() as u64;
33
34 let inode_of_file = fs.storage.vacant_entry().key();
36 let real_inode_of_file = fs.storage.insert(Node::ReadOnlyFile(ReadOnlyFileNode {
37 inode: inode_of_file,
38 name: name_of_file,
39 file,
40 metadata: {
41 let time = time();
42
43 Metadata {
44 ft: FileType {
45 file: true,
46 ..Default::default()
47 },
48 accessed: time,
49 created: time,
50 modified: time,
51 len: file_len,
52 }
53 },
54 }));
55
56 assert_eq!(
57 inode_of_file, real_inode_of_file,
58 "new file inode should have been correctly calculated",
59 );
60
61 fs.add_child_to_node(inode_of_parent, inode_of_file)?;
63
64 inode_of_file
65 }
66 };
67 Ok(())
68 }
69
70 pub fn insert_arc_file_at(
73 &self,
74 target_path: PathBuf,
75 fs: Arc<dyn crate::FileSystem + Send + Sync>,
76 source_path: PathBuf,
77 ) -> Result<()> {
78 let _ = crate::FileSystem::remove_file(self, target_path.as_path());
79 let (inode_of_parent, maybe_inode_of_file, name_of_file) =
80 self.insert_inode(target_path.as_path())?;
81
82 let inode_of_parent = match inode_of_parent {
83 InodeResolution::Found(a) => a,
84 InodeResolution::Redirect(..) => {
85 return Err(FsError::InvalidInput);
86 }
87 };
88
89 match maybe_inode_of_file {
90 Some(_inode_of_file) => return Err(FsError::AlreadyExists),
92
93 None => {
95 let mut fs_lock = self.inner.write().map_err(|_| FsError::Lock)?;
97
98 let meta = match fs.metadata(&target_path) {
100 Ok(meta) => meta,
101 _ => {
102 let time = time();
103 Metadata {
104 ft: FileType {
105 file: true,
106 ..Default::default()
107 },
108 accessed: time,
109 created: time,
110 modified: time,
111 len: 0,
112 }
113 }
114 };
115
116 let inode_of_file = fs_lock.storage.vacant_entry().key();
118 let real_inode_of_file = fs_lock.storage.insert(Node::ArcFile(ArcFileNode {
119 inode: inode_of_file,
120 name: name_of_file,
121 fs,
122 path: source_path,
123 metadata: meta,
124 }));
125
126 assert_eq!(
127 inode_of_file, real_inode_of_file,
128 "new file inode should have been correctly calculated",
129 );
130
131 fs_lock.add_child_to_node(inode_of_parent, inode_of_file)?;
133
134 inode_of_file
135 }
136 };
137 Ok(())
138 }
139
140 pub fn insert_arc_file(
143 &self,
144 target_path: PathBuf,
145 fs: Arc<dyn crate::FileSystem + Send + Sync>,
146 ) -> Result<()> {
147 self.insert_arc_file_at(target_path.clone(), fs, target_path)
148 }
149
150 pub fn insert_arc_directory_at(
153 &self,
154 target_path: PathBuf,
155 other: Arc<dyn crate::FileSystem + Send + Sync>,
156 source_path: PathBuf,
157 ) -> Result<()> {
158 let _ = crate::FileSystem::remove_dir(self, target_path.as_path());
159 let (inode_of_parent, maybe_inode_of_file, name_of_file) =
160 self.insert_inode(target_path.as_path())?;
161
162 let inode_of_parent = match inode_of_parent {
163 InodeResolution::Found(a) => a,
164 InodeResolution::Redirect(..) => {
165 return Err(FsError::InvalidInput);
166 }
167 };
168
169 match maybe_inode_of_file {
170 Some(_inode_of_file) => return Err(FsError::AlreadyExists),
172
173 None => {
175 let mut fs_lock = self.inner.write().map_err(|_| FsError::Lock)?;
177
178 let inode_of_file = fs_lock.storage.vacant_entry().key();
180 let real_inode_of_file =
181 fs_lock.storage.insert(Node::ArcDirectory(ArcDirectoryNode {
182 inode: inode_of_file,
183 name: name_of_file,
184 fs: other,
185 path: source_path,
186 metadata: {
187 let time = time();
188 Metadata {
189 ft: FileType {
190 file: true,
191 ..Default::default()
192 },
193 accessed: time,
194 created: time,
195 modified: time,
196 len: 0,
197 }
198 },
199 }));
200
201 assert_eq!(
202 inode_of_file, real_inode_of_file,
203 "new file inode should have been correctly calculated",
204 );
205
206 fs_lock.add_child_to_node(inode_of_parent, inode_of_file)?;
208
209 inode_of_file
210 }
211 };
212 Ok(())
213 }
214
215 pub fn insert_arc_directory(
218 &self,
219 target_path: PathBuf,
220 other: Arc<dyn crate::FileSystem + Send + Sync>,
221 ) -> Result<()> {
222 self.insert_arc_directory_at(target_path.clone(), other, target_path)
223 }
224
225 pub fn insert_device_file(
228 &self,
229 path: PathBuf,
230 file: Box<dyn crate::VirtualFile + Send + Sync>,
231 ) -> Result<()> {
232 let _ = crate::FileSystem::remove_file(self, path.as_path());
233 let (inode_of_parent, maybe_inode_of_file, name_of_file) =
234 self.insert_inode(path.as_path())?;
235
236 let inode_of_parent = match inode_of_parent {
237 InodeResolution::Found(a) => a,
238 InodeResolution::Redirect(..) => {
239 return Err(FsError::InvalidInput);
241 }
242 };
243
244 if let Some(_inode_of_file) = maybe_inode_of_file {
245 return Err(FsError::AlreadyExists);
247 }
248 let mut fs_lock = self.inner.write().map_err(|_| FsError::Lock)?;
250
251 let inode_of_file = fs_lock.storage.vacant_entry().key();
253 let real_inode_of_file = fs_lock.storage.insert(Node::CustomFile(CustomFileNode {
254 inode: inode_of_file,
255 name: name_of_file,
256 file: Mutex::new(file),
257 metadata: {
258 let time = time();
259 Metadata {
260 ft: FileType {
261 file: true,
262 ..Default::default()
263 },
264 accessed: time,
265 created: time,
266 modified: time,
267 len: 0,
268 }
269 },
270 }));
271
272 assert_eq!(
273 inode_of_file, real_inode_of_file,
274 "new file inode should have been correctly calculated",
275 );
276
277 fs_lock.add_child_to_node(inode_of_parent, inode_of_file)?;
279
280 Ok(())
281 }
282
283 fn insert_inode(
284 &self,
285 path: &Path,
286 ) -> Result<(InodeResolution, Option<InodeResolution>, OsString)> {
287 let fs = self.inner.read().map_err(|_| FsError::Lock)?;
289
290 let parent_of_path = path.parent().ok_or(FsError::BaseNotDirectory)?;
292
293 let name_of_file = path
295 .file_name()
296 .ok_or(FsError::InvalidInput)?
297 .to_os_string();
298
299 let inode_of_parent = match fs.inode_of_parent(parent_of_path)? {
301 InodeResolution::Found(a) => a,
302 InodeResolution::Redirect(fs, parent_path) => {
303 return Ok((
304 InodeResolution::Redirect(fs, parent_path),
305 None,
306 name_of_file,
307 ));
308 }
309 };
310
311 let maybe_inode_of_file = fs
313 .as_parent_get_position_and_inode_of_file(inode_of_parent, &name_of_file)?
314 .map(|(_nth, inode)| inode);
315
316 Ok((
317 InodeResolution::Found(inode_of_parent),
318 maybe_inode_of_file,
319 name_of_file,
320 ))
321 }
322}
323
324impl crate::FileOpener for FileSystem {
325 fn open(
326 &self,
327 path: &Path,
328 conf: &OpenOptionsConfig,
329 ) -> Result<Box<dyn VirtualFile + Send + Sync + 'static>> {
330 debug!(path=%path.display(), "open");
331
332 let read = conf.read();
333 let mut write = conf.write();
334 let append = conf.append();
335 let mut truncate = conf.truncate();
336 let mut create = conf.create();
337 let create_new = conf.create_new();
338
339 if create_new {
341 create = false;
342 truncate = false;
343 }
344
345 if truncate && !write {
347 return Err(FsError::PermissionDenied);
348 }
349
350 if append {
353 write = false;
354 }
355
356 let (inode_of_parent, maybe_inode_of_file, name_of_file) = self.insert_inode(path)?;
357
358 let inode_of_parent = match inode_of_parent {
359 InodeResolution::Found(a) => a,
360 InodeResolution::Redirect(fs, mut parent_path) => {
361 parent_path.push(name_of_file);
362 return fs
363 .new_open_options()
364 .options(conf.clone())
365 .open(parent_path);
366 }
367 };
368
369 let mut cursor = 0u64;
370 let inode_of_file = match maybe_inode_of_file {
371 Some(_inode_of_file) if create_new => return Err(FsError::AlreadyExists),
374
375 Some(inode_of_file) => {
377 let inode_of_file = match inode_of_file {
378 InodeResolution::Found(a) => a,
379 InodeResolution::Redirect(fs, path) => {
380 return fs.new_open_options().options(conf.clone()).open(path);
381 }
382 };
383
384 let mut fs = self.inner.write().map_err(|_| FsError::Lock)?;
386
387 let inode = fs.storage.get_mut(inode_of_file);
388 match inode {
389 Some(Node::File(FileNode { metadata, file, .. })) => {
390 metadata.accessed = time();
392
393 if truncate {
395 file.truncate();
396 metadata.len = 0;
397 }
398
399 if append {
401 cursor = file.len() as u64;
402 }
403 }
404
405 Some(Node::OffloadedFile(OffloadedFileNode { metadata, file, .. })) => {
406 metadata.accessed = time();
408
409 if truncate {
411 file.truncate();
412 metadata.len = 0;
413 }
414
415 if append {
417 cursor = file.len();
418 }
419 }
420
421 Some(Node::ReadOnlyFile(node)) => {
422 node.metadata.accessed = time();
424
425 if truncate || append {
427 return Err(FsError::PermissionDenied);
428 }
429 }
430
431 Some(Node::CustomFile(node)) => {
432 node.metadata.accessed = time();
434
435 let mut file = node.file.lock().unwrap();
437 if truncate {
438 file.set_len(0)?;
439 node.metadata.len = 0;
440 }
441
442 if append {
444 cursor = file.size();
445 }
446 }
447
448 Some(Node::ArcFile(node)) => {
449 node.metadata.accessed = time();
451
452 let mut file = node
453 .fs
454 .new_open_options()
455 .read(read)
456 .write(write)
457 .append(append)
458 .truncate(truncate)
459 .create(create)
460 .create_new(create_new)
461 .open(node.path.as_path())?;
462
463 if truncate {
465 file.set_len(0)?;
466 node.metadata.len = 0;
467 }
468
469 if append {
471 cursor = file.size();
472 }
473 }
474
475 None => return Err(FsError::EntryNotFound),
476 _ => return Err(FsError::NotAFile),
477 }
478
479 inode_of_file
480 }
481
482 None if (create_new || create) && (create_new || write || append) => {
486 let mut fs = self.inner.write().map_err(|_| FsError::Lock)?;
488
489 let metadata = {
490 let time = time();
491 Metadata {
492 ft: FileType {
493 file: true,
494 ..Default::default()
495 },
496 accessed: time,
497 created: time,
498 modified: time,
499 len: 0,
500 }
501 };
502 let inode_of_file = fs.storage.vacant_entry().key();
503
504 let file = match fs.backing_offload.clone() {
506 Some(offload) => {
507 let file = OffloadedFile::new(fs.limiter.clone(), offload);
508 Node::OffloadedFile(OffloadedFileNode {
509 inode: inode_of_file,
510 name: name_of_file,
511 file,
512 metadata,
513 })
514 }
515 _ => {
516 let file = File::new(fs.limiter.clone());
517 Node::File(FileNode {
518 inode: inode_of_file,
519 name: name_of_file,
520 file,
521 metadata,
522 })
523 }
524 };
525
526 let real_inode_of_file = fs.storage.insert(file);
528
529 assert_eq!(
530 inode_of_file, real_inode_of_file,
531 "new file inode should have been correctly calculated",
532 );
533
534 fs.add_child_to_node(inode_of_parent, inode_of_file)?;
536
537 inode_of_file
538 }
539
540 None if (create_new || create) => return Err(FsError::PermissionDenied),
541
542 None => return Err(FsError::EntryNotFound),
543 };
544
545 Ok(Box::new(FileHandle::new(
546 inode_of_file,
547 self.clone(),
548 read,
549 write || append || truncate,
550 append,
551 cursor,
552 )))
553 }
554}
555
556#[cfg(test)]
557mod test_file_opener {
558 use tokio::io::{AsyncReadExt, AsyncSeekExt, AsyncWriteExt};
559
560 use crate::{FileSystem as FS, FsError, mem_fs::*};
561 use std::io;
562
563 macro_rules! path {
564 ($path:expr_2021) => {
565 std::path::Path::new($path)
566 };
567 }
568
569 #[tokio::test]
570 async fn test_create_new_file() {
571 let fs = FileSystem::default();
572
573 assert!(
574 fs.new_open_options()
575 .write(true)
576 .create_new(true)
577 .open(path!("/foo.txt"))
578 .is_ok(),
579 "creating a new file",
580 );
581
582 {
583 let fs_inner = fs.inner.read().unwrap();
584
585 assert_eq!(fs_inner.storage.len(), 2, "storage has the new file");
586 assert!(
587 matches!(
588 fs_inner.storage.get(ROOT_INODE),
589 Some(Node::Directory(DirectoryNode {
590 inode: ROOT_INODE,
591 name,
592 children,
593 ..
594 })) if name == "/" && children == &[1]
595 ),
596 "`/` contains `foo.txt`",
597 );
598 assert!(
599 matches!(
600 fs_inner.storage.get(1),
601 Some(Node::File(FileNode {
602 inode: 1,
603 name,
604 ..
605 })) if name == "foo.txt"
606 ),
607 "`foo.txt` exists and is a file",
608 );
609 }
610
611 assert!(
612 matches!(
613 fs.new_open_options()
614 .write(true)
615 .create_new(true)
616 .open(path!("/foo.txt")),
617 Err(FsError::AlreadyExists)
618 ),
619 "creating a new file that already exist",
620 );
621
622 assert_eq!(
623 fs.new_open_options()
624 .write(true)
625 .create_new(true)
626 .open(path!("/foo/bar.txt"))
627 .map(|_| ()),
628 Err(FsError::EntryNotFound),
629 "creating a file in a directory that doesn't exist",
630 );
631
632 assert_eq!(fs.remove_file(path!("/foo.txt")), Ok(()), "removing a file");
633
634 assert!(
635 fs.new_open_options()
636 .write(false)
637 .create_new(true)
638 .open(path!("/foo.txt"))
639 .is_ok(),
640 "creating a file without the `write` option",
641 );
642 }
643
644 #[tokio::test]
645 async fn test_truncate_a_read_only_file() {
646 let fs = FileSystem::default();
647
648 assert!(
649 matches!(
650 fs.new_open_options()
651 .write(false)
652 .truncate(true)
653 .open(path!("/foo.txt")),
654 Err(FsError::PermissionDenied),
655 ),
656 "truncating a read-only file",
657 );
658 }
659
660 #[tokio::test]
661 async fn test_truncate() {
662 let fs = FileSystem::default();
663
664 let mut file = fs
665 .new_open_options()
666 .write(true)
667 .create_new(true)
668 .open(path!("/foo.txt"))
669 .expect("failed to create a new file");
670
671 assert!(
672 matches!(file.write(b"foobar").await, Ok(6)),
673 "writing `foobar` at the end of the file",
674 );
675
676 assert!(
677 matches!(file.seek(io::SeekFrom::Current(0)).await, Ok(6)),
678 "checking the current position is 6",
679 );
680 assert!(
681 matches!(file.seek(io::SeekFrom::End(0)).await, Ok(6)),
682 "checking the size is 6",
683 );
684
685 let mut file = fs
686 .new_open_options()
687 .write(true)
688 .truncate(true)
689 .open(path!("/foo.txt"))
690 .expect("failed to open + truncate `foo.txt`");
691
692 assert!(
693 matches!(file.seek(io::SeekFrom::Current(0)).await, Ok(0)),
694 "checking the current position is 0",
695 );
696 assert!(
697 matches!(file.seek(io::SeekFrom::End(0)).await, Ok(0)),
698 "checking the size is 0",
699 );
700 }
701
702 #[tokio::test]
703 async fn test_append() {
704 let fs = FileSystem::default();
705
706 let mut file = fs
707 .new_open_options()
708 .write(true)
709 .create_new(true)
710 .open(path!("/foo.txt"))
711 .expect("failed to create a new file");
712
713 assert!(
714 matches!(file.write(b"foobar").await, Ok(6)),
715 "writing `foobar` at the end of the file",
716 );
717
718 assert!(
719 matches!(file.seek(io::SeekFrom::Current(0)).await, Ok(6)),
720 "checking the current position is 6",
721 );
722 assert!(
723 matches!(file.seek(io::SeekFrom::End(0)).await, Ok(6)),
724 "checking the size is 6",
725 );
726
727 let mut file = fs
728 .new_open_options()
729 .append(true)
730 .open(path!("/foo.txt"))
731 .expect("failed to open `foo.txt`");
732
733 assert!(
734 matches!(file.seek(io::SeekFrom::Current(0)).await, Ok(0)),
735 "checking the current position in append-mode is 0",
736 );
737 assert!(
738 matches!(file.seek(io::SeekFrom::Start(0)).await, Ok(0)),
739 "trying to rewind in append-mode",
740 );
741 assert!(matches!(file.write(b"baz").await, Ok(3)), "writing `baz`");
742
743 let mut file = fs
744 .new_open_options()
745 .read(true)
746 .open(path!("/foo.txt"))
747 .expect("failed to open `foo.txt");
748
749 assert!(
750 matches!(file.seek(io::SeekFrom::Current(0)).await, Ok(0)),
751 "checking the current position is read-mode is 0",
752 );
753
754 let mut string = String::new();
755 assert!(
756 matches!(file.read_to_string(&mut string).await, Ok(9)),
757 "reading the entire `foo.txt` file",
758 );
759 assert_eq!(
760 string, "foobarbaz",
761 "checking append-mode is ignoring seek operations",
762 );
763 }
764
765 #[tokio::test]
766 async fn test_opening_a_file_that_already_exists() {
767 let fs = FileSystem::default();
768
769 assert!(
770 fs.new_open_options()
771 .write(true)
772 .create_new(true)
773 .open(path!("/foo.txt"))
774 .is_ok(),
775 "creating a _new_ file",
776 );
777
778 assert!(
779 matches!(
780 fs.new_open_options()
781 .create_new(true)
782 .open(path!("/foo.txt")),
783 Err(FsError::AlreadyExists),
784 ),
785 "creating a _new_ file that already exists",
786 );
787
788 assert!(
789 fs.new_open_options()
790 .read(true)
791 .open(path!("/foo.txt"))
792 .is_ok(),
793 "opening a file that already exists",
794 );
795 }
796}