10 changed files with 1011 additions and 7 deletions
@ -1 +1 @@
|
||||
#define Cdrskin_timestamP "2010.06.07.171706" |
||||
#define Cdrskin_timestamP "2010.06.07.172925" |
||||
|
@ -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 <scdbackup@gmx.net>, 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; |
||||
|
@ -0,0 +1,892 @@
|
||||
/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ |
||||
|
||||
/*
|
||||
Copyright (c) 2010 Thomas Schmitt <scdbackup@gmx.net> |
||||
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 <unistd.h> |
||||
#include <stropts.h> |
||||
#include <stdio.h> |
||||
#include <sys/types.h> |
||||
#include <errno.h> |
||||
#include <fcntl.h> |
||||
#include <sys/stat.h> |
||||
#include <string.h> |
||||
#include <stdlib.h> |
||||
#include <dirent.h> |
||||
|
||||
#ifdef Libburn_os_has_statvfS |
||||
#include <sys/statvfs.h> |
||||
#endif /* Libburn_os_has_stavtfS */ |
||||
|
||||
#include <sys/dkio.h> |
||||
#include <sys/vtoc.h> |
||||
|
||||
#include <sys/scsi/impl/uscsi.h> |
||||
|
||||
/* >>> need a size determination call for random access devices.
|
||||
e.g. USB sticks. |
||||
*/ |
||||
#ifdef __linux |
||||
/* for ioctl(BLKGETSIZE) */ |
||||
#include <sys/ioctl.h> |
||||
#include <linux/fs.h> |
||||
#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; |
||||
} |
||||
|
Loading…
Reference in new issue