DriveGeneric.cpp 104 KB
Newer Older
1
2
/*
 * @project        The CERN Tape Archive (CTA)
3
 * @copyright      Copyright(C) 2003-2021 CERN
4
5
6
7
 * @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.
8
 *
9
10
11
12
 *                 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.
13
 *
14
15
16
 *                 You should have received a copy of the GNU General Public License
 *                 along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
Eric Cano's avatar
Eric Cano committed
17

18
19
20
#include "castor/tape/tapeserver/drive/DriveInterface.hpp"
#include "castor/tape/tapeserver/drive/FakeDrive.hpp"
#include "castor/tape/tapeserver/drive/DriveGeneric.hpp"
21

Victor Kotlyar's avatar
Victor Kotlyar committed
22
#include "common/Timer.hpp"
23
#include "common/CRC.hpp"
24
#include "common/exception/MemException.hpp"
25

26
27
#include <errno.h>

28
29
#include <string>
#include <map>
Cristina Moraru's avatar
Cristina Moraru committed
30
#include <list>
31

32
33
34
namespace castor {
namespace tape {
namespace tapeserver {
35

36
drive::DriveInterface * drive::createDrive(SCSI::DeviceInfo di, 
37
38
39
    System::virtualWrapper& sw) {
  if (std::string::npos != di.product.find("T10000")) {
    return new DriveT10000(di, sw);
Victor Kotlyar's avatar
Victor Kotlyar committed
40
  } else if (std::string::npos != di.product.find("ULT") || std::string::npos != di.product.find("Ultrium")) {
41
42
43
    return new DriveLTO(di, sw);
  } else if (std::string::npos != di.product.find("03592")) {
    return new DriveIBM3592(di, sw);
44
45
  } else if (std::string::npos != di.product.find("MHVTL")) {
    return new DriveMHVTL(di, sw);
46
  } else if (std::string::npos != di.product.find("VIRTUAL")) {
47
48
    /* In case of a VIRTUAL drive, it could have been pre-allocated 
     * for testing purposes (with "pre-cooked" contents). */
49
    drive::DriveInterface * ret = sw.getDriveByPath(di.nst_dev);
50
51
52
53
54
    if (ret) {
      return ret;
    } else {
      return new FakeDrive();
    }
55
  } else {
56
    throw cta::exception::Exception(std::string("Unsupported drive type: ") + di.product);
57
58
59
  }
}

60
drive::DriveGeneric::DriveGeneric(SCSI::DeviceInfo di, System::virtualWrapper& sw) : m_SCSIInfo(di),
61
m_tapeFD(-1),  m_sysWrapper(sw), m_lbpToUse(lbpToUse::disabled) {
62
63
64
65
  /* Open the device files */
  /* We open the tape device file non-blocking as blocking open on rewind tapes (at least)
   * will fail after a long timeout when no tape is present (at least with mhvtl) 
   */
66
  cta::exception::Errnum::throwOnMinusOne(
67
68
      m_tapeFD = m_sysWrapper.open(m_SCSIInfo.nst_dev.c_str(), O_RDWR | O_NONBLOCK),
      std::string("Could not open device file: ") + m_SCSIInfo.nst_dev);
69
70
}

71
void drive::DriveIBM3592::clearCompressionStats()  {
72
73
74
  SCSI::Structures::logSelectCDB_t cdb;
  cdb.PCR = 1; /* PCR set */
  cdb.PC = 0x3; /* PC = 11b  for T10000 only*/
75
  cdb.pageCode = SCSI::logSensePages::blockBytesTransferred;
76
77
78
79
80
81
82
83
84

  SCSI::Structures::senseData_t<255> senseBuff;
  SCSI::Structures::LinuxSGIO_t sgh;

  sgh.setCDB(&cdb);
  sgh.setSenseBuffer(&senseBuff);
  sgh.dxfer_direction = SG_DXFER_NONE;

  /* Manage both system error and SCSI errors. */
85
  cta::exception::Errnum::throwOnMinusOne(
86
87
      m_sysWrapper.ioctl(m_tapeFD, SG_IO, &sgh),
      "Failed SG_IO ioctl in DriveGeneric::clearCompressionStats");
88
89
90
  SCSI::ExceptionLauncher(sgh, "SCSI error in clearCompressionStats:");
}

91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
void drive::DriveT10000::clearCompressionStats() {
  m_compressionStatsBase = getCompressionStats();
}

void drive::DriveLTO::clearCompressionStats() {
  SCSI::Structures::logSelectCDB_t cdb;
  cdb.PCR = 1; /* PCR set */
  cdb.PC = 0x3; /* PC = 11b  for T10000 only*/
  cdb.pageCode = SCSI::logSensePages::dataCompression32h;

  SCSI::Structures::senseData_t<255> senseBuff;
  SCSI::Structures::LinuxSGIO_t sgh;

  sgh.setCDB(&cdb);
  sgh.setSenseBuffer(&senseBuff);
  sgh.dxfer_direction = SG_DXFER_NONE;

  /* Manage both system error and SCSI errors. */
  cta::exception::Errnum::throwOnMinusOne(
    m_sysWrapper.ioctl(m_tapeFD, SG_IO, &sgh),
    "Failed SG_IO ioctl in DriveGeneric::clearCompressionStats");
  SCSI::ExceptionLauncher(sgh, "SCSI error in clearCompressionStats:");
}

115
116
117
118
/**
 * Information about the drive. The vendor id is used in the user labels of the files.
 * @return    The deviceInfo structure with the information about the drive.
 */
119
drive::deviceInfo drive::DriveGeneric::getDeviceInfo()  {
120
121
122
123
124
125
  SCSI::Structures::inquiryCDB_t cdb;
  SCSI::Structures::inquiryData_t inquiryData;
  SCSI::Structures::senseData_t<255> senseBuff;
  SCSI::Structures::LinuxSGIO_t sgh;
  deviceInfo devInfo;

126
127
  SCSI::Structures::setU16(cdb.allocationLength, sizeof(inquiryData));
  
128
129
130
131
132
133
  sgh.setCDB(&cdb);
  sgh.setDataBuffer(&inquiryData);
  sgh.setSenseBuffer(&senseBuff);
  sgh.dxfer_direction = SG_DXFER_FROM_DEV;

  /* Manage both system error and SCSI errors. */
134
  cta::exception::Errnum::throwOnMinusOne(
135
136
      m_sysWrapper.ioctl(m_tapeFD, SG_IO, &sgh),
      "Failed SG_IO ioctl in DriveGeneric::getDeviceInfo");
137
  SCSI::ExceptionLauncher(sgh, "SCSI error in getDeviceInfo:");
138
139
140
141
142

  devInfo.product = SCSI::Structures::toString(inquiryData.prodId);
  devInfo.productRevisionLevel = SCSI::Structures::toString(inquiryData.prodRevLvl);
  devInfo.vendor = SCSI::Structures::toString(inquiryData.T10Vendor);
  devInfo.serialNumber = getSerialNumber();
143
  devInfo.isPIsupported = inquiryData.protect;
144
145
146
  return devInfo;
}

147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
drive::deviceInfo drive::DriveT10000::getDeviceInfo()  {
  SCSI::Structures::inquiryCDB_t cdb;
  SCSI::Structures::inquiryData_t inquiryData;
  SCSI::Structures::senseData_t<255> senseBuff;
  SCSI::Structures::LinuxSGIO_t sgh;
  deviceInfo devInfo;

  SCSI::Structures::setU16(cdb.allocationLength, sizeof(inquiryData));

  sgh.setCDB(&cdb);
  sgh.setDataBuffer(&inquiryData);
  sgh.setSenseBuffer(&senseBuff);
  sgh.dxfer_direction = SG_DXFER_FROM_DEV;

  /* Manage both system error and SCSI errors. */
  cta::exception::Errnum::throwOnMinusOne(
    m_sysWrapper.ioctl(m_tapeFD, SG_IO, &sgh),
    "Failed SG_IO ioctl in DriveT10000::getDeviceInfo");
  SCSI::ExceptionLauncher(sgh, "SCSI error in getDeviceInfo:");

  devInfo.product = SCSI::Structures::toString(inquiryData.prodId);
  std::string productRevisionMinor = SCSI::Structures::toString(inquiryData.vendorSpecific1).substr(0,4);
  devInfo.productRevisionLevel = SCSI::Structures::toString(inquiryData.prodRevLvl) + productRevisionMinor;
  devInfo.vendor = SCSI::Structures::toString(inquiryData.T10Vendor);
  devInfo.serialNumber = getSerialNumber();
  devInfo.isPIsupported = inquiryData.protect;
  return devInfo;
}

