Skip to main content

xattrs/
types.rs

1#![doc = include_str!("types.md")]
2
3use crate::{
4    Error, Result,
5    util::{PrettyBytes, cow_map},
6};
7use libc::c_char;
8use ref_cast::{RefCastCustom, ref_cast_custom};
9#[cfg(unix)] use std::os::unix::prelude::{OsStrExt as _, OsStringExt as _};
10use std::{
11    borrow::Cow,
12    cmp,
13    ffi::{CStr, CString, OsStr},
14    fmt,
15    path::{Path, PathBuf},
16};
17
18/// A binary string. The borrowed counterpart to [`BString`].
19#[derive(Eq, Ord, Hash)]
20#[derive(RefCastCustom)]
21// #[cfg_attr(feature = "serde", derive(serde::Serialize))]
22#[repr(transparent)]
23pub struct BStr([u8]);
24#[zoet::zoet]
25impl BStr {
26    /// Construct from a byte slice.
27    #[ref_cast_custom]
28    pub const fn from_bytes(s: &[u8]) -> &Self;
29
30    /// Construct from anything which can be converted into a byte slice.
31    pub fn from(s: &(impl AsRef<[u8]> + ?Sized)) -> &Self {
32        Self::from_bytes(s.as_ref())
33    }
34
35    /// Construct a zero-length string.
36    #[zoet(Default)]
37    pub const fn default() -> &'static Self {
38        Self::from_bytes(b"")
39    }
40
41    /// Get the inner byte slice.
42    #[zoet(AsRef)]
43    pub const fn as_bytes(&self) -> &[u8] {
44        &self.0
45    }
46
47    /// Convert into an [`OsStr`].
48    #[zoet(AsRef)]
49    pub fn as_os_str(&self) -> &OsStr {
50        OsStr::from_bytes(self.as_bytes())
51    }
52
53    /// Convert into an owned [`BString`].
54    #[zoet(From, ToOwned)]
55    pub fn to_bstring(&self) -> BString {
56        BString(self.0.to_owned())
57    }
58
59    /// Wrap in a [`Cow::Borrowed`].
60    #[zoet(From)]
61    pub const fn as_cow<'a>(&'a self) -> Cow<'a, Self> {
62        Cow::Borrowed(self)
63    }
64
65    #[allow(dead_code, reason = "functions only used by Debug impl are incorrectly warned as dead")]
66    #[zoet(Debug)]
67    fn debug(&self, f: &mut fmt::Formatter) -> fmt::Result {
68        let pretty = PrettyBytes::new(self);
69        f.debug_tuple("BStr").field(&pretty).finish()
70    }
71}
72
73/// A binary string. The owned counterpart to [`BStr`].
74#[derive(Clone, Eq, Ord, Hash, Default)]
75// #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
76#[repr(transparent)]
77pub struct BString(Vec<u8>);
78
79#[zoet::zoet]
80impl BString {
81    /// Construct from a byte buffer.
82    #[zoet(From)]
83    pub const fn from_vec(s: Vec<u8>) -> Self {
84        Self(s)
85    }
86
87    /// Construct from anything which can be converted into a byte buffer.
88    pub fn from(s: impl Into<Vec<u8>>) -> Self {
89        Self(s.into())
90    }
91
92    /// Unwrap into a byte buffer.
93    #[zoet(From)]
94    pub fn into_inner(self) -> Vec<u8> {
95        self.0
96    }
97
98    // Doesn't need to be pub as BStr::as_bytes is.
99    #[zoet(AsRef)]
100    const fn as_bytes(&self) -> &[u8] {
101        self.0.as_slice()
102    }
103
104    /// Mutably-borrow the underlying byte buffer.
105    #[zoet(AsMut)]
106    pub const fn as_mut_vec(&mut self) -> &mut Vec<u8> {
107        &mut self.0
108    }
109
110    /// Borrow as a [`BStr`].
111    #[zoet(AsRef, Borrow, Deref)]
112    pub const fn as_bstr(&self) -> &BStr {
113        BStr::from_bytes(self.0.as_slice())
114    }
115
116    /// Wrap in a [`Cow::Owned`].
117    #[zoet(From)]
118    pub const fn into_cow(self) -> Cow<BStr, 'static> {
119        Cow::Owned(self)
120    }
121
122    #[allow(dead_code, reason = "functions only used by Debug impl are incorrectly warned as dead")]
123    #[zoet(Debug)]
124    fn debug(&self, f: &mut fmt::Formatter) -> fmt::Result {
125        let pretty = PrettyBytes::new(self.as_bytes());
126        f.debug_tuple("BString").field(&pretty).finish()
127    }
128}
129
130/// A NUL-terminated binary string. The borrowed counterpart to [`ZString`].
131#[derive(Eq, Ord, Hash)]
132#[derive(RefCastCustom)]
133// #[cfg_attr(feature = "serde", derive(serde::Serialize))]
134#[repr(transparent)]
135pub struct ZStr(CStr);
136#[zoet::zoet]
137impl ZStr {
138    /// Construct by wrapping a C string.
139    #[ref_cast_custom]
140    pub const fn from_cstr(s: &CStr) -> &Self;
141
142    // pub fn new<A: AsRef<CStr> + ?Sized>(s: &A) -> &Self {
143    //     Self::from_cstr(s.as_ref())
144    // }
145
146    /// Convert into a byte slice. The trailing NUL is omitted.
147    #[zoet(AsRef)]
148    pub const fn as_bytes(&self) -> &[u8] {
149        self.0.to_bytes()
150    }
151
152    /// Convert into a `BStr`. The trailing NUL is omitted.
153    #[zoet(AsRef)]
154    pub const fn as_bstr(&self) -> &BStr {
155        BStr::from_bytes(self.as_bytes())
156    }
157
158    /// Get the inner `CStr`.
159    #[zoet(AsRef)]
160    pub const fn as_cstr(&self) -> &CStr {
161        &self.0
162    }
163
164    /// Convert into `OsStr`.
165    #[zoet(AsRef)]
166    pub fn as_os_str(&self) -> &OsStr {
167        OsStr::from_bytes(self.as_bytes())
168    }
169
170    /// Convert into a raw pointer.
171    pub const fn as_ptr(&self) -> *const c_char {
172        self.0.as_ptr()
173    }
174
175    /// Convert into an owned [`ZString`].
176    #[zoet(From, ToOwned)]
177    pub fn to_zstring(&self) -> ZString {
178        ZString(self.0.to_owned())
179    }
180
181    /// Wrap in a [`Cow::Borrowed`].
182    #[zoet(From)]
183    pub const fn as_cow<'a>(&'a self) -> Cow<'a, ZStr> {
184        Cow::Borrowed(self)
185    }
186
187    #[allow(dead_code, reason = "functions only used by Debug impl are incorrectly warned as dead")]
188    #[zoet(Debug)]
189    fn debug(&self, f: &mut fmt::Formatter) -> fmt::Result {
190        let pretty = PrettyBytes::new(self);
191        f.debug_tuple("ZStr").field(&pretty).finish()
192    }
193}
194
195/// A NUL-terminated binary string. The owned counterpart to [`ZStr`].
196#[derive(Clone, Eq, Ord, Hash)]
197// #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
198#[repr(transparent)]
199pub struct ZString(CString);
200#[zoet::zoet]
201impl ZString {
202    /// Construct from a C string.
203    #[zoet(From)]
204    pub const fn from_cstring(s: CString) -> Self {
205        Self(s)
206    }
207
208    /// Unwrap into the inner C string.
209    #[zoet(From)]
210    pub fn into_inner(self) -> CString {
211        self.0
212    }
213
214    /// Borrow as a byte sluce. The trailing NUL is omitted.
215    #[zoet(AsRef)]
216    pub fn as_bytes(&self) -> &[u8] {
217        self.0.as_bytes()
218    }
219
220    /// Borrow as a `ZStr`.
221    #[zoet(AsRef, Borrow, Deref)]
222    pub fn as_zstr(&self) -> &ZStr {
223        ZStr::from_cstr(self.0.as_c_str())
224    }
225
226    /// Wrap in a [`Cow::Owned`].
227    #[zoet(From)]
228    pub fn into_cow(self) -> Cow<ZStr, 'static> {
229        Cow::Owned(self)
230    }
231
232    #[allow(dead_code, reason = "functions only used by Debug impl are incorrectly warned as dead")]
233    #[zoet(Debug)]
234    fn debug(&self, f: &mut fmt::Formatter) -> fmt::Result {
235        let pretty = PrettyBytes::new(self.as_bytes());
236        f.debug_tuple("ZString").field(&pretty).finish()
237    }
238}
239
240impl ZString {
241    /// Fallibly attempt to convert from something which converts into a byte buffer.
242    ///
243    /// # Errors
244    ///
245    /// This will fail if the buffer contains a NUL.
246    pub fn try_from_bytes(t: impl Into<Vec<u8>>) -> Result<Self> {
247        CString::new(t.into())
248            .map(Self)
249            .map_err(|_| Error::UnexpectedNul)
250    }
251
252    /// Prepends a value onto the inner C string.
253    ///
254    /// # Safety
255    ///
256    /// The prefix sould not contain NULs as these are not allowed in C strings. While this does
257    /// not in itself violate memory safety, it is definitely a logic bug which can affect security,
258    /// and the current implementation uses the unsafe [`CString::from_vec_with_nul_unchecked`]
259    /// which requires no inner NULs.
260    pub unsafe fn prepend_unchecked(&mut self, prefix: &[u8]) {
261        // We need to prepend some stuff onto the C string. This is Fun™ because `&mut Cow<CStr>`
262        // doesn't provide the operations required to prepend bytes, but `Vec<u8>` does. So we have
263        // to use `mem::take` to get an owned value (fortunately `Cow<CStr>` implements `Default`)
264        // which can be `into`-converted as required and then put back when done.
265        //
266        // For the purposes of this example, let's suppose `self` is a C string containing "name",
267        // and `prefix` is "user."
268        //
269        // Start off by taking the `Cow` and converting to a NUL-terminated `Vec<u8>`. The buffer
270        // now contains "name\0".
271        let mut buf = std::mem::take(&mut self.0).into_bytes_with_nul();
272        // We now append the prefix to the buffer, which now contains "name\0user.".
273        buf.extend(prefix);
274        // Rotate the buffer. It is now "user.name\0", which is what we want.
275        buf.rotate_right(prefix.len());
276        // Finally, we convert back to a CString and store it back.
277        //
278        // SAFETY: we took an already-checked C string and prepended a string to it. The caller
279        // promised that the prepended string does not contain NULs. The resulting string thus
280        // maintains CString invariants of having a single NUL at the end.
281        self.0 = unsafe { CString::from_vec_with_nul_unchecked(buf) };
282    }
283}
284
285macro_rules! make_partial_ord {
286    ($TYPE:ty) => {
287        #[zoet::zoet]
288        impl $TYPE {
289            #[zoet(PartialEq, PartialOrd)]
290            fn partial_cmp<T: AsRef<[u8]> + ?Sized>(&self, rhs: &T) -> cmp::Ordering {
291                self.as_bytes().cmp(rhs.as_ref())
292            }
293        }
294    };
295}
296
297make_partial_ord! { BStr }
298make_partial_ord! { BString }
299make_partial_ord! { ZStr }
300make_partial_ord! { ZString }
301
302/// Fallible conversion into NUL-terminated strings.
303///
304/// This is a helper trait for conversion of common path and string types into a NUL-terminated C
305/// string. Ideally it would not have needed to exist and `TryInto<Cow<ZStr>>` used instead, but
306/// `TryInto` has some unfortunate conflicting blanket impls.
307///
308/// It is intended to be used by generic functions in a similar vein to `AsRef` to say "I take
309/// anything that looks like a file or xattr name":
310///
311/// ```
312/// use xattrs::types::IntoZString;
313/// pub fn get_xattr<'a>(path: impl IntoZString<'a>, name: impl IntoZString<'a>) {
314///     // stuff
315/// }
316/// ```
317///
318/// There are various precanned `impl`s of this trait on useful standard types.
319pub trait IntoZString<'a> {
320    /// Fallibly converts into a [`ZString`].
321    ///
322    /// # Errors
323    ///
324    /// This will fail if the value cannot be converted into a [`ZString`], for example if it
325    /// contains a NUL character.
326    fn into_zstring(self) -> Result<Cow<'a, ZStr>>;
327}
328
329// Trivial map/wrap of CStr/ZStr, owned and Cow types.
330impl<'a> IntoZString<'a> for &'a CStr {
331    #[inline]
332    fn into_zstring(self) -> Result<Cow<'a, ZStr>> {
333        Ok(ZStr::from_cstr(self).into())
334    }
335}
336impl IntoZString<'static> for CString {
337    #[inline]
338    fn into_zstring(self) -> Result<Cow<'static, ZStr>> {
339        Ok(ZString::from(self).into())
340    }
341}
342impl<'a> IntoZString<'a> for Cow<'a, CStr> {
343    #[inline]
344    fn into_zstring(self) -> Result<Cow<'a, ZStr>> {
345        Ok(cow_map(self, ZStr::from_cstr, ZString::from_cstring))
346    }
347}
348impl<'a> IntoZString<'a> for &'a ZStr {
349    #[inline]
350    fn into_zstring(self) -> Result<Cow<'a, ZStr>> {
351        Ok(self.into())
352    }
353}
354impl IntoZString<'static> for ZString {
355    #[inline]
356    fn into_zstring(self) -> Result<Cow<'static, ZStr>> {
357        Ok(self.into())
358    }
359}
360impl<'a> IntoZString<'a> for Cow<'a, ZStr> {
361    #[inline]
362    fn into_zstring(self) -> Result<Cow<'a, ZStr>> {
363        Ok(self)
364    }
365}
366
367// Fallible conversion from byte strings
368impl IntoZString<'static> for &BStr {
369    #[inline]
370    fn into_zstring(self) -> Result<Cow<'static, ZStr>> {
371        self.to_owned().into_zstring()
372    }
373}
374impl IntoZString<'static> for BString {
375    #[inline]
376    fn into_zstring(self) -> Result<Cow<'static, ZStr>> {
377        ZString::try_from_bytes(self.0).map(Into::into)
378    }
379}
380
381// Path(Buf)s need to go via OsStr(ing) on their way to becoming Vec<u8>.
382impl IntoZString<'static> for PathBuf {
383    fn into_zstring(self) -> Result<Cow<'static, ZStr>> {
384        self.into_os_string().into_vec().into_zstring()
385    }
386}
387impl IntoZString<'static> for &Path {
388    fn into_zstring(self) -> Result<Cow<'static, ZStr>> {
389        self.as_os_str().as_bytes().into_zstring()
390    }
391}
392
393// Of all the things which impl Into<Vec<u8>>, only Vec<u8> (i.e. a no-op) and String are useful.
394// Callers with other things probably know what they're doing and should convert themselves.
395impl IntoZString<'static> for Vec<u8> {
396    fn into_zstring(self) -> Result<Cow<'static, ZStr>> {
397        let s = CString::new(self).map_err(|_nul_error| Error::UnexpectedNul)?;
398        s.into_zstring()
399    }
400}
401impl IntoZString<'static> for String {
402    fn into_zstring(self) -> Result<Cow<'static, ZStr>> {
403        self.into_bytes().into_zstring()
404    }
405}
406
407// CoW types
408// impl<'a> IntoZString<'a> for BString<'a> {
409//     fn into_zstring(self) -> Result<ZString<'a>> {
410//         match self.into_inner() {
411//             Cow::Borrowed(bytes) => bytes.into_zstring(),
412//             Cow::Owned(vec) => vec.into_zstring(),
413//         }
414//     }
415// }
416
417//... and the borrowed things just need to be cloned:
418impl IntoZString<'static> for &[u8] {
419    fn into_zstring(self) -> Result<Cow<'static, ZStr>> {
420        self.to_vec().into_zstring()
421    }
422}
423impl IntoZString<'static> for &str {
424    fn into_zstring(self) -> Result<Cow<'static, ZStr>> {
425        self.to_owned().into_zstring()
426    }
427}
428
429// // References, typically stuff like &PathBuf.
430// impl<'a, T: IntoZString<'a> + Clone> IntoZString<'a> for &T {
431//     fn into_zstring(self) -> Result<Cow<'a, ZStr>> {
432//         self.clone().into_zstring()
433//     }
434// }