Skip to content
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
3 changes: 2 additions & 1 deletion ext/node/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -361,6 +361,7 @@ deno_core::extension!(deno_node,
ops::fs::op_node_mkdtemp<P>,
ops::fs::op_node_open_sync<P>,
ops::fs::op_node_open<P>,
ops::fs::op_node_statfs_sync<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 @@ -530,7 +531,7 @@ deno_core::extension!(deno_node,
"_fs/_fs_rm.ts",
"_fs/_fs_rmdir.ts",
"_fs/_fs_stat.ts",
"_fs/_fs_statfs.js",
"_fs/_fs_statfs.ts",
"_fs/_fs_symlink.ts",
"_fs/_fs_truncate.ts",
"_fs/_fs_unlink.ts",
Expand Down
38 changes: 35 additions & 3 deletions ext/node/ops/fs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,11 @@ use std::rc::Rc;
use deno_core::OpState;
use deno_core::ResourceId;
use deno_core::op2;
use deno_core::unsync::spawn_blocking;
use deno_fs::FileSystemRc;
use deno_fs::OpenOptions;
use deno_io::fs::FileResource;
use deno_permissions::CheckedPath;
use deno_permissions::OpenAccessKind;
use serde::Serialize;

Expand Down Expand Up @@ -272,18 +274,40 @@ pub struct StatFs {

#[op2(stack_trace)]
#[serde]
pub fn op_node_statfs<P>(
state: Rc<RefCell<OpState>>,
pub fn op_node_statfs_sync<P>(
state: &mut OpState,
#[string] path: &str,
bigint: bool,
) -> Result<StatFs, FsError>
where
P: NodePermissions + 'static,
{
let path = state.borrow_mut::<P>().check_open(
Cow::Borrowed(Path::new(path)),
OpenAccessKind::ReadNoFollow,
Some("node:fs.statfsSync"),
)?;
state
.borrow_mut::<P>()
.check_sys("statfs", "node:fs.statfsSync")?;

statfs(path, bigint)
}

#[op2(async, stack_trace)]
#[serde]
pub async fn op_node_statfs<P>(
state: Rc<RefCell<OpState>>,
#[string] path: String,
bigint: bool,
) -> Result<StatFs, FsError>
where
P: NodePermissions + 'static,
{
let path = {
let mut state = state.borrow_mut();
let path = state.borrow_mut::<P>().check_open(
Cow::Borrowed(Path::new(path)),
Cow::Owned(PathBuf::from(path)),
OpenAccessKind::ReadNoFollow,
Some("node:fs.statfs"),
)?;
Expand All @@ -292,6 +316,14 @@ where
.check_sys("statfs", "node:fs.statfs")?;
path
};

match spawn_blocking(move || statfs(path, bigint)).await {
Ok(result) => result,
Err(err) => Err(FsError::Io(err.into())),
}
}

fn statfs(path: CheckedPath, bigint: bool) -> Result<StatFs, FsError> {
#[cfg(unix)]
{
use std::os::unix::ffi::OsStrExt;
Expand Down
56 changes: 0 additions & 56 deletions ext/node/polyfills/_fs/_fs_statfs.js

This file was deleted.

172 changes: 172 additions & 0 deletions ext/node/polyfills/_fs/_fs_statfs.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
// Copyright 2018-2025 the Deno authors. MIT license.

import { BigInt } from "ext:deno_node/internal/primordials.mjs";
import { op_node_statfs, op_node_statfs_sync } from "ext:core/ops";
import { promisify } from "ext:deno_node/internal/util.mjs";
import type { Buffer } from "node:buffer";
import { getValidatedPathToString } from "ext:deno_node/internal/fs/utils.mjs";
import { makeCallback } from "ext:deno_node/_fs/_fs_common.ts";
import { denoErrorToNodeError } from "ext:deno_node/internal/errors.ts";
import { primordials } from "ext:core/mod.js";

const { PromisePrototypeThen } = primordials;

type StatFsPromise = (
path: string | Buffer | URL,
options?: { bigint?: false },
) => Promise<StatFs<number>>;
type StatFsBigIntPromise = (
path: string | Buffer | URL,
options: { bigint: true },
) => Promise<StatFs<bigint>>;

type StatFsCallback<T> = (err: Error | null, stats?: StatFs<T>) => void;

type StatFsOptions = {
bigint?: boolean;
};

class StatFs<T> {
type: T;
bsize: T;
blocks: T;
bfree: T;
bavail: T;
files: T;
ffree: T;
constructor(
type: T,
bsize: T,
blocks: T,
bfree: T,
bavail: T,
files: T,
ffree: T,
) {
this.type = type;
this.bsize = bsize;
this.blocks = blocks;
this.bfree = bfree;
this.bavail = bavail;
this.files = files;
this.ffree = ffree;
}
}

type OpResult = {
type: number;
bsize: number;
blocks: number;
bfree: number;
bavail: number;
files: number;
ffree: number;
};

function opResultToStatFs(result: OpResult, bigint: true): StatFs<bigint>;
function opResultToStatFs(
result: OpResult,
bigint: false,
): StatFs<number>;
function opResultToStatFs(
result: OpResult,
bigint: boolean,
): StatFs<bigint> | StatFs<number> {
if (!bigint) {
return new StatFs(
result.type,
result.bsize,
result.blocks,
result.bfree,
result.bavail,
result.files,
result.ffree,
);
}
return new StatFs(
BigInt(result.type),
BigInt(result.bsize),
BigInt(result.blocks),
BigInt(result.bfree),
BigInt(result.bavail),
BigInt(result.files),
BigInt(result.ffree),
);
}

export function statfs(
path: string | Buffer | URL,
callback: StatFsCallback<number>,
): void;
export function statfs(
path: string | Buffer | URL,
options: { bigint?: false },
callback: StatFsCallback<number>,
): void;
export function statfs(
path: string | Buffer | URL,
options: { bigint: true },
callback: StatFsCallback<bigint>,
): void;
export function statfs(
path: string | Buffer | URL,
options: StatFsOptions | StatFsCallback<number> | undefined,
callback?: StatFsCallback<number> | StatFsCallback<bigint>,
): void {
if (typeof options === "function") {
callback = options;
options = undefined;
}
// @ts-expect-error callback type is known to be valid
callback = makeCallback(callback);
path = getValidatedPathToString(path);
const bigint = typeof options?.bigint === "boolean" ? options.bigint : false;

PromisePrototypeThen(
op_node_statfs(path, bigint),
(statFs) => {
callback(
null,
opResultToStatFs(statFs, bigint),
);
},
(err: Error) =>
callback(denoErrorToNodeError(err, {
syscall: "statfs",
path,
})),
);
}

export function statfsSync(
path: string | Buffer | URL,
options?: { bigint?: false },
): StatFs<number>;
export function statfsSync(
path: string | Buffer | URL,
options: { bigint: true },
): StatFs<bigint>;
export function statfsSync(
path: string | Buffer | URL,
options?: StatFsOptions,
): StatFs<number> | StatFs<bigint> {
path = getValidatedPathToString(path);
const bigint = typeof options?.bigint === "boolean" ? options.bigint : false;

try {
const statFs = op_node_statfs_sync(
path,
bigint,
);
return opResultToStatFs(statFs, bigint);
} catch (err) {
throw denoErrorToNodeError(err as Error, {
syscall: "statfs",
path,
});
}
}

export const statfsPromise = promisify(statfs) as (
StatFsPromise & StatFsBigIntPromise
);
2 changes: 1 addition & 1 deletion ext/node/polyfills/fs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ import {
statfs,
statfsPromise,
statfsSync,
} from "ext:deno_node/_fs/_fs_statfs.js";
} from "ext:deno_node/_fs/_fs_statfs.ts";
import {
symlink,
symlinkPromise,
Expand Down
1 change: 1 addition & 0 deletions tests/node_compat/config.toml
Original file line number Diff line number Diff line change
Expand Up @@ -479,6 +479,7 @@
"parallel/test-fs-ready-event-stream.js" = {}
"parallel/test-fs-rename-type-check.js" = {}
"parallel/test-fs-rmdir-type-check.js" = {}
"parallel/test-fs-statfs.js" = {}
"parallel/test-fs-stream-fs-options.js" = {}
"parallel/test-fs-stream-options.js" = {}
"parallel/test-fs-symlink-buffer-path.js" = {}
Expand Down
Loading
Loading