drive::deviceInfo drive::DriveMHVTL::getDeviceInfo()  {
  SCSI::Structures::inquiryCDB_t cdb;
  SCSI::Structures::inquiryData_t inquiryData;
  SCSI::Structures::senseData_t<255> senseBuff;
  SCSI::Structures::LinuxSGIO_t sgh;
  deviceInfo devInfo;

  SCSI::Structures::setU16(cdb.allocationLength, sizeof(inquiryData));

  sgh.setCDB(&cdb);
  sgh.setDataBuffer(&inquiryData);
  sgh.setSenseBuffer(&senseBuff);
  sgh.dxfer_direction = SG_DXFER_FROM_DEV;

  /* Manage both system error and SCSI errors. */
  cta::exception::Errnum::throwOnMinusOne(
    m_sysWrapper.ioctl(m_tapeFD, SG_IO, &sgh),
    "Failed SG_IO ioctl in DriveMHVTL::getDeviceInfo");
  SCSI::ExceptionLauncher(sgh, "SCSI error in getDeviceInfo:");

  devInfo.product = SCSI::Structures::toString(inquiryData.prodId);
  devInfo.productRevisionLevel = SCSI::Structures::toString(inquiryData.prodRevLvl);
  devInfo.vendor = SCSI::Structures::toString(inquiryData.T10Vendor);
  devInfo.serialNumber = getSerialNumber();
  devInfo.isPIsupported = inquiryData.protect;
  return devInfo;
}

204
SCSI::Structures::RAO::udsLimits drive::DriveMHVTL::getLimitUDS(){
205
  throw drive::DriveDoesNotSupportRAOException("MHVTL does not support RAO Enterprise.");
206
207
}

208
void drive::DriveMHVTL::queryRAO(std::list<SCSI::Structures::RAO::blockLims> &files, int maxSupported){
209
210
211
212
  //The query RAO method of MHVTL drive returns nothing
  //something could be implemented for testing...
}

213
214
215
216
217
218
219
220
/**
 * Generic SCSI path, used for passing to external scripts.
 * @return    Path to the generic SCSI device file.
 */
std::string drive::DriveGeneric::getGenericSCSIPath() {
  return m_SCSIInfo.sg_dev;
}

221
222
223
224
/**
 * Information about the serial number of the drive. 
 * @return   Right-aligned ASCII data for the vendor-assigned serial number.
 */
225
std::string drive::DriveGeneric::getSerialNumber()  {
226
227
228
229
230
231
232
  SCSI::Structures::inquiryCDB_t cdb;
  SCSI::Structures::inquiryUnitSerialNumberData_t inquirySerialData;
  SCSI::Structures::senseData_t<255> senseBuff;
  SCSI::Structures::LinuxSGIO_t sgh;

  cdb.EVPD = 1; /* Enable Vital Product Data */
  cdb.pageCode = SCSI::inquiryVPDPages::unitSerialNumber;
233
  SCSI::Structures::setU16(cdb.allocationLength, sizeof(inquirySerialData));
234
235
236
237
238
239
240

  sgh.setCDB(&cdb);
  sgh.setDataBuffer(&inquirySerialData);
  sgh.setSenseBuffer(&senseBuff);
  sgh.dxfer_direction = SG_DXFER_FROM_DEV;

  /* Manage both system error and SCSI errors. */
241
  cta::exception::Errnum::throwOnMinusOne(
242
243
      m_sysWrapper.ioctl(m_tapeFD, SG_IO, &sgh),
      "Failed SG_IO ioctl in DriveGeneric::getSerialNumber");
244
  SCSI::ExceptionLauncher(sgh, "SCSI error in getSerialNumber:");
245
246
247
248
249
250
251
252
253
254
255
256
257
  std::string serialNumber;
  serialNumber.append(inquirySerialData.productSerialNumber, inquirySerialData.pageLength);

  return serialNumber;
}

/**
 * Position to logical object identifier (i.e. block address). 
 * This function is blocking: the immediate bit is not set.
 * The device server will not return status until the locate operation
 * has completed.
 * @param blockId The blockId, represented in local endianness.
 */
258
void drive::DriveGeneric::positionToLogicalObject(uint32_t blockId)
259
 {
260
261
  SCSI::Structures::locate10CDB_t cdb;
  SCSI::Structures::senseData_t<255> senseBuff;
262
263
264
  SCSI::Structures::LinuxSGIO_t sgh; 
  
  SCSI::Structures::setU32(cdb.logicalObjectID, blockId);
265
266
267
268

  sgh.setCDB(&cdb);
  sgh.setSenseBuffer(&senseBuff);
  sgh.dxfer_direction = SG_DXFER_NONE;
269
  //sgh.timeout = defaultTimeout; // set globally by SCSI::Structures.hpp (defaultTimeout)
270
271

  /* Manage both system error and SCSI errors. */
272
  cta::exception::Errnum::throwOnMinusOne(
273
274
      m_sysWrapper.ioctl(m_tapeFD, SG_IO, &sgh),
      "Failed SG_IO ioctl in DriveGeneric::positionToLogicalObject");
275
276
277
278
279
280
281
282
283
  SCSI::ExceptionLauncher(sgh, "SCSI error in positionToLogicalObject:");
}

/**
 * Return logical position of the drive. This is the address of the next object
 * to read or write.
 * @return positionInfo class. This contains the logical position, plus information
 * on the dirty data still in the write buffer.
 */
284
drive::positionInfo drive::DriveGeneric::getPositionInfo()
285
 {
286
287
288
289
290
291
  SCSI::Structures::readPositionCDB_t cdb;
  SCSI::Structures::readPositionDataShortForm_t positionData;
  SCSI::Structures::senseData_t<255> senseBuff;
  SCSI::Structures::LinuxSGIO_t sgh;

  positionInfo posInfo;
292
  
293
294
295
  // We just go all defaults: service action = 00 (SHORT FORM BLOCK ID)
  // The result will come in fixed size and allocation length must be 0.
  // At least IBM drives will complain otherwise
296
  
297
298
299
300
301
302
  sgh.setCDB(&cdb);
  sgh.setDataBuffer(&positionData);
  sgh.setSenseBuffer(&senseBuff);
  sgh.dxfer_direction = SG_DXFER_FROM_DEV;

  /* Manage both system error and SCSI errors. */
303
  cta::exception::Errnum::throwOnMinusOne(
304
305
      m_sysWrapper.ioctl(m_tapeFD, SG_IO, &sgh),
      "Failed SG_IO ioctl in DriveGeneric::getPositionInfo");
306
  SCSI::ExceptionLauncher(sgh, "SCSI error in getPositionInfo:");
307
308
309
310
311
312
313
314
315
316
317
318
319

  if (0 == positionData.PERR) { // Location fields are valid
    posInfo.currentPosition = SCSI::Structures::toU32(positionData.firstBlockLocation);
    posInfo.oldestDirtyObject = SCSI::Structures::toU32(positionData.lastBlockLocation);
    posInfo.dirtyObjectsCount = SCSI::Structures::toU32(positionData.blocksInBuffer);
    posInfo.dirtyBytesCount = SCSI::Structures::toU32(positionData.bytesInBuffer);
  } else {
    /* An overflow has occurred in at least one of the returned position
     * data fields. The application should use the LONG FORM to obtain the 
     * current position or the application should use the EXTENDED FORM to
     * obtain the current position and number of bytes in the object buffer.
     * (note) For T10000 we have only SHORT FORM.
     */
320
    throw cta::exception::Exception(std::string("An overflow has occurred in getPostitionInfo"));
321
322
323
324
  }
  return posInfo;
}

