Compare commits

...

39 Commits

Author SHA1 Message Date
a64893b457 Applied some tag modifications 2006-11-22 22:29:00 +00:00
15fa006ec3 Started release preparations 2006-11-22 22:23:01 +00:00
26fa325ee4 Coordinated scsi_notify_error() and scsi_error() 2006-11-22 12:25:16 +00:00
314027631a Updated in respect to multi-session 2006-11-20 14:39:25 +00:00
6e09b56c29 Implemented new option fifo_start_at= 2006-11-20 13:30:01 +00:00
08f541a7a8 Next cdrskin-0.2.5 cycle 2006-11-20 13:25:27 +00:00
26412b4793 Removed a redundant d->start_unit() of revision 419 2006-11-20 09:07:47 +00:00
9ea689d7e4 Silenced compiler warnings 2006-11-20 09:06:30 +00:00
f3b994f845 Fixed missing brackets from revison 421 2006-11-19 16:40:53 +00:00
46906c7f65 Avoided self contradiction with "Min drive buffer fill" 2006-11-19 16:32:09 +00:00
4daed7c4a9 Implemented libburn builtin fine granulated drive buffer min-fill recording 2006-11-19 11:47:34 +00:00
ebb94ee212 Test wether SCSI 1Bh START UNIT would be helpful with ticket 90 2006-11-18 19:49:18 +00:00
18a592ad28 Shifted a comma 2006-11-17 21:30:25 +00:00
ba48efbc52 Shifted a comma 2006-11-17 21:29:19 +00:00
0513a4eb2f Obsoleted old test program 2006-11-17 20:15:14 +00:00
254852ee36 Enabled multi-session with libburner 2006-11-17 19:51:51 +00:00
ef6d76c22d Created info retrieval companion for libburner 2006-11-17 19:38:28 +00:00
66c3ead5d6 Updated help text of libburner 2006-11-16 16:53:42 +00:00
908a6b336b Obsoleted --stdin_size by automatic TAO. Cared for non-plain track files 2006-11-16 16:38:23 +00:00
373e401671 Polished porting hints 2006-11-16 14:21:27 +00:00
06d008d6c6 Polished porting hints and self-compliance to newly established specs 2006-11-16 13:40:21 +00:00
11c9444b6c Made consolidaed operating system adapters for ease of porting 2006-11-16 11:18:44 +00:00
ff284b3f51 Made consolidaed operating system adapters for ease of porting 2006-11-16 11:17:55 +00:00
628e935fe5 Narrowed system specific part of enumerate_common() 2006-11-15 20:49:27 +00:00
13641fcbae Made portability clarifications 2006-11-15 17:10:12 +00:00
595b877a24 Adjusted some texts to new multi-session situation 2006-11-15 09:14:17 +00:00
17d1d2e8a6 Enhanced optional SCSI command logging 2006-11-15 09:11:57 +00:00
c1b3d3341e Prepared tests for eventual drive which might support SAO to appendable CD 2006-11-15 09:05:06 +00:00
725b6515ff Fixed a wrong name in API description 2006-11-14 16:38:01 +00:00
61a535f3ac Implemented try to find on restricted drives a suitable write mode as default 2006-11-14 10:41:06 +00:00
7c312a33d9 Corrected a typo reported by George Danchev 2006-11-14 08:33:34 +00:00
b7d369dbb6 Changed references to libisofs 2006-11-14 08:32:32 +00:00
16cbb18f15 Removed remaining signs of libisofs in libburn tree 2006-11-14 00:52:32 +00:00
4f83818ff8 Removed references to libisofs 2006-11-13 15:00:42 +00:00
9e383a3b9e Adjusted description of write mode default 2006-11-13 13:23:34 +00:00
a9b7d8250e Next cdrskin-0.2.5 cycle 2006-11-13 13:04:38 +00:00
1ec94d9226 Refered to both libburn and libisofs in SVN description, updated history 2006-11-13 11:58:57 +00:00
c1607eeac4 Changed SVN URLs to reflect new structure 2006-11-13 08:33:21 +00:00
8927e3df9f Started logical separation of libisofs and libburn 2006-11-13 00:13:21 +00:00
64 changed files with 2429 additions and 5593 deletions

View File

@ -1,7 +1,7 @@
pkgconfigdir=$(libdir)/pkgconfig
libincludedir=$(includedir)/libburn
lib_LTLIBRARIES = libburn/libburn.la libisofs/libisofs.la
lib_LTLIBRARIES = libburn/libburn.la
## ========================================================================= ##
@ -39,6 +39,7 @@ libburn_libburn_la_SOURCES = \
libburn/null.h \
libburn/options.c \
libburn/options.h \
libburn/os.h \
libburn/read.c \
libburn/read.h \
libburn/sbc.c \
@ -64,44 +65,18 @@ libburn_libburn_la_SOURCES = \
## libburn/sg-@ARCH@.c \
libisofs_libisofs_la_LDFLAGS = \
-version-info $(LT_CURRENT):$(LT_REVISION):$(LT_AGE)
libisofs_libisofs_la_SOURCES = \
libisofs/tree.h \
libisofs/tree.c \
libisofs/volume.h \
libisofs/volume.c \
libisofs/util.h \
libisofs/util.c \
libisofs/ecma119.c \
libisofs/ecma119.h \
libisofs/ecma119_tree.c \
libisofs/ecma119_tree.h \
libisofs/susp.h \
libisofs/susp.c \
libisofs/rockridge.h \
libisofs/rockridge.c \
libisofs/joliet.c \
libisofs/joliet.h \
libisofs/exclude.c \
libisofs/exclude.h \
libisofs/hash.h \
libisofs/hash.c
libinclude_HEADERS = \
libburn/libburn.h \
libisofs/libisofs.h
libburn/libburn.h
## ========================================================================= ##
## Build test applications
noinst_PROGRAMS = \
test/libburner \
test/telltoc \
test/dewav \
test/fake_au \
test/iso \
test/poll \
test/toc \
test/structest
bin_PROGRAMS = \
@ -110,6 +85,9 @@ bin_PROGRAMS = \
test_libburner_CPPFLAGS = -Ilibburn
test_libburner_LDADD = $(libburn_libburn_la_OBJECTS) $(THREAD_LIBS)
test_libburner_SOURCES = test/libburner.c
test_telltoc_CPPFLAGS = -Ilibburn
test_telltoc_LDADD = $(libburn_libburn_la_OBJECTS) $(THREAD_LIBS)
test_telltoc_SOURCES = test/telltoc.c
test_dewav_CPPFLAGS = -Ilibburn
test_dewav_LDADD = $(libburn_libburn_la_OBJECTS) $(THREAD_LIBS)
test_dewav_SOURCES = test/dewav.c
@ -119,22 +97,15 @@ test_fake_au_SOURCES = test/fake_au.c
test_poll_CPPFLAGS = -Ilibburn
test_poll_LDADD = $(libburn_libburn_la_OBJECTS) $(THREAD_LIBS)
test_poll_SOURCES = test/poll.c
test_toc_CPPFLAGS = -Ilibburn
test_toc_LDADD = $(libburn_libburn_la_OBJECTS) $(THREAD_LIBS)
test_toc_SOURCES = test/toc.c
test_structest_CPPFLAGS = -Ilibburn
test_structest_LDADD = $(libburn_libburn_la_OBJECTS) $(THREAD_LIBS)
test_structest_SOURCES = test/structest.c
test_iso_CPPFLAGS = -Ilibisofs
test_iso_LDADD = $(libisofs_libisofs_la_OBJECTS) $(THREAD_LIBS)
test_iso_SOURCES = test/iso.c
## cdrskin construction site - ts A60816
cdrskin_cdrskin_CPPFLAGS = -Ilibburn
cdrskin_cdrskin_CFLAGS = -DCdrskin_libburn_0_2_3
cdrskin_cdrskin_LDADD = $(libburn_libburn_la_OBJECTS) $(THREAD_LIBS)
cdrskin_cdrskin_SOURCES = cdrskin/cdrskin.c cdrskin/cdrfifo.c cdrskin/cdrfifo.h cdrskin/cdrskin_timestamp.h
## cdrskin_cdrskin_SOURCES = cdrskin/cdrskin.c cdrskin/cdrfifo.c cdrskin/cdrfifo.h cdrskin/cleanup.c cdrskin/cleanup.h cdrskin/cdrskin_timestamp.h
##
## Open questions: how to compute $timestamp and express -DX="$timestamp"
##
@ -174,13 +145,9 @@ uninstall-local:
# Indent source files
indent_files = \
$(libisofs_libisofs_la_SOURCES) \
$(libburn_libburn_la_SOURCES) \
$(test_libburner_SOURCES) \
$(test_poll_SOURCES) \
$(test_toc_SOURCES) \
$(test_structest_SOURCES) \
$(test_iso_SOURCES)
$(test_structest_SOURCES)
indent: $(indent_files)
@ -196,12 +163,10 @@ indent: $(indent_files)
# Extra things
nodist_pkgconfig_DATA = \
libburn-1.pc \
libisofs-1.pc
libburn-1.pc
EXTRA_DIST = \
libburn-1.pc.in \
libisofs-1.pc.in \
version.h.in \
doc/comments \
doc/doxygen.conf.in \
@ -217,6 +182,8 @@ EXTRA_DIST = \
cdrskin/wiki_plain.txt \
cdrskin/cleanup.h \
cdrskin/cleanup.c \
libburn/os-freebsd.h \
libburn/os-linux.h \
libburn/sg-freebsd.c \
libburn/sg-linux.c \
COPYING

26
README
View File

@ -27,8 +27,8 @@ This libburn.pykix.org toplevel README (C) 2006 Thomas Schmitt
Our build system is based on autotools. For preparing the build of a SVN
snapshot you will need autotools of at least version 1.7.
Check out from SVN by
svn co http://libburn-svn.pykix.org/trunk libburn_pykix
and apply autotools by
svn co http://libburn-svn.pykix.org/libburn/trunk libburn_pykix
go into directory libburn_pykix and apply autotools by
./bootstrap
Alternatively you may unpack a release tarball for which you do not need
@ -42,22 +42,29 @@ into its toplevel directory (here: "libburn_pykix") and execute
To make the libraries accessible for running resp. developing applications
make install
The other half of the project, libisofs, is hosted in the libburn SVN, too:
svn co http://libburn-svn.pykix.org/libisofs/trunk libisofs_pykix
See README file there.
------------------------------------------------------------------------------
Overview of libburn.pykix.org
libburn.pykix.org is an open-source library for reading, mastering and writing
optical discs. For now this means only CD-R and CD-RW.
libburn.pykix.org is an open-source software project for reading, mastering and
writing optical discs. For now this means only CD-R and CD-RW.
The project comprises of several more or less interdependent parts which
together strive to be a usable foundation for application development.
These are libraries, language bindings, and middleware binaries which emulate
classical (and valuable) Linux tools.
Our scope is currently Linux 2.4 and 2.6 and we will have a hard time to widen
this for now, because of our history. The project could need advise from or
membership of skilled kernel people and people who know how to talk CD/DVD
drives into doing things.
Our scope is currently Linux 2.4 and 2.6 only. For ports to other systems
we would need : login on a development machine resp. a live OS on CD or DVD,
advise from a system person about the equivalent of Linux sg or FreeBSD CAM,
volunteers for testing of realistic use cases.
We do have a workable code base for burning data CDs, though. The burn API is
quite comprehensively documented and can be used to build a presentable
@ -158,6 +165,9 @@ Project history as far as known to me:
This version of cdrskin is much more cdrecord compatible in repect
to drive addressing and audio features.
- 30th October 2006 release of cdrskin-0.2.4 .
- 13th November 2006 splitting releases of libburn+cdrskin from libisofs.
------------------------------------------------------------------------------

View File

@ -323,7 +323,7 @@ will be the right one to replace "1,0,0" in above example.
If not --no_rc is the first argument then cdrskin attempts on startup to read
arguments from the following three files:
/etc/defaults/cdrskin
/etc/default/cdrskin
/etc/opt/cdrskin/rc
$HOME/.cdrskinrc
The files are read in the sequence given above.

View File

@ -22,7 +22,7 @@ set -x
#
# libburn version used: http://libburn.pykix.org
# Downloaded by:
# $ svn co http://libburn-svn.pykix.org/trunk libburn_pykix
# $ svn co http://libburn-svn.pykix.org/libburn/trunk libburn_pykix
# packed up in a tarball just to save it from inadverted changes by
# $ tar czf libburn_svn.tgz libburn_pykix
original="./libburn_svn.tgz"

View File

