From 5270c43e412cc636cd9923182169d166d181f78a Mon Sep 17 00:00:00 2001 From: David Sherret Date: Thu, 4 May 2023 14:28:42 -0400 Subject: [PATCH] refactor(ext/fs): boxed deno_fs::FileSystem (#18945) 1. Boxed `File` and `FileSystem` to allow more easily passing this through the CLI code (as shown within this pr). 2. `StdFileResource` is now `FileResource`. `FileResource` now contains an `Rc`. --- Cargo.lock | 3 + Cargo.toml | 1 + cli/build.rs | 9 +- cli/factory.rs | 4 + cli/standalone/mod.rs | 2 + cli/worker.rs | 36 +- core/resources.rs | 7 + ext/fs/Cargo.toml | 4 +- ext/fs/interface.rs | 164 ++------- ext/fs/lib.rs | 135 ++++---- ext/fs/ops.rs | 454 +++++++++---------------- ext/fs/std_fs.rs | 411 +++++------------------ ext/io/Cargo.toml | 3 + ext/io/fs.rs | 330 ++++++++++++++++++ ext/io/lib.rs | 737 ++++++++++++++++++++++------------------- runtime/build.rs | 6 +- runtime/ops/process.rs | 6 +- runtime/ops/tty.rs | 125 +++---- runtime/web_worker.rs | 5 +- runtime/worker.rs | 6 +- 20 files changed, 1190 insertions(+), 1258 deletions(-) create mode 100644 ext/io/fs.rs diff --git a/Cargo.lock b/Cargo.lock index 73b0f3f0de..ad816c9a2d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1059,7 +1059,10 @@ dependencies = [ name = "deno_io" version = "0.13.0" dependencies = [ + "async-trait", "deno_core", + "filetime", + "fs3", "nix", "once_cell", "tokio", diff --git a/Cargo.toml b/Cargo.toml index f7f6d553b6..4ffac7e793 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -92,6 +92,7 @@ dlopen = "0.1.8" encoding_rs = "=0.8.31" ecb = "=0.1.1" fastwebsockets = "=0.3.1" +filetime = "0.2.16" flate2 = "=1.0.24" fs3 = "0.5.0" futures = "0.3.21" diff --git a/cli/build.rs b/cli/build.rs index 21f8c229a1..6cedb53cef 100644 --- a/cli/build.rs +++ b/cli/build.rs @@ -9,7 +9,6 @@ use deno_core::Extension; use deno_core::ExtensionFileSource; use deno_core::ExtensionFileSourceCode; use deno_runtime::deno_cache::SqliteBackedCache; -use deno_runtime::deno_fs::StdFs; use deno_runtime::deno_kv::sqlite::SqliteDbHandler; use deno_runtime::permissions::PermissionsContainer; use deno_runtime::*; @@ -361,11 +360,11 @@ fn create_cli_snapshot(snapshot_path: PathBuf) { deno_napi::deno_napi::init_ops::(), deno_http::deno_http::init_ops(), deno_io::deno_io::init_ops(Default::default()), - deno_fs::deno_fs::init_ops::<_, PermissionsContainer>(false, StdFs), - deno_node::deno_node::init_ops::( - None, - Some(Arc::new(deno_node::RealFs)), + deno_fs::deno_fs::init_ops::( + false, + Arc::new(deno_fs::RealFs), ), + deno_node::deno_node::init_ops::(None, None), cli::init_ops_and_esm(), // NOTE: This needs to be init_ops_and_esm! ]; diff --git a/cli/factory.rs b/cli/factory.rs index 73d0cb8ea9..295794a51b 100644 --- a/cli/factory.rs +++ b/cli/factory.rs @@ -42,6 +42,7 @@ use crate::worker::HasNodeSpecifierChecker; use deno_core::error::AnyError; use deno_core::parking_lot::Mutex; +use deno_runtime::deno_fs; use deno_runtime::deno_node; use deno_runtime::deno_node::analyze::NodeCodeTranslator; use deno_runtime::deno_node::NodeResolver; @@ -553,6 +554,7 @@ impl CliFactory { let node_code_translator = self.node_code_translator().await?.clone(); let options = self.cli_options().clone(); let main_worker_options = self.create_cli_main_worker_options()?; + let fs = Arc::new(deno_fs::RealFs); let node_fs = self.node_fs().clone(); let root_cert_store_provider = self.root_cert_store_provider().clone(); let node_resolver = self.node_resolver().await?.clone(); @@ -579,6 +581,7 @@ impl CliFactory { ), )), root_cert_store_provider.clone(), + fs.clone(), node_fs.clone(), maybe_inspector_server.clone(), main_worker_options.clone(), @@ -610,6 +613,7 @@ impl CliFactory { ), )), self.root_cert_store_provider().clone(), + Arc::new(deno_fs::RealFs), self.node_fs().clone(), self.maybe_inspector_server().clone(), self.create_cli_main_worker_options()?, diff --git a/cli/standalone/mod.rs b/cli/standalone/mod.rs index 2ef21d417e..0f65db679e 100644 --- a/cli/standalone/mod.rs +++ b/cli/standalone/mod.rs @@ -30,6 +30,7 @@ use deno_core::ModuleSpecifier; use deno_core::ModuleType; use deno_core::ResolutionKind; use deno_graph::source::Resolver; +use deno_runtime::deno_fs; use deno_runtime::deno_node; use deno_runtime::deno_node::NodeResolver; use deno_runtime::deno_tls::rustls::RootCertStore; @@ -253,6 +254,7 @@ pub async fn run( BlobStore::default(), Box::new(module_loader_factory), root_cert_store_provider, + Arc::new(deno_fs::RealFs), node_fs, None, CliMainWorkerOptions { diff --git a/cli/worker.rs b/cli/worker.rs index ae8822fe40..5216af2638 100644 --- a/cli/worker.rs +++ b/cli/worker.rs @@ -18,6 +18,7 @@ use deno_core::SharedArrayBufferStore; use deno_core::SourceMapGetter; use deno_runtime::colors; use deno_runtime::deno_broadcast_channel::InMemoryBroadcastChannel; +use deno_runtime::deno_fs; use deno_runtime::deno_node; use deno_runtime::deno_node::NodeResolution; use deno_runtime::deno_node::NodeResolver; @@ -97,6 +98,7 @@ struct SharedWorkerState { compiled_wasm_module_store: CompiledWasmModuleStore, module_loader_factory: Box, root_cert_store_provider: Arc, + fs: Arc, node_fs: Arc, maybe_inspector_server: Option>, } @@ -308,6 +310,7 @@ impl CliMainWorkerFactory { blob_store: BlobStore, module_loader_factory: Box, root_cert_store_provider: Arc, + fs: Arc, node_fs: Arc, maybe_inspector_server: Option>, options: CliMainWorkerOptions, @@ -325,6 +328,7 @@ impl CliMainWorkerFactory { compiled_wasm_module_store: Default::default(), module_loader_factory, root_cert_store_provider, + fs, node_fs, maybe_inspector_server, }), @@ -445,6 +449,7 @@ impl CliMainWorkerFactory { should_break_on_first_statement: shared.options.inspect_brk, should_wait_for_inspector_session: shared.options.inspect_wait, module_loader, + fs: shared.fs.clone(), node_fs: Some(shared.node_fs.clone()), npm_resolver: Some(shared.npm_resolver.clone()), get_error_class_fn: Some(&errors::get_error_class_name), @@ -570,6 +575,7 @@ fn create_web_worker_callback( format_js_error_fn: Some(Arc::new(format_js_error)), source_map_getter: maybe_source_map_getter, module_loader, + fs: shared.fs.clone(), node_fs: Some(shared.node_fs.clone()), npm_resolver: Some(shared.npm_resolver.clone()), worker_type: args.worker_type, @@ -597,13 +603,8 @@ fn create_web_worker_callback( #[cfg(test)] mod tests { - use std::rc::Rc; - use super::*; use deno_core::resolve_path; - use deno_core::FsModuleLoader; - use deno_runtime::deno_broadcast_channel::InMemoryBroadcastChannel; - use deno_runtime::deno_web::BlobStore; use deno_runtime::permissions::Permissions; fn create_test_worker() -> MainWorker { @@ -612,31 +613,8 @@ mod tests { let permissions = PermissionsContainer::new(Permissions::default()); let options = WorkerOptions { - bootstrap: BootstrapOptions::default(), - extensions: vec![], startup_snapshot: Some(crate::js::deno_isolate_init()), - unsafely_ignore_certificate_errors: None, - root_cert_store_provider: None, - seed: None, - format_js_error_fn: None, - source_map_getter: None, - web_worker_preload_module_cb: Arc::new(|_| unreachable!()), - web_worker_pre_execute_module_cb: Arc::new(|_| unreachable!()), - create_web_worker_cb: Arc::new(|_| unreachable!()), - maybe_inspector_server: None, - should_break_on_first_statement: false, - should_wait_for_inspector_session: false, - module_loader: Rc::new(FsModuleLoader), - node_fs: Some(Arc::new(deno_node::RealFs)), - npm_resolver: None, - get_error_class_fn: None, - cache_storage_dir: None, - origin_storage_dir: None, - blob_store: BlobStore::default(), - broadcast_channel: InMemoryBroadcastChannel::default(), - shared_array_buffer_store: None, - compiled_wasm_module_store: None, - stdio: Default::default(), + ..Default::default() }; MainWorker::bootstrap_from_options(main_module, permissions, options) diff --git a/core/resources.rs b/core/resources.rs index 84e6847fc6..94d2a2306a 100644 --- a/core/resources.rs +++ b/core/resources.rs @@ -187,6 +187,13 @@ pub trait Resource: Any + 'static { None } + /// Resources backed by a file descriptor can let ops know to allow for + /// low-level optimizations. + #[cfg(windows)] + fn backing_fd(self: Rc) -> Option { + None + } + fn size_hint(&self) -> (u64, Option) { (0, None) } diff --git a/ext/fs/Cargo.toml b/ext/fs/Cargo.toml index 10c71a5438..f6d563b64d 100644 --- a/ext/fs/Cargo.toml +++ b/ext/fs/Cargo.toml @@ -17,8 +17,8 @@ path = "lib.rs" async-trait.workspace = true deno_core.workspace = true deno_io.workspace = true -filetime = "0.2.16" -fs3 = "0.5.0" +filetime.workspace = true +fs3.workspace = true libc.workspace = true log.workspace = true rand.workspace = true diff --git a/ext/fs/interface.rs b/ext/fs/interface.rs index 184cb8096f..1847b59828 100644 --- a/ext/fs/interface.rs +++ b/ext/fs/interface.rs @@ -1,6 +1,5 @@ // Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. -use std::io; use std::path::Path; use std::path::PathBuf; use std::rc::Rc; @@ -8,6 +7,10 @@ use std::rc::Rc; use serde::Deserialize; use serde::Serialize; +use deno_io::fs::File; +use deno_io::fs::FsResult; +use deno_io::fs::FsStat; + #[derive(Deserialize, Default, Debug, Clone, Copy)] #[serde(rename_all = "camelCase")] #[serde(default)] @@ -52,27 +55,6 @@ impl OpenOptions { } } -pub struct FsStat { - pub is_file: bool, - pub is_directory: bool, - pub is_symlink: bool, - pub size: u64, - - pub mtime: Option, - pub atime: Option, - pub birthtime: Option, - - pub dev: u64, - pub ino: u64, - pub mode: u32, - pub nlink: u64, - pub uid: u32, - pub gid: u32, - pub rdev: u64, - pub blksize: u64, - pub blocks: u64, -} - #[derive(Deserialize)] pub enum FsFileType { #[serde(rename = "file")] @@ -90,93 +72,25 @@ pub struct FsDirEntry { pub is_symlink: bool, } -pub enum FsError { - Io(io::Error), - FileBusy, - NotSupported, -} - -impl From for FsError { - fn from(err: io::Error) -> Self { - Self::Io(err) - } -} - -pub type FsResult = Result; - #[async_trait::async_trait(?Send)] -pub trait File { - fn write_all_sync(self: Rc, buf: &[u8]) -> FsResult<()>; - async fn write_all_async(self: Rc, buf: Vec) -> FsResult<()>; - - fn read_all_sync(self: Rc) -> FsResult>; - async fn read_all_async(self: Rc) -> FsResult>; - - fn chmod_sync(self: Rc, pathmode: u32) -> FsResult<()>; - async fn chmod_async(self: Rc, mode: u32) -> FsResult<()>; - - fn seek_sync(self: Rc, pos: io::SeekFrom) -> FsResult; - async fn seek_async(self: Rc, pos: io::SeekFrom) -> FsResult; - - fn datasync_sync(self: Rc) -> FsResult<()>; - async fn datasync_async(self: Rc) -> FsResult<()>; - - fn sync_sync(self: Rc) -> FsResult<()>; - async fn sync_async(self: Rc) -> FsResult<()>; - - fn stat_sync(self: Rc) -> FsResult; - async fn stat_async(self: Rc) -> FsResult; - - fn lock_sync(self: Rc, exclusive: bool) -> FsResult<()>; - async fn lock_async(self: Rc, exclusive: bool) -> FsResult<()>; - fn unlock_sync(self: Rc) -> FsResult<()>; - async fn unlock_async(self: Rc) -> FsResult<()>; - - fn truncate_sync(self: Rc, len: u64) -> FsResult<()>; - async fn truncate_async(self: Rc, len: u64) -> FsResult<()>; - - fn utime_sync( - self: Rc, - atime_secs: i64, - atime_nanos: u32, - mtime_secs: i64, - mtime_nanos: u32, - ) -> FsResult<()>; - async fn utime_async( - self: Rc, - atime_secs: i64, - atime_nanos: u32, - mtime_secs: i64, - mtime_nanos: u32, - ) -> FsResult<()>; -} - -#[async_trait::async_trait(?Send)] -pub trait FileSystem: Clone { - type File: File; - +pub trait FileSystem: Send + Sync { fn cwd(&self) -> FsResult; fn tmp_dir(&self) -> FsResult; - fn chdir(&self, path: impl AsRef) -> FsResult<()>; + fn chdir(&self, path: &Path) -> FsResult<()>; fn umask(&self, mask: Option) -> FsResult; fn open_sync( &self, - path: impl AsRef, + path: &Path, options: OpenOptions, - ) -> FsResult; + ) -> FsResult>; async fn open_async( &self, path: PathBuf, options: OpenOptions, - ) -> FsResult; + ) -> FsResult>; - fn mkdir_sync( - &self, - path: impl AsRef, - recusive: bool, - mode: u32, - ) -> FsResult<()>; + fn mkdir_sync(&self, path: &Path, recusive: bool, mode: u32) -> FsResult<()>; async fn mkdir_async( &self, path: PathBuf, @@ -184,12 +98,12 @@ pub trait FileSystem: Clone { mode: u32, ) -> FsResult<()>; - fn chmod_sync(&self, path: impl AsRef, mode: u32) -> FsResult<()>; + fn chmod_sync(&self, path: &Path, mode: u32) -> FsResult<()>; async fn chmod_async(&self, path: PathBuf, mode: u32) -> FsResult<()>; fn chown_sync( &self, - path: impl AsRef, + path: &Path, uid: Option, gid: Option, ) -> FsResult<()>; @@ -200,52 +114,36 @@ pub trait FileSystem: Clone { gid: Option, ) -> FsResult<()>; - fn remove_sync( - &self, - path: impl AsRef, - recursive: bool, - ) -> FsResult<()>; + fn remove_sync(&self, path: &Path, recursive: bool) -> FsResult<()>; async fn remove_async(&self, path: PathBuf, recursive: bool) -> FsResult<()>; - fn copy_file_sync( - &self, - oldpath: impl AsRef, - newpath: impl AsRef, - ) -> FsResult<()>; + fn copy_file_sync(&self, oldpath: &Path, newpath: &Path) -> FsResult<()>; async fn copy_file_async( &self, oldpath: PathBuf, newpath: PathBuf, ) -> FsResult<()>; - fn stat_sync(&self, path: impl AsRef) -> FsResult; + fn stat_sync(&self, path: &Path) -> FsResult; async fn stat_async(&self, path: PathBuf) -> FsResult; - fn lstat_sync(&self, path: impl AsRef) -> FsResult; + fn lstat_sync(&self, path: &Path) -> FsResult; async fn lstat_async(&self, path: PathBuf) -> FsResult; - fn realpath_sync(&self, path: impl AsRef) -> FsResult; + fn realpath_sync(&self, path: &Path) -> FsResult; async fn realpath_async(&self, path: PathBuf) -> FsResult; - fn read_dir_sync(&self, path: impl AsRef) -> FsResult>; + fn read_dir_sync(&self, path: &Path) -> FsResult>; async fn read_dir_async(&self, path: PathBuf) -> FsResult>; - fn rename_sync( - &self, - oldpath: impl AsRef, - newpath: impl AsRef, - ) -> FsResult<()>; + fn rename_sync(&self, oldpath: &Path, newpath: &Path) -> FsResult<()>; async fn rename_async( &self, oldpath: PathBuf, newpath: PathBuf, ) -> FsResult<()>; - fn link_sync( - &self, - oldpath: impl AsRef, - newpath: impl AsRef, - ) -> FsResult<()>; + fn link_sync(&self, oldpath: &Path, newpath: &Path) -> FsResult<()>; async fn link_async( &self, oldpath: PathBuf, @@ -254,8 +152,8 @@ pub trait FileSystem: Clone { fn symlink_sync( &self, - oldpath: impl AsRef, - newpath: impl AsRef, + oldpath: &Path, + newpath: &Path, file_type: Option, ) -> FsResult<()>; async fn symlink_async( @@ -265,15 +163,15 @@ pub trait FileSystem: Clone { file_type: Option, ) -> FsResult<()>; - fn read_link_sync(&self, path: impl AsRef) -> FsResult; + fn read_link_sync(&self, path: &Path) -> FsResult; async fn read_link_async(&self, path: PathBuf) -> FsResult; - fn truncate_sync(&self, path: impl AsRef, len: u64) -> FsResult<()>; + fn truncate_sync(&self, path: &Path, len: u64) -> FsResult<()>; async fn truncate_async(&self, path: PathBuf, len: u64) -> FsResult<()>; fn utime_sync( &self, - path: impl AsRef, + path: &Path, atime_secs: i64, atime_nanos: u32, mtime_secs: i64, @@ -290,12 +188,11 @@ pub trait FileSystem: Clone { fn write_file_sync( &self, - path: impl AsRef, + path: &Path, options: OpenOptions, data: &[u8], ) -> FsResult<()> { let file = self.open_sync(path, options)?; - let file = Rc::new(file); if let Some(mode) = options.mode { file.clone().chmod_sync(mode)?; } @@ -309,25 +206,22 @@ pub trait FileSystem: Clone { data: Vec, ) -> FsResult<()> { let file = self.open_async(path, options).await?; - let file = Rc::new(file); if let Some(mode) = options.mode { file.clone().chmod_async(mode).await?; } - file.write_all_async(data).await?; + file.write_all(data.into()).await?; Ok(()) } - fn read_file_sync(&self, path: impl AsRef) -> FsResult> { + fn read_file_sync(&self, path: &Path) -> FsResult> { let options = OpenOptions::read(); let file = self.open_sync(path, options)?; - let file = Rc::new(file); let buf = file.read_all_sync()?; Ok(buf) } async fn read_file_async(&self, path: PathBuf) -> FsResult> { let options = OpenOptions::read(); - let file = self.clone().open_async(path, options).await?; - let file = Rc::new(file); + let file = self.open_async(path, options).await?; let buf = file.read_all_async().await?; Ok(buf) } diff --git a/ext/fs/lib.rs b/ext/fs/lib.rs index 464d84adeb..4fdf6b3f11 100644 --- a/ext/fs/lib.rs +++ b/ext/fs/lib.rs @@ -4,25 +4,21 @@ mod interface; mod ops; mod std_fs; -pub use crate::interface::File; pub use crate::interface::FileSystem; pub use crate::interface::FsDirEntry; -pub use crate::interface::FsError; pub use crate::interface::FsFileType; -pub use crate::interface::FsResult; -pub use crate::interface::FsStat; pub use crate::interface::OpenOptions; use crate::ops::*; -pub use crate::std_fs::StdFs; +pub use crate::std_fs::RealFs; use deno_core::error::AnyError; use deno_core::OpState; -use deno_core::Resource; use std::cell::RefCell; use std::convert::From; use std::path::Path; use std::rc::Rc; +use std::sync::Arc; pub trait FsPermissions { fn check_read(&mut self, p: &Path, api_name: &str) -> Result<(), AnyError>; @@ -87,78 +83,77 @@ pub(crate) fn check_unstable2(state: &Rc>, api_name: &str) { deno_core::extension!(deno_fs, deps = [ deno_web ], - parameters = [Fs: FileSystem, P: FsPermissions], - bounds = [Fs::File: Resource], + parameters = [P: FsPermissions], ops = [ - op_cwd, - op_umask, - op_chdir, + op_cwd

, + op_umask, + op_chdir

, - op_open_sync, - op_open_async, - op_mkdir_sync, - op_mkdir_async, - op_chmod_sync, - op_chmod_async, - op_chown_sync, - op_chown_async, - op_remove_sync, - op_remove_async, - op_copy_file_sync, - op_copy_file_async, - op_stat_sync, - op_stat_async, - op_lstat_sync, - op_lstat_async, - op_realpath_sync, - op_realpath_async, - op_read_dir_sync, - op_read_dir_async, - op_rename_sync, - op_rename_async, - op_link_sync, - op_link_async, - op_symlink_sync, - op_symlink_async, - op_read_link_sync, - op_read_link_async, - op_truncate_sync, - op_truncate_async, - op_utime_sync, - op_utime_async, - op_make_temp_dir_sync, - op_make_temp_dir_async, - op_make_temp_file_sync, - op_make_temp_file_async, - op_write_file_sync, - op_write_file_async, - op_read_file_sync, - op_read_file_async, - op_read_file_text_sync, - op_read_file_text_async, + op_open_sync

, + op_open_async

, + op_mkdir_sync

, + op_mkdir_async

, + op_chmod_sync

, + op_chmod_async

, + op_chown_sync

, + op_chown_async

, + op_remove_sync

, + op_remove_async

, + op_copy_file_sync

, + op_copy_file_async

, + op_stat_sync

, + op_stat_async

, + op_lstat_sync

, + op_lstat_async

, + op_realpath_sync

, + op_realpath_async

, + op_read_dir_sync

, + op_read_dir_async

, + op_rename_sync

, + op_rename_async

, + op_link_sync

, + op_link_async

, + op_symlink_sync

, + op_symlink_async

, + op_read_link_sync

, + op_read_link_async

, + op_truncate_sync

, + op_truncate_async

, + op_utime_sync

, + op_utime_async

, + op_make_temp_dir_sync

, + op_make_temp_dir_async

, + op_make_temp_file_sync

, + op_make_temp_file_async

, + op_write_file_sync

, + op_write_file_async

, + op_read_file_sync

, + op_read_file_async

, + op_read_file_text_sync

, + op_read_file_text_async

, - op_seek_sync, - op_seek_async, - op_fdatasync_sync, - op_fdatasync_async, - op_fsync_sync, - op_fsync_async, - op_fstat_sync, - op_fstat_async, - op_flock_sync, - op_flock_async, - op_funlock_sync, - op_funlock_async, - op_ftruncate_sync, - op_ftruncate_async, - op_futime_sync, - op_futime_async, + op_seek_sync, + op_seek_async, + op_fdatasync_sync, + op_fdatasync_async, + op_fsync_sync, + op_fsync_async, + op_fstat_sync, + op_fstat_async, + op_flock_sync, + op_flock_async, + op_funlock_sync, + op_funlock_async, + op_ftruncate_sync, + op_ftruncate_async, + op_futime_sync, + op_futime_async, ], esm = [ "30_fs.js" ], options = { unstable: bool, - fs: Fs, + fs: Arc, }, state = |state, options| { state.put(UnstableChecker { unstable: options.unstable }); diff --git a/ext/fs/ops.rs b/ext/fs/ops.rs index 8c5d212015..c9996d8ce7 100644 --- a/ext/fs/ops.rs +++ b/ext/fs/ops.rs @@ -7,65 +7,39 @@ use std::io::SeekFrom; use std::path::Path; use std::path::PathBuf; use std::rc::Rc; +use std::sync::Arc; use deno_core::error::custom_error; -use deno_core::error::not_supported; -use deno_core::error::resource_unavailable; use deno_core::error::type_error; use deno_core::error::AnyError; use deno_core::op; use deno_core::CancelFuture; use deno_core::CancelHandle; use deno_core::OpState; -use deno_core::Resource; use deno_core::ResourceId; use deno_core::ZeroCopyBuf; +use deno_io::fs::FileResource; +use deno_io::fs::FsError; +use deno_io::fs::FsStat; use rand::rngs::ThreadRng; use rand::thread_rng; use rand::Rng; use serde::Serialize; -use tokio::task::JoinError; use crate::check_unstable; use crate::check_unstable2; use crate::interface::FsDirEntry; -use crate::interface::FsError; use crate::interface::FsFileType; -use crate::interface::FsStat; -use crate::File; use crate::FileSystem; use crate::FsPermissions; use crate::OpenOptions; -impl From for FsError { - fn from(err: JoinError) -> Self { - if err.is_cancelled() { - todo!("async tasks must not be cancelled") - } - if err.is_panic() { - std::panic::resume_unwind(err.into_panic()); // resume the panic on the main thread - } - unreachable!() - } -} - -impl From for AnyError { - fn from(err: FsError) -> Self { - match err { - FsError::Io(err) => AnyError::from(err), - FsError::FileBusy => resource_unavailable(), - FsError::NotSupported => not_supported(), - } - } -} - #[op] -pub fn op_cwd(state: &mut OpState) -> Result +pub fn op_cwd

(state: &mut OpState) -> Result where - Fs: FileSystem + 'static, P: FsPermissions + 'static, { - let fs = state.borrow::(); + let fs = state.borrow::>(); let path = fs.cwd()?; state .borrow_mut::

() @@ -75,34 +49,36 @@ where } #[op] -fn op_chdir(state: &mut OpState, directory: &str) -> Result<(), AnyError> +fn op_chdir

(state: &mut OpState, directory: &str) -> Result<(), AnyError> where - Fs: FileSystem + 'static, P: FsPermissions + 'static, { let d = PathBuf::from(&directory); state.borrow_mut::

().check_read(&d, "Deno.chdir()")?; - state.borrow::().chdir(&d).context_path("chdir", &d) + state + .borrow::>() + .chdir(&d) + .context_path("chdir", &d) } #[op] -fn op_umask(state: &mut OpState, mask: Option) -> Result +fn op_umask(state: &mut OpState, mask: Option) -> Result where - Fs: FileSystem + 'static, { check_unstable(state, "Deno.umask"); - state.borrow::().umask(mask).context("umask") + state + .borrow::>() + .umask(mask) + .context("umask") } #[op] -fn op_open_sync( +fn op_open_sync

( state: &mut OpState, path: String, options: Option, ) -> Result where - Fs: FileSystem + 'static, - Fs::File: Resource, P: FsPermissions + 'static, { let path = PathBuf::from(path); @@ -111,22 +87,22 @@ where let permissions = state.borrow_mut::

(); permissions.check(&options, &path, "Deno.openSync()")?; - let fs = state.borrow::(); + let fs = state.borrow::>(); let file = fs.open_sync(&path, options).context_path("open", &path)?; - let rid = state.resource_table.add(file); + let rid = state + .resource_table + .add(FileResource::new(file, "fsFile".to_string())); Ok(rid) } #[op] -async fn op_open_async( +async fn op_open_async

( state: Rc>, path: String, options: Option, ) -> Result where - Fs: FileSystem + 'static, - Fs::File: Resource, P: FsPermissions + 'static, { let path = PathBuf::from(path); @@ -136,26 +112,28 @@ where let mut state = state.borrow_mut(); let permissions = state.borrow_mut::

(); permissions.check(&options, &path, "Deno.open()")?; - state.borrow::().clone() + state.borrow::>().clone() }; let file = fs .open_async(path.clone(), options) .await .context_path("open", &path)?; - let rid = state.borrow_mut().resource_table.add(file); + let rid = state + .borrow_mut() + .resource_table + .add(FileResource::new(file, "fsFile".to_string())); Ok(rid) } #[op] -fn op_mkdir_sync( +fn op_mkdir_sync

( state: &mut OpState, path: String, recursive: bool, mode: Option, ) -> Result<(), AnyError> where - Fs: FileSystem + 'static, P: FsPermissions + 'static, { let path = PathBuf::from(path); @@ -166,7 +144,7 @@ where .borrow_mut::

() .check_write(&path, "Deno.mkdirSync()")?; - let fs = state.borrow::(); + let fs = state.borrow::>(); fs.mkdir_sync(&path, recursive, mode) .context_path("mkdir", &path)?; @@ -174,14 +152,13 @@ where } #[op] -async fn op_mkdir_async( +async fn op_mkdir_async

( state: Rc>, path: String, recursive: bool, mode: Option, ) -> Result<(), AnyError> where - Fs: FileSystem + 'static, P: FsPermissions + 'static, { let path = PathBuf::from(path); @@ -191,7 +168,7 @@ where let fs = { let mut state = state.borrow_mut(); state.borrow_mut::

().check_write(&path, "Deno.mkdir()")?; - state.borrow::().clone() + state.borrow::>().clone() }; fs.mkdir_async(path.clone(), recursive, mode) @@ -202,39 +179,37 @@ where } #[op] -fn op_chmod_sync( +fn op_chmod_sync

( state: &mut OpState, path: String, mode: u32, ) -> Result<(), AnyError> where - Fs: FileSystem + 'static, P: FsPermissions + 'static, { let path = PathBuf::from(path); state .borrow_mut::

() .check_write(&path, "Deno.chmodSync()")?; - let fs = state.borrow::(); + let fs = state.borrow::>(); fs.chmod_sync(&path, mode).context_path("chmod", &path)?; Ok(()) } #[op] -async fn op_chmod_async( +async fn op_chmod_async

( state: Rc>, path: String, mode: u32, ) -> Result<(), AnyError> where - Fs: FileSystem + 'static, P: FsPermissions + 'static, { let path = PathBuf::from(path); let fs = { let mut state = state.borrow_mut(); state.borrow_mut::

().check_write(&path, "Deno.chmod()")?; - state.borrow::().clone() + state.borrow::>().clone() }; fs.chmod_async(path.clone(), mode) .await @@ -243,42 +218,40 @@ where } #[op] -fn op_chown_sync( +fn op_chown_sync

( state: &mut OpState, path: String, uid: Option, gid: Option, ) -> Result<(), AnyError> where - Fs: FileSystem + 'static, P: FsPermissions + 'static, { let path = PathBuf::from(path); state .borrow_mut::

() .check_write(&path, "Deno.chownSync()")?; - let fs = state.borrow::(); + let fs = state.borrow::>(); fs.chown_sync(&path, uid, gid) .context_path("chown", &path)?; Ok(()) } #[op] -async fn op_chown_async( +async fn op_chown_async

( state: Rc>, path: String, uid: Option, gid: Option, ) -> Result<(), AnyError> where - Fs: FileSystem + 'static, P: FsPermissions + 'static, { let path = PathBuf::from(path); let fs = { let mut state = state.borrow_mut(); state.borrow_mut::

().check_write(&path, "Deno.chown()")?; - state.borrow::().clone() + state.borrow::>().clone() }; fs.chown_async(path.clone(), uid, gid) .await @@ -287,13 +260,12 @@ where } #[op] -fn op_remove_sync( +fn op_remove_sync

( state: &mut OpState, path: &str, recursive: bool, ) -> Result<(), AnyError> where - Fs: FileSystem + 'static, P: FsPermissions + 'static, { let path = PathBuf::from(path); @@ -302,7 +274,7 @@ where .borrow_mut::

() .check_write(&path, "Deno.removeSync()")?; - let fs = state.borrow::(); + let fs = state.borrow::>(); fs.remove_sync(&path, recursive) .context_path("remove", &path)?; @@ -310,13 +282,12 @@ where } #[op] -async fn op_remove_async( +async fn op_remove_async

( state: Rc>, path: String, recursive: bool, ) -> Result<(), AnyError> where - Fs: FileSystem + 'static, P: FsPermissions + 'static, { let path = PathBuf::from(path); @@ -326,7 +297,7 @@ where state .borrow_mut::

() .check_write(&path, "Deno.remove()")?; - state.borrow::().clone() + state.borrow::>().clone() }; fs.remove_async(path.clone(), recursive) @@ -337,13 +308,12 @@ where } #[op] -fn op_copy_file_sync( +fn op_copy_file_sync

( state: &mut OpState, from: &str, to: &str, ) -> Result<(), AnyError> where - Fs: FileSystem + 'static, P: FsPermissions + 'static, { let from = PathBuf::from(from); @@ -353,7 +323,7 @@ where permissions.check_read(&from, "Deno.copyFileSync()")?; permissions.check_write(&to, "Deno.copyFileSync()")?; - let fs = state.borrow::(); + let fs = state.borrow::>(); fs.copy_file_sync(&from, &to) .context_two_path("copy", &from, &to)?; @@ -361,13 +331,12 @@ where } #[op] -async fn op_copy_file_async( +async fn op_copy_file_async

( state: Rc>, from: String, to: String, ) -> Result<(), AnyError> where - Fs: FileSystem + 'static, P: FsPermissions + 'static, { let from = PathBuf::from(from); @@ -378,7 +347,7 @@ where let permissions = state.borrow_mut::

(); permissions.check_read(&from, "Deno.copyFile()")?; permissions.check_write(&to, "Deno.copyFile()")?; - state.borrow::().clone() + state.borrow::>().clone() }; fs.copy_file_async(from.clone(), to.clone()) @@ -389,20 +358,19 @@ where } #[op] -fn op_stat_sync( +fn op_stat_sync

( state: &mut OpState, path: String, stat_out_buf: &mut [u32], ) -> Result<(), AnyError> where - Fs: FileSystem + 'static, P: FsPermissions + 'static, { let path = PathBuf::from(path); state .borrow_mut::

() .check_read(&path, "Deno.statSync()")?; - let fs = state.borrow::(); + let fs = state.borrow::>(); let stat = fs.stat_sync(&path).context_path("stat", &path)?; let serializable_stat = SerializableStat::from(stat); serializable_stat.write(stat_out_buf); @@ -410,12 +378,11 @@ where } #[op] -async fn op_stat_async( +async fn op_stat_async

( state: Rc>, path: String, ) -> Result where - Fs: FileSystem + 'static, P: FsPermissions + 'static, { let path = PathBuf::from(path); @@ -423,7 +390,7 @@ where let mut state = state.borrow_mut(); let permissions = state.borrow_mut::

(); permissions.check_read(&path, "Deno.stat()")?; - state.borrow::().clone() + state.borrow::>().clone() }; let stat = fs .stat_async(path.clone()) @@ -433,20 +400,19 @@ where } #[op] -fn op_lstat_sync( +fn op_lstat_sync

