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