@ -796,7 +796,7 @@ ex:;
/** Fill the fifo as far as possible without writing to destination fd */
int Cdrfifo_fill(struct CdrfifO *o, int flag)
int Cdrfifo_fill(struct CdrfifO *o, int size, int flag)
{
int ret,fill= 0,space,state;
@ -810,6 +810,8 @@ int Cdrfifo_fill(struct CdrfifO *o, int flag)
} else if(state!=1)
break;
if(space<=0)
break;
if(size>=0 && fill>=size)
break;
ret= Cdrfifo_try_to_work(o,100000,NULL,NULL,2);
if(ret<0) {

View File

@ -145,10 +145,11 @@ int Cdrfifo_get_cdr_counters(struct CdrfifO *o,
int Cdrfifo_try_to_work(struct CdrfifO *o, int wait_usec,
char *reply_buffer, int *reply_count, int flag);
/** Fill the fifo as far as possible without writing to destination fd
/** Fill the fifo as far as possible without writing to destination fd.
@param size if >=0 : end filling after the given number of bytes
@return 1 on success, <=0 on failure
*/
int Cdrfifo_fill(struct CdrfifO *o, int flag);
int Cdrfifo_fill(struct CdrfifO *o, int size, int flag);
#endif /* Cdrfifo_headerfile_includeD */

View File

@ -177,6 +177,7 @@ or
#define Cdrskin_libburn_has_buffer_progresS 1
#define Cdrskin_libburn_has_pretend_fulL 1
#define Cdrskin_libburn_has_multI 1
#define Cdrskin_libburn_has_buffer_min_filL 1
#endif
#ifndef Cdrskin_libburn_versioN
@ -196,6 +197,7 @@ or
#ifdef Cdrskin_new_api_tesT
/* put macros under test caveat here */
#define Cdrskin_allow_sao_for_appendablE 1
/* could be i repaired this with getting -atip minimum speed */
#ifdef Cdrskin_libburn_has_read_atiP
@ -793,7 +795,7 @@ struct CdrtracK {
/** fd[0] of the fifo pipe. This is from where libburn reads its data. */
int fifo_outlet_fd;
int fifo_size;
int fifo_start_empty;
int fifo_start_at;
/** The possibly external fifo object which knows the real input fd and
the fd[1] of the pipe. */
@ -814,7 +816,7 @@ int Cdrtrack_set_track_type(struct CdrtracK *o, int track_type, int flag);
@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= set fifo_start_empty to 1
bit0= set fifo_start_at to 0
bit1= track is originally stdin
*/
int Cdrtrack_new(struct CdrtracK **track, struct CdrskiN *boss,
@ -828,7 +830,7 @@ int Cdrtrack_new(struct CdrtracK **track, struct CdrskiN *boss,
int *track_type_by_default, int *swap_audio_bytes,
int flag);
int Cdrskin_get_fifo_par(struct CdrskiN *skin, int *fifo_enabled,
int *fifo_size, int *fifo_start_empty, int flag);
int *fifo_size, int *fifo_start_at, int flag);
(*track)= o= TSOB_FELD(struct CdrtracK,1);
if(o==NULL)
@ -851,7 +853,7 @@ int Cdrtrack_new(struct CdrtracK **track, struct CdrskiN *boss,
o->fifo= NULL;
o->fifo_outlet_fd= -1;
o->fifo_size= 0;
o->fifo_start_empty= 0;
o->fifo_start_at= -1;
o->ff_fifo= NULL;
o->ff_idx= -1;
o->libburn_track= NULL;
@ -865,13 +867,13 @@ int Cdrtrack_new(struct CdrtracK **track, struct CdrskiN *boss,
#ifndef Cdrskin_extra_leaN
ret= Cdrskin_get_fifo_par(boss, &(o->fifo_enabled),&(o->fifo_size),
&(o->fifo_start_empty),0);
&(o->fifo_start_at),0);
if(ret<=0)
goto failed;
#endif /* ! Cdrskin_extra_leaN */
if(flag&1)
o->fifo_start_empty= 1;
o->fifo_start_at= 0;
return(1);
failed:;
Cdrtrack_destroy(track,0);
@ -913,6 +915,15 @@ int Cdrtrack_set_track_type(struct CdrtracK *o, int track_type, int flag)
}
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
@ -1174,11 +1185,15 @@ int Cdrtrack_fill_fifo(struct CdrtracK *track, int flag)
{
int ret,buffer_fill,buffer_space;
if(track->fifo==NULL || track->fifo_start_empty)
if(track->fifo==NULL || track->fifo_start_at==0)
return(2);
if(track->fifo_start_at>0 && track->fifo_start_at<track->fifo_size)
printf(
"cdrskin: NOTE : Input buffer will be initially filled up to %d bytes\n",
track->fifo_start_at);
printf("Waiting for reader process to fill input buffer ... ");
fflush(stdout);
ret= Cdrfifo_fill(track->fifo,0);
ret= Cdrfifo_fill(track->fifo,track->fifo_start_at,0);
if(ret<=0)
return(ret);
@ -1970,7 +1985,9 @@ set_dev:;
printf(" --fifo_disable disable fifo despite any fs=...\n");
printf(" --fifo_per_track use a separate fifo for each track\n");
printf(
" --fifo_start_empty do not wait for full fifo before burn start\n");
" fifo_start_at=<number> do not wait for full fifo but start burning\n");
printf(
" as soon as the given number of bytes is read\n");
printf(
" grab_drive_and_wait=<num> grab drive, wait given number of\n");
printf(
@ -2373,7 +2390,7 @@ struct CdrskiN {
/** fd[0] of the fifo pipe. This is from where libburn reads its data. */
int fifo_outlet_fd;
int fifo_size;
int fifo_start_empty;
int fifo_start_at;
int fifo_per_track;
@ -2470,7 +2487,7 @@ int Cdrskin_new(struct CdrskiN **skin, struct CdrpreskiN *preskin, int flag)
o->fifo= NULL;
o->fifo_outlet_fd= -1;
o->fifo_size= 4*1024*1024;
o->fifo_start_empty= 0;
o->fifo_start_at= -1;
o->fifo_per_track= 0;
o->adr_trn= NULL;
o->drives= NULL;
@ -2559,11 +2576,11 @@ int Cdrskin_get_source(struct CdrskiN *skin, char *source_path,
/** Return information about current fifo setting */
int Cdrskin_get_fifo_par(struct CdrskiN *skin, int *fifo_enabled,
int *fifo_size, int *fifo_start_empty, int flag)
int *fifo_size, int *fifo_start_at, int flag)
{
*fifo_enabled= skin->fifo_enabled;
*fifo_size= skin->fifo_size;
*fifo_start_empty= skin->fifo_start_empty;
*fifo_start_at= skin->fifo_start_at;
return(1);
}
@ -3235,7 +3252,7 @@ int Cdrskin_report_disc_status(struct CdrskiN *skin, enum burn_disc_status s,
int flag)
{
printf("cdrskin: status %d ",s);
if (s==BURN_DISC_FULL) {
if(s==BURN_DISC_FULL) {
printf("burn_disc_full \"There is a disc with data on it in the drive\"\n");
} else if(s==BURN_DISC_BLANK) {
printf("burn_disc_blank \"The drive holds a blank disc\"\n");
@ -3767,9 +3784,10 @@ int Cdrskin_wait_before_action(struct CdrskiN *skin, int flag)
else
sprintf(speed_text,"%.f",skin->x_speed);
printf(
"Starting to write CD/DVD at speed %s in %s %s mode for single session.\n",
"Starting to write CD/DVD at speed %s in %s %s mode for %s session.\n",
speed_text,(skin->dummy_mode?"dummy":"real"),
(flag&1?"BLANK":skin->preskin->write_mode_name));
(flag&1?"BLANK":skin->preskin->write_mode_name),
(skin->multi?"multi":"single"));
printf("Last chance to quit, starting real write in %3d seconds.",
skin->gracetime);
fflush(stdout);
@ -4202,15 +4220,19 @@ ex:;
int Cdrskin_activate_write_mode(struct CdrskiN *skin, enum burn_disc_status s,
int flag)
{
int ok;
int ok, was_still_default= 0, block_type_demand,track_type,sector_size, i;
struct burn_drive_info *drive_info;
if(strcmp(skin->preskin->write_mode_name,"DEFAULT")==0) {
was_still_default= 1;
#ifdef Cdrskin_allow_libburn_taO
if(s == BURN_DISC_APPENDABLE)
if(s == BURN_DISC_APPENDABLE) {
strcpy(skin->preskin->write_mode_name,"TAO");
else
was_still_default= 2; /*<<< prevents trying of SAO if drive dislikes TAO*/
} else
#endif
strcpy(skin->preskin->write_mode_name,"SAO");
@ -4239,26 +4261,50 @@ int Cdrskin_activate_write_mode(struct CdrskiN *skin, enum burn_disc_status s,
goto it_is_done;
}
/* <<< this should become a libburn API function.The knowledge about TAO audio
track block type is quite inappropriate here. It refers to a habit of
spc_select_write_params() (and MMC-1 table 61). But the knowledge about
the tracklist is rather cdrskin realm. (ponder ...)
*/
check_with_drive:;
drive_info= skin->drives+skin->driveno;
ok= 0;
if(skin->write_type==BURN_WRITE_RAW)
ok= !!(drive_info->raw_block_types & BURN_BLOCK_RAW96R);
else if(skin->write_type==BURN_WRITE_SAO)
ok= !!(drive_info->sao_block_types & BURN_BLOCK_SAO);
else if(skin->write_type==BURN_WRITE_TAO)
ok= ((drive_info->tao_block_types & (BURN_BLOCK_MODE1|BURN_BLOCK_RAW0))
== (BURN_BLOCK_MODE1|BURN_BLOCK_RAW0));
if(!ok) {
if(! skin->force_is_set) {
/* >>> if write mode was not set explicitely: try to find a better one */;
else if(skin->write_type==BURN_WRITE_TAO) {
block_type_demand= 0;
for(i=0;i<skin->track_counter;i++) {
Cdrtrack_get_track_type(skin->tracklist[i],&track_type,&sector_size,0);
if(track_type==BURN_AUDIO)
block_type_demand|= BURN_BLOCK_RAW0;
else
block_type_demand|= BURN_BLOCK_MODE1;
}
ok= ((drive_info->tao_block_types & block_type_demand)==block_type_demand);
}
if(!ok) {
fprintf(stderr,
"cdrskin: %s : Drive indicated refusal for write mode %s.\n",
(skin->force_is_set?"WARNING":"FATAL"),
(skin->force_is_set || was_still_default==1?"WARNING":"FATAL"),
skin->preskin->write_mode_name);
if(! skin->force_is_set) {
if(was_still_default==1) {
if(skin->write_type==BURN_WRITE_RAW ||
skin->write_type==BURN_WRITE_SAO) {
skin->write_type= BURN_WRITE_TAO;
skin->block_type= BURN_BLOCK_MODE1;
strcpy(skin->preskin->write_mode_name,"TAO");
} else {
skin->write_type= BURN_WRITE_SAO;
skin->block_type= BURN_BLOCK_SAO;
strcpy(skin->preskin->write_mode_name,"SAO");
}
was_still_default= 2; /* do not try more than once */
goto check_with_drive;
}
fprintf(stderr,"cdrskin: HINT : If you are certain that the drive will do, try option -force\n");
return(0);
}
@ -4282,7 +4328,7 @@ int Cdrskin_burn(struct CdrskiN *skin, int flag)
enum burn_drive_status drive_status;
struct burn_progress p;
struct burn_drive *drive;
int ret,loop_counter= 0,max_track= -1,i,hflag,nwa;
int ret,loop_counter= 0,max_track= -1,i,hflag,nwa,num;
int fifo_disabled= 0,fifo_percent,total_min_fill,mb,min_buffer_fill= 101;
double put_counter,get_counter,empty_counter,full_counter;
double start_time,last_time;
@ -4353,7 +4399,7 @@ int Cdrskin_burn(struct CdrskiN *skin, int flag)
if (s != BURN_DISC_BLANK) {
#endif
Cdrskin_release_drive(skin,0);
fprintf(stderr,"cdrskin: FATAL : no blank media detected.\n");
fprintf(stderr,"cdrskin: FATAL : no writeable media detected.\n");
return(0);
}
@ -4585,6 +4631,18 @@ fifo_full_at_end:;
"Cdrskin: fifo was %.f times empty and %.f times full, min fill was %d%%.\n",
empty_counter,full_counter,fifo_percent);
}
drive_status= burn_drive_get_status(drive, &p);
#ifdef Cdrskin_libburn_has_buffer_min_filL
/* cdrskin recorded its own coarse min_buffer_fill.
libburn's is finer - if enough bytes were processed so it is available.*/
if(p.buffer_min_fill<=p.buffer_capacity && p.buffer_capacity>0) {
num= 100.0 * ((double) p.buffer_min_fill)/(double) p.buffer_capacity;
if(num<min_buffer_fill)
min_buffer_fill= num;
}
#endif /* Cdrskin_libburn_has_buffer_min_filL */
if(min_buffer_fill>100)
min_buffer_fill= 50;
printf("Min drive buffer fill was %d%%\n", min_buffer_fill);
@ -4982,8 +5040,16 @@ set_driveropts:;
if(skin->verbosity>=Cdrskin_verbose_cmD)
printf("cdrskin: option fs=... disabled\n");
} else if(strcmp(argv[i],"--fifo_start_empty")==0) {
skin->fifo_start_empty= 1;
} else if(strcmp(argv[i],"--fifo_start_empty")==0) { /* obsoleted */
skin->fifo_start_at= 0;
} else if(strncmp(argv[i],"fifo_start_at=",14)==0) {
value= Scanf_io_size(argv[i]+14,0);
if(value>1024.0*1024.0*1024.0)
value= 1024.0*1024.0*1024.0;
else if(value<0)
value= 0;
skin->fifo_start_at= value;
} else if(strcmp(argv[i],"--fifo_per_track")==0) {
skin->fifo_per_track= 1;
@ -5026,6 +5092,7 @@ gracetime_equals:;
} else if(
strcmp(argv[i],"--fifo_disable")==0 ||
strcmp(argv[i],"--fifo_start_empty")==0 ||
strncmp(argv[i],"fifo_start_at=",14)==0 ||
strcmp(argv[i],"--fifo_per_track")==0 ||
strncmp(argv[i],"-fs=",4)==0 ||
strncmp(argv[i],"fs=",3)==0 ||

View File

@ -221,10 +221,12 @@ Enhancements towards previous stable version cdrskin-0.2.2:
<DT><H3>Development snapshot, version 0.2.5 :</H3></DT>
<DD>Enhancements towards stable version 0.2.4:
<UL>
<LI>Option <KBD><B>-tao</B></KBD> is fully enabled.
With single track or with undefined size from standard input,
default is -tao. With other multi-track sessions, default is -sao.
(The latter is an intentional deviation from cdrecord defaults.)
<LI>Option <KBD><B>-tao</B></KBD> is fully enabled.<BR>
SAO is still the preferred default but TAO is default if a track of
unpredicted size is present (stdin, named pipe, ...) or if a follow-up
session is written to an appendable CD.
(This is an intentional deviation from cdrecord defaults which themselves
have changed with the newest cdrecord versions.)
</LI>
<LI>Status report during blank, preparation and finalization improved.</LI>
<LI>Bug fixed: Trailing trash appended to .wav files caused error message
@ -243,7 +245,7 @@ Enhancements towards previous stable version cdrskin-0.2.2:
<DD>&nbsp;</DD>
<DT>Maintainers of cdrskin unstable packages please use SVN of
<A HREF="http://libburn.pykix.org"> libburn.pykix.org</A></DT>
<DD>Download: <KBD><B>svn co http://libburn-svn.pykix.org/trunk libburn_pykix</B>
<DD>Download: <KBD><B>svn co http://libburn-svn.pykix.org/libburn/trunk libburn_pykix</B>
</KBD></DD>
<DD>Build: <KBD><B>cd libburn_pykix ; ./bootstrap ; ./configure ; make</B>
</KBD></DD>

View File

@ -1 +1 @@
#define Cdrskin_timestamP "2006.11.12.185342"
#define Cdrskin_timestamP "2006.11.22.122228"

View File

@ -10,7 +10,8 @@ Deliberate deviations of cdrskin from cdrecord compatibility:
+ -pad is always set for audio tracks
+ -sao is default for multi-track sessions with all track lengths defined
+ -sao is default where possible, -tao is default where needed
(Note: cdrecord write mode defaults have changed in cdrtools-2.01.01a20)
+ premature end of source is not an error and leads to full announced tracksize
@ -1142,7 +1143,6 @@ Made available drive buffer fill during write
libburn/sg-freebsd.c
Updated tangling of FreeBSD code with mmc.c :(
2006.10.23.134719 [310]
cdrskin/cdrskin.c
Corrected -toc track counter and notified about "hidden" tracks
@ -1429,6 +1429,7 @@ Prepared next cdrskin-0.2.5 upload cycle
* Bug fixed: Trailing trash appended to .wav files caused error message
and, if exceeding fifo size, could even stall a burn.
2006.11.09.112832 [365]
cdrskin/cdrskin.c
Check desired write and block type with burn_drive_info.*_block_types
@ -1494,46 +1495,233 @@ Demanded (for now) -tao for writing to appendable CDs
cdrskin/cdrskin.c
Generally enabled -multi, -msinfo and writing to appendable CDs in TAO mode
11 Nov 2006 []
11 Nov 2006 [381]
cdrskin/changelog.txt
Prepared next cdrskin-0.2.5 cycle
------------------------------------ cycle - cdrskin-0.2.5 - 2006.11.11.
11 Nov 2006 [382]
cdrskin/cdrskin_eng.html
cdrskin/README
Updated docs about multi-session
------------------------------------ cycle - cdrskin-0.2.5 - 2006.11.11.163625
* Multi-session CDs (for now restricted to write mode TAO), -multi, -msinfo
* Bug fixed: False speed with first pacifier cycle. Potential SIGFPE by NaN.
* multi-session CDs (for now restricted to write mode TAO) by -multi, -msinfo
2006.11.12.085808 [383]
libburn/mmc.c
Made speed 0 in burn_drive_set_speed() really maximum speed (i.e. FFFFh)
------------------------------------ cycle - cdrskin-0.2.5 -
2006.11.12.113629 [384]
cdrskin/cdrskin.c
Made SAO preferrable default write mode, kept TAO as default where needed
2006.11.12.152723 [385]
libburn/mmc.c
libburn/libdax_msgs.h
Reacted on error condition during write operation
2006.11.12.185342 [386]
cdrskin/cdrskin.c
Made -toc on blank CD exit with value 0 (rather than 7 with no media)
13 Nov 2006 [390]
README
cdrskin/add_ts_changes_to_libburn_0_2_5
cdrskin/cdrskin_eng.html
Changed SVN URLs to reflect new structure
13 Nov 2006 [391]
README
Refered to both libburn and libisofs in SVN description, updated history
------------------------------------ cycle - cdrskin-0.2.5 - 2006.11.13.122418
* Enabled named pipes and other non-plain file types as track sources
(actually already in previous cycle)
* Bug fixed: Default speed was not highest possible but announced as "MAX"
* SAO is preferred default write mode, TAO is default when needed
2006.11.13.122418 [392]
cdrskin/changelog.txt
Next cdrskin-0.2.5 cycle
13 Nov 2006 [393]
cdrskin/cdrskin_eng.html
Adjusted description of write mode default
13 Nov 2006 [394]
Makefile.am
configure.ac
Removed references to libisofs
14 Nov 2006 [399]
README
Changed references to libisofs
14 Nov 2006 [400]
cdrskin/README
Corrected a typo reported by George Danchev
2006.11.14.104023 [401]
cdrskin/cdrskin.c
Implemented try to find on restricted drives a suitable write mode as default
14 Nov 2006 [402]
libburn/libburn.h
Fixed a wrong name in API description
15 Nov 2006 [403]
libburn/write.h
libburn/write.c
Prepared tests for eventual drive which might support SAO to appendable CD
15 Nov 2006 [404]
libburn/sg-linux.c
Enhanced optional SCSI command logging
2006.11.15.091329 [405]
cdrskin/cdrskin.c
Adjusted some texts to new multi-session situation
2006.11.15.170927 [406]
libburn/sg.h
libburn/sg-linux.c
libburn/sg-freebsd.c
+ libburn/sg-freebsd-port.c
libburn/spc.h
libburn/spc.c
libburn/drive.c
Made portability clarifications
15 Nov 2006 [407]
libburn/sg-linux.c
libburn/sg-freebsd-port.c
Narrowed system specific part of enumerate_common()
2006.11.16.111656 [408] [409]
Makefile.am
+ libburn/os.h
+ libburn/os-linux.h
+ libburn/os-freebsd.h
libburn/sg.h
libburn/sg.c
libburn/transport.h
libburn/cleanup.c
libburn/sg-linux.c
libburn/sg-freebsd-port.c
cdrskin/cleanup.c
cdrskin/compile_cdrskin.sh
Made consolidaed operating system adapters for ease of porting
2006.11.16.133951 [410]
libburn/sg-linux.c
libburn/sg-freebsd-port.c
Polished porting hints and self-compliance to newly established specs
16 Nov 2006 [411]
libburn/sg-linux.c
libburn/sg-freebsd-port.c
Polished porting hints
16 Nov 2006 [412]
test/libburner.c
Obsoleted --stdin_size by automatic TAO, cared for non-plain track files
16 Nov 2006 [413]
doc/comments
Updated help text of libburner
17 Nov 2006 [414]
Makefile.am
+ test/telltoc.c
Created info retrieval companion for libburner
17 Nov 2006 [415]
test/libburner.c
Enabled multi-session with libburner
17 Nov 2006 [416]
Makefile.am
- test/toc.c
Obsoleted old test program
17 Nov 2006 [417] [418]
test/telltoc.c
Shifted a comma
2006.11.18.194606 [419]
libburn/transport.h
libburn/sbc.h
libburn/sbc.c
libburn/drive.c
Test wether SCSI 1Bh START UNIT would be helpful with ticket 90
2006.11.19.114413 [420]
libburn/libburn.h
libburn/mmc.c
libburn/sector.c
libburn/write.c
cdrskin/cdrskin.c
Implemented libburn builtin fine granulated drive buffer min-fill recording
2006.11.19.162900 [421]
cdrskin/cdrskin.c
Avoided self contradiction with "Min drive buffer fill"
2006.11.19.163646 [422]
cdrskin/cdrskin.c
Fixed missing brackets from revison 421
2006.11.20.090207 [423]
libburn/sector.c
Silenced compiler warnings
2006.11.20.090503 [424]
libburn/drive.c
Removed a redundant d->start_unit() of revision 419
------------------------------------ cycle - cdrskin-0.2.5 - 2006.11.20.092808
* libisofs unbundled from libburn+cdrskin
* Hints for porting to other operating systems are now in sg-*.c
[]
Enable multi-session for write modes other than TAO
[]
Liberate generic SCSI code from sg-linux.c (scsi_notify_error(), scsi_error())
cdrskin/cdrskin.c
cdrskin/cdrfifo.h
cdrskin/cdrfifo.c
cdrskin/wiki_plain.txt
Implemented new option fifo_start_at=
[]
Coordinate scsi_notify_error() and scsi_error() about harmless conditions
2006 []
----------------------------------------- long term intentions:
[]
-reset: ioctl(fd,CDROMRESET) ioctl(fd,SG_SCSI_RESET,SG_SCSI_RESET_DEVICE)
2006 []
[]
Open O_EXCL all devices in address resolution chain
(Might help with non-standard hdX device siblings)
2006 []
[]
Convert libburn_experimental: into LIBDAX_MSGS_SEV_DEBUG
Convert burn_print() into libdax_msgs_submit()
2006 []
[]
Test unlocking of all drives by burn_drive_info_free()
Test unlocking of single drive by burn_drive_grab(), burn_drive_release()
2006 []
[]
Clear outdated persistent read buffer after small CD image was read (ticket 57)
[]
Enable multi-session for write modes other than TAO

View File

@ -23,31 +23,27 @@ typedef void (*sighandler_t)(int);
#include "cleanup.h"
#ifdef __FreeBSD__
#ifndef Cleanup_has_no_libburn_os_H
#include "../libburn/os.h"
/* see os.h for name of particular os-*.h where this is defined */
static int signal_list[]= { BURN_OS_SIGNAL_MACRO_LIST , -1};
static char *signal_name_list[]= { BURN_OS_SIGNAL_NAME_LIST , "@"};
static int signal_list_count= BURN_OS_SIGNAL_COUNT;
static int non_signal_list[]= { BURN_OS_NON_SIGNAL_MACRO_LIST, -1};
static int non_signal_list_count= BURN_OS_NON_SIGNAL_COUNT;
#else /* ! Cleanup_has_no_libburn_os_H */
/* Outdated. Linux only. For backward compatibility with pre-libburn-0.2.3 */
/* Signals to be caught */
static int signal_list[]= {
SIGHUP, SIGINT, SIGQUIT, SIGILL, SIGABRT,
SIGFPE, SIGSEGV, SIGPIPE, SIGALRM, SIGTERM,
SIGUSR1, SIGUSR2, SIGXCPU, SIGTSTP, SIGTTIN,
SIGTTOU,
SIGBUS, SIGPROF, SIGSYS, SIGTRAP,
SIGVTALRM, SIGXCPU, SIGXFSZ, -1
};
static char *signal_name_list[]= {
"SIGHUP", "SIGINT", "SIGQUIT", "SIGILL", "SIGABRT",
"SIGFPE", "SIGSEGV", "SIGPIPE", "SIGALRM", "SIGTERM",
"SIGUSR1", "SIGUSR2", "SIGXCPU", "SIGTSTP", "SIGTTIN",
"SIGTTOU",
"SIGBUS", "SIGPROF", "SIGSYS", "SIGTRAP",
"SIGVTALRM", "SIGXCPU", "SIGXFSZ", "@"
};
static int signal_list_count= 23;
#else /* __FreeBSD__ */
/* Signals to be caught */
static int signal_list[]= {
static int signal_list[]= {
SIGHUP, SIGINT, SIGQUIT, SIGILL, SIGABRT,
SIGFPE, SIGSEGV, SIGPIPE, SIGALRM, SIGTERM,
SIGUSR1, SIGUSR2, SIGXCPU, SIGTSTP, SIGTTIN,
@ -55,7 +51,7 @@ static int signal_list[]= {
SIGBUS, SIGPOLL, SIGPROF, SIGSYS, SIGTRAP,
SIGVTALRM, SIGXCPU, SIGXFSZ, -1
};
static char *signal_name_list[]= {
static char *signal_name_list[]= {
"SIGHUP", "SIGINT", "SIGQUIT", "SIGILL", "SIGABRT",
"SIGFPE", "SIGSEGV", "SIGPIPE", "SIGALRM", "SIGTERM",
"SIGUSR1", "SIGUSR2", "SIGXCPU", "SIGTSTP", "SIGTTIN",
@ -65,15 +61,17 @@ static char *signal_name_list[]= {
};
static int signal_list_count= 24;
#endif /* ! __FreeBSD__ */
/* Signals not to be caught */
static int non_signal_list[]= {
SIGKILL, SIGCHLD, SIGSTOP, SIGURG, -1
};
};
static int non_signal_list_count= 4;
#endif /* Cleanup_has_no_libburn_os_H */
/* run time dynamic part */
static char cleanup_msg[4096]= {""};
static int cleanup_exiting= 0;

View File

@ -32,13 +32,13 @@ do
libvers="-DCdrskin_libburn_cvs_A60220_tS"
libdax_audioxtr_o=
libdax_msgs_o="libburn/message.o"
cleanup_src_or_obj="cdrskin/cleanup.c"
cleanup_src_or_obj="-DCleanup_has_no_libburn_os_H cdrskin/cleanup.c"
elif test "$i" = "-libburn_0_2_2"
then
libvers="-DCdrskin_libburn_0_2_2"
libdax_audioxtr_o=
libdax_msgs_o="libburn/message.o"
cleanup_src_or_obj="cdrskin/cleanup.c"
cleanup_src_or_obj="-DCleanup_has_no_libburn_os_H cdrskin/cleanup.c"
elif test "$i" = "-libburn_0_2_3"
then
libvers="-DCdrskin_libburn_0_2_3"
@ -51,6 +51,7 @@ do
elif test "$i" = "-oldfashioned"
then
def_opts="$def_opts -DCdrskin_oldfashioned_api_usE"
cleanup_src_or_obj="-DCleanup_has_no_libburn_os_H cdrskin/cleanup.c"
elif test "$i" = "-no_largefile"
then
largefile_opts=

View File

@ -15,13 +15,14 @@ runs, though. The most comprehensive technical overview of cdrskin
can be found in cdrskin/README . Online available as :
http://libburn.pykix.org/browser/trunk/cdrskin/README?format=raw
About libburn API for burning CD: http://libburn-api.pykix.org
--------------------------------------------------------------------------
cdrskin with CD media fails to match its paragon cdrecord on one major field:
Multi session.
Convenient TAO burn mode is provided in development version 0.2.5 but not in
current "stable" release 0.2.4.
current "stable" release 0.2.4. The same is true for multi-session, where
follow-up sessions must be done in TAO mode. (Users who have a burner which
succeeds with a follow-up session via cdrecord -sao : please contact us.)
cdrskin does not provide DVD burning yet. See advise to use dvd+rw-tools
at the end of this text.
@ -36,9 +37,9 @@ cdrskin for its task to emulate cdrecord. There are some, nevertheless,
which provide rather exotic unique features of cdrskin.
The cdrecord-compatible options are listed in the output of
{{{
cdrskin -help
}}}
where the option "help" has *one* dash.
For these options you may expect program behavior that is roughly the
same as described in original man 1 cdrecord .
@ -46,9 +47,9 @@ same as described in original man 1 cdrecord .
Online: http://cdrecord.berlios.de/old/private/man/cdrecord-2.0.html
The cdrskin-specific options are listed by
{{{
cdrskin --help
}}}
where the option "help" has *two* dashes.
Those have no man page yet. Some are very experimental and should only be
@ -60,11 +61,10 @@ Some are of general user interest, though:
--devices allows the sysadmin to scan the system for possible drives
and displays their detected properties.
The drives are listed one per line, with fields:
libburn-drive-number sysadmin-device-file permissions : vendor type
libburn-drive-number, sysadmin-device-file, permissions, vendor, type
{{{
0 dev='/dev/sg0' rwrw-- : 'HL-DT-ST' 'DVDRAM GSA-4082B'
}}}
This feature is valuable since cdrskin -scanbus will not give you
the device file name and its current permissions.
cdrskin will accept of course the proposed dev= option as address
@ -78,7 +78,7 @@ has to offer both, r- and w-permission.
--------------------------------------------------------------------------
--fifo_start_empty is a throughput enhancer for unsteady data streams
fifo_start_at=<num> is a throughput enhancer for unsteady data streams
like they are produced by a compressing archiver program when piping to
CD on-the-fly. It makes better use of the general property of a FIFO
buffer to transport surplus bandwidth into the future. Yep. A time machine.
@ -102,10 +102,16 @@ underruns, of course.
With a very fat fs=# buffer (128 MB for 12x CD is not unrealistic) this
can cause a big delay until burning finally starts and takes its due time.
--fifo_start_empty makes cdrskin start burning without waiting for the
FIFO to be full resp. the data stream to end. It can make use of the
seconds spend with drive preparation and lead-in, it risks a few drive
buffer underruns at the beginning of burn - but modern drives stand this.
fifo_start_at=<num> makes cdrskin start burning after the given number of bytes
is read rather than waiting for the FIFO to be completely full resp. the data
stream to end. It risks a few drive buffer underruns at the beginning of burn
- but modern drives stand this.
Usage examples:
{{{
cdrskin ... fs=128m fifo_start_at=20m ...
cdrskin ... fifo_start_at=0 ...
}}}
Note: no FIFO can give you better average throughput than the average
throughput of the data source and the throughput of the burner.
@ -155,15 +161,15 @@ cdrskin the necessary hint.
Example: Your frontend insists in using "0,0,0" and --devices reported
dev='/dev/hdc' resp. cdrskin dev=ATA -scanbus reported "1,0,0" then this
would be the appropriate translation:
{{{
dev_translation=+0,0,0+/dev/hdc
}}}
The "+" character is a separator to be choosen by you.
Currently i am not aware of the need to choose any other than "+"
unless you get playful with custom translations like
{{{
dev_translation=-"cd+dvd"-1,0,0
}}}
See http://scdbackup.sourceforge.net/k3b_on_cdrskin.html
for an illustrated example with K3b 0.10 .

View File

@ -1,4 +1,4 @@
AC_INIT([libburn], [0.2.3], [http://libburn.pykix.org])
AC_INIT([libburn], [0.2.6], [http://libburn.pykix.org])
AC_PREREQ([2.50])
dnl AC_CONFIG_HEADER([config.h])
@ -25,7 +25,7 @@ dnl if MAJOR or MINOR version changes, be sure to change AC_INIT above to match
dnl
BURN_MAJOR_VERSION=0
BURN_MINOR_VERSION=2
BURN_MICRO_VERSION=3
BURN_MICRO_VERSION=6
BURN_INTERFACE_AGE=0
BURN_BINARY_AGE=0
BURN_VERSION=$BURN_MAJOR_VERSION.$BURN_MINOR_VERSION.$BURN_MICRO_VERSION
@ -112,6 +112,5 @@ AC_CONFIG_FILES([
doc/doxygen.conf
version.h
libburn-1.pc
libisofs-1.pc
])
AC_OUTPUT

View File

@ -91,9 +91,8 @@ stay upward compatible for a good while.
@subsection libburner-help Libburner --help
<pre>
Usage: test/libburner
[--drive <address>|<driveno>|"-"]
[--blank_fast|--blank_full] [--audio]
[--try_to_simulate] [--stdin_size <bytes>]
[--drive <address>|<driveno>|"-"] [--audio]
[--blank_fast|--blank_full] [--try_to_simulate]
[<one or more imagefiles>|"-"]
Examples
A bus scan (needs rw-permissions to see a drive):
@ -110,7 +109,7 @@ Burn two audio tracks
test/libburner --drive /dev/hdc --audio track1.cd track2.cd
Burn a compressed afio archive on-the-fly, pad up to 700 MB:
( cd my_directory ; find . -print | afio -oZ - ) | \
test/libburner --drive /dev/hdc --stdin_size 734003200 -
test/libburner --drive /dev/hdc -
To be read from *not mounted* CD via: afio -tvZ /dev/hdc
Program tar would need a clean EOF which our padded CD cannot deliver.
</pre>

View File

@ -23,31 +23,27 @@ typedef void (*sighandler_t)(int);
#include "cleanup.h"
#ifdef __FreeBSD__
#ifndef Cleanup_has_no_libburn_os_H
#include "../libburn/os.h"
/* see os.h for name of particular os-*.h where this is defined */
static int signal_list[]= { BURN_OS_SIGNAL_MACRO_LIST , -1};
static char *signal_name_list[]= { BURN_OS_SIGNAL_NAME_LIST , "@"};
static int signal_list_count= BURN_OS_SIGNAL_COUNT;
static int non_signal_list[]= { BURN_OS_NON_SIGNAL_MACRO_LIST, -1};
static int non_signal_list_count= BURN_OS_NON_SIGNAL_COUNT;
#else /* ! Cleanup_has_no_libburn_os_H */
/* Outdated. Linux only. For backward compatibility with pre-libburn-0.2.3 */
/* Signals to be caught */
static int signal_list[]= {
SIGHUP, SIGINT, SIGQUIT, SIGILL, SIGABRT,
SIGFPE, SIGSEGV, SIGPIPE, SIGALRM, SIGTERM,
SIGUSR1, SIGUSR2, SIGXCPU, SIGTSTP, SIGTTIN,
SIGTTOU,
SIGBUS, SIGPROF, SIGSYS, SIGTRAP,
SIGVTALRM, SIGXCPU, SIGXFSZ, -1
};
static char *signal_name_list[]= {
"SIGHUP", "SIGINT", "SIGQUIT", "SIGILL", "SIGABRT",
"SIGFPE", "SIGSEGV", "SIGPIPE", "SIGALRM", "SIGTERM",
"SIGUSR1", "SIGUSR2", "SIGXCPU", "SIGTSTP", "SIGTTIN",
"SIGTTOU",
"SIGBUS", "SIGPROF", "SIGSYS", "SIGTRAP",
"SIGVTALRM", "SIGXCPU", "SIGXFSZ", "@"
};
static int signal_list_count= 23;
#else /* __FreeBSD__ */
/* Signals to be caught */
static int signal_list[]= {
static int signal_list[]= {
SIGHUP, SIGINT, SIGQUIT, SIGILL, SIGABRT,
SIGFPE, SIGSEGV, SIGPIPE, SIGALRM, SIGTERM,
SIGUSR1, SIGUSR2, SIGXCPU, SIGTSTP, SIGTTIN,
@ -55,7 +51,7 @@ static int signal_list[]= {
SIGBUS, SIGPOLL, SIGPROF, SIGSYS, SIGTRAP,
SIGVTALRM, SIGXCPU, SIGXFSZ, -1
};
static char *signal_name_list[]= {
static char *signal_name_list[]= {
"SIGHUP", "SIGINT", "SIGQUIT", "SIGILL", "SIGABRT",
"SIGFPE", "SIGSEGV", "SIGPIPE", "SIGALRM", "SIGTERM",
"SIGUSR1", "SIGUSR2", "SIGXCPU", "SIGTSTP", "SIGTTIN",
@ -65,15 +61,17 @@ static char *signal_name_list[]= {
};
static int signal_list_count= 24;
#endif /* ! __FreeBSD__ */
/* Signals not to be caught */
static int non_signal_list[]= {
SIGKILL, SIGCHLD, SIGSTOP, SIGURG, -1
};
};
static int non_signal_list_count= 4;
#endif /* Cleanup_has_no_libburn_os_H */
/* run time dynamic part */
static char cleanup_msg[4096]= {""};
static int cleanup_exiting= 0;

View File

@ -52,7 +52,7 @@ void burn_drive_free(struct burn_drive *d)
return;
/* ts A60822 : close open fds before forgetting them */
if (burn_drive_is_open(d))
sg_close_drive(d);
d->release(d);
free((void *) d->idata);
free((void *) d->mdata);
if(d->toc_entry != NULL)
@ -160,6 +160,7 @@ int burn_drive_grab(struct burn_drive *d, int le)
d->load(d);
d->lock(d);
d->start_unit(d);
/* ts A61020 : this was BURN_DISC_BLANK as pure guess */
d->status = BURN_DISC_UNREADY;
@ -578,8 +579,10 @@ int burn_drive_scan_sync(struct burn_drive_info *drives[],
#endif /* 0 */
/* refresh the lib's drives */
sg_enumerate();
ata_enumerate();
/* ts A61115 : formerly sg_enumerate(); ata_enumerate(); */
scsi_enumerate_drives();
count = burn_drive_count();
if (count)
*drives =

View File

@ -388,9 +388,11 @@ struct burn_progress {
int index;
/** The starting logical block address */
int start_sector;
/** The number of sector */
/** On write: The number of sectors.
On blank: 0x10000 as upper limit for relative progress steps */
int sectors;
/** The current sector being processed */
/** On write: The current sector being processed.
On blank: Relative progress steps 0 to 0x10000 */
int sector;
/* ts A61023 */
@ -398,6 +400,14 @@ struct burn_progress {
unsigned buffer_capacity;
/** The free space in the drive buffer (might be slightly outdated) */
unsigned buffer_available;
/* ts A61119 */
/** The number of bytes sent to the drive buffer */
off_t buffered_bytes;
/** The minimum number of buffered bytes. (Caution: Before surely
one buffer size of bytes was processed, this value is 0xffffffff.)
*/
unsigned buffer_min_fill;
};
/** Initialize the library.
@ -643,7 +653,7 @@ void burn_drive_release(struct burn_drive *drive, int eject);
/** Returns what kind of disc a drive is holding. This function may need to be
called more than once to get a proper status from it. See burn_status
called more than once to get a proper status from it. See burn_disc_status
for details.
@param drive The drive to query for a disc.
@return The status of the drive, or what kind of disc is in it.

View File

@ -874,6 +874,14 @@ int mmc_read_buffer_capacity(struct burn_drive *d)
(data[4]<<24)|(data[5]<<16)|(data[6]<<8)|data[7];
d->progress.buffer_available =
(data[8]<<24)|(data[9]<<16)|(data[10]<<8)|data[11];
if (d->progress.buffered_bytes >= d->progress.buffer_capacity){
double fill;
fill = d->progress.buffer_capacity
- d->progress.buffer_available;
if (fill < d->progress.buffer_min_fill && fill>=0)
d->progress.buffer_min_fill = fill;
}
return 1;
}

72
libburn/os-freebsd.h Normal file
View File

@ -0,0 +1,72 @@
/* os-freebsd.h
Operating system specific libburn definitions and declarations. Included
by os.h in case of compilation for
FreeBSD with CAM
Copyright (C) 2006 Thomas Schmitt <scdbackup@gmx.net>, provided under GPL
*/
#ifndef BURN_OS_H_INCLUDED
#define BURN_OS_H_INCLUDED 1
/** List of all signals which shall be caught by signal handlers and trigger
a graceful abort of libburn. (See man 7 signal.)
*/
/* Once as system defined macros */
#define BURN_OS_SIGNAL_MACRO_LIST \
SIGHUP, SIGINT, SIGQUIT, SIGILL, SIGABRT, \
SIGFPE, SIGSEGV, SIGPIPE, SIGALRM, SIGTERM, \
SIGUSR1, SIGUSR2, SIGXCPU, SIGTSTP, SIGTTIN, \
SIGTTOU, \
SIGBUS, SIGPROF, SIGSYS, SIGTRAP, \
SIGVTALRM, SIGXCPU, SIGXFSZ
/* Once as text 1:1 list of strings for messages and interpreters */
#define BURN_OS_SIGNAL_NAME_LIST \
"SIGHUP", "SIGINT", "SIGQUIT", "SIGILL", "SIGABRT", \
"SIGFPE", "SIGSEGV", "SIGPIPE", "SIGALRM", "SIGTERM", \
"SIGUSR1", "SIGUSR2", "SIGXCPU", "SIGTSTP", "SIGTTIN", \
"SIGTTOU", \
"SIGBUS", "SIGPROF", "SIGSYS", "SIGTRAP", \
"SIGVTALRM", "SIGXCPU", "SIGXFSZ"
/* The number of above list items */
#define BURN_OS_SIGNAL_COUNT 23
/** To list all signals which shall surely not be caught */
#define BURN_OS_NON_SIGNAL_MACRO_LIST \
SIGKILL, SIGCHLD, SIGSTOP, SIGURG
/* The number of above list items */
#define BURN_OS_NON_SIGNAL_COUNT 4
/* The maximum size for a (SCSI) i/o transaction */
#define BURN_OS_TRANSPORT_BUFFER_SIZE 65536/2
/** To hold all state information of BSD device enumeration
which are now local in sg_enumerate() . So that sg_give_next_adr()
can work in BSD and sg_enumerate() can use it.
*/
#define BURN_OS_DEFINE_DRIVE_ENUMERATOR_T \
struct burn_drive_enumeration_state { \
union ccb ccb; \
int bufsize, fd; \
unsigned int i; \
int skip_device; \
}; \
typedef struct burn_drive_enumeration_state burn_drive_enumerator_t;
/* The list of operating system dependent elements in struct burn_drive.
To be initialized and used within sg-*.c .
*/
#define BURN_OS_TRANSPORT_DRIVE_ELEMENTS \
struct cam_device* cam;
#endif /* ! BURN_OS_H_INCLUDED */

63
libburn/os-linux.h Normal file
View File

@ -0,0 +1,63 @@
/* os-linux.h
Operating system specific libburn definitions and declarations. Included
by os.h in case of compilation for
Linux kernels 2.4 and 2.6 with Linux SCSI Generic (sg)
Copyright (C) 2006 Thomas Schmitt <scdbackup@gmx.net>, provided under GPL
*/
/** List of all signals which shall be caught by signal handlers and trigger
a graceful abort of libburn. (See man 7 signal.)
*/
/* Once as system defined macros */
#define BURN_OS_SIGNAL_MACRO_LIST \
SIGHUP, SIGINT, SIGQUIT, SIGILL, SIGABRT, \
SIGFPE, SIGSEGV, SIGPIPE, SIGALRM, SIGTERM, \
SIGUSR1, SIGUSR2, SIGXCPU, SIGTSTP, SIGTTIN, \
SIGTTOU, \
SIGBUS, SIGPOLL, SIGPROF, SIGSYS, SIGTRAP, \
SIGVTALRM, SIGXCPU, SIGXFSZ
/* Once as text 1:1 list of strings for messages and interpreters */
#define BURN_OS_SIGNAL_NAME_LIST \
"SIGHUP", "SIGINT", "SIGQUIT", "SIGILL", "SIGABRT", \
"SIGFPE", "SIGSEGV", "SIGPIPE", "SIGALRM", "SIGTERM", \
"SIGUSR1", "SIGUSR2", "SIGXCPU", "SIGTSTP", "SIGTTIN", \
"SIGTTOU", \
"SIGBUS", "SIGPOLL", "SIGPROF", "SIGSYS", "SIGTRAP", \
"SIGVTALRM", "SIGXCPU", "SIGXFSZ"
/* The number of above list items */
#define BURN_OS_SIGNAL_COUNT 24
/** To list all signals which shall surely not be caught */
#define BURN_OS_NON_SIGNAL_MACRO_LIST \
SIGKILL, SIGCHLD, SIGSTOP, SIGURG
/* The number of above list items */
#define BURN_OS_NON_SIGNAL_COUNT 4
/* The maximum size for a (SCSI) i/o transaction */
#define BURN_OS_TRANSPORT_BUFFER_SIZE 65536
/* To hold the index number of the most recently delivered address from
device enumeration.
*/
#define BURN_OS_DEFINE_DRIVE_ENUMERATOR_T \
typedef int burn_drive_enumerator_t;
/* The list of operating system dependent elements in struct burn_drive.
Usually they are initialized in sg-*.c:enumerate_common().
*/
#define BURN_OS_TRANSPORT_DRIVE_ELEMENTS \
int fd; \
\
/* ts A60926 : trying to lock against growisofs /dev/srN, /dev/scdN */ \
int sibling_count; \
int sibling_fds[LIBBURN_SG_MAX_SIBLINGS];

34
libburn/os.h Normal file
View File

@ -0,0 +1,34 @@
/* os.h
Operating system specific libburn definitions and declarations.
The macros defined here are used by libburn modules in order to
avoid own system dependent case distinctions.
Copyright (C) 2006 Thomas Schmitt <scdbackup@gmx.net>, provided under GPL
*/
#ifndef BURN_OS_H_INCLUDED
#define BURN_OS_H_INCLUDED 1
/*
Operating system case distinction
*/
#ifdef __FreeBSD__
/* ----------------------------- FreeBSD with CAM -------------------------- */
#include "os-freebsd.h"
#else /* operating system case distinction */
/* --------- Linux kernels 2.4 and 2.6 with Linux SCSI Generic (sg) -------- */
#include "os-linux.h"
#endif /* End of operating system case distinction */
#endif /* ! BURN_OS_H_INCLUDED */

View File

@ -11,6 +11,7 @@
/* spc command set */
static char SBC_LOAD[] = { 0x1b, 0, 0, 0, 3, 0 };
static char SBC_UNLOAD[] = { 0x1b, 0, 0, 0, 2, 0 };
static char SBC_START_UNIT[] = { 0x1b, 0, 0, 0, 1, 0 };
void sbc_load(struct burn_drive *d)
{
@ -37,12 +38,28 @@ void sbc_eject(struct burn_drive *d)
d->issue_command(d, &c);
}
/* ts A61118 : is it necessary to tell the drive to get ready for use ? */
int sbc_start_unit(struct burn_drive *d)
{
struct command c;
memcpy(c.opcode, SBC_START_UNIT, sizeof(SBC_START_UNIT));
c.retry = 1;
c.oplen = sizeof(SBC_START_UNIT);
c.dir = NO_TRANSFER;
c.page = NULL;
d->issue_command(d, &c);
return (c.error==0);
}
/* ts A61021 : the sbc specific part of sg.c:enumerate_common()
*/
int sbc_setup_drive(struct burn_drive *d)
{
d->eject = sbc_eject;
d->load = sbc_load;
d->start_unit = sbc_start_unit;
return 1;
}

View File

@ -8,6 +8,9 @@ struct burn_drive;
void sbc_load(struct burn_drive *);
void sbc_eject(struct burn_drive *);
/* ts A61118 */
int sbc_start_unit(struct burn_drive *);
/* ts A61021 : the sbc specific part of sg.c:enumerate_common()
*/
int sbc_setup_drive(struct burn_drive *d);

View File

@ -228,6 +228,8 @@ static unsigned char *get_sector(struct burn_write_opts *opts,
track->writecount += out->bytes;
track->written_sectors += out->sectors;
}
/* ts A61119 */
d->progress.buffered_bytes += out->bytes;
d->nwa += out->sectors;
out->bytes = 0;
@ -348,6 +350,8 @@ static void convert_subs(struct burn_write_opts *o, int inmode,
out[0] = ~out[0];
out[1] = ~out[1];
break;
/* ts A61119 : to silence compiler warnings */
default:;
}
}
@ -811,7 +815,14 @@ void process_q(struct burn_drive *d, unsigned char *q)
#endif
/* this needs more info. subs in the data? control/adr? */
/* ts A61119 : One should not use inofficial compiler extensions.
>>> Some day this function needs to be implemented. At least for now
the result does not match the "mode" of cdrecord -toc.
*/
/*
#warning sector_identify needs to be written
*/
int sector_identify(unsigned char *data)
{
scramble(data);

559
libburn/sg-freebsd-port.c Normal file
View File

@ -0,0 +1,559 @@
/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */
/*
This is the main operating system dependent SCSI part of libburn. It implements
the transport level aspects of SCSI control and command i/o.
Present implementation: FreeBSD CAM (untested)
PORTING:
Porting libburn typically will consist of adding a new operating system case
to the following switcher files:
os.h Operating system specific libburn definitions and declarations.
sg.c Operating system dependent transport level modules.
and of deriving the following system specific files from existing examples:
os-*.h Included by os.h. You will need some general system knowledge
about signals and knowledge about the storage object needs of your
transport level module sg-*.c.
sg-*.c This source module. You will need special system knowledge about
how to detect all potentially available drives, how to open them,
eventually how to exclusively reserve them, how to perform
SCSI transactions, how to inquire the (pseudo-)SCSI driver.
You will not need to care about CD burning, MMC or other high-level
SCSI aspects.
Said sg-*.c operations are defined by a public function interface, which has
to be implemented in a way that provides libburn with the desired services:
sg_give_next_adr() iterates over the set of potentially useful drive
address strings.
scsi_enumerate_drives() brings all available, not-whitelist-banned, and
accessible drives into libburn's list of drives.
sg_drive_is_open() tells wether libburn has the given drive in use.
sg_grab() opens the drive for SCSI commands and ensures
undisturbed access.
sg_release() closes a drive opened by sg_grab()
sg_issue_command() sends a SCSI command to the drive, receives reply,
and evaluates wether the command succeeded or shall
be retried or finally failed.
sg_obtain_scsi_adr() tries to obtain SCSI address parameters.
Porting hints are marked by the text "PORTING:".
Send feedback to libburn-hackers@pykix.org .
*/
/** PORTING : ------- OS dependent headers and definitions ------ */
#include <errno.h>
#include <unistd.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <stdlib.h>
#include <string.h>
#include <sys/poll.h>
#include <camlib.h>
#include <cam/scsi/scsi_message.h>
#include <cam/scsi/scsi_pass.h>
#include <err.h> /* XXX */
/** PORTING : ------ libburn portable headers and definitions ----- */
#include "transport.h"
#include "drive.h"
#include "sg.h"
#include "spc.h"
#include "mmc.h"
#include "sbc.h"
#include "debug.h"
#include "toc.h"
#include "util.h"
#include "libdax_msgs.h"
extern struct libdax_msgs *libdax_messenger;
/* is in portable part of libburn */
int burn_drive_is_banned(char *device_address);
/* ------------------------------------------------------------------------ */
/* ts A61115: Private functions. Port only if needed by public functions */
/* (Public functions are listed below) */
/* ------------------------------------------------------------------------ */
/* Helper function for scsi_give_next_adr() */
static int sg_init_enumerator(burn_drive_enumerator_t *idx)
{
idx->skip_device = 0;
if ((idx->fd = open(XPT_DEVICE, O_RDWR)) == -1) {
warn("couldn't open %s", XPT_DEVICE);
return -1;
}
bzero(&(idx->ccb), sizeof(union ccb));
idx->ccb.ccb_h.path_id = CAM_XPT_PATH_ID;
idx->ccb.ccb_h.target_id = CAM_TARGET_WILDCARD;
idx->ccb.ccb_h.target_lun = CAM_LUN_WILDCARD;
idx->ccb.ccb_h.func_code = XPT_DEV_MATCH;
idx->bufsize = sizeof(struct dev_match_result) * 100;
idx->ccb.cdm.match_buf_len = idx->bufsize;
idx->ccb.cdm.matches = (struct dev_match_result *)malloc(idx->bufsize);
if (idx->ccb.cdm.matches == NULL) {
warnx("can't malloc memory for matches");
close(idx->fd);
return -1;
}
idx->ccb.cdm.num_matches = 0;
idx->i = idx->ccb.cdm.num_matches; /* to trigger buffer load */
/*
* We fetch all nodes, since we display most of them in the default
* case, and all in the verbose case.
*/
idx->ccb.cdm.num_patterns = 0;
idx->ccb.cdm.pattern_buf_len = 0;
return 1;
}
/* Helper function for scsi_give_next_adr() */
static int sg_next_enumeration_buffer(burn_drive_enumerator_t *idx)
{
/*
* We do the ioctl multiple times if necessary, in case there are
* more than 100 nodes in the EDT.
*/
if (ioctl(idx->fd, CAMIOCOMMAND, &(idx->ccb)) == -1) {
warn("error sending CAMIOCOMMAND ioctl");
return -1;
}
if ((idx->ccb.ccb_h.status != CAM_REQ_CMP)
|| ((idx->ccb.cdm.status != CAM_DEV_MATCH_LAST)
&& (idx->ccb.cdm.status != CAM_DEV_MATCH_MORE))) {
warnx("got CAM error %#x, CDM error %d\n",
idx->ccb.ccb_h.status, idx->ccb.cdm.status);
return -1;
}
return 1;
}
static int sg_close_drive(struct burn_drive * d)
{
if (d->cam != NULL) {
cam_close_device(d->cam);
d->cam = NULL;
}
return 0;
}
/* ----------------------------------------------------------------------- */
/* PORTING: Private functions which contain publicly needed functionality. */
/* Their portable part must be performed. So it is probably best */
/* to replace the non-portable part and to call these functions */
/* in your port, too. */
/* ----------------------------------------------------------------------- */
/** Wraps a detected drive into libburn structures and hands it over to
libburn drive list.
*/
static void enumerate_common(char *fname, int bus_no, int host_no,
int channel_no, int target_no, int lun_no)
{
int ret;
struct burn_drive out;
/* General libburn drive setup */
burn_setup_drive(&out, fname);
/* This transport adapter uses SCSI-family commands and models
(seems the adapter would know better than its boss, if ever) */
ret = burn_scsi_setup_drive(&out, bus_no, host_no, channel_no,
target_no, lun_no, 0);
if (ret<=0)
return;
/* PORTING: ------------------- non portable part --------------- */
/* Operating system adapter is CAM */
/* Adapter specific handles and data */
out.cam = NULL;
/* PORTING: ---------------- end of non portable part ------------ */
/* Adapter specific functions with standardized names */
out.grab = sg_grab;
out.release = sg_release;
out.drive_is_open = sg_drive_is_open;
out.issue_command = sg_issue_command;
/* Finally register drive and inquire drive information */
burn_drive_finish_enum(&out);
}
/* ts A61115 */
/* ------------------------------------------------------------------------ */
/* PORTING: Public functions. These MUST be ported. */
/* ------------------------------------------------------------------------ */
/** Returns the next index number and the next enumerated drive address.
The enumeration has to cover all available and accessible drives. It is
allowed to return addresses of drives which are not available but under
some (even exotic) circumstances could be available. It is on the other
hand allowed, only to hand out addresses which can really be used right
in the moment of this call. (This implementation chooses the latter.)
@param idx An opaque handle. Make no own theories about it.
@param adr Takes the reply
@param adr_size Gives maximum size of reply including final 0
@param initialize 1 = start new,
0 = continue, use no other values for now
-1 = finish
@return 1 = reply is a valid address , 0 = no further address available
-1 = severe error (e.g. adr_size too small)
*/
int sg_give_next_adr(burn_drive_enumerator_t *idx,
char adr[], int adr_size, int initialize)
{
int ret;
if (initialize == 1) {
ret = sg_init_enumerator(idx);
if (ret<=0)
return ret;
} else if (initialize == -1) {
if(idx->fd != -1)
close(idx->fd);
idx->fd = -1;
return 0;
}
try_item:; /* This spaghetti loop keeps the number of tabs small */
/* Loop content from old scsi_enumerate_drives() */
while (idx->i >= idx->ccb.cdm.num_matches) {
ret = sg_next_enumeration_buffer(idx);
if (ret<=0)
return -1;
if (!((idx->ccb.ccb_h.status == CAM_REQ_CMP)
&& (idx->ccb.cdm.status == CAM_DEV_MATCH_MORE)) )
return 0;
idx->i = 0;
}
switch (idx->ccb.cdm.matches[idx->i].type) {
case DEV_MATCH_BUS:
break;
case DEV_MATCH_DEVICE: {
struct device_match_result* result;
result = &(idx->ccb.cdm.matches[i].result.device_result);
if (result->flags & DEV_RESULT_UNCONFIGURED)
idx->skip_device = 1;
else
idx->skip_device = 0;
break;
}
case DEV_MATCH_PERIPH: {
struct periph_match_result* result;
char buf[64];
result = &(idx->ccb.cdm.matches[i].result.periph_result);
if (idx->skip_device ||
strcmp(result->periph_name, "pass") == 0)
break;
snprintf(buf, sizeof (buf), "/dev/%s%d",
result->periph_name, result->unit_number);
if(adr_size <= strlen(buf)
return -1;
strcpy(adr, buf);
/* Found next enumerable address */
return 1;
}
default:
/* printf(stderr, "unknown match type\n"); */
break;
}
(idx->i)++;
goto try_item; /* Regular function exit is return 1 above */
}
/** Brings all available, not-whitelist-banned, and accessible drives into
libburn's list of drives.
*/
int scsi_enumerate_drives(void)
{
burn_drive_enumerator_t idx;
int initialize = 1;
char buf[64];
while(1) {
ret = sg_give_next_adr(&idx, buf, sizeof(buf), initialize);
initialize = 0;
if (ret <= 0)
break;
if (burn_drive_is_banned(buf))
continue;
enumerate_common(buf, idx.result->path_id, idx.result->path_id,
0, idx.result->target_id,
idx.result->target_lun);
}
sg_give_next_adr(&idx, buf, sizeof(buf), -1);
}
/** Tells wether libburn has the given drive in use or exclusively reserved.
If it is "open" then libburn will eventually call sg_release() on it when
it is time to give up usage resp. reservation.
*/
/** Published as burn_drive.drive_is_open() */
int sg_drive_is_open(struct burn_drive * d)
{
return (d->cam != NULL);
}
/** Opens the drive for SCSI commands and - if burn activities are prone
to external interference on your system - obtains an exclusive access lock
on the drive. (Note: this is not physical tray locking.)
A drive that has been opened with sg_grab() will eventually be handed
over to sg_release() for closing and unreserving.
*/
int sg_grab(struct burn_drive *d)
{
int count;
struct cam_device *cam;
if(d->cam != NULL)
return 0;
cam = cam_open_device(d->devname, O_RDWR);
if (cam == NULL) {
libdax_msgs_submit(libdax_messenger, d->global_index,
0x00020003,
LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH,
"Could not grab drive", 0/*os_errno*/, 0);
return 0;
}
d->cam = cam;
fcntl(cam->fd, F_SETOWN, getpid());
d->released = 0;
return 1;
}
/** PORTING: Is mainly about the call to sg_close_drive() and wether it
implements the demanded functionality.
*/
/** Gives up the drive for SCSI commands and releases eventual access locks.
(Note: this is not physical tray locking.)
*/
int sg_release(struct burn_drive *d)
{
if (d->cam == NULL) {
burn_print(1, "release an ungrabbed drive. die\n");
return 0;
}
sg_close_drive(d);
return 0;
}
/** Sends a SCSI command to the drive, receives reply and evaluates wether
the command succeeded or shall be retried or finally failed.
Returned SCSI errors shall not lead to a return value indicating failure.
The callers get notified by c->error. An SCSI failure which leads not to
a retry shall be notified via scsi_notify_error().
The Libburn_log_sg_commandS facility might be of help when problems with
a drive have to be examined. It shall stay disabled for normal use.
@return: 1 success , <=0 failure
*/
int sg_issue_command(struct burn_drive *d, struct command *c)
{
int done = 0;
int err;
union ccb *ccb;
if (d->cam == NULL) {
c->error = 0;
return 0;
}
c->error = 0;
ccb = cam_getccb(d->cam);
cam_fill_csio(&ccb->csio,
1, /* retries */
NULL, /* cbfncp */
CAM_DEV_QFRZDIS, /* flags */
MSG_SIMPLE_Q_TAG, /* tag_action */
NULL, /* data_ptr */
0, /* dxfer_len */
sizeof (ccb->csio.sense_data), /* sense_len */
0, /* cdb_len */
30*1000); /* timeout */
switch (c->dir) {
case TO_DRIVE:
ccb->csio.ccb_h.flags |= CAM_DIR_OUT;
break;
case FROM_DRIVE:
ccb->csio.ccb_h.flags |= CAM_DIR_IN;
break;
case NO_TRANSFER:
ccb->csio.ccb_h.flags |= CAM_DIR_NONE;
break;
}
ccb->csio.cdb_len = c->oplen;
memcpy(&ccb->csio.cdb_io.cdb_bytes, &c->opcode, c->oplen);
memset(&ccb->csio.sense_data, 0, sizeof (ccb->csio.sense_data));
if (c->page) {
ccb->csio.data_ptr = c->page->data;
if (c->dir == FROM_DRIVE) {
ccb->csio.dxfer_len = BUFFER_SIZE;
/* touch page so we can use valgrind */
memset(c->page->data, 0, BUFFER_SIZE);
} else {
/* ts A61115: removed a ssert() */
if(c->page->bytes <= 0)
return 0;
ccb->csio.dxfer_len = c->page->bytes;
}
} else {
ccb->csio.data_ptr = NULL;
ccb->csio.dxfer_len = 0;
}
do {
err = cam_send_ccb(d->cam, ccb);
if (err == -1) {
libdax_msgs_submit(libdax_messenger,
d->global_index, 0x0002010c,
LIBDAX_MSGS_SEV_FATAL, LIBDAX_MSGS_PRIO_HIGH,
"Failed to transfer command to drive",
errno, 0);
cam_freeccb(ccb);
sg_close_drive(d);
d->released = 1;
d->busy = BURN_DRIVE_IDLE;
c->error = 1;
return -1;
}
/* XXX */
memcpy(c->sense, &ccb->csio.sense_data, ccb->csio.sense_len);
if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) {
if (!c->retry) {
c->error = 1;
cam_freeccb(ccb);
return 1;
}
switch (scsi_error(d, c->sense, 0)) {
case RETRY:
done = 0;
break;
case FAIL:
done = 1;
c->error = 1;
break;
}
} else {
done = 1;
}
} while (!done);
cam_freeccb(ccb);
return 1;
}
/** Tries to obtain SCSI address parameters.
@return 1 is success , 0 is failure
*/
int sg_obtain_scsi_adr(char *path, int *bus_no, int *host_no, int *channel_no,
int *target_no, int *lun_no)
{
burn_drive_enumerator_t idx;
int initialize = 1;
char buf[64];
struct periph_match_result* result;
while(1) {
ret = sg_give_next_adr(&idx, buf, sizeof(buf), initialize);
initialize = 0;
if (ret <= 0)
break;
if (strcmp(adr, buf) != 0)
continue;
result = &(idx->ccb.cdm.matches[i].result.periph_result);
*bus_no = result->path_id;
*host_no = result->path_id;
*channel_no = 0;
*target_no = result->target_id
*lun_no = result->target_lun;
sg_give_next_adr(&idx, buf, sizeof(buf), -1);
return 1;
}
sg_give_next_adr(&idx, buf, sizeof(buf), -1);
return (0);
}
/** Tells wether a text is a persistent address as listed by the enumeration
functions.
*/
int sg_is_enumerable_adr(char* adr)
{
burn_drive_enumerator_t idx;
int initialize = 1;
char buf[64];
while(1) {
ret = sg_give_next_adr(&idx, buf, sizeof(buf), initialize);
initialize = 0;
if (ret <= 0)
break;
if (strcmp(adr, buf) == 0) {
sg_give_next_adr(&idx, buf, sizeof(buf), -1);
return 1;
}
}
sg_give_next_adr(&idx, buf, sizeof(buf), -1);
return (0);
}

View File

@ -1,19 +1,17 @@
/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */
/* >>> ts A61021 : for testing the new arrangement of code
please outcomment these defines : */
/* Keeps alive old enumerate_common(). New version delegates much work
/* Revives old enumerate_common(). New version delegates much work
to methods in drive, mmc, spc, and sbc .
*/
#define Scsi_freebsd_make_own_enumeratE 1
/* Keeps alive old sg_enumerate(). New version delegates most work to
/* Revives old scsi_enumerate_drives(). New version delegates most work to
sg_give_next_adr().
*/
#define Scsi_freebsd_old_sg_enumeratE 1
#define Scsi_freebsd_old_scsi_enumeratE 1
#include <assert.h>
@ -59,7 +57,7 @@ int burn_drive_is_banned(char *device_address);
int mmc_function_spy(char * text);
#ifdef Scsi_freebsd_old_sg_enumeratE
#ifdef Scsi_freebsd_old_scsi_enumeratE
int sg_give_next_adr(burn_drive_enumerator_t *idx,
char adr[], int adr_size, int initialize)
@ -78,10 +76,11 @@ int sg_obtain_scsi_adr(char *path, int *bus_no, int *host_no, int *channel_no,
return (0);
}
#else /* Scsi_freebsd_old_sg_enumeratE */
#else /* Scsi_freebsd_old_scsi_enumeratE */
/* ts A61021 : Moved most code from sg_enumerate under sg_give_next_adr() */
/* Some helper functions for sg_give_next_adr() */
/* ts A61021 : Moved most code from scsi_enumerate_drives under
sg_give_next_adr() */
/* Some helper functions for scsi_give_next_adr() */
static int sg_init_enumerator(burn_drive_enumerator_t *idx)
{
@ -172,7 +171,7 @@ int sg_give_next_adr(burn_drive_enumerator_t *idx,
try_item:; /* This spaghetti loop keeps the number of tabs small */
/* Loop content from old sg_enumerate() */
/* Loop content from old scsi_enumerate_drives() */
while (idx->i >= idx->ccb.cdm.num_matches) {
ret = sg_next_enumeration_buffer(idx);
@ -277,7 +276,7 @@ int sg_obtain_scsi_adr(char *path, int *bus_no, int *host_no, int *channel_no,
return (0);
}
#endif /* ! Scsi_freebsd_old_sg_enumeratE */
#endif /* ! Scsi_freebsd_old_scsi_enumeratE */
int sg_close_drive(struct burn_drive * d)
@ -294,20 +293,10 @@ int sg_drive_is_open(struct burn_drive * d)
return (d->cam != NULL);
}
void ata_enumerate(void)
{
/* ts A61021: Only a dummy function is needed in FreeBSD */
/* The difference between sg and ata should be encapsulated
in sg-linux.c */
;
}
void sg_enumerate(void)
int scsi_enumerate_drives(void)
{
#ifdef Scsi_freebsd_old_sg_enumeratE
#ifdef Scsi_freebsd_old_scsi_enumeratE
union ccb ccb;
int bufsize, fd;
@ -404,7 +393,7 @@ void sg_enumerate(void)
close(fd);
#else /* Scsi_freebsd_old_sg_enumeratE */
#else /* Scsi_freebsd_old_scsi_enumeratE */
burn_drive_enumerator_t idx;
int initialize = 1;
@ -423,7 +412,7 @@ void sg_enumerate(void)
}
sg_give_next_adr(&idx, buf, sizeof(buf), -1);
#endif /* ! Scsi_freebsd_old_sg_enumeratE */
#endif /* ! Scsi_freebsd_old_scsi_enumeratE */
}
@ -706,64 +695,3 @@ int sg_issue_command(struct burn_drive *d, struct command *c)
return 1;
}
enum response scsi_error(struct burn_drive *d, unsigned char *sense,
int senselen)
{
int key, asc, ascq;
senselen = senselen;
key = sense[2];
asc = sense[12];
ascq = sense[13];
burn_print(12, "CONDITION: 0x%x 0x%x 0x%x on %s %s\n",
key, asc, ascq, d->idata->vendor, d->idata->product);
switch (asc) {
case 0:
burn_print(12, "NO ERROR!\n");
return RETRY;
case 2:
burn_print(1, "not ready\n");
return RETRY;
case 4:
burn_print(1,
"logical unit is in the process of becoming ready\n");
return RETRY;
case 0x20:
if (key == 5)
burn_print(1, "bad opcode\n");
return FAIL;
case 0x21:
burn_print(1, "invalid address or something\n");
return FAIL;
case 0x24:
if (key == 5)
burn_print(1, "invalid field in cdb\n");
else
break;
return FAIL;
case 0x26:
if (key == 5)
burn_print( 1, "invalid field in parameter list\n" );
return FAIL;
case 0x28:
if (key == 6)
burn_print(1,
"Not ready to ready change, medium may have changed\n");
else
break;
return RETRY;
case 0x3A:
burn_print(12, "Medium not present in %s %s\n",
d->idata->vendor, d->idata->product);
d->status = BURN_DISC_EMPTY;
return FAIL;
}
burn_print(1, "unknown failure\n");
burn_print(1, "key:0x%x, asc:0x%x, ascq:0x%x\n", key, asc, ascq);
return FAIL;
}

View File

@ -1,7 +1,64 @@
/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */
/* ts A61010 */
/* #include <a ssert.h> */
/*
This is the main operating system dependent SCSI part of libburn. It implements
the transport level aspects of SCSI control and command i/o.
Present implementation: Linux SCSI Generic (sg)
PORTING:
Porting libburn typically will consist of adding a new operating system case
to the following switcher files:
os.h Operating system specific libburn definitions and declarations.
sg.c Operating system dependent transport level modules.
and of deriving the following system specific files from existing examples:
os-*.h Included by os.h. You will need some general system knowledge
about signals and knowledge about the storage object needs of your
transport level module sg-*.c.
sg-*.c This source module. You will need special system knowledge about
how to detect all potentially available drives, how to open them,
eventually how to exclusively reserve them, how to perform
SCSI transactions, how to inquire the (pseudo-)SCSI driver.
You will not need to care about CD burning, MMC or other high-level
SCSI aspects.
Said sg-*.c operations are defined by a public function interface, which has
to be implemented in a way that provides libburn with the desired services:
sg_give_next_adr() iterates over the set of potentially useful drive
address strings.
scsi_enumerate_drives() brings all available, not-whitelist-banned, and
accessible drives into libburn's list of drives.
sg_drive_is_open() tells wether libburn has the given drive in use.
sg_grab() opens the drive for SCSI commands and ensures
undisturbed access.
sg_release() closes a drive opened by sg_grab()
sg_issue_command() sends a SCSI command to the drive, receives reply,
and evaluates wether the command succeeded or shall
be retried or finally failed.
sg_obtain_scsi_adr() tries to obtain SCSI address parameters.
Porting hints are marked by the text "PORTING:".
Send feedback to libburn-hackers@pykix.org .
Hint: You should also look into sg-freebsd-port.c, which is a younger and
in some aspects more straightforward implementation of this interface.
*/
/** PORTING : ------- OS dependent headers and definitions ------ */
#include <errno.h>
#include <unistd.h>
@ -10,9 +67,6 @@
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
/* #include <m alloc.h> ts A61013 : not in Linux man 3 malloc */
#include <string.h>
#include <sys/poll.h>
#include <linux/hdreg.h>
@ -20,6 +74,9 @@
#include <scsi/sg.h>
#include <scsi/scsi.h>
/** PORTING : ------ libburn portable headers and definitions ----- */
#include "transport.h"
#include "drive.h"
#include "sg.h"
@ -30,20 +87,24 @@
#include "toc.h"
#include "util.h"
/* kludge! glibc headers don't define all the SCSI stuff that we use! */
#ifndef SG_GET_ACCESS_COUNT
# define SG_GET_ACCESS_COUNT 0x2289
#endif
#include "libdax_msgs.h"
extern struct libdax_msgs *libdax_messenger;
static void enumerate_common(char *fname, int bus_no, int host_no,
int channel_no, int target_no, int lun_no);
/* ts A51221 */
int burn_drive_is_banned(char *device_address);
/* ------------------------------------------------------------------------ */
/* PORTING: Private definitions. Port only if needed by public functions. */
/* (Public functions are listed below) */
/* ------------------------------------------------------------------------ */
static void enumerate_common(char *fname, int bus_no, int host_no,
int channel_no, int target_no, int lun_no);
/* >>> ts A61115 : this needs mending. A Linux aspect shows up in cdrskin. */
/* ts A60813 : storage objects are in libburn/init.c
wether to use O_EXCL
wether to use O_NOBLOCK with open(2) on devices
@ -58,6 +119,12 @@ extern int burn_sg_open_abort_busy;
int mmc_function_spy(char * text);
/* ------------------------------------------------------------------------ */
/* PORTING: Private functions. Port only if needed by public functions */
/* (Public functions are listed below) */
/* ------------------------------------------------------------------------ */
static int sgio_test(int fd)
{
unsigned char test_ops[] = { 0, 0, 0, 0, 0, 0 };
@ -73,45 +140,8 @@ static int sgio_test(int fd)
}
/* ts A60925 : ticket 74 */
int sg_close_drive_fd(char *fname, int driveno, int *fd, int sorry)
{
int ret, os_errno, sevno= LIBDAX_MSGS_SEV_DEBUG;
char msg[4096+100];
if(*fd < 0)
return(0);
ret = close(*fd);
*fd = -1337;
if(ret != -1)
return 1;
os_errno= errno;
if (fname != NULL)
sprintf(msg, "Encountered error when closing drive '%s'",
fname);
else
sprintf(msg, "Encountered error when closing drive");
if (sorry)
sevno = LIBDAX_MSGS_SEV_SORRY;
libdax_msgs_submit(libdax_messenger, driveno, 0x00020002,
sevno, LIBDAX_MSGS_PRIO_HIGH, msg, os_errno, 0);
return 0;
}
int sg_drive_is_open(struct burn_drive * d)
{
/* a bit more detailed case distinction than needed */
if (d->fd == -1337)
return 0;
if (d->fd < 0)
return 0;
return 1;
}
/* ts A60924 */
int sg_handle_busy_device(char *fname, int os_errno)
static int sg_handle_busy_device(char *fname, int os_errno)
{
char msg[4096];
@ -135,87 +165,8 @@ int sg_handle_busy_device(char *fname, int os_errno)
}
/* ts A60922 ticket 33 */
/** Returns the next index number and the next enumerated drive address.
@param idx An opaque handle. Make no own theories about it.
@param adr Takes the reply
@param adr_size Gives maximum size of reply including final 0
@param initialize 1 = start new,
0 = continue, use no other values for now
-1 = finish
@return 1 = reply is a valid address , 0 = no further address available
-1 = severe error (e.g. adr_size too small)
*/
int sg_give_next_adr(burn_drive_enumerator_t *idx,
char adr[], int adr_size, int initialize)
{
/* sg.h : typedef int burn_drive_enumerator_t; */
static int sg_limit = 32, ata_limit = 26;
int baseno = 0;
if (initialize == -1)
return 0;
if (initialize == 1)
*idx = -1;
(*idx)++;
if (*idx >= sg_limit)
goto next_ata;
if (adr_size < 10)
return -1;
sprintf(adr, "/dev/sg%d", *idx);
return 1;
next_ata:;
baseno += sg_limit;
if (*idx - baseno >= ata_limit)
goto next_nothing;
if (adr_size < 9)
return -1;
sprintf(adr, "/dev/hd%c", 'a' + (*idx - baseno));
return 1;
next_nothing:;
baseno += ata_limit;
return 0;
}
int sg_is_enumerable_adr(char *adr)
{
char fname[4096];
int i, ret = 0, first = 1;
while (1) {
ret= sg_give_next_adr(&i, fname, sizeof(fname), first);
if(ret <= 0)
break;
first = 0;
if (strcmp(adr, fname) == 0)
return 1;
}
return(0);
}
/* ts A60926 */
int sg_release_siblings(int sibling_fds[], int *sibling_count)
{
int i;
char msg[81];
for(i= 0; i < *sibling_count; i++)
sg_close_drive_fd(NULL, -1, &(sibling_fds[i]), 0);
if(*sibling_count > 0) {
sprintf(msg, "Closed %d O_EXCL scsi siblings", *sibling_count);
libdax_msgs_submit(libdax_messenger, -1, 0x00020007,
LIBDAX_MSGS_SEV_NOTE, LIBDAX_MSGS_PRIO_HIGH, msg, 0,0);
}
*sibling_count = 0;
return 1;
}
/* ts A60926 */
int sg_open_drive_fd(char *fname, int scan_mode)
static int sg_open_drive_fd(char *fname, int scan_mode)
{
int open_mode = O_RDWR, fd;
char msg[81];
@ -266,8 +217,67 @@ int sg_open_drive_fd(char *fname, int scan_mode)
}
/* ts A60925 : ticket 74 */
static int sg_close_drive_fd(char *fname, int driveno, int *fd, int sorry)
{
int ret, os_errno, sevno= LIBDAX_MSGS_SEV_DEBUG;
char msg[4096+100];
if(*fd < 0)
return(0);
ret = close(*fd);
*fd = -1337;
if(ret != -1)
return 1;
os_errno= errno;
if (fname != NULL)
sprintf(msg, "Encountered error when closing drive '%s'",
fname);
else
sprintf(msg, "Encountered error when closing drive");
if (sorry)
sevno = LIBDAX_MSGS_SEV_SORRY;
libdax_msgs_submit(libdax_messenger, driveno, 0x00020002,
sevno, LIBDAX_MSGS_PRIO_HIGH, msg, os_errno, 0);
return 0;
}
/* ts A60926 */
int sg_open_scsi_siblings(char *path, int driveno,
static int sg_release_siblings(int sibling_fds[], int *sibling_count)
{
int i;
char msg[81];
for(i= 0; i < *sibling_count; i++)
sg_close_drive_fd(NULL, -1, &(sibling_fds[i]), 0);
if(*sibling_count > 0) {
sprintf(msg, "Closed %d O_EXCL scsi siblings", *sibling_count);
libdax_msgs_submit(libdax_messenger, -1, 0x00020007,
LIBDAX_MSGS_SEV_NOTE, LIBDAX_MSGS_PRIO_HIGH, msg, 0,0);
}
*sibling_count = 0;
return 1;
}
/* ts A60926 */
static int sg_close_drive(struct burn_drive *d)
{
int ret;
if (!burn_drive_is_open(d))
return 0;
sg_release_siblings(d->sibling_fds, &(d->sibling_count));
ret = sg_close_drive_fd(d->devname, d->global_index, &(d->fd), 0);
return ret;
}
/* ts A60926 */
static int sg_open_scsi_siblings(char *path, int driveno,
int sibling_fds[], int *sibling_count,
int host_no, int channel_no, int id_no, int lun_no)
{
@ -324,19 +334,9 @@ failed:;
}
/* ts A60926 */
int sg_close_drive(struct burn_drive *d)
{
int ret;
if (!burn_drive_is_open(d))
return 0;
sg_release_siblings(d->sibling_fds, &(d->sibling_count));
ret = sg_close_drive_fd(d->devname, d->global_index, &(d->fd), 0);
return ret;
}
void ata_enumerate(void)
/** Speciality of Linux: detect non-SCSI ATAPI (EIDE) which will from
then on used used via generic SCSI as is done with (emulated) SCSI drives */
static void ata_enumerate(void)
{
struct hd_driveid tm;
int i, fd;
@ -372,7 +372,9 @@ void ata_enumerate(void)
}
}
void sg_enumerate(void)
/** Detects (probably emulated) SCSI drives */
static void sg_enumerate(void)
{
struct sg_scsi_id sid;
int i, fd, sibling_fds[LIBBURN_SG_MAX_SIBLINGS], sibling_count= 0, ret;
@ -428,6 +430,19 @@ void sg_enumerate(void)
}
}
/* ts A61115 */
/* ----------------------------------------------------------------------- */
/* PORTING: Private functions which contain publicly needed functionality. */
/* Their portable part must be performed. So it is probably best */
/* to replace the non-portable part and to call these functions */
/* in your port, too. */
/* ----------------------------------------------------------------------- */
/** Wraps a detected drive into libburn structures and hands it over to
libburn drive list.
*/
/* ts A60923 - A61005 : introduced new SCSI parameters */
/* ts A61021 : moved non os-specific code to spc,sbc,mmc,drive */
static void enumerate_common(char *fname, int bus_no, int host_no,
@ -446,13 +461,18 @@ static void enumerate_common(char *fname, int bus_no, int host_no,
if (ret<=0)
return;
/* PORTING: ------------------- non portable part --------------- */
/* Operating system adapter is Linux Generic SCSI (sg) */
/* Adapter specific handles and data */
out.fd = -1337;
out.sibling_count = 0;
for(i= 0; i<LIBBURN_SG_MAX_SIBLINGS; i++)
out.sibling_fds[i] = -1337;
/* Adapter specific functions */
/* PORTING: ---------------- end of non portable part ------------ */
/* Adapter specific functions with standardized names */
out.grab = sg_grab;
out.release = sg_release;
out.drive_is_open= sg_drive_is_open;
@ -462,17 +482,116 @@ static void enumerate_common(char *fname, int bus_no, int host_no,
burn_drive_finish_enum(&out);
}
/*
we use the sg reference count to decide whether we can use the
drive or not.
if refcount is not one, drive is open somewhere else.
ts A60813: this test is too late. O_EXCL is the stronger solution.
After all the test was disabled already in icculus.org/burn CVS.
/* ts A61115 */
/* ------------------------------------------------------------------------ */
/* PORTING: Public functions. These MUST be ported. */
/* ------------------------------------------------------------------------ */
/** PORTING:
In this Linux implementation, this function mirrors the enumeration
done in sg_enumerate and ata_enumerate(). It would be better to base those
functions on this sg_give_next_adr() but the situation is not inviting.
*/
/* ts A60922 ticket 33 : called from drive.c */
/** Returns the next index number and the next enumerated drive address.
The enumeration has to cover all available and accessible drives. It is
allowed to return addresses of drives which are not available but under
some (even exotic) circumstances could be available. It is on the other
hand allowed, only to hand out addresses which can really be used right
in the moment of this call. (This implementation chooses the former.)
@param idx An opaque handle. Make no own theories about it.
@param adr Takes the reply
@param adr_size Gives maximum size of reply including final 0
@param initialize 1 = start new,
0 = continue, use no other values for now
-1 = finish
@return 1 = reply is a valid address , 0 = no further address available
-1 = severe error (e.g. adr_size too small)
*/
int sg_give_next_adr(burn_drive_enumerator_t *idx,
char adr[], int adr_size, int initialize)
{
/* sg.h : typedef int burn_drive_enumerator_t; */
static int sg_limit = 32, ata_limit = 26;
int baseno = 0;
if (initialize == -1)
return 0;
if (initialize == 1)
*idx = -1;
(*idx)++;
if (*idx >= sg_limit)
goto next_ata;
if (adr_size < 10)
return -1;
sprintf(adr, "/dev/sg%d", *idx);
return 1;
next_ata:;
baseno += sg_limit;
if (*idx - baseno >= ata_limit)
goto next_nothing;
if (adr_size < 9)
return -1;
sprintf(adr, "/dev/hd%c", 'a' + (*idx - baseno));
return 1;
next_nothing:;
baseno += ata_limit;
return 0;
}
/** Brings all available, not-whitelist-banned, and accessible drives into
libburn's list of drives.
*/
/** PORTING:
If not stricken with an incompletely unified situation like in Linux
one would rather implement this by a loop calling sg_give_next_adr().
If needed with your sg_give_next_adr() results, do a test for existence
and accessability. If burn activities are prone to external interference
on your system it is also necessary to obtain exclusive access locks on
the drives.
Hand over each accepted drive to enumerate_common() resp. its replacement
within your port.
See FreeBSD port sketch sg-freebsd-port.c for such an implementation.
*/
/* ts A61115: replacing call to sg-implementation internals from drive.c */
int scsi_enumerate_drives(void)
{
sg_enumerate();
ata_enumerate();
return 1;
}
/** Tells wether libburn has the given drive in use or exclusively reserved.
If it is "open" then libburn will eventually call sg_release() on it when
it is time to give up usage resp. reservation.
*/
/** Published as burn_drive.drive_is_open() */
int sg_drive_is_open(struct burn_drive * d)
{
/* a bit more detailed case distinction than needed */
if (d->fd == -1337)
return 0;
if (d->fd < 0)
return 0;
return 1;
}
/** Opens the drive for SCSI commands and - if burn activities are prone
to external interference on your system - obtains an exclusive access lock
on the drive. (Note: this is not physical tray locking.)
A drive that has been opened with sg_grab() will eventually be handed
over to sg_release() for closing and unreserving.
*/
int sg_grab(struct burn_drive *d)
{
int fd, count, os_errno= 0, ret;
int fd, os_errno= 0, ret;
/* ts A60813 */
int open_mode = O_RDWR;
@ -495,12 +614,11 @@ int sg_grab(struct burn_drive *d)
if(burn_sg_open_o_nonblock)
open_mode |= O_NONBLOCK;
/* ts A60813
/* ts A60813 - A60822
After enumeration the drive fd is probably still open.
-1337 is the initial value of burn_drive.fd and the value after
relase of drive. Unclear why not the official error return
value -1 of open(2) war used. */
/* ts A60822: was if(d->fd == -1337) { */
if(! burn_drive_is_open(d)) {
/* ts A60821
@ -523,49 +641,34 @@ int sg_grab(struct burn_drive *d)
} else
fd= d->fd;
/* ts A61007 : this is redundant */
/* a ssert(fd != -1337); */
if (fd >= 0) {
/* ts A60814:
according to my experiments this test would work now ! */
/* ts A60926 : this was disabled */
/* Tests with growisofs on kernel 2.4.21 yielded that this
does not help against blocking on busy drives.
*/
/* <<< the old dummy */
/* er = ioctl(fd, SG_GET_ACCESS_COUNT, &count);*/
count = 1;
if (1 == count) {
d->fd = fd;
fcntl(fd, F_SETOWN, getpid());
d->released = 0;
return 1;
}
drive_is_in_use:;
libdax_msgs_submit(libdax_messenger, d->global_index,
0x00020003,
LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH,
"Could not grab drive - already in use", 0, 0);
sg_close_drive(d);
d->fd = -1337;
return 0;
d->fd = fd;
fcntl(fd, F_SETOWN, getpid());
d->released = 0;
return 1;
}
libdax_msgs_submit(libdax_messenger, d->global_index, 0x00020003,
LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH,
"Could not grab drive", os_errno, 0);
return 0;
drive_is_in_use:;
libdax_msgs_submit(libdax_messenger, d->global_index,
0x00020003,
LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH,
"Could not grab drive - already in use", 0, 0);
sg_close_drive(d);
d->fd = -1337;
return 0;
}
/*
non zero return means you still have the drive and it's not
in a state to be released? (is that even possible?)
*/
/** PORTING: Is mainly about the call to sg_close_drive() and wether it
implements the demanded functionality.
*/
/** Gives up the drive for SCSI commands and releases eventual access locks.
(Note: this is not physical tray locking.)
*/
int sg_release(struct burn_drive *d)
{
/* ts A60821
@ -586,14 +689,26 @@ int sg_release(struct burn_drive *d)
}
/** Sends a SCSI command to the drive, receives reply and evaluates wether
the command succeeded or shall be retried or finally failed.
Returned SCSI errors shall not lead to a return value indicating failure.
The callers get notified by c->error. An SCSI failure which leads not to
a retry shall be notified via scsi_notify_error().
The Libburn_log_sg_commandS facility might be of help when problems with
a drive have to be examined. It shall stay disabled for normal use.
@return: 1 success , <=0 failure
*/
int sg_issue_command(struct burn_drive *d, struct command *c)
{
int done = 0, no_c_page = 0;
int err;
sg_io_hdr_t s;
/*
#define Libburn_log_sg_commandS 1
*/
#ifdef Libburn_log_sg_commandS
/* <<< ts A61030 */
/* ts A61030 */
static FILE *fp= NULL;
static int fpcount= 0;
int i;
@ -607,15 +722,17 @@ int sg_issue_command(struct burn_drive *d, struct command *c)
mmc_function_spy(buf);
#ifdef Libburn_log_sg_commandS
/* <<< ts A61030 */
/* ts A61030 */
if(fp==NULL) {
fp= fopen("/tmp/libburn_sg_command_log","a");
fprintf(fp,"\n-----------------------------------------\n");
}
for(i=0;i<10;i++)
fprintf(fp,"%2.2x ", c->opcode[i]);
fprintf(fp,"\n");
fpcount++;
if(fp!=NULL) {
for(i=0;i<10;i++)
fprintf(fp,"%2.2x ", c->opcode[i]);
fprintf(fp,"\n");
fpcount++;
}
#endif /* Libburn_log_sg_commandS */
@ -712,118 +829,23 @@ int sg_issue_command(struct burn_drive *d, struct command *c)
/* ts A61106 */
ex:;
if (c->error) {
/* >>> to become d->notify_error() */
scsi_notify_error(d, c, s.sbp, s.sb_len_wr, 0);
#ifdef Libburn_log_sg_commandS
if(fp!=NULL) {
fprintf(fp,"+++ key=%X asc=%2.2Xh ascq=%2.2Xh\n",
s.sbp[2], s.sbp[12], s.sbp[13]);
fpcount++;
}
#endif /* Libburn_log_sg_commandS */
}
return 1;
}
/* ts A61030 - A61109 */
/* @param flag bit0=do report conditions which are considered not an error */
int scsi_notify_error(struct burn_drive *d, struct command *c,
unsigned char *sense, int senselen, int flag)
{
int key= -1, asc= -1, ascq= -1, ret;
char msg[160];
if (d->silent_on_scsi_error)
return 1;
if (senselen > 2)
key = sense[2];
if (senselen > 13) {
asc = sense[12];
ascq = sense[13];
}
if(!(flag & 1)) {
/* SPC : TEST UNIT READY command */
if (c->opcode[0] == 0)
return 1;
/* MMC : READ DISC INFORMATION command */
if (c->opcode[0] == 0x51)
if (key == 0x2 && asc == 0x3A &&
ascq>=0 && ascq <= 0x02) /* MEDIUM NOT PRESENT */
return 1;
}
sprintf(msg,"SCSI error condition on command %2.2Xh :", c->opcode[0]);
if (key>=0)
sprintf(msg+strlen(msg), " key=%Xh", key);
if (asc>=0)
sprintf(msg+strlen(msg), " asc=%2.2Xh", asc);
if (ascq>=0)
sprintf(msg+strlen(msg), " ascq=%2.2Xh", ascq);
ret = libdax_msgs_submit(libdax_messenger, d->global_index, 0x0002010f,
LIBDAX_MSGS_SEV_DEBUG, LIBDAX_MSGS_PRIO_HIGH, msg,0,0);
return ret;
}
enum response scsi_error(struct burn_drive *d, unsigned char *sense,
int senselen)
{
int key, asc, ascq;
senselen = senselen;
key = sense[2];
asc = sense[12];
ascq = sense[13];
burn_print(12, "CONDITION: 0x%x 0x%x 0x%x on %s %s\n",
key, asc, ascq, d->idata->vendor, d->idata->product);
switch (asc) {
case 0:
burn_print(12, "NO ERROR!\n");
return RETRY;
case 2:
burn_print(1, "not ready\n");
return RETRY;
case 4:
burn_print(1,
"logical unit is in the process of becoming ready\n");
return RETRY;
case 0x20:
if (key == 5)
burn_print(1, "bad opcode\n");
return FAIL;
case 0x21:
burn_print(1, "invalid address or something\n");
return FAIL;
case 0x24:
if (key == 5)
burn_print(1, "invalid field in cdb\n");
else
break;
return FAIL;
case 0x26:
if ( key == 5 )
burn_print( 1, "invalid field in parameter list\n" );
return FAIL;
case 0x28:
if (key == 6)
burn_print(1,
"Not ready to ready change, medium may have changed\n");
else
break;
return RETRY;
case 0x3A:
burn_print(12, "Medium not present in %s %s\n",
d->idata->vendor, d->idata->product);
d->status = BURN_DISC_EMPTY;
return FAIL;
}
burn_print(1, "unknown failure\n");
burn_print(1, "key:0x%x, asc:0x%x, ascq:0x%x\n", key, asc, ascq);
return FAIL;
}
/* ts A60922 */
/** Try to obtain SCSI address parameters.
/** Tries to obtain SCSI address parameters.
@return 1 is success , 0 is failure
*/
int sg_obtain_scsi_adr(char *path, int *bus_no, int *host_no, int *channel_no,
@ -868,3 +890,31 @@ int sg_obtain_scsi_adr(char *path, int *bus_no, int *host_no, int *channel_no,
#endif
return 1;
}
/* ts A60922 ticket 33 : called from drive.c */
/** Tells wether a text is a persistent address as listed by the enumeration
functions.
*/
int sg_is_enumerable_adr(char *adr)
{
char fname[4096];
int ret = 0, first = 1;
burn_drive_enumerator_t idx;
while (1) {
ret= sg_give_next_adr(&idx, fname, sizeof(fname), first);
if(ret <= 0)
break;
first = 0;
if (strcmp(adr, fname) == 0) {
sg_give_next_adr(&idx, fname, sizeof(fname), -1);
return 1;
}
}
sg_give_next_adr(&idx, fname, sizeof(fname), -1);
return(0);
}

View File

@ -1,5 +1,9 @@
/* ts A61013 : It would be nice if autotools could do that job */
/* sg.c
Switcher for operating system dependent transport level modules of libburn.
Copyright (C) 2006 Thomas Schmitt <scdbackup@gmx.net>, provided under GPL
*/
#ifdef __FreeBSD__

View File

@ -3,45 +3,17 @@
#ifndef __SG
#define __SG
#ifdef __FreeBSD__
/* >>> To hold all state information of BSD device enumeration
which are now local in sg_enumerate() . So that sg_give_next_adr()
can work in BSD and sg_enumerate() can use it. */
struct burn_drive_enumeration_state {
#include "os.h"
#ifdef Scsi_freebsd_old_sg_enumeratE
int dummy;
#else
union ccb ccb;
int bufsize, fd;
unsigned int i;
int skip_device;
#endif /* ! Scsi_freebsd_old_sg_enumeratE */
};
typedef struct burn_drive_enumeration_state burn_drive_enumerator_t;
/* see os.h for name of particular os-*.h where this is defined */
BURN_OS_DEFINE_DRIVE_ENUMERATOR_T
#else /* __FreeBSD__ */
/* <<< just for testing the C syntax */
struct burn_drive_enumeration_state {
int dummy;
};
typedef struct burn_drive_enumeration_state burn_drive_enumerator_tX;
typedef int burn_drive_enumerator_t;
#endif /* ! __FreeBSD__ */
struct burn_drive;
struct command;
enum response
{ RETRY, FAIL };
/* ts A60925 : ticket 74 */
int sg_close_drive_fd(char *fname, int driveno, int *fd, int sorry);
/* ts A60922 ticket 33 */
int sg_give_next_adr(burn_drive_enumerator_t *enm_context,
@ -50,23 +22,13 @@ int sg_is_enumerable_adr(char *adr);
int sg_obtain_scsi_adr(char *path, int *bus_no, int *host_no, int *channel_no,
int *target_no, int *lun_no);
/* ts A60926 : ticket 33 ++ */
int sg_open_scsi_siblings(char *fname, int driveno,
int sibling_fds[], int *sibling_count,
int host_no, int channel_no, int id_no, int lun_no);
int sg_release_siblings(int sibling_fds[], int *sibling_count);
int sg_close_drive(struct burn_drive *d);
void sg_enumerate(void);
void ata_enumerate(void);
int sg_grab(struct burn_drive *);
int sg_release(struct burn_drive *);
int sg_issue_command(struct burn_drive *, struct command *);
enum response scsi_error(struct burn_drive *, unsigned char *, int);
/* ts A61030 */
/* @param flag bit0=do also report TEST UNIT READY failures */
int scsi_notify_error(struct burn_drive *, struct command *c,
unsigned char *sense, int senselen, int flag);
/* ts A61115 : formerly sg_enumerate();ata_enumerate() */
int scsi_enumerate_drives(void);
int sg_drive_is_open(struct burn_drive * d);
#endif /* __SG */

View File

@ -523,3 +523,131 @@ int burn_scsi_setup_drive(struct burn_drive *d, int bus_no, int host_no,
}
return 1;
}
/* ts A61122 */
enum response scsi_error_msg(struct burn_drive *d, unsigned char *sense,
int senselen, char msg[161],
int *key, int *asc, int *ascq)
{
*key= *asc= *ascq= -1;
if (senselen<=0 || senselen>2)
*key = sense[2];
if (senselen<=0 || senselen>12)
*asc = sense[12];
if (senselen<=0 || senselen>13)
*ascq = sense[13];
burn_print(12, "CONDITION: 0x%x 0x%x 0x%x on %s %s\n",
*key, *asc, *ascq, d->idata->vendor, d->idata->product);
switch (*asc) {
case 0:
sprintf(msg, "(no error reported by SCSI transaction)");
return RETRY;
case 2:
sprintf(msg, "not ready");
return RETRY;
case 4:
sprintf(msg,
"logical unit is in the process of becoming ready");
return RETRY;
case 0x20:
if (*key == 5)
sprintf(msg, "bad opcode");
return FAIL;
case 0x21:
sprintf(msg, "invalid address");
return FAIL;
case 0x24:
if (*key == 5)
sprintf(msg, "invalid field in cdb");
else
break;
return FAIL;
case 0x26:
if (*key == 5 )
sprintf(msg, "invalid field in parameter list" );
return FAIL;
case 0x28:
if (*key == 6)
sprintf(msg, "Medium may have changed");
else
break;
return RETRY;
case 0x3A:
sprintf(msg, "Medium not present");
d->status = BURN_DISC_EMPTY;
return FAIL;
}
sprintf(msg,
"Failure. See mmc3r10g.pdf: Sense Key %X ASC %2.2X ASCQ %2.2X",
*key, *asc, *ascq);
return FAIL;
}
/* ts A61115 moved from sg-*.c */
/* ts A61122 made it frontend to scsi_error_msg() */
enum response scsi_error(struct burn_drive *d, unsigned char *sense,
int senselen)
{
int key, asc, ascq;
char msg[160];
enum response resp;
resp = scsi_error_msg(d, sense, senselen, msg, &key, &asc, &ascq);
if (asc == 0 || asc == 0x3A)
burn_print(12, "%s\n", msg);
else
burn_print(1, "%s\n", msg);
return resp;
}
/* ts A61030 - A61115 */
/* @param flag bit0=do report conditions which are considered not an error */
int scsi_notify_error(struct burn_drive *d, struct command *c,
unsigned char *sense, int senselen, int flag)
{
int key= -1, asc= -1, ascq= -1, ret;
char msg[320],scsi_msg[160];
if (d->silent_on_scsi_error)
return 1;
strcpy(scsi_msg, " \"");
scsi_error_msg(d, sense, senselen, scsi_msg + strlen(scsi_msg),
&key, &asc, &ascq);
strcat(scsi_msg, "\"");
if(!(flag & 1)) {
/* SPC : TEST UNIT READY command */
if (c->opcode[0] == 0)
return 1;
/* MMC : READ DISC INFORMATION command */
if (c->opcode[0] == 0x51)
if (key == 0x2 && asc == 0x3A &&
ascq>=0 && ascq <= 0x02) /* MEDIUM NOT PRESENT */
return 1;
}
sprintf(msg,"SCSI error condition on command %2.2Xh :", c->opcode[0]);
if (key>=0)
sprintf(msg+strlen(msg), " key=%Xh", key);
if (asc>=0)
sprintf(msg+strlen(msg), " asc=%2.2Xh", asc);
if (ascq>=0)
sprintf(msg+strlen(msg), " ascq=%2.2Xh", ascq);
ret = libdax_msgs_submit(libdax_messenger, d->global_index, 0x0002010f,
LIBDAX_MSGS_SEV_DEBUG, LIBDAX_MSGS_PRIO_HIGH, msg,0,0);
if (ret < 0)
return ret;
ret = libdax_msgs_submit(libdax_messenger, d->global_index, 0x0002010f,
LIBDAX_MSGS_SEV_DEBUG, LIBDAX_MSGS_PRIO_HIGH,
scsi_msg,0,0);
return ret;
}

View File

@ -33,5 +33,18 @@ int spc_setup_drive(struct burn_drive *d);
int burn_scsi_setup_drive(struct burn_drive *d, int bus_no, int host_no,
int channel_no, int target_no, int lun_no, int flag);
/* ts A61115 moved from sg-*.h */
enum response { RETRY, FAIL };
enum response scsi_error(struct burn_drive *, unsigned char *, int);
/* ts A61122 */
enum response scsi_error_msg(struct burn_drive *d, unsigned char *sense,
int senselen, char msg[161],
int *key, int *asc, int *ascq);
/* ts A61030 */
/* @param flag bit0=do report conditions which are considered not an error */
int scsi_notify_error(struct burn_drive *, struct command *c,
unsigned char *sense, int senselen, int flag);
#endif /*__SPC*/

View File

@ -4,20 +4,16 @@
#define __TRANSPORT
#include "libburn.h"
#include "os.h"
#include <pthread.h>
/* sg data structures */
#include <sys/types.h>
#ifdef __FreeBSD__
#define BUFFER_SIZE 65536/2
/* see os.h for name of particular os-*.h where this is defined */
#define BUFFER_SIZE BURN_OS_TRANSPORT_BUFFER_SIZE
#else /* __FreeBSD__ */
#define BUFFER_SIZE 65536
#endif /* ! __FreeBSD__ */
enum transfer_direction
{ TO_DRIVE, FROM_DRIVE, NO_TRANSFER };
@ -107,15 +103,10 @@ struct burn_drive
int lun;
char *devname;
#if defined(__FreeBSD__)
struct cam_device* cam;
#else
int fd;
/* ts A60926 : trying to lock against growisofs /dev/srN, /dev/scdN */
int sibling_count;
int sibling_fds[LIBBURN_SG_MAX_SIBLINGS];
#endif
/* see os.h for name of particular os-*.h where this is defined */
BURN_OS_TRANSPORT_DRIVE_ELEMENTS
/* ts A60904 : ticket 62, contribution by elmom */
/**
@ -168,6 +159,7 @@ struct burn_drive
void (*unlock) (struct burn_drive *);
void (*eject) (struct burn_drive *);
void (*load) (struct burn_drive *);
int (*start_unit) (struct burn_drive *);
void (*read_disc_info) (struct burn_drive *);
void (*read_sectors) (struct burn_drive *,
int start,

View File

@ -13,6 +13,11 @@
#define Libburn_experimental_no_close_sessioN 1
*/
/* ts A61114 : Highly experimental : try to achieve SAO on appendables
THIS DOES NOT WORK YET !
#define Libburn_sao_can_appenD 1
*/
#include <stdio.h>
#include <string.h>
@ -120,6 +125,8 @@ int burn_write_flush(struct burn_write_opts *o, struct burn_track *track)
track->writecount += d->buffer->bytes;
track->written_sectors += d->buffer->sectors;
}
/* ts A61119 */
d->progress.buffered_bytes += d->buffer->bytes;
d->nwa += d->buffer->sectors;
d->buffer->bytes = 0;
@ -173,6 +180,7 @@ int burn_write_close_track(struct burn_write_opts *o, struct burn_session *s,
d->nwa += d->buffer->sectors;
t->writecount += d->buffer->bytes;
t->written_sectors += d->buffer->sectors;
d->progress.buffered_bytes += d->buffer->bytes;
}
d->cancel = cancelled;
}
@ -285,8 +293,10 @@ static int add_cue(struct cue_sheet *sheet, unsigned char ctladr,
return 1;
}
/* ts A61114: added parameter nwa */
struct cue_sheet *burn_create_toc_entries(struct burn_write_opts *o,
struct burn_session *session)
struct burn_session *session,
int nwa)
{
int i, m, s, f, form, pform, runtime = -150, ret;
unsigned char ctladr;
@ -299,6 +309,11 @@ struct cue_sheet *burn_create_toc_entries(struct burn_write_opts *o,
d = o->drive;
#ifdef Libburn_sao_can_appenD
if (d->status == BURN_DISC_APPENDABLE)
runtime = nwa-150;
#endif
sheet = malloc(sizeof(struct cue_sheet));
/* ts A61009 : react on failures of malloc(), add_cue_sheet()
@ -570,7 +585,7 @@ int burn_write_track(struct burn_write_opts *o, struct burn_session *s,
{
struct burn_track *t = s->track[tnum];
struct burn_drive *d = o->drive;
int i, tmp = 0, open_ended = 0, ret, nwa, lba;
int i, tmp = 0, open_ended = 0, ret= 0, nwa, lba;
int sectors;
char msg[80];
@ -639,7 +654,7 @@ int burn_write_track(struct burn_write_opts *o, struct burn_session *s,
burn_print(12, "track %d is %d sectors long\n", tnum, sectors);
/* ts A61030 : this cannot happen. tnum is alsways < s-tracks */
/* ts A61030 : this cannot happen. tnum is always < s->tracks */
if (tnum == s->tracks)
tmp = sectors > 150 ? 150 : sectors;
@ -698,6 +713,8 @@ int burn_write_track(struct burn_write_opts *o, struct burn_session *s,
/* A61101 : probably this is not all payload data */
/* A61108 : but audio count is short without this */
t->writecount += d->buffer->bytes;
t->written_sectors += d->buffer->sectors;
d->progress.buffered_bytes += d->buffer->bytes;
d->nwa += d->buffer->sectors;
d->buffer->bytes = 0;
@ -750,7 +767,7 @@ void burn_disc_write_sync(struct burn_write_opts *o, struct burn_disc *disc)
struct burn_drive *d = o->drive;
struct buffer buf;
struct burn_track *lt;
int first = 1, i, ret, lba, nwa;
int first = 1, i, ret, lba, nwa = 0;
char msg[80];
/* ts A60924 : libburn/message.c gets obsoleted
@ -770,7 +787,7 @@ return crap. so we send the command, then ignore the result.
*/
/* ts A61107 : moved up send_write_parameters because LG GSA-4082B
seems to dislike get_nwa() in advance */
d->alba = d->start_lba;
d->alba = d->start_lba; /* ts A61114: this looks senseless */
d->nwa = d->alba;
if (o->write_type == BURN_WRITE_TAO) {
nwa = 0; /* get_nwa() will be called in burn_track() */
@ -803,6 +820,8 @@ return crap. so we send the command, then ignore the result.
/* ts A61023 */
d->progress.buffer_capacity = 0;
d->progress.buffer_available = 0;
d->progress.buffered_bytes = 0;
d->progress.buffer_min_fill = 0xffffffff;
d->busy = BURN_DRIVE_WRITING;
@ -811,7 +830,8 @@ return crap. so we send the command, then ignore the result.
d->progress.session = i;
d->progress.tracks = disc->session[i]->tracks;
sheet = burn_create_toc_entries(o, disc->session[i]);
/* ts A61114: added parameter nwa */
sheet = burn_create_toc_entries(o, disc->session[i], nwa);
/* ts A61009 */
if (sheet == NULL)
@ -835,8 +855,28 @@ return crap. so we send the command, then ignore the result.
d->nwa= d->alba = 0;
} else {
#ifdef Libburn_sao_can_appenD
/* ts A61114: address for d->write() */
if (d->status == BURN_DISC_APPENDABLE
&& o->write_type == BURN_WRITE_SAO) {
d->nwa = d->alba = nwa-150;
sprintf(msg,
"SAO appendable d->nwa= %d\n", d->nwa);
libdax_msgs_submit(
libdax_messenger, d->global_index, 0x000002,
LIBDAX_MSGS_SEV_DEBUG, LIBDAX_MSGS_PRIO_ZERO,
msg,0,0);
} else {
d->nwa = -150;
d->alba = -150;
}
#else
d->nwa = -150;
d->alba = -150;
#endif /* ! Libburn_sao_can_appenD */
}

View File

@ -9,7 +9,8 @@ struct burn_write_opts;
struct burn_disc;
struct cue_sheet *burn_create_toc_entries(struct burn_write_opts *o,
struct burn_session *session);
struct burn_session *session,
int nwa);
int burn_sector_length(int trackmode);
int burn_subcode_length(int trackmode);

View File

@ -1,11 +0,0 @@
prefix=@prefix@
exec_prefix=@exec_prefix@
libdir=@libdir@
includedir=@includedir@
Name: libisofs
Description: ISO9660 filesystem creation library
Version: @VERSION@
Requires:
Libs: -L${libdir} -lisofs
Cflags: -I${includedir}/libburn

View File

@ -1,4 +0,0 @@
all clean:
$(MAKE) -C .. -$(MAKEFLAGS) $@
.PHONY: all clean

View File

@ -1,49 +0,0 @@
pkgconfigdir=$(libdir)/pkgconfig
libincludedir=$(includedir)/libburn
##bin_PROGRAMS = test
lib_LTLIBRARIES = libisofs.la
libisofs_la_SOURCES = \
tree.h \
tree.c \
volume.h \
volume.c \
util.h \
util.c \
ecma119.c \
ecma119.h \
ecma119_tree.c \
ecma119_tree.h \
susp.h \
susp.c \
rockridge.h \
rockridge.c \
joliet.c \
joliet.h
libinclude_HEADERS = libisofs.h
##test_SOURCES = test.c
##test_LDADD = libisofs.la
##noinst_PROGRAMS = test
##test_SOURCES = test.c
##test_LDADD = $(libisofs_la_OBJECTS)
##INCLUDES = -I../burn/libburn
## ========================================================================= ##
indent_files = $(libisofs_la_SOURCES)
indent: $(indent_files)
indent -bad -bap -nbbb -nbbo -nbc -bli0 -br -bls \
-cdw -ce -cli0 -ncs -nbfda -i8 -l79 -lc79 \
-lp -saf -sai -nprs -npsl -saw -sob -ss -ut \
-sbi0 -nsc -ts8 -npcs -ncdb -fca \
$^
.PHONY: indent
## ========================================================================= ##

View File

@ -1,694 +0,0 @@
/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */
/* vim: set noet ts=8 sts=8 sw=8 : */
#include <string.h>
#include <wchar.h>
#include <stdlib.h>
#include <time.h>
#include <assert.h>
#include <err.h>
#include "ecma119.h"
#include "ecma119_tree.h"
#include "susp.h"
#include "rockridge.h"
#include "joliet.h"
#include "volume.h"
#include "tree.h"
#include "util.h"
#include "libisofs.h"
#include "libburn/libburn.h"
/* burn-source compatible stuff */
static int
bs_read(struct burn_source *bs, unsigned char *buf, int size);
static off_t
bs_get_size(struct burn_source *bs);
static void
bs_free_data(struct burn_source *bs);
typedef void (*write_fn)(struct ecma119_write_target*, uint8_t*);
/* return true if the given state is only required for Joliet volumes */
static int
is_joliet_state(enum ecma119_write_state);
static void
next_state(struct ecma119_write_target *t);
/* write t->state_data to the buf, one block at a time */
static void
write_data_chunk(struct ecma119_write_target *t, uint8_t *buf);
/* writing functions. All these functions assume the buf is large enough */
static void
write_pri_vol_desc(struct ecma119_write_target *t, uint8_t *buf);
static void
write_vol_desc_terminator(struct ecma119_write_target *t, uint8_t *buf);
static void
write_path_table(struct ecma119_write_target *t, int l_type, uint8_t *buf);
static void
write_l_path_table(struct ecma119_write_target *t, uint8_t *buf);
static void
write_m_path_table(struct ecma119_write_target *t, uint8_t *buf);
static void
write_one_dir_record(struct ecma119_write_target *t,
struct ecma119_tree_node *dir,
int file_id,
uint8_t *buf);
static void
write_one_dir(struct ecma119_write_target *t,
struct ecma119_tree_node *dir,
uint8_t *buf);
static void
write_dirs(struct ecma119_write_target *t, uint8_t *buf);
/* wrapper functions for writing */
static void wr_system_area(struct ecma119_write_target*, uint8_t*);
static void wr_pri_vol_desc(struct ecma119_write_target*, uint8_t*);
static void wr_vol_desc_term(struct ecma119_write_target*, uint8_t*);
static void wr_l_path_table(struct ecma119_write_target*, uint8_t*);
static void wr_m_path_table(struct ecma119_write_target*, uint8_t*);
static void wr_dir_records(struct ecma119_write_target*, uint8_t*);
static void wr_files(struct ecma119_write_target*, uint8_t*);
static const write_fn writers[] =
{
NULL,
wr_system_area,
wr_pri_vol_desc,
joliet_wr_sup_vol_desc,
wr_vol_desc_term,
wr_l_path_table,
wr_m_path_table,
joliet_wr_l_path_table,
joliet_wr_m_path_table,
wr_dir_records,
joliet_wr_dir_records,
wr_files
};
/* When a writer is created, we
* 1) create an ecma119 tree
* 2) add SUSP fields (if necessary)
* 3) calculate the size and position of all nodes in the tree
* 4) finalize SUSP fields (if necessary)
*/
static void
add_susp_fields_rec(struct ecma119_write_target *t,
struct ecma119_tree_node *node)
{
size_t i;
if (!node->iso_self)
return;
rrip_add_PX(t, node);
rrip_add_NM(t, node);
rrip_add_TF(t, node);
if (node->iso_self->attrib.st_rdev)
rrip_add_PN(t, node);
if (S_ISLNK(node->iso_self->attrib.st_mode))
rrip_add_SL(t, node);
if (node->type == ECMA119_FILE && node->file.real_me)
rrip_add_CL(t, node);
if (node->type == ECMA119_DIR
&& node->dir.real_parent != node->parent) {
rrip_add_RE(t, node);
rrip_add_PL(t, node);
}
susp_add_CE(t, node);
if (node->type == ECMA119_DIR) {
for (i = 0; i < node->dir.nchildren; i++) {
add_susp_fields_rec(t, node->dir.children[i]);
}
}
}
static void
add_susp_fields(struct ecma119_write_target *t)
{
susp_add_SP(t, t->root);
rrip_add_ER(t, t->root);
add_susp_fields_rec(t, t->root);
}
/**
* Fill out the dir.len and dir.CE_len fields for each
* ecma119_tree_node that is a directory. Also calculate the total number of
* directories and the number of files for which we need to write out data.
* (dirlist_len and filelist_len)
*/
static void
calc_dir_size(struct ecma119_write_target *t,
struct ecma119_tree_node *dir)
{
size_t i;
assert(dir->type == ECMA119_DIR);
t->dirlist_len++;
dir->dir.len = 34 + dir->dir.self_susp.non_CE_len
+ 34 + dir->dir.parent_susp.non_CE_len;
dir->dir.CE_len = dir->dir.self_susp.CE_len
+ dir->dir.parent_susp.CE_len;
for (i = 0; i < dir->dir.nchildren; i++) {
struct ecma119_tree_node *ch = dir->dir.children[i];
dir->dir.len += ch->dirent_len + ch->susp.non_CE_len;
dir->dir.CE_len += ch->susp.CE_len;
}
t->total_dir_size += round_up(dir->dir.len + dir->dir.CE_len,
t->block_size);
for (i = 0; i < dir->dir.nchildren; i++) {
struct ecma119_tree_node *ch = dir->dir.children[i];
struct iso_tree_node *iso = ch->iso_self;
if (ch->type == ECMA119_DIR) {
calc_dir_size(t, ch);
} else if (iso && iso->attrib.st_size
&& iso->loc.type == LIBISO_FILESYS
&& iso->loc.path) {
t->filelist_len++;
}
}
}
/**
* Fill out the block field in each ecma119_tree_node that is a directory and
* fill out t->dirlist.
*/
static void
calc_dir_pos(struct ecma119_write_target *t,
struct ecma119_tree_node *dir)
{
size_t i;
assert(dir->type == ECMA119_DIR);
/* we don't need to set iso_self->block since each tree writes
* its own directories */
dir->block = t->curblock;
t->curblock += div_up(dir->dir.len + dir->dir.CE_len, t->block_size);
t->dirlist[t->curfile++] = dir;
for (i = 0; i < dir->dir.nchildren; i++) {
struct ecma119_tree_node *ch = dir->dir.children[i];
if (ch->type == ECMA119_DIR)
calc_dir_pos(t, ch);
}
/* reset curfile when we're finished */
if (!dir->parent) {
t->curfile = 0;
}
}
/**
* Fill out the block field for each ecma119_tree_node that is a file and fill
* out t->filelist.
*/
static void
calc_file_pos(struct ecma119_write_target *t,
struct ecma119_tree_node *dir)
{
size_t i;
assert(dir->type == ECMA119_DIR);
for (i = 0; i < dir->dir.nchildren; i++) {
struct ecma119_tree_node *ch = dir->dir.children[i];
if (ch->type == ECMA119_FILE && ch->iso_self) {
struct iso_tree_node *iso = ch->iso_self;
off_t size = iso->attrib.st_size;
iso->block = ch->block = t->curblock;
t->curblock += div_up(size, t->block_size);
if (size && iso->loc.type == LIBISO_FILESYS
&& iso->loc.path)
t->filelist[t->curfile++] = ch;
}
}
for (i = 0; i < dir->dir.nchildren; i++) {
struct ecma119_tree_node *ch = dir->dir.children[i];
if (ch->type == ECMA119_DIR)
calc_file_pos(t, ch);
}
/* reset curfile when we're finished */
if (!dir->parent) {
t->curfile = 0;
}
}
struct ecma119_write_target*
ecma119_target_new(struct iso_volset *volset,
int volnum,
int level,
int flags)
{
struct ecma119_write_target *t =
calloc(1, sizeof(struct ecma119_write_target));
size_t i, j, cur;
struct iso_tree_node *iso_root = volset->volume[volnum]->root;
volset->refcount++;
t->root = ecma119_tree_create(t, iso_root);
t->joliet = (flags & ECMA119_JOLIET) ? 1 : 0;
if (t->joliet)
t->joliet_root = joliet_tree_create(t, iso_root);
t->volset = volset;
t->volnum = volnum;
t->now = time(NULL);
t->rockridge = (flags & ECMA119_ROCKRIDGE) ? 1 : 0;
t->iso_level = level;
t->block_size = 2048;
if (t->rockridge)
add_susp_fields(t);
calc_dir_size(t, t->root);
if (t->joliet) {
joliet_calc_dir_size(t, t->joliet_root);
t->pathlist_joliet = calloc(1, sizeof(void*) * t->dirlist_len);
t->dirlist_joliet = calloc(1, sizeof(void*) * t->dirlist_len);
}
t->dirlist = calloc(1, sizeof(void*) * t->dirlist_len);
t->pathlist = calloc(1, sizeof(void*) * t->dirlist_len);
t->filelist = calloc(1, sizeof(void*) * t->filelist_len);
/* fill out the pathlist */
t->pathlist[0] = t->root;
t->path_table_size = 10; /* root directory record */
cur = 1;
for (i = 0; i < t->dirlist_len; i++) {
struct ecma119_tree_node *dir = t->pathlist[i];
for (j = 0; j < dir->dir.nchildren; j++) {
struct ecma119_tree_node *ch = dir->dir.children[j];
if (ch->type == ECMA119_DIR) {
size_t len = 8 + strlen(ch->name);
t->pathlist[cur++] = ch;
t->path_table_size += len + len % 2;
}
}
}
t->curblock = 16 /* system area */
+ 1 /* volume desc */
+ 1; /* volume desc terminator */
if (t->joliet) /* supplementary vol desc */
t->curblock += div_up (2048, t->block_size);
t->l_path_table_pos = t->curblock;
t->curblock += div_up(t->path_table_size, t->block_size);
t->m_path_table_pos = t->curblock;
t->curblock += div_up(t->path_table_size, t->block_size);
if (t->joliet) {
joliet_prepare_path_tables(t);
t->l_path_table_pos_joliet = t->curblock;
t->curblock += div_up(t->path_table_size_joliet, t->block_size);
t->m_path_table_pos_joliet = t->curblock;
t->curblock += div_up(t->path_table_size_joliet, t->block_size);
}
calc_dir_pos(t, t->root);
if (t->joliet)
joliet_calc_dir_pos(t, t->joliet_root);
calc_file_pos(t, t->root);
if (t->joliet)
joliet_update_file_pos (t, t->joliet_root);
if (t->rockridge) {
susp_finalize(t, t->root);
rrip_finalize(t, t->root);
}
t->total_size = t->curblock * t->block_size;
t->vol_space_size = t->curblock;
/* prepare for writing */
t->curblock = 0;
t->state = ECMA119_WRITE_SYSTEM_AREA;
return t;
}
static int
is_joliet_state(enum ecma119_write_state state)
{
return state == ECMA119_WRITE_SUP_VOL_DESC_JOLIET
|| state == ECMA119_WRITE_L_PATH_TABLE_JOLIET
|| state == ECMA119_WRITE_M_PATH_TABLE_JOLIET
|| state == ECMA119_WRITE_DIR_RECORDS_JOLIET;
}
static void
next_state(struct ecma119_write_target *t)
{
t->state++;
while (!t->joliet && is_joliet_state(t->state))
t->state++;
printf ("now in state %d, curblock=%d\n", (int)t->state, (int)t->curblock);
}
static void
wr_system_area(struct ecma119_write_target *t, uint8_t *buf)
{
memset(buf, 0, t->block_size);
if (t->curblock == 15) {
next_state(t);
}
}
static void
wr_pri_vol_desc(struct ecma119_write_target *t, uint8_t *buf)
{
ecma119_start_chunking(t, write_pri_vol_desc, 2048, buf);
}
static void
wr_vol_desc_term(struct ecma119_write_target *t, uint8_t *buf)
{
ecma119_start_chunking(t, write_vol_desc_terminator, 2048, buf);
}
static void
wr_l_path_table(struct ecma119_write_target *t, uint8_t *buf)
{
ecma119_start_chunking(t, write_l_path_table, t->path_table_size, buf);
}
static void
wr_m_path_table(struct ecma119_write_target *t, uint8_t *buf)
{
ecma119_start_chunking(t, write_m_path_table, t->path_table_size, buf);
}
static void
wr_dir_records(struct ecma119_write_target *t, uint8_t *buf)
{
ecma119_start_chunking(t, write_dirs, t->total_dir_size, buf);
}
static void
wr_files(struct ecma119_write_target *t, uint8_t *buf)
{
struct state_files *f_st = &t->state_files;
size_t nread;
struct ecma119_tree_node *f = t->filelist[f_st->file];
const char *path = f->iso_self->loc.path;
if (!f_st->fd) {
f_st->data_len = f->iso_self->attrib.st_size;
f_st->fd = fopen(path, "r");
if (!f_st->fd)
err(1, "couldn't open %s for reading", path);
assert(t->curblock == f->block);
}
nread = fread(buf, 1, t->block_size, f_st->fd);
f_st->pos += t->block_size;
if (nread < 0)
warn("problem reading from %s", path);
else if (nread != t->block_size && f_st->pos < f_st->data_len)
warnx("incomplete read from %s", path);
if (f_st->pos >= f_st->data_len) {
fclose(f_st->fd);
f_st->fd = 0;
f_st->pos = 0;
f_st->file++;
if (f_st->file >= t->filelist_len)
next_state(t);
}
}
static void
write_pri_vol_desc(struct ecma119_write_target *t, uint8_t *buf)
{
struct ecma119_pri_vol_desc *vol = (struct ecma119_pri_vol_desc*)buf;
struct iso_volume *volume = t->volset->volume[t->volnum];
char *vol_id = str2ascii(volume->volume_id);
char *pub_id = str2ascii(volume->publisher_id);
char *data_id = str2ascii(volume->data_preparer_id);
char *volset_id = str2ascii(t->volset->volset_id);
vol->vol_desc_type[0] = 1;
memcpy(vol->std_identifier, "CD001", 5);
vol->vol_desc_version[0] = 1;
memcpy(vol->system_id, "SYSID", 5);
if (vol_id)
strncpy((char*)vol->volume_id, vol_id, 32);
iso_bb(vol->vol_space_size, t->vol_space_size, 4);
iso_bb(vol->vol_set_size, t->volset->volset_size, 2);
iso_bb(vol->vol_seq_number, t->volnum + 1, 2);
iso_bb(vol->block_size, t->block_size, 2);
iso_bb(vol->path_table_size, t->path_table_size, 4);
iso_lsb(vol->l_path_table_pos, t->l_path_table_pos, 4);
iso_msb(vol->m_path_table_pos, t->m_path_table_pos, 4);
write_one_dir_record(t, t->root, 3, vol->root_dir_record);
strncpy((char*)vol->vol_set_id, volset_id, 128);
strncpy((char*)vol->publisher_id, pub_id, 128);
strncpy((char*)vol->data_prep_id, data_id, 128);
strncpy((char*)vol->application_id, "APPID", 128);
iso_datetime_17(vol->vol_creation_time, t->now);
iso_datetime_17(vol->vol_modification_time, t->now);
iso_datetime_17(vol->vol_effective_time, t->now);
vol->file_structure_version[0] = 1;
free(vol_id);
free(volset_id);
free(pub_id);
free(data_id);
}
static void
write_vol_desc_terminator(struct ecma119_write_target *t, uint8_t *buf)
{
struct ecma119_vol_desc_terminator *vol =
(struct ecma119_vol_desc_terminator*) buf;
vol->vol_desc_type[0] = 255;
memcpy(vol->std_identifier, "CD001", 5);
vol->vol_desc_version[0] = 1;
}
static void
write_path_table(struct ecma119_write_target *t, int l_type, uint8_t *buf)
{
void (*write_int)(uint8_t*, uint32_t, int) = l_type ? iso_lsb
: iso_msb;
size_t i;
struct ecma119_path_table_record *rec;
struct ecma119_tree_node *dir;
int parent = 0;
for (i = 0; i < t->dirlist_len; i++) {
dir = t->pathlist[i];
while ((i) && t->pathlist[parent] != dir->parent)
parent++;
assert(parent < i || i == 0);
rec = (struct ecma119_path_table_record*) buf;
rec->len_di[0] = dir->parent ? (uint8_t) strlen(dir->name) : 1;
rec->len_xa[0] = 0;
write_int(rec->block, dir->block, 4);
write_int(rec->parent, parent + 1, 2);
if (dir->parent)
memcpy(rec->dir_id, dir->name, rec->len_di[0]);
buf += 8 + rec->len_di[0] + (rec->len_di[0] % 2);
}
}
static void
write_l_path_table(struct ecma119_write_target *t, uint8_t *buf)
{
write_path_table(t, 1, buf);
}
static void
write_m_path_table(struct ecma119_write_target *t, uint8_t *buf)
{
write_path_table(t, 0, buf);
}
/* if file_id is >= 0, we use it instead of the filename. As a magic number,
* file_id == 3 means that we are writing the root directory record (in order
* to distinguish it from the "." entry in the root directory) */
static void
write_one_dir_record(struct ecma119_write_target *t,
struct ecma119_tree_node *node,
int file_id,
uint8_t *buf)
{
uint8_t len_dr = (file_id >= 0) ? 34 : node->dirent_len;
uint8_t len_fi = (file_id >= 0) ? 1 : strlen(node->name);
uint8_t f_id = (uint8_t) ((file_id == 3) ? 0 : file_id);
uint8_t *name = (file_id >= 0) ? &f_id : (uint8_t*)node->name;
uint32_t len = (node->type == ECMA119_DIR) ? node->dir.len
: node->file.real_me ? 0 : node->iso_self->attrib.st_size;
struct ecma119_dir_record *rec = (struct ecma119_dir_record*)buf;
/* we don't write out susp fields for the root node */
if (t->rockridge) {
if (file_id == 0) {
susp_write(t, &node->dir.self_susp, &buf[len_dr]);
len_dr += node->dir.self_susp.non_CE_len;
} else if (file_id == 1) {
susp_write(t, &node->dir.parent_susp, &buf[len_dr]);
len_dr += node->dir.parent_susp.non_CE_len;
} else if (file_id < 0) {
susp_write(t, &node->susp, &buf[len_dr]);
len_dr += node->susp.non_CE_len;
}
}
if (file_id == 1 && node->parent)
node = node->parent;
rec->len_dr[0] = len_dr;
iso_bb(rec->block, node->block, 4);
iso_bb(rec->length, len, 4);
iso_datetime_7(rec->recording_time, t->now);
rec->flags[0] = (node->type == ECMA119_DIR) ? 2 : 0;
iso_bb(rec->vol_seq_number, t->volnum + 1, 2);
rec->len_fi[0] = len_fi;
memcpy(rec->file_id, name, len_fi);
}
static void
write_one_dir(struct ecma119_write_target *t,
struct ecma119_tree_node *dir,
uint8_t *buf)
{
size_t i;
uint8_t *orig_buf = buf;
assert(dir->type == ECMA119_DIR);
/* write the "." and ".." entries first */
write_one_dir_record(t, dir, 0, buf);
buf += ((struct ecma119_dir_record*) buf)->len_dr[0];
write_one_dir_record(t, dir, 1, buf);
buf += ((struct ecma119_dir_record*) buf)->len_dr[0];
for (i = 0; i < dir->dir.nchildren; i++) {
write_one_dir_record(t, dir->dir.children[i], -1, buf);
buf += ((struct ecma119_dir_record*) buf)->len_dr[0];
}
/* write the susp continuation areas */
if (t->rockridge) {
susp_write_CE(t, &dir->dir.self_susp, buf);
buf += dir->dir.self_susp.CE_len;
susp_write_CE(t, &dir->dir.parent_susp, buf);
buf += dir->dir.parent_susp.CE_len;
for (i = 0; i < dir->dir.nchildren; i++) {
susp_write_CE(t, &dir->dir.children[i]->susp, buf);
buf += dir->dir.children[i]->susp.CE_len;
}
}
assert (buf - orig_buf == dir->dir.len + dir->dir.CE_len);
}
static void
write_dirs(struct ecma119_write_target *t, uint8_t *buf)
{
size_t i;
struct ecma119_tree_node *dir;
for (i = 0; i < t->dirlist_len; i++) {
dir = t->dirlist[i];
write_one_dir(t, dir, buf);
buf += round_up(dir->dir.len + dir->dir.CE_len, t->block_size);
}
}
void
ecma119_start_chunking(struct ecma119_write_target *t,
write_fn writer,
off_t data_size,
uint8_t *buf)
{
if (data_size != t->state_data_size) {
data_size = round_up(data_size, t->block_size);
t->state_data = realloc(t->state_data, data_size);
t->state_data_size = data_size;
}
memset(t->state_data, 0, t->state_data_size);
t->state_data_off = 0;
t->state_data_valid = 1;
writer(t, t->state_data);
write_data_chunk(t, buf);
}
static void
write_data_chunk(struct ecma119_write_target *t, uint8_t *buf)
{
memcpy(buf, t->state_data + t->state_data_off, t->block_size);
t->state_data_off += t->block_size;
if (t->state_data_off >= t->state_data_size) {
assert (t->state_data_off <= t->state_data_size);
t->state_data_valid = 0;
next_state(t);
}
}
static int
bs_read(struct burn_source *bs, unsigned char *buf, int size)
{
struct ecma119_write_target *t = (struct ecma119_write_target*)bs->data;
if (size != t->block_size) {
warnx("you must read data in block-sized chunks (%d bytes)",
(int)t->block_size);
return 0;
} else if (t->curblock >= t->vol_space_size) {
return 0;
}
if (t->state_data_valid)
write_data_chunk(t, buf);
else
writers[t->state](t, buf);
t->curblock++;
return size;
}
static off_t
bs_get_size(struct burn_source *bs)
{
struct ecma119_write_target *t = (struct ecma119_write_target*)bs->data;
return t->total_size;
}
static void
bs_free_data(struct burn_source *bs)
{
struct ecma119_write_target *t = (struct ecma119_write_target*)bs->data;
ecma119_tree_free(t->root);
free(t->dirlist);
free(t->pathlist);
free(t->dirlist_joliet);
free(t->pathlist_joliet);
free(t->filelist);
free(t->state_data);
if (t->state_files.fd)
fclose(t->state_files.fd);
}
struct burn_source *iso_source_new_ecma119(struct iso_volset *volset,
int volnum,
int level,
int flags)
{
struct burn_source *ret = calloc(1, sizeof(struct burn_source));
ret->refcount = 1;
ret->read = bs_read;
ret->get_size = bs_get_size;
ret->free_data = bs_free_data;
ret->data = ecma119_target_new(volset, volnum, level, flags);
return ret;
}

View File

@ -1,267 +0,0 @@
/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */
/* vim: set noet ts=8 sts=8 sw=8 : */
/**
* \file ecma119.h
*
* Structures and definitions used for writing an emca119 (ISO9660) compatible
* volume.
*/
#ifndef LIBISO_ECMA119_H
#define LIBISO_ECMA119_H
#include <sys/time.h>
#include <stdint.h>
#include <stdio.h> /* for FILE */
#include <sys/types.h>
#include "susp.h"
struct ecma119_tree_node;
struct joliet_tree_node;
/**
* The possible states that the ecma119 writer can be in.
*/
enum ecma119_write_state
{
ECMA119_WRITE_BEFORE,
ECMA119_WRITE_SYSTEM_AREA,
ECMA119_WRITE_PRI_VOL_DESC,
ECMA119_WRITE_SUP_VOL_DESC_JOLIET,
ECMA119_WRITE_VOL_DESC_TERMINATOR,
ECMA119_WRITE_L_PATH_TABLE,
ECMA119_WRITE_M_PATH_TABLE,
ECMA119_WRITE_L_PATH_TABLE_JOLIET,
ECMA119_WRITE_M_PATH_TABLE_JOLIET,
ECMA119_WRITE_DIR_RECORDS,
ECMA119_WRITE_DIR_RECORDS_JOLIET,
ECMA119_WRITE_FILES,
ECMA119_WRITE_DONE
};
/**
* Data describing the state of the ecma119 writer. Everything here should be
* considered private!
*/
struct ecma119_write_target
{
struct ecma119_tree_node *root;
struct joliet_tree_node *joliet_root;
struct iso_volset *volset;
int volnum;
time_t now; /**< Time at which writing began. */
off_t total_size; /**< Total size of the output. This only
* includes the current volume. */
uint32_t vol_space_size;
unsigned int rockridge:1;
unsigned int joliet:1;
unsigned int iso_level:2;
int curblock;
uint16_t block_size;
uint32_t path_table_size;
uint32_t path_table_size_joliet;
uint32_t l_path_table_pos;
uint32_t m_path_table_pos;
uint32_t l_path_table_pos_joliet;
uint32_t m_path_table_pos_joliet;
uint32_t total_dir_size;
uint32_t total_dir_size_joliet;
struct ecma119_tree_node **dirlist;
/**< A pre-order list of directories
* (this is the order in which we write
* out directory records).
*/
struct ecma119_tree_node **pathlist;
/**< A breadth-first list of
* directories. This is used for
* writing out the path tables.
*/
size_t dirlist_len; /**< The length of the previous 2 lists.
*/
struct ecma119_tree_node **filelist;
/**< A pre-order list of files with
* non-NULL paths and non-zero sizes.
*/
size_t filelist_len; /* Length of the previous list. */
int curfile; /**< Used as a helper field for writing
out filelist and dirlist */
/* Joliet versions of the above lists. Since Joliet doesn't require
* directory relocation, the order of these lists might be different
* from the lists above (but they will be the same length).
*/
struct joliet_tree_node **dirlist_joliet;
struct joliet_tree_node **pathlist_joliet;
enum ecma119_write_state state; /* The current state of the writer. */
/* Most writers work by
* 1) making sure state_data is big enough for their data
* 2) writing _all_ their data into state_data
* 3) relying on write_data_chunk to write the data block
* by block.
*/
uint8_t *state_data;
off_t state_data_size;
off_t state_data_off;
int state_data_valid;
/* for writing out files */
struct state_files {
off_t pos; /* The number of bytes we have written
* so far in the current file.
*/
off_t data_len;/* The number of bytes in the currently
* open file.
*/
FILE *fd; /* The currently open file. */
int file; /* The index in filelist that we are
* currently writing (or about to write). */
} state_files;
};
/**
* Create a new ecma119_write_target from the given volume number of the
* given volume set.
*
* \pre \p volnum is less than \p volset-\>volset_size.
* \post For each node in the tree, writer_data has been allocated.
* \post The directory heirarchy has been reorganised to be ecma119-compatible.
*/
struct ecma119_write_target *ecma119_target_new(struct iso_volset *volset,
int volnum,
int level,
int flags);
#define BP(a,b) [(b) - (a) + 1]
struct ecma119_pri_vol_desc
{
uint8_t vol_desc_type BP(1, 1);
uint8_t std_identifier BP(2, 6);
uint8_t vol_desc_version BP(7, 7);
uint8_t unused1 BP(8, 8);
uint8_t system_id BP(9, 40);
uint8_t volume_id BP(41, 72);
uint8_t unused2 BP(73, 80);
uint8_t vol_space_size BP(81, 88);
uint8_t unused3 BP(89, 120);
uint8_t vol_set_size BP(121, 124);
uint8_t vol_seq_number BP(125, 128);
uint8_t block_size BP(129, 132);
uint8_t path_table_size BP(133, 140);
uint8_t l_path_table_pos BP(141, 144);
uint8_t opt_l_path_table_pos BP(145, 148);
uint8_t m_path_table_pos BP(149, 152);
uint8_t opt_m_path_table_pos BP(153, 156);
uint8_t root_dir_record BP(157, 190);
uint8_t vol_set_id BP(191, 318);
uint8_t publisher_id BP(319, 446);
uint8_t data_prep_id BP(447, 574);
uint8_t application_id BP(575, 702);
uint8_t copyright_file_id BP(703, 739);
uint8_t abstract_file_id BP(740, 776);
uint8_t bibliographic_file_id BP(777, 813);
uint8_t vol_creation_time BP(814, 830);
uint8_t vol_modification_time BP(831, 847);
uint8_t vol_expiration_time BP(848, 864);
uint8_t vol_effective_time BP(865, 881);
uint8_t file_structure_version BP(882, 882);
uint8_t reserved1 BP(883, 883);
uint8_t app_use BP(884, 1395);
uint8_t reserved2 BP(1396, 2048);
};
struct ecma119_sup_vol_desc
{
uint8_t vol_desc_type BP(1, 1);
uint8_t std_identifier BP(2, 6);
uint8_t vol_desc_version BP(7, 7);
uint8_t vol_flags BP(8, 8);
uint8_t system_id BP(9, 40);
uint8_t volume_id BP(41, 72);
uint8_t unused2 BP(73, 80);
uint8_t vol_space_size BP(81, 88);
uint8_t esc_sequences BP(89, 120);
uint8_t vol_set_size BP(121, 124);
uint8_t vol_seq_number BP(125, 128);
uint8_t block_size BP(129, 132);
uint8_t path_table_size BP(133, 140);
uint8_t l_path_table_pos BP(141, 144);
uint8_t opt_l_path_table_pos BP(145, 148);
uint8_t m_path_table_pos BP(149, 152);
uint8_t opt_m_path_table_pos BP(153, 156);
uint8_t root_dir_record BP(157, 190);
uint8_t vol_set_id BP(191, 318);
uint8_t publisher_id BP(319, 446);
uint8_t data_prep_id BP(447, 574);
uint8_t application_id BP(575, 702);
uint8_t copyright_file_id BP(703, 739);
uint8_t abstract_file_id BP(740, 776);
uint8_t bibliographic_file_id BP(777, 813);
uint8_t vol_creation_time BP(814, 830);
uint8_t vol_modification_time BP(831, 847);
uint8_t vol_expiration_time BP(848, 864);
uint8_t vol_effective_time BP(865, 881);
uint8_t file_structure_version BP(882, 882);
uint8_t reserved1 BP(883, 883);
uint8_t app_use BP(884, 1395);
uint8_t reserved2 BP(1396, 2048);
};
struct ecma119_vol_desc_terminator
{
uint8_t vol_desc_type BP(1, 1);
uint8_t std_identifier BP(2, 6);
uint8_t vol_desc_version BP(7, 7);
uint8_t reserved BP(8, 2048);
};
struct ecma119_dir_record
{
uint8_t len_dr BP(1, 1);
uint8_t len_xa BP(2, 2);
uint8_t block BP(3, 10);
uint8_t length BP(11, 18);
uint8_t recording_time BP(19, 25);
uint8_t flags BP(26, 26);
uint8_t file_unit_size BP(27, 27);
uint8_t interleave_gap_size BP(28, 28);
uint8_t vol_seq_number BP(29, 32);
uint8_t len_fi BP(33, 33);
uint8_t file_id BP(34, 34); /* 34 to 33+len_fi */
/* padding field (if len_fi is even) */
/* system use (len_dr - len_su + 1 to len_dr) */
};
struct ecma119_path_table_record
{
uint8_t len_di BP(1, 1);
uint8_t len_xa BP(2, 2);
uint8_t block BP(3, 6);
uint8_t parent BP(7, 8);
uint8_t dir_id BP(9, 9); /* 9 to 8+len_di */
/* padding field (if len_di is odd) */
};
/**
* A utility function for writers that want to write their data all at once
* rather than block-by-block. This creates a buffer of size \p size, passes
* it to the given writer, then hands out block-sized chunks.
*/
void
ecma119_start_chunking(struct ecma119_write_target *t,
void (*)(struct ecma119_write_target*, uint8_t*),
off_t size,
uint8_t *buf);
#endif /* LIBISO_ECMA119_H */

View File

@ -1,312 +0,0 @@
/* vim: set noet ts=8 sts=8 sw=8 : */
#include <string.h>
#include <wchar.h>
#include <stdlib.h>
#include <assert.h>
#include "ecma119.h"
#include "ecma119_tree.h"
#include "tree.h"
#include "util.h"
static size_t calc_dirent_len(struct ecma119_tree_node *n)
{
int ret = n->name ? strlen(n->name) + 33 : 34;
if (ret % 2) ret++;
return ret;
}
static struct ecma119_tree_node*
create_dir(struct ecma119_write_target *t,
struct ecma119_tree_node *parent,
struct iso_tree_node *iso)
{
struct ecma119_tree_node *ret;
assert(t && (!parent || parent->type == ECMA119_DIR)
&& iso && S_ISDIR(iso->attrib.st_mode));
ret = calloc(1, sizeof(struct ecma119_tree_node));
ret->name = iso->name ? ((t->iso_level == 1) ? iso_1_dirid(iso->name)
: iso_2_dirid(iso->name))
: NULL;
ret->dirent_len = calc_dirent_len(ret);
ret->iso_self = iso;
ret->target = t;
ret->type = ECMA119_DIR;
ret->parent = ret->dir.real_parent = parent;
ret->dir.depth = parent ? parent->dir.depth + 1 : 1;
ret->dir.nchildren = iso->nchildren;
ret->dir.children = calloc(1, sizeof(void*) * iso->nchildren);
return ret;
}
static struct ecma119_tree_node*
create_file(struct ecma119_write_target *t,
struct ecma119_tree_node *parent,
struct iso_tree_node *iso)
{
struct ecma119_tree_node *ret;
assert(t && iso && parent && parent->type == ECMA119_DIR);
ret = calloc(1, sizeof(struct ecma119_tree_node));
ret->name = iso->name ? ((t->iso_level == 1) ? iso_1_fileid(iso->name)
: iso_2_fileid(iso->name))
: NULL;
ret->dirent_len = calc_dirent_len(ret);
ret->parent = parent;
ret->iso_self = iso;
ret->target = t;
ret->type = ECMA119_FILE;
return ret;
}
static struct ecma119_tree_node*
create_tree(struct ecma119_write_target *t,
struct ecma119_tree_node *parent,
struct iso_tree_node *iso)
{
struct ecma119_tree_node *ret;
size_t i;
assert(t && iso);
ret = (S_ISDIR(iso->attrib.st_mode) ? create_dir : create_file)
(t, parent, iso);
for (i = 0; i < iso->nchildren; i++) {
ret->dir.children[i] = create_tree(t, ret, iso->children[i]);
}
return ret;
}
void
ecma119_tree_free(struct ecma119_tree_node *root)
{
size_t i;
if (root->type == ECMA119_DIR) {
for (i=0; i < root->dir.nchildren; i++) {
ecma119_tree_free(root->dir.children[i]);
}
free(root->dir.children);
}
free(root->name);
free(root);
}
static size_t
max_child_name_len(struct ecma119_tree_node *root)
{
size_t ret = 0, i;
assert(root->type == ECMA119_DIR);
for (i=0; i < root->dir.nchildren; i++) {
size_t len = strlen(root->dir.children[i]->name);
ret = MAX(ret, len);
}
return ret;
}
static void
reparent(struct ecma119_tree_node *child,
struct ecma119_tree_node *parent)
{
int found = 0;
size_t i;
struct ecma119_tree_node *placeholder;
assert(child && parent && parent->type == ECMA119_DIR && child->parent);
/* replace the child in the original parent with a placeholder */
for (i=0; i < child->parent->dir.nchildren; i++) {
if (child->parent->dir.children[i] == child) {
placeholder = create_file(child->target,
child->parent,
child->iso_self);
placeholder->file.real_me = child;
child->parent->dir.children[i] = placeholder;
found = 1;
break;
}
}
assert(found);
/* add the child to its new parent */
child->parent = parent;
parent->dir.nchildren++;
parent->dir.children = realloc( parent->dir.children,
sizeof(void*) * parent->dir.nchildren );
parent->dir.children[parent->dir.nchildren-1] = child;
}
/**
* Reorder the tree, if necessary, to ensure that
* - the depth is at most 8
* - each path length is at most 255 characters
*/
static void
reorder_tree(struct ecma119_tree_node *root,
struct ecma119_tree_node *cur)
{
size_t max_path;
assert(root && cur && cur->type == ECMA119_DIR);
cur->dir.depth = cur->parent ? cur->parent->dir.depth + 1 : 1;
cur->dir.path_len = cur->parent ? cur->parent->dir.path_len
+ strlen(cur->name) : 0;
max_path = cur->dir.path_len + cur->dir.depth + max_child_name_len(cur);
if (cur->dir.depth > 8 || max_path > 255) {
reparent(cur, root);
/* we are appended to the root's children now, so there is no
* need to recurse (the root will hit us again) */
} else {
size_t i;
for (i=0; i < cur->dir.nchildren; i++) {
if (cur->dir.children[i]->type == ECMA119_DIR)
reorder_tree(root, cur->dir.children[i]);
}
}
}
static int
cmp_node(const void *f1, const void *f2)
{
struct ecma119_tree_node *f = *((struct ecma119_tree_node**)f1);
struct ecma119_tree_node *g = *((struct ecma119_tree_node**)f2);
return strcmp(f->name, g->name);
}
static void
sort_tree(struct ecma119_tree_node *root)
{
size_t i;
assert(root && root->type == ECMA119_DIR);
qsort(root->dir.children, root->dir.nchildren, sizeof(void*), cmp_node);
for (i=0; i < root->dir.nchildren; i++) {
if (root->dir.children[i]->type == ECMA119_DIR)
sort_tree(root->dir.children[i]);
}
}
/**
* Change num_change characters of the given filename in order to ensure the
* name is unique. If the name is short enough (depending on the ISO level),
* we can append the characters instead of changing them.
*
* \p seq_num is the index of this file in the sequence of identical filenames.
*
* For example, seq_num=3, num_change=2, name="HELLOTHERE.TXT" changes name to
* "HELLOTHE03.TXT"
*/
static void
mangle_name(char **name, int num_change, int level, int seq_num)
{
char *dot = strrchr(*name, '.');
char *semi = strrchr(*name, ';');
size_t len = strlen(*name);
char base[len+1], ext[len+1];
char fmt[12];
size_t baselen, extlen;
if (num_change >= len) {
return;
}
strncpy(base, *name, len+1);
if (dot) {
base[dot - *name] = '\0';
strncpy(ext, dot+1, len+1);
if (semi) {
ext[semi - dot - 1] = '\0';
}
} else {
base[len-2] = '\0';
ext[0] = '\0';
}
baselen = strlen(base);
extlen = strlen(ext);
if (level == 1 && baselen + num_change > 8) {
base[8 - num_change] = '\0';
} else if (level != 1 && baselen + extlen + num_change > 30) {
base[30 - extlen - num_change] = '\0';
}
sprintf(fmt, "%%s%%0%1dd.%%s;1", num_change);
*name = realloc(*name, baselen + extlen + num_change + 4);
sprintf(*name, fmt, base, seq_num, ext);
}
static void
mangle_all(struct ecma119_tree_node *dir)
{
size_t i, j, k;
struct ecma119_dir_info d = dir->dir;
size_t n_change;
int changed;
assert(dir->type == ECMA119_DIR);
do {
changed = 0;
for (i=0; i < d.nchildren; i++) {
/* find the number of consecutive equal names */
j = 1;
while ( i+j < d.nchildren &&
!strcmp(d.children[i]->name,
d.children[i+j]->name) )
j++;
if (j == 1) continue;
/* mangle the names */
changed = 1;
n_change = j / 10 + 1;
for (k=0; k < j; k++) {
mangle_name(&(d.children[i+k]->name),
n_change,
dir->target->iso_level,
k);
d.children[i+k]->dirent_len =
calc_dirent_len(d.children[i+k]);
}
/* skip ahead by the number of mangled names */
i += j - 1;
}
} while (changed);
for (i=0; i < d.nchildren; i++) {
if (d.children[i]->type == ECMA119_DIR)
mangle_all(d.children[i]);
}
}
struct ecma119_tree_node*
ecma119_tree_create(struct ecma119_write_target *t,
struct iso_tree_node *iso_root)
{
t->root = create_tree(t, NULL, iso_root);
reorder_tree(t->root, t->root);
sort_tree(t->root);
mangle_all(t->root);
return t->root;
}
void
ecma119_tree_print(struct ecma119_tree_node *root, int spaces)
{
size_t i;
char sp[spaces+1];
memset(sp, ' ', spaces);
sp[spaces] = '\0';
printf("%s%s\n", sp, root->name);
if (root->type == ECMA119_DIR)
for (i=0; i < root->dir.nchildren; i++)
ecma119_tree_print(root->dir.children[i], spaces+2);
}

View File

@ -1,95 +0,0 @@
/* vim: set noet ts=8 sts=8 sw=8 : */
/**
* \file ecma119_tree.h
*
* Declarations for creating, modifying and printing filesystem trees that
* are compatible with ecma119.
*/
#ifndef LIBISO_ECMA119_TREE_H
#define LIBISO_ECMA119_TREE_H
struct ecma119_write_target;
enum {
ECMA119_FILE,
ECMA119_DIR
};
struct ecma119_dir_info {
struct susp_info self_susp; /**< susp entries for "." */
struct susp_info parent_susp; /**< susp entries for ".." */
size_t len; /**< sum of the lengths of children's
* Directory Records (including SU) */
size_t CE_len; /**< sum of the lengths of children's
* SUSP CE areas */
int depth;
size_t path_len; /**< The length of a path up to, and
* including, this directory. This
* cannot exceed 255. */
size_t nchildren;
struct ecma119_tree_node **children;
struct ecma119_tree_node *real_parent;
/**< The parent before relocation */
};
struct ecma119_file_info
{
struct ecma119_tree_node *real_me;
/**< If this is non-NULL, the file is
* a placeholder for a relocated
* directory and this field points to
* that relocated directory.
*/
};
/**
* A node for a tree containing all the information necessary for writing
* an ISO9660 volume.
*/
struct ecma119_tree_node
{
char *name; /**< in ASCII, conforming to the
* current ISO level. */
size_t dirent_len; /**< Length of the directory record,
* not including SU. */
size_t block;
struct ecma119_tree_node *parent;
struct iso_tree_node *iso_self;
struct ecma119_write_target *target;
struct susp_info susp;
int type; /**< file or directory */
/* union {*/
struct ecma119_dir_info dir;
struct ecma119_file_info file;
/* };*/
};
/**
* Create a new ecma119_tree that corresponds to the tree represented by
* \p iso_root.
*/
struct ecma119_tree_node*
ecma119_tree_create(struct ecma119_write_target *target,
struct iso_tree_node *iso_root);
/**
* Free an ecma119 tree.
*/
void
ecma119_tree_free(struct ecma119_tree_node *root);
/**
* Print an ecma119 tree.
*/
void
ecma119_tree_print(struct ecma119_tree_node *root, int spaces);
#endif /* LIBISO_ECMA119_TREE_H */

View File

@ -1,42 +0,0 @@
#include "hash.h"
#include "exclude.h"
static struct iso_hash_node *table[HASH_NODES]={0,};
static int num=0;
void
iso_exclude_add_path(const char *path)
{
if (!path)
return;
num += iso_hash_insert(table, path);
}
void
iso_exclude_remove_path(const char *path)
{
if (!num || !path)
return;
num -= iso_hash_remove(table, path);
}
void
iso_exclude_empty(void)
{
if (!num)
return;
iso_hash_empty(table);
num=0;
}
int
iso_exclude_lookup(const char *path)
{
if (!num || !path)
return 0;
return iso_hash_lookup(table, path);
}

View File

@ -1,12 +0,0 @@
#ifndef ISO_EXCLUDE_H
#define ISO_EXCLUDE_H
/**
* Add a path to ignore when adding a directory recursively.
*
* \param path The path, on the local filesystem, of the file.
*/
int
iso_exclude_lookup(const char *path);
#endif /* ISO_EXCLUDE */

View File

@ -1,158 +0,0 @@
#include <stdlib.h>
#include <string.h>
#include "hash.h"
static unsigned int
iso_hash_path(const char *path)
{
unsigned int hash_num=0;
const char *c;
c=path;
while(*c)
hash_num = (hash_num << 15) + (hash_num << 3) + (hash_num >> 3) + *c++;
return hash_num % HASH_NODES;
}
int
iso_hash_lookup(struct iso_hash_node **table, const char *path)
{
struct iso_hash_node *node;
unsigned int hash_num;
hash_num = iso_hash_path(path);
node=table[hash_num];
if (!node)
return 0;
if (!strcmp(path, node->path))
return 1;
while (node->next) {
node=node->next;
if (!strcmp(path, node->path))
return 1;
}
return 0;
}
static struct iso_hash_node*
iso_hash_node_new (const char *path)
{
struct iso_hash_node *node;
/*create an element to be inserted in the hash table */
node=malloc(sizeof(struct iso_hash_node));
node->path=strdup(path);
node->next=NULL;
return node;
}
int
iso_hash_insert(struct iso_hash_node **table, const char *path)
{
struct iso_hash_node *node;
unsigned int hash_num;
/* find the hash number */
hash_num = iso_hash_path(path);
/* insert it */
node = table[hash_num];
/* unfortunately, we can't safely consider that a path
* won't be twice in the hash table so make sure it
* doesn't already exists */
if (!node) {
table[hash_num]=iso_hash_node_new(path);
return 1;
}
/* if it's already in, we don't do anything */
if (!strcmp(path, node->path))
return 0;
while (node->next) {
node = node->next;
/* if it's already in, we don't do anything */
if (!strcmp (path, node->path))
return 0;
}
node->next = iso_hash_node_new(path);
return 1;
}
static void
iso_hash_node_free(struct iso_hash_node *node)
{
free(node->path);
free(node);
}
int
iso_hash_remove(struct iso_hash_node **table, const char *path)
{
unsigned int hash_num;
struct iso_hash_node *node;
hash_num = iso_hash_path(path);
node=table[hash_num];
if (!node)
return 0;
if (!strcmp(path, node->path)) {
table[hash_num]=node->next;
iso_hash_node_free(node);
return 1;
}
while (node->next) {
struct iso_hash_node *prev;
prev = node;
node = node->next;
if (!strcmp (path, node->path)) {
prev->next=node->next;
iso_hash_node_free(node);
return 1;
}
}
return 0;
}
void
iso_hash_empty(struct iso_hash_node **table)
{
int i;
for (i=0; i < HASH_NODES; i++) {
struct iso_hash_node *node;
node=table[i];
if (!node)
continue;
table[i]=NULL;
do {
struct iso_hash_node *next;
next=node->next;
iso_hash_node_free(node);
node=next;
} while (node);
}
}

View File

@ -1,46 +0,0 @@
#ifndef ISO_HASH_H
#define ISO_HASH_H
struct iso_hash_node {
struct iso_hash_node *next;
char *path;
};
#define HASH_NODES 128
/**
* Searches in the hash table if the path exists.
*
* \param table The hash table.
* \param path The path of the file to look for.
*
* \return 1 if the path exists in the hash table, 0 otherwise.
*/
int iso_hash_lookup(struct iso_hash_node **table, const char *path);
/**
* Insert a new path in the hash table.
*
* \param table The hash table.
* \param path The path of a file to add to the hash table.
*
* \return 1 if the file wasn't already in the hash table, 0 otherwise.
*/
int iso_hash_insert(struct iso_hash_node **table, const char *path);
/**
* Remove a path from the hash table.
*
* \param table The hash table.
* \param path The path of a file to remove from the hash table.
*
* \return 1 if the file was found and removed, 0 otherwise.
*/
int iso_hash_remove(struct iso_hash_node **table, const char *path);
/**
* Empty the hash table.
*/
void iso_hash_empty(struct iso_hash_node **table);
#endif /* ISO_HASH_H */

View File

@ -1,379 +0,0 @@
/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */
/* vim: set noet ts=8 sts=8 sw=8 : */
#include "joliet.h"
#include "ecma119.h"
#include "ecma119_tree.h"
#include "tree.h"
#include "util.h"
#include "volume.h"
#include <assert.h>
#include <string.h>
static struct joliet_tree_node*
create_node(struct ecma119_write_target *t,
struct joliet_tree_node *parent,
struct iso_tree_node *iso)
{
struct joliet_tree_node *ret =
calloc(1, sizeof(struct joliet_tree_node));
ret->name = iso_j_id(iso->name);
ret->dirent_len = 34 + (ret->name ? ucslen(ret->name) * 2 : 0);
ret->len = iso->attrib.st_size; /* for dirs, we'll change this */
ret->block = iso->block; /* only actually for files, not dirs */
ret->parent = parent;
ret->iso_self = iso;
ret->target = t;
ret->nchildren = iso->nchildren;
if (ret->nchildren)
ret->children = calloc(sizeof(void*), ret->nchildren);
return ret;
}
static struct joliet_tree_node*
create_tree(struct ecma119_write_target *t,
struct joliet_tree_node *parent,
struct iso_tree_node *iso_root)
{
struct joliet_tree_node *root = create_node(t, parent, iso_root);
size_t i;
for (i = 0; i < root->nchildren; i++) {
struct iso_tree_node *iso_ch = iso_root->children[i];
if (ISO_ISDIR(iso_ch))
root->children[i] = create_tree(t, root, iso_ch);
else
root->children[i] = create_node(t, root, iso_ch);
}
return root;
}
static int
cmp_node(const void *f1, const void *f2)
{
struct joliet_tree_node *f = *((struct joliet_tree_node**)f1);
struct joliet_tree_node *g = *((struct joliet_tree_node**)f2);
return ucscmp(f->name, g->name);
}
static void
sort_tree(struct joliet_tree_node *root)
{
size_t i;
assert(root && ISO_ISDIR(root->iso_self));
qsort(root->children, root->nchildren, sizeof(void*), cmp_node);
for (i = 0; i < root->nchildren; i++)
if (ISO_ISDIR(root->children[i]->iso_self))
sort_tree(root->children[i]);
}
void
joliet_prepare_path_tables(struct ecma119_write_target *t)
{
size_t cur, i, j;
t->pathlist_joliet[0] = t->joliet_root;
t->path_table_size_joliet = 10; /* root directory record */
cur = 1;
for (i = 0; i < t->dirlist_len; i++) {
struct joliet_tree_node *dir = t->pathlist_joliet[i];
for (j = 0; j < dir->nchildren; j++) {
struct joliet_tree_node *ch = dir->children[j];
if (ISO_ISDIR(ch->iso_self)) {
size_t len = 8 + ucslen(ch->name)*2;
t->pathlist_joliet[cur++] = ch;
t->path_table_size_joliet += len;
}
}
}
}
/**
* Calculate the size of each directory.
*/
void
joliet_calc_dir_size(struct ecma119_write_target *t,
struct joliet_tree_node *root)
{
size_t i;
struct joliet_tree_node *ch;
assert(root && ISO_ISDIR(root->iso_self));
root->len = 68; /* for "." and ".." entries */
for (i = 0; i < root->nchildren; i++) {
ch = root->children[i];
root->len += ch->dirent_len;
if (ISO_ISDIR(ch->iso_self))
joliet_calc_dir_size(t, ch);
}
t->total_dir_size_joliet += round_up (root->len, t->block_size);
}
/**
* Calculate the position of each directory. Also fill out t->dirlist_joliet.
*/
void
joliet_calc_dir_pos(struct ecma119_write_target *t,
struct joliet_tree_node *root)
{
size_t i;
struct joliet_tree_node *ch;
assert(root && ISO_ISDIR(root->iso_self));
root->block = t->curblock;
t->curblock += div_up(root->len, t->block_size);
t->dirlist_joliet[t->curfile++] = root;
for (i = 0; i < root->nchildren; i++) {
ch = root->children[i];
if (ISO_ISDIR(ch->iso_self))
joliet_calc_dir_pos(t, ch);
}
/* reset curfile when we're finished */
if (!root->parent)
t->curfile = 0;
}
void
joliet_update_file_pos(struct ecma119_write_target *t,
struct joliet_tree_node *dir)
{
size_t i;
assert(dir && ISO_ISDIR(dir->iso_self));
for (i = 0; i < dir->nchildren; i++) {
struct joliet_tree_node *ch;
ch = dir->children[i];
if (!ISO_ISDIR (ch->iso_self)) {
struct iso_tree_node *iso = ch->iso_self;
ch->block = iso->block;
}
else
joliet_update_file_pos(t, ch);
}
/* reset curfile when we're finished */
if (!dir->parent)
t->curfile = 0;
}
struct joliet_tree_node*
joliet_tree_create(struct ecma119_write_target *t,
struct iso_tree_node *iso_root)
{
struct joliet_tree_node *root = create_tree(t, NULL, iso_root);
sort_tree(root);
return root;
}
/* ugh. this is mostly C&P */
static void
write_path_table(struct ecma119_write_target *t,
int l_type,
uint8_t *buf)
{
void (*write_int)(uint8_t*, uint32_t, int) = l_type ?
iso_lsb : iso_msb;
size_t i;
struct ecma119_path_table_record *rec;
struct joliet_tree_node *dir;
int parent = 0;
assert (t->joliet);
for (i = 0; i < t->dirlist_len; i++) {
dir = t->pathlist_joliet[i];
while ((i) && t->pathlist_joliet[parent] != dir->parent)
parent++;
assert(parent < i || i == 0);
rec = (struct ecma119_path_table_record*) buf;
rec->len_di[0] = dir->parent ?
(uint8_t) ucslen(dir->name) * 2 : 1;
rec->len_xa[0] = 0;
write_int(rec->block, dir->block, 4);
write_int(rec->parent, parent + 1, 2);
if (dir->parent)
memcpy(rec->dir_id, dir->name, rec->len_di[0]);
buf += 8 + rec->len_di[0] + (rec->len_di[0] % 2);
}
}
/* if file_id is >= 0, we use it instead of the filename. As a magic number,
* file_id == 3 means that we are writing the root directory record (in order
* to distinguish it from the "." entry in the root directory) */
static void
write_one_dir_record(struct ecma119_write_target *t,
struct joliet_tree_node *node,
int file_id,
uint8_t *buf)
{
uint8_t len_dr = (file_id >= 0) ? 34 : node->dirent_len;
uint8_t len_fi = (file_id >= 0) ? 1 : ucslen(node->name) * 2;
uint8_t f_id = (uint8_t) ((file_id == 3) ? 0 : file_id);
uint8_t *name = (file_id >= 0) ? &f_id : (uint8_t*)node->name;
struct ecma119_dir_record *rec = (struct ecma119_dir_record*)buf;
if (file_id == 1 && node->parent)
node = node->parent;
rec->len_dr[0] = len_dr;
iso_bb(rec->block, node->block, 4);
iso_bb(rec->length, node->len, 4);
iso_datetime_7(rec->recording_time, t->now);
rec->flags[0] = ISO_ISDIR(node->iso_self) ? 2 : 0;
iso_bb(rec->vol_seq_number, t->volnum + 1, 2);
rec->len_fi[0] = len_fi;
memcpy(rec->file_id, name, len_fi);
}
static void
write_l_path_table(struct ecma119_write_target *t, uint8_t *buf)
{
write_path_table (t, 1, buf);
}
static void
write_m_path_table(struct ecma119_write_target *t, uint8_t *buf)
{
write_path_table (t, 0, buf);
}
static void
write_sup_vol_desc(struct ecma119_write_target *t, uint8_t *buf)
{
struct ecma119_sup_vol_desc *vol = (struct ecma119_sup_vol_desc*)buf;
struct iso_volume *volume = t->volset->volume[t->volnum];
uint16_t *vol_id = str2ucs(volume->volume_id);
uint16_t *pub_id = str2ucs(volume->publisher_id);
uint16_t *data_id = str2ucs(volume->data_preparer_id);
uint16_t *volset_id = str2ucs(t->volset->volset_id);
int vol_id_len = MIN(32, ucslen(vol_id) * 2);
int pub_id_len = MIN(128, ucslen(pub_id) * 2);
int data_id_len = MIN(128, ucslen(data_id) * 2);
int volset_id_len = MIN(128, ucslen(volset_id) * 2);
vol->vol_desc_type[0] = 2;
memcpy(vol->std_identifier, "CD001", 5);
vol->vol_desc_version[0] = 1;
memcpy(vol->system_id, "SYSID", 5);
if (vol_id)
memcpy(vol->volume_id, vol_id, vol_id_len);
memcpy(vol->esc_sequences, "%/E", 3);
iso_bb(vol->vol_space_size, t->vol_space_size, 4);
iso_bb(vol->vol_set_size, t->volset->volset_size, 2);
iso_bb(vol->vol_seq_number, t->volnum + 1, 2);
iso_bb(vol->block_size, t->block_size, 2);
iso_bb(vol->path_table_size, t->path_table_size_joliet, 4);
iso_lsb(vol->l_path_table_pos, t->l_path_table_pos_joliet, 4);
iso_msb(vol->m_path_table_pos, t->m_path_table_pos_joliet, 4);
write_one_dir_record(t, t->joliet_root, 3, vol->root_dir_record);
memcpy(vol->vol_set_id, volset_id, volset_id_len);
memcpy(vol->publisher_id, pub_id, pub_id_len);
memcpy(vol->data_prep_id, data_id, data_id_len);
/*memcpy(vol->application_id, "APPID", app_id_len);*/
iso_datetime_17(vol->vol_creation_time, t->now);
iso_datetime_17(vol->vol_modification_time, t->now);
iso_datetime_17(vol->vol_effective_time, t->now);
vol->file_structure_version[0] = 1;
free(vol_id);
free(volset_id);
free(pub_id);
free(data_id);
}
static void
write_one_dir(struct ecma119_write_target *t,
struct joliet_tree_node *dir,
uint8_t *buf)
{
size_t i;
uint8_t *orig_buf = buf;
assert(ISO_ISDIR (dir->iso_self));
/* write the "." and ".." entries first */
write_one_dir_record(t, dir, 0, buf);
buf += ((struct ecma119_dir_record*) buf)->len_dr[0];
write_one_dir_record(t, dir, 1, buf);
buf += ((struct ecma119_dir_record*) buf)->len_dr[0];
for (i = 0; i < dir->nchildren; i++) {
write_one_dir_record(t, dir->children[i], -1, buf);
buf += ((struct ecma119_dir_record*) buf)->len_dr[0];
}
assert (buf - orig_buf == dir->len);
}
static void
write_dirs(struct ecma119_write_target *t, uint8_t *buf)
{
size_t i;
struct joliet_tree_node *dir;
assert (t->curblock == t->dirlist_joliet[0]->block);
for (i = 0; i < t->dirlist_len; i++) {
dir = t->dirlist_joliet[i];
write_one_dir(t, dir, buf);
buf += round_up(dir->len, t->block_size);
}
}
void
joliet_wr_sup_vol_desc(struct ecma119_write_target *t,
uint8_t *buf)
{
ecma119_start_chunking(t,
write_sup_vol_desc,
2048,
buf);
}
void
joliet_wr_l_path_table(struct ecma119_write_target *t,
uint8_t *buf)
{
ecma119_start_chunking(t,
write_l_path_table,
t->path_table_size_joliet,
buf);
}
void
joliet_wr_m_path_table(struct ecma119_write_target *t,
uint8_t *buf)
{
ecma119_start_chunking(t,
write_m_path_table,
t->path_table_size_joliet,
buf);
}
void
joliet_wr_dir_records(struct ecma119_write_target *t,
uint8_t *buf)
{
ecma119_start_chunking(t,
write_dirs,
t->total_dir_size_joliet,
buf);
}

View File

@ -1,84 +0,0 @@
/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */
/* vim: set noet ts=8 sts=8 sw=8 : */
/**
* \file joliet.h
*
* Declare the filesystems trees that are Joliet-compatible and the public
* functions for tying them into an ecma119 volume.
*/
#ifndef LIBISO_JOLIET_H
#define LIBISO_JOLIET_H
#include <stdint.h>
#include <stdlib.h>
struct ecma119_write_target;
struct iso_tree_node;
struct joliet_tree_node
{
uint16_t *name; /**< In UCS-2BE. */
size_t dirent_len;
size_t len;
size_t block;
struct joliet_tree_node *parent;
struct iso_tree_node *iso_self;
struct ecma119_write_target *target;
struct joliet_tree_node **children;
size_t nchildren;
};
/**
* Create a new joliet_tree that corresponds to the tree represented by
* \p iso_root.
*/
struct joliet_tree_node*
joliet_tree_create(struct ecma119_write_target *target,
struct iso_tree_node *iso_root);
/**
* Calculate the size of each directory in the joliet heirarchy.
*/
void
joliet_calc_dir_size(struct ecma119_write_target *t, struct joliet_tree_node*);
/**
* Calculate the position of each directory in the joliet heirarchy.
*/
void
joliet_calc_dir_pos(struct ecma119_write_target *t, struct joliet_tree_node*);
/**
* Update the position of each file in the joliet hierarchy (to be called
* AFTER the file positions in the iso tree have been set).
*/
void
joliet_update_file_pos(struct ecma119_write_target *t, struct joliet_tree_node*);
/**
* Calculate the size of the joliet path table and fill in the list of
* directories.
*/
void
joliet_prepare_path_tables(struct ecma119_write_target *t);
void
joliet_tree_free(struct joliet_tree_node *root);
void
joliet_wr_sup_vol_desc(struct ecma119_write_target *t, uint8_t *buf);
void
joliet_wr_l_path_table(struct ecma119_write_target *t, uint8_t *buf);
void
joliet_wr_m_path_table(struct ecma119_write_target *t, uint8_t *buf);
void
joliet_wr_dir_records(struct ecma119_write_target *t, uint8_t *buf);
#endif /* LIBISO_JOLIET_H */

View File

@ -1,225 +0,0 @@
/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */
/* vim: set noet ts=8 sts=8 sw=8 : */
/**
* Create an ISO-9660 data volume with Rock Ridge and Joliet extensions.
* Usage is easy:
* - Create a new volume.
* - Add files and directories.
* - Write the volume to a file or create a burn source for use with Libburn.
*/
#ifndef LIBISO_LIBISOFS_H
#define LIBISO_LIBISOFS_H
/* #include <libburn.h> */
struct burn_source;
/**
* Data volume.
* @see volume.h for details.
*/
struct iso_volume;
/**
* A set of data volumes.
* @see volume.h for details.
*/
struct iso_volset;
/**
* A node in the filesystem tree.
* \see tree.h
*/
struct iso_tree_node;
enum ecma119_extension_flag {
ECMA119_ROCKRIDGE = (1<<0),
ECMA119_JOLIET = (1<<1)
};
/**
* Create a new volume.
* The parameters can be set to NULL if you wish to set them later.
*/
struct iso_volume *iso_volume_new(const char *volume_id,
const char *publisher_id,
const char *data_preparer_id);
struct iso_volume *iso_volume_new_with_root(const char *volume_id,
const char *publisher_id,
const char *data_preparer_id,
struct iso_tree_node *root);
/**
* Free a volume.
*/
void iso_volume_free(struct iso_volume *volume);
/**
* Free a set of data volumes.
*/
void iso_volset_free(struct iso_volset *volume);
/**
* Get the root directory for a volume.
*/
struct iso_tree_node *iso_volume_get_root(const struct iso_volume *volume);
/**
* Fill in the volume identifier for a volume.
*/
void iso_volume_set_volume_id(struct iso_volume *volume,
const char *volume_id);
/**
* Fill in the publisher for a volume.
*/
void iso_volume_set_publisher_id(struct iso_volume *volume,
const char *publisher_id);
/**
* Fill in the data preparer for a volume.
*/
void iso_volume_set_data_preparer_id(struct iso_volume *volume,
const char *data_preparer_id);
/**
* Locate a node by its path on disc.
*
* \param volume The volume to search in.
* \param path The path, in the image, of the file.
*
* \return The node found or NULL.
*
*/
struct iso_tree_node *iso_tree_volume_path_to_node(struct iso_volume *volume, const char *path);
/**
* Add a file or a directory (recursively) to a volume by specifying its path on the volume.
*
* \param volume The volume to add the file to.
* \param disc_path The path on the disc at which to add the disc.
* \param path The path, on the local filesystem, of the file.
*
* \return The node for the file or NULL if the parent doesn't exists on the disc.
*/
struct iso_tree_node *iso_tree_volume_add_path(struct iso_volume *volume,
const char *disc_path,
const char *path);
/**
* Creates a new, empty directory on the volume.
*
* \param volume The volume to add the directory to.
* \param disc_path The path on the volume at which to add the directory.
*
* \return A pointer to the newly created directory.
*/
struct iso_tree_node *iso_tree_volume_add_new_dir(struct iso_volume *volume,
const char *disc_path);
/**
* Create a new Volume Set consisting of only one volume.
* @param volume The first and only volume for the volset to contain.
* @param volset_id The Volume Set ID.
* @return A new iso_volset.
*/
struct iso_volset *iso_volset_new(struct iso_volume *volume,
const char *volset_id);
/**
* Add a file to a directory.
*
* \param path The path, on the local filesystem, of the file.
*
* \pre \p parent is NULL or is a directory.
* \pre \p path is non-NULL and is a valid path to a non-directory on the local
* filesystem.
* \return An iso_tree_node whose path is \p path and whose parent is \p parent.
*/
struct iso_tree_node *iso_tree_add_node(struct iso_tree_node *parent,
const char *path);
/**
* Recursively add an existing directory to the tree.
* Warning: when using this, you'll lose pointers to files or subdirectories.
* If you want to have pointers to all files and directories,
* use iso_tree_add_file and iso_tree_add_dir.
*
* \param path The path, on the local filesystem, of the directory to add.
*
* \pre \p parent is NULL or is a directory.
* \pre \p path is non-NULL and is a valid path to a directory on the local
* filesystem.
* \return a pointer to the newly created directory.
*/
struct iso_tree_node *iso_tree_radd_dir(struct iso_tree_node *parent,
const char *path);
/**
* Add the path of a file or directory to ignore when adding a directory recursively.
*
* \param path The path, on the local filesystem, of the file.
*/
void iso_exclude_add_path(const char *path);
/**
* Remove a path that was set to be ignored when adding a directory recusively.
*
* \param path The path, on the local filesystem, of the file.
*/
void iso_exclude_remove_path(const char *path);
/**
* Remove all paths that were set to be ignored when adding a directory recusively.
*/
void iso_exclude_empty(void);
/**
* Creates a new, empty directory on the volume.
*
* \pre \p parent is NULL or is a directory.
* \pre \p name is unique among the children and files belonging to \p parent.
* Also, it doesn't contain '/' characters.
*
* \post \p parent contains a child directory whose name is \p name and whose
* POSIX attributes are the same as \p parent's.
* \return a pointer to the newly created directory.
*/
struct iso_tree_node *iso_tree_add_new_dir(struct iso_tree_node *parent,
const char *name);
/**
* Set the name of a file (using the current locale).
*/
void iso_tree_node_set_name(struct iso_tree_node *file, const char *name);
/**
* Recursively print a directory to stdout.
* \param spaces The initial number of spaces on the left. Set to 0 if you
* supply a root directory.
*/
void iso_tree_print(const struct iso_tree_node *root, int spaces);
/** Create a burn_source which can be used as a data source for a track
*
* The volume set used to create the libburn_source can _not_ be modified
* until the libburn_source is freed.
*
* \param volumeset The volume set from which you want to write
* \param volnum The volume in the set which you want to write (usually 0)
* \param level ISO level to write at.
* \param flags Which extensions to support.
*
* \pre \p volumeset is non-NULL
* \pre \p volnum is less than \p volset->volset_size.
* \return A burn_source to be used for the data source for a track
*/
struct burn_source* iso_source_new_ecma119 (struct iso_volset *volumeset,
int volnum,
int level,
int flags);
#endif /* LIBISO_LIBISOFS_H */

View File

@ -1,300 +0,0 @@
/* vim: set noet ts=8 sts=8 sw=8 : */
#include "rockridge.h"
#include "util.h"
#include "ecma119.h"
#include "ecma119_tree.h"
#include "tree.h"
#include "susp.h"
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <errno.h>
#include <unistd.h>
#include <sys/stat.h>
/* create a PX field from the permissions on the current node. */
uint8_t *rrip_make_PX(struct ecma119_write_target *t,
struct ecma119_tree_node *node)
{
uint8_t *PX = malloc(44);
PX[0] = 'P';
PX[1] = 'X';
PX[2] = 44;
PX[3] = 1;
iso_bb(&PX[4], node->iso_self->attrib.st_mode, 4);
iso_bb(&PX[12], node->iso_self->attrib.st_nlink, 4);
iso_bb(&PX[20], node->iso_self->attrib.st_uid, 4);
iso_bb(&PX[28], node->iso_self->attrib.st_gid, 4);
iso_bb(&PX[36], node->iso_self->attrib.st_ino, 4);
return PX;
}
/** See IEEE 1282 4.1.1 */
void rrip_add_PX(struct ecma119_write_target *t, struct ecma119_tree_node *node)
{
susp_append(t, &node->susp, rrip_make_PX(t, node));
if (node->type == ECMA119_DIR) {
susp_append(t, &node->dir.self_susp, rrip_make_PX(t, node));
susp_append(t, &node->dir.parent_susp, rrip_make_PX(t, node));
}
}
void rrip_add_PN(struct ecma119_write_target *t, struct ecma119_tree_node *node)
{
uint8_t *PN = malloc(20);
PN[0] = 'P';
PN[1] = 'N';
PN[2] = 20;
PN[3] = 1;
iso_bb(&PN[4], node->iso_self->attrib.st_dev >> 32, 4);
iso_bb(&PN[12], node->iso_self->attrib.st_dev & 0xffffffff, 4);
susp_append(t, &node->susp, PN);
}
static void rrip_SL_append_comp(int *n, uint8_t ***comps,
char *s, int size, char fl)
{
uint8_t *comp = malloc(size + 2);
(*n)++;
comp[0] = fl;
comp[1] = size;
*comps = realloc(*comps, (*n) * sizeof(void*));
(*comps)[(*n) - 1] = comp;
if (size) {
memcpy(&comp[2], s, size);
}
}
static void rrip_SL_add_component(char *prev, char *cur, int *n_comp,
uint8_t ***comps)
{
int size = cur - prev;
if (size == 0) {
rrip_SL_append_comp(n_comp, comps, prev, 0, 1 << 3);
return;
}
if (size == 1 && prev[0] == '.') {
rrip_SL_append_comp(n_comp, comps, prev, 0, 1 << 1);
return;
}
if (size == 2 && !strncmp(prev, "..", 2)) {
rrip_SL_append_comp(n_comp, comps, prev, 0, 1 << 2);
return;
}
/* we can't make a component any bigger than 250 (is this really a
problem)? because then it won't fit inside the SL field */
while (size > 248) {
size -= 248;
rrip_SL_append_comp(n_comp, comps, prev, 248, 1 << 0);
}
rrip_SL_append_comp(n_comp, comps, prev, size, 0);
}
void rrip_add_SL(struct ecma119_write_target *t, struct ecma119_tree_node *node)
{
int ret, pathsize = 0;
char *path = NULL, *cur, *prev;
int i, j;
uint8_t **comp = NULL;
int n_comp = 0;
int total_comp_len = 0;
int written = 0, pos;
uint8_t *SL;
do {
pathsize += 128;
path = realloc(path, pathsize);
/* FIXME: what if the file is not on the local fs? */
ret = readlink(node->iso_self->loc.path, path, pathsize);
} while (ret == pathsize);
if (ret == -1) {
fprintf(stderr, "Error: couldn't read symlink: %s\n",
strerror(errno));
return;
}
path[ret] = '\0';
prev = path;
for (cur = strchr(path, '/'); cur && *cur; cur = strchr(cur, '/')) {
rrip_SL_add_component(prev, cur, &n_comp, &comp);
cur++;
prev = cur;
}
/* if there was no trailing '/', we need to add the last component. */
if (prev == path || prev != &path[ret - 1]) {
rrip_SL_add_component(prev, &path[ret], &n_comp, &comp);
}
for (i = 0; i < n_comp; i++) {
total_comp_len += comp[i][1] + 2;
if (total_comp_len > 250) {
total_comp_len -= comp[i][1] + 2;
SL = malloc(total_comp_len + 5);
SL[0] = 'S';
SL[1] = 'L';
SL[2] = total_comp_len + 5;
SL[3] = 1;
SL[4] = 1; /* CONTINUE */
pos = 5;
for (j = written; j < i; j++) {
memcpy(&SL[pos], comp[j], comp[j][2]);
pos += comp[j][2];
}
susp_append(t, &node->susp, SL);
written = i - 1;
total_comp_len = comp[i][1];
}
}
SL = malloc(total_comp_len + 5);
SL[0] = 'S';
SL[1] = 'L';
SL[2] = total_comp_len + 5;
SL[3] = 1;
SL[4] = 0;
pos = 5;
for (j = written; j < n_comp; j++) {
memcpy(&SL[pos], comp[j], comp[j][1] + 2);
pos += comp[j][1] + 2;
}
susp_append(t, &node->susp, SL);
free(path);
/* free the components */
for (i = 0; i < n_comp; i++) {
free(comp[i]);
}
free(comp);
}
static void rrip_add_NM_single(struct ecma119_write_target *t,
struct susp_info *susp,
char *name, int size, int flags)
{
uint8_t *NM = malloc(size + 5);
NM[0] = 'N';
NM[1] = 'M';
NM[2] = size + 5;
NM[3] = 1;
NM[4] = flags;
if (size) {
memcpy(&NM[5], name, size);
}
susp_append(t, susp, NM);
}
void
rrip_add_NM(struct ecma119_write_target *t, struct ecma119_tree_node *node)
{
char *name = iso_p_fileid(node->iso_self->name);
int len = name ? strlen(name) : 0;
char *pos = name;
if (!len)
return;
if (node->type == ECMA119_DIR) {
rrip_add_NM_single(t, &node->dir.self_susp, pos, 0, 1 << 1);
rrip_add_NM_single(t, &node->dir.parent_susp, pos, 0, 1 << 2);
}
while (len > 250) {
rrip_add_NM_single(t, &node->susp, pos, 250, 1);
len -= 250;
pos += 250;
}
rrip_add_NM_single(t, &node->susp, pos, len, 0);
}
void rrip_add_CL(struct ecma119_write_target *t, struct ecma119_tree_node *node)
{
uint8_t *CL = calloc(1, 12);
CL[0] = 'C';
CL[1] = 'L';
CL[2] = 12;
CL[3] = 1;
susp_append(t, &node->susp, CL);
}
void
rrip_add_PL(struct ecma119_write_target *t, struct ecma119_tree_node *node)
{
uint8_t *PL = calloc(1, 12);
PL[0] = 'P';
PL[1] = 'L';
PL[2] = 12;
PL[3] = 1;
susp_append(t, &node->dir.parent_susp, PL);
}
void
rrip_add_RE(struct ecma119_write_target *t, struct ecma119_tree_node *node)
{
uint8_t *RE = malloc(4);
RE[0] = 'R';
RE[1] = 'E';
RE[2] = 4;
RE[3] = 1;
susp_append(t, &node->susp, RE);
}
void
rrip_add_TF(struct ecma119_write_target *t, struct ecma119_tree_node *node)
{
uint8_t *TF = malloc(5 + 3 * 7);
TF[0] = 'T';
TF[1] = 'F';
TF[2] = 5 + 3 * 7;
TF[3] = 1;
TF[4] = (1 << 1) | (1 << 2) | (1 << 3) | (1 << 7);
iso_datetime_7(&TF[5], node->iso_self->attrib.st_mtime);
iso_datetime_7(&TF[12], node->iso_self->attrib.st_atime);
iso_datetime_7(&TF[19], node->iso_self->attrib.st_ctime);
susp_append(t, &node->susp, TF);
}
void
rrip_finalize(struct ecma119_write_target *t, struct ecma119_tree_node *dir)
{
int i;
assert(dir->type == ECMA119_DIR);
if (dir->parent != dir->dir.real_parent) {
uint8_t *PL = susp_find(&dir->dir.parent_susp, "PL");
assert(PL);
iso_bb(&PL[4], dir->dir.real_parent->block, 4);
}
for (i = 0; i < dir->dir.nchildren; i++) {
struct ecma119_tree_node *ch = dir->dir.children[i];
if (ch->type == ECMA119_FILE && ch->file.real_me) {
uint8_t *CL = susp_find(&ch->susp, "CL");
assert(CL);
iso_bb(&CL[4], ch->file.real_me->block, 4);
} else if (ch->type == ECMA119_DIR) {
rrip_finalize(t, ch);
}
}
}

View File

@ -1,26 +0,0 @@
/* vim: set noet ts=8 sts=8 sw=8 : */
/** Functions and structures used for Rock Ridge support. */
#ifndef ISO_ROCKRIDGE_H
#define ISO_ROCKRIDGE_H
struct ecma119_write_target;
struct ecma119_tree_node;
void rrip_add_PX(struct ecma119_write_target *, struct ecma119_tree_node *);
void rrip_add_PN(struct ecma119_write_target *, struct ecma119_tree_node *);
void rrip_add_SL(struct ecma119_write_target *, struct ecma119_tree_node *);
void rrip_add_NM(struct ecma119_write_target *, struct ecma119_tree_node *);
void rrip_add_CL(struct ecma119_write_target *, struct ecma119_tree_node *);
void rrip_add_RE(struct ecma119_write_target *, struct ecma119_tree_node *);
void rrip_add_TF(struct ecma119_write_target *, struct ecma119_tree_node *);
/* This is special because it doesn't modify the susp fields of the directory
* that gets passed to it; it modifies the susp fields of the ".." entry in
* that directory. */
void rrip_add_PL(struct ecma119_write_target *, struct ecma119_tree_node *);
void rrip_finalize(struct ecma119_write_target *, struct ecma119_tree_node *);
#endif /* ISO_ROCKRIDGE_H */

View File

@ -1,280 +0,0 @@
/* vim: set noet ts=8 sts=8 sw=8 : */
#include "susp.h"
#include "util.h"
#include "ecma119.h"
#include "ecma119_tree.h"
#include <stdlib.h>
#include <assert.h>
#include <string.h>
void susp_insert(struct ecma119_write_target *t,
struct susp_info *susp,
uint8_t *data,
int pos)
{
int i;
if (pos < 0) {
pos = susp->n_susp_fields;
}
assert(pos <= susp->n_susp_fields);
susp->n_susp_fields++;
susp->susp_fields = realloc(susp->susp_fields,
sizeof(void*) * susp->n_susp_fields);
for (i = susp->n_susp_fields-1; i > pos; i--) {
susp->susp_fields[i] = susp->susp_fields[i - 1];
}
susp->susp_fields[pos] = data;
}
void susp_append(struct ecma119_write_target *t,
struct susp_info *susp,
uint8_t *data)
{
susp_insert(t, susp, data, susp->n_susp_fields);
}
uint8_t *susp_find(struct susp_info *susp, const char *name)
{
int i;
for (i = 0; i < susp->n_susp_fields; i++) {
if (!strncmp((char *)susp->susp_fields[i], name, 2)) {
return susp->susp_fields[i];
}
}
return NULL;
}
/** Utility function for susp_add_CE because susp_add_CE needs to act 3 times
* on directories (for the "." and ".." entries.
*
* \param len The amount of space available for the System Use area.
*/
#define CE_LEN 28
static unsigned char *susp_add_single_CE(struct ecma119_write_target *t,
struct susp_info *susp,
int len)
{
int susp_length = 0, tmp_len;
int i;
unsigned char *CE;
for (i = 0; i < susp->n_susp_fields; i++) {
susp_length += susp->susp_fields[i][2];
}
if (susp_length <= len) {
/* no need for a CE field */
susp->non_CE_len = susp_length;
susp->n_fields_fit = susp->n_susp_fields;
return NULL;
}
tmp_len = susp_length;
for (i = susp->n_susp_fields - 1; i >= 0; i--) {
tmp_len -= susp->susp_fields[i][2];
if (tmp_len + CE_LEN <= len) {
susp->non_CE_len = tmp_len + CE_LEN;
susp->CE_len = susp_length - tmp_len;
/* i+1 because we have to count the CE field */
susp->n_fields_fit = i + 1;
CE = calloc(1, CE_LEN);
/* we don't fill in the BLOCK LOCATION or OFFSET
fields yet. */
CE[0] = 'C';
CE[1] = 'E';
CE[2] = (char)CE_LEN;
CE[3] = (char)1;
iso_bb(&CE[20], susp_length - tmp_len, 4);
return CE;
}
}
assert(0);
return NULL;
}
static void
try_add_CE(struct ecma119_write_target *t,
struct susp_info *susp,
size_t dirent_len)
{
uint8_t *CE = susp_add_single_CE(t, susp, 255 - dirent_len);
if (CE)
susp_insert(t, susp, CE, susp->n_fields_fit - 1);
}
/** See IEEE P1281 Draft Version 1.12/5.2. Because this function depends on the
* length of the other SUSP fields, it should always be calculated last. */
void
susp_add_CE(struct ecma119_write_target *t, struct ecma119_tree_node *node)
{
try_add_CE(t, &node->susp, node->dirent_len);
if (node->type == ECMA119_DIR) {
try_add_CE(t, &node->dir.self_susp, 34);
try_add_CE(t, &node->dir.parent_susp, 34);
}
}
/** See IEEE P1281 Draft Version 1.12/5.3 */
void
susp_add_SP(struct ecma119_write_target *t, struct ecma119_tree_node *dir)
{
unsigned char *SP = malloc(7);
assert(dir->type == ECMA119_DIR);
SP[0] = 'S';
SP[1] = 'P';
SP[2] = (char)7;
SP[3] = (char)1;
SP[4] = 0xbe;
SP[5] = 0xef;
SP[6] = 0;
susp_append(t, &dir->dir.self_susp, SP);
}
#if 0
/** See IEEE P1281 Draft Version 1.12/5.4 */
static void susp_add_ST(struct ecma119_write_target *t,
struct iso_tree_node *node)
{
unsigned char *ST = malloc(4);
ST[0] = 'S';
ST[1] = 'T';
ST[2] = (char)4;
ST[3] = (char)1;
susp_append(t, node, ST);
}
#endif
/** See IEEE P1281 Draft Version 1.12/5.5 FIXME: this is rockridge */
void
rrip_add_ER(struct ecma119_write_target *t, struct ecma119_tree_node *dir)
{
unsigned char *ER = malloc(182);
assert(dir->type == ECMA119_DIR);
ER[0] = 'E';
ER[1] = 'R';
ER[2] = 182;
ER[3] = 1;
ER[4] = 9;
ER[5] = 72;
ER[6] = 93;
ER[7] = 1;
memcpy(&ER[8], "IEEE_1282", 9);
memcpy(&ER[17], "THE IEEE 1282 PROTOCOL PROVIDES SUPPORT FOR POSIX "
"FILE SYSTEM SEMANTICS.", 72);
memcpy(&ER[89], "PLEASE CONTACT THE IEEE STANDARDS DEPARTMENT, "
"PISCATAWAY, NJ, USA FOR THE 1282 SPECIFICATION.", 93);
susp_append(t, &dir->dir.self_susp, ER);
}
/* calculate the location of the CE areas. Since CE areas don't need to be
* aligned to a block boundary, we contatenate all CE areas from a single
* directory and dump them immediately after all the directory records.
*
* Requires that the following be known:
* - position of the current directory (dir->block)
* - length of the current directory (dir->dir.len)
* - sum of the children's CE lengths (dir->dir.CE_len)
*/
static void
susp_fin_1_CE(struct ecma119_write_target *t,
struct susp_info *susp,
size_t block,
size_t *offset)
{
uint8_t *CE = susp->susp_fields[susp->n_fields_fit - 1];
if (!susp->CE_len) {
return;
}
iso_bb(&CE[4], block + (*offset) / t->block_size, 4);
iso_bb(&CE[12], (*offset) % t->block_size, 4);
*offset += susp->CE_len;
}
static void susp_fin_CE(struct ecma119_write_target *t,
struct ecma119_tree_node *dir)
{
int i;
size_t CE_offset = dir->dir.len;
assert(dir->type == ECMA119_DIR);
susp_fin_1_CE(t, &dir->dir.self_susp, dir->block, &CE_offset);
susp_fin_1_CE(t, &dir->dir.parent_susp, dir->block, &CE_offset);
for (i = 0; i < dir->dir.nchildren; i++) {
struct ecma119_tree_node *ch = dir->dir.children[i];
susp_fin_1_CE(t, &ch->susp, dir->block, &CE_offset);
}
assert(CE_offset == dir->dir.len + dir->dir.CE_len);
}
void
susp_finalize(struct ecma119_write_target *t, struct ecma119_tree_node *dir)
{
int i;
assert(dir->type = ECMA119_DIR);
if (dir->dir.depth != 1) {
susp_fin_CE(t, dir);
}
for (i = 0; i < dir->dir.nchildren; i++) {
if (dir->dir.children[i]->type == ECMA119_DIR)
susp_finalize(t, dir->dir.children[i]);
}
}
void susp_write(struct ecma119_write_target *t,
struct susp_info *susp,
unsigned char *buf)
{
int i;
int pos = 0;
for (i = 0; i < susp->n_fields_fit; i++) {
memcpy(&buf[pos], susp->susp_fields[i],
susp->susp_fields[i][2]);
pos += susp->susp_fields[i][2];
}
}
void susp_write_CE(struct ecma119_write_target *t, struct susp_info *susp,
unsigned char *buf)
{
int i;
int pos = 0;
for (i = susp->n_fields_fit; i < susp->n_susp_fields; i++) {
memcpy(&buf[pos], susp->susp_fields[i],
susp->susp_fields[i][2]);
pos += susp->susp_fields[i][2];
}
}
void susp_free_fields(struct susp_info *susp)
{
int i;
for (i=0; i<susp->n_susp_fields; i++) {
free(susp->susp_fields[i]);
}
if (susp->susp_fields) {
free(susp->susp_fields);
}
memset(susp, 0, sizeof(struct susp_info));
}

View File

@ -1,62 +0,0 @@
/* vim: set noet ts=8 sts=8 sw=8 : */
/** Functions and structures used for SUSP (IEEE 1281).
*/
#ifndef __ISO_SUSP
#define __ISO_SUSP
#include <stdint.h>
/* SUSP is only present in standard ecma119 */
struct ecma119_write_target;
struct ecma119_tree_node;
/** This contains the information that needs to go in the SUSP area of a file.
*/
struct susp_info
{
int n_susp_fields; /**< Number of SUSP fields */
uint8_t **susp_fields; /**< Data for each SUSP field */
/* the next 3 relate to CE and are filled out by susp_add_CE. */
int n_fields_fit; /**< How many of the above SUSP fields fit
* within this node's dirent. */
int non_CE_len; /**< Length of the part of the SUSP area that
* fits in the dirent. */
int CE_len; /**< Length of the part of the SUSP area that
* will go in a CE area. */
};
void susp_add_CE(struct ecma119_write_target *, struct ecma119_tree_node *);
/* these next 2 are special because they don't modify the susp fields of the
* directory; they modify the susp fields of the
* "." entry in the directory. */
void susp_add_SP(struct ecma119_write_target *, struct ecma119_tree_node *);
void rrip_add_ER(struct ecma119_write_target *, struct ecma119_tree_node *);
/** Once all the directories and files are laid out, recurse through the tree
* and finalize all SUSP CE entries. */
void susp_finalize(struct ecma119_write_target *, struct ecma119_tree_node *);
void susp_append(struct ecma119_write_target *,
struct susp_info *,
uint8_t *);
void susp_insert(struct ecma119_write_target *,
struct susp_info *,
uint8_t *,
int pos);
uint8_t *susp_find(struct susp_info *,
const char *);
void susp_write(struct ecma119_write_target *,
struct susp_info *,
uint8_t *);
void susp_write_CE(struct ecma119_write_target *,
struct susp_info *,
uint8_t *);
void susp_free_fields(struct susp_info *);
#endif /* __ISO_SUSP */

View File

@ -1,223 +0,0 @@
/* vim: set noet ts=8 sts=8 sw=8 : */
/**
* \file tree.c
*
* Implement filesystem trees.
*/
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <dirent.h>
#include <libgen.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <errno.h>
#include <err.h>
#include <stdio.h>
#include "tree.h"
#include "util.h"
#include "volume.h"
#include "exclude.h"
static void
set_default_stat(struct stat *s)
{
time_t now = time(NULL);
memset(s, 0, sizeof(struct stat));
s->st_mode = 0777 | S_IFREG;
s->st_atime = s->st_mtime = s->st_ctime = now;
}
static struct stat
get_attrib(const struct iso_tree_node *node)
{
struct stat st;
if (node) {
return node->attrib;
}
set_default_stat(&st);
return st;
}
static void
append_node(struct iso_tree_node *parent,
struct iso_tree_node *child)
{
assert((!parent || S_ISDIR(parent->attrib.st_mode)) && child);
if (!parent)
return;
parent->nchildren++;
parent->children =
realloc(parent->children, parent->nchildren * sizeof(void*));
parent->children[parent->nchildren-1] = child;
}
struct iso_tree_node*
iso_tree_new_root(struct iso_volume *vol)
{
assert(vol);
if (vol->root) {
iso_tree_free(vol->root);
}
vol->root = calloc(1, sizeof(struct iso_tree_node));
vol->root->volume = vol;
set_default_stat(&vol->root->attrib);
vol->root->attrib.st_mode = S_IFDIR | 0777;
vol->root->loc.type = LIBISO_NONE;
return vol->root;
}
struct iso_tree_node*
iso_tree_add_new_file(struct iso_tree_node *parent, const char *name)
{
struct iso_tree_node *f = calloc(1, sizeof(struct iso_tree_node));
assert((!parent || S_ISDIR(parent->attrib.st_mode)) && name);
f->volume = parent ? parent->volume : NULL;
f->parent = parent;
f->name = parent ? strdup(name) : NULL;
f->attrib = get_attrib(parent);
f->attrib.st_mode = 0777 | S_IFREG;
f->loc.type = LIBISO_NONE;
append_node(parent, f);
return f;
}
struct iso_tree_node*
iso_tree_add_new_dir(struct iso_tree_node *parent, const char *name)
{
struct iso_tree_node *d = iso_tree_add_new_file(parent, name);
assert((!parent || S_ISDIR(parent->attrib.st_mode)) && name);
d->attrib.st_mode = (d->attrib.st_mode & ~S_IFMT) | S_IFDIR;
return d;
}
struct iso_tree_node*
iso_tree_add_node(struct iso_tree_node *parent, const char *path)
{
char *p;
struct stat st;
struct iso_tree_node *ret;
assert((!parent || S_ISDIR(parent->attrib.st_mode)) && path);
if (lstat(path, &st) == -1)
return NULL;
p = strdup(path); /* because basename() might modify its arg */
/* it doesn't matter if we add a file or directory since we modify
* attrib anyway. */
ret = iso_tree_add_new_file(parent, basename(p));
ret->attrib = st;
ret->loc.type = LIBISO_FILESYS;
ret->loc.path = strdup(path);
free(p);
return ret;
}
struct iso_tree_node*
iso_tree_radd_dir (struct iso_tree_node *parent, const char *path)
{
struct iso_tree_node *new;
DIR *dir;
struct dirent *ent;
assert((!parent || S_ISDIR(parent->attrib.st_mode)) && path);
new = iso_tree_add_node(parent, path);
if (!new || !S_ISDIR(new->attrib.st_mode)) {
return new;
}
dir = opendir(path);
if (!dir) {
warn("couldn't open directory %s: %s\n", path, strerror(errno));
return new;
}
while ((ent = readdir(dir))) {
char child[strlen(ent->d_name) + strlen(path) + 2];
if (strcmp(ent->d_name, ".") == 0 ||
strcmp(ent->d_name, "..") == 0)
continue;
sprintf(child, "%s/%s", path, ent->d_name);
/* see if this child is excluded. */
if (iso_exclude_lookup(child))
continue;
iso_tree_radd_dir(new, child);
}
closedir(dir);
return new;
}
void
iso_tree_free(struct iso_tree_node *root)
{
size_t i;
for (i=0; i < root->nchildren; i++) {
iso_tree_free(root->children[i]);
}
free(root->name);
free(root->children);
free(root);
}
void
iso_tree_print(const struct iso_tree_node *root, int spaces)
{
size_t i;
char sp[spaces+1];
memset(sp, ' ', spaces);
sp[spaces] = '\0';
printf("%s%sn", sp, root->name);
for (i=0; i < root->nchildren; i++) {
iso_tree_print(root->children[i], spaces+2);
}
}
void
iso_tree_print_verbose(const struct iso_tree_node *root,
print_dir_callback dir,
print_file_callback file,
void *callback_data,
int spaces)
{
size_t i;
(S_ISDIR(root->attrib.st_mode) ? dir : file)
(root, callback_data, spaces);
for (i=0; i < root->nchildren; i++) {
iso_tree_print_verbose(root->children[i], dir,
file, callback_data, spaces+2);
}
}
void
iso_tree_node_set_name(struct iso_tree_node *file, const char *name)
{
free(file->name);
file->name = strdup(name);
}

View File

@ -1,159 +0,0 @@
/* vim: set noet ts=8 sts=8 sw=8 : */
/**
* \file tree.h
*
* Declare the structure of a libisofs filesystem tree. The files in this
* tree can come from either the local filesystem or from another .iso image
* (for multisession).
*
* This tree preserves as much information as it can about the files; names
* are stored in wchar_t and we preserve POSIX attributes. This tree does
* *not* include information that is necessary for writing out, for example,
* an ISO level 1 tree. That information will go in a different tree because
* the structure is sufficiently different.
*/
#ifndef LIBISO_TREE_H
#define LIBISO_TREE_H
#include <sys/types.h>
#include <sys/stat.h>
#include <stdint.h>
#include <wchar.h>
#include "libisofs.h"
enum file_location {
LIBISO_FILESYS,
LIBISO_PREVSESSION,
LIBISO_NONE /**< for files/dirs that were added with
* iso_tree_add_new_XXX. */
};
/**
* This tells us where to read the data from a file. Either we read from the
* local filesystem or we just point to the block on a previous session.
*/
struct iso_file_location
{
enum file_location type;
/* union {*/
char *path; /* in the current locale */
uint32_t block;
/* };*/
};
/**
* A node in the filesystem tree.
*/
struct iso_tree_node
{
struct iso_volume *volume;
struct iso_tree_node *parent;
char *name;
struct stat attrib; /**< The POSIX attributes of this node as
* documented in "man 2 stat". */
struct iso_file_location loc;
/**< Only used for regular files and symbolic
* links (ie. files for which we might have to
* copy data). */
size_t nchildren; /**< The number of children of this
* directory (if this is a directory). */
struct iso_tree_node **children;
size_t block; /**< The block at which this file will
* reside on disk. We store this here as
* well as in the various mangled trees
* because many different trees might point
* to the same file and they need to share the
* block location. */
};
/**
* Create a new root directory for a volume.
*
* \param vol The volume for which to create a new root directory.
*
* \pre \p vol is non-NULL.
* \post \p vol has a non-NULL, empty root directory with permissions 777.
* \return \p vol's new non-NULL, empty root directory.
*/
struct iso_tree_node *iso_tree_new_root(struct iso_volume *vol);
/**
* Create a new, empty, file.
*
* \param parent The parent directory of the new file. If this is null, create
* and return a new file node without adding it to any tree.
* \param name The name of the new file, encoded in the current locale.
* \pre \p name is non-NULL and it does not match any other file or directory
* name in \p parent.
* \post \p parent (if non-NULL) contains a file with the following properties:
* - the file's name is \p name (converted to wchar_t)
* - the file's POSIX permissions are the same as \p parent's
* - the file is a regular file
* - the file is empty
*
* \return \p parent's newly created file.
*/
struct iso_tree_node *iso_tree_add_new_file(struct iso_tree_node *parent,
const char *name);
/**
* Recursively free a directory.
*
* \param root The root of the directory heirarchy to free.
*
* \pre \p root is non-NULL.
*/
void iso_tree_free(struct iso_tree_node *root);
/**
* A function that prints verbose information about a directory.
*
* \param dir The directory about which to print information.
* \param data Unspecified function-dependent data.
* \param spaces The number of spaces to prepend to the output.
*
* \see iso_tree_print_verbose
*/
typedef void (*print_dir_callback) (const struct iso_tree_node *dir,
void *data,
int spaces);
/**
* A function that prints verbose information about a file.
*
* \param dir The file about which to print information.
* \param data Unspecified function-dependent data.
* \param spaces The number of spaces to prepend to the output.
*
* \see iso_tree_print_verbose
*/
typedef void (*print_file_callback) (const struct iso_tree_node *file,
void *data,
int spaces);
/**
* Recursively print a directory heirarchy. For each node in the directory
* heirarchy, call a callback function to print information more verbosely.
*
* \param root The root of the directory heirarchy to print.
* \param dir The callback function to call for each directory in the tree.
* \param file The callback function to call for each file in the tree.
* \param callback_data The data to pass to the callback functions.
* \param spaces The number of spaces to prepend to the output.
*
* \pre \p root is not NULL.
* \pre Neither of the callback functions modifies the directory heirarchy.
*/
void iso_tree_print_verbose(const struct iso_tree_node *root,
print_dir_callback dir,
print_file_callback file,
void *callback_data,
int spaces);
#define ISO_ISDIR(n) S_ISDIR(n->attrib.st_mode)
#endif /* LIBISO_TREE_H */

View File

@ -1,584 +0,0 @@
/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */
/* vim: set noet ts=8 sts=8 sw=8 : */
/**
* Utility functions for the Libisofs library.
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <wchar.h>
#include <iconv.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <assert.h>
#include <errno.h>
#include <locale.h>
#include "util.h"
/* avoids warning and names in iso, joliet and rockridge can't be > 255 bytes
* anyway. There are at most 31 characters in iso level 1, 255 for rockridge,
* 64 characters (* 2 since UCS) for joliet. */
#define NAME_BUFFER_SIZE 255
int div_up(int n, int div)
{
return (n + div - 1) / div;
}
int round_up(int n, int mul)
{
return div_up(n, mul) * mul;
}
/* this function must always return a name
* since the caller never checks if a NULL
* is returned. It also avoids some warnings. */
char *str2ascii(const char *src_arg)
{
wchar_t wsrc_[NAME_BUFFER_SIZE];
char *src = (char*)wsrc_;
char *ret_;
char *ret;
mbstate_t state;
iconv_t conv;
size_t numchars;
size_t outbytes;
size_t inbytes;
size_t n;
if (!src_arg)
return NULL;
/* convert the string to a wide character string. Note: outbytes
* is in fact the number of characters in the string and doesn't
* include the last NULL character. */
memset(&state, 0, sizeof(state));
numchars = mbsrtowcs(wsrc_, &src_arg, NAME_BUFFER_SIZE-1, &state);
if (numchars < 0)
return NULL;
inbytes = numchars * sizeof(wchar_t);
ret_ = malloc(numchars+1);
outbytes = numchars;
ret = ret_;
/* initialize iconv */
conv = iconv_open("ASCII", "WCHAR_T");
if (conv == (iconv_t)-1)
return NULL;
n = iconv(conv, &src, &inbytes, &ret, &outbytes);
while(n == -1) {
/* The destination buffer is too small. Stops here. */
if(errno == E2BIG)
break;
/* An incomplete multi bytes sequence was found. We
* can't do anything here. That's quite unlikely. */
if(errno == EINVAL)
break;
/* The last possible error is an invalid multi bytes
* sequence. Just replace the character with a "_".
* Probably the character doesn't exist in ascii like
* "é, è, à, ç, ..." in French. */
*ret++ = '_';
outbytes--;
if(!outbytes)
break;
/* There was an error with one character but some other remain
* to be converted. That's probably a multibyte character.
* See above comment. */
src += sizeof(wchar_t);
inbytes -= sizeof(wchar_t);
if(!inbytes)
break;
n = iconv(conv, &src, &inbytes, &ret, &outbytes);
}
iconv_close(conv);
*ret='\0';
return ret_;
}
/* FIXME: C&P */
uint16_t *str2ucs(const char *src_arg)
{
wchar_t wsrc_[NAME_BUFFER_SIZE];
char *src = (char*)wsrc_;
char *ret_;
char *ret;
mbstate_t state;
iconv_t conv;
size_t outbytes;
size_t numchars;
size_t inbytes;
size_t n;
if (!src_arg)
return calloc(2, 1); /* empty UCS string */
/* convert the string to a wide character string. Note: outbytes
* is in fact the number of characters in the string and doesn't
* include the last NULL character. */
memset(&state, 0, sizeof(state));
numchars = mbsrtowcs(wsrc_, &src_arg, NAME_BUFFER_SIZE-1, &state);
if (numchars < 0)
return calloc(2, 1); /* empty UCS string */
inbytes = numchars * sizeof(wchar_t);
outbytes = numchars * sizeof(uint16_t);
ret_ = malloc ((numchars+1) * sizeof(uint16_t));
ret = ret_;
/* initialize iconv */
conv = iconv_open("UCS-2BE", "WCHAR_T");
if (conv == (iconv_t)-1)
return calloc(2, 1); /* empty UCS string */
n = iconv(conv, &src, &inbytes, &ret, &outbytes);
while(n == -1) {
/* The destination buffer is too small. Stops here. */
if(errno == E2BIG)
break;
/* An incomplete multi bytes sequence was found. We
* can't do anything here. That's quite unlikely. */
if(errno == EINVAL)
break;
/* The last possible error is an invalid multi bytes
* sequence. Just replace the character with a "_".
* Probably the character doesn't exist in ascii like
* "é, è, à, ç, ..." in French. */
*((uint16_t*) ret) = '_';
ret += sizeof(uint16_t);
outbytes -= sizeof(uint16_t);
if(!outbytes)
break;
/* There was an error with one character but some other remain
* to be converted. That's probably a multibyte character.
* See above comment. */
src += sizeof(wchar_t);
inbytes -= sizeof(wchar_t);
if(!inbytes)
break;
n = iconv(conv, &src, &inbytes, &ret, &outbytes);
}
iconv_close(conv);
/* close the ucs string */
*((uint16_t*) ret) = 0;
return (uint16_t*)ret_;
}
static int valid_d_char(char c)
{
return (c >= '0' && c <= '9') || (c >= 'A' && c <= 'Z') || (c == '_');
}
static int valid_a_char(char c)
{
return (c >= ' ' && c <= '"') || (c >= '%' && c <= '?')
|| (c >= 'A' && c <= 'Z')
|| (c == '_');
}
static int valid_j_char(uint16_t c)
{
return !(c < (uint16_t)' ' || c == (uint16_t)'*' || c == (uint16_t)'/'
|| c == (uint16_t)':' || c == (uint16_t)';'
|| c == (uint16_t)'?' || c == (uint16_t)'\\');
}
/* FIXME: where are these documented? */
static int valid_p_char(char c)
{
return (c >= '0' && c <= '9') || (c >= 'A' && c <= 'Z')
|| (c >= 'a' && c <= 'z')
|| (c == '.') || (c == '_') || (c == '-');
}
static char *iso_dirid(const char *src, int size)
{
char *ret = str2ascii(src);
size_t len, i;
if (!ret)
return NULL;
len = strlen(ret);
if (len > size) {
ret[size] = '\0';
len = size;
}
for (i = 0; i < len; i++) {
char c = toupper(ret[i]);
ret[i] = valid_d_char(c) ? c : '_';
}
return ret;
}
char *iso_1_dirid(const char *src)
{
return iso_dirid(src, 8);
}
char *iso_2_dirid(const char *src)
{
return iso_dirid(src, 31);
}
char *iso_1_fileid(const char *src_arg)
{
char *src = str2ascii(src_arg);
char *dest;
char *dot; /* Position of the last dot in the
filename, will be used to calculate
lname and lext. */
int lname, lext, pos, i;
if (!src)
return NULL;
dest = malloc(15); /* 15 = 8 (name) + 1 (.) + 3 (ext) + 2
(;1) + 1 (\0) */
dot = strrchr(src, '.');
lext = dot ? strlen(dot + 1) : 0;
lname = strlen(src) - lext - (dot ? 1 : 0);
/* If we can't build a filename, return NULL. */
if (lname == 0 && lext == 0) {
free(src);
free(dest);
return NULL;
}
pos = 0;
/* Convert up to 8 characters of the filename. */
for (i = 0; i < lname && i < 8; i++) {
char c = toupper(src[i]);
dest[pos++] = valid_d_char(c) ? c : '_';
}
/* This dot is mandatory, even if there is no extension. */
dest[pos++] = '.';
/* Convert up to 3 characters of the extension, if any. */
for (i = 0; i < lext && i < 3; i++) {
char c = toupper(src[lname + 1 + i]);
dest[pos++] = valid_d_char(c) ? c : '_';
}
/* File versions are mandatory, even if they aren't used. */
dest[pos++] = ';';
dest[pos++] = '1';
dest[pos] = '\0';
dest = (char *)realloc(dest, pos + 1);
free(src);
return dest;
}
char *iso_2_fileid(const char *src_arg)
{
char *src = str2ascii(src_arg);
char *dest;
char *dot;
int lname, lext, lnname, lnext, pos, i;
if (!src)
return NULL;
dest = malloc(34); /* 34 = 30 (name + ext) + 1 (.) + 2
(;1) + 1 (\0) */
dot = strrchr(src, '.');
/* Since the maximum length can be divided freely over the name and
extension, we need to calculate their new lengths (lnname and
lnext). If the original filename is too long, we start by trimming
the extension, but keep a minimum extension length of 3. */
if (dot == NULL || dot == src || *(dot + 1) == '\0') {
lname = strlen(src);
lnname = (lname > 30) ? 30 : lname;
lext = lnext = 0;
} else {
lext = strlen(dot + 1);
lname = strlen(src) - lext - 1;
lnext = (strlen(src) > 31 && lext > 3)
? (lname < 27 ? 30 - lname : 3) : lext;
lnname = (strlen(src) > 31) ? 30 - lnext : lname;
}
if (lnname == 0 && lnext == 0) {
free(src);
free(dest);
return NULL;
}
pos = 0;
/* Convert up to lnname characters of the filename. */
for (i = 0; i < lnname; i++) {
char c = toupper(src[i]);
dest[pos++] = valid_d_char(c) ? c : '_';
}
dest[pos++] = '.';
/* Convert up to lnext characters of the extension, if any. */
for (i = 0; i < lnext; i++) {
char c = toupper(src[lname + 1 + i]);
dest[pos++] = valid_d_char(c) ? c : '_';
}
dest[pos++] = ';';
dest[pos++] = '1';
dest[pos] = '\0';
dest = (char *)realloc(dest, pos + 1);
free(src);
return dest;
}
char *
iso_p_fileid(const char *src)
{
char *ret = str2ascii(src);
size_t i, len;
if (!ret)
return NULL;
len = strlen(ret);
for (i = 0; i < len; i++) {
if (!valid_p_char(ret[i])) {
ret[i] = (uint16_t)'_';
}
}
return ret;
}
uint16_t *
iso_j_id(const char *src_arg)
{
uint16_t *j_str = str2ucs(src_arg);
size_t len = ucslen(j_str);
size_t n;
if (len > 128) {
j_str[128] = '\0';
len = 128;
}
for (n = 0; n < len; n++)
if (!valid_j_char(j_str[n]))
j_str[n] = '_';
return j_str;
}
void iso_lsb(uint8_t *buf, uint32_t num, int bytes)
{
int i;
assert(bytes <= 4);
for (i = 0; i < bytes; ++i)
buf[i] = (num >> (8 * i)) & 0xff;
}
void iso_msb(uint8_t *buf, uint32_t num, int bytes)
{
int i;
assert(bytes <= 4);
for (i = 0; i < bytes; ++i)
buf[bytes - 1 - i] = (num >> (8 * i)) & 0xff;
}
void iso_bb(uint8_t *buf, uint32_t num, int bytes)
{
iso_lsb(buf, num, bytes);
iso_msb(buf+bytes, num, bytes);
}
void iso_datetime_7(unsigned char *buf, time_t t)
{
static int tzsetup = 0;
int tzoffset;
struct tm tm;
if (!tzsetup) {
tzset();
tzsetup = 1;
}
localtime_r(&t, &tm);
buf[0] = tm.tm_year;
buf[1] = tm.tm_mon + 1;
buf[2] = tm.tm_mday;
buf[3] = tm.tm_hour;
buf[4] = tm.tm_min;
buf[5] = tm.tm_sec;
#ifdef HAVE_TM_GMTOFF
tzoffset = -tm.tm_gmtoff / 60 / 15;
#else
tzoffset = -timezone / 60 / 15;
#endif
if (tzoffset < -48)
tzoffset += 101;
buf[6] = tzoffset;
}
time_t iso_datetime_read_7(const uint8_t *buf)
{
struct tm tm;
tm.tm_year = buf[0];
tm.tm_mon = buf[1] + 1;
tm.tm_mday = buf[2];
tm.tm_hour = buf[3];
tm.tm_min = buf[4];
tm.tm_sec = buf[5];
return mktime(&tm) - buf[6] * 60 * 60;
}
void iso_datetime_17(unsigned char *buf, time_t t)
{
static int tzsetup = 0;
static int tzoffset;
struct tm tm;
if (t == (time_t) - 1) {
/* unspecified time */
memset(buf, '0', 16);
buf[16] = 0;
} else {
if (!tzsetup) {
tzset();
tzsetup = 1;
}
localtime_r(&t, &tm);
sprintf((char*)&buf[0], "%04d", tm.tm_year + 1900);
sprintf((char*)&buf[4], "%02d", tm.tm_mon + 1);
sprintf((char*)&buf[6], "%02d", tm.tm_mday);
sprintf((char*)&buf[8], "%02d", tm.tm_hour);
sprintf((char*)&buf[10], "%02d", tm.tm_min);
sprintf((char*)&buf[12], "%02d", MIN(59, tm.tm_sec));
memcpy(&buf[14], "00", 2);
#ifdef HAVE_TM_GMTOFF
tzoffset = -tm.tm_gmtoff / 60 / 15;
#else
tzoffset = -timezone / 60 / 15;
#endif
if (tzoffset < -48)
tzoffset += 101;
buf[16] = tzoffset;
}
}
time_t iso_datetime_read_17(const uint8_t *buf)
{
struct tm tm;
sscanf((char*)&buf[0], "%4d", &tm.tm_year);
sscanf((char*)&buf[4], "%2d", &tm.tm_mon);
sscanf((char*)&buf[6], "%2d", &tm.tm_mday);
sscanf((char*)&buf[8], "%2d", &tm.tm_hour);
sscanf((char*)&buf[10], "%2d", &tm.tm_min);
sscanf((char*)&buf[12], "%2d", &tm.tm_sec);
tm.tm_year -= 1900;
tm.tm_mon -= 1;
return mktime(&tm) - buf[16] * 60 * 60;
}
size_t ucslen(const uint16_t *str)
{
int i;
for (i=0; str[i]; i++)
;
return i;
}
/**
* Although each character is 2 bytes, we actually compare byte-by-byte
* (thats what the spec says).
*/
int ucscmp(const uint16_t *s1, const uint16_t *s2)
{
const char *s = (const char*)s1;
const char *t = (const char*)s2;
size_t len1 = ucslen(s1);
size_t len2 = ucslen(s2);
size_t i, len = MIN(len1, len2) * 2;
for (i=0; i < len; i++) {
if (s[i] < t[i]) {
return -1;
} else if (s[i] > t[i]) {
return 1;
}
}
if (len1 < len2)
return -1;
else if (len1 > len2)
return 1;
return 0;
}
uint32_t iso_read_lsb(const uint8_t *buf, int bytes)
{
int i;
uint32_t ret = 0;
for (i=0; i<bytes; i++) {
ret += ((uint32_t) buf[i]) << (i*8);
}
return ret;
}
uint32_t iso_read_msb(const uint8_t *buf, int bytes)
{
int i;
uint32_t ret = 0;
for (i=0; i<bytes; i++) {
ret += ((uint32_t) buf[bytes-i-1]) << (i*8);
}
return ret;
}
uint32_t iso_read_bb(const uint8_t *buf, int bytes)
{
uint32_t v1 = iso_read_lsb(buf, bytes);
uint32_t v2 = iso_read_msb(buf+bytes, bytes);
assert(v1 == v2);
return v1;
}

View File

@ -1,121 +0,0 @@
/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */
/* vim: set noet ts=8 sts=8 sw=8 : */
/**
* Utility functions for the Libisofs library.
*/
#ifndef LIBISO_UTIL_H
#define LIBISO_UTIL_H
#include <stdint.h>
#include <time.h>
#include <wchar.h>
#ifndef MAX
# define MAX(a, b) (((a) > (b)) ? (a) : (b))
#endif
#ifndef MIN
# define MIN(a, b) (((a) < (b)) ? (a) : (b))
#endif
extern inline int div_up(int n, int div)
{
return (n + div - 1) / div;
}
extern inline int round_up(int n, int mul)
{
return div_up(n, mul) * mul;
}
wchar_t *towcs(const char *);
char *str2ascii(const char*);
uint16_t *str2ucs(const char*);
/**
* Create a level 1 directory identifier.
*/
char *iso_1_dirid(const char *src);
/**
* Create a level 2 directory identifier.
*/
char *iso_2_dirid(const char *src);
/**
* Create a level 1 file identifier that consists of a name, extension and
* version number. The resulting string will have a file name of maximum
* length 8, followed by a separator (.), an optional extension of maximum
* length 3, followed by a separator (;) and a version number (digit 1).
* @return NULL if the original name and extension both are of length 0.
*/
char *iso_1_fileid(const char *src);
/**
* Create a level 2 file identifier that consists of a name, extension and
* version number. The combined file name and extension length will not exceed
* 30, the name and extension will be separated (.), and the extension will be
* followed by a separator (;) and a version number (digit 1).
* @return NULL if the original name and extension both are of length 0.
*/
char *iso_2_fileid(const char *src);
/**
* Create a Joliet file or directory identifier that consists of a name,
* extension and version number. The combined name and extension length will
* not exceed 128 bytes, the name and extension will be separated (.),
* and the extension will be followed by a separator (;) and a version number
* (digit 1). All characters consist of 2 bytes and the resulting string is
* NULL-terminated by a 2-byte NULL. Requires the locale to be set correctly.
*
* @param size will be set to the size (in bytes) of the identifier.
* @return NULL if the original name and extension both are of length 0 or the conversion from the current codeset to UCS-2BE is not available.
*/
uint16_t *iso_j_id(const char *src);
/**
* FIXME: what are the requirements for these next two? Is this for RR?
*
* Create a POSIX portable file name that consists of a name and extension.
* The resulting file name will not exceed 250 characters.
* @return NULL if the original name and extension both are of length 0.
*/
char *iso_p_fileid(const char *src);
/**
* Create a POSIX portable directory name.
* The resulting directory name will not exceed 250 characters.
* @return NULL if the original name is of length 0.
*/
char *iso_p_dirid(const char *src);
void iso_lsb(uint8_t *buf, uint32_t num, int bytes);
void iso_msb(uint8_t *buf, uint32_t num, int bytes);
void iso_bb(uint8_t *buf, uint32_t num, int bytes);
uint32_t iso_read_lsb(const uint8_t *buf, int bytes);
uint32_t iso_read_msb(const uint8_t *buf, int bytes);
uint32_t iso_read_bb(const uint8_t *buf, int bytes);
/** Records the date/time into a 7 byte buffer (9.1.5) */
void iso_datetime_7(uint8_t *buf, time_t t);
/** Records the date/time into a 17 byte buffer (8.4.26.1) */
void iso_datetime_17(uint8_t *buf, time_t t);
time_t iso_datetime_read_7(const uint8_t *buf);
time_t iso_datetime_read_17(const uint8_t *buf);
/**
* Like strlen, but for Joliet strings.
*/
size_t ucslen(const uint16_t *str);
/**
* Like strcmp, but for Joliet strings.
*/
int ucscmp(const uint16_t *s1, const uint16_t *s2);
#endif /* LIBISO_UTIL_H */

View File

@ -1,189 +0,0 @@
/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */
/* vim: set ts=8 sts=8 sw=8 noet : */
#include <stdlib.h>
#include <string.h>
#include <libgen.h>
#include "libisofs.h"
#include "tree.h"
#include "util.h"
#include "volume.h"
struct iso_volset*
iso_volset_new(struct iso_volume *vol, const char *id)
{
struct iso_volset *volset = calloc(1, sizeof(struct iso_volset));
volset->volset_size = 1;
volset->refcount = 1;
volset->volume = malloc(sizeof(void *));
volset->volume[0] = vol;
volset->volset_id = strdup(id);
vol->refcount++;
return volset;
}
void
iso_volset_free(struct iso_volset *volset)
{
if (--volset->refcount < 1) {
int i;
for (i = 0; i < volset->volset_size; i++) {
iso_volume_free(volset->volume[i]);
}
free(volset->volume);
free(volset->volset_id);
}
}
struct iso_volume*
iso_volume_new(const char *volume_id,
const char *publisher_id,
const char *data_preparer_id)
{
return iso_volume_new_with_root(volume_id,
publisher_id,
data_preparer_id,
NULL);
}
struct iso_volume*
iso_volume_new_with_root(const char *volume_id,
const char *publisher_id,
const char *data_preparer_id,
struct iso_tree_node *root)
{
struct iso_volume *volume;
volume = calloc(1, sizeof(struct iso_volume));
volume->refcount = 1;
volume->root = root ? root : iso_tree_new_root(volume);
if (volume_id != NULL)
volume->volume_id = strdup(volume_id);
if (publisher_id != NULL)
volume->publisher_id = strdup(publisher_id);
if (data_preparer_id != NULL)
volume->data_preparer_id = strdup(data_preparer_id);
return volume;
}
void
iso_volume_free(struct iso_volume *volume)
{
/* Only free if no references are in use. */
if (--volume->refcount < 1) {
iso_tree_free(volume->root);
free(volume->volume_id);
free(volume->publisher_id);
free(volume->data_preparer_id);
free(volume);
}
}
struct iso_tree_node *
iso_volume_get_root(const struct iso_volume *volume)
{
return volume->root;
}
struct iso_tree_node *
iso_tree_volume_path_to_node(struct iso_volume *volume, const char *path)
{
struct iso_tree_node *node;
char *ptr, *brk_info, *component;
/* get the first child at the root of the volume
* that is "/" */
node=iso_volume_get_root(volume);
if (!strcmp (path, "/"))
return node;
if (!node->nchildren)
return NULL;
/* the name of the nodes is in wide characters so first convert path
* into wide characters. */
ptr = strdup(path);
/* get the first component of the path */
component=strtok_r(ptr, "/", &brk_info);
while (component) {
size_t max;
size_t i;
/* search among all the children of this directory if this path component exists */
max=node->nchildren;
for (i=0; i < max; i++) {
if (!strcmp(component, node->children[i]->name)) {
node=node->children[i];
break;
}
}
/* see if a node could be found */
if (i==max) {
node=NULL;
break;
}
component=strtok_r(NULL, "/", &brk_info);
}
free(ptr);
return node;
}
struct iso_tree_node *
iso_tree_volume_add_path(struct iso_volume *volume,
const char *disc_path,
const char *path)
{
char *tmp;
struct iso_tree_node *node;
struct iso_tree_node *parent_node;
tmp=strdup(disc_path);
parent_node = iso_tree_volume_path_to_node(volume, dirname(tmp));
free(tmp);
if (!parent_node)
return NULL;
node = iso_tree_radd_dir(parent_node, path);
if (!node)
return NULL;
tmp=strdup(disc_path);
iso_tree_node_set_name(node, basename(tmp));
free(tmp);
return node;
}
struct iso_tree_node *
iso_tree_volume_add_new_dir(struct iso_volume *volume,
const char *disc_path)
{
char *tmp;
struct iso_tree_node *node;
struct iso_tree_node *parent_node;
tmp=strdup(disc_path);
parent_node = iso_tree_volume_path_to_node(volume, dirname(tmp));
free(tmp);
if (!parent_node)
return NULL;
tmp=strdup(disc_path);
node = iso_tree_add_new_dir(parent_node, basename(tmp));
free(tmp);
return node;
}

View File

@ -1,45 +0,0 @@
/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */
/* vim: set noet sts=8 ts=8 sw=8 : */
/**
* Extra declarations for use with the iso_volume structure.
*/
#ifndef LIBISO_VOLUME_H
#define LIBISO_VOLUME_H
#include "libisofs.h"
/**
* Data volume.
*/
struct iso_volume
{
int refcount; /**< Number of used references to this
volume. */
struct iso_tree_node *root; /**< Root of the directory tree for the
volume. */
char *volume_id; /**< Volume identifier. */
char *publisher_id; /**< Volume publisher. */
char *data_preparer_id; /**< Volume data preparer. */
};
/**
* A set of data volumes.
*/
struct iso_volset
{
int refcount;
struct iso_volume **volume; /**< The volumes belonging to this
volume set. */
int volset_size; /**< The number of volumes in this
volume set. */
char *volset_id; /**< The id of this volume set, encoded
in the current locale. */
};
#endif /* __ISO_VOLUME */

View File

@ -1,107 +0,0 @@
/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */
/* vim: set ts=8 sts=8 sw=8 noet : */
#define _GNU_SOURCE
#include "libisofs.h"
#include "libburn/libburn.h"
#include <getopt.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <assert.h>
#include <sys/types.h>
#include <dirent.h>
#include <string.h>
#include <err.h>
#define SECSIZE 2048
const char * const optstring = "JRL:h";
extern char *optarg;
extern int optind;
void usage()
{
printf("test [OPTIONS] DIRECTORY OUTPUT\n");
}
void help()
{
printf(
"Options:\n"
" -J Add Joliet support\n"
" -R Add Rock Ridge support\n"
" -L <num> Set the ISO level (1 or 2)\n"
" -h Print this message\n"
);
}
int main(int argc, char **argv)
{
struct iso_volset *volset;
struct iso_volume *volume;
struct iso_tree_node *root;
struct burn_source *src;
unsigned char buf[2048];
FILE *fd;
int c;
int level=1, flags=0;
while ((c = getopt(argc, argv, optstring)) != -1) {
switch(c) {
case 'h':
usage();
help();
exit(0);
break;
case 'J':
flags |= ECMA119_JOLIET;
break;
case 'R':
flags |= ECMA119_ROCKRIDGE;
break;
case 'L':
level = atoi(optarg);
break;
case '?':
usage();
exit(1);
break;
}
}
if (argc < 2) {
printf ("must pass directory to build iso from\n");
usage();
return 1;
}
if (argc < 3) {
printf ("must supply output file\n");
usage();
return 1;
}
fd = fopen(argv[optind+1], "w");
if (!fd) {
err(1, "error opening output file");
}
root = iso_tree_radd_dir(NULL, argv[optind]);
if (!root) {
err(1, "error opening input directory");
}
volume = iso_volume_new_with_root( "VOLID", "PUBID", "PREPID", root );
volset = iso_volset_new( volume, "VOLSETID" );
src = iso_source_new_ecma119(volset, 0, level, flags);
while (src->read(src, buf, 2048) == 2048) {
fwrite(buf, 1, 2048, fd);
}
fclose(fd);
return 0;
}

View File

@ -1,297 +0,0 @@
import struct
import tree
import sys
voldesc_fmt = "B" "5s" "B" "2041x"
# all these fields are common between the pri and sec voldescs
privoldesc_fmt = "B" "5s" "B" "x" "32s" "32s" "8x" "8s" "32x" "4s" "4s" "4s" "8s" "4s4s" "4s4s" "34s" "128s" \
"128s" "128s" "128s" "37s" "37s" "37s" "17s" "17s" "17s" "17s" "B" "x" "512s" "653x"
# the fields unique to the sec_vol_desc
secvoldesc_fmt = "x" "5x" "x" "B" "32x" "32x" "8x" "8x" "32s" "4x" "4x" "4x" "8x" "4x4x" "4x4x" "34x" "128x" \
"128x" "128x" "128x" "37x" "37x" "37x" "17x" "17x" "17x" "17x" "x" "x" "512x" "653x"
dirrecord_fmt = "B" "B" "8s" "8s" "7s" "B" "B" "B" "4s" "B" # + file identifier, padding field and SU area
pathrecord_fmt = "B" "B" "4s" "2s" # + directory identifier and padding field
def read_bb(str, le, be):
val1, = struct.unpack(le, str)
val2, = struct.unpack(be, str)
if val1 != val2:
print "val1=%d, val2=%d" % (val1, val2)
raise AssertionError, "values are not equal in dual byte-order field"
return val1
def read_bb4(str):
return read_bb(str, "<I4x", ">4xI")
def read_bb2(str):
return read_bb(str, "<H2x", ">2xH")
def read_lsb4(str):
return struct.unpack("<I", str)[0]
def read_lsb2(str):
return struct.unpack("<H", str)[0]
def read_msb4(str):
return struct.unpack(">I", str)[0]
def read_msb2(str):
return struct.unpack(">H", str)[0]
class VolDesc(object):
def __init__(self, data):
print "fmt len=%d, data len=%d" % ( struct.calcsize(voldesc_fmt), len(data) )
self.vol_desc_type, self.standard_id, self.vol_desc_version = struct.unpack(voldesc_fmt, data)
class PriVolDesc(VolDesc):
def __init__(self, data):
self.vol_desc_type, \
self.standard_id, \
self.vol_desc_version, \
self.system_id, \
self.volume_id, \
self.vol_space_size, \
self.vol_set_size, \
self.vol_seq_num, \
self.block_size, \
self.path_table_size, \
self.l_table_pos, \
self.l_table2_pos, \
self.m_table_pos, \
self.m_table2_pos, \
self.root_record, \
self.volset_id, \
self.publisher_id, \
self.preparer_id, \
self.application_id, \
self.copyright_file, \
self.abstract_file, \
self.bibliographic_file, \
self.creation_timestamp, \
self.modification_timestamp, \
self.expiration_timestamp, \
self.effective_timestamp, \
self.file_struct_version, \
self.application_use = struct.unpack(privoldesc_fmt, data)
# take care of reading the integer types
self.vol_space_size = read_bb4(self.vol_space_size)
self.vol_set_size = read_bb2(self.vol_set_size)
self.vol_seq_num = read_bb2(self.vol_seq_num)
self.block_size = read_bb2(self.block_size)
self.path_table_size = read_bb4(self.path_table_size)
self.l_table_pos = read_lsb4(self.l_table_pos)
self.l_table2_pos = read_lsb4(self.l_table2_pos)
self.m_table_pos = read_msb4(self.m_table_pos)
self.m_table2_pos = read_msb4(self.m_table2_pos)
# parse the root directory record
self.root_record = DirRecord(self.root_record)
def readPathTables(self, file):
file.seek( self.block_size * self.l_table_pos )
self.l_table = PathTable( file.read(self.path_table_size), 0 )
file.seek( self.block_size * self.m_table_pos )
self.m_table = PathTable( file.read(self.path_table_size), 1 )
if self.l_table2_pos:
file.seek( self.block_size * self.l_table2_pos )
self.l_table2 = PathTable( file.read(self.path_table_size), 0 )
else:
self.l_table2 = None
if self.m_table2_pos:
file.seek( self.block_size * self.m_table2_pos )
self.m_table2 = PathTable( file.read(self.path_table_size), 1 )
else:
self.m_table2 = None
def toTree(self, isofile):
ret = tree.Tree(isofile=isofile.name)
ret.root = self.root_record.toTreeNode(parent=None, isofile=isofile)
return ret
class SecVolDesc(PriVolDesc):
def __init__(self, data):
super(SecVolDesc,self).__init__(data)
self.flags, self.escape_sequences = struct.unpack(secvoldesc_fmt, data)
# return a single volume descriptor of the appropriate type
def readVolDesc(data):
desc = VolDesc(data)
if desc.standard_id != "CD001":
print "Unexpected standard_id " +desc.standard_id
return None
if desc.vol_desc_type == 1:
return PriVolDesc(data)
elif desc.vol_desc_type == 2:
return SecVolDesc(data)
elif desc.vol_desc_type == 3:
print "I don't know about partitions yet!"
return None
elif desc.vol_desc_type == 255:
return desc
else:
print "Unknown volume descriptor type %d" % (desc.vol_desc_type,)
return None
def readVolDescSet(file):
ret = [ readVolDesc(file.read(2048)) ]
while ret[-1].vol_desc_type != 255:
ret.append( readVolDesc(file.read(2048)) )
for vol in ret:
if vol.vol_desc_type == 1 or vol.vol_desc_type == 2:
vol.readPathTables(file)
return ret
class DirRecord:
def __init__(self, data):
self.len_dr, \
self.len_xa, \
self.block, \
self.len_data, \
self.timestamp, \
self.flags, \
self.unit_size, \
self.gap_size, \
self.vol_seq_number, \
self.len_fi = struct.unpack(dirrecord_fmt, data[:33])
self.children = []
if self.len_dr > len(data):
raise AssertionError, "Error: not enough data to read in DirRecord()"
elif self.len_dr < 34:
raise AssertionError, "Error: directory record too short"
fmt = str(self.len_fi) + "s"
if self.len_fi % 2 == 0:
fmt += "1x"
len_su = self.len_dr - (33 + self.len_fi + 1 - (self.len_fi % 2))
fmt += str(len_su) + "s"
if len(data) >= self.len_dr:
self.file_id, self.su = struct.unpack(fmt, data[33 : self.len_dr])
else:
print "Error: couldn't read file_id: not enough data"
self.file_id = "BLANK"
self.su = ""
# convert to integers
self.block = read_bb4(self.block)
self.len_data = read_bb4(self.len_data)
self.vol_seq_number = read_bb2(self.vol_seq_number)
def toTreeNode(self, parent, isofile, path=""):
ret = tree.TreeNode(parent=parent, isofile=isofile.name)
if len(path) > 0:
path += "/"
path += self.file_id
ret.path = path
if self.flags & 2: # we are a directory, recurse
isofile.seek( 2048 * self.block )
data = isofile.read( self.len_data )
pos = 0
while pos < self.len_data:
try:
child = DirRecord( data[pos:] )
pos += child.len_dr
if child.len_fi == 1 and (child.file_id == "\x00" or child.file_id == "\x01"):
continue
print "read child named " +child.file_id
self.children.append( child )
ret.children.append( child.toTreeNode(ret, isofile, path) )
except AssertionError:
print "Couldn't read child of directory %s, position is %d, len is %d" % \
(path, pos, self.len_data)
raise
return ret
class PathTableRecord:
def __init__(self, data, readint2, readint4):
self.len_di, self.len_xa, self.block, self.parent_number = struct.unpack(pathrecord_fmt, data[:8])
if len(data) < self.len_di + 8:
raise AssertionError, "Error: not enough data to read path table record"
fmt = str(self.len_di) + "s"
self.dir_id, = struct.unpack(fmt, data[8:8+self.len_di])
self.block = readint4(self.block)
self.parent_number = readint2(self.parent_number)
class PathTable:
def __init__(self, data, m_type):
if m_type:
readint2 = read_msb2
readint4 = read_msb4
else:
readint2 = read_lsb2
readint4 = read_lsb4
pos = 0
self.records = []
while pos < len(data):
try:
self.records.append( PathTableRecord(data[pos:], readint2, readint4) )
print "Read path record %d: dir_id %s, block %d, parent_number %d" %\
(len(self.records), self.records[-1].dir_id, self.records[-1].block, self.records[-1].parent_number)
pos += self.records[-1].len_di + 8
pos += pos % 2
except AssertionError:
print "Last successfully read path table record had dir_id %s, block %d, parent_number %d" % \
(self.records[-1].dir_id, self.records[-1].block, self.records[-1].parent_number)
print "Error was near offset %x" % (pos,)
raise
def findRecord(self, dir_id, block, parent_number):
number=1
for record in self.records:
if record.dir_id == dir_id and record.block == block and record.parent_number == parent_number:
return number, record
number += 1
return None, None
# check this path table for consistency against the actual directory heirarchy
def crossCheckDirRecords(self, root, parent_number=1):
number, rec = self.findRecord(root.file_id, root.block, parent_number)
if not rec:
print "Error: directory record parent_number %d, dir_id %s, block %d doesn't match a path table record" % \
(parent_number, root.file_id, root.block)
parent = self.records[parent_number]
print "Parent has parent_number %d, dir_id %s, block %d" % (parent.parent_number, parent.dir_id, parent.block)
return 0
for child in root.children:
if child.flags & 2:
self.crossCheckDirRecords(child, number)
if len(sys.argv) != 2:
print "Please enter the name of the .iso file to open"
sys.exit(1)
f = file(sys.argv[1])
f.seek(2048 * 16) # system area
volumes = readVolDescSet(f)
vol = volumes[0]
t = vol.toTree(f)
vol.l_table.crossCheckDirRecords(vol.root_record)
vol.m_table.crossCheckDirRecords(vol.root_record)
vol = volumes[1]
try:
t = vol.toTree(f)
vol.l_table.crossCheckDirRecords(vol.root_record)
vol.m_table.crossCheckDirRecords(vol.root_record)
except AttributeError:
pass

View File

@ -1,7 +1,7 @@
/* test/libburner.c , API illustration of burning a single data track to CD */
/* Copyright (C) 2005 - 2006 Thomas Schmitt <scdbackup@gmx.net> */
/* Provided under GPL, see also "License and copyright aspects" at file end */
/* test/libburner.c , API illustration of burning data or audio tracks to CD */
/* Copyright (C) 2005 - 2006 Thomas Schmitt <scdbackup@gmx.net> */
/* Provided under GPL, see also "License and copyright aspects" at file end */
/** Overview
@ -49,6 +49,8 @@
#include <stdlib.h>
#include <time.h>
#include <errno.h>
#include <sys/stat.h>
#include <fcntl.h>
/** For simplicity i use global variables to represent the drives.
@ -113,9 +115,6 @@ int libburner_aquire_by_adr(char *drive_adr)
if (ret <= 0) {
fprintf(stderr,"FAILURE with persistent drive address '%s'\n",
drive_adr);
if (strncmp(drive_adr,"/dev/sg",7) != 0 &&
strncmp(drive_adr,"/dev/hd",7) != 0)
fprintf(stderr,"\nHINT: Consider addresses like '/dev/hdc' or '/dev/sg0'\n");
} else {
printf("Done\n");
drive_is_grabbed = 1;
@ -175,7 +174,6 @@ int libburner_aquire_by_driveno(int *driveno)
}
printf("-----------------------------------------------------------------------------\n\n");
/*
On multi-drive systems save yourself from sysadmins' revenge.
@ -320,7 +318,7 @@ int libburner_regrab(struct burn_drive *drive) {
*/
int libburner_payload(struct burn_drive *drive,
char source_adr[][4096], int source_adr_count,
off_t stdin_size, int simulate_burn, int all_tracks_type)
int multi, int simulate_burn, int all_tracks_type)
{
struct burn_source *data_src;
struct burn_disc *target_disc;
@ -330,8 +328,10 @@ int libburner_payload(struct burn_drive *drive,
struct burn_track *track, *tracklist[99];
struct burn_progress progress;
time_t start_time;
int last_sector = 0, padding = 0, trackno;
int last_sector = 0, padding = 0, trackno, write_mode_tao = 0, fd;
off_t fixed_size;
char *adr;
struct stat stbuf;
if (all_tracks_type != BURN_AUDIO) {
all_tracks_type = BURN_MODE1;
@ -348,12 +348,21 @@ int libburner_payload(struct burn_drive *drive,
burn_track_define_data(track, 0, padding, 1, all_tracks_type);
adr = source_adr[trackno];
fixed_size = 0;
if (adr[0] == '-' && adr[1] == 0) {
data_src = burn_fd_source_new(0, -1, stdin_size);
printf("Note: using standard input as source with %.f bytes\n",
(double) stdin_size);
} else
data_src = burn_file_source_new(adr, NULL);
fd = 0;
} else {
fd = open(adr, O_RDONLY);
if (fd>=0)
if (fstat(fd,&stbuf)!=-1)
if((stbuf.st_mode&S_IFMT)==S_IFREG)
fixed_size = stbuf.st_size;
}
if (fixed_size==0)
write_mode_tao = 1;
data_src = NULL;
if (fd>=0)
data_src = burn_fd_source_new(fd, -1, fixed_size);
if (data_src == NULL) {
fprintf(stderr,
"FATAL: Could not open data source '%s'.\n",adr);
@ -378,39 +387,30 @@ int libburner_payload(struct burn_drive *drive,
/* Evaluate drive and media */
while ((disc_state = burn_disc_get_status(drive)) == BURN_DISC_UNREADY)
usleep(100001);
if (disc_state != BURN_DISC_BLANK) {
if (disc_state == BURN_DISC_FULL ||
disc_state == BURN_DISC_APPENDABLE) {
fprintf(stderr,
"FATAL: Media with data detected. Need blank media.\n");
if (disc_state == BURN_DISC_APPENDABLE) {
write_mode_tao = 1;
} else if (disc_state != BURN_DISC_BLANK) {
if (disc_state == BURN_DISC_FULL) {
fprintf(stderr, "FATAL: Closed media with data detected. Need blank or appendable media.\n");
if (burn_disc_erasable(drive))
fprintf(stderr, "HINT: Try --blank_fast\n\n");
} else if (disc_state == BURN_DISC_EMPTY)
fprintf(stderr,"FATAL: No media detected in drive\n");
else
fprintf(stderr,
"FATAL: Cannot recognize drive and media state\n");
"FATAL: Cannot recognize state of drive and media\n");
return 0;
}
burn_options = burn_write_opts_new(drive);
burn_write_opts_set_perform_opc(burn_options, 0);
#ifdef Libburner_raw_mode_which_i_do_not_likE
/* This yields higher CD capacity but hampers my IDE controller
with burning on one drive and reading on another simultaneously.
My burner does not obey the order --try_to_simulate in this mode.
*/
burn_write_opts_set_write_type(burn_options,
BURN_WRITE_RAW, BURN_BLOCK_RAW96R);
#else
/* This is by what cdrskin competes with cdrecord -sao which
i understand is the mode preferrably advised by Joerg Schilling */
burn_write_opts_set_write_type(burn_options,
BURN_WRITE_SAO, BURN_BLOCK_SAO);
#endif
burn_write_opts_set_multi(burn_options, !!multi);
if (write_mode_tao)
burn_write_opts_set_write_type(burn_options,
BURN_WRITE_TAO, BURN_BLOCK_MODE1);
else
burn_write_opts_set_write_type(burn_options,
BURN_WRITE_SAO, BURN_BLOCK_SAO);
if(simulate_burn)
printf("\n*** Will TRY to SIMULATE burning ***\n\n");
burn_write_opts_set_simulate(burn_options, simulate_burn);
@ -430,8 +430,11 @@ int libburner_payload(struct burn_drive *drive,
printf(
"Thank you for being patient since %d seconds.\n",
(int) (time(0) - start_time));
else if(write_mode_tao)
printf("Track %d : sector %d\n", progress.track+1,
progress.sector);
else
printf("Track %d : sector %d of %d\n", progress.track,
printf("Track %d : sector %d of %d\n",progress.track+1,
progress.sector, progress.sectors);
last_sector = progress.sector;
sleep(1);
@ -442,19 +445,21 @@ int libburner_payload(struct burn_drive *drive,
burn_track_free(tracklist[trackno]);
burn_session_free(session);
burn_disc_free(target_disc);
if(simulate_burn)
if (multi)
printf("NOTE: Media left appendable.\n");
if (simulate_burn)
printf("\n*** Did TRY to SIMULATE burning ***\n\n");
return 0;
}
/** The setup parameters of libburn */
static char drive_adr[BURN_DRIVE_ADR_LEN]= {""};
static int driveno= 0;
static int do_blank= 0;
/** The setup parameters of libburner */
static char drive_adr[BURN_DRIVE_ADR_LEN] = {""};
static int driveno = 0;
static int do_blank = 0;
static char source_adr[99][4096];
static int source_adr_count= 0;
static off_t stdin_size= 650*1024*1024;
static int source_adr_count = 0;
static int do_multi = 0;
static int simulate_burn = 0;
static int all_tracks_type = BURN_MODE1;
@ -499,15 +504,12 @@ int libburner_setup(int argc, char **argv)
}
strcpy(drive_adr, argv[i]);
}
} else if (!strcmp(argv[i], "--stdin_size")) {
++i;
if (i >= argc) {
fprintf(stderr,"--stdin_size requires an argument\n");
return 3;
} else
stdin_size = atoi(argv[i]);
if (stdin_size < 600*1024) /* minimum readable track size */
stdin_size = 600*1024;
} else if (!strcmp(argv[i], "--multi")) {
do_multi = 1;
} else if (!strcmp(argv[i], "--stdin_size")) { /* obsoleted */
i++;
} else if (!strcmp(argv[i], "--try_to_simulate")) {
simulate_burn = 1;
@ -539,17 +541,15 @@ int libburner_setup(int argc, char **argv)
insuffient_parameters = 0;
if (print_help || insuffient_parameters ) {
printf("Usage: %s\n", argv[0]);
printf(" [--drive <address>|<driveno>|\"-\"]\n");
printf(" [--blank_fast|--blank_full] [--audio]\n");
printf(" [--try_to_simulate] [--stdin_size <bytes>]\n");
printf(" [<one or more imagefiles>|\"-\"]\n");
printf(" [--drive <address>|<driveno>|\"-\"] [--audio]\n");
printf(" [--blank_fast|--blank_full] [--try_to_simulate]\n");
printf(" [--multi] [<one or more imagefiles>|\"-\"]\n");
printf("Examples\n");
printf("A bus scan (needs rw-permissions to see a drive):\n");
printf(" %s --drive -\n",argv[0]);
printf("Burn a file to drive chosen by number:\n");
printf(" %s --drive 0 my_image_file\n",
argv[0]);
printf("Burn a file to drive chosen by persistent address:\n");
printf("Burn a file to drive chosen by number, leave appendable:\n");
printf(" %s --drive 0 --multi my_image_file\n", argv[0]);
printf("Burn a file to drive chosen by persistent address, close:\n");
printf(" %s --drive /dev/hdc my_image_file\n", argv[0]);
printf("Blank a used CD-RW (is combinable with burning in one run):\n");
printf(" %s --drive /dev/hdc --blank_fast\n",argv[0]);
@ -557,9 +557,9 @@ int libburner_setup(int argc, char **argv)
printf(" lame --decode -t /path/to/track1.mp3 track1.cd\n");
printf(" test/dewav /path/to/track2.wav -o track2.cd\n");
printf(" %s --drive /dev/hdc --audio track1.cd track2.cd\n", argv[0]);
printf("Burn a compressed afio archive on-the-fly, pad up to 700 MB:\n");
printf("Burn a compressed afio archive on-the-fly:\n");
printf(" ( cd my_directory ; find . -print | afio -oZ - ) | \\\n");
printf(" %s --drive /dev/hdc --stdin_size 734003200 -\n", argv[0]);
printf(" %s --drive /dev/hdc -\n", argv[0]);
printf("To be read from *not mounted* CD via: afio -tvZ /dev/hdc\n");
printf("Program tar would need a clean EOF which our padded CD cannot deliver.\n");
if (insuffient_parameters)
@ -616,8 +616,8 @@ int main(int argc, char **argv)
}
if (source_adr_count > 0) {
ret = libburner_payload(drive_list[driveno].drive,
source_adr, source_adr_count, stdin_size,
simulate_burn, all_tracks_type);
source_adr, source_adr_count,
do_multi, simulate_burn, all_tracks_type);
if (ret<=0)
{ ret = 38; goto release_drive; }
}

574
test/telltoc.c Normal file
View File

@ -0,0 +1,574 @@
/* test/telltoc.c , API illustration of obtaining media status info */
/* Copyright (C) 2006 Thomas Schmitt <scdbackup@gmx.net> Provided under GPL */
/** Overview
telltoc is a minimal demo application for the library libburn as provided
on http://libburn.pykix.org . It can list the available devices, can display
some drive properties, the type of media, eventual table of content and
multisession info for mkisofs option -C .
It's main purpose, nevertheless, is to show you how to use libburn and also
to serve the libburn team as reference application. telltoc.c does indeed
define the standard way how above gestures can be implemented and stay upward
compatible for a good while.
Before you can do anything, you have to initialize libburn by
burn_initialize()
as it is done in main() at the end of this file. Then you aquire a
drive in an appropriate way conforming to the API. The two main
approaches are shown here in application functions:
telltoc_aquire_by_adr() demonstrates usage as of cdrecord traditions
telltoc_aquire_by_driveno() demonstrates a scan-and-choose approach
With that aquired drive you can call
telltoc_media() prints some information about the media in a drive
telltoc_toc() prints a table of content (if there is content)
telltoc_msinfo() prints parameters for mkisofs option -C
When everything is done, main() releases the drive and shuts down libburn:
burn_drive_release();
burn_finish()
*/
/** See this for the decisive API specs . libburn.h is The Original */
/* For using the installed header file : #include <libburn/libburn.h> */
/* This program insists in the own headerfile. */
#include "../libburn/libburn.h"
/* libburn is intended for Linux systems with kernel 2.4 or 2.6 for now */
#include <stdio.h>
#include <ctype.h>
#include <sys/types.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <time.h>
#include <errno.h>
#include <sys/stat.h>
#include <fcntl.h>
/** For simplicity i use global variables to represent the drives.
Drives are systemwide global, so we do not give away much of good style.
*/
/** This list will hold the drives known to libburn. This might be all CD
drives of the system and thus might impose severe impact on the system.
*/
static struct burn_drive_info *drive_list;
/** If you start a long lasting operation with drive_count > 1 then you are
not friendly to the users of other drives on those systems. Beware. */
static unsigned int drive_count;
/** This variable indicates wether the drive is grabbed and must be
finally released */
static int drive_is_grabbed = 0;
/* Some in-advance definitions to allow a more comprehensive ordering
of the functions and their explanations in here */
int telltoc_aquire_by_adr(char *drive_adr);
int telltoc_aquire_by_driveno(int *drive_no, int silent);
/* ------------------------------- API gestures ---------------------------- */
/** You need to aquire a drive before burning. The API offers this as one
compact call and alternatively as application controllable gestures of
whitelisting, scanning for drives and finally grabbing one of them.
If you have a persistent address of the drive, then the compact call is
to prefer because it only touches one drive. On modern Linux kernels,
there should be no fatal disturbance of ongoing burns of other libburn
instances with any of our approaches. We use open(O_EXCL) by default.
On /dev/hdX it should cooperate with growisofs and some cdrecord variants.
On /dev/sgN versus /dev/scdM expect it not to respect other programs.
*/
int telltoc_aquire_drive(char *drive_adr, int *driveno, int silent_drive)
{
int ret;
if(drive_adr != NULL && drive_adr[0] != 0)
ret = telltoc_aquire_by_adr(drive_adr);
else
ret = telltoc_aquire_by_driveno(driveno, silent_drive);
return ret;
}
/** If the persistent drive address is known, then this approach is much
more un-obtrusive to the systemwide livestock of drives. Only the
given drive device will be opened during this procedure.
*/
int telltoc_aquire_by_adr(char *drive_adr)
{
int ret;
fprintf(stderr,"Aquiring drive '%s' ...\n",drive_adr);
ret = burn_drive_scan_and_grab(&drive_list,drive_adr,1);
if (ret <= 0) {
fprintf(stderr,"FAILURE with persistent drive address '%s'\n",
drive_adr);
} else {
fprintf(stderr,"Done\n");
drive_is_grabbed = 1;
}
return ret;
}
/** This method demonstrates how to use libburn without knowing a persistent
drive address in advance. It has to make sure that after assessing the list
of available drives, all unwanted drives get closed again. As long as they
are open, no other libburn instance can see them. This is an intended
locking feature. The application is responsible for giving up the locks
by either burn_drive_release() (only after burn_drive_grab() !),
burn_drive_info_forget(), burn_drive_info_free(), or burn_finish().
@param driveno the index number in libburn's drive list. This will get
set to 0 on success and will then be the drive index to
use in the further dourse of processing.
@param silent_drive 1=do not print "Drive found :" line with *driveno >= 0
@return 1 success , <= 0 failure
*/
int telltoc_aquire_by_driveno(int *driveno, int silent_drive)
{
char adr[BURN_DRIVE_ADR_LEN];
int ret, i;
fprintf(stderr, "Beginning to scan for devices ...\n");
while (!burn_drive_scan(&drive_list, &drive_count))
usleep(1002);
if (drive_count <= 0 && *driveno >= 0) {
fprintf(stderr, "FAILED (no drives found)\n");
return 0;
}
fprintf(stderr, "Done\n");
for (i = 0; i < drive_count; i++) {
if (*driveno >= 0 && (silent_drive || *driveno != i))
continue;
if (burn_drive_get_adr(&(drive_list[i]), adr) <=0)
strcpy(adr, "-get_adr_failed-");
printf("Drive found : %d --drive '%s' : ", i,adr);
printf("%-8s %-16s (%4s)\n",
drive_list[i].vendor,drive_list[i].product,
drive_list[i].revision);
}
if (*driveno < 0) {
fprintf(stderr,
"Pseudo-drive \"-\" given : bus scanning done.\n");
return 2; /* the program will end after this */
}
/* We already made our choice via command line. (default is 0)
So we just have to keep our desired drive and drop all others.
*/
if (drive_count <= *driveno) {
fprintf(stderr,
"Found only %d drives. Number %d not available.\n",
drive_count, *driveno);
return 0; /* the program will end after this */
}
/* Drop all drives which we do not want to use */
for (i = 0; i < drive_count; i++) {
if (i == *driveno) /* the one drive we want to keep */
continue;
ret = burn_drive_info_forget(&(drive_list[i]),0);
if (ret != 1)
fprintf(stderr, "Cannot drop drive %d. Please report \"ret=%d\" to libburn-hackers@pykix.org\n",
i, ret);
else
fprintf(stderr, "Dropped unwanted drive %d\n",i);
}
/* Make the one we want ready for inquiry */
ret= burn_drive_grab(drive_list[*driveno].drive, 1);
if (ret != 1)
return 0;
drive_is_grabbed = 1;
return 1;
}
/** This gesture is necessary to get my NEC DVD_RW ND-4570A out of a state
of noisy overexcitement after it was inquired for Next Writeable Address.
The noise then still lasts 20 seconds. Same with cdrecord -toc, btw.
It opens a small gap for losing the drive to another libburn instance.
Not a problem in telltoc. This is done as very last drive operation.
Eventually the other libburn instance will have the same sanitizing effect.
*/
int telltoc_regrab(struct burn_drive *drive) {
int ret;
if (drive_is_grabbed)
burn_drive_release(drive, 0);
drive_is_grabbed = 0;
ret = burn_drive_grab(drive, 0);
if (ret != 0) {
drive_is_grabbed = 1;
}
return !!ret;
}
int telltoc_media(struct burn_drive *drive)
{
int ret, media_found = 0;
double max_speed = 0.0, min_speed = 0.0;
enum burn_disc_status s;
while (burn_drive_get_status(drive, NULL) != BURN_DRIVE_IDLE)
usleep(100001);
while ((s = burn_disc_get_status(drive)) == BURN_DISC_UNREADY)
usleep(100001);
printf("Media status : ");
if (s==BURN_DISC_FULL) {
printf("is written , is closed\n");
media_found = 1;
} else if (s==BURN_DISC_APPENDABLE) {
printf("is written , is appendable\n");
media_found = 1;
} else if (s==BURN_DISC_BLANK) {
printf("is blank\n");
media_found = 1;
} else if (s==BURN_DISC_EMPTY)
printf("is not present\n");
else
printf("is not recognizable\n");
printf("Media type : ");
if (media_found) {
if (burn_disc_erasable(drive))
printf("is erasable\n");
else
printf("is not erasable\n");
} else
printf("is not recognizable\n");
ret= burn_disc_read_atip(drive);
if(ret>0) {
ret= burn_drive_get_min_write_speed(drive);
min_speed = ((double ) ret) / 176.0;
}
ret= burn_drive_get_write_speed(drive);
max_speed = ((double ) ret) / 176.0;
if (!media_found)
printf("Drive speed : max=%.f\n", max_speed);
else if (min_speed<=0)
printf("Media speed : max=%.f\n", max_speed);
else
printf("Media speed : max=%.f , min=%.f\n",
max_speed, min_speed);
return 1;
}
int telltoc_toc(struct burn_drive *drive)
{
int num_sessions = 0 , num_tracks = 0 , lba = 0;
int track_count = 0;
int session_no, track_no;
enum burn_disc_status s;
struct burn_disc *disc= NULL;
struct burn_session **sessions;
struct burn_track **tracks;
struct burn_toc_entry toc_entry;
while (burn_drive_get_status(drive, NULL) != BURN_DRIVE_IDLE)
usleep(100001);
while ((s = burn_disc_get_status(drive)) == BURN_DISC_UNREADY)
usleep(100001);
disc = burn_drive_get_disc(drive);
if (disc==NULL) {
fprintf(stderr, "SORRY: Cannot obtain Table Of Content\n");
return 2;
}
sessions = burn_disc_get_sessions(disc, &num_sessions);
for (session_no = 0; session_no<num_sessions; session_no++) {
tracks = burn_session_get_tracks(sessions[session_no],
&num_tracks);
if (tracks==NULL)
continue;
for(track_no= 0; track_no<num_tracks; track_no++) {
track_count++;
burn_track_get_entry(tracks[track_no], &toc_entry);
lba= burn_msf_to_lba(toc_entry.pmin, toc_entry.psec,
toc_entry.pframe);
printf("Media content: session %2d ", session_no+1);
printf("track %2d %s lba: %9d %2.2u:%2.2u:%2.2u\n",
track_count,
((toc_entry.control&7)<4?"audio":"data "),
lba,
toc_entry.pmin,
toc_entry.psec,
toc_entry.pframe);
}
burn_session_get_leadout_entry(sessions[session_no],
&toc_entry);
lba = burn_msf_to_lba(toc_entry.pmin,
toc_entry.psec, toc_entry.pframe);
printf("Media content: session %2d ", session_no+1);
printf("leadout lba: %9d %2.2u:%2.2u:%2.2u\n",
lba,
toc_entry.pmin,
toc_entry.psec,
toc_entry.pframe);
}
if (disc!=NULL)
burn_disc_free(disc);
return 1;
}
int telltoc_msinfo(struct burn_drive *drive,
int msinfo_explicit, int msinfo_alone)
{
int num_sessions, session_no, ret, num_tracks;
int nwa = -123456789, lba = -123456789, aux_lba, lout_lba;
enum burn_disc_status s;
struct burn_disc *disc= NULL;
struct burn_session **sessions;
struct burn_track **tracks;
struct burn_toc_entry toc_entry;
struct burn_write_opts *o= NULL;
while (burn_drive_get_status(drive, NULL) != BURN_DRIVE_IDLE)
usleep(100001);
while ((s = burn_disc_get_status(drive)) == BURN_DISC_UNREADY)
usleep(100001);
if (s!=BURN_DISC_APPENDABLE) {
if (!msinfo_explicit)
return 2;
fprintf(stderr,
"SORRY: --msinfo can only operate on appendable media.\n");
return 0;
}
/* man mkisofs , option -C :
The first number is the sector number of the first sector in
the last session of the disk that should be appended to.
*/
disc = burn_drive_get_disc(drive);
if (disc==NULL) {
fprintf(stderr,"SORRY: Cannot obtain info about CD content\n");
return 2;
}
sessions = burn_disc_get_sessions(disc, &num_sessions);
for (session_no = 0; session_no<num_sessions; session_no++) {
tracks = burn_session_get_tracks(sessions[session_no],
&num_tracks);
if (tracks==NULL || num_tracks<=0)
continue;
burn_track_get_entry(tracks[0], &toc_entry);
lba= burn_msf_to_lba(toc_entry.pmin, toc_entry.psec,
toc_entry.pframe);
}
if(lba==-123456789) {
fprintf(stderr,"SORRY: Cannot find any track on media\n");
{ ret = 0; goto ex; }
}
/* Prepare a qualified guess as fallback for nwa inquiry */
burn_session_get_leadout_entry(sessions[num_sessions-1], &toc_entry);
lout_lba= burn_msf_to_lba(toc_entry.pmin,toc_entry.psec,
toc_entry.pframe);
/* man mkisofs , option -C :
The second number is the starting sector number of the new session.
*/
/* Set some write opts to be sent to drive. LG GSA-4082B needs it. */
o= burn_write_opts_new(drive);
if(o!=NULL) {
burn_write_opts_set_perform_opc(o, 0);
burn_write_opts_set_write_type(o,
BURN_WRITE_TAO, BURN_BLOCK_MODE1);
}
/* Now try to inquire nwa from drive */
ret= burn_disc_track_lba_nwa(drive,o,0,&aux_lba,&nwa);
telltoc_regrab(drive); /* necessary to calm down my NEC drive */
if(ret<=0) {
fprintf(stderr,
"NOTE: Guessing next writeable address from leadout\n");
if(num_sessions>0)
nwa= lout_lba+6900;
else
nwa= lout_lba+11400;
}
if (!msinfo_alone)
printf("Media msinfo : mkisofs ... -C ");
printf("%d,%d\n",lba,nwa);
ret = 1;
ex:;
if (disc!=NULL)
burn_disc_free(disc);
if (o!=NULL)
burn_write_opts_free(o);
return ret;
}
/** The setup parameters of telltoc */
static char drive_adr[BURN_DRIVE_ADR_LEN] = {""};
static int driveno = 0;
static int do_media = 0;
static int do_toc = 0;
static int do_msinfo = 0;
static int print_help = 0;
/** Converts command line arguments into above setup parameters.
drive_adr[] must provide at least BURN_DRIVE_ADR_LEN bytes.
source_adr[] must provide at least 4096 bytes.
*/
int telltoc_setup(int argc, char **argv)
{
int i;
for (i = 1; i < argc; ++i) {
if (!strcmp(argv[i], "--drive")) {
++i;
if (i >= argc) {
fprintf(stderr,"--drive requires an argument\n");
return 1;
} else if (strcmp(argv[i], "-") == 0) {
drive_adr[0] = 0;
driveno = -1;
} else if (isdigit(argv[i][0])) {
drive_adr[0] = 0;
driveno = atoi(argv[i]);
} else {
if(strlen(argv[i]) >= BURN_DRIVE_ADR_LEN) {
fprintf(stderr,"--drive address too long (max. %d)\n",
BURN_DRIVE_ADR_LEN-1);
return 2;
}
strcpy(drive_adr, argv[i]);
}
} else if (strcmp(argv[i],"--media")==0) {
do_media = 1;
} else if (!strcmp(argv[i], "--msinfo")) {
do_msinfo = 1;
} else if (!strcmp(argv[i], "--toc")) {
do_toc = 1;
} else if (!strcmp(argv[i], "--help")) {
print_help = 1;
} else {
fprintf(stderr, "Unidentified option: %s\n", argv[i]);
return 7;
}
}
if (argc==1)
print_help = 1;
if (print_help) {
printf("Usage: %s\n", argv[0]);
printf(" [--drive <address>|<driveno>|\"-\"]\n");
printf(" [--media] [--toc] [--msinfo]\n");
printf("Examples\n");
printf("A bus scan (needs rw-permissions to see a drive):\n");
printf(" %s --drive -\n",argv[0]);
printf("Obtain info about the type of loaded media:\n");
printf(" %s --drive /dev/hdc --media\n",argv[0]);
printf("Obtain table of content:\n");
printf(" %s --drive /dev/hdc --toc\n",argv[0]);
printf("Obtain parameters for option -C of program mkisofs:\n");
printf(" msinfo=$(%s --drive /dev/hdc --msinfo 2>/dev/null)\n",
argv[0]);
printf(" mkisofs ... -C \"$msinfo\" ...\n");
printf("Obtain what is available about drive 0 and its media\n");
printf(" %s --drive 0\n",argv[0]);
}
return 0;
}
int main(int argc, char **argv)
{
int ret, toc_failed = 0, msinfo_alone = 0, msinfo_explicit = 0;
int full_default = 0;
ret = telltoc_setup(argc, argv);
if (ret)
exit(ret);
/* Behavior shall be different if --msinfo is only option */
if (do_msinfo) {
msinfo_explicit = 1;
if (!(do_media || do_toc))
msinfo_alone = 1;
}
/* Default option is to do everything if possible */
if (do_media==0 && do_msinfo==0 && do_toc==0 && driveno!=-1) {
if(print_help)
exit(0);
full_default = do_media = do_msinfo = do_toc = 1;
}
fprintf(stderr, "Initializing libburn.pykix.org ...\n");
if (burn_initialize())
fprintf(stderr, "Done\n");
else {
fprintf(stderr,"\nFATAL: Failed to initialize.\n");
exit(33);
}
/* Print messages of severity SORRY or more directly to stderr */
burn_msgs_set_severities("NEVER", "SORRY", "telltoc : ");
/** Note: driveno might change its value in this call */
ret = telltoc_aquire_drive(drive_adr, &driveno, !full_default);
if (ret<=0) {
fprintf(stderr,"\nFATAL: Failed to aquire drive.\n");
{ ret = 34; goto finish_libburn; }
}
if (ret == 2)
{ ret = 0; goto release_drive; }
if (do_media) {
ret = telltoc_media(drive_list[driveno].drive);
if (ret<=0)
{ret = 36; goto release_drive; }
}
if (do_toc) {
ret = telltoc_toc(drive_list[driveno].drive);
if (ret<=0)
{ret = 37; goto release_drive; }
if (ret==2)
toc_failed = 1;
}
if (do_msinfo) {
ret = telltoc_msinfo(drive_list[driveno].drive,
msinfo_explicit, msinfo_alone);
if (ret<=0)
{ret = 38; goto release_drive; }
}
ret = 0;
if (toc_failed)
ret = 37;
release_drive:;
if (drive_is_grabbed)
burn_drive_release(drive_list[driveno].drive, 0);
finish_libburn:;
/* This app does not bother to know about exact scan state.
Better to accept a memory leak here. We are done anyway. */
/* burn_drive_info_free(drive_list); */
burn_finish();
exit(ret);
}
/* License and copyright aspects:
See libburner.c
*/

View File

@ -1,103 +0,0 @@
/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */
#include <libburn/libburn.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <assert.h>
static struct burn_drive_info *drives;
static unsigned int n_drives;
static void show_tocs()
{
struct burn_session **sessions;
struct burn_track **tracks;
struct burn_disc *disc;
int nses, ntracks, hidefirst;
unsigned int i, j, k;
struct burn_toc_entry e;
enum burn_disc_status s;
for (i = 0; i < n_drives; ++i) {
fprintf(stderr, "TOC for disc in %s - %s:\n",
drives[i].vendor, drives[i].product);
if (!burn_drive_grab(drives[i].drive, 1)) {
fprintf(stderr, "Unable to open the drive!\n");
continue;
}
while (burn_drive_get_status(drives[i].drive, NULL))
usleep(1000);
while ((s = burn_disc_get_status(drives[i].drive))
== BURN_DISC_UNREADY)
usleep(1000);
if (s != BURN_DISC_FULL) {
burn_drive_release(drives[i].drive, 0);
fprintf(stderr, "No disc found!\n");
continue;
}
disc = burn_drive_get_disc(drives[i].drive);
sessions = burn_disc_get_sessions(disc, &nses);
for (k = 0; k < nses; ++k) {
tracks = burn_session_get_tracks(sessions[k],
&ntracks);
hidefirst = burn_session_get_hidefirst(sessions[k]);
if (hidefirst)
fprintf(stderr,
"track: GAP (%2d) lba: %9d (%9d) %02d:%02d:%02d adr: X control: X mode: %d\n",
k + 1, 0, 0, 0, 2, 0,
burn_track_get_mode(tracks[0]));
for (j = !!hidefirst; j < ntracks; ++j) {
burn_track_get_entry(tracks[j], &e);
fprintf(stderr,
"track: %3d (%2d) lba: %9d (%9d) %02d:%02d:%02d "
"adr: %d control: %d mode: %d\n",
e.point, e.session,
burn_msf_to_lba(e.pmin, e.psec,
e.pframe),
burn_msf_to_lba(e.pmin, e.psec,
e.pframe) * 4,
e.pmin, e.psec, e.pframe, e.adr,
e.control,
burn_track_get_mode(tracks[j]));
}
burn_session_get_leadout_entry(sessions[k], &e);
fprintf(stderr,
"track:lout (%2d) lba: %9d (%9d) %02d:%02d:%02d "
"adr: %d control: %d mode: %d\n",
k + 1, burn_msf_to_lba(e.pmin, e.psec,
e.pframe),
burn_msf_to_lba(e.pmin, e.psec,
e.pframe) * 4, e.pmin,
e.psec, e.pframe, e.adr, e.control, -1);
}
burn_disc_free(disc);
burn_drive_release(drives[i].drive, 0);
}
}
int main()
{
fprintf(stderr, "Initializing library...");
if (burn_initialize())
fprintf(stderr, "Success\n");
else {
printf("Failed\n");
return 1;
}
fprintf(stderr, "Scanning for devices...");
while (!burn_drive_scan(&drives, &n_drives)) ;
fprintf(stderr, "Done\n");
show_tocs();
burn_drive_info_free(drives);
burn_finish();
return 0;
}