Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(ext/node): Add fs.lutimes / fs.lutimesSync #23172

Merged
merged 10 commits into from
Jul 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions cli/standalone/file_system.rs
Original file line number Diff line number Diff line change
Expand Up @@ -349,4 +349,29 @@ impl FileSystem for DenoCompileFileSystem {
.utime_async(path, atime_secs, atime_nanos, mtime_secs, mtime_nanos)
.await
}

fn lutime_sync(
&self,
path: &Path,
atime_secs: i64,
atime_nanos: u32,
mtime_secs: i64,
mtime_nanos: u32,
) -> FsResult<()> {
self.error_if_in_vfs(path)?;
RealFs.lutime_sync(path, atime_secs, atime_nanos, mtime_secs, mtime_nanos)
}
async fn lutime_async(
&self,
path: PathBuf,
atime_secs: i64,
atime_nanos: u32,
mtime_secs: i64,
mtime_nanos: u32,
) -> FsResult<()> {
self.error_if_in_vfs(&path)?;
RealFs
.lutime_async(path, atime_secs, atime_nanos, mtime_secs, mtime_nanos)
.await
}
}
4 changes: 2 additions & 2 deletions ext/fs/30_fs.js
Original file line number Diff line number Diff line change
Expand Up @@ -473,8 +473,8 @@ function toUnixTimeFromEpoch(value) {
];
}

const seconds = value;
const nanoseconds = 0;
const seconds = MathTrunc(value);
const nanoseconds = MathTrunc((value * 1e3) - (seconds * 1e3)) * 1e6;

