summaryrefslogtreecommitdiff
path: root/ext/node/ops/os
diff options
context:
space:
mode:
authorhaturau <135221985+haturatu@users.noreply.github.com>2024-11-20 01:20:47 +0900
committerGitHub <noreply@github.com>2024-11-20 01:20:47 +0900
commit85719a67e59c7aa45bead26e4942d7df8b1b42d4 (patch)
treeface0aecaac53e93ce2f23b53c48859bcf1a36ec /ext/node/ops/os
parent67697bc2e4a62a9670699fd18ad0dd8efc5bd955 (diff)
parent186b52731c6bb326c4d32905c5e732d082e83465 (diff)
Merge branch 'denoland:main' into main
Diffstat (limited to 'ext/node/ops/os')
-rw-r--r--ext/node/ops/os/mod.rs194
-rw-r--r--ext/node/ops/os/priority.rs30
2 files changed, 198 insertions, 26 deletions
diff --git a/ext/node/ops/os/mod.rs b/ext/node/ops/os/mod.rs
index ca91895f2..d291277ad 100644
--- a/ext/node/ops/os/mod.rs
+++ b/ext/node/ops/os/mod.rs
@@ -1,19 +1,31 @@
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
+use std::mem::MaybeUninit;
+
use crate::NodePermissions;
-use deno_core::error::type_error;
-use deno_core::error::AnyError;
use deno_core::op2;
use deno_core::OpState;
mod cpus;
-mod priority;
+pub mod priority;
+
+#[derive(Debug, thiserror::Error)]
+pub enum OsError {
+ #[error(transparent)]
+ Priority(priority::PriorityError),
+ #[error(transparent)]
+ Permission(#[from] deno_permissions::PermissionCheckError),
+ #[error("Failed to get cpu info")]
+ FailedToGetCpuInfo,
+ #[error("Failed to get user info")]
+ FailedToGetUserInfo(#[source] std::io::Error),
+}
#[op2(fast)]
pub fn op_node_os_get_priority<P>(
state: &mut OpState,
pid: u32,
-) -> Result<i32, AnyError>
+) -> Result<i32, OsError>
where
P: NodePermissions + 'static,
{
@@ -22,7 +34,7 @@ where
permissions.check_sys("getPriority", "node:os.getPriority()")?;
}
- priority::get_priority(pid)
+ priority::get_priority(pid).map_err(OsError::Priority)
}
#[op2(fast)]
@@ -30,7 +42,7 @@ pub fn op_node_os_set_priority<P>(
state: &mut OpState,
pid: u32,
priority: i32,
-) -> Result<(), AnyError>
+) -> Result<(), OsError>
where
P: NodePermissions + 'static,
{
@@ -39,25 +51,171 @@ where
permissions.check_sys("setPriority", "node:os.setPriority()")?;
}
- priority::set_priority(pid, priority)
+ priority::set_priority(pid, priority).map_err(OsError::Priority)
+}
+
+#[derive(serde::Serialize)]
+pub struct UserInfo {
+ username: String,
+ homedir: String,
+ shell: Option<String>,
+}
+
+#[cfg(unix)]
+fn get_user_info(uid: u32) -> Result<UserInfo, OsError> {
+ use std::ffi::CStr;
+ let mut pw: MaybeUninit<libc::passwd> = MaybeUninit::uninit();
+ let mut result: *mut libc::passwd = std::ptr::null_mut();
+ // SAFETY: libc call, no invariants
+ let max_buf_size = unsafe { libc::sysconf(libc::_SC_GETPW_R_SIZE_MAX) };
+ let buf_size = if max_buf_size < 0 {
+ // from the man page
+ 16_384
+ } else {
+ max_buf_size as usize
+ };
+ let mut buf = {
+ let mut b = Vec::<MaybeUninit<libc::c_char>>::with_capacity(buf_size);
+ // SAFETY: MaybeUninit has no initialization invariants, and len == cap
+ unsafe {
+ b.set_len(buf_size);
+ }
+ b
+ };
+ // SAFETY: libc call, args are correct
+ let s = unsafe {
+ libc::getpwuid_r(
+ uid,
+ pw.as_mut_ptr(),
+ buf.as_mut_ptr().cast(),
+ buf_size,
+ std::ptr::addr_of_mut!(result),
+ )
+ };
+ if result.is_null() {
+ if s != 0 {
+ return Err(
+ OsError::FailedToGetUserInfo(std::io::Error::last_os_error()),
+ );
+ } else {
+ return Err(OsError::FailedToGetUserInfo(std::io::Error::from(
+ std::io::ErrorKind::NotFound,
+ )));
+ }
+ }
+ // SAFETY: pw was initialized by the call to `getpwuid_r` above
+ let pw = unsafe { pw.assume_init() };
+ // SAFETY: initialized above, pw alive until end of function, nul terminated
+ let username = unsafe { CStr::from_ptr(pw.pw_name) };
+ // SAFETY: initialized above, pw alive until end of function, nul terminated
+ let homedir = unsafe { CStr::from_ptr(pw.pw_dir) };
+ // SAFETY: initialized above, pw alive until end of function, nul terminated
+ let shell = unsafe { CStr::from_ptr(pw.pw_shell) };
+ Ok(UserInfo {
+ username: username.to_string_lossy().into_owned(),
+ homedir: homedir.to_string_lossy().into_owned(),
+ shell: Some(shell.to_string_lossy().into_owned()),
+ })
+}
+
+#[cfg(windows)]
+fn get_user_info(_uid: u32) -> Result<UserInfo, OsError> {
+ use std::ffi::OsString;
+ use std::os::windows::ffi::OsStringExt;
+
+ use windows_sys::Win32::Foundation::CloseHandle;
+ use windows_sys::Win32::Foundation::GetLastError;
+ use windows_sys::Win32::Foundation::ERROR_INSUFFICIENT_BUFFER;
+ use windows_sys::Win32::Foundation::HANDLE;
+ use windows_sys::Win32::System::Threading::GetCurrentProcess;
+ use windows_sys::Win32::System::Threading::OpenProcessToken;
+ use windows_sys::Win32::UI::Shell::GetUserProfileDirectoryW;
+ struct Handle(HANDLE);
+ impl Drop for Handle {
+ fn drop(&mut self) {
+ // SAFETY: win32 call
+ unsafe {
+ CloseHandle(self.0);
+ }
+ }
+ }
+ let mut token: MaybeUninit<HANDLE> = MaybeUninit::uninit();
+
+ // Get a handle to the current process
+ // SAFETY: win32 call
+ unsafe {
+ if OpenProcessToken(
+ GetCurrentProcess(),
+ windows_sys::Win32::Security::TOKEN_READ,
+ token.as_mut_ptr(),
+ ) == 0
+ {
+ return Err(
+ OsError::FailedToGetUserInfo(std::io::Error::last_os_error()),
+ );
+ }
+ }
+
+ // SAFETY: initialized by call above
+ let token = Handle(unsafe { token.assume_init() });
+
+ let mut bufsize = 0;
+ // get the size for the homedir buf (it'll end up in `bufsize`)
+ // SAFETY: win32 call
+ unsafe {
+ GetUserProfileDirectoryW(token.0, std::ptr::null_mut(), &mut bufsize);
+ let err = GetLastError();
+ if err != ERROR_INSUFFICIENT_BUFFER {
+ return Err(OsError::FailedToGetUserInfo(
+ std::io::Error::from_raw_os_error(err as i32),
+ ));
+ }
+ }
+ let mut path = vec![0; bufsize as usize];
+ // Actually get the homedir
+ // SAFETY: path is `bufsize` elements
+ unsafe {
+ if GetUserProfileDirectoryW(token.0, path.as_mut_ptr(), &mut bufsize) == 0 {
+ return Err(
+ OsError::FailedToGetUserInfo(std::io::Error::last_os_error()),
+ );
+ }
+ }
+ // remove trailing nul
+ path.pop();
+ let homedir_wide = OsString::from_wide(&path);
+ let homedir = homedir_wide.to_string_lossy().into_owned();
+
+ Ok(UserInfo {
+ username: deno_whoami::username(),
+ homedir,
+ shell: None,
+ })
}
#[op2]
-#[string]
-pub fn op_node_os_username<P>(state: &mut OpState) -> Result<String, AnyError>
+#[serde]
+pub fn op_node_os_user_info<P>(
+ state: &mut OpState,
+ #[smi] uid: u32,
+) -> Result<UserInfo, OsError>
where
P: NodePermissions + 'static,
{
{
let permissions = state.borrow_mut::<P>();
- permissions.check_sys("username", "node:os.userInfo()")?;
+ permissions
+ .check_sys("userInfo", "node:os.userInfo()")
+ .map_err(OsError::Permission)?;
}
- Ok(deno_whoami::username())
+ get_user_info(uid)
}
#[op2(fast)]
-pub fn op_geteuid<P>(state: &mut OpState) -> Result<u32, AnyError>
+pub fn op_geteuid<P>(
+ state: &mut OpState,
+) -> Result<u32, deno_core::error::AnyError>
where
P: NodePermissions + 'static,
{
@@ -76,7 +234,9 @@ where
}
#[op2(fast)]
-pub fn op_getegid<P>(state: &mut OpState) -> Result<u32, AnyError>
+pub fn op_getegid<P>(
+ state: &mut OpState,
+) -> Result<u32, deno_core::error::AnyError>
where
P: NodePermissions + 'static,
{
@@ -96,7 +256,7 @@ where
#[op2]
#[serde]
-pub fn op_cpus<P>(state: &mut OpState) -> Result<Vec<cpus::CpuInfo>, AnyError>
+pub fn op_cpus<P>(state: &mut OpState) -> Result<Vec<cpus::CpuInfo>, OsError>
where
P: NodePermissions + 'static,
{
@@ -105,12 +265,14 @@ where
permissions.check_sys("cpus", "node:os.cpus()")?;
}
- cpus::cpu_info().ok_or_else(|| type_error("Failed to get cpu info"))
+ cpus::cpu_info().ok_or(OsError::FailedToGetCpuInfo)
}
#[op2]
#[string]
-pub fn op_homedir<P>(state: &mut OpState) -> Result<Option<String>, AnyError>
+pub fn op_homedir<P>(
+ state: &mut OpState,
+) -> Result<Option<String>, deno_core::error::AnyError>
where
P: NodePermissions + 'static,
{
diff --git a/ext/node/ops/os/priority.rs b/ext/node/ops/os/priority.rs
index 043928e2a..9a1ebcca7 100644
--- a/ext/node/ops/os/priority.rs
+++ b/ext/node/ops/os/priority.rs
@@ -1,12 +1,18 @@
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
-use deno_core::error::AnyError;
-
pub use impl_::*;
+#[derive(Debug, thiserror::Error)]
+pub enum PriorityError {
+ #[error("{0}")]
+ Io(#[from] std::io::Error),
+ #[cfg(windows)]
+ #[error("Invalid priority")]
+ InvalidPriority,
+}
+
#[cfg(unix)]
mod impl_ {
- use super::*;
use errno::errno;
use errno::set_errno;
use errno::Errno;
@@ -16,7 +22,7 @@ mod impl_ {
const PRIORITY_HIGH: i32 = -14;
// Ref: https://github.com/libuv/libuv/blob/55376b044b74db40772e8a6e24d67a8673998e02/src/unix/core.c#L1533-L1547
- pub fn get_priority(pid: u32) -> Result<i32, AnyError> {
+ pub fn get_priority(pid: u32) -> Result<i32, super::PriorityError> {
set_errno(Errno(0));
match (
// SAFETY: libc::getpriority is unsafe
@@ -29,7 +35,10 @@ mod impl_ {
}
}
- pub fn set_priority(pid: u32, priority: i32) -> Result<(), AnyError> {
+ pub fn set_priority(
+ pid: u32,
+ priority: i32,
+ ) -> Result<(), super::PriorityError> {
// SAFETY: libc::setpriority is unsafe
match unsafe { libc::setpriority(PRIO_PROCESS, pid as id_t, priority) } {
-1 => Err(std::io::Error::last_os_error().into()),
@@ -40,8 +49,6 @@ mod impl_ {
#[cfg(windows)]
mod impl_ {
- use super::*;
- use deno_core::error::type_error;
use winapi::shared::minwindef::DWORD;
use winapi::shared::minwindef::FALSE;
use winapi::shared::ntdef::NULL;
@@ -67,7 +74,7 @@ mod impl_ {
const PRIORITY_HIGHEST: i32 = -20;
// Ported from: https://github.com/libuv/libuv/blob/a877ca2435134ef86315326ef4ef0c16bdbabf17/src/win/util.c#L1649-L1685
- pub fn get_priority(pid: u32) -> Result<i32, AnyError> {
+ pub fn get_priority(pid: u32) -> Result<i32, super::PriorityError> {
// SAFETY: Windows API calls
unsafe {
let handle = if pid == 0 {
@@ -95,7 +102,10 @@ mod impl_ {
}
// Ported from: https://github.com/libuv/libuv/blob/a877ca2435134ef86315326ef4ef0c16bdbabf17/src/win/util.c#L1688-L1719
- pub fn set_priority(pid: u32, priority: i32) -> Result<(), AnyError> {
+ pub fn set_priority(
+ pid: u32,
+ priority: i32,
+ ) -> Result<(), super::PriorityError> {
// SAFETY: Windows API calls
unsafe {
let handle = if pid == 0 {
@@ -109,7 +119,7 @@ mod impl_ {
#[allow(clippy::manual_range_contains)]
let priority_class =
if priority < PRIORITY_HIGHEST || priority > PRIORITY_LOW {
- return Err(type_error("Invalid priority"));
+ return Err(super::PriorityError::InvalidPriority);
} else if priority < PRIORITY_HIGH {
REALTIME_PRIORITY_CLASS
} else if priority < PRIORITY_ABOVE_NORMAL {