#![allow(dead_code)]
use std::{ffi, fs, io, mem};
use std::io::{Read, Write};
use std::os::unix::io::{IntoRawFd, RawFd};
use std::marker::PhantomData;
use libc;
use crate::tape::TapeDevice;
use crate::fs::ArchivalSink;
use crate::spanning::RecoverableWrite;
const MTRESET: libc::c_short = 0;
const MTFSF: libc::c_short = 1;
const MTBSF: libc::c_short = 2;
const MTFSR: libc::c_short = 3;
const MTBSR: libc::c_short = 4;
const MTWEOF: libc::c_short = 5;
const MTREW: libc::c_short = 6;
const MTOFFL: libc::c_short = 7;
const MTNOP: libc::c_short = 8;
const MTRETEN: libc::c_short = 9;
const MTBSFM: libc::c_short = 10;
const MTFSFM: libc::c_short = 11;
const MTEOM: libc::c_short = 12;
const MTERASE: libc::c_short = 13;
const MTRAS1: libc::c_short = 14;
const MTRAS2: libc::c_short = 15;
const MTRAS3: libc::c_short = 16;
const MTSETBLK: libc::c_short = 20;
const MTSETDENSITY: libc::c_short = 21;
const MTSEEK: libc::c_short = 22;
const MTTELL: libc::c_short = 23;
const MTSETDRVBUFFER: libc::c_short = 24;
const MTFSS: libc::c_short = 25;
const MTBSS: libc::c_short = 26;
const MTWSM: libc::c_short = 27;
const MTLOCK: libc::c_short = 28;
const MTUNLOCK: libc::c_short = 29;
const MTLOAD: libc::c_short = 30;
const MTUNLOAD: libc::c_short = 31;
const MTCOMPRESSION: libc::c_short = 32;
const MTSETPART: libc::c_short = 33;
const MTMKPART: libc::c_short = 34;
#[repr(C)]
pub struct mtop {
mt_op: libc::c_short,
mt_count: libc::c_int
}
ioctl!(write_ptr mt_ioctop with 'm', 1; mtop);
fn conv_nix_error<T>(res: nix::Result<T>) -> io::Result<T> {
match res {
Err(nix::Error::Sys(errno)) => Err(io::Error::from_raw_os_error(errno as i32)),
Err(nix::Error::InvalidPath) => Err(io::Error::new(io::ErrorKind::Other, "Invalid Path")),
Err(nix::Error::InvalidUtf8) => Err(io::Error::new(io::ErrorKind::Other, "Invalid UTF8")),
Err(nix::Error::UnsupportedOperation) => Err(io::Error::new(io::ErrorKind::Other, "Unsupported Operation")),
Ok(e) => Ok(e)
}
}
pub struct UnixTapeDevice<P = u64> {
tape_device: RawFd,
naninani: PhantomData<P>,
block_spill_buffer: Vec<u8>,
block_spill_read_pos: usize,
}
impl<P> UnixTapeDevice<P> {
pub fn open_device(unix_device_path: &ffi::OsStr) -> io::Result<Self> {
unsafe { Ok(Self::from_file_descriptor(fs::OpenOptions::new().read(true).write(true).open(unix_device_path)?.into_raw_fd())) }
}
pub unsafe fn from_file_descriptor(unix_fd: RawFd) -> Self {
UnixTapeDevice {
tape_device: unix_fd,
naninani: PhantomData,
block_spill_buffer: Vec::with_capacity(1024),
block_spill_read_pos: 0,
}
}
fn read_next_block(&mut self) -> io::Result<()> {
loop {
let size = unsafe{ libc::read(self.tape_device, self.block_spill_buffer.as_mut_ptr() as *mut libc::c_void, self.block_spill_buffer.capacity()) };
if size >= 0 {
assert!(size as usize <= self.block_spill_buffer.capacity());
unsafe { self.block_spill_buffer.set_len(size as usize) };
return Ok(())
} else {
let err = io::Error::last_os_error();
match err.raw_os_error() {
Some(libc::ENOMEM) => {
self.block_spill_buffer.reserve(self.block_spill_buffer.capacity() * 2);
let op = mtop {
mt_op: MTBSR,
mt_count: 1
};
conv_nix_error(unsafe { mt_ioctop(self.tape_device, &op) })?;
},
_ => return Err(err)
};
}
}
}
}
impl<P> Drop for UnixTapeDevice<P> {
fn drop(&mut self) {
unsafe { libc::close(self.tape_device) };
}
}
impl<P> Write for UnixTapeDevice<P> {
fn write(&mut self, data: &[u8]) -> io::Result<usize> {
let size = unsafe{ libc::write(self.tape_device, data.as_ptr() as *const libc::c_void, data.len()) };
if size >= 0 {
Ok(size as usize)
} else {
Err(io::Error::last_os_error())
}
}
fn flush(&mut self) -> io::Result<()> {
Ok(())
}
}
impl<P> Read for UnixTapeDevice<P> {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
let mut wrote = 0;
while wrote < buf.len() {
let remain = buf.len() - wrote;
if self.block_spill_read_pos == 0 {
self.read_next_block()?;
if self.block_spill_buffer.len() <= remain {
buf[wrote..wrote + self.block_spill_buffer.len()].copy_from_slice(&self.block_spill_buffer);
wrote += self.block_spill_buffer.len();
} else {
buf[wrote..wrote + remain].copy_from_slice(&self.block_spill_buffer[..remain]);
self.block_spill_read_pos = remain;
wrote += remain;
}
} else {
let spill_remain = self.block_spill_buffer.len() - self.block_spill_read_pos;
if spill_remain <= remain {
buf[wrote..wrote + spill_remain].copy_from_slice(&self.block_spill_buffer[self.block_spill_read_pos..]);
self.block_spill_read_pos = 0;
wrote += spill_remain;
} else {
buf[wrote..wrote + remain].copy_from_slice(&self.block_spill_buffer[self.block_spill_read_pos..self.block_spill_read_pos + remain]);
self.block_spill_read_pos += remain;
wrote += remain;
}
}
}
assert!(wrote <= buf.len());
Ok(wrote)
}
}
impl<P> RecoverableWrite<P> for UnixTapeDevice<P> where P: Clone {
}
impl<P> ArchivalSink<P> for UnixTapeDevice<P> where P: Send + Clone {
fn downcast_tapedevice(&mut self) -> Option<&mut dyn TapeDevice> {
Some(self)
}
}
impl<P> TapeDevice for UnixTapeDevice<P> {
fn read_block(&mut self, buf: &mut Vec<u8>) -> io::Result<()> {
if self.block_spill_read_pos == 0 {
self.read_next_block()?;
}
let last_cap = self.block_spill_buffer.capacity();
mem::swap(buf, &mut self.block_spill_buffer);
self.block_spill_buffer = Vec::with_capacity(last_cap);
self.block_spill_read_pos = 0;
Ok(())
}
fn write_filemark(&mut self, _blocking: bool) -> io::Result<()> {
let op = mtop {
mt_op: MTWEOF,
mt_count: 1
};
conv_nix_error(unsafe { mt_ioctop(self.tape_device, &op) })?;
Ok(())
}
fn seek_blocks(&mut self, pos: io::SeekFrom) -> io::Result<()> {
match pos {
io::SeekFrom::Start(pos) => {
let op = mtop {
mt_op: MTSEEK,
mt_count: pos as i32
};
conv_nix_error(unsafe { mt_ioctop(self.tape_device, &op) })?;
},
io::SeekFrom::Current(pos) => {
let op = mtop {
mt_op: if pos > 0 {
MTFSR
} else {
MTBSR
},
mt_count: pos as i32
};
conv_nix_error(unsafe { mt_ioctop(self.tape_device, &op) })?;
},
io::SeekFrom::End(pos) => {
let mut op = mtop {
mt_op: MTEOM,
mt_count: pos as i32
};
conv_nix_error(unsafe { mt_ioctop(self.tape_device, &op) })?;
op.mt_op = MTBSR;
op.mt_count = pos as i32;
conv_nix_error(unsafe { mt_ioctop(self.tape_device, &op) })?;
}
}
Ok(())
}
fn tell_blocks(&mut self) -> io::Result<u64> {
Ok(0)
}
fn seek_filemarks(&mut self, pos: io::SeekFrom) -> io::Result<()> {
match pos {
io::SeekFrom::Start(pos) => {
let mut op = mtop {
mt_op: MTREW,
mt_count: 1
};
conv_nix_error(unsafe { mt_ioctop(self.tape_device, &op) })?;
op.mt_op = MTFSF;
op.mt_count = pos as i32;
conv_nix_error(unsafe { mt_ioctop(self.tape_device, &op) })?;
},
io::SeekFrom::Current(pos) => {
let op = mtop {
mt_op: if pos > 0 {
MTFSF
} else {
MTBSF
},
mt_count: pos as i32
};
conv_nix_error(unsafe { mt_ioctop(self.tape_device, &op) })?;
},
io::SeekFrom::End(pos) => { let mut op = mtop {
mt_op: MTEOM,
mt_count: pos as i32
};
conv_nix_error(unsafe { mt_ioctop(self.tape_device, &op) })?;
op.mt_op = MTBSF;
op.mt_count = pos as i32;
conv_nix_error(unsafe { mt_ioctop(self.tape_device, &op) })?;
}
}
Ok(())
}
fn seek_setmarks(&mut self, pos: io::SeekFrom) -> io::Result<()> {
match pos {
io::SeekFrom::Start(pos) => {
let mut op = mtop {
mt_op: MTREW,
mt_count: 1
};
conv_nix_error(unsafe { mt_ioctop(self.tape_device, &op) })?;
op.mt_op = MTFSS;
op.mt_count = pos as i32;
conv_nix_error(unsafe { mt_ioctop(self.tape_device, &op) })?;
},
io::SeekFrom::Current(pos) => {
let op = mtop {
mt_op: if pos > 0 {
MTFSS
} else {
MTBSS
},
mt_count: pos as i32
};
conv_nix_error(unsafe { mt_ioctop(self.tape_device, &op) })?;
},
io::SeekFrom::End(pos) => { let mut op = mtop {
mt_op: MTEOM,
mt_count: pos as i32
};
conv_nix_error(unsafe { mt_ioctop(self.tape_device, &op) })?;
op.mt_op = MTBSS;
op.mt_count = pos as i32;
conv_nix_error(unsafe { mt_ioctop(self.tape_device, &op) })?;
}
}
Ok(())
}
fn seek_partition(&mut self, id: u32) -> io::Result<()> {
let op = mtop {
mt_op: MTSETPART,
mt_count: id as i32
};
conv_nix_error(unsafe { mt_ioctop(self.tape_device, &op) })?;
Ok(())
}
}