libburn/cdrskin/cdrskin.c

9730 lines
308 KiB
C

/*
cdrskin.c , Copyright 2006-2015 Thomas Schmitt <scdbackup@gmx.net>
Provided under GPL version 2 or later.
A cdrecord compatible command line interface for libburn.
This project is neither directed against original cdrecord nor does it exploit
any source code of said program. It rather tries to be an alternative method
to burn CD, DVD, or BD, which is not based on the same code as cdrecord.
See also : http://scdbackup.sourceforge.net/cdrskin_eng.html
Interested users of cdrecord are encouraged to contribute further option
implementations as they need them. Contributions will get published under GPL
but it is essential that the authors allow a future release under LGPL.
There is a script test/cdrecord_spy.sh which may be installed between
the cdrecord command and real cdrecord in order to learn about the options
used by your favorite cdrecord frontend. Edit said script and install it
according to the instructions given inside.
The implementation of an option would probably consist of
- necessary structure members for structs CdrpreskiN and/or CdrskiN
- code in Cdrpreskin_setup() and Cdrskin_setup() which converts
argv[i] into CdrpreskiN/CdrskiN members (or into direct actions)
- removal of option from ignore list "ignored_partial_options" resp.
"ignored_full_options" in Cdrskin_setup()
- functions which implement the option's run time functionality
- eventually calls of those functions in Cdrskin_run()
- changes to be made within Cdrskin_burn() or Cdrskin_blank() or other
existing methods
See option blank= for an example.
------------------------------------------------------------------------------
For a more comprehensive example of the advised way to write an application
of libburn see test/libburner.c .
------------------------------------------------------------------------------
This program is currently copyright Thomas Schmitt only.
The copyrights of several components of libburnia-project.org are willfully
tangled at toplevel to form an irrevocable commitment to true open source
spirit.
We have chosen the GPL for legal compatibility and clearly express that it
shall not hamper the use of our software by non-GPL applications which show
otherwise the due respect to the open source community.
See toplevel README and cdrskin/README for that commitment.
For a short time, this place showed a promise to release a BSD license on
mere request. I have to retract that promise now, and replace it by the
promise to make above commitment reality in a way that any BSD conformant
usage in due open source spirit will be made possible somehow and in the
particular special case. I will not raise public protest if you fork yourself
a BSD license from an (outdated) cdrskin.c which still bears that old promise.
Note that this extended commitment is valid only for cdrskin.[ch],
cdrfifo.[ch] and cleanup.[ch], but not for libburnia-project.org as a whole.
cdrskin is originally inspired by libburn-0.2/test/burniso.c :
(c) Derek Foreman <derek@signalmarketing.com> and Ben Jansens <xor@orodu.net>
------------------------------------------------------------------------------
Compilation within cdrskin-* :
cd cdrskin
cc -g -I.. -DCdrskin_build_timestamP='...' \
-D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE=1 \
-o cdrskin cdrskin.c cdrfifo.c cleanup.c \
-L../libburn/.libs -lburn -lpthread
or
cd ..
cc -g -I. -DCdrskin_build_timestamP='...' \
-D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE=1 \
-o cdrskin/cdrskin cdrskin/cdrskin.c cdrskin/cdrfifo.c cdrskin/cleanup.c \
libburn/async.o libburn/crc.o libburn/debug.o libburn/drive.o \
libburn/file.o libburn/init.o libburn/lec.o \
libburn/mmc.o libburn/options.o libburn/sbc.o libburn/sector.o \
libburn/sg.o libburn/spc.o libburn/source.o libburn/structure.o \
libburn/toc.o libburn/util.o libburn/write.o libburn/read.o \
libburn/libdax_audioxtr.o libburn/libdax_msgs.o \
-lpthread
*/
/** The official program version */
#ifndef Cdrskin_prog_versioN
#define Cdrskin_prog_versioN "1.4.1"
#endif
/** The official libburn interface revision to use.
(May get changed further below)
*/
#ifndef Cdrskin_libburn_majoR
#define Cdrskin_libburn_majoR 1
#endif
#ifndef Cdrskin_libburn_minoR
#define Cdrskin_libburn_minoR 4
#endif
#ifndef Cdrskin_libburn_micrO
#define Cdrskin_libburn_micrO 1
#endif
/** The source code release timestamp */
#include "cdrskin_timestamp.h"
#ifndef Cdrskin_timestamP
#define Cdrskin_timestamP "-none-given-"
#endif
/** The binary build timestamp is to be set externally by the compiler */
#ifndef Cdrskin_build_timestamP
#define Cdrskin_build_timestamP "-none-given-"
#endif
#ifdef Cdrskin_libburn_versioN
#undef Cdrskin_libburn_versioN
#endif
#ifdef Cdrskin_libburn_1_4_0
#define Cdrskin_libburn_versioN "1.4.0"
#endif
#ifdef Cdrskin_libburn_1_4_1
#define Cdrskin_libburn_versioN "1.4.1"
#endif
#ifndef Cdrskin_libburn_versioN
#define Cdrskin_libburn_1_4_0
#define Cdrskin_libburn_versioN "1.4.0"
#endif
#ifdef Cdrskin_libburn_1_4_0
#undef Cdrskin_libburn_majoR
#undef Cdrskin_libburn_minoR
#undef Cdrskin_libburn_micrO
#define Cdrskin_libburn_majoR 1
#define Cdrskin_libburn_minoR 4
#define Cdrskin_libburn_micrO 0
#endif
#ifdef Cdrskin_libburn_1_4_1
#undef Cdrskin_libburn_majoR
#undef Cdrskin_libburn_minoR
#undef Cdrskin_libburn_micrO
#define Cdrskin_libburn_majoR 1
#define Cdrskin_libburn_minoR 4
#define Cdrskin_libburn_micrO 1
#endif
/* History of development macros.
As of version 1.1.8 they are now unconditional, thus removing the option
to compile a heavily restricted cdrskin for the old libburn at icculus.org.
*/
/* 0.2.2 */
/* Cdrskin_libburn_does_ejecT */
/* Cdrskin_libburn_has_drive_get_adR */
/* Cdrskin_progress_track_does_worK */
/* Cdrskin_is_erasable_on_load_does_worK */
/* Cdrskin_grab_abort_does_worK */
/* 0.2.4 */
/* Cdrskin_allow_libburn_taO */
/* Cdrskin_libburn_has_is_enumerablE */
/* Cdrskin_libburn_has_convert_fs_adR */
/* Cdrskin_libburn_has_convert_scsi_adR */
/* Cdrskin_libburn_has_burn_msgS */
/* Cdrskin_libburn_has_burn_aborT */
/* Cdrskin_libburn_has_cleanup_handleR */
/* Cdrskin_libburn_has_audioxtR */
/* Cdrskin_libburn_has_get_start_end_lbA */
/* Cdrskin_libburn_has_burn_disc_unsuitablE */
/* Cdrskin_libburn_has_read_atiP */
/* Cdrskin_libburn_has_buffer_progresS */
/* 0.2.6 */
/* Cdrskin_libburn_has_pretend_fulL */
/* Cdrskin_libburn_has_multI */
/* Cdrskin_libburn_has_buffer_min_filL */
/* 0.3.0 */
/* Cdrskin_atip_speed_is_oK */
/* Cdrskin_libburn_has_get_profilE */
/* Cdrskin_libburn_has_set_start_bytE */
/* Cdrskin_libburn_has_wrote_welL */
/* Cdrskin_libburn_has_bd_formattinG */
/* Cdrskin_libburn_has_burn_disc_formaT */
/* 0.3.2 */
/* Cdrskin_libburn_has_get_msc1 */
/* Cdrskin_libburn_has_toc_entry_extensionS */
/* Cdrskin_libburn_has_get_multi_capS */
/* 0.3.4 */
/* Cdrskin_libburn_has_set_filluP */
/* Cdrskin_libburn_has_get_spacE */
/* Cdrskin_libburn_write_mode_ruleS */
/* Cdrskin_libburn_has_allow_untested_profileS */
/* Cdrskin_libburn_has_set_forcE */
/* 0.3.6 */
/* Cdrskin_libburn_preset_device_familY */
/* Cdrskin_libburn_has_track_set_sizE */
/* 0.3.8 */
/* Cdrskin_libburn_has_set_waitinG */
/* Cdrskin_libburn_has_get_best_speeD */
/* 0.4.0 */
/* Cdrskin_libburn_has_random_access_rW */
/* Cdrskin_libburn_has_get_drive_rolE */
/* Cdrskin_libburn_has_drive_equals_adR */
/* 0.4.2 */
/* no novel libburn features but rather organizational changes */
/* 0.4.4 */
/* novel libburn features are transparent to cdrskin */
/* 0.4.6 */
/* Cdrskin_libburn_has_stream_recordinG */
/* 0.4.8 */
/* Bug fix release for write_start_address=... on DVD-RAM and BD-RE */
/* 0.5.0 , 0.5.2 , 0.5.4 , 0.5.6 , 0.5.8 , 0.6.0 , 0.6.2 */
/* novel libburn features are transparent to cdrskin */
/* 0.6.4 */
/* Ended to mark novelties by macros.
libburnia libburn and cdrskin are fixely in sync now.
icculus libburn did not move for 30 months.
*/
/* 1.1.8 */
/* The code which got enabled by novelty macros was made unconditional.
*/
#ifdef Cdrskin_new_api_tesT
/* put macros under test caveat here */
#endif /* Cdrskin_new_api_tesT */
/** ts A90901
The raw write modes of libburn depend in part on code borrowed from cdrdao.
Since this code is not understood by the current developers and since CDs
written with cdrskin -raw96r seem unreadable anyway, -raw96r is given up
for now.
*/
#define Cdrskin_disable_raw96R 1
/** A macro which is able to eat up a function call like printf() */
#ifdef Cdrskin_extra_leaN
#define ClN(x)
#define Cdrskin_no_cdrfifO 1
#else
#define ClN(x) x
#ifdef Cdrskin_use_libburn_fifO
/*
# define Cdrskin_no_cdrfifO 1
*/
#endif
#endif
/** Verbosity level for pacifying progress messages */
#define Cdrskin_verbose_progresS 1
/** Verbosity level for command recognition and execution logging */
#define Cdrskin_verbose_cmD 2
/** Verbosity level for reporting of debugging messages */
#define Cdrskin_verbose_debuG 3
/** Verbosity level for fifo debugging */
#define Cdrskin_verbose_debug_fifO 4
#include <stdio.h>
#include <ctype.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/time.h>
#include "../libburn/libburn.h"
#define Cleanup_set_handlers burn_set_signal_handling
#define Cleanup_app_handler_T burn_abort_handler_t
/*
# define Cdrskin_use_libburn_cleanuP 1
*/
/* May not use libburn cleanup with cdrskin fifo */
#ifndef Cdrskin_use_libburn_fifO
#ifdef Cdrskin_use_libburn_cleanuP
#undef Cdrskin_use_libburn_cleanuP
#endif
#endif
#ifdef Cdrskin_use_libburn_cleanuP
#define Cleanup_handler_funC NULL
#define Cleanup_handler_handlE "cdrskin: "
#define Cleanup_handler_flaG 48
#else
#define Cleanup_handler_funC (Cleanup_app_handler_T) Cdrskin_abort_handler
#define Cleanup_handler_handlE skin
#define Cleanup_handler_flaG 4
#endif /* ! Cdrskin_use_libburn_cleanuP */
/* 0= no abort going on, -1= Cdrskin_abort_handler was called
*/
static int Cdrskin_abort_leveL= 0;
/** The size of a string buffer for pathnames and similar texts */
#define Cdrskin_strleN 4096
/** The maximum length +1 of a drive address */
#define Cdrskin_adrleN BURN_DRIVE_ADR_LEN
/** If tsize= sets a value smaller than media capacity divided by this
number then there will be a warning and gracetime set at least to 15 */
#define Cdrskin_minimum_tsize_quotienT 2048.0
/* --------------------------------------------------------------------- */
/* Imported from scdbackup-0.8.5/src/cd_backup_planer.c */
/** Macro for creation of arrays of objects (or single objects) */
#define TSOB_FELD(typ,anz) (typ *) calloc(anz, sizeof(typ));
/** Convert a text so that eventual characters special to the shell are
made literal. Note: this does not make a text terminal-safe !
@param in_text The text to be converted
@param out_text The buffer for the result.
It should have size >= strlen(in_text)*5+2
@param flag Unused yet
@return For convenience out_text is returned
*/
char *Text_shellsafe(char *in_text, char *out_text, int flag)
{
int l,i,w=0;
/* enclose everything by hard quotes */
l= strlen(in_text);
out_text[w++]= '\'';
for(i=0;i<l;i++){
if(in_text[i]=='\''){
/* escape hard quote within the text */
out_text[w++]= '\'';
out_text[w++]= '"';
out_text[w++]= '\'';
out_text[w++]= '"';
out_text[w++]= '\'';
} else {
out_text[w++]= in_text[i];
}
}
out_text[w++]= '\'';
out_text[w++]= 0;
return(out_text);
}
/** Convert a text into a number of type double and multiply it by unit code
[kmgtpe] (2^10 to 2^60) or [s] (2048). (Also accepts capital letters.)
@param text Input like "42", "2k", "3.14m" or "-1g"
@param flag Bitfield for control purposes:
bit0= return -1 rathern than 0 on failure
@return The derived double value
*/
double Scanf_io_size(char *text, int flag)
/*
bit0= default value -1 rather than 0
*/
{
int c;
double ret= 0.0;
if(flag&1)
ret= -1.0;
if(text[0]==0)
return(ret);
sscanf(text,"%lf",&ret);
c= text[strlen(text)-1];
if(c=='k' || c=='K') ret*= 1024.0;
if(c=='m' || c=='M') ret*= 1024.0*1024.0;
if(c=='g' || c=='G') ret*= 1024.0*1024.0*1024.0;
if(c=='t' || c=='T') ret*= 1024.0*1024.0*1024.0*1024.0;
if(c=='p' || c=='P') ret*= 1024.0*1024.0*1024.0*1024.0*1024.0;
if(c=='e' || c=='E') ret*= 1024.0*1024.0*1024.0*1024.0*1024.0*1024.0;
if(c=='s' || c=='S') ret*= 2048.0;
return(ret);
}
/** Return a double representing seconds and microseconds since 1 Jan 1970 */
double Sfile_microtime(int flag)
{
struct timeval tv;
struct timezone tz;
gettimeofday(&tv,&tz);
return((double) (tv.tv_sec+1.0e-6*tv.tv_usec));
}
/** Read a line from fp and strip LF or CRLF */
char *Sfile_fgets(char *line, int maxl, FILE *fp)
{
int l;
char *ret;
ret= fgets(line,maxl,fp);
if(ret==NULL) return(NULL);
l= strlen(line);
if(l>0) if(line[l-1]=='\r') line[--l]= 0;
if(l>0) if(line[l-1]=='\n') line[--l]= 0;
if(l>0) if(line[l-1]=='\r') line[--l]= 0;
return(ret);
}
#ifndef Cdrskin_extra_leaN
/** Destroy a synthetic argument array */
int Sfile_destroy_argv(int *argc, char ***argv, int flag)
{
int i;
if(*argc>0 && *argv!=NULL){
for(i=0;i<*argc;i++){
if((*argv)[i]!=NULL)
free((*argv)[i]);
}
free((char *) *argv);
}
*argc= 0;
*argv= NULL;
return(1);
}
/** Read a synthetic argument array from a list of files.
@param progname The content for argv[0]
@param filenames The paths of the filex from where to read
@param filenamecount The number of paths in filenames
@param argc Returns the number of read arguments (+1 for progname)
@param argv Returns the array of synthetic arguments
@param argidx Returns source file indice of argv[] items
@param arglno Returns source file line numbers of argv[] items
@param flag Bitfield for control purposes:
bit0= read progname as first argument from line
bit1= just release argument array argv and return
bit2= tolerate failure to open file
@return 1=ok , 0=cannot open file , -1=cannot create memory objects
*/
int Sfile_multi_read_argv(char *progname, char **filenames, int filename_count,
int *argc, char ***argv, int **argidx, int **arglno,
int flag)
{
int ret,i,pass,maxl=0,l,argcount=0,line_no;
char buf[Cdrskin_strleN];
FILE *fp= NULL;
Sfile_destroy_argv(argc,argv,0);
if(flag&2)
return(1);
if((*argidx)!=NULL)
free((char *) *argidx);
if((*arglno)!=NULL)
free((char *) *arglno);
*argidx= *arglno= NULL;
for(pass=0;pass<2;pass++) {
if(!(flag&1)){
argcount= 1;
if(pass==0)
maxl= strlen(progname)+1;
else {
(*argv)[0]= (char *) calloc(1, strlen(progname)+1);
if((*argv)[0]==NULL)
{ret= -1; goto ex;}
strcpy((*argv)[0],progname);
}
} else {
argcount= 0;
if(pass==0)
maxl= 1;
}
for(i=0; i<filename_count;i++) {
if(strlen(filenames[i])==0)
continue;
fp= fopen(filenames[i],"rb");
if(fp==NULL) {
if(flag&4)
continue;
{ret= 0; goto ex;}
}
line_no= 0;
while(Sfile_fgets(buf,sizeof(buf)-1,fp)!=NULL) {
line_no++;
l= strlen(buf);
if(l==0 || buf[0]=='#')
continue;
if(pass==0){
if(l>maxl)
maxl= l;
} else {
if(argcount >= *argc)
break;
(*argv)[argcount]= (char *) calloc(1, l+1);
if((*argv)[argcount]==NULL)
{ret= -1; goto ex;}
strcpy((*argv)[argcount],buf);
(*argidx)[argcount]= i;
(*arglno)[argcount]= line_no;
}
argcount++;
}
fclose(fp); fp= NULL;
}
if(pass==0){
*argc= argcount;
if(argcount>0) {
*argv= (char **) calloc(argcount, sizeof(char *));
*argidx= (int *) calloc(argcount, sizeof(int));
*arglno= (int *) calloc(argcount, sizeof(int));
if(*argv==NULL || *argidx==NULL || *arglno==NULL)
{ret= -1; goto ex;}
}
for(i=0;i<*argc;i++) {
(*argv)[i]= NULL;
(*argidx)[i]= -1;
(*arglno)[i]= -1;
}
}
}
ret= 1;
ex:;
if(fp!=NULL)
fclose(fp);
return(ret);
}
/** Combine environment variable HOME with given filename
@param filename Address relative to $HOME
@param fileadr Resulting combined address
@param fa_size Size of array fileadr
@param flag Unused yet
@return 1=ok , 0=no HOME variable , -1=result address too long
*/
int Sfile_home_adr_s(char *filename, char *fileadr, int fa_size, int flag)
{
char *home;
strcpy(fileadr,filename);
home= getenv("HOME");
if(home==NULL)
return(0);
if((int) (strlen(home) + strlen(filename) + 1) >= fa_size)
return(-1);
strcpy(fileadr,home);
if(filename[0]!=0){
strcat(fileadr,"/");
strcat(fileadr,filename);
}
return(1);
}
#endif /* ! Cdrskin_extra_leaN */
/* -------------------------- other misc functions ----------------------- */
/* Learned from reading growisofs.c ,
watching mkisofs, and viewing its results via od -c */
/* @return 0=no size found , 1=*size_in_bytes is valid */
int Scan_for_iso_size(unsigned char data[2048], double *size_in_bytes,
int flag)
{
double sectors= 0.0;
if(data[0]!=1)
return(0);
if(strncmp((char *) (data+1),"CD001",5)!=0)
return(0);
sectors= data[80] | (data[81]<<8) | (data[82]<<16) | (data[83]<<24);
*size_in_bytes= sectors*2048.0;
return(1);
}
int Set_descr_iso_size(unsigned char data[2048], double size_in_bytes,
int flag)
{
unsigned int sectors, i;
sectors= size_in_bytes/2048.0;
if(size_in_bytes>((double) sectors) * 2048.0)
sectors++;
for(i=0;i<4;i++)
data[87-i]= data[80+i]= (sectors >> (8*i)) & 0xff;
return(1);
}
int Wait_for_input(int fd, int microsec, int flag)
{
struct timeval wt;
fd_set rds,wts,exs;
int ready;
FD_ZERO(&rds);
FD_ZERO(&wts);
FD_ZERO(&exs);
FD_SET(fd,&rds);
FD_SET(fd,&exs);
wt.tv_sec= microsec/1000000;
wt.tv_usec= microsec%1000000;
ready= select(fd+1,&rds,&wts,&exs,&wt);
if(ready<=0)
return(0);
if(FD_ISSET(fd,&exs))
return(-1);
if(FD_ISSET(fd,&rds))
return(1);
return(0);
}
/* --------------------------------------------------------------------- */
/** Address translation table for users/applications which do not look
for the output of -scanbus but guess a Bus,Target,Lun on their own.
*/
/** The maximum number of entries in the address translation table */
#define Cdradrtrn_leN 256
/** The address prefix which will prevent translation */
#define Cdrskin_no_transl_prefiX "LITERAL_ADR:"
struct CdradrtrN {
char *from_address[Cdradrtrn_leN];
char *to_address[Cdradrtrn_leN];
int fill_counter;
};
#ifndef Cdrskin_extra_leaN
/** Create a device address translator object */
int Cdradrtrn_new(struct CdradrtrN **trn, int flag)
{
struct CdradrtrN *o;
int i;
(*trn)= o= TSOB_FELD(struct CdradrtrN,1);
if(o==NULL)
return(-1);
for(i= 0;i<Cdradrtrn_leN;i++) {
o->from_address[i]= NULL;
o->to_address[i]= NULL;
}
o->fill_counter= 0;
return(1);
}
/** Release from memory a device address translator object */
int Cdradrtrn_destroy(struct CdradrtrN **o, int flag)
{
int i;
struct CdradrtrN *trn;
trn= *o;
if(trn==NULL)
return(0);
for(i= 0;i<trn->fill_counter;i++) {
if(trn->from_address[i]!=NULL)
free(trn->from_address[i]);
if(trn->to_address[i]!=NULL)
free(trn->to_address[i]);
}
free((char *) trn);
*o= NULL;
return(1);
}
/** Add a translation pair to the table
@param trn The translator which shall learn
@param from The user side address
@param to The cdrskin side address
@param flag Bitfield for control purposes:
bit0= "from" contains from+to address, to[0] contains delimiter
*/
int Cdradrtrn_add(struct CdradrtrN *trn, char *from, char *to, int flag)
{
char buf[2*Cdrskin_adrleN+1],*from_pt,*to_pt;
int cnt;
cnt= trn->fill_counter;
if(cnt>=Cdradrtrn_leN)
return(-1);
if(flag&1) {
if(strlen(from)>=sizeof(buf))
return(0);
strcpy(buf,from);
to_pt= strchr(buf,to[0]);
if(to_pt==NULL)
return(0);
*(to_pt)= 0;
from_pt= buf;
to_pt++;
} else {
from_pt= from;
to_pt= to;
}
if(strlen(from)>=Cdrskin_adrleN || strlen(to)>=Cdrskin_adrleN)
return(0);
trn->from_address[cnt]= calloc(1, strlen(from_pt)+1);
trn->to_address[cnt]= calloc(1, strlen(to_pt)+1);
if(trn->from_address[cnt]==NULL ||
trn->to_address[cnt]==NULL)
return(-2);
strcpy(trn->from_address[cnt],from_pt);
strcpy(trn->to_address[cnt],to_pt);
trn->fill_counter++;
return(1);
}
/** Apply eventual device address translation
@param trn The translator
@param from The address from which to translate
@param driveno With backward translation only: The libburn drive number
@param to The result of the translation
@param flag Bitfield for control purposes:
bit0= translate backward
@return <=0 error, 1=no translation found, 2=translation found,
3=collision avoided
*/
int Cdradrtrn_translate(struct CdradrtrN *trn, char *from, int driveno,
char to[Cdrskin_adrleN], int flag)
{
int i,ret= 1;
char *adr;
to[0]= 0;
adr= from;
if(flag&1)
goto backward;
if(strncmp(adr,Cdrskin_no_transl_prefiX,
strlen(Cdrskin_no_transl_prefiX))==0) {
adr= adr+strlen(Cdrskin_no_transl_prefiX);
ret= 2;
} else {
for(i=0;i<trn->fill_counter;i++)
if(strcmp(adr,trn->from_address[i])==0)
break;
if(i<trn->fill_counter) {
adr= trn->to_address[i];
ret= 2;
}
}
if(strlen(adr)>=Cdrskin_adrleN)
return(-1);
strcpy(to,adr);
return(ret);
backward:;
if(strlen(from)>=Cdrskin_adrleN)
sprintf(to,"%s%d",Cdrskin_no_transl_prefiX,driveno);
else
strcpy(to,from);
for(i=0;i<trn->fill_counter;i++)
if(strcmp(from,trn->to_address[i])==0 &&
strlen(trn->from_address[i])<Cdrskin_adrleN)
break;
if(i<trn->fill_counter) {
ret= 2;
strcpy(to,trn->from_address[i]);
} else {
for(i=0;i<trn->fill_counter;i++)
if(strcmp(from,trn->from_address[i])==0)
break;
if(i<trn->fill_counter)
if(strlen(from)+strlen(Cdrskin_no_transl_prefiX)<Cdrskin_adrleN) {
ret= 3;
sprintf(to,"%s%s",Cdrskin_no_transl_prefiX,from);
}
}
return(ret);
}
#endif /* Cdrskin_extra_leaN */
/* --------------------------------------------------------------------- */
#ifndef Cdrskin_no_cdrfifO
/* Program is to be linked with cdrfifo.c */
#include "cdrfifo.h"
#else /* ! Cdrskin_no_cdrfifO */
/* Dummy */
struct CdrfifO {
int dummy;
};
#endif /* Cdrskin_no_cdrfifO */
/* --------------------------------------------------------------------- */
/** cdrecord pads up to 600 kB in any case.
libburn yields blank result on tracks <~ 600 kB
cdrecord demands 300 sectors = 705600 bytes for -audio */
static double Cdrtrack_minimum_sizE= 300;
/** This structure represents a track resp. a data source */
struct CdrtracK {
struct CdrskiN *boss;
int trackno;
char source_path[Cdrskin_strleN];
char original_source_path[Cdrskin_strleN];
int source_fd;
int is_from_stdin;
double fixed_size;
double tao_to_sao_tsize;
double padding;
int set_by_padsize;
int sector_pad_up; /* enforce single sector padding */
int track_type;
int mode_modifiers;
double sector_size;
int track_type_by_default;
int swap_audio_bytes;
int cdxa_conversion; /* bit0-30: for burn_track_set_cdxa_conv()
bit31 : ignore bits 0 to 30
*/
char isrc[13];
char *index_string;
int sao_pregap;
int sao_postgap;
/** Eventually detected data image size */
double data_image_size;
char *iso_fs_descr; /* eventually block 16 to 31 of input during detection */
/** Whether to demand a detected data image size and use it (or else abort) */
int use_data_image_size; /* 0=no, 1=size not defined yet, 2=size defined */
/* Whether the data source is a container of defined size with possible tail*/
int extracting_container;
/** Optional fifo between input fd and libburn. It uses a pipe(2) to transfer
data to libburn.
*/
int fifo_enabled;
/** The eventual own fifo object managed by this track object. */
struct CdrfifO *fifo;
/** fd[0] of the fifo pipe. This is from where libburn reads its data. */
int fifo_outlet_fd;
int fifo_size;
/** The possibly external fifo object which knows the real input fd and
the fd[1] of the pipe. */
struct CdrfifO *ff_fifo;
/** The index number if fifo follow up fd item, -1= own fifo */
int ff_idx;
struct burn_track *libburn_track;
int libburn_track_is_own;
#ifdef Cdrskin_use_libburn_fifO
struct burn_source *libburn_fifo;
#endif /* Cdrskin_use_libburn_fifO */
};
int Cdrtrack_destroy(struct CdrtracK **o, int flag);
int Cdrtrack_set_track_type(struct CdrtracK *o, int track_type, int flag);
/** Create a track resp. data source object.
@param track Returns the address of the new object.
@param boss The cdrskin control object (corresponds to session)
@param trackno The index in the cdrskin tracklist array (is not constant)
@param flag Bitfield for control purposes:
bit0= do not set parameters by Cdrskin_get_source()
bit1= track is originally stdin
*/
int Cdrtrack_new(struct CdrtracK **track, struct CdrskiN *boss,
int trackno, int flag)
{
struct CdrtracK *o;
int ret,skin_track_type,fifo_start_at;
int Cdrskin_get_source(struct CdrskiN *skin, char *source_path,
double *fixed_size, double *tao_to_sao_tsize,
int *use_data_image_size,
double *padding, int *set_by_padsize,
int *track_type, int *track_type_by_default,
int *mode_mods, int *swap_audio_bytes,
int *cdxa_conversion, int flag);
int Cdrskin_get_fifo_par(struct CdrskiN *skin, int *fifo_enabled,
int *fifo_size, int *fifo_start_at, int flag);
(*track)= o= TSOB_FELD(struct CdrtracK,1);
if(o==NULL)
return(-1);
o->boss= boss;
o->trackno= trackno;
o->source_path[0]= 0;
o->original_source_path[0]= 0;
o->source_fd= -1;
o->is_from_stdin= !!(flag&2);
o->fixed_size= 0.0;
o->tao_to_sao_tsize= 0.0;
o->padding= 0.0;
o->set_by_padsize= 0;
o->sector_pad_up= 1;
o->track_type= BURN_MODE1;
o->mode_modifiers= 0;
o->sector_size= 2048.0;
o->track_type_by_default= 1;
o->swap_audio_bytes= 0;
o->cdxa_conversion= 0;
o->isrc[0]= 0;
o->index_string= NULL;
o->sao_pregap= -1;
o->sao_postgap= -1;
o->data_image_size= -1.0;
o->iso_fs_descr= NULL;
o->use_data_image_size= 0;
o->extracting_container= 0;
o->fifo_enabled= 0;
o->fifo= NULL;
o->fifo_outlet_fd= -1;
o->fifo_size= 0;
o->ff_fifo= NULL;
o->ff_idx= -1;
o->libburn_track= NULL;
o->libburn_track_is_own= 0;
#ifdef Cdrskin_use_libburn_fifO
o->libburn_fifo= NULL;
#endif /* Cdrskin_use_libburn_fifO */
if(flag & 1)
return(1);
ret= Cdrskin_get_source(boss,o->source_path,&(o->fixed_size),
&(o->tao_to_sao_tsize),&(o->use_data_image_size),
&(o->padding),&(o->set_by_padsize),&(skin_track_type),
&(o->track_type_by_default), &(o->mode_modifiers),
&(o->swap_audio_bytes), &(o->cdxa_conversion), 0);
if(ret<=0)
goto failed;
strcpy(o->original_source_path,o->source_path);
if(o->fixed_size>0.0)
o->extracting_container= 1;
Cdrtrack_set_track_type(o,skin_track_type,0);
#ifndef Cdrskin_extra_leaN
ret= Cdrskin_get_fifo_par(boss, &(o->fifo_enabled),&(o->fifo_size),
&fifo_start_at,0);
if(ret<=0)
goto failed;
#endif /* ! Cdrskin_extra_leaN */
return(1);
failed:;
Cdrtrack_destroy(track,0);
return(-1);
}
/** Release from memory a track object previously created by Cdrtrack_new() */
int Cdrtrack_destroy(struct CdrtracK **o, int flag)
{
struct CdrtracK *track;
track= *o;
if(track==NULL)
return(0);
#ifndef Cdrskin_no_cdrfifO
Cdrfifo_destroy(&(track->fifo),0);
#endif
if(track->libburn_track != NULL && track->libburn_track_is_own)
burn_track_free(track->libburn_track);
if(track->iso_fs_descr!=NULL)
free((char *) track->iso_fs_descr);
if(track->index_string != NULL)
free(track->index_string);
free((char *) track);
*o= NULL;
return(1);
}
int Cdrtrack_set_track_type(struct CdrtracK *o, int track_type, int flag)
{
if(track_type==BURN_AUDIO) {
o->track_type= BURN_AUDIO;
o->sector_size= 2352.0;
} else {
o->track_type= BURN_MODE1;
o->sector_size= 2048.0;
}
return(1);
}
int Cdrtrack_get_track_type(struct CdrtracK *o, int *track_type,
int *sector_size, int flag)
{
*track_type= o->track_type;
*sector_size= o->sector_size;
return(1);
}
/**
@param flag Bitfield for control purposes:
bit0= size returns number of actually processed source bytes
rather than the predicted fixed_size (if available).
padding returns the difference from number of written
bytes.
bit1= size returns fixed_size, padding returns tao_to_sao_tsize
*/
int Cdrtrack_get_size(struct CdrtracK *track, double *size, double *padding,
double *sector_size, int *use_data_image_size, int flag)
{
off_t readcounter= 0,writecounter= 0;
*size= track->fixed_size;
*padding= track->padding;
*use_data_image_size= track->use_data_image_size;
if((flag&1) && track->libburn_track!=NULL) {
burn_track_get_counters(track->libburn_track,&readcounter,&writecounter);
*size= readcounter;
*padding= writecounter-readcounter;
} else if(flag&2)
*padding= track->tao_to_sao_tsize;
*sector_size= track->sector_size;
return(1);
}
int Cdrtrack_get_iso_fs_descr(struct CdrtracK *track,
char **descr, double *size, int flag)
{
*descr= track->iso_fs_descr;
*size= track->data_image_size;
return(*descr != NULL && *size > 0.0);
}
int Cdrtrack_get_source_path(struct CdrtracK *track,
char **source_path, int *source_fd, int *is_from_stdin, int flag)
{
*source_path= track->original_source_path;
*source_fd= track->source_fd;
*is_from_stdin= track->is_from_stdin;
return(1);
}
#ifdef Cdrskin_use_libburn_fifO
int Cdrtrack_get_libburn_fifo(struct CdrtracK *track,
struct burn_source **fifo, int flag)
{
*fifo= track->libburn_fifo;
return(1);
}
int Cdrtrack_report_fifo(struct CdrtracK *track, int flag)
{
int size, free_bytes, ret;
int total_min_fill, interval_min_fill, put_counter, get_counter;
int empty_counter, full_counter;
double fifo_percent;
char *status_text;
if(track->libburn_fifo == NULL)
return(0);
/* Check for open input or leftover bytes in liburn fifo */
ret = burn_fifo_inquire_status(track->libburn_fifo, &size, &free_bytes,
&status_text);
if(ret >= 0 && size - free_bytes > 1) {
/* not clear why free_bytes is reduced by 1 */
fprintf(stderr,
"cdrskin: FATAL : Fifo still contains data after burning has ended.\n");
fprintf(stderr,
"cdrskin: FATAL : %d bytes left.\n", size - free_bytes - 1);
fprintf(stderr,
"cdrskin: FATAL : This indicates an overflow of the last track.\n");
fprintf(stderr,
"cdrskin: NOTE : The media might appear ok but is probably truncated.\n");
return(-1);
}
burn_fifo_get_statistics(track->libburn_fifo, &total_min_fill,
&interval_min_fill, &put_counter, &get_counter,
&empty_counter, &full_counter);
fifo_percent= 100.0*((double) total_min_fill)/(double) size;
if(fifo_percent==0 && total_min_fill>0)
fifo_percent= 1;
fflush(stdout);
fprintf(stderr,"Cdrskin: fifo had %d puts and %d gets.\n",
put_counter,get_counter);
fprintf(stderr,
"Cdrskin: fifo was %d times empty and %d times full, min fill was %.f%%.\n",
empty_counter, full_counter, fifo_percent);
return(1);
}
#endif /* Cdrskin_use_libburn_fifO */
int Cdrtrack_get_fifo(struct CdrtracK *track, struct CdrfifO **fifo, int flag)
{
*fifo= track->fifo;
return(1);
}
/** Try whether automatic audio extraction is appropriate and eventually open
a file descriptor to the raw data.
@return -3 identified as .wav but with cdrecord-inappropriate parameters
-2 could not open track source, no use in retrying
-1 severe error
0 not appropriate to extract, burn plain file content
1 to be extracted, *fd is a filedescriptor delivering raw data
*/
int Cdrtrack_extract_audio(struct CdrtracK *track, int *fd, off_t *xtr_size,
int flag)
{
int l, ok= 0;
struct libdax_audioxtr *xtr= NULL;
char *fmt,*fmt_info;
int num_channels,sample_rate,bits_per_sample,msb_first,ret;
*fd= -1;
if(track->track_type!=BURN_AUDIO && !track->track_type_by_default)
return(0);
l= strlen(track->source_path);
if(l>=4)
if(strcmp(track->source_path+l-4,".wav")==0)
ok= 1;
if(l>=3)
if(strcmp(track->source_path+l-3,".au")==0)
ok= 1;
if(!ok)
return(0);
if(track->track_type_by_default) {
Cdrtrack_set_track_type(track,BURN_AUDIO,0);
track->track_type_by_default= 2;
fprintf(stderr,"cdrskin: NOTE : Activated -audio for '%s'\n",
track->source_path);
}
ret= libdax_audioxtr_new(&xtr,track->source_path,0);
if(ret<=0)
return(ret);
libdax_audioxtr_get_id(xtr,&fmt,&fmt_info,
&num_channels,&sample_rate,&bits_per_sample,&msb_first,0);
if((strcmp(fmt,".wav")!=0 && strcmp(fmt,".au")!=0) ||
num_channels!=2 || sample_rate!=44100 || bits_per_sample!=16) {
fprintf(stderr,"cdrskin: ( %s )\n",fmt_info);
fprintf(stderr,"cdrskin: FATAL : Inappropriate audio coding in '%s'.\n",
track->source_path);
{ret= -3; goto ex;}
}
libdax_audioxtr_get_size(xtr,xtr_size,0);
ret= libdax_audioxtr_detach_fd(xtr,fd,0);
if(ret<=0)
{ret= -1*!!ret; goto ex;}
track->swap_audio_bytes= !!msb_first;
track->extracting_container= 1;
fprintf(stderr,"cdrskin: NOTE : %.f %saudio bytes in '%s'\n",
(double) *xtr_size, (msb_first ? "" : "(-swab) "),
track->source_path);
ret= 1;
ex:
libdax_audioxtr_destroy(&xtr,0);
return(ret);
}
/* @param flag bit0=set *size_used as the detected data image size
*/
int Cdrtrack_activate_image_size(struct CdrtracK *track, double *size_used,
int flag)
{
if(flag&1)
track->data_image_size= *size_used;
else
*size_used= track->data_image_size;
if(track->use_data_image_size!=1)
return(2);
if(*size_used<=0)
return(0);
track->fixed_size= *size_used;
track->use_data_image_size= 2;
if(track->libburn_track!=NULL)
burn_track_set_size(track->libburn_track, (off_t) *size_used);
/* man cdrecord prescribes automatic -pad with -isosize.
cdrskin obeys only if the current padding is less than that. */
if(track->padding<15*2048) {
track->padding= 15*2048;
track->set_by_padsize= 0;
}
track->extracting_container= 1;
#ifndef Cdrskin_no_cdrfifO
if(track->ff_fifo!=NULL)
Cdrfifo_set_fd_in_limit(track->ff_fifo,track->fixed_size,track->ff_idx,0);
#endif
return(1);
}
int Cdrtrack_seek_isosize(struct CdrtracK *track, int fd, int flag)
{
struct stat stbuf;
char secbuf[2048];
int ret,got,i;
double size;
if(fstat(fd,&stbuf)==-1)
return(0);
if((stbuf.st_mode&S_IFMT)!=S_IFREG && (stbuf.st_mode&S_IFMT)!=S_IFBLK)
return(2);
if(track->iso_fs_descr!=NULL)
free((char *) track->iso_fs_descr);
track->iso_fs_descr= TSOB_FELD(char,16*2048);
if(track->iso_fs_descr==NULL)
return(-1);
for(i=0;i<32 && track->data_image_size<=0;i++) {
for(got= 0; got<2048;got+= ret) {
ret= read(fd, secbuf+got, 2048-got);
if(ret<=0)
return(0);
}
if(i<16)
continue;
memcpy(track->iso_fs_descr+(i-16)*2048,secbuf,2048);
if(i>16)
continue;
ret= Scan_for_iso_size((unsigned char *) secbuf, &size, 0);
if(ret<=0)
break;
track->data_image_size= size;
if(track->use_data_image_size) {
Cdrtrack_activate_image_size(track,&size,1);
track->fixed_size= size;
track->use_data_image_size= 2;
}
}
ret= lseek(fd, (off_t) 0, SEEK_SET);
if(ret!=0) {
fprintf(stderr,
"cdrskin: FATAL : Cannot lseek() to 0 after -isosize determination\n");
if(errno!=0)
fprintf(stderr, "cdrskin: errno=%d : %s\n", errno, strerror(errno));
return(-1);
}
return(track->data_image_size>0);
}
/** Deliver an open file descriptor corresponding to the source path of track.
@param flag Bitfield for control purposes:
bit0=debugging verbosity
bit1=open as source for direct write:
no audio extract, no minimum track size
bit2=permission to use burn_os_open_track_src() (evtl O_DIRECT)
@return <=0 error, 1 success
*/
int Cdrtrack_open_source_path(struct CdrtracK *track, int *fd, int flag)
{
int is_wav= 0, size_from_file= 0, ret;
off_t xtr_size= 0;
struct stat stbuf;
char *device_adr,*raw_adr;
int no_convert_fs_adr;
int Cdrskin_get_device_adr(struct CdrskiN *skin,
char **device_adr, char **raw_adr, int *no_convert_fs_adr,int flag);
int Cdrskin_get_drive(struct CdrskiN *skin, struct burn_drive **drive,
int flag);
struct burn_drive *drive;
if(track->source_path[0]=='-' && track->source_path[1]==0)
*fd= 0;
else if(track->source_path[0]=='#' &&
(track->source_path[1]>='0' && track->source_path[1]<='9'))
*fd= atoi(track->source_path+1);
else {
*fd= -1;
ret= Cdrskin_get_device_adr(track->boss,&device_adr,&raw_adr,
&no_convert_fs_adr,0);
if(ret <= 0) {
fprintf(stderr,
"cdrskin: FATAL : No drive found. Cannot prepare track.\n");
return(0);
}
/*
fprintf(stderr,
"cdrskin: DEBUG : device_adr='%s' , raw_adr='%s' , ncfs=%d\n",
device_adr, raw_adr, no_convert_fs_adr);
*/
if(!no_convert_fs_adr) {
if(flag&1)
ClN(fprintf(stderr,
"cdrskin_debug: checking track source for identity with drive\n"));
ret= Cdrskin_get_drive(track->boss,&drive,0);
if(ret<=0) {
fprintf(stderr,
"cdrskin: FATAL : Program error. Cannot determine libburn drive.\n");
return(0);
}
if(burn_drive_equals_adr(drive,track->source_path,2)>0) {
fprintf(stderr,
"cdrskin: FATAL : track source address leads to burner drive\n");
fprintf(stderr,
"cdrskin: : dev='%s' -> '%s' <- track source '%s'\n",
raw_adr, device_adr, track->source_path);
return(0);
}
}
/*
fprintf(stderr,"cdrskin: EXPERIMENTAL : Deliberate abort\n");
return(0);
*/
if(!(flag&2))
is_wav= Cdrtrack_extract_audio(track,fd,&xtr_size,0);
if(is_wav==-1)
return(-1);
if(is_wav==-3)
return(0);
if(is_wav==0) {
if(track->track_type != BURN_MODE1 ||
(track->cdxa_conversion & 0x7fffffff))
flag&= ~4; /* Better avoid O_DIRECT with odd sectors */
if(flag & 4)
*fd= burn_os_open_track_src(track->source_path, O_RDONLY, 0);
else
*fd= open(track->source_path, O_RDONLY);
}
if(*fd==-1) {
fprintf(stderr,"cdrskin: failed to open source address '%s'\n",
track->source_path);
fprintf(stderr,"cdrskin: errno=%d , \"%s\"\n",errno,
errno==0?"-no error code available-":strerror(errno));
return(0);
}
if(track->use_data_image_size==1 && xtr_size<=0) {
ret= Cdrtrack_seek_isosize(track,*fd,0);
if(ret==-1)
return(-1);
} else if(track->fixed_size<=0) {
/* >>> ??? is it intentional that tsize overrides .wav header ? */
if(xtr_size>0) {
track->fixed_size= xtr_size;
if(track->use_data_image_size==1)
track->use_data_image_size= 2; /* count this as image size found */
size_from_file= 1;
} else {
if(fstat(*fd,&stbuf)!=-1) {
if((stbuf.st_mode&S_IFMT)==S_IFREG) {
track->fixed_size= stbuf.st_size;
size_from_file= 1;
} /* all other types are assumed of open ended size */
}
}
}
}
if(track->fixed_size < Cdrtrack_minimum_sizE * track->sector_size
&& (track->fixed_size>0 || size_from_file) && !(flag&2)) {
if(track->track_type == BURN_AUDIO) {
/* >>> cdrecord: We differ in automatic padding with audio:
Audio tracks must be at least 705600 bytes and a multiple of 2352.
*/
fprintf(stderr,
"cdrskin: FATAL : Audio tracks must be at least %.f bytes\n",
Cdrtrack_minimum_sizE*track->sector_size);
return(0);
} else {
fprintf(stderr,
"cdrskin: NOTE : Enforcing minimum track size of %.f bytes\n",
Cdrtrack_minimum_sizE*track->sector_size);
track->fixed_size= Cdrtrack_minimum_sizE*track->sector_size;
}
}
track->source_fd= *fd;
return(*fd>=0);
}
#ifndef Cdrskin_no_cdrfifO
/** Install a fifo object between data source and libburn.
Its parameters are known to track.
@param outlet_fd Returns the filedescriptor of the fifo outlet.
@param previous_fifo Object address for chaining or follow-up attachment.
@param flag Bitfield for control purposes:
bit0= Debugging verbosity
bit1= Do not create and attach a new fifo
but attach new follow-up fd pair to previous_fifo
bit2= Do not enforce fixed_size if not container extraction
@return <=0 error, 1 success
*/
int Cdrtrack_attach_fifo(struct CdrtracK *track, int *outlet_fd,
struct CdrfifO *previous_fifo, int flag)
{
struct CdrfifO *ff;
int source_fd,pipe_fds[2],ret;
*outlet_fd= -1;
if(track->fifo_size<=0)
return(2);
ret= Cdrtrack_open_source_path(track,&source_fd,
(flag&1) | (4 * (track->fifo_size >= 256 * 1024)));
if(ret<=0)
return(ret);
if(pipe(pipe_fds)==-1)
return(0);
Cdrfifo_destroy(&(track->fifo),0);
if(flag&2) {
ret= Cdrfifo_attach_follow_up_fds(previous_fifo,source_fd,pipe_fds[1],0);
if(ret<=0)
return(ret);
track->ff_fifo= previous_fifo;
track->ff_idx= ret-1;
} else {
/* >>> ??? obtain track sector size and use instead of 2048 ? */
ret= Cdrfifo_new(&ff,source_fd,pipe_fds[1],2048,track->fifo_size, flag & 1);
if(ret<=0)
return(ret);
if(previous_fifo!=NULL)
Cdrfifo_attach_peer(previous_fifo,ff,0);
track->fifo= track->ff_fifo= ff;
track->ff_idx= -1;
}
track->fifo_outlet_fd= pipe_fds[0];
if((track->extracting_container || !(flag&4)) && track->fixed_size>0)
Cdrfifo_set_fd_in_limit(track->ff_fifo,track->fixed_size,track->ff_idx,0);
if(flag&1)
printf(
"cdrskin_debug: track %d fifo replaced source_address '%s' by '#%d'\n",
track->trackno+1,track->source_path,track->fifo_outlet_fd);
sprintf(track->source_path,"#%d",track->fifo_outlet_fd);
track->source_fd= track->fifo_outlet_fd;
*outlet_fd= track->fifo_outlet_fd;
return(1);
}
#endif /* ! Cdrskin_no_cdrfifO */
#ifndef Cdrskin_extra_leaN
#ifdef Cdrskin_use_libburn_fifO
/** Read data into the eventual libburn fifo until either fifo_start_at bytes
are read (-1 = no limit), it is full or or the data source is exhausted.
@return <=0 error, 1 success
*/
int Cdrtrack_fill_libburn_fifo(struct CdrtracK *track, int fifo_start_at,
int flag)
{
int ret, bs= 32 * 1024;
int buffer_size, buffer_free;
double data_image_size;
char buf[64 * 1024], *buffer_text;
if(fifo_start_at == 0)
return(2);
if(track->libburn_fifo == NULL)
return(2);
if(fifo_start_at>0 && fifo_start_at<track->fifo_size)
printf(
"cdrskin: NOTE : Input buffer will be initially filled up to %d bytes\n",
fifo_start_at);
printf("Waiting for reader process to fill input buffer ... ");
fflush(stdout);
ret= burn_fifo_fill(track->libburn_fifo, fifo_start_at,
(fifo_start_at == -1));
if(ret < 0)
return(0);
/** Ticket 55: check fifos for input, throw error on 0-bytes from stdin
@return <=0 abort run, 1 go on with burning
*/
ret= burn_fifo_inquire_status(track->libburn_fifo, &buffer_size,
&buffer_free, &buffer_text);
if(track->is_from_stdin) {
if(ret<0 || buffer_size <= buffer_free) {
fprintf(stderr,"\ncdrskin: FATAL : (First track) fifo did not read a single byte from stdin\n");
return(0);
}
}
/* Try to obtain ISO 9660 Volume Descriptors and size from fifo.
Not an error if there is no ISO 9660. */
if(track->iso_fs_descr != NULL)
free(track->iso_fs_descr);
track->iso_fs_descr = NULL;
if(buffer_size - buffer_free >= 64 * 1024) {
ret= burn_fifo_peek_data(track->libburn_fifo, buf, 64 * 1024, 0);
if(ret == 1) {
track->iso_fs_descr = calloc(1, bs);
if(track->iso_fs_descr == NULL)
return(-1);
memcpy(track->iso_fs_descr, buf + bs, bs);
ret= Scan_for_iso_size((unsigned char *) buf + bs, &data_image_size, 0);
if(ret > 0)
track->data_image_size= data_image_size;
}
}
return(1);
}
#endif /* Cdrskin_use_libburn_fifO */
#ifdef Cdrskin_no_cdrfifO
int Cdrtrack_fill_fifo(struct CdrtracK *track, int fifo_start_at, int flag)
{
return(Cdrtrack_fill_libburn_fifo(track, fifo_start_at, 0));
}
#else /* Cdrskin_no_cdrfifO */
int Cdrtrack_fill_fifo(struct CdrtracK *track, int fifo_start_at, int flag)
{
int ret,buffer_fill,buffer_space;
double data_image_size;
if(fifo_start_at==0)
return(2);
if(track->fifo==NULL) {
#ifdef Cdrskin_use_libburn_fifO
ret= Cdrtrack_fill_libburn_fifo(track, fifo_start_at, 0);
return(ret);
#else
return(2);
#endif
}
if(fifo_start_at>0 && fifo_start_at<track->fifo_size)
printf(
"cdrskin: NOTE : Input buffer will be initially filled up to %d bytes\n",
fifo_start_at);
printf("Waiting for reader process to fill input buffer ... ");
fflush(stdout);
ret= Cdrfifo_fill(track->fifo,fifo_start_at,0);
if(ret<=0)
return(ret);
/** Ticket 55: check fifos for input, throw error on 0-bytes from stdin
@return <=0 abort run, 1 go on with burning
*/
if(track->is_from_stdin) {
ret= Cdrfifo_get_buffer_state(track->fifo,&buffer_fill,&buffer_space,0);
if(ret<0 || buffer_fill<=0) {
fprintf(stderr,"\ncdrskin: FATAL : (First track) fifo did not read a single byte from stdin\n");
return(0);
}
}
ret= Cdrfifo_get_iso_fs_size(track->fifo,&data_image_size,0);
if(ret>0)
track->data_image_size= data_image_size;
if(track->iso_fs_descr!=NULL)
free((char *) track->iso_fs_descr);
Cdrfifo_adopt_iso_fs_descr(track->fifo,&(track->iso_fs_descr),0);
return(1);
}
#endif /* ! Cdrskin_no_cdrfifO */
#endif /* ! Cdrskin_extra_leaN */
int Cdrtrack_set_indice(struct CdrtracK *track, int flag)
{
int idx= 1, adr, prev_adr= -1, ret;
char *cpt, *ept;
if(track->sao_pregap >= 0) {
ret= burn_track_set_pregap_size(track->libburn_track, track->sao_pregap, 0);
if(ret <= 0)
return(ret);
}
if(track->sao_postgap >= 0) {
ret= burn_track_set_postgap_size(track->libburn_track, track->sao_postgap,
0);
if(ret <= 0)
return(ret);
}
if(track->index_string == NULL)
return(2);
for(ept= cpt= track->index_string; ept != NULL; cpt= ept + 1) {
ept= strchr(cpt, ',');
if(ept != NULL)
*ept= 0;
adr= -1;
sscanf(cpt, "%d", &adr);
if(adr < 0) {
fprintf(stderr, "cdrskin: SORRY : Bad address number with index=\n");
return(0);
}
if(idx == 1 && adr != 0) {
fprintf(stderr,
"cdrskin: SORRY : First address number of index= is not 0\n");
return(0);
}
if(idx > 1 && adr < prev_adr) {
fprintf(stderr,
"cdrskin: SORRY : Backward address number with index=\n");
return(0);
}
ret= burn_track_set_index(track->libburn_track, idx, adr, 0);
if(ret <= 0)
return(ret);
prev_adr= adr;
if(ept != NULL)
*ept= ',';
idx++;
}
return(1);
}
/** Create a corresponding libburn track object and add it to the libburn
session. This may change the trackno index set by Cdrtrack_new().
*/
int Cdrtrack_add_to_session(struct CdrtracK *track, int trackno,
struct burn_session *session, int flag)
/*
bit0= debugging verbosity
bit1= apply padding hack (<<< should be unused for now)
bit2= permission to use O_DIRECT (if enabled at compile time)
*/
{
struct burn_track *tr;
struct burn_source *src= NULL, *fd_src= NULL;
double padding,lib_padding;
int ret,sector_pad_up;
double fixed_size;
int source_fd;
track->trackno= trackno;
tr= burn_track_create();
if(tr == NULL)
{ret= -1; goto ex;}
track->libburn_track= tr;
track->libburn_track_is_own= 1;
/* Note: track->track_type may get set in here */
if(track->source_fd==-1) {
ret= Cdrtrack_open_source_path(track, &source_fd, flag & (4 | 1));
if(ret<=0)
goto ex;
}
padding= 0.0;
sector_pad_up= track->sector_pad_up;
if(track->padding>0) {
if(track->set_by_padsize || track->track_type!=BURN_AUDIO)
padding= track->padding;
else
sector_pad_up= 1;
}
if(flag&2)
lib_padding= 0.0;
else
lib_padding= padding;
if(flag&1) {
if(sector_pad_up) {
ClN(fprintf(stderr,"cdrskin_debug: track %d telling burn_track_define_data() to pad up last sector\n",trackno+1));
}
if(lib_padding>0 || !sector_pad_up) {
ClN(fprintf(stderr,
"cdrskin_debug: track %d telling burn_track_define_data() to pad %.f bytes\n",
trackno+1,lib_padding));
}
}
burn_track_define_data(tr,0,(int) lib_padding,sector_pad_up,
track->track_type | track->mode_modifiers);
burn_track_set_default_size(tr, (off_t) track->tao_to_sao_tsize);
burn_track_set_byte_swap(tr,
(track->track_type==BURN_AUDIO && track->swap_audio_bytes));
if(!(track->cdxa_conversion & (1 << 31)))
burn_track_set_cdxa_conv(tr, track->cdxa_conversion & 0x7fffffff);
fixed_size= track->fixed_size;
if((flag&2) && track->padding>0) {
if(flag&1)
ClN(fprintf(stderr,"cdrskin_debug: padding hack : %.f + %.f = %.f\n",
track->fixed_size,track->padding,
track->fixed_size+track->padding));
fixed_size+= track->padding;
}
src= burn_fd_source_new(track->source_fd,-1,(off_t) fixed_size);
#ifdef Cdrskin_use_libburn_fifO
if(src != NULL && track->fifo == NULL) {
int fifo_enabled, fifo_size, fifo_start_at, chunksize, chunks;
int Cdrskin_get_fifo_par(struct CdrskiN *skin, int *fifo_enabled,
int *fifo_size, int *fifo_start_at, int flag);
Cdrskin_get_fifo_par(track->boss, &fifo_enabled, &fifo_size, &fifo_start_at,
0);
if(track->track_type == BURN_AUDIO)
chunksize= 2352;
else if (track->cdxa_conversion == 1)
chunksize= 2056;
else
chunksize= 2048;
chunks= fifo_size / chunksize;
if(chunks > 1 && fifo_enabled) {
fd_src= src;
src= burn_fifo_source_new(fd_src, chunksize, chunks,
(chunksize * chunks >= 128 * 1024));
if((flag & 1) || src == NULL)
fprintf(stderr, "cdrskin_DEBUG: %s libburn fifo of %d bytes\n",
src != NULL ? "installed" : "failed to install",
chunksize * chunks);
track->libburn_fifo= src;
if(src == NULL) {
src= fd_src;
fd_src= NULL;
}
}
}
#endif /* Cdrskin_use_libburn_fifO */
if(src==NULL) {
fprintf(stderr,
"cdrskin: FATAL : Could not create libburn data source object\n");
{ret= 0; goto ex;}
}
if(burn_track_set_source(tr,src)!=BURN_SOURCE_OK) {
fprintf(stderr,"cdrskin: FATAL : libburn rejects data source object\n");
{ret= 0; goto ex;}
}
ret= Cdrtrack_set_indice(track, 0);
if(ret <= 0)
goto ex;
burn_session_add_track(session,tr,BURN_POS_END);
ret= 1;
ex:
if(fd_src!=NULL)
burn_source_free(fd_src);
if(src!=NULL)
burn_source_free(src);
return(ret);
}
/** Release libburn track information after a session is done */
int Cdrtrack_cleanup(struct CdrtracK *track, int flag)
{
if(track->libburn_track==NULL)
return(0);
if(track->libburn_track_is_own)
burn_track_free(track->libburn_track);
track->libburn_track= NULL;
return(1);
}
int Cdrtrack_ensure_padding(struct CdrtracK *track, int flag)
/*
flag:
bit0= debugging verbosity
*/
{
if(track->track_type!=BURN_AUDIO)
return(2);
if(flag&1)
fprintf(stderr,"cdrskin_debug: enforcing -pad on last -audio track\n");
track->sector_pad_up= 1;
return(1);
}
int Cdrtrack_get_sectors(struct CdrtracK *track, int flag)
{
return(burn_track_get_sectors(track->libburn_track));
}
#ifndef Cdrskin_no_cdrfifO
/** Try to read bytes from the track's fifo outlet and eventually discard
them. Not to be called unless the track is completely written.
*/
int Cdrtrack_has_input_left(struct CdrtracK *track, int flag)
{
int ready,ret;
char buf[2];
if(track->fifo_outlet_fd<=0)
return(0);
ready= Wait_for_input(track->fifo_outlet_fd, 0, 0);
if(ready<=0)
return(0);
ret= read(track->fifo_outlet_fd,buf,1);
if(ret>0)
return(1);
return(0);
}
#endif /* ! Cdrskin_no_cdrfifO */
/* --------------------------------------------------------------------- */
/** The list of startup file names */
#define Cdrpreskin_rc_nuM 4
static char Cdrpreskin_sys_rc_nameS[Cdrpreskin_rc_nuM][80]= {
"/etc/default/cdrskin",
"/etc/opt/cdrskin/rc",
"/etc/cdrskin/cdrskin.conf",
"placeholder for $HOME/.cdrskinrc"
};
/** A structure which bundles several parameters for creation of the CdrskiN
object. It finally becomes a managed subordinate of the CdrskiN object.
*/
struct CdrpreskiN {
/* to be transfered into skin */
int verbosity;
char queue_severity[81];
char print_severity[81];
/** Whether to wait for available standard input data before touching drives*/
int do_waiti;
/** Stores eventually given absolute device address before translation */
char raw_device_adr[Cdrskin_adrleN];
/** Stores an eventually given translated absolute device address between
Cdrpreskin_setup() and Cdrskin_create() .
*/
char device_adr[Cdrskin_adrleN];
/** The eventual address translation table */
struct CdradrtrN *adr_trn;
/** Memorizes the abort handling mode from presetup to creation of
control object. Defined handling modes are:
0= no abort handling
1= try to cancel, release, exit (leave signal mode as set by caller)
2= try to ignore all signals
3= mode 1 in normal operation, mode 2 during abort handling
4= mode 1 in normal operation, mode 0 during abort handling
-1= install abort handling 1 only in Cdrskin_burn() after burning started
*/
int abort_handler;
/** Whether to allow getuid()!=geteuid() */
int allow_setuid;
/** Whether to allow user provided addresses like #4 */
int allow_fd_source;
/** Whether to support media types which are implemented but yet untested */
int allow_untested_media;
/** Whether to allow libburn pseudo-drives "stdio:<path>" .
0=forbidden, 1=seems ok,
2=potentially forbidden (depends on uid, euid, file type)
*/
int allow_emulated_drives;
/** Whether an option is given which needs a full bus scan */
int no_whitelist;
/** Whether the translated device address shall not follow softlinks, device
clones and SCSI addresses */
int no_convert_fs_adr;
/** Whether Bus,Target,Lun addresses shall be converted literally as old
Pseudo SCSI-Adresses. New default is to use (possibly system emulated)
real SCSI addresses via burn_drive_convert_scsi_adr() and literally
emulated and cdrecord-incompatible ATA: addresses. */
int old_pseudo_scsi_adr;
/** Whether to omit bus scanning */
int do_not_scan;
/** Whether bus scans shall exit!=0 if no drive was found */
int scan_demands_drive;
/** Whether to abort when a busy drive is encountered during bus scan */
int abort_on_busy_drive;
/** Linux specific : Whether to try to avoid collisions when opening drives */
int drive_exclusive;
/** Linux specific : Whether to obtain an exclusive drive lock via fcntl() */
int drive_fcntl_f_setlk;
/** Linux specific : Device file address family to use :
0=default , 1=sr , 2=scd , 4=sg */
int drive_scsi_dev_family;
/** Whether to try to wait for unwilling drives to become willing to open */
int drive_blocking;
/** Explicit write mode option is determined before skin processes
any track arguments */
char write_mode_name[80];
#ifndef Cdrskin_extra_leaN
/** List of startupfiles */
char rc_filenames[Cdrpreskin_rc_nuM][Cdrskin_strleN];
int rc_filename_count;
/** Non-argument options from startupfiles */
int pre_argc;
char **pre_argv;
int *pre_argidx;
int *pre_arglno;
#endif /* ! Cdrskin_extra_leaN */
/* The eventual name of a program to be executed if demands_cdrecord_caps
is >0 and demands_cdrskin_caps is <=0
*/
char fallback_program[Cdrskin_strleN];
int demands_cdrecord_caps;
int demands_cdrskin_caps;
int result_fd;
};
/** Create a preliminary cdrskin program run control object. It will become
part of the final control object.
@param preskin Returns pointer to resulting
@param flag Bitfield for control purposes: unused yet
@return <=0 error, 1 success
*/
int Cdrpreskin_new(struct CdrpreskiN **preskin, int flag)
{
struct CdrpreskiN *o;
int i;
(*preskin)= o= TSOB_FELD(struct CdrpreskiN,1);
if(o==NULL)
return(-1);
o->verbosity= 0;
strcpy(o->queue_severity,"NEVER");
strcpy(o->print_severity,"SORRY");
o->do_waiti= 0;
o->raw_device_adr[0]= 0;
o->device_adr[0]= 0;
o->adr_trn= NULL;
o->abort_handler= 3;
o->allow_setuid= 0;
o->allow_fd_source= 0;
o->allow_untested_media= 0;
o->allow_emulated_drives= 0;
o->no_whitelist= 0;
o->no_convert_fs_adr= 0;
o->old_pseudo_scsi_adr= 0;
o->do_not_scan= 0;
o->scan_demands_drive= 0;
o->abort_on_busy_drive= 0;
o->drive_exclusive= 1;
o->drive_fcntl_f_setlk= 1;
o->drive_scsi_dev_family= 0;
o->drive_blocking= 0;
strcpy(o->write_mode_name,"DEFAULT");
#ifndef Cdrskin_extra_leaN
o->rc_filename_count= Cdrpreskin_rc_nuM;
for(i=0;i<o->rc_filename_count-1;i++)
strcpy(o->rc_filenames[i],Cdrpreskin_sys_rc_nameS[i]);
o->rc_filenames[o->rc_filename_count-1][0]= 0;
o->pre_argc= 0;
o->pre_argv= NULL;
o->pre_argidx= NULL;
o->pre_arglno= NULL;
#endif /* ! Cdrskin_extra_leaN */
o->fallback_program[0]= 0;
o->demands_cdrecord_caps= 0;
o->demands_cdrskin_caps= 0;
o->result_fd = -1;
return(1);
}
int Cdrpreskin_destroy(struct CdrpreskiN **preskin, int flag)
{
struct CdrpreskiN *o;
o= *preskin;
if(o==NULL)
return(0);
#ifndef Cdrskin_extra_leaN
if((o->pre_arglno)!=NULL)
free((char *) o->pre_arglno);
if((o->pre_argidx)!=NULL)
free((char *) o->pre_argidx);
if(o->pre_argc>0 && o->pre_argv!=NULL)
Sfile_destroy_argv(&(o->pre_argc),&(o->pre_argv),0);
Cdradrtrn_destroy(&(o->adr_trn),0);
#endif /* ! Cdrskin_extra_leaN */
free((char *) o);
*preskin= NULL;
return(1);
}
int Cdrpreskin_set_severities(struct CdrpreskiN *preskin, char *queue_severity,
char *print_severity, int flag)
{
if(queue_severity!=NULL)
strcpy(preskin->queue_severity,queue_severity);
if(print_severity!=NULL)
strcpy(preskin->print_severity,print_severity);
burn_msgs_set_severities(preskin->queue_severity, preskin->print_severity,
"cdrskin: ");
return(1);
}
int Cdrpreskin_initialize_lib(struct CdrpreskiN *preskin, int flag)
{
int ret, major, minor, micro;
/* Needed are at least 44 bits in signed type off_t .
This is a popular mistake in configuration or compilation.
*/
if(sizeof(off_t) < 6) {
fprintf(stderr,
"\ncdrskin: FATAL : Compile time misconfiguration. sizeof(off_t) too small.\n"
);
return(0);
}
/* This is the minimum requirement of cdrskin towards the libburn header
at compile time.
It gets compared against the version macros in libburn/libburn.h :
burn_header_version_major
burn_header_version_minor
burn_header_version_micro
If the header is too old then the following code shall cause failure of
cdrskin compilation rather than to allow production of a program with
unpredictable bugs or memory corruption.
The compiler message supposed to appear in this case is:
error: 'LIBBURN_MISCONFIGURATION' undeclared (first use in this function)
error: 'INTENTIONAL_ABORT_OF_COMPILATION__HEADERFILE_libburn_dot_h_TOO_OLD__SEE_cdrskin_dot_c' undeclared (first use in this function)
error: 'LIBBURN_MISCONFIGURATION_' undeclared (first use in this function)
*/
/* The indendation is an advise of man gcc to help old compilers ignoring */
#if Cdrskin_libburn_majoR > burn_header_version_major
#define Cdrskin_libburn_dot_h_too_olD 1
#endif
#if Cdrskin_libburn_majoR == burn_header_version_major && Cdrskin_libburn_minoR > burn_header_version_minor
#define Cdrskin_libburn_dot_h_too_olD 1
#endif
#if Cdrskin_libburn_minoR == burn_header_version_minor && Cdrskin_libburn_micrO > burn_header_version_micro
#define Cdrskin_libburn_dot_h_too_olD 1
#endif
#ifdef Cdrskin_libburn_dot_h_too_olD
LIBBURN_MISCONFIGURATION = 0;
INTENTIONAL_ABORT_OF_COMPILATION__HEADERFILE_libburn_dot_h_TOO_OLD__SEE_cdrskin_dot_c = 0;
LIBBURN_MISCONFIGURATION_ = 0;
#endif
ret= burn_initialize();
if(ret==0) {
fprintf(stderr,"cdrskin: FATAL : Initialization of libburn failed\n");
return(0);
}
/* This is the runtime check towards eventual dynamically linked libburn.
cdrskin deliberately does not to allow the library to be older than
the header file which was seen at compile time. More liberal would be
to use here Cdrskin_libburn_* instead of burn_header_version_* .
*/
burn_version(&major, &minor, &micro);
if(major<burn_header_version_major ||
(major==burn_header_version_major && (minor<burn_header_version_minor ||
(minor==burn_header_version_minor && micro<burn_header_version_micro)))) {
fprintf(stderr,"cdrskin: FATAL : libburn version too old: %d.%d.%d . Need at least: %d.%d.%d .\n",
major, minor, micro,
Cdrskin_libburn_majoR, Cdrskin_libburn_minoR, Cdrskin_libburn_micrO);
return(-1);
}
Cdrpreskin_set_severities(preskin,NULL,NULL,0);
burn_allow_drive_role_4(1);
return(1);
}
/** Enable queuing of libburn messages or disable and print queue content.
@param flag Bitfield for control purposes:
bit0= enable queueing, else disable and print
*/
int Cdrpreskin_queue_msgs(struct CdrpreskiN *o, int flag)
{
/* In cdrskin there is not much sense in queueing library messages.
#ifndef Cdrskin_extra_leaN
#define Cdrskin_debug_libdax_msgS 1
#endif
It would be done here only for debugging
*/
#ifdef Cdrskin_debug_libdax_msgS
int ret;
static char queue_severity[81]= {"NEVER"}, print_severity[81]= {"SORRY"};
static int queueing= 0;
char msg[BURN_MSGS_MESSAGE_LEN],msg_severity[81],filler[81];
int error_code,os_errno,first,i;
if(flag&1) {
if(!queueing) {
strcpy(queue_severity,o->queue_severity);
strcpy(print_severity,o->print_severity);
}
if(o->verbosity>=Cdrskin_verbose_debuG)
Cdrpreskin_set_severities(o,"DEBUG","NEVER",0);
else
Cdrpreskin_set_severities(o,"SORRY","NEVER",0);
queueing= 1;
return(1);
}
if(queueing)
Cdrpreskin_set_severities(o,queue_severity,print_severity,0);
queueing= 0;
for(first= 1; ; first= 0) {
ret= burn_msgs_obtain("ALL",&error_code,msg,&os_errno,msg_severity);
if(ret==0)
break;
if(ret<0) {
fprintf(stderr,
"cdrskin: NOTE : Please inform libburn-hackers@pykix.org about:\n");
fprintf(stderr,
"cdrskin: burn_msgs_obtain() returns %d\n",ret);
break;
}
if(first)
fprintf(stderr,
"cdrskin: -------------------- Messages from Libburn ---------------------\n");
for(i=0;msg_severity[i]!=0;i++)
filler[i]= ' ';
filler[i]= 0;
fprintf(stderr,"cdrskin: %s : %s\n",msg_severity,msg);
if(strcmp(msg_severity,"DEBUG")!=0 && os_errno!=0)
fprintf(stderr,"cdrskin: %s ( errno=%d '%s')\n",
filler,os_errno,strerror(os_errno));
}
if(first==0)
fprintf(stderr,
"cdrskin: ----------------------------------------------------------------\n");
#endif /* Cdrskin_debug_libdax_msgS */
return(1);
}
/** Evaluate whether the user would be allowed in any case to use device_adr
as pseudo-drive */
int Cdrpreskin__allows_emulated_drives(char *device_adr, char reason[4096],
int flag)
{
struct stat stbuf;
reason[0]= 0;
if(device_adr[0]) {
if(strcmp(device_adr,"/dev/null")==0)
return(1);
strcat(reason,"File object is not /dev/null. ");
}
if(getuid()!=geteuid()) {
strcat(reason,"UID and EUID differ");
return(0);
}
if(getuid()!=0)
return(1);
strcat(reason,"UID is 0. ");
/* Directory must be owned by root and write protected against any others*/
if(lstat("/root/cdrskin_permissions",&stbuf)==-1 || !S_ISDIR(stbuf.st_mode)) {
strcat(reason, "No directory /root/cdrskin_permissions exists");
return(0);
}
if(stbuf.st_uid!=0) {
strcat(reason, "Directory /root/cdrskin_permissions not owned by UID 0");
return(0);
}
if(stbuf.st_mode & (S_IWGRP | S_IWOTH)) {
strcat(reason,
"Directory /root/cdrskin_permissions has w-permission for group or others");
return(0);
}
if(stat("/root/cdrskin_permissions/allow_emulated_drives",&stbuf)==-1) {
strcat(reason,
"No file /root/cdrskin_permissions/allow_emulated_drives exists");
return(0);
}
reason[0]= 0;
return(1);
}
int Cdrpreskin_consider_normal_user(int flag)
{
fprintf(stderr,
"cdrskin: HINT : Consider to allow rw-access to the writer devices and\n");
fprintf(stderr,
"cdrskin: HINT : to run cdrskin under your normal user identity.\n");
return(1);
}
/* Start the fallback program as replacement of the cdrskin run.
@param flag bit0=do not report start command
*/
int Cdrpreskin_fallback(struct CdrpreskiN *preskin, int argc, char **argv,
int flag)
{
char **hargv= NULL;
int i, wp= 1;
char *ept, *upt;
if(preskin->fallback_program[0] == 0)
return(1);
if(getuid()!=geteuid() && !preskin->allow_setuid) {
fprintf(stderr,
"cdrskin: SORRY : uid and euid differ. Will not start external fallback program.\n");
Cdrpreskin_consider_normal_user(0);
fprintf(stderr,
"cdrskin: HINT : Option --allow_setuid disables this safety check.\n");
goto failure;
}
if(!(flag&1)) {
fprintf(stderr,"cdrskin: --------------------------------------------------------------------\n");
fprintf(stderr,"cdrskin: Starting fallback program:\n");
}
hargv= TSOB_FELD(char *,argc+1);
if(hargv==NULL)
goto failure;
hargv[0]= strdup(preskin->fallback_program);
if(argv[0]==NULL)
goto failure;
if(!(flag&1))
fprintf(stderr," %s", hargv[0]);
for(i= 1; i<argc; i++) {
/* filter away all cdrskin specific options : --?* and *_*=* */
if(argv[i][0]=='-' && argv[i][1]=='-' && argv[i][2])
continue;
ept= strchr(argv[i],'=');
if(ept!=NULL) {
upt= strchr(argv[i],'_');
if(upt!=NULL && upt<ept)
continue;
}
hargv[wp]= strdup(argv[i]);
if(hargv[wp]==NULL)
goto failure;
if(!(flag&1))
fprintf(stderr," %s", hargv[wp]);
wp++;
}
hargv[wp]= NULL;
if(!(flag&1)) {
fprintf(stderr,"\n");
fprintf(stderr,"cdrskin: --------------------------------------------------------------------\n");
}
execvp(hargv[0], hargv);
failure:;
fprintf(stderr,"cdrskin: FATAL : Cannot start fallback program '%s'\n",
preskin->fallback_program);
fprintf(stderr,"cdrskin: errno=%d \"%s\"\n",
errno, (errno > 0 ? strerror(errno) : "unidentified error"));
exit(15);
}
/** Convert a cdrecord-style device address into a libburn device address or
into a libburn drive number. It depends on the "scsibus" number of the
cdrecord-style address which kind of libburn address emerges:
bus=0 : drive number , bus=1 : /dev/sgN , bus=2 : /dev/hdX
(This call intentionally has no CdrpreskiN argument)
@param flag Bitfield for control purposes:
bit0= old_pseudo_scsi_adr
@return 1 success, 0=no recognizable format, -1=severe error,
-2 could not find scsi device, -3 address format error
*/
int Cdrpreskin__cdrecord_to_dev(char *adr, char device_adr[Cdrskin_adrleN],
int *driveno, int flag)
{
int comma_seen= 0,digit_seen= 0,busno= 0,k,lun_no= -1;
*driveno= -1;
device_adr[0]= 0;
if(strlen(adr)==0)
return(0);
if(strncmp(adr,"stdio:",6)==0)
return(0);
/* read the trailing numeric string as device address code */
/* accepts "1" , "0,1,0" , "ATA:0,1,0" , ... */
for(k= strlen(adr)-1;k>=0;k--) {
if(adr[k]==',' && !comma_seen) {
sscanf(adr+k+1,"%d",&lun_no);
comma_seen= 1;
digit_seen= 0;
continue;
}
if(adr[k]<'0' || adr[k]>'9')
break;
digit_seen= 1;
}
if(!digit_seen) {
k= strlen(adr)-1;
if(adr[k]==':' || (adr[k]>='A' && adr[k]<='Z')) {/* empty prefix ? */
*driveno= 0;
return(1);
}
return(0);
}
sscanf(adr+k+1,"%d",driveno);
digit_seen= 0;
if(k>0) if(adr[k]==',') {
for(k--;k>=0;k--) {
if(adr[k]<'0' || adr[k]>'9')
break;
digit_seen= 1;
}
if(digit_seen) {
sscanf(adr+k+1,"%d",&busno);
if(flag&1) {
/* look for symbolic bus : 1=/dev/sgN 2=/dev/hdX */
if(busno==1) {
sprintf(device_adr,"/dev/sg%d",*driveno);
} else if(busno==2) {
sprintf(device_adr,"/dev/hd%c",'a'+(*driveno));
} else if(busno!=0) {
fprintf(stderr,
"cdrskin: FATAL : dev=[Prefix:]Bus,Target,Lun expects Bus out of {0,1,2}\n");
return(-3);
}
} else {
if(busno<0) {
fprintf(stderr,
"cdrskin: FATAL : dev=[Prefix:]Bus,Target,Lun expects Bus number >= 0\n");
return(-3);
}
if(busno>=1000) {
busno-= 1000;
goto ata_bus;
} else if((strncmp(adr,"ATA",3)==0 && (adr[3]==0 || adr[3]==':')) ||
(strncmp(adr,"ATAPI",5)==0 && (adr[5]==0 || adr[5]==':'))) {
ata_bus:;
if(busno>12 || (*driveno)<0 || (*driveno)>1) {
fprintf(stderr,
"cdrskin: FATAL : dev=ATA:Bus,Target,Lun expects Bus {0..12}, Target {0,1}\n");
return(-3);
}
sprintf(device_adr,"/dev/hd%c",'a'+(2*busno)+(*driveno));
} else {
int ret;
ret= burn_drive_convert_scsi_adr(busno,-1,-1,*driveno,lun_no,
device_adr);
if(ret==0) {
fprintf(stderr,
"cdrskin: FATAL : Cannot find /dev/* with Bus,Target,Lun = %d,%d,%d\n",
busno,*driveno,lun_no);
fprintf(stderr,
"cdrskin: HINT : This drive may be in use by another program currently\n");
return(-2);
} else if(ret<0)
return(-1);
return(1);
}
}
}
}
return(1);
}
/** Set the eventual output fd for the result of Cdrskin_msinfo()
*/
int Cdrpreskin_set_result_fd(struct CdrpreskiN *o, int result_fd, int flag)
{
o->result_fd= result_fd;
return(1);
}
#ifndef Cdrskin_extra_leaN
/** Load content startup files into preskin cache */
int Cdrpreskin_read_rc(struct CdrpreskiN *o, char *progname, int flag)
{
int ret,i;
char **filenames_v;
filenames_v= TSOB_FELD(char *, o->rc_filename_count+1);
if(filenames_v==NULL)
return(-1);
for(i=0;i<o->rc_filename_count;i++)
filenames_v[i]= o->rc_filenames[i];
Sfile_home_adr_s(".cdrskinrc",o->rc_filenames[o->rc_filename_count-1],
Cdrskin_strleN,0);
ret= Sfile_multi_read_argv(progname,filenames_v,o->rc_filename_count,
&(o->pre_argc),&(o->pre_argv),
&(o->pre_argidx),&(o->pre_arglno),4);
free((char *) filenames_v);
return(ret);
}
#endif /* ! Cdrskin_extra_leaN */
/** Interpret those arguments which do not need libburn or which influence the
startup of libburn and/or the creation of the CdrskiN object. This is run
before libburn gets initialized and before Cdrskin_new() is called.
Options which need libburn or a CdrskiN object are processed in a different
function named Cdrskin_setup().
@param flag Bitfield for control purposes:
bit0= do not finalize setup
bit1= do not read and interpret rc files
@return <=0 error, 1 success , 2 end program run with exit value 0
*/
int Cdrpreskin_setup(struct CdrpreskiN *o, int argc, char **argv, int flag)
/*
return:
<=0 error
1 ok
2 end program run (--help)
*/
{
int i,ret;
char *value_pt, reason[4096], *argpt;
#ifndef Cdrskin_extra_leaN
if(argc>1) {
if(strcmp(argv[1],"--no_rc")==0 || strcmp(argv[1],"-version")==0 ||
strcmp(argv[1],"--help")==0 || strcmp(argv[1],"-help")==0 ||
strncmp(argv[1], "textfile_to_v07t=", 17) == 0 ||
strncmp(argv[1], "-textfile_to_v07t=", 18) == 0)
flag|= 2;
}
if(!(flag&2)) {
ret= Cdrpreskin_read_rc(o,argv[0],0);
if(ret<0)
return(-1);
if(o->pre_argc>1) {
ret= Cdrpreskin_setup(o,o->pre_argc,o->pre_argv,flag|1|2);
if(ret<=0)
return(ret);
/* ??? abort on ret==2 ? */
}
}
#endif
if(argc==1) {
fprintf(stderr,"cdrskin: SORRY : No options given. Try option --help\n");
return(0);
}
/* The two predefined fallback personalities are triggered by the progname */
value_pt= strrchr(argv[0],'/');
if(value_pt==NULL)
value_pt= argv[0];
else
value_pt++;
if(strcmp(value_pt,"unicord")==0)
strcpy(o->fallback_program,"cdrecord");
else if(strcmp(value_pt,"codim")==0)
strcpy(o->fallback_program,"wodim");
for (i= 1;i<argc;i++) {
argpt= argv[i];
if (strncmp(argpt, "--", 2) == 0 && strlen(argpt) > 3)
argpt++;
if(strcmp(argv[i],"--abort_handler")==0) {
o->abort_handler= 3;
} else if(strcmp(argv[i],"--allow_emulated_drives")==0) {
if(Cdrpreskin__allows_emulated_drives("",reason,0)<=0) {
fprintf(stderr,"cdrskin: WARNING : %s.\n",reason);
fprintf(stderr,
"cdrskin: WARNING : Only /dev/null will be available with \"stdio:\".\n");
Cdrpreskin_consider_normal_user(0);
o->allow_emulated_drives= 2;
} else
o->allow_emulated_drives= 1;
} else if(strcmp(argv[i],"--allow_setuid")==0) {
o->allow_setuid= 1;
} else if(strcmp(argv[i],"--allow_untested_media")==0) {
o->allow_untested_media= 1;
} else if(strcmp(argpt, "blank=help") == 0 ||
strcmp(argpt, "-blank=help") == 0) {
#ifndef Cdrskin_extra_leaN
fprintf(stderr,"Blanking options:\n");
fprintf(stderr,"\tall\t\tblank the entire disk\n");
fprintf(stderr,"\tdisc\t\tblank the entire disk\n");
fprintf(stderr,"\tdisk\t\tblank the entire disk\n");
fprintf(stderr,"\tfast\t\tminimally blank the entire disk\n");
fprintf(stderr,"\tminimal\t\tminimally blank the entire disk\n");
fprintf(stderr,
"\tas_needed\tblank or format media to make it ready for (re-)use\n");
fprintf(stderr,
"\tdeformat_sequential\t\tfully blank, even formatted DVD-RW\n");
fprintf(stderr,
"\tdeformat_sequential_quickest\tminimally blank, even DVD-RW\n");
fprintf(stderr,
"\tformat_if_needed\t\tmake overwriteable if needed and possible\n");
fprintf(stderr,
"\tformat_overwrite\t\tformat a DVD-RW to \"Restricted Overwrite\"\n");
fprintf(stderr,
"\tformat_overwrite_quickest\tto \"Restricted Overwrite intermediate\"\n");
fprintf(stderr,
"\tformat_overwrite_full\t\tfull-size format a DVD-RW or DVD+RW\n");
fprintf(stderr,
"\tformat_defectmgt[_max|_min|_none]\tformat DVD-RAM or BD-R[E]\n");
fprintf(stderr,
"\tformat_defectmgt[_cert_on|_cert_off]\tcertification slow|quick\n");
fprintf(stderr,
"\tformat_defectmgt_payload_<size>\tformat DVD-RAM or BD-R[E]\n");
fprintf(stderr,
"\tformat_by_index_<number>\t\tformat by index from --list_formats\n");
#else /* ! Cdrskin_extra_leaN */
goto see_cdrskin_eng_html;
#endif /* ! Cdrskin_extra_leaN */
if(argc==2)
{ret= 2; goto final_checks;}
} else if(strcmp(argv[i],"--bragg_with_audio")==0) {
/* OBSOLETE 0.2.3 */;
} else if(strcmp(argv[i],"--demand_a_drive")==0) {
o->scan_demands_drive= 1;
o->demands_cdrskin_caps= 1;
} else if(strcmp(argv[i],"--devices") == 0 ||
strcmp(argv[i],"--device_links") == 0) {
o->no_whitelist= 1;
o->demands_cdrskin_caps= 1;
} else if(strncmp(argv[i],"dev_translation=",16)==0) {
o->demands_cdrskin_caps= 1;
#ifndef Cdrskin_extra_leaN
if(o->adr_trn==NULL) {
ret= Cdradrtrn_new(&(o->adr_trn),0);
if(ret<=0)
goto no_adr_trn_mem;
}
if(argv[i][16]==0) {
fprintf(stderr,
"cdrskin: FATAL : dev_translation= : missing separator character\n");
return(0);
}
ret= Cdradrtrn_add(o->adr_trn,argv[i]+17,argv[i]+16,1);
if(ret==-2) {
no_adr_trn_mem:;
fprintf(stderr,
"cdrskin: FATAL : address_translation= : cannot allocate memory\n");
} else if(ret==-1)
fprintf(stderr,
"cdrskin: FATAL : address_translation= : table full (%d items)\n",
Cdradrtrn_leN);
else if(ret==0)
fprintf(stderr,
"cdrskin: FATAL : address_translation= : no address separator '%c' found\n",
argv[i][17]);
if(ret<=0)
return(0);
#else /* ! Cdrskin_extra_leaN */
fprintf(stderr,
"cdrskin: FATAL : dev_translation= is not available in lean version\n");
return(0);
#endif /* Cdrskin_extra_leaN */
} else if(strncmp(argpt, "-dev=", 5) == 0) {
value_pt= argpt + 5;
goto set_dev;
} else if(strncmp(argpt, "dev=", 4) == 0) {
value_pt= argpt + 4;
set_dev:;
if(strcmp(value_pt,"help")==0) {
#ifndef Cdrskin_extra_leaN
printf("\nSupported SCSI transports for this platform:\n");
fflush(stdout);
if(o->old_pseudo_scsi_adr) {
fprintf(stderr,"\nTransport name:\t\tlibburn OLD_PSEUDO\n");
fprintf(stderr,
"Transport descr.:\tBus0=DriveNum , Bus1=/dev/sgN , Bus2=/dev/hdX\n");
} else {
fprintf(stderr,"\nTransport name:\t\tlibburn SCSI\n");
fprintf(stderr,
"Transport descr.:\tSCSI Bus,Id,Lun as of operating system\n");
}
fprintf(stderr,"Transp. layer ind.:\t\n");
fprintf(stderr,"Target specifier:\tbus,target,lun\n");
fprintf(stderr,"Target example:\t\t1,2,0\n");
fprintf(stderr,"SCSI Bus scanning:\tsupported\n");
fprintf(stderr,"Open via UNIX device:\tsupported\n");
if(!o->old_pseudo_scsi_adr) {
fprintf(stderr,"\nTransport name:\t\tlibburn HD\n");
fprintf(stderr,