325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
/**
 * Return physical position of the drive.
 *
 * @return physicalPositionInfo class. This contains the wrap and linear position (LPOS).
 */
drive::physicalPositionInfo drive::DriveGeneric::getPhysicalPositionInfo()
{
  SCSI::Structures::requestSenseCDB_t cdb;
  SCSI::Structures::requestSenseData_t requestSenseData;
  SCSI::Structures::senseData_t<255> senseBuff;
  SCSI::Structures::LinuxSGIO_t sgh;

  // The full Request Sense data record is 96 bytes. However, we are only interested in
  // the PHYSICAL WRAP and LPOS fields (bytes 29-33) so we can discard the rest.
  cdb.allocationLength = 34;

  sgh.setCDB(&cdb);
  sgh.setDataBuffer(&requestSenseData);
  sgh.setSenseBuffer(&senseBuff);
  sgh.dxfer_direction = SG_DXFER_FROM_DEV;

  // Manage both system error and SCSI errors
  cta::exception::Errnum::throwOnMinusOne(
    m_sysWrapper.ioctl(m_tapeFD, SG_IO, &sgh),
    "Failed SG_IO ioctl in DriveGeneric::getPhysicalPositionInfo");
  SCSI::ExceptionLauncher(sgh, "SCSI error in getPhysicalPositionInfo:");

  physicalPositionInfo posInfo;
  posInfo.wrap = requestSenseData.physicalWrap;
  posInfo.lpos = SCSI::Structures::toU32(requestSenseData.relativeLPOSValue);
  return posInfo;
}

358
359
360
361
362
363
/**
* Returns all the end of wrap positions of the mounted tape
* 
* @return a vector of endOfWrapsPositions. 
*/
std::vector<drive::endOfWrapPosition> drive::DriveGeneric::getEndOfWrapPositions() {
364
  throw cta::exception::Exception("In drive::DriveGeneric::getEndOfWrapPositions(), the drive does not support REOWP SCSI command.");
365
366
}

367
368
/**
 * Get tape alert information from the drive. There is a quite long list of possible tape alerts.
Eric Cano's avatar
Eric Cano committed
369
 * They are described in SSC-4, section 4.2.20: TapeAlert application client interface.
370
371
 * Section is 4.2.17 in SSC-3. This version gives a list of numerical codes.
 * @return list of tape alerts codes.
372
 */
373
std::vector<uint16_t> drive::DriveGeneric::getTapeAlertCodes(){
374
  /* return vector */
375
  std::vector<uint16_t> ret;
376
377
378
379
380
  /* We don't know how many elements we'll get. Prepare a 100 parameters array */
  SCSI::Structures::tapeAlertLogPage_t<100> tal;
  /* Prepare a sense buffer of 255 bytes */
  SCSI::Structures::senseData_t<255> senseBuff;
  SCSI::Structures::logSenseCDB_t cdb;
381
382
  SCSI::Structures::LinuxSGIO_t sgh;
  
383
  cdb.pageCode = SCSI::logSensePages::tapeAlert;
Eric Cano's avatar
Eric Cano committed
384
  cdb.PC = 0x01; // Current Comulative Values
385
386
  SCSI::Structures::setU16(cdb.allocationLength, sizeof(tal));
  
387
388
389
390
391
  sgh.setCDB(&cdb);
  sgh.setDataBuffer(&tal);
  sgh.setSenseBuffer(&senseBuff);
  sgh.dxfer_direction = SG_DXFER_FROM_DEV;
  /* Manage both system error and SCSI errors. */
392
  cta::exception::Errnum::throwOnMinusOne(
393
394
      m_sysWrapper.ioctl(m_tapeFD, SG_IO, &sgh),
      "Failed SG_IO ioctl in DriveGeneric::getTapeAlerts");
395
  SCSI::ExceptionLauncher(sgh, "SCSI error in getTapeAlerts:");
396
397
398
399
400
  /* Return the ACTIVE tape alerts (this is indicated by "flag" (see 
   * SSC-4: 8.2.3 TapeAlert log page). As they are simply used for logging;
   * return strings. */
  for (size_t i = 0; i < tal.parameterNumber(); i++) {
    if (tal.parameters[i].flag)
401
402
403
404
405
406
      ret.push_back(SCSI::Structures::toU16(tal.parameters[i].parameterCode));
  }
  return ret;
}

/**
407
 * Translate  tape alert codes into strings.
408
 */
409
std::vector<std::string> drive::DriveGeneric::getTapeAlerts(const std::vector<uint16_t>& tacs){
410
411
412
  /* return vector */
  std::vector<std::string> ret;
  /* convert tape alert codes to strings */
413
  for (std::vector<uint16_t>::const_iterator code =  tacs.begin(); code!= tacs.end(); code++) {
414
415
416
417
418
419
    ret.push_back(SCSI::tapeAlertToString(*code));
  }
  return ret;
}

/**
420
 * Translate tape alert codes into compact strings.
421
 */
422
std::vector<std::string> drive::DriveGeneric::getTapeAlertsCompact(const std::vector<uint16_t>& tacs){
423
424
425
  /* return vector */
  std::vector<std::string> ret;
  /* convert tape alert codes to strings */
426
  for (std::vector<uint16_t>::const_iterator code =  tacs.begin(); code!= tacs.end(); code++) {
427
    ret.push_back(SCSI::tapeAlertToCompactString(*code));
428
429
430
431
  }
  return ret;
}

432
433
434
435
436
437
438
439
440
441
442
443
444
445
//------------------------------------------------------------------------------
// tapeAlertsCriticalForWrite
//------------------------------------------------------------------------------
bool drive::DriveGeneric::tapeAlertsCriticalForWrite(
  const std::vector<uint16_t> & codes) {
  for (std::vector<uint16_t>::const_iterator code =  codes.begin(); 
    code!= codes.end(); code++) {
      if(SCSI::isTapeAlertCriticalForWrite(*code)) {
        return true;
      }
  }
  return false;
}

446
447
448
449
450
451
452
453
454
455
456
457
458
/**
 * Set the tape density and compression. 
 * We use MODE SENSE/SELECT Device Configuration (10h) mode page.
 * As soon as there is no definition in SPC-4 or SSC-3 it depends on the 
 * drives documentation. 
 * 
 * @param densityCode  The tape specific density code.
 *                     If it is 0 (default) than we use the density code 
 *                     detected by the drive itself means no changes.
 *                                
 * @param compression  The boolean variable to enable or disable compression
 *                     on the drive for the tape. By default it is enabled.
 */
459
void drive::DriveGeneric::setDensityAndCompression(bool compression,
460
    unsigned char densityCode)  {
461
462
463
464
465
466
467
  SCSI::Structures::modeSenseDeviceConfiguration_t devConfig;
  { // get info from the drive
    SCSI::Structures::modeSense6CDB_t cdb;
    SCSI::Structures::senseData_t<255> senseBuff;
    SCSI::Structures::LinuxSGIO_t sgh;

    cdb.pageCode = SCSI::modeSensePages::deviceConfiguration;
468
    cdb.allocationLength = sizeof (devConfig);
469
470
471
472
473
474
475

    sgh.setCDB(&cdb);
    sgh.setDataBuffer(&devConfig);
    sgh.setSenseBuffer(&senseBuff);
    sgh.dxfer_direction = SG_DXFER_FROM_DEV;

    /* Manage both system error and SCSI errors. */
476
    cta::exception::Errnum::throwOnMinusOne(
477
478
        m_sysWrapper.ioctl(m_tapeFD, SG_IO, &sgh),
        "Failed SG_IO ioctl in DriveGeneric::setDensityAndCompression");
479
    SCSI::ExceptionLauncher(sgh, "SCSI error in setDensityAndCompression:");
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
  }

  { // set parameters and we use filled structure devConfig from the previous SCSI call
    SCSI::Structures::modeSelect6CDB_t cdb;
    SCSI::Structures::senseData_t<255> senseBuff;
    SCSI::Structures::LinuxSGIO_t sgh;

    cdb.PF = 1; // means nothing for IBM, LTO, T10000
    cdb.paramListLength = sizeof (devConfig);

    devConfig.header.modeDataLength = 0; // must be 0 for IBM, LTO ignored by T10000
    if (0 != densityCode) devConfig.blockDescriptor.densityCode = densityCode;
    if (compression) devConfig.modePage.selectDataComprAlgorithm = 1;
    else devConfig.modePage.selectDataComprAlgorithm = 0;

    sgh.setCDB(&cdb);
    sgh.setDataBuffer(&devConfig);
    sgh.setSenseBuffer(&senseBuff);
    sgh.dxfer_direction = SG_DXFER_TO_DEV;

    /* Manage both system error and SCSI errors. */
501
    cta::exception::Errnum::throwOnMinusOne(
502
503
        m_sysWrapper.ioctl(m_tapeFD, SG_IO, &sgh),
        "Failed SG_IO ioctl in DriveGeneric::setDensityAndCompression");
504
    SCSI::ExceptionLauncher(sgh, "SCSI error in setDensityAndCompression:");
505
506
507
  }
}

