1 /*
2 BAREOS® - Backup Archiving REcovery Open Sourced
3
4 Copyright (C) 2011-2012 Planets Communications B.V.
5 Copyright (C) 2013-2019 Bareos GmbH & Co. KG
6
7 This program is Free Software; you can redistribute it and/or
8 modify it under the terms of version three of the GNU Affero General Public
9 License as published by the Free Software Foundation and included
10 in the file LICENSE.
11
12 This program is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Affero General Public License for more details.
16
17 You should have received a copy of the GNU Affero General Public License
18 along with this program; if not, write to the Free Software
19 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
20 02110-1301, USA.
21 */
22
23 /*
24 * Low level SCSI interface.
25 *
26 * Marco van Wieringen, March 2012
27 */
28
29 #include "include/bareos.h"
30 #include "lib/berrno.h"
31 #include "scsi_lli.h"
32
33 /* Forward referenced functions */
34
35 #ifdef HAVE_LOWLEVEL_SCSI_INTERFACE
36
37
38 # if defined(HAVE_LINUX_OS)
39
40 # include <scsi/sg.h>
41 # include <scsi/scsi.h>
42
43 # ifndef SIOC_REQSENSE
44 # define SIOC_REQSENSE _IOR('C', 0x02, SCSI_PAGE_SENSE)
45 # endif
46
47 /*
48 * Core interface function to lowlevel SCSI interface.
49 */
do_scsi_cmd_page(int fd,const char * device_name,void * cdb,unsigned int cdb_len,void * cmd_page,unsigned int cmd_page_len,int direction)50 static inline bool do_scsi_cmd_page(int fd,
51 const char* device_name,
52 void* cdb,
53 unsigned int cdb_len,
54 void* cmd_page,
55 unsigned int cmd_page_len,
56 int direction)
57 {
58 int rc;
59 sg_io_hdr_t io_hdr;
60 SCSI_PAGE_SENSE sense;
61 bool opened_device = false;
62 bool retval = false;
63
64 /*
65 * See if we need to open the device_name or if we got an open filedescriptor.
66 */
67 if (fd == -1) {
68 fd = open(device_name, O_RDWR | O_NONBLOCK | O_BINARY);
69 if (fd < 0) {
70 BErrNo be;
71
72 Emsg2(M_ERROR, 0, _("Failed to open %s: ERR=%s\n"), device_name,
73 be.bstrerror());
74 return false;
75 }
76 opened_device = true;
77 }
78
79 memset(&sense, 0, sizeof(sense));
80 memset(&io_hdr, 0, sizeof(io_hdr));
81 io_hdr.interface_id = 'S';
82 io_hdr.cmd_len = cdb_len;
83 io_hdr.mx_sb_len = sizeof(sense);
84 io_hdr.dxfer_direction = direction;
85 io_hdr.dxfer_len = cmd_page_len;
86 io_hdr.dxferp = (char*)cmd_page;
87 io_hdr.cmdp = (unsigned char*)cdb;
88 io_hdr.sbp = (unsigned char*)&sense;
89
90 rc = ioctl(fd, SG_IO, &io_hdr);
91 if (rc < 0) {
92 BErrNo be;
93
94 Emsg2(M_ERROR, 0, _("Unable to perform SG_IO ioctl on fd %d: ERR=%s\n"), fd,
95 be.bstrerror());
96 goto bail_out;
97 }
98
99 if ((io_hdr.info & SG_INFO_OK_MASK) != SG_INFO_OK) {
100 Emsg3(M_ERROR, 0,
101 _("Failed with info 0x%02x mask status 0x%02x msg status 0x%02x\n"),
102 io_hdr.info, io_hdr.masked_status, io_hdr.msg_status);
103 Emsg2(M_ERROR, 0, _(" host status 0x%02x driver status 0x%02x\n"),
104 io_hdr.host_status, io_hdr.driver_status);
105 goto bail_out;
106 }
107
108 retval = true;
109
110 bail_out:
111 /*
112 * See if we opened the device in this function, if so close it.
113 */
114 if (opened_device) { close(fd); }
115
116 return retval;
117 }
118
119 /*
120 * Receive a lowlevel SCSI cmd from a SCSI device using the Linux SG_IO ioctl
121 * interface.
122 */
RecvScsiCmdPage(int fd,const char * device_name,void * cdb,unsigned int cdb_len,void * cmd_page,unsigned int cmd_page_len)123 bool RecvScsiCmdPage(int fd,
124 const char* device_name,
125 void* cdb,
126 unsigned int cdb_len,
127 void* cmd_page,
128 unsigned int cmd_page_len)
129 {
130 return do_scsi_cmd_page(fd, device_name, cdb, cdb_len, cmd_page, cmd_page_len,
131 SG_DXFER_FROM_DEV);
132 }
133
134 /*
135 * Send a lowlevel SCSI cmd to a SCSI device using the Linux SG_IO ioctl
136 * interface.
137 */
send_scsi_cmd_page(int fd,const char * device_name,void * cdb,unsigned int cdb_len,void * cmd_page,unsigned int cmd_page_len)138 bool send_scsi_cmd_page(int fd,
139 const char* device_name,
140 void* cdb,
141 unsigned int cdb_len,
142 void* cmd_page,
143 unsigned int cmd_page_len)
144 {
145 return do_scsi_cmd_page(fd, device_name, cdb, cdb_len, cmd_page, cmd_page_len,
146 SG_DXFER_TO_DEV);
147 }
148
149 /*
150 * Check if the SCSI sense is EOD.
151 */
CheckScsiAtEod(int fd)152 bool CheckScsiAtEod(int fd)
153 {
154 int rc;
155 SCSI_PAGE_SENSE sense;
156
157 memset(&sense, 0, sizeof(sense));
158
159 rc = ioctl(fd, SIOC_REQSENSE, &sense);
160 if (rc != 0) { return false; }
161
162 return sense.senseKey == 0x08 && /* BLANK CHECK. */
163 sense.addSenseCode == 0x00
164 && /* End of data (Combination of asc and ascq)*/
165 sense.addSenseCodeQual == 0x05;
166 }
167
168 # elif defined(HAVE_SUN_OS)
169
170 # include <sys/scsi/impl/uscsi.h>
171
172 # ifndef LOBYTE
173 # define LOBYTE(_w) ((_w)&0xff)
174 # endif
175
176 /*
177 * Core interface function to lowlevel SCSI interface.
178 */
do_scsi_cmd_page(int fd,const char * device_name,void * cdb,unsigned int cdb_len,void * cmd_page,unsigned int cmd_page_len,int flags)179 static inline bool do_scsi_cmd_page(int fd,
180 const char* device_name,
181 void* cdb,
182 unsigned int cdb_len,
183 void* cmd_page,
184 unsigned int cmd_page_len,
185 int flags)
186 {
187 int rc;
188 struct uscsi_cmd my_cmd;
189 SCSI_PAGE_SENSE sense;
190 bool opened_device = false;
191 bool retval = false;
192
193 /*
194 * See if we need to open the device_name or if we got an open filedescriptor.
195 */
196 if (fd == -1) {
197 fd = open(device_name, O_RDWR | O_NONBLOCK | O_BINARY);
198 if (fd < 0) {
199 BErrNo be;
200
201 Emsg2(M_ERROR, 0, _("Failed to open %s: ERR=%s\n"), device_name,
202 be.bstrerror());
203 return false;
204 }
205 opened_device = true;
206 }
207
208 memset(&sense, 0, sizeof(sense));
209
210 my_cmd.uscsi_flags = flags;
211 my_cmd.uscsi_timeout = 15; /* Allow 15 seconds for completion */
212 my_cmd.uscsi_cdb = (char*)cdb;
213 my_cmd.uscsi_cdblen = cdb_len;
214 my_cmd.uscsi_bufaddr = (char*)cmd_page;
215 my_cmd.uscsi_buflen = cmd_page_len;
216 my_cmd.uscsi_rqlen = sizeof(sense);
217 my_cmd.uscsi_rqbuf = (char*)&sense;
218
219 rc = ioctl(fd, USCSICMD, &my_cmd);
220 if (rc != 0) {
221 BErrNo be;
222
223 Emsg2(M_ERROR, 0, _("Unable to perform USCSICMD ioctl on fd %d: ERR=%s\n"),
224 fd, be.bstrerror());
225 Emsg3(M_ERROR, 0, _("Sense Key: %0.2X ASC: %0.2X ASCQ: %0.2X\n"),
226 LOBYTE(sense.senseKey), sense.addSenseCode, sense.addSenseCodeQual);
227 goto bail_out;
228 }
229
230 retval = true;
231
232 bail_out:
233 /*
234 * See if we opened the device in this function, if so close it.
235 */
236 if (opened_device) { close(fd); }
237 return retval;
238 }
239
240 /*
241 * Receive a lowlevel SCSI cmd from a SCSI device using the Solaris USCSI ioctl
242 * interface.
243 */
RecvScsiCmdPage(int fd,const char * device_name,void * cdb,unsigned int cdb_len,void * cmd_page,unsigned int cmd_page_len)244 bool RecvScsiCmdPage(int fd,
245 const char* device_name,
246 void* cdb,
247 unsigned int cdb_len,
248 void* cmd_page,
249 unsigned int cmd_page_len)
250 {
251 return do_scsi_cmd_page(fd, device_name, cdb, cdb_len, cmd_page, cmd_page_len,
252 (USCSI_READ | USCSI_SILENT | USCSI_RQENABLE));
253 }
254
255 /*
256 * Send a lowlevel SCSI cmd to a SCSI device using the Solaris USCSI ioctl
257 * interface.
258 */
send_scsi_cmd_page(int fd,const char * device_name,void * cdb,unsigned int cdb_len,void * cmd_page,unsigned int cmd_page_len)259 bool send_scsi_cmd_page(int fd,
260 const char* device_name,
261 void* cdb,
262 unsigned int cdb_len,
263 void* cmd_page,
264 unsigned int cmd_page_len)
265 {
266 return do_scsi_cmd_page(fd, device_name, cdb, cdb_len, cmd_page, cmd_page_len,
267 (USCSI_WRITE | USCSI_SILENT | USCSI_RQENABLE));
268 }
269
CheckScsiAtEod(int fd)270 bool CheckScsiAtEod(int fd) { return false; }
271
272 # elif defined(HAVE_FREEBSD_OS)
273
274 # include <camlib.h>
275 # include <cam/scsi/scsi_message.h>
276
277 # ifndef SAM_STAT_CHECK_CONDITION
278 # define SAM_STAT_CHECK_CONDITION 0x2
279 # endif
280
281 # ifndef SAM_STAT_COMMAND_TERMINATED
282 # define SAM_STAT_COMMAND_TERMINATED 0x22
283 # endif
284
285 /*
286 * Core interface function to lowlevel SCSI interface.
287 */
do_scsi_cmd_page(int fd,const char * device_name,void * cdb,unsigned int cdb_len,void * cmd_page,unsigned int cmd_page_len,int direction)288 static inline bool do_scsi_cmd_page(int fd,
289 const char* device_name,
290 void* cdb,
291 unsigned int cdb_len,
292 void* cmd_page,
293 unsigned int cmd_page_len,
294 int direction)
295 {
296 int unitnum, len;
297 union ccb* ccb;
298 char errbuf[128];
299 char cam_devicename[64];
300 struct cam_device* cam_dev;
301 SCSI_PAGE_SENSE sense;
302 bool retval = false;
303
304 /*
305 * See what CAM device to use.
306 */
307 if (cam_get_device(device_name, cam_devicename, sizeof(cam_devicename),
308 &unitnum)
309 == -1) {
310 BErrNo be;
311
312 Emsg2(M_ERROR, 0, _("Failed to find CAM device for %s: ERR=%s\n"),
313 device_name, be.bstrerror());
314 return false;
315 }
316
317 cam_dev = cam_open_spec_device(cam_devicename, unitnum, O_RDWR, NULL);
318 if (!cam_dev) {
319 BErrNo be;
320
321 Emsg2(M_ERROR, 0, _("Failed to open CAM device for %s: ERR=%s\n"),
322 device_name, be.bstrerror());
323 return false;
324 }
325
326 ccb = cam_getccb(cam_dev);
327 if (!ccb) {
328 Emsg1(M_ERROR, 0, _("Failed to allocate new ccb for %s\n"), device_name);
329 goto bail_out;
330 }
331
332 /*
333 * Clear out structure, except for header that was filled for us.
334 */
335 memset(&(&ccb->ccb_h)[1], 0,
336 sizeof(struct ccb_scsiio) - sizeof(struct ccb_hdr));
337
338 cam_fill_csio(&ccb->csio, 1, /* retries */
339 NULL, /* cbfcnp */
340 direction, /* flags */
341 MSG_SIMPLE_Q_TAG, /* tagaction */
342 (uint8_t*)cmd_page, /* dataptr */
343 cmd_page_len, /* datalen */
344 sizeof(sense), /* senselength */
345 cdb_len, /* cdblength */
346 15000 /* timeout (millisecs) */);
347 memcpy(ccb->csio.cdb_io.cdb_bytes, cdb, cdb_len);
348
349 if (cam_send_ccb(cam_dev, ccb) < 0) {
350 Emsg2(M_ERROR, 0, _("Failed to send ccb to device %s: %s\n"), device_name,
351 cam_error_string(cam_dev, ccb, errbuf, sizeof(errbuf), CAM_ESF_ALL,
352 CAM_EPF_ALL));
353 cam_freeccb(ccb);
354 goto bail_out;
355 }
356
357 /*
358 * Retrieve the SCSI sense data.
359 */
360 if (((ccb->ccb_h.status & CAM_STATUS_MASK) == CAM_REQ_CMP)
361 || ((ccb->ccb_h.status & CAM_STATUS_MASK) == CAM_SCSI_STATUS_ERROR)) {
362 if ((SAM_STAT_CHECK_CONDITION == ccb->csio.scsi_status)
363 || (SAM_STAT_COMMAND_TERMINATED == ccb->csio.scsi_status)) {
364 len = sizeof(sense) - ccb->csio.sense_resid;
365 if (len) { memcpy(&sense, &(ccb->csio.sense_data), len); }
366 }
367 }
368
369 retval = true;
370
371 bail_out:
372 /*
373 * Close the CAM device.
374 */
375 cam_close_device(cam_dev);
376 return retval;
377 }
378
379 /*
380 * Receive a lowlevel SCSI cmd from a SCSI device using the FreeBSD SCSI CAM
381 * interface.
382 */
RecvScsiCmdPage(int fd,const char * device_name,void * cdb,unsigned int cdb_len,void * cmd_page,unsigned int cmd_page_len)383 bool RecvScsiCmdPage(int fd,
384 const char* device_name,
385 void* cdb,
386 unsigned int cdb_len,
387 void* cmd_page,
388 unsigned int cmd_page_len)
389 {
390 return do_scsi_cmd_page(fd, device_name, cdb, cdb_len, cmd_page, cmd_page_len,
391 CAM_DIR_IN);
392 }
393
394 /*
395 * Send a lowlevel SCSI cmd to a SCSI device using the FreeBSD SCSI CAM
396 * interface.
397 */
send_scsi_cmd_page(int fd,const char * device_name,void * cdb,unsigned int cdb_len,void * cmd_page,unsigned int cmd_page_len)398 bool send_scsi_cmd_page(int fd,
399 const char* device_name,
400 void* cdb,
401 unsigned int cdb_len,
402 void* cmd_page,
403 unsigned int cmd_page_len)
404 {
405 return do_scsi_cmd_page(fd, device_name, cdb, cdb_len, cmd_page, cmd_page_len,
406 CAM_DIR_OUT);
407 }
408
CheckScsiAtEod(int fd)409 bool CheckScsiAtEod(int fd) { return false; }
410
411 # elif defined(HAVE_NETBSD_OS) || defined(HAVE_OPENBSD_OS)
412
413 # if defined(HAVE_NETBSD_OS)
414 # include <dev/scsipi/scsipi_all.h>
415 # else
416 # include <scsi/uscsi_all.h>
417 # endif
418
419 # ifndef LOBYTE
420 # define LOBYTE(_w) ((_w)&0xff)
421 # endif
422
423 /*
424 * Core interface function to lowlevel SCSI interface.
425 */
do_scsi_cmd_page(int fd,const char * device_name,void * cdb,unsigned int cdb_len,void * cmd_page,unsigned int cmd_page_len,int flags)426 static inline bool do_scsi_cmd_page(int fd,
427 const char* device_name,
428 void* cdb,
429 unsigned int cdb_len,
430 void* cmd_page,
431 unsigned int cmd_page_len,
432 int flags)
433 {
434 int rc;
435 scsireq_t req;
436 SCSI_PAGE_SENSE* sense;
437 bool opened_device = false;
438 bool retval = false;
439
440 /*
441 * See if we need to open the device_name or if we got an open filedescriptor.
442 */
443 if (fd == -1) {
444 fd = open(device_name, O_RDWR | O_NONBLOCK | O_BINARY);
445 if (fd < 0) {
446 BErrNo be;
447
448 Emsg2(M_ERROR, 0, _("Failed to open %s: ERR=%s\n"), device_name,
449 be.bstrerror());
450 return false;
451 }
452 opened_device = true;
453 }
454
455 memset(&req, 0, sizeof(req));
456
457 memcpy(req.cmd, cdb, cdb_len);
458 req.cmdlen = cdb_len;
459 req.databuf = cmd_page;
460 req.datalen = cmd_page_len;
461 req.timeout = 15000 /* timeout (millisecs) */;
462 req.flags = flags;
463 req.senselen = sizeof(SCSI_PAGE_SENSE);
464
465 rc = ioctl(fd, SCIOCCOMMAND, &req);
466 if (rc < 0) {
467 BErrNo be;
468
469 Emsg2(M_ERROR, 0,
470 _("Unable to perform SCIOCCOMMAND ioctl on fd %d: ERR=%s\n"), fd,
471 be.bstrerror());
472 goto bail_out;
473 }
474
475 switch (req.retsts) {
476 case SCCMD_OK:
477 retval = true;
478 break;
479 case SCCMD_SENSE:
480 sense = req.sense;
481 Emsg3(M_ERROR, 0, _("Sense Key: %0.2X ASC: %0.2X ASCQ: %0.2X\n"),
482 LOBYTE(sense.senseKey), sense.addSenseCode, sense.addSenseCodeQual);
483 break;
484 case SCCMD_TIMEOUT:
485 Emsg1(M_ERROR, 0,
486 _("SCIOCCOMMAND ioctl on %s returned SCSI command timed out\n"),
487 devicename);
488 break;
489 case SCCMD_BUSY:
490 Emsg1(M_ERROR, 0, _("SCIOCCOMMAND ioctl on %s returned device is busy\n"),
491 devicename);
492 break;
493 case SCCMD_SENSE:
494 break;
495 default:
496 Emsg2(M_ERROR, 0,
497 _("SCIOCCOMMAND ioctl on %s returned unknown status %d\n"),
498 device_name, req.retsts);
499 break;
500 }
501
502 bail_out:
503 /*
504 * See if we opened the device in this function, if so close it.
505 */
506 if (opened_device) { close(fd); }
507 return retval;
508 }
509
510 /*
511 * Receive a lowlevel SCSI cmd from a SCSI device using the NetBSD/OpenBSD
512 * SCIOCCOMMAND ioctl interface.
513 */
RecvScsiCmdPage(int fd,const char * device_name,void * cdb,unsigned int cdb_len,void * cmd_page,unsigned int cmd_page_len)514 bool RecvScsiCmdPage(int fd,
515 const char* device_name,
516 void* cdb,
517 unsigned int cdb_len,
518 void* cmd_page,
519 unsigned int cmd_page_len)
520 {
521 return do_scsi_cmd_page(fd, device_name, cdb, cdb_len, cmd_page, cmd_page_len,
522 SCCMD_READ);
523 }
524
525 /*
526 * Send a lowlevel SCSI cmd to a SCSI device using the NetBSD/OpenBSD
527 * SCIOCCOMMAND ioctl interface.
528 */
send_scsi_cmd_page(int fd,const char * device_name,void * cdb,unsigned int cdb_len,void * cmd_page,unsigned int cmd_page_len)529 bool send_scsi_cmd_page(int fd,
530 const char* device_name,
531 void* cdb,
532 unsigned int cdb_len,
533 void* cmd_page,
534 unsigned int cmd_page_len)
535 {
536 return do_scsi_cmd_page(fd, device_name, cdb, cdb_len, cmd_page, cmd_page_len,
537 SCCMD_WRITE);
538 }
539
CheckScsiAtEod(int fd)540 bool CheckScsiAtEod(int fd) { return false; }
541 # endif
542 #else
543 /*
544 * Dummy lowlevel functions when no support for platform.
545 */
RecvScsiCmdPage(int fd,const char * device_name,void * cdb,unsigned int cdb_len,void * cmd_page,unsigned int cmd_page_len)546 bool RecvScsiCmdPage(int fd,
547 const char* device_name,
548 void* cdb,
549 unsigned int cdb_len,
550 void* cmd_page,
551 unsigned int cmd_page_len)
552 {
553 return false;
554 }
555
send_scsi_cmd_page(int fd,const char * device_name,void * cdb,unsigned int cdb_len,void * cmd_page,unsigned int cmd_page_len)556 bool send_scsi_cmd_page(int fd,
557 const char* device_name,
558 void* cdb,
559 unsigned int cdb_len,
560 void* cmd_page,
561 unsigned int cmd_page_len)
562 {
563 return false;
564 }
565
CheckScsiAtEod(int fd)566 bool CheckScsiAtEod(int fd) { return false; }
567 #endif /* HAVE_LOWLEVEL_SCSI_INTERFACE */
568