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