1use super::*;
4type ByteBuffer = Vec<MaybeUninit<c_char>>;
8
9#[must_use]
11pub(crate) struct ByteBufToken {}
12
13pub(crate) trait FromLibc<Input>: Sized {
19 unsafe fn from_libc(
26 input: Input,
27 buffer_live: &'_ ByteBufToken,
28 ) -> Result<Self, io::Error>;
29}
30
31type InnerCall<'c> = &'c mut dyn FnMut(
38 &mut [MaybeUninit<c_char>],
40) -> c_int;
41
42pub(crate) type LookupCall<'c, L> = &'c mut dyn FnMut(
53 *mut L,
55 *mut *mut L,
57 *mut c_char,
59 size_t,
61) -> c_int;
62
63pub(crate) fn call_repeatedly_with_bigger_buffer<'b>(
77 mlibc: impl MockableLibc,
78 buffer: &'b mut ByteBuffer,
79 sysconf: c_int,
80 call: InnerCall,
81) -> Result<&'b ByteBufToken, io::Error> {
82 let mut want_size: usize = cmp::max(
83 unsafe { (mlibc.sysconf)(sysconf) }.try_into().unwrap_or(0),
84 100,
85 );
86 loop {
87 buffer.resize(want_size, MaybeUninit::uninit());
88
89 let r = call(buffer);
90 if r == 0 {
91 return Ok(&ByteBufToken {});
92 }
93 if r != libc::ERANGE {
94 return Err(io::Error::from_raw_os_error(r));
95 }
96 want_size = buffer
97 .len()
98 .checked_mul(2)
99 .ok_or(TooLargeBufferRequiredError)?;
100 }
101}
102
103impl FromLibc<Id> for Id {
104 unsafe fn from_libc(
105 input: Id,
106 _: &ByteBufToken,
107 ) -> Result<Self, io::Error> {
108 Ok(input)
109 }
110}
111
112impl FromLibc<*mut c_char> for RawSafe {
113 unsafe fn from_libc(
114 input: *mut c_char,
115 _buffer_live: &ByteBufToken,
116 ) -> Result<Self, io::Error> {
117 let input: *const c_char = input as _;
118 if input.is_null() {
119 return Err(UnexpectedNullPointerError.into());
120 }
121 let input = CStr::from_ptr(input);
122 Ok(input.to_bytes().into())
123 }
124}
125
126impl FromLibc<*mut *mut c_char> for Box<[RawSafe]> {
128 unsafe fn from_libc(
129 input: *mut *mut c_char,
130 buffer_live: &ByteBufToken,
131 ) -> Result<Self, io::Error> {
132 if input.is_null() {
133 return Err(UnexpectedNullPointerError.into());
134 }
135 let pointers = (0..)
136 .map(|offset| input.offset(offset).read())
137 .take_while(|pointer| !pointer.is_null());
138
139 let mut output = Vec::with_capacity(pointers.clone().count());
140 for pointer in pointers {
141 output.push(FromLibc::from_libc(pointer, buffer_live)?);
142 }
143 Ok(output.into())
144 }
145}
146
147define_derive_deftly! {
148 FromLibc for struct, expect items:
149
150 impl FromLibc<NonNull<libc::${snake_case $tname}>>
151 for $tname<RawSafe>
152 {
153 unsafe fn from_libc(
154 input: NonNull<libc::${snake_case $tname}>,
156 buffer_live: &ByteBufToken,
157 ) -> Result<Self, io::Error> {
158 let input = input.as_ref();
159 let output = $tname { $( ${select1 fmeta(dummy) {
162 $fname: NonExhaustive {}
163 } else {
164 $fname: {
165 let p = input
166 .${paste ${tmeta(abbrev) as str} _ $fname};
168
169 FromLibc::from_libc(p, buffer_live)?
170 },
171 }})};
172 Ok(output)
173 }
174 }
175}
176
177define_derive_deftly! {
178 Lookup for struct, expect items:
179
180 impl $tname<RawSafe> {
181 fn lookup(
187 mlibc: impl MockableLibc,
188 call: LookupCall<libc::${snake_case $tname}>
189 ) -> io::Result<Option<Self>> {
190 #[allow(non_camel_case_types)]
191 type libc_struct = libc::${snake_case $tname};
192
193 let mut buffer = Default::default();
194 let mut out_buf = MaybeUninit::<libc_struct>::uninit();
195 let mut result = MaybeUninit::<*mut libc_struct>::uninit();
196
197 let out_buf = &mut out_buf;
201 let result = &mut result;
202
203 let sysconf = libc::
205 ${paste _SC_GET
206 ${shouty_snake_case ${tmeta(abbrev) as str}}
207 _R_SIZE_MAX};
208
209 let buffer_live = call_repeatedly_with_bigger_buffer(
210 mlibc,
211 &mut buffer,
212 sysconf,
213 &mut |buffer| {
214 let buffer_len = buffer.len();
219 let buffer: *mut MaybeUninit<c_char> = buffer.as_mut_ptr();
220 let buffer: *mut c_char = buffer as _;
221 call(
222 out_buf.as_mut_ptr(),
223 result.as_mut_ptr(),
224 buffer,
225 buffer_len,
226 )
227 },
228 )?;
229
230 let result: *mut libc_struct = unsafe { result.assume_init() };
236
237 let result = match NonNull::new(result) {
238 None => return Ok(None),
239 Some(y) => y,
240 };
241
242 let result: $tname<RawSafe> = unsafe {
246 FromLibc::from_libc(result, buffer_live)
247 }?;
248
249 Ok(Some(result))
250 }
251 }
252}
253
254pub(crate) fn getpwnam_inner<ML: MockableLibc>(
255 mlibc: ML,
256 name: &[u8],
257) -> io::Result<Option<Passwd<RawSafe>>> {
258 let name = cstring_from(name)?;
259
260 Passwd::lookup(mlibc, &mut |out_buf, result, buf, buflen| unsafe {
261 (mlibc.getpwnam_r)(name.as_ptr() as _, out_buf, buf, buflen, result)
262 })
263}
264
265pub(crate) fn getpwuid_inner<ML: MockableLibc>(
266 mlibc: ML,
267 uid: Id,
268) -> io::Result<Option<Passwd<RawSafe>>> {
269 Passwd::lookup(mlibc, &mut |out_buf, result, buf, buflen| unsafe {
270 (mlibc.getpwuid_r)(uid, out_buf, buf, buflen, result)
271 })
272}
273
274pub(crate) fn getgrnam_inner<ML: MockableLibc>(
275 mlibc: ML,
276 name: &[u8],
277) -> io::Result<Option<Group<RawSafe>>> {
278 let name = cstring_from(name)?;
279
280 Group::lookup(mlibc, &mut |out_buf, result, buf, buflen| unsafe {
281 (mlibc.getgrnam_r)(name.as_ptr() as _, out_buf, buf, buflen, result)
282 })
283}
284
285pub(crate) fn getgrgid_inner<ML: MockableLibc>(
286 mlibc: ML,
287 gid: Id,
288) -> io::Result<Option<Group<RawSafe>>> {
289 Group::lookup(mlibc, &mut |out_buf, result, buf, buflen| unsafe {
290 (mlibc.getgrgid_r)(gid, out_buf, buf, buflen, result)
291 })
292}
293
294pub(crate) fn getgroups_inner(
295 mlibc: impl MockableLibc,
296) -> io::Result<Vec<Id>> {
297 let overflow = |_| TooLargeBufferRequiredError;
300
301 let mut want_size = 0;
302 let mut buffer: Vec<Id> = vec![];
303 loop {
304 buffer.reserve(want_size);
305 buffer.truncate(0);
306 let r = unsafe {
307 let size = buffer.capacity().try_into().map_err(overflow)?;
308 let list: *mut Id = buffer.as_mut_ptr();
309 (mlibc.getgroups)(size, list)
310 };
311 if r < 0 {
312 let error = io::Error::last_os_error();
313 if error.raw_os_error() == Some(libc::EINVAL) {
314 want_size = 0;
316 continue;
317 }
318 return Err(error);
319 }
320 let r: usize = r.try_into().map_err(overflow)?;
321
322 if r <= buffer.capacity() {
323 unsafe { buffer.set_len(r) };
324 return Ok(buffer);
325 }
326
327 want_size = r;
329 }
330}
331
332macro_rules! define_getid_unsafe { {
333 $fn:ident: $id:ident. $f:ident, $doc:literal $( $real:literal )?
334} => { paste!{
335 #[inline]
336 pub(crate) fn [<$fn _inner>](
337 mlibc: impl MockableLibc,
338 ) -> Id {
339 unsafe { (mlibc.$fn)() }
342 }
343} }; {
344 $fn:ident: $id:ident. ($( $f:ident )*), $doc:literal $( $real:literal )?
345} => { paste!{
346 #[inline]
347 pub(crate) fn [<$fn _inner>](
348 mlibc: impl MockableLibc,
349 ) -> (Id, Id, Id) {
350 let mut buf: (Id, Id, Id) = Default::default();
351
352 let r = unsafe {
353 (mlibc.$fn)(
354 &mut buf.0,
355 &mut buf.1,
356 &mut buf.2,
357 )
358 };
359 assert!(r == 0, "getres* failed!");
360
361 buf
362 }
363} } }
364
365for_getid_wrappers! { define_getid_unsafe }