return [
seconds,
Expand Down
21 changes: 21 additions & 0 deletions ext/fs/in_memory_fs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -350,6 +350,27 @@ impl FileSystem for InMemoryFs {
self.utime_sync(&path, atime_secs, atime_nanos, mtime_secs, mtime_nanos)
}

fn lutime_sync(
&self,
_path: &Path,
_atime_secs: i64,
_atime_nanos: u32,
_mtime_secs: i64,
_mtime_nanos: u32,
) -> FsResult<()> {
Err(FsError::NotSupported)
}
async fn lutime_async(
&self,
path: PathBuf,
atime_secs: i64,
atime_nanos: u32,
mtime_secs: i64,
mtime_nanos: u32,
) -> FsResult<()> {
self.lutime_sync(&path, atime_secs, atime_nanos, mtime_secs, mtime_nanos)
}

fn write_file_sync(
&self,
path: &Path,
Expand Down
17 changes: 17 additions & 0 deletions ext/fs/interface.rs
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,23 @@ pub trait FileSystem: std::fmt::Debug + MaybeSend + MaybeSync {
mtime_nanos: u32,
) -> FsResult<()>;

fn lutime_sync(
&self,
path: &Path,
atime_secs: i64,
atime_nanos: u32,
mtime_secs: i64,
mtime_nanos: u32,
) -> FsResult<()>;
async fn lutime_async(
&self,
path: PathBuf,
atime_secs: i64,
atime_nanos: u32,
mtime_secs: i64,
mtime_nanos: u32,
) -> FsResult<()>;

fn write_file_sync(
&self,
path: &Path,
Expand Down
48 changes: 45 additions & 3 deletions ext/fs/std_fs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -274,6 +274,35 @@ impl FileSystem for RealFs {
.await?
}

fn lutime_sync(
&self,
path: &Path,
atime_secs: i64,
atime_nanos: u32,
mtime_secs: i64,
mtime_nanos: u32,
) -> FsResult<()> {
let atime = filetime::FileTime::from_unix_time(atime_secs, atime_nanos);
let mtime = filetime::FileTime::from_unix_time(mtime_secs, mtime_nanos);
filetime::set_symlink_file_times(path, atime, mtime).map_err(Into::into)
}

async fn lutime_async(
&self,
path: PathBuf,
atime_secs: i64,
atime_nanos: u32,
mtime_secs: i64,
mtime_nanos: u32,
) -> FsResult<()> {
let atime = filetime::FileTime::from_unix_time(atime_secs, atime_nanos);
let mtime = filetime::FileTime::from_unix_time(mtime_secs, mtime_nanos);
spawn_blocking(move || {
filetime::set_symlink_file_times(path, atime, mtime).map_err(Into::into)
})
.await?
}

fn write_file_sync(
&self,
path: &Path,
Expand Down Expand Up @@ -927,9 +956,14 @@ fn open_with_access_check(
};
(*access_check)(true, &path, &options)?;

// For windows
#[allow(unused_mut)]
let mut opts: fs::OpenOptions = open_options(options);
#[cfg(windows)]
{
// allow opening directories
use std::os::windows::fs::OpenOptionsExt;
opts.custom_flags(winapi::um::winbase::FILE_FLAG_BACKUP_SEMANTICS);
}

#[cfg(unix)]
{
// Don't follow symlinks on open -- we must always pass fully-resolved files
Expand All @@ -943,7 +977,15 @@ fn open_with_access_check(

Ok(opts.open(&path)?)
} else {
let opts = open_options(options);
// for unix
#[allow(unused_mut)]
let mut opts = open_options(options);
#[cfg(windows)]
{
// allow opening directories
use std::os::windows::fs::OpenOptionsExt;
opts.custom_flags(winapi::um::winbase::FILE_FLAG_BACKUP_SEMANTICS);
}
Ok(opts.open(path)?)
}
}
3 changes: 3 additions & 0 deletions ext/node/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -315,6 +315,8 @@ deno_core::extension!(deno_node,
ops::fs::op_node_fs_exists_sync<P>,
ops::fs::op_node_cp_sync<P>,
ops::fs::op_node_cp<P>,
ops::fs::op_node_lutimes_sync<P>,
ops::fs::op_node_lutimes<P>,
ops::fs::op_node_statfs<P>,
ops::winerror::op_node_sys_to_uv_error,
ops::v8::op_v8_cached_data_version_tag,
Expand Down Expand Up @@ -426,6 +428,7 @@ deno_core::extension!(deno_node,
"_fs/_fs_futimes.ts",
"_fs/_fs_link.ts",
"_fs/_fs_lstat.ts",
"_fs/_fs_lutimes.ts",
"_fs/_fs_mkdir.ts",
"_fs/_fs_mkdtemp.ts",
"_fs/_fs_open.ts",
Expand Down
51 changes: 51 additions & 0 deletions ext/node/ops/fs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -222,3 +222,54 @@ where
Err(anyhow!("Unsupported platform."))
}
}

#[op2(fast)]
pub fn op_node_lutimes_sync<P>(
state: &mut OpState,
#[string] path: &str,
#[number] atime_secs: i64,
#[smi] atime_nanos: u32,
#[number] mtime_secs: i64,
#[smi] mtime_nanos: u32,
) -> Result<(), AnyError>
where
P: NodePermissions + 'static,
{
let path = Path::new(path);

state
.borrow_mut::<P>()
.check_write_with_api_name(path, Some("node:fs.lutimes"))?;

let fs = state.borrow::<FileSystemRc>();
fs.lutime_sync(path, atime_secs, atime_nanos, mtime_secs, mtime_nanos)?;
Ok(())
}

#[op2(async)]
pub async fn op_node_lutimes<P>(
state: Rc<RefCell<OpState>>,
#[string] path: String,
#[number] atime_secs: i64,
#[smi] atime_nanos: u32,
#[number] mtime_secs: i64,
#[smi] mtime_nanos: u32,
) -> Result<(), AnyError>
where
P: NodePermissions + 'static,
{
let path = PathBuf::from(path);

let fs = {
let mut state = state.borrow_mut();
state
.borrow_mut::<P>()
.check_write_with_api_name(&path, Some("node:fs.lutimesSync"))?;
state.borrow::<FileSystemRc>().clone()
};

fs.lutime_async(path, atime_secs, atime_nanos, mtime_secs, mtime_nanos)
.await?;

Ok(())
}
16 changes: 15 additions & 1 deletion ext/node/polyfills/_fs/_fs_futimes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@

import type { CallbackWithError } from "ext:deno_node/_fs/_fs_common.ts";
import { FsFile } from "ext:deno_fs/30_fs.js";
import { validateInteger } from "ext:deno_node/internal/validators.mjs";
import { ERR_INVALID_ARG_TYPE } from "ext:deno_node/internal/errors.ts";
import { toUnixTimestamp } from "ext:deno_node/internal/fs/utils.mjs";

function getValidTime(
time: number | string | Date,
Expand All @@ -23,7 +26,7 @@ function getValidTime(
);
}

return time;
return toUnixTimestamp(time);
}

export function futimes(
Expand All @@ -35,6 +38,11 @@ export function futimes(
if (!callback) {
throw new Deno.errors.InvalidData("No callback function supplied");
}
if (typeof fd !== "number") {
throw new ERR_INVALID_ARG_TYPE("fd", "number", fd);
}

validateInteger(fd, "fd", 0, 2147483647);

atime = getValidTime(atime, "atime");
mtime = getValidTime(mtime, "mtime");
Expand All @@ -51,6 +59,12 @@ export function futimesSync(
atime: number | string | Date,
mtime: number | string | Date,
) {
if (typeof fd !== "number") {
throw new ERR_INVALID_ARG_TYPE("fd", "number", fd);
}

validateInteger(fd, "fd", 0, 2147483647);

atime = getValidTime(atime, "atime");
mtime = getValidTime(mtime, "mtime");

Expand Down
85 changes: 85 additions & 0 deletions ext/node/polyfills/_fs/_fs_lutimes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.

// deno-lint-ignore-file prefer-primordials

import type { CallbackWithError } from "ext:deno_node/_fs/_fs_common.ts";
import { type Buffer } from "node:buffer";
import { primordials } from "ext:core/mod.js";
import { op_node_lutimes, op_node_lutimes_sync } from "ext:core/ops";
import { promisify } from "ext:deno_node/internal/util.mjs";
import {
getValidatedPath,
toUnixTimestamp,
} from "ext:deno_node/internal/fs/utils.mjs";

const { MathTrunc } = primordials;

type TimeLike = number | string | Date;
type PathLike = string | Buffer | URL;

function getValidUnixTime(
value: TimeLike,
name: string,
): [number, number] {
if (typeof value === "string") {
value = Number(value);
}

if (
typeof value === "number" &&
(Number.isNaN(value) || !Number.isFinite(value))
) {
throw new Deno.errors.InvalidData(
`invalid ${name}, must not be infinity or NaN`,
);
}

const unixSeconds = toUnixTimestamp(value);

const seconds = MathTrunc(unixSeconds);
const nanoseconds = MathTrunc((unixSeconds * 1e3) - (seconds * 1e3)) * 1e6;

return [
seconds,
nanoseconds,
];
}

export function lutimes(
path: PathLike,
atime: TimeLike,
mtime: TimeLike,
callback: CallbackWithError,
): void {
if (!callback) {
throw new Error("No callback function supplied");
}
const [atimeSecs, atimeNanos] = getValidUnixTime(atime, "atime");
const [mtimeSecs, mtimeNanos] = getValidUnixTime(mtime, "mtime");

path = getValidatedPath(path).toString();

op_node_lutimes(path, atimeSecs, atimeNanos, mtimeSecs, mtimeNanos).then(
() => callback(null),
callback,
);
}

export function lutimesSync(
path: PathLike,
atime: TimeLike,
mtime: TimeLike,
): void {
const { 0: atimeSecs, 1: atimeNanos } = getValidUnixTime(atime, "atime");
const { 0: mtimeSecs, 1: mtimeNanos } = getValidUnixTime(mtime, "mtime");

path = getValidatedPath(path).toString();

op_node_lutimes_sync(path, atimeSecs, atimeNanos, mtimeSecs, mtimeNanos);
}

export const lutimesPromise = promisify(lutimes) as (
path: PathLike,
atime: TimeLike,
mtime: TimeLike,
) => Promise<void>;
Loading