xref: /drstd/dlibc/src/unix/header/pwd/mod.rs (revision 69bbf99969c635b975633fbae5786a97353ca9ae)
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