You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
9852 lines
312 KiB
9852 lines
312 KiB
|
|
/* |
|
cdrskin.c , Copyright 2006-2016 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" or |
|
"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.7" |
|
#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 6 |
|
#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_6 |
|
#define Cdrskin_libburn_versioN "1.4.6" |
|
#endif |
|
|
|
#ifdef Cdrskin_libburn_1_4_7 |
|
#define Cdrskin_libburn_versioN "1.4.7" |
|
#endif |
|
|
|
#ifndef Cdrskin_libburn_versioN |
|
#define Cdrskin_libburn_1_4_6 |
|
#define Cdrskin_libburn_versioN "1.4.6" |
|
#endif |
|
|
|
#ifdef Cdrskin_libburn_1_4_6 |
|
#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 6 |
|
#endif |
|
#ifdef Cdrskin_libburn_1_4_7 |
|
#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 7 |
|
#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 and its 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 and its 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, self_opened= 0; |
|
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; |
|
self_opened= 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) { |
|
close(*fd); |
|
*fd= -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 */ |
|
} |
|
} |
|
} |
|
} |
|
|
|
track->source_fd= *fd; |
|
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; |
|
} |
|
} |
|
return((*fd >= 0) * (1 + self_opened)); |
|
} |
|
|
|
|
|
#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; |
|
double padding,lib_padding; |
|
int ret,sector_pad_up; |
|
double fixed_size; |
|
int source_fd; |
|
|
|
#ifdef Cdrskin_use_libburn_fifO |
|
struct burn_source *fd_src= NULL; |
|
#endif |
|
|
|
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: |
|
|
|
#ifdef Cdrskin_use_libburn_fifO |
|
if(fd_src!=NULL) |
|
burn_source_free(fd_src); |
|
#endif |
|
|
|
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 transferred 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, µ); |
|
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> |