deno_io/
lib.rs

1// Copyright 2018-2025 the Deno authors. MIT license.
2
3use 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::rc::Rc;
17#[cfg(windows)]
18use std::sync::Arc;
19
20use deno_core::futures::TryFutureExt;
21use deno_core::op2;
22use deno_core::unsync::spawn_blocking;
23use deno_core::unsync::TaskQueue;
24use deno_core::AsyncMutFuture;
25use deno_core::AsyncRefCell;
26use deno_core::AsyncResult;
27use deno_core::BufMutView;
28use deno_core::BufView;
29use deno_core::CancelHandle;
30use deno_core::CancelTryFuture;
31use deno_core::OpState;
32use deno_core::RcRef;
33use deno_core::Resource;
34use deno_core::ResourceHandle;
35use deno_core::ResourceHandleFd;
36use deno_error::JsErrorBox;
37use fs::FileResource;
38use fs::FsError;
39use fs::FsResult;
40use fs::FsStat;
41use fs3::FileExt;
42use once_cell::sync::Lazy;
43#[cfg(windows)]
44use parking_lot::Condvar;
45#[cfg(windows)]
46use parking_lot::Mutex;
47use tokio::io::AsyncRead;
48use tokio::io::AsyncReadExt;
49use tokio::io::AsyncWrite;
50use tokio::io::AsyncWriteExt;
51use tokio::process;
52#[cfg(windows)]
53use winapi::um::processenv::GetStdHandle;
54#[cfg(windows)]
55use winapi::um::winbase;
56
57pub mod fs;
58mod pipe;
59#[cfg(windows)]
60mod winpipe;
61
62mod bi_pipe;
63
64pub use bi_pipe::bi_pipe_pair_raw;
65pub use bi_pipe::BiPipe;
66pub use bi_pipe::BiPipeRead;
67pub use bi_pipe::BiPipeResource;
68pub use bi_pipe::BiPipeWrite;
69pub use bi_pipe::RawBiPipeHandle;
70pub use pipe::pipe;
71pub use pipe::AsyncPipeRead;
72pub use pipe::AsyncPipeWrite;
73pub use pipe::PipeRead;
74pub use pipe::PipeWrite;
75pub use pipe::RawPipeHandle;
76
77/// Abstraction over `AsRawFd` (unix) and `AsRawHandle` (windows)
78pub trait AsRawIoHandle {
79  fn as_raw_io_handle(&self) -> RawIoHandle;
80}
81
82#[cfg(unix)]
83impl<T> AsRawIoHandle for T
84where
85  T: std::os::unix::io::AsRawFd,
86{
87  fn as_raw_io_handle(&self) -> RawIoHandle {
88    self.as_raw_fd()
89  }
90}
91
92#[cfg(windows)]
93impl<T> AsRawIoHandle for T
94where
95  T: std::os::windows::io::AsRawHandle,
96{
97  fn as_raw_io_handle(&self) -> RawIoHandle {
98    self.as_raw_handle()
99  }
100}
101
102/// Abstraction over `IntoRawFd` (unix) and `IntoRawHandle` (windows)
103pub trait IntoRawIoHandle {
104  fn into_raw_io_handle(self) -> RawIoHandle;
105}
106
107#[cfg(unix)]
108impl<T> IntoRawIoHandle for T
109where
110  T: std::os::unix::io::IntoRawFd,
111{
112  fn into_raw_io_handle(self) -> RawIoHandle {
113    self.into_raw_fd()
114  }
115}
116
117#[cfg(windows)]
118impl<T> IntoRawIoHandle for T
119where
120  T: std::os::windows::io::IntoRawHandle,
121{
122  fn into_raw_io_handle(self) -> RawIoHandle {
123    self.into_raw_handle()
124  }
125}
126
127/// Abstraction over `FromRawFd` (unix) and `FromRawHandle` (windows)
128pub trait FromRawIoHandle: Sized {
129  /// Constructs a type from a raw io handle (fd/HANDLE).
130  ///
131  /// # Safety
132  ///
133  /// Refer to the standard library docs ([unix](https://doc.rust-lang.org/stable/std/os/windows/io/trait.FromRawHandle.html#tymethod.from_raw_handle)) ([windows](https://doc.rust-lang.org/stable/std/os/fd/trait.FromRawFd.html#tymethod.from_raw_fd))
134  ///
135  unsafe fn from_raw_io_handle(handle: RawIoHandle) -> Self;
136}
137
138#[cfg(unix)]
139impl<T> FromRawIoHandle for T
140where
141  T: std::os::unix::io::FromRawFd,
142{
143  unsafe fn from_raw_io_handle(fd: RawIoHandle) -> T {
144    // SAFETY: upheld by caller
145    unsafe { T::from_raw_fd(fd) }
146  }
147}
148
149#[cfg(windows)]
150impl<T> FromRawIoHandle for T
151where
152  T: std::os::windows::io::FromRawHandle,
153{
154  unsafe fn from_raw_io_handle(fd: RawIoHandle) -> T {
155    // SAFETY: upheld by caller
156    unsafe { T::from_raw_handle(fd) }
157  }
158}
159
160#[cfg(unix)]
161pub type RawIoHandle = std::os::fd::RawFd;
162
163#[cfg(windows)]
164pub type RawIoHandle = std::os::windows::io::RawHandle;
165
166pub fn close_raw_handle(handle: RawIoHandle) {
167  #[cfg(unix)]
168  {
169    // SAFETY: libc call
170    unsafe {
171      libc::close(handle);
172    }
173  }
174  #[cfg(windows)]
175  {
176    // SAFETY: win32 call
177    unsafe {
178      windows_sys::Win32::Foundation::CloseHandle(handle as _);
179    }
180  }
181}
182
183// Store the stdio fd/handles in global statics in order to keep them
184// alive for the duration of the application since the last handle/fd
185// being dropped will close the corresponding pipe.
186#[cfg(unix)]
187pub static STDIN_HANDLE: Lazy<StdFile> = Lazy::new(|| {
188  // SAFETY: corresponds to OS stdin
189  unsafe { StdFile::from_raw_fd(0) }
190});
191#[cfg(unix)]
192pub static STDOUT_HANDLE: Lazy<StdFile> = Lazy::new(|| {
193  // SAFETY: corresponds to OS stdout
194  unsafe { StdFile::from_raw_fd(1) }
195});
196#[cfg(unix)]
197pub static STDERR_HANDLE: Lazy<StdFile> = Lazy::new(|| {
198  // SAFETY: corresponds to OS stderr
199  unsafe { StdFile::from_raw_fd(2) }
200});
201
202#[cfg(windows)]
203pub static STDIN_HANDLE: Lazy<StdFile> = Lazy::new(|| {
204  // SAFETY: corresponds to OS stdin
205  unsafe { StdFile::from_raw_handle(GetStdHandle(winbase::STD_INPUT_HANDLE)) }
206});
207#[cfg(windows)]
208pub static STDOUT_HANDLE: Lazy<StdFile> = Lazy::new(|| {
209  // SAFETY: corresponds to OS stdout
210  unsafe { StdFile::from_raw_handle(GetStdHandle(winbase::STD_OUTPUT_HANDLE)) }
211});
212#[cfg(windows)]
213pub static STDERR_HANDLE: Lazy<StdFile> = Lazy::new(|| {
214  // SAFETY: corresponds to OS stderr
215  unsafe { StdFile::from_raw_handle(GetStdHandle(winbase::STD_ERROR_HANDLE)) }
216});
217
218deno_core::extension!(deno_io,
219  deps = [ deno_web ],
220  esm = [ "12_io.js" ],
221  options = {
222    stdio: Option<Stdio>,
223  },
224  middleware = |op| match op.name {
225    "op_print" => op_print(),
226    _ => op,
227  },
228  state = |state, options| {
229    if let Some(stdio) = options.stdio {
230      #[cfg(windows)]
231      let stdin_state = {
232        let st = Arc::new(Mutex::new(WinTtyState::default()));
233        state.put(st.clone());
234        st
235      };
236      #[cfg(unix)]
237      let stdin_state = ();
238
239      let t = &mut state.resource_table;
240
241      let rid = t.add(fs::FileResource::new(
242        Rc::new(match stdio.stdin.pipe {
243          StdioPipeInner::Inherit => StdFileResourceInner::new(
244            StdFileResourceKind::Stdin(stdin_state),
245            STDIN_HANDLE.try_clone().unwrap(),
246          ),
247          StdioPipeInner::File(pipe) => StdFileResourceInner::file(pipe),
248        }),
249        "stdin".to_string(),
250      ));
251      assert_eq!(rid, 0, "stdin must have ResourceId 0");
252
253      let rid = t.add(FileResource::new(
254        Rc::new(match stdio.stdout.pipe {
255          StdioPipeInner::Inherit => StdFileResourceInner::new(
256            StdFileResourceKind::Stdout,
257            STDOUT_HANDLE.try_clone().unwrap(),
258          ),
259          StdioPipeInner::File(pipe) => StdFileResourceInner::file(pipe),
260        }),
261        "stdout".to_string(),
262      ));
263      assert_eq!(rid, 1, "stdout must have ResourceId 1");
264
265      let rid = t.add(FileResource::new(
266        Rc::new(match stdio.stderr.pipe {
267          StdioPipeInner::Inherit => StdFileResourceInner::new(
268            StdFileResourceKind::Stderr,
269            STDERR_HANDLE.try_clone().unwrap(),
270          ),
271          StdioPipeInner::File(pipe) => StdFileResourceInner::file(pipe),
272        }),
273        "stderr".to_string(),
274      ));
275      assert_eq!(rid, 2, "stderr must have ResourceId 2");
276    }
277  },
278);
279
280#[derive(Default)]
281pub struct StdioPipe {
282  pipe: StdioPipeInner,
283}
284
285impl StdioPipe {
286  pub const fn inherit() -> Self {
287    StdioPipe {
288      pipe: StdioPipeInner::Inherit,
289    }
290  }
291
292  pub fn file(f: impl Into<StdFile>) -> Self {
293    StdioPipe {
294      pipe: StdioPipeInner::File(f.into()),
295    }
296  }
297}
298
299#[derive(Default)]
300enum StdioPipeInner {
301  #[default]
302  Inherit,
303  File(StdFile),
304}
305
306impl Clone for StdioPipe {
307  fn clone(&self) -> Self {
308    match &self.pipe {
309      StdioPipeInner::Inherit => Self {
310        pipe: StdioPipeInner::Inherit,
311      },
312      StdioPipeInner::File(pipe) => Self {
313        pipe: StdioPipeInner::File(pipe.try_clone().unwrap()),
314      },
315    }
316  }
317}
318
319/// Specify how stdin, stdout, and stderr are piped.
320/// By default, inherits from the process.
321#[derive(Clone, Default)]
322pub struct Stdio {
323  pub stdin: StdioPipe,
324  pub stdout: StdioPipe,
325  pub stderr: StdioPipe,
326}
327
328#[derive(Debug)]
329pub struct WriteOnlyResource<S> {
330  stream: AsyncRefCell<S>,
331}
332
333impl<S: 'static> From<S> for WriteOnlyResource<S> {
334  fn from(stream: S) -> Self {
335    Self {
336      stream: stream.into(),
337    }
338  }
339}
340
341impl<S> WriteOnlyResource<S>
342where
343  S: AsyncWrite + Unpin + 'static,
344{
345  pub fn borrow_mut(self: &Rc<Self>) -> AsyncMutFuture<S> {
346    RcRef::map(self, |r| &r.stream).borrow_mut()
347  }
348
349  async fn write(self: Rc<Self>, data: &[u8]) -> Result<usize, io::Error> {
350    let mut stream = self.borrow_mut().await;
351    let nwritten = stream.write(data).await?;
352    Ok(nwritten)
353  }
354
355  async fn shutdown(self: Rc<Self>) -> Result<(), io::Error> {
356    let mut stream = self.borrow_mut().await;
357    stream.shutdown().await?;
358    Ok(())
359  }
360
361  pub fn into_inner(self) -> S {
362    self.stream.into_inner()
363  }
364}
365
366#[derive(Debug)]
367pub struct ReadOnlyResource<S> {
368  stream: AsyncRefCell<S>,
369  cancel_handle: CancelHandle,
370}
371
372impl<S: 'static> From<S> for ReadOnlyResource<S> {
373  fn from(stream: S) -> Self {
374    Self {
375      stream: stream.into(),
376      cancel_handle: Default::default(),
377    }
378  }
379}
380
381impl<S> ReadOnlyResource<S>
382where
383  S: AsyncRead + Unpin + 'static,
384{
385  pub fn borrow_mut(self: &Rc<Self>) -> AsyncMutFuture<S> {
386    RcRef::map(self, |r| &r.stream).borrow_mut()
387  }
388
389  pub fn cancel_handle(self: &Rc<Self>) -> RcRef<CancelHandle> {
390    RcRef::map(self, |r| &r.cancel_handle)
391  }
392
393  pub fn cancel_read_ops(&self) {
394    self.cancel_handle.cancel()
395  }
396
397  async fn read(self: Rc<Self>, data: &mut [u8]) -> Result<usize, io::Error> {
398    let mut rd = self.borrow_mut().await;
399    let nread = rd.read(data).try_or_cancel(self.cancel_handle()).await?;
400    Ok(nread)
401  }
402
403  pub fn into_inner(self) -> S {
404    self.stream.into_inner()
405  }
406}
407
408pub type ChildStdinResource = WriteOnlyResource<process::ChildStdin>;
409
410impl Resource for ChildStdinResource {
411  fn name(&self) -> Cow<str> {
412    "childStdin".into()
413  }
414
415  deno_core::impl_writable!();
416
417  fn shutdown(self: Rc<Self>) -> AsyncResult<()> {
418    Box::pin(self.shutdown().map_err(JsErrorBox::from_err))
419  }
420}
421
422pub type ChildStdoutResource = ReadOnlyResource<process::ChildStdout>;
423
424impl Resource for ChildStdoutResource {
425  deno_core::impl_readable_byob!();
426
427  fn name(&self) -> Cow<str> {
428    "childStdout".into()
429  }
430
431  fn close(self: Rc<Self>) {
432    self.cancel_read_ops();
433  }
434}
435
436pub type ChildStderrResource = ReadOnlyResource<process::ChildStderr>;
437
438impl Resource for ChildStderrResource {
439  deno_core::impl_readable_byob!();
440
441  fn name(&self) -> Cow<str> {
442    "childStderr".into()
443  }
444
445  fn close(self: Rc<Self>) {
446    self.cancel_read_ops();
447  }
448}
449
450#[cfg(windows)]
451#[derive(Default)]
452pub struct WinTtyState {
453  pub cancelled: bool,
454  pub reading: bool,
455  pub screen_buffer_info:
456    Option<winapi::um::wincon::CONSOLE_SCREEN_BUFFER_INFO>,
457  pub cvar: Arc<Condvar>,
458}
459
460#[derive(Clone)]
461enum StdFileResourceKind {
462  File,
463  // For stdout and stderr, we sometimes instead use std::io::stdout() directly,
464  // because we get some Windows specific functionality for free by using Rust
465  // std's wrappers. So we take a bit of a complexity hit in order to not
466  // have to duplicate the functionality in Rust's std/src/sys/windows/stdio.rs
467  #[cfg(windows)]
468  Stdin(Arc<Mutex<WinTtyState>>),
469  #[cfg(not(windows))]
470  Stdin(()),
471  Stdout,
472  Stderr,
473}
474
475pub struct StdFileResourceInner {
476  kind: StdFileResourceKind,
477  // We can't use an AsyncRefCell here because we need to allow
478  // access to the resource synchronously at any time and
479  // asynchronously one at a time in order
480  cell: RefCell<Option<StdFile>>,
481  // Used to keep async actions in order and only allow one
482  // to occur at a time
483  cell_async_task_queue: Rc<TaskQueue>,
484  handle: ResourceHandleFd,
485}
486
487impl StdFileResourceInner {
488  pub fn file(fs_file: StdFile) -> Self {
489    StdFileResourceInner::new(StdFileResourceKind::File, fs_file)
490  }
491
492  fn new(kind: StdFileResourceKind, fs_file: StdFile) -> Self {
493    // We know this will be an fd
494    let handle = ResourceHandle::from_fd_like(&fs_file).as_fd_like().unwrap();
495    StdFileResourceInner {
496      kind,
497      handle,
498      cell: RefCell::new(Some(fs_file)),
499      cell_async_task_queue: Default::default(),
500    }
501  }
502
503  fn with_sync<F, R>(&self, action: F) -> FsResult<R>
504  where
505    F: FnOnce(&mut StdFile) -> FsResult<R>,
506  {
507    match self.cell.try_borrow_mut() {
508      Ok(mut cell) if cell.is_some() => action(cell.as_mut().unwrap()),
509      _ => Err(fs::FsError::FileBusy),
510    }
511  }
512
513  fn with_inner_blocking_task<F, R: 'static + Send>(
514    &self,
515    action: F,
516  ) -> impl Future<Output = R> + '_
517  where
518    F: FnOnce(&mut StdFile) -> R + Send + 'static,
519  {
520    // we want to restrict this to one async action at a time
521    let acquire_fut = self.cell_async_task_queue.acquire();
522    async move {
523      let permit = acquire_fut.await;
524      // we take the value out of the cell, use it on a blocking task,
525      // then put it back into the cell when we're done
526      let mut did_take = false;
527      let mut cell_value = {
528        let mut cell = self.cell.borrow_mut();
529        match cell.as_mut().unwrap().try_clone().ok() {
530          Some(value) => value,
531          None => {
532            did_take = true;
533            cell.take().unwrap()
534          }
535        }
536      };
537      let (cell_value, result) = spawn_blocking(move || {
538        let result = action(&mut cell_value);
539        (cell_value, result)
540      })
541      .await
542      .unwrap();
543
544      if did_take {
545        // put it back
546        self.cell.borrow_mut().replace(cell_value);
547      }
548
549      drop(permit); // explicit for clarity
550      result
551    }
552  }
553
554  fn with_blocking_task<F, R: 'static + Send>(
555    &self,
556    action: F,
557  ) -> impl Future<Output = R>
558  where
559    F: FnOnce() -> R + Send + 'static,
560  {
561    // we want to restrict this to one async action at a time
562    let acquire_fut = self.cell_async_task_queue.acquire();
563    async move {
564      let _permit = acquire_fut.await;
565      spawn_blocking(action).await.unwrap()
566    }
567  }
568
569  #[cfg(windows)]
570  async fn handle_stdin_read(
571    &self,
572    state: Arc<Mutex<WinTtyState>>,
573    mut buf: BufMutView,
574  ) -> FsResult<(usize, BufMutView)> {
575    loop {
576      let state = state.clone();
577
578      let fut = self.with_inner_blocking_task(move |file| {
579        /* Start reading, and set the reading flag to true */
580        state.lock().reading = true;
581        let nread = match file.read(&mut buf) {
582          Ok(nread) => nread,
583          Err(e) => return Err((e.into(), buf)),
584        };
585
586        let mut state = state.lock();
587        state.reading = false;
588
589        /* If we canceled the read by sending a VK_RETURN event, restore
590        the screen state to undo the visual effect of the VK_RETURN event */
591        if state.cancelled {
592          if let Some(screen_buffer_info) = state.screen_buffer_info {
593            // SAFETY: WinAPI calls to open conout$ and restore visual state.
594            unsafe {
595              let handle = winapi::um::fileapi::CreateFileW(
596                "conout$"
597                  .encode_utf16()
598                  .chain(Some(0))
599                  .collect::<Vec<_>>()
600                  .as_ptr(),
601                winapi::um::winnt::GENERIC_READ
602                  | winapi::um::winnt::GENERIC_WRITE,
603                winapi::um::winnt::FILE_SHARE_READ
604                  | winapi::um::winnt::FILE_SHARE_WRITE,
605                std::ptr::null_mut(),
606                winapi::um::fileapi::OPEN_EXISTING,
607                0,
608                std::ptr::null_mut(),
609              );
610
611              let mut pos = screen_buffer_info.dwCursorPosition;
612              /* If the cursor was at the bottom line of the screen buffer, the
613              VK_RETURN would have caused the buffer contents to scroll up by
614              one line. The right position to reset the cursor to is therefore one
615              line higher */
616              if pos.Y == screen_buffer_info.dwSize.Y - 1 {
617                pos.Y -= 1;
618              }
619
620              winapi::um::wincon::SetConsoleCursorPosition(handle, pos);
621              winapi::um::handleapi::CloseHandle(handle);
622            }
623          }
624
625          /* Reset the cancelled flag */
626          state.cancelled = false;
627
628          /* Unblock the main thread */
629          state.cvar.notify_one();
630
631          return Err((FsError::FileBusy, buf));
632        }
633
634        Ok((nread, buf))
635      });
636
637      match fut.await {
638        Err((FsError::FileBusy, b)) => {
639          buf = b;
640          continue;
641        }
642        other => return other.map_err(|(e, _)| e),
643      }
644    }
645  }
646}
647
648#[async_trait::async_trait(?Send)]
649impl crate::fs::File for StdFileResourceInner {
650  fn write_sync(self: Rc<Self>, buf: &[u8]) -> FsResult<usize> {
651    // Rust will line buffer and we don't want that behavior
652    // (see https://github.com/denoland/deno/issues/948), so flush stdout and stderr.
653    // Although an alternative solution could be to bypass Rust's std by
654    // using the raw fds/handles, it will cause encoding issues on Windows
655    // that we get solved for free by using Rust's stdio wrappers (see
656    // std/src/sys/windows/stdio.rs in Rust's source code).
657    match self.kind {
658      StdFileResourceKind::File => self.with_sync(|file| Ok(file.write(buf)?)),
659      StdFileResourceKind::Stdin(_) => {
660        Err(Into::<std::io::Error>::into(ErrorKind::Unsupported).into())
661      }
662      StdFileResourceKind::Stdout => {
663        // bypass the file and use std::io::stdout()
664        let mut stdout = std::io::stdout().lock();
665        let nwritten = stdout.write(buf)?;
666        stdout.flush()?;
667        Ok(nwritten)
668      }
669      StdFileResourceKind::Stderr => {
670        // bypass the file and use std::io::stderr()
671        let mut stderr = std::io::stderr().lock();
672        let nwritten = stderr.write(buf)?;
673        stderr.flush()?;
674        Ok(nwritten)
675      }
676    }
677  }
678
679  fn read_sync(self: Rc<Self>, buf: &mut [u8]) -> FsResult<usize> {
680    match self.kind {
681      StdFileResourceKind::File | StdFileResourceKind::Stdin(_) => {
682        self.with_sync(|file| Ok(file.read(buf)?))
683      }
684      StdFileResourceKind::Stdout | StdFileResourceKind::Stderr => {
685        Err(FsError::NotSupported)
686      }
687    }
688  }
689
690  fn write_all_sync(self: Rc<Self>, buf: &[u8]) -> FsResult<()> {
691    match self.kind {
692      StdFileResourceKind::File => {
693        self.with_sync(|file| Ok(file.write_all(buf)?))
694      }
695      StdFileResourceKind::Stdin(_) => {
696        Err(Into::<std::io::Error>::into(ErrorKind::Unsupported).into())
697      }
698      StdFileResourceKind::Stdout => {
699        // bypass the file and use std::io::stdout()
700        let mut stdout = std::io::stdout().lock();
701        stdout.write_all(buf)?;
702        stdout.flush()?;
703        Ok(())
704      }
705      StdFileResourceKind::Stderr => {
706        // bypass the file and use std::io::stderr()
707        let mut stderr = std::io::stderr().lock();
708        stderr.write_all(buf)?;
709        stderr.flush()?;
710        Ok(())
711      }
712    }
713  }
714  async fn write_all(self: Rc<Self>, buf: BufView) -> FsResult<()> {
715    match self.kind {
716      StdFileResourceKind::File => {
717        self
718          .with_inner_blocking_task(move |file| Ok(file.write_all(&buf)?))
719          .await
720      }
721      StdFileResourceKind::Stdin(_) => {
722        Err(Into::<std::io::Error>::into(ErrorKind::Unsupported).into())
723      }
724      StdFileResourceKind::Stdout => {
725        self
726          .with_blocking_task(move || {
727            // bypass the file and use std::io::stdout()
728            let mut stdout = std::io::stdout().lock();
729            stdout.write_all(&buf)?;
730            stdout.flush()?;
731            Ok(())
732          })
733          .await
734      }
735      StdFileResourceKind::Stderr => {
736        self
737          .with_blocking_task(move || {
738            // bypass the file and use std::io::stderr()
739            let mut stderr = std::io::stderr().lock();
740            stderr.write_all(&buf)?;
741            stderr.flush()?;
742            Ok(())
743          })
744          .await
745      }
746    }
747  }
748
749  async fn write(
750    self: Rc<Self>,
751    view: BufView,
752  ) -> FsResult<deno_core::WriteOutcome> {
753    match self.kind {
754      StdFileResourceKind::File => {
755        self
756          .with_inner_blocking_task(|file| {
757            let nwritten = file.write(&view)?;
758            Ok(deno_core::WriteOutcome::Partial { nwritten, view })
759          })
760          .await
761      }
762      StdFileResourceKind::Stdin(_) => {
763        Err(Into::<std::io::Error>::into(ErrorKind::Unsupported).into())
764      }
765      StdFileResourceKind::Stdout => {
766        self
767          .with_blocking_task(|| {
768            // bypass the file and use std::io::stdout()
769            let mut stdout = std::io::stdout().lock();
770            let nwritten = stdout.write(&view)?;
771            stdout.flush()?;
772            Ok(deno_core::WriteOutcome::Partial { nwritten, view })
773          })
774          .await
775      }
776      StdFileResourceKind::Stderr => {
777        self
778          .with_blocking_task(|| {
779            // bypass the file and use std::io::stderr()
780            let mut stderr = std::io::stderr().lock();
781            let nwritten = stderr.write(&view)?;
782            stderr.flush()?;
783            Ok(deno_core::WriteOutcome::Partial { nwritten, view })
784          })
785          .await
786      }
787    }
788  }
789
790  fn read_all_sync(self: Rc<Self>) -> FsResult<Cow<'static, [u8]>> {
791    match self.kind {
792      StdFileResourceKind::File | StdFileResourceKind::Stdin(_) => {
793        let mut buf = Vec::new();
794        self.with_sync(|file| Ok(file.read_to_end(&mut buf)?))?;
795        Ok(Cow::Owned(buf))
796      }
797      StdFileResourceKind::Stdout | StdFileResourceKind::Stderr => {
798        Err(FsError::NotSupported)
799      }
800    }
801  }
802  async fn read_all_async(self: Rc<Self>) -> FsResult<Cow<'static, [u8]>> {
803    match self.kind {
804      StdFileResourceKind::File | StdFileResourceKind::Stdin(_) => {
805        self
806          .with_inner_blocking_task(|file| {
807            let mut buf = Vec::new();
808            file.read_to_end(&mut buf)?;
809            Ok(Cow::Owned(buf))
810          })
811          .await
812      }
813      StdFileResourceKind::Stdout | StdFileResourceKind::Stderr => {
814        Err(FsError::NotSupported)
815      }
816    }
817  }
818
819  fn chmod_sync(self: Rc<Self>, _mode: u32) -> FsResult<()> {
820    #[cfg(unix)]
821    {
822      use std::os::unix::prelude::PermissionsExt;
823      self.with_sync(|file| {
824        Ok(file.set_permissions(std::fs::Permissions::from_mode(_mode))?)
825      })
826    }
827    #[cfg(not(unix))]
828    Err(FsError::NotSupported)
829  }
830  async fn chmod_async(self: Rc<Self>, _mode: u32) -> FsResult<()> {
831    #[cfg(unix)]
832    {
833      use std::os::unix::prelude::PermissionsExt;
834      self
835        .with_inner_blocking_task(move |file| {
836          Ok(file.set_permissions(std::fs::Permissions::from_mode(_mode))?)
837        })
838        .await
839    }
840    #[cfg(not(unix))]
841    Err(FsError::NotSupported)
842  }
843
844  fn seek_sync(self: Rc<Self>, pos: io::SeekFrom) -> FsResult<u64> {
845    self.with_sync(|file| Ok(file.seek(pos)?))
846  }
847  async fn seek_async(self: Rc<Self>, pos: io::SeekFrom) -> FsResult<u64> {
848    self
849      .with_inner_blocking_task(move |file| Ok(file.seek(pos)?))
850      .await
851  }
852
853  fn datasync_sync(self: Rc<Self>) -> FsResult<()> {
854    self.with_sync(|file| Ok(file.sync_data()?))
855  }
856  async fn datasync_async(self: Rc<Self>) -> FsResult<()> {
857    self
858      .with_inner_blocking_task(|file| Ok(file.sync_data()?))
859      .await
860  }
861
862  fn sync_sync(self: Rc<Self>) -> FsResult<()> {
863    self.with_sync(|file| Ok(file.sync_all()?))
864  }
865  async fn sync_async(self: Rc<Self>) -> FsResult<()> {
866    self
867      .with_inner_blocking_task(|file| Ok(file.sync_all()?))
868      .await
869  }
870
871  fn stat_sync(self: Rc<Self>) -> FsResult<FsStat> {
872    self.with_sync(|file| Ok(file.metadata().map(FsStat::from_std)?))
873  }
874  async fn stat_async(self: Rc<Self>) -> FsResult<FsStat> {
875    self
876      .with_inner_blocking_task(|file| {
877        Ok(file.metadata().map(FsStat::from_std)?)
878      })
879      .await
880  }
881
882  fn lock_sync(self: Rc<Self>, exclusive: bool) -> FsResult<()> {
883    self.with_sync(|file| {
884      if exclusive {
885        file.lock_exclusive()?;
886      } else {
887        file.lock_shared()?;
888      }
889      Ok(())
890    })
891  }
892  async fn lock_async(self: Rc<Self>, exclusive: bool) -> FsResult<()> {
893    self
894      .with_inner_blocking_task(move |file| {
895        if exclusive {
896          file.lock_exclusive()?;
897        } else {
898          file.lock_shared()?;
899        }
900        Ok(())
901      })
902      .await
903  }
904
905  fn unlock_sync(self: Rc<Self>) -> FsResult<()> {
906    self.with_sync(|file| Ok(file.unlock()?))
907  }
908  async fn unlock_async(self: Rc<Self>) -> FsResult<()> {
909    self
910      .with_inner_blocking_task(|file| Ok(file.unlock()?))
911      .await
912  }
913
914  fn truncate_sync(self: Rc<Self>, len: u64) -> FsResult<()> {
915    self.with_sync(|file| Ok(file.set_len(len)?))
916  }
917  async fn truncate_async(self: Rc<Self>, len: u64) -> FsResult<()> {
918    self
919      .with_inner_blocking_task(move |file| Ok(file.set_len(len)?))
920      .await
921  }
922
923  fn utime_sync(
924    self: Rc<Self>,
925    atime_secs: i64,
926    atime_nanos: u32,
927    mtime_secs: i64,
928    mtime_nanos: u32,
929  ) -> FsResult<()> {
930    let atime = filetime::FileTime::from_unix_time(atime_secs, atime_nanos);
931    let mtime = filetime::FileTime::from_unix_time(mtime_secs, mtime_nanos);
932
933    self.with_sync(|file| {
934      filetime::set_file_handle_times(file, Some(atime), Some(mtime))?;
935      Ok(())
936    })
937  }
938  async fn utime_async(
939    self: Rc<Self>,
940    atime_secs: i64,
941    atime_nanos: u32,
942    mtime_secs: i64,
943    mtime_nanos: u32,
944  ) -> FsResult<()> {
945    let atime = filetime::FileTime::from_unix_time(atime_secs, atime_nanos);
946    let mtime = filetime::FileTime::from_unix_time(mtime_secs, mtime_nanos);
947
948    self
949      .with_inner_blocking_task(move |file| {
950        filetime::set_file_handle_times(file, Some(atime), Some(mtime))?;
951        Ok(())
952      })
953      .await
954  }
955
956  async fn read_byob(
957    self: Rc<Self>,
958    mut buf: BufMutView,
959  ) -> FsResult<(usize, BufMutView)> {
960    match &self.kind {
961      /* On Windows, we need to handle special read cancellation logic for stdin */
962      #[cfg(windows)]
963      StdFileResourceKind::Stdin(state) => {
964        self.handle_stdin_read(state.clone(), buf).await
965      }
966      _ => {
967        self
968          .with_inner_blocking_task(|file| {
969            let nread = file.read(&mut buf)?;
970            Ok((nread, buf))
971          })
972          .await
973      }
974    }
975  }
976
977  fn try_clone_inner(self: Rc<Self>) -> FsResult<Rc<dyn fs::File>> {
978    let inner: &Option<_> = &self.cell.borrow();
979    match inner {
980      Some(inner) => Ok(Rc::new(StdFileResourceInner {
981        kind: self.kind.clone(),
982        cell: RefCell::new(Some(inner.try_clone()?)),
983        cell_async_task_queue: Default::default(),
984        handle: self.handle,
985      })),
986      None => Err(FsError::FileBusy),
987    }
988  }
989
990  fn as_stdio(self: Rc<Self>) -> FsResult<std::process::Stdio> {
991    match self.kind {
992      StdFileResourceKind::File => self.with_sync(|file| {
993        let file = file.try_clone()?;
994        Ok(file.into())
995      }),
996      _ => Ok(std::process::Stdio::inherit()),
997    }
998  }
999
1000  fn backing_fd(self: Rc<Self>) -> Option<ResourceHandleFd> {
1001    Some(self.handle)
1002  }
1003}
1004
1005// override op_print to use the stdout and stderr in the resource table
1006#[op2(fast)]
1007pub fn op_print(
1008  state: &mut OpState,
1009  #[string] msg: &str,
1010  is_err: bool,
1011) -> Result<(), JsErrorBox> {
1012  let rid = if is_err { 2 } else { 1 };
1013  FileResource::with_file(state, rid, move |file| {
1014    file
1015      .write_all_sync(msg.as_bytes())
1016      .map_err(JsErrorBox::from_err)
1017  })
1018}