1 //! Wrapper functions for the libc xattr calls
4 use std
::os
::unix
::io
::RawFd
;
9 use proxmox
::tools
::vec
;
11 /// `"security.capability"` as a CStr to avoid typos.
13 /// This cannot be `const` until `const_cstr_unchecked` is stable.
15 pub fn xattr_name_fcaps() -> &'
static CStr
{
16 c_str
!("security.capability")
19 /// Result of `flistxattr`, allows iterating over the attributes as a list of `&CStr`s.
21 /// Listing xattrs produces a list separated by zeroes, inherently making them available as `&CStr`
22 /// already, so we make use of this fact and reflect this in the interface.
23 pub struct ListXAttr
{
28 fn new(data
: Vec
<u8>) -> Self {
33 impl<'a
> IntoIterator
for &'a ListXAttr
{
35 type IntoIter
= ListXAttrIter
<'a
>;
37 fn into_iter(self) -> Self::IntoIter
{
45 /// Iterator over the extended attribute entries in a `ListXAttr`.
46 pub struct ListXAttrIter
<'a
> {
51 impl<'a
> Iterator
for ListXAttrIter
<'a
> {
54 fn next(&mut self) -> Option
<&'a CStr
> {
55 let data
= &self.data
[self.at
..];
56 let next
= data
.iter().position(|b
| *b
== 0)?
+ 1;
58 Some(unsafe { CStr::from_bytes_with_nul_unchecked(&data[..next]) }
)
62 /// Return a list of extended attributes accessible as an iterator over items of type `&CStr`.
63 pub fn flistxattr(fd
: RawFd
) -> Result
<ListXAttr
, nix
::errno
::Errno
> {
64 // Initial buffer size for the attribute list, if content does not fit
65 // it gets dynamically increased until big enough.
67 let mut buffer
= vec
::undefined(size
);
68 let mut bytes
= unsafe {
69 libc
::flistxattr(fd
, buffer
.as_mut_ptr() as *mut i8, buffer
.len())
72 let err
= Errno
::last();
75 // Buffer was not big enough to fit the list, retry with double the size
76 size
= size
.checked_mul(2).ok_or(Errno
::ENOMEM
)?
;
80 // Retry to read the list with new buffer
81 buffer
.resize(size
, 0);
83 libc
::flistxattr(fd
, buffer
.as_mut_ptr() as *mut i8, buffer
.len())
86 buffer
.truncate(bytes
as usize);
88 Ok(ListXAttr
::new(buffer
))
91 /// Get an extended attribute by name.
93 /// Extended attributes may not contain zeroes, which we enforce in the API by using a `&CStr`
95 pub fn fgetxattr(fd
: RawFd
, name
: &CStr
) -> Result
<Vec
<u8>, nix
::errno
::Errno
> {
97 let mut buffer
= vec
::undefined(size
);
98 let mut bytes
= unsafe {
99 libc
::fgetxattr(fd
, name
.as_ptr(), buffer
.as_mut_ptr() as *mut core
::ffi
::c_void
, buffer
.len())
102 let err
= Errno
::last();
105 // Buffer was not big enough to fit the value, retry with double the size
106 size
= size
.checked_mul(2).ok_or(Errno
::ENOMEM
)?
;
108 _
=> return Err(err
),
110 buffer
.resize(size
, 0);
112 libc
::fgetxattr(fd
, name
.as_ptr() as *const i8, buffer
.as_mut_ptr() as *mut core
::ffi
::c_void
, buffer
.len())
115 buffer
.resize(bytes
as usize, 0);
120 /// Set an extended attribute on a file descriptor.
121 pub fn fsetxattr(fd
: RawFd
, name
: &CStr
, data
: &[u8]) -> Result
<(), nix
::errno
::Errno
> {
122 let flags
= 0 as libc
::c_int
;
123 let result
= unsafe {
124 libc
::fsetxattr(fd
, name
.as_ptr(), data
.as_ptr() as *const libc
::c_void
, data
.len(), flags
)
127 return Err(Errno
::last());
133 pub fn fsetxattr_fcaps(fd
: RawFd
, fcaps
: &[u8]) -> Result
<(), nix
::errno
::Errno
> {
134 // TODO casync checks and removes capabilities if they are set
135 fsetxattr(fd
, xattr_name_fcaps(), fcaps
)
138 pub fn is_security_capability(name
: &CStr
) -> bool
{
139 name
.to_bytes() == xattr_name_fcaps().to_bytes()
142 /// Check if the passed name buffer starts with a valid xattr namespace prefix
143 /// and is within the length limit of 255 bytes
144 pub fn is_valid_xattr_name(c_name
: &CStr
) -> bool
{
145 let name
= c_name
.to_bytes();
146 if name
.is_empty() || name
.len() > 255 {
149 if name
.starts_with(b
"user.") || name
.starts_with(b
"trusted.") {
152 is_security_capability(c_name
)
159 use std
::ffi
::CString
;
160 use std
::fs
::OpenOptions
;
161 use std
::os
::unix
::io
::AsRawFd
;
163 use nix
::errno
::Errno
;
168 fn test_fsetxattr_fgetxattr() {
169 let path
= "./tests/xattrs.txt";
170 let file
= OpenOptions
::new()
176 let fd
= file
.as_raw_fd();
178 let mut name
= b
"user.".to_vec();
183 let invalid_name
= CString
::new(name
).unwrap();
185 assert
!(fsetxattr(fd
, c_str
!("user.attribute0"), b
"value0").is_ok());
186 assert
!(fsetxattr(fd
, c_str
!("user.empty"), b
"").is_ok());
188 if nix
::unistd
::Uid
::current() != nix
::unistd
::ROOT
{
189 assert_eq
!(fsetxattr(fd
, c_str
!("trusted.attribute0"), b
"value0"), Err(Errno
::EPERM
));
192 assert_eq
!(fsetxattr(fd
, c_str
!("garbage.attribute0"), b
"value"), Err(Errno
::EOPNOTSUPP
));
193 assert_eq
!(fsetxattr(fd
, &invalid_name
, b
"err"), Err(Errno
::ERANGE
));
195 let v0
= fgetxattr(fd
, c_str
!("user.attribute0")).unwrap();
196 let v1
= fgetxattr(fd
, c_str
!("user.empty")).unwrap();
198 assert_eq
!(v0
, b
"value0".as_ref());
199 assert_eq
!(v1
, b
"".as_ref());
200 assert_eq
!(fgetxattr(fd
, c_str
!("user.attribute1")), Err(Errno
::ENODATA
));
202 std
::fs
::remove_file(&path
).unwrap();
206 fn test_is_valid_xattr_name() {
207 let too_long
= CString
::new(vec
![b'a'
; 265]).unwrap();
209 assert
!(!is_valid_xattr_name(&too_long
));
210 assert
!(!is_valid_xattr_name(c_str
!("system.attr")));
211 assert
!(is_valid_xattr_name(c_str
!("user.attr")));
212 assert
!(is_valid_xattr_name(c_str
!("trusted.attr")));
213 assert
!(is_valid_xattr_name(super::xattr_name_fcaps()));