[pbs-devel] [PATCH 04/11] tape: implement format/erase

Dietmar Maurer dietmar at proxmox.com
Wed Apr 7 12:23:01 CEST 2021


---
 src/api2/tape/drive.rs         | 28 ++++++++++-----------
 src/bin/pmt.rs                 | 32 +++++++++++++++++++++++-
 src/bin/proxmox-tape.rs        | 10 ++++----
 src/tape/drive/lto/mod.rs      |  9 ++++---
 src/tape/drive/lto/sg_tape.rs  | 45 +++++++++++++++++++++++++++++++---
 src/tape/drive/mod.rs          |  6 ++---
 src/tape/drive/virtual_tape.rs |  2 +-
 www/Utils.js                   |  2 +-
 www/tape/DriveStatus.js        |  8 +++---
 www/tape/window/Erase.js       |  4 +--
 10 files changed, 108 insertions(+), 38 deletions(-)

diff --git a/src/api2/tape/drive.rs b/src/api2/tape/drive.rs
index 80d17a27..e354f4c0 100644
--- a/src/api2/tape/drive.rs
+++ b/src/api2/tape/drive.rs
@@ -321,8 +321,8 @@ pub fn unload(
         permission: &Permission::Privilege(&["tape", "device", "{drive}"], PRIV_TAPE_WRITE, false),
     },
 )]
