rasi/
fs.rs

1//! Future-based filesystem manipulation operations.
2//!
3//! This module contains basic methods to manipulate the contents of the local filesystem.
4//! All methods in this module represent cross-platform filesystem operations.
5//! Extra platform-specific functionality can be found in the extension traits of
6//! rasi::fs::$platform.
7//!
8//! # Driver configuration
9//! Before using these filesystem api, we need to use function [`register_fs_driver`] to
10//! inject one filesystem driver. here is an example of how to inject the filesystem
11//! [`driver`](syscall::Driver) with the [`rasi-mio`] library.
12//!
13//! ```no_run
14//! # futures::executor::block_on(async {
15//! use rasi::fs;
16//!
17//! /// Here, we use `rasi_mio` crate as the filesystem implementation,
18//! /// injected into the context of the current application.
19//!
20//! /// rasi_mio::fs::register_mio_filesystem();
21//!
22//! let current_path = fs::canonicalize("./").await;
23//!
24//! # });
25//! ```
26//!
27//! # Custom filesystem
28//!
29//! Check out the [`rasi-mio`] to learn how to implement a Filesystem driver  from the ground up!
30//!
31//! [`rasi-mio`]: https://github.com/HalaOS/RASI/tree/main/crates/mio
32
33use bitmask_enum::bitmask;
34use futures::{future::poll_fn, AsyncRead, AsyncSeek, AsyncWrite, Stream};
35use std::{
36    io::{Result, SeekFrom},
37    ops::Deref,
38    path::{Path, PathBuf},
39    sync::{Arc, OnceLock},
40    task::{Context, Poll},
41};
42
43pub use std::fs::{FileType, Metadata, Permissions};
44
45/// A bitmask for open file.
46///
47/// See [`open_file`](FileSystem::open_file) for more information.
48#[bitmask(u8)]
49pub enum FileOpenMode {
50    /// Configures the option for append mode.
51    ///
52    /// When set to true, this option means the file will be writable after opening
53    /// and the file cursor will be moved to the end of file before every write operaiton.
54    Append,
55    /// Configures the option for write mode.
56    /// If the file already exists, write calls on it will overwrite the previous contents without truncating it.
57    Writable,
58    /// Configures the option for read mode.
59    /// When set to true, this option means the file will be readable after opening.
60    Readable,
61
62    /// Configures the option for creating a new file if it doesn’t exist.
63    /// When set to true, this option means a new file will be created if it doesn’t exist.
64    /// The file must be opened in [`Writable`](FileOpenMode::Writable)
65    /// or [`Append`](FileOpenMode::Append) mode for file creation to work.
66    Create,
67
68    /// Configures the option for creating a new file or failing if it already exists.
69    /// When set to true, this option means a new file will be created, or the open
70    /// operation will fail if the file already exists.
71    /// The file must be opened in [`Writable`](FileOpenMode::Writable)
72    /// or [`Append`](FileOpenMode::Append) mode for file creation to work.
73    CreateNew,
74
75    /// Configures the option for truncating the previous file.
76    /// When set to true, the file will be truncated to the length of 0 bytes.
77    /// The file must be opened in [`Writable`](FileOpenMode::Writable)
78    /// or [`Append`](FileOpenMode::Append) mode for file creation to work.
79    Truncate,
80}
81
82#[cfg(windows)]
83pub mod windows {
84
85    use std::io::ErrorKind;
86
87    use super::*;
88
89    pub struct NamedPipeListener(Box<dyn syscall::windows::DriverNamedPipeListener>);
90
91    impl Deref for NamedPipeListener {
92        type Target = dyn syscall::windows::DriverNamedPipeListener;
93        fn deref(&self) -> &Self::Target {
94            &*self.0
95        }
96    }
97
98    impl<F: syscall::windows::DriverNamedPipeListener + 'static> From<F> for NamedPipeListener {
99        fn from(value: F) -> Self {
100            Self(Box::new(value))
101        }
102    }
103
104    impl NamedPipeListener {
105        /// Returns internal `DriverNamedPipeListener` object.
106        pub fn as_raw_ptr(&self) -> &dyn syscall::windows::DriverNamedPipeListener {
107            &*self.0
108        }
109
110        pub async fn accept(&self) -> Result<NamedPipeStream> {
111            poll_fn(|cx| self.as_raw_ptr().poll_next(cx)).await
112        }
113    }
114
115    impl Stream for NamedPipeListener {
116        type Item = Result<NamedPipeStream>;
117
118        fn poll_next(
119            self: std::pin::Pin<&mut Self>,
120            cx: &mut Context<'_>,
121        ) -> Poll<Option<Self::Item>> {
122            match self.as_raw_ptr().poll_next(cx) {
123                Poll::Ready(Ok(stream)) => Poll::Ready(Some(Ok(stream))),
124                Poll::Ready(Err(err)) => {
125                    if err.kind() == ErrorKind::BrokenPipe {
126                        Poll::Ready(None)
127                    } else {
128                        Poll::Ready(Some(Err(err)))
129                    }
130                }
131                Poll::Pending => Poll::Pending,
132            }
133        }
134    }
135
136    pub struct NamedPipeStream(Arc<Box<dyn syscall::windows::DriverNamedPipeStream>>);
137
138    impl Deref for NamedPipeStream {
139        type Target = dyn syscall::windows::DriverNamedPipeStream;
140        fn deref(&self) -> &Self::Target {
141            &**self.0
142        }
143    }
144
145    impl<F: syscall::windows::DriverNamedPipeStream + 'static> From<F> for NamedPipeStream {
146        fn from(value: F) -> Self {
147            Self(Arc::new(Box::new(value)))
148        }
149    }
150
151    impl NamedPipeStream {
152        /// Returns internal `DriverNamedPipeStream` object.
153        pub fn as_raw_ptr(&self) -> &dyn syscall::windows::DriverNamedPipeStream {
154            &**self.0
155        }
156    }
157
158    impl AsyncRead for NamedPipeStream {
159        fn poll_read(
160            self: std::pin::Pin<&mut Self>,
161            cx: &mut Context<'_>,
162            buf: &mut [u8],
163        ) -> Poll<Result<usize>> {
164            self.as_raw_ptr().poll_read(cx, buf)
165        }
166    }
167
168    impl AsyncWrite for NamedPipeStream {
169        fn poll_write(
170            self: std::pin::Pin<&mut Self>,
171            cx: &mut Context<'_>,
172            buf: &[u8],
173        ) -> Poll<Result<usize>> {
174            self.as_raw_ptr().poll_write(cx, buf)
175        }
176
177        fn poll_flush(self: std::pin::Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<Result<()>> {
178            Poll::Ready(Ok(()))
179        }
180
181        fn poll_close(self: std::pin::Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<()>> {
182            self.as_raw_ptr().poll_close(cx)
183        }
184    }
185}
186
187/// A filesystem driver must implement the `Driver-*` traits in this module.
188pub mod syscall {
189    use super::*;
190
191    #[cfg(windows)]
192    pub mod windows {
193
194        use super::*;
195        pub trait DriverNamedPipeListener: Sync + Send {
196            fn poll_ready(&self, cx: &mut Context<'_>) -> Poll<Result<()>>;
197
198            fn poll_next(
199                &self,
200                cx: &mut Context<'_>,
201            ) -> Poll<Result<crate::fs::windows::NamedPipeStream>>;
202        }
203
204        pub trait DriverNamedPipeStream: Sync + Send {
205            fn poll_ready(&self, cx: &mut Context<'_>) -> Poll<Result<()>>;
206
207            /// Write a buffer into this writer, returning how many bytes were written
208            fn poll_write(&self, cx: &mut Context<'_>, buf: &[u8]) -> Poll<Result<usize>>;
209
210            /// Pull some bytes from this source into the specified buffer, returning how many bytes were read.
211            fn poll_read(&self, cx: &mut Context<'_>, buf: &mut [u8]) -> Poll<Result<usize>>;
212
213            fn poll_close(&self, cx: &mut Context<'_>) -> Poll<Result<()>>;
214        }
215    }
216
217    /// A driver is the main entry to access asynchronously filesystem functions
218    pub trait Driver: Sync + Send {
219        /// Open new file with provided `mode`.
220        fn open_file(&self, path: &Path, mode: FileOpenMode) -> Result<File>;
221
222        /// Returns the canonical form of a path.
223        /// The returned path is in absolute form with all intermediate components
224        /// normalized and symbolic links resolved.
225        /// This function is an async version of [`std::fs::canonicalize`].
226        fn canonicalize(&self, path: &Path) -> Result<PathBuf>;
227
228        /// Copies the contents and permissions of a file to a new location.
229        /// On success, the total number of bytes copied is returned and equals
230        /// the length of the to file after this operation.
231        /// The old contents of to will be overwritten. If from and to both point
232        /// to the same file, then the file will likely get truncated as a result of this operation.
233        fn poll_copy(&self, cx: &mut Context<'_>, from: &Path, to: &Path) -> Poll<Result<u64>>;
234
235        /// Creates a new directory.
236        /// Note that this function will only create the final directory in path.
237        /// If you want to create all of its missing parent directories too, use
238        /// the [`create_dir_all`](FileSystem::create_dir_all) function instead.
239        ///
240        /// This function is an async version of [`std::fs::create_dir`].
241        fn poll_create_dir(&self, cx: &mut Context<'_>, path: &Path) -> Poll<Result<()>>;
242
243        /// Creates a new directory and all of its parents if they are missing.
244        /// This function is an async version of [`std::fs::create_dir_all`].
245        fn poll_create_dir_all(&self, cx: &mut Context<'_>, path: &Path) -> Poll<Result<()>>;
246
247        /// Creates a hard link on the filesystem.
248        /// The dst path will be a link pointing to the src path. Note that operating
249        /// systems often require these two paths to be located on the same filesystem.
250        ///
251        /// This function is an async version of [`std::fs::hard_link`].
252        fn poll_hard_link(&self, cx: &mut Context<'_>, from: &Path, to: &Path) -> Poll<Result<()>>;
253
254        /// Reads metadata for a path.
255        /// This function will traverse symbolic links to read metadata for the target
256        /// file or directory. If you want to read metadata without following symbolic
257        /// links, use symlink_metadata instead.
258        ///
259        /// This function is an async version of [`std::fs::metadata`].
260        fn poll_metadata(&self, cx: &mut Context<'_>, path: &Path) -> Poll<Result<Metadata>>;
261
262        /// Reads a symbolic link and returns the path it points to.
263        ///
264        /// This function is an async version of [`std::fs::read_link`].
265        fn poll_read_link(&self, cx: &mut Context<'_>, path: &Path) -> Poll<Result<PathBuf>>;
266
267        /// Removes an empty directory,
268        /// if the `path` is not an empty directory, use the function
269        /// [`remove_dir_all`](FileSystem::remove_dir_all) instead.
270        ///
271        /// This function is an async version of std::fs::remove_dir.
272        fn poll_remove_dir(&self, cx: &mut Context<'_>, path: &Path) -> Poll<Result<()>>;
273
274        /// Removes a directory and all of its contents.
275        ///
276        /// This function is an async version of [`std::fs::remove_dir_all`].
277        fn poll_remove_dir_all(&self, cx: &mut Context<'_>, path: &Path) -> Poll<Result<()>>;
278
279        /// Removes a file.
280        /// This function is an async version of [`std::fs::remove_file`].
281        fn poll_remove_file(&self, cx: &mut Context<'_>, path: &Path) -> Poll<Result<()>>;
282
283        /// Renames a file or directory to a new location.
284        /// If a file or directory already exists at the target location, it will be overwritten by this operation.
285        /// This function is an async version of std::fs::rename.
286        fn poll_rename(&self, cx: &mut Context<'_>, from: &Path, to: &Path) -> Poll<Result<()>>;
287
288        /// Changes the permissions of a file or directory.
289        /// This function is an async version of [`std::fs::set_permissions`].
290        fn poll_set_permissions(
291            &self,
292            cx: &mut Context<'_>,
293            path: &Path,
294            perm: &Permissions,
295        ) -> Poll<Result<()>>;
296
297        /// Reads metadata for a path without following symbolic links.
298        /// If you want to follow symbolic links before reading metadata of the target file or directory,
299        /// use [`metadata`](FileSystem::metadata) instead.
300        ///
301        /// This function is an async version of [`std::fs::symlink_metadata`].
302        fn poll_symlink_metadata(
303            &self,
304            cx: &mut Context<'_>,
305            path: &Path,
306        ) -> Poll<Result<Metadata>>;
307
308        /// Returns a iterator handle of entries in a directory.
309        fn read_dir(&self, path: &Path) -> Result<ReadDir>;
310
311        #[cfg(any(windows))]
312        /// Opens the named pipe identified by `addr`.
313        fn named_pipe_client_open(
314            &self,
315            addr: &std::ffi::OsStr,
316        ) -> Result<crate::fs::windows::NamedPipeStream>;
317
318        #[cfg(any(windows))]
319        /// Creates the named pipe identified by `addr` for use as a server.
320        ///
321        /// This uses the [`CreateNamedPipe`] function.
322        fn named_pipe_server_create(
323            &self,
324            addr: &std::ffi::OsStr,
325        ) -> Result<crate::fs::windows::NamedPipeListener>;
326    }
327
328    pub trait DriverDirEntry: Sync + Send {
329        /// Returns the bare name of this entry without the leading path.
330        fn name(&self) -> String;
331
332        /// Returns the full path to this entry.
333        /// The full path is created by joining the original path passed to [`read_dir`](syscall::Driver::read_dir) with the name of this entry.
334        fn path(&self) -> PathBuf;
335
336        /// Reads the metadata for this entry.
337        ///
338        /// This function will traverse symbolic links to read the metadata.
339        fn meta(&self) -> Result<Metadata>;
340
341        /// eads the file type for this entry.
342        /// This function will not traverse symbolic links if this entry points at one.
343        /// If you want to read metadata with following symbolic links, use [`meta`](syscall::DriverDirEntry::meta) instead.
344        fn file_type(&self) -> Result<FileType>;
345    }
346
347    /// Driver-specific `File` object.
348    pub trait DriverFile: Sync + Send {
349        /// Poll if the file object is actually opened.
350        fn poll_ready(&self, cx: &mut Context<'_>) -> Poll<Result<()>>;
351
352        /// Write a buffer into this writer, returning how many bytes were written
353        fn poll_write(&self, cx: &mut Context<'_>, buf: &[u8]) -> Poll<Result<usize>>;
354
355        /// Pull some bytes from this source into the specified buffer, returning how many bytes were read.
356        fn poll_read(&self, cx: &mut Context<'_>, buf: &mut [u8]) -> Poll<Result<usize>>;
357
358        /// Attempts to sync all OS-internal metadata to disk.
359        ///
360        /// This function will attempt to ensure that all in-memory data reaches the filesystem before returning.
361        ///
362        /// This can be used to handle errors that would otherwise only be caught when the File is closed.
363        /// Dropping a file will ignore errors in synchronizing this in-memory data.
364        fn poll_flush(&self, cx: &mut Context<'_>) -> Poll<Result<()>>;
365
366        /// Seek to an offset, in bytes, in a stream.
367        ///
368        /// A seek beyond the end of a stream is allowed, but behavior is defined by the implementation.
369        ///
370        /// If the seek operation completed successfully, this method returns the new position from the
371        /// start of the stream. That position can be used later with [`SeekFrom::Start`].
372        ///
373        /// # Errors
374        /// Seeking can fail, for example because it might involve flushing a buffer.
375        ///
376        /// Seeking to a negative offset is considered an error.
377        fn poll_seek(&self, cx: &mut Context<'_>, pos: SeekFrom) -> Poll<Result<u64>>;
378
379        ///  Reads the file's metadata.
380        fn poll_meta(&self, cx: &mut Context<'_>) -> Poll<Result<Metadata>>;
381
382        /// Changes the permissions on the file.
383        fn poll_set_permissions(
384            &self,
385            cx: &mut Context<'_>,
386            perm: &Permissions,
387        ) -> Poll<Result<()>>;
388
389        /// Truncates or extends the file.
390        ///
391        /// If `size` is less than the current file size, then the file will be truncated. If it is
392        /// greater than the current file size, then the file will be extended to `size` and have all
393        /// intermediate data filled with zeros.
394        ///
395        /// The file's cursor stays at the same position, even if the cursor ends up being past the end
396        /// of the file after this operation.
397        ///
398        fn poll_set_len(&self, cx: &mut Context<'_>, size: u64) -> Poll<Result<()>>;
399    }
400
401    pub trait DriverReadDir: Sync + Send {
402        /// Get asynchronously opened event.
403        fn poll_ready(&self, cx: &mut Context<'_>) -> Poll<Result<()>>;
404
405        fn poll_next(&self, cx: &mut Context<'_>) -> Poll<Option<Result<DirEntry>>>;
406    }
407}
408
409/// Entries returned by the [`ReadDir`] iterator.
410pub struct DirEntry(Box<dyn syscall::DriverDirEntry>);
411
412impl Deref for DirEntry {
413    type Target = dyn syscall::DriverDirEntry;
414    fn deref(&self) -> &Self::Target {
415        &*self.0
416    }
417}
418
419impl<F: syscall::DriverDirEntry + 'static> From<F> for DirEntry {
420    fn from(value: F) -> Self {
421        Self(Box::new(value))
422    }
423}
424
425impl DirEntry {
426    /// Returns internal `FSDDirEntry` object.
427    pub fn as_raw_ptr(&self) -> &dyn syscall::DriverDirEntry {
428        &*self.0
429    }
430}
431
432/// Iterator over the entries in a directory.
433pub struct ReadDir(Box<dyn syscall::DriverReadDir>);
434
435impl Deref for ReadDir {
436    type Target = dyn syscall::DriverReadDir;
437    fn deref(&self) -> &Self::Target {
438        &*self.0
439    }
440}
441
442impl<F: syscall::DriverReadDir + 'static> From<F> for ReadDir {
443    fn from(value: F) -> Self {
444        Self(Box::new(value))
445    }
446}
447
448impl ReadDir {
449    /// Returns internal `FSDReadDir` object.
450    pub fn as_raw_ptr(&self) -> &dyn syscall::DriverReadDir {
451        &*self.0
452    }
453
454    pub async fn new<P: AsRef<Path>>(path: P) -> Result<Self> {
455        Self::new_with(path, get_fs_driver()).await
456    }
457
458    pub async fn new_with<P: AsRef<Path>>(path: P, driver: &dyn syscall::Driver) -> Result<Self> {
459        let readdir = driver.read_dir(path.as_ref())?;
460
461        poll_fn(|cx| readdir.as_raw_ptr().poll_ready(cx))
462            .await
463            .map(|_| readdir)
464    }
465}
466
467impl Stream for ReadDir {
468    type Item = Result<DirEntry>;
469    fn poll_next(self: std::pin::Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
470        self.as_raw_ptr().poll_next(cx)
471    }
472}
473
474/// An open file on the filesystem.
475///
476/// Depending on what options the file was opened with, this type can be used for reading and/or writing.
477/// Files are automatically closed when they get dropped and any errors detected on closing are ignored.
478/// Use the sync_all method before dropping a file if such errors need to be handled.
479///
480/// This type is an async version of std::fs::File.
481pub struct File(Arc<Box<dyn syscall::DriverFile>>);
482
483impl Deref for File {
484    type Target = dyn syscall::DriverFile;
485    fn deref(&self) -> &Self::Target {
486        &**self.0
487    }
488}
489
490impl<F: syscall::DriverFile + 'static> From<F> for File {
491    fn from(value: F) -> Self {
492        Self(Arc::new(Box::new(value)))
493    }
494}
495
496impl File {
497    /// Returns internal `FSDFile` object.
498    pub fn as_raw_ptr(&self) -> &dyn syscall::DriverFile {
499        &**self.0
500    }
501
502    /// Open a file with provided `FileOpenMode`.
503    pub async fn open<P: AsRef<Path>>(path: P, mode: FileOpenMode) -> Result<Self> {
504        Self::open_with(path, mode, get_fs_driver()).await
505    }
506
507    /// Use custom `FileSystemDriver` to open file.
508    pub async fn open_with<P: AsRef<Path>>(
509        path: P,
510        mode: FileOpenMode,
511        driver: &dyn syscall::Driver,
512    ) -> Result<Self> {
513        let file = driver.open_file(path.as_ref(), mode)?;
514
515        // Wait for file opened event.
516        poll_fn(|cx| file.as_raw_ptr().poll_ready(cx)).await?;
517
518        Ok(file)
519    }
520
521    pub async fn meta(&self) -> Result<Metadata> {
522        poll_fn(|cx| self.as_raw_ptr().poll_meta(cx)).await
523    }
524
525    pub async fn set_permissions(&self, perm: &Permissions) -> Result<()> {
526        poll_fn(|cx| self.as_raw_ptr().poll_set_permissions(cx, perm)).await
527    }
528
529    pub async fn set_len(&self, len: u64) -> Result<()> {
530        poll_fn(|cx| self.as_raw_ptr().poll_set_len(cx, len)).await
531    }
532}
533
534impl AsyncRead for File {
535    fn poll_read(
536        self: std::pin::Pin<&mut Self>,
537        cx: &mut Context<'_>,
538        buf: &mut [u8],
539    ) -> Poll<Result<usize>> {
540        self.as_raw_ptr().poll_read(cx, buf)
541    }
542}
543
544impl AsyncWrite for File {
545    fn poll_write(
546        self: std::pin::Pin<&mut Self>,
547        cx: &mut Context<'_>,
548        buf: &[u8],
549    ) -> Poll<Result<usize>> {
550        self.as_raw_ptr().poll_write(cx, buf)
551    }
552
553    fn poll_flush(self: std::pin::Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<()>> {
554        self.as_raw_ptr().poll_flush(cx)
555    }
556
557    fn poll_close(self: std::pin::Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<()>> {
558        self.poll_flush(cx)
559    }
560}
561
562impl AsyncSeek for File {
563    fn poll_seek(
564        self: std::pin::Pin<&mut Self>,
565        cx: &mut Context<'_>,
566        pos: SeekFrom,
567    ) -> Poll<Result<u64>> {
568        self.as_raw_ptr().poll_seek(cx, pos)
569    }
570}
571
572/// Returns the canonical form of a path.
573/// The returned path is in absolute form with all intermediate components
574/// normalized and symbolic links resolved.
575/// This function is an async version of [`std::fs::canonicalize`].
576pub async fn canonicalize<P: AsRef<Path>>(path: P) -> Result<PathBuf> {
577    get_fs_driver().canonicalize(path.as_ref())
578}
579
580/// Copies the contents and permissions of a file to a new location.
581/// On success, the total number of bytes copied is returned and equals
582/// the length of the to file after this operation.
583/// The old contents of to will be overwritten. If from and to both point
584/// to the same file, then the file will likely get truncated as a result of this operation.
585pub async fn copy<F: AsRef<Path>, T: AsRef<Path>>(from: F, to: T) -> Result<u64> {
586    poll_fn(|cx| get_fs_driver().poll_copy(cx, from.as_ref(), to.as_ref())).await
587}
588
589/// Creates a new directory.
590/// Note that this function will only create the final directory in path.
591/// If you want to create all of its missing parent directories too, use
592/// the [`create_dir_all`](FileSystem::create_dir_all) function instead.
593///
594/// This function is an async version of [`std::fs::create_dir`].
595pub async fn create_dir<P: AsRef<Path>>(path: P) -> Result<()> {
596    poll_fn(|cx| get_fs_driver().poll_create_dir(cx, path.as_ref())).await
597}
598
599/// Creates a new directory and all of its parents if they are missing.
600/// This function is an async version of [`std::fs::create_dir_all`].
601pub async fn create_dir_all<P: AsRef<Path>>(path: P) -> Result<()> {
602    poll_fn(|cx| get_fs_driver().poll_create_dir_all(cx, path.as_ref())).await
603}
604
605/// Creates a hard link on the filesystem.
606/// The dst path will be a link pointing to the src path. Note that operating
607/// systems often require these two paths to be located on the same filesystem.
608///
609/// This function is an async version of [`std::fs::hard_link`].
610pub async fn hard_link<F: AsRef<Path>, T: AsRef<Path>>(from: F, to: T) -> Result<()> {
611    poll_fn(|cx| get_fs_driver().poll_hard_link(cx, from.as_ref(), to.as_ref())).await
612}
613
614/// Reads metadata for a path.
615/// This function will traverse symbolic links to read metadata for the target
616/// file or directory. If you want to read metadata without following symbolic
617/// links, use symlink_metadata instead.
618///
619/// This function is an async version of [`std::fs::metadata`].
620pub async fn metadata<P: AsRef<Path>>(path: P) -> Result<Metadata> {
621    poll_fn(|cx| get_fs_driver().poll_metadata(cx, path.as_ref())).await
622}
623
624/// Reads a symbolic link and returns the path it points to.
625///
626/// This function is an async version of [`std::fs::read_link`].
627pub async fn read_link<P: AsRef<Path>>(path: P) -> Result<PathBuf> {
628    poll_fn(|cx| get_fs_driver().poll_read_link(cx, path.as_ref())).await
629}
630
631/// Removes an empty directory,
632/// if the `path` is not an empty directory, use the function
633/// [`remove_dir_all`](FileSystem::remove_dir_all) instead.
634///
635/// This function is an async version of std::fs::remove_dir.
636pub async fn remove_dir<P: AsRef<Path>>(path: P) -> Result<()> {
637    poll_fn(|cx| get_fs_driver().poll_remove_dir(cx, path.as_ref())).await
638}
639
640/// Removes a directory and all of its contents.
641///
642/// This function is an async version of [`std::fs::remove_dir_all`].
643pub async fn remove_dir_all<P: AsRef<Path>>(path: P) -> Result<()> {
644    poll_fn(|cx| get_fs_driver().poll_remove_dir_all(cx, path.as_ref())).await
645}
646
647/// Removes a file.
648/// This function is an async version of [`std::fs::remove_file`].
649pub async fn remove_file<P: AsRef<Path>>(path: P) -> Result<()> {
650    poll_fn(|cx| get_fs_driver().poll_remove_file(cx, path.as_ref())).await
651}
652
653/// Renames a file or directory to a new location.
654/// If a file or directory already exists at the target location, it will be overwritten by this operation.
655/// This function is an async version of std::fs::rename.
656pub async fn rename<F: AsRef<Path>, T: AsRef<Path>>(from: F, to: T) -> Result<()> {
657    poll_fn(|cx| get_fs_driver().poll_rename(cx, from.as_ref(), to.as_ref())).await
658}
659
660/// Changes the permissions of a file or directory.
661/// This function is an async version of [`std::fs::set_permissions`].
662pub async fn set_permissions<P: AsRef<Path>>(path: P, perm: &Permissions) -> Result<()> {
663    poll_fn(|cx| get_fs_driver().poll_set_permissions(cx, path.as_ref(), perm)).await
664}
665
666/// Reads metadata for a path without following symbolic links.
667/// If you want to follow symbolic links before reading metadata of the target file or directory,
668/// use [`metadata`](FileSystem::metadata) instead.
669///
670/// This function is an async version of [`std::fs::symlink_metadata`].
671pub async fn symlink_metadata<P: AsRef<Path>>(path: P) -> Result<Metadata> {
672    poll_fn(|cx| get_fs_driver().poll_symlink_metadata(cx, path.as_ref())).await
673}
674
675/// Returns `true` if the path exists on disk and is pointing at a directory.
676///
677/// This function will traverse symbolic links to query information about the
678/// destination file. In case of broken symbolic links this will return `false`.
679///
680/// If you cannot access the directory containing the file, e.g., because of a
681/// permission error, this will return `false`.
682///
683/// # See Also
684///
685/// This is a convenience function that coerces errors to false. If you want to
686/// check errors, call [fs::metadata] and handle its Result. Then call
687/// [fs::Metadata::is_dir] if it was Ok.
688///
689/// [fs::metadata]: ../fs/fn.metadata.html
690/// [fs::Metadata::is_dir]: ../fs/struct.Metadata.html#method.is_dir
691pub async fn is_dir<P: AsRef<Path>>(path: P) -> bool {
692    metadata(path).await.map(|m| m.is_dir()).unwrap_or(false)
693}
694
695pub struct FileSystem<'a> {
696    driver: &'a dyn syscall::Driver,
697}
698
699impl<'a> From<&'a dyn syscall::Driver> for FileSystem<'a> {
700    fn from(value: &'a dyn syscall::Driver) -> Self {
701        Self { driver: value }
702    }
703}
704
705impl<'a> FileSystem<'a> {
706    /// Returns `true` if the path exists on disk and is pointing at a directory.
707    ///
708    /// This function will traverse symbolic links to query information about the
709    /// destination file. In case of broken symbolic links this will return `false`.
710    ///
711    /// If you cannot access the directory containing the file, e.g., because of a
712    /// permission error, this will return `false`.
713    ///
714    ///
715    /// # See Also
716    ///
717    /// This is a convenience function that coerces errors to false. If you want to
718    /// check errors, call [fs::metadata] and handle its Result. Then call
719    /// [fs::Metadata::is_dir] if it was Ok.
720    ///
721    /// [fs::metadata]: ../fs/fn.metadata.html
722    /// [fs::Metadata::is_dir]: ../fs/struct.Metadata.html#method.is_dir
723    pub async fn is_dir<P: AsRef<Path>>(&self, path: P) -> bool {
724        self.metadata(path)
725            .await
726            .map(|m| m.is_dir())
727            .unwrap_or(false)
728    }
729
730    /// Returns `true` if the path exists on disk and is pointing at a regular file.
731    ///
732    /// This function will traverse symbolic links to query information about the
733    /// destination file. In case of broken symbolic links this will return `false`.
734    ///
735    /// If you cannot access the directory containing the file, e.g., because of a
736    /// permission error, this will return `false`.
737    ///
738    /// # See Also
739    ///
740    /// This is a convenience function that coerces errors to false. If you want to
741    /// check errors, call [fs::metadata] and handle its Result. Then call
742    /// [fs::Metadata::is_file] if it was Ok.
743    ///
744    /// [fs::metadata]: ../fs/fn.metadata.html
745    /// [fs::Metadata::is_file]: ../fs/struct.Metadata.html#method.is_file
746
747    pub async fn is_file<P: AsRef<Path>>(&self, path: P) -> bool {
748        self.metadata(path)
749            .await
750            .map(|m| m.is_file())
751            .unwrap_or(false)
752    }
753
754    pub async fn open_file<P: AsRef<Path>>(&self, path: P, mode: FileOpenMode) -> Result<File> {
755        File::open_with(path, mode, self.driver).await
756    }
757
758    /// Returns the canonical form of a path.
759    /// The returned path is in absolute form with all intermediate components
760    /// normalized and symbolic links resolved.
761    /// This function is an async version of [`std::fs::canonicalize`].
762    pub async fn canonicalize<P: AsRef<Path>>(&self, path: P) -> Result<PathBuf> {
763        self.driver.canonicalize(path.as_ref())
764    }
765
766    /// Copies the contents and permissions of a file to a new location.
767    /// On success, the total number of bytes copied is returned and equals
768    /// the length of the to file after this operation.
769    /// The old contents of to will be overwritten. If from and to both point
770    /// to the same file, then the file will likely get truncated as a result of this operation.
771    pub async fn copy<F: AsRef<Path>, T: AsRef<Path>>(&self, from: F, to: T) -> Result<u64> {
772        poll_fn(|cx| self.driver.poll_copy(cx, from.as_ref(), to.as_ref())).await
773    }
774
775    /// Creates a new directory.
776    /// Note that this function will only create the final directory in path.
777    /// If you want to create all of its missing parent directories too, use
778    /// the [`create_dir_all`](FileSystem::create_dir_all) function instead.
779    ///
780    /// This function is an async version of [`std::fs::create_dir`].
781    pub async fn create_dir<P: AsRef<Path>>(&self, path: P) -> Result<()> {
782        poll_fn(|cx| self.driver.poll_create_dir(cx, path.as_ref())).await
783    }
784
785    /// Creates a new directory and all of its parents if they are missing.
786    /// This function is an async version of [`std::fs::create_dir_all`].
787    pub async fn create_dir_all<P: AsRef<Path>>(&self, path: P) -> Result<()> {
788        poll_fn(|cx| self.driver.poll_create_dir_all(cx, path.as_ref())).await
789    }
790
791    /// Creates a hard link on the filesystem.
792    /// The dst path will be a link pointing to the src path. Note that operating
793    /// systems often require these two paths to be located on the same filesystem.
794    ///
795    /// This function is an async version of [`std::fs::hard_link`].
796    pub async fn hard_link<F: AsRef<Path>, T: AsRef<Path>>(&self, from: F, to: T) -> Result<()> {
797        poll_fn(|cx| self.driver.poll_hard_link(cx, from.as_ref(), to.as_ref())).await
798    }
799
800    /// Reads metadata for a path.
801    /// This function will traverse symbolic links to read metadata for the target
802    /// file or directory. If you want to read metadata without following symbolic
803    /// links, use symlink_metadata instead.
804    ///
805    /// This function is an async version of [`std::fs::metadata`].
806    pub async fn metadata<P: AsRef<Path>>(&self, path: P) -> Result<Metadata> {
807        poll_fn(|cx| self.driver.poll_metadata(cx, path.as_ref())).await
808    }
809
810    /// Reads a symbolic link and returns the path it points to.
811    ///
812    /// This function is an async version of [`std::fs::read_link`].
813    pub async fn read_link<P: AsRef<Path>>(&self, path: P) -> Result<PathBuf> {
814        poll_fn(|cx| self.driver.poll_read_link(cx, path.as_ref())).await
815    }
816
817    /// Removes an empty directory,
818    /// if the `path` is not an empty directory, use the function
819    /// [`remove_dir_all`](FileSystem::remove_dir_all) instead.
820    ///
821    /// This function is an async version of std::fs::remove_dir.
822    pub async fn remove_dir<P: AsRef<Path>>(&self, path: P) -> Result<()> {
823        poll_fn(|cx| self.driver.poll_remove_dir(cx, path.as_ref())).await
824    }
825
826    /// Removes a directory and all of its contents.
827    ///
828    /// This function is an async version of [`std::fs::remove_dir_all`].
829    pub async fn remove_dir_all<P: AsRef<Path>>(&self, path: P) -> Result<()> {
830        poll_fn(|cx| self.driver.poll_remove_dir_all(cx, path.as_ref())).await
831    }
832
833    /// Removes a file.
834    /// This function is an async version of [`std::fs::remove_file`].
835    pub async fn remove_file<P: AsRef<Path>>(&self, path: P) -> Result<()> {
836        poll_fn(|cx| self.driver.poll_remove_file(cx, path.as_ref())).await
837    }
838
839    /// Renames a file or directory to a new location.
840    /// If a file or directory already exists at the target location, it will be overwritten by this operation.
841    /// This function is an async version of std::fs::rename.
842    pub async fn rename<F: AsRef<Path>, T: AsRef<Path>>(&self, from: F, to: T) -> Result<()> {
843        poll_fn(|cx| self.driver.poll_rename(cx, from.as_ref(), to.as_ref())).await
844    }
845
846    /// Changes the permissions of a file or directory.
847    /// This function is an async version of [`std::fs::set_permissions`].
848    pub async fn set_permissions<P: AsRef<Path>>(&self, path: P, perm: &Permissions) -> Result<()> {
849        poll_fn(|cx| self.driver.poll_set_permissions(cx, path.as_ref(), perm)).await
850    }
851
852    /// Reads metadata for a path without following symbolic links.
853    /// If you want to follow symbolic links before reading metadata of the target file or directory,
854    /// use [`metadata`](FileSystem::metadata) instead.
855    ///
856    /// This function is an async version of [`std::fs::symlink_metadata`].
857    pub async fn symlink_metadata<P: AsRef<Path>>(&self, path: P) -> Result<Metadata> {
858        poll_fn(|cx| self.driver.poll_symlink_metadata(cx, path.as_ref())).await
859    }
860}
861
862static FIFLE_SYSTEM_DRIVER: OnceLock<Box<dyn syscall::Driver>> = OnceLock::new();
863
864/// Get the filesystem driver(Implementation) from the global context of this application.
865///
866/// # Panic
867///
868/// Before calling this function, [`get_fs_driver`] should be called to register an implementation.
869/// otherwise panics the current thread with message `Call register_network_driver first.`.
870pub fn get_fs_driver() -> &'static dyn syscall::Driver {
871    FIFLE_SYSTEM_DRIVER
872        .get()
873        .expect("Call register_network_driver first.")
874        .as_ref()
875}
876
877/// Inject a filesystem driver(Implementation) into the global context of this application.
878///
879/// # Examples
880/// ```no_run
881///
882/// fn main() {
883///   // The `register_mio_filesystem` function indirectly calls the `register_fs_driver` function.
884///   // rasi_mio::fs::register_mio_filesystem();
885/// }
886///
887/// ```
888///
889/// # Panic
890///
891/// Multiple calls to this function are not permitted!!!
892pub fn register_fs_driver<E: syscall::Driver + 'static>(driver: E) {
893    if FIFLE_SYSTEM_DRIVER.set(Box::new(driver)).is_err() {
894        panic!("Multiple calls to register_network_driver are not permitted!!!");
895    }
896}