use std::cell::{RefCell, Cell};
use std::cmp;
use std::fs;
use std::io::prelude::*;
use std::io::{self, SeekFrom};
use std::marker;
use std::mem;
use std::ops::{Deref, DerefMut};
use std::path::{Path, Component};
use entry::EntryFields;
use error::TarError;
use {Entry, Header};
use other;
macro_rules! try_iter {
($me:expr, $e:expr) => (match $e {
Ok(e) => e,
Err(e) => { $me.done = true; return Some(Err(e)) }
})
}
pub struct Archive<R: ?Sized> {
pos: Cell<u64>,
obj: RefCell<AlignHigher<R>>,
}
struct AlignHigher<R: ?Sized>(u64, R);
impl<R: ?Sized> Deref for AlignHigher<R> {
type Target = R;
fn deref(&self) -> &R { &self.1 }
}
impl<R: ?Sized> DerefMut for AlignHigher<R> {
fn deref_mut(&mut self) -> &mut R { &mut self.1 }
}
#[doc(hidden)]
pub type Files<'a, T> = Entries<'a, T>;
pub struct Entries<'a, R: 'a> {
fields: EntriesFields<'a>,
_ignored: marker::PhantomData<&'a Archive<R>>,
}
struct EntriesFields<'a> {
archive: &'a Archive<ReadAndSeek + 'a>,
archive_read: &'a Archive<Read + 'a>,
done: bool,
offset: u64,
}
#[doc(hidden)]
pub type FilesMut<'a, T> = EntriesMut<'a, T>;
pub struct EntriesMut<'a, R: 'a> {
fields: EntriesMutFields<'a>,
_ignored: marker::PhantomData<&'a Archive<R>>,
}
struct EntriesMutFields<'a> {
archive: &'a Archive<Read + 'a>,
next: u64,
done: bool,
}
impl<O> Archive<O> {
pub fn new(obj: O) -> Archive<O> {
Archive { obj: RefCell::new(AlignHigher(0, obj)), pos: Cell::new(0) }
}
pub fn into_inner(self) -> O {
self.obj.into_inner().1
}
}
impl<R: Seek + Read> Archive<R> {
#[doc(hidden)]
pub fn files(&self) -> io::Result<Entries<R>> {
self.entries()
}
pub fn entries(&self) -> io::Result<Entries<R>> {
let me: &Archive<ReadAndSeek> = self;
let me2: &Archive<Read> = self;
me._entries(me2).map(|fields| {
Entries { fields: fields, _ignored: marker::PhantomData }
})
}
}
trait ReadAndSeek: Read + Seek {}
impl<R: Read + Seek> ReadAndSeek for R {}
impl<'a> Archive<ReadAndSeek + 'a> {
fn _entries<'b>(&'b self, read: &'b Archive<Read + 'a>)
-> io::Result<EntriesFields<'b>> {
try!(self._seek(0));
Ok(EntriesFields {
archive: self,
archive_read: read,
done: false,
offset: 0,
})
}
fn _seek(&self, pos: u64) -> io::Result<()> {
if self.pos.get() == pos {
return Ok(())
}
try!(self.obj.borrow_mut().seek(SeekFrom::Start(pos)));
self.pos.set(pos);
Ok(())
}
}
impl<R: Read> Archive<R> {
pub fn entries_mut(&mut self) -> io::Result<EntriesMut<R>> {
let me: &mut Archive<Read> = self;
me._entries_mut().map(|fields| {
EntriesMut { fields: fields, _ignored: marker::PhantomData }
})
}
#[doc(hidden)]
pub fn files_mut(&mut self) -> io::Result<EntriesMut<R>> {
self.entries_mut()
}
pub fn unpack<P: AsRef<Path>>(&mut self, dst: P) -> io::Result<()> {
let me: &mut Archive<Read> = self;
me._unpack(dst.as_ref())
}
}
impl<'a> Archive<Read + 'a> {
fn _entries_mut(&mut self) -> io::Result<EntriesMutFields> {
if self.pos.get() != 0 {
return Err(other("cannot call entries_mut unless archive is at \
position 0"))
}
Ok(EntriesMutFields {
archive: self,
done: false,
next: 0,
})
}
fn _unpack(&mut self, dst: &Path) -> io::Result<()> {
'outer: for entry in try!(self._entries_mut()) {
let file = try!(entry.map_err(|e| {
TarError::new("failed to iterate over archive", e)
}));
let mut file_dst = dst.to_path_buf();
{
let path = try!(file.header.path().map_err(|e| {
TarError::new("invalid path in entry header", e)
}));
for part in path.components() {
match part {
Component::Prefix(..) |
Component::RootDir |
Component::CurDir => continue,
Component::ParentDir => continue 'outer,
Component::Normal(part) => file_dst.push(part),
}
}
}
if *dst == *file_dst {
continue
}
if let Some(parent) = file_dst.parent() {
try!(fs::create_dir_all(&parent).map_err(|e| {
TarError::new(&format!("failed to create `{}`",
parent.display()), e)
}));
}
try!(file.into_entry::<fs::File>().unpack(&file_dst).map_err(|e| {
TarError::new(&format!("failed to unpacked `{}`",
file_dst.display()), e)
}));
}
Ok(())
}
fn _skip(&self, mut amt: u64) -> io::Result<()> {
let mut buf = [0u8; 4096 * 8];
let mut me = self;
while amt > 0 {
let n = cmp::min(amt, buf.len() as u64);
let n = try!(Read::read(&mut me, &mut buf[..n as usize]));
if n == 0 {
return Err(other("unexpected EOF during skip"))
}
amt -= n as u64;
}
Ok(())
}
fn _next_entry(&self,
offset: &mut u64,
seek: Box<Fn(&EntryFields) -> io::Result<()> + 'a>)
-> io::Result<Option<EntryFields>> {
let mut chunk = [0; 512];
let mut me = self;
try!(read_all(&mut me, &mut chunk));
*offset += 512;
if chunk.iter().all(|i| *i == 0) {
try!(read_all(&mut me, &mut chunk));
*offset += 512;
return if chunk.iter().all(|i| *i == 0) {
Ok(None)
} else {
Err(other("found block of 0s not followed by a second \
block of 0s"))
}
}
let sum = chunk[..148].iter().map(|i| *i as u32).fold(0, |a, b| a + b) +
chunk[156..].iter().map(|i| *i as u32).fold(0, |a, b| a + b) +
32 * 8;
let header: Header = unsafe { mem::transmute(chunk) };
let ret = EntryFields {
archive: self,
pos: 0,
size: try!(header.size()),
header: header,
tar_offset: *offset,
seek: seek,
};
let cksum = try!(ret.header.cksum());
if sum != cksum {
return Err(other("archive header checksum mismatch"))
}
let size = (ret.size + 511) & !(512 - 1);
*offset += size;
return Ok(Some(ret));
}
}
impl<W: Write> Archive<W> {
pub fn append(&self, header: &Header, data: &mut Read) -> io::Result<()> {
let me: &Archive<Write> = self;
me._append(header, data)
}
pub fn append_path<P: AsRef<Path>>(&self, path: P) -> io::Result<()> {
let me: &Archive<Write> = self;
me._append_path(path.as_ref())
}
pub fn append_file<P: AsRef<Path>>(&self, path: P, file: &mut fs::File)
-> io::Result<()> {
let me: &Archive<Write> = self;
me._append_file(path.as_ref(), file)
}
pub fn append_dir<P, Q>(&self, path: P, src_path: Q) -> io::Result<()>
where P: AsRef<Path>, Q: AsRef<Path>
{
let me: &Archive<Write> = self;
me._append_dir(path.as_ref(), src_path.as_ref())
}
pub fn finish(&self) -> io::Result<()> {
let me: &Archive<Write> = self;
me._finish()
}
}
impl<'a> Archive<Write + 'a> {
fn _append(&self, header: &Header, mut data: &mut Read) -> io::Result<()> {
let mut obj = self.obj.borrow_mut();
try!(obj.write_all(header.as_bytes()));
let len = try!(io::copy(&mut data, &mut &mut **obj));
let buf = [0; 512];
let remaining = 512 - (len % 512);
if remaining < 512 {
try!(obj.write_all(&buf[..remaining as usize]));
}
Ok(())
}
fn _append_path(&self, path: &Path) -> io::Result<()> {
let stat = try!(fs::metadata(path));
if stat.is_file() {
self.append_fs(path, &stat, &mut try!(fs::File::open(path)))
} else if stat.is_dir() {
self.append_fs(path, &stat, &mut io::empty())
} else {
Err(other("path has unknown file type"))
}
}
fn _append_file(&self, path: &Path, file: &mut fs::File) -> io::Result<()> {
let stat = try!(file.metadata());
self.append_fs(path, &stat, file)
}
fn _append_dir(&self, path: &Path, src_path: &Path) -> io::Result<()> {
let stat = try!(fs::metadata(src_path));
self.append_fs(path, &stat, &mut io::empty())
}
fn append_fs(&self,
path: &Path,
meta: &fs::Metadata,
read: &mut Read) -> io::Result<()> {
let mut header = Header::new();
try!(header.set_path(path));
header.set_metadata(meta);
header.set_cksum();
self._append(&header, read)
}
fn _finish(&self) -> io::Result<()> {
let b = [0; 1024];
self.obj.borrow_mut().write_all(&b)
}
}
impl<'a, R: Read + ?Sized> Read for &'a Archive<R> {
fn read(&mut self, into: &mut [u8]) -> io::Result<usize> {
self.obj.borrow_mut().read(into).map(|i| {
self.pos.set(self.pos.get() + i as u64);
i
})
}
}
impl<'a, R: Seek + Read> Iterator for Entries<'a, R> {
type Item = io::Result<Entry<'a, R>>;
fn next(&mut self) -> Option<io::Result<Entry<'a, R>>> {
self.fields.next().map(|result| {
result.map(|fields| fields.into_entry())
})
}
}
impl<'a> Iterator for EntriesFields<'a> {
type Item = io::Result<EntryFields<'a>>;
fn next(&mut self) -> Option<io::Result<EntryFields<'a>>> {
if self.done { return None }
try_iter!(self, self.archive._seek(self.offset));
let archive = self.archive;
let seek = Box::new(move |entry: &EntryFields| {
archive._seek(entry.tar_offset + entry.pos)
});
let archive = self.archive_read;
match try_iter!(self, archive._next_entry(&mut self.offset, seek)) {
Some(f) => Some(Ok(f)),
None => { self.done = true; None }
}
}
}
impl<'a, R: Read> Iterator for EntriesMut<'a, R> {
type Item = io::Result<Entry<'a, R>>;
fn next(&mut self) -> Option<io::Result<Entry<'a, R>>> {
self.fields.next().map(|result| {
result.map(|fields| fields.into_entry())
})
}
}
impl<'a> Iterator for EntriesMutFields<'a> {
type Item = io::Result<EntryFields<'a>>;
fn next(&mut self) -> Option<io::Result<EntryFields<'a>>> {
if self.done { return None }
let delta = self.next - self.archive.pos.get();
try_iter!(self, self.archive._skip(delta));
let seek = Box::new(|_: &EntryFields| Ok(()));
match try_iter!(self, self.archive._next_entry(&mut self.next, seek)) {
Some(f) => Some(Ok(f)),
None => { self.done = true; None }
}
}
}
fn read_all<R: Read>(r: &mut R, buf: &mut [u8]) -> io::Result<()> {
let mut read = 0;
while read < buf.len() {
match try!(r.read(&mut buf[read..])) {
0 => return Err(other("failed to read entire block")),
n => read += n,
}
}
Ok(())
}