Logo Search packages:      
Sourcecode: qpxtool version File versions  Download package

transport.cpp

//
// This is part of dvd+rw-tools by Andy Polyakov <appro@fy.chalmers.se>
//
// Use-it-on-your-own-risk, GPL bless...
//
// For further details see http://fy.chalmers.se/~appro/linux/DVD+RW/
//

#if defined(__unix) || defined(__unix__)
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <poll.h>
#include <sys/time.h>

#include "transport.h"

inline long getmsecs()
{ struct timeval tv;
    gettimeofday (&tv,NULL);
  return tv.tv_sec*1000+tv.tv_usec/1000;
}

#include <errno.h>

#ifndef EMEDIUMTYPE
#define EMEDIUMTYPE     EINVAL
#endif
#ifndef     ENOMEDIUM
#define     ENOMEDIUM   ENODEV
#endif

#elif defined(_WIN32)
#include <windows.h>
#include <stdio.h>

#define EINVAL          ERROR_BAD_ARGUMENTS
#define ENOMEM          ERROR_OUTOFMEMORY
#define EMEDIUMTYPE     ERROR_MEDIA_INCOMPATIBLE
#define ENOMEDIUM ERROR_MEDIA_OFFLINE
#define ENODEV          ERROR_BAD_COMMAND
#define EAGAIN          ERROR_NOT_READY
#define ENOSPC          ERROR_DISK_FULL
#define EIO       ERROR_NOT_SUPPORTED
#define ENXIO           ERROR_GEN_FAILURE

static class _win32_errno {
    public:
      operator int()          { return GetLastError(); }
      int operator=(int e)    { SetLastError(e); return e; }
} _sys_errno;
#ifdef errno
#undef errno
#endif
#define errno _sys_errno

inline void perror (const char *str)
{ LPVOID lpMsgBuf;

    FormatMessage( 
      FORMAT_MESSAGE_ALLOCATE_BUFFER |
      FORMAT_MESSAGE_FROM_SYSTEM | 
      FORMAT_MESSAGE_IGNORE_INSERTS,
      NULL,
      GetLastError(),
      0, // Default language
      (LPTSTR) &lpMsgBuf,
      0,
      NULL 
      );
    if (str)
      fprintf (stderr,"%s: %s",str,lpMsgBuf);
    else
      fprintf (stderr,"%s",lpMsgBuf);

    LocalFree(lpMsgBuf);
}

#define poll(a,b,t)     Sleep(t)
#define getmsecs()      GetTickCount()
#define exit(e)         ExitProcess(e)

#endif

#define CREAM_ON_ERRNO_NAKED(s)                       \
    switch ((s)[12])                            \
    { case 0x04:  errno=EAGAIN;     break;            \
      case 0x20:  errno=ENODEV;     break;            \
      case 0x21:  if ((s)[13]==0)   errno=ENOSPC;     \
                  else        errno=EINVAL;     \
                  break;                        \
      case 0x30:  errno=EMEDIUMTYPE;      break;      \
      case 0x3A:  errno=ENOMEDIUM;  break;      \
    }
#define CREAM_ON_ERRNO(s)     do { CREAM_ON_ERRNO_NAKED(s) } while(0)

#define     FATAL_START(er)   (0x80|(er))
#define ERRCODE(s)      ((((s)[2]&0x0F)<<16)|((s)[12]<<8)|((s)[13]))
#define     SK(errcode) (((errcode)>>16)&0xF)
#define     ASC(errcode)      (((errcode)>>8)&0xFF)
#define ASCQ(errcode)   ((errcode)&0xFF)

void sperror (const char *cmd,int err)
{ int saved_errno=errno;

    if (err==-1)
      fprintf (stderr,":-( unable to %s: ",cmd);
    else
      fprintf (stderr,":-[ %s failed with SK=%Xh/ASC=%02Xh/ACQ=%02Xh]: ",
                  cmd,SK(err),ASC(err),ASCQ(err));
    errno=saved_errno, perror (NULL);
}

