From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from firstgate.proxmox.com (firstgate.proxmox.com [212.224.123.68]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits)) (No client certificate requested) by lists.proxmox.com (Postfix) with ESMTPS id 278D971283 for ; Wed, 7 Apr 2021 12:24:47 +0200 (CEST) Received: from firstgate.proxmox.com (localhost [127.0.0.1]) by firstgate.proxmox.com (Proxmox) with ESMTP id E1987F518 for ; Wed, 7 Apr 2021 12:24:16 +0200 (CEST) Received: from elsa.proxmox.com (unknown [94.136.29.99]) by firstgate.proxmox.com (Proxmox) with ESMTP id 493D7F34E for ; Wed, 7 Apr 2021 12:24:07 +0200 (CEST) Received: by elsa.proxmox.com (Postfix, from userid 0) id 0C6BFAEAF23; Wed, 7 Apr 2021 12:24:07 +0200 (CEST) From: Dietmar Maurer To: pbs-devel@lists.proxmox.com Date: Wed, 7 Apr 2021 12:23:01 +0200 Message-Id: <20210407102308.9750-5-dietmar@proxmox.com> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20210407102308.9750-1-dietmar@proxmox.com> References: <20210407102308.9750-1-dietmar@proxmox.com> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-SPAM-LEVEL: Spam detection results: 1 KAM_DMARC_STATUS 0.01 Test Rule for DKIM or SPF Failure with Strict Alignment RDNS_NONE 1.274 Delivered to internal network by a host with no rDNS SPF_HELO_NONE 0.001 SPF: HELO does not publish an SPF Record SPF_PASS -0.001 SPF: sender matches SPF record URIBL_BLOCKED 0.001 ADMINISTRATOR NOTICE: The query to URIBL was blocked. See http://wiki.apache.org/spamassassin/DnsBlocklists#dnsbl-block for more information. [proxmox-tape.rs, pmt.rs, drive.rs, mod.rs] Subject: [pbs-devel] [PATCH 04/11] tape: implement format/erase X-BeenThere: pbs-devel@lists.proxmox.com X-Mailman-Version: 2.1.29 Precedence: list List-Id: Proxmox Backup Server development discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Wed, 07 Apr 2021 10:24:47 -0000 --- 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, label_text: Option, @@ -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, @@ -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, param: Value) -> Result<(), Error> { let mut handle = get_tape_handle(¶m)?; @@ -418,6 +418,35 @@ fn erase(fast: Option, 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, param: Value) -> Result<(), Error> { + + let mut handle = get_tape_handle(¶m)?; + 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(¶m); @@ -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>, 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; /// 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>, 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