diff --git a/Makefile.am b/Makefile.am index b0d4f19..8d9c87d 100644 --- a/Makefile.am +++ b/Makefile.am @@ -203,10 +203,12 @@ EXTRA_DIST = \ libburn/os-freebsd.h \ libburn/os-linux.h \ libburn/os-libcdio.h \ + libburn/os-solaris.h \ libburn/sg-dummy.c \ libburn/sg-freebsd.c \ libburn/sg-linux.c \ libburn/sg-libcdio.c \ + libburn/sg-solaris.c \ COPYING \ NEWS \ ChangeLog \ diff --git a/README b/README index 7fd4206..75fe376 100644 --- a/README +++ b/README @@ -105,8 +105,9 @@ The project comprises of several more or less interdependent parts which together strive to be a usable foundation for application development. These are libraries, language bindings, and middleware binaries which emulate classical (and valuable) Linux tools. -Currently it is supported on Linux with kernels >= 2.4 and on FreeBSD versions -with ATAPI/CAM support enabled in the kernel, see atapicam(4). +Currently it is supported on GNU/Linux with kernels >= 2.4, +on FreeBSD with ATAPI/CAM enabled in the kernel (see man atapicam), +and on OpenSolaris (tested with kernel 5.11). On other X/Open compliant systems there will only be pseudo drives, but no direct MMC operation on real CD/DVD/BD drives. diff --git a/cdrskin/README b/cdrskin/README index 55088b8..7c9fd1e 100644 --- a/cdrskin/README +++ b/cdrskin/README @@ -13,7 +13,8 @@ Copyright (C) 2006-2010 Thomas Schmitt, provided under GPL version 2 or later. cdrskin is a limited cdrecord compatibility wrapper which allows to use most of the libburn features from the command line. -Currently it is supported on Linux with kernels >= 2.4 and on FreeBSD. +Currently it is supported on GNU/Linux with kernels >= 2.4, +on FreeBSD and on OpenSolaris. IDE drives under Linux 2.4. need kernel module ide-scsi. ATA and SATA drives under FreeBSD need kernel module atapicam. On other X/Open compliant systems there will only be emulated drives, but no diff --git a/cdrskin/cdrskin_eng.html b/cdrskin/cdrskin_eng.html index dcc4b51..ed906f7 100644 --- a/cdrskin/cdrskin_eng.html +++ b/cdrskin/cdrskin_eng.html @@ -269,8 +269,9 @@ Bug fixes towards cdrskin-0.7.8.pl00:

Development snapshot, version 0.8.1 :

