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::*;