]> git.proxmox.com Git - proxmox-backup.git/commitdiff
tape: add helpers to emulate tape read/write behavior
authorDietmar Maurer <dietmar@proxmox.com>
Sun, 6 Dec 2020 08:41:16 +0000 (09:41 +0100)
committerDietmar Maurer <dietmar@proxmox.com>
Sun, 6 Dec 2020 08:41:16 +0000 (09:41 +0100)
Cargo.toml
src/tape/helpers/emulate_tape_reader.rs [new file with mode: 0644]
src/tape/helpers/emulate_tape_writer.rs [new file with mode: 0644]
src/tape/helpers/mod.rs [new file with mode: 0644]
src/tape/mod.rs

index 820d5709a859f913e8a10e43711745a6a29c54a0..856e1abf0ecade0f05dafdb234c26437d1006dc4 100644 (file)
@@ -48,7 +48,7 @@ percent-encoding = "2.1"
 pin-utils = "0.1.0"
 pin-project = "0.4"
 pathpatterns = "0.1.2"
-proxmox = { version = "0.8.0", features = [ "sortable-macro", "api-macro", "websocket" ] }
+proxmox = { version = "0.8.1", features = [ "sortable-macro", "api-macro", "websocket" ] }
 #proxmox = { git = "git://git.proxmox.com/git/proxmox", version = "0.1.2", features = [ "sortable-macro", "api-macro" ] }
 #proxmox = { path = "../proxmox/proxmox", features = [ "sortable-macro", "api-macro", "websocket" ] }
 proxmox-fuse = "0.1.0"
diff --git a/src/tape/helpers/emulate_tape_reader.rs b/src/tape/helpers/emulate_tape_reader.rs
new file mode 100644 (file)
index 0000000..1b6d4c5
--- /dev/null
@@ -0,0 +1,56 @@
+use std::io::{self, Read};
+
+use crate::tape::file_formats::PROXMOX_TAPE_BLOCK_SIZE;
+
+/// Emulate tape read behavior on a normal Reader
+///
+/// Tapes reads are always return one whole block PROXMOX_TAPE_BLOCK_SIZE.
+pub struct EmulateTapeReader<R> {
+    reader: R,
+}
+
+impl <R: Read> EmulateTapeReader<R> {
+
+    pub fn new(reader: R) -> Self {
+        Self { reader }
+    }
+}
+
+impl <R: Read> Read for EmulateTapeReader<R> {
+
+    fn read(&mut self, mut buffer: &mut [u8]) -> Result<usize, io::Error> {
+
+        let initial_buffer_len = buffer.len(); // store, check later
+
+        let mut bytes = 0;
+
+        while !buffer.is_empty() {
+            match self.reader.read(buffer) {
+                Ok(0) => break,
+                Ok(n) => {
+                    bytes += n;
+                    let tmp = buffer;
+                    buffer = &mut tmp[n..];
+                }
+                Err(ref e) if e.kind() == io::ErrorKind::Interrupted => {}
+                Err(e) => return Err(e),
+            }
+        }
+
+        if bytes == 0 {
+            return Ok(0);
+        }
+
+        // test buffer len after EOF test (to allow EOF test with small buffers in BufferedReader)
+        if initial_buffer_len != PROXMOX_TAPE_BLOCK_SIZE {
+            proxmox::io_bail!("EmulateTapeReader: got read with wrong block size ({} != {})",
+                              buffer.len(), PROXMOX_TAPE_BLOCK_SIZE);
+        }
+
+        if !buffer.is_empty() {
+            Err(io::Error::new(io::ErrorKind::UnexpectedEof, "failed to fill whole buffer"))
+        } else {
+            Ok(bytes)
+        }
+    }
+}
diff --git a/src/tape/helpers/emulate_tape_writer.rs b/src/tape/helpers/emulate_tape_writer.rs
new file mode 100644 (file)
index 0000000..b385d6b
--- /dev/null
@@ -0,0 +1,68 @@
+use std::io::{self, Write};
+
+use crate::tape::file_formats::PROXMOX_TAPE_BLOCK_SIZE;
+
+/// Emulate tape write behavior on a normal Writer
+///
+/// Data need to be written in blocks of size PROXMOX_TAPE_BLOCK_SIZE.
+/// Before reaching the EOT, the writer returns ENOSPC (like a linux
+/// tape device).
+pub struct EmulateTapeWriter<W> {
+    block_nr: usize,
+    max_blocks: usize,
+    writer: W,
+    leom_sent: bool,
+}
+
+impl <W: Write> EmulateTapeWriter<W> {
+
+    /// Create a new instance allowing to write about max_size bytes
+    pub fn new(writer: W, max_size: usize) -> Self {
+
+        let mut max_blocks = max_size/PROXMOX_TAPE_BLOCK_SIZE;
+
+        if max_blocks < 2 {
+            max_blocks = 2; // at least 2 blocks
+        }
+
+        Self {
+            block_nr: 0,
+            leom_sent: false,
+            writer,
+            max_blocks,
+        }
+    }
+}
+
+impl <W: Write> Write for EmulateTapeWriter<W> {
+
+    fn write(&mut self, buffer: &[u8]) -> Result<usize, io::Error> {
+
+        if buffer.len() != PROXMOX_TAPE_BLOCK_SIZE {
+            proxmox::io_bail!("EmulateTapeWriter: got write with wrong block size ({} != {}",
+                              buffer.len(), PROXMOX_TAPE_BLOCK_SIZE);
+        }
+
+        if self.block_nr >= self.max_blocks + 2 {
+            return Err(io::Error::from_raw_os_error(nix::errno::Errno::ENOSPC as i32));
+        }
+
+        if self.block_nr >= self.max_blocks {
+            if !self.leom_sent {
+                self.leom_sent = true;
+                return Err(io::Error::from_raw_os_error(nix::errno::Errno::ENOSPC as i32));
+            } else {
+                self.leom_sent = false;
+            }
+        }
+
+        self.writer.write_all(buffer)?;
+        self.block_nr += 1;
+
+        Ok(buffer.len())
+    }
+
+    fn flush(&mut self) -> Result<(), io::Error> {
+        proxmox::io_bail!("EmulateTapeWriter does not support flush");
+    }
+}
diff --git a/src/tape/helpers/mod.rs b/src/tape/helpers/mod.rs
new file mode 100644 (file)
index 0000000..66c8fea
--- /dev/null
@@ -0,0 +1,5 @@
+mod emulate_tape_writer;
+pub use emulate_tape_writer::*;
+
+mod emulate_tape_reader;
+pub use emulate_tape_reader::*;
index 9e11ea4e584b98e1f7d7016f77f0c15cbc742d40..85de1328a786985d5e5d945a04d8ae7c584ce3a4 100644 (file)
@@ -6,6 +6,9 @@ pub use tape_write::*;
 mod tape_read;
 pub use tape_read::*;
 
+mod helpers;
+pub use helpers::*;
+
 mod inventory;
 pub use inventory::*;