Enhancements towards current stable version 0.8.0.pl00: @@ -278,8 +279,14 @@ Bug fixes towards cdrskin-0.7.8.pl00:
Bug fixes towards cdrskin-0.8.0.pl00:
diff --git a/cdrskin/cdrskin_timestamp.h b/cdrskin/cdrskin_timestamp.h index 9f7f569..9929168 100644 --- a/cdrskin/cdrskin_timestamp.h +++ b/cdrskin/cdrskin_timestamp.h @@ -1 +1 @@ -#define Cdrskin_timestamP "2010.06.07.171706" +#define Cdrskin_timestamP "2010.06.07.172925" diff --git a/libburn/libdax_msgs.h b/libburn/libdax_msgs.h index 061bd7c..1b34b85 100644 --- a/libburn/libdax_msgs.h +++ b/libburn/libdax_msgs.h @@ -434,6 +434,8 @@ Range "scdbackup" : 0x00020000 to 0x0002ffff 0x00020009 (SORRY,HIGH) = Neither stdio-path nor its directory exist 0x0002000a (FAILURE,HIGH) = Cannot accept '...' as SG_IO CDROM drive 0x0002000b (FAILURE,HIGH) = File object '...' not found + 0x0002000c (FAILURE,HIGH) = Cannot start device file enumeration + 0x0002000d (FAILURE,HIGH) = Cannot enumerate next device General library operations: diff --git a/libburn/os-solaris.h b/libburn/os-solaris.h new file mode 100644 index 0000000..f0b1847 --- /dev/null +++ b/libburn/os-solaris.h @@ -0,0 +1,68 @@ + +/* os-libcdio.h + Operating system specific libburn definitions and declarations. Included + by os.h in case of compilation for + Solaris based systems, e.g. SunOS 5.11 + with Solaris uscsi MMC transport adapter sg-solaris.c + + Copyright (C) 2010 Thomas Schmitt , provided under GPLv2+ +*/ + + +/** List of all signals which shall be caught by signal handlers and trigger + a graceful abort of libburn. (See man signal.h) +*/ +/* Once as system defined macros */ +#define BURN_OS_SIGNAL_MACRO_LIST \ + SIGHUP, SIGINT, SIGQUIT, SIGILL, SIGABRT, \ + SIGFPE, SIGSEGV, SIGPIPE, SIGALRM, SIGTERM, \ + SIGUSR1, SIGUSR2, SIGXCPU, SIGTSTP, SIGTTIN, \ + SIGTTOU + +/* Once as text 1:1 list of strings for messages and interpreters */ +#define BURN_OS_SIGNAL_NAME_LIST \ + "SIGHUP", "SIGINT", "SIGQUIT", "SIGILL", "SIGABRT", \ + "SIGFPE", "SIGSEGV", "SIGPIPE", "SIGALRM", "SIGTERM", \ + "SIGUSR1", "SIGUSR2", "SIGXCPU", "SIGTSTP", "SIGTTIN", \ + "SIGTTOU" + +/* The number of above list items */ +#define BURN_OS_SIGNAL_COUNT 16 + +/** To list all signals which shall surely not be caught */ +#define BURN_OS_NON_SIGNAL_MACRO_LIST \ +SIGKILL, SIGCHLD, SIGSTOP, SIGURG, SIGWINCH + +/* The number of above list items */ +#define BURN_OS_NON_SIGNAL_COUNT 5 + + +/* The maximum size for a (SCSI) i/o transaction */ +/* Important : MUST be at least 32768 ! */ +/* My Blu-ray burner LG GGW-H20 writes junk if stream recording is combined + with buffer size 32 kB. So stream recording is allowed only with size 64k. + + >>> ??? is 64 kB ok for Solaris uscsi ? +*/ +#define BURN_OS_TRANSPORT_BUFFER_SIZE 65536 + + +/* >>> */ + +/* To hold the position of the most recently delivered address from + device enumeration. +*/ +struct burn_drive_enumerator_struct { + void *dir; +}; + +#define BURN_OS_DEFINE_DRIVE_ENUMERATOR_T \ +typedef struct burn_drive_enumerator_struct burn_drive_enumerator_t; + + +/* The list of operating system dependent elements in struct burn_drive. + Usually they are initialized in sg-*.c:enumerate_common(). +*/ +#define BURN_OS_TRANSPORT_DRIVE_ELEMENTS \ + int fd; + diff --git a/libburn/os.h b/libburn/os.h index 376650a..9fc6149 100644 --- a/libburn/os.h +++ b/libburn/os.h @@ -14,6 +14,14 @@ */ +#ifdef Libburn_use_sg_dummY + + +/* --------- Any other system. With dummy MMC transport sg-dummy.c --------- */ +#include "os-dummy.h" + + +#else #ifdef Libburn_use_libcdiO @@ -45,6 +53,14 @@ #include "os-linux.h" +#else +#ifdef __sun + + +/* ------- Solaris (e.g. SunOS 5.11) with uscsi ------ */ +#include "os-solaris.h" + + #else @@ -52,10 +68,12 @@ #include "os-dummy.h" +#endif /* ! __sun*/ #endif /* ! __linux */ #endif /* ! __FreeBSD__kernel__ */ #endif /* ! __FreeBSD__ */ #endif /* ! Libburn_use_libcdiO */ +#endif /* ! Libburn_use_sg_dummY */ #endif /* ! BURN_OS_H_INCLUDED */ diff --git a/libburn/sg-solaris.c b/libburn/sg-solaris.c new file mode 100644 index 0000000..784f565 --- /dev/null +++ b/libburn/sg-solaris.c @@ -0,0 +1,892 @@ +/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ + +/* + Copyright (c) 2010 Thomas Schmitt + Provided under GPL version 2 or later. +*/ + + +/* + +This is the main operating system dependent SCSI part of libburn. It implements +the transport level aspects of SCSI control and command i/o. + +Present implementation: Solaris uscsi, e.g. for SunOS 5.11 + + +PORTING: + +Porting libburn typically will consist of adding a new operating system case +to the following switcher files: + os.h Operating system specific libburn definitions and declarations. + sg.c Operating system dependent transport level modules. +and of deriving the following system specific files from existing examples: + os-*.h Included by os.h. You will need some general system knowledge + about signals and knowledge about the storage object needs of your + transport level module sg-*.c. + + sg-*.c This source module. You will need special system knowledge about + how to detect all potentially available drives, how to open them, + eventually how to exclusively reserve them, how to perform + SCSI transactions, how to inquire the (pseudo-)SCSI driver. + You will not need to care about CD burning, MMC or other high-level + SCSI aspects. + +Said sg-*.c operations are defined by a public function interface, which has +to be implemented in a way that provides libburn with the desired services: + +sg_id_string() returns an id string of the SCSI transport adapter. + It may be called before initialization but then may + return only a preliminary id. + +sg_initialize() performs global initialization of the SCSI transport + adapter and eventually needed operating system + facilities. Checks for compatibility of supporting + software components. + +sg_shutdown() performs global finalizations and releases golbally + aquired resources. + +sg_give_next_adr() iterates over the set of potentially useful drive + address strings. + +scsi_enumerate_drives() brings all available, not-whitelist-banned, and + accessible drives into libburn's list of drives. + +sg_dispose_drive() finalizes adapter specifics of struct burn_drive + on destruction. Releases resources which were aquired + underneath scsi_enumerate_drives(). + +sg_drive_is_open() tells wether libburn has the given drive in use. + +sg_grab() opens the drive for SCSI commands and ensures + undisturbed access. + +sg_release() closes a drive opened by sg_grab() + +sg_issue_command() sends a SCSI command to the drive, receives reply, + and evaluates wether the command succeeded or shall + be retried or finally failed. + +sg_obtain_scsi_adr() tries to obtain SCSI address parameters. + + +burn_os_is_2k_seekrw() tells whether the given path leads to a file object + that can be used in 2 kB granularity by lseek(2), + read(2), and possibly write(2) if not read-only.. + E.g. a USB stick or a hard disk. + +burn_os_stdio_capacity() estimates the emulated media space of stdio-drives. + +burn_os_open_track_src() opens a disk file in a way that allows best + throughput with file reading and/or SCSI write command + transmission. + +burn_os_alloc_buffer() allocates a memory area that is suitable for file + descriptors issued by burn_os_open_track_src(). + The buffer size may be rounded up for alignment + reasons. + +burn_os_free_buffer() delete a buffer obtained by burn_os_alloc_buffer(). + +Porting hints are marked by the text "PORTING:". +Send feedback to libburn-hackers@pykix.org . + +*/ + +#ifdef HAVE_CONFIG_H +#include "../config.h" +#endif + + +/** PORTING : ------- OS dependent headers and definitions ------ */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef Libburn_os_has_statvfS +#include +#endif /* Libburn_os_has_stavtfS */ + +#include +#include + +#include + +/* >>> need a size determination call for random access devices. + e.g. USB sticks. +*/ +#ifdef __linux +/* for ioctl(BLKGETSIZE) */ +#include +#include +#endif + + +/* The waiting time before eventually retrying a failed SCSI command. + Before each retry wait Libburn_sg_linux_retry_incR longer than with + the previous one. +*/ +#define Libburn_sg_solaris_retry_usleeP 100000 +#define Libburn_sg_solaris_retry_incR 100000 + + +/** PORTING : ------ libburn portable headers and definitions ----- */ + +#include "transport.h" +#include "drive.h" +#include "sg.h" +#include "spc.h" +#include "sbc.h" +#include "debug.h" +#include "toc.h" +#include "util.h" + +#include "libdax_msgs.h" +extern struct libdax_msgs *libdax_messenger; + + +/* is in portable part of libburn */ +int burn_drive_is_banned(char *device_address); +int burn_drive_resolve_link(char *path, char adr[], + int *recursion_count, int flag); /* drive.c */ + +/* Whether to log SCSI commands: + bit0= log in /tmp/libburn_sg_command_log + bit1= log to stderr + bit2= flush every line +*/ +extern int burn_sg_log_scsi; + + +/* ------------------------------------------------------------------------ */ +/* PORTING: Private definitions. Port only if needed by public functions. */ +/* (Public functions are listed below) */ +/* ------------------------------------------------------------------------ */ + + +/* Storage object is in libburn/init.c + whether to strive for exclusive access to the drive +*/ +extern int burn_sg_open_o_excl; + + +/* ------------------------------------------------------------------------ */ +/* PORTING: Private functions. Port only if needed by public functions */ +/* (Public functions are listed below) */ +/* ------------------------------------------------------------------------ */ + + +static int sg_close_drive(struct burn_drive * d) +{ + if (d->fd != -1) { + close(d->fd); + d->fd = -1; + return 1; + } + return 0; +} + + +static int decode_btl_number(char **cpt, int stopper, int *no) +{ + *no = 0; + for ((*cpt)++; **cpt != stopper; (*cpt)++) { + if (**cpt < '0' || **cpt > '9') + return 0; + *no = *no * 10 + **cpt - '0'; + } + return 1; +} + + +/* Read bus, target, lun from name "cXtYdZs2". + Return 0 if name is not of the desired form. +*/ +static int decode_btl_solaris(char *name, int *busno, int *tgtno, int *lunno, + int flag) +{ + char *cpt; + int ret; + + *busno = *tgtno = *lunno = -1; + cpt = name; + if (*cpt != 'c') + return 0; + ret = decode_btl_number(&cpt, 't', busno); + if (ret <= 0) + return ret; + ret = decode_btl_number(&cpt, 'd', tgtno); + if (ret <= 0) + return ret; + ret = decode_btl_number(&cpt, 's', lunno); + if (ret <= 0) + return ret; + cpt++; + if (*cpt != '2' || *(cpt + 1) != 0) + return 0; + return 1; +} + + +static int start_enum_cXtYdZs2(burn_drive_enumerator_t *idx, int flag) +{ + DIR *dir; + + idx->dir = NULL; + dir = opendir("/dev/rdsk"); + if (dir == NULL) { + libdax_msgs_submit(libdax_messenger, -1, + 0x0002000c, LIBDAX_MSGS_SEV_FAILURE, LIBDAX_MSGS_PRIO_HIGH, + "Cannot start device file enumeration. opendir(\"/dev/rdsk\") failed.", + errno, 0); + return 0; + } + idx->dir = dir; + return 1; +} + + +static int next_enum_cXtYdZs2(burn_drive_enumerator_t *idx, + char adr[], int adr_size, int flag) +{ + int busno, tgtno, lunno, ret, fd = -1; + char volpath[160]; + struct dirent *entry; + struct dk_cinfo cinfo; + DIR *dir; + + dir = idx->dir; + while (1) { + errno = 0; + entry = readdir(dir); + if (entry == NULL) { + if (errno) { + libdax_msgs_submit(libdax_messenger, + -1, 0x0002000d, + LIBDAX_MSGS_SEV_FAILURE, LIBDAX_MSGS_PRIO_HIGH, + "Cannot enumerate next device. readdir() from \"/dev/rdsk\" failed.", + errno, 0); + return -1; + } + break; + } + if (strlen(entry->d_name) > sizeof(volpath) - 11) + continue; + ret = decode_btl_solaris(entry->d_name, + &busno, &tgtno, &lunno, 0); + if (ret <= 0) + continue; /* not cXtYdZs2 */ + + sprintf(volpath, "/dev/rdsk/%s", entry->d_name); + if (burn_drive_is_banned(volpath)) + continue; + + fd = open(volpath, O_RDONLY | O_NDELAY); + if (fd < 0) + continue; + /* See man dkio */ + ret = ioctl(fd, DKIOCINFO, &cinfo); + close(fd); + if (ret < 0) + continue; + if (cinfo.dki_ctype != DKC_CDROM) + continue; + if (adr_size <= strlen(volpath)) + return -1; + strcpy(adr, volpath); + return 1; + } + return 0; +} + + +static int end_enum_cXtYdZs2(burn_drive_enumerator_t *idx, int flag) +{ + DIR *dir; + + dir = idx->dir; + if(dir != NULL) + closedir(dir); + idx->dir = NULL; + return 1; +} + + +/* ----------------------------------------------------------------------- */ +/* PORTING: Private functions which contain publicly needed functionality. */ +/* Their portable part must be performed. So it is probably best */ +/* to replace the non-portable part and to call these functions */ +/* in your port, too. */ +/* ----------------------------------------------------------------------- */ + + +/** Wraps a detected drive into libburn structures and hands it over to + libburn drive list. +*/ +static void enumerate_common(char *fname, + int bus_no, int host_no, + int channel_no, int target_no, int lun_no) +{ + int ret; + struct burn_drive out; + + /* General libburn drive setup */ + burn_setup_drive(&out, fname); + + /* This transport adapter uses SCSI-family commands and models + (seems the adapter would know better than its boss, if ever) */ + ret = burn_scsi_setup_drive(&out, bus_no, host_no, channel_no, + target_no, lun_no, 0); + if (ret <= 0) + return; + + /* PORTING: ------------------- non portable part --------------- */ + + /* Transport adapter is Solaris uscsi */ + /* Adapter specific handles and data */ + out.fd = -1; + + /* PORTING: ---------------- end of non portable part ------------ */ + + /* Adapter specific functions with standardized names */ + out.grab = sg_grab; + out.release = sg_release; + out.drive_is_open = sg_drive_is_open; + out.issue_command = sg_issue_command; + /* Finally register drive and inquire drive information */ + burn_drive_finish_enum(&out); +} + + +/* ------------------------------------------------------------------------ */ +/* PORTING: Public functions. These MUST be ported. */ +/* ------------------------------------------------------------------------ */ + + +/** Returns the id string of the SCSI transport adapter and eventually + needed operating system facilities. + This call is usable even if sg_initialize() was not called yet. In that + case a preliminary constant message might be issued if detailed info is + not available yet. + @param msg returns id string + @param flag unused yet, submit 0 + @return 1 = success, <=0 = failure +*/ +int sg_id_string(char msg[1024], int flag) +{ + sprintf(msg, "internal Solaris uscsi adapter sg-solaris"); + return 1; +} + + +/** Performs global initialization of the SCSI transport adapter and eventually + needed operating system facilities. Checks for compatibility of supporting + software components. + @param msg returns ids and/or error messages of eventual helpers + @param flag unused yet, submit 0 + @return 1 = success, <=0 = failure +*/ +int sg_initialize(char msg[1024], int flag) +{ + return sg_id_string(msg, 0); +} + + +/** Performs global finalization of the SCSI transport adapter and eventually + needed operating system facilities. Releases globally aquired resources. + @param flag unused yet, submit 0 + @return 1 = success, <=0 = failure +*/ +int sg_shutdown(int flag) +{ + return 1; +} + + +/** Finalizes BURN_OS_TRANSPORT_DRIVE_ELEMENTS, the components of + struct burn_drive which are defined in os-*.h. + The eventual initialization of those components was made underneath + scsi_enumerate_drives(). + This will be called when a burn_drive gets disposed. + @param d the drive to be finalized + @param flag unused yet, submit 0 + @return 1 = success, <=0 = failure +*/ +int sg_dispose_drive(struct burn_drive *d, int flag) +{ + return 1; +} + + +/** Returns the next index number and the next enumerated drive address. + The enumeration has to cover all available and accessible drives. It is + allowed to return addresses of drives which are not available but under + some (even exotic) circumstances could be available. It is on the other + hand allowed, only to hand out addresses which can really be used right + in the moment of this call. (This implementation chooses the latter.) + @param idx An opaque handle. Make no own theories about it. + @param adr Takes the reply + @param adr_size Gives maximum size of reply including final 0 + @param initialize 1 = start new, + 0 = continue, use no other values for now + -1 = finish + @return 1 = reply is a valid address , 0 = no further address available + -1 = severe error (e.g. adr_size too small) +*/ +int sg_give_next_adr(burn_drive_enumerator_t *idx, + char adr[], int adr_size, int initialize) +{ + int ret; + + if (initialize == 1) { + ret = start_enum_cXtYdZs2(idx, 0); + if (ret <= 0) + return ret; + } else if (initialize == -1) { + ret = end_enum_cXtYdZs2(idx, 0); + return 0; + } + ret = next_enum_cXtYdZs2(idx, adr, adr_size, 0); + return ret; +} + + +/** Brings all available, not-whitelist-banned, and accessible drives into + libburn's list of drives. +*/ +int scsi_enumerate_drives(void) +{ + burn_drive_enumerator_t idx; + int initialize = 1, ret, i_bus_no = -1; + int i_host_no = -1, i_channel_no = -1, i_target_no = -1, i_lun_no = -1; + char buf[4096]; + + while(1) { + ret = sg_give_next_adr(&idx, buf, sizeof(buf), initialize); + initialize = 0; + if (ret <= 0) + break; + if (burn_drive_is_banned(buf)) + continue; + sg_obtain_scsi_adr(buf, &i_bus_no, &i_host_no, + &i_channel_no, &i_target_no, &i_lun_no); + enumerate_common(buf, + i_bus_no, i_host_no, i_channel_no, + i_target_no, i_lun_no); + } + sg_give_next_adr(&idx, buf, sizeof(buf), -1); + return 1; +} + + +/** Tells whether libburn has the given drive in use or exclusively reserved. + If it is "open" then libburn will eventually call sg_release() on it when + it is time to give up usage resp. reservation. +*/ +/** Published as burn_drive.drive_is_open() */ +int sg_drive_is_open(struct burn_drive * d) +{ + return (d->fd != -1); +} + + +/** Opens the drive for SCSI commands and - if burn activities are prone + to external interference on your system - obtains an exclusive access lock + on the drive. (Note: this is not physical tray locking.) + A drive that has been opened with sg_grab() will eventually be handed + over to sg_release() for closing and unreserving. +*/ +int sg_grab(struct burn_drive *d) +{ + char msg[4096]; + int os_errno, ret; + struct dk_cinfo cinfo; + + if (d->fd != -1) { + d->released = 0; + return 1; + } + d->fd = open(d->devname, O_RDONLY | O_NDELAY); + if (d->fd == -1) { + os_errno = errno; + sprintf(msg, "Could not grab drive '%s'", d->devname); + libdax_msgs_submit(libdax_messenger, d->global_index, + 0x00020003, + LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH, + msg, os_errno, 0); + return 0; + } + ret = ioctl(d->fd, DKIOCINFO, &cinfo); + if (ret < 0) + goto revoke; + if (cinfo.dki_ctype != DKC_CDROM) + goto revoke; + + /* >>> obtain eventual locks */; + + d->released = 0; + return 1; +revoke:; + sprintf(msg, "Could not grab drive '%s'. Not a CDROM device.", + d->devname); + libdax_msgs_submit(libdax_messenger, d->global_index, + 0x00020003, + LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH, + msg, 0, 0); + return 0; +} + + +/** PORTING: Is mainly about the call to sg_close_drive() and whether it + implements the demanded functionality. +*/ +/** Gives up the drive for SCSI commands and releases eventual access locks. + (Note: this is not physical tray locking.) +*/ +int sg_release(struct burn_drive *d) +{ + if (d->fd < 0) { + burn_print(1, "release an ungrabbed drive. die\n"); + return 0; + } + sg_close_drive(d); + return 0; +} + + +/** Sends a SCSI command to the drive, receives reply and evaluates wether + the command succeeded or shall be retried or finally failed. + Returned SCSI errors shall not lead to a return value indicating failure. + The callers get notified by c->error. An SCSI failure which leads not to + a retry shall be notified via scsi_notify_error(). + The Libburn_log_sg_commandS facility might be of help when problems with + a drive have to be examined. It shall stay disabled for normal use. + @return: 1 success , <=0 failure +*/ +int sg_issue_command(struct burn_drive *d, struct command *c) +{ + int i, usleep_time, timeout_ms, no_retry = 0, ret; + time_t start_time; + struct uscsi_cmd cgc; + static FILE *fp = NULL; + + c->error = 0; + memset(c->sense, 0, sizeof(c->sense)); + if (d->fd == -1) + return 0; + + if (burn_sg_log_scsi & 1) { + if (fp == NULL) { + fp= fopen("/tmp/libburn_sg_command_log", "a"); + fprintf(fp, + "\n-----------------------------------------\n"); + } + } + if (burn_sg_log_scsi & 3) + scsi_log_cmd(c,fp,0); + + memset (&cgc, 0, sizeof (struct uscsi_cmd)); + /* No error messages, no retries, + do not execute with other commands, request sense data + */ + cgc.uscsi_flags = USCSI_SILENT | USCSI_DIAGNOSE | USCSI_ISOLATE + | USCSI_RQENABLE; + cgc.uscsi_timeout = 200; + cgc.uscsi_cdb = (caddr_t) c->opcode; + cgc.uscsi_bufaddr = (caddr_t) c->page->data; + if (c->dir == TO_DRIVE) { + cgc.uscsi_flags |= USCSI_WRITE; + cgc.uscsi_buflen = c->page->bytes; + } else if (c->dir == FROM_DRIVE) { + cgc.uscsi_flags |= USCSI_READ; + if (c->dxfer_len >= 0) + cgc.uscsi_buflen = c->dxfer_len; + else + cgc.uscsi_buflen = BUFFER_SIZE; + /* touch page so we can use valgrind */ + memset(c->page->data, 0, BUFFER_SIZE); + } else { + cgc.uscsi_buflen = 0; + } + cgc.uscsi_cdblen = c->oplen; + cgc.uscsi_rqlen = sizeof(c->sense); + cgc.uscsi_rqbuf = (caddr_t) c->sense; + + /* retry-loop */ + start_time = time(NULL); + timeout_ms = 200000; + for(i = 0; ; i++) { + + ret = ioctl(d->fd, USCSICMD, &cgc); + + /* For cgc.uscsi_status see SAM-3 5.3.1, Table 22 + 0 = GOOD , 2 = CHECK CONDITION : Sense Data are delivered + 8 = BUSY + */ + if (ret != 0 && + cgc.uscsi_status != 2 && cgc.uscsi_status != 8) { + libdax_msgs_submit(libdax_messenger, + d->global_index, 0x0002010c, + LIBDAX_MSGS_SEV_FATAL, LIBDAX_MSGS_PRIO_HIGH, + "Failed to transfer command to drive", + errno, 0); + sg_close_drive(d); + d->released = 1; + d->busy = BURN_DRIVE_IDLE; + c->error = 1; + return -1; + } + + c->sense[2] &= 15; + + if (cgc.uscsi_status == 8) { + /* >>> fake BUSY sense */; + } + + /* >>> valid sense: cgc.uscsi_rqlen - cgc.uscsi_rqresid */; + + if (c->sense[2] || c->sense[12] || c->sense[13]) { + if (no_retry || !c->retry) { + c->error = 1; + goto ex; + } + switch (scsi_error(d, c->sense, 18)) { + case RETRY: + if (burn_sg_log_scsi & 3) { + /* >>> Need own duration time + measurement. Then remove bit1 */ + scsi_log_err(c, fp, c->sense, 0, + (c->error != 0) | 2); + scsi_log_cmd(c,fp,0); + } + break; + case FAIL: + c->error = 1; + goto ex; + } + /* + Calming down retries and breaking up endless cycle + */ + usleep_time = Libburn_sg_solaris_retry_usleeP + + i * Libburn_sg_solaris_retry_incR; + if (time(NULL) + usleep_time / 1000000 - start_time > + timeout_ms / 1000 + 1) { + c->error = 1; + goto ex; + } + usleep(usleep_time); + } else + break; /* retry-loop */ + } /* end of retry-loop */ + +ex:; + if (c->error) + scsi_notify_error(d, c, c->sense, 18, 0); + + if (burn_sg_log_scsi & 3) + /* >>> Need own duration time measurement. Then remove bit1 */ + scsi_log_err(c, fp, c->sense, 0, (c->error != 0) | 2); + return 1; +} + +/* >>> */ + +/** Tries to obtain SCSI address parameters. + @return 1 is success , 0 is failure +*/ +int sg_obtain_scsi_adr(char *path, int *bus_no, int *host_no, int *channel_no, + int *target_no, int *lun_no) +{ + int ret; + + /* Try to guess from path */ + if (strncmp("/dev/rdsk/", path, 10) == 0) { + ret = decode_btl_solaris(path + 10, + bus_no, target_no, lun_no, 0); + if (ret > 0) { + *host_no = *bus_no; + *channel_no = 0; + return 1; + } + } + *bus_no = *host_no = *channel_no = *target_no = *lun_no = -1; + + /* >>> Could need a ioctl which gives SCSI numbers */; + + return (0); +} + + +/** Tells wether a text is a persistent address as listed by the enumeration + functions. +*/ +int sg_is_enumerable_adr(char* adr) +{ + burn_drive_enumerator_t idx; + int initialize = 1, ret; + char buf[64]; + + while(1) { + ret = sg_give_next_adr(&idx, buf, sizeof(buf), initialize); + initialize = 0; + if (ret <= 0) + break; + if (strcmp(adr, buf) == 0) { + sg_give_next_adr(&idx, buf, sizeof(buf), -1); + return 1; + } + } + sg_give_next_adr(&idx, buf, sizeof(buf), -1); + return (0); +} + + + +/* Return 1 if the given path leads to a regular file or a device that can be + seeked, read, and possibly written with 2 kB granularity. +*/ +int burn_os_is_2k_seekrw(char *path, int flag) +{ + struct stat stbuf; + + if (stat(path, &stbuf) == -1) + return 0; + if (S_ISREG(stbuf.st_mode)) + return 1; + if (S_ISBLK(stbuf.st_mode)) + return 1; + return 0; +} + + +/** Estimate the potential payload capacity of a file address. + @param path The address of the file to be examined. If it does not + exist yet, then the directory will be inquired. + @param bytes The pointed value gets modified, but only if an estimation is + possible. + @return -2 = cannot perform necessary operations on file object + -1 = neither path nor dirname of path exist + 0 = could not estimate size capacity of file object + 1 = estimation has been made, bytes was set +*/ +int burn_os_stdio_capacity(char *path, off_t *bytes) +{ + struct stat stbuf; + +#ifdef Libburn_os_has_statvfS + struct statvfs vfsbuf; +#endif + + char testpath[4096], *cpt; + long blocks; + off_t add_size = 0; + + testpath[0] = 0; + blocks = *bytes / 512; + if (stat(path, &stbuf) == -1) { + strcpy(testpath, path); + cpt = strrchr(testpath, '/'); + if(cpt == NULL) + strcpy(testpath, "."); + else if(cpt == testpath) + testpath[1] = 0; + else + *cpt = 0; + if (stat(testpath, &stbuf) == -1) + return -1; + +#ifdef __linux + + /* GNU/Linux specific determination of block device size */ + } else if(S_ISBLK(stbuf.st_mode)) { + int open_mode = O_RDONLY, fd, ret; + + fd = open(path, open_mode); + if (fd == -1) + return -2; + ret = ioctl(fd, BLKGETSIZE, &blocks); + close(fd); + if (ret == -1) + return -2; + *bytes = ((off_t) blocks) * (off_t) 512; + +#endif /* __linux */ + + + /* >>> ioctl to determine capacity of block device + ? DKIOCGEXTVTOC , man dkio ? + ? DKIOCEXTPARTINFO , + struct extpart_info.p_length * 512, man dkio ? + */; + + + } else if(S_ISREG(stbuf.st_mode)) { + add_size = stbuf.st_blocks * (off_t) 512; + strcpy(testpath, path); + } else + return 0; + + if (testpath[0]) { + +#ifdef Libburn_os_has_statvfS + + if (statvfs(testpath, &vfsbuf) == -1) + return -2; + *bytes = add_size + ((off_t) vfsbuf.f_frsize) * + (off_t) vfsbuf.f_bavail; + +#else /* Libburn_os_has_statvfS */ + + return 0; + +#endif /* ! Libburn_os_has_stavtfS */ + + } + return 1; +} + + +/* ts A91122 : an interface to open(O_DIRECT) or similar OS tricks. */ + +#ifdef Libburn_read_o_direcT + + /* No special O_DIRECT-like precautions are implemented here */ + +#endif /* Libburn_read_o_direcT */ + + +int burn_os_open_track_src(char *path, int open_flags, int flag) +{ + int fd; + + fd = open(path, open_flags); + return fd; +} + + +void *burn_os_alloc_buffer(size_t amount, int flag) +{ + void *buf = NULL; + + buf = calloc(1, amount); + return buf; +} + + +int burn_os_free_buffer(void *buffer, size_t amount, int flag) +{ + if (buffer == NULL) + return 0; + free(buffer); + return 1; +} + diff --git a/libburn/sg.c b/libburn/sg.c index e855b39..1ee17e6 100644 --- a/libburn/sg.c +++ b/libburn/sg.c @@ -1,7 +1,8 @@ /* sg.c Switcher for operating system dependent transport level modules of libburn. - Copyright (C) 2009 Thomas Schmitt , provided under GPLv2+ + Copyright (C) 2009 - 2010 Thomas Schmitt , + provided under GPLv2+ */ #ifdef HAVE_CONFIG_H @@ -10,6 +11,11 @@ #endif +#ifdef Libburn_use_sg_dummY + +#include "sg-dummy.c" + +#else #ifdef Libburn_use_libcdiO #include "sg-libcdio.c" @@ -29,6 +35,11 @@ #include "sg-linux.c" +#else +#ifdef __sun + +#include "sg-solaris.c" + #else /* The dummy adapter formally fulfills the expectations of libburn towards @@ -51,8 +62,10 @@ static int intentional_compiler_warning(void) #include "sg-dummy.c" +#endif /* ! __sun */ #endif /* ! __linux */ #endif /* ! __FreeBSD_kernel__ */ #endif /* ! __FreeBSD__ */ #endif /* ! Libburn_use_libcdiO */ +#endif /* ! Libburn_use_sg_dummY */