1 //! pwd implementation for relibc 2 3 use alloc::{boxed::Box, vec::Vec}; 4 use core::{ 5 ops::{Deref, DerefMut}, 6 pin::Pin, 7 ptr, 8 }; 9 10 use crate::{ 11 fs::File, 12 header::{errno, fcntl, string::strcmp}, 13 io::{prelude::*, BufReader, SeekFrom}, 14 platform::{self, types::*}, 15 }; 16 17 #[cfg(target_os = "linux")] 18 mod linux; 19 20 #[cfg(target_os = "dragonos")] 21 mod dragonos; 22 #[cfg(target_os = "redox")] 23 mod redox; 24 25 #[cfg(target_os = "dragonos")] 26 use self::dragonos as sys; 27 #[cfg(target_os = "linux")] 28 use self::linux as sys; 29 #[cfg(target_os = "redox")] 30 use self::redox as sys; 31 32 #[repr(C)] 33 #[derive(Debug)] 34 pub struct passwd { 35 pw_name: *mut c_char, 36 pw_passwd: *mut c_char, 37 pw_uid: uid_t, 38 pw_gid: gid_t, 39 pw_gecos: *mut c_char, 40 pw_dir: *mut c_char, 41 pw_shell: *mut c_char, 42 } 43 44 static mut PASSWD_BUF: Option<MaybeAllocated> = None; 45 static mut PASSWD: passwd = passwd { 46 pw_name: ptr::null_mut(), 47 pw_passwd: ptr::null_mut(), 48 pw_uid: 0, 49 pw_gid: 0, 50 pw_gecos: ptr::null_mut(), 51 pw_dir: ptr::null_mut(), 52 pw_shell: ptr::null_mut(), 53 }; 54 55 #[derive(Clone, Copy, Debug)] 56 struct DestBuffer { 57 ptr: *mut u8, 58 len: usize, 59 } 60 61 #[derive(Debug)] 62 enum MaybeAllocated { 63 Owned(Pin<Box<[u8]>>), 64 Borrowed(DestBuffer), 65 } 66 impl Deref for MaybeAllocated { 67 type Target = [u8]; 68 69 fn deref(&self) -> &Self::Target { 70 match self { 71 MaybeAllocated::Owned(boxed) => boxed, 72 MaybeAllocated::Borrowed(dst) => unsafe { 73 core::slice::from_raw_parts(dst.ptr, dst.len) 74 }, 75 } 76 } 77 } 78 impl DerefMut for MaybeAllocated { 79 fn deref_mut(&mut self) -> &mut Self::Target { 80 match self { 81 MaybeAllocated::Owned(boxed) => boxed, 82 MaybeAllocated::Borrowed(dst) => unsafe { 83 core::slice::from_raw_parts_mut(dst.ptr, dst.len) 84 }, 85 } 86 } 87 } 88 89 #[derive(Debug)] 90 struct OwnedPwd { 91 buffer: MaybeAllocated, 92 reference: passwd, 93 } 94 95 impl OwnedPwd { 96 fn into_global(self) -> *mut passwd { 97 unsafe { 98 PASSWD_BUF = Some(self.buffer); 99 PASSWD = self.reference; 100 &mut PASSWD 101 } 102 } 103 } 104 105 #[derive(Clone, Copy, Debug)] 106 enum Cause { 107 Eof, 108 Other, 109 } 110 111 static mut READER: Option<BufReader<File>> = None; 112 113 fn parsed<I, O>(buf: Option<I>) -> Option<O> 114 where 115 I: core::borrow::Borrow<[u8]>, 116 O: core::str::FromStr, 117 { 118 let buf = buf?; 119 let string = core::str::from_utf8(buf.borrow()).ok()?; 120 string.parse().ok() 121 } 122 123 fn getpwent_r( 124 reader: &mut BufReader<File>, 125 destination: Option<DestBuffer>, 126 ) -> Result<OwnedPwd, Cause> { 127 let mut buf = Vec::new(); 128 if reader 129 .read_until(b'\n', &mut buf) 130 .map_err(|_| Cause::Other)? 131 == 0 132 { 133 return Err(Cause::Eof); 134 } 135 136 // Replace all occurences of ':' with terminating NUL byte 137 let mut start = 0; 138 while let Some(i) = memchr::memchr(b':', &buf[start..]) { 139 buf[start + i] = 0; 140 start += i + 1; 141 } 142 143 // Place terminating NUL byte at the end, replace newline 144 let last = buf.last_mut(); 145 if last == Some(&mut b'\n') { 146 *last.unwrap() = 0; 147 } else { 148 buf.push(0); 149 } 150 151 let mut buf = match destination { 152 None => MaybeAllocated::Owned(Box::into_pin(buf.into_boxed_slice())), 153 Some(dst) => { 154 let mut new = MaybeAllocated::Borrowed(dst); 155 if new.len() < buf.len() { 156 unsafe { 157 platform::errno = errno::ERANGE; 158 } 159 return Err(Cause::Other); 160 } 161 new[..buf.len()].copy_from_slice(&buf); 162 new 163 } 164 }; 165 166 // Chop up the result into a valid structure 167 let passwd = sys::split(&mut buf).ok_or(Cause::Other)?; 168 169 Ok(OwnedPwd { 170 buffer: buf, 171 reference: passwd, 172 }) 173 } 174 175 fn pwd_lookup<F>(mut matches: F, destination: Option<DestBuffer>) -> Result<OwnedPwd, Cause> 176 where 177 F: FnMut(&passwd) -> bool, 178 { 179 let file = match File::open(c_str!("/etc/passwd"), fcntl::O_RDONLY) { 180 Ok(file) => file, 181 Err(_) => return Err(Cause::Other), 182 }; 183 184 let mut reader = BufReader::new(file); 185 186 loop { 187 let entry = getpwent_r(&mut reader, destination)?; 188 189 if matches(&entry.reference) { 190 return Ok(entry); 191 } 192 } 193 } 194 195 unsafe fn mux( 196 status: Result<OwnedPwd, Cause>, 197 out: *mut passwd, 198 result: *mut *mut passwd, 199 ) -> c_int { 200 match status { 201 Ok(owned) => { 202 *out = owned.reference; 203 *result = out; 204 0 205 } 206 Err(Cause::Eof) => { 207 *result = ptr::null_mut(); 208 0 209 } 210 Err(Cause::Other) => { 211 *result = ptr::null_mut(); 212 -1 213 } 214 } 215 } 216 217 #[no_mangle] 218 pub unsafe extern "C" fn getpwnam_r( 219 name: *const c_char, 220 out: *mut passwd, 221 buf: *mut c_char, 222 size: size_t, 223 result: *mut *mut passwd, 224 ) -> c_int { 225 mux( 226 pwd_lookup( 227 |parts| strcmp(parts.pw_name, name) == 0, 228 Some(DestBuffer { 229 ptr: buf as *mut u8, 230 len: size, 231 }), 232 ), 233 out, 234 result, 235 ) 236 } 237 238 #[no_mangle] 239 pub unsafe extern "C" fn getpwuid_r( 240 uid: uid_t, 241 out: *mut passwd, 242 buf: *mut c_char, 243 size: size_t, 244 result: *mut *mut passwd, 245 ) -> c_int { 246 let slice = core::slice::from_raw_parts_mut(buf as *mut u8, size); 247 mux( 248 pwd_lookup( 249 |part| part.pw_uid == uid, 250 Some(DestBuffer { 251 ptr: buf as *mut u8, 252 len: size, 253 }), 254 ), 255 out, 256 result, 257 ) 258 } 259 260 #[no_mangle] 261 pub extern "C" fn getpwnam(name: *const c_char) -> *mut passwd { 262 pwd_lookup(|parts| unsafe { strcmp(parts.pw_name, name) } == 0, None) 263 .map(|res| res.into_global()) 264 .unwrap_or(ptr::null_mut()) 265 } 266 267 #[no_mangle] 268 pub extern "C" fn getpwuid(uid: uid_t) -> *mut passwd { 269 pwd_lookup(|parts| parts.pw_uid == uid, None) 270 .map(|res| res.into_global()) 271 .unwrap_or(ptr::null_mut()) 272 } 273 274 #[no_mangle] 275 pub extern "C" fn getpwent() -> *mut passwd { 276 let reader = match unsafe { &mut READER } { 277 Some(reader) => reader, 278 None => { 279 let file = match File::open(c_str!("/etc/passwd"), fcntl::O_RDONLY) { 280 Ok(file) => file, 281 Err(_) => return ptr::null_mut(), 282 }; 283 let reader = BufReader::new(file); 284 unsafe { 285 READER = Some(reader); 286 READER.as_mut().unwrap() 287 } 288 } 289 }; 290 getpwent_r(reader, None) 291 .map(|res| res.into_global()) 292 .unwrap_or(ptr::null_mut()) 293 } 294 295 #[no_mangle] 296 pub extern "C" fn setpwent() { 297 if let Some(reader) = unsafe { &mut READER } { 298 let _ = reader.seek(SeekFrom::Start(0)); 299 } 300 } 301 302 #[no_mangle] 303 pub extern "C" fn endpwent() { 304 unsafe { 305 READER = None; 306 } 307 } 308