autofree::autofree()
      { ptr=NULL; }
autofree::~autofree()
      { if (ptr) free(ptr); }

#if defined(__linux)

#include <sys/ioctl.h>
#include <linux/cdrom.h>
#include <mntent.h>
#include <sys/wait.h>
#include <sys/utsname.h>
#include <scsi/sg.h>
#if !defined(SG_FLAG_LUN_INHIBIT)
# if defined(SG_FLAG_UNUSED_LUN_INHIBIT)
#  define SG_FLAG_LUN_INHIBIT SG_FLAG_UNUSED_LUN_INHIBIT
# else
#  define SG_FLAG_LUN_INHIBIT 0
# endif
#endif
#ifndef CHECK_CONDITION
#define CHECK_CONDITION 0x01
#endif

#ifdef SG_IO

USE_SG_IO::USE_SG_IO()
{ struct utsname buf;
      uname (&buf);
      // was CDROM_SEND_PACKET declared dead in 2.5?
      yes_or_no=(strcmp(buf.release,"2.5.43")>=0);
}

USE_SG_IO::~USE_SG_IO(){}

#endif
    Scsi_Command::Scsi_Command()    { fd=-1, autoclose=1; filename=NULL; }
    Scsi_Command::Scsi_Command(int f)     { fd=f,  autoclose=0; filename=NULL; }
    Scsi_Command::Scsi_Command(void*f){ fd=(long)f, autoclose=0; filename=NULL; }
    Scsi_Command::~Scsi_Command()   { if (fd>=0 && autoclose) close(fd),fd=-1;
                    if (filename) free(filename),filename=NULL;
                  }
    int Scsi_Command::associate (const char *file,const struct stat *ref=NULL)
    { struct stat sb;

      /*
       * O_RDWR is expected to provide for none set-root-uid
       * execution under Linux kernel 2.6[.8]. Under 2.4 it
       * falls down to O_RDONLY...
       */
      if ((fd=open (file,O_RDWR|O_NONBLOCK)) < 0 &&
          (fd=open (file,O_RDONLY|O_NONBLOCK)) < 0)   return 0;
      if (fstat(fd,&sb) < 0)                    return 0;
      if (!S_ISBLK(sb.st_mode))     { errno=ENOTBLK;return 0; }

      if (ref && (!S_ISBLK(ref->st_mode) || ref->st_rdev!=sb.st_rdev))
      {   errno=ENXIO; return 0;   }

      filename=strdup(file);

      return 1;
    }
    void Scsi_Command::timeout(int i)           { cgc.timeout=sg_io.timeout=i*1000; }
#ifdef SG_IO
    size_t Scsi_Command::residue()        { return use_sg_io?sg_io.resid:0; }
#else
    size_t Scsi_Command::residue()        { return 0; }
