From 0c5b9e78200d8a70addfb3a2e47051f800e54fb0 Mon Sep 17 00:00:00 2001 From: Dietmar Maurer Date: Sat, 16 Jan 2021 15:15:54 +0100 Subject: [PATCH] tape: sgutils2.rs - add do_out_command() Make it possible to run commands that writes data. --- src/tape/sgutils2.rs | 112 +++++++++++++++++++++++++++++++++++++------ 1 file changed, 97 insertions(+), 15 deletions(-) diff --git a/src/tape/sgutils2.rs b/src/tape/sgutils2.rs index 30826074..464acbd9 100644 --- a/src/tape/sgutils2.rs +++ b/src/tape/sgutils2.rs @@ -16,6 +16,12 @@ impl Drop for SgPtBase { } } +pub const SCSI_PT_RESULT_GOOD:c_int = 0; +pub const SCSI_PT_RESULT_STATUS:c_int = 1; +pub const SCSI_PT_RESULT_SENSE:c_int = 2; +pub const SCSI_PT_RESULT_TRANSPORT_ERR:c_int = 3; +pub const SCSI_PT_RESULT_OS_ERR:c_int = 4; + #[link(name = "sgutils2")] extern { @@ -39,6 +45,12 @@ extern { dxfer_ilen: c_int, ); + pub fn set_scsi_pt_data_out( + objp: *mut SgPtBase, + dxferp: *const u8, + dxfer_olen: c_int, + ); + pub fn set_scsi_pt_cdb( objp: *mut SgPtBase, cdb: *const u8, @@ -63,6 +75,8 @@ extern { pub fn get_scsi_pt_sense_len(objp: *const SgPtBase) -> c_int; pub fn get_scsi_pt_status_response(objp: *const SgPtBase) -> c_int; + + pub fn get_scsi_pt_result_category(objp: *const SgPtBase) -> c_int; } /// Creates a Box @@ -87,6 +101,19 @@ pub struct SgRaw<'a, F> { sense_buffer: [u8; 32], } +// alloc page aligned buffer +pub fn alloc_page_aligned_buffer(buffer_size: usize) -> Result , Error> { + let page_size = unsafe { libc::sysconf(libc::_SC_PAGESIZE) } as usize; + let layout = std::alloc::Layout::from_size_align(buffer_size, page_size)?; + let dinp = unsafe { std::alloc::alloc_zeroed(layout) }; + if dinp.is_null() { + bail!("alloc SCSI output buffer failed"); + } + + let buffer_slice = unsafe { std::slice::from_raw_parts_mut(dinp, buffer_size)}; + Ok(unsafe { Box::from_raw(buffer_slice) }) +} + impl <'a, F: AsRawFd> SgRaw<'a, F> { /// Create a new instance to run commands @@ -94,15 +121,13 @@ impl <'a, F: AsRawFd> SgRaw<'a, F> { /// The file must be a handle to a SCSI device. pub fn new(file: &'a mut F, buffer_size: usize) -> Result { - let page_size = unsafe { libc::sysconf(libc::_SC_PAGESIZE) } as usize; - let layout = std::alloc::Layout::from_size_align(buffer_size, page_size)?; - let dinp = unsafe { std::alloc::alloc(layout) }; - if dinp.is_null() { - bail!("alloc SCSI output buffer failed"); - } + let buffer; - let buffer = unsafe { std::slice::from_raw_parts_mut(dinp, buffer_size)}; - let buffer = unsafe { Box::from_raw(buffer) }; + if buffer_size > 0 { + buffer = alloc_page_aligned_buffer(buffer_size)?; + } else { + buffer = Box::new([]); + } let sense_buffer = [0u8; 32]; @@ -114,13 +139,15 @@ impl <'a, F: AsRawFd> SgRaw<'a, F> { let mut ptvp = boxed_scsi_pt_obj()?; - unsafe { - set_scsi_pt_data_in( - &mut *ptvp, - self.buffer.as_ptr(), - self.buffer.len() as c_int, - ) - }; + if self.buffer.len() > 0 { + unsafe { + set_scsi_pt_data_in( + &mut *ptvp, + self.buffer.as_ptr(), + self.buffer.len() as c_int, + ) + }; + } unsafe { set_scsi_pt_sense( @@ -140,6 +167,10 @@ impl <'a, F: AsRawFd> SgRaw<'a, F> { bail!("no valid SCSI command"); } + if self.buffer.len() < 16 { + bail!("output buffer too small"); + } + let mut ptvp = self.create_boxed_scsi_pt_obj()?; unsafe { @@ -176,4 +207,55 @@ impl <'a, F: AsRawFd> SgRaw<'a, F> { Ok(&self.buffer[..data_len]) } + + /// Run dataout command + /// + /// Note: use alloc_page_aligned_buffer to alloc data transfer buffer + pub fn do_out_command(&mut self, cmd: &[u8], data: &[u8]) -> Result<(), Error> { + + if !unsafe { sg_is_scsi_cdb(cmd.as_ptr(), cmd.len() as c_int) } { + bail!("no valid SCSI command"); + } + + let page_size = unsafe { libc::sysconf(libc::_SC_PAGESIZE) } as usize; + if ((data.as_ptr() as usize) & (page_size -1)) != 0 { + bail!("wrong transfer buffer alignment"); + } + + let mut ptvp = self.create_boxed_scsi_pt_obj()?; + + unsafe { + set_scsi_pt_data_out( + &mut *ptvp, + data.as_ptr(), + data.len() as c_int, + ); + + set_scsi_pt_cdb( + &mut *ptvp, + cmd.as_ptr(), + cmd.len() as c_int, + ); + }; + + let res = unsafe { do_scsi_pt(&mut *ptvp, self.file.as_raw_fd(), 0, 0) }; + if res < 0 { + let err = nix::Error::last(); + bail!("do_scsi_pt failed - {}", err); + } + if res != 0 { + bail!("do_scsi_pt failed {}", res); + } + + // todo: what about sense data? + let _sense_len = unsafe { get_scsi_pt_sense_len(&mut *ptvp) }; + + let status = unsafe { get_scsi_pt_status_response(&mut *ptvp) }; + if status != 0 { + // toto: improve error reporting + bail!("unknown scsi error - status response {}", status); + } + + Ok(()) + } }