508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
//------------------------------------------------------------------------------
// setLogicalBlockProtection
//------------------------------------------------------------------------------
void drive::DriveGeneric::setLogicalBlockProtection(
  const unsigned char method, const unsigned char methodLength,
  const bool enableLBPforRead, const bool enableLBPforWrite) {

  SCSI::Structures::modeSenseControlDataProtection_t controlDataProtection;
  {
    /* fetch Control Data Protection */
    SCSI::Structures::modeSense6CDB_t cdb;
    SCSI::Structures::senseData_t<255> senseBuff;
    SCSI::Structures::LinuxSGIO_t sgh;

    cdb.pageCode = SCSI::modeSensePages::controlDataProtection;
    cdb.subPageCode = SCSI::modePageControlDataProtection::subpageCode;
    cdb.allocationLength = sizeof (controlDataProtection);

    sgh.setCDB(&cdb);
    sgh.setDataBuffer(&controlDataProtection);
    sgh.setSenseBuffer(&senseBuff);
    sgh.dxfer_direction = SG_DXFER_FROM_DEV;

    /* Manage both system error and SCSI errors. */
532
    cta::exception::Errnum::throwOnMinusOne(
533
534
535
536
537
538
539
540
541
542
543
544
545
      m_sysWrapper.ioctl(m_tapeFD, SG_IO, &sgh),
      "Failed SG_IO ioctl"  );     
    SCSI::ExceptionLauncher(sgh,
      std::string("SCSI error fetching data in setLogicalBlockProtection: ") +
      SCSI::statusToString(sgh.status));
  }

  {
    /* Use the previously fetched page, modify fields and submit it */
    SCSI::Structures::modeSelect6CDB_t cdb;
    SCSI::Structures::senseData_t<255> senseBuff;
    SCSI::Structures::LinuxSGIO_t sgh;

546
547
548
549
550
551
552
    cdb.PF = 1; // means nothing for IBM, LTO, T10000
    cdb.paramListLength = sizeof (controlDataProtection.header) +
      sizeof (controlDataProtection.blockDescriptor) +
      SCSI::controlDataProtectionModePageLengthAddition + 
      SCSI::Structures::toU16(controlDataProtection.modePage.pageLength);    
    if (cdb.paramListLength > sizeof(controlDataProtection)) {
      // should never happen 
553
      throw cta::exception::Exception(
554
555
556
        std::string("cdb.paramListLength greater then size "
                    "of controlDataProtection in setLogicalBlockProtection"));
    }
557
558
559
560
    controlDataProtection.header.modeDataLength = 0; // must be 0 for IBM, LTO 
                                                     // ignored by T10000
    controlDataProtection.modePage.LBPMethod = method;
    controlDataProtection.modePage.LBPInformationLength = methodLength;
561
562
    controlDataProtection.modePage.LBP_W = enableLBPforWrite;
    controlDataProtection.modePage.LBP_R = enableLBPforRead;
563
564
565
566
567
568
569

    sgh.setCDB(&cdb);
    sgh.setDataBuffer(&controlDataProtection);
    sgh.setSenseBuffer(&senseBuff);
    sgh.dxfer_direction = SG_DXFER_TO_DEV;

    /* Manage both system error and SCSI errors. */
570
    cta::exception::Errnum::throwOnMinusOne(   
571
572
573
574
575
576
577
578
579
580
581
582
    m_sysWrapper.ioctl(m_tapeFD, SG_IO, &sgh),
    "Failed SG_IO ioctl"  );     
    SCSI::ExceptionLauncher(sgh,
            std::string("SCSI error setting data in setDataProtection : ") +
            SCSI::statusToString(sgh.status));
  }
}

//------------------------------------------------------------------------------
// enableCRC32CLogicalBlockProtectionReadOnly
//------------------------------------------------------------------------------
void drive::DriveGeneric::enableCRC32CLogicalBlockProtectionReadOnly() {
583
  m_lbpToUse=lbpToUse::crc32cReadOnly;
584
585
  setLogicalBlockProtection(SCSI::logicBlockProtectionMethod::CRC32C,
    SCSI::logicBlockProtectionMethod::CRC32CLength,true,false);
586
}
Daniele Kruse's avatar
Daniele Kruse committed
587

588
589
590
591
//------------------------------------------------------------------------------
// enableCRC32CLogicalBlockProtectionReadWrite
//------------------------------------------------------------------------------
void drive::DriveGeneric::enableCRC32CLogicalBlockProtectionReadWrite() {
592
  m_lbpToUse=lbpToUse::crc32cReadWrite;
593
594
  setLogicalBlockProtection(SCSI::logicBlockProtectionMethod::CRC32C,
    SCSI::logicBlockProtectionMethod::CRC32CLength,true,true);
595
596
}

597
598
599
600
//------------------------------------------------------------------------------
// disableLogicalBlockProtection
//------------------------------------------------------------------------------
void drive::DriveGeneric::disableLogicalBlockProtection() {
601
  m_lbpToUse=lbpToUse::disabled;
602
603
  setLogicalBlockProtection(0,0,false,false);      
}
604
605
606
607
608
609
610

//------------------------------------------------------------------------------
// getLBPInfo
//------------------------------------------------------------------------------
drive::LBPInfo drive::DriveGeneric::getLBPInfo() {
  SCSI::Structures::modeSenseControlDataProtection_t controlDataProtection;
  drive::LBPInfo LBPdata;
611
  
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
  /* fetch Control Data Protection */
  SCSI::Structures::modeSense6CDB_t cdb;
  SCSI::Structures::senseData_t<255> senseBuff;
  SCSI::Structures::LinuxSGIO_t sgh;

  cdb.pageCode = SCSI::modeSensePages::controlDataProtection;
  cdb.subPageCode = SCSI::modePageControlDataProtection::subpageCode;
  cdb.allocationLength = sizeof (controlDataProtection);

  sgh.setCDB(&cdb);
  sgh.setDataBuffer(&controlDataProtection);
  sgh.setSenseBuffer(&senseBuff);
  sgh.dxfer_direction = SG_DXFER_FROM_DEV;

  /* Manage both system error and SCSI errors. */
627
  cta::exception::Errnum::throwOnMinusOne(
628
629
630
631
632
633
634
635
636
637
638
639
640
641
    m_sysWrapper.ioctl(m_tapeFD, SG_IO, &sgh),
    "Failed SG_IO ioctl"  );     
  SCSI::ExceptionLauncher(sgh,
    std::string("SCSI error fetching data in getLBPInfo: ") +
    SCSI::statusToString(sgh.status));

  LBPdata.method = controlDataProtection.modePage.LBPMethod;
  LBPdata.methodLength = controlDataProtection.modePage.LBPInformationLength;  
  LBPdata.enableLBPforRead = (1 == controlDataProtection.modePage.LBP_R);
  LBPdata.enableLBPforWrite = (1 == controlDataProtection.modePage.LBP_W);

  return LBPdata;
}