#endif
    int Scsi_Command::transport(Direction dir=NONE,void *buf=NULL,size_t sz=0)
    { int ret = 0;

#ifdef SG_IO
#define KERNEL_BROKEN 0
      if (use_sg_io)
      {   sg_io.dxferp        = buf;
          sg_io.dxfer_len           = sz;
          sg_io.dxfer_direction     = use_sg_io[dir];
          if (ioctl (fd,SG_IO,&sg_io)) return -1;

#if !KERNEL_BROKEN
          if ((sg_io.info&SG_INFO_OK_MASK) != SG_INFO_OK)
#else
          if (sg_io.status)
#endif
          { errno=EIO; ret=-1;
#if !KERNEL_BROKEN
            if (sg_io.masked_status&CHECK_CONDITION)
#endif
            {   ret = ERRCODE(sg_io.sbp);
                if (ret==0) ret=-1;
                else    CREAM_ON_ERRNO(sg_io.sbp);
            }
          }
          return ret;
      }
      else
#undef KERNEL_BROKEN
#endif
      {   cgc.buffer          = (unsigned char *)buf;
          cgc.buflen          = sz;
          cgc.data_direction  = dir;
          if (ioctl (fd,CDROM_SEND_PACKET,&cgc))
          { ret = ERRCODE(_sense.u);
            if (ret==0) ret=-1;
          }
      }
      return ret;
    }
    int Scsi_Command::umount(int f=-1)
    { struct stat    fsb,msb;
      struct mntent *mb;
      FILE          *fp;
      pid_t          pid,rpid;
      int            ret=0,rval;

      if (f==-1) f=fd;
      if (fstat (f,&fsb) < 0)                   return -1;
      if ((fp=setmntent ("/proc/mounts","r"))==NULL)  return -1;

      while ((mb=getmntent (fp))!=NULL)
      {   if (stat (mb->mnt_fsname,&msb) < 0) continue; // corrupted line?
          if (msb.st_rdev == fsb.st_rdev)
          { ret = -1;
            if ((pid = fork()) == (pid_t)-1)    break;
            if (pid == 0) execl ("/bin/umount","umount",mb->mnt_dir,NULL);
            while (1)
            {   rpid = waitpid (pid,&rval,0);
                if (rpid == (pid_t)-1)
                { if (errno==EINTR) continue;
                  else              break;
                }
                else if (rpid != pid)
                { errno = ECHILD;
                  break;
                }
                if (WIFEXITED(rval))
                { if (WEXITSTATUS(rval) == 0) ret=0;
                  else                  errno=EBUSY; // most likely
                  break;
                }
                else
                { errno = ENOLINK;  // some phony errno
                  break;
                }
            }
            break;
          }
      }
      endmntent (fp);

      return ret;
    }
    int Scsi_Command::is_reload_needed ()
    { return ioctl (fd,CDROM_MEDIA_CHANGED,CDSL_CURRENT) == 0;   }

#elif defined(__OpenBSD__) || defined(__NetBSD__)

#include <sys/ioctl.h>
#include <sys/scsiio.h>
#include <sys/wait.h>
#include <sys/param.h>
#include <sys/mount.h>

typedef off_t off64_t;
#define stat64   stat
#define fstat64  fstat
#define open64   open
#define pread64    pread
#define pwrite64 pwrite
#define lseek64  lseek

    Scsi_Command::Scsi_Command()    { fd=-1, autoclose=1; filename=NULL; }
    Scsi_Command::Scsi_Command(int f)     { fd=f,  autoclose=0; filename=NULL; }
    Scsi_Command::Scsi_Command(void*f){ fd=(long)f, autoclose=0; filename=NULL; }
    Scsi_Command::~Scsi_Command()   { if (fd>=0 && autoclose) close(fd),fd=-1;
                    if (filename) free(filename),filename=NULL;
                  }
    int Scsi_Command::associate (const char *file,const struct stat *ref=NULL)
    { struct stat sb;

      fd=open(file,O_RDWR|O_NONBLOCK);
      // this is --^^^^^^-- why we have to run set-root-uid...

      if (fd < 0)                         return 0;
      if (fstat(fd,&sb) < 0)                    return 0;
      if (!S_ISCHR(sb.st_mode))     { errno=EINVAL; return 0; }

      if (ref && (!S_ISCHR(ref->st_mode) || ref->st_rdev!=sb.st_rdev))
      {   errno=ENXIO; return 0;   }

      filename=strdup(file);

      return 1;
    }
    void Scsi_Command::timeout(int i)                 { req.timeout=i*1000; }
    size_t Scsi_Command::residue()              { return req.datalen-req.datalen_used; }
    int Scsi_Command::transport(Direction dir=NONE,void *buf=NULL,size_t sz=0)
    { int ret=0;

      req.databuf = (caddr_t)buf;
      req.datalen = sz;
      req.flags |= dir;
      if (ioctl (fd,SCIOCCOMMAND,&req) < 0)     return -1;
      if (req.retsts==SCCMD_OK)           return 0;

      errno=EIO; ret=-1;
      if (req.retsts==SCCMD_SENSE)
      {   ret = ERRCODE(req.sense);
          if (ret==0) ret=-1;
          else    CREAM_ON_ERRNO(req.sense);
      }
      return ret;
    }
    // this code is basically redundant... indeed, we normally want to
    // open device O_RDWR, but we can't do that as long as it's mounted.
    // in other words, whenever this routine is invoked, device is not
    // mounted, so that it could as well just return 0;
    int Scsi_Command::umount(int f=-1)
    { struct stat    fsb,msb;
      struct statfs *mntbuf;
      int            ret=0,mntsize,i;

      if (f==-1) f=fd;

      if (fstat (f,&fsb) < 0)                   return -1;
      if ((mntsize=getmntinfo(&mntbuf,MNT_NOWAIT))==0)return -1;

      for (i=0;i<mntsize;i++)
      { char rdev[MNAMELEN+1],*slash,*rslash;

          mntbuf[i].f_mntfromname[MNAMELEN-1]='\0';   // paranoia
          if ((slash=strrchr (mntbuf[i].f_mntfromname,'/'))==NULL) continue;
          strcpy (rdev,mntbuf[i].f_mntfromname); // rdev is 1 byte larger!
          rslash = strrchr  (rdev,'/');
          *(rslash+1) = 'r', strcpy (rslash+2,slash+1);
          if (stat (rdev,&msb) < 0) continue;
          if (msb.st_rdev == fsb.st_rdev)
          { ret=unmount (mntbuf[i].f_mntonname,0);
            break;
            }
      }

      return ret;
    }
    int Scsi_Command::is_reload_needed ()
    { return 1;   }

