mirror of
https://github.com/denoland/deno.git
synced 2025-09-26 12:19:12 +00:00
perf(fs): don't canonicalize path when opening file if --allow-all
is passed (#28716)
Fixes #28702. Super artificial benchmark: ```ts const perf = performance; async function asyncOpen() { const start = perf.now(); for (let i = 0; i < 100_000; i++) { const file = await Deno.open("./foo.txt"); file.close(); } const end = perf.now(); console.log(end - start); } function syncOpen() { const start = perf.now(); for (let i = 0; i < 100_000; i++) { const file = Deno.openSync("./foo.txt"); file.close(); } const end = perf.now(); console.log(end - start); } if (Deno.args[0]?.trim() === "async") { await asyncOpen(); } else { syncOpen(); } ``` Results (average of 10 for each): ``` deno sync 1785.59 deno-this-pr sync 491.69 deno async 1839.71 deno-this-pr async 528.78 ``` --------- Co-authored-by: David Sherret <dsherret@users.noreply.github.com>
This commit is contained in:
parent
1a171f10df
commit
c22d17824b
14 changed files with 205 additions and 134 deletions
|
@ -11,6 +11,7 @@ use std::path::PathBuf;
|
|||
|
||||
pub use deno_io::fs::FsError;
|
||||
use deno_permissions::PermissionCheckError;
|
||||
pub use interface::CheckedPath;
|
||||
|
||||
pub use crate::interface::AccessCheckCb;
|
||||
pub use crate::interface::AccessCheckFn;
|
||||
|
@ -31,12 +32,12 @@ pub use crate::sync::MaybeSync;
|
|||
pub trait FsPermissions {
|
||||
fn check_open<'a>(
|
||||
&mut self,
|
||||
resolved: bool,
|
||||
read: bool,
|
||||
write: bool,
|
||||
path: &'a Path,
|
||||
path: Cow<'a, Path>,
|
||||
api_name: &str,
|
||||
) -> Result<std::borrow::Cow<'a, Path>, FsError>;
|
||||
get_path: &'a dyn GetPath,
|
||||
) -> Result<CheckedPath<'a>, FsError>;
|
||||
#[must_use = "the resolved return value to mitigate time-of-check to time-of-use issues"]
|
||||
fn check_read(
|
||||
&mut self,
|
||||
|
@ -46,7 +47,7 @@ pub trait FsPermissions {
|
|||
#[must_use = "the resolved return value to mitigate time-of-check to time-of-use issues"]
|
||||
fn check_read_path<'a>(
|
||||
&mut self,
|
||||
path: &'a Path,
|
||||
path: Cow<'a, Path>,
|
||||
api_name: &str,
|
||||
) -> Result<Cow<'a, Path>, PermissionCheckError>;
|
||||
fn check_read_all(
|
||||
|
@ -68,7 +69,7 @@ pub trait FsPermissions {
|
|||
#[must_use = "the resolved return value to mitigate time-of-check to time-of-use issues"]
|
||||
fn check_write_path<'a>(
|
||||
&mut self,
|
||||
path: &'a Path,
|
||||
path: Cow<'a, Path>,
|
||||
api_name: &str,
|
||||
) -> Result<Cow<'a, Path>, PermissionCheckError>;
|
||||
#[must_use = "the resolved return value to mitigate time-of-check to time-of-use issues"]
|
||||
|
@ -90,56 +91,69 @@ pub trait FsPermissions {
|
|||
|
||||
fn check<'a>(
|
||||
&mut self,
|
||||
resolved: bool,
|
||||
open_options: &OpenOptions,
|
||||
path: &'a Path,
|
||||
path: Cow<'a, Path>,
|
||||
api_name: &str,
|
||||
) -> Result<std::borrow::Cow<'a, Path>, FsError> {
|
||||
get_path: &'a dyn GetPath,
|
||||
) -> Result<CheckedPath<'a>, FsError> {
|
||||
self.check_open(
|
||||
resolved,
|
||||
open_options.read,
|
||||
open_options.write || open_options.append,
|
||||
path,
|
||||
api_name,
|
||||
get_path,
|
||||
)
|
||||
}
|
||||
|
||||
fn allows_all(&self) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
impl FsPermissions for deno_permissions::PermissionsContainer {
|
||||
fn check_open<'a>(
|
||||
&mut self,
|
||||
resolved: bool,
|
||||
read: bool,
|
||||
write: bool,
|
||||
path: &'a Path,
|
||||
path: Cow<'a, Path>,
|
||||
api_name: &str,
|
||||
) -> Result<Cow<'a, Path>, FsError> {
|
||||
if resolved {
|
||||
self
|
||||
.check_special_file(path, api_name)
|
||||
.map_err(FsError::NotCapable)?;
|
||||
return Ok(Cow::Borrowed(path));
|
||||
get_path: &'a dyn GetPath,
|
||||
) -> Result<CheckedPath<'a>, FsError> {
|
||||
if self.allows_all() {
|
||||
return Ok(CheckedPath::Unresolved(path));
|
||||
}
|
||||
|
||||
let (needs_canonicalize, path) = get_path.normalized(path)?;
|
||||
// If somehow read or write aren't specified, use read
|
||||
let read = read || !write;
|
||||
let mut path: Cow<'a, Path> = Cow::Borrowed(path);
|
||||
if read {
|
||||
let resolved_path = FsPermissions::check_read_path(self, &path, api_name)
|
||||
.map_err(|_| FsError::NotCapable("read"))?;
|
||||
if let Cow::Owned(resolved_path) = resolved_path {
|
||||
path = Cow::Owned(resolved_path);
|
||||
}
|
||||
let path = if read {
|
||||
FsPermissions::check_read_path(self, path, api_name)
|
||||
.map_err(|_| FsError::NotCapable("read"))?
|
||||
} else {
|
||||
path
|
||||
};
|
||||
let path = if write {
|
||||
FsPermissions::check_write_path(self, path.clone(), api_name)
|
||||
.map_err(|_| FsError::NotCapable("write"))?
|
||||
} else {
|
||||
path
|
||||
};
|
||||
|
||||
let resolved_path = if needs_canonicalize {
|
||||
Cow::Owned(get_path.resolved(&path)?)
|
||||
} else {
|
||||
path
|
||||
};
|
||||
|
||||
self
|
||||
.check_special_file(&resolved_path, api_name)
|
||||
.map_err(FsError::NotCapable)?;
|
||||
|
||||
if needs_canonicalize {
|
||||
Ok(CheckedPath::Resolved(resolved_path))
|
||||
} else {
|
||||
Ok(CheckedPath::Unresolved(resolved_path))
|
||||
}
|
||||
if write {
|
||||
let resolved_path =
|
||||
FsPermissions::check_write_path(self, &path, api_name)
|
||||
.map_err(|_| FsError::NotCapable("write"))?;
|
||||
if let Cow::Owned(resolved_path) = resolved_path {
|
||||
path = Cow::Owned(resolved_path);
|
||||
}
|
||||
}
|
||||
Ok(path)
|
||||
}
|
||||
|
||||
fn check_read(
|
||||
|
@ -152,7 +166,7 @@ impl FsPermissions for deno_permissions::PermissionsContainer {
|
|||
|
||||
fn check_read_path<'a>(
|
||||
&mut self,
|
||||
path: &'a Path,
|
||||
path: Cow<'a, Path>,
|
||||
api_name: &str,
|
||||
) -> Result<Cow<'a, Path>, PermissionCheckError> {
|
||||
deno_permissions::PermissionsContainer::check_read_path(
|
||||
|
@ -182,7 +196,7 @@ impl FsPermissions for deno_permissions::PermissionsContainer {
|
|||
|
||||
fn check_write_path<'a>(
|
||||
&mut self,
|
||||
path: &'a Path,
|
||||
path: Cow<'a, Path>,
|
||||
api_name: &str,
|
||||
) -> Result<Cow<'a, Path>, PermissionCheckError> {
|
||||
deno_permissions::PermissionsContainer::check_write_path(
|
||||
|
@ -224,6 +238,10 @@ impl FsPermissions for deno_permissions::PermissionsContainer {
|
|||
) -> Result<(), PermissionCheckError> {
|
||||
deno_permissions::PermissionsContainer::check_write_all(self, api_name)
|
||||
}
|
||||
|
||||
fn allows_all(&self) -> bool {
|
||||
self.allows_all()
|
||||
}
|
||||
}
|
||||
|
||||
pub const UNSTABLE_FEATURE_NAME: &str = "fs";
|
||||
|
@ -305,3 +323,11 @@ deno_core::extension!(deno_fs,
|
|||
state.put(options.fs);
|
||||
},
|
||||
);
|
||||
|
||||
pub trait GetPath {
|
||||
fn normalized<'a>(
|
||||
&self,
|
||||
path: Cow<'a, Path>,
|
||||
) -> Result<(bool, Cow<'a, Path>), FsError>;
|
||||
fn resolved(&self, path: &Path) -> Result<PathBuf, FsError>;
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue