tape: improve EOT error handling
This commit is contained in:
parent
164ad7b706
commit
318b310638
@ -10,7 +10,6 @@ use proxmox::{
|
|||||||
identity,
|
identity,
|
||||||
list_subdirs_api_method,
|
list_subdirs_api_method,
|
||||||
tools::Uuid,
|
tools::Uuid,
|
||||||
sys::error::SysError,
|
|
||||||
api::{
|
api::{
|
||||||
api,
|
api,
|
||||||
section_config::SectionConfigData,
|
section_config::SectionConfigData,
|
||||||
@ -60,6 +59,7 @@ use crate::{
|
|||||||
Inventory,
|
Inventory,
|
||||||
MediaCatalog,
|
MediaCatalog,
|
||||||
MediaId,
|
MediaId,
|
||||||
|
BlockReadError,
|
||||||
lock_media_set,
|
lock_media_set,
|
||||||
lock_media_pool,
|
lock_media_pool,
|
||||||
lock_unassigned_media_pool,
|
lock_unassigned_media_pool,
|
||||||
@ -528,15 +528,11 @@ pub fn label_media(
|
|||||||
drive.rewind()?;
|
drive.rewind()?;
|
||||||
|
|
||||||
match drive.read_next_file() {
|
match drive.read_next_file() {
|
||||||
Ok(Some(_file)) => bail!("media is not empty (format it first)"),
|
Ok(_reader) => bail!("media is not empty (format it first)"),
|
||||||
Ok(None) => { /* EOF mark at BOT, assume tape is empty */ },
|
Err(BlockReadError::EndOfFile) => { /* EOF mark at BOT, assume tape is empty */ },
|
||||||
|
Err(BlockReadError::EndOfStream) => { /* tape is empty */ },
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
println!("TEST {:?}", err);
|
bail!("media read error - {}", err);
|
||||||
if err.is_errno(nix::errno::Errno::ENOSPC) || err.is_errno(nix::errno::Errno::EIO) {
|
|
||||||
/* assume tape is empty */
|
|
||||||
} else {
|
|
||||||
bail!("media read error - {}", err);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1091,18 +1087,15 @@ fn barcode_label_media_worker(
|
|||||||
drive.rewind()?;
|
drive.rewind()?;
|
||||||
|
|
||||||
match drive.read_next_file() {
|
match drive.read_next_file() {
|
||||||
Ok(Some(_file)) => {
|
Ok(_reader) => {
|
||||||
worker.log(format!("media '{}' is not empty (format it first)", label_text));
|
worker.log(format!("media '{}' is not empty (format it first)", label_text));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
Ok(None) => { /* EOF mark at BOT, assume tape is empty */ },
|
Err(BlockReadError::EndOfFile) => { /* EOF mark at BOT, assume tape is empty */ },
|
||||||
Err(err) => {
|
Err(BlockReadError::EndOfStream) => { /* tape is empty */ },
|
||||||
if err.is_errno(nix::errno::Errno::ENOSPC) || err.is_errno(nix::errno::Errno::EIO) {
|
Err(_err) => {
|
||||||
/* assume tape is empty */
|
worker.warn(format!("media '{}' read error (maybe not empty - format it first)", label_text));
|
||||||
} else {
|
continue;
|
||||||
worker.warn(format!("media '{}' read error (maybe not empty - format it first)", label_text));
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -70,6 +70,7 @@ use crate::{
|
|||||||
tape::{
|
tape::{
|
||||||
TAPE_STATUS_DIR,
|
TAPE_STATUS_DIR,
|
||||||
TapeRead,
|
TapeRead,
|
||||||
|
BlockReadError,
|
||||||
MediaId,
|
MediaId,
|
||||||
MediaSet,
|
MediaSet,
|
||||||
MediaCatalog,
|
MediaCatalog,
|
||||||
@ -418,12 +419,19 @@ pub fn restore_media(
|
|||||||
|
|
||||||
loop {
|
loop {
|
||||||
let current_file_number = drive.current_file_number()?;
|
let current_file_number = drive.current_file_number()?;
|
||||||
let reader = match drive.read_next_file()? {
|
let reader = match drive.read_next_file() {
|
||||||
None => {
|
Err(BlockReadError::EndOfFile) => {
|
||||||
|
task_log!(worker, "skip unexpected filemark at pos {}", current_file_number);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
Err(BlockReadError::EndOfStream) => {
|
||||||
task_log!(worker, "detected EOT after {} files", current_file_number);
|
task_log!(worker, "detected EOT after {} files", current_file_number);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
Some(reader) => reader,
|
Err(BlockReadError::Error(err)) => {
|
||||||
|
return Err(err.into());
|
||||||
|
}
|
||||||
|
Ok(reader) => reader,
|
||||||
};
|
};
|
||||||
|
|
||||||
restore_archive(worker, reader, current_file_number, target, &mut catalog, verbose)?;
|
restore_archive(worker, reader, current_file_number, target, &mut catalog, verbose)?;
|
||||||
@ -811,12 +819,19 @@ pub fn fast_catalog_restore(
|
|||||||
let current_file_number = drive.current_file_number()?;
|
let current_file_number = drive.current_file_number()?;
|
||||||
|
|
||||||
{ // limit reader scope
|
{ // limit reader scope
|
||||||
let mut reader = match drive.read_next_file()? {
|
let mut reader = match drive.read_next_file() {
|
||||||
None => {
|
Err(BlockReadError::EndOfFile) => {
|
||||||
|
task_log!(worker, "skip unexpected filemark at pos {}", current_file_number);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
Err(BlockReadError::EndOfStream) => {
|
||||||
task_log!(worker, "detected EOT after {} files", current_file_number);
|
task_log!(worker, "detected EOT after {} files", current_file_number);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
Some(reader) => reader,
|
Err(BlockReadError::Error(err)) => {
|
||||||
|
return Err(err.into());
|
||||||
|
}
|
||||||
|
Ok(reader) => reader,
|
||||||
};
|
};
|
||||||
|
|
||||||
let header: MediaContentHeader = unsafe { reader.read_le_value()? };
|
let header: MediaContentHeader = unsafe { reader.read_le_value()? };
|
||||||
|
@ -43,6 +43,7 @@ use proxmox_backup::{
|
|||||||
media_pool::complete_pool_name,
|
media_pool::complete_pool_name,
|
||||||
},
|
},
|
||||||
tape::{
|
tape::{
|
||||||
|
BlockReadError,
|
||||||
drive::{
|
drive::{
|
||||||
open_drive,
|
open_drive,
|
||||||
lock_tape_device,
|
lock_tape_device,
|
||||||
@ -587,12 +588,19 @@ fn debug_scan(mut param: Value) -> Result<(), Error> {
|
|||||||
loop {
|
loop {
|
||||||
let file_number = drive.current_file_number()?;
|
let file_number = drive.current_file_number()?;
|
||||||
|
|
||||||
match drive.read_next_file()? {
|
match drive.read_next_file() {
|
||||||
None => {
|
Err(BlockReadError::EndOfFile) => {
|
||||||
println!("EOD");
|
println!("filemark number {}", file_number);
|
||||||
continue;
|
continue;
|
||||||
},
|
}
|
||||||
Some(mut reader) => {
|
Err(BlockReadError::EndOfStream) => {
|
||||||
|
println!("got EOT");
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
Err(BlockReadError::Error(err)) => {
|
||||||
|
return Err(err.into());
|
||||||
|
}
|
||||||
|
Ok(mut reader) => {
|
||||||
println!("got file number {}", file_number);
|
println!("got file number {}", file_number);
|
||||||
|
|
||||||
let header: Result<MediaContentHeader, _> = unsafe { reader.read_le_value() };
|
let header: Result<MediaContentHeader, _> = unsafe { reader.read_le_value() };
|
||||||
|
@ -43,6 +43,7 @@ use crate::{
|
|||||||
tape::{
|
tape::{
|
||||||
TapeRead,
|
TapeRead,
|
||||||
TapeWrite,
|
TapeWrite,
|
||||||
|
BlockReadError,
|
||||||
drive::{
|
drive::{
|
||||||
TapeDriver,
|
TapeDriver,
|
||||||
},
|
},
|
||||||
@ -320,16 +321,9 @@ impl TapeDriver for LtoTapeHandle {
|
|||||||
self.sg_tape.format_media(fast)
|
self.sg_tape.format_media(fast)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn read_next_file<'a>(&'a mut self) -> Result<Option<Box<dyn TapeRead + 'a>>, std::io::Error> {
|
fn read_next_file<'a>(&'a mut self) -> Result<Box<dyn TapeRead + 'a>, BlockReadError> {
|
||||||
let reader = self.sg_tape.open_reader()?;
|
let reader = self.sg_tape.open_reader()?;
|
||||||
let handle = match reader {
|
let handle: Box<dyn TapeRead> = Box::new(reader);
|
||||||
Some(reader) => {
|
|
||||||
let reader: Box<dyn TapeRead> = Box::new(reader);
|
|
||||||
Some(reader)
|
|
||||||
}
|
|
||||||
None => None,
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(handle)
|
Ok(handle)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -32,7 +32,7 @@ use crate::{
|
|||||||
},
|
},
|
||||||
tape::{
|
tape::{
|
||||||
BlockRead,
|
BlockRead,
|
||||||
BlockReadStatus,
|
BlockReadError,
|
||||||
BlockWrite,
|
BlockWrite,
|
||||||
file_formats::{
|
file_formats::{
|
||||||
BlockedWriter,
|
BlockedWriter,
|
||||||
@ -526,11 +526,13 @@ impl SgTape {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn read_block(&mut self, buffer: &mut [u8]) -> Result<BlockReadStatus, std::io::Error> {
|
fn read_block(&mut self, buffer: &mut [u8]) -> Result<usize, BlockReadError> {
|
||||||
let transfer_len = buffer.len();
|
let transfer_len = buffer.len();
|
||||||
|
|
||||||
if transfer_len > 0xFFFFFF {
|
if transfer_len > 0xFFFFFF {
|
||||||
proxmox::io_bail!("read failed - buffer too large");
|
return Err(BlockReadError::Error(
|
||||||
|
proxmox::io_format_err!("read failed - buffer too large")
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut sg_raw = SgRaw::new(&mut self.file, 0)
|
let mut sg_raw = SgRaw::new(&mut self.file, 0)
|
||||||
@ -549,21 +551,25 @@ impl SgTape {
|
|||||||
let data = match sg_raw.do_in_command(&cmd, buffer) {
|
let data = match sg_raw.do_in_command(&cmd, buffer) {
|
||||||
Ok(data) => data,
|
Ok(data) => data,
|
||||||
Err(ScsiError::Sense(SenseInfo { sense_key: 0, asc: 0, ascq: 1 })) => {
|
Err(ScsiError::Sense(SenseInfo { sense_key: 0, asc: 0, ascq: 1 })) => {
|
||||||
return Ok(BlockReadStatus::EndOfFile);
|
return Err(BlockReadError::EndOfFile);
|
||||||
}
|
}
|
||||||
Err(ScsiError::Sense(SenseInfo { sense_key: 8, asc: 0, ascq: 5 })) => {
|
Err(ScsiError::Sense(SenseInfo { sense_key: 8, asc: 0, ascq: 5 })) => {
|
||||||
return Ok(BlockReadStatus::EndOfStream);
|
return Err(BlockReadError::EndOfStream);
|
||||||
}
|
}
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
proxmox::io_bail!("read failed - {}", err);
|
return Err(BlockReadError::Error(
|
||||||
|
proxmox::io_format_err!("read failed - {}", err)
|
||||||
|
));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if data.len() != transfer_len {
|
if data.len() != transfer_len {
|
||||||
proxmox::io_bail!("read failed - unexpected block len ({} != {})", data.len(), buffer.len())
|
return Err(BlockReadError::Error(
|
||||||
|
proxmox::io_format_err!("read failed - unexpected block len ({} != {})", data.len(), buffer.len())
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(BlockReadStatus::Ok(transfer_len))
|
Ok(transfer_len)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn open_writer(&mut self) -> BlockedWriter<SgTapeWriter> {
|
pub fn open_writer(&mut self) -> BlockedWriter<SgTapeWriter> {
|
||||||
@ -571,12 +577,9 @@ impl SgTape {
|
|||||||
BlockedWriter::new(writer)
|
BlockedWriter::new(writer)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn open_reader(&mut self) -> Result<Option<BlockedReader<SgTapeReader>>, std::io::Error> {
|
pub fn open_reader(&mut self) -> Result<BlockedReader<SgTapeReader>, BlockReadError> {
|
||||||
let reader = SgTapeReader::new(self);
|
let reader = SgTapeReader::new(self);
|
||||||
match BlockedReader::open(reader)? {
|
BlockedReader::open(reader)
|
||||||
Some(reader) => Ok(Some(reader)),
|
|
||||||
None => Ok(None),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set important drive options
|
/// Set important drive options
|
||||||
@ -702,7 +705,7 @@ impl <'a> SgTapeReader<'a> {
|
|||||||
|
|
||||||
impl <'a> BlockRead for SgTapeReader<'a> {
|
impl <'a> BlockRead for SgTapeReader<'a> {
|
||||||
|
|
||||||
fn read_block(&mut self, buffer: &mut [u8]) -> Result<BlockReadStatus, std::io::Error> {
|
fn read_block(&mut self, buffer: &mut [u8]) -> Result<usize, BlockReadError> {
|
||||||
self.sg_tape.read_block(buffer)
|
self.sg_tape.read_block(buffer)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -44,6 +44,7 @@ use crate::{
|
|||||||
tape::{
|
tape::{
|
||||||
TapeWrite,
|
TapeWrite,
|
||||||
TapeRead,
|
TapeRead,
|
||||||
|
BlockReadError,
|
||||||
MediaId,
|
MediaId,
|
||||||
drive::lto::TapeAlertFlags,
|
drive::lto::TapeAlertFlags,
|
||||||
file_formats::{
|
file_formats::{
|
||||||
@ -86,7 +87,7 @@ pub trait TapeDriver {
|
|||||||
fn format_media(&mut self, fast: bool) -> Result<(), Error>;
|
fn format_media(&mut self, fast: bool) -> Result<(), Error>;
|
||||||
|
|
||||||
/// Read/Open the next file
|
/// Read/Open the next file
|
||||||
fn read_next_file<'a>(&'a mut self) -> Result<Option<Box<dyn TapeRead + 'a>>, std::io::Error>;
|
fn read_next_file<'a>(&'a mut self) -> Result<Box<dyn TapeRead + 'a>, BlockReadError>;
|
||||||
|
|
||||||
/// Write/Append a new file
|
/// Write/Append a new file
|
||||||
fn write_file<'a>(&'a mut self) -> Result<Box<dyn TapeWrite + 'a>, std::io::Error>;
|
fn write_file<'a>(&'a mut self) -> Result<Box<dyn TapeWrite + 'a>, std::io::Error>;
|
||||||
@ -132,9 +133,17 @@ pub trait TapeDriver {
|
|||||||
self.rewind()?;
|
self.rewind()?;
|
||||||
|
|
||||||
let label = {
|
let label = {
|
||||||
let mut reader = match self.read_next_file()? {
|
let mut reader = match self.read_next_file() {
|
||||||
None => return Ok((None, None)), // tape is empty
|
Err(BlockReadError::EndOfStream) => {
|
||||||
Some(reader) => reader,
|
return Ok((None, None)); // tape is empty
|
||||||
|
}
|
||||||
|
Err(BlockReadError::EndOfFile) => {
|
||||||
|
bail!("got unexpected filemark at BOT");
|
||||||
|
}
|
||||||
|
Err(BlockReadError::Error(err)) => {
|
||||||
|
return Err(err.into());
|
||||||
|
}
|
||||||
|
Ok(reader) => reader,
|
||||||
};
|
};
|
||||||
|
|
||||||
let header: MediaContentHeader = unsafe { reader.read_le_value()? };
|
let header: MediaContentHeader = unsafe { reader.read_le_value()? };
|
||||||
@ -155,9 +164,17 @@ pub trait TapeDriver {
|
|||||||
let mut media_id = MediaId { label, media_set_label: None };
|
let mut media_id = MediaId { label, media_set_label: None };
|
||||||
|
|
||||||
// try to read MediaSet label
|
// try to read MediaSet label
|
||||||
let mut reader = match self.read_next_file()? {
|
let mut reader = match self.read_next_file() {
|
||||||
None => return Ok((Some(media_id), None)),
|
Err(BlockReadError::EndOfStream) => {
|
||||||
Some(reader) => reader,
|
return Ok((Some(media_id), None));
|
||||||
|
}
|
||||||
|
Err(BlockReadError::EndOfFile) => {
|
||||||
|
bail!("got unexpected filemark after label");
|
||||||
|
}
|
||||||
|
Err(BlockReadError::Error(err)) => {
|
||||||
|
return Err(err.into());
|
||||||
|
}
|
||||||
|
Ok(reader) => reader,
|
||||||
};
|
};
|
||||||
|
|
||||||
let header: MediaContentHeader = unsafe { reader.read_le_value()? };
|
let header: MediaContentHeader = unsafe { reader.read_le_value()? };
|
||||||
|
@ -15,6 +15,7 @@ use crate::{
|
|||||||
tape::{
|
tape::{
|
||||||
TapeWrite,
|
TapeWrite,
|
||||||
TapeRead,
|
TapeRead,
|
||||||
|
BlockReadError,
|
||||||
changer::{
|
changer::{
|
||||||
MediaChange,
|
MediaChange,
|
||||||
MtxStatus,
|
MtxStatus,
|
||||||
@ -261,18 +262,18 @@ impl TapeDriver for VirtualTapeHandle {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
fn read_next_file(&mut self) -> Result<Option<Box<dyn TapeRead>>, io::Error> {
|
fn read_next_file(&mut self) -> Result<Box<dyn TapeRead>, BlockReadError> {
|
||||||
let mut status = self.load_status()
|
let mut status = self.load_status()
|
||||||
.map_err(|err| io::Error::new(io::ErrorKind::Other, err.to_string()))?;
|
.map_err(|err| BlockReadError::Error(io::Error::new(io::ErrorKind::Other, err.to_string())))?;
|
||||||
|
|
||||||
match status.current_tape {
|
match status.current_tape {
|
||||||
Some(VirtualTapeStatus { ref name, ref mut pos }) => {
|
Some(VirtualTapeStatus { ref name, ref mut pos }) => {
|
||||||
|
|
||||||
let index = self.load_tape_index(name)
|
let index = self.load_tape_index(name)
|
||||||
.map_err(|err| io::Error::new(io::ErrorKind::Other, err.to_string()))?;
|
.map_err(|err| BlockReadError::Error(io::Error::new(io::ErrorKind::Other, err.to_string())))?;
|
||||||
|
|
||||||
if *pos >= index.files {
|
if *pos >= index.files {
|
||||||
return Ok(None); // EOM
|
return Err(BlockReadError::EndOfStream);
|
||||||
}
|
}
|
||||||
|
|
||||||
let path = self.tape_file_path(name, *pos);
|
let path = self.tape_file_path(name, *pos);
|
||||||
@ -282,16 +283,15 @@ impl TapeDriver for VirtualTapeHandle {
|
|||||||
|
|
||||||
*pos += 1;
|
*pos += 1;
|
||||||
self.store_status(&status)
|
self.store_status(&status)
|
||||||
.map_err(|err| io::Error::new(io::ErrorKind::Other, err.to_string()))?;
|
.map_err(|err| BlockReadError::Error(io::Error::new(io::ErrorKind::Other, err.to_string())))?;
|
||||||
|
|
||||||
let reader = EmulateTapeReader::new(file);
|
let reader = EmulateTapeReader::new(file);
|
||||||
|
let reader = BlockedReader::open(reader)?;
|
||||||
match BlockedReader::open(reader)? {
|
Ok(Box::new(reader))
|
||||||
Some(reader) => Ok(Some(Box::new(reader))),
|
}
|
||||||
None => Ok(None),
|
None => {
|
||||||
}
|
return Err(BlockReadError::Error(proxmox::io_format_err!("drive is empty (no tape loaded).")));
|
||||||
}
|
}
|
||||||
None => proxmox::io_bail!("drive is empty (no tape loaded)."),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3,7 +3,7 @@ use std::io::Read;
|
|||||||
use crate::tape::{
|
use crate::tape::{
|
||||||
TapeRead,
|
TapeRead,
|
||||||
BlockRead,
|
BlockRead,
|
||||||
BlockReadStatus,
|
BlockReadError,
|
||||||
file_formats::{
|
file_formats::{
|
||||||
PROXMOX_TAPE_BLOCK_HEADER_MAGIC_1_0,
|
PROXMOX_TAPE_BLOCK_HEADER_MAGIC_1_0,
|
||||||
BlockHeader,
|
BlockHeader,
|
||||||
@ -37,15 +37,13 @@ impl <R: BlockRead> BlockedReader<R> {
|
|||||||
|
|
||||||
/// Create a new BlockedReader instance.
|
/// Create a new BlockedReader instance.
|
||||||
///
|
///
|
||||||
/// This tries to read the first block, and returns None if we are
|
/// This tries to read the first block. Please inspect the error
|
||||||
/// at EOT.
|
/// to detect EOF and EOT.
|
||||||
pub fn open(mut reader: R) -> Result<Option<Self>, std::io::Error> {
|
pub fn open(mut reader: R) -> Result<Self, BlockReadError> {
|
||||||
|
|
||||||
let mut buffer = BlockHeader::new();
|
let mut buffer = BlockHeader::new();
|
||||||
|
|
||||||
if !Self::read_block_frame(&mut buffer, &mut reader)? {
|
Self::read_block_frame(&mut buffer, &mut reader)?;
|
||||||
return Ok(None);
|
|
||||||
}
|
|
||||||
|
|
||||||
let (_size, found_end_marker) = Self::check_buffer(&buffer, 0)?;
|
let (_size, found_end_marker) = Self::check_buffer(&buffer, 0)?;
|
||||||
|
|
||||||
@ -58,7 +56,7 @@ impl <R: BlockRead> BlockedReader<R> {
|
|||||||
got_eod = true;
|
got_eod = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(Some(Self {
|
Ok(Self {
|
||||||
reader,
|
reader,
|
||||||
buffer,
|
buffer,
|
||||||
found_end_marker,
|
found_end_marker,
|
||||||
@ -67,7 +65,7 @@ impl <R: BlockRead> BlockedReader<R> {
|
|||||||
seq_nr: 1,
|
seq_nr: 1,
|
||||||
read_error: false,
|
read_error: false,
|
||||||
read_pos: 0,
|
read_pos: 0,
|
||||||
}))
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn check_buffer(buffer: &BlockHeader, seq_nr: u32) -> Result<(usize, bool), std::io::Error> {
|
fn check_buffer(buffer: &BlockHeader, seq_nr: u32) -> Result<(usize, bool), std::io::Error> {
|
||||||
@ -95,7 +93,7 @@ impl <R: BlockRead> BlockedReader<R> {
|
|||||||
Ok((size, found_end_marker))
|
Ok((size, found_end_marker))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn read_block_frame(buffer: &mut BlockHeader, reader: &mut R) -> Result<bool, std::io::Error> {
|
fn read_block_frame(buffer: &mut BlockHeader, reader: &mut R) -> Result<(), BlockReadError> {
|
||||||
|
|
||||||
let data = unsafe {
|
let data = unsafe {
|
||||||
std::slice::from_raw_parts_mut(
|
std::slice::from_raw_parts_mut(
|
||||||
@ -104,38 +102,28 @@ impl <R: BlockRead> BlockedReader<R> {
|
|||||||
)
|
)
|
||||||
};
|
};
|
||||||
|
|
||||||
match reader.read_block(data) {
|
let bytes = reader.read_block(data)?;
|
||||||
Ok(BlockReadStatus::Ok(bytes)) => {
|
|
||||||
if bytes != BlockHeader::SIZE {
|
if bytes != BlockHeader::SIZE {
|
||||||
proxmox::io_bail!("got wrong block size");
|
return Err(proxmox::io_format_err!("got wrong block size").into());
|
||||||
}
|
|
||||||
Ok(true)
|
|
||||||
}
|
|
||||||
Ok(BlockReadStatus::EndOfFile) => {
|
|
||||||
Ok(false)
|
|
||||||
}
|
|
||||||
Ok(BlockReadStatus::EndOfStream) => {
|
|
||||||
return Err(std::io::Error::from_raw_os_error(nix::errno::Errno::ENOSPC as i32));
|
|
||||||
}
|
|
||||||
Err(err) => {
|
|
||||||
Err(err)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn consume_eof_marker(reader: &mut R) -> Result<(), std::io::Error> {
|
fn consume_eof_marker(reader: &mut R) -> Result<(), std::io::Error> {
|
||||||
let mut tmp_buf = [0u8; 512]; // use a small buffer for testing EOF
|
let mut tmp_buf = [0u8; 512]; // use a small buffer for testing EOF
|
||||||
match reader.read_block(&mut tmp_buf) {
|
match reader.read_block(&mut tmp_buf) {
|
||||||
Ok(BlockReadStatus::Ok(_)) => {
|
Ok(_) => {
|
||||||
proxmox::io_bail!("detected tape block after block-stream end marker");
|
proxmox::io_bail!("detected tape block after block-stream end marker");
|
||||||
}
|
}
|
||||||
Ok(BlockReadStatus::EndOfFile) => {
|
Err(BlockReadError::EndOfFile) => {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
Ok(BlockReadStatus::EndOfStream) => {
|
Err(BlockReadError::EndOfStream) => {
|
||||||
proxmox::io_bail!("got unexpected end of tape");
|
proxmox::io_bail!("got unexpected end of tape");
|
||||||
}
|
}
|
||||||
Err(err) => {
|
Err(BlockReadError::Error(err)) => {
|
||||||
return Err(err);
|
return Err(err);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -143,13 +131,22 @@ impl <R: BlockRead> BlockedReader<R> {
|
|||||||
|
|
||||||
fn read_block(&mut self) -> Result<usize, std::io::Error> {
|
fn read_block(&mut self) -> Result<usize, std::io::Error> {
|
||||||
|
|
||||||
if !Self::read_block_frame(&mut self.buffer, &mut self.reader)? {
|
match Self::read_block_frame(&mut self.buffer, &mut self.reader) {
|
||||||
self.got_eod = true;
|
Ok(()) => { /* ok */ }
|
||||||
self.read_pos = self.buffer.payload.len();
|
Err(BlockReadError::EndOfFile) => {
|
||||||
if !self.found_end_marker {
|
self.got_eod = true;
|
||||||
proxmox::io_bail!("detected tape stream without end marker");
|
self.read_pos = self.buffer.payload.len();
|
||||||
|
if !self.found_end_marker {
|
||||||
|
proxmox::io_bail!("detected tape stream without end marker");
|
||||||
|
}
|
||||||
|
return Ok(0); // EOD
|
||||||
|
}
|
||||||
|
Err(BlockReadError::EndOfStream) => {
|
||||||
|
proxmox::io_bail!("got unexpected end of tape");
|
||||||
|
}
|
||||||
|
Err(BlockReadError::Error(err)) => {
|
||||||
|
return Err(err);
|
||||||
}
|
}
|
||||||
return Ok(0); // EOD
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let (size, found_end_marker) = Self::check_buffer(&self.buffer, self.seq_nr)?;
|
let (size, found_end_marker) = Self::check_buffer(&self.buffer, self.seq_nr)?;
|
||||||
|
@ -4,7 +4,7 @@ use proxmox::tools::io::ReadExt;
|
|||||||
|
|
||||||
use crate::tape::{
|
use crate::tape::{
|
||||||
BlockRead,
|
BlockRead,
|
||||||
BlockReadStatus,
|
BlockReadError,
|
||||||
file_formats::PROXMOX_TAPE_BLOCK_SIZE,
|
file_formats::PROXMOX_TAPE_BLOCK_SIZE,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -24,22 +24,27 @@ impl <R: Read> EmulateTapeReader<R> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl <R: Read> BlockRead for EmulateTapeReader<R> {
|
impl <R: Read> BlockRead for EmulateTapeReader<R> {
|
||||||
fn read_block(&mut self, buffer: &mut [u8]) -> Result<BlockReadStatus, std::io::Error> {
|
fn read_block(&mut self, buffer: &mut [u8]) -> Result<usize, BlockReadError> {
|
||||||
if self.got_eof {
|
if self.got_eof {
|
||||||
proxmox::io_bail!("detected read after EOF!");
|
return Err(BlockReadError::Error(proxmox::io_format_err!("detected read after EOF!")));
|
||||||
}
|
}
|
||||||
match self.reader.read_exact_or_eof(buffer)? {
|
match self.reader.read_exact_or_eof(buffer)? {
|
||||||
false => {
|
false => {
|
||||||
self.got_eof = true;
|
self.got_eof = true;
|
||||||
Ok(BlockReadStatus::EndOfFile)
|
Err(BlockReadError::EndOfFile)
|
||||||
}
|
}
|
||||||
true => {
|
true => {
|
||||||
// test buffer len after EOF test (to allow EOF test with small buffers in BufferedReader)
|
// test buffer len after EOF test (to allow EOF test with small buffers in BufferedReader)
|
||||||
if buffer.len() != PROXMOX_TAPE_BLOCK_SIZE {
|
if buffer.len() != PROXMOX_TAPE_BLOCK_SIZE {
|
||||||
proxmox::io_bail!("EmulateTapeReader: read_block with wrong block size ({} != {})",
|
return Err(BlockReadError::Error(
|
||||||
buffer.len(), PROXMOX_TAPE_BLOCK_SIZE);
|
proxmox::io_format_err!(
|
||||||
|
"EmulateTapeReader: read_block with wrong block size ({} != {})",
|
||||||
|
buffer.len(),
|
||||||
|
PROXMOX_TAPE_BLOCK_SIZE,
|
||||||
|
)
|
||||||
|
));
|
||||||
}
|
}
|
||||||
Ok(BlockReadStatus::Ok(buffer.len()))
|
Ok(buffer.len())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -15,14 +15,18 @@ pub trait TapeRead: Read {
|
|||||||
fn has_end_marker(&self) -> Result<bool, std::io::Error>;
|
fn has_end_marker(&self) -> Result<bool, std::io::Error>;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub enum BlockReadStatus {
|
#[derive(thiserror::Error, Debug)]
|
||||||
Ok(usize),
|
pub enum BlockReadError {
|
||||||
|
#[error("{0}")]
|
||||||
|
Error(#[from] std::io::Error),
|
||||||
|
#[error("end of file")]
|
||||||
EndOfFile,
|
EndOfFile,
|
||||||
|
#[error("end of data stream")]
|
||||||
EndOfStream,
|
EndOfStream,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Read streams of blocks
|
/// Read streams of blocks
|
||||||
pub trait BlockRead {
|
pub trait BlockRead {
|
||||||
/// Read the next block (whole buffer)
|
/// Read the next block (whole buffer)
|
||||||
fn read_block(&mut self, buffer: &mut [u8]) -> Result<BlockReadStatus, std::io::Error>;
|
fn read_block(&mut self, buffer: &mut [u8]) -> Result<usize, BlockReadError>;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user