642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
//------------------------------------------------------------------------------
// Encryption interface
//------------------------------------------------------------------------------
void drive::DriveGeneric::setEncryptionKey(const std::string &encryption_key) {
  if(!isEncryptionCapEnabled())
    throw cta::exception::Exception("In DriveGeneric::setEncryptionKey: Tried to enable encryption on drive "
                                    "without encryption capabilities enabled.");
  if (encryption_key.empty())
    clearEncryptionKey();
  else {
    SCSI::Structures::LinuxSGIO_t sgh;
    SCSI::Structures::encryption::spoutCDB_t cdb;
    SCSI::Structures::senseData_t<255> senseBuff;
    SCSI::Structures::encryption::spoutSDEParam_t sps;

    SCSI::Structures::setU16(sps.pageCode, SCSI::encryption::spoutSecurityProtocolSpecificPages::setDataEncryptionPage);
    /*
     * Length is size of the spoutSDEParam_t - (sizeof(sps.pageCode) + sizeof(sps.length))
     */
    SCSI::Structures::setU16(sps.length, sizeof(SCSI::Structures::encryption::spoutSDEParam_t) - 4);

    sps.nexusScope = SCSI::encryption::encryptionNexusScopes::scopeLocal; // oracle compatibility
    sps.encryptionMode = SCSI::encryption::encryptionModes::modeEncrypt;
    sps.decryptionMode = SCSI::encryption::decryptionModes::modeMixed;
    sps.algorithmIndex = 0x01;
    sps.keyFormat = SCSI::encryption::keyFormatTypes::keyFormatNormal;
    strncpy((char *)sps.keyData, encryption_key.c_str(), SCSI::encryption::ENC_KEY_LENGTH);
    /*
     * This means that if the key given is smaller, it's right padded with zeros.
     * If the key given is bigger, we the first 256-bit are used.
     */

    cdb.securityProtocol = SCSI::encryption::spoutSecurityProtocolPages::tapeDataEncryption;
    SCSI::Structures::setU16(cdb.securityProtocolSpecific,
                             SCSI::encryption::spoutSecurityProtocolSpecificPages::setDataEncryptionPage);
    SCSI::Structures::setU32(cdb.allocationLength, sizeof(SCSI::Structures::encryption::spoutSDEParam_t));

    sgh.setCDB(&cdb);
    sgh.setDataBuffer(&sps);
    sgh.setSenseBuffer(&senseBuff);
    sgh.dxfer_direction = SG_DXFER_TO_DEV;

    cta::exception::Errnum::throwOnMinusOne(
      m_sysWrapper.ioctl(this->m_tapeFD, SG_IO, &sgh),
      "Failed SG_IO ioctl in DriveGeneric::setEncryptionKey");
    SCSI::ExceptionLauncher(sgh, "SCSI error in DriveGeneric::setEncryptionKey");
  }
}

bool drive::DriveGeneric::clearEncryptionKey() {
  if (!isEncryptionCapEnabled())
    return false;
  SCSI::Structures::LinuxSGIO_t sgh;
  SCSI::Structures::encryption::spoutCDB_t cdb;
  SCSI::Structures::senseData_t<255> senseBuff;
  SCSI::Structures::encryption::spoutSDEParam_t sps;

  SCSI::Structures::setU16(sps.pageCode, SCSI::encryption::spoutSecurityProtocolSpecificPages::setDataEncryptionPage);

  sps.nexusScope = SCSI::encryption::encryptionNexusScopes::scopeLocal; // oracle compatibility
  sps.encryptionMode = SCSI::encryption::encryptionModes::modeDisable;
  sps.decryptionMode = SCSI::encryption::decryptionModes::modeDisable;
  sps.algorithmIndex = 0x01;
  sps.keyFormat = SCSI::encryption::keyFormatTypes::keyFormatNormal;

  /*
   * Length is size of the spoutSDEParam_t - (sizeof(sps.pageCode) + sizeof(sps.length))
   */
  SCSI::Structures::setU16(sps.length, sizeof(SCSI::Structures::encryption::spoutSDEParam_t) - 4);

  cdb.securityProtocol = SCSI::encryption::spoutSecurityProtocolPages::tapeDataEncryption;
  SCSI::Structures::setU16(cdb.securityProtocolSpecific, SCSI::encryption::spoutSecurityProtocolSpecificPages::setDataEncryptionPage);
  SCSI::Structures::setU32(cdb.allocationLength, sizeof(SCSI::Structures::encryption::spoutSDEParam_t));


  sgh.setCDB(&cdb);
  sgh.setDataBuffer(&sps);
  sgh.setSenseBuffer(&senseBuff);
  sgh.dxfer_direction = SG_DXFER_TO_DEV;

  cta::exception::Errnum::throwOnMinusOne(
    m_sysWrapper.ioctl(this->m_tapeFD, SG_IO, &sgh),
    "Failed SG_IO ioctl in DriveGeneric::clearEncryptionKey");
  SCSI::ExceptionLauncher(sgh, "SCSI error in DriveGeneric::clearEncryptionKey");
  return true;
}

bool drive::DriveGeneric::isEncryptionCapEnabled() {
  return false;
}

bool drive::DriveIBM3592::isEncryptionCapEnabled() {
  /*
   * We are acquiring the encryption capability from the length of the SPIN index page.
   * If it has one page (only 0x00), then encryption is disabled from the libary.
   * If it has more pages, the encryption(0x00, 0x20 at the moment of writing), then it is enabled.
   */
  SCSI::Structures::LinuxSGIO_t sgh;
  SCSI::Structures::encryption::spinCDB_t cdb;
  SCSI::Structures::senseData_t<255> senseBuff;
  SCSI::Structures::encryption::spinPageList_t<20> pl;

  cdb.securityProtocol = SCSI::encryption::spinSecurityProtocolPages::securityProtocolInformation;
  SCSI::Structures::setU16(cdb.securityProtocolSpecific, 0x0000);
  SCSI::Structures::setU32(cdb.allocationLength, sizeof(pl));

  sgh.setCDB(&cdb);
  sgh.setDataBuffer(&pl);
  sgh.setSenseBuffer(&senseBuff);
  sgh.dxfer_direction = SG_DXFER_FROM_DEV;

  cta::exception::Errnum::throwOnMinusOne(
    m_sysWrapper.ioctl(this->m_tapeFD, SG_IO, &sgh),
    "Failed SG_IO ioctl in DriveIBM3592::isEncryptionCapEnabled");
  SCSI::ExceptionLauncher(sgh, "SCSI error in DriveIBM3592::clearEncryptionKey");

  return SCSI::Structures::toU16(pl.supportedProtocolListLength) > 1;
}

bool drive::DriveT10000::isEncryptionCapEnabled() {
  /*
   * We are acquiring the encryption capability from the inquiry page on Oracle T10k drives
   * because the SPIN index page is not available when encryption is disabled from
   * the Oracle libary interface (invalid cdb error).
   */
  SCSI::Structures::LinuxSGIO_t sgh;
  SCSI::Structures::inquiryCDB_t cdb;
  SCSI::Structures::senseData_t<255> senseBuff;
  SCSI::Structures::inquiryDataT10k_t inqData;

  // EVPD and page code set to zero => standard inquiry
  SCSI::Structures::setU16(cdb.allocationLength, sizeof(inqData));

  sgh.setCDB(&cdb);
  sgh.setDataBuffer(&inqData);
  sgh.setSenseBuffer(&senseBuff);
  sgh.dxfer_direction = SG_DXFER_FROM_DEV;

  cta::exception::Errnum::throwOnMinusOne(
    m_sysWrapper.ioctl(this->m_tapeFD, SG_IO, &sgh),
    "Failed SG_IO ioctl in DriveT10000::isEncryptionCapEnabled");
  SCSI::ExceptionLauncher(sgh, "SCSI error in DriveT10000::clearEncryptionKey");

  unsigned char keyManagement = inqData.keyMgmt; // oracle metric for key management

  return keyManagement != 0x0;
}

