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