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