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}