( state: &mut OpState, path: String, stat_out_buf: &mut [u32], ) -> Result<(), AnyError> where - Fs: FileSystem + 'static, P: FsPermissions + 'static, { let path = PathBuf::from(path); state .borrow_mut::

() .check_read(&path, "Deno.lstatSync()")?; - let fs = state.borrow::(); + let fs = state.borrow::>(); let stat = fs.lstat_sync(&path).context_path("lstat", &path)?; let serializable_stat = SerializableStat::from(stat); serializable_stat.write(stat_out_buf); @@ -454,12 +420,11 @@ where } #[op] -async fn op_lstat_async( +async fn op_lstat_async

( state: Rc>, path: String, ) -> Result where - Fs: FileSystem + 'static, P: FsPermissions + 'static, { let path = PathBuf::from(path); @@ -467,7 +432,7 @@ where let mut state = state.borrow_mut(); let permissions = state.borrow_mut::

(); permissions.check_read(&path, "Deno.lstat()")?; - state.borrow::().clone() + state.borrow::>().clone() }; let stat = fs .lstat_async(path.clone()) @@ -477,17 +442,16 @@ where } #[op] -fn op_realpath_sync( +fn op_realpath_sync

( state: &mut OpState, path: String, ) -> Result where - Fs: FileSystem + 'static, P: FsPermissions + 'static, { let path = PathBuf::from(path); - let fs = state.borrow::().clone(); + let fs = state.borrow::>().clone(); let permissions = state.borrow_mut::

(); permissions.check_read(&path, "Deno.realPathSync()")?; if path.is_relative() { @@ -502,12 +466,11 @@ where } #[op] -async fn op_realpath_async( +async fn op_realpath_async

( state: Rc>, path: String, ) -> Result where - Fs: FileSystem + 'static, P: FsPermissions + 'static, { let path = PathBuf::from(path); @@ -515,7 +478,7 @@ where let fs; { let mut state = state.borrow_mut(); - fs = state.borrow::().clone(); + fs = state.borrow::>().clone(); let permissions = state.borrow_mut::

(); permissions.check_read(&path, "Deno.realPath()")?; if path.is_relative() { @@ -532,12 +495,11 @@ where } #[op] -fn op_read_dir_sync( +fn op_read_dir_sync

( state: &mut OpState, path: String, ) -> Result, AnyError> where - Fs: FileSystem + 'static, P: FsPermissions + 'static, { let path = PathBuf::from(path); @@ -546,19 +508,18 @@ where .borrow_mut::

() .check_read(&path, "Deno.readDirSync()")?; - let fs = state.borrow::(); + let fs = state.borrow::>(); let entries = fs.read_dir_sync(&path).context_path("readdir", &path)?; Ok(entries) } #[op] -async fn op_read_dir_async( +async fn op_read_dir_async

( state: Rc>, path: String, ) -> Result, AnyError> where - Fs: FileSystem + 'static, P: FsPermissions + 'static, { let path = PathBuf::from(path); @@ -568,7 +529,7 @@ where state .borrow_mut::

() .check_read(&path, "Deno.readDir()")?; - state.borrow::().clone() + state.borrow::>().clone() }; let entries = fs @@ -580,13 +541,12 @@ where } #[op] -fn op_rename_sync( +fn op_rename_sync

( state: &mut OpState, oldpath: String, newpath: String, ) -> Result<(), AnyError> where - Fs: FileSystem + 'static, P: FsPermissions + 'static, { let oldpath = PathBuf::from(oldpath); @@ -597,7 +557,7 @@ where permissions.check_write(&oldpath, "Deno.renameSync()")?; permissions.check_write(&newpath, "Deno.renameSync()")?; - let fs = state.borrow::(); + let fs = state.borrow::>(); fs.rename_sync(&oldpath, &newpath) .context_two_path("rename", &oldpath, &newpath)?; @@ -605,13 +565,12 @@ where } #[op] -async fn op_rename_async( +async fn op_rename_async

( state: Rc>, oldpath: String, newpath: String, ) -> Result<(), AnyError> where - Fs: FileSystem + 'static, P: FsPermissions + 'static, { let oldpath = PathBuf::from(oldpath); @@ -623,7 +582,7 @@ where permissions.check_read(&oldpath, "Deno.rename()")?; permissions.check_write(&oldpath, "Deno.rename()")?; permissions.check_write(&newpath, "Deno.rename()")?; - state.borrow::().clone() + state.borrow::>().clone() }; fs.rename_async(oldpath.clone(), newpath.clone()) @@ -634,13 +593,12 @@ where } #[op] -fn op_link_sync( +fn op_link_sync

( state: &mut OpState, oldpath: &str, newpath: &str, ) -> Result<(), AnyError> where - Fs: FileSystem + 'static, P: FsPermissions + 'static, { let oldpath = PathBuf::from(oldpath); @@ -652,7 +610,7 @@ where permissions.check_read(&newpath, "Deno.linkSync()")?; permissions.check_write(&newpath, "Deno.linkSync()")?; - let fs = state.borrow::(); + let fs = state.borrow::>(); fs.link_sync(&oldpath, &newpath) .context_two_path("link", &oldpath, &newpath)?; @@ -660,13 +618,12 @@ where } #[op] -async fn op_link_async( +async fn op_link_async

( state: Rc>, oldpath: String, newpath: String, ) -> Result<(), AnyError> where - Fs: FileSystem + 'static, P: FsPermissions + 'static, { let oldpath = PathBuf::from(&oldpath); @@ -679,7 +636,7 @@ where permissions.check_write(&oldpath, "Deno.link()")?; permissions.check_read(&newpath, "Deno.link()")?; permissions.check_write(&newpath, "Deno.link()")?; - state.borrow::().clone() + state.borrow::>().clone() }; fs.link_async(oldpath.clone(), newpath.clone()) @@ -690,14 +647,13 @@ where } #[op] -fn op_symlink_sync( +fn op_symlink_sync

