1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
#![allow(clippy::comparison_chain)]
//! A pure-Rust library to manage extended attributes.
//!
//! It provides support for manipulating extended attributes
//! (`xattrs`) on modern Unix filesystems. See the `attr(5)`
//! manpage for more details.
//!
//! An extension trait [`FileExt`] is provided to directly work with
//! standard `File` objects and file descriptors.
//!
//! If the path argument is a symlink, the get/set/list/remove functions
//! operate on the symlink itself. To operate on the symlink target, use
//! the _deref variant of these functions.
//!
//! ```rust
//! let mut xattrs = xattr::list("/").unwrap().peekable();
//!
//! if xattrs.peek().is_none() {
//!     println!("no xattr set on root");
//!     return;
//! }
//!
//! println!("Extended attributes:");
//! for attr in xattrs {
//!     println!(" - {:?}", attr);
//! }
//! ```

mod error;
mod sys;
mod util;

use std::ffi::OsStr;
use std::fs::File;
use std::io;
use std::os::unix::io::{AsRawFd, BorrowedFd};
use std::path::Path;

pub use error::UnsupportedPlatformError;
pub use sys::{XAttrs, SUPPORTED_PLATFORM};

/// Get an extended attribute for the specified file.
pub fn get<N, P>(path: P, name: N) -> io::Result<Option<Vec<u8>>>
where
    P: AsRef<Path>,
    N: AsRef<OsStr>,
{
    util::extract_noattr(sys::get_path(path.as_ref(), name.as_ref(), false))
}

/// Get an extended attribute for the specified file (dereference symlinks).
pub fn get_deref<N, P>(path: P, name: N) -> io::Result<Option<Vec<u8>>>
where
    P: AsRef<Path>,
    N: AsRef<OsStr>,
{
    util::extract_noattr(sys::get_path(path.as_ref(), name.as_ref(), true))
}

/// Set an extended attribute on the specified file.
pub fn set<N, P>(path: P, name: N, value: &[u8]) -> io::Result<()>
where
    P: AsRef<Path>,
    N: AsRef<OsStr>,
{
    sys::set_path(path.as_ref(), name.as_ref(), value, false)
}

/// Set an extended attribute on the specified file (dereference symlinks).
pub fn set_deref<N, P>(path: P, name: N, value: &[u8]) -> io::Result<()>
where
    P: AsRef<Path>,
    N: AsRef<OsStr>,
{
    sys::set_path(path.as_ref(), name.as_ref(), value, true)
}

/// Remove an extended attribute from the specified file.
pub fn remove<N, P>(path: P, name: N) -> io::Result<()>
where
    P: AsRef<Path>,
    N: AsRef<OsStr>,
{
    sys::remove_path(path.as_ref(), name.as_ref(), false)
}

/// Remove an extended attribute from the specified file (dereference symlinks).
pub fn remove_deref<N, P>(path: P, name: N) -> io::Result<()>
where
    P: AsRef<Path>,
    N: AsRef<OsStr>,
{
    sys::remove_path(path.as_ref(), name.as_ref(), true)
}

/// List extended attributes attached to the specified file.
///
/// Note: this may not list *all* attributes. Speficially, it definitely won't list any trusted
/// attributes unless you are root and it may not list system attributes.
pub fn list<P>(path: P) -> io::Result<XAttrs>
where
    P: AsRef<Path>,
{
    sys::list_path(path.as_ref(), false)
}

/// List extended attributes attached to the specified file (dereference symlinks).
pub fn list_deref<P>(path: P) -> io::Result<XAttrs>
where
    P: AsRef<Path>,
{
    sys::list_path(path.as_ref(), true)
}

/// Extension trait to manipulate extended attributes on `File`-like objects.
pub trait FileExt: AsRawFd {
    /// Get an extended attribute for the specified file.
    fn get_xattr<N>(&self, name: N) -> io::Result<Option<Vec<u8>>>
    where
        N: AsRef<OsStr>,
    {
        // SAFETY: Implement I/O safety later.
        let fd = unsafe { BorrowedFd::borrow_raw(self.as_raw_fd()) };
        util::extract_noattr(sys::get_fd(fd, name.as_ref()))
    }

    /// Set an extended attribute on the specified file.
    fn set_xattr<N>(&self, name: N, value: &[u8]) -> io::Result<()>
    where
        N: AsRef<OsStr>,
    {
        let fd = unsafe { BorrowedFd::borrow_raw(self.as_raw_fd()) };
        sys::set_fd(fd, name.as_ref(), value)
    }

    /// Remove an extended attribute from the specified file.
    fn remove_xattr<N>(&self, name: N) -> io::Result<()>
    where
        N: AsRef<OsStr>,
    {
        let fd = unsafe { BorrowedFd::borrow_raw(self.as_raw_fd()) };
        sys::remove_fd(fd, name.as_ref())
    }

    /// List extended attributes attached to the specified file.
    ///
    /// Note: this may not list *all* attributes. Speficially, it definitely won't list any trusted
    /// attributes unless you are root and it may not list system attributes.
    fn list_xattr(&self) -> io::Result<XAttrs> {
        let fd = unsafe { BorrowedFd::borrow_raw(self.as_raw_fd()) };
        sys::list_fd(fd)
    }
}

impl FileExt for File {}