/* * @project The CERN Tape Archive (CTA) * @copyright Copyright(C) 2003-2021 CERN * @license This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #pragma once #include #include #include #include #include #include #include #include #include #include "Constants.hpp" #include "common/exception/Exception.hpp" namespace castor { namespace tape { namespace SCSI { const unsigned int defaultTimeout=900000; //millisecs /** * Maximum number of tape wraps. * * This is used to determine the maximum size of the response from the REOWP command, * which returns 12 bytes per wrap, plus a 4-byte header. * * LTO-8 has 208 physical wraps. LTO-9 has 280 wraps. This number should be adjusted * upwards when the LTO-10 specification is announced. */ const unsigned int maxLTOTapeWraps = 280; /** * Structures as defined in the SCSI specifications, and helper functions for them. * SPC-4 (SCSI primary commands) can be found at: * http://hackipedia.org/Hardware/SCSI/Primary%20Commands/SCSI%20Primary%20Commands%20-%204.pdf * * and SSC-3 (SCSI stream commands, i.e. tape drives) at: * http://hackipedia.org/Hardware/SCSI/Stream%20Commands/SCSI%20Stream%20Commands%20-%203.pdf */ namespace Structures { /** * Helper template to zero a structure. Small boilerplate reduction. * Should not be used with classes with virtual tables! With a zeroed * out virtual table pointer, this will be detected soon enough. * @param s pointer the struct/class. */ template void zeroStruct(C * s) { memset (s, 0, sizeof(C)); } /** * Class wrapping around Linux' SG_IO struct, providing * zeroing and automatic filling up for the mandatory structures * (cdb, databuffer, sense buffer, magic 'S', and default timeout). * Make it look like a bare sg_io_hdr_t when using & operator. * Another little boilerplate killer. */ class LinuxSGIO_t: public sg_io_hdr_t { public: LinuxSGIO_t() { zeroStruct(this); interface_id = 'S'; timeout = defaultTimeout; } template void setCDB(T * cdb) { cmdp = (unsigned char *)cdb; cmd_len = sizeof(T); } template void setSenseBuffer(T * senseBuff) { if (sizeof(T) > UCHAR_MAX) throw cta::exception::Exception("sense structure too big in LinuxSGIO_t::setSense"); mx_sb_len = (unsigned char) sizeof(T); sbp = (unsigned char *)senseBuff; } template void setDataBuffer(T * dataBuff) { dxferp = dataBuff; dxfer_len = sizeof (T); } // If dataBuff is a pointer to a variable length array, sizeof will return // the size of one element. This function allows to manually set the buffer size template void setDataBuffer(T * dataBuff, unsigned int dataBuffSize) { dxferp = dataBuff; dxfer_len = dataBuffSize; } sg_io_hdr_t * operator & () { return (sg_io_hdr_t *) this; } }; /** * Helper function to deal with endianness. * @param t byte array in SCSI order representing a 64 bits number * @return */ inline uint64_t toU64(const unsigned char(& t)[8]) { /* Like network, SCSI is BigEndian */ return (uint64_t) ntohl ( (*(uint64_t *) t << 32) >> 32) << 32 | ntohl(*(uint64_t *) t >>32); } /** * Helper function to deal with endianness: 6-byte version * * The REOWP SCSI command assigns 48 bits to store the LOGICAL OBJECT IDENTIFIER (although other * commands use 64 bits or sometimes 32 bits). This function converts the 48-bit byte array * into a 64-bit unsigned integer. * * @param t byte array in SCSI order representing a 48-bit number * @return 64-bit unsigned integer */ inline uint64_t toU64(const unsigned char(& t)[6]) { /* Like network, SCSI is BigEndian */ return (uint64_t) ntohl ( (*(uint64_t *) t << 32) >> 16) << 32 | ntohl(*(uint64_t *) t >>16); } /** * Helper function to deal with endianness. * @param t byte array in SCSI order representing a 32 bits number * @return */ inline uint32_t toU32(const unsigned char(& t)[4]) { /* Like network, SCSI is BigEndian */ return ntohl (*((uint32_t *) t)); } /** * Helper function to deal with endianness. * for 3 bytes! fields in SCSI replies * @param t byte array in SCSI order representing a 32 bits number * @return */ inline uint32_t toU32(const unsigned char(& t)[3]) { union { unsigned char tmp[4]; uint32_t val; } u; u.tmp[0]=0;u.tmp[1]=t[0];u.tmp[2]=t[1];u.tmp[3]=t[2]; /* Like network, SCSI is BigEndian */ return ntohl (u.val); } /** * Helper function to deal with endianness. * for signed values * @param t byte array in SCSI order representing a 32 bits number * @return */ inline int32_t toS32(const unsigned char(& t)[4]) { /* Like network, SCSI is BigEndian */ return (int32_t)(ntohl (*((uint32_t *) t))); } /** * Helper function to deal with endianness. * @param t byte array in SCSI order representing a 16 bits number * @return */ inline uint16_t toU16(const unsigned char(& t)[2]) { /* Like network, SCSI is BigEndian */ return ntohs (*((uint16_t *) t)); } /** * Helper function setting in place a 32 bits SCSI number from a value * expressed in the local endianness. * @param t pointer to the char array at the 32 bits value position. * @param val the value. */ inline void setU32(unsigned char(& t)[4], uint32_t val) { *((uint32_t *) t) = htonl(val); } /** * Helper function setting in place a 16 bits SCSI number from a value * expressed in the local endianness. * @param t pointer to the char array at the 16 bits value position. * @param val the value. */ inline void setU16(unsigned char(& t)[2], uint16_t val) { *((uint16_t *) t) = htons(val); } /** * Helper function setting in place a 64 bits SCSI number from a value * expressed in the local endianness. * @param t pointer to the char array at the 64 bits value position. * @param val the value. */ inline void setU64(unsigned char(& t)[8], uint64_t val) { *((uint64_t *) t) = htobe64(val); } /** * Inquiry CDB as described in SPC-4. */ class inquiryCDB_t { public: inquiryCDB_t() { zeroStruct(this); opCode = SCSI::Commands::INQUIRY; } unsigned char opCode; unsigned char EVPD : 1; unsigned char : 7; unsigned char pageCode; unsigned char allocationLength[2]; unsigned char control; }; /** * Inquiry data as described in SPC-4. */ class inquiryData_t { public: inquiryData_t () { zeroStruct(this); } unsigned char perifDevType : 5; unsigned char perifQualifyer : 3; unsigned char : 7; unsigned char RMB : 1; unsigned char version : 8; unsigned char respDataFmt : 4; unsigned char HiSup : 1; unsigned char normACA : 1; unsigned char : 2; unsigned char addLength : 8; unsigned char protect : 1; unsigned char : 2; unsigned char threePC : 1; unsigned char TPGS : 2; unsigned char ACC : 1; unsigned char SCCS : 1; unsigned char addr16 : 1; unsigned char : 3; unsigned char multiP : 1; unsigned char VS1 : 1; unsigned char encServ : 1; unsigned char : 1; unsigned char VS2 : 1; unsigned char cmdQue : 1; unsigned char : 2; unsigned char sync : 1; unsigned char wbus16 : 1; unsigned char : 2; char T10Vendor[8]; char prodId[16]; char prodRevLvl[4]; char vendorSpecific1[20]; unsigned char IUS : 1; unsigned char QAS : 1; unsigned char clocking : 2; unsigned char : 4; unsigned char reserved1; unsigned char versionDescriptor[8][2]; unsigned char reserved2[22]; unsigned char vendorSpecific2[1]; }; /** * Oracle T10K Inquiry data */ class inquiryDataT10k_t { public: inquiryDataT10k_t () { zeroStruct(this); } unsigned char perifDevType : 5; unsigned char perifQualifyer : 3; unsigned char : 7; unsigned char RMB : 1; unsigned char version; unsigned char respDataFmt : 4; unsigned char HiSup : 1; unsigned char normACA : 1; unsigned char RSVD1: 1; unsigned char AERC: 1; unsigned char addLength; unsigned char protect : 1; unsigned char : 2; unsigned char threePC : 1; unsigned char TPGS : 2; unsigned char ACC : 1; unsigned char SCCS : 1; unsigned char : 3; unsigned char mChngr: 1; unsigned char multiP : 1; unsigned char VS1 : 1; unsigned char encServ : 1; unsigned char bQue : 1; unsigned char VS2 : 1; unsigned char cmdQue : 1; unsigned char RSVD2: 1; unsigned char linked: 1; unsigned char : 3; unsigned char relAdr : 1; char vendorId[8]; char prodId[16]; char prodRevLvl[8]; char vendorSpecific1[14]; unsigned char keyMgmt; unsigned char CSL : 1; unsigned char DCMP : 1; unsigned char volSafe : 1; unsigned char libAtt : 1; unsigned char encr : 1; unsigned char : 3; unsigned char reserved1[2]; unsigned char versionDescriptor[8][2]; }; /** * Inquiry unit serial number vital product data as described in SPC-4. */ class inquiryUnitSerialNumberData_t { public: inquiryUnitSerialNumberData_t() { zeroStruct(this); } // byte 0 unsigned char peripheralDeviceType: 5; // (000b) connected to this LUN unsigned char peripheralQualifier : 3; // (01h) tape drive // byte 1 unsigned char pageCode; // (80h) Vital Product Data page for serial // byte 2 unsigned char :8; // Reserved // byte 3 unsigned char pageLength; // n-3 // bytes 4-n char productSerialNumber[12]; // 12 bytes for T10000&IBM, 10 for LTO }; /** * LOCATE(10) CDB as described in SSC-3. */ class locate10CDB_t { public: locate10CDB_t() { zeroStruct(this); opCode = SCSI::Commands::LOCATE_10; } // byte 0 unsigned char opCode; // OPERATION CODE (2Bh) // byte 1 unsigned char IMMED : 1; // Immediate unsigned char CP : 1; // Change Partition unsigned char BT : 1; // Block address Type unsigned char : 5; // Reserved // byte 2 unsigned char : 8; // Reserved // bytes 3-6 unsigned char logicalObjectID[4] ; // Logical object identifier or block address // byte 7 unsigned char :8; // Reserved // byte 8 unsigned char partition; // Partition // byte 9 unsigned char control; // Control byte }; /** * READ POSITION CDB as described in SSC-3. */ class readPositionCDB_t { public: readPositionCDB_t() { zeroStruct(this); opCode = SCSI::Commands::READ_POSITION; } // byte 0 unsigned char opCode; // OPERATION CODE (34h) // byte 1 // *note* for T10000 we have BT:1, LONG:1, TCLP:1, Reserved:5 unsigned char serviceAction: 5; // Service action to choice FORM unsigned char : 3; // Reserved // bytes 2-6 unsigned char reserved[5]; // Reserved // bytes 7-8 unsigned char allocationLength[2] ; // used for EXTENDENT FORM // byte 9 unsigned char control; // Control byte }; /** * READ POSITION data format, short form as described in SSC-3. */ class readPositionDataShortForm_t { public: readPositionDataShortForm_t() { zeroStruct(this); } // byte 0 unsigned char BPEW :1; // Beyond Programmable Early Warning unsigned char PERR :1; // Position ERroR unsigned char LOLU :1; // Logical Object Location Unknown or Block Position Unknown(BPU) for T10000 unsigned char :1; // Reserved unsigned char BYCU :1; // BYte Count Unknown unsigned char LOCU :1; // Logical Object Count Unknown or Block Count Unknown(BCU) for T10000 unsigned char EOP :1; // End Of Partition unsigned char BOP :1; // Beginning of Partition // byte 1 unsigned char partitionNumber; // Service action to choice FORM // bytes 2-3 unsigned char reserved[2]; // Reserved // bytes 4-7 unsigned char firstBlockLocation[4]; // First Logical object location in SSC3,IBM,LTO // bytes 8-11 unsigned char lastBlockLocation[4]; // Last Logical object location in SSC3,IBM,LTO // byte 12 unsigned char :8; // Reserved // bytes 13-15 unsigned char blocksInBuffer[3]; // Number of logical objects in object buffer // bytes 16-19 unsigned char bytesInBuffer[4]; // Number if bytes in object buffer }; /** * LOG SELECT CDB as described in SPC-4. */ class logSelectCDB_t { public: logSelectCDB_t() { zeroStruct(this); opCode = SCSI::Commands::LOG_SELECT; } // byte 0 unsigned char opCode; // OPERATION CODE (4Ch) // byte 1 unsigned char SP : 1; // the Save Parameters unsigned char PCR: 1; // the Parameter Code Reset unsigned char : 6; // Reserved // byte 2 unsigned char pageCode: 6; // PAGE CODE unsigned char PC: 2; // the Page Control // byte 3 unsigned char subPageCode; // SUBPAGE CODE (Reserved for T10000) // bytes 4-6 unsigned char reserved[3]; // Reserved // bytes 7-8 unsigned char parameterListLength[2];// PARAMETER LIST LENGTH // byte 9 unsigned char control; // CONTROL }; /** * Log sense CDB as described in SPC-4, */ class logSenseCDB_t { public: logSenseCDB_t() { zeroStruct(this); opCode = SCSI::Commands::LOG_SENSE; } unsigned char opCode; unsigned char SP : 1; unsigned char PPC: 1; unsigned char :6; unsigned char pageCode : 6; unsigned char PC : 2; unsigned char subPageCode; unsigned char reserved; unsigned char parameterPointer[2]; unsigned char allocationLength[2]; unsigned char control; }; /** * Log sense Log Page Parameter Format as described in SPC-4, */ class logSenseParameterHeader_t { public: // bytes 0-1 unsigned char parameterCode [2]; // byte 2 unsigned char formatAndLinking : 2; // reserved and List Parameter bits unsigned char TMC : 2; // Threshold Met Criteria unsigned char ETC : 1; // Enable Threshold Comparison unsigned char TSD : 1; // Target Save Disable unsigned char : 1; // DS Disable Save for T10000 unsigned char DU : 1; // Disable Update // byte 3 unsigned char parameterLength; // n-3 }; class logSenseParameter_t { public: // bytes 0-3 logSenseParameterHeader_t header; // bytes 4-n unsigned char parameterValue[1]; // parameters have variable length /** * Gets the parameter value * * @return The value of the log sense parameter as uint64_t. * If we have a parameter length more than 8 bytes the returning * value is not determined. */ inline uint64_t getU64Value() { union { unsigned char tmp[8]; uint64_t val64; } u; u.tmp[0]=(header.parameterLength>0)?parameterValue[0]:0; u.tmp[1]=(header.parameterLength>1)?parameterValue[1]:0; u.tmp[2]=(header.parameterLength>2)?parameterValue[2]:0; u.tmp[3]=(header.parameterLength>3)?parameterValue[3]:0; u.tmp[4]=(header.parameterLength>4)?parameterValue[4]:0; u.tmp[5]=(header.parameterLength>5)?parameterValue[5]:0; u.tmp[6]=(header.parameterLength>6)?parameterValue[6]:0; u.tmp[7]=(header.parameterLength>7)?parameterValue[7]:0; u.val64 = be64toh(u.val64); return u.val64>>(64-(header.parameterLength<<3)); } /** * Gets the parameter value. * * @return The value of the log sense parameter as int64_t. * If we have a parameter length more than 8 bytes the returning * value is not determined. */ inline int64_t getS64Value() { union { unsigned char tmp[8]; uint64_t val64U; int64_t val64S; } u; u.tmp[0]=(header.parameterLength>0)?parameterValue[0]:0; u.tmp[1]=(header.parameterLength>1)?parameterValue[1]:0; u.tmp[2]=(header.parameterLength>2)?parameterValue[2]:0; u.tmp[3]=(header.parameterLength>3)?parameterValue[3]:0; u.tmp[4]=(header.parameterLength>4)?parameterValue[4]:0; u.tmp[5]=(header.parameterLength>5)?parameterValue[5]:0; u.tmp[6]=(header.parameterLength>6)?parameterValue[6]:0; u.tmp[7]=(header.parameterLength>7)?parameterValue[7]:0; u.val64U = be64toh(u.val64U); return (u.val64S < 0?-(-u.val64S>> (64-(header.parameterLength<<3))): (u.val64S>>(64-(header.parameterLength<<3)))); } }; /** * Log sense Log Page Format as described in SPC-4, */ class logSenseLogPageHeader_t { public: // byte 0 unsigned char pageCode : 6; unsigned char SPF: 1; // the Subpage format unsigned char DS: 1; // the Disable Slave bit // byte 1 unsigned char subPageCode; // bytes 2-3 unsigned char pageLength[2]; // n-3 number of bytes without header }; /** * Log sense Log Page Format as described in SPC-4, */ class logSenseLogPage_t { public: // bytes 0-3 logSenseLogPageHeader_t header; // bytes 4-n logSenseParameter_t parameters [1]; // parameters have variable length }; /** * MODE SENSE(6) CDB as described in SPC-4. */ class modeSense6CDB_t { public: modeSense6CDB_t() { zeroStruct(this); opCode = SCSI::Commands::MODE_SENSE_6; } // byte 0 unsigned char opCode; // OPERATION CODE (1Ah) // byte 1 unsigned char : 3; // Reserved unsigned char DBD : 1; // Disable Block Descriptors unsigned char : 4; // Reserved // byte 2 unsigned char pageCode : 6; // Page code unsigned char PC : 2; // Page Control // byte3 unsigned char subPageCode ; // Subpage code // byte4 unsigned char allocationLength; // The maximum number of bytes to be transferred // byte 5 unsigned char control; // Control byte }; /** * MODE SENSE(6) MODE SELECT(6) parameter header as described in SPC-4. */ class modeParameterHeader6_t { public: // byte 0 unsigned char modeDataLength; // The mode data length does not include itself // byte 1 unsigned char mediumType; // The medium type in the drive // byte 2 /* in SPC-4 we have device-specific parameter byte here * but from all drive specifications the fields are the same * so we use them here. */ unsigned char speed : 4; // Read/write speed unsigned char bufferedMode : 3; // Returns after data is in the buffer or on the medium unsigned char WP : 1; // Write Protect // byte3 unsigned char blockDescriptorLength ; // (08h) or (00h) }; /** * MODE SENSE(6,10) and MODE SELECT(6,10) block descriptor as described in SPC-4. */ class modeParameterBlockDecriptor_t { public: // byte 0 unsigned char densityCode; // Density code // bytes 1-3 unsigned char numberOfBlocks[3]; // Number of block or block count // byte 4 unsigned char : 8; // Reserved // bytes 5-7 unsigned char blockLength[3] ; // Block length }; /** * MODE SENSE(6) or MODE SENSE(10) mode page 10h: Device Configuration. * There is no description in SPC-4 or SSC-3. * We use descriptions from: * IBM System Storage Tape Drive 3592 SCSI Reference, * Sun StorageTekTM T10000 Tape Drive Fibre Channel Interface Reference Manual, * IBM TotalStorage LTO Ultrium Tape Drive SCSI Reference. */ class modePageDeviceConfiguration_t { public: // byte 0 unsigned char pageCode :6; // Page code (10h) unsigned char SPF :1; // SubPage Format (0b) unsigned char PS :1; // Parameters Savable // byte 1 unsigned char pageLength; // (0Eh) // byte 2 unsigned char activeFormat : 5; // Active Format unsigned char CAF : 1; // Change Active Format unsigned char CAP : 1; // Change Active Partition unsigned char : 1; // Reserved // byte 3 unsigned char activePartition ; // Active Partition // byte 4 unsigned char writeBufferFullRatio; // Write object buffer full ratio // byte 5 unsigned char readBufferEmptyRatio; // Read object buffer empty ratio // bytes 6-7 unsigned char writeDelayTime[2]; // Write delay time in 100ms for IBM, LTO and in sec for T1000 // byte 8 unsigned char REW : 1; // Report Early Warning unsigned char RBO : 1; // Recover Buffer Order unsigned char SOCF: 2; // Stop On Consecutive Filemarks unsigned char AVC : 1; // Automatic Velocity Control unsigned char RSMK : 1; // Report SetMarKs (obsolete for IBM,LTO) unsigned char LOIS : 1; // Logical Object ID Supported or Block IDs Supported for T10000 unsigned char OBR : 1; // Object Buffer Recovery or Data Buffer Recovery for T10000 // byte 9 unsigned char gapSize; // Obsolete for IBM, LTO // byte 10 unsigned char BAM : 1; // Block Address Mode or reserved for T10000 unsigned char BAML: 1; // Block Address Mode Lock or reserved for T10000 unsigned char SWP : 1; // Soft Write Protect unsigned char SEW : 1; // Synchronize at Early Warning unsigned char EEG : 1; // EOD Enabled Generation unsigned char eodDefined :3; // End Of Data // bytes 11-13 unsigned char bufSizeAtEarlyWarning[3]; // Object buffer size at early warning // byte 14 unsigned char selectDataComprAlgorithm; // Select data compression algorithm // byte 15 unsigned char PRMWP : 1; // PeRManent Write Protect unsigned char PERSWP : 1; // PERSistent Write Protect unsigned char ASOCWP : 1; // ASsOCiated Write Protect unsigned char rewindOnReset : 2; // Reserved for T10000 unsigned char OIR : 1; // Only If Reserved or reserved for T10000 unsigned char WTRE : 2; // WORM Tamper Read Enable }; class modeSenseDeviceConfiguration_t { public: modeSenseDeviceConfiguration_t() { zeroStruct(this); } modeParameterHeader6_t header; modeParameterBlockDecriptor_t blockDescriptor; modePageDeviceConfiguration_t modePage; }; /** * MODE SENSE(6) or MODE SENSE(10) mode page 0Ah: Control Data Protection. * as described in SSC-5. */ class modePageControlDataProtection_t { public: // byte 0 unsigned char pageCode :6; // Page code (0Ah) unsigned char SPF :1; // SubPage Format (1b) unsigned char PS :1; // Parameters Savable // 0b required for MODE SELECT IBM,LTO // 1b returned in MODE SENSE IBM, LTO // 0b Not supported for T10000 // byte 1 unsigned char subpageCode; // SubPage code (F0h) // bytes 2-3 unsigned char pageLength[2]; // Page length (n - 3) 1Ch for IBM,LTO // byte 4 unsigned char LBPMethod; // LBP method // byte 5 unsigned char LBPInformationLength : 6; // LBP information length unsigned char : 2; // Reserved // byte 6 unsigned char : 5; // Reserved unsigned char RBDP : 1; // Recover Buffered Data Protected unsigned char LBP_R : 1; // Logical blocks protected during read unsigned char LBP_W : 1; // Logical blocks protected during write // byte 7 unsigned char : 4; // Reserved unsigned char T10PIexponent : 4; // T1000 only for T10 PI mode // bytes 8-31 unsigned char reserved[24]; // Reserved. Added for IBM, LTO and do // not used by T10000 }; /** * MODE SENSE(6) structure for mode page 0Ah: Control Data Protection. * as described in SSC-5. */ class modeSenseControlDataProtection_t { public: modeSenseControlDataProtection_t() { zeroStruct(this); } modeParameterHeader6_t header; modeParameterBlockDecriptor_t blockDescriptor; modePageControlDataProtection_t modePage; }; /** * MODE SELECT(6) CDB as described in SPC-4. */ class modeSelect6CDB_t { public: modeSelect6CDB_t() { zeroStruct(this); opCode = SCSI::Commands::MODE_SELECT_6; } // byte 0 unsigned char opCode; // OPERATION CODE (15h) // byte 1 unsigned char SP : 1; // Save Parameters unsigned char : 3; // Reserved unsigned char PF : 1; // Page Format unsigned char : 3; // Reserved // bytes 2-3 unsigned char reserved[2]; // Reserved // byte 4 unsigned char paramListLength; // Parameter list length // byte 5 unsigned char control; // Control byte }; /** * TEST UNIT READY as described in SPC-4. */ class testUnitReadyCDB_t { public: testUnitReadyCDB_t() { zeroStruct(this); opCode = SCSI::Commands::TEST_UNIT_READY; } // byte 0 unsigned char opCode; // OPERATION CODE (00h) // byte 1 unsigned char : 8; // Reserved // byte 2 unsigned char EDCC : 1; // Enable Deferred CHECK CONDITION (IBM only) unsigned char : 7; // Reserved // byte 3-4 unsigned char reserverd[2]; // Reserved // byte 5 unsigned char control; // Control byte }; /** * Part of a tape alert log page. * This structure does not need to be initialized, as the containing structure * (tapeAlertLogPage_t) will do it while initializing itself. */ class tapeAlertLogParameter_t { public: unsigned char parameterCode [2]; unsigned char formatAndLinking : 2; unsigned char TMC : 2; unsigned char ETC : 1; unsigned char TSD : 1; unsigned char : 1; unsigned char DU : 1; unsigned char parameterLength; unsigned char flag : 1; unsigned char : 7; }; /** * Tape alert log page, returned by LOG SENSE. Defined in SSC-3, section 8.2.3 TapeAler log page. */ template class tapeAlertLogPage_t { public: tapeAlertLogPage_t() { zeroStruct(this); } unsigned char pageCode : 6; unsigned char : 2; unsigned char subPageCode; unsigned char pageLength[2]; tapeAlertLogParameter_t parameters [n]; /** * Utility function computing the number of parameters. This converts a * length in bytes (as found in the struct) in a parameter count. * @return number of parameters. */ unsigned int parameterNumber() { unsigned int numFromLength = SCSI::Structures::toU16(pageLength) / sizeof (tapeAlertLogParameter_t); return numFromLength; } }; /** * Sense buffer as defined in SPC-4, * section 4.5.2 Descriptor format sense data and * section 4.5.3 Fixed format sense data * The sense buffer size is stored in the form of * a single byte. Therefore, the constructor forbids * creation of a senseData_t structure bigger than * 255 bytes. * As the structure will be different depending on the response code, * everything after the first byte is represented by a union, which * can be any of the 2 forms (fixedFormat/descriptorFormat). * getXXXXX helper member function allow the getting of on of the other * version of the common fields. */ template class senseData_t { public: senseData_t() { if (sizeof(*this) > 255) throw cta::exception::Exception("In SCSI::Structures::senseData_t::senseData_t(): size too big (> 255>"); zeroStruct(this); } // byte 0 unsigned char responseCode: 7; unsigned char : 1; // Following bytes can take 2 versions: union { struct { // byte 1 unsigned char senseKey : 4; unsigned char : 4; // Additional sense code (byte 2)) unsigned char ASC; // Additional sense code qualifier (byte 3) unsigned char ASCQ; // byte 4 unsigned char : 7; unsigned char SDAT_OVFL : 1; // byte 5-6 unsigned char reserved[2]; // byte 7 unsigned char additionalSenseLength; // byte 8 onwards unsigned char additionalSenseBuffer[n - 8]; } descriptorFormat; struct { // byte 1 unsigned char obsolete; // byte 2 unsigned char senseKey : 4; unsigned char SDAT_OVFL : 1; unsigned char ILI : 1; unsigned char EOM : 1; unsigned char filemark : 1; // bytes 3-6 unsigned char information[4]; // byte 7 unsigned char additionalSenseLength; // bytes 8 - 11 unsigned char commandSpecificInformation[4]; // Additional sense code (byte 12)) unsigned char ASC; // Additional sense code qualifier (byte 13) unsigned char ASCQ; // bytes 14 unsigned char fieldReplaceableUnitCode; // bytes 15-17 unsigned char senseSpecificInformation[3]; // bytes 18 onwards unsigned char aditionalSenseBuffer[n - 18]; } fixedFormat; // Helper functions for common fields // First make the difference between the fixed/descriptor // and current/deffered }; bool isFixedFormat() { return responseCode == 0x70 || responseCode == 0x71; } bool isDescriptorFormat() { return responseCode == 0x72 || responseCode == 0x73; } bool isCurrent() { return responseCode == 0x70 || responseCode == 0x72; } bool isDeferred() { return responseCode == 0x71 || responseCode == 0x73; } uint8_t getASC() { if (isFixedFormat()) { return fixedFormat.ASC; } else if (isDescriptorFormat()) { return descriptorFormat.ASC; } else { std::stringstream err; err << "In senseData_t::getASC: no ACS with this response code or response code not supported (" << std::hex << std::showbase << (int)responseCode << ")"; throw cta::exception::Exception(err.str()); } } uint8_t getASCQ() { if (isFixedFormat()) { return fixedFormat.ASCQ; } else if (isDescriptorFormat()) { return descriptorFormat.ASCQ; } else { std::stringstream err; err << "In senseData_t::getASCQ: no ACSQ with this response code or response code not supported (" << std::hex << std::showbase << (int)responseCode << ")"; throw cta::exception::Exception(err.str()); } } /** * Returns the Sense Key value. */ unsigned char getSenseKey() { if (isFixedFormat()) { return fixedFormat.senseKey; } else if (isDescriptorFormat()) { return descriptorFormat.senseKey; } else { std::stringstream err; err << "In senseData_t::getSenseKey: no Sense Key with this response " "code or response code not supported (" << std::hex << std::showbase << (int)responseCode << ")"; throw cta::exception::Exception(err.str()); } } /** * Returns the Sense Key value as string. */ std::string getSenseKeyString() { if ( castor::tape::SCSI::senseKeys::lastWithText >= getSenseKey()) { return castor::tape::SCSI::senseKeys::senseKeysText[getSenseKey()]; } else { std::stringstream err; err << "In senseData_t::getSenseKeyString: no Sense Key with this " "value ("<< std::hex << std::showbase << (int)getSenseKey() << ")"; throw cta::exception::Exception(err.str()); } } /** * Function turning the ACS/ACSQ contents into a string. * This function is taken from the Linux kernel sources. * see scsi_extd_sense_format. * @return the error string as defined by SCSI specifications. */ std::string getACSString() { SCSI::senseConstants sc; uint8_t asc = getASC(); uint8_t ascq = getASCQ(); uint16_t code = (asc << 8) | ascq; for (int i = 0; sc.ascStrings[i].text; i++) if (sc.ascStrings[i].code12 == code) return std::string(sc.ascStrings[i].text); for (int i = 0; sc.ascRangesStrings[i].text; i++) if (sc.ascRangesStrings[i].asc == asc && sc.ascRangesStrings[i].ascq_min <= ascq && sc.ascRangesStrings[i].ascq_max >= ascq) { char buff[100]; snprintf(buff, sizeof (buff), sc.ascRangesStrings[i].text, ascq); return std::string(buff); } char buff[100]; snprintf(buff, sizeof (buff), "Unknown ASC/ASCQ:%02x/%02x", asc, ascq); return std::string(buff); } /* TODO: add support for other bits. See section 4.5.6 * of SPC-4 for sense key = NO SENSE. */ }; /** * READ END OF WRAP POSITION CDB as described in LTO-8 SCSI Reference, p.119 */ class readEndOfWrapPositionCDB_t { public: readEndOfWrapPositionCDB_t() { zeroStruct(this); // REOWP is a service action of the MAINTENANCE_IN command: A3 1F 45 opCode = SCSI::Commands::MAINTENANCE_IN; SERVICE_ACTION = 0x1F; serviceActionQualifier = 0x45; } // byte 0 unsigned char opCode; // OPERATION CODE (A3h) // byte 1 unsigned char SERVICE_ACTION :5; // 1Fh unsigned char :3; // byte 2 unsigned char serviceActionQualifier; // 45h // byte 3 unsigned char WNV :1; // Wrap Number Valid: 0 = request data for first wrap on the tape // 1 = return data for wrap in wrapNumber field unsigned char RA :1; // Report All: 0 = short form reply (return data for a single wrap) // 1 = long form reply (return data for all wraps) unsigned char :6; // byte 4 unsigned char reserved1; // Reserved // byte 5 unsigned char wrapNumber; // Wrap for which the end of wrap position is requested. // If set, WNV must be 1 and RA must be 0. // bytes 6-9 unsigned char allocationLength[4]; // Maximum number of bytes to be transferred // In the case of Report All, each wrap descriptor is 12 // bytes. LTO-9 tapes have 280 wraps, so reply can be up // to 3084 bytes. And longer in case of LTO-10 (details // not available at time of writing). // byte 10 unsigned char reserved2; // Reserved // byte 11 unsigned char control; // Control byte }; /** * REOWP Short form parameter data, as described in LTO-8 SCSI Reference, p.120 */ class readEndOfWrapPositionDataShortForm_t { public: readEndOfWrapPositionDataShortForm_t() { zeroStruct(this); } // bytes 0-1 unsigned char responseDataLength[2]; // 08h, the number of bytes to follow // bytes 2-3 unsigned char reserved[2]; // Reserved // bytes 4-9 unsigned char logicalObjectIdentifier[6]; // The logical object identifier of the object at the // end of the wrap requested by the WNV bit and the // WRAP_NUMBER field }; /** * REOWP Long form parameter data, as described in LTO-8 SCSI Reference, p.120-121 */ class readEndOfWrapPositionDataLongForm_t { public: readEndOfWrapPositionDataLongForm_t() { zeroStruct(this); } // bytes 0-1 unsigned char responseDataLength[2]; // n-1, the number of bytes to follow // bytes 2-3 unsigned char reserved[2]; // Reserved // bytes 4-n struct WrapDescriptor { unsigned char wrapNumber[2]; // Wrap number unsigned char partition[2]; // The partition number of the above wrap unsigned char reserved[2]; // Reserved unsigned char logicalObjectIdentifier[6]; // The logical object identifier of the object at the end of the above wrap } wrapDescriptor[maxLTOTapeWraps]; // Array of wrap descriptiors uint16_t getNbWrapsReturned(){ return ((SCSI::Structures::toU16(responseDataLength) - sizeof(reserved)) / sizeof(WrapDescriptor)); } }; /** * REQUEST SENSE CDB as described in LTO-8 SCSI Reference, p.157 */ class requestSenseCDB_t { public: requestSenseCDB_t() { zeroStruct(this); opCode = SCSI::Commands::REQUEST_SENSE; } // byte 0 unsigned char opCode; // OPERATION CODE (03h) // bytes 1-3 unsigned char reserved[3]; // Reserved // byte 4 unsigned char allocationLength; // Maximum number of bytes to be transferred (up to 96), see 5.2.29 // byte 5 unsigned char control; // Control byte }; /** * REQUEST SENSE data format, as described in LTO-8 SCSI Reference, p.158 */ class requestSenseData_t { public: requestSenseData_t() { zeroStruct(this); } // byte 0 unsigned char RESPONSE_CODE :7; // 70h current, 71h deferred unsigned char VALID :1; // Information bytes 3-6 are valid // byte 1 unsigned char :8; // Obsolete // byte 2 unsigned char SENSE_KEY :4; // See Annex B unsigned char :1; // Reserved unsigned char ILI :1; // Incorrect Length Indicator unsigned char EOM :1; // Device is at end of medium unsigned char FILEMARK :1; // The current command has encountered a filemark // bytes 3-6 unsigned char information[4]; // Valid if VALID bit is set. Generally only used for non-deferred errors. // byte 7 unsigned char additionalSenseLength; // 0Ah only 18 bytes of sense data returned, 58h full 96 bytes of sense data returned // bytes 8-11 unsigned char commandSpecificInformation[4]; // Not supported by LTO-8 drives // byte 12 unsigned char additionalSenseCode; // See Annex B // byte 13 unsigned char additionalSenseCodeQualifier; // See Annex B // byte 14 unsigned char fieldReplacableUnitCode; // Used for extended fault isolation information // byte 15 unsigned char BIT_POINTER :3; // Points to bit in error of the field specified by the FIELD_POINTER unsigned char BPV :1; // Bit Pointer Valid, indicates whether BIT_POINTER contains information unsigned char :2; // Reserved unsigned char C_D :1; // Control/Data, indicates if error is in a data field or CDB field unsigned char SKSV_BIT :1; // Sense Key Specific Valid // bytes 16-17 unsigned char SKSV[2]; // Field Pointer, points to the CDB byte or parameter byte in error // bytes 18-19 unsigned char reportingErrorFlagData[2]; // Reporting Error Flag Data // byte 20 unsigned char :8; // Reserved // byte 21 unsigned char VOLVALID :1; // Indicates if Volume Label and Volume Label Cartridge Type contain valid information unsigned char DUMP :1; // Indicates if a debug dump is present in the drive unsigned char :1; // Reserved unsigned char CLN :1; // Is the device requesting a clean? unsigned char DRVSRVC :1; // Does the drive have a hardware fault causing it to be inoperative? unsigned char :3; // Reserved // bytes 22-28 char volumeLabel[7]; // Seven characters from left of Volume Label // byte 29 unsigned char physicalWrap; // Physical wrap of the current location. LSB reflects current physical direction: // 0b - current direction is away from the physical beginning of tape // 1b - current direction is towards the physical beginning of tape // FFh - logical wrap number exceeds 254, physical direction is not reflected // bytes 30-33 unsigned char relativeLPOSValue[4]; // The current physical position on tape // byte 34 unsigned char SCSIAddress; // Obsolete, use portIdentifier instead // byte 35 unsigned char RS422Information; // May contain a value passed across the RS-422 serial interface by a tape library // byte 36 unsigned char activePartition :3; // Partition number of the current logical position of the volume unsigned char :5; // Reserved // bytes 37-39 unsigned char portIdentifier[3]; // Address of the port through which the sense is reported, fibre channel or SAS // byte 40 unsigned char relativeTgtPort :3; // Relative target port through which sense data is reported unsigned char :3; // Reserved unsigned char tapePartitionsExist :1; // Does the mounted volume contain more than one partition? unsigned char tapeDirectoryValid :1; // Is the tape directory valid? // byte 41 unsigned char hostCommand; // SCSI Opcode of the command to which sense data is being returned // byte 42 unsigned char mediaType :4; // Vendor reserved unsigned char cartridgeGenType :4; // Cartridge generation type, 000b = Gen1, 111b = Gen 8 // bytes 43-44 unsigned char volumeLabelCartridgeType[2]; // Valid if VOLVALID bit is set to 1b. Can be 'L7', 'M8', 'L8', etc. // bytes 45-48 unsigned char logicalBlockNumber[4]; // Current LBA that would be reported in Read Position command // bytes 49-52 unsigned char datasetNumber[4]; // bytes 53-54 unsigned char firstErrorFSC[2]; // bytes 55-56 unsigned char firstErrorFlagData[2]; // bytes 57-58 unsigned char secondErrorFSC[2]; // bytes 59-60 unsigned char secondErrorFlagData[2]; // bytes 61-62 unsigned char nextToLastErrorFSC[2]; // bytes 63-64 unsigned char nextToLastErrorFlagData[2]; // bytes 65-66 unsigned char lastErrorFSC[2]; // bytes 67-68 unsigned char lastErrorFlagData[2]; // byte 69 unsigned char LPOSRegion; // bytes 70-85 char ERPSummaryInformation[16]; // bytes 86-95 char cartridgeSerialNumber[10]; // This is the value from the CRM right-justified, not the Barcode }; namespace encryption { class spinCDB_t { public: spinCDB_t() { zeroStruct(this); opCode = SCSI::Commands::SECURITY_PROTOCOL_IN; } unsigned char opCode; unsigned char securityProtocol; unsigned char securityProtocolSpecific[2]; unsigned char reserved[2]; unsigned char allocationLength[4]; unsigned char reserved2; unsigned char controlByte; }; class spoutCDB_t { public: spoutCDB_t() { zeroStruct(this); opCode = SCSI::Commands::SECURITY_PROTOCOL_OUT; } unsigned char opCode; unsigned char securityProtocol; unsigned char securityProtocolSpecific[2]; unsigned char reserved[2]; unsigned char allocationLength[4]; unsigned char reserved2; unsigned char controlByte; }; template class spinPageList_t { public: spinPageList_t() { zeroStruct(this); } unsigned char reserved[6]; unsigned char supportedProtocolListLength[2]; unsigned char list[n]; }; /** * Security Protocol OUT-Set Data Encryption Page as described in SSC-4. */ class spoutSDEParam_t { public: spoutSDEParam_t() { zeroStruct(this); setU16(pageCode, SCSI::encryption::spoutSecurityProtocolSpecificPages::setDataEncryptionPage); setU16(keyLength, SCSI::encryption::ENC_KEY_LENGTH); } unsigned char pageCode[2]; unsigned char length[2]; unsigned char lock : 1; unsigned char : 4; unsigned char nexusScope : 3; // Specifies the scope of the data encryption parameters unsigned char CKORL : 1; // Clear key on reservation loss unsigned char CKORP : 1; // Clear key on reservation preempt unsigned char CKOD : 1; // Clear key on reservation demount unsigned char SDK : 1; // Supplemental decryption key unsigned char RDMC : 2; // Raw decryption mode control unsigned char CEEM : 2; // Check external encryption mode unsigned char encryptionMode; unsigned char decryptionMode; unsigned char algorithmIndex; unsigned char keyFormat; unsigned char kadFormat; unsigned char reserved[7]; unsigned char keyLength[2]; unsigned char keyData[SCSI::encryption::ENC_KEY_LENGTH]; }; } namespace RAO { /** * Receive RAO Command Descriptor Block (CDB) */ class recieveRAO_t { public: recieveRAO_t() { zeroStruct(this); opcode = SCSI::Commands::MAINTENANCE_IN; } unsigned char opcode; unsigned char serviceAction :5; unsigned char :2; unsigned char udsLimits :1; unsigned char raoListOffset[4]; unsigned char allocationLength[4]; unsigned char udsType :3; unsigned char :5; unsigned char control; }; /** * UDS (User Data Segments) limits page */ class udsLimitsPage_t { public: udsLimitsPage_t() { zeroStruct(this); } unsigned char maxSupported[2]; unsigned char maxSize[2]; }; class udsLimits { public: uint16_t maxSupported; uint16_t maxSize; }; /** * Generate RAO CDB */ class generateRAO_t { public: generateRAO_t() { zeroStruct(this); opcode = SCSI::Commands::MAINTENANCE_OUT; raoProcess = 2; } unsigned char opcode; unsigned char serviceAction :5; unsigned char :3; unsigned char raoProcess :3; unsigned char :5; unsigned char udsType :3; unsigned char :5; unsigned char reserved[2]; unsigned char paramsListLength[4]; unsigned char reserved2; unsigned char control; }; class udsDescriptor_t { public: udsDescriptor_t() { zeroStruct(this); setU16(descriptorLength, 0x1e); } unsigned char descriptorLength[2]; unsigned char reserved[3]; unsigned char udsName[10]; unsigned char partitionNumber; unsigned char beginLogicalObjID[8]; unsigned char endLogicalObjID[8]; }; /** * RAO list struct */ class raoList_t { public: raoList_t() { zeroStruct(this); } unsigned char raoProcess :3; unsigned char :5; unsigned char status :3; unsigned char :5; unsigned char res[2]; unsigned char raoDescriptorListLength[4]; udsDescriptor_t udsDescriptors[1]; }; /** * Generate RAO parameters */ class generateRAOParams_t { public: generateRAOParams_t() { zeroStruct(this); } unsigned char res[4]; unsigned char udsListLength[4]; udsDescriptor_t udsDescriptors[1]; }; /** * Block Limits */ class blockLims { public: blockLims() { zeroStruct(this); } unsigned char fseq[10]; uint64_t begin; uint64_t end; }; } template /** * Extract a string from a fixed size array. This function * gets rid of zeros in array and stops the extracted string * there. In SCSI, the arrays are space padded, so the string * should have a size equal to n usually. This function is templated * to manage the fixed-size array in the SCSI structures conveniently. * @param t array pointer to the char array. * @return the extracted string. */ std::string toString(const char(& t)[n]) { std::stringstream r; r.write(t, std::find(t, t + n, '\0') - t); return r.str(); } std::string toString(const inquiryData_t &); template std::string hexDump(const unsigned char(& d)[n]) { std::stringstream hex; hex << std::hex << std::setfill('0'); size_t pos = 0; while (pos < (8* (n / 8))) { hex << std::setw(4) << pos << " | "; for (int i=0; i<8; i++) hex << std::setw(2) << ((int) d[pos + i]) << " "; hex << "| "; for (int i=0; i<8; i++) hex << std::setw(0) << d[pos + i]; hex << std::endl; pos += 8; } if (n % 8) { hex << std::setw(4) << pos << " | "; for (size_t i=0; i<(n % 8); i++) hex << std::setw(2) << ((int) d[pos + i]) << " "; for (size_t i=(n % 8); i<8; i++) hex << " "; hex << "| "; for (size_t i=0; i<(n % 8); i++) hex << std::setw(0) << d[pos + i]; hex << std::endl; } return hex.str(); } } // namespace Structures } // namespace SCSI } // namespace tape } // namespace castor