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}