feat(cfg): turn groupnames into gids at parse time
This commit is contained in:
103
src/serdes/inner_helpers.rs
Normal file
103
src/serdes/inner_helpers.rs
Normal file
@@ -0,0 +1,103 @@
|
||||
use std::ffi::CString;
|
||||
|
||||
use libc::gid_t;
|
||||
use serde::{Deserialize, Deserializer, Serialize, de::Error};
|
||||
|
||||
/// Private module until its worth it to promote from size to a crate module.
|
||||
mod __libc_wrappers {
|
||||
use std::{ffi::CStr, mem::MaybeUninit};
|
||||
|
||||
use libc::c_char;
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum GetgrnamError {
|
||||
#[error("no matching group record found")]
|
||||
NoMatchingRecord,
|
||||
/// ace variant for unexpected errors, error handling is shallow here
|
||||
#[error("unexpected getgrnam_r error")]
|
||||
Unexpected,
|
||||
}
|
||||
|
||||
pub fn get_group_by_name(cname: &CStr) -> Result<libc::gid_t, GetgrnamError> {
|
||||
use GetgrnamError as E;
|
||||
|
||||
let name = cname.as_ptr();
|
||||
let mut grp = MaybeUninit::uninit();
|
||||
let mut buf = [c_char::from(0); 1024];
|
||||
let mut result: MaybeUninit<*mut libc::group> = MaybeUninit::uninit();
|
||||
let ret = unsafe {
|
||||
libc::getgrnam_r(
|
||||
name,
|
||||
grp.as_mut_ptr(),
|
||||
buf.as_mut_ptr(),
|
||||
buf.len(),
|
||||
result.as_mut_ptr(),
|
||||
)
|
||||
};
|
||||
|
||||
// SAFETY: Pointers inside `grp` point may point to the buffer `buf` so they cannot be
|
||||
// moved outside of this function.
|
||||
|
||||
// > On success, getgrnam_r() [..] return zero, and set `*result` to `grp`.
|
||||
//
|
||||
// > If no matching group record was found, these functions return 0 and store NULL in
|
||||
// `*result`.
|
||||
//
|
||||
// > In case of error, an error number is returned, and NULL stored in `*result`.
|
||||
//
|
||||
// SAFETY: Either way, `result` is initialized to a nullable pointer.
|
||||
let result_ptr = unsafe { result.assume_init() };
|
||||
if ret == 0 && result_ptr == grp.as_mut_ptr() {
|
||||
let grp_gid = unsafe { grp.assume_init() }.gr_gid;
|
||||
Ok(grp_gid)
|
||||
} else if ret == 0 && result_ptr.is_null() {
|
||||
Err(E::NoMatchingRecord)
|
||||
} else {
|
||||
// ret should be != 0 and result NULL, but any other case is also unexpected and I'm
|
||||
// not doing much FFI error handling rn
|
||||
Err(E::Unexpected)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
#[serde(untagged)]
|
||||
enum EitherTyp {
|
||||
Gid(gid_t),
|
||||
Groupname(String),
|
||||
}
|
||||
|
||||
/// Deserialize either a gid number or a groupname into a [`gid_t`]
|
||||
#[allow(clippy::missing_errors_doc)]
|
||||
pub fn deserialize_group<'de, D>(d: D) -> Result<gid_t, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
let either = EitherTyp::deserialize(d)?;
|
||||
|
||||
match either {
|
||||
EitherTyp::Gid(gid) => Ok(gid),
|
||||
EitherTyp::Groupname(groupname) => {
|
||||
use __libc_wrappers::GetgrnamError as E;
|
||||
use serde::de::Unexpected;
|
||||
|
||||
let cname = CString::new(groupname).map_err(|nul_err| {
|
||||
D::Error::invalid_value(
|
||||
Unexpected::Bytes(&nul_err.into_vec()),
|
||||
&"a string without null bytes",
|
||||
)
|
||||
})?;
|
||||
|
||||
let gid = __libc_wrappers::get_group_by_name(&cname).map_err(|err| match err {
|
||||
E::NoMatchingRecord => D::Error::invalid_value(
|
||||
Unexpected::Other("non-existent group"),
|
||||
&"existent group",
|
||||
),
|
||||
E::Unexpected => D::Error::custom("caught an unexpected getgrnam error"),
|
||||
})?;
|
||||
|
||||
Ok(gid)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
16
src/serdes/mod.rs
Normal file
16
src/serdes/mod.rs
Normal file
@@ -0,0 +1,16 @@
|
||||
use std::fmt::Debug;
|
||||
|
||||
use libc::gid_t;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
pub mod inner_helpers;
|
||||
|
||||
#[repr(transparent)]
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct Group(#[serde(deserialize_with = "inner_helpers::deserialize_group")] pub gid_t);
|
||||
|
||||
impl Debug for Group {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
Debug::fmt(&self.0, f)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user