summaryrefslogtreecommitdiff
path: root/runtime
diff options
context:
space:
mode:
authorBartek IwaƄczuk <biwanczuk@gmail.com>2023-03-07 05:13:44 -0400
committerGitHub <noreply@github.com>2023-03-07 18:13:44 +0900
commitfe368b72c1dd2f2d17b7b7e5965b9e3d9ca61c35 (patch)
treeebc76fb86e3bee1baf1e336feb36a507affa7316 /runtime
parent64354f41125642d420d80cbf617dfb8ddca398e5 (diff)
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.
Diffstat (limited to 'runtime')
-rw-r--r--runtime/Cargo.toml2
-rw-r--r--runtime/build.rs38
-rw-r--r--runtime/js/30_fs.js899
-rw-r--r--runtime/js/40_process.js2
-rw-r--r--runtime/js/90_deno_ns.js2
-rw-r--r--runtime/lib.rs1
-rw-r--r--runtime/ops/fs.rs2214
-rw-r--r--runtime/ops/mod.rs1
-rw-r--r--runtime/permissions/mod.rs2
-rw-r--r--runtime/web_worker.rs2
-rw-r--r--runtime/worker.rs2
11 files changed, 45 insertions, 3120 deletions
diff --git a/runtime/Cargo.toml b/runtime/Cargo.toml
index 5a4028349..112156203 100644
--- a/runtime/Cargo.toml
+++ b/runtime/Cargo.toml
@@ -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
diff --git a/runtime/build.rs b/runtime/build.rs
index 76c0534bf..166421586 100644
--- a/runtime/build.rs
+++ b/runtime/build.rs
@@ -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
diff --git a/runtime/js/30_fs.js b/runtime/js/30_fs.js
deleted file mode 100644
index 19e7f372b..000000000
--- a/runtime/js/30_fs.js
+++ /dev/null
@@ -1,899 +0,0 @@
-// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
-
-const core = globalThis.Deno.core;
-const ops = core.ops;
-const primordials = globalThis.__bootstrap.primordials;
-const {
- ArrayPrototypeFilter,
- Date,
- DatePrototype,
- Error,
- Function,
- MathTrunc,
- ObjectEntries,
- ObjectPrototypeIsPrototypeOf,
- ObjectValues,
- SymbolAsyncIterator,
- SymbolIterator,
- Uint32Array,
-} = primordials;
-import { read, readSync, write, writeSync } from "internal:deno_io/12_io.js";
-import * as abortSignal from "internal:deno_web/03_abort_signal.js";
-import {
- readableStreamForRid,
- ReadableStreamPrototype,
- writableStreamForRid,
-} from "internal:deno_web/06_streams.js";
-import { pathFromURL } from "internal:deno_web/00_infra.js";
-
-function chmodSync(path, mode) {
- ops.op_chmod_sync(pathFromURL(path), mode);
-}
-
-async function chmod(path, mode) {
- await core.opAsync("op_chmod_async", pathFromURL(path), mode);
-}
-
-function chownSync(
- path,
- uid,
- gid,
-) {
- ops.op_chown_sync(pathFromURL(path), uid, gid);
-}
-
-async function chown(
- path,
- uid,
- gid,
-) {
- await core.opAsync(
- "op_chown_async",
- pathFromURL(path),
- uid,
- gid,
- );
-}
-
-function copyFileSync(
- fromPath,
- toPath,
-) {
- ops.op_copy_file_sync(
- pathFromURL(fromPath),
- pathFromURL(toPath),
- );
-}
-
-async function copyFile(
- fromPath,
- toPath,
-) {
- await core.opAsync(
- "op_copy_file_async",
- pathFromURL(fromPath),
- pathFromURL(toPath),
- );
-}
-
-function cwd() {
- return ops.op_cwd();
-}
-
-function chdir(directory) {
- ops.op_chdir(pathFromURL(directory));
-}
-
-function makeTempDirSync(options = {}) {
- return ops.op_make_temp_dir_sync(options);
-}
-
-function makeTempDir(options = {}) {
- return core.opAsync("op_make_temp_dir_async", options);
-}
-
-function makeTempFileSync(options = {}) {
- return ops.op_make_temp_file_sync(options);
-}
-
-function makeTempFile(options = {}) {
- return core.opAsync("op_make_temp_file_async", options);
-}
-
-function mkdirArgs(path, options) {
- const args = { path: pathFromURL(path), recursive: false };
- if (options != null) {
- if (typeof options.recursive == "boolean") {
- args.recursive = options.recursive;
- }
- if (options.mode) {
- args.mode = options.mode;
- }
- }
- return args;
-}
-
-function mkdirSync(path, options) {
- ops.op_mkdir_sync(mkdirArgs(path, options));
-}
-
-async function mkdir(
- path,
- options,
-) {
- await core.opAsync("op_mkdir_async", mkdirArgs(path, options));
-}
-
-function readDirSync(path) {
- return ops.op_read_dir_sync(pathFromURL(path))[
- SymbolIterator
- ]();
-}
-
-function readDir(path) {
- const array = core.opAsync(
- "op_read_dir_async",
- pathFromURL(path),
- );
- return {
- async *[SymbolAsyncIterator]() {
- yield* await array;
- },
- };
-}
-
-function readLinkSync(path) {
- return ops.op_read_link_sync(pathFromURL(path));
-}
-
-function readLink(path) {
- return core.opAsync("op_read_link_async", pathFromURL(path));
-}
-
-function realPathSync(path) {
- return ops.op_realpath_sync(pathFromURL(path));
-}
-
-function realPath(path) {
- return core.opAsync("op_realpath_async", pathFromURL(path));
-}
-
-function removeSync(
- path,
- options = {},
-) {
- ops.op_remove_sync(
- pathFromURL(path),
- !!options.recursive,
- );
-}
-
-async function remove(
- path,
- options = {},
-) {
- await core.opAsync(
- "op_remove_async",
- pathFromURL(path),
- !!options.recursive,
- );
-}
-
-function renameSync(oldpath, newpath) {
- ops.op_rename_sync(
- pathFromURL(oldpath),
- pathFromURL(newpath),
- );
-}
-
-async function rename(oldpath, newpath) {
- await core.opAsync(
- "op_rename_async",
- pathFromURL(oldpath),
- pathFromURL(newpath),
- );
-}
-
-// Extract the FsStat object from the encoded buffer.
-// See `runtime/ops/fs.rs` for the encoder.
-//
-// This is not a general purpose decoder. There are 4 types:
-//
-// 1. date
-// offset += 4
-// 1/0 | extra padding | high u32 | low u32
-// if date[0] == 1, new Date(u64) else null
-//
-// 2. bool
-// offset += 2
-// 1/0 | extra padding
-//
-// 3. u64
-// offset += 2
-// high u32 | low u32
-//
-// 4. ?u64 converts a zero u64 value to JS null on Windows.
-function createByteStruct(types) {
- // types can be "date", "bool" or "u64".
- // `?` prefix means optional on windows.
- let offset = 0;
- let str =
- 'const unix = Deno.build.os === "darwin" || Deno.build.os === "linux"; return {';
- const typeEntries = ObjectEntries(types);
- for (let i = 0; i < typeEntries.length; ++i) {
- let { 0: name, 1: type } = typeEntries[i];
-
- const optional = type.startsWith("?");
- if (optional) type = type.slice(1);
-
- if (type == "u64") {
- if (!optional) {
- str += `${name}: view[${offset}] + view[${offset + 1}] * 2**32,`;
- } else {
- str += `${name}: (unix ? (view[${offset}] + view[${
- offset + 1
- }] * 2**32) : (view[${offset}] + view[${
- offset + 1
- }] * 2**32) || null),`;
- }
- } else if (type == "date") {
- str += `${name}: view[${offset}] === 0 ? null : new Date(view[${
- offset + 2
- }] + view[${offset + 3}] * 2**32),`;
- offset += 2;
- } else {
- str += `${name}: !!(view[${offset}] + view[${offset + 1}] * 2**32),`;
- }
- offset += 2;
- }
- str += "};";
- // ...so you don't like eval huh? don't worry, it only executes during snapshot :)
- return [new Function("view", str), new Uint32Array(offset)];
-}
-
-const { 0: statStruct, 1: statBuf } = createByteStruct({
- isFile: "bool",
- isDirectory: "bool",
- isSymlink: "bool",
- size: "u64",
- mtime: "date",
- atime: "date",
- birthtime: "date",
- dev: "?u64",
- ino: "?u64",
- mode: "?u64",
- nlink: "?u64",
- uid: "?u64",
- gid: "?u64",
- rdev: "?u64",
- blksize: "?u64",
- blocks: "?u64",
-});
-
-function parseFileInfo(response) {
- const unix = core.build.os === "darwin" || core.build.os === "linux";
- return {
- isFile: response.isFile,
- isDirectory: response.isDirectory,
- isSymlink: response.isSymlink,
- size: response.size,
- mtime: response.mtimeSet !== null ? new Date(response.mtime) : null,
- atime: response.atimeSet !== null ? new Date(response.atime) : null,
- birthtime: response.birthtimeSet !== null
- ? new Date(response.birthtime)
- : null,
- // Only non-null if on Unix
- dev: unix ? response.dev : null,
- ino: unix ? response.ino : null,
- mode: unix ? response.mode : null,
- nlink: unix ? response.nlink : null,
- uid: unix ? response.uid : null,
- gid: unix ? response.gid : null,
- rdev: unix ? response.rdev : null,
- blksize: unix ? response.blksize : null,
- blocks: unix ? response.blocks : null,
- };
-}
-
-function fstatSync(rid) {
- ops.op_fstat_sync(rid, statBuf);
- return statStruct(statBuf);
-}
-
-async function fstat(rid) {
- return parseFileInfo(await core.opAsync("op_fstat_async", rid));
-}
-
-async function lstat(path) {
- const res = await core.opAsync("op_stat_async", {
- path: pathFromURL(path),
- lstat: true,
- });
- return parseFileInfo(res);
-}
-
-function lstatSync(path) {
- ops.op_stat_sync(
- pathFromURL(path),
- true,
- statBuf,
- );
- return statStruct(statBuf);
-}
-
-async function stat(path) {
- const res = await core.opAsync("op_stat_async", {
- path: pathFromURL(path),
- lstat: false,
- });
- return parseFileInfo(res);
-}
-
-function statSync(path) {
- ops.op_stat_sync(
- pathFromURL(path),
- false,
- statBuf,
- );
- return statStruct(statBuf);
-}
-
-function coerceLen(len) {
- if (len == null || len < 0) {
- return 0;
- }
-
- return len;
-}
-
-function ftruncateSync(rid, len) {
- ops.op_ftruncate_sync(rid, coerceLen(len));
-}
-
-async function ftruncate(rid, len) {
- await core.opAsync("op_ftruncate_async", rid, coerceLen(len));
-}
-
-function truncateSync(path, len) {
- ops.op_truncate_sync(path, coerceLen(len));
-}
-
-async function truncate(path, len) {
- await core.opAsync("op_truncate_async", path, coerceLen(len));
-}
-
-function umask(mask) {
- return ops.op_umask(mask);
-}
-
-function linkSync(oldpath, newpath) {
- ops.op_link_sync(oldpath, newpath);
-}
-
-async function link(oldpath, newpath) {
- await core.opAsync("op_link_async", oldpath, newpath);
-}
-
-function toUnixTimeFromEpoch(value) {
- if (ObjectPrototypeIsPrototypeOf(DatePrototype, value)) {
- const time = value.valueOf();
- const seconds = MathTrunc(time / 1e3);
- const nanoseconds = MathTrunc(time - (seconds * 1e3)) * 1e6;
-
- return [
- seconds,
- nanoseconds,
- ];
- }
-
- const seconds = value;
- const nanoseconds = 0;
-
- return [
- seconds,
- nanoseconds,
- ];
-}
-
-function futimeSync(
- rid,
- atime,
- mtime,
-) {
- const { 0: atimeSec, 1: atimeNsec } = toUnixTimeFromEpoch(atime);
- const { 0: mtimeSec, 1: mtimeNsec } = toUnixTimeFromEpoch(mtime);
- ops.op_futime_sync(rid, atimeSec, atimeNsec, mtimeSec, mtimeNsec);
-}
-
-async function futime(
- rid,
- atime,
- mtime,
-) {
- const { 0: atimeSec, 1: atimeNsec } = toUnixTimeFromEpoch(atime);
- const { 0: mtimeSec, 1: mtimeNsec } = toUnixTimeFromEpoch(mtime);
- await core.opAsync(
- "op_futime_async",
- rid,
- atimeSec,
- atimeNsec,
- mtimeSec,
- mtimeNsec,
- );
-}
-
-function utimeSync(
- path,
- atime,
- mtime,
-) {
- const { 0: atimeSec, 1: atimeNsec } = toUnixTimeFromEpoch(atime);
- const { 0: mtimeSec, 1: mtimeNsec } = toUnixTimeFromEpoch(mtime);
- ops.op_utime_sync(
- pathFromURL(path),
- atimeSec,
- atimeNsec,
- mtimeSec,
- mtimeNsec,
- );
-}
-
-async function utime(
- path,
- atime,
- mtime,
-) {
- const { 0: atimeSec, 1: atimeNsec } = toUnixTimeFromEpoch(atime);
- const { 0: mtimeSec, 1: mtimeNsec } = toUnixTimeFromEpoch(mtime);
- await core.opAsync(
- "op_utime_async",
- pathFromURL(path),
- atimeSec,
- atimeNsec,
- mtimeSec,
- mtimeNsec,
- );
-}
-
-function symlinkSync(
- oldpath,
- newpath,
- options,
-) {
- ops.op_symlink_sync(
- pathFromURL(oldpath),
- pathFromURL(newpath),
- options?.type,
- );
-}
-
-async function symlink(
- oldpath,
- newpath,
- options,
-) {
- await core.opAsync(
- "op_symlink_async",
- pathFromURL(oldpath),
- pathFromURL(newpath),
- options?.type,
- );
-}
-
-function fdatasyncSync(rid) {
- ops.op_fdatasync_sync(rid);
-}
-
-async function fdatasync(rid) {
- await core.opAsync("op_fdatasync_async", rid);
-}
-
-function fsyncSync(rid) {
- ops.op_fsync_sync(rid);
-}
-
-async function fsync(rid) {
- await core.opAsync("op_fsync_async", rid);
-}
-
-function flockSync(rid, exclusive) {
- ops.op_flock_sync(rid, exclusive === true);
-}
-
-async function flock(rid, exclusive) {
- await core.opAsync("op_flock_async", rid, exclusive === true);
-}
-
-function funlockSync(rid) {
- ops.op_funlock_sync(rid);
-}
-
-async function funlock(rid) {
- await core.opAsync("op_funlock_async", rid);
-}
-
-function seekSync(
- rid,
- offset,
- whence,
-) {
- return ops.op_seek_sync({ rid, offset, whence });
-}
-
-function seek(
- rid,
- offset,
- whence,
-) {
- return core.opAsync("op_seek_async", { rid, offset, whence });
-}
-
-function openSync(
- path,
- options,
-) {
- if (options) checkOpenOptions(options);
- const mode = options?.mode;
- const rid = ops.op_open_sync(
- pathFromURL(path),
- options,
- mode,
- );
-
- return new FsFile(rid);
-}
-
-async function open(
- path,
- options,
-) {
- if (options) checkOpenOptions(options);
- const mode = options?.mode;
- const rid = await core.opAsync(
- "op_open_async",
- pathFromURL(path),
- options,
- mode,
- );
-
- return new FsFile(rid);
-}
-
-function createSync(path) {
- return openSync(path, {
- read: true,
- write: true,
- truncate: true,
- create: true,
- });
-}
-
-function create(path) {
- return open(path, {
- read: true,
- write: true,
- truncate: true,
- create: true,
- });
-}
-
-class FsFile {
- #rid = 0;
-
- #readable;
- #writable;
-
- constructor(rid) {
- this.#rid = rid;
- }
-
- get rid() {
- return this.#rid;
- }
-
- write(p) {
- return write(this.rid, p);
- }
-
- writeSync(p) {
- return writeSync(this.rid, p);
- }
-
- truncate(len) {
- return ftruncate(this.rid, len);
- }
-
- truncateSync(len) {
- return ftruncateSync(this.rid, len);
- }
-
- read(p) {
- return read(this.rid, p);
- }
-
- readSync(p) {
- return readSync(this.rid, p);
- }
-
- seek(offset, whence) {
- return seek(this.rid, offset, whence);
- }
-
- seekSync(offset, whence) {
- return seekSync(this.rid, offset, whence);
- }
-
- stat() {
- return fstat(this.rid);
- }
-
- statSync() {
- return fstatSync(this.rid);
- }
-
- close() {
- core.close(this.rid);
- }
-
- get readable() {
- if (this.#readable === undefined) {
- this.#readable = readableStreamForRid(this.rid);
- }
- return this.#readable;
- }
-
- get writable() {
- if (this.#writable === undefined) {
- this.#writable = writableStreamForRid(this.rid);
- }
- return this.#writable;
- }
-}
-
-function checkOpenOptions(options) {
- if (
- ArrayPrototypeFilter(
- ObjectValues(options),
- (val) => val === true,
- ).length === 0
- ) {
- throw new Error("OpenOptions requires at least one option to be true");
- }
-
- if (options.truncate && !options.write) {
- throw new Error("'truncate' option requires 'write' option");
- }
-
- const createOrCreateNewWithoutWriteOrAppend =
- (options.create || options.createNew) &&
- !(options.write || options.append);
-
- if (createOrCreateNewWithoutWriteOrAppend) {
- throw new Error(
- "'create' or 'createNew' options require 'write' or 'append' option",
- );
- }
-}
-
-const File = FsFile;
-
-function readFileSync(path) {
- return ops.op_readfile_sync(pathFromURL(path));
-}
-
-async function readFile(path, options) {
- let cancelRid;
- let abortHandler;
- if (options?.signal) {
- options.signal.throwIfAborted();
- cancelRid = ops.op_cancel_handle();
- abortHandler = () => core.tryClose(cancelRid);
- options.signal[abortSignal.add](abortHandler);
- }
-
- try {
- const read = await core.opAsync(
- "op_readfile_async",
- pathFromURL(path),
- cancelRid,
- );
- return read;
- } finally {
- if (options?.signal) {
- options.signal[abortSignal.remove](abortHandler);
-
- // always throw the abort error when aborted
- options.signal.throwIfAborted();
- }
- }
-}
-
-function readTextFileSync(path) {
- return ops.op_readfile_text_sync(pathFromURL(path));
-}
-
-async function readTextFile(path, options) {
- let cancelRid;
- let abortHandler;
- if (options?.signal) {
- options.signal.throwIfAborted();
- cancelRid = ops.op_cancel_handle();
- abortHandler = () => core.tryClose(cancelRid);
- options.signal[abortSignal.add](abortHandler);
- }
-
- try {
- const read = await core.opAsync(
- "op_readfile_text_async",
- pathFromURL(path),
- cancelRid,
- );
- return read;
- } finally {
- if (options?.signal) {
- options.signal[abortSignal.remove](abortHandler);
-
- // always throw the abort error when aborted
- options.signal.throwIfAborted();
- }
- }
-}
-
-function writeFileSync(
- path,
- data,
- options = {},
-) {
- options.signal?.throwIfAborted();
- ops.op_write_file_sync(
- pathFromURL(path),
- options.mode,
- options.append ?? false,
- options.create ?? true,
- options.createNew ?? false,
- data,
- );
-}
-
-async function writeFile(
- path,
- data,
- options = {},
-) {
- let cancelRid;
- let abortHandler;
- if (options.signal) {
- options.signal.throwIfAborted();
- cancelRid = ops.op_cancel_handle();
- abortHandler = () => core.tryClose(cancelRid);
- options.signal[abortSignal.add](abortHandler);
- }
- try {
- if (ObjectPrototypeIsPrototypeOf(ReadableStreamPrototype, data)) {
- const file = await open(path, {
- mode: options.mode,
- append: options.append ?? false,
- create: options.create ?? true,
- createNew: options.createNew ?? false,
- write: true,
- });
- await data.pipeTo(file.writable, {
- signal: options.signal,
- });
- } else {
- await core.opAsync(
- "op_write_file_async",
- pathFromURL(path),
- options.mode,
- options.append ?? false,
- options.create ?? true,
- options.createNew ?? false,
- data,
- cancelRid,
- );
- }
- } finally {
- if (options.signal) {
- options.signal[abortSignal.remove](abortHandler);
-
- // always throw the abort error when aborted
- options.signal.throwIfAborted();
- }
- }
-}
-
-function writeTextFileSync(
- path,
- data,
- options = {},
-) {
- const encoder = new TextEncoder();
- return writeFileSync(path, encoder.encode(data), options);
-}
-
-function writeTextFile(
- path,
- data,
- options = {},
-) {
- if (ObjectPrototypeIsPrototypeOf(ReadableStreamPrototype, data)) {
- return writeFile(
- path,
- data.pipeThrough(new TextEncoderStream()),
- options,
- );
- } else {
- const encoder = new TextEncoder();
- return writeFile(path, encoder.encode(data), options);
- }
-}
-
-export {
- chdir,
- chmod,
- chmodSync,
- chown,
- chownSync,
- copyFile,
- copyFileSync,
- create,
- createSync,
- cwd,
- fdatasync,
- fdatasyncSync,
- File,
- flock,
- flockSync,
- FsFile,
- fstat,
- fstatSync,
- fsync,
- fsyncSync,
- ftruncate,
- ftruncateSync,
- funlock,
- funlockSync,
- futime,
- futimeSync,
- link,
- linkSync,
- lstat,
- lstatSync,
- makeTempDir,
- makeTempDirSync,
- makeTempFile,
- makeTempFileSync,
- mkdir,
- mkdirSync,
- open,
- openSync,
- readDir,
- readDirSync,
- readFile,
- readFileSync,
- readLink,
- readLinkSync,
- readTextFile,
- readTextFileSync,
- realPath,
- realPathSync,
- remove,
- removeSync,
- rename,
- renameSync,
- seek,
- seekSync,
- stat,
- statSync,
- symlink,
- symlinkSync,
- truncate,
- truncateSync,
- umask,
- utime,
- utimeSync,
- writeFile,
- writeFileSync,
- writeTextFile,
- writeTextFileSync,
-};
diff --git a/runtime/js/40_process.js b/runtime/js/40_process.js
index 28bb2870c..661f972df 100644
--- a/runtime/js/40_process.js
+++ b/runtime/js/40_process.js
@@ -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";
diff --git a/runtime/js/90_deno_ns.js b/runtime/js/90_deno_ns.js
index 45db05292..93d327787 100644
--- a/runtime/js/90_deno_ns.js
+++ b/runtime/js/90_deno_ns.js
@@ -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";
diff --git a/runtime/lib.rs b/runtime/lib.rs
index 6bb84698d..97034bca9 100644
--- a/runtime/lib.rs
+++ b/runtime/lib.rs
@@ -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;
diff --git a/runtime/ops/fs.rs b/runtime/ops/fs.rs
deleted file mode 100644
index 546d1d68a..000000000
--- a/runtime/ops/fs.rs
+++ /dev/null
@@ -1,2214 +0,0 @@
-// 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::op;
-use deno_core::CancelFuture;
-use deno_core::CancelHandle;
-use deno_core::Extension;
-use deno_core::OpState;
-use deno_core::ResourceId;
-use deno_core::ZeroCopyBuf;
-use deno_crypto::rand::thread_rng;
-use deno_crypto::rand::Rng;
-use deno_io::StdFileResource;
-use log::debug;
-use serde::Deserialize;
-use serde::Serialize;
-use std::borrow::Cow;
-use std::cell::RefCell;
-use std::convert::From;
-use std::env::current_dir;
-use std::env::set_current_dir;
-use std::env::temp_dir;
-use std::io;
-use std::io::Error;
-use std::io::Seek;
-use std::io::SeekFrom;
-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;
-
-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>;
- fn check_read_blind(
- &mut self,
- p: &Path,
- display: &str,
- api_name: &str,
- ) -> Result<(), AnyError>;
- fn check_write(&mut self, p: &Path, api_name: &str) -> Result<(), AnyError>;
- fn check_write_all(&mut self, api_name: &str) -> Result<(), AnyError>;
-}
-
-#[cfg(not(unix))]
-use deno_core::error::generic_error;
-#[cfg(not(unix))]
-use deno_core::error::not_supported;
-
-pub fn init<P: FsPermissions + 'static>() -> Extension {
- Extension::builder("deno_fs")
- .ops(vec![
- op_open_sync::decl::<P>(),
- op_open_async::decl::<P>(),
- op_write_file_sync::decl::<P>(),
- op_write_file_async::decl::<P>(),
- op_seek_sync::decl(),
- op_seek_async::decl(),
- op_fdatasync_sync::decl(),
- op_fdatasync_async::decl(),
- op_fsync_sync::decl(),
- op_fsync_async::decl(),
- op_fstat_sync::decl(),
- op_fstat_async::decl(),
- op_flock_sync::decl(),
- op_flock_async::decl(),
- op_funlock_sync::decl(),
- op_funlock_async::decl(),
- op_umask::decl(),
- op_chdir::decl::<P>(),
- op_mkdir_sync::decl::<P>(),
- op_mkdir_async::decl::<P>(),
- op_chmod_sync::decl::<P>(),
- op_chmod_async::decl::<P>(),
- op_chown_sync::decl::<P>(),
- op_chown_async::decl::<P>(),
- op_remove_sync::decl::<P>(),
- op_remove_async::decl::<P>(),
- op_copy_file_sync::decl::<P>(),
- op_copy_file_async::decl::<P>(),
- op_stat_sync::decl::<P>(),
- op_stat_async::decl::<P>(),
- op_realpath_sync::decl::<P>(),
- op_realpath_async::decl::<P>(),
- op_read_dir_sync::decl::<P>(),
- op_read_dir_async::decl::<P>(),
- op_rename_sync::decl::<P>(),
- op_rename_async::decl::<P>(),
- op_link_sync::decl::<P>(),
- op_link_async::decl::<P>(),
- op_symlink_sync::decl::<P>(),
- op_symlink_async::decl::<P>(),
- op_read_link_sync::decl::<P>(),
- op_read_link_async::decl::<P>(),
- op_ftruncate_sync::decl(),
- op_ftruncate_async::decl(),
- op_truncate_sync::decl::<P>(),
- op_truncate_async::decl::<P>(),
- op_make_temp_dir_sync::decl::<P>(),
- op_make_temp_dir_async::decl::<P>(),
- op_make_temp_file_sync::decl::<P>(),
- op_make_temp_file_async::decl::<P>(),
- op_cwd::decl::<P>(),
- op_futime_sync::decl(),
- op_futime_async::decl(),
- op_utime_sync::decl::<P>(),
- op_utime_async::decl::<P>(),
- op_readfile_sync::decl::<P>(),
- op_readfile_text_sync::decl::<P>(),
- op_readfile_async::decl::<P>(),
- op_readfile_text_async::decl::<P>(),
- ])
- .build()
-}
-
-fn default_err_mapper(err: Error, desc: String) -> Error {
- Error::new(err.kind(), format!("{err}, {desc}"))
-}
-
-#[derive(Deserialize, Default, Debug)]
-#[serde(rename_all = "camelCase")]
-#[serde(default)]
-pub struct OpenOptions {
- read: bool,
- write: bool,
- create: bool,
- truncate: bool,
- append: bool,
- create_new: bool,
-}
-
-#[inline]
-fn open_helper<P>(
- state: &mut OpState,
- path: &str,
- mode: Option<u32>,
- options: Option<&OpenOptions>,
- api_name: &str,
-) -> Result<(PathBuf, std::fs::OpenOptions), AnyError>
-where
- P: FsPermissions + 'static,
-{
- let path = Path::new(path).to_path_buf();
-
- let mut open_options = std::fs::OpenOptions::new();
-
- if let Some(mode) = mode {
- // mode only used if creating the file on Unix
- // if not specified, defaults to 0o666
- #[cfg(unix)]
- {
- use std::os::unix::fs::OpenOptionsExt;
- open_options.mode(mode & 0o777);
- }
- #[cfg(not(unix))]
- let _ = mode; // avoid unused warning
- }
-
- let permissions = state.borrow_mut::<P>();
-
- match options {
- None => {
- permissions.check_read(&path, api_name)?;
- open_options
- .read(true)
- .create(false)
- .write(false)
- .truncate(false)
- .append(false)
- .create_new(false);
- }
- Some(options) => {
- if options.read {
- permissions.check_read(&path, api_name)?;
- }
-
- if options.write || options.append {
- permissions.check_write(&path, api_name)?;
- }
-
- open_options
- .read(options.read)
- .create(options.create)
- .write(options.write)
- .truncate(options.truncate)
- .append(options.append)
- .create_new(options.create_new);
- }
- }
-
- Ok((path, open_options))
-}
-
-#[op]
-fn op_open_sync<P>(
- state: &mut OpState,
- path: String,
- options: Option<OpenOptions>,
- mode: Option<u32>,
-) -> Result<ResourceId, AnyError>
-where
- P: FsPermissions + 'static,
-{
- let (path, open_options) =
- open_helper::<P>(state, &path, mode, options.as_ref(), "Deno.openSync()")?;
- let std_file = open_options.open(&path).map_err(|err| {
- default_err_mapper(err, format!("open '{}'", path.display()))
- })?;
- let resource = StdFileResource::fs_file(std_file);
- let rid = state.resource_table.add(resource);
- Ok(rid)
-}
-
-#[op]
-async fn op_open_async<P>(
- state: Rc<RefCell<OpState>>,
- path: String,
- options: Option<OpenOptions>,
- mode: Option<u32>,
-) -> Result<ResourceId, AnyError>
-where
- P: FsPermissions + 'static,
-{
- let (path, open_options) = open_helper::<P>(
- &mut state.borrow_mut(),
- &path,
- mode,
- options.as_ref(),
- "Deno.open()",
- )?;
- let std_file = tokio::task::spawn_blocking(move || {
- open_options.open(&path).map_err(|err| {
- default_err_mapper(err, format!("open '{}'", path.display()))
- })
- })
- .await?;
- let resource = StdFileResource::fs_file(std_file?);
- let rid = state.borrow_mut().resource_table.add(resource);
- Ok(rid)
-}
-
-#[inline]
-fn write_open_options(
- create: bool,
- append: bool,
- create_new: bool,
-) -> OpenOptions {
- OpenOptions {
- read: false,
- write: true,
- create,
- truncate: !append,
- append,
- create_new,
- }
-}
-
-#[op]
-fn op_write_file_sync<P>(
- state: &mut OpState,
- path: String,
- mode: Option<u32>,
- append: bool,
- create: bool,
- create_new: bool,
- data: ZeroCopyBuf,
-) -> Result<(), AnyError>
-where
- P: FsPermissions + 'static,
-{
- let (path, open_options) = open_helper::<P>(
- state,
- &path,
- mode,
- Some(&write_open_options(create, append, create_new)),
- "Deno.writeFileSync()",
- )?;
- write_file(&path, open_options, mode, data)
-}
-
-#[op]
-async fn op_write_file_async<P>(
- state: Rc<RefCell<OpState>>,
- path: String,
- mode: Option<u32>,
- append: bool,
- create: bool,
- create_new: bool,
- data: ZeroCopyBuf,
- cancel_rid: Option<ResourceId>,
-) -> Result<(), AnyError>
-where
- P: FsPermissions + 'static,
-{
- let (path, open_options) = open_helper::<P>(
- &mut state.borrow_mut(),
- &path,
- mode,
- Some(&write_open_options(create, append, create_new)),
- "Deno.writeFile()",
- )?;
-
- let write_future = tokio::task::spawn_blocking(move || {
- write_file(&path, open_options, mode, data)
- });
-
- let cancel_handle = cancel_rid.and_then(|rid| {
- state
- .borrow_mut()
- .resource_table
- .get::<CancelHandle>(rid)
- .ok()
- });
-
- if let Some(cancel_handle) = cancel_handle {
- let write_future_rv = write_future.or_cancel(cancel_handle).await;
-
- if let Some(cancel_rid) = cancel_rid {
- state.borrow_mut().resource_table.close(cancel_rid).ok();
- };
-
- return write_future_rv??;
- }
-
- write_future.await?
-}
-
-fn write_file(
- path: &Path,
- open_options: std::fs::OpenOptions,
- _mode: Option<u32>,
- data: ZeroCopyBuf,
-) -> Result<(), AnyError> {
- let mut std_file = open_options.open(path).map_err(|err| {
- default_err_mapper(err, format!("open '{}'", path.display()))
- })?;
-
- // need to chmod the file if it already exists and a mode is specified
- #[cfg(unix)]
- if let Some(mode) = _mode {
- use std::os::unix::fs::PermissionsExt;
- let permissions = PermissionsExt::from_mode(mode & 0o777);
- std_file.set_permissions(permissions).map_err(|err| {
- default_err_mapper(err, format!("chmod '{}'", path.display()))
- })?;
- }
-
- std_file.write_all(&data)?;
- Ok(())
-}
-
-#[derive(Deserialize)]
-#[serde(rename_all = "camelCase")]
-pub struct SeekArgs {
- rid: ResourceId,
- offset: i64,
- whence: i32,
-}
-
-fn seek_helper(args: SeekArgs) -> Result<(u32, SeekFrom), AnyError> {
- let rid = args.rid;
- let offset = args.offset;
- let whence = args.whence as u32;
- // Translate seek mode to Rust repr.
- let seek_from = match whence {
- 0 => SeekFrom::Start(offset as u64),
- 1 => SeekFrom::Current(offset),
- 2 => SeekFrom::End(offset),
- _ => {
- return Err(type_error(format!("Invalid seek mode: {whence}")));
- }
- };
-
- Ok((rid, seek_from))
-}
-
-#[op]
-fn op_seek_sync(state: &mut OpState, args: SeekArgs) -> Result<u64, AnyError> {
- let (rid, seek_from) = seek_helper(args)?;
- StdFileResource::with_file(state, rid, |std_file| {
- std_file.seek(seek_from).map_err(AnyError::from)
- })
-}
-
-#[op]
-async fn op_seek_async(
- state: Rc<RefCell<OpState>>,
- args: SeekArgs,
-) -> Result<u64, AnyError> {
- let (rid, seek_from) = seek_helper(args)?;
-
- StdFileResource::with_file_blocking_task(state, rid, move |std_file| {
- std_file.seek(seek_from).map_err(AnyError::from)
- })
- .await
-}
-
-#[op]
-fn op_fdatasync_sync(
- state: &mut OpState,
- rid: ResourceId,
-) -> Result<(), AnyError> {
- StdFileResource::with_file(state, rid, |std_file| {
- std_file.sync_data().map_err(AnyError::from)
- })
-}
-
-#[op]
-async fn op_fdatasync_async(
- state: Rc<RefCell<OpState>>,
- rid: ResourceId,
-) -> Result<(), AnyError> {
- StdFileResource::with_file_blocking_task(state, rid, move |std_file| {
- std_file.sync_data().map_err(AnyError::from)
- })
- .await
-}
-
-#[op]
-fn op_fsync_sync(state: &mut OpState, rid: ResourceId) -> Result<(), AnyError> {
- StdFileResource::with_file(state, rid, |std_file| {
- std_file.sync_all().map_err(AnyError::from)
- })
-}
-
-#[op]
-async fn op_fsync_async(
- state: Rc<RefCell<OpState>>,
- rid: ResourceId,
-) -> Result<(), AnyError> {
- StdFileResource::with_file_blocking_task(state, rid, move |std_file| {
- std_file.sync_all().map_err(AnyError::from)
- })
- .await
-}
-
-#[op]
-fn op_fstat_sync(
- state: &mut OpState,
- rid: ResourceId,
- out_buf: &mut [u32],
-) -> Result<(), AnyError> {
- let metadata = StdFileResource::with_file(state, rid, |std_file| {
- std_file.metadata().map_err(AnyError::from)
- })?;
- let stat = get_stat(metadata);
- stat.write(out_buf);
- Ok(())
-}
-
-#[op]
-async fn op_fstat_async(
- state: Rc<RefCell<OpState>>,
- rid: ResourceId,
-) -> Result<FsStat, AnyError> {
- let metadata =
- StdFileResource::with_file_blocking_task(state, rid, move |std_file| {
- std_file.metadata().map_err(AnyError::from)
- })
- .await?;
- Ok(get_stat(metadata))
-}
-
-#[op]
-fn op_flock_sync(
- state: &mut OpState,
- rid: ResourceId,
- exclusive: bool,
-) -> Result<(), AnyError> {
- use fs3::FileExt;
- super::check_unstable(state, "Deno.flockSync");
-
- StdFileResource::with_file(state, rid, |std_file| {
- if exclusive {
- std_file.lock_exclusive()?;
- } else {
- std_file.lock_shared()?;
- }
- Ok(())
- })
-}
-
-#[op]
-async fn op_flock_async(
- state: Rc<RefCell<OpState>>,
- rid: ResourceId,
- exclusive: bool,
-) -> Result<(), AnyError> {
- use fs3::FileExt;
- super::check_unstable2(&state, "Deno.flock");
-
- StdFileResource::with_file_blocking_task(state, rid, move |std_file| {
- if exclusive {
- std_file.lock_exclusive()?;
- } else {
- std_file.lock_shared()?;
- }
- Ok(())
- })
- .await
-}
-
-#[op]
-fn op_funlock_sync(
- state: &mut OpState,
- rid: ResourceId,
-) -> Result<(), AnyError> {
- use fs3::FileExt;
- super::check_unstable(state, "Deno.funlockSync");
-
- StdFileResource::with_file(state, rid, |std_file| {
- std_file.unlock()?;
- Ok(())
- })
-}
-
-#[op]
-async fn op_funlock_async(
- state: Rc<RefCell<OpState>>,
- rid: ResourceId,
-) -> Result<(), AnyError> {
- use fs3::FileExt;
- super::check_unstable2(&state, "Deno.funlock");
-
- StdFileResource::with_file_blocking_task(state, rid, move |std_file| {
- std_file.unlock()?;
- Ok(())
- })
- .await
-}
-
-#[op]
-fn op_umask(state: &mut OpState, mask: Option<u32>) -> Result<u32, AnyError> {
- super::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
- #[cfg(not(unix))]
- {
- let _ = mask; // avoid unused warning.
- Err(not_supported())
- }
- #[cfg(unix)]
- {
- use nix::sys::stat::mode_t;
- use nix::sys::stat::umask;
- use nix::sys::stat::Mode;
- let r = if let Some(mask) = mask {
- // If mask provided, return previous.
- umask(Mode::from_bits_truncate(mask as mode_t))
- } else {
- // If no mask provided, we query the current. Requires two syscalls.
- let prev = umask(Mode::from_bits_truncate(0o777));
- let _ = umask(prev);
- prev
- };
- #[cfg(target_os = "linux")]
- {
- Ok(r.bits())
- }
- #[cfg(target_os = "macos")]
- {
- Ok(r.bits() as u32)
- }
- }
-}
-
-#[op]
-fn op_chdir<P>(state: &mut OpState, directory: &str) -> Result<(), AnyError>
-where
- P: FsPermissions + 'static,
-{
- let d = PathBuf::from(&directory);
- state.borrow_mut::<P>().check_read(&d, "Deno.chdir()")?;
- set_current_dir(&d)
- .map_err(|err| default_err_mapper(err, format!("chdir '{directory}'")))?;
- Ok(())
-}
-
-#[derive(Deserialize)]
-#[serde(rename_all = "camelCase")]
-pub struct MkdirArgs {
- path: String,
- recursive: bool,
- mode: Option<u32>,
-}
-
-#[op]
-fn op_mkdir_sync<P>(
- state: &mut OpState,
- args: MkdirArgs,
-) -> Result<(), AnyError>
-where
- P: FsPermissions + 'static,
-{
- let path = Path::new(&args.path).to_path_buf();
- let mode = args.mode.unwrap_or(0o777) & 0o777;
- state
- .borrow_mut::<P>()
- .check_write(&path, "Deno.mkdirSync()")?;
- debug!("op_mkdir {} {:o} {}", path.display(), mode, args.recursive);
- let mut builder = std::fs::DirBuilder::new();
- builder.recursive(args.recursive);
- #[cfg(unix)]
- {
- use std::os::unix::fs::DirBuilderExt;
- builder.mode(mode);
- }
- builder.create(&path).map_err(|err| {
- default_err_mapper(err, format!("mkdir '{}'", path.display()))
- })?;
- Ok(())
-}
-
-#[op]
-async fn op_mkdir_async<P>(
- state: Rc<RefCell<OpState>>,
- args: MkdirArgs,
-) -> Result<(), AnyError>
-where
- P: FsPermissions + 'static,
-{
- let path = Path::new(&args.path).to_path_buf();
- let mode = args.mode.unwrap_or(0o777) & 0o777;
-
- {
- let mut state = state.borrow_mut();
- state.borrow_mut::<P>().check_write(&path, "Deno.mkdir()")?;
- }
-
- tokio::task::spawn_blocking(move || {
- debug!("op_mkdir {} {:o} {}", path.display(), mode, args.recursive);
- let mut builder = std::fs::DirBuilder::new();
- builder.recursive(args.recursive);
- #[cfg(unix)]
- {
- use std::os::unix::fs::DirBuilderExt;
- builder.mode(mode);
- }
- builder.create(&path).map_err(|err| {
- default_err_mapper(err, format!("mkdir '{}'", path.display()))
- })?;
- Ok(())
- })
- .await
- .unwrap()
-}
-
-#[op]
-fn op_chmod_sync<P>(
- state: &mut OpState,
- path: &str,
- mode: u32,
-) -> Result<(), AnyError>
-where
- P: FsPermissions + 'static,
-{
- let path = Path::new(path);
- let mode = mode & 0o777;
-
- state
- .borrow_mut::<P>()
- .check_write(path, "Deno.chmodSync()")?;
- raw_chmod(path, mode)
-}
-
-#[op]
-async fn op_chmod_async<P>(
- state: Rc<RefCell<OpState>>,
- path: String,
- mode: u32,
-) -> Result<(), AnyError>
-where
- P: FsPermissions + 'static,
-{
- let path = Path::new(&path).to_path_buf();
- let mode = mode & 0o777;
-
- {
- let mut state = state.borrow_mut();
- state.borrow_mut::<P>().check_write(&path, "Deno.chmod()")?;
- }
-
- tokio::task::spawn_blocking(move || raw_chmod(&path, mode))
- .await
- .unwrap()
-}
-
-fn raw_chmod(path: &Path, _raw_mode: u32) -> Result<(), AnyError> {
- let err_mapper =
- |err| default_err_mapper(err, format!("chmod '{}'", path.display()));
- #[cfg(unix)]
- {
- use std::os::unix::fs::PermissionsExt;
- let permissions = PermissionsExt::from_mode(_raw_mode);
- std::fs::set_permissions(path, permissions).map_err(err_mapper)?;
- Ok(())
- }
- // TODO Implement chmod for Windows (#4357)
- #[cfg(not(unix))]
- {
- // Still check file/dir exists on Windows
- let _metadata = std::fs::metadata(path).map_err(err_mapper)?;
- Err(not_supported())
- }
-}
-
-#[op]
-fn op_chown_sync<P>(
- state: &mut OpState,
- path: &str,
- #[cfg_attr(windows, allow(unused_variables))] uid: Option<u32>,
- #[cfg_attr(windows, allow(unused_variables))] gid: Option<u32>,
-) -> Result<(), AnyError>
-where
- P: FsPermissions + 'static,
-{
- let path = Path::new(path).to_path_buf();
- state
- .borrow_mut::<P>()
- .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;
- let nix_uid = uid.map(Uid::from_raw);
- let nix_gid = gid.map(Gid::from_raw);
- chown(&path, nix_uid, nix_gid).map_err(|err| {
- custom_error(
- get_nix_error_class(&err),
- format!("{}, chown '{}'", err.desc(), path.display()),
- )
- })?;
- Ok(())
- }
- // TODO Implement chown for Windows
- #[cfg(not(unix))]
- {
- Err(generic_error("Not implemented"))
- }
-}
-
-#[op]
-async fn op_chown_async<P>(
- state: Rc<RefCell<OpState>>,
- path: String,
- #[cfg_attr(windows, allow(unused_variables))] uid: Option<u32>,
- #[cfg_attr(windows, allow(unused_variables))] gid: Option<u32>,
-) -> Result<(), AnyError>
-where
- P: FsPermissions + 'static,
-{
- let path = Path::new(&path).to_path_buf();
-
- {
- let mut state = state.borrow_mut();
- state.borrow_mut::<P>().check_write(&path, "Deno.chown()")?;
- }
-
- 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;
- let nix_uid = uid.map(Uid::from_raw);
- let nix_gid = gid.map(Gid::from_raw);
- chown(&path, nix_uid, nix_gid).map_err(|err| {
- custom_error(
- get_nix_error_class(&err),
- format!("{}, chown '{}'", err.desc(), path.display()),
- )
- })?;
- Ok(())
- }
- // TODO Implement chown for Windows
- #[cfg(not(unix))]
- Err(not_supported())
- })
- .await
- .unwrap()
-}
-
-#[op]
-fn op_remove_sync<P>(
- state: &mut OpState,
- path: &str,
- recursive: bool,
-) -> Result<(), AnyError>
-where
- P: FsPermissions + 'static,
-{
- let path = PathBuf::from(path);
-
- state
- .borrow_mut::<P>()
- .check_write(&path, "Deno.removeSync()")?;
-
- #[cfg(not(unix))]
- use std::os::windows::prelude::MetadataExt;
-
- let err_mapper =
- |err| default_err_mapper(err, format!("remove '{}'", path.display()));
- let metadata = std::fs::symlink_metadata(&path).map_err(err_mapper)?;
-
- let file_type = metadata.file_type();
- if file_type.is_file() {
- std::fs::remove_file(&path).map_err(err_mapper)?;
- } else if recursive {
- std::fs::remove_dir_all(&path).map_err(err_mapper)?;
- } else if file_type.is_symlink() {
- #[cfg(unix)]
- std::fs::remove_file(&path).map_err(err_mapper)?;
- #[cfg(not(unix))]
- {
- use winapi::um::winnt::FILE_ATTRIBUTE_DIRECTORY;
- if metadata.file_attributes() & FILE_ATTRIBUTE_DIRECTORY != 0 {
- std::fs::remove_dir(&path).map_err(err_mapper)?;
- } else {
- std::fs::remove_file(&path).map_err(err_mapper)?;
- }
- }
- } else if file_type.is_dir() {
- std::fs::remove_dir(&path).map_err(err_mapper)?;
- } else {
- // pipes, sockets, etc...
- std::fs::remove_file(&path).map_err(err_mapper)?;
- }
- Ok(())
-}
-
-#[op]
-async fn op_remove_async<P>(
- state: Rc<RefCell<OpState>>,
- path: String,
- recursive: bool,
-) -> Result<(), AnyError>
-where
- P: FsPermissions + 'static,
-{
- let path = PathBuf::from(&path);
-
- {
- let mut state = state.borrow_mut();
- state
- .borrow_mut::<P>()
- .check_write(&path, "Deno.remove()")?;
- }
-
- tokio::task::spawn_blocking(move || {
- #[cfg(not(unix))]
- use std::os::windows::prelude::MetadataExt;
- let err_mapper =
- |err| default_err_mapper(err, format!("remove '{}'", path.display()));
- let metadata = std::fs::symlink_metadata(&path).map_err(err_mapper)?;
-
- debug!("op_remove_async {} {}", path.display(), recursive);
- let file_type = metadata.file_type();
- if file_type.is_file() {
- std::fs::remove_file(&path).map_err(err_mapper)?;
- } else if recursive {
- std::fs::remove_dir_all(&path).map_err(err_mapper)?;
- } else if file_type.is_symlink() {
- #[cfg(unix)]
- std::fs::remove_file(&path).map_err(err_mapper)?;
- #[cfg(not(unix))]
- {
- use winapi::um::winnt::FILE_ATTRIBUTE_DIRECTORY;
- if metadata.file_attributes() & FILE_ATTRIBUTE_DIRECTORY != 0 {
- std::fs::remove_dir(&path).map_err(err_mapper)?;
- } else {
- std::fs::remove_file(&path).map_err(err_mapper)?;
- }
- }
- } else if file_type.is_dir() {
- std::fs::remove_dir(&path).map_err(err_mapper)?;
- } else {
- // pipes, sockets, etc...
- std::fs::remove_file(&path).map_err(err_mapper)?;
- }
- Ok(())
- })
- .await
- .unwrap()
-}
-
-#[op]
-fn op_copy_file_sync<P>(
- state: &mut OpState,
- from: &str,
- to: &str,
-) -> Result<(), AnyError>
-where
- P: FsPermissions + 'static,
-{
- let from_path = PathBuf::from(from);
- let to_path = PathBuf::from(to);
-
- let permissions = state.borrow_mut::<P>();
- permissions.check_read(&from_path, "Deno.copyFileSync()")?;
- permissions.check_write(&to_path, "Deno.copyFileSync()")?;
-
- // On *nix, Rust reports non-existent `from` as ErrorKind::InvalidInput
- // See https://github.com/rust-lang/rust/issues/54800
- // Once the issue is resolved, we should remove this workaround.
- if cfg!(unix) && !from_path.is_file() {
- return Err(custom_error(
- "NotFound",
- format!(
- "File not found, copy '{}' -> '{}'",
- from_path.display(),
- to_path.display()
- ),
- ));
- }
-
- let err_mapper = |err| {
- default_err_mapper(
- err,
- format!("copy '{}' -> '{}'", from_path.display(), to_path.display()),
- )
- };
-
- #[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;
-
- let from = CString::new(from).unwrap();
- let to = CString::new(to).unwrap();
-
- // SAFETY: `from` and `to` are valid C strings.
- // std::fs::copy does open() + fcopyfile() on macOS. We try to use
- // clonefile() instead, which is more efficient.
- unsafe {
- let mut st = std::mem::zeroed();
- let ret = stat(from.as_ptr(), &mut st);
- if ret != 0 {
- return Err(err_mapper(Error::last_os_error()).into());
- }
-
- if st.st_size > 128 * 1024 {
- // Try unlink. If it fails, we are going to try clonefile() anyway.
- let _ = unlink(to.as_ptr());
- // Matches rust stdlib behavior for io::copy.
- // https://github.com/rust-lang/rust/blob/3fdd578d72a24d4efc2fe2ad18eec3b6ba72271e/library/std/src/sys/unix/fs.rs#L1613-L1616
- if clonefile(from.as_ptr(), to.as_ptr(), 0) == 0 {
- return Ok(());
- }
- } else {
- // Do a regular copy. fcopyfile() is an overkill for < 128KB
- // files.
- let mut buf = [0u8; 128 * 1024];
- let mut from_file =
- std::fs::File::open(&from_path).map_err(err_mapper)?;
- let perm = from_file.metadata().map_err(err_mapper)?.permissions();
-
- let mut to_file = std::fs::OpenOptions::new()
- // create the file with the correct mode right away
- .mode(perm.mode())
- .write(true)
- .create(true)
- .truncate(true)
- .open(&to_path)
- .map_err(err_mapper)?;
- let writer_metadata = to_file.metadata()?;
- if writer_metadata.is_file() {
- // Set the correct file permissions, in case the file already existed.
- // Don't set the permissions on already existing non-files like
- // pipes/FIFOs or device nodes.
- to_file.set_permissions(perm)?;
- }
- loop {
- let nread = from_file.read(&mut buf).map_err(err_mapper)?;
- if nread == 0 {
- break;
- }
- to_file.write_all(&buf[..nread]).map_err(err_mapper)?;
- }
- return Ok(());
- }
- }
-
- // clonefile() failed, fall back to std::fs::copy().
- }
-
- // returns size of from as u64 (we ignore)
- std::fs::copy(&from_path, &to_path).map_err(err_mapper)?;
- Ok(())
-}
-
-#[op]
-async fn op_copy_file_async<P>(
- state: Rc<RefCell<OpState>>,
- from: String,
- to: String,
-) -> Result<(), AnyError>
-where
- P: FsPermissions + 'static,
-{
- let from = PathBuf::from(&from);
- let to = PathBuf::from(&to);
-
- {
- let mut state = state.borrow_mut();
- let permissions = state.borrow_mut::<P>();
- permissions.check_read(&from, "Deno.copyFile()")?;
- permissions.check_write(&to, "Deno.copyFile()")?;
- }
-
- tokio::task::spawn_blocking(move || {
- // On *nix, Rust reports non-existent `from` as ErrorKind::InvalidInput
- // See https://github.com/rust-lang/rust/issues/54800
- // Once the issue is resolved, we should remove this workaround.
- if cfg!(unix) && !from.is_file() {
- return Err(custom_error(
- "NotFound",
- format!(
- "File not found, copy '{}' -> '{}'",
- from.display(),
- to.display()
- ),
- ));
- }
- // returns size of from as u64 (we ignore)
- std::fs::copy(&from, &to).map_err(|err| {
- default_err_mapper(
- err,
- format!("copy '{}' -> '{}'", from.display(), to.display()),
- )
- })?;
- Ok(())
- })
- .await
- .unwrap()
-}
-
-fn to_msec(maybe_time: Result<SystemTime, io::Error>) -> (u64, bool) {
- match maybe_time {
- Ok(time) => (
- time
- .duration_since(UNIX_EPOCH)
- .map(|t| t.as_millis() as u64)
- .unwrap_or_else(|err| err.duration().as_millis() as u64),
- true,
- ),
- Err(_) => (0, false),
- }
-}
-
-macro_rules! create_struct_writer {
- (pub struct $name:ident { $($field:ident: $type:ty),* $(,)? }) => {
- impl $name {
- fn write(self, buf: &mut [u32]) {
- let mut offset = 0;
- $(
- let value = self.$field as u64;
- buf[offset] = value as u32;
- buf[offset + 1] = (value >> 32) as u32;
- #[allow(unused_assignments)]
- {
- offset += 2;
- }
- )*
- }
- }
-
- #[derive(Serialize)]
- #[serde(rename_all = "camelCase")]
- struct $name {
- $($field: $type),*
- }
- };
-}
-
-create_struct_writer! {
- pub struct FsStat {
- is_file: bool,
- is_directory: bool,
- is_symlink: bool,
- size: u64,
- // In milliseconds, like JavaScript. Available on both Unix or Windows.
- mtime_set: bool,
- mtime: u64,
- atime_set: bool,
- atime: u64,
- birthtime_set: bool,
- birthtime: u64,
- // Following are only valid under Unix.
- dev: u64,
- ino: u64,
- mode: u32,
- nlink: u64,
- uid: u32,
- gid: u32,
- rdev: u64,
- blksize: u64,
- blocks: u64,
- }
-}
-
-#[inline(always)]
-fn get_stat(metadata: std::fs::Metadata) -> FsStat {
- // Unix stat member (number types only). 0 if not on unix.
- macro_rules! usm {
- ($member:ident) => {{
- #[cfg(unix)]
- {
- metadata.$member()
- }
- #[cfg(not(unix))]
- {
- 0
- }
- }};
- }
-
- #[cfg(unix)]
- use std::os::unix::fs::MetadataExt;
- let (mtime, mtime_set) = to_msec(metadata.modified());
- let (atime, atime_set) = to_msec(metadata.accessed());
- let (birthtime, birthtime_set) = to_msec(metadata.created());
-
- FsStat {
- is_file: metadata.is_file(),
- is_directory: metadata.is_dir(),
- is_symlink: metadata.file_type().is_symlink(),
- size: metadata.len(),
- // In milliseconds, like JavaScript. Available on both Unix or Windows.
- mtime_set,
- mtime,
- atime_set,
- atime,
- birthtime_set,
- birthtime,
- // Following are only valid under Unix.
- dev: usm!(dev),
- ino: usm!(ino),
- mode: usm!(mode),
- nlink: usm!(nlink),
- uid: usm!(uid),
- gid: usm!(gid),
- rdev: usm!(rdev),
- blksize: usm!(blksize),
- blocks: usm!(blocks),
- }
-}
-
-#[derive(Deserialize)]
-#[serde(rename_all = "camelCase")]
-pub struct StatArgs {
- path: String,
- lstat: bool,
-}
-
-#[op]
-fn op_stat_sync<P>(
- state: &mut OpState,
- path: &str,
- lstat: bool,
- out_buf: &mut [u32],
-) -> Result<(), AnyError>
-where
- P: FsPermissions + 'static,
-{
- let path = PathBuf::from(path);
- state
- .borrow_mut::<P>()
- .check_read(&path, "Deno.statSync()")?;
- let err_mapper =
- |err| default_err_mapper(err, format!("stat '{}'", path.display()));
- let metadata = if lstat {
- std::fs::symlink_metadata(&path).map_err(err_mapper)?
- } else {
- std::fs::metadata(&path).map_err(err_mapper)?
- };
-
- let stat = get_stat(metadata);
- stat.write(out_buf);
-
- Ok(())
-}
-
-#[op]
-async fn op_stat_async<P>(
- state: Rc<RefCell<OpState>>,
- args: StatArgs,
-) -> Result<FsStat, AnyError>
-where
- P: FsPermissions + 'static,
-{
- let path = PathBuf::from(&args.path);
- let lstat = args.lstat;
-
- {
- let mut state = state.borrow_mut();
- state.borrow_mut::<P>().check_read(&path, "Deno.stat()")?;
- }
-
- tokio::task::spawn_blocking(move || {
- debug!("op_stat_async {} {}", path.display(), lstat);
- let err_mapper =
- |err| default_err_mapper(err, format!("stat '{}'", path.display()));
- let metadata = if lstat {
- std::fs::symlink_metadata(&path).map_err(err_mapper)?
- } else {
- std::fs::metadata(&path).map_err(err_mapper)?
- };
- Ok(get_stat(metadata))
- })
- .await
- .unwrap()
-}
-
-#[op]
-fn op_realpath_sync<P>(
- state: &mut OpState,
- path: String,
-) -> Result<String, AnyError>
-where
- P: FsPermissions + 'static,
-{
- let path = PathBuf::from(&path);
-
- let permissions = state.borrow_mut::<P>();
- permissions.check_read(&path, "Deno.realPathSync()")?;
- if path.is_relative() {
- permissions.check_read_blind(
- &current_dir()?,
- "CWD",
- "Deno.realPathSync()",
- )?;
- }
-
- debug!("op_realpath_sync {}", path.display());
- // corresponds to the realpath on Unix and
- // CreateFile and GetFinalPathNameByHandle on Windows
- let realpath = canonicalize_path(&path)?;
- let realpath_str = into_string(realpath.into_os_string())?;
- Ok(realpath_str)
-}
-
-#[op]
-async fn op_realpath_async<P>(
- state: Rc<RefCell<OpState>>,
- path: String,
-) -> Result<String, AnyError>
-where
- P: FsPermissions + 'static,
-{
- let path = PathBuf::from(&path);
-
- {
- let mut state = state.borrow_mut();
- let permissions = state.borrow_mut::<P>();
- permissions.check_read(&path, "Deno.realPath()")?;
- if path.is_relative() {
- permissions.check_read_blind(
- &current_dir()?,
- "CWD",
- "Deno.realPath()",
- )?;
- }
- }
-
- tokio::task::spawn_blocking(move || {
- debug!("op_realpath_async {}", path.display());
- // corresponds to the realpath on Unix and
- // CreateFile and GetFinalPathNameByHandle on Windows
- let realpath = canonicalize_path(&path)?;
- let realpath_str = into_string(realpath.into_os_string())?;
- Ok(realpath_str)
- })
- .await
- .unwrap()
-}
-
-#[derive(Serialize)]
-#[serde(rename_all = "camelCase")]
-pub struct DirEntry {
- name: String,
- is_file: bool,
- is_directory: bool,
- is_symlink: bool,
-}
-
-#[op]
-fn op_read_dir_sync<P>(
- state: &mut OpState,
- path: String,
-) -> Result<Vec<DirEntry>, AnyError>
-where
- P: FsPermissions + 'static,
-{
- let path = PathBuf::from(&path);
-
- state
- .borrow_mut::<P>()
- .check_read(&path, "Deno.readDirSync()")?;
-
- debug!("op_read_dir_sync {}", path.display());
-
- let entries: Vec<_> = std::fs::read_dir(&path)
- .map_err(|err| {
- default_err_mapper(err, format!("readdir '{}'", path.display()))
- })?
- .filter_map(|entry| {
- let entry = entry.unwrap();
- // Not all filenames can be encoded as UTF-8. Skip those for now.
- if let Ok(name) = into_string(entry.file_name()) {
- Some(DirEntry {
- name,
- is_file: entry
- .file_type()
- .map_or(false, |file_type| file_type.is_file()),
- is_directory: entry
- .file_type()
- .map_or(false, |file_type| file_type.is_dir()),
- is_symlink: entry
- .file_type()
- .map_or(false, |file_type| file_type.is_symlink()),
- })
- } else {
- None
- }
- })
- .collect();
-
- Ok(entries)
-}
-
-#[op]
-async fn op_read_dir_async<P>(
- state: Rc<RefCell<OpState>>,
- path: String,
-) -> Result<Vec<DirEntry>, AnyError>
-where
- P: FsPermissions + 'static,
-{
- let path = PathBuf::from(&path);
- {
- let mut state = state.borrow_mut();
- state
- .borrow_mut::<P>()
- .check_read(&path, "Deno.readDir()")?;
- }
- tokio::task::spawn_blocking(move || {
- debug!("op_read_dir_async {}", path.display());
-
- let entries: Vec<_> = std::fs::read_dir(&path)
- .map_err(|err| {
- default_err_mapper(err, format!("readdir '{}'", path.display()))
- })?
- .filter_map(|entry| {
- let entry = entry.unwrap();
- // Not all filenames can be encoded as UTF-8. Skip those for now.
- if let Ok(name) = into_string(entry.file_name()) {
- Some(DirEntry {
- name,
- is_file: entry
- .file_type()
- .map_or(false, |file_type| file_type.is_file()),
- is_directory: entry
- .file_type()
- .map_or(false, |file_type| file_type.is_dir()),
- is_symlink: entry
- .file_type()
- .map_or(false, |file_type| file_type.is_symlink()),
- })
- } else {
- None
- }
- })
- .collect();
-
- Ok(entries)
- })
- .await
- .unwrap()
-}
-
-#[op]
-fn op_rename_sync<P>(
- state: &mut OpState,
- oldpath: &str,
- newpath: &str,
-) -> Result<(), AnyError>
-where
- P: FsPermissions + 'static,
-{
- let oldpath = PathBuf::from(oldpath);
- let newpath = PathBuf::from(newpath);
-
- let permissions = state.borrow_mut::<P>();
- permissions.check_read(&oldpath, "Deno.renameSync()")?;
- permissions.check_write(&oldpath, "Deno.renameSync()")?;
- permissions.check_write(&newpath, "Deno.renameSync()")?;
-
- std::fs::rename(&oldpath, &newpath).map_err(|err| {
- default_err_mapper(
- err,
- format!("rename '{}' -> '{}'", oldpath.display(), newpath.display()),
- )
- })?;
- Ok(())
-}
-
-#[op]
-async fn op_rename_async<P>(
- state: Rc<RefCell<OpState>>,
- oldpath: String,
- newpath: String,
-) -> Result<(), AnyError>
-where
- P: FsPermissions + 'static,
-{
- let oldpath = PathBuf::from(&oldpath);
- let newpath = PathBuf::from(&newpath);
- {
- let mut state = state.borrow_mut();
- let permissions = state.borrow_mut::<P>();
- permissions.check_read(&oldpath, "Deno.rename()")?;
- permissions.check_write(&oldpath, "Deno.rename()")?;
- permissions.check_write(&newpath, "Deno.rename()")?;
- }
-
- tokio::task::spawn_blocking(move || {
- std::fs::rename(&oldpath, &newpath).map_err(|err| {
- default_err_mapper(
- err,
- format!("rename '{}' -> '{}'", oldpath.display(), newpath.display()),
- )
- })?;
- Ok(())
- })
- .await
- .unwrap()
-}
-
-#[op]
-fn op_link_sync<P>(
- state: &mut OpState,
- oldpath: &str,
- newpath: &str,
-) -> Result<(), AnyError>
-where
- P: FsPermissions + 'static,
-{
- let oldpath = PathBuf::from(oldpath);
- let newpath = PathBuf::from(newpath);
-
- let permissions = state.borrow_mut::<P>();
- permissions.check_read(&oldpath, "Deno.linkSync()")?;
- permissions.check_write(&oldpath, "Deno.linkSync()")?;
- permissions.check_read(&newpath, "Deno.linkSync()")?;
- permissions.check_write(&newpath, "Deno.linkSync()")?;
-
- std::fs::hard_link(&oldpath, &newpath).map_err(|err| {
- default_err_mapper(
- err,
- format!("link '{}' -> '{}'", oldpath.display(), newpath.display()),
- )
- })?;
- Ok(())
-}
-
-#[op]
-async fn op_link_async<P>(
- state: Rc<RefCell<OpState>>,
- oldpath: String,
- newpath: String,
-) -> Result<(), AnyError>
-where
- P: FsPermissions + 'static,
-{
- let oldpath = PathBuf::from(&oldpath);
- let newpath = PathBuf::from(&newpath);
-
- {
- let mut state = state.borrow_mut();
- let permissions = state.borrow_mut::<P>();
- permissions.check_read(&oldpath, "Deno.link()")?;
- permissions.check_write(&oldpath, "Deno.link()")?;
- permissions.check_read(&newpath, "Deno.link()")?;
- permissions.check_write(&newpath, "Deno.link()")?;
- }
-
- tokio::task::spawn_blocking(move || {
- let err_mapper = |err| {
- default_err_mapper(
- err,
- format!("link '{}' -> '{}'", oldpath.display(), newpath.display()),
- )
- };
- std::fs::hard_link(&oldpath, &newpath).map_err(err_mapper)?;
- Ok(())
- })
- .await
- .unwrap()
-}
-
-#[op]
-fn op_symlink_sync<P>(
- state: &mut OpState,
- oldpath: &str,
- newpath: &str,
- _type: Option<String>,
-) -> Result<(), AnyError>
-where
- P: FsPermissions + 'static,
-{
- let oldpath = PathBuf::from(oldpath);
- let newpath = PathBuf::from(newpath);
-
- state
- .borrow_mut::<P>()
- .check_write_all("Deno.symlinkSync()")?;
- state
- .borrow_mut::<P>()
- .check_read_all("Deno.symlinkSync()")?;
-
- let err_mapper = |err| {
- default_err_mapper(
- err,
- format!("symlink '{}' -> '{}'", oldpath.display(), newpath.display()),
- )
- };
-
- #[cfg(unix)]
- {
- use std::os::unix::fs::symlink;
- symlink(&oldpath, &newpath).map_err(err_mapper)?;
- Ok(())
- }
- #[cfg(not(unix))]
- {
- use std::os::windows::fs::symlink_dir;
- use std::os::windows::fs::symlink_file;
-
- match _type {
- Some(ty) => match ty.as_ref() {
- "file" => symlink_file(&oldpath, &newpath).map_err(err_mapper)?,
- "dir" => symlink_dir(&oldpath, &newpath).map_err(err_mapper)?,
- _ => return Err(type_error("unsupported type")),
- },
- None => {
- let old_meta = std::fs::metadata(&oldpath);
- match old_meta {
- Ok(metadata) => {
- if metadata.is_file() {
- symlink_file(&oldpath, &newpath).map_err(err_mapper)?
- } else if metadata.is_dir() {
- symlink_dir(&oldpath, &newpath).map_err(err_mapper)?
- }
- }
- Err(_) => return Err(type_error("you must pass a `options` argument for non-existent target path in windows".to_string())),
- }
- }
- };
- Ok(())
- }
-}
-
-#[op]
-async fn op_symlink_async<P>(
- state: Rc<RefCell<OpState>>,
- oldpath: String,
- newpath: String,
- _type: Option<String>,
-) -> Result<(), AnyError>
-where
- P: FsPermissions + 'static,
-{
- let oldpath = PathBuf::from(&oldpath);
- let newpath = PathBuf::from(&newpath);
-
- {
- let mut state = state.borrow_mut();
- state.borrow_mut::<P>().check_write_all("Deno.symlink()")?;
- state.borrow_mut::<P>().check_read_all("Deno.symlink()")?;
- }
-
- tokio::task::spawn_blocking(move || {
- let err_mapper = |err| default_err_mapper(err, format!(
- "symlink '{}' -> '{}'",
- oldpath.display(),
- newpath.display()
- ));
-
- #[cfg(unix)]
- {
- use std::os::unix::fs::symlink;
- symlink(&oldpath, &newpath).map_err(err_mapper)?;
- Ok(())
- }
- #[cfg(not(unix))]
- {
- use std::os::windows::fs::{symlink_dir, symlink_file};
-
- match _type {
- Some(ty) => match ty.as_ref() {
- "file" => symlink_file(&oldpath, &newpath).map_err(err_mapper)?,
- "dir" => symlink_dir(&oldpath, &newpath).map_err(err_mapper)?,
- _ => return Err(type_error("unsupported type")),
- },
- None => {
- let old_meta = std::fs::metadata(&oldpath);
- match old_meta {
- Ok(metadata) => {
- if metadata.is_file() {
- symlink_file(&oldpath, &newpath).map_err(err_mapper)?
- } else if metadata.is_dir() {
- symlink_dir(&oldpath, &newpath).map_err(err_mapper)?
- }
- }
- Err(_) => return Err(type_error("you must pass a `options` argument for non-existent target path in windows".to_string())),
- }
- }
- };
- Ok(())
- }
- })
- .await
- .unwrap()
-}
-
-#[op]
-fn op_read_link_sync<P>(
- state: &mut OpState,
- path: String,
-) -> Result<String, AnyError>
-where
- P: FsPermissions + 'static,
-{
- let path = PathBuf::from(&path);
-
- state
- .borrow_mut::<P>()
- .check_read(&path, "Deno.readLink()")?;
-
- debug!("op_read_link_value {}", path.display());
- let target = std::fs::read_link(&path)
- .map_err(|err| {
- default_err_mapper(err, format!("readlink '{}'", path.display()))
- })?
- .into_os_string();
- let targetstr = into_string(target)?;
- Ok(targetstr)
-}
-
-#[op]
-async fn op_read_link_async<P>(
- state: Rc<RefCell<OpState>>,
- path: String,
-) -> Result<String, AnyError>
-where
- P: FsPermissions + 'static,
-{
- let path = PathBuf::from(&path);
- {
- let mut state = state.borrow_mut();
- state
- .borrow_mut::<P>()
- .check_read(&path, "Deno.readLink()")?;
- }
- tokio::task::spawn_blocking(move || {
- debug!("op_read_link_async {}", path.display());
- let target = std::fs::read_link(&path)
- .map_err(|err| {
- default_err_mapper(err, format!("readlink '{}'", path.display()))
- })?
- .into_os_string();
- let targetstr = into_string(target)?;
- Ok(targetstr)
- })
- .await
- .unwrap()
-}
-
-#[op]
-fn op_ftruncate_sync(
- state: &mut OpState,
- rid: u32,
- len: i32,
-) -> Result<(), AnyError> {
- let len = len as u64;
- StdFileResource::with_file(state, rid, |std_file| {
- std_file.set_len(len).map_err(AnyError::from)
- })?;
- Ok(())
-}
-
-#[op]
-async fn op_ftruncate_async(
- state: Rc<RefCell<OpState>>,
- rid: ResourceId,
- len: i32,
-) -> Result<(), AnyError> {
- let len = len as u64;
-
- StdFileResource::with_file_blocking_task(state, rid, move |std_file| {
- std_file.set_len(len)?;
- Ok(())
- })
- .await
-}
-
-#[op]
-fn op_truncate_sync<P>(
- state: &mut OpState,
- path: &str,
- len: u64,
-) -> Result<(), AnyError>
-where
- P: FsPermissions + 'static,
-{
- let path = PathBuf::from(path);
-
- state
- .borrow_mut::<P>()
- .check_write(&path, "Deno.truncateSync()")?;
-
- debug!("op_truncate_sync {} {}", path.display(), len);
- let err_mapper =
- |err| default_err_mapper(err, format!("truncate '{}'", path.display()));
- let f = std::fs::OpenOptions::new()
- .write(true)
- .open(&path)
- .map_err(err_mapper)?;
- f.set_len(len).map_err(err_mapper)?;
- Ok(())
-}
-
-#[op]
-async fn op_truncate_async<P>(
- state: Rc<RefCell<OpState>>,
- path: String,
- len: u64,
-) -> Result<(), AnyError>
-where
- P: FsPermissions + 'static,
-{
- let path = PathBuf::from(&path);
-
- {
- let mut state = state.borrow_mut();
- state
- .borrow_mut::<P>()
- .check_write(&path, "Deno.truncate()")?;
- }
- tokio::task::spawn_blocking(move || {
- debug!("op_truncate_async {} {}", path.display(), len);
- let err_mapper =
- |err| default_err_mapper(err, format!("truncate '{}'", path.display()));
- let f = std::fs::OpenOptions::new()
- .write(true)
- .open(&path)
- .map_err(err_mapper)?;
- f.set_len(len).map_err(err_mapper)?;
- Ok(())
- })
- .await
- .unwrap()
-}
-
-fn make_temp(
- dir: Option<&Path>,
- prefix: Option<&str>,
- suffix: Option<&str>,
- is_dir: bool,
-) -> std::io::Result<PathBuf> {
- let prefix_ = prefix.unwrap_or("");
- let suffix_ = suffix.unwrap_or("");
- let mut buf: PathBuf = match dir {
- Some(p) => p.to_path_buf(),
- None => temp_dir(),
- }
- .join("_");
- let mut rng = thread_rng();
- loop {
- let unique = rng.gen::<u32>();
- buf.set_file_name(format!("{prefix_}{unique:08x}{suffix_}"));
- let r = if is_dir {
- #[allow(unused_mut)]
- let mut builder = std::fs::DirBuilder::new();
- #[cfg(unix)]
- {
- use std::os::unix::fs::DirBuilderExt;
- builder.mode(0o700);
- }
- builder.create(buf.as_path())
- } else {
- let mut open_options = std::fs::OpenOptions::new();
- open_options.write(true).create_new(true);
- #[cfg(unix)]
- {
- use std::os::unix::fs::OpenOptionsExt;
- open_options.mode(0o600);
- }
- open_options.open(buf.as_path())?;
- Ok(())
- };
- match r {
- Err(ref e) if e.kind() == std::io::ErrorKind::AlreadyExists => continue,
- Ok(_) => return Ok(buf),
- Err(e) => return Err(e),
- }
- }
-}
-
-#[derive(Deserialize)]
-#[serde(rename_all = "camelCase")]
-pub struct MakeTempArgs {
- dir: Option<String>,
- prefix: Option<String>,
- suffix: Option<String>,
-}
-
-#[op]
-fn op_make_temp_dir_sync<P>(
- state: &mut OpState,
- args: MakeTempArgs,
-) -> Result<String, AnyError>
-where
- P: FsPermissions + 'static,
-{
- let dir = args.dir.map(|s| PathBuf::from(&s));
- let prefix = args.prefix.map(String::from);
- let suffix = args.suffix.map(String::from);
-
- state.borrow_mut::<P>().check_write(
- dir.clone().unwrap_or_else(temp_dir).as_path(),
- "Deno.makeTempDirSync()",
- )?;
-
- // TODO(piscisaureus): use byte vector for paths, not a string.
- // See https://github.com/denoland/deno/issues/627.
- // We can't assume that paths are always valid utf8 strings.
- let path = make_temp(
- // Converting Option<String> to Option<&str>
- dir.as_deref(),
- prefix.as_deref(),
- suffix.as_deref(),
- true,
- )?;
- let path_str = into_string(path.into_os_string())?;
-
- Ok(path_str)
-}
-
-#[op]
-async fn op_make_temp_dir_async<P>(
- state: Rc<RefCell<OpState>>,
- args: MakeTempArgs,
-) -> Result<String, AnyError>
-where
- P: FsPermissions + 'static,
-{
- let dir = args.dir.map(|s| PathBuf::from(&s));
- let prefix = args.prefix.map(String::from);
- let suffix = args.suffix.map(String::from);
- {
- let mut state = state.borrow_mut();
- state.borrow_mut::<P>().check_write(
- dir.clone().unwrap_or_else(temp_dir).as_path(),
- "Deno.makeTempDir()",
- )?;
- }
- tokio::task::spawn_blocking(move || {
- // TODO(piscisaureus): use byte vector for paths, not a string.
- // See https://github.com/denoland/deno/issues/627.
- // We can't assume that paths are always valid utf8 strings.
- let path = make_temp(
- // Converting Option<String> to Option<&str>
- dir.as_deref(),
- prefix.as_deref(),
- suffix.as_deref(),
- true,
- )?;
- let path_str = into_string(path.into_os_string())?;
-
- Ok(path_str)
- })
- .await
- .unwrap()
-}
-
-#[op]
-fn op_make_temp_file_sync<P>(
- state: &mut OpState,
- args: MakeTempArgs,
-) -> Result<String, AnyError>
-where
- P: FsPermissions + 'static,
-{
- let dir = args.dir.map(|s| PathBuf::from(&s));
- let prefix = args.prefix.map(String::from);
- let suffix = args.suffix.map(String::from);
-
- state.borrow_mut::<P>().check_write(
- dir.clone().unwrap_or_else(temp_dir).as_path(),
- "Deno.makeTempFileSync()",
- )?;
-
- // TODO(piscisaureus): use byte vector for paths, not a string.
- // See https://github.com/denoland/deno/issues/627.
- // We can't assume that paths are always valid utf8 strings.
- let path = make_temp(
- // Converting Option<String> to Option<&str>
- dir.as_deref(),
- prefix.as_deref(),
- suffix.as_deref(),
- false,
- )?;
- let path_str = into_string(path.into_os_string())?;
-
- Ok(path_str)
-}
-
-#[op]
-async fn op_make_temp_file_async<P>(
- state: Rc<RefCell<OpState>>,
- args: MakeTempArgs,
-) -> Result<String, AnyError>
-where
- P: FsPermissions + 'static,
-{
- let dir = args.dir.map(|s| PathBuf::from(&s));
- let prefix = args.prefix.map(String::from);
- let suffix = args.suffix.map(String::from);
- {
- let mut state = state.borrow_mut();
- state.borrow_mut::<P>().check_write(
- dir.clone().unwrap_or_else(temp_dir).as_path(),
- "Deno.makeTempFile()",
- )?;
- }
- tokio::task::spawn_blocking(move || {
- // TODO(piscisaureus): use byte vector for paths, not a string.
- // See https://github.com/denoland/deno/issues/627.
- // We can't assume that paths are always valid utf8 strings.
- let path = make_temp(
- // Converting Option<String> to Option<&str>
- dir.as_deref(),
- prefix.as_deref(),
- suffix.as_deref(),
- false,
- )?;
- let path_str = into_string(path.into_os_string())?;
-
- Ok(path_str)
- })
- .await
- .unwrap()
-}
-
-#[op]
-fn op_futime_sync(
- state: &mut OpState,
- rid: ResourceId,
- atime_secs: i64,
- atime_nanos: u32,
- mtime_secs: i64,
- mtime_nanos: u32,
-) -> Result<(), AnyError> {
- let atime = filetime::FileTime::from_unix_time(atime_secs, atime_nanos);
- let mtime = filetime::FileTime::from_unix_time(mtime_secs, mtime_nanos);
-
- StdFileResource::with_file(state, rid, |std_file| {
- filetime::set_file_handle_times(std_file, Some(atime), Some(mtime))
- .map_err(AnyError::from)
- })?;
-
- Ok(())
-}
-
-#[op]
-async fn op_futime_async(
- state: Rc<RefCell<OpState>>,
- rid: ResourceId,
- atime_secs: i64,
- atime_nanos: u32,
- mtime_secs: i64,
- mtime_nanos: u32,
-) -> Result<(), AnyError> {
- let atime = filetime::FileTime::from_unix_time(atime_secs, atime_nanos);
- let mtime = filetime::FileTime::from_unix_time(mtime_secs, mtime_nanos);
-
- StdFileResource::with_file_blocking_task(state, rid, move |std_file| {
- filetime::set_file_handle_times(std_file, Some(atime), Some(mtime))?;
- Ok(())
- })
- .await
-}
-
-#[op]
-fn op_utime_sync<P>(
- state: &mut OpState,
- path: &str,
- atime_secs: i64,
- atime_nanos: u32,
- mtime_secs: i64,
- mtime_nanos: u32,
-) -> Result<(), AnyError>
-where
- P: FsPermissions + 'static,
-{
- let path = PathBuf::from(path);
- let atime = filetime::FileTime::from_unix_time(atime_secs, atime_nanos);
- let mtime = filetime::FileTime::from_unix_time(mtime_secs, mtime_nanos);
-
- state.borrow_mut::<P>().check_write(&path, "Deno.utime()")?;
- filetime::set_file_times(&path, atime, mtime).map_err(|err| {
- default_err_mapper(err, format!("utime '{}'", path.display()))
- })?;
- Ok(())
-}
-
-#[op]
-async fn op_utime_async<P>(
- state: Rc<RefCell<OpState>>,
- path: String,
- atime_secs: i64,
- atime_nanos: u32,
- mtime_secs: i64,
- mtime_nanos: u32,
-) -> Result<(), AnyError>
-where
- P: FsPermissions + 'static,
-{
- let path = PathBuf::from(&path);
- let atime = filetime::FileTime::from_unix_time(atime_secs, atime_nanos);
- let mtime = filetime::FileTime::from_unix_time(mtime_secs, mtime_nanos);
-
- state
- .borrow_mut()
- .borrow_mut::<P>()
- .check_write(&path, "Deno.utime()")?;
-
- tokio::task::spawn_blocking(move || {
- filetime::set_file_times(&path, atime, mtime).map_err(|err| {
- default_err_mapper(err, format!("utime '{}'", path.display()))
- })?;
- Ok(())
- })
- .await
- .unwrap()
-}
-
-#[op]
-fn op_cwd<P>(state: &mut OpState) -> Result<String, AnyError>
-where
- P: FsPermissions + 'static,
-{
- let path = current_dir()?;
- state
- .borrow_mut::<P>()
- .check_read_blind(&path, "CWD", "Deno.cwd()")?;
- let path_str = into_string(path.into_os_string())?;
- Ok(path_str)
-}
-
-#[op]
-fn op_readfile_sync<P>(
- state: &mut OpState,
- path: String,
-) -> Result<ZeroCopyBuf, AnyError>
-where
- P: FsPermissions + 'static,
-{
- let path = Path::new(&path);
- state
- .borrow_mut::<P>()
- .check_read(path, "Deno.readFileSync()")?;
- Ok(
- std::fs::read(path)
- .map_err(|err| {
- default_err_mapper(err, format!("readfile '{}'", path.display()))
- })?
- .into(),
- )
-}
-
-#[op]
-fn op_readfile_text_sync<P>(
- state: &mut OpState,
- path: String,
-) -> Result<String, AnyError>
-where
- P: FsPermissions + 'static,
-{
- let path = Path::new(&path);
- state
- .borrow_mut::<P>()
- .check_read(path, "Deno.readTextFileSync()")?;
- Ok(string_from_utf8_lossy(std::fs::read(path).map_err(
- |err| default_err_mapper(err, format!("readfile '{}'", path.display())),
- )?))
-}
-
-#[op]
-async fn op_readfile_async<P>(
- state: Rc<RefCell<OpState>>,
- path: String,
- cancel_rid: Option<ResourceId>,
-) -> Result<ZeroCopyBuf, AnyError>
-where
- P: FsPermissions + 'static,
-{
- {
- let path = Path::new(&path);
- let mut state = state.borrow_mut();
- state
- .borrow_mut::<P>()
- .check_read(path, "Deno.readFile()")?;
- }
-
- let read_future = tokio::task::spawn_blocking(move || {
- let path = Path::new(&path);
- Ok(std::fs::read(path).map(ZeroCopyBuf::from).map_err(|err| {
- default_err_mapper(err, format!("readfile '{}'", path.display()))
- })?)
- });
-
- let cancel_handle = cancel_rid.and_then(|rid| {
- state
- .borrow_mut()
- .resource_table
- .get::<CancelHandle>(rid)
- .ok()
- });
-
- if let Some(cancel_handle) = cancel_handle {
- let read_future_rv = read_future.or_cancel(cancel_handle).await;
-
- if let Some(cancel_rid) = cancel_rid {
- state.borrow_mut().resource_table.close(cancel_rid).ok();
- };
-
- return read_future_rv??;
- }
-
- read_future.await?
-}
-
-#[op]
-async fn op_readfile_text_async<P>(
- state: Rc<RefCell<OpState>>,
- path: String,
- cancel_rid: Option<ResourceId>,
-) -> Result<String, AnyError>
-where
- P: FsPermissions + 'static,
-{
- {
- let path = Path::new(&path);
- let mut state = state.borrow_mut();
- state
- .borrow_mut::<P>()
- .check_read(path, "Deno.readTextFile()")?;
- }
-
- let read_future = tokio::task::spawn_blocking(move || {
- let path = Path::new(&path);
- Ok(string_from_utf8_lossy(std::fs::read(path).map_err(
- |err| default_err_mapper(err, format!("readfile '{}'", path.display())),
- )?))
- });
-
- let cancel_handle = cancel_rid.and_then(|rid| {
- state
- .borrow_mut()
- .resource_table
- .get::<CancelHandle>(rid)
- .ok()
- });
-
- if let Some(cancel_handle) = cancel_handle {
- let read_future_rv = read_future.or_cancel(cancel_handle).await;
-
- if let Some(cancel_rid) = cancel_rid {
- state.borrow_mut().resource_table.close(cancel_rid).ok();
- };
-
- return read_future_rv??;
- }
-
- read_future.await?
-}
-
-// Like String::from_utf8_lossy but operates on owned values
-fn string_from_utf8_lossy(buf: Vec<u8>) -> String {
- match String::from_utf8_lossy(&buf) {
- // buf contained non-utf8 chars than have been patched
- Cow::Owned(s) => s,
- // SAFETY: if Borrowed then the buf only contains utf8 chars,
- // we do this instead of .into_owned() to avoid copying the input buf
- Cow::Borrowed(_) => unsafe { String::from_utf8_unchecked(buf) },
- }
-}
diff --git a/runtime/ops/mod.rs b/runtime/ops/mod.rs
index 48c22ca92..5cb7dcbff 100644
--- a/runtime/ops/mod.rs
+++ b/runtime/ops/mod.rs
@@ -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;
diff --git a/runtime/permissions/mod.rs b/runtime/permissions/mod.rs
index 5edfbe8f7..a954387e5 100644
--- a/runtime/permissions/mod.rs
+++ b/runtime/permissions/mod.rs
@@ -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,
diff --git a/runtime/web_worker.rs b/runtime/web_worker.rs
index 06b594dd1..cce69fabb 100644
--- a/runtime/web_worker.rs
+++ b/runtime/web_worker.rs
@@ -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>(
diff --git a/runtime/worker.rs b/runtime/worker.rs
index 66497489d..1a6b6f2fc 100644
--- a/runtime/worker.rs
+++ b/runtime/worker.rs
@@ -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>(