Skip to main content

rustix/fs/
xattr.rs

1//! Extended attribute functions.
2
3#![allow(unsafe_code)]
4
5use crate::buffer::Buffer;
6use crate::{backend, ffi, io, path};
7use backend::c;
8use backend::fd::AsFd;
9use bitflags::bitflags;
10use core::ptr::null_mut;
11
12bitflags! {
13    /// `XATTR_*` constants for use with [`setxattr`], and other `*setxattr`
14    /// functions.
15    #[repr(transparent)]
16    #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
17    pub struct XattrFlags: ffi::c_uint {
18        /// `XATTR_CREATE`
19        const CREATE = c::XATTR_CREATE as c::c_uint;
20
21        /// `XATTR_REPLACE`
22        const REPLACE = c::XATTR_REPLACE as c::c_uint;
23
24        /// <https://docs.rs/bitflags/*/bitflags/#externally-defined-flags>
25        const _ = !0;
26    }
27}
28
29/// `getxattr(path, name, value)`—Get extended filesystem attributes.
30///
31/// Unlike the libc `getxattr` function, this function does not treat an
32/// buffer with zero length as a way to return the size of the attribute.
33/// To obtain the size, use [`getxattr_query`].
34///
35/// For a higher-level API to xattr functionality, see the [xattr] crate.
36///
37/// [xattr]: https://crates.io/crates/xattr
38///
39/// # References
40///  - [Linux]
41///  - [Apple]
42///
43/// [Linux]: https://man7.org/linux/man-pages/man2/getxattr.2.html
44/// [Apple]: https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man2/getxattr.2.html
45#[inline]
46pub fn getxattr<P: path::Arg, Name: path::Arg, Buf: Buffer<u8>>(
47    path: P,
48    name: Name,
49    mut value: Buf,
50) -> io::Result<Buf::Output> {
51    path.into_with_c_str(|path| {
52        name.into_with_c_str(|name| {
53            let parts = value.parts_mut();
54            // SAFETY: `getxattr` behaves.
55            let len = unsafe { backend::fs::syscalls::getxattr(path, name, parts)? };
56            if parts.1 == 0 {
57                // `getxattr` special-cases an input size of 0 as a size
58                // query, which we handle through a different API function.
59                unsafe { Ok(value.assume_init(0)) }
60            } else {
61                // SAFETY: `getxattr` behaves, and we've handled the special
62                // case of an empty buffer above.
63                unsafe { Ok(value.assume_init(len)) }
64            }
65        })
66    })
67}
68
69/// `getxattr(path, name, NULL, 0)`—Query the length of an extended filesystem
70/// attribute.
71///
72/// This function uses `getxattr` with no buffer to just return the size of
73/// the attribute. This can be used to determine the size of a buffer to
74/// allocate for a subsequent call to [`getxattr`], however keep in mind
75/// that other processes may modify attributes at any time.
76///
77/// # References
78///  - [Linux]
79///  - [Apple]
80///
81/// [Linux]: https://man7.org/linux/man-pages/man2/getxattr.2.html
82/// [Apple]: https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man2/getxattr.2.html
83#[inline]
84pub fn getxattr_query<P: path::Arg, Name: path::Arg>(path: P, name: Name) -> io::Result<usize> {
85    path.into_with_c_str(|path| {
86        name.into_with_c_str(|name| {
87            // SAFETY: `getxattr` behaves.
88            unsafe { backend::fs::syscalls::getxattr(path, name, (null_mut(), 0)) }
89        })
90    })
91}
92
93/// `lgetxattr(path, name, value.as_ptr(), value.len())`—Get extended
94/// filesystem attributes, without following symlinks in the last path
95/// component.
96///
97/// Unlike the libc `lgetxattr` function, this function does not treat an
98/// buffer with zero length as a way to return the size of the attribute.
99/// To obtain the size, use [`lgetxattr_query`].
100///
101/// # References
102///  - [Linux]
103///
104/// [Linux]: https://man7.org/linux/man-pages/man2/lgetxattr.2.html
105#[inline]
106pub fn lgetxattr<P: path::Arg, Name: path::Arg, Buf: Buffer<u8>>(
107    path: P,
108    name: Name,
109    mut value: Buf,
110) -> io::Result<Buf::Output> {
111    path.into_with_c_str(|path| {
112        name.into_with_c_str(|name| {
113            let parts = value.parts_mut();
114            // SAFETY: `lgetxattr` behaves.
115            let len = unsafe { backend::fs::syscalls::lgetxattr(path, name, parts)? };
116            if parts.1 == 0 {
117                // `lgetxattr` special-cases an input size of 0 as a size
118                // query, which we handle through a different API function.
119                unsafe { Ok(value.assume_init(0)) }
120            } else {
121                // SAFETY: `lgetxattr` behaves, and we've handled the special
122                // case of an empty buffer above.
123                unsafe { Ok(value.assume_init(len)) }
124            }
125        })
126    })
127}
128
129/// `lgetxattr(path, name, NULL, 0)`—Query the length of an extended
130/// filesystem attribute, without following symlinks in the last path
131/// component.
132///
133/// This function uses `lgetxattr` with no buffer to just return the size of
134/// the attribute. This can be used to determine the size of a buffer to
135/// allocate for a subsequent call to [`lgetxattr`], however keep in mind
136/// that other processes may modify attributes at any time.
137///
138/// # References
139///  - [Linux]
140///
141/// [Linux]: https://man7.org/linux/man-pages/man2/lgetxattr.2.html
142#[inline]
143pub fn lgetxattr_query<P: path::Arg, Name: path::Arg>(path: P, name: Name) -> io::Result<usize> {
144    path.into_with_c_str(|path| {
145        name.into_with_c_str(|name| {
146            // SAFETY: `lgetxattr` behaves.
147            unsafe { backend::fs::syscalls::lgetxattr(path, name, (null_mut(), 0)) }
148        })
149    })
150}
151
152/// `fgetxattr(fd, name, value.as_ptr(), value.len())`—Get extended
153/// filesystem attributes on an open file descriptor.
154///
155/// Unlike the libc `fgetxattr` function, this function does not treat an
156/// buffer with zero length as a way to return the size of the attribute.
157/// To obtain the size, use [`fgetxattr_query`].
158///
159/// # References
160///  - [Linux]
161///  - [Apple]
162///
163/// [Linux]: https://man7.org/linux/man-pages/man2/fgetxattr.2.html
164/// [Apple]: https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man2/fgetxattr.2.html
165#[inline]
166pub fn fgetxattr<Fd: AsFd, Name: path::Arg, Buf: Buffer<u8>>(
167    fd: Fd,
168    name: Name,
169    mut value: Buf,
170) -> io::Result<Buf::Output> {
171    name.into_with_c_str(|name| {
172        let parts = value.parts_mut();
173        // SAFETY: `fgetxattr` behaves.
174        let len = unsafe { backend::fs::syscalls::fgetxattr(fd.as_fd(), name, parts)? };
175        if parts.1 == 0 {
176            // `fgetxattr` special-cases an input size of 0 as a size
177            // query, which we handle through a different API function.
178            unsafe { Ok(value.assume_init(0)) }
179        } else {
180            // SAFETY: `fgetxattr` behaves, and we've handled the special
181            // case of an empty buffer above.
182            unsafe { Ok(value.assume_init(len)) }
183        }
184    })
185}
186
187/// `fgetxattr(fd, name, NULL, 0)`—Query the length of an extended
188/// filesystem attribute on an open file descriptor.
189///
190/// This function uses `fgetxattr` with no buffer to just return the size of
191/// the attribute. This can be used to determine the size of a buffer to
192/// allocate for a subsequent call to [`fgetxattr`], however keep in mind
193/// that other processes may modify attributes at any time.
194///
195/// # References
196///  - [Linux]
197///  - [Apple]
198///
199/// [Linux]: https://man7.org/linux/man-pages/man2/fgetxattr.2.html
200/// [Apple]: https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man2/fgetxattr.2.html
201#[inline]
202pub fn fgetxattr_query<Fd: AsFd, Name: path::Arg>(fd: Fd, name: Name) -> io::Result<usize> {
203    name.into_with_c_str(|name| {
204        // SAFETY: `fgetxattr` behaves.
205        unsafe { backend::fs::syscalls::fgetxattr(fd.as_fd(), name, (null_mut(), 0)) }
206    })
207}
208
209/// `setxattr(path, name, value.as_ptr(), value.len(), flags)`—Set extended
210/// filesystem attributes.
211///
212/// # References
213///  - [Linux]
214///  - [Apple]
215///
216/// [Linux]: https://man7.org/linux/man-pages/man2/setxattr.2.html
217/// [Apple]: https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man2/setxattr.2.html
218#[inline]
219pub fn setxattr<P: path::Arg, Name: path::Arg>(
220    path: P,
221    name: Name,
222    value: &[u8],
223    flags: XattrFlags,
224) -> io::Result<()> {
225    path.into_with_c_str(|path| {
226        name.into_with_c_str(|name| backend::fs::syscalls::setxattr(path, name, value, flags))
227    })
228}
229
230/// `setxattr(path, name, value.as_ptr(), value.len(), flags)`—Set extended
231/// filesystem attributes, without following symlinks in the last path
232/// component.
233///
234/// # References
235///  - [Linux]
236///
237/// [Linux]: https://man7.org/linux/man-pages/man2/lsetxattr.2.html
238#[inline]
239pub fn lsetxattr<P: path::Arg, Name: path::Arg>(
240    path: P,
241    name: Name,
242    value: &[u8],
243    flags: XattrFlags,
244) -> io::Result<()> {
245    path.into_with_c_str(|path| {
246        name.into_with_c_str(|name| backend::fs::syscalls::lsetxattr(path, name, value, flags))
247    })
248}
249
250/// `fsetxattr(fd, name, value.as_ptr(), value.len(), flags)`—Set extended
251/// filesystem attributes on an open file descriptor.
252///
253/// # References
254///  - [Linux]
255///  - [Apple]
256///
257/// [Linux]: https://man7.org/linux/man-pages/man2/fsetxattr.2.html
258/// [Apple]: https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man2/fsetxattr.2.html
259#[inline]
260pub fn fsetxattr<Fd: AsFd, Name: path::Arg>(
261    fd: Fd,
262    name: Name,
263    value: &[u8],
264    flags: XattrFlags,
265) -> io::Result<()> {
266    name.into_with_c_str(|name| backend::fs::syscalls::fsetxattr(fd.as_fd(), name, value, flags))
267}
268
269/// `listxattr(path, list.as_ptr(), list.len())`—List extended filesystem
270/// attributes.
271///
272/// Unlike the libc `listxattr` function, this function does not treat an
273/// buffer with zero length as a way to return the size of the attribute.
274/// To obtain the size, use [`listxattr_query`].
275///
276/// # References
277///  - [Linux]
278///  - [Apple]
279///
280/// [Linux]: https://man7.org/linux/man-pages/man2/listxattr.2.html
281/// [Apple]: https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man2/listxattr.2.html
282#[inline]
283pub fn listxattr<P: path::Arg, Buf: Buffer<u8>>(path: P, mut list: Buf) -> io::Result<Buf::Output> {
284    path.into_with_c_str(|path| {
285        let parts = list.parts_mut();
286        // SAFETY: `listxattr` behaves.
287        let len = unsafe { backend::fs::syscalls::listxattr(path, parts)? };
288        if parts.1 == 0 {
289            // `listxattr` special-cases an input size of 0 as a size
290            // query, which we handle through a different API function.
291            unsafe { Ok(list.assume_init(0)) }
292        } else {
293            // SAFETY: `listxattr` behaves, and we've handled the special
294            // case of an empty buffer above.
295            unsafe { Ok(list.assume_init(len)) }
296        }
297    })
298}
299
300/// `listxattr(path, NULL, 0)`—Query the length of the list of extended
301/// filesystem attributes.
302///
303/// This function uses `listxattr` with no buffer to just return the size of
304/// the list. This can be used to determine the size of a buffer to
305/// allocate for a subsequent call to [`listxattr`], however keep in mind
306/// that other processes may modify attributes at any time.
307///
308/// # References
309///  - [Linux]
310///  - [Apple]
311///
312/// [Linux]: https://man7.org/linux/man-pages/man2/listxattr.2.html
313/// [Apple]: https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man2/listxattr.2.html
314#[inline]
315pub fn listxattr_query<P: path::Arg>(path: P) -> io::Result<usize> {
316    path.into_with_c_str(|path| {
317        // SAFETY: `listxattr` behaves.
318        unsafe { backend::fs::syscalls::listxattr(path, (null_mut(), 0)) }
319    })
320}
321
322/// `llistxattr(path, list.as_ptr(), list.len())`—List extended filesystem
323/// attributes, without following symlinks in the last path component.
324///
325/// Unlike the libc `llistxattr` function, this function does not treat an
326/// buffer with zero length as a way to return the size of the attribute.
327/// To obtain the size, use [`llistxattr_query`].
328///
329/// # References
330///  - [Linux]
331///
332/// [Linux]: https://man7.org/linux/man-pages/man2/llistxattr.2.html
333#[inline]
334pub fn llistxattr<P: path::Arg, Buf: Buffer<u8>>(
335    path: P,
336    mut list: Buf,
337) -> io::Result<Buf::Output> {
338    path.into_with_c_str(|path| {
339        let parts = list.parts_mut();
340        // SAFETY: `llistxattr` behaves.
341        let len = unsafe { backend::fs::syscalls::llistxattr(path, parts)? };
342        if parts.1 == 0 {
343            // `llistxattr` special-cases an input size of 0 as a size
344            // query, which we handle through a different API function.
345            unsafe { Ok(list.assume_init(0)) }
346        } else {
347            // SAFETY: `llistxattr` behaves, and we've handled the special
348            // case of an empty buffer above.
349            unsafe { Ok(list.assume_init(len)) }
350        }
351    })
352}
353
354/// `llistxattr(path, NULL, 0)`—Query the length of the list of extended
355/// filesystem attributes, without following symlinks in the last path
356/// component.
357///
358/// This function uses `llistxattr` with no buffer to just return the size of
359/// the list. This can be used to determine the size of a buffer to
360/// allocate for a subsequent call to [`llistxattr`], however keep in mind
361/// that other processes may modify attributes at any time.
362///
363/// # References
364///  - [Linux]
365///
366/// [Linux]: https://man7.org/linux/man-pages/man2/llistxattr.2.html
367#[inline]
368pub fn llistxattr_query<P: path::Arg>(path: P) -> io::Result<usize> {
369    path.into_with_c_str(|path| {
370        // SAFETY: `llistxattr` behaves.
371        unsafe { backend::fs::syscalls::llistxattr(path, (null_mut(), 0)) }
372    })
373}
374
375/// `flistxattr(fd, list.as_ptr(), list.len())`—List extended filesystem
376/// attributes on an open file descriptor.
377///
378/// Unlike the libc `flistxattr` function, this function does not treat an
379/// buffer with zero length as a way to return the size of the attribute.
380/// To obtain the size, use [`flistxattr_query`].
381///
382/// # References
383///  - [Linux]
384///  - [Apple]
385///
386/// [Linux]: https://man7.org/linux/man-pages/man2/flistxattr.2.html
387/// [Apple]: https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man2/flistxattr.2.html
388#[inline]
389pub fn flistxattr<Fd: AsFd, Buf: Buffer<u8>>(fd: Fd, mut list: Buf) -> io::Result<Buf::Output> {
390    let parts = list.parts_mut();
391    // SAFETY: `flistxattr` behaves.
392    let len = unsafe { backend::fs::syscalls::flistxattr(fd.as_fd(), parts)? };
393    if parts.1 == 0 {
394        // `flistxattr` special-cases an input size of 0 as a size
395        // query, which we handle through a different API function.
396        unsafe { Ok(list.assume_init(0)) }
397    } else {
398        // SAFETY: `flistxattr` behaves, and we've handled the special
399        // case of an empty buffer above.
400        unsafe { Ok(list.assume_init(len)) }
401    }
402}
403
404/// `flistxattr(fd, list.as_ptr(), list.len())`—Query the length of the
405/// list of extended filesystem attributes on an open file descriptor.
406///
407/// This function uses `flistxattr` with no buffer to just return the size of
408/// the list. This can be used to determine the size of a buffer to
409/// allocate for a subsequent call to [`flistxattr`], however keep in mind
410/// that other processes may modify attributes at any time.
411///
412/// # References
413///  - [Linux]
414///
415/// [Linux]: https://man7.org/linux/man-pages/man2/flistxattr.2.html
416#[inline]
417pub fn flistxattr_query<Fd: AsFd>(fd: Fd) -> io::Result<usize> {
418    // SAFETY: `flistxattr` behaves.
419    unsafe { backend::fs::syscalls::flistxattr(fd.as_fd(), (null_mut(), 0)) }
420}
421
422/// `removexattr(path, name)`—Remove an extended filesystem attribute.
423///
424/// # References
425///  - [Linux]
426///  - [Apple]
427///
428/// [Linux]: https://man7.org/linux/man-pages/man2/removexattr.2.html
429/// [Apple]: https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man2/removexattr.2.html
430pub fn removexattr<P: path::Arg, Name: path::Arg>(path: P, name: Name) -> io::Result<()> {
431    path.into_with_c_str(|path| {
432        name.into_with_c_str(|name| backend::fs::syscalls::removexattr(path, name))
433    })
434}
435
436/// `lremovexattr(path, name)`—Remove an extended filesystem attribute,
437/// without following symlinks in the last path component.
438///
439/// # References
440///  - [Linux]
441///
442/// [Linux]: https://man7.org/linux/man-pages/man2/lremovexattr.2.html
443pub fn lremovexattr<P: path::Arg, Name: path::Arg>(path: P, name: Name) -> io::Result<()> {
444    path.into_with_c_str(|path| {
445        name.into_with_c_str(|name| backend::fs::syscalls::lremovexattr(path, name))
446    })
447}
448
449/// `fremovexattr(fd, name)`—Remove an extended filesystem attribute on an
450/// open file descriptor.
451///
452/// # References
453///  - [Linux]
454///  - [Apple]
455///
456/// [Linux]: https://man7.org/linux/man-pages/man2/fremovexattr.2.html
457/// [Apple]: https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man2/fremovexattr.2.html
458pub fn fremovexattr<Fd: AsFd, Name: path::Arg>(fd: Fd, name: Name) -> io::Result<()> {
459    name.into_with_c_str(|name| backend::fs::syscalls::fremovexattr(fd.as_fd(), name))
460}