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