#elif defined(__FreeBSD__)

#include <sys/ioctl.h>
#include <camlib.h>
#include <cam/scsi/scsi_message.h>
#include <cam/scsi/scsi_pass.h>
#include <sys/wait.h>
#include <sys/param.h>
#include <sys/mount.h>
#include <dirent.h>

typedef off_t off64_t;
#define stat64   stat
#define fstat64  fstat
#define open64   open
#define pread64  pread
#define pwrite64 pwrite
#define lseek64  lseek

#define ioctl_fd (((struct cam_device *)ioctl_handle)->fd)

    Scsi_Command::Scsi_Command()
    { cam=NULL, fd=-1, autoclose=1; filename=NULL;   }
    Scsi_Command::Scsi_Command(int f)
    { char pass[32];    // periph_name is 16 chars long

      cam=NULL, fd=-1, autoclose=1, filename=NULL;

      memset (&ccb,0,sizeof(ccb));
      ccb.ccb_h.func_code = XPT_GDEVLIST;
      if (ioctl (f,CAMGETPASSTHRU,&ccb) < 0) return;

      sprintf (pass,"/dev/%.15s%u",ccb.cgdl.periph_name,ccb.cgdl.unit_number);
      cam=cam_open_pass (pass,O_RDWR,NULL);
    }
    Scsi_Command::Scsi_Command(void *f)
    { cam=(struct cam_device *)f, autoclose=0; fd=-1; filename=NULL;  }
    Scsi_Command::~Scsi_Command()
    { if (cam && autoclose)   cam_close_device(cam), cam=NULL;
      if (fd>=0)        close(fd);
      if (filename)           free(filename), filename=NULL;
    }

    int Scsi_Command::associate (const char *file,const struct stat *ref=NULL)
    { struct stat sb;
      char pass[32];          // periph_name is 16 chars long

      fd=open(file,O_RDONLY|O_NONBLOCK);

      // all if (ref) code is actually redundant, it never runs
      // as long as RELOAD_NEVER_NEEDED...
      if (ref && fd<0 && errno==EPERM)
      {   // expectedly we would get here if file is /dev/passN
          if (stat(file,&sb) < 0)         return 0;
          if (!S_ISCHR(ref->st_mode) || ref->st_rdev!=sb.st_rdev)
            return (errno=ENXIO,0);
          fd=open(file,O_RDWR);
      }

      if (fd < 0)                   return 0;
      if (fstat(fd,&sb) < 0)              return 0;
      if (!S_ISCHR(sb.st_mode))           return (errno=EINVAL,0);

      if (ref && (!S_ISCHR(ref->st_mode) || ref->st_rdev!=sb.st_rdev))
          return (errno=ENXIO,0);

      memset (&ccb,0,sizeof(ccb));
      ccb.ccb_h.func_code = XPT_GDEVLIST;
      if (ioctl(fd,CAMGETPASSTHRU,&ccb)<0)      return (close(fd),fd=-1,0);

      sprintf (pass,"/dev/%.15s%u",ccb.cgdl.periph_name,ccb.cgdl.unit_number);
      cam=cam_open_pass (pass,O_RDWR,NULL);
      if (cam==NULL)                      return (close(fd),fd=-1,0);

      filename=strdup(file);

      return 1;
    }
    void Scsi_Command::timeout(int i)     { ccb.ccb_h.timeout=i*1000; }
    size_t Scsi_Command::residue()  { return ccb.csio.resid; }
    int Scsi_Command::transport(Direction dir=NONE,void *buf=NULL,size_t sz=0)
    { int ret=0;

      ccb.csio.ccb_h.flags |= dir;
      ccb.csio.data_ptr  = (u_int8_t *)buf;
      ccb.csio.dxfer_len = sz;

      if ((ret = cam_send_ccb(cam, &ccb)) < 0)
          return -1;

      if ((ccb.ccb_h.status & CAM_STATUS_MASK) == CAM_REQ_CMP)
          return 0;

      unsigned char  *sense=(unsigned char *)&ccb.csio.sense_data;

      errno = EIO;
      // FreeBSD 5-CURRENT since 2003-08-24, including 5.2 fails to
      // pull sense data automatically, at least for ATAPI transport,
      // so I reach for it myself...
      if ((ccb.csio.scsi_status==SCSI_STATUS_CHECK_COND) &&
          !(ccb.ccb_h.status&CAM_AUTOSNS_VALID))
      {   u_int8_t  _sense[18];
          u_int32_t resid=ccb.csio.resid;

          memset(_sense,0,sizeof(_sense));

          operator[](0)      = 0x03;      // REQUEST SENSE
          ccb.csio.cdb_io.cdb_bytes[4] = sizeof(_sense);
          ccb.csio.cdb_len   = 6;
          ccb.csio.ccb_h.flags |= CAM_DIR_IN|CAM_DIS_AUTOSENSE;
          ccb.csio.data_ptr  = _sense;
          ccb.csio.dxfer_len = sizeof(_sense);
          ccb.csio.sense_len = 0;
          ret = cam_send_ccb(cam, &ccb);

          ccb.csio.resid = resid;
          if (ret<0)    return -1;
          if ((ccb.ccb_h.status&CAM_STATUS_MASK) != CAM_REQ_CMP)
            return errno=EIO,-1;

          memcpy(sense,_sense,sizeof(_sense));
      }

      ret = ERRCODE(sense);
      if (ret == 0)     ret = -1;
      else        CREAM_ON_ERRNO(sense);

      return ret;
    }
    int Scsi_Command::umount(int f=-1)
    { struct stat    fsb,msb;
      struct statfs *mntbuf;
      int            ret=0,mntsize,i;

      if (f==-1) f=fd;

      if (fstat (f,&fsb) < 0)                   return -1;
      if ((mntsize=getmntinfo(&mntbuf,MNT_NOWAIT))==0)return -1;

      for (i=0;i<mntsize;i++)
      {   if (stat (mntbuf[i].f_mntfromname,&msb) < 0) continue;
          if (msb.st_rdev == fsb.st_rdev)
          { ret=unmount (mntbuf[i].f_mntonname,0);
            break;
          }
      }

      return ret;
    }
#define RELOAD_NEVER_NEEDED   // according to Matthew Dillon
    int Scsi_Command::is_reload_needed ()
    {  return 0;   }

#else
#error "Unsupported OS"
#endif

#undef ERRCODE
#undef CREAM_ON_ERRNO
#undef CREAM_ON_ERRNO_NAKED

Generated by  Doxygen 1.6.0   Back to index