2023-08-08 07:39:20 +02:00
|
|
|
use std::{
|
|
|
|
ffi::{OsStr, OsString},
|
|
|
|
fmt::{Display, Formatter},
|
|
|
|
os::unix::prelude::OsStringExt,
|
|
|
|
};
|
|
|
|
|
|
|
|
use clap::{builder::TypedValueParser, Arg, Command};
|
2023-08-04 23:28:38 +02:00
|
|
|
use nom::{
|
2023-08-08 21:16:08 +02:00
|
|
|
branch::alt,
|
|
|
|
bytes::complete::{tag, take_while1},
|
|
|
|
character::complete::char,
|
|
|
|
character::is_alphabetic,
|
|
|
|
sequence::separated_pair,
|
|
|
|
IResult,
|
2023-08-04 23:28:38 +02:00
|
|
|
};
|
2023-08-08 07:39:20 +02:00
|
|
|
use serde::Serialize;
|
2023-08-04 23:28:38 +02:00
|
|
|
|
2023-08-08 07:39:20 +02:00
|
|
|
#[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize)]
|
2023-08-04 23:28:38 +02:00
|
|
|
pub struct Xattr {
|
|
|
|
pub namespace: Vec<u8>,
|
|
|
|
pub attr: Vec<u8>,
|
2023-08-08 07:39:20 +02:00
|
|
|
all: Vec<u8>,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Xattr {
|
|
|
|
pub fn new(namespace: Vec<u8>, attr: Vec<u8>) -> Self {
|
|
|
|
let all: Vec<u8> = namespace
|
|
|
|
.iter()
|
|
|
|
.copied()
|
|
|
|
.chain(std::iter::once(b'.'))
|
|
|
|
.chain(attr.iter().copied())
|
|
|
|
.collect();
|
|
|
|
|
|
|
|
Self {
|
|
|
|
namespace,
|
|
|
|
attr,
|
|
|
|
all,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn to_osstring(&self) -> OsString {
|
|
|
|
OsString::from_vec(self.all.clone())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// impl AsRef<OsStr> for Xattr {
|
|
|
|
// fn as_ref(&self) -> &OsStr {
|
|
|
|
// OsString::from_vec(self.all.clone()).as_os_str()
|
|
|
|
// }
|
|
|
|
// }
|
|
|
|
|
|
|
|
impl From<&str> for Xattr {
|
|
|
|
fn from(s: &str) -> Self {
|
|
|
|
parse_xattr(s.as_bytes()).unwrap().1
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl From<&[u8]> for Xattr {
|
|
|
|
fn from(b: &[u8]) -> Self {
|
|
|
|
parse_xattr(b).unwrap().1
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Display for Xattr {
|
|
|
|
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> {
|
|
|
|
write!(
|
|
|
|
f,
|
|
|
|
"{}.{}",
|
|
|
|
String::from_utf8_lossy(self.namespace.as_slice()),
|
|
|
|
String::from_utf8_lossy(self.attr.as_slice())
|
|
|
|
)
|
|
|
|
}
|
2023-08-04 23:28:38 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Parse the `namespace.attr` (or just bare `attr`) xattr names, filling in the `user` namespace
|
|
|
|
/// when none is provided.
|
|
|
|
// pub fn parse_xattr<'a, E: ParseError<&'a [u8]>>(i: &'a [u8]) -> IResult<&'a [u8], Xattr, E> {
|
|
|
|
// pair(
|
|
|
|
// alt((tag(""), terminated(take_while(is_alphabetic), tag(".")))),
|
|
|
|
// take_while(is_alphabetic),
|
|
|
|
// )(i)
|
|
|
|
// .map(|(i, (namespace, attr))| {
|
|
|
|
// (
|
|
|
|
// i,
|
|
|
|
// Xattr {
|
|
|
|
// namespace: if namespace.is_empty() {
|
|
|
|
// b"user.".into_iter().copied().collect()
|
|
|
|
// } else {
|
|
|
|
// namespace.iter().copied().collect()
|
|
|
|
// },
|
|
|
|
// attr: attr.iter().copied().collect(),
|
|
|
|
// },
|
|
|
|
// )
|
|
|
|
// })
|
|
|
|
// }
|
|
|
|
|
2023-08-08 21:16:08 +02:00
|
|
|
fn is_alphabetic_or_dot(c: u8) -> bool {
|
|
|
|
is_alphabetic(c) || c == '.' as u8
|
|
|
|
}
|
|
|
|
|
2023-08-04 23:28:38 +02:00
|
|
|
fn parse_xattr_complete(i: &[u8]) -> IResult<&[u8], Xattr> {
|
|
|
|
separated_pair(
|
2023-08-08 21:16:08 +02:00
|
|
|
alt((tag("trusted"), tag("security"), tag("system"), tag("user"))),
|
2023-08-04 23:28:38 +02:00
|
|
|
char('.'),
|
2023-08-08 21:16:08 +02:00
|
|
|
take_while1(is_alphabetic_or_dot),
|
2023-08-04 23:28:38 +02:00
|
|
|
)(i)
|
|
|
|
.map(|(i, (namespace, attr))| {
|
|
|
|
(
|
|
|
|
i,
|
2023-08-08 07:39:20 +02:00
|
|
|
Xattr::new(
|
|
|
|
namespace.iter().copied().collect(),
|
|
|
|
attr.iter().copied().collect(),
|
|
|
|
),
|
2023-08-04 23:28:38 +02:00
|
|
|
)
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
fn parse_xattr_bare(i: &[u8]) -> IResult<&[u8], Xattr> {
|
2023-08-08 21:16:08 +02:00
|
|
|
take_while1(is_alphabetic_or_dot)(i).map(|(i, attr)| {
|
2023-08-04 23:28:38 +02:00
|
|
|
(
|
|
|
|
i,
|
2023-08-08 07:39:20 +02:00
|
|
|
Xattr::new(b"user".to_vec(), attr.iter().copied().collect()),
|
2023-08-04 23:28:38 +02:00
|
|
|
)
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
// pub fn parse_xattr(i: &[u8]) -> IResult<&[u8], Xattr> {
|
|
|
|
// pair(
|
|
|
|
// alt((tag(""), terminated(take_while(is_alphabetic), tag(".")))),
|
|
|
|
// take_while1(is_alphabetic),
|
|
|
|
// )(i)
|
|
|
|
// .map(|(i, (namespace, attr))| {
|
|
|
|
// (
|
|
|
|
// i,
|
|
|
|
// Xattr {
|
|
|
|
// namespace: if namespace.is_empty() {
|
|
|
|
// b"user".into_iter().copied().collect()
|
|
|
|
// } else {
|
|
|
|
// namespace.iter().copied().collect()
|
|
|
|
// },
|
|
|
|
// attr: attr.iter().copied().collect(),
|
|
|
|
// },
|
|
|
|
// )
|
|
|
|
// })
|
|
|
|
// }
|
|
|
|
|
|
|
|
pub fn parse_xattr(i: &[u8]) -> IResult<&[u8], Xattr> {
|
|
|
|
alt((parse_xattr_complete, parse_xattr_bare))(i)
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_parse_xattr() {
|
|
|
|
assert!(parse_xattr(b"").is_err());
|
|
|
|
assert_eq!(
|
|
|
|
parse_xattr(b"abc"),
|
|
|
|
Ok((
|
|
|
|
b"".as_slice(),
|
2023-08-08 07:39:20 +02:00
|
|
|
Xattr::new(b"user".to_vec(), b"abc".to_vec())
|
2023-08-04 23:28:38 +02:00
|
|
|
))
|
|
|
|
);
|
|
|
|
|
|
|
|
assert_eq!(
|
|
|
|
parse_xattr(b"system.abc"),
|
|
|
|
Ok((
|
|
|
|
b"".as_slice(),
|
2023-08-08 07:39:20 +02:00
|
|
|
Xattr::new(b"system".to_vec(), b"abc".to_vec())
|
2023-08-04 23:28:38 +02:00
|
|
|
))
|
|
|
|
);
|
|
|
|
assert_eq!(
|
|
|
|
parse_xattr(b"blah.blah.blah"),
|
|
|
|
Ok((
|
2023-08-08 21:16:08 +02:00
|
|
|
b"".as_slice(),
|
|
|
|
Xattr::new(b"user".to_vec(), b"blah.blah.blah".to_vec())
|
2023-08-04 23:28:38 +02:00
|
|
|
))
|
|
|
|
);
|
|
|
|
}
|
2023-08-08 07:39:20 +02:00
|
|
|
#[derive(Clone)]
|
|
|
|
pub struct XattrParser;
|
|
|
|
impl TypedValueParser for XattrParser {
|
|
|
|
type Value = Xattr;
|
|
|
|
|
|
|
|
fn parse_ref(
|
|
|
|
&self,
|
|
|
|
_cmd: &Command,
|
|
|
|
_arg: Option<&Arg>,
|
|
|
|
value: &OsStr,
|
|
|
|
) -> Result<Self::Value, clap::error::Error<clap::error::RichFormatter>> {
|
|
|
|
parse_xattr(value.to_string_lossy().as_bytes())
|
|
|
|
.map(|(_remainder, predicate)| predicate)
|
|
|
|
.map_err(|e| {
|
|
|
|
clap::error::Error::raw(
|
|
|
|
clap::error::ErrorKind::InvalidValue,
|
|
|
|
format!("Malformed xattr: {}\n", e),
|
|
|
|
)
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|