void drive::DriveMHVTL::setEncryptionKey(const std::string &encryption_key) {
  throw cta::exception::Exception("In DriveMHVTL::setEncryptionKey: Encryption cannot be enabled.");
}

bool drive::DriveMHVTL::clearEncryptionKey() {
  return false;
}

bool drive::DriveMHVTL::isEncryptionCapEnabled() {
  return false;
}

Cristina Moraru's avatar
Cristina Moraru committed
802
SCSI::Structures::RAO::udsLimits drive::DriveGeneric::getLimitUDS() {
803
804
805
    SCSI::Structures::LinuxSGIO_t sgh;
    SCSI::Structures::RAO::recieveRAO_t cdb;
    SCSI::Structures::senseData_t<127> senseBuff;
Cristina Moraru's avatar
Cristina Moraru committed
806
807
    SCSI::Structures::RAO::udsLimitsPage_t limitsSCSI;
    SCSI::Structures::RAO::udsLimits lims;
808

Cristina Moraru's avatar
Cristina Moraru committed
809
    cdb.serviceAction = 0x1d;
810
    cdb.udsLimits = 1;
Cristina Moraru's avatar
Cristina Moraru committed
811
    SCSI::Structures::setU32(cdb.allocationLength, sizeof(SCSI::Structures::RAO::udsLimitsPage_t));
Cristina Moraru's avatar
Cristina Moraru committed
812
    
813
814
    sgh.setCDB(&cdb);
    sgh.setSenseBuffer(&senseBuff);
Cristina Moraru's avatar
Cristina Moraru committed
815
    sgh.setDataBuffer(&limitsSCSI);
816
817
818
819
820
821
822
823
    sgh.dxfer_direction = SG_DXFER_FROM_DEV;

    /* Manage both system error and SCSI errors. */
    cta::exception::Errnum::throwOnMinusOne(
    m_sysWrapper.ioctl(this->m_tapeFD, SG_IO, &sgh),
            "Failed SG_IO ioctl in DriveGeneric::getLimitUDS");
    SCSI::ExceptionLauncher(sgh, "SCSI error in DriveGeneric::getLimitUDS");

Cristina Moraru's avatar
Cristina Moraru committed
824
825
826
827
    lims.maxSupported = SCSI::Structures::toU16(limitsSCSI.maxSupported);
    lims.maxSize = SCSI::Structures::toU16(limitsSCSI.maxSize);

    return lims;
828
829
}

Cristina Moraru's avatar
Cristina Moraru committed
830
831
void drive::DriveGeneric::generateRAO(std::list<SCSI::Structures::RAO::blockLims> &files,
                                      int maxSupported) {
832
833
834
835
    SCSI::Structures::LinuxSGIO_t sgh;
    SCSI::Structures::RAO::generateRAO_t cdb;
    SCSI::Structures::senseData_t<127> senseBuff;

Cristina Moraru's avatar
Cristina Moraru committed
836
837
    int udSize = std::min((int) files.size(), maxSupported);
    
838
    std::unique_ptr<SCSI::Structures::RAO::udsDescriptor_t[]>  ud (new SCSI::Structures::RAO::udsDescriptor_t[udSize]());
Cristina Moraru's avatar
Cristina Moraru committed
839
840
841
842
843
844
    
    auto it = files.begin();
    for (int i = 0; i < udSize; ++i) {
      strncpy((char*)ud.get()[i].udsName, (char*)it->fseq, 10);
      SCSI::Structures::setU64(ud.get()[i].beginLogicalObjID, it->begin);
      SCSI::Structures::setU64(ud.get()[i].endLogicalObjID, it->end);
845
846
847
848
      ++it;
    }

    SCSI::Structures::RAO::generateRAOParams_t params;
849
    int real_params_len = sizeof(params) + (udSize - 1) *
850
851
                          sizeof(SCSI::Structures::RAO::udsDescriptor_t);
    std::unique_ptr<unsigned char[]>  dataBuff (new unsigned char[real_params_len]());
852

Cristina Moraru's avatar
Cristina Moraru committed
853
    cdb.serviceAction = 0x1d;
854
855

    SCSI::Structures::setU32(cdb.paramsListLength, real_params_len);
856
857
858
    SCSI::Structures::setU32(params.udsListLength, udSize * sizeof(SCSI::Structures::RAO::udsDescriptor_t));
    memcpy(dataBuff.get(), &params, 8); // copy first 2 fields
    memcpy(dataBuff.get() + 8, ud.get(), udSize * sizeof(SCSI::Structures::RAO::udsDescriptor_t));
859
860
861
    
    sgh.setCDB(&cdb);
    sgh.setSenseBuffer(&senseBuff);
Cristina Moraru's avatar
Cristina Moraru committed
862
    sgh.setDataBuffer(dataBuff.get(), real_params_len);
863
864
865
866
867
868
869
870
871
    sgh.dxfer_direction = SG_DXFER_TO_DEV;

    /* Manage both system error and SCSI errors. */
    cta::exception::Errnum::throwOnMinusOne(
    m_sysWrapper.ioctl(this->m_tapeFD, SG_IO, &sgh),
            "Failed SG_IO ioctl in DriveGeneric::requestRAO");
    SCSI::ExceptionLauncher(sgh, "SCSI error in DriveGeneric::requestRAO");
}

Cristina Moraru's avatar
Cristina Moraru committed
872
void drive::DriveGeneric::receiveRAO(std::list<SCSI::Structures::RAO::blockLims> &files) {
873
874
875
    SCSI::Structures::LinuxSGIO_t sgh;
    SCSI::Structures::RAO::recieveRAO_t cdb;
    SCSI::Structures::senseData_t<255> senseBuff;
876
877
878
879
880
881
882
    SCSI::Structures::RAO::raoList_t *params;

    int udSize = files.size();
    int real_params_len = sizeof(SCSI::Structures::RAO::raoList_t) + (udSize - 1) *
                          sizeof(SCSI::Structures::RAO::udsDescriptor_t);
    std::unique_ptr<unsigned char[]>  dataBuff (new unsigned char[real_params_len]());
    memset(dataBuff.get(), 0, real_params_len);
883
884

    cdb.udsLimits = 0;
Cristina Moraru's avatar
Cristina Moraru committed
885
    cdb.serviceAction = 0x1d;
886

Cristina Moraru's avatar
Cristina Moraru committed
887
    SCSI::Structures::setU32(cdb.allocationLength, real_params_len);
888
889
890

    sgh.setCDB(&cdb);
    sgh.setSenseBuffer(&senseBuff);
Cristina Moraru's avatar
Cristina Moraru committed
891
    sgh.setDataBuffer(dataBuff.get(), real_params_len);
892
893
894
895
896
897
898
899
    sgh.dxfer_direction = SG_DXFER_FROM_DEV;

    /* Manage both system error and SCSI errors. */
    cta::exception::Errnum::throwOnMinusOne(
    m_sysWrapper.ioctl(this->m_tapeFD, SG_IO, &sgh),
            "Failed SG_IO ioctl in DriveGeneric::getRAO");
    SCSI::ExceptionLauncher(sgh, "SCSI error in DriveGeneric::getRAO");

900
    params = (SCSI::Structures::RAO::raoList_t *) dataBuff.get();
901

902
903
904
905
906
    files.clear();
    uint32_t desc_list_len = SCSI::Structures::toU32(params->raoDescriptorListLength);
    int files_no = desc_list_len / sizeof(SCSI::Structures::RAO::udsDescriptor_t);
    SCSI::Structures::RAO::udsDescriptor_t *ud = params->udsDescriptors;
    while (files_no > 0) {
907
        SCSI::Structures::RAO::blockLims bl;
908
909
910
        strncpy((char*)bl.fseq, (char*)ud->udsName, 10);
        bl.begin = SCSI::Structures::toU64(ud->beginLogicalObjID);
        bl.end = SCSI::Structures::toU64(ud->endLogicalObjID);
911
        files.emplace_back(bl);
912
913
        ud++;
        files_no--;
914
915
916
    }
}

