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