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