mirror of
https://github.com/denoland/deno.git
synced 2024-11-25 15:29:32 -05:00
perf: fs optimizations - part 1 (#15873)
This commit is contained in:
parent
abe39155bd
commit
14ad75f0ae
7 changed files with 595 additions and 320 deletions
1
cli/bench/fs/.gitignore
vendored
Normal file
1
cli/bench/fs/.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
*.json
|
26
cli/bench/fs/README.md
Normal file
26
cli/bench/fs/README.md
Normal file
|
@ -0,0 +1,26 @@
|
|||
## `fs` benchmarks
|
||||
|
||||
### adding new benchmarks
|
||||
|
||||
```js
|
||||
const copyFileSync = getFunction("copyFileSync");
|
||||
bench(() => copyFileSync("test", "test2"));
|
||||
|
||||
// For functions with side-effects, clean up after `bench` like so:
|
||||
const removeSync = getFunction("removeSync");
|
||||
removeSync("test2");
|
||||
```
|
||||
|
||||
### running
|
||||
|
||||
```bash
|
||||
deno run -A --unstable run.mjs
|
||||
node run.js
|
||||
```
|
||||
|
||||
### view report
|
||||
|
||||
```bash
|
||||
deno run --allow-net=127.0.0.1:9000 serve.jsx
|
||||
# View rendered report at http://127.0.0.1:9000/
|
||||
```
|
66
cli/bench/fs/run.mjs
Normal file
66
cli/bench/fs/run.mjs
Normal file
|
@ -0,0 +1,66 @@
|
|||
// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
let total = 5;
|
||||
let current = "";
|
||||
const values = {};
|
||||
const runtime = typeof Deno !== "undefined" ? "deno" : "node";
|
||||
|
||||
function bench(fun, count = 100000) {
|
||||
if (total === 5) console.log(fun.toString());
|
||||
const start = Date.now();
|
||||
for (let i = 0; i < count; i++) fun();
|
||||
const elapsed = Date.now() - start;
|
||||
const rate = Math.floor(count / (elapsed / 1000));
|
||||
console.log(`time ${elapsed} ms rate ${rate}`);
|
||||
values[current] = values[current] || [];
|
||||
values[current].push(rate);
|
||||
if (--total) bench(fun, count);
|
||||
else total = 5;
|
||||
}
|
||||
|
||||
let fs;
|
||||
if (runtime === "node") {
|
||||
fs = await import("fs");
|
||||
}
|
||||
|
||||
const getFunction = runtime === "deno"
|
||||
? (name) => {
|
||||
current = name;
|
||||
return Deno[name];
|
||||
}
|
||||
: (name) => {
|
||||
current = name;
|
||||
return fs[name];
|
||||
};
|
||||
|
||||
const writeFileSync = getFunction("writeFileSync");
|
||||
writeFileSync("test", new Uint8Array(1024 * 1024), { truncate: true });
|
||||
|
||||
const copyFileSync = getFunction("copyFileSync");
|
||||
bench(() => copyFileSync("test", "test2"), 10000);
|
||||
|
||||
const truncateSync = getFunction("truncateSync");
|
||||
bench(() => truncateSync("test", 0));
|
||||
|
||||
const lstatSync = getFunction("lstatSync");
|
||||
bench(() => lstatSync("test"));
|
||||
|
||||
const { uid, gid } = lstatSync("test");
|
||||
|
||||
const chownSync = getFunction("chownSync");
|
||||
bench(() => chownSync("test", uid, gid));
|
||||
|
||||
const chmodSync = getFunction("chmodSync");
|
||||
bench(() => chmodSync("test", 0o666));
|
||||
|
||||
// const cwd = getFunction("cwd");
|
||||
// bench(() => cwd());
|
||||
|
||||
// const chdir = getFunction("chdir");
|
||||
// bench(() => chdir("/"));
|
||||
|
||||
const readFileSync = getFunction("readFileSync");
|
||||
writeFileSync("test", new Uint8Array(1024), { truncate: true });
|
||||
bench(() => readFileSync("test"));
|
||||
|
||||
writeFileSync(new URL(`./${runtime}.json`, import.meta.url), new TextEncoder().encode(JSON.stringify(values, null, 2)), { truncate: true });
|
57
cli/bench/fs/serve.jsx
Normal file
57
cli/bench/fs/serve.jsx
Normal file
|
@ -0,0 +1,57 @@
|
|||
// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
/** @jsx h */
|
||||
import results from "./deno.json" assert { type: "json" };
|
||||
import nodeResults from "./node.json" assert { type: "json" };
|
||||
import { h, ssr } from "https://crux.land/nanossr@0.0.4";
|
||||
import { router } from "https://crux.land/router@0.0.11";
|
||||
|
||||
function once(fn) {
|
||||
let called = false;
|
||||
let result;
|
||||
return function () {
|
||||
if (!called) {
|
||||
called = true;
|
||||
result = fn();
|
||||
return result;
|
||||
}
|
||||
return result;
|
||||
};
|
||||
}
|
||||
|
||||
const body = once(() =>
|
||||
Object.entries(results).map(([name, data]) => (
|
||||
<tr>
|
||||
<td class="border px-4 py-2">{name}</td>
|
||||
<td class="border px-4 py-2">
|
||||
{data.reduce((a, b) => a + b, 0) / data.length} ops/sec
|
||||
</td>
|
||||
<td class="border px-4 py-2">
|
||||
{nodeResults[name].reduce((a, b) => a + b, 0) /
|
||||
nodeResults[name].length} ops/sec
|
||||
</td>
|
||||
</tr>
|
||||
))
|
||||
);
|
||||
|
||||
function App() {
|
||||
return (
|
||||
<table class="table-auto">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="px-4 py-2">Benchmark</th>
|
||||
<th class="px-4 py-2">Deno</th>
|
||||
<th class="px-4 py-2">Node</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{body()}
|
||||
</tbody>
|
||||
</table>
|
||||
);
|
||||
}
|
||||
|
||||
const { serve } = Deno;
|
||||
serve(router({
|
||||
"/": () => ssr(() => <App />),
|
||||
}));
|
|
@ -11,16 +11,19 @@
|
|||
ObjectPrototypeIsPrototypeOf,
|
||||
SymbolAsyncIterator,
|
||||
SymbolIterator,
|
||||
Function,
|
||||
ObjectEntries,
|
||||
Uint32Array,
|
||||
} = window.__bootstrap.primordials;
|
||||
const { pathFromURL } = window.__bootstrap.util;
|
||||
const build = window.__bootstrap.build.build;
|
||||
|
||||
function chmodSync(path, mode) {
|
||||
ops.op_chmod_sync({ path: pathFromURL(path), mode });
|
||||
ops.op_chmod_sync(pathFromURL(path), mode);
|
||||
}
|
||||
|
||||
async function chmod(path, mode) {
|
||||
await core.opAsync("op_chmod_async", { path: pathFromURL(path), mode });
|
||||
await core.opAsync("op_chmod_async", pathFromURL(path), mode);
|
||||
}
|
||||
|
||||
function chownSync(
|
||||
|
@ -28,7 +31,7 @@
|
|||
uid,
|
||||
gid,
|
||||
) {
|
||||
ops.op_chown_sync({ path: pathFromURL(path), uid, gid });
|
||||
ops.op_chown_sync(pathFromURL(path), uid, gid);
|
||||
}
|
||||
|
||||
async function chown(
|
||||
|
@ -38,7 +41,9 @@
|
|||
) {
|
||||
await core.opAsync(
|
||||
"op_chown_async",
|
||||
{ path: pathFromURL(path), uid, gid },
|
||||
pathFromURL(path),
|
||||
uid,
|
||||
gid,
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -46,20 +51,21 @@
|
|||
fromPath,
|
||||
toPath,
|
||||
) {
|
||||
ops.op_copy_file_sync({
|
||||
from: pathFromURL(fromPath),
|
||||
to: pathFromURL(toPath),
|
||||
});
|
||||
ops.op_copy_file_sync(
|
||||
pathFromURL(fromPath),
|
||||
pathFromURL(toPath),
|
||||
);
|
||||
}
|
||||
|
||||
async function copyFile(
|
||||
fromPath,
|
||||
toPath,
|
||||
) {
|
||||
await core.opAsync("op_copy_file_async", {
|
||||
from: pathFromURL(fromPath),
|
||||
to: pathFromURL(toPath),
|
||||
});
|
||||
await core.opAsync(
|
||||
"op_copy_file_async",
|
||||
pathFromURL(fromPath),
|
||||
pathFromURL(toPath),
|
||||
);
|
||||
}
|
||||
|
||||
function cwd() {
|
||||
|
@ -148,36 +154,111 @@
|
|||
path,
|
||||
options = {},
|
||||
) {
|
||||
ops.op_remove_sync({
|
||||
path: pathFromURL(path),
|
||||
recursive: !!options.recursive,
|
||||
});
|
||||
ops.op_remove_sync(
|
||||
pathFromURL(path),
|
||||
!!options.recursive,
|
||||
);
|
||||
}
|
||||
|
||||
async function remove(
|
||||
path,
|
||||
options = {},
|
||||
) {
|
||||
await core.opAsync("op_remove_async", {
|
||||
path: pathFromURL(path),
|
||||
recursive: !!options.recursive,
|
||||
});
|
||||
await core.opAsync(
|
||||
"op_remove_async",
|
||||
pathFromURL(path),
|
||||
!!options.recursive,
|
||||
);
|
||||
}
|
||||
|
||||
function renameSync(oldpath, newpath) {
|
||||
ops.op_rename_sync({
|
||||
oldpath: pathFromURL(oldpath),
|
||||
newpath: pathFromURL(newpath),
|
||||
});
|
||||
ops.op_rename_sync(
|
||||
pathFromURL(oldpath),
|
||||
pathFromURL(newpath),
|
||||
);
|
||||
}
|
||||
|
||||
async function rename(oldpath, newpath) {
|
||||
await core.opAsync("op_rename_async", {
|
||||
oldpath: pathFromURL(oldpath),
|
||||
newpath: pathFromURL(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 {';
|
||||
for (let [name, type] of ObjectEntries(types)) {
|
||||
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 [statStruct, 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 = build.os === "darwin" || build.os === "linux";
|
||||
return {
|
||||
|
@ -185,9 +266,9 @@
|
|||
isDirectory: response.isDirectory,
|
||||
isSymlink: response.isSymlink,
|
||||
size: response.size,
|
||||
mtime: response.mtime != null ? new Date(response.mtime) : null,
|
||||
atime: response.atime != null ? new Date(response.atime) : null,
|
||||
birthtime: response.birthtime != null
|
||||
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
|
||||
|
@ -204,7 +285,8 @@
|
|||
}
|
||||
|
||||
function fstatSync(rid) {
|
||||
return parseFileInfo(ops.op_fstat_sync(rid));
|
||||
ops.op_fstat_sync(rid, statBuf);
|
||||
return statStruct(statBuf);
|
||||
}
|
||||
|
||||
async function fstat(rid) {
|
||||
|
@ -220,11 +302,12 @@
|
|||
}
|
||||
|
||||
function lstatSync(path) {
|
||||
const res = ops.op_stat_sync({
|
||||
path: pathFromURL(path),
|
||||
lstat: true,
|
||||
});
|
||||
return parseFileInfo(res);
|
||||
ops.op_stat_sync(
|
||||
pathFromURL(path),
|
||||
true,
|
||||
statBuf,
|
||||
);
|
||||
return statStruct(statBuf);
|
||||
}
|
||||
|
||||
async function stat(path) {
|
||||
|
@ -236,11 +319,12 @@
|
|||
}
|
||||
|
||||
function statSync(path) {
|
||||
const res = ops.op_stat_sync({
|
||||
path: pathFromURL(path),
|
||||
lstat: false,
|
||||
});
|
||||
return parseFileInfo(res);
|
||||
ops.op_stat_sync(
|
||||
pathFromURL(path),
|
||||
false,
|
||||
statBuf,
|
||||
);
|
||||
return statStruct(statBuf);
|
||||
}
|
||||
|
||||
function coerceLen(len) {
|
||||
|
@ -252,19 +336,19 @@
|
|||
}
|
||||
|
||||
function ftruncateSync(rid, len) {
|
||||
ops.op_ftruncate_sync({ rid, len: coerceLen(len) });
|
||||
ops.op_ftruncate_sync(rid, coerceLen(len));
|
||||
}
|
||||
|
||||
async function ftruncate(rid, len) {
|
||||
await core.opAsync("op_ftruncate_async", { rid, len: coerceLen(len) });
|
||||
await core.opAsync("op_ftruncate_async", rid, coerceLen(len));
|
||||
}
|
||||
|
||||
function truncateSync(path, len) {
|
||||
ops.op_truncate_sync({ path, len: coerceLen(len) });
|
||||
ops.op_truncate_sync(path, coerceLen(len));
|
||||
}
|
||||
|
||||
async function truncate(path, len) {
|
||||
await core.opAsync("op_truncate_async", { path, len: coerceLen(len) });
|
||||
await core.opAsync("op_truncate_async", path, coerceLen(len));
|
||||
}
|
||||
|
||||
function umask(mask) {
|
||||
|
@ -272,11 +356,11 @@
|
|||
}
|
||||
|
||||
function linkSync(oldpath, newpath) {
|
||||
ops.op_link_sync({ oldpath, newpath });
|
||||
ops.op_link_sync(oldpath, newpath);
|
||||
}
|
||||
|
||||
async function link(oldpath, newpath) {
|
||||
await core.opAsync("op_link_async", { oldpath, newpath });
|
||||
await core.opAsync("op_link_async", oldpath, newpath);
|
||||
}
|
||||
|
||||
function toUnixTimeFromEpoch(value) {
|
||||
|
@ -305,11 +389,9 @@
|
|||
atime,
|
||||
mtime,
|
||||
) {
|
||||
ops.op_futime_sync({
|
||||
rid,
|
||||
atime: toUnixTimeFromEpoch(atime),
|
||||
mtime: toUnixTimeFromEpoch(mtime),
|
||||
});
|
||||
const [atimeSec, atimeNsec] = toUnixTimeFromEpoch(atime);
|
||||
const [mtimeSec, mtimeNsec] = toUnixTimeFromEpoch(mtime);
|
||||
ops.op_futime_sync(rid, atimeSec, atimeNsec, mtimeSec, mtimeNsec);
|
||||
}
|
||||
|
||||
async function futime(
|
||||
|
@ -317,11 +399,16 @@
|
|||
atime,
|
||||
mtime,
|
||||
) {
|
||||
await core.opAsync("op_futime_async", {
|
||||
const [atimeSec, atimeNsec] = toUnixTimeFromEpoch(atime);
|
||||
const [mtimeSec, mtimeNsec] = toUnixTimeFromEpoch(mtime);
|
||||
await core.opAsync(
|
||||
"op_futime_async",
|
||||
rid,
|
||||
atime: toUnixTimeFromEpoch(atime),
|
||||
mtime: toUnixTimeFromEpoch(mtime),
|
||||
});
|
||||
atimeSec,
|
||||
atimeNsec,
|
||||
mtimeSec,
|
||||
mtimeNsec,
|
||||
);
|
||||
}
|
||||
|
||||
function utimeSync(
|
||||
|
@ -329,11 +416,15 @@
|
|||
atime,
|
||||
mtime,
|
||||
) {
|
||||
ops.op_utime_sync({
|
||||
path: pathFromURL(path),
|
||||
atime: toUnixTimeFromEpoch(atime),
|
||||
mtime: toUnixTimeFromEpoch(mtime),
|
||||
});
|
||||
const [atimeSec, atimeNsec] = toUnixTimeFromEpoch(atime);
|
||||
const [mtimeSec, mtimeNsec] = toUnixTimeFromEpoch(mtime);
|
||||
ops.op_utime_sync(
|
||||
pathFromURL(path),
|
||||
atimeSec,
|
||||
atimeNsec,
|
||||
mtimeSec,
|
||||
mtimeNsec,
|
||||
);
|
||||
}
|
||||
|
||||
async function utime(
|
||||
|
@ -341,11 +432,16 @@
|
|||
atime,
|
||||
mtime,
|
||||
) {
|
||||
await core.opAsync("op_utime_async", {
|
||||
path: pathFromURL(path),
|
||||
atime: toUnixTimeFromEpoch(atime),
|
||||
mtime: toUnixTimeFromEpoch(mtime),
|
||||
});
|
||||
const [atimeSec, atimeNsec] = toUnixTimeFromEpoch(atime);
|
||||
const [mtimeSec, mtimeNsec] = toUnixTimeFromEpoch(mtime);
|
||||
await core.opAsync(
|
||||
"op_utime_async",
|
||||
pathFromURL(path),
|
||||
atimeSec,
|
||||
atimeNsec,
|
||||
mtimeSec,
|
||||
mtimeNsec,
|
||||
);
|
||||
}
|
||||
|
||||
function symlinkSync(
|
||||
|
@ -353,11 +449,11 @@
|
|||
newpath,
|
||||
options,
|
||||
) {
|
||||
ops.op_symlink_sync({
|
||||
oldpath: pathFromURL(oldpath),
|
||||
newpath: pathFromURL(newpath),
|
||||
options,
|
||||
});
|
||||
ops.op_symlink_sync(
|
||||
pathFromURL(oldpath),
|
||||
pathFromURL(newpath),
|
||||
options?.type,
|
||||
);
|
||||
}
|
||||
|
||||
async function symlink(
|
||||
|
@ -365,11 +461,12 @@
|
|||
newpath,
|
||||
options,
|
||||
) {
|
||||
await core.opAsync("op_symlink_async", {
|
||||
oldpath: pathFromURL(oldpath),
|
||||
newpath: pathFromURL(newpath),
|
||||
options,
|
||||
});
|
||||
await core.opAsync(
|
||||
"op_symlink_async",
|
||||
pathFromURL(oldpath),
|
||||
pathFromURL(newpath),
|
||||
options?.type,
|
||||
);
|
||||
}
|
||||
|
||||
function fdatasyncSync(rid) {
|
||||
|
|
|
@ -394,11 +394,14 @@ async fn op_fsync_async(
|
|||
fn op_fstat_sync(
|
||||
state: &mut OpState,
|
||||
rid: ResourceId,
|
||||
) -> Result<FsStat, AnyError> {
|
||||
out_buf: &mut [u32],
|
||||
) -> Result<(), AnyError> {
|
||||
let metadata = StdFileResource::with_file(state, rid, |std_file| {
|
||||
std_file.metadata().map_err(AnyError::from)
|
||||
})?;
|
||||
Ok(get_stat(metadata))
|
||||
let stat = get_stat(metadata);
|
||||
stat.write(out_buf);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[op]
|
||||
|
@ -579,40 +582,34 @@ async fn op_mkdir_async(
|
|||
.unwrap()
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct ChmodArgs {
|
||||
#[op]
|
||||
fn op_chmod_sync(
|
||||
state: &mut OpState,
|
||||
path: String,
|
||||
mode: u32,
|
||||
}
|
||||
|
||||
#[op]
|
||||
fn op_chmod_sync(state: &mut OpState, args: ChmodArgs) -> Result<(), AnyError> {
|
||||
let path = Path::new(&args.path);
|
||||
let mode = args.mode & 0o777;
|
||||
) -> Result<(), AnyError> {
|
||||
let path = Path::new(&path);
|
||||
let mode = mode & 0o777;
|
||||
|
||||
state.borrow_mut::<Permissions>().write.check(path)?;
|
||||
debug!("op_chmod_sync {} {:o}", path.display(), mode);
|
||||
raw_chmod(path, mode)
|
||||
}
|
||||
|
||||
#[op]
|
||||
async fn op_chmod_async(
|
||||
state: Rc<RefCell<OpState>>,
|
||||
args: ChmodArgs,
|
||||
path: String,
|
||||
mode: u32,
|
||||
) -> Result<(), AnyError> {
|
||||
let path = Path::new(&args.path).to_path_buf();
|
||||
let mode = args.mode & 0o777;
|
||||
let path = Path::new(&path).to_path_buf();
|
||||
let mode = mode & 0o777;
|
||||
|
||||
{
|
||||
let mut state = state.borrow_mut();
|
||||
state.borrow_mut::<Permissions>().write.check(&path)?;
|
||||
}
|
||||
|
||||
tokio::task::spawn_blocking(move || {
|
||||
debug!("op_chmod_async {} {:o}", path.display(), mode);
|
||||
raw_chmod(&path, mode)
|
||||
})
|
||||
tokio::task::spawn_blocking(move || raw_chmod(&path, mode))
|
||||
.await
|
||||
.unwrap()
|
||||
}
|
||||
|
@ -637,30 +634,21 @@ fn raw_chmod(path: &Path, _raw_mode: u32) -> Result<(), AnyError> {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct ChownArgs {
|
||||
#[op]
|
||||
fn op_chown_sync(
|
||||
state: &mut OpState,
|
||||
path: String,
|
||||
uid: Option<u32>,
|
||||
gid: Option<u32>,
|
||||
}
|
||||
|
||||
#[op]
|
||||
fn op_chown_sync(state: &mut OpState, args: ChownArgs) -> Result<(), AnyError> {
|
||||
let path = Path::new(&args.path).to_path_buf();
|
||||
) -> Result<(), AnyError> {
|
||||
let path = Path::new(&path).to_path_buf();
|
||||
state.borrow_mut::<Permissions>().write.check(&path)?;
|
||||
debug!(
|
||||
"op_chown_sync {} {:?} {:?}",
|
||||
path.display(),
|
||||
args.uid,
|
||||
args.gid,
|
||||
);
|
||||
#[cfg(unix)]
|
||||
{
|
||||
use crate::errors::get_nix_error_class;
|
||||
use nix::unistd::{chown, Gid, Uid};
|
||||
let nix_uid = args.uid.map(Uid::from_raw);
|
||||
let nix_gid = args.gid.map(Gid::from_raw);
|
||||
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),
|
||||
|
@ -679,9 +667,11 @@ fn op_chown_sync(state: &mut OpState, args: ChownArgs) -> Result<(), AnyError> {
|
|||
#[op]
|
||||
async fn op_chown_async(
|
||||
state: Rc<RefCell<OpState>>,
|
||||
args: ChownArgs,
|
||||
path: String,
|
||||
uid: Option<u32>,
|
||||
gid: Option<u32>,
|
||||
) -> Result<(), AnyError> {
|
||||
let path = Path::new(&args.path).to_path_buf();
|
||||
let path = Path::new(&path).to_path_buf();
|
||||
|
||||
{
|
||||
let mut state = state.borrow_mut();
|
||||
|
@ -689,18 +679,12 @@ async fn op_chown_async(
|
|||
}
|
||||
|
||||
tokio::task::spawn_blocking(move || {
|
||||
debug!(
|
||||
"op_chown_async {} {:?} {:?}",
|
||||
path.display(),
|
||||
args.uid,
|
||||
args.gid,
|
||||
);
|
||||
#[cfg(unix)]
|
||||
{
|
||||
use crate::errors::get_nix_error_class;
|
||||
use nix::unistd::{chown, Gid, Uid};
|
||||
let nix_uid = args.uid.map(Uid::from_raw);
|
||||
let nix_gid = args.gid.map(Gid::from_raw);
|
||||
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),
|
||||
|
@ -717,20 +701,13 @@ async fn op_chown_async(
|
|||
.unwrap()
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct RemoveArgs {
|
||||
path: String,
|
||||
recursive: bool,
|
||||
}
|
||||
|
||||
#[op]
|
||||
fn op_remove_sync(
|
||||
state: &mut OpState,
|
||||
args: RemoveArgs,
|
||||
path: String,
|
||||
recursive: bool,
|
||||
) -> Result<(), AnyError> {
|
||||
let path = PathBuf::from(&args.path);
|
||||
let recursive = args.recursive;
|
||||
let path = PathBuf::from(&path);
|
||||
|
||||
state.borrow_mut::<Permissions>().write.check(&path)?;
|
||||
|
||||
|
@ -742,7 +719,6 @@ fn op_remove_sync(
|
|||
};
|
||||
let metadata = std::fs::symlink_metadata(&path).map_err(err_mapper)?;
|
||||
|
||||
debug!("op_remove_sync {} {}", path.display(), recursive);
|
||||
let file_type = metadata.file_type();
|
||||
if file_type.is_file() {
|
||||
std::fs::remove_file(&path).map_err(err_mapper)?;
|
||||
|
@ -772,10 +748,10 @@ fn op_remove_sync(
|
|||
#[op]
|
||||
async fn op_remove_async(
|
||||
state: Rc<RefCell<OpState>>,
|
||||
args: RemoveArgs,
|
||||
path: String,
|
||||
recursive: bool,
|
||||
) -> Result<(), AnyError> {
|
||||
let path = PathBuf::from(&args.path);
|
||||
let recursive = args.recursive;
|
||||
let path = PathBuf::from(&path);
|
||||
|
||||
{
|
||||
let mut state = state.borrow_mut();
|
||||
|
@ -820,36 +796,29 @@ async fn op_remove_async(
|
|||
.unwrap()
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct CopyFileArgs {
|
||||
from: String,
|
||||
to: String,
|
||||
}
|
||||
|
||||
#[op]
|
||||
fn op_copy_file_sync(
|
||||
state: &mut OpState,
|
||||
args: CopyFileArgs,
|
||||
from: String,
|
||||
to: String,
|
||||
) -> Result<(), AnyError> {
|
||||
let from = PathBuf::from(&args.from);
|
||||
let to = PathBuf::from(&args.to);
|
||||
let from_path = PathBuf::from(&from);
|
||||
let to_path = PathBuf::from(&to);
|
||||
|
||||
let permissions = state.borrow_mut::<Permissions>();
|
||||
permissions.read.check(&from)?;
|
||||
permissions.write.check(&to)?;
|
||||
permissions.read.check(&from_path)?;
|
||||
permissions.write.check(&to_path)?;
|
||||
|
||||
debug!("op_copy_file_sync {} {}", from.display(), to.display());
|
||||
// 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() {
|
||||
if cfg!(unix) && !from_path.is_file() {
|
||||
return Err(custom_error(
|
||||
"NotFound",
|
||||
format!(
|
||||
"File not found, copy '{}' -> '{}'",
|
||||
from.display(),
|
||||
to.display()
|
||||
from_path.display(),
|
||||
to_path.display()
|
||||
),
|
||||
));
|
||||
}
|
||||
|
@ -857,21 +826,80 @@ fn op_copy_file_sync(
|
|||
let err_mapper = |err: Error| {
|
||||
Error::new(
|
||||
err.kind(),
|
||||
format!("{}, copy '{}' -> '{}'", err, from.display(), to.display()),
|
||||
format!(
|
||||
"{}, copy '{}' -> '{}'",
|
||||
err,
|
||||
from_path.display(),
|
||||
to_path.display()
|
||||
),
|
||||
)
|
||||
};
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
{
|
||||
use libc::chmod;
|
||||
use libc::clonefile;
|
||||
use libc::stat;
|
||||
use libc::unlink;
|
||||
use std::ffi::CString;
|
||||
use std::io::Read;
|
||||
|
||||
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 mut to_file =
|
||||
std::fs::File::create(&to_path).map_err(err_mapper)?;
|
||||
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, &to).map_err(err_mapper)?;
|
||||
std::fs::copy(&from_path, &to_path).map_err(err_mapper)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[op]
|
||||
async fn op_copy_file_async(
|
||||
state: Rc<RefCell<OpState>>,
|
||||
args: CopyFileArgs,
|
||||
from: String,
|
||||
to: String,
|
||||
) -> Result<(), AnyError> {
|
||||
let from = PathBuf::from(&args.from);
|
||||
let to = PathBuf::from(&args.to);
|
||||
let from = PathBuf::from(&from);
|
||||
let to = PathBuf::from(&to);
|
||||
|
||||
{
|
||||
let mut state = state.borrow_mut();
|
||||
|
@ -880,7 +908,6 @@ async fn op_copy_file_async(
|
|||
permissions.write.check(&to)?;
|
||||
}
|
||||
|
||||
debug!("op_copy_file_async {} {}", from.display(), to.display());
|
||||
tokio::task::spawn_blocking(move || {
|
||||
// On *nix, Rust reports non-existent `from` as ErrorKind::InvalidInput
|
||||
// See https://github.com/rust-lang/rust/issues/54800
|
||||
|
@ -910,30 +937,57 @@ async fn op_copy_file_async(
|
|||
.unwrap()
|
||||
}
|
||||
|
||||
fn to_msec(maybe_time: Result<SystemTime, io::Error>) -> Option<u64> {
|
||||
fn to_msec(maybe_time: Result<SystemTime, io::Error>) -> (u64, bool) {
|
||||
match maybe_time {
|
||||
Ok(time) => {
|
||||
let msec = 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);
|
||||
Some(msec)
|
||||
.unwrap_or_else(|err| err.duration().as_millis() as u64),
|
||||
true,
|
||||
),
|
||||
Err(_) => (0, false),
|
||||
}
|
||||
Err(_) => None,
|
||||
}
|
||||
|
||||
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: Option<u64>,
|
||||
atime: Option<u64>,
|
||||
birthtime: Option<u64>,
|
||||
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,
|
||||
|
@ -945,6 +999,7 @@ pub struct FsStat {
|
|||
blksize: u64,
|
||||
blocks: u64,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn get_stat(metadata: std::fs::Metadata) -> FsStat {
|
||||
|
@ -964,15 +1019,22 @@ fn get_stat(metadata: std::fs::Metadata) -> FsStat {
|
|||
|
||||
#[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: to_msec(metadata.modified()),
|
||||
atime: to_msec(metadata.accessed()),
|
||||
birthtime: to_msec(metadata.created()),
|
||||
mtime_set,
|
||||
mtime,
|
||||
atime_set,
|
||||
atime,
|
||||
birthtime_set,
|
||||
birthtime,
|
||||
// Following are only valid under Unix.
|
||||
dev: usm!(dev),
|
||||
ino: usm!(ino),
|
||||
|
@ -996,12 +1058,12 @@ pub struct StatArgs {
|
|||
#[op]
|
||||
fn op_stat_sync(
|
||||
state: &mut OpState,
|
||||
args: StatArgs,
|
||||
) -> Result<FsStat, AnyError> {
|
||||
let path = PathBuf::from(&args.path);
|
||||
let lstat = args.lstat;
|
||||
path: String,
|
||||
lstat: bool,
|
||||
out_buf: &mut [u32],
|
||||
) -> Result<(), AnyError> {
|
||||
let path = PathBuf::from(&path);
|
||||
state.borrow_mut::<Permissions>().read.check(&path)?;
|
||||
debug!("op_stat_sync {} {}", path.display(), lstat);
|
||||
let err_mapper = |err: Error| {
|
||||
Error::new(err.kind(), format!("{}, stat '{}'", err, path.display()))
|
||||
};
|
||||
|
@ -1010,7 +1072,11 @@ fn op_stat_sync(
|
|||
} else {
|
||||
std::fs::metadata(&path).map_err(err_mapper)?
|
||||
};
|
||||
Ok(get_stat(metadata))
|
||||
|
||||
let stat = get_stat(metadata);
|
||||
stat.write(out_buf);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[op]
|
||||
|
@ -1185,26 +1251,20 @@ async fn op_read_dir_async(
|
|||
.unwrap()
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct RenameArgs {
|
||||
oldpath: String,
|
||||
newpath: String,
|
||||
}
|
||||
|
||||
#[op]
|
||||
fn op_rename_sync(
|
||||
state: &mut OpState,
|
||||
args: RenameArgs,
|
||||
oldpath: String,
|
||||
newpath: String,
|
||||
) -> Result<(), AnyError> {
|
||||
let oldpath = PathBuf::from(&args.oldpath);
|
||||
let newpath = PathBuf::from(&args.newpath);
|
||||
let oldpath = PathBuf::from(&oldpath);
|
||||
let newpath = PathBuf::from(&newpath);
|
||||
|
||||
let permissions = state.borrow_mut::<Permissions>();
|
||||
permissions.read.check(&oldpath)?;
|
||||
permissions.write.check(&oldpath)?;
|
||||
permissions.write.check(&newpath)?;
|
||||
debug!("op_rename_sync {} {}", oldpath.display(), newpath.display());
|
||||
|
||||
let err_mapper = |err: Error| {
|
||||
Error::new(
|
||||
err.kind(),
|
||||
|
@ -1223,10 +1283,11 @@ fn op_rename_sync(
|
|||
#[op]
|
||||
async fn op_rename_async(
|
||||
state: Rc<RefCell<OpState>>,
|
||||
args: RenameArgs,
|
||||
oldpath: String,
|
||||
newpath: String,
|
||||
) -> Result<(), AnyError> {
|
||||
let oldpath = PathBuf::from(&args.oldpath);
|
||||
let newpath = PathBuf::from(&args.newpath);
|
||||
let oldpath = PathBuf::from(&oldpath);
|
||||
let newpath = PathBuf::from(&newpath);
|
||||
{
|
||||
let mut state = state.borrow_mut();
|
||||
let permissions = state.borrow_mut::<Permissions>();
|
||||
|
@ -1235,11 +1296,6 @@ async fn op_rename_async(
|
|||
permissions.write.check(&newpath)?;
|
||||
}
|
||||
tokio::task::spawn_blocking(move || {
|
||||
debug!(
|
||||
"op_rename_async {} {}",
|
||||
oldpath.display(),
|
||||
newpath.display()
|
||||
);
|
||||
let err_mapper = |err: Error| {
|
||||
Error::new(
|
||||
err.kind(),
|
||||
|
@ -1258,17 +1314,14 @@ async fn op_rename_async(
|
|||
.unwrap()
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct LinkArgs {
|
||||
#[op]
|
||||
fn op_link_sync(
|
||||
state: &mut OpState,
|
||||
oldpath: String,
|
||||
newpath: String,
|
||||
}
|
||||
|
||||
#[op]
|
||||
fn op_link_sync(state: &mut OpState, args: LinkArgs) -> Result<(), AnyError> {
|
||||
let oldpath = PathBuf::from(&args.oldpath);
|
||||
let newpath = PathBuf::from(&args.newpath);
|
||||
) -> Result<(), AnyError> {
|
||||
let oldpath = PathBuf::from(&oldpath);
|
||||
let newpath = PathBuf::from(&newpath);
|
||||
|
||||
let permissions = state.borrow_mut::<Permissions>();
|
||||
permissions.read.check(&oldpath)?;
|
||||
|
@ -1276,7 +1329,6 @@ fn op_link_sync(state: &mut OpState, args: LinkArgs) -> Result<(), AnyError> {
|
|||
permissions.read.check(&newpath)?;
|
||||
permissions.write.check(&newpath)?;
|
||||
|
||||
debug!("op_link_sync {} {}", oldpath.display(), newpath.display());
|
||||
let err_mapper = |err: Error| {
|
||||
Error::new(
|
||||
err.kind(),
|
||||
|
@ -1295,10 +1347,11 @@ fn op_link_sync(state: &mut OpState, args: LinkArgs) -> Result<(), AnyError> {
|
|||
#[op]
|
||||
async fn op_link_async(
|
||||
state: Rc<RefCell<OpState>>,
|
||||
args: LinkArgs,
|
||||
oldpath: String,
|
||||
newpath: String,
|
||||
) -> Result<(), AnyError> {
|
||||
let oldpath = PathBuf::from(&args.oldpath);
|
||||
let newpath = PathBuf::from(&args.newpath);
|
||||
let oldpath = PathBuf::from(&oldpath);
|
||||
let newpath = PathBuf::from(&newpath);
|
||||
|
||||
{
|
||||
let mut state = state.borrow_mut();
|
||||
|
@ -1310,7 +1363,6 @@ async fn op_link_async(
|
|||
}
|
||||
|
||||
tokio::task::spawn_blocking(move || {
|
||||
debug!("op_link_async {} {}", oldpath.display(), newpath.display());
|
||||
let err_mapper = |err: Error| {
|
||||
Error::new(
|
||||
err.kind(),
|
||||
|
@ -1329,38 +1381,19 @@ async fn op_link_async(
|
|||
.unwrap()
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct SymlinkArgs {
|
||||
oldpath: String,
|
||||
newpath: String,
|
||||
#[cfg(not(unix))]
|
||||
options: Option<SymlinkOptions>,
|
||||
}
|
||||
|
||||
#[cfg(not(unix))]
|
||||
#[derive(Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct SymlinkOptions {
|
||||
_type: String,
|
||||
}
|
||||
|
||||
#[op]
|
||||
fn op_symlink_sync(
|
||||
state: &mut OpState,
|
||||
args: SymlinkArgs,
|
||||
oldpath: String,
|
||||
newpath: String,
|
||||
_type: Option<String>,
|
||||
) -> Result<(), AnyError> {
|
||||
let oldpath = PathBuf::from(&args.oldpath);
|
||||
let newpath = PathBuf::from(&args.newpath);
|
||||
let oldpath = PathBuf::from(&oldpath);
|
||||
let newpath = PathBuf::from(&newpath);
|
||||
|
||||
state.borrow_mut::<Permissions>().write.check_all()?;
|
||||
state.borrow_mut::<Permissions>().read.check_all()?;
|
||||
|
||||
debug!(
|
||||
"op_symlink_sync {} {}",
|
||||
oldpath.display(),
|
||||
newpath.display()
|
||||
);
|
||||
let err_mapper = |err: Error| {
|
||||
Error::new(
|
||||
err.kind(),
|
||||
|
@ -1382,8 +1415,8 @@ fn op_symlink_sync(
|
|||
{
|
||||
use std::os::windows::fs::{symlink_dir, symlink_file};
|
||||
|
||||
match args.options {
|
||||
Some(options) => match options._type.as_ref() {
|
||||
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")),
|
||||
|
@ -1409,10 +1442,12 @@ fn op_symlink_sync(
|
|||
#[op]
|
||||
async fn op_symlink_async(
|
||||
state: Rc<RefCell<OpState>>,
|
||||
args: SymlinkArgs,
|
||||
oldpath: String,
|
||||
newpath: String,
|
||||
_type: Option<String>,
|
||||
) -> Result<(), AnyError> {
|
||||
let oldpath = PathBuf::from(&args.oldpath);
|
||||
let newpath = PathBuf::from(&args.newpath);
|
||||
let oldpath = PathBuf::from(&oldpath);
|
||||
let newpath = PathBuf::from(&newpath);
|
||||
|
||||
{
|
||||
let mut state = state.borrow_mut();
|
||||
|
@ -1421,7 +1456,6 @@ async fn op_symlink_async(
|
|||
}
|
||||
|
||||
tokio::task::spawn_blocking(move || {
|
||||
debug!("op_symlink_async {} {}", oldpath.display(), newpath.display());
|
||||
let err_mapper = |err: Error| {
|
||||
Error::new(
|
||||
err.kind(),
|
||||
|
@ -1443,8 +1477,8 @@ async fn op_symlink_async(
|
|||
{
|
||||
use std::os::windows::fs::{symlink_dir, symlink_file};
|
||||
|
||||
match args.options {
|
||||
Some(options) => match options._type.as_ref() {
|
||||
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")),
|
||||
|
@ -1521,20 +1555,13 @@ async fn op_read_link_async(
|
|||
.unwrap()
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct FtruncateArgs {
|
||||
rid: ResourceId,
|
||||
len: i32,
|
||||
}
|
||||
|
||||
#[op]
|
||||
fn op_ftruncate_sync(
|
||||
state: &mut OpState,
|
||||
args: FtruncateArgs,
|
||||
rid: u32,
|
||||
len: i32,
|
||||
) -> Result<(), AnyError> {
|
||||
let rid = args.rid;
|
||||
let len = args.len as u64;
|
||||
let len = len as u64;
|
||||
StdFileResource::with_file(state, rid, |std_file| {
|
||||
std_file.set_len(len).map_err(AnyError::from)
|
||||
})?;
|
||||
|
@ -1544,10 +1571,10 @@ fn op_ftruncate_sync(
|
|||
#[op]
|
||||
async fn op_ftruncate_async(
|
||||
state: Rc<RefCell<OpState>>,
|
||||
args: FtruncateArgs,
|
||||
rid: ResourceId,
|
||||
len: i32,
|
||||
) -> Result<(), AnyError> {
|
||||
let rid = args.rid;
|
||||
let len = args.len as u64;
|
||||
let len = len as u64;
|
||||
|
||||
StdFileResource::with_file_blocking_task(state, rid, move |std_file| {
|
||||
std_file.set_len(len)?;
|
||||
|
@ -1556,20 +1583,13 @@ async fn op_ftruncate_async(
|
|||
.await
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct TruncateArgs {
|
||||
path: String,
|
||||
len: u64,
|
||||
}
|
||||
|
||||
#[op]
|
||||
fn op_truncate_sync(
|
||||
state: &mut OpState,
|
||||
args: TruncateArgs,
|
||||
path: String,
|
||||
len: u64,
|
||||
) -> Result<(), AnyError> {
|
||||
let path = PathBuf::from(&args.path);
|
||||
let len = args.len;
|
||||
let path = PathBuf::from(&path);
|
||||
|
||||
state.borrow_mut::<Permissions>().write.check(&path)?;
|
||||
|
||||
|
@ -1591,10 +1611,11 @@ fn op_truncate_sync(
|
|||
#[op]
|
||||
async fn op_truncate_async(
|
||||
state: Rc<RefCell<OpState>>,
|
||||
args: TruncateArgs,
|
||||
path: String,
|
||||
len: u64,
|
||||
) -> Result<(), AnyError> {
|
||||
let path = PathBuf::from(&args.path);
|
||||
let len = args.len;
|
||||
let path = PathBuf::from(&path);
|
||||
|
||||
{
|
||||
let mut state = state.borrow_mut();
|
||||
state.borrow_mut::<Permissions>().write.check(&path)?;
|
||||
|
@ -1797,23 +1818,18 @@ async fn op_make_temp_file_async(
|
|||
.unwrap()
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct FutimeArgs {
|
||||
rid: ResourceId,
|
||||
atime: (i64, u32),
|
||||
mtime: (i64, u32),
|
||||
}
|
||||
|
||||
#[op]
|
||||
fn op_futime_sync(
|
||||
state: &mut OpState,
|
||||
args: FutimeArgs,
|
||||
rid: ResourceId,
|
||||
atime_secs: i64,
|
||||
atime_nanos: u32,
|
||||
mtime_secs: i64,
|
||||
mtime_nanos: u32,
|
||||
) -> Result<(), AnyError> {
|
||||
super::check_unstable(state, "Deno.futimeSync");
|
||||
let rid = args.rid;
|
||||
let atime = filetime::FileTime::from_unix_time(args.atime.0, args.atime.1);
|
||||
let mtime = filetime::FileTime::from_unix_time(args.mtime.0, args.mtime.1);
|
||||
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))
|
||||
|
@ -1826,12 +1842,15 @@ fn op_futime_sync(
|
|||
#[op]
|
||||
async fn op_futime_async(
|
||||
state: Rc<RefCell<OpState>>,
|
||||
args: FutimeArgs,
|
||||
rid: ResourceId,
|
||||
atime_secs: i64,
|
||||
atime_nanos: u32,
|
||||
mtime_secs: i64,
|
||||
mtime_nanos: u32,
|
||||
) -> Result<(), AnyError> {
|
||||
super::check_unstable2(&state, "Deno.futime");
|
||||
let rid = args.rid;
|
||||
let atime = filetime::FileTime::from_unix_time(args.atime.0, args.atime.1);
|
||||
let mtime = filetime::FileTime::from_unix_time(args.mtime.0, args.mtime.1);
|
||||
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))?;
|
||||
|
@ -1840,21 +1859,20 @@ async fn op_futime_async(
|
|||
.await
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct UtimeArgs {
|
||||
path: String,
|
||||
atime: (i64, u32),
|
||||
mtime: (i64, u32),
|
||||
}
|
||||
|
||||
#[op]
|
||||
fn op_utime_sync(state: &mut OpState, args: UtimeArgs) -> Result<(), AnyError> {
|
||||
fn op_utime_sync(
|
||||
state: &mut OpState,
|
||||
path: String,
|
||||
atime_secs: i64,
|
||||
atime_nanos: u32,
|
||||
mtime_secs: i64,
|
||||
mtime_nanos: u32,
|
||||
) -> Result<(), AnyError> {
|
||||
super::check_unstable(state, "Deno.utime");
|
||||
|
||||
let path = PathBuf::from(&args.path);
|
||||
let atime = filetime::FileTime::from_unix_time(args.atime.0, args.atime.1);
|
||||
let mtime = filetime::FileTime::from_unix_time(args.mtime.0, args.mtime.1);
|
||||
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::<Permissions>().write.check(&path)?;
|
||||
filetime::set_file_times(&path, atime, mtime).map_err(|err| {
|
||||
|
@ -1866,13 +1884,17 @@ fn op_utime_sync(state: &mut OpState, args: UtimeArgs) -> Result<(), AnyError> {
|
|||
#[op]
|
||||
async fn op_utime_async(
|
||||
state: Rc<RefCell<OpState>>,
|
||||
args: UtimeArgs,
|
||||
path: String,
|
||||
atime_secs: i64,
|
||||
atime_nanos: u32,
|
||||
mtime_secs: i64,
|
||||
mtime_nanos: u32,
|
||||
) -> Result<(), AnyError> {
|
||||
super::check_unstable(&state.borrow(), "Deno.utime");
|
||||
|
||||
let path = PathBuf::from(&args.path);
|
||||
let atime = filetime::FileTime::from_unix_time(args.atime.0, args.atime.1);
|
||||
let mtime = filetime::FileTime::from_unix_time(args.mtime.0, args.mtime.1);
|
||||
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()
|
||||
|
|
|
@ -302,6 +302,9 @@ pub struct FfiDescriptor(pub PathBuf);
|
|||
|
||||
impl UnaryPermission<ReadDescriptor> {
|
||||
pub fn query(&self, path: Option<&Path>) -> PermissionState {
|
||||
if self.global_state == PermissionState::Granted {
|
||||
return PermissionState::Granted;
|
||||
}
|
||||
let path = path.map(|p| resolve_from_cwd(p).unwrap());
|
||||
if self.global_state == PermissionState::Denied
|
||||
&& match path.as_ref() {
|
||||
|
@ -454,6 +457,9 @@ impl Default for UnaryPermission<ReadDescriptor> {
|
|||
|
||||
impl UnaryPermission<WriteDescriptor> {
|
||||
pub fn query(&self, path: Option<&Path>) -> PermissionState {
|
||||
if self.global_state == PermissionState::Granted {
|
||||
return PermissionState::Granted;
|
||||
}
|
||||
let path = path.map(|p| resolve_from_cwd(p).unwrap());
|
||||
if self.global_state == PermissionState::Denied
|
||||
&& match path.as_ref() {
|
||||
|
|
Loading…
Reference in a new issue