1 /* OGMDvd - A wrapper library around libdvdread
2  * Copyright (C) 2009-2012 Olivier Rolland <billl@users.sourceforge.net>
3  *
4  * Code from dvd+rw-tools
5  * Copyright (C) Andy Polyakov <appro@fy.chalmers.se>
6  *
7  * dvd+rw-tools is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  *
12  * dvd+rw-tools is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU Library General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
20  */
21 
22 #ifdef HAVE_CONFIG_H
23 #include "config.h"
24 #endif
25 
26 #include "ogmdvd-transport.h"
27 
28 #include <poll.h>
29 #include <errno.h>
30 #include <fcntl.h>
31 #include <unistd.h>
32 #include <stdlib.h>
33 #include <string.h>
34 
35 #include <sys/types.h>
36 #include <sys/time.h>
37 #include <sys/stat.h>
38 
39 #ifndef CHECK_CONDITION
40 #define CHECK_CONDITION 0x01
41 #endif
42 
43 #ifndef EMEDIUMTYPE
44 #define EMEDIUMTYPE EINVAL
45 #endif
46 
47 #ifndef	ENOMEDIUM
48 #define	ENOMEDIUM ENODEV
49 #endif
50 
51 #define ERRCODE(s)      ((((s)[2]&0x0F)<<16)|((s)[12]<<8)|((s)[13]))
52 #define SK(errcode)     (((errcode)>>16)&0xF)
53 #define ASC(errcode)    (((errcode)>>8)&0xFF)
54 #define ASCQ(errcode)   ((errcode)&0xFF)
55 
56 #define CREAM_ON_ERRNO_NAKED(s)                    \
57     switch ((s)[12])                               \
58     {   case 0x04: errno=EAGAIN;   break;          \
59         case 0x20: errno=ENODEV;   break;          \
60         case 0x21: if ((s)[13]==0) errno=ENOSPC;   \
61                    else            errno=EINVAL;   \
62                    break;                          \
63         case 0x30: errno=EMEDIUMTYPE;      break;  \
64         case 0x3A: errno=ENOMEDIUM;        break;  \
65     }
66 #define CREAM_ON_ERRNO(s) do { CREAM_ON_ERRNO_NAKED(s) } while(0)
67 
68 inline long
getmsecs()69 getmsecs ()
70 {
71   struct timeval tv;
72   gettimeofday (&tv, NULL);
73   return tv.tv_sec * 1000 + tv.tv_usec / 1000;
74 }
75 
76 void
sperror(const char * cmd,int err)77 sperror (const char *cmd, int err)
78 {
79   int saved_errno = errno;
80 
81   if (err == -1)
82     fprintf (stderr, ":-( unable to %s: ", cmd);
83   else
84     fprintf (stderr, ":-[ %s failed with SK=%Xh/ASC=%02Xh/ACQ=%02Xh]: ",
85              cmd, SK (err), ASC (err), ASCQ (err));
86   errno = saved_errno, perror (NULL);
87 }
88 
89 #if defined(HAVE_SG_IO_HDR_T)
90 
91 #include <limits.h>
92 #include <mntent.h>
93 
94 #include <linux/cdrom.h>
95 
96 #include <sys/wait.h>
97 #include <sys/ioctl.h>
98 #include <sys/utsname.h>
99 
100 #include <scsi/sg.h>
101 
102 static const int Dir_xlate[] =
103 {
104   SG_DXFER_NONE,
105   SG_DXFER_FROM_DEV,
106   SG_DXFER_TO_DEV
107 };
108 
109 struct _Scsi_Command
110 {
111   int fd, autoclose;
112   char *filename;
113   struct cdrom_generic_command cgc;
114   union
115   {
116     struct request_sense s;
117     unsigned char u[18];
118   } _sense;
119   struct sg_io_hdr sg_io;
120 };
121 
122 Scsi_Command *
scsi_command_new_from_fd(int fd)123 scsi_command_new_from_fd (int fd)
124 {
125   Scsi_Command *cmd;
126 
127   cmd = (Scsi_Command *) malloc (sizeof (Scsi_Command));
128   cmd->fd = fd;
129   cmd->autoclose = 0;
130   cmd->filename = NULL;
131 
132   return cmd;
133 }
134 
135 void
scsi_command_free(Scsi_Command * cmd)136 scsi_command_free (Scsi_Command *cmd)
137 {
138   if (cmd->fd >= 0 && cmd->autoclose)
139   {
140     close (cmd->fd);
141     cmd->fd = -1;
142   }
143 
144   if (cmd->filename)
145   {
146     free (cmd->filename);
147     cmd->filename = NULL;
148   }
149 
150   free (cmd);
151 }
152 
153 void
scsi_command_set(Scsi_Command * cmd,size_t index,unsigned char value)154 scsi_command_set (Scsi_Command *cmd, size_t index, unsigned char value)
155 {
156   if (index == 0)
157   {
158     memset (&cmd->cgc, 0, sizeof (cmd->cgc)), memset (&cmd->_sense, 0, sizeof (cmd->_sense));
159     cmd->cgc.quiet = 1;
160     cmd->cgc.sense = &cmd->_sense.s;
161     memset (&cmd->sg_io, 0, sizeof (cmd->sg_io));
162     cmd->sg_io.interface_id = 'S';
163     cmd->sg_io.mx_sb_len = sizeof (cmd->_sense);
164     cmd->sg_io.cmdp = cmd->cgc.cmd;
165     cmd->sg_io.sbp = cmd->_sense.u;
166     cmd->sg_io.flags = SG_FLAG_LUN_INHIBIT | SG_FLAG_DIRECT_IO;
167   }
168   cmd->sg_io.cmd_len = index + 1;
169   cmd->cgc.cmd[index] = value;
170 }
171 
172 static const int real_dir[] =
173 {
174   CGC_DATA_NONE,
175   CGC_DATA_READ,
176   CGC_DATA_WRITE
177 };
178 
179 int
scsi_command_transport(Scsi_Command * cmd,Direction dir,void * buf,size_t sz)180 scsi_command_transport (Scsi_Command *cmd, Direction dir, void *buf, size_t sz)
181 {
182   int ret = 0;
183 
184   cmd->sg_io.dxferp = buf;
185   cmd->sg_io.dxfer_len = sz;
186   cmd->sg_io.dxfer_direction = Dir_xlate[dir];
187   if (ioctl (cmd->fd, SG_IO, &cmd->sg_io))
188     return -1;
189 
190   if ((cmd->sg_io.info & SG_INFO_OK_MASK) != SG_INFO_OK)
191   {
192     errno = EIO;
193     ret = -1;
194     if (cmd->sg_io.masked_status & CHECK_CONDITION)
195     {
196       ret = ERRCODE ((unsigned char *) cmd->sg_io.sbp);
197       if (ret == 0)
198         ret = -1;
199       else
200         CREAM_ON_ERRNO ((unsigned char *) cmd->sg_io.sbp);
201     }
202   }
203 
204   return ret;
205 }
206 
207 #elif defined(HAVE_SCSIREQ_T)
208 
209 #include <sys/ioctl.h>
210 #include <sys/scsiio.h>
211 #include <sys/wait.h>
212 #include <sys/param.h>
213 #include <sys/mount.h>
214 
215 typedef off_t off64_t;
216 #define stat64   stat
217 #define fstat64  fstat
218 #define open64   open
219 #define pread64  pread
220 #define pwrite64 pwrite
221 #define lseek64  lseek
222 
223 typedef enum
224 {
225   NONE = 0,
226   READ = SCCMD_READ,
227   WRITE = SCCMD_WRITE
228 } Direction;
229 
230 struct _Scsi_Command
231 {
232   int fd;
233   int autoclose;
234   char *filename;
235   scsireq_t req;
236 };
237 
238 Scsi_Command *
scsi_command_new_from_fd(int f)239 scsi_command_new_from_fd (int f)
240 {
241   Scsi_Command *cmd;
242 
243   cmd = (Scsi_Command *) malloc (sizeof (Scsi_Command));
244   cmd->fd = f;
245   cmd->autoclose = 0;
246   cmd->filename = NULL;
247 
248   return cmd;
249 }
250 
251 void
scsi_command_free(Scsi_Command * cmd)252 scsi_command_free (Scsi_Command *cmd)
253 {
254  if (cmd->fd >= 0 && cmd->autoclose)
255  {
256    close (cmd->fd);
257    cmd->fd = -1;
258  }
259 
260  if (cmd->filename)
261  {
262    free (cmd->filename);
263    cmd->filename = NULL;
264  }
265 
266  free (cmd);
267 }
268 
269 void
scsi_command_set(Scsi_Command * cmd,size_t index,unsigned char value)270 scsi_command_set (Scsi_Command *cmd, size_t index, unsigned char value)
271 {
272   if (index == 0)
273   {
274     memset (&req, 0, sizeof (req));
275     req.flags = SCCMD_ESCAPE;
276     req.timeout = 30000;
277     req.senselen = 18; //sizeof(req.sense);
278   }
279   req.cmdlen = index + 1;
280   req.cmd[index] = value;
281 }
282 
283 int
scsi_command_transport(Scsi_Command * cmd,Direction dir,void * buf,size_t sz)284 scsi_command_transport (Scsi_Command *cmd, Direction dir, void *buf, size_t sz)
285 {
286   int ret = 0;
287 
288   req.databuf = (caddr_t) buf;
289   req.datalen = sz;
290   req.flags |= dir;
291 
292   if (ioctl (fd, SCIOCCOMMAND, &req) < 0)
293     return -1;
294   if (req.retsts == SCCMD_OK)
295     return 0;
296 
297   errno = EIO;
298   ret = -1;
299   if (req.retsts == SCCMD_SENSE)
300   {
301     ret = ERRCODE (req.sense);
302     if (ret == 0)
303       ret = -1;
304     else
305       CREAM_ON_ERRNO (req.sense);
306   }
307 
308   return ret;
309 }
310 
311 #elif defined(HAVE_STRUCT_USCSI_CMD)
312 
313 /* Licensing terms for commercial distribution for Solaris are to be
314    settled with Inserve Technology, Åvägen 6, 412 50 GÖTEBORG, Sweden,
315    tel. +46-(0)31-86 87 88, see http://www.inserve.se/ for further
316   details. */
317 
318 #include <sys/scsi/impl/uscsi.h>
319 
320 struct _Scsi_Command
321 {
322   int fd;
323   int autoclose;
324   char *filename;
325   struct uscsi_cmd ucmd;
326   unsigned char cdb[16];
327   unsigned char _sense[18];
328 };
329 
330 Scsi_Command *
scsi_command_new_from_fd(int f)331 scsi_command_new_from_fd (int f)
332 {
333   Scsi_Command *cmd;
334 
335   cmd = (Scsi_Command *) malloc (sizeof (Scsi_Command));
336   cmd->fd = f;
337   cmd->autoclose = 0;
338   cmd->filename = NULL;
339 
340   return cmd;
341 }
342 
343 void
scsi_command_free(Scsi_Command * cmd)344 scsi_command_free (Scsi_Command *cmd)
345 {
346  if (cmd->fd >= 0 && cmd->autoclose)
347  {
348    close (cmd->fd);
349    cmd->fd = -1;
350  }
351 
352  if (cmd->filename)
353  {
354    free (cmd->filename);
355    cmd->filename = NULL;
356  }
357 
358  free (cmd);
359 }
360 
361 void
scsi_command_set(Scsi_Command * cmd,size_t index,unsigned char value)362 scsi_command_set (Scsi_Command *cmd, size_t index, unsigned char value)
363 {
364   if (index == 0)
365   {
366     memset (&cmd->ucmd, 0, sizeof (cmd->ucmd));
367     memset (cmd->cdb, 0, sizeof (cmd->cdb));
368     memset (cmd->_sense, 0, sizeof (cmd->_sense));
369     cmd->ucmd.uscsi_cdb = (caddr_t) cmd->cdb;
370     cmd->ucmd.uscsi_rqbuf = (caddr_t) cmd->_sense;
371     cmd->ucmd.uscsi_rqlen = sizeof (cmd->_sense);
372     cmd->ucmd.uscsi_flags = USCSI_SILENT | USCSI_DIAGNOSE |
373       USCSI_ISOLATE | USCSI_RQENABLE;
374     cmd->ucmd.uscsi_timeout = 60;
375   }
376   cmd->ucmd.uscsi_cdblen = index + 1;
377   cmd->cdb[index] = value;
378 }
379 
380 static const int real_dir[] =
381 {
382   0,
383   USCSI_READ,
384   USCSI_WRITE
385 };
386 
387 int
scsi_command_transport(Scsi_Command * cmd,Direction dir,void * buf,size_t sz)388 scsi_command_transport (Scsi_Command *cmd, Direction dir, void *buf, size_t sz)
389 {
390   int ret = 0;
391 
392   cmd->ucmd.uscsi_bufaddr = (caddr_t) buf;
393   cmd->ucmd.uscsi_buflen = sz;
394   cmd->ucmd.uscsi_flags |= dir;
395   if (ioctl (cmd->fd, USCSICMD, &cmd->ucmd))
396   {
397     if (errno == EIO && cmd->_sense[0] == 0) /* USB seems to be broken... */
398     {
399       size_t residue = cmd->ucmd.uscsi_resid;
400       memset (cmd->cdb, 0, sizeof (cmd->cdb));
401       cmd->cdb[0] = 0x03;          /* REQUEST SENSE */
402       cmd->cdb[4] = sizeof (cmd->_sense);
403       cmd->ucmd.uscsi_cdblen = 6;
404       cmd->ucmd.uscsi_bufaddr = (caddr_t) cmd->_sense;
405       cmd->ucmd.uscsi_buflen = sizeof (cmd->_sense);
406       cmd->ucmd.uscsi_flags = USCSI_SILENT | USCSI_DIAGNOSE |
407         USCSI_ISOLATE | USCSI_READ;
408       cmd->ucmd.uscsi_timeout = 1;
409       ret = ioctl (cmd->fd, USCSICMD, &cmd->ucmd);
410       cmd->ucmd.uscsi_resid = residue;
411       if (ret)
412         return -1;
413     }
414     ret = ERRCODE (cmd->_sense);
415     if (ret == 0)
416       ret = -1;
417     /*else  CREAM_ON_ERRNO(_sense); */
418   }
419   return ret;
420 }
421 
422 #elif defined(HAVE_STRUCT_CAM_DEVICE)
423 
424 #include <camlib.h>
425 #include <dirent.h>
426 
427 #include <sys/wait.h>
428 #include <sys/ioctl.h>
429 #include <sys/mount.h>
430 #include <sys/param.h>
431 
432 #include <bus/cam/scsi/scsi_message.h>
433 #include <bus/cam/scsi/scsi_pass.h>
434 
435 typedef off_t off64_t;
436 #define stat64   stat
437 #define fstat64  fstat
438 #define open64   open
439 #define pread64  pread
440 #define pwrite64 pwrite
441 #define lseek64  lseek
442 
443 #define ioctl_fd (((struct cam_device *)ioctl_handle)->fd)
444 
445 struct _Scsi_Command
446 {
447   int fd;
448   int autoclose;
449   char *filename;
450   struct cam_device *cam;
451   union ccb ccb;
452 };
453 
454 Scsi_Command *
scsi_command_new_from_fd(int f)455 scsi_command_new_from_fd (int f)
456 {
457   Scsi_Command *cmd;
458 
459   char pass[32]; /* periph_name is 16 chars long */
460 
461   cmd = (Scsi_Command *) malloc (sizeof (Scsi_Command));
462   cmd->fd = -1;
463   cmd->autoclose = 1;
464   cmd->filename = NULL;
465 
466   memset (&cmd->ccb, 0, sizeof (cmd->ccb));
467   cmd->ccb.ccb_h.func_code = XPT_GDEVLIST;
468   if (ioctl (f, CAMGETPASSTHRU, &cmd->ccb) < 0)
469   {
470     free (cmd);
471     return NULL;
472   }
473 
474   sprintf (pass, "/dev/%.15s%u", cmd->ccb.cgdl.periph_name,
475            cmd->ccb.cgdl.unit_number);
476   cmd->cam = cam_open_pass (pass, O_RDWR, NULL);
477 
478   return cmd;
479 }
480 
481 void
scsi_command_free(Scsi_Command * cmd)482 scsi_command_free (Scsi_Command *cmd)
483 {
484   if (cmd->cam && cmd->autoclose)
485   {
486     cam_close_device (cmd->cam);
487     cmd->cam = NULL;
488   }
489 
490   if (cmd->fd >= 0)
491     close (cmd->fd);
492 
493   if (cmd->filename)
494   {
495     free (cmd->filename);
496     cmd->filename = NULL;
497   }
498 
499   free (cmd);
500 }
501 
502 void
scsi_command_set(Scsi_Command * cmd,size_t index,unsigned char value)503 scsi_command_set (Scsi_Command *cmd, size_t index, unsigned char value)
504 {
505   if (index == 0)
506   {
507     memset (&cmd->ccb, 0, sizeof (cmd->ccb));
508     cmd->ccb.ccb_h.path_id = cmd->cam->path_id;
509     cmd->ccb.ccb_h.target_id = cmd->cam->target_id;
510     cmd->ccb.ccb_h.target_lun = cmd->cam->target_lun;
511     cam_fill_csio (&(cmd->ccb.csio), 1,  /* retries */
512                    NULL,      /* cbfncp */
513                    CAM_DEV_QFRZDIS, /* flags */
514                    MSG_SIMPLE_Q_TAG,  /* tag_action */
515                    NULL,      /* data_ptr */
516                    0,         /* dxfer_len */
517                    sizeof (cmd->ccb.csio.sense_data),  /* sense_len */
518                    0,         /* cdb_len */
519                    30 * 1000);  /* timeout */
520   }
521   cmd->ccb.csio.cdb_len = index + 1;
522   cmd->ccb.csio.cdb_io.cdb_bytes[index] = value;
523 }
524 
525 static const int real_dir[] =
526 {
527   CAM_DIR_NONE,
528   CAM_DIR_IN,
529   CAM_DIR_OUT
530 };
531 
532 int
scsi_command_transport(Scsi_Command * cmd,Direction dir,void * buf,size_t sz)533 scsi_command_transport (Scsi_Command *cmd, Direction dir, void *buf, size_t sz)
534 {
535   int ret = 0;
536 
537   cmd->ccb.csio.ccb_h.flags |= real_dir[dir];
538   cmd->ccb.csio.data_ptr = (u_int8_t *) buf;
539   cmd->ccb.csio.dxfer_len = sz;
540 
541   if ((ret = cam_send_ccb (cmd->cam, &cmd->ccb)) < 0)
542     return -1;
543 
544   if ((cmd->ccb.ccb_h.status & CAM_STATUS_MASK) == CAM_REQ_CMP)
545     return 0;
546 
547   unsigned char *sense = (unsigned char *) &cmd->ccb.csio.sense_data;
548 
549   errno = EIO;
550   /* FreeBSD 5-CURRENT since 2003-08-24, including 5.2 fails to
551      pull sense data automatically, at least for ATAPI transport,
552      so I reach for it myself... */
553   if ((cmd->ccb.csio.scsi_status == SCSI_STATUS_CHECK_COND) &&
554       !(cmd->ccb.ccb_h.status & CAM_AUTOSNS_VALID))
555   {
556     u_int8_t _sense[18];
557     u_int32_t resid = cmd->ccb.csio.resid;
558 
559     memset (_sense, 0, sizeof (_sense));
560 
561     scsi_command_set (cmd, 0, 0x03); /* REQUEST SENSE */
562     cmd->ccb.csio.cdb_io.cdb_bytes[4] = sizeof (_sense);
563     cmd->ccb.csio.cdb_len = 6;
564     cmd->ccb.csio.ccb_h.flags |= CAM_DIR_IN | CAM_DIS_AUTOSENSE;
565     cmd->ccb.csio.data_ptr = _sense;
566     cmd->ccb.csio.dxfer_len = sizeof (_sense);
567     cmd->ccb.csio.sense_len = 0;
568     ret = cam_send_ccb (cmd->cam, &cmd->ccb);
569 
570     cmd->ccb.csio.resid = resid;
571     if (ret < 0)
572       return -1;
573     if ((cmd->ccb.ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP)
574       return errno = EIO, -1;
575 
576     memcpy (sense, _sense, sizeof (_sense));
577   }
578 
579   ret = ERRCODE (sense);
580   if (ret == 0)
581     ret = -1;
582   else
583     CREAM_ON_ERRNO (sense);
584 
585   return ret;
586 }
587 
588 #endif
589 
590