1use 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#[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
309pub 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 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 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}