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 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
400 Some(Node::OffloadedFile(OffloadedFileNode { metadata, file, .. })) => {
401 metadata.accessed = time();
403
404 if truncate {
406 file.truncate();
407 metadata.len = 0;
408 }
409 }
410
411 Some(Node::ReadOnlyFile(node)) => {
412 node.metadata.accessed = time();
414
415 if truncate || append {
417 return Err(FsError::PermissionDenied);
418 }
419 }
420
421 Some(Node::CustomFile(node)) => {
422 node.metadata.accessed = time();
424
425 let mut file = node.file.lock().unwrap();
427 if truncate {
428 file.set_len(0)?;
429 node.metadata.len = 0;
430 }
431 }
432
433 Some(Node::ArcFile(node)) => {
434 node.metadata.accessed = time();
436
437 let mut file = node
438 .fs
439 .new_open_options()
440 .read(read)
441 .write(write)
442 .append(append)
443 .truncate(truncate)
444 .create(create)
445 .create_new(create_new)
446 .open(node.path.as_path())?;
447
448 if truncate {
450 file.set_len(0)?;
451 node.metadata.len = 0;
452 }
453 }
454
455 None => return Err(FsError::EntryNotFound),
456 _ => return Err(FsError::NotAFile),
457 }
458
459 inode_of_file
460 }
461
462 None if (create_new || create) && (create_new || write || append) => {
466 let mut fs = self.inner.write().map_err(|_| FsError::Lock)?;
468
469 let metadata = {
470 let time = time();
471 Metadata {
472 ft: FileType {
473 file: true,
474 ..Default::default()
475 },
476 accessed: time,
477 created: time,
478 modified: time,
479 len: 0,
480 }
481 };
482 let inode_of_file = fs.storage.vacant_entry().key();
483
484 let file = match fs.backing_offload.clone() {
486 Some(offload) => {
487 let file = OffloadedFile::new(fs.limiter.clone(), offload);
488 Node::OffloadedFile(OffloadedFileNode {
489 inode: inode_of_file,
490 name: name_of_file,
491 file,
492 metadata,
493 })
494 }
495 _ => {
496 let file = File::new(fs.limiter.clone());
497 Node::File(FileNode {
498 inode: inode_of_file,
499 name: name_of_file,
500 file,
501 metadata,
502 })
503 }
504 };
505
506 let real_inode_of_file = fs.storage.insert(file);
508
509 assert_eq!(
510 inode_of_file, real_inode_of_file,
511 "new file inode should have been correctly calculated",
512 );
513
514 fs.add_child_to_node(inode_of_parent, inode_of_file)?;
516
517 inode_of_file
518 }
519
520 None if (create_new || create) => return Err(FsError::PermissionDenied),
521
522 None => return Err(FsError::EntryNotFound),
523 };
524
525 Ok(Box::new(FileHandle::new(
526 inode_of_file,
527 self.clone(),
528 read,
529 write || append || truncate,
530 append,
531 cursor,
532 )))
533 }
534}
535
536#[cfg(test)]
537mod test_file_opener {
538 use tokio::io::{AsyncReadExt, AsyncSeekExt, AsyncWriteExt};
539
540 use crate::{FileSystem as FS, FsError, mem_fs::*};
541 use std::io;
542
543 macro_rules! path {
544 ($path:expr) => {
545 std::path::Path::new($path)
546 };
547 }
548
549 #[tokio::test]
550 async fn test_create_new_file() {
551 let fs = FileSystem::default();
552
553 assert!(
554 fs.new_open_options()
555 .write(true)
556 .create_new(true)
557 .open(path!("/foo.txt"))
558 .is_ok(),
559 "creating a new file",
560 );
561
562 {
563 let fs_inner = fs.inner.read().unwrap();
564
565 assert_eq!(fs_inner.storage.len(), 2, "storage has the new file");
566 assert!(
567 matches!(
568 fs_inner.storage.get(ROOT_INODE),
569 Some(Node::Directory(DirectoryNode {
570 inode: ROOT_INODE,
571 name,
572 children,
573 ..
574 })) if name == "/" && children == &[1]
575 ),
576 "`/` contains `foo.txt`",
577 );
578 assert!(
579 matches!(
580 fs_inner.storage.get(1),
581 Some(Node::File(FileNode {
582 inode: 1,
583 name,
584 ..
585 })) if name == "foo.txt"
586 ),
587 "`foo.txt` exists and is a file",
588 );
589 }
590
591 assert!(
592 matches!(
593 fs.new_open_options()
594 .write(true)
595 .create_new(true)
596 .open(path!("/foo.txt")),
597 Err(FsError::AlreadyExists)
598 ),
599 "creating a new file that already exist",
600 );
601
602 assert_eq!(
603 fs.new_open_options()
604 .write(true)
605 .create_new(true)
606 .open(path!("/foo/bar.txt"))
607 .map(|_| ()),
608 Err(FsError::EntryNotFound),
609 "creating a file in a directory that doesn't exist",
610 );
611
612 assert_eq!(fs.remove_file(path!("/foo.txt")), Ok(()), "removing a file");
613
614 assert!(
615 fs.new_open_options()
616 .write(false)
617 .create_new(true)
618 .open(path!("/foo.txt"))
619 .is_ok(),
620 "creating a file without the `write` option",
621 );
622 }
623
624 #[tokio::test]
625 async fn test_truncate_a_read_only_file() {
626 let fs = FileSystem::default();
627
628 assert!(
629 matches!(
630 fs.new_open_options()
631 .write(false)
632 .truncate(true)
633 .open(path!("/foo.txt")),
634 Err(FsError::PermissionDenied),
635 ),
636 "truncating a read-only file",
637 );
638 }
639
640 #[tokio::test]
641 async fn test_truncate() {
642 let fs = FileSystem::default();
643
644 let mut file = fs
645 .new_open_options()
646 .write(true)
647 .create_new(true)
648 .open(path!("/foo.txt"))
649 .expect("failed to create a new file");
650
651 assert!(
652 matches!(file.write(b"foobar").await, Ok(6)),
653 "writing `foobar` at the end of the file",
654 );
655
656 assert!(
657 matches!(file.seek(io::SeekFrom::Current(0)).await, Ok(6)),
658 "checking the current position is 6",
659 );
660 assert!(
661 matches!(file.seek(io::SeekFrom::End(0)).await, Ok(6)),
662 "checking the size is 6",
663 );
664
665 let mut file = fs
666 .new_open_options()
667 .write(true)
668 .truncate(true)
669 .open(path!("/foo.txt"))
670 .expect("failed to open + truncate `foo.txt`");
671
672 assert!(
673 matches!(file.seek(io::SeekFrom::Current(0)).await, Ok(0)),
674 "checking the current position is 0",
675 );
676 assert!(
677 matches!(file.seek(io::SeekFrom::End(0)).await, Ok(0)),
678 "checking the size is 0",
679 );
680 }
681
682 #[tokio::test]
683 async fn test_append() {
684 let fs = FileSystem::default();
685
686 let mut file = fs
687 .new_open_options()
688 .write(true)
689 .create_new(true)
690 .open(path!("/foo.txt"))
691 .expect("failed to create a new file");
692
693 assert!(
694 matches!(file.write(b"foobar").await, Ok(6)),
695 "writing `foobar` at the end of the file",
696 );
697
698 assert!(
699 matches!(file.seek(io::SeekFrom::Current(0)).await, Ok(6)),
700 "checking the current position is 6",
701 );
702 assert!(
703 matches!(file.seek(io::SeekFrom::End(0)).await, Ok(6)),
704 "checking the size is 6",
705 );
706
707 let mut file = fs
708 .new_open_options()
709 .append(true)
710 .open(path!("/foo.txt"))
711 .expect("failed to open `foo.txt`");
712
713 assert!(
714 matches!(file.seek(io::SeekFrom::Current(0)).await, Ok(0)),
715 "checking the current position in append-mode is 0",
716 );
717 assert!(
718 matches!(file.seek(io::SeekFrom::Start(0)).await, Ok(0)),
719 "trying to rewind in append-mode",
720 );
721 assert!(matches!(file.write(b"baz").await, Ok(3)), "writing `baz`");
722
723 let mut file = fs
724 .new_open_options()
725 .read(true)
726 .open(path!("/foo.txt"))
727 .expect("failed to open `foo.txt");
728
729 assert!(
730 matches!(file.seek(io::SeekFrom::Current(0)).await, Ok(0)),
731 "checking the current position is read-mode is 0",
732 );
733
734 let mut string = String::new();
735 assert!(
736 matches!(file.read_to_string(&mut string).await, Ok(9)),
737 "reading the entire `foo.txt` file",
738 );
739 assert_eq!(
740 string, "foobarbaz",
741 "checking append-mode is ignoring seek operations",
742 );
743 }
744
745 #[tokio::test]
746 async fn test_opening_a_file_that_already_exists() {
747 let fs = FileSystem::default();
748
749 assert!(
750 fs.new_open_options()
751 .write(true)
752 .create_new(true)
753 .open(path!("/foo.txt"))
754 .is_ok(),
755 "creating a _new_ file",
756 );
757
758 assert!(
759 matches!(
760 fs.new_open_options()
761 .create_new(true)
762 .open(path!("/foo.txt")),
763 Err(FsError::AlreadyExists),
764 ),
765 "creating a _new_ file that already exists",
766 );
767
768 assert!(
769 fs.new_open_options()
770 .read(true)
771 .open(path!("/foo.txt"))
772 .is_ok(),
773 "opening a file that already exists",
774 );
775 }
776}