use std::{io, fs, path, ffi, ptr, mem};
use std::os::unix::prelude::*;
use libc::{getpwuid_r, getgrgid_r, passwd, group, ERANGE};
use crate::{tar, tape, spanning};
use crate::tape::unix::UnixTapeDevice;
use crate::blocking::BlockingWriter;
use crate::concurrentbuf::ConcurrentWriteBuffer;
use crate::tuning::Configuration;
pub use crate::fs::portable::ArchivalSink;
pub fn open_sink<P: AsRef<path::Path>, I>(outfile: P, tuning: &Configuration, limit: Option<u64>) -> io::Result<Box<ArchivalSink<I>>> where ffi::OsString: From<P>, P: Clone, I: 'static + Send + Clone + PartialEq {
if let Ok(metadata) = fs::metadata(outfile.clone()) {
if metadata.file_type().is_char_device() {
return match UnixTapeDevice::open_device(&ffi::OsString::from(outfile)) {
Ok(tape) => match limit {
Some(limit) => Ok(Box::new(spanning::LimitingWriter::wrap(BlockingWriter::new_with_factor(ConcurrentWriteBuffer::new(tape, tuning.serial_buffer_limit), tuning.blocking_factor), limit))),
None => Ok(Box::new(BlockingWriter::new_with_factor(ConcurrentWriteBuffer::new(tape, tuning.serial_buffer_limit), tuning.blocking_factor)))
},
Err(e) => Err(e)
}
}
}
let file = fs::File::create(outfile.as_ref())?;
match limit {
Some(limit) => Ok(Box::new(spanning::LimitingWriter::wrap(ConcurrentWriteBuffer::new(file, tuning.serial_buffer_limit), limit))),
None => Ok(Box::new(ConcurrentWriteBuffer::new(file, tuning.serial_buffer_limit)))
}
}
pub fn open_tape<P: AsRef<path::Path>>(tapedev: P) -> io::Result<Box<tape::TapeDevice>> where ffi::OsString: From<P>, P: Clone {
match UnixTapeDevice::<u64>::open_device(&ffi::OsString::from(tapedev.clone())) {
Ok(tape) => {
return Ok(Box::new(tape));
}
Err(e) => Err(e)
}
}
pub fn get_unix_mode(metadata: &fs::Metadata) -> io::Result<u32> {
Ok(metadata.permissions().mode())
}
pub fn get_file_type(metadata: &fs::Metadata) -> io::Result<tar::header::TarFileType> {
if metadata.file_type().is_block_device() {
Ok(tar::header::TarFileType::BlockDevice)
} else if metadata.file_type().is_char_device() {
Ok(tar::header::TarFileType::CharacterDevice)
} else if metadata.file_type().is_fifo() {
Ok(tar::header::TarFileType::FIFOPipe)
} else if metadata.file_type().is_socket() {
Err(io::Error::new(io::ErrorKind::InvalidData, "Sockets are not archivable"))
} else if metadata.file_type().is_dir() {
Ok(tar::header::TarFileType::Directory)
} else if metadata.file_type().is_file() {
Ok(tar::header::TarFileType::FileStream)
} else if metadata.file_type().is_symlink() {
Ok(tar::header::TarFileType::SymbolicLink)
} else {
Err(io::Error::new(io::ErrorKind::InvalidInput, "Metadata did not yield any valid file type for tarball"))
}
}
pub fn get_unix_owner(metadata: &fs::Metadata, _path: &path::Path) -> io::Result<(u32, String)> {
let mut username;
let mut passwd = unsafe { mem::zeroed() }; let mut buf = Vec::with_capacity(1024);
loop {
let mut out_passwd = &mut passwd as *mut passwd;
let res = unsafe { getpwuid_r(metadata.uid(), &mut passwd, buf.as_mut_ptr(), buf.capacity(), &mut out_passwd) };
if (out_passwd as *mut passwd) == ptr::null_mut() {
match res {
ERANGE => buf.reserve(buf.capacity() * 2),
_ => return Err(io::Error::from_raw_os_error(res))
}
continue;
}
username = unsafe {ffi::CStr::from_ptr(passwd.pw_name).to_string_lossy().into_owned()};
break;
}
Ok((metadata.uid(), username))
}
pub fn get_unix_group(metadata: &fs::Metadata, _path: &path::Path) -> io::Result<(u32, String)> {
let mut groupname;
let mut group = unsafe { mem::zeroed() }; let mut buf = Vec::with_capacity(1024);
loop {
let mut out_group = &mut group as *mut group;
let res = unsafe { getgrgid_r(metadata.gid(), &mut group, buf.as_mut_ptr(), buf.capacity(), &mut out_group) };
if (out_group as *mut group) == ptr::null_mut() {
match res {
ERANGE => buf.reserve(buf.capacity() * 2),
_ => return Err(io::Error::from_raw_os_error(res))
}
continue;
}
groupname = unsafe {ffi::CStr::from_ptr(group.gr_name).to_string_lossy().into_owned()};
break;
}
Ok((metadata.gid(), groupname))
}