diff options
author | Nathan Whitaker <17734409+nathanwhit@users.noreply.github.com> | 2024-07-05 11:32:51 -0700 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-07-05 18:32:51 +0000 |
commit | d4d3a3c54f5e26dec0cc79e273dc488f8a47f2b3 (patch) | |
tree | e6ff88b550211257ea7a7997e221d10fdf22e242 /ext/node | |
parent | 28d2ff7bdc023a3b7aff47503aa03a8dd65fe87f (diff) |
fix(node): Implement `fs.lchown` (and `process.getegid`) (#24418)
Closes https://github.com/denoland/deno/issues/21260.
Part of https://github.com/denoland/deno/issues/18218.
Implements `node:fs.lchown`, and enables the node_compat test for it.
The test uses `process.getegid`, which we didn't have implemented, so I
went ahead and implemented that as well to get the test working.
Diffstat (limited to 'ext/node')
-rw-r--r-- | ext/node/lib.rs | 4 | ||||
-rw-r--r-- | ext/node/ops/fs.rs | 41 | ||||
-rw-r--r-- | ext/node/ops/os/mod.rs | 19 | ||||
-rw-r--r-- | ext/node/polyfills/_fs/_fs_lchown.ts | 61 | ||||
-rw-r--r-- | ext/node/polyfills/fs.ts | 9 | ||||
-rw-r--r-- | ext/node/polyfills/process.ts | 10 |
6 files changed, 141 insertions, 3 deletions
diff --git a/ext/node/lib.rs b/ext/node/lib.rs index 01c464df1..e86413346 100644 --- a/ext/node/lib.rs +++ b/ext/node/lib.rs @@ -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_lchown_sync<P>, + ops::fs::op_node_lchown<P>, ops::fs::op_node_lutimes_sync<P>, ops::fs::op_node_lutimes<P>, ops::fs::op_node_statfs<P>, @@ -365,6 +367,7 @@ deno_core::extension!(deno_node, ops::os::op_node_os_set_priority<P>, ops::os::op_node_os_username<P>, ops::os::op_geteuid<P>, + ops::os::op_getegid<P>, ops::os::op_cpus<P>, ops::os::op_homedir<P>, op_node_build_os, @@ -426,6 +429,7 @@ deno_core::extension!(deno_node, "_fs/_fs_fsync.ts", "_fs/_fs_ftruncate.ts", "_fs/_fs_futimes.ts", + "_fs/_fs_lchown.ts", "_fs/_fs_link.ts", "_fs/_fs_lstat.ts", "_fs/_fs_lutimes.ts", diff --git a/ext/node/ops/fs.rs b/ext/node/ops/fs.rs index 304a6c253..47b66ee1d 100644 --- a/ext/node/ops/fs.rs +++ b/ext/node/ops/fs.rs @@ -273,3 +273,44 @@ where Ok(()) } + +#[op2] +pub fn op_node_lchown_sync<P>( + state: &mut OpState, + #[string] path: String, + uid: Option<u32>, + gid: Option<u32>, +) -> Result<(), AnyError> +where + P: NodePermissions + 'static, +{ + let path = PathBuf::from(path); + state + .borrow_mut::<P>() + .check_write_with_api_name(&path, Some("node:fs.lchownSync"))?; + let fs = state.borrow::<FileSystemRc>(); + fs.lchown_sync(&path, uid, gid)?; + Ok(()) +} + +#[op2(async)] +pub async fn op_node_lchown<P>( + state: Rc<RefCell<OpState>>, + #[string] path: String, + uid: Option<u32>, + gid: Option<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.lchown"))?; + state.borrow::<FileSystemRc>().clone() + }; + fs.lchown_async(path, uid, gid).await?; + Ok(()) +} diff --git a/ext/node/ops/os/mod.rs b/ext/node/ops/os/mod.rs index 5b32113e5..b7374dc32 100644 --- a/ext/node/ops/os/mod.rs +++ b/ext/node/ops/os/mod.rs @@ -75,6 +75,25 @@ where Ok(euid) } +#[op2(fast)] +pub fn op_getegid<P>(state: &mut OpState) -> Result<u32, AnyError> +where + P: NodePermissions + 'static, +{ + { + let permissions = state.borrow_mut::<P>(); + permissions.check_sys("getegid", "node:os.getegid()")?; + } + + #[cfg(windows)] + let egid = 0; + #[cfg(unix)] + // SAFETY: Call to libc getegid. + let egid = unsafe { libc::getegid() }; + + Ok(egid) +} + #[op2] #[serde] pub fn op_cpus<P>(state: &mut OpState) -> Result<Vec<cpus::CpuInfo>, AnyError> diff --git a/ext/node/polyfills/_fs/_fs_lchown.ts b/ext/node/polyfills/_fs/_fs_lchown.ts new file mode 100644 index 000000000..8611c8021 --- /dev/null +++ b/ext/node/polyfills/_fs/_fs_lchown.ts @@ -0,0 +1,61 @@ +// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. + +// TODO(petamoriken): enable prefer-primordials for node polyfills +// deno-lint-ignore-file prefer-primordials + +import { + type CallbackWithError, + makeCallback, +} from "ext:deno_node/_fs/_fs_common.ts"; +import { + getValidatedPath, + kMaxUserId, +} from "ext:deno_node/internal/fs/utils.mjs"; +import * as pathModule from "node:path"; +import { validateInteger } from "ext:deno_node/internal/validators.mjs"; +import type { Buffer } from "node:buffer"; +import { promisify } from "ext:deno_node/internal/util.mjs"; +import { op_node_lchown, op_node_lchown_sync } from "ext:core/ops"; + +/** + * Asynchronously changes the owner and group + * of a file, without following symlinks. + */ +export function lchown( + path: string | Buffer | URL, + uid: number, + gid: number, + callback: CallbackWithError, +) { + callback = makeCallback(callback); + path = getValidatedPath(path).toString(); + validateInteger(uid, "uid", -1, kMaxUserId); + validateInteger(gid, "gid", -1, kMaxUserId); + + op_node_lchown(pathModule.toNamespacedPath(path), uid, gid).then( + () => callback(null), + callback, + ); +} + +export const lchownPromise = promisify(lchown) as ( + path: string | Buffer | URL, + uid: number, + gid: number, +) => Promise<void>; + +/** + * Synchronously changes the owner and group + * of a file, without following symlinks. + */ +export function lchownSync( + path: string | Buffer | URL, + uid: number, + gid: number, +) { + path = getValidatedPath(path).toString(); + validateInteger(uid, "uid", -1, kMaxUserId); + validateInteger(gid, "gid", -1, kMaxUserId); + + op_node_lchown_sync(pathModule.toNamespacedPath(path), uid, gid); +} diff --git a/ext/node/polyfills/fs.ts b/ext/node/polyfills/fs.ts index 6f0c53e4d..7a3cf4e67 100644 --- a/ext/node/polyfills/fs.ts +++ b/ext/node/polyfills/fs.ts @@ -27,6 +27,11 @@ import { fstat, fstatSync } from "ext:deno_node/_fs/_fs_fstat.ts"; import { fsync, fsyncSync } from "ext:deno_node/_fs/_fs_fsync.ts"; import { ftruncate, ftruncateSync } from "ext:deno_node/_fs/_fs_ftruncate.ts"; import { futimes, futimesSync } from "ext:deno_node/_fs/_fs_futimes.ts"; +import { + lchown, + lchownPromise, + lchownSync, +} from "ext:deno_node/_fs/_fs_lchown.ts"; import { link, linkPromise, linkSync } from "ext:deno_node/_fs/_fs_link.ts"; import { lstat, lstatPromise, lstatSync } from "ext:deno_node/_fs/_fs_lstat.ts"; import { @@ -173,7 +178,7 @@ const promises = { unlink: unlinkPromise, chmod: chmodPromise, // lchmod: promisify(lchmod), - // lchown: promisify(lchown), + lchown: lchownPromise, chown: chownPromise, utimes: utimesPromise, lutimes: lutimesPromise, @@ -218,6 +223,8 @@ export default { ftruncateSync, futimes, futimesSync, + lchown, + lchownSync, link, linkSync, lstat, diff --git a/ext/node/polyfills/process.ts b/ext/node/polyfills/process.ts index 02837f827..de48fea3e 100644 --- a/ext/node/polyfills/process.ts +++ b/ext/node/polyfills/process.ts @@ -7,6 +7,7 @@ import { core, internals } from "ext:core/mod.js"; import { initializeDebugEnv } from "ext:deno_node/internal/util/debuglog.ts"; import { + op_getegid, op_geteuid, op_node_process_kill, op_process_abort, @@ -309,15 +310,16 @@ export function kill(pid: number, sig: string | number = "SIGTERM") { return true; } -let getgid, getuid, geteuid; +let getgid, getuid, getegid, geteuid; if (!isWindows) { getgid = () => Deno.gid(); getuid = () => Deno.uid(); + getegid = () => op_getegid(); geteuid = () => op_geteuid(); } -export { geteuid, getgid, getuid }; +export { getegid, geteuid, getgid, getuid }; const ALLOWED_FLAGS = buildAllowedFlags(); @@ -686,6 +688,9 @@ Process.prototype.getgid = getgid; Process.prototype.getuid = getuid; /** This method is removed on Windows */ +Process.prototype.getegid = getegid; + +/** This method is removed on Windows */ Process.prototype.geteuid = geteuid; // TODO(kt3k): Implement this when we added -e option to node compat mode @@ -726,6 +731,7 @@ Process.prototype.noDeprecation = false; if (isWindows) { delete Process.prototype.getgid; delete Process.prototype.getuid; + delete Process.prototype.getegid; delete Process.prototype.geteuid; } |