extattr/platforms/
linux_and_android.rs

1//! EA syscall bindings for Linux and Android
2
3use crate::Result;
4use bitflags::bitflags;
5use errno::{errno, Errno};
6use std::{
7    ffi::{CString, OsStr, OsString},
8    os::unix::{ffi::OsStrExt, io::RawFd},
9    path::Path,
10    ptr::null_mut,
11};
12
13bitflags! {
14    /// `flags` used when setting EAs
15    pub struct Flags: libc::c_int {
16        /// Perform a pure create, which fails if the named attribute exists
17        /// already.
18        const XATTR_CREATE = libc::XATTR_CREATE;
19        /// Perform a pure replace operation, which fails if the named attribute
20        /// does not already exist.
21        const XATTR_REPLACE = libc::XATTR_REPLACE;
22    }
23}
24
25/// Retrieves the list of extended attribute names associated with the given `path`
26/// in the filesystem. If `path` is a symbolic link, it will be dereferenced.
27///
28/// For more infomation, see [listxattr(2)](https://man7.org/linux/man-pages/man2/listxattr.2.html)
29pub fn listxattr<P: AsRef<Path>>(path: P) -> Result<Vec<OsString>> {
30    let path = match CString::new(path.as_ref().as_os_str().as_bytes()) {
31        Ok(p) => p,
32        _ => return Err(Errno(libc::EINVAL)),
33    };
34
35    // query the buffer size
36    let buffer_size =
37        match unsafe { libc::listxattr(path.as_ptr(), null_mut(), 0) } {
38            -1 => return Err(errno()),
39            0 => return Ok(Vec::new()),
40            buffer_size => buffer_size as usize,
41        };
42
43    let mut buffer: Vec<u8> = Vec::with_capacity(buffer_size);
44    let res = unsafe {
45        libc::listxattr(path.as_ptr(), buffer.as_mut_ptr().cast(), buffer_size)
46    };
47
48    match res {
49        -1 => Err(errno()),
50        len => {
51            unsafe { buffer.set_len(len as usize) };
52            Ok(buffer[..(len - 1) as usize]
53                .split(|&item| item == 0)
54                .map(OsStr::from_bytes)
55                .map(|str| str.to_owned())
56                .collect::<Vec<OsString>>())
57        }
58    }
59}
60
61/// Retrieves the list of extended attribute names associated with the given `path`
62/// in the filesystem. If `path` is a symbolic link, the list of names associated
63/// with the link *itself* will be returned.
64///
65/// For more infomation, see [llistxattr(2)](https://man7.org/linux/man-pages/man2/listxattr.2.html)
66pub fn llistxattr<P: AsRef<Path>>(path: P) -> Result<Vec<OsString>> {
67    let path = match CString::new(path.as_ref().as_os_str().as_bytes()) {
68        Ok(p) => p,
69        _ => return Err(Errno(libc::EINVAL)),
70    };
71
72    // query the buffer size
73    let buffer_size =
74        match unsafe { libc::llistxattr(path.as_ptr(), null_mut(), 0) } {
75            -1 => return Err(errno()),
76            0 => return Ok(Vec::new()),
77            buffer_size => buffer_size as usize,
78        };
79
80    let mut buffer: Vec<u8> = Vec::with_capacity(buffer_size);
81    let res = unsafe {
82        libc::llistxattr(path.as_ptr(), buffer.as_mut_ptr().cast(), buffer_size)
83    };
84
85    match res {
86        -1 => Err(errno()),
87        len => {
88            unsafe { buffer.set_len(len as usize) };
89            Ok(buffer[..(len - 1) as usize]
90                .split(|&item| item == 0)
91                .map(OsStr::from_bytes)
92                .map(|str| str.to_owned())
93                .collect::<Vec<OsString>>())
94        }
95    }
96}
97
98/// Retrieves the list of extended attribute names associated with the file
99/// specified by the open file descriptor `fd` in the filesystem.
100///
101/// For more infomation, see [flistxattr(2)](https://man7.org/linux/man-pages/man2/listxattr.2.html)
102pub fn flistxattr(fd: RawFd) -> Result<Vec<OsString>> {
103    // query the buffer size
104    let buffer_size = match unsafe { libc::flistxattr(fd, null_mut(), 0) } {
105        -1 => return Err(errno()),
106        0 => return Ok(Vec::new()),
107        buffer_size => buffer_size as usize,
108    };
109
110    let mut buffer: Vec<u8> = Vec::with_capacity(buffer_size);
111    let res = unsafe {
112        libc::flistxattr(fd, buffer.as_mut_ptr().cast(), buffer_size)
113    };
114
115    match res {
116        -1 => Err(errno()),
117        len => {
118            unsafe { buffer.set_len(len as usize) };
119            Ok(buffer[..(len - 1) as usize]
120                .split(|&item| item == 0)
121                .map(OsStr::from_bytes)
122                .map(|str| str.to_owned())
123                .collect::<Vec<OsString>>())
124        }
125    }
126}
127
128/// Retrieves the value of the extended attribute identified by `name` and
129/// associated with the given `path` in the filesystem. If `path` is a symbolic
130/// link, it will be dereferenced.
131///
132/// For more information, see [getxattr(2)](https://man7.org/linux/man-pages/man2/getxattr.2.html)
133pub fn getxattr<P, S>(path: P, name: S) -> Result<Vec<u8>>
134where
135    P: AsRef<Path>,
136    S: AsRef<OsStr>,
137{
138    let name = match CString::new(name.as_ref().as_bytes()) {
139        Ok(n) => n,
140        _ => return Err(Errno(libc::EINVAL)),
141    };
142    let path = match CString::new(path.as_ref().as_os_str().as_bytes()) {
143        Ok(n) => n,
144        _ => return Err(Errno(libc::EINVAL)),
145    };
146
147    // query the buffer size
148    let buffer_size = match unsafe {
149        libc::getxattr(path.as_ptr(), name.as_ptr(), null_mut(), 0)
150    } {
151        -1 => return Err(errno()),
152        0 => return Ok(Vec::new()),
153        buffer_size => buffer_size as usize,
154    };
155
156    let mut buffer: Vec<u8> = Vec::with_capacity(buffer_size);
157
158    let res = unsafe {
159        libc::getxattr(
160            path.as_ptr(),
161            name.as_ptr(),
162            buffer.as_mut_ptr().cast(),
163            buffer_size,
164        )
165    };
166
167    match res {
168        -1 => Err(errno()),
169        len => {
170            unsafe { buffer.set_len(len as usize) };
171            Ok(buffer)
172        }
173    }
174}
175
176/// Retrieves the value of the extended attribute identified by `name` and
177/// associated with the given `path` in the filesystem. If `path` is a symbolic
178/// link, the list of names associated with the link *itself* will be returned.
179///
180/// For more information, see [lgetxattr(2)](https://man7.org/linux/man-pages/man2/getxattr.2.html)
181pub fn lgetxattr<P, S>(path: P, name: S) -> Result<Vec<u8>>
182where
183    P: AsRef<Path>,
184    S: AsRef<OsStr>,
185{
186    let name = match CString::new(name.as_ref().as_bytes()) {
187        Ok(n) => n,
188        _ => return Err(Errno(libc::EINVAL)),
189    };
190    let path = match CString::new(path.as_ref().as_os_str().as_bytes()) {
191        Ok(n) => n,
192        _ => return Err(Errno(libc::EINVAL)),
193    };
194
195    // query the buffer size
196    let buffer_size = match unsafe {
197        libc::lgetxattr(path.as_ptr(), name.as_ptr(), null_mut(), 0)
198    } {
199        -1 => return Err(errno()),
200        0 => return Ok(Vec::new()),
201        buffer_size => buffer_size as usize,
202    };
203
204    let mut buffer: Vec<u8> = Vec::with_capacity(buffer_size);
205
206    let res = unsafe {
207        libc::lgetxattr(
208            path.as_ptr(),
209            name.as_ptr(),
210            buffer.as_mut_ptr().cast(),
211            buffer_size,
212        )
213    };
214
215    match res {
216        -1 => Err(errno()),
217        len => {
218            unsafe { buffer.set_len(len as usize) };
219            Ok(buffer)
220        }
221    }
222}
223
224/// Retrieves the value of the extended attribute identified by `name` and
225/// associated with the file specified by the open file descriptor `fd` in the
226/// filesystem.
227///
228/// For more information, see [fgetxattr(2)](https://man7.org/linux/man-pages/man2/getxattr.2.html)
229pub fn fgetxattr<S>(fd: RawFd, name: S) -> Result<Vec<u8>>
230where
231    S: AsRef<OsStr>,
232{
233    let name = match CString::new(name.as_ref().as_bytes()) {
234        Ok(name) => name,
235        _ => return Err(Errno(libc::EINVAL)),
236    };
237
238    // query the buffer size
239    let buffer_size =
240        match unsafe { libc::fgetxattr(fd, name.as_ptr(), null_mut(), 0) } {
241            -1 => return Err(errno()),
242            0 => return Ok(Vec::new()),
243            buffer_size => buffer_size as usize,
244        };
245
246    let mut buffer: Vec<u8> = Vec::with_capacity(buffer_size);
247
248    let res = unsafe {
249        libc::fgetxattr(
250            fd,
251            name.as_ptr(),
252            buffer.as_mut_ptr().cast(),
253            buffer_size,
254        )
255    };
256
257    match res {
258        -1 => Err(errno()),
259        len => {
260            unsafe { buffer.set_len(len as usize) };
261            Ok(buffer)
262        }
263    }
264}
265
266/// Removes the extended attribute identified by `name` and associated with the
267/// given `path` in the filesystem. If `path` is a symbolic link, it will be
268/// dereferenced.
269///
270/// For more information, see [removexattr(2)](https://man7.org/linux/man-pages/man2/removexattr.2.html)
271pub fn removexattr<P, S>(path: P, name: S) -> Result<()>
272where
273    P: AsRef<Path>,
274    S: AsRef<OsStr>,
275{
276    let path = match CString::new(path.as_ref().as_os_str().as_bytes()) {
277        Ok(n) => n,
278        _ => return Err(Errno(libc::EINVAL)),
279    };
280    let name = match CString::new(name.as_ref().as_bytes()) {
281        Ok(name) => name,
282        _ => return Err(Errno(libc::EINVAL)),
283    };
284
285    let res = unsafe { libc::removexattr(path.as_ptr(), name.as_ptr()) };
286
287    match res {
288        -1 => Err(errno()),
289        _ => Ok(()),
290    }
291}
292
293/// Removes the extended attribute identified by `name` and associated with the
294/// given `path` in the filesystem. If `path` is a symbolic link, extended
295/// attribute is removed from the link *itself*.
296///
297/// For more information, see [lremovexattr(2)](https://man7.org/linux/man-pages/man2/removexattr.2.html)
298pub fn lremovexattr<P, S>(path: P, name: S) -> Result<()>
299where
300    P: AsRef<Path>,
301    S: AsRef<OsStr>,
302{
303    let path = match CString::new(path.as_ref().as_os_str().as_bytes()) {
304        Ok(n) => n,
305        _ => return Err(Errno(libc::EINVAL)),
306    };
307    let name = match CString::new(name.as_ref().as_bytes()) {
308        Ok(name) => name,
309        _ => return Err(Errno(libc::EINVAL)),
310    };
311
312    let res = unsafe { libc::lremovexattr(path.as_ptr(), name.as_ptr()) };
313
314    match res {
315        -1 => Err(errno()),
316        _ => Ok(()),
317    }
318}
319
320/// Removes the extended attribute identified by `name` and associated with the
321/// file specified by the open file descriptor `fd`.
322///
323/// For more information, see [fremovexattr(2)](https://man7.org/linux/man-pages/man2/removexattr.2.html)
324pub fn fremovexattr<S>(fd: RawFd, name: S) -> Result<()>
325where
326    S: AsRef<OsStr>,
327{
328    let name = match CString::new(name.as_ref().as_bytes()) {
329        Ok(name) => name,
330        _ => return Err(Errno(libc::EINVAL)),
331    };
332    let res = unsafe { libc::fremovexattr(fd, name.as_ptr()) };
333
334    match res {
335        -1 => Err(errno()),
336        _ => Ok(()),
337    }
338}
339
340/// Sets the `value` of the extended attribute identified by `name` and associated
341/// with the given `path` in the filesystem. If `path` is a symbolic link, it will
342/// be dereferenced.
343///
344/// For more information, see [setxattr(2)](https://man7.org/linux/man-pages/man2/lsetxattr.2.html)
345pub fn setxattr<P, S, B>(path: P, name: S, value: B, flags: Flags) -> Result<()>
346where
347    P: AsRef<Path>,
348    S: AsRef<OsStr>,
349    B: AsRef<[u8]>,
350{
351    let path = match CString::new(path.as_ref().as_os_str().as_bytes()) {
352        Ok(n) => n,
353        _ => return Err(Errno(libc::EINVAL)),
354    };
355    let name = match CString::new(name.as_ref().as_bytes()) {
356        Ok(name) => name,
357        _ => return Err(Errno(libc::EINVAL)),
358    };
359
360    let value_ptr = value.as_ref().as_ptr().cast();
361    let value_len = value.as_ref().len();
362
363    let res = unsafe {
364        libc::setxattr(
365            path.as_ptr(),
366            name.as_ptr(),
367            value_ptr,
368            value_len,
369            flags.bits(),
370        )
371    };
372
373    match res {
374        -1 => Err(errno()),
375        _ => Ok(()),
376    }
377}
378
379/// Sets the `value` of the extended attribute identified by `name` and associated
380/// with the given `path` in the filesystem. If `path` is a symbolic link, the
381/// extended attribute is set on the link *itself*.
382///
383/// For more information, see [lsetxattr(2)](https://man7.org/linux/man-pages/man2/lsetxattr.2.html)
384pub fn lsetxattr<P, S, B>(
385    path: P,
386    name: S,
387    value: B,
388    flags: Flags,
389) -> Result<()>
390where
391    P: AsRef<Path>,
392    S: AsRef<OsStr>,
393    B: AsRef<[u8]>,
394{
395    let path = match CString::new(path.as_ref().as_os_str().as_bytes()) {
396        Ok(n) => n,
397        _ => return Err(Errno(libc::EINVAL)),
398    };
399    let name = match CString::new(name.as_ref().as_bytes()) {
400        Ok(name) => name,
401        _ => return Err(Errno(libc::EINVAL)),
402    };
403
404    let value_ptr = value.as_ref().as_ptr().cast();
405    let value_len = value.as_ref().len();
406
407    let res = unsafe {
408        libc::lsetxattr(
409            path.as_ptr(),
410            name.as_ptr(),
411            value_ptr,
412            value_len,
413            flags.bits(),
414        )
415    };
416
417    match res {
418        -1 => Err(errno()),
419        _ => Ok(()),
420    }
421}
422
423/// Sets the `value` of the extended attribute identified by `name` and associated
424/// with the file specified by the open file descriptor `fd`.
425///
426/// For more information, see [fsetxattr(2)](https://man7.org/linux/man-pages/man2/lsetxattr.2.html)
427pub fn fsetxattr<S, B>(fd: RawFd, name: S, value: B, flags: Flags) -> Result<()>
428where
429    S: AsRef<OsStr>,
430    B: AsRef<[u8]>,
431{
432    let name = match CString::new(name.as_ref().as_bytes()) {
433        Ok(name) => name,
434        _ => return Err(Errno(libc::EINVAL)),
435    };
436
437    let value_ptr = value.as_ref().as_ptr().cast();
438    let value_len = value.as_ref().len();
439
440    let res = unsafe {
441        libc::fsetxattr(fd, name.as_ptr(), value_ptr, value_len, flags.bits())
442    };
443
444    match res {
445        -1 => Err(errno()),
446        _ => Ok(()),
447    }
448}