rasi_syscall/
fs.rs

1//! syscall for filesystem.
2
3use std::{
4    fs::{FileType, Metadata, Permissions},
5    io::{self, SeekFrom},
6    sync::OnceLock,
7    task::Waker,
8};
9
10use bitmask_enum::bitmask;
11
12use crate::path::{Path, PathBuf};
13use crate::{CancelablePoll, Handle};
14
15/// A bitmask for open file.
16///
17/// See [`open_file`](FileSystem::open_file) for more information.
18#[bitmask(u8)]
19pub enum FileOpenMode {
20    /// Configures the option for append mode.
21    ///
22    /// When set to true, this option means the file will be writable after opening
23    /// and the file cursor will be moved to the end of file before every write operaiton.
24    Append,
25    /// Configures the option for write mode.
26    /// If the file already exists, write calls on it will overwrite the previous contents without truncating it.
27    Writable,
28    /// Configures the option for read mode.
29    /// When set to true, this option means the file will be readable after opening.
30    Readable,
31
32    /// Configures the option for creating a new file if it doesn’t exist.
33    /// When set to true, this option means a new file will be created if it doesn’t exist.
34    /// The file must be opened in [`Writable`](FileOpenMode::Writable)
35    /// or [`Append`](FileOpenMode::Append) mode for file creation to work.
36    Create,
37
38    /// Configures the option for creating a new file or failing if it already exists.
39    /// When set to true, this option means a new file will be created, or the open
40    /// operation will fail if the file already exists.
41    /// The file must be opened in [`Writable`](FileOpenMode::Writable)
42    /// or [`Append`](FileOpenMode::Append) mode for file creation to work.
43    CreateNew,
44
45    /// Configures the option for truncating the previous file.
46    /// When set to true, the file will be truncated to the length of 0 bytes.
47    /// The file must be opened in [`Writable`](FileOpenMode::Writable)
48    /// or [`Append`](FileOpenMode::Append) mode for file creation to work.
49    Truncate,
50}
51
52/// Filesystem-related system call interface
53pub trait FileSystem: Sync + Send {
54    /// Opens a file with [`FileOpenMode`]
55    fn open_file(
56        &self,
57        waker: Waker,
58        path: &Path,
59        open_mode: &FileOpenMode,
60    ) -> CancelablePoll<io::Result<Handle>>;
61
62    /// Write a buffer into this writer, returning how many bytes were written
63    fn file_write(
64        &self,
65        waker: Waker,
66        file: &Handle,
67        buf: &[u8],
68    ) -> CancelablePoll<io::Result<usize>>;
69
70    /// Pull some bytes from this source into the specified buffer, returning how many bytes were read.
71    fn file_read(
72        &self,
73        waker: Waker,
74        file: &Handle,
75        buf: &mut [u8],
76    ) -> CancelablePoll<io::Result<usize>>;
77
78    /// Attempts to sync all OS-internal metadata to disk.
79    ///
80    /// This function will attempt to ensure that all in-memory data reaches the filesystem before returning.
81    ///
82    /// This can be used to handle errors that would otherwise only be caught when the File is closed.
83    /// Dropping a file will ignore errors in synchronizing this in-memory data.
84    fn file_flush(&self, waker: Waker, file: &Handle) -> CancelablePoll<io::Result<()>>;
85
86    /// Seek to an offset, in bytes, in a stream.
87    ///
88    /// A seek beyond the end of a stream is allowed, but behavior is defined by the implementation.
89    ///
90    /// If the seek operation completed successfully, this method returns the new position from the
91    /// start of the stream. That position can be used later with [`SeekFrom::Start`].
92    ///
93    /// # Errors
94    /// Seeking can fail, for example because it might involve flushing a buffer.
95    ///
96    /// Seeking to a negative offset is considered an error.
97    fn file_seek(
98        &self,
99        waker: Waker,
100        file: &Handle,
101        pos: SeekFrom,
102    ) -> CancelablePoll<io::Result<u64>>;
103
104    ///  Reads the file's metadata.
105    fn file_meta(&self, waker: Waker, file: &Handle) -> CancelablePoll<io::Result<Metadata>>;
106
107    /// Changes the permissions on the file.
108    fn file_set_permissions(
109        &self,
110        waker: Waker,
111        file: &Handle,
112        perm: &Permissions,
113    ) -> CancelablePoll<io::Result<()>>;
114
115    /// Truncates or extends the file.
116    ///
117    /// If `size` is less than the current file size, then the file will be truncated. If it is
118    /// greater than the current file size, then the file will be extended to `size` and have all
119    /// intermediate data filled with zeros.
120    ///
121    /// The file's cursor stays at the same position, even if the cursor ends up being past the end
122    /// of the file after this operation.
123    ///
124    fn file_set_len(
125        &self,
126        waker: Waker,
127        file: &Handle,
128        size: u64,
129    ) -> CancelablePoll<io::Result<()>>;
130
131    /// Returns the canonical form of a path.
132    /// The returned path is in absolute form with all intermediate components
133    /// normalized and symbolic links resolved.
134    /// This function is an async version of [`std::fs::canonicalize`].
135    fn canonicalize(&self, waker: Waker, path: &Path) -> CancelablePoll<io::Result<PathBuf>>;
136
137    /// Copies the contents and permissions of a file to a new location.
138    /// On success, the total number of bytes copied is returned and equals
139    /// the length of the to file after this operation.
140    /// The old contents of to will be overwritten. If from and to both point
141    /// to the same file, then the file will likely get truncated as a result of this operation.
142    fn copy(&self, waker: Waker, from: &Path, to: &Path) -> CancelablePoll<io::Result<u64>>;
143
144    /// Creates a new directory.
145    /// Note that this function will only create the final directory in path.
146    /// If you want to create all of its missing parent directories too, use
147    /// the [`create_dir_all`](FileSystem::create_dir_all) function instead.
148    ///
149    /// This function is an async version of [`std::fs::create_dir`].
150    fn create_dir(&self, waker: Waker, path: &Path) -> CancelablePoll<io::Result<()>>;
151
152    /// Creates a new directory and all of its parents if they are missing.
153    /// This function is an async version of [`std::fs::create_dir_all`].
154    fn create_dir_all(&self, waker: Waker, path: &Path) -> CancelablePoll<io::Result<()>>;
155
156    /// Creates a hard link on the filesystem.
157    /// The dst path will be a link pointing to the src path. Note that operating
158    /// systems often require these two paths to be located on the same filesystem.
159    ///
160    /// This function is an async version of [`std::fs::hard_link`].
161    fn hard_link(&self, waker: Waker, from: &Path, to: &Path) -> CancelablePoll<io::Result<()>>;
162
163    /// Reads metadata for a path.
164    /// This function will traverse symbolic links to read metadata for the target
165    /// file or directory. If you want to read metadata without following symbolic
166    /// links, use symlink_metadata instead.
167    ///
168    /// This function is an async version of [`std::fs::metadata`].
169    fn metadata(&self, waker: Waker, path: &Path) -> CancelablePoll<io::Result<Metadata>>;
170
171    /// Returns a iterator handle of entries in a directory.
172    ///
173    /// See [`dir_entry_next`](FileSystem::dir_entry_next) for more information about iteration.
174    fn read_dir(&self, waker: Waker, path: &Path) -> CancelablePoll<io::Result<Handle>>;
175
176    /// Advances the directory entry iterator and returns the next value.
177    ///
178    /// Returns None when iteration is finished.
179    ///
180    /// You can create a directory entry iterator by call function [`read_dir`](FileSystem::read_dir)
181    fn dir_entry_next(
182        &self,
183        waker: Waker,
184        read_dir_handle: &Handle,
185    ) -> CancelablePoll<io::Result<Option<Handle>>>;
186
187    /// Returns the bare name of this entry without the leading path.
188    fn dir_entry_file_name(&self, entry: &Handle) -> String;
189
190    /// Returns the full path to this entry.
191    /// The full path is created by joining the original path passed to [`read_dir`](FileSystem::read_dir) with the name of this entry.
192    fn dir_entry_path(&self, entry: &Handle) -> PathBuf;
193
194    /// Reads the metadata for this entry.
195    ///
196    /// This function will traverse symbolic links to read the metadata.
197    fn dir_entry_metadata(
198        &self,
199        waker: Waker,
200        entry: &Handle,
201    ) -> CancelablePoll<io::Result<Metadata>>;
202
203    /// eads the file type for this entry.
204    /// This function will not traverse symbolic links if this entry points at one.
205    /// If you want to read metadata with following symbolic links, use [`dir_entry_metadata`](FileSystem::dir_entry_metadata) instead.
206    fn dir_entry_file_type(
207        &self,
208        waker: Waker,
209        entry: &Handle,
210    ) -> CancelablePoll<io::Result<FileType>>;
211
212    /// Reads a symbolic link and returns the path it points to.
213    ///
214    /// This function is an async version of [`std::fs::read_link`].
215    fn read_link(&self, waker: Waker, path: &Path) -> CancelablePoll<io::Result<PathBuf>>;
216
217    /// Removes an empty directory,
218    /// if the `path` is not an empty directory, use the function
219    /// [`remove_dir_all`](FileSystem::remove_dir_all) instead.
220    ///
221    /// This function is an async version of std::fs::remove_dir.
222    fn remove_dir(&self, waker: Waker, path: &Path) -> CancelablePoll<io::Result<()>>;
223
224    /// Removes a directory and all of its contents.
225    ///
226    /// This function is an async version of [`std::fs::remove_dir_all`].
227    fn remove_dir_all(&self, waker: Waker, path: &Path) -> CancelablePoll<io::Result<()>>;
228
229    /// Removes a file.
230    /// This function is an async version of [`std::fs::remove_file`].
231    fn remove_file(&self, waker: Waker, path: &Path) -> CancelablePoll<io::Result<()>>;
232
233    /// Renames a file or directory to a new location.
234    /// If a file or directory already exists at the target location, it will be overwritten by this operation.
235    /// This function is an async version of std::fs::rename.
236    fn rename(&self, waker: Waker, from: &Path, to: &Path) -> CancelablePoll<io::Result<()>>;
237
238    /// Changes the permissions of a file or directory.
239    /// This function is an async version of [`std::fs::set_permissions`].
240    fn set_permissions(
241        &self,
242        waker: Waker,
243        path: &Path,
244        perm: &Permissions,
245    ) -> CancelablePoll<io::Result<()>>;
246
247    /// Reads metadata for a path without following symbolic links.
248    /// If you want to follow symbolic links before reading metadata of the target file or directory,
249    /// use [`metadata`](FileSystem::metadata) instead.
250    ///
251    /// This function is an async version of [`std::fs::symlink_metadata`].
252    fn symlink_metadata(&self, waker: Waker, path: &Path) -> CancelablePoll<io::Result<Metadata>>;
253}
254
255static GLOBAL_FILESYSTEM: OnceLock<Box<dyn FileSystem>> = OnceLock::new();
256
257/// Register provided [`FileSystem`] as global filesystem implementation.
258///
259/// # Panic
260///
261/// Multiple calls to this function are not permitted!!!
262pub fn register_global_filesystem<FS: FileSystem + 'static>(fs: FS) {
263    if GLOBAL_FILESYSTEM.set(Box::new(fs)).is_err() {
264        panic!("Multiple calls to register_global_filesystem are not permitted!!!");
265    }
266}
267
268/// Get the globally registered instance of [`FileSystem`].
269///
270/// # Panic
271///
272/// You should call [`register_global_filesystem`] first to register implementation,
273/// otherwise this function will cause a panic with `Call register_global_filesystem first`
274pub fn global_filesystem() -> &'static dyn FileSystem {
275    GLOBAL_FILESYSTEM
276        .get()
277        .expect("Call register_global_filesystem first")
278        .as_ref()
279}
280
281#[cfg(all(windows, feature = "windows_named_pipe"))]
282
283mod windows {
284    use super::*;
285
286    pub trait NamedPipe: Send + Sync {
287        /// Opens the named pipe identified by `addr`.
288        fn client_open(
289            &self,
290            waker: Waker,
291            addr: &std::ffi::OsStr,
292        ) -> CancelablePoll<io::Result<Handle>>;
293
294        /// Creates the named pipe identified by `addr` for use as a server.
295        ///
296        /// This uses the [`CreateNamedPipe`] function.
297        fn server_create(
298            &self,
299            waker: Waker,
300            addr: &std::ffi::OsStr,
301        ) -> CancelablePoll<io::Result<Handle>>;
302
303        /// Enables a named pipe server process to wait for a client process to
304        /// connect to an instance of a named pipe. A client process connects by
305        /// creating a named pipe with the same name.
306        fn server_accept(&self, waker: Waker, file: &Handle) -> CancelablePoll<io::Result<()>>;
307
308        /// Disconnects the server end of a named pipe instance from a client
309        /// process.
310        fn server_disconnect(&self, file: &Handle) -> io::Result<()>;
311
312        /// Write a buffer into this writer, returning how many bytes were written
313        fn write(
314            &self,
315            waker: Waker,
316            file: &Handle,
317            buf: &[u8],
318        ) -> CancelablePoll<io::Result<usize>>;
319
320        /// Pull some bytes from this source into the specified buffer, returning how many bytes were read.
321        fn read(
322            &self,
323            waker: Waker,
324            file: &Handle,
325            buf: &mut [u8],
326        ) -> CancelablePoll<io::Result<usize>>;
327    }
328
329    static GLOBAL_NAMED_PIPE: OnceLock<Box<dyn NamedPipe>> = OnceLock::new();
330
331    /// Register provided [`NamedPipe`] as global named pipe implementation.
332    ///
333    /// # Panic
334    ///
335    /// Multiple calls to this function are not permitted!!!
336    pub fn register_global_named_pipe<N: NamedPipe + 'static>(fs: N) {
337        if GLOBAL_NAMED_PIPE.set(Box::new(fs)).is_err() {
338            panic!("Multiple calls to register_global_filesystem are not permitted!!!");
339        }
340    }
341
342    /// Get the globally registered instance of [`NamedPipe`].
343    ///
344    /// # Panic
345    ///
346    /// You should call [`register_global_named_pipe`] first to register implementation,
347    /// otherwise this function will cause a panic with `Call register_global_filesystem first`
348    pub fn global_named_pipe() -> &'static dyn NamedPipe {
349        GLOBAL_NAMED_PIPE
350            .get()
351            .expect("Call register_global_named_pipe first")
352            .as_ref()
353    }
354}
355
356#[cfg(all(windows, feature = "windows_named_pipe"))]
357pub use windows::*;