1
0
Fork 0
mirror of https://github.com/denoland/deno.git synced 2024-11-30 16:40:57 -05:00

refactor: Add "deno_fs" extension crate (#18040)

This commit factors out APIs related to file system from "runtime/"
to a separate "deno_fs" extension crate.
This commit is contained in:
Bartek Iwańczuk 2023-03-07 05:13:44 -04:00 committed by Yoshiya Hinosawa
parent c879a00cc9
commit b67df0f07b
18 changed files with 179 additions and 19 deletions

18
Cargo.lock generated
View file

@ -1115,6 +1115,23 @@ dependencies = [
"tokio",
]
[[package]]
name = "deno_fs"
version = "0.1.0"
dependencies = [
"deno_core",
"deno_crypto",
"deno_io",
"filetime",
"fs3",
"libc",
"log",
"nix",
"serde",
"tokio",
"winapi",
]
[[package]]
name = "deno_graph"
version = "0.44.3"
@ -1276,6 +1293,7 @@ dependencies = [
"deno_fetch",
"deno_ffi",
"deno_flash",
"deno_fs",
"deno_http",
"deno_io",
"deno_napi",

View file

@ -20,6 +20,7 @@ members = [
"ext/fetch",
"ext/flash",
"ext/ffi",
"ext/fs",
"ext/http",
"ext/io",
"ext/net",
@ -62,6 +63,7 @@ deno_crypto = { version = "0.105.0", path = "./ext/crypto" }
deno_fetch = { version = "0.115.0", path = "./ext/fetch" }
deno_ffi = { version = "0.78.0", path = "./ext/ffi" }
deno_flash = { version = "0.27.0", path = "./ext/flash" }
deno_fs = { version = "0.1.0", path = "./ext/fs" }
deno_http = { version = "0.86.0", path = "./ext/http" }
deno_io = { version = "0.1.0", path = "./ext/io" }
deno_net = { version = "0.83.0", path = "./ext/net" }

View file

@ -343,6 +343,7 @@ fn create_cli_snapshot(snapshot_path: PathBuf) {
false, // No --unstable.
),
deno_io::init(Default::default()),
deno_fs::init::<PermissionsContainer>(false),
deno_node::init::<PermissionsContainer>(None), // No --unstable.
deno_node::init_polyfill(),
deno_ffi::init::<PermissionsContainer>(false),

31
ext/fs/Cargo.toml Normal file
View file

@ -0,0 +1,31 @@
# Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
[package]
name = "deno_fs"
version = "0.1.0"
authors.workspace = true
edition.workspace = true
license.workspace = true
readme = "README.md"
repository.workspace = true
description = "Ops for interacting with the file system"
[lib]
path = "lib.rs"
[dependencies]
deno_core.workspace = true
deno_crypto.workspace = true
deno_io.workspace = true
filetime = "0.2.16"
fs3 = "0.5.0"
libc.workspace = true
log.workspace = true
serde.workspace = true
tokio.workspace = true
[target.'cfg(unix)'.dependencies]
nix.workspace = true
[target.'cfg(windows)'.dependencies]
winapi = { workspace = true, features = ["winbase"] }

3
ext/fs/README.md Normal file
View file

@ -0,0 +1,3 @@
# deno_fs
This crate provides ops for interacting with the file system.

View file

@ -1,10 +1,10 @@
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
// Some deserializer fields are only used on Unix and Windows build fails without it
use super::utils::into_string;
use crate::fs_util::canonicalize_path;
use deno_core::error::custom_error;
use deno_core::error::type_error;
use deno_core::error::AnyError;
use deno_core::include_js_files;
use deno_core::op;
use deno_core::CancelFuture;
use deno_core::CancelHandle;
@ -35,6 +35,70 @@ use std::rc::Rc;
use std::time::SystemTime;
use std::time::UNIX_EPOCH;
/// Similar to `std::fs::canonicalize()` but strips UNC prefixes on Windows.
fn canonicalize_path(path: &Path) -> Result<PathBuf, Error> {
let mut canonicalized_path = path.canonicalize()?;
if cfg!(windows) {
canonicalized_path = PathBuf::from(
canonicalized_path
.display()
.to_string()
.trim_start_matches("\\\\?\\"),
);
}
Ok(canonicalized_path)
}
/// A utility function to map OsStrings to Strings
fn into_string(s: std::ffi::OsString) -> Result<String, AnyError> {
s.into_string().map_err(|s| {
let message = format!("File name or path {s:?} is not valid UTF-8");
custom_error("InvalidData", message)
})
}
#[cfg(unix)]
pub fn get_nix_error_class(error: &nix::Error) -> &'static str {
match error {
nix::Error::ECHILD => "NotFound",
nix::Error::EINVAL => "TypeError",
nix::Error::ENOENT => "NotFound",
nix::Error::ENOTTY => "BadResource",
nix::Error::EPERM => "PermissionDenied",
nix::Error::ESRCH => "NotFound",
nix::Error::UnknownErrno => "Error",
&nix::Error::ENOTSUP => unreachable!(),
_ => "Error",
}
}
struct UnstableChecker {
pub unstable: bool,
}
impl UnstableChecker {
// NOTE(bartlomieju): keep in sync with `cli/program_state.rs`
pub fn check_unstable(&self, api_name: &str) {
if !self.unstable {
eprintln!(
"Unstable API '{api_name}'. The --unstable flag must be provided."
);
std::process::exit(70);
}
}
}
/// Helper for checking unstable features. Used for sync ops.
fn check_unstable(state: &OpState, api_name: &str) {
state.borrow::<UnstableChecker>().check_unstable(api_name)
}
/// Helper for checking unstable features. Used for async ops.
fn check_unstable2(state: &Rc<RefCell<OpState>>, api_name: &str) {
let state = state.borrow();
state.borrow::<UnstableChecker>().check_unstable(api_name)
}
pub trait FsPermissions {
fn check_read(&mut self, p: &Path, api_name: &str) -> Result<(), AnyError>;
fn check_read_all(&mut self, api_name: &str) -> Result<(), AnyError>;
@ -53,8 +117,13 @@ use deno_core::error::generic_error;
#[cfg(not(unix))]
use deno_core::error::not_supported;
pub fn init<P: FsPermissions + 'static>() -> Extension {
pub fn init<P: FsPermissions + 'static>(unstable: bool) -> Extension {
Extension::builder("deno_fs")
.esm(include_js_files!("30_fs.js",))
.state(move |state| {
state.put(UnstableChecker { unstable });
Ok(())
})
.ops(vec![
op_open_sync::decl::<P>(),
op_open_async::decl::<P>(),
@ -474,7 +543,7 @@ fn op_flock_sync(
exclusive: bool,
) -> Result<(), AnyError> {
use fs3::FileExt;
super::check_unstable(state, "Deno.flockSync");
check_unstable(state, "Deno.flockSync");
StdFileResource::with_file(state, rid, |std_file| {
if exclusive {
@ -493,7 +562,7 @@ async fn op_flock_async(
exclusive: bool,
) -> Result<(), AnyError> {
use fs3::FileExt;
super::check_unstable2(&state, "Deno.flock");
check_unstable2(&state, "Deno.flock");
StdFileResource::with_file_blocking_task(state, rid, move |std_file| {
if exclusive {
@ -512,7 +581,7 @@ fn op_funlock_sync(
rid: ResourceId,
) -> Result<(), AnyError> {
use fs3::FileExt;
super::check_unstable(state, "Deno.funlockSync");
check_unstable(state, "Deno.funlockSync");
StdFileResource::with_file(state, rid, |std_file| {
std_file.unlock()?;
@ -526,7 +595,7 @@ async fn op_funlock_async(
rid: ResourceId,
) -> Result<(), AnyError> {
use fs3::FileExt;
super::check_unstable2(&state, "Deno.funlock");
check_unstable2(&state, "Deno.funlock");
StdFileResource::with_file_blocking_task(state, rid, move |std_file| {
std_file.unlock()?;
@ -537,7 +606,7 @@ async fn op_funlock_async(
#[op]
fn op_umask(state: &mut OpState, mask: Option<u32>) -> Result<u32, AnyError> {
super::check_unstable(state, "Deno.umask");
check_unstable(state, "Deno.umask");
// TODO implement umask for Windows
// see https://github.com/nodejs/node/blob/master/src/node_process_methods.cc
// and https://docs.microsoft.com/fr-fr/cpp/c-runtime-library/reference/umask?view=vs-2019
@ -727,7 +796,6 @@ where
.check_write(&path, "Deno.chownSync()")?;
#[cfg(unix)]
{
use crate::errors::get_nix_error_class;
use nix::unistd::chown;
use nix::unistd::Gid;
use nix::unistd::Uid;
@ -768,7 +836,6 @@ where
tokio::task::spawn_blocking(move || {
#[cfg(unix)]
{
use crate::errors::get_nix_error_class;
use nix::unistd::chown;
use nix::unistd::Gid;
use nix::unistd::Uid;

View file

@ -323,7 +323,7 @@ pub fn init_polyfill() -> Extension {
Extension::builder(env!("CARGO_PKG_NAME"))
.esm(esm_files)
.esm_entry_point("internal:deno_node/module_all.ts")
.dependencies(vec!["deno_io"])
.dependencies(vec!["deno_io", "deno_fs"])
.ops(vec![
crypto::op_node_create_hash::decl(),
crypto::op_node_hash_update::decl(),

View file

@ -7,7 +7,7 @@
const core = globalThis.Deno.core;
import { nextTick as _nextTick } from "internal:deno_node/_next_tick.ts";
import { _exiting } from "internal:deno_node/_process/exiting.ts";
import * as fs from "internal:runtime/30_fs.js";
import * as fs from "internal:deno_fs/30_fs.js";
/** Returns the operating system CPU architecture for which the Deno binary was compiled */
export function arch(): string {

View file

@ -42,6 +42,7 @@ deno_crypto.workspace = true
deno_fetch.workspace = true
deno_ffi.workspace = true
deno_flash.workspace = true
deno_fs.workspace = true
deno_http.workspace = true
deno_io.workspace = true
deno_net.workspace = true
@ -70,6 +71,7 @@ deno_crypto.workspace = true
deno_fetch.workspace = true
deno_ffi.workspace = true
deno_flash.workspace = true
deno_fs.workspace = true
deno_http.workspace = true
deno_io.workspace = true
deno_napi.workspace = true

View file

@ -165,6 +165,41 @@ mod startup_snapshot {
}
}
impl deno_fs::FsPermissions for Permissions {
fn check_read(
&mut self,
_path: &Path,
_api_name: &str,
) -> Result<(), AnyError> {
unreachable!("snapshotting!")
}
fn check_read_blind(
&mut self,
_path: &Path,
_display: &str,
_api_name: &str,
) -> Result<(), AnyError> {
unreachable!("snapshotting!")
}
fn check_write(
&mut self,
_path: &Path,
_api_name: &str,
) -> Result<(), AnyError> {
unreachable!("snapshotting!")
}
fn check_read_all(&mut self, _api_name: &str) -> Result<(), AnyError> {
unreachable!("snapshotting!")
}
fn check_write_all(&mut self, _api_name: &str) -> Result<(), AnyError> {
unreachable!("snapshotting!")
}
}
fn create_runtime_snapshot(
snapshot_path: PathBuf,
maybe_additional_extension: Option<Extension>,
@ -191,6 +226,7 @@ mod startup_snapshot {
"deno_http",
"deno_flash",
"deno_io",
"deno_fs",
])
.esm(include_js_files!(
dir "js",
@ -200,7 +236,6 @@ mod startup_snapshot {
"10_permissions.js",
"11_workers.js",
"13_buffer.js",
"30_fs.js",
"30_os.js",
"40_fs_events.js",
"40_http.js",
@ -240,6 +275,7 @@ mod startup_snapshot {
deno_napi::init::<Permissions>(),
deno_http::init(),
deno_io::init(Default::default()),
deno_fs::init::<Permissions>(false),
deno_flash::init::<Permissions>(false), // No --unstable
runtime_extension,
// FIXME(bartlomieju): these extensions are specified last, because they

View file

@ -16,7 +16,7 @@ const {
SymbolFor,
Symbol,
} = primordials;
import { FsFile } from "internal:runtime/30_fs.js";
import { FsFile } from "internal:deno_fs/30_fs.js";
import { readAll } from "internal:deno_io/12_io.js";
import { assert, pathFromURL } from "internal:deno_web/00_infra.js";
import * as abortSignal from "internal:deno_web/03_abort_signal.js";

View file

@ -15,7 +15,7 @@ import * as version from "internal:runtime/01_version.ts";
import * as permissions from "internal:runtime/10_permissions.js";
import * as io from "internal:deno_io/12_io.js";
import * as buffer from "internal:runtime/13_buffer.js";
import * as fs from "internal:runtime/30_fs.js";
import * as fs from "internal:deno_fs/30_fs.js";
import * as os from "internal:runtime/30_os.js";
import * as fsEvents from "internal:runtime/40_fs_events.js";
import * as process from "internal:runtime/40_process.js";

View file

@ -8,6 +8,7 @@ pub use deno_crypto;
pub use deno_fetch;
pub use deno_ffi;
pub use deno_flash;
pub use deno_fs;
pub use deno_http;
pub use deno_io;
pub use deno_napi;

View file

@ -1,6 +1,5 @@
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
pub mod fs;
pub mod fs_events;
pub mod http;
pub mod os;

View file

@ -1915,7 +1915,7 @@ impl deno_websocket::WebSocketPermissions for PermissionsContainer {
}
}
impl crate::ops::fs::FsPermissions for PermissionsContainer {
impl deno_fs::FsPermissions for PermissionsContainer {
fn check_read(
&mut self,
path: &Path,

View file

@ -425,7 +425,7 @@ impl WebWorker {
),
// Extensions providing Deno.* features
ops::fs_events::init(),
ops::fs::init::<PermissionsContainer>(),
deno_fs::init::<PermissionsContainer>(unstable),
deno_io::init(options.stdio),
deno_tls::init(),
deno_net::init::<PermissionsContainer>(

View file

@ -255,7 +255,7 @@ impl MainWorker {
options.format_js_error_fn.clone(),
),
ops::fs_events::init(),
ops::fs::init::<PermissionsContainer>(),
deno_fs::init::<PermissionsContainer>(unstable),
deno_io::init(options.stdio),
deno_tls::init(),
deno_net::init::<PermissionsContainer>(