rasi_default/
fs.rs

1//! This mode is a wrapper of [`std::fs`] that implement [`FileSystem`] trait.
2//!
3
4use std::{
5    fs::{DirEntry, File, OpenOptions, ReadDir},
6    io::{Read, Seek, Write},
7};
8
9use rasi_syscall::{
10    path::*, ready, register_global_filesystem, CancelablePoll, FileOpenMode, FileSystem, Handle,
11};
12
13/// The wrapper of [`std::fs`] that implement [`FileSystem`] trait.
14#[derive(Default)]
15pub struct StdFileSystem;
16
17impl FileSystem for StdFileSystem {
18    fn open_file(
19        &self,
20        _waker: std::task::Waker,
21        path: &Path,
22        open_mode: &FileOpenMode,
23    ) -> CancelablePoll<std::io::Result<Handle>> {
24        ready(|| {
25            let mut ops = OpenOptions::new();
26
27            if open_mode.contains(FileOpenMode::Create) {
28                ops.create(true);
29            }
30
31            if open_mode.contains(FileOpenMode::CreateNew) {
32                ops.create_new(true);
33            }
34
35            if open_mode.contains(FileOpenMode::Append) {
36                ops.append(true);
37            }
38
39            if open_mode.contains(FileOpenMode::Readable) {
40                ops.read(true);
41            }
42
43            if open_mode.contains(FileOpenMode::Truncate) {
44                ops.truncate(true);
45            }
46
47            if open_mode.contains(FileOpenMode::Writable) {
48                ops.write(true);
49            }
50
51            let file = ops.open(path)?;
52
53            Ok(Handle::new(file))
54        })
55    }
56
57    fn file_write(
58        &self,
59        _waker: std::task::Waker,
60        file: &Handle,
61        buf: &[u8],
62    ) -> CancelablePoll<std::io::Result<usize>> {
63        let mut file = file.downcast::<File>().expect("Expect std::fs::File");
64
65        ready(|| file.write(buf))
66    }
67
68    fn file_read(
69        &self,
70        _waker: std::task::Waker,
71        file: &Handle,
72        buf: &mut [u8],
73    ) -> CancelablePoll<std::io::Result<usize>> {
74        let mut file = file.downcast::<File>().expect("Expect std::fs::File");
75
76        ready(|| file.read(buf))
77    }
78
79    fn file_flush(
80        &self,
81        _waker: std::task::Waker,
82        file: &Handle,
83    ) -> CancelablePoll<std::io::Result<()>> {
84        let mut file = file.downcast::<File>().expect("Expect std::fs::File");
85
86        ready(|| file.flush())
87    }
88
89    fn file_seek(
90        &self,
91        _waker: std::task::Waker,
92        file: &Handle,
93        pos: std::io::SeekFrom,
94    ) -> CancelablePoll<std::io::Result<u64>> {
95        let mut file = file.downcast::<File>().expect("Expect std::fs::File");
96
97        ready(|| file.seek(pos))
98    }
99
100    fn file_meta(
101        &self,
102        _waker: std::task::Waker,
103        file: &Handle,
104    ) -> CancelablePoll<std::io::Result<std::fs::Metadata>> {
105        let file = file.downcast::<File>().expect("Expect std::fs::File");
106
107        ready(|| file.metadata())
108    }
109
110    fn file_set_permissions(
111        &self,
112        _waker: std::task::Waker,
113        file: &Handle,
114        perm: &std::fs::Permissions,
115    ) -> CancelablePoll<std::io::Result<()>> {
116        let file = file.downcast::<File>().expect("Expect std::fs::File");
117
118        ready(|| file.set_permissions(perm.clone()))
119    }
120
121    fn file_set_len(
122        &self,
123        _waker: std::task::Waker,
124        file: &Handle,
125        size: u64,
126    ) -> CancelablePoll<std::io::Result<()>> {
127        let file = file.downcast::<File>().expect("Expect std::fs::File");
128
129        ready(|| file.set_len(size))
130    }
131
132    fn canonicalize(
133        &self,
134        _waker: std::task::Waker,
135        path: &Path,
136    ) -> CancelablePoll<std::io::Result<PathBuf>> {
137        let path: &std::path::Path = path.as_ref();
138
139        ready(|| std::fs::canonicalize(path).map(Into::into))
140    }
141
142    fn copy(
143        &self,
144        _waker: std::task::Waker,
145        from: &Path,
146        to: &Path,
147    ) -> CancelablePoll<std::io::Result<u64>> {
148        ready(|| std::fs::copy(from, to))
149    }
150
151    fn create_dir(
152        &self,
153        _waker: std::task::Waker,
154        path: &Path,
155    ) -> CancelablePoll<std::io::Result<()>> {
156        ready(|| std::fs::create_dir(path))
157    }
158
159    fn create_dir_all(
160        &self,
161        _waker: std::task::Waker,
162        path: &Path,
163    ) -> CancelablePoll<std::io::Result<()>> {
164        ready(|| std::fs::create_dir_all(path))
165    }
166
167    fn hard_link(
168        &self,
169        _waker: std::task::Waker,
170        from: &Path,
171        to: &Path,
172    ) -> CancelablePoll<std::io::Result<()>> {
173        ready(|| std::fs::hard_link(from, to))
174    }
175
176    fn metadata(
177        &self,
178        _waker: std::task::Waker,
179        path: &Path,
180    ) -> CancelablePoll<std::io::Result<std::fs::Metadata>> {
181        ready(|| std::fs::metadata(path))
182    }
183
184    fn read_dir(
185        &self,
186        _waker: std::task::Waker,
187        path: &Path,
188    ) -> CancelablePoll<std::io::Result<Handle>> {
189        ready(|| {
190            let read_dir = std::fs::read_dir(path)?;
191
192            Ok(Handle::new(parking_lot::Mutex::new(read_dir)))
193        })
194    }
195
196    fn dir_entry_next(
197        &self,
198        _waker: std::task::Waker,
199        read_dir_handle: &Handle,
200    ) -> CancelablePoll<std::io::Result<Option<Handle>>> {
201        let read_dir = read_dir_handle
202            .downcast::<parking_lot::Mutex<ReadDir>>()
203            .expect("Expect ReadDir");
204
205        ready(|| {
206            if let Some(next) = read_dir.lock().next() {
207                match next {
208                    Ok(next) => Ok(Some(Handle::new(next))),
209                    Err(err) => Err(err),
210                }
211            } else {
212                Ok(None)
213            }
214        })
215    }
216
217    fn dir_entry_file_name(&self, entry: &Handle) -> String {
218        let dir_entry = entry.downcast::<DirEntry>().expect("Expect ReadDir");
219
220        dir_entry.file_name().to_string_lossy().to_string()
221    }
222
223    fn dir_entry_path(&self, entry: &Handle) -> PathBuf {
224        let dir_entry = entry.downcast::<DirEntry>().expect("Expect ReadDir");
225
226        dir_entry.path().into()
227    }
228
229    fn dir_entry_metadata(
230        &self,
231        _waker: std::task::Waker,
232        entry: &Handle,
233    ) -> CancelablePoll<std::io::Result<std::fs::Metadata>> {
234        let dir_entry = entry.downcast::<DirEntry>().expect("Expect ReadDir");
235
236        ready(|| dir_entry.metadata())
237    }
238
239    fn dir_entry_file_type(
240        &self,
241        _waker: std::task::Waker,
242        entry: &Handle,
243    ) -> CancelablePoll<std::io::Result<std::fs::FileType>> {
244        let dir_entry = entry.downcast::<DirEntry>().expect("Expect ReadDir");
245
246        ready(|| dir_entry.file_type())
247    }
248
249    fn read_link(
250        &self,
251        _waker: std::task::Waker,
252        path: &Path,
253    ) -> CancelablePoll<std::io::Result<PathBuf>> {
254        let path: &std::path::Path = path.as_ref();
255        ready(|| std::fs::read_link(path).map(Into::into))
256    }
257
258    fn remove_dir(
259        &self,
260        _waker: std::task::Waker,
261        path: &Path,
262    ) -> CancelablePoll<std::io::Result<()>> {
263        ready(|| std::fs::remove_dir(path))
264    }
265
266    fn remove_dir_all(
267        &self,
268        _waker: std::task::Waker,
269        path: &Path,
270    ) -> CancelablePoll<std::io::Result<()>> {
271        ready(|| std::fs::remove_dir_all(path))
272    }
273
274    fn remove_file(
275        &self,
276        _waker: std::task::Waker,
277        path: &Path,
278    ) -> CancelablePoll<std::io::Result<()>> {
279        ready(|| std::fs::remove_file(path))
280    }
281
282    fn rename(
283        &self,
284        _waker: std::task::Waker,
285        from: &Path,
286        to: &Path,
287    ) -> CancelablePoll<std::io::Result<()>> {
288        ready(|| std::fs::rename(from, to))
289    }
290
291    fn set_permissions(
292        &self,
293        _waker: std::task::Waker,
294        path: &Path,
295        perm: &std::fs::Permissions,
296    ) -> CancelablePoll<std::io::Result<()>> {
297        ready(|| std::fs::set_permissions(path, perm.clone()))
298    }
299
300    fn symlink_metadata(
301        &self,
302        _waker: std::task::Waker,
303        path: &Path,
304    ) -> CancelablePoll<std::io::Result<std::fs::Metadata>> {
305        ready(|| std::fs::symlink_metadata(path))
306    }
307}
308
309/// This function using [`register_global_filesystem`] to register the [`StdFileSystem`] to global registry.
310///
311/// So you may not call this function twice, otherwise will cause a panic. [`read more`](`register_global_filesystem`)
312pub fn register_std_filesystem() {
313    register_global_filesystem(StdFileSystem)
314}
315
316#[cfg(all(windows, feature = "windows_named_pipe"))]
317mod windows {
318    use crate::{
319        reactor::{global_reactor, would_block, MioSocket},
320        TokenSequence,
321    };
322
323    use super::ready;
324
325    use std::{
326        ffi::OsStr,
327        io::{self, Read, Write},
328        ops::Deref,
329        os::windows::{ffi::OsStrExt, io::FromRawHandle},
330        ptr::null,
331    };
332
333    use mio::{Interest, Token};
334    use rasi_syscall::{register_global_named_pipe, Handle, NamedPipe};
335    use windows_sys::Win32::{
336        Foundation::{GENERIC_READ, GENERIC_WRITE, INVALID_HANDLE_VALUE},
337        Storage::FileSystem::{
338            CreateFileW, FILE_FLAG_OVERLAPPED, OPEN_EXISTING, PIPE_ACCESS_DUPLEX,
339        },
340        System::Pipes::{
341            CreateNamedPipeW, PIPE_REJECT_REMOTE_CLIENTS, PIPE_TYPE_BYTE, PIPE_UNLIMITED_INSTANCES,
342        },
343    };
344
345    /// This function using [`register_global_filesystem`] to register the [`StdFileSystem`] to global registry.
346    ///
347    /// So you may not call this function twice, otherwise will cause a panic. [`read more`](`register_global_filesystem`)
348    pub fn register_mio_named_pipe() {
349        register_global_named_pipe(MioNamedPipe::default())
350    }
351
352    pub struct MioNamedPipe {
353        buffer_size: u32,
354    }
355
356    impl Default for MioNamedPipe {
357        fn default() -> Self {
358            Self { buffer_size: 512 }
359        }
360    }
361
362    impl MioNamedPipe {
363        /// Create default `MioNamedPipe` configuration.
364        pub fn new() -> Self {
365            Default::default()
366        }
367
368        pub fn register(self) {
369            register_global_named_pipe(self);
370        }
371    }
372
373    fn encode_addr(addr: &OsStr) -> Box<[u16]> {
374        let len = addr.encode_wide().count();
375        let mut vec = Vec::with_capacity(len + 1);
376        vec.extend(addr.encode_wide());
377        vec.push(0);
378        vec.into_boxed_slice()
379    }
380
381    impl NamedPipe for MioNamedPipe {
382        fn client_open(
383            &self,
384            _waker: std::task::Waker,
385            addr: &std::ffi::OsStr,
386        ) -> rasi_syscall::CancelablePoll<std::io::Result<rasi_syscall::Handle>> {
387            let addr = encode_addr(addr);
388
389            let desired_access = GENERIC_READ | GENERIC_WRITE;
390
391            let flag = FILE_FLAG_OVERLAPPED;
392
393            ready(|| unsafe {
394                let handle = CreateFileW(
395                    addr.as_ptr(),
396                    desired_access,
397                    0,
398                    null(),
399                    OPEN_EXISTING,
400                    flag,
401                    0,
402                );
403
404                if handle == INVALID_HANDLE_VALUE {
405                    return Err(io::Error::last_os_error());
406                }
407
408                let mut socket = mio::windows::NamedPipe::from_raw_handle(handle as _);
409
410                let token = Token::next();
411
412                global_reactor().register(
413                    &mut socket,
414                    token,
415                    Interest::READABLE.add(Interest::WRITABLE),
416                )?;
417
418                Ok(Handle::new(MioSocket::from((token, socket))))
419            })
420        }
421
422        fn server_create(
423            &self,
424            _waker: std::task::Waker,
425            addr: &std::ffi::OsStr,
426        ) -> rasi_syscall::CancelablePoll<std::io::Result<rasi_syscall::Handle>> {
427            let addr = encode_addr(addr);
428
429            let pipe_mode = PIPE_TYPE_BYTE | PIPE_REJECT_REMOTE_CLIENTS;
430
431            let open_mode = FILE_FLAG_OVERLAPPED | PIPE_ACCESS_DUPLEX;
432
433            ready(|| unsafe {
434                let handle = CreateNamedPipeW(
435                    addr.as_ptr(),
436                    open_mode,
437                    pipe_mode,
438                    PIPE_UNLIMITED_INSTANCES,
439                    self.buffer_size,
440                    self.buffer_size,
441                    0,
442                    null(),
443                );
444
445                if handle == INVALID_HANDLE_VALUE {
446                    return Err(io::Error::last_os_error());
447                }
448
449                let mut socket = mio::windows::NamedPipe::from_raw_handle(handle as _);
450
451                let token = Token::next();
452
453                global_reactor().register(
454                    &mut socket,
455                    token,
456                    Interest::READABLE.add(Interest::WRITABLE),
457                )?;
458
459                Ok(Handle::new(MioSocket::from((token, socket))))
460            })
461        }
462
463        fn server_accept(
464            &self,
465            waker: std::task::Waker,
466            socket: &rasi_syscall::Handle,
467        ) -> rasi_syscall::CancelablePoll<std::io::Result<()>> {
468            let socket = socket
469                .downcast::<MioSocket<mio::windows::NamedPipe>>()
470                .expect("Expect NamedPipe.");
471
472            would_block(socket.token, waker, Interest::WRITABLE, || socket.connect())
473        }
474
475        fn server_disconnect(&self, socket: &rasi_syscall::Handle) -> std::io::Result<()> {
476            let socket = socket
477                .downcast::<MioSocket<mio::windows::NamedPipe>>()
478                .expect("Expect NamedPipe.");
479
480            socket.disconnect()
481        }
482
483        fn write(
484            &self,
485            waker: std::task::Waker,
486            socket: &rasi_syscall::Handle,
487            buf: &[u8],
488        ) -> rasi_syscall::CancelablePoll<std::io::Result<usize>> {
489            let socket = socket
490                .downcast::<MioSocket<mio::windows::NamedPipe>>()
491                .expect("Expect NamedPipe.");
492
493            would_block(socket.token, waker, Interest::WRITABLE, || {
494                socket.deref().write(buf)
495            })
496        }
497
498        fn read(
499            &self,
500            waker: std::task::Waker,
501            socket: &rasi_syscall::Handle,
502            buf: &mut [u8],
503        ) -> rasi_syscall::CancelablePoll<std::io::Result<usize>> {
504            let socket = socket
505                .downcast::<MioSocket<mio::windows::NamedPipe>>()
506                .expect("Expect NamedPipe.");
507
508            would_block(socket.token, waker, Interest::READABLE, || {
509                socket.deref().read(buf)
510            })
511        }
512    }
513}
514
515#[cfg(windows)]
516pub use windows::*;
517
518#[cfg(test)]
519mod tests {
520    use std::sync::OnceLock;
521
522    use rasi_spec::fs::run_fs_spec;
523
524    #[cfg(all(windows, feature = "windows_named_pipe"))]
525    use rasi_syscall::NamedPipe;
526
527    use super::*;
528
529    static INIT: OnceLock<Box<dyn FileSystem>> = OnceLock::new();
530
531    fn get_syscall() -> &'static dyn FileSystem {
532        INIT.get_or_init(|| Box::new(StdFileSystem::default()))
533            .as_ref()
534    }
535
536    #[futures_test::test]
537    async fn test_std_fs() {
538        run_fs_spec(get_syscall()).await;
539    }
540
541    #[cfg(all(windows, feature = "windows_named_pipe"))]
542    static INIT_NAMED_PIPE: OnceLock<Box<dyn NamedPipe>> = OnceLock::new();
543
544    #[cfg(all(windows, feature = "windows_named_pipe"))]
545    fn get_named_pipe_syscall() -> &'static dyn NamedPipe {
546        INIT_NAMED_PIPE
547            .get_or_init(|| Box::new(MioNamedPipe::default()))
548            .as_ref()
549    }
550
551    #[cfg(all(windows, feature = "windows_named_pipe"))]
552    #[futures_test::test]
553    async fn test_named_pipe() {
554        use rasi_spec::windows::run_windows_spec;
555
556        run_windows_spec(get_named_pipe_syscall()).await;
557    }
558}