libcryptsetup_rs/
macros.rs

1// This Source Code Form is subject to the terms of the Mozilla Public
2// License, v. 2.0. If a copy of the MPL was not distributed with this
3// file, You can obtain one at http://mozilla.org/MPL/2.0/.
4
5/// Wrap all libcryptsetup_rs_sys calls in the macro. It will expand to a
6/// feature-flagged mutex lock call. If the `mutex` feature is not enabled, it is
7/// a no-op.
8macro_rules! mutex {
9    ( $libcryptsetup_call:expr ) => {{
10        #[cfg(feature = "mutex")]
11        #[allow(unused_variables)]
12        let lock = $crate::MUTEX.acquire();
13
14        #[cfg(not(feature = "mutex"))]
15        if *$crate::THREAD_ID != std::thread::current().id() {
16            panic!("Enable the mutex feature for this crate to allow calling libcryptsetup methods from multiple threads");
17        }
18
19        unsafe { $libcryptsetup_call }
20    }};
21}
22
23/// Convert an errno-zero-success return pattern into a `Result<(), LibcryptErr>`
24macro_rules! errno {
25    ( $rc:expr ) => {
26        match $rc {
27            i if i < 0 => {
28                return Err($crate::err::LibcryptErr::IOError(
29                    std::io::Error::from_raw_os_error(-i),
30                ))
31            }
32            i if i > 0 => panic!("Unexpected return value {}", i),
33            _ => Result::<(), $crate::err::LibcryptErr>::Ok(()),
34        }
35    };
36}
37
38/// Convert an errno-positive-int-success return pattern into a `Result<std::os::raw::c_int, LibcryptErr>`
39macro_rules! errno_int_success {
40    ( $rc:expr ) => {
41        match $rc {
42            i if i < 0 => {
43                return Err($crate::err::LibcryptErr::IOError(
44                    std::io::Error::from_raw_os_error(-i),
45                ))
46            }
47            i => Result::<_, $crate::err::LibcryptErr>::Ok(i),
48        }
49    };
50}
51
52/// Convert an integer return value into specified type
53macro_rules! int_to_return {
54    ( $rc:expr, $type:ty ) => {
55        <$type>::from($rc)
56    };
57}
58
59/// Try converting an integer return value into specified type
60macro_rules! try_int_to_return {
61    ( $rc:expr, $type:ty ) => {
62        <$type>::try_from($rc)
63    };
64}
65
66/// Convert a pointer to an `Option` containing a pointer
67macro_rules! ptr_to_option {
68    ( $ptr:expr ) => {{
69        let p = $ptr;
70        if p.is_null() {
71            None
72        } else {
73            Some(p)
74        }
75    }};
76}
77
78/// Convert a pointer to a `Option` containing a reference
79macro_rules! ptr_to_option_with_reference {
80    ( $ptr:expr ) => {{
81        let p = $ptr;
82        unsafe { p.as_ref() }
83    }};
84}
85
86/// Convert a pointer to an `Result` containing a pointer
87macro_rules! ptr_to_result {
88    ( $ptr:expr ) => {{
89        ptr_to_option!($ptr).ok_or($crate::err::LibcryptErr::NullPtr)
90    }};
91}
92
93/// Convert a pointer to a `Result` containing a reference
94macro_rules! ptr_to_result_with_reference {
95    ( $ptr:expr ) => {{
96        let p = $ptr;
97        unsafe { p.as_ref() }.ok_or($crate::err::LibcryptErr::NullPtr)
98    }};
99}
100
101/// Convert a `Path` type into `CString`
102macro_rules! path_to_cstring {
103    ( $path:expr ) => {
104        match $path
105            .to_str()
106            .ok_or_else(|| LibcryptErr::InvalidConversion)
107            .and_then(|s| std::ffi::CString::new(s).map_err(LibcryptErr::NullError))
108        {
109            Ok(s) => Ok(s),
110            Err(e) => Err(e),
111        }
112    };
113}
114
115/// Convert a string type into `CString`
116macro_rules! to_cstring {
117    ( $str:expr ) => {
118        match std::ffi::CString::new($str.as_bytes()) {
119            Ok(s) => Ok(s),
120            Err(e) => Err($crate::err::LibcryptErr::NullError(e)),
121        }
122    };
123}
124
125/// Convert a byte slice into `*const c_char`
126macro_rules! to_byte_ptr {
127    ( $bytes:expr ) => {
128        $bytes.as_ptr().cast::<std::os::raw::c_char>()
129    };
130}
131
132/// Convert a byte slice into `*mut c_char`
133macro_rules! to_mut_byte_ptr {
134    ( $bytes:expr ) => {
135        $bytes.as_mut_ptr().cast::<std::os::raw::c_char>()
136    };
137}
138
139/// Convert a `*const c_char` into a `&str` type
140#[macro_export]
141macro_rules! from_str_ptr {
142    ( $str_ptr:expr ) => {{
143        let str_ptr = $str_ptr;
144        unsafe { ::std::ffi::CStr::from_ptr(str_ptr) }
145            .to_str()
146            .map_err($crate::LibcryptErr::Utf8Error)
147    }};
148}
149
150/// Convert a `*const c_char` into a `String` type
151macro_rules! from_str_ptr_to_owned {
152    ( $str_ptr:expr ) => {
153        unsafe { ::std::ffi::CStr::from_ptr($str_ptr) }
154            .to_str()
155            .map_err($crate::err::LibcryptErr::Utf8Error)
156            .map(|s| s.to_string())
157    };
158}
159
160/// Convert constants to and from a flag enum
161macro_rules! consts_to_from_enum {
162    ( #[$meta:meta] $flag_enum:ident, $flag_type:ty, $( $name:ident => $constant:expr ),* ) => {
163        #[$meta]
164        #[derive(Copy, Clone, Debug, Eq, PartialEq)]
165        pub enum $flag_enum {
166            $(
167                #[allow(missing_docs)]
168                $name,
169            )*
170        }
171
172        #[allow(clippy::from_over_into)]
173        impl std::convert::Into<$flag_type> for $flag_enum {
174            fn into(self) -> $flag_type {
175                match self {
176                    $(
177                        $flag_enum::$name => $constant,
178                    )*
179                }
180            }
181        }
182
183        impl std::convert::TryFrom<$flag_type> for $flag_enum {
184            type Error = $crate::err::LibcryptErr;
185
186            fn try_from(v: $flag_type) -> Result<Self, Self::Error> {
187                Ok(match v {
188                    $(
189                        i if i == $constant => $flag_enum::$name,
190                    )*
191                    _ => return Err($crate::err::LibcryptErr::InvalidConversion),
192                })
193            }
194        }
195    };
196}
197
198#[macro_export]
199/// Create a C-compatible static string with a null byte
200macro_rules! c_str {
201    ( $str:tt ) => {
202        concat!($str, "\0")
203    };
204}
205
206#[macro_export]
207/// Create a C-compatible callback to determine user confirmation which wraps safe Rust code
208macro_rules! c_confirm_callback {
209    ( $fn_name:ident, $type:ty, $safe_fn_name:ident ) => {
210        extern "C" fn $fn_name(
211            msg: *const std::os::raw::c_char,
212            usrptr: *mut std::os::raw::c_void,
213        ) -> std::os::raw::c_int {
214            let msg_str =
215                $crate::from_str_ptr!(msg).expect("Invalid message string passed to cryptsetup-rs");
216            let generic_ptr = usrptr.cast::<$type>();
217            let generic_ref = unsafe { generic_ptr.as_mut() };
218
219            $safe_fn_name(msg_str, generic_ref) as std::os::raw::c_int
220        }
221    };
222}
223
224#[macro_export]
225/// Create a C-compatible logging callback which wraps safe Rust code
226macro_rules! c_logging_callback {
227    ( $fn_name:ident, $type:ty, $safe_fn_name:ident ) => {
228        extern "C" fn $fn_name(
229            level: std::os::raw::c_int,
230            msg: *const std::os::raw::c_char,
231            usrptr: *mut std::os::raw::c_void,
232        ) {
233            let level = <$crate::consts::vals::CryptLogLevel as std::convert::TryFrom<
234                std::os::raw::c_int,
235            >>::try_from(level)
236            .expect("Invalid logging level passed to cryptsetup-rs");
237            let msg_str = $crate::from_str_ptr!(msg)
238                .expect("Invalid message string passed to cryptsetup-rs")
239                .trim();
240            let generic_ptr = usrptr.cast::<$type>();
241            let generic_ref = unsafe { generic_ptr.as_mut() };
242
243            $safe_fn_name(level, msg_str, generic_ref);
244        }
245    };
246}
247
248#[macro_export]
249/// Create a C-compatible progress callback for wiping a device which wraps safe Rust code
250macro_rules! c_progress_callback {
251    ( $fn_name:ident, $type:ty, $safe_fn_name:ident ) => {
252        extern "C" fn $fn_name(
253            size: u64,
254            offset: u64,
255            usrptr: *mut std::os::raw::c_void,
256        ) -> std::os::raw::c_int {
257            let generic_ptr = usrptr.cast::<$type>();
258            let generic_ref = unsafe { generic_ptr.as_mut() };
259
260            $safe_fn_name(size, offset, generic_ref) as std::os::raw::c_int
261        }
262    };
263}
264
265#[macro_export]
266/// Create a C-compatible open callback compatible with `CryptTokenHandler`
267macro_rules! c_token_handler_open {
268    ( $fn_name:ident, $type:ty, $safe_fn_name:ident ) => {
269        extern "C" fn $fn_name(
270            cd: *mut libcryptsetup_rs_sys::crypt_device,
271            token_id: std::os::raw::c_int,
272            buffer: *mut *mut std::os::raw::c_char,
273            buffer_len: *mut $crate::SizeT,
274            usrptr: *mut std::os::raw::c_void,
275        ) -> std::os::raw::c_int {
276            let device = $crate::device::CryptDevice::from_ptr(cd);
277            let generic_ptr = usrptr as *mut $type;
278            let generic_ref = unsafe { generic_ptr.as_mut() };
279
280            let buffer: Result<Box<[u8]>, $crate::LibcryptErr> =
281                $safe_fn_name(device, token_id, generic_ref);
282            match buffer {
283                Ok(()) => {
284                    *buffer = Box::into_raw(buffer) as *mut std::os::raw::c_char;
285                    0
286                }
287                Err(_) => -1,
288            }
289        }
290    };
291}
292
293#[macro_export]
294/// Create a C-compatible callback for free compatible with `CryptTokenHandler`
295macro_rules! c_token_handler_free {
296    ( $fn_name:ident, $safe_fn_name:ident ) => {
297        extern "C" fn $fn_name(buffer: *mut std::os::raw::c_void, buffer_len: $crate::SizeT) {
298            let boxed_slice = unsafe {
299                Box::from_raw(std::slice::from_raw_parts_mut(
300                    buffer as *mut u8,
301                    buffer_len as usize,
302                ))
303            };
304
305            $safe_fn_name(boxed_slice)
306        }
307    };
308}
309
310#[macro_export]
311/// Create a C-compatible callback for validate compatible with `CryptTokenHandler`
312macro_rules! c_token_handler_validate {
313    ( $fn_name:ident, $safe_fn_name:ident ) => {
314        extern "C" fn $fn_name(
315            cd: *mut libcryptsetup_rs_sys::crypt_device,
316            json: *mut std::os::raw::c_char,
317        ) -> std::os::raw::c_int {
318            let device = $crate::device::CryptDevice::from_ptr(cd);
319            let s = match $crate::from_str_ptr!(json) {
320                Ok(s) => s,
321                Err(_) => return -1,
322            };
323            let json_obj = match serde_json::from_str(s) {
324                Ok(j) => j,
325                Err(_) => return -1,
326            };
327
328            let rc: Result<(), $crate::LibcryptErr> = $safe_fn_name(device, json_obj);
329            match rc {
330                Ok(()) => 0,
331                Err(_) => -1,
332            }
333        }
334    };
335}
336
337#[macro_export]
338/// Create a C-compatible callback for compatible with `CryptTokenHandler`
339macro_rules! c_token_handler_dump {
340    ( $fn_name:ident, $safe_fn_name:ident ) => {
341        extern "C" fn $fn_name(
342            cd: *mut libcryptsetup_rs_sys::crypt_device,
343            json: *mut std::os::raw::c_char,
344        ) {
345            let device = $crate::device::CryptDevice::from_ptr(cd);
346            let s = match $crate::from_str_ptr!(json) {
347                Ok(s) => s,
348                Err(_) => return,
349            };
350            let json_obj = match serde_json::from_str(s) {
351                Ok(j) => j,
352                Err(_) => return,
353            };
354
355            $safe_fn_name(device, json_obj)
356        }
357    };
358}
359
360#[cfg(test)]
361mod test {
362    use crate::consts::vals::CryptLogLevel;
363
364    fn safe_confirm_callback(_msg: &str, usrdata: Option<&mut u32>) -> bool {
365        *usrdata.unwrap() != 0
366    }
367
368    c_confirm_callback!(confirm_callback, u32, safe_confirm_callback);
369
370    fn safe_logging_callback(_level: CryptLogLevel, _msg: &str, _usrdata: Option<&mut u32>) {}
371
372    c_logging_callback!(logging_callback, u32, safe_logging_callback);
373
374    fn safe_progress_callback(_size: u64, _offset: u64, usrdata: Option<&mut u32>) -> bool {
375        *usrdata.unwrap() != 0
376    }
377
378    c_progress_callback!(progress_callback, u32, safe_progress_callback);
379
380    #[test]
381    fn test_c_confirm_callback() {
382        let ret = confirm_callback(
383            "\0".as_ptr().cast::<std::os::raw::c_char>(),
384            (&mut 1u32 as *mut u32).cast::<std::os::raw::c_void>(),
385        );
386        assert_eq!(1, ret);
387
388        let ret = confirm_callback(
389            "\0".as_ptr().cast::<std::os::raw::c_char>(),
390            (&mut 0u32 as *mut u32).cast::<std::os::raw::c_void>(),
391        );
392        assert_eq!(0, ret);
393    }
394
395    #[test]
396    fn test_c_logging_callback() {
397        logging_callback(
398            libcryptsetup_rs_sys::CRYPT_LOG_ERROR as i32,
399            "\0".as_ptr().cast::<std::os::raw::c_char>(),
400            (&mut 1u32 as *mut u32).cast::<std::os::raw::c_void>(),
401        );
402
403        logging_callback(
404            libcryptsetup_rs_sys::CRYPT_LOG_DEBUG,
405            "\0".as_ptr().cast::<std::os::raw::c_char>(),
406            (&mut 0u32 as *mut u32).cast::<std::os::raw::c_void>(),
407        );
408    }
409
410    #[test]
411    fn test_c_progress_callback() {
412        let ret = progress_callback(0, 0, (&mut 1u32 as *mut u32).cast::<std::os::raw::c_void>());
413        assert_eq!(1, ret);
414
415        let ret = progress_callback(0, 0, (&mut 0u32 as *mut u32).cast::<std::os::raw::c_void>());
416        assert_eq!(0, ret);
417    }
418
419    consts_to_from_enum!(
420        /// An enum for testing `PartialEq`
421        PETestEnum,
422        u16,
423        This => 0,
424        Can => 1,
425        Use => 2,
426        PartialEq => 3
427    );
428
429    #[test]
430    fn test_enum_partial_eq() {
431        assert_eq!(PETestEnum::This, PETestEnum::try_from(0).unwrap());
432        assert_eq!(PETestEnum::Can, PETestEnum::try_from(1).unwrap());
433        assert_eq!(PETestEnum::Use, PETestEnum::try_from(2).unwrap());
434        assert_eq!(PETestEnum::PartialEq, PETestEnum::try_from(3).unwrap());
435    }
436
437    #[cfg(not(feature = "mutex"))]
438    #[test]
439    #[should_panic(expected = "Enable the mutex feature")]
440    fn test_multiple_threads_no_mutex_feature() {
441        std::thread::spawn(|| {
442            crate::get_sector_size(None);
443        })
444        .join()
445        .unwrap();
446        crate::get_sector_size(None);
447    }
448}