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 /// `"system.posix_acl_access"` as a CStr to avoid typos.
21 /// This cannot be `const` until `const_cstr_unchecked` is stable.
23 pub fn xattr_acl_access() -> &'
static CStr
{
24 c_str
!("system.posix_acl_access")
27 /// `"system.posix_acl_default"` as a CStr to avoid typos.
29 /// This cannot be `const` until `const_cstr_unchecked` is stable.
31 pub fn xattr_acl_default() -> &'
static CStr
{
32 c_str
!("system.posix_acl_default")
35 /// Result of `flistxattr`, allows iterating over the attributes as a list of `&CStr`s.
37 /// Listing xattrs produces a list separated by zeroes, inherently making them available as `&CStr`
38 /// already, so we make use of this fact and reflect this in the interface.
39 pub struct ListXAttr
{
44 fn new(data
: Vec
<u8>) -> Self {
49 impl<'a
> IntoIterator
for &'a ListXAttr
{
51 type IntoIter
= ListXAttrIter
<'a
>;
53 fn into_iter(self) -> Self::IntoIter
{
61 /// Iterator over the extended attribute entries in a `ListXAttr`.
62 pub struct ListXAttrIter
<'a
> {
67 impl<'a
> Iterator
for ListXAttrIter
<'a
> {
70 fn next(&mut self) -> Option
<&'a CStr
> {
71 let data
= &self.data
[self.at
..];
72 let next
= data
.iter().position(|b
| *b
== 0)?
+ 1;
74 Some(unsafe { CStr::from_bytes_with_nul_unchecked(&data[..next]) }
)
78 /// Return a list of extended attributes accessible as an iterator over items of type `&CStr`.
79 pub fn flistxattr(fd
: RawFd
) -> Result
<ListXAttr
, nix
::errno
::Errno
> {
80 // Initial buffer size for the attribute list, if content does not fit
81 // it gets dynamically increased until big enough.
83 let mut buffer
= vec
::undefined(size
);
84 let mut bytes
= unsafe {
85 libc
::flistxattr(fd
, buffer
.as_mut_ptr() as *mut libc
::c_char
, buffer
.len())
88 let err
= Errno
::last();
91 // Buffer was not big enough to fit the list, retry with double the size
92 size
= size
.checked_mul(2).ok_or(Errno
::ENOMEM
)?
;
96 // Retry to read the list with new buffer
97 buffer
.resize(size
, 0);
99 libc
::flistxattr(fd
, buffer
.as_mut_ptr() as *mut libc
::c_char
, buffer
.len())
102 buffer
.truncate(bytes
as usize);
104 Ok(ListXAttr
::new(buffer
))
107 /// Get an extended attribute by name.
109 /// Extended attributes may not contain zeroes, which we enforce in the API by using a `&CStr`
111 pub fn fgetxattr(fd
: RawFd
, name
: &CStr
) -> Result
<Vec
<u8>, nix
::errno
::Errno
> {
113 let mut buffer
= vec
::undefined(size
);
114 let mut bytes
= unsafe {
115 libc
::fgetxattr(fd
, name
.as_ptr(), buffer
.as_mut_ptr() as *mut core
::ffi
::c_void
, buffer
.len())
118 let err
= Errno
::last();
121 // Buffer was not big enough to fit the value, retry with double the size
122 size
= size
.checked_mul(2).ok_or(Errno
::ENOMEM
)?
;
124 _
=> return Err(err
),
126 buffer
.resize(size
, 0);
128 libc
::fgetxattr(fd
, name
.as_ptr() as *const libc
::c_char
, buffer
.as_mut_ptr() as *mut core
::ffi
::c_void
, buffer
.len())
131 buffer
.resize(bytes
as usize, 0);
136 /// Set an extended attribute on a file descriptor.
137 pub fn fsetxattr(fd
: RawFd
, name
: &CStr
, data
: &[u8]) -> Result
<(), nix
::errno
::Errno
> {
138 let flags
= 0 as libc
::c_int
;
139 let result
= unsafe {
140 libc
::fsetxattr(fd
, name
.as_ptr(), data
.as_ptr() as *const libc
::c_void
, data
.len(), flags
)
143 return Err(Errno
::last());
149 pub fn fsetxattr_fcaps(fd
: RawFd
, fcaps
: &[u8]) -> Result
<(), nix
::errno
::Errno
> {
150 // TODO casync checks and removes capabilities if they are set
151 fsetxattr(fd
, xattr_name_fcaps(), fcaps
)
154 pub fn is_security_capability(name
: &CStr
) -> bool
{
155 name
.to_bytes() == xattr_name_fcaps().to_bytes()
158 pub fn is_acl(name
: &CStr
) -> bool
{
159 name
.to_bytes() == xattr_acl_access().to_bytes()
160 || name
.to_bytes() == xattr_acl_default().to_bytes()
163 /// Check if the passed name buffer starts with a valid xattr namespace prefix
164 /// and is within the length limit of 255 bytes
165 pub fn is_valid_xattr_name(c_name
: &CStr
) -> bool
{
166 let name
= c_name
.to_bytes();
167 if name
.is_empty() || name
.len() > 255 {
170 if name
.starts_with(b
"user.") || name
.starts_with(b
"trusted.") {
173 // samba saves windows ACLs there
174 if name
== b
"security.NTACL" {
177 is_security_capability(c_name
)
184 use std
::ffi
::CString
;
185 use std
::fs
::OpenOptions
;
186 use std
::os
::unix
::io
::AsRawFd
;
188 use nix
::errno
::Errno
;
193 fn test_fsetxattr_fgetxattr() {
194 let path
= "./tests/xattrs.txt";
195 let file
= OpenOptions
::new()
201 let fd
= file
.as_raw_fd();
203 let mut name
= b
"user.".to_vec();
208 let invalid_name
= CString
::new(name
).unwrap();
210 assert
!(fsetxattr(fd
, c_str
!("user.attribute0"), b
"value0").is_ok());
211 assert
!(fsetxattr(fd
, c_str
!("user.empty"), b
"").is_ok());
213 if nix
::unistd
::Uid
::current() != nix
::unistd
::ROOT
{
214 assert_eq
!(fsetxattr(fd
, c_str
!("trusted.attribute0"), b
"value0"), Err(Errno
::EPERM
));
217 assert_eq
!(fsetxattr(fd
, c_str
!("garbage.attribute0"), b
"value"), Err(Errno
::EOPNOTSUPP
));
218 assert_eq
!(fsetxattr(fd
, &invalid_name
, b
"err"), Err(Errno
::ERANGE
));
220 let v0
= fgetxattr(fd
, c_str
!("user.attribute0")).unwrap();
221 let v1
= fgetxattr(fd
, c_str
!("user.empty")).unwrap();
223 assert_eq
!(v0
, b
"value0".as_ref());
224 assert_eq
!(v1
, b
"".as_ref());
225 assert_eq
!(fgetxattr(fd
, c_str
!("user.attribute1")), Err(Errno
::ENODATA
));
227 std
::fs
::remove_file(&path
).unwrap();
231 fn test_is_valid_xattr_name() {
232 let too_long
= CString
::new(vec
![b'a'
; 265]).unwrap();
234 assert
!(!is_valid_xattr_name(&too_long
));
235 assert
!(!is_valid_xattr_name(c_str
!("system.attr")));
236 assert
!(is_valid_xattr_name(c_str
!("user.attr")));
237 assert
!(is_valid_xattr_name(c_str
!("trusted.attr")));
238 assert
!(is_valid_xattr_name(super::xattr_name_fcaps()));