From bba553bea5938932518dc6382e464968ce8374b4 Mon Sep 17 00:00:00 2001 From: Divy Srivastava Date: Wed, 15 May 2024 22:22:40 -0700 Subject: [PATCH] fix(ext/node): homedir() `getpwuid`/`SHGetKnownFolderPath` fallback (#23841) **Unix**: Returns the value of the HOME environment variable if it is set even if it is an empty string. Otherwise, it tries to determine the home directory by invoking the [getpwuid_r](https://linux.die.net/man/3/getpwuid_r) function with the UID of the current user. **Windows**: Returns the value of the USERPROFILE environment variable if it is set and it is not an empty string. Otherwise, it tries to determine the home directory by invoking the [SHGetKnownFolderPath](https://learn.microsoft.com/en-us/windows/win32/api/shlobj_core/nf-shlobj_core-shgetknownfolderpath) function with [FOLDERID_Profile](https://learn.microsoft.com/en-us/windows/win32/shell/knownfolderid). Fixes https://github.com/denoland/deno/issues/23824 --- Cargo.lock | 1 + ext/node/Cargo.toml | 1 + ext/node/lib.rs | 1 + ext/node/ops/os/mod.rs | 14 ++++++++++++++ ext/node/polyfills/os.ts | 19 +++---------------- runtime/permissions/lib.rs | 2 +- tests/unit_node/os_test.ts | 8 ++++++++ 7 files changed, 29 insertions(+), 17 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 1d31de8be7..2dd72b3534 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1680,6 +1680,7 @@ dependencies = [ "faster-hex", "h2 0.3.26", "hkdf", + "home", "http 0.2.12", "idna 0.3.0", "indexmap", diff --git a/ext/node/Cargo.toml b/ext/node/Cargo.toml index acc5e758cf..f930f3f3ae 100644 --- a/ext/node/Cargo.toml +++ b/ext/node/Cargo.toml @@ -36,6 +36,7 @@ errno = "0.2.8" faster-hex.workspace = true h2 = { version = "0.3.26", features = ["unstable"] } hkdf.workspace = true +home = "0.5.9" http_v02.workspace = true idna = "0.3.0" indexmap.workspace = true diff --git a/ext/node/lib.rs b/ext/node/lib.rs index 43a5b158e3..b4eeb71c25 100644 --- a/ext/node/lib.rs +++ b/ext/node/lib.rs @@ -309,6 +309,7 @@ deno_core::extension!(deno_node, ops::os::op_node_os_username

, ops::os::op_geteuid

, ops::os::op_cpus

, + ops::os::op_homedir

, op_node_build_os, op_node_is_promise_rejected, op_npm_process_state, diff --git a/ext/node/ops/os/mod.rs b/ext/node/ops/os/mod.rs index 603f678e0b..5b32113e5b 100644 --- a/ext/node/ops/os/mod.rs +++ b/ext/node/ops/os/mod.rs @@ -88,3 +88,17 @@ where cpus::cpu_info().ok_or_else(|| type_error("Failed to get cpu info")) } + +#[op2] +#[string] +pub fn op_homedir

(state: &mut OpState) -> Result, AnyError> +where + P: NodePermissions + 'static, +{ + { + let permissions = state.borrow_mut::

(); + permissions.check_sys("homedir", "node:os.homedir()")?; + } + + Ok(home::home_dir().map(|path| path.to_string_lossy().to_string())) +} diff --git a/ext/node/polyfills/os.ts b/ext/node/polyfills/os.ts index bc88b06015..753e393198 100644 --- a/ext/node/polyfills/os.ts +++ b/ext/node/polyfills/os.ts @@ -25,6 +25,7 @@ import { op_cpus, + op_homedir, op_node_os_get_priority, op_node_os_set_priority, op_node_os_username, @@ -32,7 +33,7 @@ import { import { validateIntegerRange } from "ext:deno_node/_utils.ts"; import process from "node:process"; -import { isWindows, osType } from "ext:deno_node/_util/os.ts"; +import { isWindows } from "ext:deno_node/_util/os.ts"; import { ERR_OS_NO_HOMEDIR } from "ext:deno_node/internal/errors.ts"; import { os } from "ext:deno_node/internal_binding/constants.ts"; import { osUptime } from "ext:runtime/30_os.js"; @@ -173,21 +174,7 @@ export function getPriority(pid = 0): number { /** Returns the string path of the current user's home directory. */ export function homedir(): string | null { - // Note: Node/libuv calls getpwuid() / GetUserProfileDirectory() when the - // environment variable isn't set but that's the (very uncommon) fallback - // path. IMO, it's okay to punt on that for now. - switch (osType) { - case "windows": - return Deno.env.get("USERPROFILE") || null; - case "linux": - case "android": - case "darwin": - case "freebsd": - case "openbsd": - return Deno.env.get("HOME") || null; - default: - throw Error("unreachable"); - } + return op_homedir(); } /** Returns the host name of the operating system as a string. */ diff --git a/runtime/permissions/lib.rs b/runtime/permissions/lib.rs index 06db9958c8..2e94e3aec3 100644 --- a/runtime/permissions/lib.rs +++ b/runtime/permissions/lib.rs @@ -913,7 +913,7 @@ impl Descriptor for SysDescriptor { pub fn parse_sys_kind(kind: &str) -> Result<&str, AnyError> { match kind { "hostname" | "osRelease" | "osUptime" | "loadavg" | "networkInterfaces" - | "systemMemoryInfo" | "uid" | "gid" | "cpus" => Ok(kind), + | "systemMemoryInfo" | "uid" | "gid" | "cpus" | "homedir" => Ok(kind), _ => Err(type_error(format!("unknown system info kind \"{kind}\""))), } } diff --git a/tests/unit_node/os_test.ts b/tests/unit_node/os_test.ts index 9ce9fc9eb7..810c225186 100644 --- a/tests/unit_node/os_test.ts +++ b/tests/unit_node/os_test.ts @@ -47,6 +47,14 @@ Deno.test({ }, }); +Deno.test({ + name: "home directory when HOME is not set", + fn() { + Deno.env.delete("HOME"); + assertEquals(typeof os.homedir(), "string"); + }, +}); + Deno.test({ name: "tmp directory is a string", fn() {