xattr/
lib.rs

1#![allow(clippy::comparison_chain)]
2//! A pure-Rust library to manage extended attributes.
3//!
4//! It provides support for manipulating extended attributes
5//! (`xattrs`) on modern Unix filesystems. See the `attr(5)`
6//! manpage for more details.
7//!
8//! An extension trait [`FileExt`] is provided to directly work with
9//! standard `File` objects and file descriptors.
10//!
11//! If the path argument is a symlink, the get/set/list/remove functions
12//! operate on the symlink itself. To operate on the symlink target, use
13//! the _deref variant of these functions.
14//!
15//! ```rust
16//! let mut xattrs = xattr::list("/").unwrap().peekable();
17//!
18//! if xattrs.peek().is_none() {
19//!     println!("no xattr set on root");
20//!     return;
21//! }
22//!
23//! println!("Extended attributes:");
24//! for attr in xattrs {
25//!     println!(" - {:?}", attr);
26//! }
27//! ```
28
29mod error;
30mod sys;
31mod util;
32
33use std::ffi::OsStr;
34use std::fs::File;
35use std::os::unix::io::{AsRawFd, BorrowedFd};
36use std::path::Path;
37use std::{fmt, io};
38
39pub use error::UnsupportedPlatformError;
40pub use sys::{XAttrs, SUPPORTED_PLATFORM};
41
42/// Get an extended attribute for the specified file.
43pub fn get<N, P>(path: P, name: N) -> io::Result<Option<Vec<u8>>>
44where
45    P: AsRef<Path>,
46    N: AsRef<OsStr>,
47{
48    util::extract_noattr(sys::get_path(path.as_ref(), name.as_ref(), false))
49}
50
51/// Get an extended attribute for the specified file (dereference symlinks).
52pub fn get_deref<N, P>(path: P, name: N) -> io::Result<Option<Vec<u8>>>
53where
54    P: AsRef<Path>,
55    N: AsRef<OsStr>,
56{
57    util::extract_noattr(sys::get_path(path.as_ref(), name.as_ref(), true))
58}
59
60/// Set an extended attribute on the specified file.
61pub fn set<N, P>(path: P, name: N, value: &[u8]) -> io::Result<()>
62where
63    P: AsRef<Path>,
64    N: AsRef<OsStr>,
65{
66    sys::set_path(path.as_ref(), name.as_ref(), value, false)
67}
68
69/// Set an extended attribute on the specified file (dereference symlinks).
70pub fn set_deref<N, P>(path: P, name: N, value: &[u8]) -> io::Result<()>
71where
72    P: AsRef<Path>,
73    N: AsRef<OsStr>,
74{
75    sys::set_path(path.as_ref(), name.as_ref(), value, true)
76}
77
78/// Remove an extended attribute from the specified file.
79pub fn remove<N, P>(path: P, name: N) -> io::Result<()>
80where
81    P: AsRef<Path>,
82    N: AsRef<OsStr>,
83{
84    sys::remove_path(path.as_ref(), name.as_ref(), false)
85}
86
87/// Remove an extended attribute from the specified file (dereference symlinks).
88pub fn remove_deref<N, P>(path: P, name: N) -> io::Result<()>
89where
90    P: AsRef<Path>,
91    N: AsRef<OsStr>,
92{
93    sys::remove_path(path.as_ref(), name.as_ref(), true)
94}
95
96/// List extended attributes attached to the specified file.
97///
98/// Note: this may not list *all* attributes. Speficially, it definitely won't list any trusted
99/// attributes unless you are root and it may not list system attributes.
100pub fn list<P>(path: P) -> io::Result<XAttrs>
101where
102    P: AsRef<Path>,
103{
104    sys::list_path(path.as_ref(), false)
105}
106
107/// List extended attributes attached to the specified file (dereference symlinks).
108pub fn list_deref<P>(path: P) -> io::Result<XAttrs>
109where
110    P: AsRef<Path>,
111{
112    sys::list_path(path.as_ref(), true)
113}
114
115/// Extension trait to manipulate extended attributes on `File`-like objects.
116pub trait FileExt: AsRawFd {
117    /// Get an extended attribute for the specified file.
118    fn get_xattr<N>(&self, name: N) -> io::Result<Option<Vec<u8>>>
119    where
120        N: AsRef<OsStr>,
121    {
122        // SAFETY: Implement I/O safety later.
123        let fd = unsafe { BorrowedFd::borrow_raw(self.as_raw_fd()) };
124        util::extract_noattr(sys::get_fd(fd, name.as_ref()))
125    }
126
127    /// Set an extended attribute on the specified file.
128    fn set_xattr<N>(&self, name: N, value: &[u8]) -> io::Result<()>
129    where
130        N: AsRef<OsStr>,
131    {
132        let fd = unsafe { BorrowedFd::borrow_raw(self.as_raw_fd()) };
133        sys::set_fd(fd, name.as_ref(), value)
134    }
135
136    /// Remove an extended attribute from the specified file.
137    fn remove_xattr<N>(&self, name: N) -> io::Result<()>
138    where
139        N: AsRef<OsStr>,
140    {
141        let fd = unsafe { BorrowedFd::borrow_raw(self.as_raw_fd()) };
142        sys::remove_fd(fd, name.as_ref())
143    }
144
145    /// List extended attributes attached to the specified file.
146    ///
147    /// Note: this may not list *all* attributes. Speficially, it definitely won't list any trusted
148    /// attributes unless you are root and it may not list system attributes.
149    fn list_xattr(&self) -> io::Result<XAttrs> {
150        let fd = unsafe { BorrowedFd::borrow_raw(self.as_raw_fd()) };
151        sys::list_fd(fd)
152    }
153}
154
155impl FileExt for File {}
156
157impl fmt::Debug for XAttrs {
158    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
159        // Waiting on https://github.com/rust-lang/rust/issues/117729 to stabilize...
160        struct AsList<'a>(&'a XAttrs);
161        impl<'a> fmt::Debug for AsList<'a> {
162            fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
163                f.debug_list().entries(self.0.clone()).finish()
164            }
165        }
166        f.debug_tuple("XAttrs").field(&AsList(self)).finish()
167    }
168}