1use futures::future::BoxFuture;
6use shared_buffer::OwnedBuffer;
7use tokio::io::AsyncRead;
8use tokio::io::{AsyncSeek, AsyncWrite};
9
10use self::offloaded_file::OffloadWrite;
11
12use super::*;
13use crate::limiter::TrackedVec;
14use crate::{CopyOnWriteFile, FsError, Result, VirtualFile};
15use std::cmp;
16use std::convert::TryInto;
17use std::fmt;
18use std::io;
19use std::pin::Pin;
20use std::task::{Context, Poll};
21
22pub(super) struct FileHandle {
30 inode: Inode,
31 filesystem: FileSystem,
32 lifecycle: Arc<FileLifecycle>,
33 readable: bool,
34 writable: bool,
35 append_mode: bool,
36 cursor: u64,
37 arc_file: Option<Result<Box<dyn VirtualFile + Send + Sync + 'static>>>,
38}
39
40impl Clone for FileHandle {
41 fn clone(&self) -> Self {
42 self.lifecycle.opened();
43 Self {
44 inode: self.inode,
45 filesystem: self.filesystem.clone(),
46 lifecycle: self.lifecycle.clone(),
47 readable: self.readable,
48 writable: self.writable,
49 append_mode: self.append_mode,
50 cursor: self.cursor,
51 arc_file: None,
52 }
53 }
54}
55
56impl FileHandle {
57 pub(super) fn new_opened(
58 inode: Inode,
59 filesystem: FileSystem,
60 lifecycle: Arc<FileLifecycle>,
61 readable: bool,
62 writable: bool,
63 append_mode: bool,
64 cursor: u64,
65 ) -> Self {
66 Self {
67 inode,
68 filesystem,
69 lifecycle,
70 readable,
71 writable,
72 append_mode,
73 cursor,
74 arc_file: None,
75 }
76 }
77
78 fn lazy_load_arc_file_mut(&mut self) -> Result<&mut dyn VirtualFile> {
79 if self.arc_file.is_none() {
80 let fs = match self.filesystem.inner.read() {
81 Ok(fs) => fs,
82 _ => return Err(FsError::EntryNotFound),
83 };
84
85 let inode = fs.storage.get(self.inode);
86 match inode {
87 Some(Node::ArcFile(node)) => {
88 self.arc_file.replace(
89 node.fs
90 .new_open_options()
91 .read(self.readable)
92 .write(self.writable)
93 .append(self.append_mode)
94 .open(node.path.as_path()),
95 );
96 }
97 _ => return Err(FsError::EntryNotFound),
98 }
99 }
100 Ok(self
101 .arc_file
102 .as_mut()
103 .unwrap()
104 .as_mut()
105 .map_err(|err| *err)?
106 .as_mut())
107 }
108
109 fn write_cursor(&self, file_size: u64) -> u64 {
110 if self.append_mode {
111 file_size
112 } else {
113 self.cursor
114 }
115 }
116}
117
118impl VirtualFile for FileHandle {
119 fn last_accessed(&self) -> u64 {
120 let fs = match self.filesystem.inner.read() {
121 Ok(fs) => fs,
122 _ => return 0,
123 };
124
125 let inode = fs.storage.get(self.inode);
126 match inode {
127 Some(node) => node.metadata().accessed,
128 _ => 0,
129 }
130 }
131
132 fn last_modified(&self) -> u64 {
133 let fs = match self.filesystem.inner.read() {
134 Ok(fs) => fs,
135 _ => return 0,
136 };
137
138 let inode = fs.storage.get(self.inode);
139 match inode {
140 Some(node) => node.metadata().modified,
141 _ => 0,
142 }
143 }
144
145 fn created_time(&self) -> u64 {
146 let fs = match self.filesystem.inner.read() {
147 Ok(fs) => fs,
148 _ => return 0,
149 };
150
151 let inode = fs.storage.get(self.inode);
152 let node = match inode {
153 Some(node) => node,
154 _ => return 0,
155 };
156
157 node.metadata().created
158 }
159
160 fn set_times(&mut self, atime: Option<u64>, mtime: Option<u64>) -> crate::Result<()> {
161 let mut fs = match self.filesystem.inner.write() {
162 Ok(fs) => fs,
163 _ => return Err(crate::FsError::Lock),
164 };
165
166 let inode = fs.storage.get_mut(self.inode);
167 if let Some(node) = inode {
168 if let Some(atime) = atime {
169 node.metadata_mut().accessed = atime;
170 }
171 if let Some(mtime) = mtime {
172 node.metadata_mut().modified = mtime;
173 }
174
175 return Ok(());
176 }
177
178 Err(crate::FsError::UnknownError)
179 }
180
181 fn size(&self) -> u64 {
182 let fs = match self.filesystem.inner.read() {
183 Ok(fs) => fs,
184 _ => return 0,
185 };
186
187 let inode = fs.storage.get(self.inode);
188 match inode {
189 Some(Node::File(node)) => node.file.len().try_into().unwrap_or(0),
190 Some(Node::OffloadedFile(node)) => node.file.len(),
191 Some(Node::ReadOnlyFile(node)) => node.file.len().try_into().unwrap_or(0),
192 Some(Node::CustomFile(node)) => {
193 let file = node.file.lock().unwrap();
194 file.size()
195 }
196 Some(Node::ArcFile(node)) => match self.arc_file.as_ref() {
197 Some(file) => file.as_ref().map(|file| file.size()).unwrap_or(0),
198 None => node
199 .fs
200 .new_open_options()
201 .read(self.readable)
202 .write(self.writable)
203 .append(self.append_mode)
204 .open(node.path.as_path())
205 .map(|file| file.size())
206 .unwrap_or(0),
207 },
208 _ => 0,
209 }
210 }
211
212 fn set_len(&mut self, new_size: u64) -> Result<()> {
213 let mut fs = self.filesystem.inner.write().map_err(|_| FsError::Lock)?;
214
215 let inode = fs.storage.get_mut(self.inode);
216 match inode {
217 Some(Node::File(FileNode { file, metadata, .. })) => {
218 file.buffer
219 .resize(new_size.try_into().map_err(|_| FsError::UnknownError)?, 0)?;
220 metadata.len = new_size;
221 }
222 Some(Node::OffloadedFile(OffloadedFileNode { file, metadata, .. })) => {
223 file.resize(new_size, 0);
224 metadata.len = new_size;
225 }
226 Some(Node::CustomFile(node)) => {
227 let mut file = node.file.lock().unwrap();
228 file.set_len(new_size)?;
229 node.metadata.len = new_size;
230 }
231 Some(Node::ReadOnlyFile { .. }) => return Err(FsError::PermissionDenied),
232 Some(Node::ArcFile { .. }) => {
233 drop(fs);
234 let file = self.lazy_load_arc_file_mut()?;
235 file.set_len(new_size)?;
236 }
237 _ => return Err(FsError::NotAFile),
238 }
239
240 self.cursor = self.cursor.min(new_size);
242 Ok(())
243 }
244
245 fn unlink(&mut self) -> Result<()> {
246 let filesystem = self.filesystem.clone();
247 let inode = self.inode;
248
249 let (inode_of_parent, position, inode_of_file) = {
250 let fs = filesystem.inner.read().map_err(|_| FsError::Lock)?;
252
253 let inode_of_file = inode;
255
256 let (position, inode_of_parent) = fs
259 .storage
260 .iter()
261 .find_map(|(inode_of_parent, node)| match node {
262 Node::Directory(DirectoryNode { children, .. }) => {
263 children.iter().enumerate().find_map(|(nth, inode)| {
264 if inode == &inode_of_file {
265 Some((nth, inode_of_parent))
266 } else {
267 None
268 }
269 })
270 }
271
272 _ => None,
273 })
274 .ok_or(FsError::BaseNotDirectory)?;
275
276 (inode_of_parent, position, inode_of_file)
277 };
278
279 {
280 let mut fs = filesystem.inner.write().map_err(|_| FsError::Lock)?;
282 fs.unlink_file_inode(inode_of_parent, position, inode_of_file)?;
283 }
284
285 Ok(())
286 }
287
288 fn get_special_fd(&self) -> Option<u32> {
289 let fs = match self.filesystem.inner.read() {
290 Ok(a) => a,
291 Err(_) => {
292 return None;
293 }
294 };
295
296 let inode = fs.storage.get(self.inode);
297 match inode {
298 Some(Node::CustomFile(node)) => {
299 let file = node.file.lock().unwrap();
300 file.get_special_fd()
301 }
302 Some(Node::ArcFile(node)) => match self.arc_file.as_ref() {
303 Some(file) => file
304 .as_ref()
305 .map(|file| file.get_special_fd())
306 .unwrap_or(None),
307 None => node
308 .fs
309 .new_open_options()
310 .read(self.readable)
311 .write(self.writable)
312 .append(self.append_mode)
313 .open(node.path.as_path())
314 .map(|file| file.get_special_fd())
315 .unwrap_or(None),
316 },
317 _ => None,
318 }
319 }
320
321 fn copy_reference(
322 &mut self,
323 src: Box<dyn VirtualFile + Send + Sync + 'static>,
324 ) -> BoxFuture<'_, std::io::Result<()>> {
325 let inner = self.filesystem.inner.clone();
326 Box::pin(async move {
327 let mut fs = inner.write().unwrap();
328 let inode = fs.storage.get_mut(self.inode);
329 match inode {
330 Some(inode) => {
331 let metadata = Metadata {
332 ft: crate::FileType {
333 file: true,
334 ..Default::default()
335 },
336 accessed: src.last_accessed(),
337 created: src.created_time(),
338 modified: src.last_modified(),
339 len: src.size(),
340 };
341
342 *inode = Node::CustomFile(CustomFileNode {
343 inode: inode.inode(),
344 name: inode.name().to_string_lossy().to_string().into(),
345 file: Mutex::new(Box::new(CopyOnWriteFile::new(src))),
346 metadata,
347 lifecycle: inode.file_lifecycle().cloned().unwrap_or_else(Arc::default),
348 });
349 Ok(())
350 }
351 None => Err(std::io::ErrorKind::InvalidInput.into()),
352 }
353 })
354 }
355
356 fn copy_from_owned_buffer(&mut self, src: &OwnedBuffer) -> BoxFuture<'_, std::io::Result<()>> {
357 let inner = self.filesystem.inner.clone();
358 let src = src.clone();
359 Box::pin(async move {
360 let mut fs = inner.write().unwrap();
361 let inode = fs.storage.get_mut(self.inode);
362 match inode {
363 Some(inode) => {
364 let metadata = Metadata {
365 ft: crate::FileType {
366 file: true,
367 ..Default::default()
368 },
369 accessed: 1,
370 created: 1,
371 modified: 1,
372 len: src.len() as u64,
373 };
374
375 *inode = Node::ReadOnlyFile(ReadOnlyFileNode {
376 inode: inode.inode(),
377 name: inode.name().to_string_lossy().to_string().into(),
378 file: ReadOnlyFile { buffer: src },
379 metadata,
380 lifecycle: inode.file_lifecycle().cloned().unwrap_or_else(Arc::default),
381 });
382 Ok(())
383 }
384 None => Err(std::io::ErrorKind::InvalidInput.into()),
385 }
386 })
387 }
388
389 fn poll_read_ready(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<usize>> {
390 if !self.readable {
391 return Poll::Ready(Err(io::Error::new(
392 io::ErrorKind::PermissionDenied,
393 format!(
394 "the file (inode `{}) doesn't have the `read` permission",
395 self.inode
396 ),
397 )));
398 }
399
400 let mut fs = self
401 .filesystem
402 .inner
403 .write()
404 .map_err(|_| io::Error::other("failed to acquire a write lock"))?;
405
406 let inode = fs.storage.get_mut(self.inode);
407 match inode {
408 Some(Node::File(node)) => {
409 let remaining = node.file.buffer.len() - (self.cursor as usize);
410 Poll::Ready(Ok(remaining))
411 }
412 Some(Node::OffloadedFile(node)) => {
413 let remaining = node.file.len() as usize - (self.cursor as usize);
414 Poll::Ready(Ok(remaining))
415 }
416 Some(Node::ReadOnlyFile(node)) => {
417 let remaining = node.file.buffer.len() - (self.cursor as usize);
418 Poll::Ready(Ok(remaining))
419 }
420 Some(Node::CustomFile(node)) => {
421 let mut file = node.file.lock().unwrap();
422 let file = Pin::new(file.as_mut());
423 file.poll_read_ready(cx)
424 }
425 Some(Node::ArcFile(_)) => {
426 drop(fs);
427 match self.lazy_load_arc_file_mut() {
428 Ok(file) => {
429 let file = Pin::new(file);
430 file.poll_read_ready(cx)
431 }
432 Err(_) => Poll::Ready(Err(io::Error::new(
433 io::ErrorKind::NotFound,
434 format!("inode `{}` doesn't match a file", self.inode),
435 ))),
436 }
437 }
438 _ => Poll::Ready(Err(io::Error::new(
439 io::ErrorKind::NotFound,
440 format!("inode `{}` doesn't match a file", self.inode),
441 ))),
442 }
443 }
444
445 fn poll_write_ready(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<usize>> {
446 if !self.writable {
447 return Poll::Ready(Err(io::Error::new(
448 io::ErrorKind::PermissionDenied,
449 format!(
450 "the file (inode `{}) doesn't have the `write` permission",
451 self.inode
452 ),
453 )));
454 }
455
456 let mut fs = self
457 .filesystem
458 .inner
459 .write()
460 .map_err(|_| io::Error::other("failed to acquire a write lock"))?;
461
462 let inode = fs.storage.get_mut(self.inode);
463 match inode {
464 Some(Node::File(_)) => Poll::Ready(Ok(8192)),
465 Some(Node::OffloadedFile(_)) => Poll::Ready(Ok(8192)),
466 Some(Node::ReadOnlyFile(_)) => Poll::Ready(Ok(0)),
467 Some(Node::CustomFile(node)) => {
468 let mut file = node.file.lock().unwrap();
469 let file = Pin::new(file.as_mut());
470 file.poll_write_ready(cx)
471 }
472 Some(Node::ArcFile(_)) => {
473 drop(fs);
474 match self.lazy_load_arc_file_mut() {
475 Ok(file) => {
476 let file = Pin::new(file);
477 file.poll_write_ready(cx)
478 }
479 Err(_) => Poll::Ready(Err(io::Error::new(
480 io::ErrorKind::NotFound,
481 format!("inode `{}` doesn't match a file", self.inode),
482 ))),
483 }
484 }
485 _ => Poll::Ready(Err(io::Error::new(
486 io::ErrorKind::NotFound,
487 format!("inode `{}` doesn't match a file", self.inode),
488 ))),
489 }
490 }
491
492 fn write_from_mmap(&mut self, offset: u64, size: u64) -> std::io::Result<()> {
493 if !self.writable {
494 return Err(io::Error::new(
495 io::ErrorKind::PermissionDenied,
496 format!(
497 "the file (inode `{}) doesn't have the `write` permission",
498 self.inode
499 ),
500 ));
501 }
502
503 let mut cursor = self.cursor;
504 {
505 let mut fs = self
506 .filesystem
507 .inner
508 .write()
509 .map_err(|_| io::Error::other("failed to acquire a write lock"))?;
510
511 let inode = fs.storage.get_mut(self.inode);
512 match inode {
513 Some(Node::OffloadedFile(node)) => {
514 node.file
515 .write(OffloadWrite::MmapOffset { offset, size }, &mut cursor)?;
516 node.metadata.len = node.file.len();
517 }
518 _ => {
519 return Err(io::ErrorKind::Unsupported.into());
520 }
521 }
522 }
523 self.cursor = cursor;
524 Ok(())
525 }
526
527 fn as_owned_buffer(&self) -> Option<OwnedBuffer> {
528 let fs = self.filesystem.inner.read().ok()?;
529
530 let inode = fs.storage.get(self.inode)?;
531 match inode {
532 Node::ReadOnlyFile(f) => Some(f.file.buffer.clone()),
533 Node::CustomFile(f) => f.file.lock().ok()?.as_owned_buffer(),
534 _ => None,
535 }
536 }
537}
538
539#[cfg(test)]
540mod test_virtual_file {
541 use crate::{BufferFile, FileSystem as FS, FsError, mem_fs::*};
542 use std::io;
543 use std::pin::Pin;
544 use std::task::{Context, Poll, Waker};
545 use std::thread::sleep;
546 use std::time::Duration;
547 use tokio::io::{AsyncReadExt, AsyncSeekExt, AsyncWriteExt};
548
549 macro_rules! path {
550 ($path:expr) => {
551 std::path::Path::new($path)
552 };
553 }
554
555 #[tokio::test]
556 async fn test_last_accessed() {
557 let fs = FileSystem::default();
558
559 let file = fs
560 .new_open_options()
561 .write(true)
562 .create_new(true)
563 .open(path!("/foo.txt"))
564 .expect("failed to create a new file");
565 let last_accessed_time = file.last_accessed();
566
567 assert!(last_accessed_time > 0, "last accessed time is not zero");
568
569 sleep(Duration::from_secs(3));
570
571 let file = fs
572 .new_open_options()
573 .read(true)
574 .open(path!("/foo.txt"))
575 .expect("failed to open a file");
576 let next_last_accessed_time = file.last_accessed();
577
578 assert!(
579 next_last_accessed_time > last_accessed_time,
580 "the last accessed time is updated"
581 );
582 }
583
584 #[tokio::test]
585 async fn test_last_modified() {
586 let fs = FileSystem::default();
587
588 let file = fs
589 .new_open_options()
590 .write(true)
591 .create_new(true)
592 .open(path!("/foo.txt"))
593 .expect("failed to create a new file");
594
595 assert!(file.last_modified() > 0, "last modified time is not zero");
596 }
597
598 #[tokio::test]
599 async fn test_created_time() {
600 let fs = FileSystem::default();
601
602 let file = fs
603 .new_open_options()
604 .write(true)
605 .create_new(true)
606 .open(path!("/foo.txt"))
607 .expect("failed to create a new file");
608 let created_time = file.created_time();
609
610 assert!(created_time > 0, "created time is not zero");
611
612 let file = fs
613 .new_open_options()
614 .read(true)
615 .open(path!("/foo.txt"))
616 .expect("failed to create a new file");
617 let next_created_time = file.created_time();
618
619 assert_eq!(
620 next_created_time, created_time,
621 "created time stays constant"
622 );
623 }
624
625 #[tokio::test]
626 async fn test_size() {
627 let fs = FileSystem::default();
628
629 let file = fs
630 .new_open_options()
631 .write(true)
632 .create_new(true)
633 .open(path!("/foo.txt"))
634 .expect("failed to create a new file");
635
636 assert_eq!(file.size(), 0, "new file is empty");
637 }
638
639 #[tokio::test]
640 async fn test_set_len() {
641 let fs = FileSystem::default();
642
643 let mut file = fs
644 .new_open_options()
645 .write(true)
646 .create_new(true)
647 .open(path!("/foo.txt"))
648 .expect("failed to create a new file");
649
650 assert!(matches!(file.set_len(7), Ok(())), "setting a new length");
651 assert_eq!(file.size(), 7, "file has a new length");
652 }
653
654 #[tokio::test]
655 async fn test_unlink() {
656 let fs = FileSystem::default();
657
658 let mut file = fs
659 .new_open_options()
660 .read(true)
661 .write(true)
662 .create_new(true)
663 .open(path!("/foo.txt"))
664 .expect("failed to create a new file");
665
666 file.write_all(b"foo").await.expect("write before unlink");
667
668 {
669 let fs_inner = fs.inner.read().unwrap();
670
671 assert_eq!(fs_inner.storage.len(), 2, "storage has the new file");
672 assert!(
673 matches!(
674 fs_inner.storage.get(ROOT_INODE),
675 Some(Node::Directory(DirectoryNode {
676 inode: ROOT_INODE,
677 name,
678 children,
679 ..
680 })) if name == "/" && children == &[1]
681 ),
682 "`/` contains `foo.txt`",
683 );
684 assert!(
685 matches!(
686 fs_inner.storage.get(1),
687 Some(Node::File(FileNode {
688 inode: 1,
689 name,
690 ..
691 })) if name == "foo.txt"
692 ),
693 "`foo.txt` exists and is a file",
694 );
695 }
696
697 assert_eq!(file.unlink(), Ok(()), "unlinking the file");
698 assert!(
699 matches!(
700 fs.new_open_options().read(true).open(path!("/foo.txt")),
701 Err(FsError::EntryNotFound)
702 ),
703 "the path disappears immediately after unlink",
704 );
705
706 file.write_all(b"bar")
707 .await
708 .expect("write after unlink should still work");
709 file.seek(io::SeekFrom::Start(0))
710 .await
711 .expect("rewind after unlink");
712
713 let mut contents = String::new();
714 file.read_to_string(&mut contents)
715 .await
716 .expect("read after unlink should still work");
717 assert_eq!(contents, "foobar");
718
719 {
720 let fs_inner = fs.inner.read().unwrap();
721
722 assert_eq!(
723 fs_inner.storage.len(),
724 2,
725 "storage keeps the unlinked file alive while a handle is open"
726 );
727 assert!(
728 matches!(
729 fs_inner.storage.get(ROOT_INODE),
730 Some(Node::Directory(DirectoryNode {
731 inode: ROOT_INODE,
732 name,
733 children,
734 ..
735 })) if name == "/" && children.is_empty()
736 ),
737 "`/` is empty",
738 );
739 }
740
741 drop(file);
742
743 let fs_inner = fs.inner.read().unwrap();
744 assert_eq!(
745 fs_inner.storage.len(),
746 1,
747 "storage drops the unlinked file after the last handle closes"
748 );
749 }
750
751 #[tokio::test]
752 async fn test_unlink_with_multiple_handles() {
753 let fs = FileSystem::default();
754
755 let mut first = fs
756 .new_open_options()
757 .read(true)
758 .write(true)
759 .create_new(true)
760 .open(path!("/foo.txt"))
761 .expect("failed to create a new file");
762 first.write_all(b"foo").await.expect("seed contents");
763
764 let mut second = fs
765 .new_open_options()
766 .read(true)
767 .write(true)
768 .open(path!("/foo.txt"))
769 .expect("failed to open the same file a second time");
770
771 assert_eq!(first.unlink(), Ok(()), "unlinking through the first handle");
772
773 second
774 .seek(io::SeekFrom::End(0))
775 .await
776 .expect("seek to end after unlink");
777 second
778 .write_all(b"bar")
779 .await
780 .expect("second handle stays writable after unlink");
781
782 drop(first);
783
784 {
785 let fs_inner = fs.inner.read().unwrap();
786 assert_eq!(
787 fs_inner.storage.len(),
788 2,
789 "the inode stays alive until the last open handle is dropped"
790 );
791 }
792
793 second
794 .seek(io::SeekFrom::Start(0))
795 .await
796 .expect("rewind second handle");
797 let mut contents = String::new();
798 second
799 .read_to_string(&mut contents)
800 .await
801 .expect("read through surviving handle");
802 assert_eq!(contents, "foobar");
803
804 drop(second);
805
806 let fs_inner = fs.inner.read().unwrap();
807 assert_eq!(
808 fs_inner.storage.len(),
809 1,
810 "the inode is reclaimed after the last handle closes"
811 );
812 }
813
814 #[tokio::test]
815 async fn test_poll_write_ready_for_write_only_file() {
816 let fs = FileSystem::default();
817 let mut file = fs
818 .new_open_options()
819 .write(true)
820 .create_new(true)
821 .open(path!("/foo.txt"))
822 .expect("failed to create a new file");
823 let waker = Waker::noop();
824 let mut cx = Context::from_waker(waker);
825
826 assert!(matches!(
827 Pin::new(file.as_mut()).poll_write_ready(&mut cx),
828 Poll::Ready(Ok(8192))
829 ));
830 }
831
832 #[tokio::test]
833 async fn test_poll_write_ready_for_custom_device_file() {
834 let fs = FileSystem::default();
835 fs.insert_device_file(path!("/dev").to_path_buf(), Box::<BufferFile>::default())
836 .expect("failed to insert a device file");
837
838 let mut file = fs
839 .new_open_options()
840 .write(true)
841 .open(path!("/dev"))
842 .expect("failed to open device file");
843 let waker = Waker::noop();
844 let mut cx = Context::from_waker(waker);
845
846 assert!(matches!(
847 Pin::new(file.as_mut()).poll_write_ready(&mut cx),
848 Poll::Ready(Ok(8192))
849 ));
850 }
851}
852
853impl AsyncRead for FileHandle {
854 fn poll_read(
855 mut self: Pin<&mut Self>,
856 cx: &mut Context<'_>,
857 buf: &mut tokio::io::ReadBuf<'_>,
858 ) -> Poll<io::Result<()>> {
859 if !self.readable {
860 return Poll::Ready(Err(io::Error::new(
861 io::ErrorKind::PermissionDenied,
862 format!(
863 "the file (inode `{}) doesn't have the `read` permission",
864 self.inode
865 ),
866 )));
867 }
868
869 let mut cursor = self.cursor;
870 let ret = {
871 let mut fs = self
872 .filesystem
873 .inner
874 .write()
875 .map_err(|_| io::Error::other("failed to acquire a write lock"))?;
876
877 let inode = fs.storage.get_mut(self.inode);
878 match inode {
879 Some(Node::File(node)) => {
880 let read = unsafe {
881 node.file.read(
882 std::mem::transmute::<&mut [std::mem::MaybeUninit<u8>], &mut [u8]>(
883 buf.unfilled_mut(),
884 ),
885 &mut cursor,
886 )
887 };
888 if let Ok(read) = &read {
889 unsafe { buf.assume_init(*read) };
890 buf.advance(*read);
891 }
892 Poll::Ready(read.map(|_| ()))
893 }
894 Some(Node::OffloadedFile(node)) => {
895 let read = unsafe {
896 node.file.read(
897 std::mem::transmute::<&mut [std::mem::MaybeUninit<u8>], &mut [u8]>(
898 buf.unfilled_mut(),
899 ),
900 &mut cursor,
901 )
902 };
903 if let Ok(read) = &read {
904 unsafe { buf.assume_init(*read) };
905 buf.advance(*read);
906 }
907 Poll::Ready(read.map(|_| ()))
908 }
909 Some(Node::ReadOnlyFile(node)) => {
910 let read = unsafe {
911 node.file.read(
912 std::mem::transmute::<&mut [std::mem::MaybeUninit<u8>], &mut [u8]>(
913 buf.unfilled_mut(),
914 ),
915 &mut cursor,
916 )
917 };
918 if let Ok(read) = &read {
919 unsafe { buf.assume_init(*read) };
920 buf.advance(*read);
921 }
922 Poll::Ready(read.map(|_| ()))
923 }
924 Some(Node::CustomFile(node)) => {
925 let mut file = node.file.lock().unwrap();
926 let file = Pin::new(file.as_mut());
927 file.poll_read(cx, buf)
928 }
929 Some(Node::ArcFile(_)) => {
930 drop(fs);
931 match self.lazy_load_arc_file_mut() {
932 Ok(file) => {
933 let file = Pin::new(file);
934 file.poll_read(cx, buf)
935 }
936 Err(_) => {
937 return Poll::Ready(Err(io::Error::new(
938 io::ErrorKind::NotFound,
939 format!("inode `{}` doesn't match a file", self.inode),
940 )));
941 }
942 }
943 }
944 _ => {
945 return Poll::Ready(Err(io::Error::new(
946 io::ErrorKind::NotFound,
947 format!("inode `{}` doesn't match a file", self.inode),
948 )));
949 }
950 }
951 };
952 self.cursor = cursor;
953 ret
954 }
955}
956
957impl AsyncSeek for FileHandle {
958 fn start_seek(mut self: Pin<&mut Self>, position: io::SeekFrom) -> io::Result<()> {
959 let mut cursor = self.cursor;
960 let ret = {
961 let mut fs = self
962 .filesystem
963 .inner
964 .write()
965 .map_err(|_| io::Error::other("failed to acquire a write lock"))?;
966
967 let inode = fs.storage.get_mut(self.inode);
968 match inode {
969 Some(Node::File(node)) => {
970 node.file.seek(position, &mut cursor)?;
971 Ok(())
972 }
973 Some(Node::OffloadedFile(node)) => {
974 node.file.seek(position, &mut cursor)?;
975 Ok(())
976 }
977 Some(Node::ReadOnlyFile(node)) => {
978 node.file.seek(position, &mut cursor)?;
979 Ok(())
980 }
981 Some(Node::CustomFile(node)) => {
982 let mut file = node.file.lock().unwrap();
983 let file = Pin::new(file.as_mut());
984 file.start_seek(position)
985 }
986 Some(Node::ArcFile(_)) => {
987 drop(fs);
988 match self.lazy_load_arc_file_mut() {
989 Ok(file) => {
990 let file = Pin::new(file);
991 file.start_seek(position)
992 }
993 Err(_) => {
994 return Err(io::Error::new(
995 io::ErrorKind::NotFound,
996 format!("inode `{}` doesn't match a file", self.inode),
997 ));
998 }
999 }
1000 }
1001 _ => {
1002 return Err(io::Error::new(
1003 io::ErrorKind::NotFound,
1004 format!("inode `{}` doesn't match a file", self.inode),
1005 ));
1006 }
1007 }
1008 };
1009 self.cursor = cursor;
1010 ret
1011 }
1012
1013 fn poll_complete(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<u64>> {
1014 let mut fs = self
1015 .filesystem
1016 .inner
1017 .write()
1018 .map_err(|_| io::Error::other("failed to acquire a write lock"))?;
1019
1020 let inode = fs.storage.get_mut(self.inode);
1021 match inode {
1022 Some(Node::File { .. }) => Poll::Ready(Ok(self.cursor)),
1023 Some(Node::OffloadedFile { .. }) => Poll::Ready(Ok(self.cursor)),
1024 Some(Node::ReadOnlyFile { .. }) => Poll::Ready(Ok(self.cursor)),
1025 Some(Node::CustomFile(node)) => {
1026 let mut file = node.file.lock().unwrap();
1027 let file = Pin::new(file.as_mut());
1028 file.poll_complete(cx)
1029 }
1030 Some(Node::ArcFile { .. }) => {
1031 drop(fs);
1032 match self.lazy_load_arc_file_mut() {
1033 Ok(file) => {
1034 let file = Pin::new(file);
1035 file.poll_complete(cx)
1036 }
1037 Err(_) => Poll::Ready(Err(io::Error::new(
1038 io::ErrorKind::NotFound,
1039 format!("inode `{}` doesn't match a file", self.inode),
1040 ))),
1041 }
1042 }
1043 _ => Poll::Ready(Err(io::Error::new(
1044 io::ErrorKind::NotFound,
1045 format!("inode `{}` doesn't match a file", self.inode),
1046 ))),
1047 }
1048 }
1049}
1050
1051impl AsyncWrite for FileHandle {
1052 fn poll_write(
1053 mut self: Pin<&mut Self>,
1054 cx: &mut Context<'_>,
1055 buf: &[u8],
1056 ) -> Poll<io::Result<usize>> {
1057 if !self.writable {
1058 return Poll::Ready(Err(io::Error::new(
1059 io::ErrorKind::PermissionDenied,
1060 format!(
1061 "the file (inode `{}) doesn't have the `write` permission",
1062 self.inode
1063 ),
1064 )));
1065 }
1066
1067 let mut cursor;
1068 let bytes_written = {
1069 let mut fs = self
1070 .filesystem
1071 .inner
1072 .write()
1073 .map_err(|_| io::Error::other("failed to acquire a write lock"))?;
1074
1075 let inode = fs.storage.get_mut(self.inode);
1076 match inode {
1077 Some(Node::File(node)) => {
1078 cursor = self.write_cursor(node.file.len() as u64);
1079 let bytes_written = node.file.write(buf, &mut cursor)?;
1080 node.metadata.len = node.file.len().try_into().unwrap();
1081 bytes_written
1082 }
1083 Some(Node::OffloadedFile(node)) => {
1084 cursor = self.write_cursor(node.file.len());
1085 let bytes_written = node.file.write(OffloadWrite::Buffer(buf), &mut cursor)?;
1086 node.metadata.len = node.file.len();
1087 bytes_written
1088 }
1089 Some(Node::ReadOnlyFile(node)) => {
1090 cursor = self.write_cursor(node.file.len() as u64);
1091 let bytes_written = node.file.write(buf, &mut cursor)?;
1092 node.metadata.len = node.file.len().try_into().unwrap();
1093 bytes_written
1094 }
1095 Some(Node::CustomFile(node)) => {
1096 let mut guard = node.file.lock().unwrap();
1097 cursor = self.write_cursor(guard.size());
1098
1099 let file = Pin::new(guard.as_mut());
1100 if let Err(err) = file.start_seek(io::SeekFrom::Start(cursor)) {
1101 return Poll::Ready(Err(err));
1102 }
1103
1104 let file = Pin::new(guard.as_mut());
1105 let _ = file.poll_complete(cx);
1106
1107 let file = Pin::new(guard.as_mut());
1108 let bytes_written = match file.poll_write(cx, buf) {
1109 Poll::Ready(Ok(a)) => a,
1110 Poll::Ready(Err(err)) => return Poll::Ready(Err(err)),
1111 Poll::Pending => return Poll::Pending,
1112 };
1113 cursor += bytes_written as u64;
1114 node.metadata.len = guard.size();
1115 bytes_written
1116 }
1117 Some(Node::ArcFile(_)) => {
1118 drop(fs);
1119 match self.lazy_load_arc_file_mut() {
1120 Ok(file) => {
1121 let file = Pin::new(file);
1122 return file.poll_write(cx, buf);
1123 }
1124 Err(_) => {
1125 return Poll::Ready(Err(io::Error::new(
1126 io::ErrorKind::NotFound,
1127 format!("inode `{}` doesn't match a file", self.inode),
1128 )));
1129 }
1130 }
1131 }
1132 _ => {
1133 return Poll::Ready(Err(io::Error::new(
1134 io::ErrorKind::NotFound,
1135 format!("inode `{}` doesn't match a file", self.inode),
1136 )));
1137 }
1138 }
1139 };
1140 self.cursor = cursor;
1141 Poll::Ready(Ok(bytes_written))
1142 }
1143
1144 fn poll_write_vectored(
1145 mut self: Pin<&mut Self>,
1146 cx: &mut Context<'_>,
1147 bufs: &[io::IoSlice<'_>],
1148 ) -> Poll<io::Result<usize>> {
1149 let mut cursor = self.cursor;
1150 let ret = {
1151 let mut fs = self
1152 .filesystem
1153 .inner
1154 .write()
1155 .map_err(|_| io::Error::other("failed to acquire a write lock"))?;
1156
1157 let inode = fs.storage.get_mut(self.inode);
1158 match inode {
1159 Some(Node::File(node)) => {
1160 cursor = self.write_cursor(node.file.len() as u64);
1161 let buf = bufs
1162 .iter()
1163 .find(|b| !b.is_empty())
1164 .map_or(&[][..], |b| &**b);
1165 let bytes_written = node.file.write(buf, &mut cursor)?;
1166 node.metadata.len = node.file.buffer.len() as u64;
1167 Poll::Ready(Ok(bytes_written))
1168 }
1169 Some(Node::OffloadedFile(node)) => {
1170 cursor = self.write_cursor(node.file.len());
1171 let buf = bufs
1172 .iter()
1173 .find(|b| !b.is_empty())
1174 .map_or(&[][..], |b| &**b);
1175 let bytes_written = node.file.write(OffloadWrite::Buffer(buf), &mut cursor)?;
1176 node.metadata.len = node.file.len();
1177 Poll::Ready(Ok(bytes_written))
1178 }
1179 Some(Node::ReadOnlyFile(node)) => {
1180 cursor = self.write_cursor(node.file.len() as u64);
1181 let buf = bufs
1182 .iter()
1183 .find(|b| !b.is_empty())
1184 .map_or(&[][..], |b| &**b);
1185 let bytes_written = node.file.write(buf, &mut cursor)?;
1186 node.metadata.len = node.file.buffer.len() as u64;
1187 Poll::Ready(Ok(bytes_written))
1188 }
1189 Some(Node::CustomFile(node)) => {
1190 let mut file = node.file.lock().unwrap();
1191 let file = Pin::new(file.as_mut());
1192 file.poll_write_vectored(cx, bufs)
1193 }
1194 Some(Node::ArcFile(_)) => {
1195 drop(fs);
1196 match self.lazy_load_arc_file_mut() {
1197 Ok(file) => {
1198 let file = Pin::new(file);
1199 file.poll_write_vectored(cx, bufs)
1200 }
1201 Err(_) => Poll::Ready(Err(io::Error::new(
1202 io::ErrorKind::NotFound,
1203 format!("inode `{}` doesn't match a file", self.inode),
1204 ))),
1205 }
1206 }
1207 _ => Poll::Ready(Err(io::Error::new(
1208 io::ErrorKind::NotFound,
1209 format!("inode `{}` doesn't match a file", self.inode),
1210 ))),
1211 }
1212 };
1213 self.cursor = cursor;
1214 ret
1215 }
1216
1217 fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
1218 let mut fs = self
1219 .filesystem
1220 .inner
1221 .write()
1222 .map_err(|_| io::Error::other("failed to acquire a write lock"))?;
1223
1224 let inode = fs.storage.get_mut(self.inode);
1225 match inode {
1226 Some(Node::File(node)) => Poll::Ready(node.file.flush()),
1227 Some(Node::OffloadedFile(node)) => Poll::Ready(node.file.flush()),
1228 Some(Node::ReadOnlyFile(node)) => Poll::Ready(node.file.flush()),
1229 Some(Node::CustomFile(node)) => {
1230 let mut file = node.file.lock().unwrap();
1231 let file = Pin::new(file.as_mut());
1232 file.poll_flush(cx)
1233 }
1234 Some(Node::ArcFile { .. }) => {
1235 drop(fs);
1236 match self.lazy_load_arc_file_mut() {
1237 Ok(file) => {
1238 let file = Pin::new(file);
1239 file.poll_flush(cx)
1240 }
1241 Err(_) => Poll::Ready(Err(io::Error::new(
1242 io::ErrorKind::NotFound,
1243 format!("inode `{}` doesn't match a file", self.inode),
1244 ))),
1245 }
1246 }
1247 _ => Poll::Ready(Err(io::Error::new(
1248 io::ErrorKind::NotFound,
1249 format!("inode `{}` doesn't match a file", self.inode),
1250 ))),
1251 }
1252 }
1253
1254 fn poll_shutdown(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
1255 let mut fs = self
1256 .filesystem
1257 .inner
1258 .write()
1259 .map_err(|_| io::Error::other("failed to acquire a write lock"))?;
1260
1261 let inode = fs.storage.get_mut(self.inode);
1262 match inode {
1263 Some(Node::File { .. }) => Poll::Ready(Ok(())),
1264 Some(Node::OffloadedFile { .. }) => Poll::Ready(Ok(())),
1265 Some(Node::ReadOnlyFile { .. }) => Poll::Ready(Ok(())),
1266 Some(Node::CustomFile(node)) => {
1267 let mut file = node.file.lock().unwrap();
1268 let file = Pin::new(file.as_mut());
1269 file.poll_shutdown(cx)
1270 }
1271 Some(Node::ArcFile { .. }) => {
1272 drop(fs);
1273 match self.lazy_load_arc_file_mut() {
1274 Ok(file) => {
1275 let file = Pin::new(file);
1276 file.poll_shutdown(cx)
1277 }
1278 Err(_) => Poll::Ready(Err(io::Error::new(
1279 io::ErrorKind::NotFound,
1280 format!("inode `{}` doesn't match a file", self.inode),
1281 ))),
1282 }
1283 }
1284 _ => Poll::Ready(Err(io::Error::new(
1285 io::ErrorKind::NotFound,
1286 format!("inode `{}` doesn't match a file", self.inode),
1287 ))),
1288 }
1289 }
1290
1291 fn is_write_vectored(&self) -> bool {
1292 let mut fs = match self.filesystem.inner.write() {
1293 Ok(a) => a,
1294 Err(_) => return false,
1295 };
1296
1297 let inode = fs.storage.get_mut(self.inode);
1298 match inode {
1299 Some(Node::File { .. }) => false,
1300 Some(Node::OffloadedFile { .. }) => false,
1301 Some(Node::ReadOnlyFile { .. }) => false,
1302 Some(Node::CustomFile(node)) => {
1303 let file = node.file.lock().unwrap();
1304 file.is_write_vectored()
1305 }
1306 Some(Node::ArcFile { .. }) => {
1307 drop(fs);
1308 match self.arc_file.as_ref() {
1309 Some(Ok(file)) => file.is_write_vectored(),
1310 _ => false,
1311 }
1312 }
1313 _ => false,
1314 }
1315 }
1316}
1317
1318#[cfg(test)]
1319mod test_read_write_seek {
1320 use shared_buffer::OwnedBuffer;
1321 use tokio::io::{AsyncReadExt, AsyncSeekExt, AsyncWriteExt};
1322
1323 use crate::{FileSystem as FS, mem_fs::*};
1324 use std::io;
1325
1326 macro_rules! path {
1327 ($path:expr) => {
1328 std::path::Path::new($path)
1329 };
1330 }
1331
1332 #[tokio::test]
1333 async fn test_writing_at_various_positions() {
1334 let fs = FileSystem::default();
1335
1336 let mut file = fs
1337 .new_open_options()
1338 .read(true)
1339 .write(true)
1340 .create_new(true)
1341 .open(path!("/foo.txt"))
1342 .expect("failed to create a new file");
1343
1344 assert!(
1345 matches!(file.write(b"foo").await, Ok(3)),
1346 "writing `foo` at the end of the file",
1347 );
1348 assert_eq!(file.size(), 3, "checking the size of the file");
1349
1350 assert!(
1351 matches!(file.write(b"bar").await, Ok(3)),
1352 "writing `bar` at the end of the file",
1353 );
1354 assert_eq!(file.size(), 6, "checking the size of the file");
1355
1356 assert!(
1357 matches!(file.seek(io::SeekFrom::Start(0)).await, Ok(0)),
1358 "seeking to 0",
1359 );
1360
1361 assert!(
1362 matches!(file.write(b"baz").await, Ok(3)),
1363 "writing `baz` at the beginning of the file",
1364 );
1365 assert_eq!(file.size(), 6, "checking the size of the file");
1366
1367 assert!(
1368 matches!(file.write(b"qux").await, Ok(3)),
1369 "writing `qux` in the middle of the file",
1370 );
1371 assert_eq!(file.size(), 6, "checking the size of the file");
1372
1373 assert!(
1374 matches!(file.seek(io::SeekFrom::Start(0)).await, Ok(0)),
1375 "seeking to 0",
1376 );
1377
1378 let mut string = String::new();
1379 assert!(
1380 matches!(file.read_to_string(&mut string).await, Ok(6)),
1381 "reading `bazqux`",
1382 );
1383 assert_eq!(string, "bazqux");
1384
1385 assert!(
1386 matches!(file.seek(io::SeekFrom::Current(-3)).await, Ok(3)),
1387 "seeking to 3",
1388 );
1389
1390 let mut string = String::new();
1391 assert!(
1392 matches!(file.read_to_string(&mut string).await, Ok(3)),
1393 "reading `qux`",
1394 );
1395 assert_eq!(string, "qux");
1396
1397 assert!(
1398 matches!(file.seek(io::SeekFrom::End(0)).await, Ok(6)),
1399 "seeking to 6",
1400 );
1401
1402 let mut string = String::new();
1403 assert!(
1404 matches!(file.read_to_string(&mut string).await, Ok(0)),
1405 "reading ``",
1406 );
1407 assert_eq!(string, "");
1408 }
1409
1410 #[test]
1411 pub fn writing_to_middle() {
1412 fn assert_contents(file: &File, expected: &[u8]) {
1413 let mut buf = vec![0; expected.len() + 1];
1414 let mut cursor = 0;
1415 let read = file.read(buf.as_mut(), &mut cursor).unwrap();
1416 assert_eq!(read, expected.len(), "Must have the same amount of data");
1417 assert_eq!(buf[0..expected.len()], *expected);
1418 }
1419
1420 let mut file = File::new(None);
1421
1422 let mut cursor = 0;
1423
1424 file.write(b"hello, world.", &mut cursor).unwrap();
1426 assert_eq!(cursor, 13);
1427 assert_contents(&file, b"hello, world.");
1428
1429 file.write(b"goodbye!", &mut cursor).unwrap();
1431 assert_eq!(cursor, 21);
1432 assert_contents(&file, b"hello, world.goodbye!");
1433
1434 cursor = 5;
1436 file.write(b"BOOM", &mut cursor).unwrap();
1437 assert_eq!(cursor, 9);
1438 assert_contents(&file, b"helloBOOMrld.goodbye!");
1439
1440 cursor = 17;
1442 file.write(b"BANG", &mut cursor).unwrap();
1443 assert_eq!(cursor, 21);
1444 assert_contents(&file, b"helloBOOMrld.goodBANG");
1445
1446 cursor = 17;
1448 file.write(b"OUCH!", &mut cursor).unwrap();
1449 assert_eq!(cursor, 22);
1450 assert_contents(&file, b"helloBOOMrld.goodOUCH!");
1451 }
1452
1453 #[tokio::test]
1454 async fn test_reading() {
1455 let fs = FileSystem::default();
1456
1457 let mut file = fs
1458 .new_open_options()
1459 .read(true)
1460 .write(true)
1461 .create_new(true)
1462 .open(path!("/foo.txt"))
1463 .expect("failed to create a new file");
1464
1465 assert!(
1466 matches!(fs.metadata(path!("/foo.txt")), Ok(Metadata { len: 0, .. })),
1467 "checking the `metadata.len` is 0",
1468 );
1469 assert!(
1470 matches!(file.write(b"foobarbazqux").await, Ok(12)),
1471 "writing `foobarbazqux`",
1472 );
1473
1474 assert!(
1475 matches!(file.seek(io::SeekFrom::Start(0)).await, Ok(0)),
1476 "seeking to 0",
1477 );
1478
1479 let mut buffer = [0; 6];
1480 assert!(
1481 matches!(file.read(&mut buffer[..]).await, Ok(6)),
1482 "reading 6 bytes",
1483 );
1484 assert_eq!(buffer, b"foobar"[..], "checking the 6 bytes");
1485
1486 assert!(
1487 matches!(file.seek(io::SeekFrom::Start(0)).await, Ok(0)),
1488 "seeking to 0",
1489 );
1490
1491 let mut buffer = [0; 16];
1492 assert!(
1493 matches!(file.read(&mut buffer[..]).await, Ok(12)),
1494 "reading more bytes than available",
1495 );
1496 assert_eq!(buffer[..12], b"foobarbazqux"[..], "checking the 12 bytes");
1497 assert!(
1498 matches!(fs.metadata(path!("/foo.txt")), Ok(Metadata { len: 12, .. })),
1499 "checking the `metadata.len` is 0",
1500 );
1501 }
1502
1503 #[tokio::test]
1504 async fn test_reading_to_the_end() {
1505 let fs = FileSystem::default();
1506
1507 let mut file = fs
1508 .new_open_options()
1509 .read(true)
1510 .write(true)
1511 .create_new(true)
1512 .open(path!("/foo.txt"))
1513 .expect("failed to create a new file");
1514
1515 assert!(
1516 matches!(file.write(b"foobarbazqux").await, Ok(12)),
1517 "writing `foobarbazqux`",
1518 );
1519
1520 assert!(
1521 matches!(file.seek(io::SeekFrom::Start(0)).await, Ok(0)),
1522 "seeking to 0",
1523 );
1524
1525 let mut buffer = Vec::new();
1526 assert!(
1527 matches!(file.read_to_end(&mut buffer).await, Ok(12)),
1528 "reading all bytes",
1529 );
1530 assert_eq!(buffer, b"foobarbazqux"[..], "checking all the bytes");
1531 }
1532
1533 #[tokio::test]
1534 async fn test_reading_to_string() {
1535 let fs = FileSystem::default();
1536
1537 let mut file = fs
1538 .new_open_options()
1539 .read(true)
1540 .write(true)
1541 .create_new(true)
1542 .open(path!("/foo.txt"))
1543 .expect("failed to create a new file");
1544
1545 assert!(
1546 matches!(file.write(b"foobarbazqux").await, Ok(12)),
1547 "writing `foobarbazqux`",
1548 );
1549
1550 assert!(
1551 matches!(file.seek(io::SeekFrom::Start(6)).await, Ok(6)),
1552 "seeking to 0",
1553 );
1554
1555 let mut string = String::new();
1556 assert!(
1557 matches!(file.read_to_string(&mut string).await, Ok(6)),
1558 "reading a string",
1559 );
1560 assert_eq!(string, "bazqux", "checking the string");
1561 }
1562
1563 #[tokio::test]
1564 async fn test_reading_exact_buffer() {
1565 let fs = FileSystem::default();
1566
1567 let mut file = fs
1568 .new_open_options()
1569 .read(true)
1570 .write(true)
1571 .create_new(true)
1572 .open(path!("/foo.txt"))
1573 .expect("failed to create a new file");
1574
1575 assert!(
1576 matches!(file.write(b"foobarbazqux").await, Ok(12)),
1577 "writing `foobarbazqux`",
1578 );
1579
1580 assert!(
1581 matches!(file.seek(io::SeekFrom::Start(6)).await, Ok(6)),
1582 "seeking to 0",
1583 );
1584
1585 let mut buffer = [0; 16];
1586 assert!(
1587 file.read_exact(&mut buffer).await.is_err(),
1588 "failing to read an exact buffer",
1589 );
1590
1591 assert!(
1592 matches!(file.seek(io::SeekFrom::End(-5)).await, Ok(7)),
1593 "seeking to 7",
1594 );
1595
1596 let mut buffer = [0; 3];
1597 assert!(
1598 file.read_exact(&mut buffer).await.is_ok(),
1599 "failing to read an exact buffer",
1600 );
1601 }
1602
1603 #[tokio::test]
1604 async fn test_readonly_file_seek_and_reading_at_eof_returns_zero() {
1605 let fs = FileSystem::default();
1606 fs.insert_ro_file(path!("/foo.txt"), OwnedBuffer::from_static(b"foo"))
1607 .expect("failed to insert readonly file");
1608
1609 let mut file = fs
1610 .new_open_options()
1611 .read(true)
1612 .open(path!("/foo.txt"))
1613 .expect("failed to open readonly file");
1614
1615 assert!(
1616 matches!(file.seek(io::SeekFrom::End(0)).await, Ok(3)),
1617 "seeking to EOF",
1618 );
1619
1620 let mut buf = [0; 8];
1621 assert!(
1622 matches!(file.read(&mut buf).await, Ok(0)),
1623 "reading at EOF should return zero bytes",
1624 );
1625
1626 assert!(
1627 matches!(file.seek(io::SeekFrom::Start(10)).await, Ok(10)),
1628 "seeking past EOF",
1629 );
1630 assert!(
1631 matches!(file.read(&mut buf).await, Ok(0)),
1632 "reading past EOF should return zero bytes",
1633 );
1634 }
1635}
1636
1637impl fmt::Debug for FileHandle {
1638 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
1639 formatter
1640 .debug_struct("FileHandle")
1641 .field("inode", &self.inode)
1642 .field("readable", &self.readable)
1643 .field("writable", &self.writable)
1644 .finish()
1645 }
1646}
1647
1648impl Drop for FileHandle {
1649 fn drop(&mut self) {
1650 let remaining = self.lifecycle.closed();
1651 if remaining != 0 || !self.lifecycle.is_unlinked() {
1652 return;
1653 }
1654
1655 let Ok(mut fs) = self.filesystem.inner.write() else {
1656 return;
1657 };
1658
1659 let Some(node) = fs.storage.get(self.inode) else {
1660 return;
1661 };
1662
1663 let Some(lifecycle) = node.file_lifecycle() else {
1664 return;
1665 };
1666
1667 if Arc::ptr_eq(lifecycle, &self.lifecycle)
1668 && lifecycle.is_unlinked()
1669 && lifecycle.open_handle_count() == 0
1670 {
1671 fs.storage.remove(self.inode);
1672 }
1673 }
1674}
1675
1676#[derive(Debug)]
1679pub(super) struct File {
1680 buffer: TrackedVec,
1681}
1682
1683impl File {
1684 pub(super) fn new(limiter: Option<crate::limiter::DynFsMemoryLimiter>) -> Self {
1685 Self {
1686 buffer: TrackedVec::new(limiter),
1687 }
1688 }
1689
1690 pub(super) fn truncate(&mut self) {
1691 self.buffer.clear();
1692 }
1693
1694 pub(super) fn len(&self) -> usize {
1695 self.buffer.len()
1696 }
1697}
1698
1699impl File {
1700 pub fn read(&self, buf: &mut [u8], cursor: &mut u64) -> io::Result<usize> {
1701 let cur_pos = *cursor as usize;
1702
1703 if cur_pos >= self.buffer.len() {
1705 return Ok(0);
1706 }
1707
1708 let max_to_read = cmp::min(self.buffer.len() - cur_pos, buf.len());
1709 let data_to_copy = &self.buffer[cur_pos..][..max_to_read];
1710
1711 buf[..max_to_read].copy_from_slice(data_to_copy);
1714
1715 *cursor += max_to_read as u64;
1716
1717 Ok(max_to_read)
1718 }
1719}
1720
1721impl File {
1722 pub fn seek(&self, position: io::SeekFrom, cursor: &mut u64) -> io::Result<u64> {
1723 let to_err = |_| io::ErrorKind::InvalidInput;
1724
1725 let next_cursor: i64 = match position {
1727 io::SeekFrom::Start(offset) => offset.try_into().map_err(to_err)?,
1729
1730 io::SeekFrom::End(offset) => {
1732 TryInto::<i64>::try_into(self.buffer.len()).map_err(to_err)? + offset
1733 }
1734
1735 io::SeekFrom::Current(offset) => {
1737 TryInto::<i64>::try_into(*cursor).map_err(to_err)? + offset
1738 }
1739 };
1740
1741 if next_cursor < 0 {
1743 return Err(io::Error::new(
1744 io::ErrorKind::InvalidInput,
1745 "seeking before the byte 0",
1746 ));
1747 }
1748
1749 let next_cursor = next_cursor.try_into().map_err(to_err)?;
1750 *cursor = next_cursor;
1751
1752 let cursor = *cursor;
1753 Ok(cursor)
1754 }
1755}
1756
1757impl File {
1758 pub fn write(&mut self, buf: &[u8], cursor: &mut u64) -> io::Result<usize> {
1759 let position = *cursor as usize;
1760 let end = position
1761 .checked_add(buf.len())
1762 .ok_or(io::ErrorKind::InvalidInput)?;
1763
1764 if position > self.buffer.len() {
1765 self.buffer.resize(position, 0)?;
1767 }
1768
1769 if end > self.buffer.len() {
1770 let len_after_end = end - self.buffer.len();
1772 let let_to_end = buf.len() - len_after_end;
1773 self.buffer[position..end - len_after_end].copy_from_slice(&buf[0..let_to_end]);
1774 self.buffer.extend_from_slice(&buf[let_to_end..buf.len()])?;
1775 } else {
1776 self.buffer[position..end].copy_from_slice(buf);
1777 }
1778
1779 *cursor += buf.len() as u64;
1780
1781 Ok(buf.len())
1782 }
1783
1784 fn flush(&mut self) -> io::Result<()> {
1785 Ok(())
1786 }
1787}
1788
1789#[derive(Debug)]
1791pub(super) struct ReadOnlyFile {
1792 buffer: OwnedBuffer,
1793}
1794
1795impl ReadOnlyFile {
1796 pub(super) fn new(buffer: OwnedBuffer) -> Self {
1797 Self { buffer }
1798 }
1799
1800 pub(super) fn len(&self) -> usize {
1801 self.buffer.len()
1802 }
1803}
1804
1805impl ReadOnlyFile {
1806 pub fn read(&self, buf: &mut [u8], cursor: &mut u64) -> io::Result<usize> {
1807 let cur_pos = *cursor as usize;
1808
1809 if cur_pos >= self.buffer.len() {
1810 return Ok(0);
1811 }
1812
1813 let max_to_read = cmp::min(self.buffer.len() - cur_pos, buf.len());
1814 let data_to_copy = &self.buffer[cur_pos..][..max_to_read];
1815
1816 buf[..max_to_read].copy_from_slice(data_to_copy);
1819
1820 *cursor += max_to_read as u64;
1821
1822 Ok(max_to_read)
1823 }
1824}
1825
1826impl ReadOnlyFile {
1827 pub fn seek(&self, position: io::SeekFrom, cursor: &mut u64) -> io::Result<u64> {
1828 let to_err = |_| io::ErrorKind::InvalidInput;
1829
1830 let next_cursor: i64 = match position {
1831 io::SeekFrom::Start(offset) => offset.try_into().map_err(to_err)?,
1832 io::SeekFrom::End(offset) => {
1833 TryInto::<i64>::try_into(self.buffer.len()).map_err(to_err)? + offset
1834 }
1835 io::SeekFrom::Current(offset) => {
1836 TryInto::<i64>::try_into(*cursor).map_err(to_err)? + offset
1837 }
1838 };
1839
1840 if next_cursor < 0 {
1841 return Err(io::Error::new(
1842 io::ErrorKind::InvalidInput,
1843 "seeking before the byte 0",
1844 ));
1845 }
1846
1847 *cursor = next_cursor.try_into().map_err(to_err)?;
1848 Ok(*cursor)
1849 }
1850}
1851
1852impl ReadOnlyFile {
1853 pub fn write(&mut self, _buf: &[u8], _cursor: &mut u64) -> io::Result<usize> {
1854 Err(io::Error::new(
1855 io::ErrorKind::PermissionDenied,
1856 "file is read-only",
1857 ))
1858 }
1859
1860 pub fn flush(&mut self) -> io::Result<()> {
1861 Ok(())
1862 }
1863}