1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
use crate::error::Error;
use libyal_rs_common::ffi::AsTypeRef;
use libfsntfs_sys::FILE;
use log::trace;
use std::convert::TryFrom;
use std::ffi::{c_void, CStr};
use std::fmt::{self, Display, Formatter};
use std::mem;

#[repr(C)]
pub struct __LibfsntfsError(isize);

pub type LibfsntfsErrorRefMut = *mut __LibfsntfsError;
pub type LibfsntfsErrorRef = *const __LibfsntfsError;

#[repr(C)]
pub struct LibfsntfsError(LibfsntfsErrorRefMut);

impl AsTypeRef for LibfsntfsError {
    type Ref = LibfsntfsErrorRef;
    type RefMut = LibfsntfsErrorRefMut;

    #[inline]
    fn as_type_ref(&self) -> Self::Ref {
        self.0 as *const _
    }

    #[inline]
    fn as_type_ref_mut(&mut self) -> Self::RefMut {
        self.0
    }

    #[inline]
    fn as_raw(&mut self) -> *mut Self::RefMut {
        &mut self.0 as *mut _
    }
}

extern "C" {
    pub fn libfsntfs_error_free(error: *mut LibfsntfsErrorRefMut);
    pub fn libfsntfs_error_fprint(
        error: LibfsntfsErrorRef,
        stream: *mut FILE,
    ) -> ::std::os::raw::c_int;
    pub fn libfsntfs_error_sprint(
        error: LibfsntfsErrorRef,
        string: *mut ::std::os::raw::c_char,
        size: usize,
    ) -> ::std::os::raw::c_int;
    pub fn libfsntfs_error_backtrace_fprint(
        error: LibfsntfsErrorRef,
        stream: *mut FILE,
    ) -> ::std::os::raw::c_int;
    pub fn libfsntfs_error_backtrace_sprint(
        error: LibfsntfsErrorRef,
        string: *mut ::std::os::raw::c_char,
        size: usize,
    ) -> ::std::os::raw::c_int;
}

impl Drop for LibfsntfsError {
    fn drop(&mut self) {
        trace!("Calling `libfsntfs_error_free`");

        unsafe { libfsntfs_error_free(self.as_raw()) };
    }
}

impl TryFrom<*mut __LibfsntfsError> for Error {
    type Error = Error;

    fn try_from(err: *mut __LibfsntfsError) -> Result<Self, Self::Error> {
        if err.is_null() {
            return Err(Error::Other("Error pointer cannot be NULL".to_owned()));
        }

        let mut buffer = vec![0; 1024];

        let retcode =
            unsafe { libfsntfs_error_backtrace_sprint(err as *const _, buffer.as_mut_ptr(), buffer.len()) };

        if retcode == -1 {
            Err(Error::FFI("Failed to print error".to_owned()))
        } else {
            let repr = unsafe { CStr::from_ptr(buffer.as_ptr()) };
            Ok(Error::FFI(repr.to_string_lossy().to_string()))
        }
    }
}

#[cfg(test)]
mod tests {
    use crate::error::Error;
    use crate::fixtures::*;
    use crate::volume::{AccessMode, Volume};

    #[test]
    fn test_error() {
        let result = Volume::open("non-existent", AccessMode::Read);
        assert!(result.is_err());

        if let Err(e) = result {
            if let Error::FFI(s) = e {
                dbg!(&s);
                assert!(
                    s.find("libfsntfs_volume_open").is_some(),
                    "should contain FFI function name"
                );
                assert!(
                    s.find("unable to open volume").is_some(),
                    "should contain message string"
                );

                return;
            }
        }

        panic!("Test should not reach here!");
    }
}