-/// Erase media. Check for label-text if given (cancels if wrong media).
-pub fn erase_media(
+/// Format media. Check for label-text if given (cancels if wrong media).
+pub fn format_media(
     drive: String,
     fast: Option<bool>,
     label_text: Option<String>,
@@ -331,7 +331,7 @@ pub fn erase_media(
     let upid_str = run_drive_worker(
         rpcenv,
         drive.clone(),
-        "erase-media",
+        "format-media",
         Some(drive.clone()),
         move |worker, config| {
             if let Some(ref label) = label_text {
@@ -350,15 +350,15 @@ pub fn erase_media(
                     }
                     /* assume drive contains no or unrelated data */
                     task_log!(worker, "unable to read media label: {}", err);
-                    task_log!(worker, "erase anyways");
-                    handle.erase_media(fast.unwrap_or(true))?;
+                    task_log!(worker, "format anyways");
+                    handle.format_media(fast.unwrap_or(true))?;
                 }
                 Ok((None, _)) => {
                     if let Some(label) = label_text {
                         bail!("expected label '{}', found empty tape", label);
                     }
-                    task_log!(worker, "found empty media - erase anyways");
-                    handle.erase_media(fast.unwrap_or(true))?;
+                    task_log!(worker, "found empty media - format anyways");
+                    handle.format_media(fast.unwrap_or(true))?;
                 }
                 Ok((Some(media_id), _key_config)) => {
                     if let Some(label_text) = label_text {
@@ -391,7 +391,7 @@ pub fn erase_media(
                         inventory.remove_media(&media_id.label.uuid)?;
                     };
 
-                    handle.erase_media(fast.unwrap_or(true))?;
+                    handle.format_media(fast.unwrap_or(true))?;
                 }
             }
 
@@ -503,7 +503,7 @@ pub fn eject_media(
 /// Write a new media label to the media in 'drive'. The media is
 /// assigned to the specified 'pool', or else to the free media pool.
 ///
-/// Note: The media need to be empty (you may want to erase it first).
+/// Note: The media need to be empty (you may want to format it first).
 pub fn label_media(
     drive: String,
     pool: Option<String>,
@@ -528,7 +528,7 @@ pub fn label_media(
             drive.rewind()?;
 
             match drive.read_next_file() {
-                Ok(Some(_file)) => bail!("media is not empty (erase first)"),
+                Ok(Some(_file)) => bail!("media is not empty (format it first)"),
                 Ok(None) => { /* EOF mark at BOT, assume tape is empty */ },
                 Err(err) => {
                     println!("TEST {:?}", err);
@@ -1092,7 +1092,7 @@ fn barcode_label_media_worker(
 
         match drive.read_next_file() {
             Ok(Some(_file)) => {
-                worker.log(format!("media '{}' is not empty (erase first)", label_text));
+                worker.log(format!("media '{}' is not empty (format it first)", label_text));
                 continue;
             }
             Ok(None) => { /* EOF mark at BOT, assume tape is empty */ },
@@ -1100,7 +1100,7 @@ fn barcode_label_media_worker(
                 if err.is_errno(nix::errno::Errno::ENOSPC) || err.is_errno(nix::errno::Errno::EIO) {
                     /* assume tape is empty */
                 } else {
-                    worker.warn(format!("media '{}' read error (maybe not empty - erase first)", label_text));
+                    worker.warn(format!("media '{}' read error (maybe not empty - format it first)", label_text));
                     continue;
                 }
             }
@@ -1430,9 +1430,9 @@ pub const SUBDIRS: SubdirMap = &sorted!([
             .post(&API_METHOD_EJECT_MEDIA)
     ),
     (
-        "erase-media",
+        "format-media",
         &Router::new()
-            .post(&API_METHOD_ERASE_MEDIA)
+            .post(&API_METHOD_FORMAT_MEDIA)
     ),
     (
         "export-media",
diff --git a/src/bin/pmt.rs b/src/bin/pmt.rs
index df3ad9ec..da0d4fd9 100644
--- a/src/bin/pmt.rs
+++ b/src/bin/pmt.rs
@@ -409,7 +409,7 @@ fn eod(param: Value) -> Result<(), Error> {
         },
     },
 )]
-/// Erase media
+/// Erase media (from current position)
 fn erase(fast: Option<bool>, param: Value) -> Result<(), Error> {
 
     let mut handle = get_tape_handle(&param)?;
@@ -418,6 +418,35 @@ fn erase(fast: Option<bool>, param: Value) -> Result<(), Error> {
     Ok(())
 }
 
+#[api(
+   input: {
+        properties: {
+            drive: {
+                schema: DRIVE_NAME_SCHEMA,
+                optional: true,
+            },
+            device: {
+                schema: LTO_DRIVE_PATH_SCHEMA,
+                optional: true,
+            },
+            fast: {
+                description: "Use fast erase.",
+                type: bool,
+                optional: true,
+                default: true,
+            },
+        },
+    },
+)]
+/// Format media,  single partition
+fn format(fast: Option<bool>, param: Value) -> Result<(), Error> {
+
+    let mut handle = get_tape_handle(&param)?;
+    handle.format_media(fast.unwrap_or(true))?;
+
+    Ok(())
+}
+
 #[api(
    input: {
         properties: {
@@ -800,6 +829,7 @@ fn main() -> Result<(), Error> {
         .insert("eject", std_cmd(&API_METHOD_EJECT))
         .insert("eod", std_cmd(&API_METHOD_EOD))
         .insert("erase", std_cmd(&API_METHOD_ERASE))
+        .insert("format", std_cmd(&API_METHOD_FORMAT))
         .insert("fsf", std_cmd(&API_METHOD_FSF).arg_param(&["count"]))
         .insert("fsfm", std_cmd(&API_METHOD_FSFM).arg_param(&["count"]))
         .insert("fsr", std_cmd(&API_METHOD_FSR).arg_param(&["count"]))
diff --git a/src/bin/proxmox-tape.rs b/src/bin/proxmox-tape.rs
index cddac1b4..2a784632 100644
--- a/src/bin/proxmox-tape.rs
+++ b/src/bin/proxmox-tape.rs
@@ -115,8 +115,8 @@ pub fn extract_drive_name(
        },
     },
 )]
-/// Erase media
-async fn erase_media(mut param: Value) -> Result<(), Error> {
+/// Format media
+async fn format_media(mut param: Value) -> Result<(), Error> {
 
     let output_format = get_output_format(&param);
 
@@ -126,7 +126,7 @@ async fn erase_media(mut param: Value) -> Result<(), Error> {
 
     let mut client = connect_to_localhost()?;
 
-    let path = format!("api2/json/tape/drive/{}/erase-media", drive);
+    let path = format!("api2/json/tape/drive/{}/format-media", drive);
     let result = client.post(&path, Some(param)).await?;
 
     view_task_result(&mut client, result, &output_format).await?;
@@ -992,8 +992,8 @@ fn main() {
                 .completion_cb("drive", complete_drive_name)
         )
         .insert(
-            "erase",
-            CliCommand::new(&API_METHOD_ERASE_MEDIA)
+            "format",
+            CliCommand::new(&API_METHOD_FORMAT_MEDIA)
                 .completion_cb("drive", complete_drive_name)
         )
         .insert(
diff --git a/src/tape/drive/lto/mod.rs b/src/tape/drive/lto/mod.rs
index becbad50..a4e0499e 100644
--- a/src/tape/drive/lto/mod.rs
+++ b/src/tape/drive/lto/mod.rs
@@ -179,6 +179,10 @@ impl LtoTapeHandle {
         Ok(status)
     }
 
+    pub fn erase_media(&mut self, fast: bool) -> Result<(), Error> {
+        self.sg_tape.erase_media(fast)
+    }
+
     pub fn load(&mut self) ->  Result<(), Error> {
         self.sg_tape.load()
     }
@@ -223,9 +227,8 @@ impl TapeDriver for LtoTapeHandle {
         self.sg_tape.current_file_number()
     }
 
-    fn erase_media(&mut self, fast: bool) -> Result<(), Error> {
-        self.rewind()?; // important - erase from BOT
-        self.sg_tape.erase_media(fast)
+    fn format_media(&mut self, fast: bool) -> Result<(), Error> {
+        self.sg_tape.format_media(fast)
     }
 
     fn read_next_file<'a>(&'a mut self) -> Result<Option<Box<dyn TapeRead + 'a>>, std::io::Error> {
diff --git a/src/tape/drive/lto/sg_tape.rs b/src/tape/drive/lto/sg_tape.rs
index 802756fa..531acbee 100644
--- a/src/tape/drive/lto/sg_tape.rs
+++ b/src/tape/drive/lto/sg_tape.rs
@@ -100,9 +100,48 @@ impl SgTape {
         scsi_inquiry(&mut self.file)
     }
 
-    pub fn erase_media(&mut self, _fast: bool) -> Result<(), Error> {
-        // fixme:
-        unimplemented!();
+    /// Erase medium.
+    ///
+    /// EOD is written at the current position, which marks it as end
+    /// of data. After the command is successfully completed, the
+    /// drive is positioned immediately before End Of Data (not End Of
+    /// Tape).
+    pub fn erase_media(&mut self, fast: bool) -> Result<(), Error> {
+        let mut sg_raw = SgRaw::new(&mut self.file, 16)?;
+        sg_raw.set_timeout(Self::SCSI_TAPE_DEFAULT_TIMEOUT);
+        let mut cmd = Vec::new();
+        cmd.push(0x19);
+        if fast {
+            cmd.push(0); // LONG=0
+        } else {
+            cmd.push(1); // LONG=1
+        }
+        cmd.extend(&[0, 0, 0, 0]);
+
+        sg_raw.do_command(&cmd)
+            .map_err(|err| format_err!("erase failed - {}", err))?;
+
+        Ok(())
+    }
+
+    /// Format media, single partition
+    pub fn format_media(&mut self, fast: bool) -> Result<(), Error> {
+
+        self.rewind()?;
+
+        let mut sg_raw = SgRaw::new(&mut self.file, 16)?;
+        sg_raw.set_timeout(Self::SCSI_TAPE_DEFAULT_TIMEOUT);
+        let mut cmd = Vec::new();
+        cmd.extend(&[0x04, 0, 0, 0, 0, 0]);
+
+        sg_raw.do_command(&cmd)
+            .map_err(|err| format_err!("erase failed - {}", err))?;
+
+        if !fast {
+            self.erase_media(false)?; // overwrite everything
+        }
+
+        Ok(())
     }
 
     pub fn rewind(&mut self) -> Result<(), Error> {
diff --git a/src/tape/drive/mod.rs b/src/tape/drive/mod.rs
index 71f61642..061c1cfc 100644
--- a/src/tape/drive/mod.rs
+++ b/src/tape/drive/mod.rs
@@ -111,7 +111,7 @@ pub trait TapeDriver {
     fn current_file_number(&mut self) -> Result<u64, Error>;
 
     /// Completely erase the media
-    fn erase_media(&mut self, fast: bool) -> Result<(), Error>;
+    fn format_media(&mut self, fast: bool) -> Result<(), Error>;
 
     /// Read/Open the next file
     fn read_next_file<'a>(&'a mut self) -> Result<Option<Box<dyn TapeRead + 'a>>, std::io::Error>;
@@ -122,11 +122,9 @@ pub trait TapeDriver {
     /// Write label to tape (erase tape content)
     fn label_tape(&mut self, label: &MediaLabel) -> Result<(), Error> {
 
-        self.rewind()?;
-
         self.set_encryption(None)?;
 
-        self.erase_media(true)?;
+        self.format_media(true)?; // this rewinds the tape
 
         let raw = serde_json::to_string_pretty(&serde_json::to_value(&label)?)?;
 
diff --git a/src/tape/drive/virtual_tape.rs b/src/tape/drive/virtual_tape.rs
index 54e0887f..e4d09c2f 100644
--- a/src/tape/drive/virtual_tape.rs
+++ b/src/tape/drive/virtual_tape.rs
@@ -360,7 +360,7 @@ impl TapeDriver for VirtualTapeHandle {
         }
     }
 
-    fn erase_media(&mut self, _fast: bool) -> Result<(), Error> {
+    fn format_media(&mut self, _fast: bool) -> Result<(), Error> {
         let mut status = self.load_status()?;
         match status.current_tape {
             Some(VirtualTapeStatus { ref name, ref mut pos }) => {
diff --git a/www/Utils.js b/www/Utils.js
index dc7e539f..b9012374 100644
--- a/www/Utils.js
+++ b/www/Utils.js
@@ -374,7 +374,7 @@ Ext.define('PBS.Utils', {
 	    dircreate: [gettext('Directory Storage'), gettext('Create')],
 	    dirremove: [gettext('Directory'), gettext('Remove')],
 	    'eject-media': [gettext('Drive'), gettext('Eject Media')],
-	    'erase-media': [gettext('Drive'), gettext('Erase Media')],
+	    "format-media": [gettext('Drive'), gettext('Format media')],
 	    garbage_collection: ['Datastore', gettext('Garbage Collect')],
 	    'inventory-update': [gettext('Drive'), gettext('Inventory Update')],
 	    'label-media': [gettext('Drive'), gettext('Label Media')],
diff --git a/www/tape/DriveStatus.js b/www/tape/DriveStatus.js
index 65197285..2bf05f88 100644
--- a/www/tape/DriveStatus.js
+++ b/www/tape/DriveStatus.js
@@ -84,11 +84,11 @@ Ext.define('PBS.TapeManagement.DriveStatus', {
 	    }).show();
 	},
 
-	erase: function() {
+	format: function() {
 	    let me = this;
 	    let view = me.getView();
 	    let driveid = view.drive;
-	    PBS.Utils.driveCommand(driveid, 'erase-media', {
+	    PBS.Utils.driveCommand(driveid, 'format-media', {
 		waitMsgTarget: view,
 		method: 'POST',
 		success: function(response) {
@@ -212,9 +212,9 @@ Ext.define('PBS.TapeManagement.DriveStatus', {
 	    },
 	},
 	{
-	    text: gettext('Erase'),
+	    text: gettext('Format'),
 	    xtype: 'proxmoxButton',
-	    handler: 'erase',
+	    handler: 'format',
 	    iconCls: 'fa fa-trash-o',
 	    dangerous: true,
 	    confirmMsg: gettext('Are you sure you want to erase the inserted tape?'),
diff --git a/www/tape/window/Erase.js b/www/tape/window/Erase.js
index 61bd2130..1177dfeb 100644
--- a/www/tape/window/Erase.js
+++ b/www/tape/window/Erase.js
@@ -11,13 +11,13 @@ Ext.define('PBS.TapeManagement.EraseWindow', {
 	return {};
     },
 
-    title: gettext('Erase'),
+    title: gettext('Format/Erase'),
     url: `/api2/extjs/tape/drive`,
     showProgress: true,
     submitUrl: function(url, values) {
 	let drive = values.drive;
 	delete values.drive;
-	return `${url}/${drive}/erase-media`;
+	return `${url}/${drive}/format-media`;
     },
 
     method: 'POST',
-- 
2.20.1





More information about the pbs-devel mailing list