1 /*
2 BAREOS® - Backup Archiving REcovery Open Sourced
3
4 Copyright (C) 2011-2012 Planets Communications B.V.
5 Copyright (C) 2013-2013 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 ==
164 0x00 && /* 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) == -1) {
309 BErrNo be;
310
311 Emsg2(M_ERROR, 0, _("Failed to find CAM device for %s: ERR=%s\n"),
312 device_name, be.bstrerror());
313 return false;
314 }
315
316 cam_dev = cam_open_spec_device(cam_devicename, unitnum, O_RDWR, NULL);
317 if (!cam_dev) {
318 BErrNo be;
319
320 Emsg2(M_ERROR, 0, _("Failed to open CAM device for %s: ERR=%s\n"),
321 device_name, be.bstrerror());
322 return false;
323 }
324
325 ccb = cam_getccb(cam_dev);
326 if (!ccb) {
327 Emsg1(M_ERROR, 0, _("Failed to allocate new ccb for %s\n"), device_name);
328 goto bail_out;
329 }
330
331 /*
332 * Clear out structure, except for header that was filled for us.
333 */
334 memset(&(&ccb->ccb_h)[1], 0,
335 sizeof(struct ccb_scsiio) - sizeof(struct ccb_hdr));
336
337 cam_fill_csio(&ccb->csio, 1, /* retries */
338 NULL, /* cbfcnp */
339 direction, /* flags */
340 MSG_SIMPLE_Q_TAG, /* tagaction */
341 (u_int8_t*)cmd_page, /* dataptr */
342 cmd_page_len, /* datalen */
343 sizeof(sense), /* senselength */
344 cdb_len, /* cdblength */
345 15000 /* timeout (millisecs) */);
346 memcpy(ccb->csio.cdb_io.cdb_bytes, cdb, cdb_len);
347
348 if (cam_send_ccb(cam_dev, ccb) < 0) {
349 Emsg2(M_ERROR, 0, _("Failed to send ccb to device %s: %s\n"), device_name,
350 cam_error_string(cam_dev, ccb, errbuf, sizeof(errbuf), CAM_ESF_ALL,
351 CAM_EPF_ALL));
352 cam_freeccb(ccb);
353 goto bail_out;
354 }
355
356 /*
357 * Retrieve the SCSI sense data.
358 */
359 if (((ccb->ccb_h.status & CAM_STATUS_MASK) == CAM_REQ_CMP) ||
360 ((ccb->ccb_h.status & CAM_STATUS_MASK) == CAM_SCSI_STATUS_ERROR)) {
361 if ((SAM_STAT_CHECK_CONDITION == ccb->csio.scsi_status) ||
362 (SAM_STAT_COMMAND_TERMINATED == ccb->csio.scsi_status)) {
363 len = sizeof(sense) - ccb->csio.sense_resid;
364 if (len) { memcpy(&sense, &(ccb->csio.sense_data), len); }
365 }
366 }
367
368 retval = true;
369
370 bail_out:
371 /*
372 * Close the CAM device.
373 */
374 cam_close_device(cam_dev);
375 return retval;
376 }
377
378 /*
379 * Receive a lowlevel SCSI cmd from a SCSI device using the FreeBSD SCSI CAM
380 * interface.
381 */
RecvScsiCmdPage(int fd,const char * device_name,void * cdb,unsigned int cdb_len,void * cmd_page,unsigned int cmd_page_len)382 bool RecvScsiCmdPage(int fd,
383 const char* device_name,
384 void* cdb,
385 unsigned int cdb_len,
386 void* cmd_page,
387 unsigned int cmd_page_len)
388 {
389 return do_scsi_cmd_page(fd, device_name, cdb, cdb_len, cmd_page, cmd_page_len,
390 CAM_DIR_IN);
391 }
392
393 /*
394 * Send a lowlevel SCSI cmd to a SCSI device using the FreeBSD SCSI CAM
395 * interface.
396 */
send_scsi_cmd_page(int fd,const char * device_name,void * cdb,unsigned int cdb_len,void * cmd_page,unsigned int cmd_page_len)397 bool send_scsi_cmd_page(int fd,
398 const char* device_name,
399 void* cdb,
400 unsigned int cdb_len,
401 void* cmd_page,
402 unsigned int cmd_page_len)
403 {
404 return do_scsi_cmd_page(fd, device_name, cdb, cdb_len, cmd_page, cmd_page_len,
405 CAM_DIR_OUT);
406 }
407
CheckScsiAtEod(int fd)408 bool CheckScsiAtEod(int fd) { return false; }
409
410 #elif defined(HAVE_NETBSD_OS) || defined(HAVE_OPENBSD_OS)
411
412 #if defined(HAVE_NETBSD_OS)
413 #include <dev/scsipi/scsipi_all.h>
414 #else
415 #include <scsi/uscsi_all.h>
416 #endif
417
418 #ifndef LOBYTE
419 #define LOBYTE(_w) ((_w)&0xff)
420 #endif
421
422 /*
423 * Core interface function to lowlevel SCSI interface.
424 */
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)425 static inline bool do_scsi_cmd_page(int fd,
426 const char* device_name,
427 void* cdb,
428 unsigned int cdb_len,
429 void* cmd_page,
430 unsigned int cmd_page_len,
431 int flags)
432 {
433 int rc;
434 scsireq_t req;
435 SCSI_PAGE_SENSE* sense;
436 bool opened_device = false;
437 bool retval = false;
438
439 /*
440 * See if we need to open the device_name or if we got an open filedescriptor.
441 */
442 if (fd == -1) {
443 fd = open(device_name, O_RDWR | O_NONBLOCK | O_BINARY);
444 if (fd < 0) {
445 BErrNo be;
446
447 Emsg2(M_ERROR, 0, _("Failed to open %s: ERR=%s\n"), device_name,
448 be.bstrerror());
449 return false;
450 }
451 opened_device = true;
452 }
453
454 memset(&req, 0, sizeof(req));
455
456 memcpy(req.cmd, cdb, cdb_len);
457 req.cmdlen = cdb_len;
458 req.databuf = cmd_page;
459 req.datalen = cmd_page_len;
460 req.timeout = 15000 /* timeout (millisecs) */;
461 req.flags = flags;
462 req.senselen = sizeof(SCSI_PAGE_SENSE);
463
464 rc = ioctl(fd, SCIOCCOMMAND, &req);
465 if (rc < 0) {
466 BErrNo be;
467
468 Emsg2(M_ERROR, 0,
469 _("Unable to perform SCIOCCOMMAND ioctl on fd %d: ERR=%s\n"), fd,
470 be.bstrerror());
471 goto bail_out;
472 }
473
474 switch (req.retsts) {
475 case SCCMD_OK:
476 retval = true;
477 break;
478 case SCCMD_SENSE:
479 sense = req.sense;
480 Emsg3(M_ERROR, 0, _("Sense Key: %0.2X ASC: %0.2X ASCQ: %0.2X\n"),
481 LOBYTE(sense.senseKey), sense.addSenseCode, sense.addSenseCodeQual);
482 break;
483 case SCCMD_TIMEOUT:
484 Emsg1(M_ERROR, 0,
485 _("SCIOCCOMMAND ioctl on %s returned SCSI command timed out\n"),
486 devicename);
487 break;
488 case SCCMD_BUSY:
489 Emsg1(M_ERROR, 0, _("SCIOCCOMMAND ioctl on %s returned device is busy\n"),
490 devicename);
491 break;
492 case SCCMD_SENSE:
493 break;
494 default:
495 Emsg2(M_ERROR, 0,
496 _("SCIOCCOMMAND ioctl on %s returned unknown status %d\n"),
497 device_name, req.retsts);
498 break;
499 }
500
501 bail_out:
502 /*
503 * See if we opened the device in this function, if so close it.
504 */
505 if (opened_device) { close(fd); }
506 return retval;
507 }
508
509 /*
510 * Receive a lowlevel SCSI cmd from a SCSI device using the NetBSD/OpenBSD
511 * SCIOCCOMMAND ioctl interface.
512 */
RecvScsiCmdPage(int fd,const char * device_name,void * cdb,unsigned int cdb_len,void * cmd_page,unsigned int cmd_page_len)513 bool RecvScsiCmdPage(int fd,
514 const char* device_name,
515 void* cdb,
516 unsigned int cdb_len,
517 void* cmd_page,
518 unsigned int cmd_page_len)
519 {
520 return do_scsi_cmd_page(fd, device_name, cdb, cdb_len, cmd_page, cmd_page_len,
521 SCCMD_READ);
522 }
523
524 /*
525 * Send a lowlevel SCSI cmd to a SCSI device using the NetBSD/OpenBSD
526 * SCIOCCOMMAND ioctl interface.
527 */
send_scsi_cmd_page(int fd,const char * device_name,void * cdb,unsigned int cdb_len,void * cmd_page,unsigned int cmd_page_len)528 bool send_scsi_cmd_page(int fd,
529 const char* device_name,
530 void* cdb,
531 unsigned int cdb_len,
532 void* cmd_page,
533 unsigned int cmd_page_len)
534 {
535 return do_scsi_cmd_page(fd, device_name, cdb, cdb_len, cmd_page, cmd_page_len,
536 SCCMD_WRITE);
537 }
538
CheckScsiAtEod(int fd)539 bool CheckScsiAtEod(int fd) { return false; }
540 #endif
541 #else
542 /*
543 * Dummy lowlevel functions when no support for platform.
544 */
RecvScsiCmdPage(int fd,const char * device_name,void * cdb,unsigned int cdb_len,void * cmd_page,unsigned int cmd_page_len)545 bool RecvScsiCmdPage(int fd,
546 const char* device_name,
547 void* cdb,
548 unsigned int cdb_len,
549 void* cmd_page,
550 unsigned int cmd_page_len)
551 {
552 return false;
553 }
554
send_scsi_cmd_page(int fd,const char * device_name,void * cdb,unsigned int cdb_len,void * cmd_page,unsigned int cmd_page_len)555 bool send_scsi_cmd_page(int fd,
556 const char* device_name,
557 void* cdb,
558 unsigned int cdb_len,
559 void* cmd_page,
560 unsigned int cmd_page_len)
561 {
562 return false;
563 }
564
CheckScsiAtEod(int fd)565 bool CheckScsiAtEod(int fd) { return false; }
566 #endif /* HAVE_LOWLEVEL_SCSI_INTERFACE */
567