( state: &mut OpState, oldpath: &str, newpath: &str, file_type: Option, ) -> Result<(), AnyError> where - Fs: FileSystem + 'static, P: FsPermissions + 'static, { let oldpath = PathBuf::from(oldpath); @@ -707,7 +663,7 @@ where permissions.check_write_all("Deno.symlinkSync()")?; permissions.check_read_all("Deno.symlinkSync()")?; - let fs = state.borrow::(); + let fs = state.borrow::>(); fs.symlink_sync(&oldpath, &newpath, file_type) .context_two_path("symlink", &oldpath, &newpath)?; @@ -715,14 +671,13 @@ where } #[op] -async fn op_symlink_async( +async fn op_symlink_async

( state: Rc>, oldpath: String, newpath: String, file_type: Option, ) -> Result<(), AnyError> where - Fs: FileSystem + 'static, P: FsPermissions + 'static, { let oldpath = PathBuf::from(&oldpath); @@ -733,7 +688,7 @@ where let permissions = state.borrow_mut::

(); permissions.check_write_all("Deno.symlink()")?; permissions.check_read_all("Deno.symlink()")?; - state.borrow::().clone() + state.borrow::>().clone() }; fs.symlink_async(oldpath.clone(), newpath.clone(), file_type) @@ -744,12 +699,11 @@ where } #[op] -fn op_read_link_sync( +fn op_read_link_sync

( state: &mut OpState, path: String, ) -> Result where - Fs: FileSystem + 'static, P: FsPermissions + 'static, { let path = PathBuf::from(path); @@ -758,7 +712,7 @@ where .borrow_mut::

() .check_read(&path, "Deno.readLink()")?; - let fs = state.borrow::(); + let fs = state.borrow::>(); let target = fs.read_link_sync(&path).context_path("readlink", &path)?; let target_string = path_into_string(target.into_os_string())?; @@ -766,12 +720,11 @@ where } #[op] -async fn op_read_link_async( +async fn op_read_link_async

( state: Rc>, path: String, ) -> Result where - Fs: FileSystem + 'static, P: FsPermissions + 'static, { let path = PathBuf::from(path); @@ -781,7 +734,7 @@ where state .borrow_mut::

() .check_read(&path, "Deno.readLink()")?; - state.borrow::().clone() + state.borrow::>().clone() }; let target = fs @@ -793,13 +746,12 @@ where } #[op] -fn op_truncate_sync( +fn op_truncate_sync

( state: &mut OpState, path: &str, len: u64, ) -> Result<(), AnyError> where - Fs: FileSystem + 'static, P: FsPermissions + 'static, { let path = PathBuf::from(path); @@ -808,7 +760,7 @@ where .borrow_mut::

() .check_write(&path, "Deno.truncateSync()")?; - let fs = state.borrow::(); + let fs = state.borrow::>(); fs.truncate_sync(&path, len) .context_path("truncate", &path)?; @@ -816,13 +768,12 @@ where } #[op] -async fn op_truncate_async( +async fn op_truncate_async

( state: Rc>, path: String, len: u64, ) -> Result<(), AnyError> where - Fs: FileSystem + 'static, P: FsPermissions + 'static, { let path = PathBuf::from(path); @@ -832,7 +783,7 @@ where state .borrow_mut::

() .check_write(&path, "Deno.truncate()")?; - state.borrow::().clone() + state.borrow::>().clone() }; fs.truncate_async(path.clone(), len) @@ -843,7 +794,7 @@ where } #[op] -fn op_utime_sync( +fn op_utime_sync