917
void drive::DriveGeneric::queryRAO(std::list<SCSI::Structures::RAO::blockLims> &files,
918
                                   int maxSupported) {
919
    generateRAO(files, maxSupported);
Cristina Moraru's avatar
Cristina Moraru committed
920
    receiveRAO(files);
921
922
}

Daniele Kruse's avatar
Daniele Kruse committed
923
924
925
926
/**
 * Function that checks if a tape is blank (contains no records) 
 * @return true if tape is blank, false otherwise
 */      
927
bool drive::DriveGeneric::isTapeBlank() {
Daniele Kruse's avatar
Daniele Kruse committed
928
929
930
931
932
933
934
935
  struct mtop mtCmd1;
  mtCmd1.mt_op = MTREW;
  mtCmd1.mt_count = 1;
  
  struct mtop mtCmd2;
  mtCmd2.mt_op = MTFSR;
  mtCmd2.mt_count = 1;
 
936
937
  struct mtget mtInfo;
  
938
939
  if((0 == m_sysWrapper.ioctl(m_tapeFD, MTIOCTOP, &mtCmd1)) && (0 != m_sysWrapper.ioctl(m_tapeFD, MTIOCTOP, &mtCmd2))) {
    //we are doing it the old CASTOR way (see readlbl.c)
940
941
    if(m_sysWrapper.ioctl(m_tapeFD, MTIOCGET, &mtInfo)>=0) {
      if(GMT_EOD(mtInfo.mt_gstat) && GMT_BOT(mtInfo.mt_gstat)) {
Daniele Kruse's avatar
Daniele Kruse committed
942
943
944
945
946
947
948
949
950
951
952
953
954
        return true;
      }
    }    
  }
  
  struct mtop mtCmd3;
  mtCmd3.mt_op = MTREW;
  mtCmd3.mt_count = 1;
  m_sysWrapper.ioctl(m_tapeFD, MTIOCTOP, &mtCmd3);
  
  return false;
}

955
956
957
958
959
960
/**
 * Set the buffer write switch in the st driver. This is directly matching a configuration
 * parameter in CASTOR, so this function has to be public and usable by a higher level
 * layer, unless the parameter turns out to be disused.
 * @param bufWrite: value of the buffer write switch
 */
961
void drive::DriveGeneric::setSTBufferWrite(bool bufWrite)  {
962
963
964
  struct mtop m_mtCmd;
  m_mtCmd.mt_op = MTSETDRVBUFFER;
  m_mtCmd.mt_count = bufWrite ? (MT_ST_SETBOOLEANS | MT_ST_BUFFER_WRITES) : (MT_ST_CLEARBOOLEANS | MT_ST_BUFFER_WRITES);
965
  cta::exception::Errnum::throwOnMinusOne(
966
967
     m_sysWrapper.ioctl(m_tapeFD, MTIOCTOP, &m_mtCmd),
     "Failed ST ioctl (MTSETDRVBUFFER) in DriveGeneric::setSTBufferWrite");
968
969
970
971
972
973
974
975
976
977
}

/**
 * Jump to end of recorded media. This will use setSTFastMTEOM() to disable MT_ST_FAST_MTEOM.
 * (See TapeServer's handbook for details). This is used to rebuild the MIR (StorageTek)
 * or tape directory (IBM).
 * Tape directory rebuild is described only for IBM but currently applied to 
 * all tape drives.
 * TODO: synchronous? Timeout?
 */    
978
void drive::DriveGeneric::spaceToEOM(void)  {
979
980
981
982
  setSTFastMTEOM(false);
  struct mtop m_mtCmd;
  m_mtCmd.mt_op = MTEOM;
  m_mtCmd.mt_count = 1;
983
  cta::exception::Errnum::throwOnMinusOne(
984
985
     m_sysWrapper.ioctl(m_tapeFD, MTIOCTOP, &m_mtCmd),
     "Failed ST ioctl (MTEOM) in DriveGeneric::spaceToEOM");
986
987
988
989
990
991
992
993
}

/**
 * Set the MTFastEOM option of the ST driver. This function is used only internally in 
 * mounttape (in CAStor), so it could be a private function, not visible to 
 * the higher levels of the software (TODO: protected?).
 * @param fastMTEOM the option switch.
 */
994
void drive::DriveGeneric::setSTFastMTEOM(bool fastMTEOM)  {
995
996
997
  struct mtop m_mtCmd;
  m_mtCmd.mt_op = MTSETDRVBUFFER;
  m_mtCmd.mt_count = fastMTEOM ? (MT_ST_SETBOOLEANS | MT_ST_FAST_MTEOM) : (MT_ST_CLEARBOOLEANS | MT_ST_FAST_MTEOM);
998
  cta::exception::Errnum::throwOnMinusOne(
999
1000
     m_sysWrapper.ioctl(m_tapeFD, MTIOCTOP, &m_mtCmd),
     "Failed ST ioctl (MTSETDRVBUFFER) in DriveGeneric::setSTFastMTEOM");
1001
1002
1003
1004
1005
1006
}

/**
 * Jump to end of data. EOM in ST driver jargon, end of data (which is more accurate)
 * in SCSI terminology). This uses the fast setting (not to be used for MIR rebuild) 
 */
1007
void drive::DriveGeneric::fastSpaceToEOM(void)  {
1008
1009
1010
1011
  setSTFastMTEOM(true);
  struct mtop m_mtCmd;
  m_mtCmd.mt_op = MTEOM;
  m_mtCmd.mt_count = 1;
1012
  cta::exception::Errnum::throwOnMinusOne(
1013
1014
      m_sysWrapper.ioctl(m_tapeFD, MTIOCTOP, &m_mtCmd),
      "Failed ST ioctl (MTEOM) in DriveGeneric::fastSpaceToEOM");
1015
1016
1017
1018
1019
}

/**
 * Rewind tape.
 */
1020
void drive::DriveGeneric::rewind(void)  {
1021
1022
1023
  struct mtop m_mtCmd;
  m_mtCmd.mt_op = MTREW;
  m_mtCmd.mt_count = 1;
1024
  cta::exception::Errnum::throwOnMinusOne(
1025
1026
      m_sysWrapper.ioctl(m_tapeFD, MTIOCTOP, &m_mtCmd),
      "Failed ST ioctl (MTREW) in DriveGeneric::rewind");
1027
1028
1029
}

/**
1030
 * Space count file marks backwards.
1031
1032
 * @param count
 */
1033
void drive::DriveGeneric::spaceFileMarksBackwards(size_t count)  {
1034
  size_t tobeskipped = count;
1035
1036
  struct mtop m_mtCmd;
  m_mtCmd.mt_op = MTBSF;
1037
1038
1039
  while (tobeskipped > 0) {
    size_t c = (tobeskipped > 0x7FFFFF) ? 0x7FFFFF : tobeskipped;
    m_mtCmd.mt_count = (int)c;
1040
    cta::exception::Errnum::throwOnMinusOne(
1041
1042
    m_sysWrapper.ioctl(m_tapeFD, MTIOCTOP, &m_mtCmd), 
    "Failed ST ioctl (MTBSF) in DriveGeneric::spaceFileMarksBackwards");
1043
1044
    tobeskipped -= c;
  }  
1045
1046
1047
}

/**
1048
 * Space count file marks forward.
1049
1050
 * @param count
 */
1051
void drive::DriveGeneric::spaceFileMarksForward(size_t count)  {
1052
  size_t tobeskipped = count;
1053
1054
  struct mtop m_mtCmd;
  m_mtCmd.mt_op = MTFSF;
1055
1056
1057
  while (tobeskipped > 0) {
    size_t c = (tobeskipped > 0x7FFFFF) ? 0x7FFFFF : tobeskipped;
    m_mtCmd.mt_count = (int)c;
1058
    cta::exception::Errnum::throwOnMinusOne(
1059
1060
      m_sysWrapper.ioctl(m_tapeFD, MTIOCTOP, &m_mtCmd), 
      "Failed ST ioctl (MTFSF) in DriveGeneric::spaceFileMarksForward");
1061
1062
    tobeskipped -= c;
  }  
1063
1064
1065
1066
1067
}

