virtual_fs/mem_fs/
file.rs

1//! This module contains the `FileHandle` and `File`
2//! implementations. They aren't exposed to the public API. Only
3//! `FileHandle` can be used through the `VirtualFile` trait object.
4
5use 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
22/// A file handle. The file system doesn't return the [`File`] type
23/// directly, but rather this `FileHandle` type, which contains the
24/// inode, the flags, and (a light copy of) the filesystem. For each
25/// operations, it is checked that the permissions allow the
26/// operations to be executed, and then it is checked that the file
27/// still exists in the file system. After that, the operation is
28/// delegated to the file itself.
29pub(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        // Update current cursor position if file got truncated
241        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            // Read lock.
251            let fs = filesystem.inner.read().map_err(|_| FsError::Lock)?;
252
253            // The inode of the file.
254            let inode_of_file = inode;
255
256            // Find the position of the file in the parent, and the
257            // inode of the parent.
258            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            // Write lock.
281            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 tokio::io::{AsyncReadExt, AsyncSeekExt, AsyncWriteExt};
1321
1322    use crate::{FileSystem as FS, mem_fs::*};
1323    use std::io;
1324
1325    macro_rules! path {
1326        ($path:expr) => {
1327            std::path::Path::new($path)
1328        };
1329    }
1330
1331    #[tokio::test]
1332    async fn test_writing_at_various_positions() {
1333        let fs = FileSystem::default();
1334
1335        let mut file = fs
1336            .new_open_options()
1337            .read(true)
1338            .write(true)
1339            .create_new(true)
1340            .open(path!("/foo.txt"))
1341            .expect("failed to create a new file");
1342
1343        assert!(
1344            matches!(file.write(b"foo").await, Ok(3)),
1345            "writing `foo` at the end of the file",
1346        );
1347        assert_eq!(file.size(), 3, "checking the size of the file");
1348
1349        assert!(
1350            matches!(file.write(b"bar").await, Ok(3)),
1351            "writing `bar` at the end of the file",
1352        );
1353        assert_eq!(file.size(), 6, "checking the size of the file");
1354
1355        assert!(
1356            matches!(file.seek(io::SeekFrom::Start(0)).await, Ok(0)),
1357            "seeking to 0",
1358        );
1359
1360        assert!(
1361            matches!(file.write(b"baz").await, Ok(3)),
1362            "writing `baz` at the beginning of the file",
1363        );
1364        assert_eq!(file.size(), 6, "checking the size of the file");
1365
1366        assert!(
1367            matches!(file.write(b"qux").await, Ok(3)),
1368            "writing `qux` in the middle of the file",
1369        );
1370        assert_eq!(file.size(), 6, "checking the size of the file");
1371
1372        assert!(
1373            matches!(file.seek(io::SeekFrom::Start(0)).await, Ok(0)),
1374            "seeking to 0",
1375        );
1376
1377        let mut string = String::new();
1378        assert!(
1379            matches!(file.read_to_string(&mut string).await, Ok(6)),
1380            "reading `bazqux`",
1381        );
1382        assert_eq!(string, "bazqux");
1383
1384        assert!(
1385            matches!(file.seek(io::SeekFrom::Current(-3)).await, Ok(3)),
1386            "seeking to 3",
1387        );
1388
1389        let mut string = String::new();
1390        assert!(
1391            matches!(file.read_to_string(&mut string).await, Ok(3)),
1392            "reading `qux`",
1393        );
1394        assert_eq!(string, "qux");
1395
1396        assert!(
1397            matches!(file.seek(io::SeekFrom::End(0)).await, Ok(6)),
1398            "seeking to 6",
1399        );
1400
1401        let mut string = String::new();
1402        assert!(
1403            matches!(file.read_to_string(&mut string).await, Ok(0)),
1404            "reading ``",
1405        );
1406        assert_eq!(string, "");
1407    }
1408
1409    #[test]
1410    pub fn writing_to_middle() {
1411        fn assert_contents(file: &File, expected: &[u8]) {
1412            let mut buf = vec![0; expected.len() + 1];
1413            let mut cursor = 0;
1414            let read = file.read(buf.as_mut(), &mut cursor).unwrap();
1415            assert_eq!(read, expected.len(), "Must have the same amount of data");
1416            assert_eq!(buf[0..expected.len()], *expected);
1417        }
1418
1419        let mut file = File::new(None);
1420
1421        let mut cursor = 0;
1422
1423        // Write to empty file
1424        file.write(b"hello, world.", &mut cursor).unwrap();
1425        assert_eq!(cursor, 13);
1426        assert_contents(&file, b"hello, world.");
1427
1428        // Write to end of file
1429        file.write(b"goodbye!", &mut cursor).unwrap();
1430        assert_eq!(cursor, 21);
1431        assert_contents(&file, b"hello, world.goodbye!");
1432
1433        // Write to middle of file
1434        cursor = 5;
1435        file.write(b"BOOM", &mut cursor).unwrap();
1436        assert_eq!(cursor, 9);
1437        assert_contents(&file, b"helloBOOMrld.goodbye!");
1438
1439        // Write to middle of file until last byte
1440        cursor = 17;
1441        file.write(b"BANG", &mut cursor).unwrap();
1442        assert_eq!(cursor, 21);
1443        assert_contents(&file, b"helloBOOMrld.goodBANG");
1444
1445        // Write to middle past end of file
1446        cursor = 17;
1447        file.write(b"OUCH!", &mut cursor).unwrap();
1448        assert_eq!(cursor, 22);
1449        assert_contents(&file, b"helloBOOMrld.goodOUCH!");
1450    }
1451
1452    #[tokio::test]
1453    async fn test_reading() {
1454        let fs = FileSystem::default();
1455
1456        let mut file = fs
1457            .new_open_options()
1458            .read(true)
1459            .write(true)
1460            .create_new(true)
1461            .open(path!("/foo.txt"))
1462            .expect("failed to create a new file");
1463
1464        assert!(
1465            matches!(fs.metadata(path!("/foo.txt")), Ok(Metadata { len: 0, .. })),
1466            "checking the `metadata.len` is 0",
1467        );
1468        assert!(
1469            matches!(file.write(b"foobarbazqux").await, Ok(12)),
1470            "writing `foobarbazqux`",
1471        );
1472
1473        assert!(
1474            matches!(file.seek(io::SeekFrom::Start(0)).await, Ok(0)),
1475            "seeking to 0",
1476        );
1477
1478        let mut buffer = [0; 6];
1479        assert!(
1480            matches!(file.read(&mut buffer[..]).await, Ok(6)),
1481            "reading 6 bytes",
1482        );
1483        assert_eq!(buffer, b"foobar"[..], "checking the 6 bytes");
1484
1485        assert!(
1486            matches!(file.seek(io::SeekFrom::Start(0)).await, Ok(0)),
1487            "seeking to 0",
1488        );
1489
1490        let mut buffer = [0; 16];
1491        assert!(
1492            matches!(file.read(&mut buffer[..]).await, Ok(12)),
1493            "reading more bytes than available",
1494        );
1495        assert_eq!(buffer[..12], b"foobarbazqux"[..], "checking the 12 bytes");
1496        assert!(
1497            matches!(fs.metadata(path!("/foo.txt")), Ok(Metadata { len: 12, .. })),
1498            "checking the `metadata.len` is 0",
1499        );
1500    }
1501
1502    #[tokio::test]
1503    async fn test_reading_to_the_end() {
1504        let fs = FileSystem::default();
1505
1506        let mut file = fs
1507            .new_open_options()
1508            .read(true)
1509            .write(true)
1510            .create_new(true)
1511            .open(path!("/foo.txt"))
1512            .expect("failed to create a new file");
1513
1514        assert!(
1515            matches!(file.write(b"foobarbazqux").await, Ok(12)),
1516            "writing `foobarbazqux`",
1517        );
1518
1519        assert!(
1520            matches!(file.seek(io::SeekFrom::Start(0)).await, Ok(0)),
1521            "seeking to 0",
1522        );
1523
1524        let mut buffer = Vec::new();
1525        assert!(
1526            matches!(file.read_to_end(&mut buffer).await, Ok(12)),
1527            "reading all bytes",
1528        );
1529        assert_eq!(buffer, b"foobarbazqux"[..], "checking all the bytes");
1530    }
1531
1532    #[tokio::test]
1533    async fn test_reading_to_string() {
1534        let fs = FileSystem::default();
1535
1536        let mut file = fs
1537            .new_open_options()
1538            .read(true)
1539            .write(true)
1540            .create_new(true)
1541            .open(path!("/foo.txt"))
1542            .expect("failed to create a new file");
1543
1544        assert!(
1545            matches!(file.write(b"foobarbazqux").await, Ok(12)),
1546            "writing `foobarbazqux`",
1547        );
1548
1549        assert!(
1550            matches!(file.seek(io::SeekFrom::Start(6)).await, Ok(6)),
1551            "seeking to 0",
1552        );
1553
1554        let mut string = String::new();
1555        assert!(
1556            matches!(file.read_to_string(&mut string).await, Ok(6)),
1557            "reading a string",
1558        );
1559        assert_eq!(string, "bazqux", "checking the string");
1560    }
1561
1562    #[tokio::test]
1563    async fn test_reading_exact_buffer() {
1564        let fs = FileSystem::default();
1565
1566        let mut file = fs
1567            .new_open_options()
1568            .read(true)
1569            .write(true)
1570            .create_new(true)
1571            .open(path!("/foo.txt"))
1572            .expect("failed to create a new file");
1573
1574        assert!(
1575            matches!(file.write(b"foobarbazqux").await, Ok(12)),
1576            "writing `foobarbazqux`",
1577        );
1578
1579        assert!(
1580            matches!(file.seek(io::SeekFrom::Start(6)).await, Ok(6)),
1581            "seeking to 0",
1582        );
1583
1584        let mut buffer = [0; 16];
1585        assert!(
1586            file.read_exact(&mut buffer).await.is_err(),
1587            "failing to read an exact buffer",
1588        );
1589
1590        assert!(
1591            matches!(file.seek(io::SeekFrom::End(-5)).await, Ok(7)),
1592            "seeking to 7",
1593        );
1594
1595        let mut buffer = [0; 3];
1596        assert!(
1597            file.read_exact(&mut buffer).await.is_ok(),
1598            "failing to read an exact buffer",
1599        );
1600    }
1601}
1602
1603impl fmt::Debug for FileHandle {
1604    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
1605        formatter
1606            .debug_struct("FileHandle")
1607            .field("inode", &self.inode)
1608            .field("readable", &self.readable)
1609            .field("writable", &self.writable)
1610            .finish()
1611    }
1612}
1613
1614impl Drop for FileHandle {
1615    fn drop(&mut self) {
1616        let remaining = self.lifecycle.closed();
1617        if remaining != 0 || !self.lifecycle.is_unlinked() {
1618            return;
1619        }
1620
1621        let Ok(mut fs) = self.filesystem.inner.write() else {
1622            return;
1623        };
1624
1625        let Some(node) = fs.storage.get(self.inode) else {
1626            return;
1627        };
1628
1629        let Some(lifecycle) = node.file_lifecycle() else {
1630            return;
1631        };
1632
1633        if Arc::ptr_eq(lifecycle, &self.lifecycle)
1634            && lifecycle.is_unlinked()
1635            && lifecycle.open_handle_count() == 0
1636        {
1637            fs.storage.remove(self.inode);
1638        }
1639    }
1640}
1641
1642/// The real file! It is simply a buffer of bytes with a cursor that
1643/// represents a read/write position in the buffer.
1644#[derive(Debug)]
1645pub(super) struct File {
1646    buffer: TrackedVec,
1647}
1648
1649impl File {
1650    pub(super) fn new(limiter: Option<crate::limiter::DynFsMemoryLimiter>) -> Self {
1651        Self {
1652            buffer: TrackedVec::new(limiter),
1653        }
1654    }
1655
1656    pub(super) fn truncate(&mut self) {
1657        self.buffer.clear();
1658    }
1659
1660    pub(super) fn len(&self) -> usize {
1661        self.buffer.len()
1662    }
1663}
1664
1665impl File {
1666    pub fn read(&self, buf: &mut [u8], cursor: &mut u64) -> io::Result<usize> {
1667        let cur_pos = *cursor as usize;
1668
1669        // POSIX regular files return EOF, not an error, when reading at or beyond EOF.
1670        if cur_pos >= self.buffer.len() {
1671            return Ok(0);
1672        }
1673
1674        let max_to_read = cmp::min(self.buffer.len() - cur_pos, buf.len());
1675        let data_to_copy = &self.buffer[cur_pos..][..max_to_read];
1676
1677        // SAFETY: `buf[..max_to_read]` and `data_to_copy` have the same size, due to
1678        // how `max_to_read` is computed.
1679        buf[..max_to_read].copy_from_slice(data_to_copy);
1680
1681        *cursor += max_to_read as u64;
1682
1683        Ok(max_to_read)
1684    }
1685}
1686
1687impl File {
1688    pub fn seek(&self, position: io::SeekFrom, cursor: &mut u64) -> io::Result<u64> {
1689        let to_err = |_| io::ErrorKind::InvalidInput;
1690
1691        // Calculate the next cursor.
1692        let next_cursor: i64 = match position {
1693            // Calculate from the beginning, so `0 + offset`.
1694            io::SeekFrom::Start(offset) => offset.try_into().map_err(to_err)?,
1695
1696            // Calculate from the end, so `buffer.len() + offset`.
1697            io::SeekFrom::End(offset) => {
1698                TryInto::<i64>::try_into(self.buffer.len()).map_err(to_err)? + offset
1699            }
1700
1701            // Calculate from the current cursor, so `cursor + offset`.
1702            io::SeekFrom::Current(offset) => {
1703                TryInto::<i64>::try_into(*cursor).map_err(to_err)? + offset
1704            }
1705        };
1706
1707        // It's an error to seek before byte 0.
1708        if next_cursor < 0 {
1709            return Err(io::Error::new(
1710                io::ErrorKind::InvalidInput,
1711                "seeking before the byte 0",
1712            ));
1713        }
1714
1715        let next_cursor = next_cursor.try_into().map_err(to_err)?;
1716        *cursor = next_cursor;
1717
1718        let cursor = *cursor;
1719        Ok(cursor)
1720    }
1721}
1722
1723impl File {
1724    pub fn write(&mut self, buf: &[u8], cursor: &mut u64) -> io::Result<usize> {
1725        let position = *cursor as usize;
1726        let end = position
1727            .checked_add(buf.len())
1728            .ok_or(io::ErrorKind::InvalidInput)?;
1729
1730        if position > self.buffer.len() {
1731            // Materialize the sparse gap with zeroes in this contiguous in-memory buffer.
1732            self.buffer.resize(position, 0)?;
1733        }
1734
1735        if end > self.buffer.len() {
1736            // Writing past the end of the current buffer, must reallocate
1737            let len_after_end = end - self.buffer.len();
1738            let let_to_end = buf.len() - len_after_end;
1739            self.buffer[position..end - len_after_end].copy_from_slice(&buf[0..let_to_end]);
1740            self.buffer.extend_from_slice(&buf[let_to_end..buf.len()])?;
1741        } else {
1742            self.buffer[position..end].copy_from_slice(buf);
1743        }
1744
1745        *cursor += buf.len() as u64;
1746
1747        Ok(buf.len())
1748    }
1749
1750    fn flush(&mut self) -> io::Result<()> {
1751        Ok(())
1752    }
1753}
1754
1755/// Read only file that uses copy-on-write
1756#[derive(Debug)]
1757pub(super) struct ReadOnlyFile {
1758    buffer: OwnedBuffer,
1759}
1760
1761impl ReadOnlyFile {
1762    pub(super) fn new(buffer: OwnedBuffer) -> Self {
1763        Self { buffer }
1764    }
1765
1766    pub(super) fn len(&self) -> usize {
1767        self.buffer.len()
1768    }
1769}
1770
1771impl ReadOnlyFile {
1772    pub fn read(&self, buf: &mut [u8], cursor: &mut u64) -> io::Result<usize> {
1773        let cur_pos = *cursor as usize;
1774        let max_to_read = cmp::min(self.buffer.len() - cur_pos, buf.len());
1775        let data_to_copy = &self.buffer[cur_pos..][..max_to_read];
1776
1777        // SAFETY: `buf[..max_to_read]` and `data_to_copy` have the same size, due to
1778        // how `max_to_read` is computed.
1779        buf[..max_to_read].copy_from_slice(data_to_copy);
1780
1781        *cursor += max_to_read as u64;
1782
1783        Ok(max_to_read)
1784    }
1785}
1786
1787impl ReadOnlyFile {
1788    pub fn seek(&self, _position: io::SeekFrom, _cursor: &mut u64) -> io::Result<u64> {
1789        Err(io::Error::new(
1790            io::ErrorKind::PermissionDenied,
1791            "file is read-only",
1792        ))
1793    }
1794}
1795
1796impl ReadOnlyFile {
1797    pub fn write(&mut self, _buf: &[u8], _cursor: &mut u64) -> io::Result<usize> {
1798        Err(io::Error::new(
1799            io::ErrorKind::PermissionDenied,
1800            "file is read-only",
1801        ))
1802    }
1803
1804    pub fn flush(&mut self) -> io::Result<()> {
1805        Ok(())
1806    }
1807}