( state: &mut OpState, path: &str, atime_secs: i64, @@ -852,14 +803,13 @@ fn op_utime_sync( mtime_nanos: u32, ) -> Result<(), AnyError> where - Fs: FileSystem + 'static, P: FsPermissions + 'static, { let path = PathBuf::from(path); state.borrow_mut::

().check_write(&path, "Deno.utime()")?; - let fs = state.borrow::(); + let fs = state.borrow::>(); fs.utime_sync(&path, atime_secs, atime_nanos, mtime_secs, mtime_nanos) .context_path("utime", &path)?; @@ -867,7 +817,7 @@ where } #[op] -async fn op_utime_async( +async fn op_utime_async

( state: Rc>, path: String, atime_secs: i64, @@ -876,7 +826,6 @@ async fn op_utime_async( mtime_nanos: u32, ) -> Result<(), AnyError> where - Fs: FileSystem + 'static, P: FsPermissions + 'static, { let path = PathBuf::from(path); @@ -884,7 +833,7 @@ where let fs = { let mut state = state.borrow_mut(); state.borrow_mut::

().check_write(&path, "Deno.utime()")?; - state.borrow::().clone() + state.borrow::>().clone() }; fs.utime_async( @@ -901,17 +850,16 @@ where } #[op] -fn op_make_temp_dir_sync( +fn op_make_temp_dir_sync

( state: &mut OpState, dir: Option, prefix: Option, suffix: Option, ) -> Result where - Fs: FileSystem + 'static, P: FsPermissions + 'static, { - let (dir, fs) = make_temp_check_sync::(state, dir)?; + let (dir, fs) = make_temp_check_sync::

(state, dir)?; let mut rng = thread_rng(); @@ -935,17 +883,16 @@ where } #[op] -async fn op_make_temp_dir_async( +async fn op_make_temp_dir_async

( state: Rc>, dir: Option, prefix: Option, suffix: Option, ) -> Result where - Fs: FileSystem + 'static, P: FsPermissions + 'static, { - let (dir, fs) = make_temp_check_async::(state, dir)?; + let (dir, fs) = make_temp_check_async::

(state, dir)?; let mut rng = thread_rng(); @@ -969,17 +916,16 @@ where } #[op] -fn op_make_temp_file_sync( +fn op_make_temp_file_sync

( state: &mut OpState, dir: Option, prefix: Option, suffix: Option, ) -> Result where - Fs: FileSystem + 'static, P: FsPermissions + 'static, { - let (dir, fs) = make_temp_check_sync::(state, dir)?; + let (dir, fs) = make_temp_check_sync::

(state, dir)?; let open_opts = OpenOptions { write: true, @@ -1010,17 +956,16 @@ where } #[op] -async fn op_make_temp_file_async( +async fn op_make_temp_file_async

( state: Rc>, dir: Option, prefix: Option, suffix: Option, ) -> Result where - Fs: FileSystem + 'static, P: FsPermissions + 'static, { - let (dir, fs) = make_temp_check_async::(state, dir)?; + let (dir, fs) = make_temp_check_async::

(state, dir)?; let open_opts = OpenOptions { write: true, @@ -1049,15 +994,14 @@ where .context("tmpfile") } -fn make_temp_check_sync( +fn make_temp_check_sync

( state: &mut OpState, dir: Option, -) -> Result<(PathBuf, Fs), AnyError> +) -> Result<(PathBuf, Arc), AnyError> where - Fs: FileSystem + 'static, P: FsPermissions + 'static, { - let fs = state.borrow::().clone(); + let fs = state.borrow::>().clone(); let dir = match dir { Some(dir) => { let dir = PathBuf::from(dir); @@ -1079,16 +1023,15 @@ where Ok((dir, fs)) } -fn make_temp_check_async( +fn make_temp_check_async

( state: Rc>, dir: Option, -) -> Result<(PathBuf, Fs), AnyError> +) -> Result<(PathBuf, Arc), AnyError> where - Fs: FileSystem + 'static, P: FsPermissions + 'static, { let mut state = state.borrow_mut(); - let fs = state.borrow::().clone(); + let fs = state.borrow::>().clone(); let dir = match dir { Some(dir) => { let dir = PathBuf::from(dir); @@ -1128,7 +1071,7 @@ fn tmp_name( } #[op] -fn op_write_file_sync( +fn op_write_file_sync

( state: &mut OpState, path: String, mode: Option, @@ -1138,7 +1081,6 @@ fn op_write_file_sync( data: ZeroCopyBuf, ) -> Result<(), AnyError> where - Fs: FileSystem + 'static, P: FsPermissions + 'static, { let path = PathBuf::from(path); @@ -1147,7 +1089,7 @@ where let options = OpenOptions::write(create, append, create_new, mode); permissions.check(&options, &path, "Deno.writeFileSync()")?; - let fs = state.borrow::(); + let fs = state.borrow::>(); fs.write_file_sync(&path, options, &data) .context_path("writefile", &path)?; @@ -1156,7 +1098,7 @@ where } #[op] -async fn op_write_file_async( +async fn op_write_file_async

( state: Rc>, path: String, mode: Option, @@ -1167,7 +1109,6 @@ async fn op_write_file_async( cancel_rid: Option, ) -> Result<(), AnyError> where - Fs: FileSystem + 'static, P: FsPermissions + 'static, { let path = PathBuf::from(path); @@ -1180,7 +1121,7 @@ where permissions.check(&options, &path, "Deno.writeFile()")?; let cancel_handle = cancel_rid .and_then(|rid| state.resource_table.get::(rid).ok()); - (state.borrow::().clone(), cancel_handle) + (state.borrow::>().clone(), cancel_handle) }; let fut = fs.write_file_async(path.clone(), options, data.to_vec()); @@ -1201,12 +1142,11 @@ where } #[op] -fn op_read_file_sync( +fn op_read_file_sync

( state: &mut OpState, path: String, ) -> Result where - Fs: FileSystem + 'static, P: FsPermissions + 'static, { let path = PathBuf::from(path); @@ -1214,20 +1154,19 @@ where let permissions = state.borrow_mut::

(); permissions.check_read(&path, "Deno.readFileSync()")?; - let fs = state.borrow::(); - let buf = fs.read_file_sync(path).context("readfile")?; + let fs = state.borrow::>(); + let buf = fs.read_file_sync(&path).context("readfile")?; Ok(buf.into()) } #[op] -async fn op_read_file_async( +async fn op_read_file_async

( state: Rc>, path: String, cancel_rid: Option, ) -> Result where - Fs: FileSystem + 'static, P: FsPermissions + 'static, { let path = PathBuf::from(path); @@ -1238,7 +1177,7 @@ where permissions.check_read(&path, "Deno.readFile()")?; let cancel_handle = cancel_rid .and_then(|rid| state.resource_table.get::(rid).ok()); - (state.borrow::().clone(), cancel_handle) + (state.borrow::>().clone(), cancel_handle) }; let fut = fs.read_file_async(path.clone()); @@ -1259,12 +1198,11 @@ where } #[op] -fn op_read_file_text_sync( +fn op_read_file_text_sync

( state: &mut OpState, path: String, ) -> Result where - Fs: FileSystem + 'static, P: FsPermissions + 'static, { let path = PathBuf::from(path); @@ -1272,20 +1210,19 @@ where let permissions = state.borrow_mut::

(); permissions.check_read(&path, "Deno.readFileSync()")?; - let fs = state.borrow::(); - let buf = fs.read_file_sync(path).context("readfile")?; + let fs = state.borrow::>(); + let buf = fs.read_file_sync(&path).context("readfile")?; Ok(string_from_utf8_lossy(buf)) } #[op] -async fn op_read_file_text_async( +async fn op_read_file_text_async

( state: Rc>, path: String, cancel_rid: Option, ) -> Result where - Fs: FileSystem + 'static, P: FsPermissions + 'static, { let path = PathBuf::from(path); @@ -1296,7 +1233,7 @@ where permissions.check_read(&path, "Deno.readFile()")?; let cancel_handle = cancel_rid .and_then(|rid| state.resource_table.get::(rid).ok()); - (state.borrow::().clone(), cancel_handle) + (state.borrow::>().clone(), cancel_handle) }; let fut = fs.read_file_async(path.clone()); @@ -1340,106 +1277,75 @@ fn to_seek_from(offset: i64, whence: i32) -> Result { } #[op] -fn op_seek_sync( +fn op_seek_sync( state: &mut OpState, rid: ResourceId, offset: i64, whence: i32, -) -> Result -where - Fs: FileSystem + 'static, - Fs::File: Resource, -{ +) -> Result { let pos = to_seek_from(offset, whence)?; - let file = state.resource_table.get::(rid)?; + let file = FileResource::get_file(state, rid)?; let cursor = file.seek_sync(pos)?; Ok(cursor) } #[op] -async fn op_seek_async( +async fn op_seek_async( state: Rc>, rid: ResourceId, offset: i64, whence: i32, -) -> Result -where - Fs: FileSystem + 'static, - Fs::File: Resource, -{ +) -> Result { let pos = to_seek_from(offset, whence)?; - let file = state.borrow().resource_table.get::(rid)?; + let file = FileResource::get_file(&state.borrow(), rid)?; let cursor = file.seek_async(pos).await?; Ok(cursor) } #[op] -fn op_fdatasync_sync( +fn op_fdatasync_sync( state: &mut OpState, rid: ResourceId, -) -> Result<(), AnyError> -where - Fs: FileSystem + 'static, - Fs::File: Resource, -{ - let file = state.resource_table.get::(rid)?; +) -> Result<(), AnyError> { + let file = FileResource::get_file(state, rid)?; file.datasync_sync()?; Ok(()) } #[op] -async fn op_fdatasync_async( +async fn op_fdatasync_async( state: Rc>, rid: ResourceId, -) -> Result<(), AnyError> -where - Fs: FileSystem + 'static, - Fs::File: Resource, -{ - let file = state.borrow().resource_table.get::(rid)?; +) -> Result<(), AnyError> { + let file = FileResource::get_file(&state.borrow(), rid)?; file.datasync_async().await?; Ok(()) } #[op] -fn op_fsync_sync( - state: &mut OpState, - rid: ResourceId, -) -> Result<(), AnyError> -where - Fs: FileSystem + 'static, - Fs::File: Resource, -{ - let file = state.resource_table.get::(rid)?; +fn op_fsync_sync(state: &mut OpState, rid: ResourceId) -> Result<(), AnyError> { + let file = FileResource::get_file(state, rid)?; file.sync_sync()?; Ok(()) } #[op] -async fn op_fsync_async( +async fn op_fsync_async( state: Rc>, rid: ResourceId, -) -> Result<(), AnyError> -where - Fs: FileSystem + 'static, - Fs::File: Resource, -{ - let file = state.borrow().resource_table.get::(rid)?; +) -> Result<(), AnyError> { + let file = FileResource::get_file(&state.borrow(), rid)?; file.sync_async().await?; Ok(()) } #[op] -fn op_fstat_sync( +fn op_fstat_sync( state: &mut OpState, rid: ResourceId, stat_out_buf: &mut [u32], -) -> Result<(), AnyError> -where - Fs: FileSystem + 'static, - Fs::File: Resource, -{ - let file = state.resource_table.get::(rid)?; +) -> Result<(), AnyError> { + let file = FileResource::get_file(state, rid)?; let stat = file.stat_sync()?; let serializable_stat = SerializableStat::from(stat); serializable_stat.write(stat_out_buf); @@ -1447,143 +1353,107 @@ where } #[op] -async fn op_fstat_async( +async fn op_fstat_async( state: Rc>, rid: ResourceId, -) -> Result -where - Fs: FileSystem + 'static, - Fs::File: Resource, -{ - let file = state.borrow().resource_table.get::(rid)?; +) -> Result { + let file = FileResource::get_file(&state.borrow(), rid)?; let stat = file.stat_async().await?; Ok(stat.into()) } #[op] -fn op_flock_sync( +fn op_flock_sync( state: &mut OpState, rid: ResourceId, exclusive: bool, -) -> Result<(), AnyError> -where - Fs: FileSystem + 'static, - Fs::File: Resource, -{ +) -> Result<(), AnyError> { check_unstable(state, "Deno.flockSync"); - let file = state.resource_table.get::(rid)?; + let file = FileResource::get_file(state, rid)?; file.lock_sync(exclusive)?; Ok(()) } #[op] -async fn op_flock_async( +async fn op_flock_async( state: Rc>, rid: ResourceId, exclusive: bool, -) -> Result<(), AnyError> -where - Fs: FileSystem + 'static, - Fs::File: Resource, -{ +) -> Result<(), AnyError> { check_unstable2(&state, "Deno.flock"); - let file = state.borrow().resource_table.get::(rid)?; + let file = FileResource::get_file(&state.borrow(), rid)?; file.lock_async(exclusive).await?; Ok(()) } #[op] -fn op_funlock_sync( +fn op_funlock_sync( state: &mut OpState, rid: ResourceId, -) -> Result<(), AnyError> -where - Fs: FileSystem + 'static, - Fs::File: Resource, -{ +) -> Result<(), AnyError> { check_unstable(state, "Deno.funlockSync"); - let file = state.resource_table.get::(rid)?; + let file = FileResource::get_file(state, rid)?; file.unlock_sync()?; Ok(()) } #[op] -async fn op_funlock_async( +async fn op_funlock_async( state: Rc>, rid: ResourceId, -) -> Result<(), AnyError> -where - Fs: FileSystem + 'static, - Fs::File: Resource, -{ +) -> Result<(), AnyError> { check_unstable2(&state, "Deno.funlock"); - let file = state.borrow().resource_table.get::(rid)?; + let file = FileResource::get_file(&state.borrow(), rid)?; file.unlock_async().await?; Ok(()) } #[op] -fn op_ftruncate_sync( +fn op_ftruncate_sync( state: &mut OpState, rid: ResourceId, len: u64, -) -> Result<(), AnyError> -where - Fs: FileSystem + 'static, - Fs::File: Resource, -{ - let file = state.resource_table.get::(rid)?; +) -> Result<(), AnyError> { + let file = FileResource::get_file(state, rid)?; file.truncate_sync(len)?; Ok(()) } #[op] -async fn op_ftruncate_async( +async fn op_ftruncate_async( state: Rc>, rid: ResourceId, len: u64, -) -> Result<(), AnyError> -where - Fs: FileSystem + 'static, - Fs::File: Resource, -{ - let file = state.borrow().resource_table.get::(rid)?; +) -> Result<(), AnyError> { + let file = FileResource::get_file(&state.borrow(), rid)?; file.truncate_async(len).await?; Ok(()) } #[op] -fn op_futime_sync( +fn op_futime_sync( state: &mut OpState, rid: ResourceId, atime_secs: i64, atime_nanos: u32, mtime_secs: i64, mtime_nanos: u32, -) -> Result<(), AnyError> -where - Fs: FileSystem + 'static, - Fs::File: Resource, -{ - let file = state.resource_table.get::(rid)?; +) -> Result<(), AnyError> { + let file = FileResource::get_file(state, rid)?; file.utime_sync(atime_secs, atime_nanos, mtime_secs, mtime_nanos)?; Ok(()) } #[op] -async fn op_futime_async( +async fn op_futime_async( state: Rc>, rid: ResourceId, atime_secs: i64, atime_nanos: u32, mtime_secs: i64, mtime_nanos: u32, -) -> Result<(), AnyError> -where - Fs: FileSystem + 'static, - Fs::File: Resource, -{ - let file = state.borrow().resource_table.get::(rid)?; +) -> Result<(), AnyError> { + let file = FileResource::get_file(&state.borrow(), rid)?; file .utime_async(atime_secs, atime_nanos, mtime_secs, mtime_nanos) .await?; diff --git a/ext/fs/std_fs.rs b/ext/fs/std_fs.rs index 4bdbf49432..a657939db2 100644 --- a/ext/fs/std_fs.rs +++ b/ext/fs/std_fs.rs @@ -4,34 +4,29 @@ use std::fs; use std::io; -use std::io::Read; -use std::io::Seek; use std::io::Write; use std::path::Path; use std::path::PathBuf; use std::rc::Rc; -use std::time::SystemTime; -use std::time::UNIX_EPOCH; -use deno_io::StdFileResource; -use fs3::FileExt; +use deno_io::fs::File; +use deno_io::fs::FsResult; +use deno_io::fs::FsStat; +use deno_io::StdFileResourceInner; use crate::interface::FsDirEntry; -use crate::interface::FsError; use crate::interface::FsFileType; -use crate::interface::FsResult; -use crate::interface::FsStat; -use crate::File; use crate::FileSystem; use crate::OpenOptions; +#[cfg(not(unix))] +use deno_io::fs::FsError; + #[derive(Clone)] -pub struct StdFs; +pub struct RealFs; #[async_trait::async_trait(?Send)] -impl FileSystem for StdFs { - type File = StdFileResource; - +impl FileSystem for RealFs { fn cwd(&self) -> FsResult { std::env::current_dir().map_err(Into::into) } @@ -40,7 +35,7 @@ impl FileSystem for StdFs { Ok(std::env::temp_dir()) } - fn chdir(&self, path: impl AsRef) -> FsResult<()> { + fn chdir(&self, path: &Path) -> FsResult<()> { std::env::set_current_dir(path).map_err(Into::into) } @@ -78,27 +73,27 @@ impl FileSystem for StdFs { fn open_sync( &self, - path: impl AsRef, + path: &Path, options: OpenOptions, - ) -> FsResult { + ) -> FsResult> { let opts = open_options(options); let std_file = opts.open(path)?; - Ok(StdFileResource::fs_file(std_file)) + Ok(Rc::new(StdFileResourceInner::file(std_file))) } async fn open_async( &self, path: PathBuf, options: OpenOptions, - ) -> FsResult { + ) -> FsResult> { let opts = open_options(options); let std_file = tokio::task::spawn_blocking(move || opts.open(path)).await??; - Ok(StdFileResource::fs_file(std_file)) + Ok(Rc::new(StdFileResourceInner::file(std_file))) } fn mkdir_sync( &self, - path: impl AsRef, + path: &Path, recursive: bool, mode: u32, ) -> FsResult<()> { @@ -110,19 +105,19 @@ impl FileSystem for StdFs { recursive: bool, mode: u32, ) -> FsResult<()> { - tokio::task::spawn_blocking(move || mkdir(path, recursive, mode)).await? + tokio::task::spawn_blocking(move || mkdir(&path, recursive, mode)).await? } - fn chmod_sync(&self, path: impl AsRef, mode: u32) -> FsResult<()> { + fn chmod_sync(&self, path: &Path, mode: u32) -> FsResult<()> { chmod(path, mode) } async fn chmod_async(&self, path: PathBuf, mode: u32) -> FsResult<()> { - tokio::task::spawn_blocking(move || chmod(path, mode)).await? + tokio::task::spawn_blocking(move || chmod(&path, mode)).await? } fn chown_sync( &self, - path: impl AsRef, + path: &Path, uid: Option, gid: Option, ) -> FsResult<()> { @@ -134,68 +129,56 @@ impl FileSystem for StdFs { uid: Option, gid: Option, ) -> FsResult<()> { - tokio::task::spawn_blocking(move || chown(path, uid, gid)).await? + tokio::task::spawn_blocking(move || chown(&path, uid, gid)).await? } - fn remove_sync( - &self, - path: impl AsRef, - recursive: bool, - ) -> FsResult<()> { + fn remove_sync(&self, path: &Path, recursive: bool) -> FsResult<()> { remove(path, recursive) } async fn remove_async(&self, path: PathBuf, recursive: bool) -> FsResult<()> { - tokio::task::spawn_blocking(move || remove(path, recursive)).await? + tokio::task::spawn_blocking(move || remove(&path, recursive)).await? } - fn copy_file_sync( - &self, - from: impl AsRef, - to: impl AsRef, - ) -> FsResult<()> { + fn copy_file_sync(&self, from: &Path, to: &Path) -> FsResult<()> { copy_file(from, to) } async fn copy_file_async(&self, from: PathBuf, to: PathBuf) -> FsResult<()> { - tokio::task::spawn_blocking(move || copy_file(from, to)).await? + tokio::task::spawn_blocking(move || copy_file(&from, &to)).await? } - fn stat_sync(&self, path: impl AsRef) -> FsResult { + fn stat_sync(&self, path: &Path) -> FsResult { stat(path).map(Into::into) } async fn stat_async(&self, path: PathBuf) -> FsResult { - tokio::task::spawn_blocking(move || stat(path)) + tokio::task::spawn_blocking(move || stat(&path)) .await? .map(Into::into) } - fn lstat_sync(&self, path: impl AsRef) -> FsResult { + fn lstat_sync(&self, path: &Path) -> FsResult { lstat(path).map(Into::into) } async fn lstat_async(&self, path: PathBuf) -> FsResult { - tokio::task::spawn_blocking(move || lstat(path)) + tokio::task::spawn_blocking(move || lstat(&path)) .await? .map(Into::into) } - fn realpath_sync(&self, path: impl AsRef) -> FsResult { + fn realpath_sync(&self, path: &Path) -> FsResult { realpath(path) } async fn realpath_async(&self, path: PathBuf) -> FsResult { - tokio::task::spawn_blocking(move || realpath(path)).await? + tokio::task::spawn_blocking(move || realpath(&path)).await? } - fn read_dir_sync(&self, path: impl AsRef) -> FsResult> { + fn read_dir_sync(&self, path: &Path) -> FsResult> { read_dir(path) } async fn read_dir_async(&self, path: PathBuf) -> FsResult> { - tokio::task::spawn_blocking(move || read_dir(path)).await? + tokio::task::spawn_blocking(move || read_dir(&path)).await? } - fn rename_sync( - &self, - oldpath: impl AsRef, - newpath: impl AsRef, - ) -> FsResult<()> { + fn rename_sync(&self, oldpath: &Path, newpath: &Path) -> FsResult<()> { fs::rename(oldpath, newpath).map_err(Into::into) } async fn rename_async( @@ -208,11 +191,7 @@ impl FileSystem for StdFs { .map_err(Into::into) } - fn link_sync( - &self, - oldpath: impl AsRef, - newpath: impl AsRef, - ) -> FsResult<()> { + fn link_sync(&self, oldpath: &Path, newpath: &Path) -> FsResult<()> { fs::hard_link(oldpath, newpath).map_err(Into::into) } async fn link_async( @@ -227,8 +206,8 @@ impl FileSystem for StdFs { fn symlink_sync( &self, - oldpath: impl AsRef, - newpath: impl AsRef, + oldpath: &Path, + newpath: &Path, file_type: Option, ) -> FsResult<()> { symlink(oldpath, newpath, file_type) @@ -239,11 +218,11 @@ impl FileSystem for StdFs { newpath: PathBuf, file_type: Option, ) -> FsResult<()> { - tokio::task::spawn_blocking(move || symlink(oldpath, newpath, file_type)) + tokio::task::spawn_blocking(move || symlink(&oldpath, &newpath, file_type)) .await? } - fn read_link_sync(&self, path: impl AsRef) -> FsResult { + fn read_link_sync(&self, path: &Path) -> FsResult { fs::read_link(path).map_err(Into::into) } async fn read_link_async(&self, path: PathBuf) -> FsResult { @@ -252,16 +231,16 @@ impl FileSystem for StdFs { .map_err(Into::into) } - fn truncate_sync(&self, path: impl AsRef, len: u64) -> FsResult<()> { + fn truncate_sync(&self, path: &Path, len: u64) -> FsResult<()> { truncate(path, len) } async fn truncate_async(&self, path: PathBuf, len: u64) -> FsResult<()> { - tokio::task::spawn_blocking(move || truncate(path, len)).await? + tokio::task::spawn_blocking(move || truncate(&path, len)).await? } fn utime_sync( &self, - path: impl AsRef, + path: &Path, atime_secs: i64, atime_nanos: u32, mtime_secs: i64, @@ -289,7 +268,7 @@ impl FileSystem for StdFs { fn write_file_sync( &self, - path: impl AsRef, + path: &Path, options: OpenOptions, data: &[u8], ) -> FsResult<()> { @@ -324,7 +303,7 @@ impl FileSystem for StdFs { .await? } - fn read_file_sync(&self, path: impl AsRef) -> FsResult> { + fn read_file_sync(&self, path: &Path) -> FsResult> { fs::read(path).map_err(Into::into) } async fn read_file_async(&self, path: PathBuf) -> FsResult> { @@ -334,7 +313,7 @@ impl FileSystem for StdFs { } } -fn mkdir(path: impl AsRef, recursive: bool, mode: u32) -> FsResult<()> { +fn mkdir(path: &Path, recursive: bool, mode: u32) -> FsResult<()> { let mut builder = fs::DirBuilder::new(); builder.recursive(recursive); #[cfg(unix)] @@ -350,7 +329,7 @@ fn mkdir(path: impl AsRef, recursive: bool, mode: u32) -> FsResult<()> { } #[cfg(unix)] -fn chmod(path: impl AsRef, mode: u32) -> FsResult<()> { +fn chmod(path: &Path, mode: u32) -> FsResult<()> { use std::os::unix::fs::PermissionsExt; let permissions = fs::Permissions::from_mode(mode); fs::set_permissions(path, permissions)?; @@ -359,24 +338,20 @@ fn chmod(path: impl AsRef, mode: u32) -> FsResult<()> { // TODO: implement chmod for Windows (#4357) #[cfg(not(unix))] -fn chmod(path: impl AsRef, _mode: u32) -> FsResult<()> { +fn chmod(path: &Path, _mode: u32) -> FsResult<()> { // Still check file/dir exists on Windows std::fs::metadata(path)?; Err(FsError::NotSupported) } #[cfg(unix)] -fn chown( - path: impl AsRef, - uid: Option, - gid: Option, -) -> FsResult<()> { +fn chown(path: &Path, uid: Option, gid: Option) -> FsResult<()> { use nix::unistd::chown; use nix::unistd::Gid; use nix::unistd::Uid; let owner = uid.map(Uid::from_raw); let group = gid.map(Gid::from_raw); - let res = chown(path.as_ref(), owner, group); + let res = chown(path, owner, group); if let Err(err) = res { return Err(io::Error::from_raw_os_error(err as i32).into()); } @@ -385,60 +360,57 @@ fn chown( // TODO: implement chown for Windows #[cfg(not(unix))] -fn chown( - _path: impl AsRef, - _uid: Option, - _gid: Option, -) -> FsResult<()> { +fn chown(_path: &Path, _uid: Option, _gid: Option) -> FsResult<()> { Err(FsError::NotSupported) } -fn remove(path: impl AsRef, recursive: bool) -> FsResult<()> { +fn remove(path: &Path, recursive: bool) -> FsResult<()> { // TODO: this is racy. This should open fds, and then `unlink` those. - let metadata = fs::symlink_metadata(&path)?; + let metadata = fs::symlink_metadata(path)?; let file_type = metadata.file_type(); let res = if file_type.is_dir() { if recursive { - fs::remove_dir_all(&path) + fs::remove_dir_all(path) } else { - fs::remove_dir(&path) + fs::remove_dir(path) } } else if file_type.is_symlink() { #[cfg(unix)] { - fs::remove_file(&path) + fs::remove_file(path) } #[cfg(not(unix))] { use std::os::windows::prelude::MetadataExt; use winapi::um::winnt::FILE_ATTRIBUTE_DIRECTORY; if metadata.file_attributes() & FILE_ATTRIBUTE_DIRECTORY != 0 { - fs::remove_dir(&path) + fs::remove_dir(path) } else { - fs::remove_file(&path) + fs::remove_file(path) } } } else { - fs::remove_file(&path) + fs::remove_file(path) }; res.map_err(Into::into) } -fn copy_file(from: impl AsRef, to: impl AsRef) -> FsResult<()> { +fn copy_file(from: &Path, to: &Path) -> FsResult<()> { #[cfg(target_os = "macos")] { use libc::clonefile; use libc::stat; use libc::unlink; use std::ffi::CString; + use std::io::Read; use std::os::unix::fs::OpenOptionsExt; use std::os::unix::fs::PermissionsExt; use std::os::unix::prelude::OsStrExt; - let from_str = CString::new(from.as_ref().as_os_str().as_bytes()).unwrap(); - let to_str = CString::new(to.as_ref().as_os_str().as_bytes()).unwrap(); + let from_str = CString::new(from.as_os_str().as_bytes()).unwrap(); + let to_str = CString::new(to.as_os_str().as_bytes()).unwrap(); // SAFETY: `from` and `to` are valid C strings. // std::fs::copy does open() + fcopyfile() on macOS. We try to use @@ -499,36 +471,37 @@ fn copy_file(from: impl AsRef, to: impl AsRef) -> FsResult<()> { } #[cfg(not(windows))] -fn stat(path: impl AsRef) -> FsResult { +fn stat(path: &Path) -> FsResult { let metadata = fs::metadata(path)?; - Ok(metadata_to_fsstat(metadata)) + Ok(FsStat::from_std(metadata)) } #[cfg(windows)] -fn stat(path: impl AsRef) -> FsResult { - let metadata = fs::metadata(path.as_ref())?; - let mut fsstat = metadata_to_fsstat(metadata); +fn stat(path: &Path) -> FsResult { + let metadata = fs::metadata(path)?; + let mut fsstat = FsStat::from_std(metadata); use winapi::um::winbase::FILE_FLAG_BACKUP_SEMANTICS; - let path = path.as_ref().canonicalize()?; + let path = path.canonicalize()?; stat_extra(&mut fsstat, &path, FILE_FLAG_BACKUP_SEMANTICS)?; Ok(fsstat) } #[cfg(not(windows))] -fn lstat(path: impl AsRef) -> FsResult { +fn lstat(path: &Path) -> FsResult { let metadata = fs::symlink_metadata(path)?; - Ok(metadata_to_fsstat(metadata)) + Ok(FsStat::from_std(metadata)) } #[cfg(windows)] -fn lstat(path: impl AsRef) -> FsResult { - let metadata = fs::symlink_metadata(path.as_ref())?; - let mut fsstat = metadata_to_fsstat(metadata); +fn lstat(path: &Path) -> FsResult { use winapi::um::winbase::FILE_FLAG_BACKUP_SEMANTICS; use winapi::um::winbase::FILE_FLAG_OPEN_REPARSE_POINT; + + let metadata = fs::symlink_metadata(path)?; + let mut fsstat = FsStat::from_std(metadata); stat_extra( &mut fsstat, - path.as_ref(), + path, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, )?; Ok(fsstat) @@ -595,62 +568,11 @@ fn stat_extra( } } -#[inline(always)] -fn metadata_to_fsstat(metadata: fs::Metadata) -> FsStat { - macro_rules! unix_or_zero { - ($member:ident) => {{ - #[cfg(unix)] - { - use std::os::unix::fs::MetadataExt; - metadata.$member() - } - #[cfg(not(unix))] - { - 0 - } - }}; - } - - #[inline(always)] - fn to_msec(maybe_time: Result) -> Option { - match maybe_time { - Ok(time) => Some( - time - .duration_since(UNIX_EPOCH) - .map(|t| t.as_millis() as u64) - .unwrap_or_else(|err| err.duration().as_millis() as u64), - ), - Err(_) => None, - } - } - - FsStat { - is_file: metadata.is_file(), - is_directory: metadata.is_dir(), - is_symlink: metadata.file_type().is_symlink(), - size: metadata.len(), - - mtime: to_msec(metadata.modified()), - atime: to_msec(metadata.accessed()), - birthtime: to_msec(metadata.created()), - - dev: unix_or_zero!(dev), - ino: unix_or_zero!(ino), - mode: unix_or_zero!(mode), - nlink: unix_or_zero!(nlink), - uid: unix_or_zero!(uid), - gid: unix_or_zero!(gid), - rdev: unix_or_zero!(rdev), - blksize: unix_or_zero!(blksize), - blocks: unix_or_zero!(blocks), - } +fn realpath(path: &Path) -> FsResult { + Ok(deno_core::strip_unc_prefix(path.canonicalize()?)) } -fn realpath(path: impl AsRef) -> FsResult { - Ok(deno_core::strip_unc_prefix(path.as_ref().canonicalize()?)) -} - -fn read_dir(path: impl AsRef) -> FsResult> { +fn read_dir(path: &Path) -> FsResult> { let entries = fs::read_dir(path)? .filter_map(|entry| { let entry = entry.ok()?; @@ -679,24 +601,24 @@ fn read_dir(path: impl AsRef) -> FsResult> { #[cfg(not(windows))] fn symlink( - oldpath: impl AsRef, - newpath: impl AsRef, + oldpath: &Path, + newpath: &Path, _file_type: Option, ) -> FsResult<()> { - std::os::unix::fs::symlink(oldpath.as_ref(), newpath.as_ref())?; + std::os::unix::fs::symlink(oldpath, newpath)?; Ok(()) } #[cfg(windows)] fn symlink( - oldpath: impl AsRef, - newpath: impl AsRef, + oldpath: &Path, + newpath: &Path, file_type: Option, ) -> FsResult<()> { let file_type = match file_type { Some(file_type) => file_type, None => { - let old_meta = fs::metadata(&oldpath); + let old_meta = fs::metadata(oldpath); match old_meta { Ok(metadata) => { if metadata.is_file() { @@ -723,17 +645,17 @@ fn symlink( match file_type { FsFileType::File => { - std::os::windows::fs::symlink_file(&oldpath, &newpath)?; + std::os::windows::fs::symlink_file(oldpath, newpath)?; } FsFileType::Directory => { - std::os::windows::fs::symlink_dir(&oldpath, &newpath)?; + std::os::windows::fs::symlink_dir(oldpath, newpath)?; } }; Ok(()) } -fn truncate(path: impl AsRef, len: u64) -> FsResult<()> { +fn truncate(path: &Path, len: u64) -> FsResult<()> { let file = fs::OpenOptions::new().write(true).open(path)?; file.set_len(len)?; Ok(()) @@ -760,162 +682,3 @@ fn open_options(options: OpenOptions) -> fs::OpenOptions { open_options.create_new(options.create_new); open_options } - -fn sync( - resource: Rc, - f: impl FnOnce(&mut fs::File) -> io::Result, -) -> FsResult { - let res = resource - .with_file2(|file| f(file)) - .ok_or(FsError::FileBusy)??; - Ok(res) -} - -async fn nonblocking( - resource: Rc, - f: impl FnOnce(&mut fs::File) -> io::Result + Send + 'static, -) -> FsResult { - let res = resource.with_file_blocking_task2(f).await?; - Ok(res) -} - -#[async_trait::async_trait(?Send)] -impl File for StdFileResource { - fn write_all_sync(self: Rc, buf: &[u8]) -> FsResult<()> { - sync(self, |file| file.write_all(buf)) - } - async fn write_all_async(self: Rc, buf: Vec) -> FsResult<()> { - nonblocking(self, move |file| file.write_all(&buf)).await - } - - fn read_all_sync(self: Rc) -> FsResult> { - sync(self, |file| { - let mut buf = Vec::new(); - file.read_to_end(&mut buf)?; - Ok(buf) - }) - } - async fn read_all_async(self: Rc) -> FsResult> { - nonblocking(self, |file| { - let mut buf = Vec::new(); - file.read_to_end(&mut buf)?; - Ok(buf) - }) - .await - } - - fn chmod_sync(self: Rc, _mode: u32) -> FsResult<()> { - #[cfg(unix)] - { - sync(self, |file| { - use std::os::unix::prelude::PermissionsExt; - file.set_permissions(fs::Permissions::from_mode(_mode)) - }) - } - #[cfg(not(unix))] - Err(FsError::NotSupported) - } - - async fn chmod_async(self: Rc, _mode: u32) -> FsResult<()> { - #[cfg(unix)] - { - nonblocking(self, move |file| { - use std::os::unix::prelude::PermissionsExt; - file.set_permissions(fs::Permissions::from_mode(_mode)) - }) - .await - } - #[cfg(not(unix))] - Err(FsError::NotSupported) - } - - fn seek_sync(self: Rc, pos: io::SeekFrom) -> FsResult { - sync(self, |file| file.seek(pos)) - } - async fn seek_async(self: Rc, pos: io::SeekFrom) -> FsResult { - nonblocking(self, move |file| file.seek(pos)).await - } - - fn datasync_sync(self: Rc) -> FsResult<()> { - sync(self, |file| file.sync_data()) - } - async fn datasync_async(self: Rc) -> FsResult<()> { - nonblocking(self, |file| file.sync_data()).await - } - - fn sync_sync(self: Rc) -> FsResult<()> { - sync(self, |file| file.sync_all()) - } - async fn sync_async(self: Rc) -> FsResult<()> { - nonblocking(self, |file| file.sync_all()).await - } - - fn stat_sync(self: Rc) -> FsResult { - sync(self, |file| file.metadata().map(metadata_to_fsstat)) - } - async fn stat_async(self: Rc) -> FsResult { - nonblocking(self, |file| file.metadata().map(metadata_to_fsstat)).await - } - - fn lock_sync(self: Rc, exclusive: bool) -> FsResult<()> { - sync(self, |file| { - if exclusive { - file.lock_exclusive() - } else { - file.lock_shared() - } - }) - } - async fn lock_async(self: Rc, exclusive: bool) -> FsResult<()> { - nonblocking(self, move |file| { - if exclusive { - file.lock_exclusive() - } else { - file.lock_shared() - } - }) - .await - } - - fn unlock_sync(self: Rc) -> FsResult<()> { - sync(self, |file| file.unlock()) - } - async fn unlock_async(self: Rc) -> FsResult<()> { - nonblocking(self, |file| file.unlock()).await - } - - fn truncate_sync(self: Rc, len: u64) -> FsResult<()> { - sync(self, |file| file.set_len(len)) - } - async fn truncate_async(self: Rc, len: u64) -> FsResult<()> { - nonblocking(self, move |file| file.set_len(len)).await - } - - fn utime_sync( - self: Rc, - atime_secs: i64, - atime_nanos: u32, - mtime_secs: i64, - mtime_nanos: u32, - ) -> FsResult<()> { - let atime = filetime::FileTime::from_unix_time(atime_secs, atime_nanos); - let mtime = filetime::FileTime::from_unix_time(mtime_secs, mtime_nanos); - sync(self, |file| { - filetime::set_file_handle_times(file, Some(atime), Some(mtime)) - }) - } - async fn utime_async( - self: Rc, - atime_secs: i64, - atime_nanos: u32, - mtime_secs: i64, - mtime_nanos: u32, - ) -> FsResult<()> { - let atime = filetime::FileTime::from_unix_time(atime_secs, atime_nanos); - let mtime = filetime::FileTime::from_unix_time(mtime_secs, mtime_nanos); - nonblocking(self, move |file| { - filetime::set_file_handle_times(file, Some(atime), Some(mtime)) - }) - .await - } -} diff --git a/ext/io/Cargo.toml b/ext/io/Cargo.toml index 55b2ccab66..fc9de711ff 100644 --- a/ext/io/Cargo.toml +++ b/ext/io/Cargo.toml @@ -14,7 +14,10 @@ description = "IO promitives for Deno extensions" path = "lib.rs" [dependencies] +async-trait.workspace = true deno_core.workspace = true +filetime.workspace = true +fs3.workspace = true once_cell.workspace = true tokio.workspace = true diff --git a/ext/io/fs.rs b/ext/io/fs.rs new file mode 100644 index 0000000000..bb6bdec4fc --- /dev/null +++ b/ext/io/fs.rs @@ -0,0 +1,330 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. + +use std::borrow::Cow; +use std::io; +use std::rc::Rc; +use std::time::SystemTime; +use std::time::UNIX_EPOCH; + +use deno_core::error::not_supported; +use deno_core::error::resource_unavailable; +use deno_core::error::AnyError; +use deno_core::BufMutView; +use deno_core::BufView; +use deno_core::OpState; +use deno_core::ResourceId; +use tokio::task::JoinError; + +pub enum FsError { + Io(io::Error), + FileBusy, + NotSupported, +} + +impl From for FsError { + fn from(err: io::Error) -> Self { + Self::Io(err) + } +} + +impl From for AnyError { + fn from(err: FsError) -> Self { + match err { + FsError::Io(err) => AnyError::from(err), + FsError::FileBusy => resource_unavailable(), + FsError::NotSupported => not_supported(), + } + } +} + +impl From for FsError { + fn from(err: JoinError) -> Self { + if err.is_cancelled() { + todo!("async tasks must not be cancelled") + } + if err.is_panic() { + std::panic::resume_unwind(err.into_panic()); // resume the panic on the main thread + } + unreachable!() + } +} + +pub type FsResult = Result; + +pub struct FsStat { + pub is_file: bool, + pub is_directory: bool, + pub is_symlink: bool, + pub size: u64, + + pub mtime: Option, + pub atime: Option, + pub birthtime: Option, + + pub dev: u64, + pub ino: u64, + pub mode: u32, + pub nlink: u64, + pub uid: u32, + pub gid: u32, + pub rdev: u64, + pub blksize: u64, + pub blocks: u64, +} + +impl FsStat { + pub fn from_std(metadata: std::fs::Metadata) -> Self { + macro_rules! unix_or_zero { + ($member:ident) => {{ + #[cfg(unix)] + { + use std::os::unix::fs::MetadataExt; + metadata.$member() + } + #[cfg(not(unix))] + { + 0 + } + }}; + } + + #[inline(always)] + fn to_msec(maybe_time: Result) -> Option { + match maybe_time { + Ok(time) => Some( + time + .duration_since(UNIX_EPOCH) + .map(|t| t.as_millis() as u64) + .unwrap_or_else(|err| err.duration().as_millis() as u64), + ), + Err(_) => None, + } + } + + Self { + is_file: metadata.is_file(), + is_directory: metadata.is_dir(), + is_symlink: metadata.file_type().is_symlink(), + size: metadata.len(), + + mtime: to_msec(metadata.modified()), + atime: to_msec(metadata.accessed()), + birthtime: to_msec(metadata.created()), + + dev: unix_or_zero!(dev), + ino: unix_or_zero!(ino), + mode: unix_or_zero!(mode), + nlink: unix_or_zero!(nlink), + uid: unix_or_zero!(uid), + gid: unix_or_zero!(gid), + rdev: unix_or_zero!(rdev), + blksize: unix_or_zero!(blksize), + blocks: unix_or_zero!(blocks), + } + } +} + +#[async_trait::async_trait(?Send)] +pub trait File { + fn read_sync(self: Rc, buf: &mut [u8]) -> FsResult; + async fn read(self: Rc, limit: usize) -> FsResult { + let vec = vec![0; limit]; + let buf = BufMutView::from(vec); + let (nread, buf) = self.read_byob(buf).await?; + let mut vec = buf.unwrap_vec(); + if vec.len() != nread { + vec.truncate(nread); + } + Ok(BufView::from(vec)) + } + async fn read_byob( + self: Rc, + buf: BufMutView, + ) -> FsResult<(usize, BufMutView)>; + + fn write_sync(self: Rc, buf: &[u8]) -> FsResult; + async fn write( + self: Rc, + buf: BufView, + ) -> FsResult; + + fn write_all_sync(self: Rc, buf: &[u8]) -> FsResult<()>; + async fn write_all(self: Rc, buf: BufView) -> FsResult<()>; + + fn read_all_sync(self: Rc) -> FsResult>; + async fn read_all_async(self: Rc) -> FsResult>; + + fn chmod_sync(self: Rc, pathmode: u32) -> FsResult<()>; + async fn chmod_async(self: Rc, mode: u32) -> FsResult<()>; + + fn seek_sync(self: Rc, pos: io::SeekFrom) -> FsResult; + async fn seek_async(self: Rc, pos: io::SeekFrom) -> FsResult; + + fn datasync_sync(self: Rc) -> FsResult<()>; + async fn datasync_async(self: Rc) -> FsResult<()>; + + fn sync_sync(self: Rc) -> FsResult<()>; + async fn sync_async(self: Rc) -> FsResult<()>; + + fn stat_sync(self: Rc) -> FsResult; + async fn stat_async(self: Rc) -> FsResult; + + fn lock_sync(self: Rc, exclusive: bool) -> FsResult<()>; + async fn lock_async(self: Rc, exclusive: bool) -> FsResult<()>; + + fn unlock_sync(self: Rc) -> FsResult<()>; + async fn unlock_async(self: Rc) -> FsResult<()>; + + fn truncate_sync(self: Rc, len: u64) -> FsResult<()>; + async fn truncate_async(self: Rc, len: u64) -> FsResult<()>; + + fn utime_sync( + self: Rc, + atime_secs: i64, + atime_nanos: u32, + mtime_secs: i64, + mtime_nanos: u32, + ) -> FsResult<()>; + async fn utime_async( + self: Rc, + atime_secs: i64, + atime_nanos: u32, + mtime_secs: i64, + mtime_nanos: u32, + ) -> FsResult<()>; + + // lower level functionality + fn as_stdio(self: Rc) -> FsResult; + #[cfg(unix)] + fn backing_fd(self: Rc) -> Option; + #[cfg(windows)] + fn backing_fd(self: Rc) -> Option; + fn try_clone_inner(self: Rc) -> FsResult>; +} + +pub struct FileResource { + name: String, + file: Rc, +} + +impl FileResource { + pub fn new(file: Rc, name: String) -> Self { + Self { name, file } + } + + pub fn with_resource( + state: &OpState, + rid: ResourceId, + f: F, + ) -> Result + where + F: FnOnce(Rc) -> Result, + { + let resource = state.resource_table.get::(rid)?; + f(resource) + } + + pub fn get_file( + state: &OpState, + rid: ResourceId, + ) -> Result, AnyError> { + let resource = state.resource_table.get::(rid)?; + Ok(resource.file()) + } + + pub fn with_file( + state: &OpState, + rid: ResourceId, + f: F, + ) -> Result + where + F: FnOnce(Rc) -> Result, + { + Self::with_resource(state, rid, |r| f(r.file.clone())) + } + + pub fn file(&self) -> Rc { + self.file.clone() + } +} + +impl deno_core::Resource for FileResource { + fn name(&self) -> Cow { + Cow::Borrowed(&self.name) + } + + fn read( + self: Rc, + limit: usize, + ) -> deno_core::AsyncResult { + Box::pin(async move { + self + .file + .clone() + .read(limit) + .await + .map_err(|err| err.into()) + }) + } + + fn read_byob( + self: Rc, + buf: deno_core::BufMutView, + ) -> deno_core::AsyncResult<(usize, deno_core::BufMutView)> { + Box::pin(async move { + self + .file + .clone() + .read_byob(buf) + .await + .map_err(|err| err.into()) + }) + } + + fn write( + self: Rc, + buf: deno_core::BufView, + ) -> deno_core::AsyncResult { + Box::pin(async move { + self.file.clone().write(buf).await.map_err(|err| err.into()) + }) + } + + fn write_all( + self: Rc, + buf: deno_core::BufView, + ) -> deno_core::AsyncResult<()> { + Box::pin(async move { + self + .file + .clone() + .write_all(buf) + .await + .map_err(|err| err.into()) + }) + } + + fn read_byob_sync( + self: Rc, + data: &mut [u8], + ) -> Result { + self.file.clone().read_sync(data).map_err(|err| err.into()) + } + + fn write_sync( + self: Rc, + data: &[u8], + ) -> Result { + self.file.clone().write_sync(data).map_err(|err| err.into()) + } + + #[cfg(unix)] + fn backing_fd(self: Rc) -> Option { + self.file.clone().backing_fd() + } + + #[cfg(windows)] + fn backing_fd(self: Rc) -> Option { + self.file.clone().backing_fd() + } +} diff --git a/ext/io/lib.rs b/ext/io/lib.rs index 73ce725780..49e4ab714f 100644 --- a/ext/io/lib.rs +++ b/ext/io/lib.rs @@ -1,6 +1,5 @@ // Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. -use deno_core::error::resource_unavailable; use deno_core::error::AnyError; use deno_core::op; use deno_core::AsyncMutFuture; @@ -13,8 +12,12 @@ use deno_core::CancelTryFuture; use deno_core::OpState; use deno_core::RcRef; use deno_core::Resource; -use deno_core::ResourceId; use deno_core::TaskQueue; +use fs::FileResource; +use fs::FsError; +use fs::FsResult; +use fs::FsStat; +use fs3::FileExt; use once_cell::sync::Lazy; use std::borrow::Cow; use std::cell::RefCell; @@ -22,6 +25,7 @@ use std::fs::File as StdFile; use std::io; use std::io::ErrorKind; use std::io::Read; +use std::io::Seek; use std::io::Write; use std::rc::Rc; use tokio::io::AsyncRead; @@ -40,6 +44,8 @@ use winapi::um::processenv::GetStdHandle; #[cfg(windows)] use winapi::um::winbase; +pub mod fs; + // Store the stdio fd/handles in global statics in order to keep them // alive for the duration of the application since the last handle/fd // being dropped will close the corresponding pipe. @@ -89,39 +95,39 @@ deno_core::extension!(deno_io, if let Some(stdio) = options.stdio { let t = &mut state.resource_table; - let rid = t.add(StdFileResource::stdio( - match stdio.stdin { - StdioPipe::Inherit => StdFileResourceInner { - kind: StdFileResourceKind::Stdin, - file: STDIN_HANDLE.try_clone().unwrap(), - }, + let rid = t.add(fs::FileResource::new( + Rc::new(match stdio.stdin { + StdioPipe::Inherit => StdFileResourceInner::new( + StdFileResourceKind::Stdin, + STDIN_HANDLE.try_clone().unwrap(), + ), StdioPipe::File(pipe) => StdFileResourceInner::file(pipe), - }, - "stdin", + }), + "stdin".to_string(), )); assert_eq!(rid, 0, "stdin must have ResourceId 0"); - let rid = t.add(StdFileResource::stdio( - match stdio.stdout { - StdioPipe::Inherit => StdFileResourceInner { - kind: StdFileResourceKind::Stdout, - file: STDOUT_HANDLE.try_clone().unwrap(), - }, + let rid = t.add(FileResource::new( + Rc::new(match stdio.stdout { + StdioPipe::Inherit => StdFileResourceInner::new( + StdFileResourceKind::Stdout, + STDOUT_HANDLE.try_clone().unwrap(), + ), StdioPipe::File(pipe) => StdFileResourceInner::file(pipe), - }, - "stdout", + }), + "stdout".to_string(), )); assert_eq!(rid, 1, "stdout must have ResourceId 1"); - let rid = t.add(StdFileResource::stdio( - match stdio.stderr { - StdioPipe::Inherit => StdFileResourceInner { - kind: StdFileResourceKind::Stderr, - file: STDERR_HANDLE.try_clone().unwrap(), - }, + let rid = t.add(FileResource::new( + Rc::new(match stdio.stderr { + StdioPipe::Inherit => StdFileResourceInner::new( + StdFileResourceKind::Stderr, + STDERR_HANDLE.try_clone().unwrap(), + ), StdioPipe::File(pipe) => StdFileResourceInner::file(pipe), - }, - "stderr", + }), + "stderr".to_string(), )); assert_eq!(rid, 2, "stderr must have ResourceId 2"); } @@ -291,150 +297,43 @@ enum StdFileResourceKind { Stderr, } -struct StdFileResourceInner { +pub struct StdFileResourceInner { kind: StdFileResourceKind, - file: StdFile, -} - -impl StdFileResourceInner { - pub fn file(fs_file: StdFile) -> Self { - StdFileResourceInner { - kind: StdFileResourceKind::File, - file: fs_file, - } - } - - pub fn with_file(&mut self, f: impl FnOnce(&mut StdFile) -> R) -> R { - f(&mut self.file) - } - - pub fn try_clone(&self) -> Result { - Ok(Self { - kind: self.kind, - file: self.file.try_clone()?, - }) - } - - pub fn write_and_maybe_flush( - &mut self, - buf: &[u8], - ) -> Result { - // Rust will line buffer and we don't want that behavior - // (see https://github.com/denoland/deno/issues/948), so flush stdout and stderr. - // Although an alternative solution could be to bypass Rust's std by - // using the raw fds/handles, it will cause encoding issues on Windows - // that we get solved for free by using Rust's stdio wrappers (see - // std/src/sys/windows/stdio.rs in Rust's source code). - match self.kind { - StdFileResourceKind::File => Ok(self.file.write(buf)?), - StdFileResourceKind::Stdin => { - Err(Into::::into(ErrorKind::Unsupported).into()) - } - StdFileResourceKind::Stdout => { - // bypass the file and use std::io::stdout() - let mut stdout = std::io::stdout().lock(); - let nwritten = stdout.write(buf)?; - stdout.flush()?; - Ok(nwritten) - } - StdFileResourceKind::Stderr => { - // bypass the file and use std::io::stderr() - let mut stderr = std::io::stderr().lock(); - let nwritten = stderr.write(buf)?; - stderr.flush()?; - Ok(nwritten) - } - } - } - - pub fn write_all_and_maybe_flush( - &mut self, - buf: &[u8], - ) -> Result<(), AnyError> { - // this method exists instead of using a `Write` implementation - // so that we can acquire the locks once and do both actions - match self.kind { - StdFileResourceKind::File => Ok(self.file.write_all(buf)?), - StdFileResourceKind::Stdin => { - Err(Into::::into(ErrorKind::Unsupported).into()) - } - StdFileResourceKind::Stdout => { - // bypass the file and use std::io::stdout() - let mut stdout = std::io::stdout().lock(); - stdout.write_all(buf)?; - stdout.flush()?; - Ok(()) - } - StdFileResourceKind::Stderr => { - // bypass the file and use std::io::stderr() - let mut stderr = std::io::stderr().lock(); - stderr.write_all(buf)?; - stderr.flush()?; - Ok(()) - } - } - } -} - -impl Read for StdFileResourceInner { - fn read(&mut self, buf: &mut [u8]) -> std::io::Result { - match self.kind { - StdFileResourceKind::File | StdFileResourceKind::Stdin => { - self.file.read(buf) - } - StdFileResourceKind::Stdout | StdFileResourceKind::Stderr => { - Err(ErrorKind::Unsupported.into()) - } - } - } -} - -pub struct StdFileResource { - name: String, // We can't use an AsyncRefCell here because we need to allow // access to the resource synchronously at any time and // asynchronously one at a time in order - cell: RefCell>, + cell: RefCell>, // Used to keep async actions in order and only allow one // to occur at a time cell_async_task_queue: TaskQueue, } -impl StdFileResource { - fn stdio(inner: StdFileResourceInner, name: &str) -> Self { - Self { - cell: RefCell::new(Some(inner)), +impl StdFileResourceInner { + pub fn file(fs_file: StdFile) -> Self { + StdFileResourceInner::new(StdFileResourceKind::File, fs_file) + } + + fn new(kind: StdFileResourceKind, fs_file: StdFile) -> Self { + StdFileResourceInner { + kind, + cell: RefCell::new(Some(fs_file)), cell_async_task_queue: Default::default(), - name: name.to_string(), } } - pub fn fs_file(fs_file: StdFile) -> Self { - Self { - cell: RefCell::new(Some(StdFileResourceInner::file(fs_file))), - cell_async_task_queue: Default::default(), - name: "fsFile".to_string(), - } - } - - fn with_inner( - &self, - action: impl FnOnce(&mut StdFileResourceInner) -> Result, - ) -> Option> { - match self.cell.try_borrow_mut() { - Ok(mut cell) if cell.is_some() => { - let mut file = cell.take().unwrap(); - let result = action(&mut file); - cell.replace(file); - Some(result) - } - _ => None, - } - } - - async fn with_inner_blocking_task(&self, action: F) -> R + fn with_sync(&self, action: F) -> FsResult where - F: FnOnce(&mut StdFileResourceInner) -> R + Send + 'static, + F: FnOnce(&mut StdFile) -> FsResult, + { + match self.cell.try_borrow_mut() { + Ok(mut cell) if cell.is_some() => action(cell.as_mut().unwrap()), + _ => Err(fs::FsError::FileBusy), + } + } + + async fn with_inner_blocking_task(&self, action: F) -> R + where + F: FnOnce(&mut StdFile) -> R + Send + 'static, { // we want to restrict this to one async action at a time let _permit = self.cell_async_task_queue.acquire().await; @@ -443,9 +342,9 @@ impl StdFileResource { let mut did_take = false; let mut cell_value = { let mut cell = self.cell.borrow_mut(); - match cell.as_mut().unwrap().try_clone() { - Ok(value) => value, - Err(_) => { + match cell.as_mut().unwrap().try_clone().ok() { + Some(value) => value, + None => { did_take = true; cell.take().unwrap() } @@ -466,200 +365,369 @@ impl StdFileResource { result } - async fn read_byob( - self: Rc, - mut buf: BufMutView, - ) -> Result<(usize, BufMutView), AnyError> { - self - .with_inner_blocking_task(move |inner| { - let nread = inner.read(&mut buf)?; - Ok((nread, buf)) - }) - .await + async fn with_blocking_task(&self, action: F) -> R + where + F: FnOnce() -> R + Send + 'static, + { + // we want to restrict this to one async action at a time + let _permit = self.cell_async_task_queue.acquire().await; + + tokio::task::spawn_blocking(action).await.unwrap() + } +} + +#[async_trait::async_trait(?Send)] +impl crate::fs::File for StdFileResourceInner { + fn write_sync(self: Rc, buf: &[u8]) -> FsResult { + // Rust will line buffer and we don't want that behavior + // (see https://github.com/denoland/deno/issues/948), so flush stdout and stderr. + // Although an alternative solution could be to bypass Rust's std by + // using the raw fds/handles, it will cause encoding issues on Windows + // that we get solved for free by using Rust's stdio wrappers (see + // std/src/sys/windows/stdio.rs in Rust's source code). + match self.kind { + StdFileResourceKind::File => self.with_sync(|file| Ok(file.write(buf)?)), + StdFileResourceKind::Stdin => { + Err(Into::::into(ErrorKind::Unsupported).into()) + } + StdFileResourceKind::Stdout => { + // bypass the file and use std::io::stdout() + let mut stdout = std::io::stdout().lock(); + let nwritten = stdout.write(buf)?; + stdout.flush()?; + Ok(nwritten) + } + StdFileResourceKind::Stderr => { + // bypass the file and use std::io::stderr() + let mut stderr = std::io::stderr().lock(); + let nwritten = stderr.write(buf)?; + stderr.flush()?; + Ok(nwritten) + } + } + } + + fn read_sync(self: Rc, buf: &mut [u8]) -> FsResult { + match self.kind { + StdFileResourceKind::File | StdFileResourceKind::Stdin => { + self.with_sync(|file| Ok(file.read(buf)?)) + } + StdFileResourceKind::Stdout | StdFileResourceKind::Stderr => { + Err(FsError::NotSupported) + } + } + } + + fn write_all_sync(self: Rc, buf: &[u8]) -> FsResult<()> { + match self.kind { + StdFileResourceKind::File => { + self.with_sync(|file| Ok(file.write_all(buf)?)) + } + StdFileResourceKind::Stdin => { + Err(Into::::into(ErrorKind::Unsupported).into()) + } + StdFileResourceKind::Stdout => { + // bypass the file and use std::io::stdout() + let mut stdout = std::io::stdout().lock(); + stdout.write_all(buf)?; + stdout.flush()?; + Ok(()) + } + StdFileResourceKind::Stderr => { + // bypass the file and use std::io::stderr() + let mut stderr = std::io::stderr().lock(); + stderr.write_all(buf)?; + stderr.flush()?; + Ok(()) + } + } + } + async fn write_all(self: Rc, buf: BufView) -> FsResult<()> { + match self.kind { + StdFileResourceKind::File => { + self + .with_inner_blocking_task(move |file| Ok(file.write_all(&buf)?)) + .await + } + StdFileResourceKind::Stdin => { + Err(Into::::into(ErrorKind::Unsupported).into()) + } + StdFileResourceKind::Stdout => { + self + .with_blocking_task(move || { + // bypass the file and use std::io::stdout() + let mut stdout = std::io::stdout().lock(); + stdout.write_all(&buf)?; + stdout.flush()?; + Ok(()) + }) + .await + } + StdFileResourceKind::Stderr => { + self + .with_blocking_task(move || { + // bypass the file and use std::io::stderr() + let mut stderr = std::io::stderr().lock(); + stderr.write_all(&buf)?; + stderr.flush()?; + Ok(()) + }) + .await + } + } } async fn write( self: Rc, view: BufView, - ) -> Result { - self - .with_inner_blocking_task(move |inner| { - let nwritten = inner.write_and_maybe_flush(&view)?; - Ok(deno_core::WriteOutcome::Partial { nwritten, view }) - }) - .await - } - - async fn write_all(self: Rc, view: BufView) -> Result<(), AnyError> { - self - .with_inner_blocking_task(move |inner| { - inner.write_all_and_maybe_flush(&view) - }) - .await - } - - fn read_byob_sync(self: Rc, buf: &mut [u8]) -> Result { - self - .with_inner(|inner| inner.read(buf)) - .ok_or_else(resource_unavailable)? - .map_err(Into::into) - } - - fn write_sync(self: Rc, data: &[u8]) -> Result { - self - .with_inner(|inner| inner.write_and_maybe_flush(data)) - .ok_or_else(resource_unavailable)? - } - - fn with_resource( - state: &mut OpState, - rid: ResourceId, - f: F, - ) -> Result - where - F: FnOnce(Rc) -> Result, - { - let resource = state.resource_table.get::(rid)?; - f(resource) - } - - pub fn with_file( - state: &mut OpState, - rid: ResourceId, - f: F, - ) -> Result - where - F: FnOnce(&mut StdFile) -> Result, - { - Self::with_resource(state, rid, move |resource| { - resource - .with_inner(move |inner| inner.with_file(f)) - .ok_or_else(resource_unavailable)? - }) - } - - pub fn with_file2(self: Rc, f: F) -> Option> - where - F: FnOnce(&mut StdFile) -> Result, - { - self.with_inner(move |inner| inner.with_file(f)) - } - - pub async fn with_file_blocking_task( - state: Rc>, - rid: ResourceId, - f: F, - ) -> Result - where - F: (FnOnce(&mut StdFile) -> Result) + Send + 'static, - { - let resource = state - .borrow_mut() - .resource_table - .get::(rid)?; - - resource - .with_inner_blocking_task(move |inner| inner.with_file(f)) - .await - } - - pub async fn with_file_blocking_task2( - self: Rc, - f: F, - ) -> Result - where - F: (FnOnce(&mut StdFile) -> Result) + Send + 'static, - { - self - .with_inner_blocking_task(move |inner| inner.with_file(f)) - .await - } - - pub fn clone_file( - state: &mut OpState, - rid: ResourceId, - ) -> Result { - Self::with_file(state, rid, move |std_file| { - std_file.try_clone().map_err(AnyError::from) - }) - } - - pub fn as_stdio( - state: &mut OpState, - rid: u32, - ) -> Result { - Self::with_resource(state, rid, |resource| { - resource - .with_inner(|inner| match inner.kind { - StdFileResourceKind::File => { - let file = inner.file.try_clone()?; - Ok(file.into()) - } - _ => Ok(std::process::Stdio::inherit()), - }) - .ok_or_else(resource_unavailable)? - }) - } -} - -impl Resource for StdFileResource { - fn name(&self) -> Cow { - self.name.as_str().into() - } - - fn read(self: Rc, limit: usize) -> AsyncResult { - Box::pin(async move { - let vec = vec![0; limit]; - let buf = BufMutView::from(vec); - let (nread, buf) = StdFileResource::read_byob(self, buf).await?; - let mut vec = buf.unwrap_vec(); - if vec.len() != nread { - vec.truncate(nread); + ) -> FsResult { + match self.kind { + StdFileResourceKind::File => { + self + .with_inner_blocking_task(|file| { + let nwritten = file.write(&view)?; + Ok(deno_core::WriteOutcome::Partial { nwritten, view }) + }) + .await } - Ok(BufView::from(vec)) + StdFileResourceKind::Stdin => { + Err(Into::::into(ErrorKind::Unsupported).into()) + } + StdFileResourceKind::Stdout => { + self + .with_blocking_task(|| { + // bypass the file and use std::io::stdout() + let mut stdout = std::io::stdout().lock(); + let nwritten = stdout.write(&view)?; + stdout.flush()?; + Ok(deno_core::WriteOutcome::Partial { nwritten, view }) + }) + .await + } + StdFileResourceKind::Stderr => { + self + .with_blocking_task(|| { + // bypass the file and use std::io::stderr() + let mut stderr = std::io::stderr().lock(); + let nwritten = stderr.write(&view)?; + stderr.flush()?; + Ok(deno_core::WriteOutcome::Partial { nwritten, view }) + }) + .await + } + } + } + + fn read_all_sync(self: Rc) -> FsResult> { + match self.kind { + StdFileResourceKind::File | StdFileResourceKind::Stdin => { + let mut buf = Vec::new(); + self.with_sync(|file| Ok(file.read_to_end(&mut buf)?))?; + Ok(buf) + } + StdFileResourceKind::Stdout | StdFileResourceKind::Stderr => { + Err(FsError::NotSupported) + } + } + } + async fn read_all_async(self: Rc) -> FsResult> { + match self.kind { + StdFileResourceKind::File | StdFileResourceKind::Stdin => { + self + .with_inner_blocking_task(|file| { + let mut buf = Vec::new(); + file.read_to_end(&mut buf)?; + Ok(buf) + }) + .await + } + StdFileResourceKind::Stdout | StdFileResourceKind::Stderr => { + Err(FsError::NotSupported) + } + } + } + + fn chmod_sync(self: Rc, _mode: u32) -> FsResult<()> { + #[cfg(unix)] + { + use std::os::unix::prelude::PermissionsExt; + self.with_sync(|file| { + Ok(file.set_permissions(std::fs::Permissions::from_mode(_mode))?) + }) + } + #[cfg(not(unix))] + Err(FsError::NotSupported) + } + async fn chmod_async(self: Rc, _mode: u32) -> FsResult<()> { + #[cfg(unix)] + { + use std::os::unix::prelude::PermissionsExt; + self + .with_inner_blocking_task(move |file| { + Ok(file.set_permissions(std::fs::Permissions::from_mode(_mode))?) + }) + .await + } + #[cfg(not(unix))] + Err(FsError::NotSupported) + } + + fn seek_sync(self: Rc, pos: io::SeekFrom) -> FsResult { + self.with_sync(|file| Ok(file.seek(pos)?)) + } + async fn seek_async(self: Rc, pos: io::SeekFrom) -> FsResult { + self + .with_inner_blocking_task(move |file| Ok(file.seek(pos)?)) + .await + } + + fn datasync_sync(self: Rc) -> FsResult<()> { + self.with_sync(|file| Ok(file.sync_data()?)) + } + async fn datasync_async(self: Rc) -> FsResult<()> { + self + .with_inner_blocking_task(|file| Ok(file.sync_data()?)) + .await + } + + fn sync_sync(self: Rc) -> FsResult<()> { + self.with_sync(|file| Ok(file.sync_all()?)) + } + async fn sync_async(self: Rc) -> FsResult<()> { + self + .with_inner_blocking_task(|file| Ok(file.sync_all()?)) + .await + } + + fn stat_sync(self: Rc) -> FsResult { + self.with_sync(|file| Ok(file.metadata().map(FsStat::from_std)?)) + } + async fn stat_async(self: Rc) -> FsResult { + self + .with_inner_blocking_task(|file| { + Ok(file.metadata().map(FsStat::from_std)?) + }) + .await + } + + fn lock_sync(self: Rc, exclusive: bool) -> FsResult<()> { + self.with_sync(|file| { + if exclusive { + file.lock_exclusive()?; + } else { + file.lock_shared()?; + } + Ok(()) }) } - - fn read_byob( - self: Rc, - buf: deno_core::BufMutView, - ) -> AsyncResult<(usize, deno_core::BufMutView)> { - Box::pin(StdFileResource::read_byob(self, buf)) + async fn lock_async(self: Rc, exclusive: bool) -> FsResult<()> { + self + .with_inner_blocking_task(move |file| { + if exclusive { + file.lock_exclusive()?; + } else { + file.lock_shared()?; + } + Ok(()) + }) + .await } - fn write( - self: Rc, - view: deno_core::BufView, - ) -> AsyncResult { - Box::pin(StdFileResource::write(self, view)) + fn unlock_sync(self: Rc) -> FsResult<()> { + self.with_sync(|file| Ok(file.unlock()?)) + } + async fn unlock_async(self: Rc) -> FsResult<()> { + self + .with_inner_blocking_task(|file| Ok(file.unlock()?)) + .await } - fn write_all(self: Rc, view: deno_core::BufView) -> AsyncResult<()> { - Box::pin(StdFileResource::write_all(self, view)) + fn truncate_sync(self: Rc, len: u64) -> FsResult<()> { + self.with_sync(|file| Ok(file.set_len(len)?)) + } + async fn truncate_async(self: Rc, len: u64) -> FsResult<()> { + self + .with_inner_blocking_task(move |file| Ok(file.set_len(len)?)) + .await } - fn write_sync( + fn utime_sync( self: Rc, - data: &[u8], - ) -> Result { - StdFileResource::write_sync(self, data) + atime_secs: i64, + atime_nanos: u32, + mtime_secs: i64, + mtime_nanos: u32, + ) -> FsResult<()> { + let atime = filetime::FileTime::from_unix_time(atime_secs, atime_nanos); + let mtime = filetime::FileTime::from_unix_time(mtime_secs, mtime_nanos); + + self.with_sync(|file| { + filetime::set_file_handle_times(file, Some(atime), Some(mtime))?; + Ok(()) + }) + } + async fn utime_async( + self: Rc, + atime_secs: i64, + atime_nanos: u32, + mtime_secs: i64, + mtime_nanos: u32, + ) -> FsResult<()> { + let atime = filetime::FileTime::from_unix_time(atime_secs, atime_nanos); + let mtime = filetime::FileTime::from_unix_time(mtime_secs, mtime_nanos); + + self + .with_inner_blocking_task(move |file| { + filetime::set_file_handle_times(file, Some(atime), Some(mtime))?; + Ok(()) + }) + .await } - fn read_byob_sync( + async fn read_byob( self: Rc, - data: &mut [u8], - ) -> Result { - StdFileResource::read_byob_sync(self, data) + mut buf: BufMutView, + ) -> FsResult<(usize, BufMutView)> { + self + .with_inner_blocking_task(|file| { + let nread = file.read(&mut buf)?; + Ok((nread, buf)) + }) + .await + } + + fn try_clone_inner(self: Rc) -> FsResult> { + let inner: &Option<_> = &self.cell.borrow(); + match inner { + Some(inner) => Ok(Rc::new(StdFileResourceInner { + kind: self.kind, + cell: RefCell::new(Some(inner.try_clone()?)), + cell_async_task_queue: Default::default(), + })), + None => Err(FsError::FileBusy), + } + } + + fn as_stdio(self: Rc) -> FsResult { + match self.kind { + StdFileResourceKind::File => self.with_sync(|file| { + let file = file.try_clone()?; + Ok(file.into()) + }), + _ => Ok(std::process::Stdio::inherit()), + } } #[cfg(unix)] fn backing_fd(self: Rc) -> Option { use std::os::unix::io::AsRawFd; - self - .with_inner(move |std_file| { - Ok::<_, ()>(std_file.with_file(|f| f.as_raw_fd())) - })? - .ok() + self.with_sync(|file| Ok(file.as_raw_fd())).ok() + } + + #[cfg(windows)] + fn backing_fd(self: Rc) -> Option { + use std::os::windows::prelude::AsRawHandle; + self.with_sync(|file| Ok(file.as_raw_handle())).ok() } } @@ -671,12 +739,7 @@ pub fn op_print( is_err: bool, ) -> Result<(), AnyError> { let rid = if is_err { 2 } else { 1 }; - StdFileResource::with_resource(state, rid, move |resource| { - resource - .with_inner(|inner| { - inner.write_all_and_maybe_flush(msg.as_bytes())?; - Ok(()) - }) - .ok_or_else(resource_unavailable)? + FileResource::with_file(state, rid, move |file| { + Ok(file.write_all_sync(msg.as_bytes())?) }) } diff --git a/runtime/build.rs b/runtime/build.rs index bba2eae551..d096df7db1 100644 --- a/runtime/build.rs +++ b/runtime/build.rs @@ -18,7 +18,6 @@ mod startup_snapshot { use deno_core::Extension; use deno_core::ExtensionFileSource; use deno_core::ModuleCode; - use deno_fs::StdFs; use std::path::Path; fn transpile_ts_for_snapshotting( @@ -310,7 +309,10 @@ mod startup_snapshot { deno_napi::deno_napi::init_ops_and_esm::(), deno_http::deno_http::init_ops_and_esm(), deno_io::deno_io::init_ops_and_esm(Default::default()), - deno_fs::deno_fs::init_ops_and_esm::<_, Permissions>(false, StdFs), + deno_fs::deno_fs::init_ops_and_esm::( + false, + std::sync::Arc::new(deno_fs::RealFs), + ), runtime::init_ops_and_esm(), // FIXME(bartlomieju): these extensions are specified last, because they // depend on `runtime`, even though it should be other way around diff --git a/runtime/ops/process.rs b/runtime/ops/process.rs index cf8740255d..d991c961f2 100644 --- a/runtime/ops/process.rs +++ b/runtime/ops/process.rs @@ -12,10 +12,10 @@ use deno_core::RcRef; use deno_core::Resource; use deno_core::ResourceId; use deno_core::ZeroCopyBuf; +use deno_io::fs::FileResource; use deno_io::ChildStderrResource; use deno_io::ChildStdinResource; use deno_io::ChildStdoutResource; -use deno_io::StdFileResource; use serde::Deserialize; use serde::Serialize; use std::borrow::Cow; @@ -93,7 +93,9 @@ impl StdioOrRid { ) -> Result { match &self { StdioOrRid::Stdio(val) => Ok(val.as_stdio()), - StdioOrRid::Rid(rid) => StdFileResource::as_stdio(state, *rid), + StdioOrRid::Rid(rid) => { + FileResource::with_file(state, *rid, |file| Ok(file.as_stdio()?)) + } } } } diff --git a/runtime/ops/tty.rs b/runtime/ops/tty.rs index a3dc03a6fa..7f24daec4b 100644 --- a/runtime/ops/tty.rs +++ b/runtime/ops/tty.rs @@ -1,10 +1,14 @@ // Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +use std::io::Error; +use std::rc::Rc; + +use deno_core::error::resource_unavailable; use deno_core::error::AnyError; use deno_core::op; use deno_core::OpState; -use deno_io::StdFileResource; -use std::io::Error; +use deno_core::Resource; +use deno_io::fs::FileResource; #[cfg(unix)] use deno_core::ResourceId; @@ -14,8 +18,6 @@ use nix::sys::termios; use std::cell::RefCell; #[cfg(unix)] use std::collections::HashMap; -#[cfg(unix)] -use std::rc::Rc; #[cfg(unix)] #[derive(Default, Clone)] @@ -44,13 +46,14 @@ use winapi::shared::minwindef::DWORD; use winapi::um::wincon; #[cfg(windows)] -fn get_windows_handle( - f: &std::fs::File, +fn get_fd_from_resource( + resource: Rc, ) -> Result { - use std::os::windows::io::AsRawHandle; use winapi::um::handleapi; - let handle = f.as_raw_handle(); + let Some(handle) = resource.backing_fd() else { + return Err(resource_unavailable()); + }; if handle == handleapi::INVALID_HANDLE_VALUE { return Err(Error::last_os_error().into()); } else if handle.is_null() { @@ -59,6 +62,16 @@ fn get_windows_handle( Ok(handle) } +#[cfg(not(windows))] +fn get_fd_from_resource( + resource: Rc, +) -> Result { + match resource.backing_fd() { + Some(fd) => Ok(fd), + None => Err(resource_unavailable()), + } +} + deno_core::extension!( deno_tty, ops = [op_stdin_set_raw, op_isatty, op_console_size], @@ -106,23 +119,15 @@ fn op_stdin_set_raw( // Copyright (c) 2019 Timon. MIT license. #[cfg(windows)] { - use std::os::windows::io::AsRawHandle; use winapi::shared::minwindef::FALSE; use winapi::um::consoleapi; - use winapi::um::handleapi; if cbreak { return Err(deno_core::error::not_supported()); } - StdFileResource::with_file(state, rid, move |std_file| { - let handle = std_file.as_raw_handle(); - - if handle == handleapi::INVALID_HANDLE_VALUE { - return Err(Error::last_os_error().into()); - } else if handle.is_null() { - return Err(custom_error("ReferenceError", "null handle")); - } + FileResource::with_resource(state, rid, move |resource| { + let handle = get_fd_from_resource(resource)?; let mut original_mode: DWORD = 0; // SAFETY: winapi call if unsafe { consoleapi::GetConsoleMode(handle, &mut original_mode) } @@ -147,13 +152,11 @@ fn op_stdin_set_raw( } #[cfg(unix)] { - use std::os::unix::io::AsRawFd; - let tty_mode_store = state.borrow::().clone(); let previous_mode = tty_mode_store.get(rid); - StdFileResource::with_file(state, rid, move |std_file| { - let raw_fd = std_file.as_raw_fd(); + FileResource::with_resource(state, rid, move |resource| { + let raw_fd = get_fd_from_resource(resource)?; if is_raw { let mut raw = match previous_mode { @@ -201,13 +204,14 @@ fn op_isatty( rid: u32, out: &mut [u8], ) -> Result<(), AnyError> { - StdFileResource::with_file(state, rid, move |std_file| { + FileResource::with_resource(state, rid, move |resource| { + let raw_fd = get_fd_from_resource(resource)?; #[cfg(windows)] { use winapi::shared::minwindef::FALSE; use winapi::um::consoleapi; - let handle = get_windows_handle(std_file)?; + let handle = raw_fd; let mut test_mode: DWORD = 0; // If I cannot get mode out of console, it is not a console. // TODO(bartlomieju): @@ -220,8 +224,6 @@ fn op_isatty( } #[cfg(unix)] { - use std::os::unix::io::AsRawFd; - let raw_fd = std_file.as_raw_fd(); // TODO(bartlomieju): #[allow(clippy::undocumented_unsafe_blocks)] { @@ -242,8 +244,9 @@ fn op_console_size( result: &mut [u32], rid: u32, ) -> Result<(), AnyError> { - StdFileResource::with_file(state, rid, move |std_file| { - let size = console_size(std_file)?; + FileResource::with_resource(state, rid, move |resource| { + let fd = get_fd_from_resource(resource)?; + let size = console_size_from_fd(fd)?; result[0] = size.cols; result[1] = size.rows; Ok(()) @@ -276,40 +279,50 @@ pub fn console_size( { use std::os::windows::io::AsRawHandle; let handle = std_file.as_raw_handle(); - - // SAFETY: winapi calls - unsafe { - let mut bufinfo: winapi::um::wincon::CONSOLE_SCREEN_BUFFER_INFO = - std::mem::zeroed(); - - if winapi::um::wincon::GetConsoleScreenBufferInfo(handle, &mut bufinfo) - == 0 - { - return Err(Error::last_os_error()); - } - Ok(ConsoleSize { - cols: bufinfo.dwSize.X as u32, - rows: bufinfo.dwSize.Y as u32, - }) - } + console_size_from_fd(handle) } - #[cfg(unix)] { use std::os::unix::io::AsRawFd; - let fd = std_file.as_raw_fd(); - // SAFETY: libc calls - unsafe { - let mut size: libc::winsize = std::mem::zeroed(); - if libc::ioctl(fd, libc::TIOCGWINSZ, &mut size as *mut _) != 0 { - return Err(Error::last_os_error()); - } - Ok(ConsoleSize { - cols: size.ws_col as u32, - rows: size.ws_row as u32, - }) + console_size_from_fd(fd) + } +} + +#[cfg(windows)] +fn console_size_from_fd( + handle: std::os::windows::io::RawHandle, +) -> Result { + // SAFETY: winapi calls + unsafe { + let mut bufinfo: winapi::um::wincon::CONSOLE_SCREEN_BUFFER_INFO = + std::mem::zeroed(); + + if winapi::um::wincon::GetConsoleScreenBufferInfo(handle, &mut bufinfo) == 0 + { + return Err(Error::last_os_error()); } + Ok(ConsoleSize { + cols: bufinfo.dwSize.X as u32, + rows: bufinfo.dwSize.Y as u32, + }) + } +} + +#[cfg(not(windows))] +fn console_size_from_fd( + fd: std::os::unix::prelude::RawFd, +) -> Result { + // SAFETY: libc calls + unsafe { + let mut size: libc::winsize = std::mem::zeroed(); + if libc::ioctl(fd, libc::TIOCGWINSZ, &mut size as *mut _) != 0 { + return Err(Error::last_os_error()); + } + Ok(ConsoleSize { + cols: size.ws_col as u32, + rows: size.ws_row as u32, + }) } } diff --git a/runtime/web_worker.rs b/runtime/web_worker.rs index 1b3dd28096..e485c0c35b 100644 --- a/runtime/web_worker.rs +++ b/runtime/web_worker.rs @@ -34,7 +34,7 @@ use deno_core::RuntimeOptions; use deno_core::SharedArrayBufferStore; use deno_core::Snapshot; use deno_core::SourceMapGetter; -use deno_fs::StdFs; +use deno_fs::FileSystem; use deno_io::Stdio; use deno_kv::sqlite::SqliteDbHandler; use deno_tls::RootCertStoreProvider; @@ -331,6 +331,7 @@ pub struct WebWorkerOptions { pub unsafely_ignore_certificate_errors: Option>, pub root_cert_store_provider: Option>, pub seed: Option, + pub fs: Arc, pub module_loader: Rc, pub node_fs: Option>, pub npm_resolver: Option>, @@ -441,7 +442,7 @@ impl WebWorker { deno_napi::deno_napi::init_ops::(), deno_http::deno_http::init_ops(), deno_io::deno_io::init_ops(Some(options.stdio)), - deno_fs::deno_fs::init_ops::<_, PermissionsContainer>(unstable, StdFs), + deno_fs::deno_fs::init_ops::(unstable, options.fs), deno_node::deno_node::init_ops::( options.npm_resolver, options.node_fs, diff --git a/runtime/worker.rs b/runtime/worker.rs index ac67011f0d..b9db217804 100644 --- a/runtime/worker.rs +++ b/runtime/worker.rs @@ -30,7 +30,7 @@ use deno_core::RuntimeOptions; use deno_core::SharedArrayBufferStore; use deno_core::Snapshot; use deno_core::SourceMapGetter; -use deno_fs::StdFs; +use deno_fs::FileSystem; use deno_io::Stdio; use deno_kv::sqlite::SqliteDbHandler; use deno_tls::RootCertStoreProvider; @@ -87,6 +87,7 @@ pub struct WorkerOptions { pub root_cert_store_provider: Option>, pub seed: Option, + pub fs: Arc, /// Implementation of `ModuleLoader` which will be /// called when V8 requests to load ES modules. /// @@ -149,6 +150,7 @@ impl Default for WorkerOptions { create_web_worker_cb: Arc::new(|_| { unimplemented!("web workers are not supported") }), + fs: Arc::new(deno_fs::RealFs), module_loader: Rc::new(FsModuleLoader), seed: None, unsafely_ignore_certificate_errors: Default::default(), @@ -266,7 +268,7 @@ impl MainWorker { deno_napi::deno_napi::init_ops::(), deno_http::deno_http::init_ops(), deno_io::deno_io::init_ops(Some(options.stdio)), - deno_fs::deno_fs::init_ops::<_, PermissionsContainer>(unstable, StdFs), + deno_fs::deno_fs::init_ops::(unstable, options.fs), deno_node::deno_node::init_ops::( options.npm_resolver, options.node_fs,