/**
 * Unload the tape.
 */
1068
void drive::DriveGeneric::unloadTape(void)  {
1069
1070
1071
  struct mtop m_mtCmd;
  m_mtCmd.mt_op = MTUNLOAD;
  m_mtCmd.mt_count = 1;
1072
  cta::exception::Errnum::throwOnMinusOne(
1073
1074
      m_sysWrapper.ioctl(m_tapeFD, MTIOCTOP, &m_mtCmd),
      "Failed ST ioctl (MTUNLOAD) in DriveGeneric::unloadTape");
1075
1076
1077
1078
}

/**
 * Synch call to the tape drive. This function will not return before the 
1079
 * data in the drive's buffer is actually committed to the medium.
1080
 */
1081
void drive::DriveGeneric::flush(void)  {
1082
  struct mtop m_mtCmd;
1083
1084
1085
1086
  m_mtCmd.mt_op = MTWEOF; //Not using MTNOP because it doesn't do what it claims (see st source code) so here we put "write sync file marks" with count set to 0.
  // The following text is a quote from the SCSI Stream commands manual (SSC-3):
  // NOTE 25 Upon completion of any buffered write operation, the application client may issue a WRITE FILEMARKS(16) command with the IMMED bit set to zero and the FILEMARK COUNT field set to zero to perform a synchronize operation (see 4.2.10).
  m_mtCmd.mt_count = 0;
1087
  cta::exception::Errnum::throwOnMinusOne(
1088
      m_sysWrapper.ioctl(m_tapeFD, MTIOCTOP, &m_mtCmd),
1089
      "Failed ST ioctl (MTWEOF) in DriveGeneric::flush");
1090
1091
1092
1093
1094
1095
1096
}

/**
 * Write count file marks. The function does not return before the file marks 
 * are committed to medium.
 * @param count
 */
1097
void drive::DriveGeneric::writeSyncFileMarks(size_t count)  {
1098
1099
1100
  struct mtop m_mtCmd;
  m_mtCmd.mt_op = MTWEOF;
  m_mtCmd.mt_count = (int)count;
1101
  cta::exception::Errnum::throwOnMinusOne(
1102
1103
      m_sysWrapper.ioctl(m_tapeFD, MTIOCTOP, &m_mtCmd),
      "Failed ST ioctl (MTWEOF) in DriveGeneric::writeSyncFileMarks");
1104
1105
1106
1107
1108
1109
1110
}

/**
 * Write count file marks asynchronously. The file marks are just added to the drive's
 * buffer and the function return immediately.
 * @param count
 */
1111
void drive::DriveGeneric::writeImmediateFileMarks(size_t count)  {
1112
1113
1114
  struct mtop m_mtCmd;
  m_mtCmd.mt_op = MTWEOFI; //Undocumented in "man st" needs the mtio_add.hh header file (see above)
  m_mtCmd.mt_count = (int)count;
1115
  cta::exception::Errnum::throwOnMinusOne(
1116
1117
      m_sysWrapper.ioctl(m_tapeFD, MTIOCTOP, &m_mtCmd),
      "Failed ST ioctl (MTWEOFI) in DriveGeneric::writeImmediateFileMarks");
1118
1119
1120
1121
1122
1123
1124
}

/**
 * Write a data block to tape.
 * @param data pointer the the data block
 * @param count size of the data block
 */
1125
void drive::DriveGeneric::writeBlock(const void * data, size_t count)  {
1126
1127
1128
1129
1130
1131
  switch (m_lbpToUse) {
    case lbpToUse::crc32cReadWrite:
      {
        uint8_t * dataWithCrc32c =(new (std::nothrow)
          uint8_t [count+SCSI::logicBlockProtectionMethod::CRC32CLength]);
        if(NULL == dataWithCrc32c) {
1132
          throw cta::exception::MemException("Failed to allocate memory "
1133
1134
1135
1136
            " for a new MemBlock in DriveGeneric::writeBlock!");
        }
        memcpy(dataWithCrc32c, data, count);
        const size_t countWithCrc32c =
1137
          cta::addCrc32cToMemoryBlock(
1138
1139
1140
1141
1142
            SCSI::logicBlockProtectionMethod::CRC32CSeed,
            count, dataWithCrc32c);
        if (countWithCrc32c !=
          count+SCSI::logicBlockProtectionMethod::CRC32CLength) {
          delete[] dataWithCrc32c;
1143
          cta::exception::Errnum::throwOnMinusOne(-1,
1144
1145
1146
1147
1148
1149
            "Failed in DriveGeneric::writeBlock: incorrect length for block"
            " with crc32c");
        }
        if (-1 == m_sysWrapper.write(m_tapeFD, dataWithCrc32c,
          countWithCrc32c)) {
          delete[] dataWithCrc32c;
1150
          cta::exception::Errnum::throwOnMinusOne(-1,
1151
            "Failed ST write with crc32c in DriveGeneric::writeBlock");
1152
1153
1154
1155
        }
        delete[] dataWithCrc32c;
        break;
      }
1156
    case lbpToUse::crc32cReadOnly:
1157
      throw cta::exception::Exception("In DriveGeneric::writeBlock: "
1158
1159
          "trying to write a block in CRC-readonly mode");
    case lbpToUse::disabled:
1160
      {
1161
          cta::exception::Errnum::throwOnMinusOne(
1162
1163
1164
1165
            m_sysWrapper.write(m_tapeFD, data, count),
            "Failed ST write in DriveGeneric::writeBlock");
          break;
      }
1166
    default:
1167
      throw cta::exception::Exception("In DriveGeneric::writeBlock: "
1168
          "unknown LBP mode");
1169
  }
1170
1171
1172
1173
1174
1175
}

/**
 * Read a data block from tape.
 * @param data pointer the the data block
 * @param count size of the data block
1176
 * @return the actual size of read data
1177
 */
1178
ssize_t drive::DriveGeneric::readBlock(void * data, size_t count)  {
1179
1180
1181
1182
1183
1184
1185
  switch (m_lbpToUse) {
    case lbpToUse::crc32cReadWrite:
    case lbpToUse::crc32cReadOnly:
      {
        uint8_t * dataWithCrc32c =(new (std::nothrow)
          uint8_t [count+SCSI::logicBlockProtectionMethod::CRC32CLength]);
        if(NULL == dataWithCrc32c) {
1186
          throw cta::exception::MemException("In DriveGeneric::readBlock: Failed to allocate memory");
1187
        }
1188
        const ssize_t res = m_sysWrapper.read(m_tapeFD, dataWithCrc32c,
1189
1190
1191
          count+SCSI::logicBlockProtectionMethod::CRC32CLength);
        if ( -1 == res ) {
          delete[] dataWithCrc32c;
1192
          cta::exception::Errnum::throwOnMinusOne(res,
1193
            "In DriveGeneric::readBlock: Failed ST read (with checksum)");
1194
1195
1196
1197
1198
1199
1200
1201
1202
        }
        if ( 0 == res ) {
          delete[] dataWithCrc32c;
          return 0;
        }
        const size_t dataLenWithoutCrc32c = res -
          SCSI::logicBlockProtectionMethod::CRC32CLength;
        if ( 0>= dataLenWithoutCrc32c) {
          delete[] dataWithCrc32c;
1203
          throw cta::exception::Exception("In DriveGeneric::readBlock: wrong data block size, checksum cannot fit");
1204
        }
1205
        if (cta::verifyCrc32cForMemoryBlockWithCrc32c(
1206
1207
1208
1209
1210
1211
1212
1213
          SCSI::logicBlockProtectionMethod::CRC32CSeed,
          res, dataWithCrc32c)) {
            // everything is fine here do mem copy
            memcpy(data, dataWithCrc32c, dataLenWithoutCrc32c);
            delete[] dataWithCrc32c;
            return dataLenWithoutCrc32c;
        } else {
          delete[] dataWithCrc32c;
1214
          throw cta::exception::Exception(
1215
              "In DriveGeneric::readBlock: Failed checksum verification");
1216
1217
1218
        }
        break;