diff --git a/src/header/grp/mod.rs b/src/header/grp/mod.rs index ec013240c5..75b7479710 100644 --- a/src/header/grp/mod.rs +++ b/src/header/grp/mod.rs @@ -38,6 +38,8 @@ const SEPARATOR: char = ':'; #[cfg(target_os = "redox")] const SEPARATOR: char = ';'; +const GROUP_FILE: &core::ffi::CStr = c"/etc/group"; + #[derive(Clone, Copy, Debug)] struct DestBuffer { ptr: *mut u8, @@ -249,7 +251,7 @@ fn parse_grp(line: String, destbuf: Option) -> Result *mut group { - let Ok(db) = File::open(c"/etc/group".into(), fcntl::O_RDONLY) else { + let Ok(db) = File::open(GROUP_FILE.into(), fcntl::O_RDONLY) else { return ptr::null_mut(); }; @@ -272,7 +274,7 @@ pub unsafe extern "C" fn getgrgid(gid: gid_t) -> *mut group { // MT-Unsafe race:grnam locale #[no_mangle] pub unsafe extern "C" fn getgrnam(name: *const c_char) -> *mut group { - let Ok(db) = File::open(c"/etc/group".into(), fcntl::O_RDONLY) else { + let Ok(db) = File::open(GROUP_FILE.into(), fcntl::O_RDONLY) else { return ptr::null_mut(); }; @@ -314,7 +316,7 @@ pub unsafe extern "C" fn getgrgid_r( *result = ptr::null_mut(); } - let Ok(db) = File::open(c"/etc/group".into(), fcntl::O_RDONLY) else { + let Ok(db) = File::open(GROUP_FILE.into(), fcntl::O_RDONLY) else { return ENOENT; }; @@ -368,7 +370,7 @@ pub unsafe extern "C" fn getgrnam_r( buflen: usize, result: *mut *mut group, ) -> c_int { - let Ok(db) = File::open(c"/etc/group".into(), fcntl::O_RDONLY) else { + let Ok(db) = File::open(GROUP_FILE.into(), fcntl::O_RDONLY) else { return ENOENT; }; @@ -409,7 +411,7 @@ pub unsafe extern "C" fn getgrent() -> *mut group { let mut line_reader = unsafe { &mut *LINE_READER.get() }; if line_reader.is_none() { - let Ok(db) = File::open(c"/etc/group".into(), fcntl::O_RDONLY) else { + let Ok(db) = File::open(GROUP_FILE.into(), fcntl::O_RDONLY) else { return ptr::null_mut(); }; *line_reader = Some(BufReader::new(db).lines()); @@ -445,7 +447,7 @@ pub unsafe extern "C" fn endgrent() { #[no_mangle] pub unsafe extern "C" fn setgrent() { let mut line_reader = unsafe { &mut *LINE_READER.get() }; - let Ok(db) = File::open(c"/etc/group".into(), fcntl::O_RDONLY) else { + let Ok(db) = File::open(GROUP_FILE.into(), fcntl::O_RDONLY) else { return; }; *line_reader = Some(BufReader::new(db).lines()); @@ -471,7 +473,7 @@ pub unsafe extern "C" fn getgrouplist( return 0; }; - let Ok(db) = File::open(c"/etc/group".into(), fcntl::O_RDONLY) else { + let Ok(db) = File::open(GROUP_FILE.into(), fcntl::O_RDONLY) else { return 0; }; diff --git a/src/header/mod.rs b/src/header/mod.rs index 4ad22103f3..fec4f937b9 100644 --- a/src/header/mod.rs +++ b/src/header/mod.rs @@ -61,6 +61,7 @@ pub mod sched; pub mod semaphore; pub mod setjmp; pub mod sgtty; +pub mod shadow; pub mod signal; // TODO: spawn.h // TODO: stdalign.h (likely C implementation) diff --git a/src/header/shadow/cbindgen.toml b/src/header/shadow/cbindgen.toml new file mode 100644 index 0000000000..f826c22c6a --- /dev/null +++ b/src/header/shadow/cbindgen.toml @@ -0,0 +1,12 @@ +sys_includes = ["sys/types.h", "stdint.h"] +include_guard = "_RELIBC_SHADOW_H" +language = "C" +style = "Tag" +no_includes = true +cpp_compat = true + +[enum] +prefix_with_name = true + +[export] +include = ["group"] diff --git a/src/header/shadow/mod.rs b/src/header/shadow/mod.rs new file mode 100644 index 0000000000..369b81f019 --- /dev/null +++ b/src/header/shadow/mod.rs @@ -0,0 +1,271 @@ +#![deny(unsafe_op_in_unsafe_fn)] + +use core::{ + cell::SyncUnsafeCell, + convert::TryInto, + mem, + ops::{Deref, DerefMut}, + pin::Pin, + ptr, + str::FromStr, +}; + +use alloc::{boxed::Box, string::String, vec::Vec}; + +use crate::{ + c_str::CStr, + fs::File, + header::{errno, fcntl, string::strlen}, + io::{prelude::*, BufReader, Lines}, + platform, + platform::types::*, +}; + +use super::errno::*; + +#[cfg(target_os = "linux")] +const SEPARATOR: char = ':'; + +#[cfg(target_os = "redox")] +const SEPARATOR: char = ';'; + +const SHADOW_FILE: &core::ffi::CStr = c"/etc/shadow"; + +#[derive(Clone, Copy, Debug)] +struct DestBuffer { + ptr: *mut u8, + len: usize, +} + +#[derive(Debug)] +enum MaybeAllocated { + Owned(Pin>), + Borrowed(DestBuffer), +} +impl Deref for MaybeAllocated { + type Target = [u8]; + + fn deref(&self) -> &Self::Target { + match self { + MaybeAllocated::Owned(boxed) => boxed, + MaybeAllocated::Borrowed(dst) => unsafe { + core::slice::from_raw_parts(dst.ptr, dst.len) + }, + } + } +} +impl DerefMut for MaybeAllocated { + fn deref_mut(&mut self) -> &mut Self::Target { + match self { + MaybeAllocated::Owned(boxed) => boxed, + MaybeAllocated::Borrowed(dst) => unsafe { + core::slice::from_raw_parts_mut(dst.ptr, dst.len) + }, + } + } +} + +static mut SHADOW_BUF: Option = None; +static mut SHADOW: spwd = spwd { + sp_namp: ptr::null_mut(), + sp_pwdp: ptr::null_mut(), + sp_lstchg: -1, + sp_min: -1, + sp_max: -1, + sp_warn: -1, + sp_inact: -1, + sp_expire: -1, + sp_flag: 0, +}; + +static LINE_READER: SyncUnsafeCell>>> = SyncUnsafeCell::new(None); + +#[repr(C)] +#[derive(Debug, Clone, Copy)] +pub struct spwd { + pub sp_namp: *mut c_char, + pub sp_pwdp: *mut c_char, + pub sp_lstchg: c_long, + pub sp_min: c_long, + pub sp_max: c_long, + pub sp_warn: c_long, + pub sp_inact: c_long, + pub sp_expire: c_long, + pub sp_flag: c_ulong, +} + +#[derive(Debug)] +enum Error { + EOF, + BufTooSmall, + Syntax, +} + +#[derive(Debug)] +struct OwnedSpwd { + buffer: MaybeAllocated, + reference: spwd, +} + +impl OwnedSpwd { + fn into_global(self) -> *mut spwd { + unsafe { + SHADOW_BUF = Some(self.buffer); + SHADOW = self.reference; + &mut SHADOW + } + } +} +fn to_long(s: &str) -> c_long { + c_long::from_str(s).unwrap_or(-1) +} +fn to_ulong(s: &str) -> c_ulong { + c_ulong::from_str(s).unwrap_or(0) +} + +fn parse_spwd(line: String, destbuf: Option) -> Result { + let mut parts = line.split(SEPARATOR); + + let sp_namp_str = parts.next().ok_or(Error::Syntax)?; + let sp_pwdp_str = parts.next().ok_or(Error::Syntax)?; + + //TODO: these are not implemented redox-users crate + let sp_lstchg = to_long(parts.next().unwrap_or("")); + let sp_min = to_long(parts.next().unwrap_or("")); + let sp_max = to_long(parts.next().unwrap_or("")); + let sp_warn = to_long(parts.next().unwrap_or("")); + let sp_inact = to_long(parts.next().unwrap_or("")); + let sp_expire = to_long(parts.next().unwrap_or("")); + let sp_flag = to_ulong(parts.next().unwrap_or("")); + + let string_data_len = sp_namp_str.len() + 1 + sp_pwdp_str.len() + 1; + + let mut buffer = match destbuf { + Some(buf) => { + if buf.len < string_data_len { + platform::ERRNO.set(ERANGE); + return Err(Error::BufTooSmall); + } + MaybeAllocated::Borrowed(buf) + } + None => { + let mut vec = Vec::with_capacity(string_data_len); + vec.resize(string_data_len, 0); + MaybeAllocated::Owned(Box::into_pin(vec.into_boxed_slice())) + } + }; + + let (name_slice, rest) = buffer.split_at_mut(sp_namp_str.len() + 1); + name_slice[..sp_namp_str.len()].copy_from_slice(sp_namp_str.as_bytes()); + name_slice[sp_namp_str.len()] = 0; + + let (pwd_slice, _) = rest.split_at_mut(sp_pwdp_str.len() + 1); + pwd_slice[..sp_pwdp_str.len()].copy_from_slice(sp_pwdp_str.as_bytes()); + pwd_slice[sp_pwdp_str.len()] = 0; + + let sp_namp = name_slice.as_mut_ptr() as *mut c_char; + let sp_pwdp = pwd_slice.as_mut_ptr() as *mut c_char; + + let reference = spwd { + sp_namp, + sp_pwdp, + sp_lstchg, + sp_min, + sp_max, + sp_warn, + sp_inact, + sp_expire, + sp_flag, + }; + + Ok(OwnedSpwd { buffer, reference }) +} + +#[no_mangle] +pub unsafe extern "C" fn getspnam(name: *const c_char) -> *mut spwd { + let Ok(db) = File::open(SHADOW_FILE.into(), fcntl::O_RDONLY) else { + return ptr::null_mut(); + }; + let c_name = unsafe { CStr::from_ptr(name) }; + + for line in BufReader::new(db).lines() { + let Ok(line) = line else { continue }; + if line.starts_with(c_name.to_str().unwrap_or("\0")) { + if let Ok(pwd) = parse_spwd(line, None) { + return pwd.into_global(); + } + } + } + ptr::null_mut() +} + +#[no_mangle] +pub unsafe extern "C" fn getspnam_r( + name: *const c_char, + result_buf: *mut spwd, + buffer: *mut c_char, + buflen: size_t, + result: *mut *mut spwd, +) -> c_int { + unsafe { *result = ptr::null_mut() }; + + let Ok(db) = File::open(SHADOW_FILE.into(), fcntl::O_RDONLY) else { + return ENOENT; + }; + let c_name = unsafe { CStr::from_ptr(name).to_str().unwrap_or("\0") }; + + for line in BufReader::new(db).lines() { + let Ok(line) = line else { continue }; + if line.starts_with(c_name) { + let dest_buf = Some(DestBuffer { + ptr: buffer as *mut u8, + len: buflen, + }); + return match parse_spwd(line, dest_buf) { + Ok(sp) => { + unsafe { + *result_buf = sp.reference; + *result = result_buf; + } + 0 + } + Err(Error::BufTooSmall) => ERANGE, + _ => ENOENT, + }; + } + } + ENOENT +} + +#[no_mangle] +pub unsafe extern "C" fn setspent() { + let mut line_reader = unsafe { &mut *LINE_READER.get() }; + if let Ok(db) = File::open(SHADOW_FILE.into(), fcntl::O_RDONLY) { + *line_reader = Some(BufReader::new(db).lines()); + } +} + +#[no_mangle] +pub unsafe extern "C" fn endspent() { + unsafe { + *LINE_READER.get() = None; + } +} + +#[no_mangle] +pub unsafe extern "C" fn getspent() -> *mut spwd { + let line_reader = unsafe { &mut *LINE_READER.get() }; + if line_reader.is_none() { + unsafe { + setspent(); + } + } + if let Some(lines) = line_reader { + if let Some(Ok(line)) = lines.next() { + if let Ok(sp) = parse_spwd(line, None) { + return sp.into_global(); + } + } + } + ptr::null_mut() +}