mirror of
https://github.com/denoland/deno.git
synced 2024-12-23 15:49:44 -05:00
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<dyn File>`.
This commit is contained in:
parent
c2a61a4192
commit
59fbdc4a93
20 changed files with 1190 additions and 1258 deletions
3
Cargo.lock
generated
3
Cargo.lock
generated
|
@ -1059,7 +1059,10 @@ dependencies = [
|
|||
name = "deno_io"
|
||||
version = "0.13.0"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"deno_core",
|
||||
"filetime",
|
||||
"fs3",
|
||||
"nix",
|
||||
"once_cell",
|
||||
"tokio",
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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::<PermissionsContainer>(),
|
||||
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::<PermissionsContainer>(
|
||||
None,
|
||||
Some(Arc::new(deno_node::RealFs)),
|
||||
deno_fs::deno_fs::init_ops::<PermissionsContainer>(
|
||||
false,
|
||||
Arc::new(deno_fs::RealFs),
|
||||
),
|
||||
deno_node::deno_node::init_ops::<PermissionsContainer>(None, None),
|
||||
cli::init_ops_and_esm(), // NOTE: This needs to be init_ops_and_esm!
|
||||
];
|
||||
|
||||
|
|
|
@ -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()?,
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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<dyn ModuleLoaderFactory>,
|
||||
root_cert_store_provider: Arc<dyn RootCertStoreProvider>,
|
||||
fs: Arc<dyn deno_fs::FileSystem>,
|
||||
node_fs: Arc<dyn deno_node::NodeFs>,
|
||||
maybe_inspector_server: Option<Arc<InspectorServer>>,
|
||||
}
|
||||
|
@ -308,6 +310,7 @@ impl CliMainWorkerFactory {
|
|||
blob_store: BlobStore,
|
||||
module_loader_factory: Box<dyn ModuleLoaderFactory>,
|
||||
root_cert_store_provider: Arc<dyn RootCertStoreProvider>,
|
||||
fs: Arc<dyn deno_fs::FileSystem>,
|
||||
node_fs: Arc<dyn deno_node::NodeFs>,
|
||||
maybe_inspector_server: Option<Arc<InspectorServer>>,
|
||||
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)
|
||||
|
|
|
@ -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<Self>) -> Option<std::os::windows::io::RawHandle> {
|
||||
None
|
||||
}
|
||||
|
||||
fn size_hint(&self) -> (u64, Option<u64>) {
|
||||
(0, None)
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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<u64>,
|
||||
pub atime: Option<u64>,
|
||||
pub birthtime: Option<u64>,
|
||||
|
||||
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<io::Error> for FsError {
|
||||
fn from(err: io::Error) -> Self {
|
||||
Self::Io(err)
|
||||
}
|
||||
}
|
||||
|
||||
pub type FsResult<T> = Result<T, FsError>;
|
||||
|
||||
#[async_trait::async_trait(?Send)]
|
||||
pub trait File {
|
||||
fn write_all_sync(self: Rc<Self>, buf: &[u8]) -> FsResult<()>;
|
||||
async fn write_all_async(self: Rc<Self>, buf: Vec<u8>) -> FsResult<()>;
|
||||
|
||||
fn read_all_sync(self: Rc<Self>) -> FsResult<Vec<u8>>;
|
||||
async fn read_all_async(self: Rc<Self>) -> FsResult<Vec<u8>>;
|
||||
|
||||
fn chmod_sync(self: Rc<Self>, pathmode: u32) -> FsResult<()>;
|
||||
async fn chmod_async(self: Rc<Self>, mode: u32) -> FsResult<()>;
|
||||
|
||||
fn seek_sync(self: Rc<Self>, pos: io::SeekFrom) -> FsResult<u64>;
|
||||
async fn seek_async(self: Rc<Self>, pos: io::SeekFrom) -> FsResult<u64>;
|
||||
|
||||
fn datasync_sync(self: Rc<Self>) -> FsResult<()>;
|
||||
async fn datasync_async(self: Rc<Self>) -> FsResult<()>;
|
||||
|
||||
fn sync_sync(self: Rc<Self>) -> FsResult<()>;
|
||||
async fn sync_async(self: Rc<Self>) -> FsResult<()>;
|
||||
|
||||
fn stat_sync(self: Rc<Self>) -> FsResult<FsStat>;
|
||||
async fn stat_async(self: Rc<Self>) -> FsResult<FsStat>;
|
||||
|
||||
fn lock_sync(self: Rc<Self>, exclusive: bool) -> FsResult<()>;
|
||||
async fn lock_async(self: Rc<Self>, exclusive: bool) -> FsResult<()>;
|
||||
fn unlock_sync(self: Rc<Self>) -> FsResult<()>;
|
||||
async fn unlock_async(self: Rc<Self>) -> FsResult<()>;
|
||||
|
||||
fn truncate_sync(self: Rc<Self>, len: u64) -> FsResult<()>;
|
||||
async fn truncate_async(self: Rc<Self>, len: u64) -> FsResult<()>;
|
||||
|
||||
fn utime_sync(
|
||||
self: Rc<Self>,
|
||||
atime_secs: i64,
|
||||
atime_nanos: u32,
|
||||
mtime_secs: i64,
|
||||
mtime_nanos: u32,
|
||||
) -> FsResult<()>;
|
||||
async fn utime_async(
|
||||
self: Rc<Self>,
|
||||
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<PathBuf>;
|
||||
fn tmp_dir(&self) -> FsResult<PathBuf>;
|
||||
fn chdir(&self, path: impl AsRef<Path>) -> FsResult<()>;
|
||||
fn chdir(&self, path: &Path) -> FsResult<()>;
|
||||
fn umask(&self, mask: Option<u32>) -> FsResult<u32>;
|
||||
|
||||
fn open_sync(
|
||||
&self,
|
||||
path: impl AsRef<Path>,
|
||||
path: &Path,
|
||||
options: OpenOptions,
|
||||
) -> FsResult<Self::File>;
|
||||
) -> FsResult<Rc<dyn File>>;
|
||||
async fn open_async(
|
||||
&self,
|
||||
path: PathBuf,
|
||||
options: OpenOptions,
|
||||
) -> FsResult<Self::File>;
|
||||
) -> FsResult<Rc<dyn File>>;
|
||||
|
||||
fn mkdir_sync(
|
||||
&self,
|
||||
path: impl AsRef<Path>,
|
||||
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<Path>, 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: &Path,
|
||||
uid: Option<u32>,
|
||||
gid: Option<u32>,
|
||||
) -> FsResult<()>;
|
||||
|
@ -200,52 +114,36 @@ pub trait FileSystem: Clone {
|
|||
gid: Option<u32>,
|
||||
) -> FsResult<()>;
|
||||
|
||||
fn remove_sync(
|
||||
&self,
|
||||
path: impl AsRef<Path>,
|
||||
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<Path>,
|
||||
newpath: impl AsRef<Path>,
|
||||
) -> 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<Path>) -> FsResult<FsStat>;
|
||||
fn stat_sync(&self, path: &Path) -> FsResult<FsStat>;
|
||||
async fn stat_async(&self, path: PathBuf) -> FsResult<FsStat>;
|
||||
|
||||
fn lstat_sync(&self, path: impl AsRef<Path>) -> FsResult<FsStat>;
|
||||
fn lstat_sync(&self, path: &Path) -> FsResult<FsStat>;
|
||||
async fn lstat_async(&self, path: PathBuf) -> FsResult<FsStat>;
|
||||
|
||||
fn realpath_sync(&self, path: impl AsRef<Path>) -> FsResult<PathBuf>;
|
||||
fn realpath_sync(&self, path: &Path) -> FsResult<PathBuf>;
|
||||
async fn realpath_async(&self, path: PathBuf) -> FsResult<PathBuf>;
|
||||
|
||||
fn read_dir_sync(&self, path: impl AsRef<Path>) -> FsResult<Vec<FsDirEntry>>;
|
||||
fn read_dir_sync(&self, path: &Path) -> FsResult<Vec<FsDirEntry>>;
|
||||
async fn read_dir_async(&self, path: PathBuf) -> FsResult<Vec<FsDirEntry>>;
|
||||
|
||||
fn rename_sync(
|
||||
&self,
|
||||
oldpath: impl AsRef<Path>,
|
||||
newpath: impl AsRef<Path>,
|
||||
) -> 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<Path>,
|
||||
newpath: impl AsRef<Path>,
|
||||
) -> 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<Path>,
|
||||
newpath: impl AsRef<Path>,
|
||||
oldpath: &Path,
|
||||
newpath: &Path,
|
||||
file_type: Option<FsFileType>,
|
||||
) -> FsResult<()>;
|
||||
async fn symlink_async(
|
||||
|
@ -265,15 +163,15 @@ pub trait FileSystem: Clone {
|
|||
file_type: Option<FsFileType>,
|
||||
) -> FsResult<()>;
|
||||
|
||||
fn read_link_sync(&self, path: impl AsRef<Path>) -> FsResult<PathBuf>;
|
||||
fn read_link_sync(&self, path: &Path) -> FsResult<PathBuf>;
|
||||
async fn read_link_async(&self, path: PathBuf) -> FsResult<PathBuf>;
|
||||
|
||||
fn truncate_sync(&self, path: impl AsRef<Path>, 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: &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: &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<u8>,
|
||||
) -> 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<Path>) -> FsResult<Vec<u8>> {
|
||||
fn read_file_sync(&self, path: &Path) -> FsResult<Vec<u8>> {
|
||||
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<Vec<u8>> {
|
||||
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)
|
||||
}
|
||||
|
|
135
ext/fs/lib.rs
135
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<RefCell<OpState>>, 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<Fs, P>,
|
||||
op_umask<Fs>,
|
||||
op_chdir<Fs, P>,
|
||||
op_cwd<P>,
|
||||
op_umask,
|
||||
op_chdir<P>,
|
||||
|
||||
op_open_sync<Fs, P>,
|
||||
op_open_async<Fs, P>,
|
||||
op_mkdir_sync<Fs, P>,
|
||||
op_mkdir_async<Fs, P>,
|
||||
op_chmod_sync<Fs, P>,
|
||||
op_chmod_async<Fs, P>,
|
||||
op_chown_sync<Fs, P>,
|
||||
op_chown_async<Fs, P>,
|
||||
op_remove_sync<Fs, P>,
|
||||
op_remove_async<Fs, P>,
|
||||
op_copy_file_sync<Fs, P>,
|
||||
op_copy_file_async<Fs, P>,
|
||||
op_stat_sync<Fs, P>,
|
||||
op_stat_async<Fs, P>,
|
||||
op_lstat_sync<Fs, P>,
|
||||
op_lstat_async<Fs, P>,
|
||||
op_realpath_sync<Fs, P>,
|
||||
op_realpath_async<Fs, P>,
|
||||
op_read_dir_sync<Fs, P>,
|
||||
op_read_dir_async<Fs, P>,
|
||||
op_rename_sync<Fs, P>,
|
||||
op_rename_async<Fs, P>,
|
||||
op_link_sync<Fs, P>,
|
||||
op_link_async<Fs, P>,
|
||||
op_symlink_sync<Fs, P>,
|
||||
op_symlink_async<Fs, P>,
|
||||
op_read_link_sync<Fs, P>,
|
||||
op_read_link_async<Fs, P>,
|
||||
op_truncate_sync<Fs, P>,
|
||||
op_truncate_async<Fs, P>,
|
||||
op_utime_sync<Fs, P>,
|
||||
op_utime_async<Fs, P>,
|
||||
op_make_temp_dir_sync<Fs, P>,
|
||||
op_make_temp_dir_async<Fs, P>,
|
||||
op_make_temp_file_sync<Fs, P>,
|
||||
op_make_temp_file_async<Fs, P>,
|
||||
op_write_file_sync<Fs, P>,
|
||||
op_write_file_async<Fs, P>,
|
||||
op_read_file_sync<Fs, P>,
|
||||
op_read_file_async<Fs, P>,
|
||||
op_read_file_text_sync<Fs, P>,
|
||||
op_read_file_text_async<Fs, P>,
|
||||
op_open_sync<P>,
|
||||
op_open_async<P>,
|
||||
op_mkdir_sync<P>,
|
||||
op_mkdir_async<P>,
|
||||
op_chmod_sync<P>,
|
||||
op_chmod_async<P>,
|
||||
op_chown_sync<P>,
|
||||
op_chown_async<P>,
|
||||
op_remove_sync<P>,
|
||||
op_remove_async<P>,
|
||||
op_copy_file_sync<P>,
|
||||
op_copy_file_async<P>,
|
||||
op_stat_sync<P>,
|
||||
op_stat_async<P>,
|
||||
op_lstat_sync<P>,
|
||||
op_lstat_async<P>,
|
||||
op_realpath_sync<P>,
|
||||
op_realpath_async<P>,
|
||||
op_read_dir_sync<P>,
|
||||
op_read_dir_async<P>,
|
||||
op_rename_sync<P>,
|
||||
op_rename_async<P>,
|
||||
op_link_sync<P>,
|
||||
op_link_async<P>,
|
||||
op_symlink_sync<P>,
|
||||
op_symlink_async<P>,
|
||||
op_read_link_sync<P>,
|
||||
op_read_link_async<P>,
|
||||
op_truncate_sync<P>,
|
||||
op_truncate_async<P>,
|
||||
op_utime_sync<P>,
|
||||
op_utime_async<P>,
|
||||
op_make_temp_dir_sync<P>,
|
||||
op_make_temp_dir_async<P>,
|
||||
op_make_temp_file_sync<P>,
|
||||
op_make_temp_file_async<P>,
|
||||
op_write_file_sync<P>,
|
||||
op_write_file_async<P>,
|
||||
op_read_file_sync<P>,
|
||||
op_read_file_async<P>,
|
||||
op_read_file_text_sync<P>,
|
||||
op_read_file_text_async<P>,
|
||||
|
||||
op_seek_sync<Fs>,
|
||||
op_seek_async<Fs>,
|
||||
op_fdatasync_sync<Fs>,
|
||||
op_fdatasync_async<Fs>,
|
||||
op_fsync_sync<Fs>,
|
||||
op_fsync_async<Fs>,
|
||||
op_fstat_sync<Fs>,
|
||||
op_fstat_async<Fs>,
|
||||
op_flock_sync<Fs>,
|
||||
op_flock_async<Fs>,
|
||||
op_funlock_sync<Fs>,
|
||||
op_funlock_async<Fs>,
|
||||
op_ftruncate_sync<Fs>,
|
||||
op_ftruncate_async<Fs>,
|
||||
op_futime_sync<Fs>,
|
||||
op_futime_async<Fs>,
|
||||
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<dyn FileSystem>,
|
||||
},
|
||||
state = |state, options| {
|
||||
state.put(UnstableChecker { unstable: options.unstable });
|
||||
|
|
454
ext/fs/ops.rs
454
ext/fs/ops.rs
File diff suppressed because it is too large
Load diff
411
ext/fs/std_fs.rs
411
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<PathBuf> {
|
||||
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<Path>) -> 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: &Path,
|
||||
options: OpenOptions,
|
||||
) -> FsResult<Self::File> {
|
||||
) -> FsResult<Rc<dyn File>> {
|
||||
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<Self::File> {
|
||||
) -> FsResult<Rc<dyn File>> {
|
||||
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: &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<Path>, 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: &Path,
|
||||
uid: Option<u32>,
|
||||
gid: Option<u32>,
|
||||
) -> FsResult<()> {
|
||||
|
@ -134,68 +129,56 @@ impl FileSystem for StdFs {
|
|||
uid: Option<u32>,
|
||||
gid: Option<u32>,
|
||||
) -> 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<Path>,
|
||||
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<Path>,
|
||||
to: impl AsRef<Path>,
|
||||
) -> 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<Path>) -> FsResult<FsStat> {
|
||||
fn stat_sync(&self, path: &Path) -> FsResult<FsStat> {
|
||||
stat(path).map(Into::into)
|
||||
}
|
||||
async fn stat_async(&self, path: PathBuf) -> FsResult<FsStat> {
|
||||
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<Path>) -> FsResult<FsStat> {
|
||||
fn lstat_sync(&self, path: &Path) -> FsResult<FsStat> {
|
||||
lstat(path).map(Into::into)
|
||||
}
|
||||
async fn lstat_async(&self, path: PathBuf) -> FsResult<FsStat> {
|
||||
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<Path>) -> FsResult<PathBuf> {
|
||||
fn realpath_sync(&self, path: &Path) -> FsResult<PathBuf> {
|
||||
realpath(path)
|
||||
}
|
||||
async fn realpath_async(&self, path: PathBuf) -> FsResult<PathBuf> {
|
||||
tokio::task::spawn_blocking(move || realpath(path)).await?
|
||||
tokio::task::spawn_blocking(move || realpath(&path)).await?
|
||||
}
|
||||
|
||||
fn read_dir_sync(&self, path: impl AsRef<Path>) -> FsResult<Vec<FsDirEntry>> {
|
||||
fn read_dir_sync(&self, path: &Path) -> FsResult<Vec<FsDirEntry>> {
|
||||
read_dir(path)
|
||||
}
|
||||
async fn read_dir_async(&self, path: PathBuf) -> FsResult<Vec<FsDirEntry>> {
|
||||
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<Path>,
|
||||
newpath: impl AsRef<Path>,
|
||||
) -> 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<Path>,
|
||||
newpath: impl AsRef<Path>,
|
||||
) -> 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<Path>,
|
||||
newpath: impl AsRef<Path>,
|
||||
oldpath: &Path,
|
||||
newpath: &Path,
|
||||
file_type: Option<FsFileType>,
|
||||
) -> FsResult<()> {
|
||||
symlink(oldpath, newpath, file_type)
|
||||
|
@ -239,11 +218,11 @@ impl FileSystem for StdFs {
|
|||
newpath: PathBuf,
|
||||
file_type: Option<FsFileType>,
|
||||
) -> 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<Path>) -> FsResult<PathBuf> {
|
||||
fn read_link_sync(&self, path: &Path) -> FsResult<PathBuf> {
|
||||
fs::read_link(path).map_err(Into::into)
|
||||
}
|
||||
async fn read_link_async(&self, path: PathBuf) -> FsResult<PathBuf> {
|
||||
|
@ -252,16 +231,16 @@ impl FileSystem for StdFs {
|
|||
.map_err(Into::into)
|
||||
}
|
||||
|
||||
fn truncate_sync(&self, path: impl AsRef<Path>, 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: &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: &Path,
|
||||
options: OpenOptions,
|
||||
data: &[u8],
|
||||
) -> FsResult<()> {
|
||||
|
@ -324,7 +303,7 @@ impl FileSystem for StdFs {
|
|||
.await?
|
||||
}
|
||||
|
||||
fn read_file_sync(&self, path: impl AsRef<Path>) -> FsResult<Vec<u8>> {
|
||||
fn read_file_sync(&self, path: &Path) -> FsResult<Vec<u8>> {
|
||||
fs::read(path).map_err(Into::into)
|
||||
}
|
||||
async fn read_file_async(&self, path: PathBuf) -> FsResult<Vec<u8>> {
|
||||
|
@ -334,7 +313,7 @@ impl FileSystem for StdFs {
|
|||
}
|
||||
}
|
||||
|
||||
fn mkdir(path: impl AsRef<Path>, 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<Path>, recursive: bool, mode: u32) -> FsResult<()> {
|
|||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
fn chmod(path: impl AsRef<Path>, 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<Path>, mode: u32) -> FsResult<()> {
|
|||
|
||||
// TODO: implement chmod for Windows (#4357)
|
||||
#[cfg(not(unix))]
|
||||
fn chmod(path: impl AsRef<Path>, _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<Path>,
|
||||
uid: Option<u32>,
|
||||
gid: Option<u32>,
|
||||
) -> FsResult<()> {
|
||||
fn chown(path: &Path, uid: Option<u32>, gid: Option<u32>) -> 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<Path>,
|
||||
_uid: Option<u32>,
|
||||
_gid: Option<u32>,
|
||||
) -> FsResult<()> {
|
||||
fn chown(_path: &Path, _uid: Option<u32>, _gid: Option<u32>) -> FsResult<()> {
|
||||
Err(FsError::NotSupported)
|
||||
}
|
||||
|
||||
fn remove(path: impl AsRef<Path>, 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<Path>, to: impl AsRef<Path>) -> 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<Path>, to: impl AsRef<Path>) -> FsResult<()> {
|
|||
}
|
||||
|
||||
#[cfg(not(windows))]
|
||||
fn stat(path: impl AsRef<Path>) -> FsResult<FsStat> {
|
||||
fn stat(path: &Path) -> FsResult<FsStat> {
|
||||
let metadata = fs::metadata(path)?;
|
||||
Ok(metadata_to_fsstat(metadata))
|
||||
Ok(FsStat::from_std(metadata))
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
fn stat(path: impl AsRef<Path>) -> FsResult<FsStat> {
|
||||
let metadata = fs::metadata(path.as_ref())?;
|
||||
let mut fsstat = metadata_to_fsstat(metadata);
|
||||
fn stat(path: &Path) -> FsResult<FsStat> {
|
||||
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<Path>) -> FsResult<FsStat> {
|
||||
fn lstat(path: &Path) -> FsResult<FsStat> {
|
||||
let metadata = fs::symlink_metadata(path)?;
|
||||
Ok(metadata_to_fsstat(metadata))
|
||||
Ok(FsStat::from_std(metadata))
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
fn lstat(path: impl AsRef<Path>) -> FsResult<FsStat> {
|
||||
let metadata = fs::symlink_metadata(path.as_ref())?;
|
||||
let mut fsstat = metadata_to_fsstat(metadata);
|
||||
fn lstat(path: &Path) -> FsResult<FsStat> {
|
||||
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<SystemTime, io::Error>) -> Option<u64> {
|
||||
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<PathBuf> {
|
||||
Ok(deno_core::strip_unc_prefix(path.canonicalize()?))
|
||||
}
|
||||
|
||||
fn realpath(path: impl AsRef<Path>) -> FsResult<PathBuf> {
|
||||
Ok(deno_core::strip_unc_prefix(path.as_ref().canonicalize()?))
|
||||
}
|
||||
|
||||
fn read_dir(path: impl AsRef<Path>) -> FsResult<Vec<FsDirEntry>> {
|
||||
fn read_dir(path: &Path) -> FsResult<Vec<FsDirEntry>> {
|
||||
let entries = fs::read_dir(path)?
|
||||
.filter_map(|entry| {
|
||||
let entry = entry.ok()?;
|
||||
|
@ -679,24 +601,24 @@ fn read_dir(path: impl AsRef<Path>) -> FsResult<Vec<FsDirEntry>> {
|
|||
|
||||
#[cfg(not(windows))]
|
||||
fn symlink(
|
||||
oldpath: impl AsRef<Path>,
|
||||
newpath: impl AsRef<Path>,
|
||||
oldpath: &Path,
|
||||
newpath: &Path,
|
||||
_file_type: Option<FsFileType>,
|
||||
) -> 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<Path>,
|
||||
newpath: impl AsRef<Path>,
|
||||
oldpath: &Path,
|
||||
newpath: &Path,
|
||||
file_type: Option<FsFileType>,
|
||||
) -> 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<Path>, 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<T>(
|
||||
resource: Rc<StdFileResource>,
|
||||
f: impl FnOnce(&mut fs::File) -> io::Result<T>,
|
||||
) -> FsResult<T> {
|
||||
let res = resource
|
||||
.with_file2(|file| f(file))
|
||||
.ok_or(FsError::FileBusy)??;
|
||||
Ok(res)
|
||||
}
|
||||
|
||||
async fn nonblocking<T: Send + 'static>(
|
||||
resource: Rc<StdFileResource>,
|
||||
f: impl FnOnce(&mut fs::File) -> io::Result<T> + Send + 'static,
|
||||
) -> FsResult<T> {
|
||||
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<Self>, buf: &[u8]) -> FsResult<()> {
|
||||
sync(self, |file| file.write_all(buf))
|
||||
}
|
||||
async fn write_all_async(self: Rc<Self>, buf: Vec<u8>) -> FsResult<()> {
|
||||
nonblocking(self, move |file| file.write_all(&buf)).await
|
||||
}
|
||||
|
||||
fn read_all_sync(self: Rc<Self>) -> FsResult<Vec<u8>> {
|
||||
sync(self, |file| {
|
||||
let mut buf = Vec::new();
|
||||
file.read_to_end(&mut buf)?;
|
||||
Ok(buf)
|
||||
})
|
||||
}
|
||||
async fn read_all_async(self: Rc<Self>) -> FsResult<Vec<u8>> {
|
||||
nonblocking(self, |file| {
|
||||
let mut buf = Vec::new();
|
||||
file.read_to_end(&mut buf)?;
|
||||
Ok(buf)
|
||||
})
|
||||
.await
|
||||
}
|
||||
|
||||
fn chmod_sync(self: Rc<Self>, _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<Self>, _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<Self>, pos: io::SeekFrom) -> FsResult<u64> {
|
||||
sync(self, |file| file.seek(pos))
|
||||
}
|
||||
async fn seek_async(self: Rc<Self>, pos: io::SeekFrom) -> FsResult<u64> {
|
||||
nonblocking(self, move |file| file.seek(pos)).await
|
||||
}
|
||||
|
||||
fn datasync_sync(self: Rc<Self>) -> FsResult<()> {
|
||||
sync(self, |file| file.sync_data())
|
||||
}
|
||||
async fn datasync_async(self: Rc<Self>) -> FsResult<()> {
|
||||
nonblocking(self, |file| file.sync_data()).await
|
||||
}
|
||||
|
||||
fn sync_sync(self: Rc<Self>) -> FsResult<()> {
|
||||
sync(self, |file| file.sync_all())
|
||||
}
|
||||
async fn sync_async(self: Rc<Self>) -> FsResult<()> {
|
||||
nonblocking(self, |file| file.sync_all()).await
|
||||
}
|
||||
|
||||
fn stat_sync(self: Rc<Self>) -> FsResult<FsStat> {
|
||||
sync(self, |file| file.metadata().map(metadata_to_fsstat))
|
||||
}
|
||||
async fn stat_async(self: Rc<Self>) -> FsResult<FsStat> {
|
||||
nonblocking(self, |file| file.metadata().map(metadata_to_fsstat)).await
|
||||
}
|
||||
|
||||
fn lock_sync(self: Rc<Self>, exclusive: bool) -> FsResult<()> {
|
||||
sync(self, |file| {
|
||||
if exclusive {
|
||||
file.lock_exclusive()
|
||||
} else {
|
||||
file.lock_shared()
|
||||
}
|
||||
})
|
||||
}
|
||||
async fn lock_async(self: Rc<Self>, exclusive: bool) -> FsResult<()> {
|
||||
nonblocking(self, move |file| {
|
||||
if exclusive {
|
||||
file.lock_exclusive()
|
||||
} else {
|
||||
file.lock_shared()
|
||||
}
|
||||
})
|
||||
.await
|
||||
}
|
||||
|
||||
fn unlock_sync(self: Rc<Self>) -> FsResult<()> {
|
||||
sync(self, |file| file.unlock())
|
||||
}
|
||||
async fn unlock_async(self: Rc<Self>) -> FsResult<()> {
|
||||
nonblocking(self, |file| file.unlock()).await
|
||||
}
|
||||
|
||||
fn truncate_sync(self: Rc<Self>, len: u64) -> FsResult<()> {
|
||||
sync(self, |file| file.set_len(len))
|
||||
}
|
||||
async fn truncate_async(self: Rc<Self>, len: u64) -> FsResult<()> {
|
||||
nonblocking(self, move |file| file.set_len(len)).await
|
||||
}
|
||||
|
||||
fn utime_sync(
|
||||
self: Rc<Self>,
|
||||
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<Self>,
|
||||
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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
330
ext/io/fs.rs
Normal file
330
ext/io/fs.rs
Normal file
|
@ -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<io::Error> for FsError {
|
||||
fn from(err: io::Error) -> Self {
|
||||
Self::Io(err)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<FsError> 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<JoinError> 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<T> = Result<T, FsError>;
|
||||
|
||||
pub struct FsStat {
|
||||
pub is_file: bool,
|
||||
pub is_directory: bool,
|
||||
pub is_symlink: bool,
|
||||
pub size: u64,
|
||||
|
||||
pub mtime: Option<u64>,
|
||||
pub atime: Option<u64>,
|
||||
pub birthtime: Option<u64>,
|
||||
|
||||
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<SystemTime, io::Error>) -> Option<u64> {
|
||||
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<Self>, buf: &mut [u8]) -> FsResult<usize>;
|
||||
async fn read(self: Rc<Self>, limit: usize) -> FsResult<BufView> {
|
||||
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<Self>,
|
||||
buf: BufMutView,
|
||||
) -> FsResult<(usize, BufMutView)>;
|
||||
|
||||
fn write_sync(self: Rc<Self>, buf: &[u8]) -> FsResult<usize>;
|
||||
async fn write(
|
||||
self: Rc<Self>,
|
||||
buf: BufView,
|
||||
) -> FsResult<deno_core::WriteOutcome>;
|
||||
|
||||
fn write_all_sync(self: Rc<Self>, buf: &[u8]) -> FsResult<()>;
|
||||
async fn write_all(self: Rc<Self>, buf: BufView) -> FsResult<()>;
|
||||
|
||||
fn read_all_sync(self: Rc<Self>) -> FsResult<Vec<u8>>;
|
||||
async fn read_all_async(self: Rc<Self>) -> FsResult<Vec<u8>>;
|
||||
|
||||
fn chmod_sync(self: Rc<Self>, pathmode: u32) -> FsResult<()>;
|
||||
async fn chmod_async(self: Rc<Self>, mode: u32) -> FsResult<()>;
|
||||
|
||||
fn seek_sync(self: Rc<Self>, pos: io::SeekFrom) -> FsResult<u64>;
|
||||
async fn seek_async(self: Rc<Self>, pos: io::SeekFrom) -> FsResult<u64>;
|
||||
|
||||
fn datasync_sync(self: Rc<Self>) -> FsResult<()>;
|
||||
async fn datasync_async(self: Rc<Self>) -> FsResult<()>;
|
||||
|
||||
fn sync_sync(self: Rc<Self>) -> FsResult<()>;
|
||||
async fn sync_async(self: Rc<Self>) -> FsResult<()>;
|
||||
|
||||
fn stat_sync(self: Rc<Self>) -> FsResult<FsStat>;
|
||||
async fn stat_async(self: Rc<Self>) -> FsResult<FsStat>;
|
||||
|
||||
fn lock_sync(self: Rc<Self>, exclusive: bool) -> FsResult<()>;
|
||||
async fn lock_async(self: Rc<Self>, exclusive: bool) -> FsResult<()>;
|
||||
|
||||
fn unlock_sync(self: Rc<Self>) -> FsResult<()>;
|
||||
async fn unlock_async(self: Rc<Self>) -> FsResult<()>;
|
||||
|
||||
fn truncate_sync(self: Rc<Self>, len: u64) -> FsResult<()>;
|
||||
async fn truncate_async(self: Rc<Self>, len: u64) -> FsResult<()>;
|
||||
|
||||
fn utime_sync(
|
||||
self: Rc<Self>,
|
||||
atime_secs: i64,
|
||||
atime_nanos: u32,
|
||||
mtime_secs: i64,
|
||||
mtime_nanos: u32,
|
||||
) -> FsResult<()>;
|
||||
async fn utime_async(
|
||||
self: Rc<Self>,
|
||||
atime_secs: i64,
|
||||
atime_nanos: u32,
|
||||
mtime_secs: i64,
|
||||
mtime_nanos: u32,
|
||||
) -> FsResult<()>;
|
||||
|
||||
// lower level functionality
|
||||
fn as_stdio(self: Rc<Self>) -> FsResult<std::process::Stdio>;
|
||||
#[cfg(unix)]
|
||||
fn backing_fd(self: Rc<Self>) -> Option<std::os::unix::prelude::RawFd>;
|
||||
#[cfg(windows)]
|
||||
fn backing_fd(self: Rc<Self>) -> Option<std::os::windows::io::RawHandle>;
|
||||
fn try_clone_inner(self: Rc<Self>) -> FsResult<Rc<dyn File>>;
|
||||
}
|
||||
|
||||
pub struct FileResource {
|
||||
name: String,
|
||||
file: Rc<dyn File>,
|
||||
}
|
||||
|
||||
impl FileResource {
|
||||
pub fn new(file: Rc<dyn File>, name: String) -> Self {
|
||||
Self { name, file }
|
||||
}
|
||||
|
||||
pub fn with_resource<F, R>(
|
||||
state: &OpState,
|
||||
rid: ResourceId,
|
||||
f: F,
|
||||
) -> Result<R, AnyError>
|
||||
where
|
||||
F: FnOnce(Rc<FileResource>) -> Result<R, AnyError>,
|
||||
{
|
||||
let resource = state.resource_table.get::<FileResource>(rid)?;
|
||||
f(resource)
|
||||
}
|
||||
|
||||
pub fn get_file(
|
||||
state: &OpState,
|
||||
rid: ResourceId,
|
||||
) -> Result<Rc<dyn File>, AnyError> {
|
||||
let resource = state.resource_table.get::<FileResource>(rid)?;
|
||||
Ok(resource.file())
|
||||
}
|
||||
|
||||
pub fn with_file<F, R>(
|
||||
state: &OpState,
|
||||
rid: ResourceId,
|
||||
f: F,
|
||||
) -> Result<R, AnyError>
|
||||
where
|
||||
F: FnOnce(Rc<dyn File>) -> Result<R, AnyError>,
|
||||
{
|
||||
Self::with_resource(state, rid, |r| f(r.file.clone()))
|
||||
}
|
||||
|
||||
pub fn file(&self) -> Rc<dyn File> {
|
||||
self.file.clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl deno_core::Resource for FileResource {
|
||||
fn name(&self) -> Cow<str> {
|
||||
Cow::Borrowed(&self.name)
|
||||
}
|
||||
|
||||
fn read(
|
||||
self: Rc<Self>,
|
||||
limit: usize,
|
||||
) -> deno_core::AsyncResult<deno_core::BufView> {
|
||||
Box::pin(async move {
|
||||
self
|
||||
.file
|
||||
.clone()
|
||||
.read(limit)
|
||||
.await
|
||||
.map_err(|err| err.into())
|
||||
})
|
||||
}
|
||||
|
||||
fn read_byob(
|
||||
self: Rc<Self>,
|
||||
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<Self>,
|
||||
buf: deno_core::BufView,
|
||||
) -> deno_core::AsyncResult<deno_core::WriteOutcome> {
|
||||
Box::pin(async move {
|
||||
self.file.clone().write(buf).await.map_err(|err| err.into())
|
||||
})
|
||||
}
|
||||
|
||||
fn write_all(
|
||||
self: Rc<Self>,
|
||||
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<Self>,
|
||||
data: &mut [u8],
|
||||
) -> Result<usize, deno_core::anyhow::Error> {
|
||||
self.file.clone().read_sync(data).map_err(|err| err.into())
|
||||
}
|
||||
|
||||
fn write_sync(
|
||||
self: Rc<Self>,
|
||||
data: &[u8],
|
||||
) -> Result<usize, deno_core::anyhow::Error> {
|
||||
self.file.clone().write_sync(data).map_err(|err| err.into())
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
fn backing_fd(self: Rc<Self>) -> Option<std::os::unix::prelude::RawFd> {
|
||||
self.file.clone().backing_fd()
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
fn backing_fd(self: Rc<Self>) -> Option<std::os::windows::io::RawHandle> {
|
||||
self.file.clone().backing_fd()
|
||||
}
|
||||
}
|
737
ext/io/lib.rs
737
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<R>(&mut self, f: impl FnOnce(&mut StdFile) -> R) -> R {
|
||||
f(&mut self.file)
|
||||
}
|
||||
|
||||
pub fn try_clone(&self) -> Result<Self, std::io::Error> {
|
||||
Ok(Self {
|
||||
kind: self.kind,
|
||||
file: self.file.try_clone()?,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn write_and_maybe_flush(
|
||||
&mut self,
|
||||
buf: &[u8],
|
||||
) -> Result<usize, AnyError> {
|
||||
// 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::<std::io::Error>::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::<std::io::Error>::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<usize> {
|
||||
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<Option<StdFileResourceInner>>,
|
||||
cell: RefCell<Option<StdFile>>,
|
||||
// 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<TResult, E>(
|
||||
&self,
|
||||
action: impl FnOnce(&mut StdFileResourceInner) -> Result<TResult, E>,
|
||||
) -> Option<Result<TResult, E>> {
|
||||
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<F, R: Send + 'static>(&self, action: F) -> R
|
||||
fn with_sync<F, R>(&self, action: F) -> FsResult<R>
|
||||
where
|
||||
F: FnOnce(&mut StdFileResourceInner) -> R + Send + 'static,
|
||||
F: FnOnce(&mut StdFile) -> FsResult<R>,
|
||||
{
|
||||
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<F, R: 'static + Send>(&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<Self>,
|
||||
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<F, R: 'static + Send>(&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<Self>, buf: &[u8]) -> FsResult<usize> {
|
||||
// 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::<std::io::Error>::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<Self>, buf: &mut [u8]) -> FsResult<usize> {
|
||||
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<Self>, buf: &[u8]) -> FsResult<()> {
|
||||
match self.kind {
|
||||
StdFileResourceKind::File => {
|
||||
self.with_sync(|file| Ok(file.write_all(buf)?))
|
||||
}
|
||||
StdFileResourceKind::Stdin => {
|
||||
Err(Into::<std::io::Error>::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<Self>, 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::<std::io::Error>::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<Self>,
|
||||
view: BufView,
|
||||
) -> Result<deno_core::WriteOutcome, AnyError> {
|
||||
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<Self>, 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<Self>, buf: &mut [u8]) -> Result<usize, AnyError> {
|
||||
self
|
||||
.with_inner(|inner| inner.read(buf))
|
||||
.ok_or_else(resource_unavailable)?
|
||||
.map_err(Into::into)
|
||||
}
|
||||
|
||||
fn write_sync(self: Rc<Self>, data: &[u8]) -> Result<usize, AnyError> {
|
||||
self
|
||||
.with_inner(|inner| inner.write_and_maybe_flush(data))
|
||||
.ok_or_else(resource_unavailable)?
|
||||
}
|
||||
|
||||
fn with_resource<F, R>(
|
||||
state: &mut OpState,
|
||||
rid: ResourceId,
|
||||
f: F,
|
||||
) -> Result<R, AnyError>
|
||||
where
|
||||
F: FnOnce(Rc<StdFileResource>) -> Result<R, AnyError>,
|
||||
{
|
||||
let resource = state.resource_table.get::<StdFileResource>(rid)?;
|
||||
f(resource)
|
||||
}
|
||||
|
||||
pub fn with_file<F, R>(
|
||||
state: &mut OpState,
|
||||
rid: ResourceId,
|
||||
f: F,
|
||||
) -> Result<R, AnyError>
|
||||
where
|
||||
F: FnOnce(&mut StdFile) -> Result<R, AnyError>,
|
||||
{
|
||||
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<F, R>(self: Rc<Self>, f: F) -> Option<Result<R, io::Error>>
|
||||
where
|
||||
F: FnOnce(&mut StdFile) -> Result<R, io::Error>,
|
||||
{
|
||||
self.with_inner(move |inner| inner.with_file(f))
|
||||
}
|
||||
|
||||
pub async fn with_file_blocking_task<F, R: Send + 'static>(
|
||||
state: Rc<RefCell<OpState>>,
|
||||
rid: ResourceId,
|
||||
f: F,
|
||||
) -> Result<R, AnyError>
|
||||
where
|
||||
F: (FnOnce(&mut StdFile) -> Result<R, AnyError>) + Send + 'static,
|
||||
{
|
||||
let resource = state
|
||||
.borrow_mut()
|
||||
.resource_table
|
||||
.get::<StdFileResource>(rid)?;
|
||||
|
||||
resource
|
||||
.with_inner_blocking_task(move |inner| inner.with_file(f))
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn with_file_blocking_task2<F, R: Send + 'static>(
|
||||
self: Rc<Self>,
|
||||
f: F,
|
||||
) -> Result<R, io::Error>
|
||||
where
|
||||
F: (FnOnce(&mut StdFile) -> Result<R, io::Error>) + Send + 'static,
|
||||
{
|
||||
self
|
||||
.with_inner_blocking_task(move |inner| inner.with_file(f))
|
||||
.await
|
||||
}
|
||||
|
||||
pub fn clone_file(
|
||||
state: &mut OpState,
|
||||
rid: ResourceId,
|
||||
) -> Result<StdFile, AnyError> {
|
||||
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<std::process::Stdio, AnyError> {
|
||||
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<str> {
|
||||
self.name.as_str().into()
|
||||
}
|
||||
|
||||
fn read(self: Rc<Self>, limit: usize) -> AsyncResult<deno_core::BufView> {
|
||||
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<deno_core::WriteOutcome> {
|
||||
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::<std::io::Error>::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<Self>) -> FsResult<Vec<u8>> {
|
||||
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<Self>) -> FsResult<Vec<u8>> {
|
||||
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<Self>, _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<Self>, _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<Self>, pos: io::SeekFrom) -> FsResult<u64> {
|
||||
self.with_sync(|file| Ok(file.seek(pos)?))
|
||||
}
|
||||
async fn seek_async(self: Rc<Self>, pos: io::SeekFrom) -> FsResult<u64> {
|
||||
self
|
||||
.with_inner_blocking_task(move |file| Ok(file.seek(pos)?))
|
||||
.await
|
||||
}
|
||||
|
||||
fn datasync_sync(self: Rc<Self>) -> FsResult<()> {
|
||||
self.with_sync(|file| Ok(file.sync_data()?))
|
||||
}
|
||||
async fn datasync_async(self: Rc<Self>) -> FsResult<()> {
|
||||
self
|
||||
.with_inner_blocking_task(|file| Ok(file.sync_data()?))
|
||||
.await
|
||||
}
|
||||
|
||||
fn sync_sync(self: Rc<Self>) -> FsResult<()> {
|
||||
self.with_sync(|file| Ok(file.sync_all()?))
|
||||
}
|
||||
async fn sync_async(self: Rc<Self>) -> FsResult<()> {
|
||||
self
|
||||
.with_inner_blocking_task(|file| Ok(file.sync_all()?))
|
||||
.await
|
||||
}
|
||||
|
||||
fn stat_sync(self: Rc<Self>) -> FsResult<FsStat> {
|
||||
self.with_sync(|file| Ok(file.metadata().map(FsStat::from_std)?))
|
||||
}
|
||||
async fn stat_async(self: Rc<Self>) -> FsResult<FsStat> {
|
||||
self
|
||||
.with_inner_blocking_task(|file| {
|
||||
Ok(file.metadata().map(FsStat::from_std)?)
|
||||
})
|
||||
.await
|
||||
}
|
||||
|
||||
fn lock_sync(self: Rc<Self>, exclusive: bool) -> FsResult<()> {
|
||||
self.with_sync(|file| {
|
||||
if exclusive {
|
||||
file.lock_exclusive()?;
|
||||
} else {
|
||||
file.lock_shared()?;
|
||||
}
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
|
||||
fn read_byob(
|
||||
self: Rc<Self>,
|
||||
buf: deno_core::BufMutView,
|
||||
) -> AsyncResult<(usize, deno_core::BufMutView)> {
|
||||
Box::pin(StdFileResource::read_byob(self, buf))
|
||||
async fn lock_async(self: Rc<Self>, 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<Self>,
|
||||
view: deno_core::BufView,
|
||||
) -> AsyncResult<deno_core::WriteOutcome> {
|
||||
Box::pin(StdFileResource::write(self, view))
|
||||
fn unlock_sync(self: Rc<Self>) -> FsResult<()> {
|
||||
self.with_sync(|file| Ok(file.unlock()?))
|
||||
}
|
||||
async fn unlock_async(self: Rc<Self>) -> FsResult<()> {
|
||||
self
|
||||
.with_inner_blocking_task(|file| Ok(file.unlock()?))
|
||||
.await
|
||||
}
|
||||
|
||||
fn write_all(self: Rc<Self>, view: deno_core::BufView) -> AsyncResult<()> {
|
||||
Box::pin(StdFileResource::write_all(self, view))
|
||||
fn truncate_sync(self: Rc<Self>, len: u64) -> FsResult<()> {
|
||||
self.with_sync(|file| Ok(file.set_len(len)?))
|
||||
}
|
||||
async fn truncate_async(self: Rc<Self>, len: u64) -> FsResult<()> {
|
||||
self
|
||||
.with_inner_blocking_task(move |file| Ok(file.set_len(len)?))
|
||||
.await
|
||||
}
|
||||
|
||||
fn write_sync(
|
||||
fn utime_sync(
|
||||
self: Rc<Self>,
|
||||
data: &[u8],
|
||||
) -> Result<usize, deno_core::anyhow::Error> {
|
||||
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<Self>,
|
||||
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<Self>,
|
||||
data: &mut [u8],
|
||||
) -> Result<usize, deno_core::anyhow::Error> {
|
||||
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<Self>) -> FsResult<Rc<dyn fs::File>> {
|
||||
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<Self>) -> FsResult<std::process::Stdio> {
|
||||
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<Self>) -> Option<std::os::unix::prelude::RawFd> {
|
||||
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<Self>) -> Option<std::os::windows::io::RawHandle> {
|
||||
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())?)
|
||||
})
|
||||
}
|
||||
|
|
|
@ -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::<Permissions>(),
|
||||
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::<Permissions>(
|
||||
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
|
||||
|
|
|
@ -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<std::process::Stdio, AnyError> {
|
||||
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()?))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<FileResource>,
|
||||
) -> Result<std::os::windows::io::RawHandle, AnyError> {
|
||||
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<FileResource>,
|
||||
) -> Result<std::os::unix::prelude::RawFd, AnyError> {
|
||||
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::<TtyModeStore>().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<ConsoleSize, std::io::Error> {
|
||||
// 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<ConsoleSize, std::io::Error> {
|
||||
// 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,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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<Vec<String>>,
|
||||
pub root_cert_store_provider: Option<Arc<dyn RootCertStoreProvider>>,
|
||||
pub seed: Option<u64>,
|
||||
pub fs: Arc<dyn FileSystem>,
|
||||
pub module_loader: Rc<dyn ModuleLoader>,
|
||||
pub node_fs: Option<Arc<dyn deno_node::NodeFs>>,
|
||||
pub npm_resolver: Option<Arc<dyn deno_node::NpmResolver>>,
|
||||
|
@ -441,7 +442,7 @@ impl WebWorker {
|
|||
deno_napi::deno_napi::init_ops::<PermissionsContainer>(),
|
||||
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::<PermissionsContainer>(unstable, options.fs),
|
||||
deno_node::deno_node::init_ops::<PermissionsContainer>(
|
||||
options.npm_resolver,
|
||||
options.node_fs,
|
||||
|
|
|
@ -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<Arc<dyn RootCertStoreProvider>>,
|
||||
pub seed: Option<u64>,
|
||||
|
||||
pub fs: Arc<dyn FileSystem>,
|
||||
/// 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::<PermissionsContainer>(),
|
||||
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::<PermissionsContainer>(unstable, options.fs),
|
||||
deno_node::deno_node::init_ops::<PermissionsContainer>(
|
||||
options.npm_resolver,
|
||||
options.node_fs,
|
||||
|
|
Loading…
Reference in a new issue