1use std::borrow::Cow;
4use std::cell::RefCell;
5use std::fs::File as StdFile;
6use std::future::Future;
7use std::io;
8use std::io::ErrorKind;
9use std::io::Read;
10use std::io::Seek;
11use std::io::Write;
12#[cfg(unix)]
13use std::os::unix::io::FromRawFd;
14#[cfg(windows)]
15use std::os::windows::io::FromRawHandle;
16use std::path::Path;
17use std::path::PathBuf;
18#[cfg(unix)]
19use std::process::Stdio as StdStdio;
20use std::rc::Rc;
21#[cfg(windows)]
22use std::sync::Arc;
23
24use deno_core::AsyncMutFuture;
25use deno_core::AsyncRefCell;
26use deno_core::AsyncResult;
27use deno_core::BufMutView;
28use deno_core::BufView;
29use deno_core::CancelFuture;
30use deno_core::CancelHandle;
31use deno_core::CancelTryFuture;
32use deno_core::JsBuffer;
33use deno_core::OpState;
34use deno_core::RcRef;
35use deno_core::Resource;
36use deno_core::ResourceHandle;
37use deno_core::ResourceHandleFd;
38use deno_core::futures::TryFutureExt;
39use deno_core::op2;
40use deno_core::unsync::TaskQueue;
41use deno_core::unsync::spawn_blocking;
42use deno_error::JsErrorBox;
43#[cfg(windows)]
44use deno_subprocess_windows::Stdio as StdStdio;
45use fs::FileResource;
46use fs::FsError;
47use fs::FsResult;
48use fs::FsStat;
49use once_cell::sync::Lazy;
50#[cfg(windows)]
51use parking_lot::Condvar;
52#[cfg(windows)]
53use parking_lot::Mutex;
54use tokio::io::AsyncRead;
55use tokio::io::AsyncReadExt;
56use tokio::io::AsyncWrite;
57use tokio::io::AsyncWriteExt;
58use tokio::process;
59#[cfg(windows)]
60use winapi::um::processenv::GetStdHandle;
61#[cfg(windows)]
62use winapi::um::winbase;
63
64pub mod fs;
65mod pipe;
66#[cfg(windows)]
67mod winpipe;
68
69mod bi_pipe;
70
71pub use bi_pipe::BiPipe;
72pub use bi_pipe::BiPipeRead;
73pub use bi_pipe::BiPipeResource;
74pub use bi_pipe::BiPipeWrite;
75pub use bi_pipe::RawBiPipeHandle;
76pub use bi_pipe::bi_pipe_pair_raw;
77pub use pipe::AsyncPipeRead;
78pub use pipe::AsyncPipeWrite;
79pub use pipe::PipeRead;
80pub use pipe::PipeWrite;
81pub use pipe::RawPipeHandle;
82pub use pipe::pipe;
83
84pub trait AsRawIoHandle {
86 fn as_raw_io_handle(&self) -> RawIoHandle;
87}
88
89#[cfg(unix)]
90impl<T> AsRawIoHandle for T
91where
92 T: std::os::unix::io::AsRawFd,
93{
94 fn as_raw_io_handle(&self) -> RawIoHandle {
95 self.as_raw_fd()
96 }
97}
98
99#[cfg(windows)]
100impl<T> AsRawIoHandle for T
101where
102 T: std::os::windows::io::AsRawHandle,
103{
104 fn as_raw_io_handle(&self) -> RawIoHandle {
105 self.as_raw_handle()
106 }
107}
108
109pub trait IntoRawIoHandle {
111 fn into_raw_io_handle(self) -> RawIoHandle;
112}
113
114#[cfg(unix)]
115impl<T> IntoRawIoHandle for T
116where
117 T: std::os::unix::io::IntoRawFd,
118{
119 fn into_raw_io_handle(self) -> RawIoHandle {
120 self.into_raw_fd()
121 }
122}
123
124#[cfg(windows)]
125impl<T> IntoRawIoHandle for T
126where
127 T: std::os::windows::io::IntoRawHandle,
128{
129 fn into_raw_io_handle(self) -> RawIoHandle {
130 self.into_raw_handle()
131 }
132}
133
134pub trait FromRawIoHandle: Sized {
136 unsafe fn from_raw_io_handle(handle: RawIoHandle) -> Self;
143}
144
145#[cfg(unix)]
146impl<T> FromRawIoHandle for T
147where
148 T: std::os::unix::io::FromRawFd,
149{
150 unsafe fn from_raw_io_handle(fd: RawIoHandle) -> T {
151 unsafe { T::from_raw_fd(fd) }
153 }
154}
155
156#[cfg(windows)]
157impl<T> FromRawIoHandle for T
158where
159 T: std::os::windows::io::FromRawHandle,
160{
161 unsafe fn from_raw_io_handle(fd: RawIoHandle) -> T {
162 unsafe { T::from_raw_handle(fd) }
164 }
165}
166
167#[cfg(unix)]
168pub type RawIoHandle = std::os::fd::RawFd;
169
170#[cfg(windows)]
171pub type RawIoHandle = std::os::windows::io::RawHandle;
172
173pub fn close_raw_handle(handle: RawIoHandle) {
174 #[cfg(unix)]
175 {
176 unsafe {
178 libc::close(handle);
179 }
180 }
181 #[cfg(windows)]
182 {
183 unsafe {
185 windows_sys::Win32::Foundation::CloseHandle(handle as _);
186 }
187 }
188}
189
190#[cfg(unix)]
194pub static STDIN_HANDLE: Lazy<StdFile> = Lazy::new(|| {
195 unsafe { StdFile::from_raw_fd(0) }
197});
198#[cfg(unix)]
199pub static STDOUT_HANDLE: Lazy<StdFile> = Lazy::new(|| {
200 unsafe { StdFile::from_raw_fd(1) }
202});
203#[cfg(unix)]
204pub static STDERR_HANDLE: Lazy<StdFile> = Lazy::new(|| {
205 unsafe { StdFile::from_raw_fd(2) }
207});
208
209#[cfg(windows)]
210pub static STDIN_HANDLE: Lazy<StdFile> = Lazy::new(|| {
211 unsafe { StdFile::from_raw_handle(GetStdHandle(winbase::STD_INPUT_HANDLE)) }
213});
214#[cfg(windows)]
215pub static STDOUT_HANDLE: Lazy<StdFile> = Lazy::new(|| {
216 unsafe { StdFile::from_raw_handle(GetStdHandle(winbase::STD_OUTPUT_HANDLE)) }
218});
219#[cfg(windows)]
220pub static STDERR_HANDLE: Lazy<StdFile> = Lazy::new(|| {
221 unsafe { StdFile::from_raw_handle(GetStdHandle(winbase::STD_ERROR_HANDLE)) }
223});
224
225deno_core::extension!(deno_io,
226 deps = [ deno_web ],
227 ops = [
228 op_read_with_cancel_handle,
229 op_read_create_cancel_handle,
230 ],
231 esm = [ "12_io.js" ],
232 options = {
233 stdio: Option<Stdio>,
234 },
235 middleware = |op| match op.name {
236 "op_print" => op_print(),
237 _ => op,
238 },
239 state = |state, options| {
240 if let Some(stdio) = options.stdio {
241 #[cfg(windows)]
242 let stdin_state = {
243 let st = Arc::new(Mutex::new(WinTtyState::default()));
244 state.put(st.clone());
245 st
246 };
247 #[cfg(unix)]
248 let stdin_state = ();
249
250 let t = &mut state.resource_table;
251
252 let rid = t.add(fs::FileResource::new(
253 Rc::new(match stdio.stdin.pipe {
254 StdioPipeInner::Inherit => StdFileResourceInner::new(
255 StdFileResourceKind::Stdin(stdin_state),
256 STDIN_HANDLE.try_clone().unwrap(),
257 None,
258 ),
259 StdioPipeInner::File(pipe) => StdFileResourceInner::file(pipe, None),
260 }),
261 "stdin".to_string(),
262 ));
263 assert_eq!(rid, 0, "stdin must have ResourceId 0");
264
265 let rid = t.add(FileResource::new(
266 Rc::new(match stdio.stdout.pipe {
267 StdioPipeInner::Inherit => StdFileResourceInner::new(
268 StdFileResourceKind::Stdout,
269 STDOUT_HANDLE.try_clone().unwrap(),
270 None,
271 ),
272 StdioPipeInner::File(pipe) => StdFileResourceInner::file(pipe, None),
273 }),
274 "stdout".to_string(),
275 ));
276 assert_eq!(rid, 1, "stdout must have ResourceId 1");
277
278 let rid = t.add(FileResource::new(
279 Rc::new(match stdio.stderr.pipe {
280 StdioPipeInner::Inherit => StdFileResourceInner::new(
281 StdFileResourceKind::Stderr,
282 STDERR_HANDLE.try_clone().unwrap(),
283 None,
284 ),
285 StdioPipeInner::File(pipe) => StdFileResourceInner::file(pipe, None),
286 }),
287 "stderr".to_string(),
288 ));
289 assert_eq!(rid, 2, "stderr must have ResourceId 2");
290 }
291 },
292);
293
294#[derive(Default)]
295pub struct StdioPipe {
296 pipe: StdioPipeInner,
297}
298
299impl StdioPipe {
300 pub const fn inherit() -> Self {
301 StdioPipe {
302 pipe: StdioPipeInner::Inherit,
303 }
304 }
305
306 pub fn file(f: impl Into<StdFile>) -> Self {
307 StdioPipe {
308 pipe: StdioPipeInner::File(f.into()),
309 }
310 }
311}
312
313#[derive(Default)]
314enum StdioPipeInner {
315 #[default]
316 Inherit,
317 File(StdFile),
318}
319
320impl Clone for StdioPipe {
321 fn clone(&self) -> Self {
322 match &self.pipe {
323 StdioPipeInner::Inherit => Self {
324 pipe: StdioPipeInner::Inherit,
325 },
326 StdioPipeInner::File(pipe) => Self {
327 pipe: StdioPipeInner::File(pipe.try_clone().unwrap()),
328 },
329 }
330 }
331}
332
333#[derive(Clone, Default)]
336pub struct Stdio {
337 pub stdin: StdioPipe,
338 pub stdout: StdioPipe,
339 pub stderr: StdioPipe,
340}
341
342#[derive(Debug)]
343pub struct WriteOnlyResource<S> {
344 stream: AsyncRefCell<S>,
345}
346
347impl<S: 'static> From<S> for WriteOnlyResource<S> {
348 fn from(stream: S) -> Self {
349 Self {
350 stream: stream.into(),
351 }
352 }
353}
354
355impl<S> WriteOnlyResource<S>
356where
357 S: AsyncWrite + Unpin + 'static,
358{
359 pub fn borrow_mut(self: &Rc<Self>) -> AsyncMutFuture<S> {
360 RcRef::map(self, |r| &r.stream).borrow_mut()
361 }
362
363 async fn write(self: Rc<Self>, data: &[u8]) -> Result<usize, io::Error> {
364 let mut stream = self.borrow_mut().await;
365 let nwritten = stream.write(data).await?;
366 Ok(nwritten)
367 }
368
369 async fn shutdown(self: Rc<Self>) -> Result<(), io::Error> {
370 let mut stream = self.borrow_mut().await;
371 stream.shutdown().await?;
372 Ok(())
373 }
374
375 pub fn into_inner(self) -> S {
376 self.stream.into_inner()
377 }
378}
379
380#[derive(Debug)]
381pub struct ReadOnlyResource<S> {
382 stream: AsyncRefCell<S>,
383 cancel_handle: CancelHandle,
384}
385
386impl<S: 'static> From<S> for ReadOnlyResource<S> {
387 fn from(stream: S) -> Self {
388 Self {
389 stream: stream.into(),
390 cancel_handle: Default::default(),
391 }
392 }
393}
394
395impl<S> ReadOnlyResource<S>
396where
397 S: AsyncRead + Unpin + 'static,
398{
399 pub fn borrow_mut(self: &Rc<Self>) -> AsyncMutFuture<S> {
400 RcRef::map(self, |r| &r.stream).borrow_mut()
401 }
402
403 pub fn cancel_handle(self: &Rc<Self>) -> RcRef<CancelHandle> {
404 RcRef::map(self, |r| &r.cancel_handle)
405 }
406
407 pub fn cancel_read_ops(&self) {
408 self.cancel_handle.cancel()
409 }
410
411 async fn read(self: Rc<Self>, data: &mut [u8]) -> Result<usize, io::Error> {
412 let mut rd = self.borrow_mut().await;
413 let nread = rd.read(data).try_or_cancel(self.cancel_handle()).await?;
414 Ok(nread)
415 }
416
417 pub fn into_inner(self) -> S {
418 self.stream.into_inner()
419 }
420}
421
422pub type ChildStdinResource = WriteOnlyResource<process::ChildStdin>;
423
424impl Resource for ChildStdinResource {
425 fn name(&self) -> Cow<'_, str> {
426 "childStdin".into()
427 }
428
429 deno_core::impl_writable!();
430
431 fn shutdown(self: Rc<Self>) -> AsyncResult<()> {
432 Box::pin(self.shutdown().map_err(JsErrorBox::from_err))
433 }
434}
435
436pub type ChildStdoutResource = ReadOnlyResource<process::ChildStdout>;
437
438impl Resource for ChildStdoutResource {
439 deno_core::impl_readable_byob!();
440
441 fn name(&self) -> Cow<'_, str> {
442 "childStdout".into()
443 }
444
445 fn close(self: Rc<Self>) {
446 self.cancel_read_ops();
447 }
448}
449
450pub type ChildStderrResource = ReadOnlyResource<process::ChildStderr>;
451
452impl Resource for ChildStderrResource {
453 deno_core::impl_readable_byob!();
454
455 fn name(&self) -> Cow<'_, str> {
456 "childStderr".into()
457 }
458
459 fn close(self: Rc<Self>) {
460 self.cancel_read_ops();
461 }
462}
463
464#[cfg(windows)]
465#[derive(Default)]
466pub struct WinTtyState {
467 pub cancelled: bool,
468 pub reading: bool,
469 pub screen_buffer_info:
470 Option<winapi::um::wincon::CONSOLE_SCREEN_BUFFER_INFO>,
471 pub cvar: Arc<Condvar>,
472}
473
474#[derive(Clone)]
475enum StdFileResourceKind {
476 File,
477 #[cfg(windows)]
482 Stdin(Arc<Mutex<WinTtyState>>),
483 #[cfg(not(windows))]
484 Stdin(()),
485 Stdout,
486 Stderr,
487}
488
489pub struct StdFileResourceInner {
490 kind: StdFileResourceKind,
491 cell: RefCell<Option<StdFile>>,
495 cell_async_task_queue: Rc<TaskQueue>,
498 handle: ResourceHandleFd,
499 maybe_path: Option<PathBuf>,
500}
501
502impl StdFileResourceInner {
503 pub fn file(fs_file: StdFile, maybe_path: Option<PathBuf>) -> Self {
504 StdFileResourceInner::new(StdFileResourceKind::File, fs_file, maybe_path)
505 }
506
507 fn new(
508 kind: StdFileResourceKind,
509 fs_file: StdFile,
510 maybe_path: Option<PathBuf>,
511 ) -> Self {
512 let handle = ResourceHandle::from_fd_like(&fs_file).as_fd_like().unwrap();
514 StdFileResourceInner {
515 kind,
516 handle,
517 cell: RefCell::new(Some(fs_file)),
518 cell_async_task_queue: Default::default(),
519 maybe_path,
520 }
521 }
522
523 fn with_sync<F, R>(&self, action: F) -> FsResult<R>
524 where
525 F: FnOnce(&mut StdFile) -> FsResult<R>,
526 {
527 match self.cell.try_borrow_mut() {
528 Ok(mut cell) if cell.is_some() => action(cell.as_mut().unwrap()),
529 _ => Err(fs::FsError::FileBusy),
530 }
531 }
532
533 fn with_inner_blocking_task<F, R: 'static + Send>(
534 &self,
535 action: F,
536 ) -> impl Future<Output = R> + '_
537 where
538 F: FnOnce(&mut StdFile) -> R + Send + 'static,
539 {
540 let acquire_fut = self.cell_async_task_queue.acquire();
542 async move {
543 let permit = acquire_fut.await;
544 let mut did_take = false;
547 let mut cell_value = {
548 let mut cell = self.cell.borrow_mut();
549 match cell.as_mut().unwrap().try_clone().ok() {
550 Some(value) => value,
551 None => {
552 did_take = true;
553 cell.take().unwrap()
554 }
555 }
556 };
557 let (cell_value, result) = spawn_blocking(move || {
558 let result = action(&mut cell_value);
559 (cell_value, result)
560 })
561 .await
562 .unwrap();
563
564 if did_take {
565 self.cell.borrow_mut().replace(cell_value);
567 }
568
569 drop(permit); result
571 }
572 }
573
574 fn with_blocking_task<F, R: 'static + Send>(
575 &self,
576 action: F,
577 ) -> impl Future<Output = R> + use<F, R>
578 where
579 F: FnOnce() -> R + Send + 'static,
580 {
581 let acquire_fut = self.cell_async_task_queue.acquire();
583 async move {
584 let _permit = acquire_fut.await;
585 spawn_blocking(action).await.unwrap()
586 }
587 }
588
589 #[cfg(windows)]
590 async fn handle_stdin_read(
591 &self,
592 state: Arc<Mutex<WinTtyState>>,
593 mut buf: BufMutView,
594 ) -> FsResult<(usize, BufMutView)> {
595 loop {
596 let state = state.clone();
597
598 let fut = self.with_inner_blocking_task(move |file| {
599 state.lock().reading = true;
601 let nread = match file.read(&mut buf) {
602 Ok(nread) => nread,
603 Err(e) => return Err((e.into(), buf)),
604 };
605
606 let mut state = state.lock();
607 state.reading = false;
608
609 if state.cancelled {
612 if let Some(screen_buffer_info) = state.screen_buffer_info {
613 unsafe {
615 let handle = winapi::um::fileapi::CreateFileW(
616 "conout$"
617 .encode_utf16()
618 .chain(Some(0))
619 .collect::<Vec<_>>()
620 .as_ptr(),
621 winapi::um::winnt::GENERIC_READ
622 | winapi::um::winnt::GENERIC_WRITE,
623 winapi::um::winnt::FILE_SHARE_READ
624 | winapi::um::winnt::FILE_SHARE_WRITE,
625 std::ptr::null_mut(),
626 winapi::um::fileapi::OPEN_EXISTING,
627 0,
628 std::ptr::null_mut(),
629 );
630
631 let mut pos = screen_buffer_info.dwCursorPosition;
632 if pos.Y == screen_buffer_info.dwSize.Y - 1 {
637 pos.Y -= 1;
638 }
639
640 winapi::um::wincon::SetConsoleCursorPosition(handle, pos);
641 winapi::um::handleapi::CloseHandle(handle);
642 }
643 }
644
645 state.cancelled = false;
647
648 state.cvar.notify_one();
650
651 return Err((FsError::FileBusy, buf));
652 }
653
654 Ok((nread, buf))
655 });
656
657 match fut.await {
658 Err((FsError::FileBusy, b)) => {
659 buf = b;
660 continue;
661 }
662 other => return other.map_err(|(e, _)| e),
663 }
664 }
665 }
666}
667
668#[async_trait::async_trait(?Send)]
669impl crate::fs::File for StdFileResourceInner {
670 fn maybe_path(&self) -> Option<&Path> {
671 self.maybe_path.as_deref()
672 }
673
674 fn write_sync(self: Rc<Self>, buf: &[u8]) -> FsResult<usize> {
675 match self.kind {
682 StdFileResourceKind::File => self.with_sync(|file| Ok(file.write(buf)?)),
683 StdFileResourceKind::Stdin(_) => {
684 Err(Into::<std::io::Error>::into(ErrorKind::Unsupported).into())
685 }
686 StdFileResourceKind::Stdout => {
687 let mut stdout = std::io::stdout().lock();
689 let nwritten = stdout.write(buf)?;
690 stdout.flush()?;
691 Ok(nwritten)
692 }
693 StdFileResourceKind::Stderr => {
694 let mut stderr = std::io::stderr().lock();
696 let nwritten = stderr.write(buf)?;
697 stderr.flush()?;
698 Ok(nwritten)
699 }
700 }
701 }
702
703 fn read_sync(self: Rc<Self>, buf: &mut [u8]) -> FsResult<usize> {
704 match self.kind {
705 StdFileResourceKind::File | StdFileResourceKind::Stdin(_) => {
706 self.with_sync(|file| Ok(file.read(buf)?))
707 }
708 StdFileResourceKind::Stdout | StdFileResourceKind::Stderr => {
709 Err(FsError::NotSupported)
710 }
711 }
712 }
713
714 fn write_all_sync(self: Rc<Self>, buf: &[u8]) -> FsResult<()> {
715 match self.kind {
716 StdFileResourceKind::File => {
717 self.with_sync(|file| Ok(file.write_all(buf)?))
718 }
719 StdFileResourceKind::Stdin(_) => {
720 Err(Into::<std::io::Error>::into(ErrorKind::Unsupported).into())
721 }
722 StdFileResourceKind::Stdout => {
723 let mut stdout = std::io::stdout().lock();
725 stdout.write_all(buf)?;
726 stdout.flush()?;
727 Ok(())
728 }
729 StdFileResourceKind::Stderr => {
730 let mut stderr = std::io::stderr().lock();
732 stderr.write_all(buf)?;
733 stderr.flush()?;
734 Ok(())
735 }
736 }
737 }
738 async fn write_all(self: Rc<Self>, buf: BufView) -> FsResult<()> {
739 match self.kind {
740 StdFileResourceKind::File => {
741 self
742 .with_inner_blocking_task(move |file| Ok(file.write_all(&buf)?))
743 .await
744 }
745 StdFileResourceKind::Stdin(_) => {
746 Err(Into::<std::io::Error>::into(ErrorKind::Unsupported).into())
747 }
748 StdFileResourceKind::Stdout => {
749 self
750 .with_blocking_task(move || {
751 let mut stdout = std::io::stdout().lock();
753 stdout.write_all(&buf)?;
754 stdout.flush()?;
755 Ok(())
756 })
757 .await
758 }
759 StdFileResourceKind::Stderr => {
760 self
761 .with_blocking_task(move || {
762 let mut stderr = std::io::stderr().lock();
764 stderr.write_all(&buf)?;
765 stderr.flush()?;
766 Ok(())
767 })
768 .await
769 }
770 }
771 }
772
773 async fn write(
774 self: Rc<Self>,
775 view: BufView,
776 ) -> FsResult<deno_core::WriteOutcome> {
777 match self.kind {
778 StdFileResourceKind::File => {
779 self
780 .with_inner_blocking_task(|file| {
781 let nwritten = file.write(&view)?;
782 Ok(deno_core::WriteOutcome::Partial { nwritten, view })
783 })
784 .await
785 }
786 StdFileResourceKind::Stdin(_) => {
787 Err(Into::<std::io::Error>::into(ErrorKind::Unsupported).into())
788 }
789 StdFileResourceKind::Stdout => {
790 self
791 .with_blocking_task(|| {
792 let mut stdout = std::io::stdout().lock();
794 let nwritten = stdout.write(&view)?;
795 stdout.flush()?;
796 Ok(deno_core::WriteOutcome::Partial { nwritten, view })
797 })
798 .await
799 }
800 StdFileResourceKind::Stderr => {
801 self
802 .with_blocking_task(|| {
803 let mut stderr = std::io::stderr().lock();
805 let nwritten = stderr.write(&view)?;
806 stderr.flush()?;
807 Ok(deno_core::WriteOutcome::Partial { nwritten, view })
808 })
809 .await
810 }
811 }
812 }
813
814 fn read_all_sync(self: Rc<Self>) -> FsResult<Cow<'static, [u8]>> {
815 match self.kind {
816 StdFileResourceKind::File | StdFileResourceKind::Stdin(_) => {
817 let mut buf = Vec::new();
818 self.with_sync(|file| Ok(file.read_to_end(&mut buf)?))?;
819 Ok(Cow::Owned(buf))
820 }
821 StdFileResourceKind::Stdout | StdFileResourceKind::Stderr => {
822 Err(FsError::NotSupported)
823 }
824 }
825 }
826 async fn read_all_async(self: Rc<Self>) -> FsResult<Cow<'static, [u8]>> {
827 match self.kind {
828 StdFileResourceKind::File | StdFileResourceKind::Stdin(_) => {
829 self
830 .with_inner_blocking_task(|file| {
831 let mut buf = Vec::new();
832 file.read_to_end(&mut buf)?;
833 Ok(Cow::Owned(buf))
834 })
835 .await
836 }
837 StdFileResourceKind::Stdout | StdFileResourceKind::Stderr => {
838 Err(FsError::NotSupported)
839 }
840 }
841 }
842
843 fn chmod_sync(self: Rc<Self>, mode: u32) -> FsResult<()> {
844 #[cfg(unix)]
845 {
846 use std::os::unix::prelude::PermissionsExt;
847 self.with_sync(|file| {
848 Ok(file.set_permissions(std::fs::Permissions::from_mode(mode))?)
849 })
850 }
851 #[cfg(windows)]
852 {
853 self.with_sync(|file| {
854 let mut permissions = file.metadata()?.permissions();
855 if mode & libc::S_IWRITE as u32 > 0 {
856 #[allow(
859 clippy::permissions_set_readonly_false,
860 reason = "only applicable to Unix platforms"
861 )]
862 permissions.set_readonly(false);
863 } else {
864 permissions.set_readonly(true);
865 }
866 file.set_permissions(permissions)?;
867 Ok(())
868 })
869 }
870 }
871 async fn chmod_async(self: Rc<Self>, mode: u32) -> FsResult<()> {
872 #[cfg(unix)]
873 {
874 use std::os::unix::prelude::PermissionsExt;
875 self
876 .with_inner_blocking_task(move |file| {
877 Ok(file.set_permissions(std::fs::Permissions::from_mode(mode))?)
878 })
879 .await
880 }
881 #[cfg(windows)]
882 {
883 self
884 .with_inner_blocking_task(move |file| {
885 let mut permissions = file.metadata()?.permissions();
886 if mode & libc::S_IWRITE as u32 > 0 {
887 #[allow(
890 clippy::permissions_set_readonly_false,
891 reason = "only applicable to Unix platforms"
892 )]
893 permissions.set_readonly(false);
894 } else {
895 permissions.set_readonly(true);
896 }
897 file.set_permissions(permissions)?;
898 Ok(())
899 })
900 .await
901 }
902 #[cfg(not(any(unix, windows)))]
903 {
904 Err(FsError::NotSupported)
905 }
906 }
907
908 fn chown_sync(
909 self: Rc<Self>,
910 _uid: Option<u32>,
911 _gid: Option<u32>,
912 ) -> FsResult<()> {
913 #[cfg(unix)]
914 {
915 let owner = _uid.map(nix::unistd::Uid::from_raw);
916 let group = _gid.map(nix::unistd::Gid::from_raw);
917 let raw_fd = unsafe { std::os::fd::BorrowedFd::borrow_raw(self.handle) };
919 let res = nix::unistd::fchown(raw_fd, owner, group);
920 if let Err(err) = res {
921 Err(io::Error::from_raw_os_error(err as i32).into())
922 } else {
923 Ok(())
924 }
925 }
926 #[cfg(not(unix))]
927 Err(FsError::NotSupported)
928 }
929
930 async fn chown_async(
931 self: Rc<Self>,
932 _uid: Option<u32>,
933 _gid: Option<u32>,
934 ) -> FsResult<()> {
935 #[cfg(unix)]
936 {
937 self
938 .with_inner_blocking_task(move |file| {
939 use std::os::fd::AsFd;
940 let owner = _uid.map(nix::unistd::Uid::from_raw);
941 let group = _gid.map(nix::unistd::Gid::from_raw);
942 nix::unistd::fchown(file.as_fd(), owner, group)
943 .map_err(|err| io::Error::from_raw_os_error(err as i32).into())
944 })
945 .await
946 }
947 #[cfg(not(unix))]
948 Err(FsError::NotSupported)
949 }
950
951 fn seek_sync(self: Rc<Self>, pos: io::SeekFrom) -> FsResult<u64> {
952 self.with_sync(|file| Ok(file.seek(pos)?))
953 }
954 async fn seek_async(self: Rc<Self>, pos: io::SeekFrom) -> FsResult<u64> {
955 self
956 .with_inner_blocking_task(move |file| Ok(file.seek(pos)?))
957 .await
958 }
959
960 fn datasync_sync(self: Rc<Self>) -> FsResult<()> {
961 self.with_sync(|file| Ok(file.sync_data()?))
962 }
963 async fn datasync_async(self: Rc<Self>) -> FsResult<()> {
964 self
965 .with_inner_blocking_task(|file| Ok(file.sync_data()?))
966 .await
967 }
968
969 fn sync_sync(self: Rc<Self>) -> FsResult<()> {
970 self.with_sync(|file| Ok(file.sync_all()?))
971 }
972 async fn sync_async(self: Rc<Self>) -> FsResult<()> {
973 self
974 .with_inner_blocking_task(|file| Ok(file.sync_all()?))
975 .await
976 }
977
978 fn stat_sync(self: Rc<Self>) -> FsResult<FsStat> {
979 #[cfg(unix)]
980 {
981 self.with_sync(|file| Ok(file.metadata().map(FsStat::from_std)?))
982 }
983 #[cfg(windows)]
984 {
985 self.with_sync(|file| {
986 let mut fs_stat = file.metadata().map(FsStat::from_std)?;
987 stat_extra(file, &mut fs_stat)?;
988 Ok(fs_stat)
989 })
990 }
991 #[cfg(not(any(unix, windows)))]
992 {
993 Err(FsError::NotSupported)
994 }
995 }
996 async fn stat_async(self: Rc<Self>) -> FsResult<FsStat> {
997 #[cfg(unix)]
998 {
999 self
1000 .with_inner_blocking_task(|file| {
1001 Ok(file.metadata().map(FsStat::from_std)?)
1002 })
1003 .await
1004 }
1005 #[cfg(windows)]
1006 {
1007 self
1008 .with_inner_blocking_task(|file| {
1009 let mut fs_stat = file.metadata().map(FsStat::from_std)?;
1010 stat_extra(file, &mut fs_stat)?;
1011 Ok(fs_stat)
1012 })
1013 .await
1014 }
1015 #[cfg(not(any(unix, windows)))]
1016 {
1017 Err(FsError::NotSupported)
1018 }
1019 }
1020
1021 fn lock_sync(self: Rc<Self>, exclusive: bool) -> FsResult<()> {
1022 self.with_sync(|file| {
1023 if exclusive {
1024 file.lock()?;
1025 } else {
1026 file.lock_shared()?;
1027 }
1028 Ok(())
1029 })
1030 }
1031 async fn lock_async(self: Rc<Self>, exclusive: bool) -> FsResult<()> {
1032 self
1033 .with_inner_blocking_task(move |file| {
1034 if exclusive {
1035 file.lock()?;
1036 } else {
1037 file.lock_shared()?;
1038 }
1039 Ok(())
1040 })
1041 .await
1042 }
1043
1044 fn try_lock_sync(self: Rc<Self>, exclusive: bool) -> FsResult<bool> {
1045 use std::fs::TryLockError;
1046 self.with_sync(|file| {
1047 let result = if exclusive {
1048 file.try_lock()
1049 } else {
1050 file.try_lock_shared()
1051 };
1052 match result {
1053 Ok(()) => Ok(true),
1054 Err(TryLockError::WouldBlock) => Ok(false),
1055 Err(TryLockError::Error(err)) => Err(err.into()),
1056 }
1057 })
1058 }
1059 async fn try_lock_async(self: Rc<Self>, exclusive: bool) -> FsResult<bool> {
1060 use std::fs::TryLockError;
1061 self
1062 .with_inner_blocking_task(move |file| {
1063 let result = if exclusive {
1064 file.try_lock()
1065 } else {
1066 file.try_lock_shared()
1067 };
1068 match result {
1069 Ok(()) => Ok(true),
1070 Err(TryLockError::WouldBlock) => Ok(false),
1071 Err(TryLockError::Error(err)) => Err(err.into()),
1072 }
1073 })
1074 .await
1075 }
1076
1077 fn unlock_sync(self: Rc<Self>) -> FsResult<()> {
1078 self.with_sync(|file| Ok(file.unlock()?))
1079 }
1080 async fn unlock_async(self: Rc<Self>) -> FsResult<()> {
1081 self
1082 .with_inner_blocking_task(|file| Ok(file.unlock()?))
1083 .await
1084 }
1085
1086 fn truncate_sync(self: Rc<Self>, len: u64) -> FsResult<()> {
1087 self.with_sync(|file| Ok(file.set_len(len)?))
1088 }
1089 async fn truncate_async(self: Rc<Self>, len: u64) -> FsResult<()> {
1090 self
1091 .with_inner_blocking_task(move |file| Ok(file.set_len(len)?))
1092 .await
1093 }
1094
1095 fn utime_sync(
1096 self: Rc<Self>,
1097 atime_secs: i64,
1098 atime_nanos: u32,
1099 mtime_secs: i64,
1100 mtime_nanos: u32,
1101 ) -> FsResult<()> {
1102 let atime = filetime::FileTime::from_unix_time(atime_secs, atime_nanos);
1103 let mtime = filetime::FileTime::from_unix_time(mtime_secs, mtime_nanos);
1104
1105 self.with_sync(|file| {
1106 filetime::set_file_handle_times(file, Some(atime), Some(mtime))?;
1107 Ok(())
1108 })
1109 }
1110 async fn utime_async(
1111 self: Rc<Self>,
1112 atime_secs: i64,
1113 atime_nanos: u32,
1114 mtime_secs: i64,
1115 mtime_nanos: u32,
1116 ) -> FsResult<()> {
1117 let atime = filetime::FileTime::from_unix_time(atime_secs, atime_nanos);
1118 let mtime = filetime::FileTime::from_unix_time(mtime_secs, mtime_nanos);
1119
1120 self
1121 .with_inner_blocking_task(move |file| {
1122 filetime::set_file_handle_times(file, Some(atime), Some(mtime))?;
1123 Ok(())
1124 })
1125 .await
1126 }
1127
1128 async fn read_byob(
1129 self: Rc<Self>,
1130 mut buf: BufMutView,
1131 ) -> FsResult<(usize, BufMutView)> {
1132 match &self.kind {
1133 #[cfg(windows)]
1135 StdFileResourceKind::Stdin(state) => {
1136 self.handle_stdin_read(state.clone(), buf).await
1137 }
1138 _ => {
1139 self
1140 .with_inner_blocking_task(|file| {
1141 let nread = file.read(&mut buf)?;
1142 Ok((nread, buf))
1143 })
1144 .await
1145 }
1146 }
1147 }
1148
1149 fn try_clone_inner(self: Rc<Self>) -> FsResult<Rc<dyn fs::File>> {
1150 let inner: &Option<_> = &self.cell.borrow();
1151 match inner {
1152 Some(inner) => Ok(Rc::new(StdFileResourceInner {
1153 kind: self.kind.clone(),
1154 cell: RefCell::new(Some(inner.try_clone()?)),
1155 cell_async_task_queue: Default::default(),
1156 handle: self.handle,
1157 maybe_path: self.maybe_path.clone(),
1158 })),
1159 None => Err(FsError::FileBusy),
1160 }
1161 }
1162
1163 fn as_stdio(self: Rc<Self>) -> FsResult<StdStdio> {
1164 match self.kind {
1165 StdFileResourceKind::File => self.with_sync(|file| {
1166 let file = file.try_clone()?;
1167 Ok(file.into())
1168 }),
1169 _ => Ok(StdStdio::inherit()),
1170 }
1171 }
1172
1173 fn backing_fd(self: Rc<Self>) -> Option<ResourceHandleFd> {
1174 Some(self.handle)
1175 }
1176}
1177
1178pub struct ReadCancelResource(Rc<CancelHandle>);
1179
1180impl Resource for ReadCancelResource {
1181 fn name(&self) -> Cow<'_, str> {
1182 "readCancel".into()
1183 }
1184
1185 fn close(self: Rc<Self>) {
1186 self.0.cancel();
1187 }
1188}
1189
1190#[op2(fast)]
1191#[smi]
1192pub fn op_read_create_cancel_handle(state: &mut OpState) -> u32 {
1193 state
1194 .resource_table
1195 .add(ReadCancelResource(CancelHandle::new_rc()))
1196}
1197
1198#[op2]
1199pub async fn op_read_with_cancel_handle(
1200 state: Rc<RefCell<OpState>>,
1201 #[smi] rid: u32,
1202 #[smi] cancel_handle: u32,
1203 #[buffer] buf: JsBuffer,
1204) -> Result<u32, JsErrorBox> {
1205 let (fut, cancel_rc) = {
1206 let state = state.borrow();
1207 let cancel_handle = state
1208 .resource_table
1209 .get::<ReadCancelResource>(cancel_handle)
1210 .unwrap()
1211 .0
1212 .clone();
1213
1214 (
1215 FileResource::with_file(&state, rid, |file| {
1216 let view = BufMutView::from(buf);
1217 Ok(file.read_byob(view))
1218 }),
1219 cancel_handle,
1220 )
1221 };
1222
1223 fut?
1224 .or_cancel(cancel_rc)
1225 .await
1226 .map_err(|_| JsErrorBox::generic("cancelled"))?
1227 .map(|(n, _)| n as u32)
1228 .map_err(JsErrorBox::from_err)
1229}
1230
1231#[op2(fast)]
1233pub fn op_print(
1234 state: &mut OpState,
1235 #[string] msg: &str,
1236 is_err: bool,
1237) -> Result<(), JsErrorBox> {
1238 let rid = if is_err { 2 } else { 1 };
1239 FileResource::with_file(state, rid, move |file| {
1240 match file.write_all_sync(msg.as_bytes()) {
1241 Err(FsError::Io(io)) if io.kind() == ErrorKind::BrokenPipe => Ok(()),
1242 other => other,
1243 }
1244 .map_err(JsErrorBox::from_err)
1245 })
1246}
1247
1248#[cfg(windows)]
1249pub fn stat_extra(file: &std::fs::File, fsstat: &mut FsStat) -> FsResult<()> {
1250 use std::os::windows::io::AsRawHandle;
1251
1252 unsafe fn get_dev(
1253 handle: winapi::shared::ntdef::HANDLE,
1254 ) -> std::io::Result<u64> {
1255 use winapi::shared::minwindef::FALSE;
1256 use winapi::um::fileapi::BY_HANDLE_FILE_INFORMATION;
1257 use winapi::um::fileapi::GetFileInformationByHandle;
1258
1259 unsafe {
1261 let info = {
1262 let mut info =
1263 std::mem::MaybeUninit::<BY_HANDLE_FILE_INFORMATION>::zeroed();
1264 if GetFileInformationByHandle(handle, info.as_mut_ptr()) == FALSE {
1265 return Err(std::io::Error::last_os_error());
1266 }
1267
1268 info.assume_init()
1269 };
1270
1271 Ok(info.dwVolumeSerialNumber as u64)
1272 }
1273 }
1274
1275 const WINDOWS_TICK: i64 = 10_000; const SEC_TO_UNIX_EPOCH: i64 = 11_644_473_600; fn windows_time_to_unix_time_msec(windows_time: &i64) -> i64 {
1279 let milliseconds_since_windows_epoch = windows_time / WINDOWS_TICK;
1280 milliseconds_since_windows_epoch - SEC_TO_UNIX_EPOCH * 1000
1281 }
1282
1283 use windows_sys::Wdk::Storage::FileSystem::FILE_ALL_INFORMATION;
1284 use windows_sys::Win32::Foundation::NTSTATUS;
1285
1286 unsafe fn query_file_information(
1287 handle: winapi::shared::ntdef::HANDLE,
1288 ) -> Result<FILE_ALL_INFORMATION, NTSTATUS> {
1289 use windows_sys::Wdk::Storage::FileSystem::NtQueryInformationFile;
1290 use windows_sys::Win32::Foundation::ERROR_MORE_DATA;
1291 use windows_sys::Win32::Foundation::RtlNtStatusToDosError;
1292 use windows_sys::Win32::System::IO::IO_STATUS_BLOCK;
1293
1294 unsafe {
1296 let mut info = std::mem::MaybeUninit::<FILE_ALL_INFORMATION>::zeroed();
1297 let mut io_status_block =
1298 std::mem::MaybeUninit::<IO_STATUS_BLOCK>::zeroed();
1299 let status = NtQueryInformationFile(
1300 handle as _,
1301 io_status_block.as_mut_ptr(),
1302 info.as_mut_ptr() as *mut _,
1303 std::mem::size_of::<FILE_ALL_INFORMATION>() as _,
1304 18, );
1306
1307 if status < 0 {
1308 let converted_status = RtlNtStatusToDosError(status);
1309
1310 if converted_status != ERROR_MORE_DATA {
1315 return Err(converted_status as NTSTATUS);
1316 }
1317 }
1318
1319 Ok(info.assume_init())
1320 }
1321 }
1322
1323 unsafe {
1325 let file_handle = file.as_raw_handle();
1326
1327 fsstat.dev = get_dev(file_handle)?;
1328
1329 if let Ok(file_info) = query_file_information(file_handle) {
1330 fsstat.ctime = Some(windows_time_to_unix_time_msec(
1331 &file_info.BasicInformation.ChangeTime,
1332 ) as u64);
1333
1334 if file_info.BasicInformation.FileAttributes
1335 & winapi::um::winnt::FILE_ATTRIBUTE_REPARSE_POINT
1336 != 0
1337 {
1338 fsstat.is_symlink = true;
1339 }
1340
1341 if file_info.BasicInformation.FileAttributes
1342 & winapi::um::winnt::FILE_ATTRIBUTE_DIRECTORY
1343 != 0
1344 {
1345 fsstat.mode |= libc::S_IFDIR as u32;
1346 fsstat.size = 0;
1347 } else {
1348 fsstat.mode |= libc::S_IFREG as u32;
1349 fsstat.size = file_info.StandardInformation.EndOfFile as u64;
1350 }
1351
1352 if file_info.BasicInformation.FileAttributes
1353 & winapi::um::winnt::FILE_ATTRIBUTE_READONLY
1354 != 0
1355 {
1356 fsstat.mode |=
1357 (libc::S_IREAD | (libc::S_IREAD >> 3) | (libc::S_IREAD >> 6)) as u32;
1358 } else {
1359 fsstat.mode |= ((libc::S_IREAD | libc::S_IWRITE)
1360 | ((libc::S_IREAD | libc::S_IWRITE) >> 3)
1361 | ((libc::S_IREAD | libc::S_IWRITE) >> 6))
1362 as u32;
1363 }
1364
1365 fsstat.blocks =
1367 Some(file_info.StandardInformation.AllocationSize as u64 >> 9);
1368 fsstat.ino = Some(file_info.InternalInformation.IndexNumber as u64);
1369 fsstat.nlink = Some(file_info.StandardInformation.NumberOfLinks as u64);
1370 }
1371
1372 Ok(())
1373 }
1374}