use crate::failx::*;
use crate::unix;
use std::fmt;
use std::io;
#[cfg(target_os = "macos")]
use uuid::Uuid;
#[cfg(any(target_os = "linux", target_os = "freebsd"))]
const OWNER_NAME: &str = "";
#[cfg(any(target_os = "linux", target_os = "freebsd"))]
const OTHER_NAME: &str = "";
#[cfg(any(target_os = "linux", target_os = "freebsd"))]
const MASK_NAME: &str = "";
#[cfg(target_os = "freebsd")]
const EVERYONE_NAME: &str = "";
#[derive(Debug, PartialEq, Eq)]
pub enum Qualifier {
User(unix::uid_t),
Group(unix::gid_t),
#[cfg(target_os = "macos")]
Guid(Uuid),
#[cfg(any(target_os = "linux", target_os = "freebsd"))]
UserObj,
#[cfg(any(target_os = "linux", target_os = "freebsd"))]
GroupObj,
#[cfg(any(target_os = "linux", target_os = "freebsd"))]
Other,
#[cfg(any(target_os = "linux", target_os = "freebsd"))]
Mask,
#[cfg(target_os = "freebsd")]
Everyone,
Unknown(String),
}
impl Qualifier {
#[cfg(target_os = "macos")]
pub fn from_guid(guid: Uuid) -> io::Result<Qualifier> {
let qualifier = match unix::guid_to_id(guid)? {
(Some(uid), None) => Qualifier::User(uid),
(None, Some(gid)) => Qualifier::Group(gid),
(None, None) => Qualifier::Guid(guid),
_ => unreachable!("guid_to_id bug"),
};
Ok(qualifier)
}
#[cfg(target_os = "macos")]
pub fn user_named(name: &str) -> io::Result<Qualifier> {
match unix::name_to_uid(name) {
Ok(uid) => Ok(Qualifier::User(uid)),
Err(err) => {
Uuid::parse_str(name).map_or(Err(err), Qualifier::from_guid)
}
}
}
#[cfg(any(target_os = "linux", target_os = "freebsd"))]
pub fn user_named(name: &str) -> io::Result<Qualifier> {
match name {
OWNER_NAME => Ok(Qualifier::UserObj),
s => match unix::name_to_uid(s) {
Ok(uid) => Ok(Qualifier::User(uid)),
Err(err) => Err(err),
},
}
}
#[cfg(target_os = "macos")]
pub fn group_named(name: &str) -> io::Result<Qualifier> {
match unix::name_to_gid(name) {
Ok(gid) => Ok(Qualifier::Group(gid)),
Err(err) => Uuid::parse_str(name).map_or(Err(err), Qualifier::from_guid),
}
}
#[cfg(any(target_os = "linux", target_os = "freebsd"))]
pub fn group_named(name: &str) -> io::Result<Qualifier> {
match name {
OWNER_NAME => Ok(Qualifier::GroupObj),
s => match unix::name_to_gid(s) {
Ok(gid) => Ok(Qualifier::Group(gid)),
Err(err) => Err(err),
},
}
}
#[cfg(any(target_os = "linux", target_os = "freebsd"))]
pub fn mask_named(name: &str) -> io::Result<Qualifier> {
match name {
MASK_NAME => Ok(Qualifier::Mask),
s => fail_custom(&format!("unknown mask name: {s:?}")),
}
}
#[cfg(any(target_os = "linux", target_os = "freebsd"))]
pub fn other_named(name: &str) -> io::Result<Qualifier> {
match name {
OTHER_NAME => Ok(Qualifier::Other),
s => fail_custom(&format!("unknown other name: {s:?}")),
}
}
#[cfg(target_os = "freebsd")]
pub fn everyone_named(name: &str) -> io::Result<Qualifier> {
match name {
EVERYONE_NAME => Ok(Qualifier::Everyone),
s => fail_custom(&format!("unknown everyone name: {s:?}")),
}
}
#[cfg(target_os = "macos")]
pub fn guid(&self) -> io::Result<Uuid> {
match self {
Qualifier::User(uid) => unix::uid_to_guid(*uid),
Qualifier::Group(gid) => unix::gid_to_guid(*gid),
Qualifier::Guid(guid) => Ok(*guid),
Qualifier::Unknown(tag) => fail_custom(&format!("unknown tag: {tag:?}")),
}
}
pub fn name(&self) -> io::Result<String> {
let result = match self {
Qualifier::User(uid) => unix::uid_to_name(*uid)?,
Qualifier::Group(gid) => unix::gid_to_name(*gid)?,
#[cfg(target_os = "macos")]
Qualifier::Guid(guid) => guid.to_string(),
#[cfg(any(target_os = "linux", target_os = "freebsd"))]
Qualifier::UserObj | Qualifier::GroupObj => OWNER_NAME.to_string(),
#[cfg(any(target_os = "linux", target_os = "freebsd"))]
Qualifier::Other => OTHER_NAME.to_string(),
#[cfg(any(target_os = "linux", target_os = "freebsd"))]
Qualifier::Mask => MASK_NAME.to_string(),
#[cfg(target_os = "freebsd")]
Qualifier::Everyone => EVERYONE_NAME.to_string(),
Qualifier::Unknown(s) => s.clone(),
};
Ok(result)
}
}
impl fmt::Display for Qualifier {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Qualifier::User(uid) => write!(f, "user:{uid}"),
Qualifier::Group(gid) => write!(f, "group:{gid}"),
#[cfg(target_os = "macos")]
Qualifier::Guid(guid) => write!(f, "guid:{guid}"),
#[cfg(any(target_os = "linux", target_os = "freebsd"))]
Qualifier::UserObj => write!(f, "user"),
#[cfg(any(target_os = "linux", target_os = "freebsd"))]
Qualifier::GroupObj => write!(f, "group"),
#[cfg(any(target_os = "linux", target_os = "freebsd"))]
Qualifier::Other => write!(f, "other"),
#[cfg(any(target_os = "linux", target_os = "freebsd"))]
Qualifier::Mask => write!(f, "mask"),
#[cfg(target_os = "freebsd")]
Qualifier::Everyone => write!(f, "everyone"),
Qualifier::Unknown(s) => write!(f, "unknown:{s}"),
}
}
}
#[cfg(test)]
mod qualifier_tests {
use super::*;
#[cfg(any(target_os = "linux", target_os = "freebsd"))]
fn getent(name: &str) -> (u32, u32) {
use std::str::FromStr;
let cmd = std::process::Command::new("getent")
.arg("passwd")
.arg(name)
.output()
.expect("Valid command");
let out = String::from_utf8(cmd.stdout).expect("Valid utf8");
let tokens = out.split(':').collect::<Vec<_>>();
assert_eq!(tokens[0], name);
let user_id = u32::from_str(tokens[2]).expect("Valid uid");
let group_id = u32::from_str(tokens[3]).expect("Valid gid");
(user_id, group_id)
}
#[test]
#[cfg(target_os = "macos")]
fn test_from_guid() {
let user =
Qualifier::from_guid(Uuid::parse_str("ffffeeee-dddd-cccc-bbbb-aaaa00000059").unwrap())
.ok();
assert_eq!(user, Some(Qualifier::User(89)));
let group =
Qualifier::from_guid(Uuid::parse_str("abcdefab-cdef-abcd-efab-cdef00000059").unwrap())
.ok();
assert_eq!(group, Some(Qualifier::Group(89)));
let user = Qualifier::from_guid(Uuid::nil()).ok();
assert_eq!(user, Some(Qualifier::Guid(Uuid::nil())));
}
#[test]
fn test_user_named() {
let user = Qualifier::user_named("89").ok();
assert_eq!(user, Some(Qualifier::User(89)));
#[cfg(target_os = "macos")]
{
let user = Qualifier::user_named("_spotlight").ok();
assert_eq!(user, Some(Qualifier::User(89)));
let user = Qualifier::user_named("ffffeeee-dddd-cccc-bbbb-aaaa00000059").ok();
assert_eq!(user, Some(Qualifier::User(89)));
}
#[cfg(any(target_os = "linux", target_os = "freebsd"))]
{
let (user_id, _) = getent("daemon");
let user = Qualifier::user_named("daemon").ok();
assert_eq!(user, Some(Qualifier::User(user_id)));
}
}
#[test]
fn test_group_named() {
let group = Qualifier::group_named("89").ok();
assert_eq!(group, Some(Qualifier::Group(89)));
#[cfg(target_os = "macos")]
{
let group = Qualifier::group_named("_spotlight").ok();
assert_eq!(group, Some(Qualifier::Group(89)));
let group = Qualifier::group_named("abcdefab-cdef-abcd-efab-cdef00000059").ok();
assert_eq!(group, Some(Qualifier::Group(89)));
}
#[cfg(any(target_os = "linux", target_os = "freebsd"))]
{
let (_, group_id) = getent("daemon");
let group = Qualifier::group_named("daemon").ok();
assert_eq!(group, Some(Qualifier::Group(group_id)));
}
}
}