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
//! Basic operations with tape archives (tar).
//!
//! ## Example
//!
//! ```rust
//! # extern crate tape;
//! # extern crate temporary;
//! let (from, into) = ("foo.tar", "bar");
//! # let from = "tests/fixtures/foo.tar";
//! # let directory = temporary::Directory::new("tape").unwrap();
//! # let into = directory.path();
//! tape::open(from).unwrap().extract(into).unwrap();
//! ```

#[macro_use]
extern crate lazy_static;
extern crate libc;

extern crate tar_sys as raw;

use std::io::{Error, ErrorKind, Result};
use std::path::Path;
use std::sync::Mutex;

lazy_static! {
    // libtar is not thread safe.
    static ref MUTEX: Mutex<isize> = Mutex::new(0);
}

macro_rules! done(
    ($result:expr) => (
        if $result != 0 {
            return Err(Error::last_os_error());
        }
    );
);

macro_rules! raise(
    ($message:expr) => (
        return Err(Error::new(ErrorKind::Other, $message))
    );
);

macro_rules! path_to_cstr(
    ($path:expr) => (match $path.to_str() {
        Some(path) => match CString::new(path) {
            Ok(path) => path,
            _ => raise!("the path is invalid"),
        },
        _ => raise!("the path is invalid"),
    });
);

/// An archive.
pub struct Archive {
    raw: *mut raw::TAR,
}

impl Archive {
    /// Open an archive.
    pub fn open<T: AsRef<Path>>(path: T) -> Result<Archive> {
        use libc::O_RDONLY;
        use std::ffi::CString;

        let mut tar = 0 as *mut raw::TAR;
        unsafe {
            let _guard = MUTEX.lock().unwrap();
            let path = path_to_cstr!(path.as_ref());
            done!(raw::tar_open(&mut tar, path.as_ptr(), 0 as *mut _, O_RDONLY, 0, 0));
        }
        Ok(Archive { raw: tar })
    }

    /// Extract all files from the archive into a directory.
    pub fn extract<T: AsRef<Path>>(&self, path: T) -> Result<()> {
        use std::ffi::CString;

        unsafe {
            let _guard = MUTEX.lock().unwrap();
            let path = path_to_cstr!(path.as_ref());
            done!(raw::tar_extract_all(self.raw, path.as_ptr()));
        }
        Ok(())
    }
}

/// Open an archive.
#[inline]
pub fn open<T: AsRef<Path>>(path: T) -> Result<Archive> {
    Archive::open(path)
}

impl Drop for Archive {
    #[inline]
    fn drop(&mut self) {
        unsafe {
            let _guard = MUTEX.lock().unwrap();
            raw::tar_close(self.raw);
        }
    }
}