Compare commits

...

No commits in common. "ZeroTwoFive" and "master" have entirely different histories.

60 changed files with 12337 additions and 960 deletions

View File

@ -1,4 +1,2 @@
Developers:
Mario Danic

View File

@ -1,2 +1,4 @@
Joe Neeman
Philippe Rouquier
Suriyan Laohaprapanon
Vreixo Formoso Lopes

View File

@ -1 +1,13 @@
nothing here now
Development
===========
- Support for reading of plain iso images.
- Support for reading RR extensions
Version 0.2.8
=============
TODO

View File

@ -29,7 +29,22 @@ libisofs_libisofs_la_SOURCES = \
libisofs/exclude.c \
libisofs/exclude.h \
libisofs/hash.h \
libisofs/hash.c
libisofs/hash.c \
libisofs/file.h \
libisofs/file.c \
libisofs/file_src.h \
libisofs/file_src.c \
libisofs/eltorito.h \
libisofs/eltorito.c \
libisofs/data_source.c \
libisofs/ecma119_read.h \
libisofs/ecma119_read.c \
libisofs/ecma119_read_rr.h \
libisofs/ecma119_read_rr.c \
libisofs/libiso_msgs.h \
libisofs/libiso_msgs.c \
libisofs/messages.h \
libisofs/messages.c
libinclude_HEADERS = \
libisofs/libisofs.h
@ -38,12 +53,51 @@ libinclude_HEADERS = \
## Build test applications
noinst_PROGRAMS = \
test/iso
test/iso \
test/isoread \
test/isoms \
test/isoadd \
test/isogrow
test_iso_CPPFLAGS = -Ilibisofs
test_iso_LDADD = $(libisofs_libisofs_la_OBJECTS) $(THREAD_LIBS)
test_iso_SOURCES = test/iso.c
test_isoread_CPPFLAGS = -Ilibisofs
test_isoread_LDADD = $(libisofs_libisofs_la_OBJECTS) $(THREAD_LIBS)
test_isoread_SOURCES = test/iso_read.c
test_isoms_CPPFLAGS = -Ilibisofs
test_isoms_LDADD = $(libisofs_libisofs_la_OBJECTS) $(THREAD_LIBS)
test_isoms_SOURCES = test/iso_ms.c
test_isoadd_CPPFLAGS = -Ilibisofs
test_isoadd_LDADD = $(libisofs_libisofs_la_OBJECTS) $(THREAD_LIBS)
test_isoadd_SOURCES = test/iso_add.c
test_isogrow_CPPFLAGS = -Ilibisofs -Ilibburn
test_isogrow_LDADD = $(libisofs_libisofs_la_OBJECTS) $(THREAD_LIBS) -lburn
test_isogrow_SOURCES = test/iso_grow.c
## Build unit test
check_PROGRAMS = \
test/test
test_test_CPPFLAGS = -Ilibisofs
test_test_LDADD = $(libisofs_libisofs_la_OBJECTS) $(THREAD_LIBS) -lcunit
test_test_LDFLAGS = -L.. -lm
test_test_SOURCES = \
test/test_exclude.c \
test/test_tree.c \
test/test_ecma119_tree.c \
test/test_file_hashtable.c \
test/test_util.c \
test/test_volume.c \
test/test_data_source.c \
test/test_read.c \
test/test.c
## ========================================================================= ##
@ -95,10 +149,10 @@ indent: $(indent_files)
# Extra things
nodist_pkgconfig_DATA = \
libisofs-1.pc
libisofs-5.pc
EXTRA_DIST = \
libisofs-1.pc.in \
libisofs-5.pc.in \
version.h.in \
doc/comments \
doc/doxygen.conf.in \

26
README
View File

@ -1,10 +1,10 @@
------------------------------------------------------------------------------
libburnia.pykix.org
libburnia-project.org
------------------------------------------------------------------------------
This all is under GPL.
(See GPL reference, our clarification and commitment at the end of this text)
------------------------------------------------------------------------------
libburnia.pykix.org
libburnia-project.org
By Mario Danic <mario.danic@gmail.com> and Thomas Schmitt <scdbackup@gmx.net>
Copyright (C) 2006-2007 Mario Danic, Thomas Schmitt
@ -12,14 +12,14 @@ Still containing parts of
Libburn. By Derek Foreman <derek@signalmarketing.com> and
Ben Jansens <xor@orodu.net>
Copyright (C) 2002-2006 Derek Foreman and Ben Jansens
These parts are to be replaced by own code of above libburnia.pykix.org
copyright holders and then libburnia.pykix.org is to be their sole copyright.
These parts are to be replaced by own code of above libburnia-project.org
copyright holders and then libburnia-project.org is to be their sole copyright.
This is done to achieve the right to issue the clarification and the
commitment as written at the end of this text.
The rights and merits of the Libburn-copyright holders Derek Foreman and
Ben Jansens will be duely respected.
This libburnia.pykix.org toplevel README (C) 2006-2007 Thomas Schmitt
This libburnia-project.org toplevel README (C) 2006-2007 Thomas Schmitt
------------------------------------------------------------------------------
Build and Installation
@ -27,16 +27,16 @@ This libburnia.pykix.org toplevel README (C) 2006-2007 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://libburnia-svn.pykix.org/libburn/trunk libburn_pykix
go into directory libburn_pykix and apply autotools by
svn co http://svn.libburnia-project.org/libburn/trunk libburn
go into directory libburn and apply autotools by
./bootstrap
Alternatively you may unpack a release tarball for which you do not need
autotools installed.
To build a libburnia.pykix.org subproject it should be sufficient to go
into its toplevel directory (here: "libburn_pykix") and execute
./configure
To build a libburnia-project.org subproject it should be sufficient to go
into its toplevel directory (here: "libburn") and execute
./configure --prefix=/usr
make
To make the libraries accessible for running resp. developing applications
@ -44,16 +44,16 @@ To make the libraries accessible for running resp. developing applications
The other half of the project, libisofs, is hosted in the libburnia SVN, too:
svn co http://libburnia-svn.pykix.org/libisofs/trunk libisofs_pykix
svn co http://svn.libburnia-project.org/libisofs/trunk libisofs
See README file there.
------------------------------------------------------------------------------
Overview of libburnia.pykix.org
Overview of libburnia-project.org
libburnia.pykix.org is an open-source software project for reading, mastering
libburnia-project.org is an open-source software project for reading, mastering
and writing optical discs.
For now this means only CD media and all single layer DVD media except DVD+R.

64
TODO Normal file
View File

@ -0,0 +1,64 @@
GENERAL
=======
Improve documentation
Build system improvements
Clarify licencing (GPL2 only (!) with exception)
FEATURES
========
El-Torito
Support for multiple images
HFS/HFS+
CD reading
[ok] plain iso
[ok] Rock Ridge
[ok] Joliet
Merge RR and Joliet trees
[ok] User options to customize reading
[ok] Read El-Torito info
[ok] Multisession
[ok] DVD+RW image growing
El-Torito images from previous session
Add new el-torito image from prev. session file
Path for isolinux from prev. session
UDF
[ok] ISO relaxed contraints
ISO 9660:1998
Support for special files (only dirs, reg. files and symlinks are supported).
Modification of timestamps attribs on nodes
TESTS
=====
[several done]
Test all util.h functions, especially date-related ones
Test for RR read functions
For all
IMPLEMENTATION
==============
a way to return NULL sources meaning a failure!!
[ok] Error message queue
Better charset support
[ok] default input charset to locale one, no always UTF-8
add charset management on image reading
use iso-8859-1 instead of UTF-8 on RR?
[ok] Improve date handling
for DVD+RW, the VD to be written at the beginning of disc must be
returned as 32KB block
Sources to write file contents
encyrption/compression of files
auto cut of files to 2gb limit
BUGS
====
Joliet names need ";1" at the end
RR Continuation Areas can't be in Directory Record block
[ok] Fix mangle names when iso relaxed constraints

View File

@ -1,4 +1,4 @@
AC_INIT([libisofs], [0.2.5], [http://libburnia-project.org])
AC_INIT([libisofs], [0.2.9], [http://libburnia-project.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=5
BURN_MICRO_VERSION=4
BURN_INTERFACE_AGE=0
BURN_BINARY_AGE=0
BURN_VERSION=$BURN_MAJOR_VERSION.$BURN_MINOR_VERSION.$BURN_MICRO_VERSION
@ -39,7 +39,7 @@ AC_SUBST(BURN_VERSION)
dnl Libtool versioning
LT_RELEASE=$BURN_MAJOR_VERSION.$BURN_MINOR_VERSION
LT_CURRENT=4
LT_CURRENT=`expr $BURN_MICRO_VERSION - $BURN_INTERFACE_AGE`
LT_REVISION=$BURN_INTERFACE_AGE
LT_AGE=`expr $BURN_BINARY_AGE - $BURN_INTERFACE_AGE`
LT_CURRENT_MINUS_AGE=`expr $LT_CURRENT - $LT_AGE`
@ -112,6 +112,6 @@ AC_CONFIG_FILES([
Makefile
doc/doxygen.conf
version.h
libisofs-1.pc
libisofs-5.pc
])
AC_OUTPUT

File diff suppressed because it is too large Load Diff

0
libisofs/Makefile Executable file → Normal file
View File

107
libisofs/data_source.c Normal file
View File

@ -0,0 +1,107 @@
/*
* Contains a simple implementation of a data source that reads from a
* given file.
*/
#include <assert.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>
#include "libisofs.h"
#define BLOCK_SIZE 2048
#define BLOCK_OUT_OF_FILE -1;
#define READ_ERROR -2;
#define SEEK_ERROR -3;
struct file_data_src {
int fd;
int nblocks;
};
static int
ds_read_block(struct data_source *src, int lba, unsigned char *buffer)
{
struct file_data_src *data;
assert(src && buffer);
data = (struct file_data_src*)src->data;
/* For block devices size is always 0, so this can't be used.
* if (lba >= data->nblocks)
* return BLOCK_OUT_OF_FILE;
*/
/* goes to requested block */
if ( lseek(data->fd, (off_t)lba * (off_t)BLOCK_SIZE, SEEK_SET) == (off_t) -1 )
return SEEK_ERROR;
if ( read(data->fd, buffer, BLOCK_SIZE) != BLOCK_SIZE )
return READ_ERROR;
return 0;
}
static int ds_get_size(struct data_source *src)
{
struct file_data_src *data;
assert(src);
data = (struct file_data_src*)src->data;
return data->nblocks;
}
static void ds_free_data(struct data_source *src)
{
struct file_data_src *data;
assert(src);
data = (struct file_data_src*)src->data;
/* close the file */
close(data->fd);
free(data);
}
struct data_source *data_source_from_file(const char *path)
{
int fd;
struct stat info;
struct file_data_src *data;
struct data_source *ret;
assert(path);
fd = open(path, O_RDONLY);
if (fd == -1)
return NULL;
fstat(fd, &info);
data = malloc(sizeof(struct file_data_src));
data->fd = fd;
data->nblocks = info.st_size / BLOCK_SIZE;
ret = malloc(sizeof(struct data_source));
ret->refcount = 1;
ret->read_block = ds_read_block;
ret->get_size = ds_get_size;
ret->free_data = ds_free_data;
ret->data = data;
return ret;
}
void data_source_free(struct data_source *src)
{
if (--src->refcount == 0) {
src->free_data(src);
free(src);
}
}

587
libisofs/ecma119.c Executable file → Normal file
View File

@ -7,6 +7,9 @@
#include <time.h>
#include <assert.h>
#include <err.h>
#include <locale.h>
#include <langinfo.h>
#include <limits.h>
#include "ecma119.h"
#include "ecma119_tree.h"
@ -16,8 +19,12 @@
#include "volume.h"
#include "tree.h"
#include "util.h"
#include "file.h"
#include "file_src.h"
#include "libisofs.h"
#include "libburn/libburn.h"
#include "eltorito.h"
#include "messages.h"
/* burn-source compatible stuff */
static int
@ -77,6 +84,7 @@ static const write_fn writers[] =
NULL,
wr_system_area,
wr_pri_vol_desc,
el_torito_wr_boot_vol_desc,
joliet_wr_sup_vol_desc,
wr_vol_desc_term,
wr_l_path_table,
@ -85,6 +93,7 @@ static const write_fn writers[] =
joliet_wr_m_path_table,
wr_dir_records,
joliet_wr_dir_records,
el_torito_wr_catalog,
wr_files
};
@ -101,30 +110,35 @@ add_susp_fields_rec(struct ecma119_write_target *t,
{
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);
switch (node->type) {
case ECMA119_FILE:
break;
case ECMA119_SYMLINK:
rrip_add_SL(t, node);
break;
case ECMA119_DIR:
if (node->info.dir.real_parent != node->parent) {
rrip_add_RE(t, node);
rrip_add_PL(t, node);
}
for (i = 0; i < node->info.dir.nchildren; i++) {
add_susp_fields_rec(t, node->info.dir.children[i]);
}
break;
case ECMA119_PLACEHOLDER:
rrip_add_CL(t, node);
break;
default:
// FIXME support for device blocks by uncommenting this
//if (node->iso_self->attrib.st_rdev)
// rrip_add_PN(t, node);
break;
}
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
@ -151,34 +165,28 @@ calc_dir_size(struct ecma119_write_target *t,
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->info.dir.len = 34 + dir->info.dir.self_susp.non_CE_len
+ 34 + dir->info.dir.parent_susp.non_CE_len;
dir->info.dir.CE_len = dir->info.dir.self_susp.CE_len
+ dir->info.dir.parent_susp.CE_len;
for (i = 0; i < dir->info.dir.nchildren; ++i) {
struct ecma119_tree_node *ch = dir->info.dir.children[i];
newlen = dir->dir.len + ch->dirent_len + ch->susp.non_CE_len;
if ((newlen % 2048) < (dir->dir.len % 2048)) {
dir->dir.len = newlen + (2048 - (dir->dir.len % 2048));
newlen = dir->info.dir.len + ch->dirent_len + ch->susp.non_CE_len;
if ((newlen % 2048) < (dir->info.dir.len % 2048)) {
dir->info.dir.len = newlen + (2048 - (dir->info.dir.len % 2048));
} else {
dir->info.dir.len += ch->dirent_len + ch->susp.non_CE_len;
}
else {
dir->dir.len += ch->dirent_len + ch->susp.non_CE_len;
}
dir->dir.CE_len += ch->susp.CE_len;
dir->info.dir.CE_len += ch->susp.CE_len;
}
t->total_dir_size += round_up(dir->dir.len + dir->dir.CE_len,
t->total_dir_size += round_up(dir->info.dir.len + dir->info.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;
for (i = 0; i < dir->info.dir.nchildren; i++) {
struct ecma119_tree_node *ch = dir->info.dir.children[i];
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++;
}
}
}
@ -195,97 +203,165 @@ calc_dir_pos(struct ecma119_write_target *t,
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);
dir->info.dir.block = t->curblock;
t->curblock += div_up(dir->info.dir.len + dir->info.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];
for (i = 0; i < dir->info.dir.nchildren; i++) {
struct ecma119_tree_node *ch = dir->info.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;
}
static int
cmp_file(const void *f1, const void *f2)
{
struct iso_file *f = *((struct iso_file**)f1);
struct iso_file *g = *((struct iso_file**)f2);
/* higher weighted first */
return g->sort_weight - f->sort_weight;
}
/**
* Fill out the block field for each ecma119_tree_node that is a file and fill
* out t->filelist.
* Fill out the block field for each file and fill out t->filelist.
*/
static void
calc_file_pos(struct ecma119_write_target *t,
struct ecma119_tree_node *dir)
calc_file_pos(struct ecma119_write_target *t)
{
size_t i;
assert(dir->type == ECMA119_DIR);
assert(t);
t->filelist = calloc(1, sizeof(struct iso_file *) * t->file_table->count);
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;
for (i = 0; i < FILE_HASH_NODES; ++i) {
struct iso_file_hash_node *node;
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;
}
node = t->file_table->table[i];
if (!node)
continue;
do {
struct iso_file *file = node->file;
/*
* We only need to write the file when.
* a) The size is greater than 0.
* b) The file is new (not from a previous image) or we
* are writting an image not for ms (i.e., we are modifying an
* image).
*/
if ( file->size && (!file->prev_img || !t->ms_block) )
t->filelist[t->curfile++] = file;
node = node->next;
} while (node);
}
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);
t->filelist_len = t->curfile;
/* sort */
if ( t->sort_files )
qsort(t->filelist, t->filelist_len, sizeof(void*), cmp_file);
/* fill block value */
for ( i = 0; i < t->filelist_len; ++i) {
struct iso_file *file = t->filelist[i];
file->block = t->curblock;
t->curblock += div_up(file->size, t->block_size);
}
/* reset curfile when we're finished */
if (!dir->parent) {
t->curfile = 0;
}
t->curfile = 0;
}
struct ecma119_write_target*
/**
* 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.
*/
static struct ecma119_write_target*
ecma119_target_new(struct iso_volset *volset,
int volnum,
int level,
int flags)
struct ecma119_source_opts *opts)
{
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;
struct iso_tree_node *iso_root =
(struct iso_tree_node*) volset->volume[opts->volnum]->root;
t->cache_inodes = opts->no_cache_inodes ? 0 : 1;
t->replace_mode = opts->default_mode ? 0 : 1;
if ( opts->replace_dir_mode )
t->replace_mode |= 0x02;
if ( opts->replace_file_mode )
t->replace_mode |= 0x04;
if ( opts->replace_gid )
t->replace_mode |= 0x08;
if ( opts->replace_uid )
t->replace_mode |= 0x10;
t->dir_mode = opts->dir_mode;
t->file_mode = opts->file_mode;
t->gid = opts->gid;
t->uid = opts->uid;
if (opts->input_charset) {
t->input_charset = opts->input_charset;
} else {
/* default to locale charset */
setlocale(LC_CTYPE, "");
t->input_charset = nl_langinfo(CODESET);
}
t->ouput_charset = opts->ouput_charset ? opts->ouput_charset : "UTF-8";
t->sort_files = opts->sort_files;
t->ms_block = opts->ms_block;
t->src = opts->src;
t->file_table = iso_file_table_new(t->cache_inodes);
volset->refcount++;
t->iso_level = opts->level;
t->block_size = 2048;
t->relaxed_constraints = opts->relaxed_constraints;
t->rockridge = (opts->flags & ECMA119_ROCKRIDGE) ? 1 : 0;
t->joliet = (opts->flags & ECMA119_JOLIET) ? 1 : 0;
t->catalog = volset->volume[opts->volnum]->bootcat;
t->eltorito = t->catalog ? 1 : 0;
t->write_eltorito = opts->copy_eltorito;
if (t->eltorito && (!t->ms_block || !t->catalog->proc) ) {
/*
* For new and modified images we always need to write el-torito.
* For ms images, if the boot catalog was newly added, we also need
* to write it!
*/
t->write_eltorito = 1;
}
/* create the trees */
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->volnum = opts->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->pathlist_joliet = calloc(1, sizeof(void*) * t->dirlist_len_joliet);
t->dirlist_joliet = calloc(1, sizeof(void*) * t->dirlist_len_joliet);
}
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;
@ -293,20 +369,23 @@ ecma119_target_new(struct iso_volset *volset,
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];
for (j = 0; j < dir->info.dir.nchildren; j++) {
struct ecma119_tree_node *ch = dir->info.dir.children[j];
if (ch->type == ECMA119_DIR) {
size_t len = 8 + strlen(ch->name);
size_t len = 8 + strlen(ch->iso_name);
t->pathlist[cur++] = ch;
t->path_table_size += len + len % 2;
}
}
}
t->curblock = 16 /* system area */
+ 1 /* volume desc */
+ 1; /* volume desc terminator */
t->curblock = t->ms_block /* nwa for ms, usually 0 */
+ 16 /* system area */
+ 1 /* primary volume desc */
+ 1; /* volume desc terminator */
if (t->eltorito)
t->curblock += 1; /* boot record volume descriptor */
if (t->joliet) /* supplementary vol desc */
t->curblock += div_up (2048, t->block_size);
@ -323,23 +402,84 @@ ecma119_target_new(struct iso_volset *volset,
}
calc_dir_pos(t, t->root);
if (t->joliet)
/* reset curfile when we're finished */
t->curfile = 0;
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);
/* reset curfile when we're finished */
t->curfile = 0;
}
/* el-torito? */
if (t->eltorito) {
if (t->write_eltorito) {
/* add catalog block */
t->catblock = t->curblock;
t->curblock += div_up(2048, t->block_size);
/* add img block */
t->imgblock = t->curblock;
t->curblock += div_up(t->catalog->image->node->node.attrib.st_size,
t->block_size);
} else {
assert(t->ms_block);
assert(t->catalog->proc);
t->catblock = t->catalog->node->loc.block;
t->imgblock = t->catalog->image->node->loc.block;
}
}
calc_file_pos(t);
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;
t->total_size = (t->curblock - t->ms_block) * t->block_size;
if (opts->overwrite) {
/*
* Get a copy of the volume descriptors to be written in a DVD+RW
* disc
*/
uint8_t *buf;
/* skip the first 16 blocks (system area) */
buf = opts->overwrite + 16 * t->block_size;
/*
* In the PVM to be written in the 16th sector of the disc, we
* need to specify the full size.
*/
t->vol_space_size = t->curblock;
write_pri_vol_desc(t, buf);
buf += t->block_size;
if (t->joliet) {
joliet_write_sup_vol_desc(t, buf);
buf += t->block_size;
}
if (t->eltorito) {
el_torito_write_boot_vol_desc(t, buf);
buf += t->block_size;
}
write_vol_desc_terminator(t, buf);
}
/*
* The volume space size is just the size of the last session, in
* case of ms images.
*/
t->vol_space_size = t->curblock - t->ms_block;
/* prepare for writing */
t->curblock = 0;
t->state = ECMA119_WRITE_SYSTEM_AREA;
t->bytes_read = 2048;
return t;
}
@ -353,14 +493,25 @@ is_joliet_state(enum ecma119_write_state state)
|| state == ECMA119_WRITE_DIR_RECORDS_JOLIET;
}
static int
is_eltorito_state(enum ecma119_write_state state)
{
return state == ECMA119_WRITE_ELTORITO_BOOT_VOL_DESC
|| state == ECMA119_WRITE_ELTORITO_CATALOG;
}
static void
next_state(struct ecma119_write_target *t)
{
char msg[42];
t->state++;
while (!t->joliet && is_joliet_state(t->state))
while ( (!t->joliet && is_joliet_state(t->state))
||(!t->eltorito && is_eltorito_state(t->state))
||(!t->write_eltorito && t->state == ECMA119_WRITE_ELTORITO_CATALOG) )
t->state++;
printf ("now in state %d, curblock=%d\n", (int)t->state, (int)t->curblock);
sprintf(msg, "Now in state %d, curblock=%d.", t->state, t->curblock);
iso_msg_debug(msg);
}
static void
@ -404,29 +555,33 @@ wr_dir_records(struct ecma119_write_target *t, uint8_t *buf)
static void
wr_files(struct ecma119_write_target *t, uint8_t *buf)
{
int res;
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;
struct iso_file *f = t->filelist[f_st->file];
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);
if (!f_st->src) {
if (!f->prev_img) {
char msg[PATH_MAX + 14];
sprintf(msg, "Writing file %s", f->path);
iso_msg_debug(msg);
}
f_st->src = f->src;
if ( f->src->open(f->src) <= 0) {
//FIXME instead of exit, just print a msg error and
//skip file (what about reading from /dev/zero? instead)
/* can only happen with new files */
err(1, "couldn't open %s for reading", f->path);
}
assert(t->curblock + t->ms_block == 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;
res = f_st->src->read_block(f_st->src, buf);
if (res == 0) {
/* we have read the expected bytes from file */
f_st->src->close(f_st->src);
f_st->src = NULL;
f_st->file++;
if (f_st->file >= t->filelist_len)
next_state(t);
@ -438,15 +593,25 @@ 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);
char *vol_id = str2d_char(volume->volume_id, t->input_charset);
char *pub_id = str2a_char(volume->publisher_id, t->input_charset);
char *data_id = str2a_char(volume->data_preparer_id, t->input_charset);
char *volset_id = str2d_char(t->volset->volset_id, t->input_charset);
char *system_id = str2a_char(volume->system_id, t->input_charset);
char *application_id = str2a_char(volume->application_id, t->input_charset);
char *copyright_file_id = str2d_char(volume->copyright_file_id, t->input_charset);
char *abstract_file_id = str2d_char(volume->abstract_file_id, t->input_charset);
char *biblio_file_id = str2d_char(volume->biblio_file_id, t->input_charset);
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 (system_id)
strncpy((char*)vol->system_id, system_id, 32);
else
/* put linux by default? */
memcpy(vol->system_id, "LINUX", 5);
if (vol_id)
strncpy((char*)vol->volume_id, vol_id, 32);
iso_bb(vol->vol_space_size, t->vol_space_size, 4);
@ -459,10 +624,19 @@ write_pri_vol_desc(struct ecma119_write_target *t, uint8_t *buf)
write_one_dir_record(t, t->root, 3, vol->root_dir_record);
/* mmm, why not check for null? */
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);
if (application_id)
strncpy((char*)vol->application_id, application_id, 128);
if (copyright_file_id)
strncpy((char*)vol->copyright_file_id, copyright_file_id, 37);
if (abstract_file_id)
strncpy((char*)vol->abstract_file_id, abstract_file_id, 37);
if (biblio_file_id)
strncpy((char*)vol->bibliographic_file_id, biblio_file_id, 37);
iso_datetime_17(vol->vol_creation_time, t->now);
iso_datetime_17(vol->vol_modification_time, t->now);
@ -473,6 +647,11 @@ write_pri_vol_desc(struct ecma119_write_target *t, uint8_t *buf)
free(volset_id);
free(pub_id);
free(data_id);
free(system_id);
free(application_id);
free(copyright_file_id);
free(abstract_file_id);
free(biblio_file_id);
}
static void
@ -498,17 +677,18 @@ write_path_table(struct ecma119_write_target *t, int l_type, uint8_t *buf)
for (i = 0; i < t->dirlist_len; i++) {
dir = t->pathlist[i];
assert(dir->type == ECMA119_DIR);
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_di[0] = dir->parent ? (uint8_t) strlen(dir->iso_name) : 1;
rec->len_xa[0] = 0;
write_int(rec->block, dir->block, 4);
write_int(rec->block, dir->info.dir.block, 4);
write_int(rec->parent, parent + 1, 2);
if (dir->parent)
memcpy(rec->dir_id, dir->name, rec->len_di[0]);
memcpy(rec->dir_id, dir->iso_name, rec->len_di[0]);
buf += 8 + rec->len_di[0] + (rec->len_di[0] % 2);
}
}
@ -534,22 +714,46 @@ write_one_dir_record(struct ecma119_write_target *t,
int file_id,
uint8_t *buf)
{
uint32_t len;
uint32_t block;
uint8_t len_dr = (file_id >= 0) ? 34 : node->dirent_len;
uint8_t len_fi = (file_id >= 0) ? 1 : strlen(node->name);
uint8_t len_fi = (file_id >= 0) ? 1 : strlen(node->iso_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;
uint8_t *name = (file_id >= 0) ? &f_id : (uint8_t*)node->iso_name;
struct ecma119_dir_record *rec = (struct ecma119_dir_record*)buf;
if (node->type == ECMA119_DIR) {
len = node->info.dir.len;
block = node->info.dir.block;
} else if (node->type == ECMA119_FILE) {
len = node->info.file->size;
block = node->info.file->block;
} else if (node->type == ECMA119_BOOT) {
assert(t->eltorito);
if (node->info.boot_img) {
block = t->imgblock;
len = t->catalog->image->node->node.attrib.st_size;
} else {
/* we always assume 2048 as catalog len */
block = t->catblock;
len = 2048;
}
} else {
/* for nodes other than files and dirs, we set both len and block to 0 */
len = 0;
block = 0;
}
/* 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;
assert(node->type == ECMA119_DIR);
susp_write(t, &node->info.dir.self_susp, &buf[len_dr]);
len_dr += node->info.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;
assert(node->type == ECMA119_DIR);
susp_write(t, &node->info.dir.parent_susp, &buf[len_dr]);
len_dr += node->info.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;
@ -559,7 +763,7 @@ write_one_dir_record(struct ecma119_write_target *t,
node = node->parent;
rec->len_dr[0] = len_dr;
iso_bb(rec->block, node->block, 4);
iso_bb(rec->block, 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;
@ -587,8 +791,8 @@ write_one_dir(struct ecma119_write_target *t,
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);
for (i = 0; i < dir->info.dir.nchildren; i++) {
write_one_dir_record(t, dir->info.dir.children[i], -1, buf);
len = ((struct ecma119_dir_record*) buf)->len_dr[0];
if ((buf + len - prior_buf) >= 2048) {
for (j = len - 1; j >= 0; j--) {
@ -605,16 +809,16 @@ write_one_dir(struct ecma119_write_target *t,
/* 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;
susp_write_CE(t, &dir->info.dir.self_susp, buf);
buf += dir->info.dir.self_susp.CE_len;
susp_write_CE(t, &dir->info.dir.parent_susp, buf);
buf += dir->info.dir.parent_susp.CE_len;
for (i = 0; i < dir->info.dir.nchildren; i++) {
susp_write_CE(t, &dir->info.dir.children[i]->susp, buf);
buf += dir->info.dir.children[i]->susp.CE_len;
}
}
assert (buf - orig_buf == dir->dir.len + dir->dir.CE_len);
assert (buf - orig_buf == dir->info.dir.len + dir->info.dir.CE_len);
}
static void
@ -625,7 +829,7 @@ write_dirs(struct ecma119_write_target *t, uint8_t *buf)
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);
buf += round_up(dir->info.dir.len + dir->info.dir.CE_len, t->block_size);
}
}
@ -660,22 +864,57 @@ write_data_chunk(struct ecma119_write_target *t, uint8_t *buf)
}
static int
bs_read(struct burn_source *bs, unsigned char *buf, int size)
bs_read_block(struct burn_source *bs)
{
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) {
if (t->curblock >= t->vol_space_size) {
/* total_size could be setted by libburn */
if ( t->curblock < (t->total_size / (off_t) t->block_size) ) {
/* we pad the image */
memset(t->buffer, 0, t->block_size);
t->curblock++;
return t->block_size;
}
return 0;
}
if (t->state_data_valid)
write_data_chunk(t, buf);
write_data_chunk(t, t->buffer);
else
writers[t->state](t, buf);
writers[t->state](t, t->buffer);
t->curblock++;
return size;
return t->block_size;
}
static int
bs_read(struct burn_source *bs, unsigned char *buf, int size)
{
int ret = 0,summed_ret = 0;
struct ecma119_write_target *t = (struct ecma119_write_target*)bs->data;
/* make safe against partial buffer returns */
while (1) {
if (t->bytes_read == 2048) {
/* we need to read next block */
t->bytes_read = 0;
ret = bs_read_block(bs);
if (ret <= 0)
return ret;
}
int bytes_to_read = 2048 - t->bytes_read;
if (summed_ret + bytes_to_read > size) {
bytes_to_read = size - summed_ret;
}
memcpy(buf + summed_ret, t->buffer, bytes_to_read);
t->bytes_read += bytes_to_read;
summed_ret += bytes_to_read;
if (summed_ret >= size)
break;
}
return summed_ret;
}
static off_t
@ -690,26 +929,38 @@ bs_free_data(struct burn_source *bs)
{
struct ecma119_write_target *t = (struct ecma119_write_target*)bs->data;
ecma119_tree_free(t->root);
iso_file_table_clear(t->file_table);
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);
if (t->joliet)
joliet_tree_free(t->joliet_root);
// TODO is this needed?
if (t->state_files.src)
t->state_files.src->close(t->state_files.src);
}
int bs_set_size(struct burn_source *bs, off_t size)
{
struct ecma119_write_target *t = (struct ecma119_write_target*)bs->data;
t->total_size = size;
return 1;
}
struct burn_source *iso_source_new_ecma119(struct iso_volset *volset,
int volnum,
int level,
int flags)
struct ecma119_source_opts *opts)
{
struct burn_source *ret = calloc(1, sizeof(struct burn_source));
ret->refcount = 1;
ret->read = bs_read;
ret->get_size = bs_get_size;
ret->set_size = bs_set_size;
ret->free_data = bs_free_data;
ret->data = ecma119_target_new(volset, volnum, level, flags);
ret->data = ecma119_target_new(volset, opts);
return ret;
}

99
libisofs/ecma119.h Executable file → Normal file
View File

@ -29,6 +29,7 @@ enum ecma119_write_state
ECMA119_WRITE_SYSTEM_AREA,
ECMA119_WRITE_PRI_VOL_DESC,
ECMA119_WRITE_ELTORITO_BOOT_VOL_DESC,
ECMA119_WRITE_SUP_VOL_DESC_JOLIET,
ECMA119_WRITE_VOL_DESC_TERMINATOR,
ECMA119_WRITE_L_PATH_TABLE,
@ -37,6 +38,7 @@ enum ecma119_write_state
ECMA119_WRITE_M_PATH_TABLE_JOLIET,
ECMA119_WRITE_DIR_RECORDS,
ECMA119_WRITE_DIR_RECORDS_JOLIET,
ECMA119_WRITE_ELTORITO_CATALOG,
ECMA119_WRITE_FILES,
ECMA119_WRITE_DONE
@ -61,7 +63,57 @@ struct ecma119_write_target
unsigned int rockridge:1;
unsigned int joliet:1;
unsigned int iso_level:2;
unsigned int eltorito:1;
unsigned int write_eltorito:1;
/**<
* In multisession discs, select whether to copy el-torito catalog
* and boot image. Copy is needed for isolinux images, that need to
* be patched. However, it can lead to problems when the image is
* not present in the iso filesystem, because we can't figure out
* its size. In those cases, we only copy 1 block of data.
* When modifying images, we always need to copy data. Thus, this is
* always 1 for both new and modified images.
*/
struct el_torito_boot_catalog *catalog;
uint32_t catblock; /**< location of the boot catalog in the new image */
uint32_t imgblock; /**< location of the boot image in the new image */
int relaxed_constraints; /**< see ecma119_relaxed_constraints_flag */
int replace_mode; /**< Replace ownership and modes of files
*
* 0. filesystem values
* 1. useful values
* bits 1-4 bitmask:
* 2 - replace dir
* 3 - replace file
* 4 - replace gid
* 5 - replace uid
*/
mode_t dir_mode;
mode_t file_mode;
gid_t gid;
uid_t uid;
char *input_charset;
char *ouput_charset;
int cache_inodes;
int sort_files; /**< if sort files or not. Sorting is based of
* the weight of each file */
/**
* In the CD, each file must have an unique inode number. So each
* time we add a new file, this is incremented.
*/
ino_t ino;
uint32_t ms_block; /**< if != 0, nwa for multisession */
struct data_source* src;
int curblock;
uint16_t block_size;
uint32_t path_table_size;
@ -86,10 +138,13 @@ struct ecma119_write_target
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.
struct iso_file_table *file_table;
/**<
* A hash table with info about all files
*/
struct iso_file **filelist; /**< A pre-order list of files.*/
size_t filelist_len; /* Length of the previous list. */
int curfile; /**< Used as a helper field for writing
@ -101,6 +156,8 @@ struct ecma119_write_target
*/
struct joliet_tree_node **dirlist_joliet;
struct joliet_tree_node **pathlist_joliet;
size_t dirlist_len_joliet;
enum ecma119_write_state state; /* The current state of the writer. */
@ -117,30 +174,15 @@ struct ecma119_write_target
/* 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. */
struct iso_file_src *src; /* source for reading from the 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);
/* temp buffer for read functions */
uint8_t buffer[2048];
int bytes_read;
};
#define BP(a,b) [(b) - (a) + 1]
@ -218,6 +260,17 @@ struct ecma119_sup_vol_desc
uint8_t reserved2 BP(1396, 2048);
};
struct ecma119_boot_rec_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 boot_sys_id BP(8, 39);
uint8_t boot_id BP(40, 71);
uint8_t boot_catalog BP(72, 75);
uint8_t unused BP(76, 2048);
};
struct ecma119_vol_desc_terminator
{
uint8_t vol_desc_type BP(1, 1);

888
libisofs/ecma119_read.c Normal file
View File

@ -0,0 +1,888 @@
/*
* Functions to read an ISO image.
*/
/*
* TODO
* we need some kind of force option, to continue reading image on
* minor errors, such as incorrect time stamps....
*
* TODO
* need to check the ZF linux-especific extension for transparent decompresion
* TODO
* what the RR entry is?
*/
#include <assert.h>
#include <string.h>
#include <stdlib.h>
#include <time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include "ecma119_read.h"
#include "ecma119_read_rr.h"
#include "ecma119.h"
#include "util.h"
#include "volume.h"
#include "tree.h"
#include "messages.h"
#include "eltorito.h"
#define BLOCK_SIZE 2048
static int
iso_read_dir(struct iso_read_info *info, struct iso_tree_node_dir *parent,
uint32_t block);
static struct el_torito_boot_catalog *
read_el_torito_boot_catalog(struct iso_read_info *info, uint32_t block)
{
struct el_torito_validation_entry *ve;
struct el_torito_default_entry *entry;
struct el_torito_boot_catalog *catalog;
struct el_torito_boot_image *image;
unsigned char buffer[BLOCK_SIZE];
if ( info->src->read_block(info->src, block, buffer) < 0 ) {
info->error = LIBISOFS_READ_FAILURE;
return NULL;
}
ve = (struct el_torito_validation_entry*)buffer;
/* check if it is a valid catalog (TODO: check also the checksum)*/
if ( (ve->header_id[0] != 1) || (ve->key_byte1[0] != 0x55)
|| (ve->key_byte2[0] != 0xAA) ) {
iso_msg_sorry(LIBISO_EL_TORITO_WRONG, "Wrong or damaged El-Torito "
"Catalog.\n El-Torito info will be ignored.");
return NULL;
}
/* check for a valid platform */
if (ve->platform_id[0] != 0) {
iso_msg_hint(LIBISO_EL_TORITO_UNHANLED, "Unsupported El-Torito platform.\n"
"Only 80x86 si supported. El-Torito info will be ignored.");
}
/* ok, once we are here we assume it is a valid catalog */
catalog = malloc(sizeof(struct el_torito_boot_catalog));
image = calloc(1, sizeof(struct el_torito_boot_image));
catalog->image = image;
catalog->proc = LIBISO_PREVIMG;
{
/*
* Create the placeholder.
* Note that this could be modified later if we find a directory entry
* for the catalog in the iso tree.
*/
struct iso_tree_node_boot *boot;
boot = calloc(1, sizeof(struct iso_tree_node_boot));
boot->node.refcount = 1;
boot->node.attrib.st_mode = S_IFREG | 0777;
boot->node.attrib.st_atime = boot->node.attrib.st_mtime
= boot->node.attrib.st_ctime = time(NULL);
boot->node.attrib.st_size = 2048;
boot->node.type = LIBISO_NODE_BOOT;
boot->node.procedence = LIBISO_PREVIMG;
boot->node.name = NULL;
boot->loc.block = block;
catalog->node = boot;
}
/* parse the default entry */
entry = (struct el_torito_default_entry *)(buffer + 32);
image->bootable = entry->boot_indicator[0] ? 1 : 0;
//FIXME we need a way to handle patch_isolinux in ms images!!!
image->isolinux = 0;
image->type = entry->boot_media_type[0];
image->partition_type = entry->system_type[0];
image->load_seg = iso_read_lsb(entry->load_seg, 2);
image->load_size = iso_read_lsb(entry->sec_count, 2);
{
/*
* Create the placeholder.
* Note that this could be modified later if we find a directory entry
* for the image in the iso tree.
*/
struct iso_tree_node_boot *boot;
boot = calloc(1, sizeof(struct iso_tree_node_boot));
boot->node.refcount = 1;
boot->node.attrib.st_mode = S_IFREG | 0777;
boot->node.attrib.st_atime = boot->node.attrib.st_mtime
= boot->node.attrib.st_ctime = time(NULL);
boot->node.attrib.st_size = 2048;
boot->node.type = LIBISO_NODE_BOOT;
boot->node.procedence = LIBISO_PREVIMG;
boot->node.name = NULL;
boot->img = 1;
boot->loc.block = iso_read_lsb(entry->block, 4);
image->node = boot;
}
//TODO how can we check if there are more entries?
return catalog;
}
static struct el_torito_boot_catalog *
read_el_torito_vol_desc(struct iso_read_info *info, unsigned char *buf)
{
struct ecma119_boot_rec_vol_desc *vol;
vol = (struct ecma119_boot_rec_vol_desc*)buf;
/* some sanity checks */
if ( strncmp((char*)vol->std_identifier, "CD001", 5)
|| vol->vol_desc_version[0] != 1
|| strncmp((char*)vol->boot_sys_id, "EL TORITO SPECIFICATION", 23)) {
iso_msg_hint(LIBISO_BOOT_VD_UNHANLED, "Unsupported Boot Vol. Desc.\n"
"Only El-Torito Specification, Version 1.0 Volume "
"Descriptors are supported. Ignoring boot info");
return NULL;
}
return read_el_torito_boot_catalog(info, iso_read_lsb(vol->boot_catalog, 4));
}
/**
* This reads the "." directory entry, and set the properties of the
* given directory propertly.
*/
static int
iso_read_dot_record(struct iso_read_info *info,
struct iso_tree_node_dir *dir,
struct ecma119_dir_record *record)
{
struct susp_sys_user_entry *sue;
struct susp_iterator *iter;
assert( info && dir && record );
iter = susp_iter_new(info, record);
while ( (sue = susp_iter_next(iter)) ) {
/* ignore entries from different version */
if (sue->version[0] != 1)
continue;
/* we don't care about any RR entry but PX and TF */
if (SUSP_SIG(sue, 'P', 'X')) {
if (read_rr_PX(info, sue, &dir->node.attrib))
break;
} else if (SUSP_SIG(sue, 'T', 'F')) {
if (read_rr_TF(info, sue, &dir->node.attrib))
break;
}
}
susp_iter_free(iter);
if (info->error)
return -1;
return 0;
}
/**
* Creates a suitable iso_tree_node from a directory record, and adds
* it to parent dir. If the directory record refers to a dir, it calls
* recursively iso_read_dir.
* On success, return 0.
* If file is not supported, return 0 but a new tree node is not added
* to parent.
* On serious error, returns -1
*/
static int
iso_read_single_directory_record(struct iso_read_info *info,
struct iso_tree_node_dir *parent,
struct ecma119_dir_record *record)
{
struct iso_tree_node *node;
struct stat atts;
time_t recorded;
char *name = NULL;
char *linkdest = NULL;
uint32_t relocated_dir = 0;
assert(info && record && parent);
memset(&atts, 0, sizeof(atts));
/*
* The idea is to read all the RR entries (if we want to do that and RR
* extensions exist on image), storing the info we want from that.
* Then, we need some sanity checks.
* Finally, we select what kind of node it is, and set values properly.
*/
if (info->rr) {
struct susp_sys_user_entry *sue;
struct susp_iterator *iter;
iter = susp_iter_new(info, record);
while ( (sue = susp_iter_next(iter)) ) {
/* ignore entries from different version */
if (sue->version[0] != 1)
continue;
if (SUSP_SIG(sue, 'P', 'X')) {
if (read_rr_PX(info, sue, &atts))
break;
} else if (SUSP_SIG(sue, 'T', 'F')) {
if (read_rr_TF(info, sue, &atts))
break;
} else if (SUSP_SIG(sue, 'N', 'M')) {
name = read_rr_NM(sue, name);
if (!name) {
info->error = LIBISOFS_WRONG_RR;
break;
}
} else if (SUSP_SIG(sue, 'S', 'L')) {
linkdest = read_rr_SL(sue, linkdest);
if (!linkdest) {
info->error = LIBISOFS_WRONG_RR;
break;
}
} else if (SUSP_SIG(sue, 'R', 'E')) {
/*
* this directory entry refers to a relocated directory.
* We simply ignore it, as it will be correctly handled
* when found the CL
*/
susp_iter_free(iter);
free(name);
return 0; /* is not an error */
} else if (SUSP_SIG(sue, 'C', 'L')) {
/*
* This entry is a placeholder for a relocated dir.
* We need to ignore other entries, with the exception of NM.
* Then we create a directory node that represents the
* relocated dir, and iterate over its children.
*/
relocated_dir = iso_read_bb(sue->data.CL.child_loc, 4, NULL);
} else if (SUSP_SIG(sue, 'S', 'F')) {
iso_msg_sorry(LIBISO_RR_UNSUPPORTED, "Sparse files not supported.");
info->error = LIBISOFS_UNSUPPORTED_IMAGE;
break;
} else if (SUSP_SIG(sue, 'R', 'R')) {
/* TODO I've seen this RR on mkisofs images. what's this? */
continue;
} else {
char msg[28];
sprintf(msg, "Unhandled SUSP entry %c%c.", sue->sig[0], sue->sig[1]);
iso_msg_hint(LIBISO_SUSP_UNHANLED, msg);
}
}
if ( !info->error && !relocated_dir && atts.st_mode == (mode_t) 0 ) {
iso_msg_sorry(LIBISO_RR_ERROR, "Mandatory Rock Ridge PX entry is "
"not present or it contains invalid values.");
info->error = LIBISOFS_WRONG_RR;
}
susp_iter_free(iter);
if (info->error)
return -1;
//TODO convert name to needed charset!!
} else {
/* RR extensions are not read / used */
atts.st_mode = info->mode;
atts.st_gid = info->gid;
atts.st_uid = info->uid;
if (record->flags[0] & 0x02)
atts.st_mode |= S_IFDIR;
else
atts.st_mode |= S_IFREG;
atts.st_ino = ++info->ino;
}
/*
* if we haven't RR extensions, or no NM entry is present,
* we use the name in directory record
*/
if (!name) {
size_t len;
name = info->get_name((char*)record->file_id, record->len_fi[0]);
/* remove trailing version number */
len = strlen(name);
if (len > 2 && name[len-2] == ';' && name[len-1] == '1') {
name[len-2] = '\0';
}
}
/*
* if we haven't RR extensions, or a needed TF time stamp is not present,
* we use plain iso recording time
*/
recorded = iso_datetime_read_7(record->recording_time);
if ( atts.st_atime == (time_t) 0 ) {
atts.st_atime = recorded;
}
if ( atts.st_ctime == (time_t) 0 ) {
atts.st_ctime = recorded;
}
if ( atts.st_mtime == (time_t) 0 ) {
atts.st_mtime = recorded;
}
/* the size is read from iso directory record */
atts.st_size = iso_read_bb(record->length, 4, NULL);
if (relocated_dir) {
/*
* Ensure that a placeholder for a relocated dir appears as
* a directory (mode & S_IFDIR).
* This is need because the placeholder is really a file, and
* in theory PX entry must be ignored.
* However, to make code clearer, we don't ignore it, because
* anyway it will be replaced by "." entry when recursing.
*/
atts.st_mode = S_IFDIR | (atts.st_mode & ~S_IFMT);
}
//TODO sanity checks!!
switch(atts.st_mode & S_IFMT) {
case S_IFDIR:
{
node = calloc(1, sizeof(struct iso_tree_node_dir));
node->type = LIBISO_NODE_DIR;
}
break;
case S_IFREG:
{
uint32_t block;
block = iso_read_bb(record->block, 4, NULL);
if (info->bootcat && block == info->bootcat->node->loc.block) {
/* it is the boot catalog */
node = (struct iso_tree_node*)info->bootcat->node;
} else if (info->bootcat && block == info->bootcat->image->node->loc.block) {
/* it is the boot image */
node = (struct iso_tree_node*)info->bootcat->image->node;
} else {
/* it is a file */
node = calloc(1, sizeof(struct iso_tree_node_file));
node->type = LIBISO_NODE_FILE;
/* set block with extend */
((struct iso_tree_node_file*)node)->loc.block = block;
}
}
break;
case S_IFLNK:
{
node = calloc(1, sizeof(struct iso_tree_node_symlink));
node->type = LIBISO_NODE_SYMLINK;
/* set the link dest */
((struct iso_tree_node_symlink*)node)->dest = linkdest;
}
break;
default:
iso_msg_sorry(LIBISO_RR_UNSUPPORTED, "File type not supported.");
return -1;
}
node->name = name;
node->attrib = atts;
node->refcount++; /* 1 for news, 2 for boot nodes */
node->procedence = LIBISO_PREVIMG;
iso_tree_add_child(parent, node);
if (node->type == LIBISO_NODE_DIR) {
uint32_t block;
if (relocated_dir)
block = relocated_dir;
else
block = iso_read_bb(record->block, 4, NULL);
/* add all children */
return iso_read_dir(info, (struct iso_tree_node_dir*)node, block);
} else
return 0;
}
/**
* Read all directory records in a directory, and creates a node for each
* of them, adding them to \p dir.
*/
static int
iso_read_dir(struct iso_read_info *info, struct iso_tree_node_dir *dir,
uint32_t block)
{
unsigned char buffer[2048];
struct ecma119_dir_record *record;
uint32_t size;
uint32_t pos = 0;
uint32_t tlen = 0;
if ( info->src->read_block(info->src, block, buffer) < 0 ) {
info->error = LIBISOFS_READ_FAILURE;
return -1;
}
/* Attributes of dir are set in the "." entry */
record = (struct ecma119_dir_record *)(buffer + pos);
size = iso_read_bb(record->length, 4, NULL);
if (info->rr)
iso_read_dot_record(info, dir, record);
tlen += record->len_dr[0];
pos += record->len_dr[0];
/* skip ".." */
record = (struct ecma119_dir_record *)(buffer + pos);
tlen += record->len_dr[0];
pos += record->len_dr[0];
while( tlen < size ) {
record = (struct ecma119_dir_record *)(buffer + pos);
if (pos == 2048 || record->len_dr[0] == 0) {
/*
* The directory entries are splitted in several blocks
* read next block
*/
if ( info->src->read_block(info->src, ++block, buffer) < 0 ) {
info->error = LIBISOFS_READ_FAILURE;
return -1;
}
tlen += 2048 - pos;
pos = 0;
/* next block must begin with a non-0 directory record */
assert(buffer[0] != 0);
continue;
}
/*
* What about ignoring files with existence flag?
* if (record->flags[0] & 0x01)
* continue;
*/
/*
* TODO
* For a extrange reason, mkisofs relocates directories under
* a RR_MOVED dir. It seems that it is only used for that purposes,
* and thus it should be removed from the iso tree before
* generating a new image with libisofs, that don't uses it.
* We can do that here, but I think it's a better option doing it
* on an app. using the library, such as genisofs.
*
* if ( record->len_fi[0] == 8 &&
* !strncmp(record->file_id,"RR_MOVED", 8) ) {
* continue;
* }
*/
/* check for unsupported multiextend */
if (record->flags[0] & 0x80) {
iso_msg_fatal(LIBISO_IMG_UNSUPPORTED, "Unsupported image.\n"
"This image makes use of Multi-Extend features, that "
"are not supported at this time.\n"
"If you need support for that, please request us this feature.\n"
"Thank you in advance\n");
info->error = LIBISOFS_UNSUPPORTED_IMAGE;
return -1;
}
/* check for unsupported interleaved mode */
if ( record->file_unit_size[0] || record->interleave_gap_size[0] ) {
iso_msg_fatal(LIBISO_IMG_UNSUPPORTED, "Unsupported image.\n"
"This image has at least one file recorded in "
"interleaved mode.\n"
"We don't support this mode, as we think it's not used.\n"
"If you're reading this, then we're wrong :)\n"
"Please contact libisofs developers, so we can fix this.\n"
"Thank you in advance\n");
info->error = LIBISOFS_UNSUPPORTED_IMAGE;
return -1;
}
//TODO check for unsupported extended attribs?
//TODO check for other flags?
if ( iso_read_single_directory_record(info, dir, record) )
return -1;
tlen += record->len_dr[0];
pos += record->len_dr[0];
}
return 0;
}
/**
* Read the SUSP system user entries of the "." entry of the root directory,
* indentifying when Rock Ridge extensions are being used.
*/
static int
read_root_susp_entries(struct iso_read_info *info,
struct iso_tree_node_dir *root,
uint32_t block)
{
unsigned char buffer[2048];
struct ecma119_dir_record *record;
struct susp_sys_user_entry *sue;
struct susp_iterator *iter;
if ( info->src->read_block(info->src, block, buffer) < 0 ) {
info->error = LIBISOFS_READ_FAILURE;
return -1;
}
/* record will be the "." directory entry for the root record */
record = (struct ecma119_dir_record *)buffer;
/*
* TODO
* SUSP specification claims that for CD-ROM XA the SP entry
* is not at position BP 1, but at BP 15. Is that used?
* In that case, we need to set info->len_skp to 15!!
*/
iter = susp_iter_new(info, record);
/* first entry must be an SP system use entry */
sue = susp_iter_next(iter);
if (!sue && info->error) {
susp_iter_free(iter);
return -1;
} else if (!sue || !SUSP_SIG(sue, 'S', 'P') ) {
iso_msg_debug("SUSP/RR is not being used.");
susp_iter_free(iter);
return 0;
}
/* it is a SP system use entry */
if ( sue->version[0] != 1 || sue->data.SP.be[0] != 0xBE
|| sue->data.SP.ef[0] != 0xEF) {
iso_msg_sorry(LIBISO_SUSP_WRONG, "SUSP SP system use entry seems to "
"be wrong. Ignoring Rock Ridge Extensions.");
susp_iter_free(iter);
return 0;
}
iso_msg_debug("SUSP/RR is being used.");
/*
* The LEN_SKP field, defined in IEEE 1281, SUSP. 5.3, specifies the
* number of bytes to be skipped within each System Use field.
* I think this will be always 0, but given that support this standard
* features is easy...
*/
info->len_skp = sue->data.SP.len_skp[0];
/*
* Ok, now search for ER entry.
* Just notice that the attributes for root dir are read in
* iso_read_dir
*
* TODO if several ER are present, we need to identify the position of
* what refers to RR, and then look for corresponding ES entry in
* each directory record. I have not implemented this (it's not used,
* no?), but if we finally need it, it can be easily implemented in
* the iterator, transparently for the rest of the code.
*/
while ( (sue = susp_iter_next(iter)) ) {
/* ignore entries from different version */
if (sue->version[0] != 1)
continue;
if (SUSP_SIG(sue, 'E', 'R')) {
if (info->rr) {
iso_msg_warn(LIBISO_SUSP_MULTIPLE_ER,
"More than one ER has found. This is not supported.\n"
"It will be ignored, but can cause problems. "
"Please notify us about this.\n");
}
/*
* it seems that Rock Ridge can be identified with any
* of the following
*/
if ( sue->data.ER.len_id[0] == 10 &&
!strncmp((char*)sue->data.ER.ext_id, "RRIP_1991A", 10) ) {
iso_msg_debug("Suitable Rock Ridge ER found. Version 1.10.");
info->rr = RR_EXT_110;
} else if ( ( sue->data.ER.len_id[0] == 10 &&
!strncmp((char*)sue->data.ER.ext_id, "IEEE_P1282", 10) )
|| ( sue->data.ER.len_id[0] == 9 &&
!strncmp((char*)sue->data.ER.ext_id, "IEEE_1282", 9) ) ) {
iso_msg_debug("Suitable Rock Ridge ER found. Version 1.12.");
info->rr = RR_EXT_112;
//TODO check also version?
} else {
iso_msg_warn(LIBISO_SUSP_MULTIPLE_ER,
"Not Rock Ridge ER found.\n"
"That will be ignored, but can cause problems in "
"image reading. Please notify us about this");
}
}
}
susp_iter_free(iter);
if (info->error)
return -1;
return 0;
}
static struct iso_volset *
read_pvm(struct iso_read_info *info, uint32_t block)
{
struct ecma119_pri_vol_desc *pvm;
struct iso_volume *volume;
struct iso_volset *volset;
struct ecma119_dir_record *rootdr;
char* volset_id;
unsigned char buffer[BLOCK_SIZE];
if ( info->src->read_block(info->src, block, buffer) < 0 ) {
info->error = LIBISOFS_READ_FAILURE;
return NULL;
}
pvm = (struct ecma119_pri_vol_desc *)buffer;
/* sanity checks */
if ( pvm->vol_desc_type[0] != 1
|| strncmp((char*)pvm->std_identifier, "CD001", 5)
|| pvm->vol_desc_version[0] != 1
|| pvm->file_structure_version[0] != 1 ) {
iso_msg_fatal(LIBISO_WRONG_IMG, "Wrong PVM. Maybe this is a damaged "
"image, or it's not an ISO-9660 image.\n");
info->error = LIBISOFS_WRONG_PVM;
return NULL;
}
volume = iso_volume_new(NULL, NULL, NULL);
/* fill strings */
volume->volume_id = strcopy((char*)pvm->volume_id, 32);
volume->publisher_id = strcopy((char*)pvm->publisher_id, 128);
volume->data_preparer_id = strcopy((char*)pvm->data_prep_id, 128);
volume->system_id = strcopy((char*)pvm->system_id, 32);
volume->application_id = strcopy((char*)pvm->application_id, 128);
volume->copyright_file_id = strcopy((char*)pvm->copyright_file_id, 37);
volume->abstract_file_id = strcopy((char*)pvm->abstract_file_id, 37);
volume->biblio_file_id = strcopy((char*)pvm->bibliographic_file_id, 37);
volset_id = strcopy((char*)pvm->vol_set_id, 128);
*(info->size) = iso_read_bb(pvm->vol_space_size, 4, NULL);
volset = iso_volset_new(volume, volset_id);
free(volset_id);
/*
* TODO
* I don't like the way the differences volset - volume are hanled now.
* While theorically right (a volset can contain several volumes), in
* practice it seems that this never happen. Current implementation, with
* the volume array in volset, make things innecessarily harder. I think
* we can refactor that in a single way.
*/
//volset->volset_size = pvm->vol_set_size[0];
rootdr = (struct ecma119_dir_record *)pvm->root_dir_record;
/*
* check if RR is being used. Note that this functions returns
* != 0 on error. Info about if RR is being used is stored in info
*/
if ( read_root_susp_entries(info, volume->root,
iso_read_bb(rootdr->block, 4, NULL)) ) {
/* error, cleanup and return */
iso_volset_free(volset);
return NULL;
}
/* are RR ext present */
info->hasRR = info->rr ? 1 : 0;
info->iso_root_block = iso_read_bb(rootdr->block, 4, NULL);
/*
* PVM has things that can be interested, but don't have a member in
* volume struct, such as creation date. In a multisession disc, we could
* keep the creation date and update the modification date, for example.
*/
return volset;
}
struct iso_volset *
iso_volset_read(struct data_source *src, struct ecma119_read_opts *opts)
{
struct iso_read_info info;
struct iso_volset *volset;
uint32_t block, root_dir_block;
unsigned char buffer[BLOCK_SIZE];
assert(src && opts);
/* fill info with suitable values */
info.error = LIBISOFS_READ_OK;
info.src = src;
info.rr = RR_EXT_NO;
info.len_skp = 0;
info.ino = 0;
info.norock = opts->norock;
info.uid = opts->uid;
info.gid = opts->gid;
info.mode = opts->mode & ~S_IFMT;
info.size = &opts->size;
info.bootcat = NULL;
root_dir_block = 0;
/* read primary volume description */
volset = read_pvm(&info, opts->block + 16);
if (volset == NULL) {
opts->error = info.error;
return NULL;
}
block = opts->block + 17;
do {
if ( info.src->read_block(info.src, block, buffer) < 0 ) {
info.error = LIBISOFS_READ_FAILURE;
/* cleanup and exit */
goto read_cleanup;
}
switch (buffer[0]) {
case 0:
/*
* This is a boot record
* Here we handle el-torito
*/
info.bootcat = read_el_torito_vol_desc(&info, buffer);
break;
case 2:
/* suplementary volume descritor */
{
struct ecma119_sup_vol_desc *sup;
struct ecma119_dir_record *root;
sup = (struct ecma119_sup_vol_desc*)buffer;
if (sup->esc_sequences[0] == 0x25 &&
sup->esc_sequences[1] == 0x2F &&
(sup->esc_sequences[2] == 0x40 ||
sup->esc_sequences[2] == 0x43 ||
sup->esc_sequences[2] == 0x45) ) {
/* it's a Joliet Sup. Vol. Desc. */
info.hasJoliet = 1;
root = (struct ecma119_dir_record*)sup->root_dir_record;
root_dir_block = iso_read_bb(root->block, 4, NULL);
//TODO maybe we can set the volume attribs from this
//descriptor
} else {
iso_msg_hint(LIBISO_UNSUPPORTED_VD,
"Not supported Sup. Vol. Desc found.");
}
}
break;
case 255:
/*
* volume set terminator
* ignore, as it's checked in loop end condition
*/
break;
default:
{
char msg[32];
sprintf(msg, "Ignoring Volume descriptor %d.", buffer[0]);
iso_msg_hint(LIBISO_UNSUPPORTED_VD, msg);
}
break;
}
block++;
} while (buffer[0] != 255);
opts->hasRR = info.hasRR;
opts->hasJoliet = info.hasJoliet;
/* user doesn't want to read RR extensions */
if (info.norock)
info.rr = RR_EXT_NO;
/* select what tree to read */
if (info.rr) {
/* RR extensions are available */
if (opts->preferjoliet && info.hasJoliet) {
/* if user prefers joliet, that is used */
iso_msg_debug("Reading Joliet extensions.");
info.get_name = ucs2str;
info.rr = RR_EXT_NO;
/* root_dir_block already contains root for joliet */
} else {
/* RR will be used */
iso_msg_debug("Reading Rock Ridge extensions.");
root_dir_block = info.iso_root_block;
info.get_name = strcopy;
}
} else {
/* RR extensions are not available */
if (info.hasJoliet && !opts->nojoliet) {
/* joliet will be used */
iso_msg_debug("Reading Joliet extensions.");
info.get_name = ucs2str;
/* root_dir_block already contains root for joliet */
} else {
/* default to plain iso */
iso_msg_debug("Reading plain ISO-9660 tree.");
root_dir_block = info.iso_root_block;
info.get_name = strcopy;
}
}
/* Read the ISO/RR or Joliet tree */
if ( iso_read_dir(&info, volset->volume[0]->root, root_dir_block) ) {
/* error, cleanup and return */
goto read_cleanup;
}
// TODO merge tree info
/* Add El-Torito info to the volume */
if (info.bootcat) {
/* ok, add the bootcat to the volume */
iso_msg_debug("Found El-Torito bootable volume");
volset->volume[0]->bootcat = info.bootcat;
}
return volset;
read_cleanup:;
if (info.bootcat) {
el_torito_boot_catalog_free(info.bootcat);
}
iso_volset_free(volset);
return NULL;
}

76
libisofs/ecma119_read.h Normal file
View File

@ -0,0 +1,76 @@
/*
* ecma119_read.h
*
* Defines structures for reading from a iso image.
*/
#ifndef ECMA119_READ_H_
#define ECMA119_READ_H_
#include <stdint.h>
#include "libisofs.h"
enum read_error {
LIBISOFS_READ_OK = 0,
LIBISOFS_READ_FAILURE, /**< Truncated image or read error */
LIBISOFS_WRONG_PVM, /**< Incorrect PVM */
LIBISOFS_UNSUPPORTED_IMAGE, /**< Format not supported (interleaved...) */
LIBISOFS_WRONG_RR /**< Wrong RR/SUSP extension format */
};
/**
* Should the RR extensions be read?
*/
enum read_rr_ext {
RR_EXT_NO = 0, /*< Do not use RR extensions */
RR_EXT_110, /*< RR extensions conforming version 1.10 */
RR_EXT_112 /*< RR extensions conforming version 1.12 */
};
/**
* Structure that keeps info needed in the read process.
*/
struct iso_read_info {
struct data_source *src;
enum read_error error;
uid_t uid; /**< Default uid when no RR */
gid_t gid; /**< Default uid when no RR */
mode_t mode; /**< Default mode when no RR (only permissions) */
uint32_t iso_root_block; /**< Will be filled with the block lba of the
* extend for the root directory, as read from
* the PVM
*/
enum read_rr_ext rr; /*< If we need to read RR extensions. i.e., if the image
* contains RR extensions, and the user wants to read them. */
char *(*get_name)(const char *, size_t);
/**<
* The function used to read the name from a directoy record. For
* ISO, the name is in US-ASCII. For Joliet, in UCS-2BE. Thus, we
* need different functions for both.
*/
ino_t ino; /*< Joliet and RR version 1.10 does not have file serial numbers,
* we need to generate it. */
uint8_t len_skp; /*< bytes skipped within the System Use field of a
directory record, before the beginning of the SUSP
system user entries. See IEEE 1281, SUSP. 5.3. */
unsigned int norock:1; /*< Do not read Rock Ridge extensions */
unsigned int hasRR:1; /*< It will be set to 1 if RR extensions are present,
to 0 if not. */
unsigned int hasJoliet:1; /*< It will be set to 1 if Joliet ext are present,
to 0 if not. */
uint32_t *size;
/* place for el-torito boot catalog */
struct el_torito_boot_catalog *bootcat;
};
#endif /*ECMA119_READ_H_*/

316
libisofs/ecma119_read_rr.c Normal file
View File

@ -0,0 +1,316 @@
/*
* This file contains functions related to the reading of SUSP and
* Rock Ridge extensions on an ECMA-119 image.
*/
#include <assert.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "ecma119.h"
#include "ecma119_read.h"
#include "ecma119_read_rr.h"
#include "util.h"
#include "messages.h"
#define BLOCK_SIZE 2048
/**
* Fills a struct stat with the values of a Rock Ridge PX entry
* On error, info->error is set propertly and the function returns != 0
*/
int
read_rr_PX(struct iso_read_info *info, struct susp_sys_user_entry *px,
struct stat *st)
{
assert( info && px && st);
assert( px->sig[0] == 'P' && px->sig[1] == 'X');
if ( info->rr == RR_EXT_112 && px->len_sue[0] != 44 ) {
iso_msg_sorry(LIBISO_RR_ERROR, "Invalid PX entry for RR version 1.12");
info->error = LIBISOFS_WRONG_RR;
return -1;
} else if ( info->rr == RR_EXT_110 && px->len_sue[0] != 36 ) {
iso_msg_sorry(LIBISO_RR_ERROR, "Invalid PX entry for RR version 1.10");
info->error = LIBISOFS_WRONG_RR;
return -1;
}
st->st_mode = iso_read_bb(px->data.PX.mode, 4, NULL);
st->st_nlink = iso_read_bb(px->data.PX.links, 4, NULL);
st->st_uid = iso_read_bb(px->data.PX.uid, 4, NULL);
st->st_gid = iso_read_bb(px->data.PX.gid, 4, NULL);
if (info->rr == RR_EXT_112) {
st->st_ino = iso_read_bb(px->data.PX.serial, 4, NULL);
} else {
st->st_ino = ++info->ino;
}
return 0;
}
/**
* Fills a struct stat with the values of a Rock Ridge TF entry
* On error, info->error is set propertly and the function returns != 0
*/
int
read_rr_TF(struct iso_read_info *info, struct susp_sys_user_entry *tf,
struct stat *st)
{
time_t time;
int s;
int nts = 0;
assert( info && tf && st);
assert( tf->sig[0] == 'T' && tf->sig[1] == 'F');
if (tf->data.TF.flags[0] & (1 << 7)) {
/* long form */
s = 17;
} else {
s = 7;
}
/* 1. Creation time */
if (tf->data.TF.flags[0] & (1 << 0)) {
/* the creation is the recording time. we ignore this */
/* TODO maybe it would be good to manage it in ms discs, where
* the recording time could be different than now!! */
++nts;
}
/* 2. modify time */
if (tf->data.TF.flags[0] & (1 << 1)) {
if (tf->len_sue[0] < 5 + (nts+1) * s) {
iso_msg_sorry(LIBISO_RR_ERROR, "RR TF entry too short.");
info->error = LIBISOFS_WRONG_RR;
return -1;
}
if (s == 7) {
time = iso_datetime_read_7(&tf->data.TF.t_stamps[nts*7]);
} else {
time = iso_datetime_read_17(&tf->data.TF.t_stamps[nts*17]);
}
st->st_mtime = time;
++nts;
}
/* 3. access time */
if (tf->data.TF.flags[0] & (1 << 2)) {
if (tf->len_sue[0] < 5 + (nts+1) * s) {
iso_msg_sorry(LIBISO_RR_ERROR, "RR TF entry too short.");
info->error = LIBISOFS_WRONG_RR;
return -1;
}
if (s == 7) {
time = iso_datetime_read_7(&tf->data.TF.t_stamps[nts*7]);
} else {
time = iso_datetime_read_17(&tf->data.TF.t_stamps[nts*17]);
}
st->st_atime = time;
++nts;
}
/* 4. attributes time */
if (tf->data.TF.flags[0] & (1 << 3)) {
if (tf->len_sue[0] < 5 + (nts+1) * s) {
iso_msg_sorry(LIBISO_RR_ERROR, "RR TF entry too short.");
info->error = LIBISOFS_WRONG_RR;
return -1;
}
if (s == 7) {
time = iso_datetime_read_7(&tf->data.TF.t_stamps[nts*7]);
} else {
time = iso_datetime_read_17(&tf->data.TF.t_stamps[nts*17]);
}
st->st_ctime = time;
++nts;
}
/* we ignore backup, expire and effect times */
return 0;
}
char *
read_rr_NM(struct susp_sys_user_entry *nm, char *name)
{
assert(nm);
assert( nm->sig[0] == 'N' && nm->sig[1] == 'M');
/* concatenate the results */
if (name) {
name = realloc(name, strlen(name) + nm->len_sue[0] - 5 + 1);
strncat(name, (char*)nm->data.NM.name, nm->len_sue[0] - 5);
} else {
name = strcopy((char*)nm->data.NM.name, nm->len_sue[0] - 5);
}
return name;
}
char *
read_rr_SL(struct susp_sys_user_entry *sl, char *dest)
{
int pos;
assert(sl);
assert( sl->sig[0] == 'S' && sl->sig[1] == 'L');
for (pos = 0; pos + 5 < sl->len_sue[0];
pos += 2 + sl->data.SL.comps[pos + 1]) {
char *comp;
uint8_t len;
uint8_t flags = sl->data.SL.comps[pos];
if (flags & 0x2) {
/* current directory */
len = 1;
comp = ".";
} else if (flags & 0x4) {
/* parent directory */
len = 2;
comp = "..";
} else if (flags & 0x8) {
/* root directory */
len = 1;
comp = "/";
} else if (flags & ~0x01) {
char msg[38];
sprintf(msg, "SL component flag %x not supported.", flags);
iso_msg_sorry(LIBISO_RR_ERROR, msg);
return NULL;
} else {
len = sl->data.SL.comps[pos + 1];
comp = (char*)&sl->data.SL.comps[pos + 2];
}
if (dest) {
int size = strlen(dest);
dest = realloc(dest, strlen(dest) + len + 2);
if ( dest[size-1] != '/' ) {
dest[size] = '/';
dest[size+1] = '\0';
}
strncat(dest, comp, len);
} else {
dest = strcopy(comp, len);
}
}
return dest;
}
struct susp_iterator {
uint8_t* base;
int pos;
int size;
struct iso_read_info *info;
uint32_t ce_block;
uint32_t ce_off;
uint32_t ce_len; /*< Length of the next continuation area, 0 if
no more CA are specified */
uint8_t *buffer; /*< If there are continuation areas */
};
struct susp_iterator *
susp_iter_new(struct iso_read_info *info, struct ecma119_dir_record *record)
{
struct susp_iterator *iter = malloc(sizeof(struct susp_iterator));
int pad = (record->len_fi[0] + 1) % 2;
iter->base = record->file_id + record->len_fi[0] + pad;
iter->pos = info->len_skp; /* 0 in most cases */
iter->size = record->len_dr[0] - record->len_fi[0] - 33 -pad;
iter->info = info;
iter->ce_len = 0;
iter->buffer = NULL;
return iter;
}
struct susp_sys_user_entry *
susp_iter_next(struct susp_iterator* iter)
{
struct susp_sys_user_entry *entry;
entry = (struct susp_sys_user_entry*)(iter->base + iter->pos);
if ( (iter->pos + 4 > iter->size) || (SUSP_SIG(entry, 'S', 'T')) ) {
/*
* End of the System Use Area or Continuation Area.
* Note that ST is not needed when the space left is less than 4.
* (IEEE 1281, SUSP. section 4)
*/
if (iter->ce_len) {
uint32_t block;
int nblocks;
/* A CE has found, there is another continuation area */
nblocks = div_up(iter->ce_off + iter->ce_len, BLOCK_SIZE);
iter->buffer = realloc(iter->buffer, nblocks * BLOCK_SIZE);
/* read all block needed to cache the full CE */
for (block = 0; block < nblocks; ++block) {
if (iter->info->src->read_block(iter->info->src,
iter->ce_block + block,
iter->buffer + block * BLOCK_SIZE)) {
iter->info->error = LIBISOFS_READ_FAILURE;
return NULL;
}
}
iter->base = iter->buffer + iter->ce_off;
iter->pos = 0;
iter->size = iter->ce_len;
iter->ce_len = 0;
entry = (struct susp_sys_user_entry*)iter->base;
} else {
return NULL;
}
}
if (entry->len_sue[0] == 0) {
/* a wrong image with this lead us to a infinity loop */
iso_msg_sorry(LIBISO_RR_ERROR, "Damaged RR/SUSP information.");
iter->info->error = LIBISOFS_WRONG_RR;
return NULL;
}
iter->pos += entry->len_sue[0];
if ( SUSP_SIG(entry, 'C', 'E') ) {
/* Continuation entry */
if (iter->ce_len) {
iso_msg_sorry(LIBISO_RR_ERROR, "More than one CE System user entry "
"has found in a single System Use field or continuation area. "
"This breaks SUSP standard and it's not supported.\n"
"Ignoring last CE. Maybe the image is damaged.\n");
} else {
iter->ce_block = iso_read_bb(entry->data.CE.block, 4, NULL);
iter->ce_off = iso_read_bb(entry->data.CE.offset, 4, NULL);
iter->ce_len = iso_read_bb(entry->data.CE.len, 4, NULL);
}
/* we don't want to return CE entry to the user */
return susp_iter_next(iter);
} else if ( SUSP_SIG(entry, 'P', 'D') ) {
/* skip padding */
return susp_iter_next(iter);
}
return entry;
}
void
susp_iter_free(struct susp_iterator* iter)
{
free(iter->buffer);
free(iter);
}

144
libisofs/ecma119_read_rr.h Normal file
View File

@ -0,0 +1,144 @@
/*
* This file contains functions related to the reading of SUSP and
* Rock Ridge extensions on an ECMA-119 image.
*/
#ifndef ECMA119_READ_RR_H_
#define ECMA119_READ_RR_H_
#include "libisofs.h"
#include "ecma119.h"
#include "ecma119_read.h"
#define SUSP_SIG(entry, a, b) ( (entry->sig[0] == a) && (entry->sig[1] == b) )
/**
* The SUSP iterator is used to iterate over the System User Entries
* of a ECMA-168 directory record.
* It takes care about Continuation Areas, handles the end of the different
* system user entries and skip padding areas. Thus, using an iteration
* we are accessing just to the meaning entries.
*/
struct susp_iterator;
struct susp_iterator *susp_iter_new(struct iso_read_info *info,
struct ecma119_dir_record *record);
/**
* Get the next SUSP System User Entry using given iterator.
* The returned pointer refers directly to an internal buffer and it's not
* guaranteed to be allocated after calling susp_iter_next() again. Thus,
* if you need to keep some entry you have to do a copy.
*
* It return NULL when no more entries are available. Also, it will return
* NULL on error. You must check info->error to distinguish between both
* situations.
*/
struct susp_sys_user_entry *susp_iter_next(struct susp_iterator* iter);
/**
* Free a given susp iterator.
*/
void susp_iter_free(struct susp_iterator* iter);
struct susp_CE {
uint8_t block[8];
uint8_t offset[8];
uint8_t len[8];
};
struct susp_SP {
uint8_t be[1];
uint8_t ef[1];
uint8_t len_skp[1];
};
struct susp_ER {
uint8_t len_id[1];
uint8_t len_des[1];
uint8_t len_src[1];
uint8_t ext_ver[1];
uint8_t ext_id[1]; /*< up to len_id bytes */
/* ext_des, ext_src */
};
/** POSIX file attributes. */
struct rr_PX {
uint8_t mode[8];
uint8_t links[8];
uint8_t uid[8];
uint8_t gid[8];
uint8_t serial[8];
};
/** Time stamps for a file. */
struct rr_TF {
uint8_t flags[1];
uint8_t t_stamps[1];
};
/** Alternate name. */
struct rr_NM {
uint8_t flags[1];
uint8_t name[1];
};
/** Link for a relocated directory. */
struct rr_CL {
uint8_t child_loc[8];
};
/** Sim link. */
struct rr_SL {
uint8_t flags[1];
uint8_t comps[1];
};
/**
* Struct for a SUSP System User Entry
*/
struct susp_sys_user_entry
{
uint8_t sig[2];
uint8_t len_sue[1];
uint8_t version[1];
union {
struct susp_CE CE;
struct susp_SP SP;
struct susp_ER ER;
struct rr_PX PX;
struct rr_TF TF;
struct rr_NM NM;
struct rr_CL CL;
struct rr_SL SL;
} data; /* 5 to 4+len_sue */
};
/**
* Fills a struct stat with the values of a Rock Ridge PX entry
* On error, info->error is set propertly and the function returns != 0
*/
int read_rr_PX(struct iso_read_info *info, struct susp_sys_user_entry *px,
struct stat *st);
/**
* Fills a struct stat with the values of a Rock Ridge TF entry
* On error, info->error is set propertly and the function returns != 0
*/
int read_rr_TF(struct iso_read_info *info, struct susp_sys_user_entry *tf,
struct stat *st);
/**
* Apends the content of given Rock Ridge NM entry to \p name
* On error, returns NULL
*/
char *read_rr_NM(struct susp_sys_user_entry *nm, char *name);
/**
* Apends the components in specified SL entry to \p dest, adding
* needed '/'.
* On error, returns NULL
*/
char *read_rr_SL(struct susp_sys_user_entry *sl, char *dest);
#endif /*ECMA119_READ_RR_H_*/

View File

@ -9,74 +9,339 @@
#include "ecma119_tree.h"
#include "tree.h"
#include "util.h"
#include "eltorito.h"
static size_t calc_dirent_len(struct ecma119_tree_node *n)
{
int ret = n->name ? strlen(n->name) + 33 : 34;
int ret = n->iso_name ? strlen(n->iso_name) + 33 : 34;
if (ret % 2) ret++;
return ret;
}
/**
* Replace the file permissions and user/group id of an ECMA-119 node.
* This is used when a replace mode is selected, i.e., when we want to
* create a disc where the mode of each file or directory will be
* different than the mode in the original source.
*/
static void
replace_node_mode(struct ecma119_write_target *t, struct stat *st)
{
if ( S_ISDIR(st->st_mode) ) {
if ( t->replace_mode & 0x02 ) {
/* replace dir mode with specific */
st->st_mode &= S_IFMT;
st->st_mode |= t->dir_mode;
} else if (t->replace_mode & 0x01) {
/* replace dir mode with default */
/* read perm */
mode_t new_mode = (st->st_mode & S_IFMT) | 0444;
/* search bit if any */
if ( st->st_mode & 0111)
new_mode |= 0111;
st->st_mode = new_mode;
}
} else {
if ( t->replace_mode & 0x04 ) {
/* replace file mode with specific */
st->st_mode &= S_IFMT;
st->st_mode |= t->file_mode;
} else if (t->replace_mode & 0x01) {
/* replace file mode with default */
/* read perm */
mode_t new_mode = (st->st_mode & S_IFMT) | 0444;
/* execute bit if any */
if ( st->st_mode & 0111)
new_mode |= 0111;
st->st_mode = new_mode;
}
}
if ( t->replace_mode & 0x08 ) {
/* replace gid mode with specific */
st->st_gid = t->gid;
} else if (t->replace_mode & 0x01) {
st->st_gid = 0;
}
if ( t->replace_mode & 0x10 ) {
/* replace gid mode with specific */
st->st_uid = t->uid;
} else if (t->replace_mode & 0x01) {
st->st_uid = 0;
}
}
/**
* Creates a new ECMA-119 node from the given iso tree node, and initializes
* the fields that are common to all kind of nodes (dir, reg file, symlink...).
*
* @param t
* The options for the ECMA-119 tree that is being created
* @param parent
* The parent of the node, or NULL if it's the root.
* @param iso
* The node from which this function creates a ECMA-119 node
* @return
* The created node.
*/
static struct ecma119_tree_node*
create_ecma119_node(struct ecma119_write_target *t,
struct ecma119_tree_node *parent,
struct iso_tree_node *iso)
{
struct ecma119_tree_node *ret;
char *(*iso_name)(const char *, const char *) = ISO_ISDIR(iso) ?
((t->iso_level == 1) ? iso_1_dirid : iso_2_dirid)
: ((t->iso_level == 1) ? iso_1_fileid : iso_2_fileid);
char *(*iso_r_name)(const char *, const char *, int) =
ISO_ISDIR(iso) ? iso_r_dirid : iso_r_fileid;
assert(t && (!parent || parent->type == ECMA119_DIR) && iso );
ret = calloc(1, sizeof(struct ecma119_tree_node));
/*
* If selected one ISO relaxed constraints other than NO_DIR_REALOCATION,
* we use the function that computes the relaxed name, otherwise normal
* function for specified level is used.
*/
ret->iso_name = iso->name ?
( t->relaxed_constraints & ~ECMA119_NO_DIR_REALOCATION ?
iso_r_name(iso->name, t->input_charset, t->relaxed_constraints) :
iso_name(iso->name, t->input_charset)
) : NULL;
ret->dirent_len = calc_dirent_len(ret);
/* iso node keeps the same file attribs as the original file. */
ret->attrib = iso->attrib;
/*
* When using RR extension and replace mode, we will replace the
* permissions and uid/gid of each file with those previously selected
* by the user.
*/
if ( t->rockridge && t->replace_mode )
replace_node_mode(t, &ret->attrib);
if (!iso->name)
ret->full_name = NULL;
else if ( strcmp(t->input_charset,t->ouput_charset) )
/* convert the file name charset */
ret->full_name = convert_str(iso->name, t->input_charset,
t->ouput_charset);
else
ret->full_name = strdup(iso->name);
ret->target = t;
ret->parent = parent;
return ret;
}
/**
* Create a new ECMA-119 node representing a directory from a iso directory
* node.
*/
static struct ecma119_tree_node*
create_dir(struct ecma119_write_target *t,
struct ecma119_tree_node *parent,
struct iso_tree_node *iso)
struct iso_tree_node_dir *iso)
{
struct ecma119_tree_node *ret;
assert(t && (!parent || parent->type == ECMA119_DIR)
&& iso && S_ISDIR(iso->attrib.st_mode));
&& iso && S_ISDIR(iso->node.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 = create_ecma119_node(t, parent, (struct iso_tree_node*) iso);
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);
ret->info.dir.real_parent = parent;
ret->info.dir.depth = parent ? parent->info.dir.depth + 1 : 1;
ret->info.dir.nchildren = 0;
ret->info.dir.children = calloc(1, sizeof(void*) * iso->nchildren);
return ret;
}
/**
* Create a new ECMA-119 node representing a regular file from a iso file
* node.
*/
static struct ecma119_tree_node*
create_file(struct ecma119_write_target *t,
struct ecma119_tree_node *parent,
struct iso_tree_node *iso)
struct iso_tree_node_file *iso)
{
struct ecma119_tree_node *ret;
struct iso_file *file;
assert(t && iso && parent && parent->type == ECMA119_DIR);
ret = create_ecma119_node(t, parent, (struct iso_tree_node*) iso);
ret->type = ECMA119_FILE;
/* get iso_file struct */
file = iso_file_table_lookup(t->file_table, iso);
if ( file == NULL ) {
/*
* If the file is not already added to the disc, we add it now
* to the file table, and get a new inode number for it.
*/
file = iso_file_new(t, iso);
if (!file) {
/*
* That was an error.
* TODO currently this cause the file to be ignored... Maybe
* throw an error is a better alternative
*/
ecma119_tree_free(ret);
return NULL;
}
iso_file_table_add_file(t->file_table, file);
file->ino = ++t->ino;
} else {
/* increment number of hard-links */
file->nlink++;
}
ret->attrib.st_nlink = file->nlink;
ret->attrib.st_ino = file->ino;
ret->info.file = file;
return ret;
}
/**
* Create a new ECMA-119 node representing a placeholder for a relocated
* dir.
*
* See IEEE P1282, section 4.1.5 for details
*/
static struct ecma119_tree_node*
create_placeholder(struct ecma119_write_target *t,
struct ecma119_tree_node *parent,
struct ecma119_tree_node *real)
{
struct ecma119_tree_node *ret;
assert(t && real && real->type == ECMA119_DIR
&& parent && parent->type == ECMA119_DIR);
ret = calloc(1, sizeof(struct ecma119_tree_node));
ret->iso_name = real->iso_name; /* TODO strdup? */
/* FIXME
* if we strdup above, if name changes in mangle_all,
* this probably keeps as original.
* if not, both change, but we need to update dirent_len.
* I think that attributes of a placeholder must be taken from
* real_me, not keept here.
* FIXME
* Another question is that real is a dir, while placeholder is
* a file, and ISO name restricctions are different, what to do?
*/
ret->dirent_len = real->dirent_len;
ret->attrib = real->attrib;
ret->full_name = strdup(real->full_name);
ret->target = t;
ret->parent = parent;
ret->type = ECMA119_PLACEHOLDER;
ret->info.real_me = real;
ret->attrib.st_nlink = 1;
ret->attrib.st_ino = ++t->ino;
return ret;
}
/**
* Create a new ECMA-119 node representing a symbolic link from a iso symlink
* node.
*/
static struct ecma119_tree_node*
create_symlink(struct ecma119_write_target *t,
struct ecma119_tree_node *parent,
struct iso_tree_node_symlink *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;
ret = create_ecma119_node(t, parent, (struct iso_tree_node*) iso);
ret->type = ECMA119_SYMLINK;
ret->info.dest = iso->dest; /* TODO strdup? */
ret->attrib.st_nlink = 1;
ret->attrib.st_ino = ++t->ino;
return ret;
}
/**
* Create a new ECMA-119 node representing a boot catalog or image.
* This will be treated as a normal file when written the directory record,
* but its contents are written in a different way.
*
* See "El Torito" Bootable CD-ROM Format Specification Version 1.0 for
* more details.
*/
static struct ecma119_tree_node*
create_boot(struct ecma119_write_target *t,
struct ecma119_tree_node *parent,
struct iso_tree_node_boot *iso)
{
struct ecma119_tree_node *ret;
assert(t && iso && parent && parent->type == ECMA119_DIR);
ret = create_ecma119_node(t, parent, (struct iso_tree_node*) iso);
ret->type = ECMA119_BOOT;
ret->info.boot_img = iso->img;
ret->attrib.st_nlink = 1;
ret->attrib.st_ino = ++t->ino;
return ret;
}
/**
* Create a new ECMA-119 node that corresponds to the given iso tree node.
* If that node is a dir, this function recurses over all their children,
* thus creating a ECMA-119 tree whose root is the given iso dir.
*/
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;
struct ecma119_tree_node *ret = NULL;
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]);
if ( iso->hide_flags & LIBISO_HIDE_ON_RR )
return NULL;
switch ( iso->type ) {
case LIBISO_NODE_FILE:
ret = create_file(t, parent, (struct iso_tree_node_file*)iso);
break;
case LIBISO_NODE_SYMLINK:
if ( !t->rockridge )
printf("Can't add symlinks to a non ISO tree. Skipping %s \n",
iso->name);
else
ret = create_symlink(t, parent, (struct iso_tree_node_symlink*)iso);
break;
case LIBISO_NODE_DIR:
{
size_t i;
struct iso_tree_node_dir *dir = (struct iso_tree_node_dir*)iso;
ret = create_dir(t, parent, dir);
for (i = 0; i < dir->nchildren; i++) {
struct ecma119_tree_node *child;
child = create_tree(t, ret, dir->children[i]);
if (child)
ret->info.dir.children[ret->info.dir.nchildren++] = child;
}
}
break;
case LIBISO_NODE_BOOT:
ret = create_boot(t, parent, (struct iso_tree_node_boot*)iso);
break;
default:
/* should never happen */
assert( 0 );
break;
}
return ret;
}
@ -87,12 +352,13 @@ 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]);
for (i=0; i < root->info.dir.nchildren; i++) {
ecma119_tree_free(root->info.dir.children[i]);
}
free(root->dir.children);
free(root->info.dir.children);
}
free(root->name);
free(root->iso_name);
free(root->full_name);
free(root);
}
@ -102,13 +368,19 @@ 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);
for (i=0; i < root->info.dir.nchildren; i++) {
size_t len = strlen(root->info.dir.children[i]->iso_name);
ret = MAX(ret, len);
}
return ret;
}
/**
* Relocates a directory, as specified in Rock Ridge Specification
* (see IEEE P1282, section 4.1.5). This is needed when the number of levels
* on a directory hierarchy exceeds 8, or the length of a path is higher
* than 255 characters, as specified in ECMA-119, section 6.8.2.1
*/
static void
reparent(struct ecma119_tree_node *child,
struct ecma119_tree_node *parent)
@ -120,13 +392,11 @@ reparent(struct ecma119_tree_node *child,
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;
for (i=0; i < child->parent->info.dir.nchildren; i++) {
if (child->parent->info.dir.children[i] == child) {
placeholder = create_placeholder(child->target,
child->parent, child);
child->parent->info.dir.children[i] = placeholder;
found = 1;
break;
}
@ -135,52 +405,83 @@ reparent(struct ecma119_tree_node *child,
/* 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;
parent->info.dir.nchildren++;
parent->info.dir.children = realloc( parent->info.dir.children,
sizeof(void*) * parent->info.dir.nchildren );
parent->info.dir.children[parent->info.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
* This restriction is imposed by ECMA-119 specification (see ECMA-119,
* 6.8.2.1).
*/
static void
reorder_tree(struct ecma119_tree_node *root,
reorder_tree(struct ecma119_write_target *t,
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);
cur->info.dir.depth = cur->parent ? cur->parent->info.dir.depth + 1 : 1;
cur->info.dir.path_len = cur->parent ? cur->parent->info.dir.path_len
+ strlen(cur->iso_name) : 0;
max_path = cur->info.dir.path_len + cur->info.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) */
if (cur->info.dir.depth > 8 || max_path > 255) {
if (t->rockridge) {
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 {
/* we need to delete cur */
size_t i,j;
struct ecma119_tree_node *parent = cur->parent;
printf("Can't dirs deeper than 8 without RR. Skipping %s\n",
cur->full_name);
for (i=0; i < parent->info.dir.nchildren; ++i) {
if (parent->info.dir.children[i] == cur) {
break;
}
}
assert ( i < parent->info.dir.nchildren);
for ( j = i; j < parent->info.dir.nchildren - 1; ++j)
parent->info.dir.children[j] = parent->info.dir.children[j+1];
parent->info.dir.nchildren--;
ecma119_tree_free(cur);
}
} 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]);
for (i=0; i < cur->info.dir.nchildren; i++) {
if (cur->info.dir.children[i]->type == ECMA119_DIR)
reorder_tree(t, root, cur->info.dir.children[i]);
}
}
}
/**
* Compare the iso name of two ECMA-119 nodes
*/
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);
return strcmp(f->iso_name, g->iso_name);
}
/**
* Sorts a the children of each directory in the ECMA-119 tree represented
* by \p root, acording to the order specified in ECMA-119, section 9.3.
*/
static void
sort_tree(struct ecma119_tree_node *root)
{
@ -188,10 +489,11 @@ sort_tree(struct ecma119_tree_node *root)
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]);
qsort(root->info.dir.children, root->info.dir.nchildren,
sizeof(void*), cmp_node);
for (i=0; i < root->info.dir.nchildren; i++) {
if (root->info.dir.children[i]->type == ECMA119_DIR)
sort_tree(root->info.dir.children[i]);
}
}
@ -206,7 +508,7 @@ sort_tree(struct ecma119_tree_node *root)
* "HELLOTHE03.TXT"
*/
static void
mangle_name(char **name, int num_change, int level, int seq_num)
mangle_name(char **name, int num_change, int level, int relaxed, int seq_num)
{
char *dot = strrchr(*name, '.');
char *semi = strrchr(*name, ';');
@ -219,6 +521,24 @@ mangle_name(char **name, int num_change, int level, int seq_num)
return;
}
strncpy(base, *name, len+1);
if (relaxed & ECMA119_RELAXED_FILENAMES) {
/* relaxed filenames, don't care about extension */
int maxlen = (relaxed & (1<<1)) ? 37 : 31;
base[maxlen - num_change] = '\0';
baselen = strlen(base);
if (relaxed & ECMA119_OMIT_VERSION_NUMBERS) {
sprintf(fmt, "%%s%%0%1dd", num_change);
*name = realloc(*name, baselen + num_change + 1);
} else {
sprintf(fmt, "%%s%%0%1dd;1", num_change);
*name = realloc(*name, baselen + num_change + 3);
}
sprintf(*name, fmt, base, seq_num);
return;
}
if (dot) {
base[dot - *name] = '\0';
strncpy(ext, dot+1, len+1);
@ -231,44 +551,61 @@ mangle_name(char **name, int num_change, int level, int seq_num)
}
baselen = strlen(base);
extlen = strlen(ext);
if (level == 1 && baselen + num_change > 8) {
if (relaxed & (1<<1)) {
/* 37 char filenames */
base[36 - extlen - num_change] = '\0';
} else 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);
if (relaxed & ECMA119_OMIT_VERSION_NUMBERS) {
sprintf(fmt, "%%s%%0%1dd.%%s", num_change);
*name = realloc(*name, baselen + extlen + num_change + 1);
} else {
sprintf(fmt, "%%s%%0%1dd.%%s;1", num_change);
*name = realloc(*name, baselen + extlen + num_change + 4);
}
sprintf(*name, fmt, base, seq_num, ext);
}
/**
* Ensures that the ISO name of each children of the given dir is unique,
* changing some of them if needed.
*/
static void
mangle_all(struct ecma119_tree_node *dir)
{
size_t i, j, k;
struct ecma119_dir_info d = dir->dir;
struct ecma119_dir_info d = dir->info.dir;
size_t n_change;
int changed;
size_t digits;
assert(dir->type == ECMA119_DIR);
digits = 1;
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) )
!strcmp(d.children[i]->iso_name,
d.children[i+j]->iso_name) )
j++;
if (j == 1) continue;
/* mangle the names */
changed = 1;
n_change = j / 10 + 1;
n_change = j / 10 + digits;
for (k=0; k < j; k++) {
mangle_name(&(d.children[i+k]->name),
mangle_name(&(d.children[i+k]->iso_name),
n_change,
dir->target->iso_level,
dir->target->relaxed_constraints,
k);
d.children[i+k]->dirent_len =
calc_dirent_len(d.children[i+k]);
@ -277,6 +614,12 @@ mangle_all(struct ecma119_tree_node *dir)
/* skip ahead by the number of mangled names */
i += j - 1;
}
if (changed) {
/* we need to reorder */
qsort(dir->info.dir.children, dir->info.dir.nchildren,
sizeof(void*), cmp_node);
}
digits++;
} while (changed);
for (i=0; i < d.nchildren; i++) {
@ -290,7 +633,8 @@ 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);
if ( !(t->relaxed_constraints & ECMA119_NO_DIR_REALOCATION) )
reorder_tree(t, t->root, t->root);
sort_tree(t->root);
mangle_all(t->root);
return t->root;
@ -305,8 +649,8 @@ ecma119_tree_print(struct ecma119_tree_node *root, int spaces)
memset(sp, ' ', spaces);
sp[spaces] = '\0';
printf("%s%s\n", sp, root->name);
printf("%s%s\n", sp, root->iso_name);
if (root->type == ECMA119_DIR)
for (i=0; i < root->dir.nchildren; i++)
ecma119_tree_print(root->dir.children[i], spaces+2);
for (i=0; i < root->info.dir.nchildren; i++)
ecma119_tree_print(root->info.dir.children[i], spaces+2);
}

View File

@ -10,11 +10,19 @@
#ifndef LIBISO_ECMA119_TREE_H
#define LIBISO_ECMA119_TREE_H
struct ecma119_write_target;
#include <sys/stat.h>
enum {
#include "file.h"
struct ecma119_write_target;
struct iso_tree_node;
enum ecma119_node_type {
ECMA119_FILE,
ECMA119_DIR
ECMA119_SYMLINK,
ECMA119_DIR,
ECMA119_PLACEHOLDER, /**< placeholder for a relocated dir. */
ECMA119_BOOT
};
struct ecma119_dir_info {
@ -25,6 +33,7 @@ struct ecma119_dir_info {
* Directory Records (including SU) */
size_t CE_len; /**< sum of the lengths of children's
* SUSP CE areas */
size_t block;
int depth;
size_t path_len; /**< The length of a path up to, and
@ -37,39 +46,38 @@ struct ecma119_dir_info {
/**< 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. */
char *iso_name; /**< in ASCII, conforming to the
* current ISO level. */
char *full_name; /**< full name, in current locale */
size_t dirent_len; /**< Length of the directory record,
* not including SU. */
size_t block;
* not including SU. */
struct ecma119_tree_node *parent;
struct iso_tree_node *iso_self;
struct ecma119_write_target *target;
struct stat attrib;
struct susp_info susp;
int type; /**< file or directory */
/* union {*/
enum ecma119_node_type type; /**< file, symlink, directory or placeholder */
union {
struct iso_file *file;
char *dest;
struct ecma119_dir_info dir;
struct ecma119_file_info file;
/* };*/
struct ecma119_tree_node *real_me; /**< this field points to
* the relocated directory.
*/
unsigned int boot_img:1; /** For boot nodes, it identifies if this
* corresponds to image(1) or catalog(0).
* The block is stored in ecma119_write_target
*/
} info;
};
/**

619
libisofs/eltorito.c Normal file
View File

@ -0,0 +1,619 @@
#include "libisofs.h"
#include "eltorito.h"
#include "volume.h"
#include "util.h"
#include "messages.h"
#include <assert.h>
#include <malloc.h>
#include <time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <string.h>
#include <stdio.h>
#include <fcntl.h>
/**
* This table should be written with accuracy values at offset
* 8 of boot image, when used ISOLINUX boot loader
*/
struct boot_info_table {
uint8_t bi_pvd BP(1, 4); /* LBA of primary volume descriptor */
uint8_t bi_file BP(5, 8); /* LBA of boot file */
uint8_t bi_length BP(9, 12); /* Length of boot file */
uint8_t bi_csum BP(13, 16); /* Checksum of boot file */
uint8_t bi_reserved BP(17, 56); /* Reserved */
};
struct partition_desc {
uint8_t boot_ind;
uint8_t begin_chs[3];
uint8_t type;
uint8_t end_chs[3];
uint8_t start[4];
uint8_t size[4];
};
struct hard_disc_mbr {
uint8_t code_area[440];
uint8_t opt_disk_sg[4];
uint8_t pad[2];
struct partition_desc partition[4];
uint8_t sign1;
uint8_t sign2;
};
static void
el_torito_image_free(struct el_torito_boot_image *image)
{
assert(image);
/* unref image node */
iso_tree_free((struct iso_tree_node*)image->node);
free(image);
}
static struct iso_tree_node_boot*
create_boot_image_node(struct iso_tree_node_file *image)
{
struct iso_tree_node_boot *boot;
struct iso_tree_node_dir *parent;
assert(image);
boot = calloc(1, sizeof(struct iso_tree_node_boot));
boot->node.attrib = image->node.attrib;
boot->node.type = LIBISO_NODE_BOOT;
boot->node.name = strdup(image->node.name);
boot->node.hide_flags = image->node.hide_flags;
boot->node.refcount = 2; /* mine and tree */
boot->loc.path = strdup(image->loc.path);
boot->img = 1; /* it refers to image */
/* replace iso_tree_node_file with the image */
parent = image->node.parent;
iso_tree_node_remove(parent, (struct iso_tree_node*)image);
iso_tree_add_child(parent, (struct iso_tree_node*) boot);
return boot;
}
static struct el_torito_boot_image *
create_image(const char *path,
enum eltorito_boot_media_type type)
{
struct stat attrib;
struct el_torito_boot_image *boot;
int boot_media_type = 0;
int load_sectors = 0; /* number of sector to load */
unsigned char partition_type = 0;
if (stat(path, &attrib)) {
iso_msg_sorry(LIBISO_EL_TORITO_WRONG_IMG, "Can't access file.");
libisofs_errno = NO_FILE;
return NULL;
}
if ( !S_ISREG(attrib.st_mode) ) {
iso_msg_sorry(LIBISO_EL_TORITO_WRONG_IMG,
"We can only create boot images from regular files");
libisofs_errno = ELTORITO_WRONG_IMAGE;
return NULL;
}
switch (type) {
case ELTORITO_FLOPPY_EMUL:
switch (attrib.st_size) {
case 1200 * 1024:
boot_media_type = 1; /* 1.2 meg diskette */
break;
case 1440 * 1024:
boot_media_type = 2; /* 1.44 meg diskette */
break;
case 2880 * 1024:
boot_media_type = 3; /* 2.88 meg diskette */
break;
default:
{
char msg[72];
sprintf(msg, "Invalid image size %d Kb. Must be one of 1.2, 1.44"
"or 2.88 Mb", (int) attrib.st_size / 1024);
iso_msg_sorry(LIBISO_EL_TORITO_WRONG_IMG, msg);
libisofs_errno = ELTORITO_WRONG_IMAGE_SIZE;
return NULL;
}
break;
}
/* it seems that for floppy emulation we need to load
* a single sector (512b) */
load_sectors = 1;
break;
case ELTORITO_HARD_DISC_EMUL:
{
size_t i;
int fd;
struct hard_disc_mbr mbr;
int used_partition;
/* read the MBR on disc and get the type of the partition */
fd = open(path, O_RDONLY);
if ( fd == -1 ) {
iso_msg_sorry(LIBISO_EL_TORITO_WRONG_IMG, "Can't open image file.");
libisofs_errno = NO_READ_ACCESS;
return NULL;
}
if ( read(fd, &mbr, sizeof(mbr)) ) {
iso_msg_sorry(LIBISO_EL_TORITO_WRONG_IMG,
"Can't read MBR from image file.");
libisofs_errno = ELTORITO_WRONG_IMAGE;
close(fd);
return NULL;
}
close(fd);
/* check valid MBR signature */
if ( mbr.sign1 != 0x55 || mbr.sign2 != 0xAA ) {
iso_msg_sorry(LIBISO_EL_TORITO_WRONG_IMG,
"Invalid MBR. Wrong signature.");
libisofs_errno = ELTORITO_WRONG_IMAGE;
return NULL;
}
/* ensure single partition */
used_partition = -1;
for (i = 0; i < 4; ++i) {
if (mbr.partition[i].type != 0) {
/* it's an used partition */
if (used_partition != -1) {
char msg[72];
sprintf(msg, "Invalid MBR. At least 2 partitions: %d and "
"%d, are being used\n", used_partition, i);
iso_msg_sorry(LIBISO_EL_TORITO_WRONG_IMG, msg);
libisofs_errno = ELTORITO_WRONG_IMAGE;
return NULL;
} else
used_partition = i;
}
}
partition_type = mbr.partition[used_partition].type;
}
boot_media_type = 4;
/* only load the MBR */
load_sectors = 1;
break;
case ELTORITO_NO_EMUL:
boot_media_type = 0;
break;
}
boot = calloc(1, sizeof(struct el_torito_boot_image));
boot->bootable = 1;
boot->type = boot_media_type;
boot->load_size = load_sectors;
boot->partition_type = partition_type;
return boot;
}
/* parent and name can be null */
static struct iso_tree_node_boot*
create_boot_catalog_node(struct iso_tree_node_dir *parent,
const char *name)
{
struct iso_tree_node_boot *boot;
assert( (parent && name) || (!parent && !name) );
boot = calloc(1, sizeof(struct iso_tree_node_boot));
boot->node.attrib.st_mode = S_IFREG | 0444;
boot->node.attrib.st_atime =
boot->node.attrib.st_mtime =
boot->node.attrib.st_ctime = time(NULL);
boot->node.attrib.st_size = 2048;
boot->node.type = LIBISO_NODE_BOOT;
boot->node.name = name ? strdup(name) : NULL;
boot->node.refcount = 1; /* mine */
if (parent) {
boot->node.refcount++; /* add tree ref */
iso_tree_add_child(parent, (struct iso_tree_node*) boot);
}
return boot;
}
struct el_torito_boot_image*
iso_volume_set_boot_image(struct iso_volume *volume,
struct iso_tree_node *image,
enum eltorito_boot_media_type type,
struct iso_tree_node_dir *dir,
char *name)
{
struct el_torito_boot_image *boot_image;
struct iso_tree_node_boot *cat_node;
struct el_torito_boot_catalog *catalog;
struct iso_tree_node_file *imgfile;
assert(volume && !volume->bootcat);
assert(image && ISO_ISREG(image));
assert(dir && name);
imgfile = (struct iso_tree_node_file*)image;
if (image->procedence == LIBISO_PREVIMG) {
/* FIXME mmm, what to do here? */
iso_msg_sorry(LIBISO_EL_TORITO_WRONG_IMG, "Can't use prev. image files "
"as boot images");
return NULL;
}
boot_image = create_image(imgfile->loc.path, type);
if ( !boot_image )
return NULL;
/* create image node */
boot_image->node = create_boot_image_node(imgfile);
/* creates the catalog with the given default image */
catalog = malloc(sizeof(struct el_torito_boot_catalog));
if (!catalog) {
el_torito_image_free(boot_image);
return NULL;
}
catalog->image = boot_image;
/* add catalog file */
cat_node = create_boot_catalog_node(dir, name);
catalog->node = cat_node;
volume->bootcat = catalog;
return boot_image;
}
struct el_torito_boot_image*
iso_volume_set_boot_image_hidden(struct iso_volume *volume, const char* path,
enum eltorito_boot_media_type type)
{
struct el_torito_boot_image *boot_image;
struct iso_tree_node_boot *cat_node;
struct el_torito_boot_catalog *catalog;
assert(volume && !volume->bootcat);
assert(path);
boot_image = create_image(path, type);
if ( !boot_image )
return NULL;
/* create image node */
{
struct iso_tree_node_boot *boot;
boot = calloc(1, sizeof(struct iso_tree_node_boot));
/* we assume the stat won't fail, as we already stat the same file above */
stat(path, &boot->node.attrib);
boot->node.type = LIBISO_NODE_BOOT;
boot->node.refcount = 1; /* mine */
boot->loc.path = strdup(path);
boot->img = 1; /* it refers to image */
boot_image->node = boot;
}
/* creates the catalog with the given default image */
catalog = malloc(sizeof(struct el_torito_boot_catalog));
if (!catalog) {
el_torito_image_free(boot_image);
return NULL;
}
catalog->image = boot_image;
/* add catalog file */
cat_node = create_boot_catalog_node(NULL, NULL);
catalog->node = cat_node;
volume->bootcat = catalog;
return boot_image;
}
struct el_torito_boot_image*
iso_volume_get_boot_image(struct iso_volume *volume,
struct iso_tree_node **imgnode,
struct iso_tree_node **catnode)
{
struct el_torito_boot_catalog *catalog;
assert(volume);
catalog = volume->bootcat;
if (!catalog)
return NULL;
if (imgnode)
*imgnode = (struct iso_tree_node*)catalog->image->node;
if (catnode)
*catnode = (struct iso_tree_node*)catalog->node;
return catalog->image;
}
void
iso_volume_remove_boot_image(struct iso_volume *volume)
{
struct el_torito_boot_catalog *catalog;
struct iso_tree_node *catnode;
struct iso_tree_node *imgnode;
assert(volume);
catalog = volume->bootcat;
if (!catalog)
return;
/* remove node from tree */
catnode = (struct iso_tree_node*)catalog->node;
if (catnode->parent)
iso_tree_node_remove(catnode->parent, catnode);
/* remove image from tree */
imgnode = (struct iso_tree_node*)catalog->image->node;
if (imgnode->parent)
iso_tree_node_remove(imgnode->parent, imgnode);
/* remove catalog */
el_torito_boot_catalog_free(catalog);
volume->bootcat = NULL;
}
void
el_torito_set_load_seg(struct el_torito_boot_image *bootimg, int segment)
{
if (bootimg->type != ELTORITO_NO_EMUL)
return;
bootimg->load_seg = segment;
}
void
el_torito_set_load_size(struct el_torito_boot_image *bootimg, int sectors)
{
if (bootimg->type != ELTORITO_NO_EMUL)
return;
bootimg->load_size = sectors;
}
void
el_torito_set_no_bootable(struct el_torito_boot_image *bootimg)
{
bootimg->bootable = 0;
}
void
el_torito_patch_isolinux_image(struct el_torito_boot_image *bootimg)
{
bootimg->isolinux = 1;
}
void el_torito_boot_catalog_free(struct el_torito_boot_catalog *cat)
{
assert(cat);
el_torito_image_free(cat->image);
iso_tree_free((struct iso_tree_node*)cat->node);
free(cat);
}
/**
* Write the Boot Record Volume Descriptor
*/
void
el_torito_write_boot_vol_desc(struct ecma119_write_target *t, uint8_t *buf)
{
struct el_torito_boot_catalog *cat = t->catalog;
struct ecma119_boot_rec_vol_desc *vol =
(struct ecma119_boot_rec_vol_desc*)buf;
assert(cat);
vol->vol_desc_type[0] = 0;
memcpy(vol->std_identifier, "CD001", 5);
vol->vol_desc_version[0] = 1;
memcpy(vol->boot_sys_id, "EL TORITO SPECIFICATION", 23);
iso_lsb(vol->boot_catalog, t->catblock, 4);
}
static void
write_validation_entry(struct ecma119_write_target *t, uint8_t *buf)
{
size_t i;
int checksum;
struct el_torito_validation_entry *ve =
(struct el_torito_validation_entry*)buf;
ve->header_id[0] = 1;
ve->platform_id[0] = 0; /* 0: 80x86, 1: PowerPC, 2: Mac */
ve->key_byte1[0] = 0x55;
ve->key_byte2[0] = 0xAA;
/* calculate the checksum, to ensure sum of all words is 0 */
checksum = 0;
for (i = 0; i < sizeof(struct el_torito_validation_entry); i += 2) {
checksum -= buf[i];
checksum -= (buf[i] << 8);
}
iso_lsb(ve->checksum, checksum, 2);
}
static void
patch_boot_image(uint8_t *buf, struct ecma119_write_target *t, ssize_t imgsize)
{
struct boot_info_table *info;
uint32_t checksum;
ssize_t offset;
memset(&info, 0, sizeof(info));
/* compute checksum, as the the sum of all 32 bit words in boot image
* from offset 64 */
checksum = 0;
offset = (ssize_t) 64;
while (offset < imgsize) {
checksum += iso_read_lsb(buf + offset, 4);
offset += 4;
}
if (offset != imgsize) {
/* file length not multiple of 4 */
iso_msg_warn(LIBISO_ISOLINUX_CANT_PATCH,
"Unexpected isolinux image length. Patch might not work.");
}
/* patch boot info table */
info = (struct boot_info_table*)(buf + 8);
memset(info, 0, sizeof(struct boot_info_table));
iso_lsb(info->bi_pvd, t->ms_block + 16, 4);
iso_lsb(info->bi_file, t->imgblock, 4);
iso_lsb(info->bi_length, imgsize, 4);
iso_lsb(info->bi_csum, checksum, 4);
}
static void
write_boot_image(uint8_t *buf, struct ecma119_write_target *t)
{
ssize_t imgsize;
struct el_torito_boot_image *image;
image = t->catalog->image;
imgsize= image->node->node.attrib.st_size;
/* copy image contents to memory buffer */
if (image->node->node.procedence == LIBISO_PREVIMG) {
int block, nblocks;
uint8_t *memloc;
assert(t->src);
block = 0;
memloc = buf;
nblocks = imgsize ? div_up(imgsize, 2048) : 1;
while (block < nblocks) {
if (t->src->read_block(t->src,
image->node->loc.block + block, memloc)) {
iso_msg_sorry(LIBISO_CANT_READ_IMG,
"Unable to read boot image from previous image.");
break; /* exit loop */
}
memloc += 2048;
++block;
}
} else {
int fd;
ssize_t nread, tread;
uint8_t *memloc;
const char *path = image->node->loc.path;
memloc = buf;
fd = open(path, O_RDONLY);
if (fd == -1) {
iso_msg_sorry(LIBISO_CANT_READ_FILE,
"Can't open boot image file for reading. Skipping");
return;
}
tread = 0;
while (tread < imgsize) {
nread = read(fd, memloc, t->block_size);
if (nread <= 0) {
iso_msg_sorry(LIBISO_CANT_READ_FILE,
"Problem reading boot image file. Skipping");
break; /* exit loop */
}
tread += nread;
memloc += nread;
}
close(fd);
}
if (image->isolinux) {
/* we need to patch the image */
patch_boot_image(buf, t, imgsize);
}
}
/**
* Write one section entry.
* Currently this is used only for default image (the only supported just now)
*/
static void
write_section_entry(uint8_t *buf, struct ecma119_write_target *t)
{
struct el_torito_boot_image *img;
struct el_torito_section_entry *se =
(struct el_torito_section_entry*)buf;
img = t->catalog->image;
assert(img);
se->boot_indicator[0] = img->bootable ? 0x88 : 0x00;
se->boot_media_type[0] = img->type;
iso_lsb(se->load_seg, img->load_seg, 2);
se->system_type[0] = img->partition_type;
iso_lsb(se->sec_count, img->load_size, 2);
iso_lsb(se->block, t->imgblock, 4);
}
/**
* Write El-Torito Boot Catalog plus image
*/
static void
write_catalog(struct ecma119_write_target *t, uint8_t *buf)
{
struct el_torito_boot_catalog *cat = t->catalog;
assert(cat);
assert(cat->image);
write_validation_entry(t, buf);
/* write default entry */
write_section_entry(buf + 32, t);
write_boot_image(buf + 2048, t);
}
void
el_torito_wr_boot_vol_desc(struct ecma119_write_target *t, uint8_t *buf)
{
assert(t->catalog);
ecma119_start_chunking(t,
el_torito_write_boot_vol_desc,
2048,
buf);
}
void
el_torito_wr_catalog(struct ecma119_write_target *t, uint8_t *buf)
{
off_t size;
off_t imgsize;
assert(t->catalog);
/*
* Note that when this is called, we need to write el-torito info to
* image. Note, however, that the image could come from a previous session
* and maybe we don't know its size
*/
imgsize = t->catalog->image->node->node.attrib.st_size;
if (imgsize == 0) {
iso_msg_warn(LIBISO_EL_TORITO_BLIND_COPY,
"Can get boot image size, only one block will be copied");
imgsize = 2048;
}
size = 2048 + div_up(imgsize, t->block_size);
ecma119_start_chunking(t,
write_catalog,
size,
buf);
}

83
libisofs/eltorito.h Normal file
View File

@ -0,0 +1,83 @@
#ifndef ELTORITO_H_
#define ELTORITO_H_
#include "tree.h"
#include "file.h"
#include "ecma119.h"
struct el_torito_boot_catalog {
struct iso_tree_node_boot *node; /* node of the catalog */
struct el_torito_boot_image *image; /* default boot image */
enum tree_node_from proc; /* whether the catalog is new or read from a
* prev session/image */
};
struct el_torito_boot_image {
struct iso_tree_node_boot *node;
unsigned int bootable:1; /**< If the entry is bootable. */
unsigned int isolinux:1; /**< If the image will be patched */
unsigned char type; /**< The type of image */
unsigned char partition_type; /**< type of partition for HD-emul images */
short load_seg; /**< Load segment for the initial boot image. */
short load_size; /**< Number of sector to load. */
};
struct el_torito_validation_entry {
uint8_t header_id BP(1, 1);
uint8_t platform_id BP(2, 2);
uint8_t reserved BP(3, 4);
uint8_t id_string BP(5, 28);
uint8_t checksum BP(29, 30);
uint8_t key_byte1 BP(31, 31);
uint8_t key_byte2 BP(32, 32);
};
struct el_torito_default_entry {
uint8_t boot_indicator BP(1, 1);
uint8_t boot_media_type BP(2, 2);
uint8_t load_seg BP(3, 4);
uint8_t system_type BP(5, 5);
uint8_t unused1 BP(6, 6);
uint8_t sec_count BP(7, 8);
uint8_t block BP(9, 12);
uint8_t unused2 BP(13, 32);
};
struct el_torito_section_header_entry {
uint8_t header_indicator BP(1, 1);
uint8_t platform_id BP(2, 2);
uint8_t number BP(3, 4);
uint8_t character BP(5, 32);
};
struct el_torito_section_entry {
uint8_t boot_indicator BP(1, 1);
uint8_t boot_media_type BP(2, 2);
uint8_t load_seg BP(3, 4);
uint8_t system_type BP(5, 5);
uint8_t unused1 BP(6, 6);
uint8_t sec_count BP(7, 8);
uint8_t block BP(9, 12);
uint8_t selec_criteria BP(13, 13);
uint8_t vendor_sc BP(14, 32);
};
void el_torito_boot_catalog_free(struct el_torito_boot_catalog *cat);
/**
* Write the Boot Record Volume Descriptor
*/
void
el_torito_write_boot_vol_desc(struct ecma119_write_target *t, uint8_t *buf);
void
el_torito_wr_boot_vol_desc(struct ecma119_write_target *t, uint8_t *buf);
/**
* Write catalog + image
*/
void
el_torito_wr_catalog(struct ecma119_write_target *t, uint8_t *buf);
#endif /*ELTORITO_H_*/

View File

@ -1,42 +1,29 @@
#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)
iso_exclude_add_path(struct iso_hash_table *table, const char *path)
{
if (!path)
return;
num += iso_hash_insert(table, path);
table->num += iso_hash_insert(table->table, path);
}
void
iso_exclude_remove_path(const char *path)
iso_exclude_empty(struct iso_hash_table *table)
{
if (!num || !path)
if (!table->num)
return;
num -= iso_hash_remove(table, path);
}
void
iso_exclude_empty(void)
{
if (!num)
return;
iso_hash_empty(table);
num=0;
iso_hash_empty(table->table);
table->num=0;
}
int
iso_exclude_lookup(const char *path)
iso_exclude_lookup(struct iso_hash_table *table, const char *path)
{
if (!num || !path)
if (!table->num || !path)
return 0;
return iso_hash_lookup(table, path);
return iso_hash_lookup(table->table, path);
}

View File

@ -1,12 +1,30 @@
#ifndef ISO_EXCLUDE_H
#define ISO_EXCLUDE_H
#include "hash.h"
struct iso_hash_table {
struct iso_hash_node *table[HASH_NODES];
int num;
};
/**
* 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);
int iso_exclude_lookup(struct iso_hash_table *table, 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(struct iso_hash_table *table, const char *path);
/**
* Remove all paths that were set to be ignored when adding a directory recusively.
*/
void iso_exclude_empty(struct iso_hash_table *table);
#endif /* ISO_EXCLUDE */

254
libisofs/file.c Normal file
View File

@ -0,0 +1,254 @@
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <assert.h>
#include <limits.h>
#include "file.h"
#include "tree.h"
#include "file_src.h"
#include "messages.h"
#include "ecma119.h"
//TODO: refactor both hash and this hash table into a single one??
struct iso_file *
iso_file_new(struct ecma119_write_target *t, struct iso_tree_node_file *f)
{
struct iso_file *file = calloc(1, sizeof(struct iso_file));
if (!file)
return NULL;
if (f->node.procedence == LIBISO_NEW) {
file->path = f->loc.path; /*TODO strdup? it needs to be free on clear then */
file->real_dev = f->node.attrib.st_dev;
file->real_ino = f->node.attrib.st_ino;
file->src = iso_file_src_from_path(file->path);
} else {
file->block = f->loc.block;
file->prev_img = 1;
file->real_dev = 0; /* we use 0 as dev for prev. session files */
/* don't take care about inode number read from RR TX, block
* number is good enouht for this. Moreover, when we are modifying
* an image, we will modify file->block with the block where the
* file needs to be written in the new image. So, we store the block
* in original image here */
file->real_ino = f->loc.block;
file->src = iso_file_src_from_prev_img(t->src, f->loc.block,
f->node.attrib.st_size);
}
if (!file->src)
goto creation_error;
file->size = file->src->get_size(file->src);
if (file->size != f->node.attrib.st_size) {
char msg[PATH_MAX + 32];
/* can only happen on path files */
assert(f->node.procedence == LIBISO_NEW);
sprintf(msg, "Size of file %s has changed\n", file->path);
iso_msg_sorry(LIBISO_CANT_READ_FILE, msg);
}
file->nlink = 1;
file->sort_weight = f->sort_weight;
return file;
creation_error:;
free(file);
return NULL;
}
static unsigned int
iso_file_table_hash(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 % FILE_HASH_NODES;
}
static inline unsigned int
iso_file_table_hash_inode(dev_t dev, ino_t ino)
{
return (dev ^ ino) % FILE_HASH_NODES;
}
struct iso_file_table*
iso_file_table_new(int cache_inodes)
{
struct iso_file_table *table = calloc(1, sizeof(struct iso_file_table));
table->cache_inodes = cache_inodes;
return table;
}
static struct iso_file_hash_node *
iso_file_table_node_new(struct iso_file *file)
{
struct iso_file_hash_node *node;
node = calloc(1, sizeof(struct iso_file_hash_node) );
node->file = file;
return node;
}
static void
iso_file_table_node_free(struct iso_file_hash_node *node)
{
iso_file_src_free(node->file->src);
free(node->file);
free(node);
}
void
iso_file_table_clear(struct iso_file_table *ft)
{
int i;
for (i=0; i < FILE_HASH_NODES; i++) {
struct iso_file_hash_node *node;
node=ft->table[i];
if (!node)
continue;
ft->table[i] = NULL;
do {
struct iso_file_hash_node *next;
next = node->next;
iso_file_table_node_free(node);
node = next;
} while (node);
}
ft->count = 0;
}
/**
* return 0 if equal, != 0 if not
*/
static int
iso_table_compare_files(struct iso_file_table *ft,
struct iso_file *f1, struct iso_file *f2)
{
assert(ft && f1 && f2);
if (f1->prev_img || f2->prev_img) {
if (f1->prev_img && f2->prev_img)
return f1->real_ino != f2->real_ino;
else
return 1;
}
if (ft->cache_inodes) {
return (f1->real_dev != f2->real_dev) || (f1->real_ino != f2->real_ino);
} else {
return strcmp(f1->path, f2->path);
}
}
int
iso_file_table_add_file(struct iso_file_table *ft, struct iso_file *f)
{
struct iso_file_hash_node *node;
unsigned int hash_num;
assert(ft && f);
/* find the hash number */
if (f->prev_img)
hash_num = f->real_ino % FILE_HASH_NODES;
else if (ft->cache_inodes)
hash_num = iso_file_table_hash_inode(f->real_dev, f->real_ino);
else
hash_num = iso_file_table_hash(f->path);
/* insert it */
node = ft->table[hash_num];
/* unfortunately, we can't safely consider that a file
* won't be twice in the hash table so make sure it
* doesn't already exists */
if (!node) {
ft->table[hash_num]=iso_file_table_node_new(f);
ft->count++;
return 1;
}
/* if it's already in, we don't do anything */
if (!iso_table_compare_files(ft, f, node->file))
return 0;
while (node->next) {
node = node->next;
/* if it's already in, we don't do anything */
if (!iso_table_compare_files(ft, f, node->file))
return 0;
}
node->next = iso_file_table_node_new(f);
ft->count++;
return 1;
}
/** 0 on equal, != 0 otherwise */
static int
iso_table_compare_node_file(struct iso_file_table *ft,
struct iso_tree_node_file *f1, struct iso_file *f2)
{
assert(ft && f1 && f2);
if (f1->node.procedence || f2->prev_img) {
if (f1->node.procedence && f2->prev_img)
return f1->loc.block != f2->real_ino;
else
return 1;
}
if (ft->cache_inodes) {
return (f1->node.attrib.st_dev != f2->real_dev)
|| (f1->node.attrib.st_ino != f2->real_ino);
} else {
return strcmp(f1->loc.path, f2->path);
}
}
struct iso_file *
iso_file_table_lookup(struct iso_file_table *ft, struct iso_tree_node_file *f)
{
struct iso_file_hash_node *node;
unsigned int hash_num;
int equal;
assert(ft && f);
/* find the hash number */
if (f->node.procedence == LIBISO_PREVIMG)
hash_num = f->loc.block % FILE_HASH_NODES;
else if ( ft->cache_inodes )
hash_num = iso_file_table_hash_inode(f->node.attrib.st_dev,
f->node.attrib.st_ino);
else
hash_num = iso_file_table_hash(f->loc.path);
node = ft->table[hash_num];
if (!node)
return NULL;
equal = !iso_table_compare_node_file(ft, f, node->file);
if (equal)
return node->file;
while (node->next) {
node = node->next;
equal = !iso_table_compare_node_file(ft, f, node->file);
if (equal)
return node->file;
}
return NULL;
}

74
libisofs/file.h Normal file
View File

@ -0,0 +1,74 @@
/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */
/* vim: set noet ts=8 sts=8 sw=8 : */
/**
* \file file.h
*
* Declare the structs to keep track of the files to be written into image.
* These files are stored in a hash table. Two modes of operation are supported:
* when cache inodes is enabled, the files are indexed into the table by the
* device and inode id in the local filesystem. This way, two different files
* with same inode and device id are treated as if they were a single file.
* This is usually the correct behavior, as a different file with same inode
* and device used to be a hard link.
* When cache inode is disabled, indexing is done by path on local filesystem.
*/
#ifndef FILE_H_
#define FILE_H_
#include <stdint.h>
#define FILE_HASH_NODES 2048
struct iso_file {
unsigned int prev_img:1; /**< if the file comes from a previous image */
char *path; /**< Path of the file on local filesystem */
off_t size; /**< size of this file */
ino_t ino; /**< This will be the inode number on CD of the file (RR) */
nlink_t nlink; /**< Number of hard links of the file on CD (RR) */
uint32_t block; /**< Block where this file is to be written on image */
dev_t real_dev;
ino_t real_ino; /**< for lookup by inode caching */
int sort_weight;
struct iso_file_src *src;
};
struct iso_file_hash_node {
struct iso_file_hash_node *next;
struct iso_file *file;
};
struct iso_file_table {
struct iso_file_hash_node *table[FILE_HASH_NODES];
size_t count;
int cache_inodes; /**< 1 to index by inode number */
};
struct iso_tree_node_file;
struct ecma119_write_target;
/**
* Create a struct that represents the specified iso_tree_node_file,
* suitable to be stored into the table,
*/
struct iso_file *iso_file_new(struct ecma119_write_target *t,
struct iso_tree_node_file*);
struct iso_file_table *iso_file_table_new(int cache_inodes);
/**
* Clear a hash table. All iso_file structs stored will also be freed,
* but not the path of each iso_file
*/
void iso_file_table_clear(struct iso_file_table *ft);
/**
* Add a new file to the table.
* \return 1 if the file is added, 0 if the file already exist on table
*/
int iso_file_table_add_file(struct iso_file_table *ft, struct iso_file *f);
struct iso_file *iso_file_table_lookup(struct iso_file_table *ft,
struct iso_tree_node_file *f);
#endif /*FILE_H_*/

306
libisofs/file_src.c Normal file
View File

@ -0,0 +1,306 @@
/*
* Implementation of iso_file_src for:
*
* a) read from local filesystem files
* b) read from previous session / image files
*/
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <assert.h>
#include <string.h>
#include <limits.h>
#include "file_src.h"
#include "messages.h"
#include "libisofs.h"
#include "util.h"
#define BLOCK_SIZE 2048
void iso_file_src_free(struct iso_file_src *src)
{
src->free_data(src);
free(src);
}
/*
* ==========================================================================
* A) FILE SRC FOR LOCAL FILESYSTEM FILES
* ==========================================================================
*/
struct local_file_data {
int fd; /* the file descriptor, once the file is opened */
off_t size; /* file size */
off_t bytes_read; /* bytes already read from file */
int error;
char *path; /* path of the file on local filesystem */
};
static
int lf_open(struct iso_file_src *src)
{
struct local_file_data *data;
assert(src);
data = (struct local_file_data*) src->data;
assert(data->fd == -1);
data->fd = open(data->path, O_RDONLY);
data->bytes_read = (off_t) 0;
data->error = 0;
return (data->fd != -1 ? 1 : 0);
}
static
void lf_close(struct iso_file_src *src)
{
struct local_file_data *data;
assert(src);
data = (struct local_file_data*) src->data;
assert(data->fd != -1);
close(data->fd);
data->fd = -1;
}
static
int lf_read_block(struct iso_file_src *src, unsigned char *buffer)
{
ssize_t bytes;
struct local_file_data *data;
assert(src);
assert(buffer);
data = (struct local_file_data*) src->data;
assert(data->fd != -1);
if (data->bytes_read >= data->size) {
return 0;
}
if (data->error) {
memset(buffer, 0, BLOCK_SIZE);
data->bytes_read += BLOCK_SIZE;
return -2;
}
bytes = 0;
do {
ssize_t result;
result = read(data->fd, buffer + bytes, BLOCK_SIZE - bytes);
if (result < 0) {
char msg[PATH_MAX + 32];
sprintf(msg, "Problem reading from %s", data->path);
iso_msg_sorry(LIBISO_CANT_READ_FILE, msg);
/* fill buffer with 0s and return */
memset(buffer + bytes, 0, BLOCK_SIZE - bytes);
data->bytes_read += BLOCK_SIZE;
return -1;
}
if (!result)
break;
bytes += result;
} while (bytes < BLOCK_SIZE);
if (bytes < BLOCK_SIZE) {
/* eof */
memset(buffer + bytes, 0, BLOCK_SIZE - bytes);
}
data->bytes_read += (off_t) bytes;
if (data->bytes_read >= data->size) {
return 0;
} else {
return 1;
}
}
static
off_t lf_get_size(struct iso_file_src *src)
{
struct local_file_data *data;
assert(src);
data = (struct local_file_data*) src->data;
return data->size;
}
static
void lf_free_data(struct iso_file_src *src)
{
struct local_file_data *data;
data = (struct local_file_data*) src->data;
free(data->path);
free(data);
}
struct iso_file_src*
iso_file_src_from_path(const char *path)
{
struct stat info;
struct iso_file_src *src;
struct local_file_data *data;
assert(path);
if (lstat(path, &info) < 0) {
iso_msg_fatal(LIBISO_FILE_DOESNT_EXIST, "File doesn't exist");
return NULL;
}
if (access(path, R_OK) < 0) {
iso_msg_fatal(LIBISO_FILE_NO_READ_ACCESS, "No read access");
return NULL;
}
data = malloc(sizeof(struct local_file_data));
if (!data)
return NULL;
src = malloc(sizeof(struct iso_file_src));
if (!src) {
free(data);
return NULL;
}
data->fd = -1;
data->path = strdup(path);
data->size = info.st_size;
src->open = lf_open;
src->close = lf_close;
src->read_block = lf_read_block;
src->get_size = lf_get_size;
src->free_data = lf_free_data;
src->data = data;
return src;
}
/*
* ==========================================================================
* B) FILE SRC FOR PREVIOUS IMAGE FILES
* ==========================================================================
*/
struct prev_img_file_data {
int block; /* block where image begins */
off_t size; /* file size */
int nblocks; /* file size in blocks */
int current; /* last block read from file */
int error;
struct data_source *src; /* source for reading from previous image */
};
static
int pi_open(struct iso_file_src *src)
{
struct prev_img_file_data *data;
assert(src);
data = (struct prev_img_file_data*) src->data;
data->current = data->block;
data->error = 0;
return 1;
}
static
void pi_close(struct iso_file_src *src)
{
/* nothing needed */
return;
}
static
int pi_read_block(struct iso_file_src *src, unsigned char *buffer)
{
int res;
struct prev_img_file_data *data;
assert(src);
assert(buffer);
data = (struct prev_img_file_data*) src->data;
if (data->current >= data->block + data->nblocks) {
return 0;
}
if (data->error) {
memset(buffer, 0, BLOCK_SIZE);
data->current++;
return -2;
}
res = data->src->read_block(data->src, data->current++, buffer);
if (res < 0) {
iso_msg_sorry(LIBISO_CANT_READ_IMG,
"Problem reading file from previous image");
/* fill buffer with 0s and return */
memset(buffer, 0, BLOCK_SIZE);
return -1;
}
if (data->current >= data->block + data->nblocks) {
return 0;
} else {
return 1;
}
}
static
off_t pi_get_size(struct iso_file_src *src)
{
struct prev_img_file_data *data;
assert(src);
data = (struct prev_img_file_data*) src->data;
return data->size;
}
static
void pi_free_data(struct iso_file_src *src)
{
free(src->data);
}
struct iso_file_src*
iso_file_src_from_prev_img(struct data_source* data_src, int block, off_t size)
{
struct iso_file_src *src;
struct prev_img_file_data *data;
data = malloc(sizeof(struct prev_img_file_data));
if (!data)
return NULL;
src = malloc(sizeof(struct iso_file_src));
if (!src) {
free(data);
return NULL;
}
data->block = block;
data->size = size;
data->nblocks = div_up(size, BLOCK_SIZE);
data->src = data_src;
src->open = pi_open;
src->close = pi_close;
src->read_block = pi_read_block;
src->get_size = pi_get_size;
src->free_data = pi_free_data;
src->data = data;
return src;
}

94
libisofs/file_src.h Normal file
View File

@ -0,0 +1,94 @@
/*
* Class for reading data from files, with base implementations
* for reading local filesystem files and previous session files.
*
* TODO this is kept private for now, it can be useful to make that
* public later.
*/
#ifndef FILE_SRC_H_
#define FILE_SRC_H_
#include <sys/types.h>
struct data_source;
struct iso_file_src {
/**
* Opens the file.
* This should be called before any call to read_block().
* @return 1 on success, <= 0 on error
*/
int (*open)(struct iso_file_src *src);
/**
* Close the file.
* This should be called when the iso_file_src is no more needed.
*/
void (*close)(struct iso_file_src *src);
/**
* Read data from the file in 2048 bytes chunks.
*
* A pointer to the contents of the file will be updated, in such a way
* that each time this function is called, new data is read to the buffer.
* It behaves in the same way as usual read(2) call this way.
*
* This always reads 2048 bytes, unless an EOF is found, or we reach the
* size previously returned by get_size(). In this case, the buffer is
* completed with 0s.
*
* @param buffer Buffer where the data will be written. Its size must
* be at least 2048 bytes.
* @return
* 1 if file contains more data. If we reach EOF before the expected
* size, this keeps returning 1, and next blocks are filled with 0s.
* 0 when we reach the expected size, that in normal usage is also EOF
* on file.
* < 0 on error. If such case, next calls to read_block will keep
* returning < 0 until we reach the expected size, filling blocks with
* 0s.
*/
int (*read_block)(struct iso_file_src *src, unsigned char *buffer);
/**
* Get the size in bytes of the file.
*
* This is set when the iso_file_src is created, and should not change. If
* the actual file size changes, it will be truncated or padding with 0s
* when the block is read
*/
off_t (*get_size)(struct iso_file_src *);
/** Clean up the source specific data */
void (*free_data)(struct iso_file_src *);
/** Source specific data */
void *data;
};
/**
* Get a iso_file_src() for reading from a local filesystem file.
*/
struct iso_file_src* iso_file_src_from_path(const char *path);
/**
* Get a iso_file_src() for reading from a previous image file.
* @param src data_source to read from previous image. It is not freed,
* so you need to free it when no more needed.
* @param block block on image where file begins
* @param nblocks number of block of the file
*/
struct iso_file_src*
iso_file_src_from_prev_img(struct data_source* src, int block, off_t size);
/**
* free a iso_file_src
*/
void iso_file_src_free(struct iso_file_src *src);
#endif /*FILE_SRC_H_*/

View File

@ -7,6 +7,8 @@
#include "tree.h"
#include "util.h"
#include "volume.h"
#include "eltorito.h"
#include "messages.h"
#include <assert.h>
#include <string.h>
@ -19,33 +21,88 @@ create_node(struct ecma119_write_target *t,
struct joliet_tree_node *ret =
calloc(1, sizeof(struct joliet_tree_node));
ret->name = iso_j_id(iso->name);
ret->name = iso_j_id(iso->name, t->input_charset);
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);
if ( ISO_ISDIR(iso) ) {
struct iso_tree_node_dir *dir = (struct iso_tree_node_dir *) iso;
ret->info.dir.children = calloc(sizeof(void*), dir->nchildren);
ret->type = JOLIET_DIR;
} else if (ISO_ISREG(iso)) {
/* it's a file */
struct iso_tree_node_file *iso_f = (struct iso_tree_node_file *) iso;
struct iso_file *file;
file = iso_file_table_lookup(t->file_table, iso_f);
if ( file == NULL ) {
file = iso_file_new(t, iso_f);
if (!file) {
/*
* That was an error.
* TODO currently this cause the file to be ignored... Maybe
* throw an error is a better alternative
*/
joliet_tree_free(ret);
return NULL;
}
iso_file_table_add_file(t->file_table, file);
}
ret->info.file = file;
ret->type = JOLIET_FILE;
} else if (ISO_ISBOOT(iso)) {
/* it's boot catalog info */
struct iso_tree_node_boot *boot = (struct iso_tree_node_boot*) iso;
ret->info.boot_img = boot->img;
ret->type = JOLIET_BOOT;
} else {
/* this should never happen */
assert(0);
}
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);
struct iso_tree_node *iso)
{
struct joliet_tree_node *root;
assert(t && iso);
if ( iso->hide_flags & LIBISO_HIDE_ON_JOLIET )
return NULL;
switch (iso->type) {
case LIBISO_NODE_FILE:
case LIBISO_NODE_BOOT:
root = create_node(t, parent, iso);
break;
case LIBISO_NODE_DIR:
{
size_t i;
struct joliet_tree_node *node;
struct iso_tree_node_dir *dir;
root = create_node(t, parent, iso);
dir = (struct iso_tree_node_dir*)iso;
for (i = 0; i < dir->nchildren; ++i) {
node = create_tree(t, root, dir->children[i]);
if ( node != NULL ) {
root->info.dir.children[root->info.dir.nchildren++] = node;
}
}
}
break;
default:
{
char msg[512];
sprintf(msg, "Can't add %s to Joliet tree. This kind of files "
"can only be added to a Rock Ridget tree. Skipping", iso->name);
iso_msg_note(LIBISO_JOLIET_WRONG_FILE_TYPE, msg);
return NULL;
}
break;
}
return root;
}
@ -63,12 +120,15 @@ sort_tree(struct joliet_tree_node *root)
{
size_t i;
assert(root && ISO_ISDIR(root->iso_self));
assert(root && (root->type == JOLIET_DIR));
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]);
qsort(root->info.dir.children, root->info.dir.nchildren,
sizeof(void*), cmp_node);
for (i = 0; i < root->info.dir.nchildren; i++) {
struct joliet_tree_node *child = root->info.dir.children[i];
if ( child->type == JOLIET_DIR )
sort_tree(child);
}
}
void
@ -80,11 +140,11 @@ joliet_prepare_path_tables(struct ecma119_write_target *t)
t->path_table_size_joliet = 10; /* root directory record */
cur = 1;
for (i = 0; i < t->dirlist_len; i++) {
for (i = 0; i < t->dirlist_len_joliet; 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)) {
for (j = 0; j < dir->info.dir.nchildren; j++) {
struct joliet_tree_node *ch = dir->info.dir.children[j];
if (ch->type == JOLIET_DIR) {
size_t len = 8 + ucslen(ch->name)*2;
t->pathlist_joliet[cur++] = ch;
t->path_table_size_joliet += len;
@ -104,22 +164,22 @@ joliet_calc_dir_size(struct ecma119_write_target *t,
size_t newlen;
struct joliet_tree_node *ch;
assert(root && ISO_ISDIR(root->iso_self));
assert(root && (root->type == JOLIET_DIR) );
root->len = 68; /* for "." and ".." entries */
for (i = 0; i < root->nchildren; i++) {
ch = root->children[i];
newlen = root->len + ch->dirent_len;
if ((newlen % 2048) < (root->len % 2048)) {
root->len = newlen + (2048 - (root->len % 2048));
t->dirlist_len_joliet++;
root->info.dir.len = 68; /* for "." and ".." entries */
for (i = 0; i < root->info.dir.nchildren; ++i) {
ch = root->info.dir.children[i];
newlen = root->info.dir.len + ch->dirent_len;
if ((newlen % 2048) < (root->info.dir.len % 2048)) {
root->info.dir.len = newlen + (2048 - (root->info.dir.len % 2048));
} else {
root->info.dir.len += ch->dirent_len;
}
else {
root->len += ch->dirent_len;
}
if (ISO_ISDIR(ch->iso_self))
if (ch->type == JOLIET_DIR)
joliet_calc_dir_size(t, ch);
}
t->total_dir_size_joliet += round_up (root->len, t->block_size);
t->total_dir_size_joliet += round_up (root->info.dir.len, t->block_size);
}
/**
@ -132,45 +192,17 @@ joliet_calc_dir_pos(struct ecma119_write_target *t,
size_t i;
struct joliet_tree_node *ch;
assert(root && ISO_ISDIR(root->iso_self));
assert(root && (root->type == JOLIET_DIR));
root->block = t->curblock;
t->curblock += div_up(root->len, t->block_size);
root->info.dir.block = t->curblock;
t->curblock += div_up(root->info.dir.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))
for (i = 0; i < root->info.dir.nchildren; i++) {
ch = root->info.dir.children[i];
if (ch->type == JOLIET_DIR)
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*
@ -183,6 +215,21 @@ joliet_tree_create(struct ecma119_write_target *t,
return root;
}
void
joliet_tree_free(struct joliet_tree_node *root)
{
size_t i;
if (root->type == JOLIET_DIR) {
for (i=0; i < root->info.dir.nchildren; i++) {
joliet_tree_free(root->info.dir.children[i]);
}
free(root->info.dir.children);
}
free(root->name);
free(root);
}
/* ugh. this is mostly C&P */
static void
write_path_table(struct ecma119_write_target *t,
@ -192,7 +239,7 @@ write_path_table(struct ecma119_write_target *t,
void (*write_int)(uint8_t*, uint32_t, int) = l_type ?
iso_lsb : iso_msb;
size_t i;
size_t i;
struct ecma119_path_table_record *rec;
struct joliet_tree_node *dir;
int parent = 0;
@ -209,7 +256,7 @@ write_path_table(struct ecma119_write_target *t,
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->block, dir->info.dir.block, 4);
write_int(rec->parent, parent + 1, 2);
if (dir->parent)
memcpy(rec->dir_id, dir->name, rec->len_di[0]);
@ -227,20 +274,42 @@ write_one_dir_record(struct ecma119_write_target *t,
int file_id,
uint8_t *buf)
{
uint32_t len;
uint32_t block;
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 (node->type == JOLIET_DIR) {
len = node->info.dir.len;
block = node->info.dir.block;
} else if (node->type == JOLIET_BOOT) {
assert(t->eltorito);
if (node->info.boot_img) {
block = t->imgblock;
len = t->catalog->image->node->node.attrib.st_size;
} else {
/* we always assume 2048 as catalog len */
block = t->catblock;
len = 2048;
}
} else {
/* file */
assert(node->type == JOLIET_FILE);
len = node->info.file->size;
block = node->info.file->block;
}
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_bb(rec->block, block, 4);
iso_bb(rec->length, len, 4);
iso_datetime_7(rec->recording_time, t->now);
rec->flags[0] = ISO_ISDIR(node->iso_self) ? 2 : 0;
rec->flags[0] = (node->type == JOLIET_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);
@ -258,24 +327,34 @@ 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)
void
joliet_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);
uint16_t *vol_id = str2ucs(volume->volume_id, t->input_charset);
uint16_t *pub_id = str2ucs(volume->publisher_id, t->input_charset);
uint16_t *data_id = str2ucs(volume->data_preparer_id, t->input_charset);
uint16_t *volset_id = str2ucs(t->volset->volset_id, t->input_charset);
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);
uint16_t *system_id = str2ucs(volume->system_id, t->input_charset);
uint16_t *application_id = str2ucs(volume->application_id, t->input_charset);
uint16_t *copyright_file_id = str2ucs(volume->copyright_file_id, t->input_charset);
uint16_t *abstract_file_id = str2ucs(volume->abstract_file_id, t->input_charset);
uint16_t *biblio_file_id = str2ucs(volume->biblio_file_id, t->input_charset);
int system_id_len = MIN(32, ucslen(system_id) * 2);
int application_id_len = MIN(128, ucslen(application_id) * 2);
int copyright_file_id_len = MIN(37, ucslen(copyright_file_id) * 2);
int abstract_file_id_len = MIN(37, ucslen(abstract_file_id) * 2);
int biblio_file_id_len = MIN(37, ucslen(biblio_file_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);
@ -292,8 +371,13 @@ write_sup_vol_desc(struct ecma119_write_target *t, uint8_t *buf)
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);*/
memcpy(vol->system_id, system_id, system_id_len);
memcpy(vol->application_id, "APPID", application_id_len);
memcpy(vol->copyright_file_id, copyright_file_id, copyright_file_id_len);
memcpy(vol->abstract_file_id, abstract_file_id, abstract_file_id_len);
memcpy(vol->bibliographic_file_id, biblio_file_id, biblio_file_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);
@ -303,6 +387,11 @@ write_sup_vol_desc(struct ecma119_write_target *t, uint8_t *buf)
free(volset_id);
free(pub_id);
free(data_id);
free(system_id);
free(application_id);
free(copyright_file_id);
free(abstract_file_id);
free(biblio_file_id);
}
@ -317,7 +406,7 @@ write_one_dir(struct ecma119_write_target *t,
uint8_t *orig_buf = buf;
uint8_t *prior_buf = buf;
assert(ISO_ISDIR (dir->iso_self));
assert(dir->type == JOLIET_DIR);
/* write the "." and ".." entries first */
write_one_dir_record(t, dir, 0, buf);
buf += ((struct ecma119_dir_record*) buf)->len_dr[0];
@ -325,8 +414,8 @@ write_one_dir(struct ecma119_write_target *t,
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);
for (i = 0; i < dir->info.dir.nchildren; i++) {
write_one_dir_record(t, dir->info.dir.children[i], -1, buf);
len = ((struct ecma119_dir_record*) buf)->len_dr[0];
if ((buf + len - prior_buf) >= 2048) {
for (j = len - 1; j >= 0; j--) {
@ -341,7 +430,7 @@ write_one_dir(struct ecma119_write_target *t,
}
}
assert (buf - orig_buf == dir->len);
assert (buf - orig_buf == dir->info.dir.len);
}
static void
@ -350,11 +439,11 @@ 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++) {
assert (t->curblock + t->ms_block == t->dirlist_joliet[0]->info.dir.block);
for (i = 0; i < t->dirlist_len_joliet; i++) {
dir = t->dirlist_joliet[i];
write_one_dir(t, dir, buf);
buf += round_up(dir->len, t->block_size);
buf += round_up(dir->info.dir.len, t->block_size);
}
}
@ -363,7 +452,7 @@ joliet_wr_sup_vol_desc(struct ecma119_write_target *t,
uint8_t *buf)
{
ecma119_start_chunking(t,
write_sup_vol_desc,
joliet_write_sup_vol_desc,
2048,
buf);
}

View File

@ -17,19 +17,36 @@
struct ecma119_write_target;
struct iso_tree_node;
enum joliet_node_type {
JOLIET_FILE,
JOLIET_DIR,
JOLIET_BOOT
};
struct joliet_dir_info {
struct joliet_tree_node **children;
size_t nchildren;
size_t len;
size_t block;
};
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;
enum joliet_node_type type;
union {
struct iso_file *file;
struct joliet_dir_info dir;
unsigned int boot_img:1; /** For boot nodes, it identifies if this
* corresponds to image(1) or catalog(0).
* The block is stored in ecma119_write_target
*/
} info;
};
/**
@ -52,13 +69,6 @@ joliet_calc_dir_size(struct ecma119_write_target *t, struct joliet_tree_node*);
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.
@ -69,6 +79,9 @@ joliet_prepare_path_tables(struct ecma119_write_target *t);
void
joliet_tree_free(struct joliet_tree_node *root);
void
joliet_write_sup_vol_desc(struct ecma119_write_target *t, uint8_t *buf);
void
joliet_wr_sup_vol_desc(struct ecma119_write_target *t, uint8_t *buf);

404
libisofs/libiso_msgs.c Normal file
View File

@ -0,0 +1,404 @@
/* libiso_msgs
Message handling facility of libiso.
Copyright (C) 2006 Thomas Schmitt <scdbackup@gmx.net>, provided under GPL
*/
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/time.h>
/* Only this single source module is entitled to do this */
#define LIBISO_MSGS_H_INTERNAL 1
/* All participants in the messaging system must do this */
#include "libiso_msgs.h"
/* ----------------------------- libiso_msgs_item ------------------------- */
static int libiso_msgs_item_new(struct libiso_msgs_item **item,
struct libiso_msgs_item *link, int flag)
{
int ret;
struct libiso_msgs_item *o;
struct timeval tv;
struct timezone tz;
(*item)= o=
(struct libiso_msgs_item *) malloc(sizeof(struct libiso_msgs_item));
if(o==NULL)
return(-1);
o->timestamp= 0.0;
ret= gettimeofday(&tv,&tz);
if(ret==0)
o->timestamp= tv.tv_sec+0.000001*tv.tv_usec;
o->process_id= getpid();
o->driveno= -1;
o->severity= LIBISO_MSGS_SEV_ALL;
o->priority= LIBISO_MSGS_PRIO_ZERO;
o->error_code= 0;
o->msg_text= NULL;
o->os_errno= 0;
o->prev= link;
o->next= NULL;
if(link!=NULL) {
if(link->next!=NULL) {
link->next->prev= o;
o->next= link->next;
}
link->next= o;
}
return(1);
}
/** Detaches item from its queue and eventually readjusts start, end pointers
of the queue */
int libiso_msgs_item_unlink(struct libiso_msgs_item *o,
struct libiso_msgs_item **chain_start,
struct libiso_msgs_item **chain_end, int flag)
{
if(o->prev!=NULL)
o->prev->next= o->next;
if(o->next!=NULL)
o->next->prev= o->prev;
if(chain_start!=NULL)
if(*chain_start == o)
*chain_start= o->next;
if(chain_end!=NULL)
if(*chain_end == o)
*chain_end= o->prev;
o->next= o->prev= NULL;
return(1);
}
int libiso_msgs_item_destroy(struct libiso_msgs_item **item,
int flag)
{
struct libiso_msgs_item *o;
o= *item;
if(o==NULL)
return(0);
libiso_msgs_item_unlink(o,NULL,NULL,0);
if(o->msg_text!=NULL)
free((char *) o->msg_text);
free((char *) o);
*item= NULL;
return(1);
}
int libiso_msgs_item_get_msg(struct libiso_msgs_item *item,
int *error_code, char **msg_text, int *os_errno,
int flag)
{
*error_code= item->error_code;
*msg_text= item->msg_text;
*os_errno= item->os_errno;
return(1);
}
int libiso_msgs_item_get_origin(struct libiso_msgs_item *item,
double *timestamp, pid_t *process_id, int *driveno,
int flag)
{
*timestamp= item->timestamp;
*process_id= item->process_id;
*driveno= item->driveno;
return(1);
}
int libiso_msgs_item_get_rank(struct libiso_msgs_item *item,
int *severity, int *priority, int flag)
{
*severity= item->severity;
*priority= item->priority;
return(1);
}
/* ------------------------------- libiso_msgs ---------------------------- */
int libiso_msgs_new(struct libiso_msgs **m, int flag)
{
struct libiso_msgs *o;
(*m)= o= (struct libiso_msgs *) malloc(sizeof(struct libiso_msgs));
if(o==NULL)
return(-1);
o->oldest= NULL;
o->youngest= NULL;
o->count= 0;
o->queue_severity= LIBISO_MSGS_SEV_ALL;
o->print_severity= LIBISO_MSGS_SEV_NEVER;
strcpy(o->print_id,"libiso: ");
#ifndef LIBISO_MSGS_SINGLE_THREADED
pthread_mutex_init(&(o->lock_mutex),NULL);
#endif
return(1);
}
int libiso_msgs_destroy(struct libiso_msgs **m, int flag)
{
struct libiso_msgs *o;
struct libiso_msgs_item *item, *next_item;
o= *m;
if(o==NULL)
return(0);
#ifndef LIBISO_MSGS_SINGLE_THREADED
if(pthread_mutex_destroy(&(o->lock_mutex))!=0) {
pthread_mutex_unlock(&(o->lock_mutex));
pthread_mutex_destroy(&(o->lock_mutex));
}
#endif
for(item= o->oldest; item!=NULL; item= next_item) {
next_item= item->next;
libiso_msgs_item_destroy(&item,0);
}
free((char *) o);
*m= NULL;
return(1);
}
int libiso_msgs_set_severities(struct libiso_msgs *m, int queue_severity,
int print_severity, char *print_id, int flag)
{
m->queue_severity= queue_severity;
m->print_severity= print_severity;
strncpy(m->print_id,print_id,80);
m->print_id[80]= 0;
return(1);
}
static int libiso_msgs_lock(struct libiso_msgs *m, int flag)
{
#ifndef LIBISO_MSGS_SINGLE_THREADED
int ret;
ret= pthread_mutex_lock(&(m->lock_mutex));
if(ret!=0)
return(0);
#endif
return(1);
}
static int libiso_msgs_unlock(struct libiso_msgs *m, int flag)
{
#ifndef LIBISO_MSGS_SINGLE_THREADED
int ret;
ret= pthread_mutex_unlock(&(m->lock_mutex));
if(ret!=0)
return(0);
#endif
return(1);
}
int libiso_msgs__text_to_sev(char *severity_name, int *severity,
int flag)
{
if(strncmp(severity_name,"NEVER",5)==0)
*severity= LIBISO_MSGS_SEV_NEVER;
else if(strncmp(severity_name,"ABORT",5)==0)
*severity= LIBISO_MSGS_SEV_ABORT;
else if(strncmp(severity_name,"FATAL",5)==0)
*severity= LIBISO_MSGS_SEV_FATAL;
else if(strncmp(severity_name,"SORRY",5)==0)
*severity= LIBISO_MSGS_SEV_SORRY;
else if(strncmp(severity_name,"WARNING",7)==0)
*severity= LIBISO_MSGS_SEV_WARNING;
else if(strncmp(severity_name,"HINT",4)==0)
*severity= LIBISO_MSGS_SEV_HINT;
else if(strncmp(severity_name,"NOTE",4)==0)
*severity= LIBISO_MSGS_SEV_NOTE;
else if(strncmp(severity_name,"UPDATE",6)==0)
*severity= LIBISO_MSGS_SEV_UPDATE;
else if(strncmp(severity_name,"DEBUG",5)==0)
*severity= LIBISO_MSGS_SEV_DEBUG;
else if(strncmp(severity_name,"ALL",3)==0)
*severity= LIBISO_MSGS_SEV_ALL;
else {
*severity= LIBISO_MSGS_SEV_NEVER;
return(0);
}
return(1);
}
int libiso_msgs__sev_to_text(int severity, char **severity_name,
int flag)
{
if(flag&1) {
*severity_name=
"NEVER\nABORT\nFATAL\nSORRY\nWARNING\nHINT\nNOTE\nUPDATE\nDEBUG\nALL";
return(1);
}
*severity_name= "";
if(severity>=LIBISO_MSGS_SEV_NEVER)
*severity_name= "NEVER";
else if(severity>=LIBISO_MSGS_SEV_ABORT)
*severity_name= "ABORT";
else if(severity>=LIBISO_MSGS_SEV_FATAL)
*severity_name= "FATAL";
else if(severity>=LIBISO_MSGS_SEV_SORRY)
*severity_name= "SORRY";
else if(severity>=LIBISO_MSGS_SEV_WARNING)
*severity_name= "WARNING";
else if(severity>=LIBISO_MSGS_SEV_HINT)
*severity_name= "HINT";
else if(severity>=LIBISO_MSGS_SEV_NOTE)
*severity_name= "NOTE";
else if(severity>=LIBISO_MSGS_SEV_UPDATE)
*severity_name= "UPDATE";
else if(severity>=LIBISO_MSGS_SEV_DEBUG)
*severity_name= "DEBUG";
else if(severity>=LIBISO_MSGS_SEV_ALL)
*severity_name= "ALL";
else {
*severity_name= "";
return(0);
}
return(1);
}
int libiso_msgs_submit(struct libiso_msgs *m, int driveno, int error_code,
int severity, int priority, char *msg_text,
int os_errno, int flag)
{
int ret;
char *textpt,*sev_name,sev_text[81];
struct libiso_msgs_item *item= NULL;
if(severity >= m->print_severity) {
if(msg_text==NULL)
textpt= "";
else
textpt= msg_text;
sev_text[0]= 0;
ret= libiso_msgs__sev_to_text(severity,&sev_name,0);
if(ret>0)
sprintf(sev_text,"%s : ",sev_name);
fprintf(stderr,"%s%s%s\n",m->print_id,sev_text,textpt);
if(os_errno!=0) {
ret= libiso_msgs_lock(m,0);
if(ret<=0)
return(-1);
fprintf(stderr,"%s( Most recent system error: %d '%s' )\n",
m->print_id,os_errno,strerror(os_errno));
libiso_msgs_unlock(m,0);
}
}
if(severity < m->queue_severity)
return(0);
ret= libiso_msgs_lock(m,0);
if(ret<=0)
return(-1);
ret= libiso_msgs_item_new(&item,m->youngest,0);
if(ret<=0)
goto failed;
item->driveno= driveno;
item->error_code= error_code;
item->severity= severity;
item->priority= priority;
if(msg_text!=NULL) {
item->msg_text= malloc(strlen(msg_text)+1);
if(item->msg_text==NULL)
goto failed;
strcpy(item->msg_text,msg_text);
}
item->os_errno= os_errno;
if(m->oldest==NULL)
m->oldest= item;
m->youngest= item;
m->count++;
libiso_msgs_unlock(m,0);
/*
fprintf(stderr,"libiso_experimental: message submitted to queue (now %d)\n",
m->count);
*/
return(1);
failed:;
libiso_msgs_item_destroy(&item,0);
libiso_msgs_unlock(m,0);
return(-1);
}
int libiso_msgs_obtain(struct libiso_msgs *m, struct libiso_msgs_item **item,
int severity, int priority, int flag)
{
int ret;
struct libiso_msgs_item *im, *next_im= NULL;
*item= NULL;
ret= libiso_msgs_lock(m,0);
if(ret<=0)
return(-1);
for(im= m->oldest; im!=NULL; im= next_im) {
for(; im!=NULL; im= next_im) {
next_im= im->next;
if(im->severity>=severity)
break;
libiso_msgs_item_unlink(im,&(m->oldest),&(m->youngest),0);
libiso_msgs_item_destroy(&im,0); /* severity too low: delete */
}
if(im==NULL)
break;
if(im->priority>=priority)
break;
}
if(im==NULL)
{ret= 0; goto ex;}
libiso_msgs_item_unlink(im,&(m->oldest),&(m->youngest),0);
*item= im;
ret= 1;
ex:;
libiso_msgs_unlock(m,0);
return(ret);
}
int libiso_msgs_destroy_item(struct libiso_msgs *m,
struct libiso_msgs_item **item, int flag)
{
int ret;
ret= libiso_msgs_lock(m,0);
if(ret<=0)
return(-1);
ret= libiso_msgs_item_destroy(item,0);
libiso_msgs_unlock(m,0);
return(ret);
}

454
libisofs/libiso_msgs.h Normal file
View File

@ -0,0 +1,454 @@
/* libiso_msgs
Message handling facility of libisofs.
Copyright (C) 2006-2007 Thomas Schmitt <scdbackup@gmx.net>,
provided under GPL
*/
/*
*Never* set this macro outside libiso_msgs.c !
The entrails of the message handling facility are not to be seen by
the other library components or the applications.
*/
#ifdef LIBISO_MSGS_H_INTERNAL
#ifndef LIBISO_MSGS_SINGLE_THREADED
#include <pthread.h>
#endif
struct libiso_msgs_item {
double timestamp;
pid_t process_id;
int driveno;
int severity;
int priority;
/* Apply for your developer's error code range at
libburn-hackers@pykix.org
Report introduced codes in the list below. */
int error_code;
char *msg_text;
int os_errno;
struct libiso_msgs_item *prev,*next;
};
struct libiso_msgs {
struct libiso_msgs_item *oldest;
struct libiso_msgs_item *youngest;
int count;
int queue_severity;
int print_severity;
char print_id[81];
#ifndef LIBISO_MSGS_SINGLE_THREADED
pthread_mutex_t lock_mutex;
#endif
};
#endif /* LIBISO_MSGS_H_INTERNAL */
#ifndef LIBISO_MSGS_H_INCLUDED
#define LIBISO_MSGS_H_INCLUDED 1
#ifndef LIBISO_MSGS_H_INTERNAL
/* Public Opaque Handles */
/** A pointer to this is a opaque handle to a message handling facility */
struct libiso_msgs;
/** A pointer to this is a opaque handle to a single message item */
struct libiso_msgs_item;
#endif /* ! LIBISO_MSGS_H_INTERNAL */
/* Public Macros */
/* Registered Severities */
/* It is well advisable to let applications select severities via strings and
forwarded functions libiso_msgs__text_to_sev(), libiso_msgs__sev_to_text().
These macros are for use by libdax/libburn only.
*/
/** Use this to get messages of any severity. Do not use for submitting.
*/
#define LIBISO_MSGS_SEV_ALL 0x00000000
/** Debugging messages not to be visible to normal users by default
*/
#define LIBISO_MSGS_SEV_DEBUG 0x10000000
/** Update of a progress report about long running actions
*/
#define LIBISO_MSGS_SEV_UPDATE 0x20000000
/** Not so usual events which were gracefully handled
*/
#define LIBISO_MSGS_SEV_NOTE 0x30000000
/** Possibilities to achieve a better result
*/
#define LIBISO_MSGS_SEV_HINT 0x40000000
/** Warnings about problems which could not be handled optimally
*/
#define LIBISO_MSGS_SEV_WARNING 0x50000000
/** Non-fatal error messages indicating that parts of the action failed
but processing will/should go on
*/
#define LIBISO_MSGS_SEV_SORRY 0x60000000
/** An error message which puts the whole operation of libdax in question
*/
#define LIBISO_MSGS_SEV_FATAL 0x70000000
/** A message from an abort handler which will finally finish libburn
*/
#define LIBISO_MSGS_SEV_ABORT 0x71000000
/** A severity to exclude resp. discard any possible message.
Do not use this severity for submitting.
*/
#define LIBISO_MSGS_SEV_NEVER 0x7fffffff
/* Registered Priorities */
/* Priorities are to be used by libburn/libdax only. */
#define LIBISO_MSGS_PRIO_ZERO 0x00000000
#define LIBISO_MSGS_PRIO_LOW 0x10000000
#define LIBISO_MSGS_PRIO_MEDIUM 0x20000000
#define LIBISO_MSGS_PRIO_HIGH 0x30000000
#define LIBISO_MSGS_PRIO_TOP 0x7ffffffe
/* Do not use this priority for submitting */
#define LIBISO_MSGS_PRIO_NEVER 0x7fffffff
/* Public Functions */
/* Calls initiated from inside libdax/libburn */
/** Create new empty message handling facility with queue.
@param flag Bitfield for control purposes (unused yet, submit 0)
@return >0 success, <=0 failure
*/
int libiso_msgs_new(struct libiso_msgs **m, int flag);
/** Destroy a message handling facility and all its eventual messages.
The submitted pointer gets set to NULL.
@param flag Bitfield for control purposes (unused yet, submit 0)
@return 1 for success, 0 for pointer to NULL
*/
int libiso_msgs_destroy(struct libiso_msgs **m, int flag);
/** Submit a message to a message handling facility.
@param driveno libdax drive number. Use -1 if no number is known.
@param error_code Unique error code. Use only registered codes. See below.
The same unique error_code may be issued at different
occasions but those should be equivalent out of the view
of a libdax application. (E.g. "cannot open ATA drive"
versus "cannot open SCSI drive" would be equivalent.)
@param severity The LIBISO_MSGS_SEV_* of the event.
@param priority The LIBISO_MSGS_PRIO_* number of the event.
@param msg_text Printable and human readable message text.
@param os_errno Eventual error code from operating system (0 if none)
@param flag Bitfield for control purposes (unused yet, submit 0)
@return 1 on success, 0 on rejection, <0 for severe errors
*/
int libiso_msgs_submit(struct libiso_msgs *m, int driveno, int error_code,
int severity, int priority, char *msg_text,
int os_errno, int flag);
/* Calls from applications (to be forwarded by libdax/libburn) */
/** Convert a registered severity number into a severity name
@param flag Bitfield for control purposes:
bit0= list all severity names in a newline separated string
@return >0 success, <=0 failure
*/
int libiso_msgs__sev_to_text(int severity, char **severity_name,
int flag);
/** Convert a severity name into a severity number,
@param flag Bitfield for control purposes (unused yet, submit 0)
@return >0 success, <=0 failure
*/
int libiso_msgs__text_to_sev(char *severity_name, int *severity,
int flag);
/** Set minimum severity for messages to be queued (default
LIBISO_MSGS_SEV_ALL) and for messages to be printed directly to stderr
(default LIBISO_MSGS_SEV_NEVER).
@param print_id A text of at most 80 characters to be printed before
any eventually printed message (default is "libdax: ").
@param flag Bitfield for control purposes (unused yet, submit 0)
@return always 1 for now
*/
int libiso_msgs_set_severities(struct libiso_msgs *m, int queue_severity,
int print_severity, char *print_id, int flag);
/** Obtain a message item that has at least the given severity and priority.
Usually all older messages of lower severity are discarded then. If no
item of sufficient severity was found, all others are discarded from the
queue.
@param flag Bitfield for control purposes (unused yet, submit 0)
@return 1 if a matching item was found, 0 if not, <0 for severe errors
*/
int libiso_msgs_obtain(struct libiso_msgs *m, struct libiso_msgs_item **item,
int severity, int priority, int flag);
/** Destroy a message item obtained by libiso_msgs_obtain(). The submitted
pointer gets set to NULL.
Caution: Copy eventually obtained msg_text before destroying the item,
if you want to use it further.
@param flag Bitfield for control purposes (unused yet, submit 0)
@return 1 for success, 0 for pointer to NULL, <0 for severe errors
*/
int libiso_msgs_destroy_item(struct libiso_msgs *m,
struct libiso_msgs_item **item, int flag);
/** Obtain from a message item the three application oriented components as
submitted with the originating call of libiso_msgs_submit().
Caution: msg_text becomes a pointer into item, not a copy.
@param flag Bitfield for control purposes (unused yet, submit 0)
@return 1 on success, 0 on invalid item, <0 for servere errors
*/
int libiso_msgs_item_get_msg(struct libiso_msgs_item *item,
int *error_code, char **msg_text, int *os_errno,
int flag);
/** Obtain from a message item the submitter identification submitted
with the originating call of libiso_msgs_submit().
@param flag Bitfield for control purposes (unused yet, submit 0)
@return 1 on success, 0 on invalid item, <0 for servere errors
*/
int libiso_msgs_item_get_origin(struct libiso_msgs_item *item,
double *timestamp, pid_t *process_id, int *driveno,
int flag);
/** Obtain from a message item severity and priority as submitted
with the originating call of libiso_msgs_submit().
@param flag Bitfield for control purposes (unused yet, submit 0)
@return 1 on success, 0 on invalid item, <0 for servere errors
*/
int libiso_msgs_item_get_rank(struct libiso_msgs_item *item,
int *severity, int *priority, int flag);
#ifdef LIDBAX_MSGS_________________
/* Registered Error Codes */
Format: error_code (LIBISO_MSGS_SEV_*,LIBISO_MSGS_PRIO_*) = explanation
If no severity or priority are fixely associates, use "(,)".
------------------------------------------------------------------------------
Range "libiso_msgs" : 0x00000000 to 0x0000ffff
0x00000000 (ALL,ZERO) = Initial setting in new libiso_msgs_item
0x00000001 (DEBUG,ZERO) = Test error message
0x00000002 (DEBUG,ZERO) = Debugging message
------------------------------------------------------------------------------
Range "elmom" : 0x00010000 to 0x0001ffff
------------------------------------------------------------------------------
Range "scdbackup" : 0x00020000 to 0x0002ffff
Acessing and defending drives:
0x00020001 (SORRY,LOW) = Cannot open busy device
0x00020002 (SORRY,HIGH) = Encountered error when closing drive
0x00020003 (SORRY,HIGH) = Could not grab drive
0x00020004 (NOTE,HIGH) = Opened O_EXCL scsi sibling
0x00020005 (SORRY,HIGH) = Failed to open device
0x00020006 (FATAL,HIGH) = Too many scsi siblings
0x00020007 (NOTE,HIGH) = Closed O_EXCL scsi siblings
0x00020008 (SORRY,HIGH) = Device busy. Failed to fcntl-lock
General library operations:
0x00020101 (WARNING,HIGH) = Cannot find given worker item
0x00020102 (SORRY,HIGH) = A drive operation is still going on
0x00020103 (WARNING,HIGH) = After scan a drive operation is still going on
0x00020104 (SORRY,HIGH) = NULL pointer caught
0x00020105 (SORRY,HIGH) = Drive is already released
0x00020106 (SORRY,HIGH) = Drive is busy on attempt to close
0x00020107 (SORRY,HIGH) = Drive is busy on attempt to shut down library
0x00020108 (SORRY,HIGH) = Drive is not grabbed on disc status inquiry
0x00020108 (FATAL,HIGH) = Could not allocate new drive object
0x00020109 (FATAL,HIGH) = Library not running
0x0002010a (FATAL,HIGH) = Unsuitable track mode
0x0002010b (FATAL,HIGH) = Burn run failed
0x0002010c (FATAL,HIGH) = Failed to transfer command to drive
0x0002010d (DEBUG,HIGH) = Could not inquire TOC
0x0002010e (FATAL,HIGH) = Attempt to read ATIP from ungrabbed drive
0x0002010f (DEBUG,HIGH) = SCSI error condition on command
0x00020110 (FATAL,HIGH) = Persistent drive address too long
0x00020111 (FATAL,HIGH) = Could not allocate new auxiliary object
0x00020112 (SORRY,HIGH) = Bad combination of write_type and block_type
0x00020113 (FATAL,HIGH) = Drive capabilities not inquired yet
0x00020114 (SORRY,HIGH) = Attempt to set ISRC with bad data
0x00020115 (SORRY,HIGH) = Attempt to set track mode to unusable value
0x00020116 (FATAL,HIGH) = Track mode has unusable value
0x00020117 (FATAL,HIGH) = toc_entry of drive is already in use
0x00020118 (DEBUG,HIGH) = Closing track
0x00020119 (DEBUG,HIGH) = Closing session
0x0002011a (NOTE,HIGH) = Padding up track to minimum size
0x0002011b (FATAL,HIGH) = Attempt to read track info from ungrabbed drive
0x0002011c (FATAL,HIGH) = Attempt to read track info from busy drive
0x0002011d (FATAL,HIGH) = SCSI error on write
0x0002011e (SORRY,HIGH) = Unsuitable media detected
0x0002011f (SORRY,HIGH) = Burning is restricted to a single track
0x00020120 (NOTE,HIGH) = FORMAT UNIT ignored
0x00020121 (FATAL,HIGH) = Write preparation setup failed
0x00020122 (FATAL,HIGH) = SCSI error on format_unit
0x00020123 (SORRY,HIGH) = DVD Media are unsuitable for desired track type
0x00020124 (SORRY,HIGH) = SCSI error on set_streaming
0x00020125 (SORRY,HIGH) = Write start address not supported
0x00020126 (SORRY,HIGH) = Write start address not properly aligned
0x00020127 (NOTE,HIGH) = Write start address is ...
0x00020128 (FATAL,HIGH) = Unsupported inquiry_type with mmc_get_performance
0x00020129 (SORRY,HIGH) = Will not format media type
0x0002012a (FATAL,HIGH) = Cannot inquire write mode capabilities
0x0002012b (FATAL,HIGH) = Drive offers no suitable write mode with this job
0x0002012c (SORRY,HIGH) = Too many logical tracks recorded
0x0002012d (FATAL,HIGH) = Exceeding range of permissible write addresses
0x0002012e (NOTE,HIGH) = Activated track default size
0x0002012f (SORRY,HIGH) = SAO is restricted to single fixed size session
0x00020130 (SORRY,HIGH) = Drive and media state unsuitable for blanking
0x00020131 (SORRY,HIGH) = No suitable formatting type offered by drive
0x00020132 (SORRY,HIGH) = Selected format is not suitable for libburn
0x00020133 (SORRY,HIGH) = Cannot mix data and audio in SAO mode
0x00020134 (NOTE,HIGH) = Defaulted TAO to DAO
0x00020135 (SORRY,HIGH) = Cannot perform TAO, job unsuitable for DAO
0x00020136 (SORRY,HIGH) = DAO burning restricted to single fixed size track
0x00020137 (HINT,HIGH) = TAO would be possible
0x00020138 (FATAL,HIGH) = Cannot reserve track
0x00020139 (SORRY,HIGH) = Write job parameters are unsuitable
0x0002013a (FATAL,HIGH) = No suitable media detected
0x0002013b (DEBUG,HIGH) = SCSI command indicates host or driver error
0x0002013c (SORRY,HIGH) = Malformed capabilities page 2Ah received
0x0002013d (DEBUG,LOW) = Waiting for free buffer space takes long time
0x0002013e (SORRY,HIGH) = Timeout with waiting for free buffer. Now disabled
0x0002013f (DEBUG,LOW) = Reporting total time spent with waiting for buffer
libiso_audioxtr:
0x00020200 (SORRY,HIGH) = Cannot open audio source file
0x00020201 (SORRY,HIGH) = Audio source file has unsuitable format
0x00020202 (SORRY,HIGH) = Failed to prepare reading of audio data
------------------------------------------------------------------------------
Range "vreixo" : 0x00030000 to 0x0003ffff
General:
0x00031001 (SORRY,HIGH) = Cannot read file (ignored)
0x00031002 (FATAL,HIGH) = Cannot read file (operation canceled)
0x00031003 (FATAL,HIGH) = File doesnt exist
0x00031004 (FATAL,HIGH) = Read access denied
Image reading:
0x00031000 (FATAL,HIGH) = Unsupported ISO-9660 image
0x00031001 (HINT,MEDIUM) = Unsupported Vol Desc that will be ignored
0x00031002 (FATAL,HIGH) = Damaged ISO-9660 image
0x00031003 (SORRY,HIGH) = Cannot read previous image file
Rock-Ridge:
0x00030101 (HINT,MEDIUM) = Unsupported SUSP entry that will be ignored
0x00030102 (SORRY,HIGH) = Wrong/damaged SUSP entry
0x00030103 (WARNING,MEDIUM)= Multiple SUSP ER entries where found
0x00030111 (SORRY,HIGH) = Unsupported RR feature
0x00030112 (SORRY,HIGH) = Error in a Rock Ridge entry
El-Torito:
0x00030201 (HINT,MEDIUM) = Unsupported Boot Vol Desc that will be ignored
0x00030202 (SORRY,HIGH) = Wrong El-Torito catalog
0x00030203 (HINT,MEDIUM) = Unsupported El-Torito feature
0x00030204 (SORRY,HIGH) = Invalid file to be an El-Torito image
0x00030205 (WARNING,MEDIUM)= Cannot properly patch isolinux image
0x00030206 (WARNING,MEDIUM)= Copying El-Torito from a previous image without
enought info about it
Joliet:
0x00030301 (NOTE,MEDIUM) = Unsupported file type for Joliet tree
------------------------------------------------------------------------------
#endif /* LIDBAX_MSGS_________________ */
#ifdef LIBISO_MSGS_H_INTERNAL
/* Internal Functions */
/** Lock before doing side effect operations on m */
static int libiso_msgs_lock(struct libiso_msgs *m, int flag);
/** Unlock after effect operations on m are done */
static int libiso_msgs_unlock(struct libiso_msgs *m, int flag);
/** Create new empty message item.
@param link Previous item in queue
@param flag Bitfield for control purposes (unused yet, submit 0)
@return >0 success, <=0 failure
*/
static int libiso_msgs_item_new(struct libiso_msgs_item **item,
struct libiso_msgs_item *link, int flag);
/** Destroy a message item obtained by libiso_msgs_obtain(). The submitted
pointer gets set to NULL.
@param flag Bitfield for control purposes (unused yet, submit 0)
@return 1 for success, 0 for pointer to NULL
*/
static int libiso_msgs_item_destroy(struct libiso_msgs_item **item, int flag);
#endif /* LIBISO_MSGS_H_INTERNAL */
#endif /* ! LIBISO_MSGS_H_INCLUDED */

1052
libisofs/libisofs.h Executable file → Normal file

File diff suppressed because it is too large Load Diff

160
libisofs/messages.c Normal file
View File

@ -0,0 +1,160 @@
#include <stdlib.h>
#include <string.h>
#include "libisofs.h"
#include "messages.h"
struct libiso_msgs *libiso_messenger = NULL;
int iso_init()
{
if (libiso_messenger == NULL) {
if (libiso_msgs_new(&libiso_messenger, 0) <= 0)
return 0;
}
libiso_msgs_set_severities(libiso_messenger, LIBISO_MSGS_SEV_NEVER,
LIBISO_MSGS_SEV_FATAL, "libisofs: ", 0);
return 1;
}
void iso_finish()
{
libiso_msgs_destroy(&libiso_messenger,0);
}
void iso_msg_debug(char *msg_text)
{
libiso_msgs_submit(libiso_messenger, -1, 0x00000002,
LIBISO_MSGS_SEV_DEBUG, LIBISO_MSGS_PRIO_ZERO,
msg_text, 0, 0);
}
void iso_msg_note(int error_code, char *msg_text)
{
libiso_msgs_submit(libiso_messenger, -1, error_code,
LIBISO_MSGS_SEV_NOTE, LIBISO_MSGS_PRIO_MEDIUM,
msg_text, 0, 0);
}
void iso_msg_hint(int error_code, char *msg_text)
{
libiso_msgs_submit(libiso_messenger, -1, error_code,
LIBISO_MSGS_SEV_HINT, LIBISO_MSGS_PRIO_MEDIUM,
msg_text, 0, 0);
}
void iso_msg_warn(int error_code, char *msg_text)
{
libiso_msgs_submit(libiso_messenger, -1, error_code,
LIBISO_MSGS_SEV_WARNING, LIBISO_MSGS_PRIO_MEDIUM,
msg_text, 0, 0);
}
void iso_msg_sorry(int error_code, char *msg_text)
{
libiso_msgs_submit(libiso_messenger, -1, error_code,
LIBISO_MSGS_SEV_SORRY, LIBISO_MSGS_PRIO_HIGH,
msg_text, 0, 0);
}
void iso_msg_fatal(int error_code, char *msg_text)
{
libiso_msgs_submit(libiso_messenger, -1, error_code,
LIBISO_MSGS_SEV_FATAL, LIBISO_MSGS_PRIO_HIGH,
msg_text, 0, 0);
}
/**
* Control queueing and stderr printing of messages from libisofs.
* Severity may be one of "NEVER", "FATAL", "SORRY", "WARNING", "HINT",
* "NOTE", "UPDATE", "DEBUG", "ALL".
*
* @param queue_severity Gives the minimum limit for messages to be queued.
* Default: "NEVER". If you queue messages then you
* must consume them by iso_msgs_obtain().
* @param print_severity Does the same for messages to be printed directly
* to stderr.
* @param print_id A text prefix to be printed before the message.
* @return >0 for success, <=0 for error
*/
int iso_msgs_set_severities(char *queue_severity,
char *print_severity, char *print_id)
{
int ret, queue_sevno, print_sevno;
ret = libiso_msgs__text_to_sev(queue_severity, &queue_sevno, 0);
if (ret <= 0)
return 0;
ret = libiso_msgs__text_to_sev(print_severity, &print_sevno, 0);
if (ret <= 0)
return 0;
ret = libiso_msgs_set_severities(libiso_messenger, queue_sevno,
print_sevno, print_id, 0);
if (ret <= 0)
return 0;
return 1;
}
#define ISO_MSGS_MESSAGE_LEN 4096
/**
* Obtain the oldest pending libisofs message from the queue which has at
* least the given minimum_severity. This message and any older message of
* lower severity will get discarded from the queue and is then lost forever.
*
* Severity may be one of "NEVER", "FATAL", "SORRY", "WARNING", "HINT",
* "NOTE", "UPDATE", "DEBUG", "ALL". To call with minimum_severity "NEVER"
* will discard the whole queue.
*
* @param error_code Will become a unique error code as listed in messages.h
* @param msg_text Must provide at least ISO_MSGS_MESSAGE_LEN bytes.
* @param os_errno Will become the eventual errno related to the message
* @param severity Will become the severity related to the message and
* should provide at least 80 bytes.
* @return 1 if a matching item was found, 0 if not, <0 for severe errors
*/
int iso_msgs_obtain(char *minimum_severity,
int *error_code, char msg_text[], int *os_errno,
char severity[])
{
int ret, minimum_sevno, sevno, priority;
char *textpt, *sev_name;
struct libiso_msgs_item *item = NULL;
if (libiso_messenger == NULL)
return 0;
ret = libiso_msgs__text_to_sev(minimum_severity, &minimum_sevno, 0);
if (ret <= 0)
return 0;
ret = libiso_msgs_obtain(libiso_messenger, &item, minimum_sevno,
LIBISO_MSGS_PRIO_ZERO, 0);
if (ret <= 0)
goto ex;
ret = libiso_msgs_item_get_msg(item, error_code, &textpt, os_errno, 0);
if (ret <= 0)
goto ex;
strncpy(msg_text, textpt, ISO_MSGS_MESSAGE_LEN-1);
if(strlen(textpt) >= ISO_MSGS_MESSAGE_LEN)
msg_text[ISO_MSGS_MESSAGE_LEN-1] = 0;
severity[0]= 0;
ret = libiso_msgs_item_get_rank(item, &sevno, &priority, 0);
if(ret <= 0)
goto ex;
ret = libiso_msgs__sev_to_text(sevno, &sev_name, 0);
if(ret <= 0)
goto ex;
strcpy(severity,sev_name);
ret = 1;
ex:
libiso_msgs_destroy_item(libiso_messenger, &item, 0);
return ret;
}
void *iso_get_messenger(void)
{
return libiso_messenger;
}

67
libisofs/messages.h Normal file
View File

@ -0,0 +1,67 @@
/*
* Message handling for libisofs
*/
#ifndef MESSAGES_H_
#define MESSAGES_H_
#include "libiso_msgs.h"
/** Can't read file (ignored) */
#define LIBISO_CANT_READ_FILE 0x00031001
/** Can't read file (operation canceled) */
#define LIBISO_FILE_READ_ERROR 0x00031002
/** File doesn't exist */
#define LIBISO_FILE_DOESNT_EXIST 0x00031003
/** Read access denied */
#define LIBISO_FILE_NO_READ_ACCESS 0x00031004
/** Unsupported image feature */
#define LIBISO_IMG_UNSUPPORTED 0x00031000
/** Unsupported Vol Desc that will be ignored */
#define LIBISO_UNSUPPORTED_VD 0x00031001
/** damaged image */
#define LIBISO_WRONG_IMG 0x00031002
/** Can't read previous image file */
#define LIBISO_CANT_READ_IMG 0x00031003
/* Unsupported SUSP entry */
#define LIBISO_SUSP_UNHANLED 0x00030101
/* Wrong SUSP entry, that cause RR to be ignored */
#define LIBISO_SUSP_WRONG 0x00030102
/* Unsupported multiple SUSP ER entries where found */
#define LIBISO_SUSP_MULTIPLE_ER 0x00030103
/** Unsupported RR feature. */
#define LIBISO_RR_UNSUPPORTED 0x00030111
/** Error in a Rock Ridge entry. */
#define LIBISO_RR_ERROR 0x00030112
/** Unsupported boot vol desc. */
#define LIBISO_BOOT_VD_UNHANLED 0x00030201
/** Wrong or damaged el-torito catalog */
#define LIBISO_EL_TORITO_WRONG 0x00030202
/** Unsupproted el-torito feature */
#define LIBISO_EL_TORITO_UNHANLED 0x00030203
/** Trying to add an invalid file as a El-Torito image */
#define LIBISO_EL_TORITO_WRONG_IMG 0x00030204
/** Can't properly patch isolinux image */
#define LIBISO_ISOLINUX_CANT_PATCH 0x00030205
/** Copying El-Torito from a previous image without enought info about it */
#define LIBISO_EL_TORITO_BLIND_COPY 0x00030206
/** Unsupported file type for Joliet tree */
#define LIBISO_JOLIET_WRONG_FILE_TYPE 0x00030301
void iso_msg_debug(char *msg_text);
void iso_msg_note(int error_code, char *msg_text);
void iso_msg_hint(int error_code, char *msg_text);
void iso_msg_warn(int error_code, char *msg_text);
void iso_msg_sorry(int error_code, char *msg_text);
void iso_msg_fatal(int error_code, char *msg_text);
#endif /*MESSAGES_H_*/

95
libisofs/rockridge.c Executable file → Normal file
View File

@ -14,6 +14,30 @@
#include <unistd.h>
#include <sys/stat.h>
/** See IEEE P1281 Draft Version 1.12/5.5 */
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->info.dir.self_susp, ER);
}
/* 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)
@ -24,11 +48,11 @@ uint8_t *rrip_make_PX(struct ecma119_write_target *t,
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);
iso_bb(&PX[4], node->attrib.st_mode, 4);
iso_bb(&PX[12], node->attrib.st_nlink, 4);
iso_bb(&PX[20], node->attrib.st_uid, 4);
iso_bb(&PX[28], node->attrib.st_gid, 4);
iso_bb(&PX[36], node->attrib.st_ino, 4);
return PX;
}
@ -37,8 +61,8 @@ 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));
susp_append(t, &node->info.dir.self_susp, rrip_make_PX(t, node));
susp_append(t, &node->info.dir.parent_susp, rrip_make_PX(t, node));
}
}
@ -50,8 +74,8 @@ void rrip_add_PN(struct ecma119_write_target *t, struct ecma119_tree_node *node)
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);
iso_bb(&PN[4], node->attrib.st_dev >> 32, 4);
iso_bb(&PN[12], node->attrib.st_dev & 0xffffffff, 4);
susp_append(t, &node->susp, PN);
}
@ -102,7 +126,7 @@ static void rrip_SL_add_component(char *prev, char *cur, int *n_comp,
void rrip_add_SL(struct ecma119_write_target *t, struct ecma119_tree_node *node)
{
int ret, pathsize = 0;
int path_size;
char *path = NULL, *cur, *prev;
int i, j;
@ -113,19 +137,9 @@ void rrip_add_SL(struct ecma119_write_target *t, struct ecma119_tree_node *node)
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';
path = node->info.dest;
path_size = strlen(path);
prev = path;
for (cur = strchr(path, '/'); cur && *cur; cur = strchr(cur, '/')) {
rrip_SL_add_component(prev, cur, &n_comp, &comp);
@ -134,8 +148,8 @@ void rrip_add_SL(struct ecma119_write_target *t, struct ecma119_tree_node *node)
}
/* 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);
if (prev == path || prev != &path[path_size - 1]) {
rrip_SL_add_component(prev, &path[path_size], &n_comp, &comp);
}
for (i = 0; i < n_comp; i++) {
@ -172,7 +186,6 @@ void rrip_add_SL(struct ecma119_write_target *t, struct ecma119_tree_node *node)
}
susp_append(t, &node->susp, SL);
free(path);
/* free the components */
for (i = 0; i < n_comp; i++) {
free(comp[i]);
@ -200,7 +213,7 @@ static void rrip_add_NM_single(struct ecma119_write_target *t,
void
rrip_add_NM(struct ecma119_write_target *t, struct ecma119_tree_node *node)
{
char *name = iso_p_fileid(node->iso_self->name);
char *name = node->full_name;
int len = name ? strlen(name) : 0;
char *pos = name;
@ -208,8 +221,8 @@ rrip_add_NM(struct ecma119_write_target *t, struct ecma119_tree_node *node)
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);
rrip_add_NM_single(t, &node->info.dir.self_susp, pos, 0, 1 << 1);
rrip_add_NM_single(t, &node->info.dir.parent_susp, pos, 0, 1 << 2);
}
while (len > 250) {
@ -240,7 +253,7 @@ rrip_add_PL(struct ecma119_write_target *t, struct ecma119_tree_node *node)
PL[1] = 'L';
PL[2] = 12;
PL[3] = 1;
susp_append(t, &node->dir.parent_susp, PL);
susp_append(t, &node->info.dir.parent_susp, PL);
}
void
@ -264,10 +277,10 @@ rrip_add_TF(struct ecma119_write_target *t, struct ecma119_tree_node *node)
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);
TF[4] = (1 << 1) | (1 << 2) | (1 << 3);
iso_datetime_7(&TF[5], node->attrib.st_mtime);
iso_datetime_7(&TF[12], node->attrib.st_atime);
iso_datetime_7(&TF[19], node->attrib.st_ctime);
susp_append(t, &node->susp, TF);
}
@ -278,21 +291,21 @@ rrip_finalize(struct ecma119_write_target *t, struct ecma119_tree_node *dir)
assert(dir->type == ECMA119_DIR);
if (dir->parent != dir->dir.real_parent) {
uint8_t *PL = susp_find(&dir->dir.parent_susp, "PL");
if (dir->parent != dir->info.dir.real_parent) {
uint8_t *PL = susp_find(&dir->info.dir.parent_susp, "PL");
assert(PL);
iso_bb(&PL[4], dir->dir.real_parent->block, 4);
iso_bb(&PL[4], dir->info.dir.real_parent->info.dir.block, 4);
}
for (i = 0; i < dir->dir.nchildren; i++) {
struct ecma119_tree_node *ch = dir->dir.children[i];
for (i = 0; i < dir->info.dir.nchildren; i++) {
struct ecma119_tree_node *ch = dir->info.dir.children[i];
if (ch->type == ECMA119_FILE && ch->file.real_me) {
if (ch->type == ECMA119_PLACEHOLDER) {
uint8_t *CL = susp_find(&ch->susp, "CL");
assert(CL);
iso_bb(&CL[4], ch->file.real_me->block, 4);
iso_bb(&CL[4], ch->info.real_me->info.dir.block, 4);
} else if (ch->type == ECMA119_DIR) {
rrip_finalize(t, ch);
}

96
libisofs/rockridge.h Executable file → Normal file
View File

@ -1,6 +1,11 @@
/* vim: set noet ts=8 sts=8 sw=8 : */
/** Functions and structures used for Rock Ridge support. */
/**
* Functions and structures used for Rock Ridge support.
*
* See IEEE P1282, Rock Ridge Interchange Protocol, Draft Standard version
* 1.12 for further details.
*/
#ifndef ISO_ROCKRIDGE_H
#define ISO_ROCKRIDGE_H
@ -8,18 +13,99 @@
struct ecma119_write_target;
struct ecma119_tree_node;
/**
* Add a SUSP "ER" System Use Entry to identify the Rock Ridge specification.
*
* The "ER" System Use Entry is used to uniquely identify a specification
* compliant with SUSP. This method adds to the given tree node "." entry
* the "ER" corresponding to the RR protocol.
*
* See IEEE P1281, section 5.5 and IEEE P1282, section 4.3 for more details.
*/
void rrip_add_ER(struct ecma119_write_target *, struct ecma119_tree_node *);
/**
* Add a PX System Use Entry to the given tree node and, if that node is
* a directory, to its "." and ".." entries. The PX System Use Entry is
* used to add POSIX file attributes, such as access permissions or user and
* group id, to a ECMA 119 directory record.
*
* See IEEE P1282, section 4.1.1 for more details.
*/
void rrip_add_PX(struct ecma119_write_target *, struct ecma119_tree_node *);
/**
* Add a PN System Use Entry to the given tree node.
* The PN System Use Entry is used to store the device number, and it's
* mandatory if the tree node corresponds to a character or block device.
*
* See IEEE P1282, section 4.1.2 for more details.
*/
void rrip_add_PN(struct ecma119_write_target *, struct ecma119_tree_node *);
/**
* Add a SL System Use Entry to the given tree node. This is used to store
* the content of a symbolic link, and is mandatory if the tree node
* indicates a symbolic link.
*
* See IEEE P1282, section 4.1.3 for more details.
*/
void rrip_add_SL(struct ecma119_write_target *, struct ecma119_tree_node *);
/**
* Add a NM System Use Entry to the given tree node. The purpose of this
* System Use Entry is to store the content of an Alternate Name to support
* POSIX-style or other names.
*
* See IEEE P1282, section 4.1.4 for more details.
*/
void rrip_add_NM(struct ecma119_write_target *, struct ecma119_tree_node *);
/*
* The next 3 System Use Entries are used to handle Deep Directory
* Hierarchies, i.e., hierarchies where the number of directory levels
* exceed the eight limit of ECMA-119.
*/
/**
* Add to the given tree node a CL System Use Entry, that is used to record
* the new location of a directory which has been relocated.
*
* See IEEE P1282, section 4.1.5.1 for more details.
*/
void rrip_add_CL(struct ecma119_write_target *, struct ecma119_tree_node *);
/**
* Add a PL System Use Entry, used to record the location of the original
* parent directory of a directory which has been relocated.
*
* 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.
*
* See IEEE P1282, section 4.1.5.2 for more details.
*/
void rrip_add_PL(struct ecma119_write_target *, struct ecma119_tree_node *);
/**
* Add a RE System Use Entry to the given tree node. The purpose of the
* this System Use Entry is to indicate to an RRIP-compliant receiving
* system that the Directory Record in which an "RE" System Use Entry is
* recorded has been relocated from another position in the original
* Directory Hierarchy.
*
* See IEEE P1282, section 4.1.5.3 for more details.
*/
void rrip_add_RE(struct ecma119_write_target *, struct ecma119_tree_node *);
/**
* Add to the given tree node a TF System Use Entry, used to record some
* time stamps related to the file.
*
* See IEEE P1282, section 4.1.6 for more details.
*/
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 *);

62
libisofs/susp.c Executable file → Normal file
View File

@ -117,8 +117,8 @@ 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);
try_add_CE(t, &node->info.dir.self_susp, 34);
try_add_CE(t, &node->info.dir.parent_susp, 34);
}
}
@ -137,7 +137,7 @@ susp_add_SP(struct ecma119_write_target *t, struct ecma119_tree_node *dir)
SP[4] = 0xbe;
SP[5] = 0xef;
SP[6] = 0;
susp_append(t, &dir->dir.self_susp, SP);
susp_append(t, &dir->info.dir.self_susp, SP);
}
#if 0
@ -155,38 +155,14 @@ static void susp_add_ST(struct ecma119_write_target *t,
}
#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)
* - position of the current directory (dir->info.dir.block)
* - length of the current directory (dir->info.dir.len)
* - sum of the children's CE lengths (dir->info.dir.CE_len)
*/
static void
susp_fin_1_CE(struct ecma119_write_target *t,
@ -208,18 +184,18 @@ static void susp_fin_CE(struct ecma119_write_target *t,
struct ecma119_tree_node *dir)
{
int i;
size_t CE_offset = dir->dir.len;
size_t CE_offset = dir->info.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);
susp_fin_1_CE(t, &dir->info.dir.self_susp, dir->info.dir.block, &CE_offset);
susp_fin_1_CE(t, &dir->info.dir.parent_susp, dir->info.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);
for (i = 0; i < dir->info.dir.nchildren; i++) {
struct ecma119_tree_node *ch = dir->info.dir.children[i];
susp_fin_1_CE(t, &ch->susp, dir->info.dir.block, &CE_offset);
}
assert(CE_offset == dir->dir.len + dir->dir.CE_len);
assert(CE_offset == dir->info.dir.len + dir->info.dir.CE_len);
}
void
@ -229,13 +205,11 @@ susp_finalize(struct ecma119_write_target *t, struct ecma119_tree_node *dir)
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]);
susp_fin_CE(t, dir);
for (i = 0; i < dir->info.dir.nchildren; i++) {
if (dir->info.dir.children[i]->type == ECMA119_DIR)
susp_finalize(t, dir->info.dir.children[i]);
}
}

24
libisofs/susp.h Executable file → Normal file
View File

@ -1,6 +1,10 @@
/* vim: set noet ts=8 sts=8 sw=8 : */
/** Functions and structures used for SUSP (IEEE 1281).
/**
* Functions and structures used for SUSP (IEEE 1281).
*
* Please refer to IEEE P1281 System Use Sharing Protocol, draft standard
* version 1.12 for more details.
*/
#ifndef __ISO_SUSP
@ -28,13 +32,23 @@ struct susp_info
* will go in a CE area. */
};
/**
* Add a CE System Use Entry to the given tree node. A "CE" is used to add
* a continuation area, where additional System Use Entry can be written.
* See IEEE P1281, section 5.1.
*/
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. */
/**
* Add a SP System Use Entry to the "." entry of the directory. The SP provide
* an identifier that the SUSP is used within the volume. The SP shall be
* recorded in the "." entry of the root directory.
* See IEEE P1281, section 5.3 for more details.
*
* this is special because it doesn't modify the susp fields of the
* directory; it modifies 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. */

679
libisofs/tree.c Executable file → Normal file
View File

@ -20,8 +20,6 @@
#include <stdio.h>
#include "tree.h"
#include "util.h"
#include "volume.h"
#include "exclude.h"
static void
@ -30,171 +28,613 @@ 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_mode = 0555;
s->st_atime = s->st_mtime = s->st_ctime = now;
}
static struct stat
get_attrib(const struct iso_tree_node *node)
void
iso_tree_add_child(struct iso_tree_node_dir *parent,
struct iso_tree_node *child)
{
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;
assert(parent && child);
assert(!child->parent);
parent->nchildren++;
parent->children =
realloc(parent->children, parent->nchildren * sizeof(void*));
parent->children[parent->nchildren-1] = child;
child->parent = parent;
}
struct iso_tree_node_dir*
iso_tree_new_root()
{
struct iso_tree_node_dir *root;
root = calloc(1, sizeof(struct iso_tree_node_dir));
set_default_stat(&root->node.attrib);
root->node.refcount = 1;
root->node.attrib.st_mode = S_IFDIR | 0777;
root->node.type = LIBISO_NODE_DIR;
return root;
}
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)
iso_tree_add_file(struct iso_tree_node_dir *parent, const char *path)
{
struct iso_tree_node_file *f;
char *p;
struct stat st;
struct iso_tree_node *ret;
assert((!parent || S_ISDIR(parent->attrib.st_mode)) && path);
if (lstat(path, &st) == -1)
assert( parent && path);
if (lstat(path, &st) == -1) {
libisofs_errno = NO_FILE;
return NULL;
}
if ( !S_ISREG(st.st_mode) ) {
libisofs_errno = UNEXPECTED_FILE_TYPE;
return NULL;
}
if ( access(path, R_OK) ) {
libisofs_errno = NO_READ_ACCESS;
return NULL;
}
f = calloc(1, sizeof(struct iso_tree_node_file));
/* fill fields */
f->node.refcount = 1;
f->node.attrib = st;
f->loc.path = strdup(path);
f->node.type = LIBISO_NODE_FILE;
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);
f->node.name = strdup( basename(p) );
free(p);
return ret;
/* add to parent (this also sets f->node->parent) */
iso_tree_add_child(parent, (struct iso_tree_node*) f);
return (struct iso_tree_node*) f;
}
struct iso_tree_node*
iso_tree_radd_dir (struct iso_tree_node *parent, const char *path)
iso_tree_add_symlink(struct iso_tree_node_dir *parent,
const char *name, const char *dest)
{
struct iso_tree_node_symlink *link;
assert( parent && name && dest);
link = calloc(1, sizeof(struct iso_tree_node_symlink));
/* fill fields */
set_default_stat(&link->node.attrib);
link->node.refcount = 1;
link->node.attrib.st_mode |= S_IFLNK;
link->node.name = strdup(name);
link->node.type = LIBISO_NODE_SYMLINK;
link->dest = strdup(dest);
/* add to parent (this also sets link->node->parent) */
iso_tree_add_child(parent, (struct iso_tree_node*) link);
return (struct iso_tree_node*) link;
}
struct iso_tree_node_dir*
iso_tree_add_dir(struct iso_tree_node_dir *parent,
const char *name)
{
struct iso_tree_node_dir *dir;
assert( parent && name );
dir = calloc(1, sizeof(struct iso_tree_node_dir));
dir->node.refcount = 1;
dir->node.attrib = parent->node.attrib;
dir->node.type = LIBISO_NODE_DIR;
dir->node.name = strdup(name);
iso_tree_add_child(parent, (struct iso_tree_node*) dir);
return dir;
}
enum iso_tree_node_type
iso_tree_node_get_type(struct iso_tree_node *node)
{
assert(node);
return node->type;
}
void
iso_tree_node_set_name(struct iso_tree_node *node, const char *name)
{
free(node->name);
node->name = strdup(name);
}
const char *
iso_tree_node_get_name(struct iso_tree_node *node)
{
assert(node);
return node->name;
}
void
iso_tree_node_set_hidden(struct iso_tree_node *node, int hide_attrs)
{
assert(node);
node->hide_flags = hide_attrs;
}
int
iso_tree_node_is_hidden(struct iso_tree_node *node)
{
assert(node);
return node->hide_flags;
}
void
iso_tree_node_set_gid(struct iso_tree_node *node, gid_t gid)
{
assert(node);
node->attrib.st_gid = gid;
}
gid_t
iso_tree_node_get_gid(struct iso_tree_node *node)
{
assert(node);
return node->attrib.st_gid;
}
void
iso_tree_node_set_uid(struct iso_tree_node *node, uid_t uid)
{
assert(node);
node->attrib.st_uid = uid;
}
uid_t
iso_tree_node_get_uid(struct iso_tree_node *node)
{
assert(node);
return node->attrib.st_uid;
}
void
iso_tree_node_set_permissions(struct iso_tree_node *node, mode_t mode)
{
assert(node);
node->attrib.st_mode = (node->attrib.st_mode & S_IFMT) |
(mode & ~S_IFMT);
}
mode_t
iso_tree_node_get_permissions(struct iso_tree_node *node)
{
assert(node);
return node->attrib.st_mode & ~S_IFMT;
}
off_t
iso_tree_node_get_size(struct iso_tree_node *node)
{
return node->attrib.st_size;
}
void
iso_tree_node_set_mtime(struct iso_tree_node *node, time_t time)
{
node->attrib.st_mtime = time;
}
time_t
iso_tree_node_get_mtime(struct iso_tree_node *node)
{
return node->attrib.st_mtime;
}
void
iso_tree_node_set_atime(struct iso_tree_node *node, time_t time)
{
node->attrib.st_atime = time;
}
time_t
iso_tree_node_get_atime(struct iso_tree_node *node)
{
return node->attrib.st_atime;
}
void
iso_tree_node_set_ctime(struct iso_tree_node *node, time_t time)
{
node->attrib.st_ctime = time;
}
time_t
iso_tree_node_get_ctime(struct iso_tree_node *node)
{
return node->attrib.st_ctime;
}
void
iso_tree_node_set_sort_weight(struct iso_tree_node *node, int w)
{
assert(node);
if ( ISO_ISDIR(node) ) {
size_t i;
struct iso_tree_node_dir *dir;
dir = (struct iso_tree_node_dir *) node;
for (i=0; i < dir->nchildren; i++) {
iso_tree_node_set_sort_weight(dir->children[i], w);
}
} else if ( ISO_ISREG(node) ) {
struct iso_tree_node_file *file;
file = (struct iso_tree_node_file *) node;
file->sort_weight = w;
}
}
void
iso_tree_node_symlink_set_dest(struct iso_tree_node_symlink *node,
const char *dest)
{
assert(node && dest);
free(node->dest);
node->dest = strdup(dest);
}
const char *
iso_tree_node_symlink_get_dest(struct iso_tree_node_symlink *node)
{
assert(node);
return node->dest;
}
struct iso_tree_node*
iso_tree_add_node(struct iso_tree_node_dir *parent,
const char *path)
{
struct stat st;
struct iso_tree_node *node;
assert( parent && path);
if (lstat(path, &st) == -1) {
libisofs_errno = NO_FILE;
return NULL;
}
if ( access(path, R_OK) ) {
libisofs_errno = NO_READ_ACCESS;
return NULL;
}
switch (st.st_mode & S_IFMT) {
case S_IFREG:
/* regular file */
node = iso_tree_add_file(parent, path);
break;
case S_IFLNK:
/* symlink */
{
char dest[PATH_MAX];
char *p;
int n;
n = readlink(path, dest, PATH_MAX);
if ( n == -1 ) {
libisofs_errno = INTERNAL_ERROR;
return NULL;
}
dest[n] = '\0';
p = strdup(path); /* because basename() might modify its arg */
node = iso_tree_add_symlink(parent, basename(p), dest);
free(p);
node->attrib = st;
}
break;
case S_IFDIR:
/* directory */
{
char *p;
p = strdup(path); /* because basename() might modify its arg */
node = (struct iso_tree_node*) iso_tree_add_dir(parent, basename(p));
free(p);
node->attrib = st;
}
break;
default:
libisofs_errno = UNEXPECTED_FILE_TYPE;
node = NULL;
break;
}
return node;
}
struct iso_tree_iter *
iso_tree_node_children(struct iso_tree_node_dir *dir)
{
struct iso_tree_iter *iter;
assert(dir);
iter = malloc(sizeof(struct iso_tree_iter));
iter->dir = dir;
iter->index = -1;
return iter;
}
struct iso_tree_node *
iso_tree_iter_next(struct iso_tree_iter *iter)
{
assert(iter);
if ( ++iter->index < iter->dir->nchildren )
return iter->dir->children[iter->index];
else
return NULL;
}
int
iso_tree_iter_has_next(struct iso_tree_iter *iter)
{
assert(iter);
return iter->index + 1 < iter->dir->nchildren;
}
void
iso_tree_iter_free(struct iso_tree_iter *iter)
{
free(iter);
}
int
iso_tree_node_take(struct iso_tree_node_dir *dir, struct iso_tree_node *node)
{
int i;
assert(dir && node);
/* search for the node in the dir */
for (i = 0; i < dir->nchildren; ++i) {
if ( dir->children[i] == node )
break;
}
if (i < dir->nchildren) {
int j;
for (j = i+1; j < dir->nchildren; ++j) {
dir->children[j-1] = dir->children[j];
}
--dir->nchildren;
dir->children = realloc(dir->children, dir->nchildren * sizeof(void*));
node->parent = NULL;
return 0;
} else {
/* the node doesn't exist on dir */
return -1;
}
}
int
iso_tree_node_remove(struct iso_tree_node_dir *dir, struct iso_tree_node *node)
{
int res;
assert(dir && node);
res = iso_tree_node_take(dir, node);
if (!res)
iso_tree_free(node);
return res;
}
int
iso_tree_node_take_iter(struct iso_tree_iter *iter)
{
int j;
struct iso_tree_node_dir *dir;
struct iso_tree_node *node;
assert(iter);
dir = iter->dir;
if (iter->index < 0)
return -1; /* index before beginning */
if (iter->index >= dir->nchildren)
return -2; /* index after end */
node = dir->children[iter->index];
node->parent = NULL;
for (j = iter->index+1; j < dir->nchildren; ++j) {
dir->children[j-1] = dir->children[j];
}
--dir->nchildren;
dir->children = realloc(dir->children, dir->nchildren * sizeof(void*));
/* update iter index */
--iter->index;
return 0;
}
int
iso_tree_node_remove_iter(struct iso_tree_iter *iter)
{
int j;
struct iso_tree_node_dir *dir;
struct iso_tree_node *node;
assert(iter);
dir = iter->dir;
if (iter->index < 0)
return -1; /* index before beginning */
if (iter->index >= dir->nchildren)
return -2; /* index after end */
node = dir->children[iter->index];
for (j = iter->index+1; j < dir->nchildren; ++j) {
dir->children[j-1] = dir->children[j];
}
--dir->nchildren;
dir->children = realloc(dir->children, dir->nchildren * sizeof(void*));
/* update iter index */
--iter->index;
/* and free node */
node->parent = NULL;
iso_tree_free(node);
return 0;
}
struct iso_tree_node_dir *
iso_tree_node_get_parent(struct iso_tree_node *node)
{
assert(node);
return node->parent;
}
void
iso_tree_node_ref(struct iso_tree_node *node)
{
++node->refcount;
}
void
iso_tree_free(struct iso_tree_node *root)
{
if (!root)
return;
if (--root->refcount < 1) {
if ( ISO_ISDIR(root) ) {
size_t i;
struct iso_tree_node_dir *dir;
dir = (struct iso_tree_node_dir *) root;
for (i=0; i < dir->nchildren; i++) {
iso_tree_free(dir->children[i]);
}
free(dir->children);
} else if ( ISO_ISLNK(root) ) {
struct iso_tree_node_symlink *link;
link = (struct iso_tree_node_symlink *) root;
free(link->dest);
} else if ( ISO_ISREG(root) ) {
struct iso_tree_node_file *file;
file = (struct iso_tree_node_file *) root;
if (root->procedence == LIBISO_NEW)
free(file->loc.path);
} else if ( ISO_ISBOOT(root) ) {
struct iso_tree_node_boot *boot;
boot = (struct iso_tree_node_boot *) root;
if (root->procedence == LIBISO_NEW && boot->img)
free(boot->loc.path);
}
free(root->name);
free(root);
}
}
static void
iso_tree_radd_dir_aux(struct iso_tree_node_dir *parent, const char *path,
struct iso_tree_radd_dir_behavior *behavior,
struct iso_hash_table *excludes)
{
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;
return;
}
while ((ent = readdir(dir))) {
char child[strlen(ent->d_name) + strlen(path) + 2];
if (behavior->stop_on_error & behavior->error)
break;
if (strcmp(ent->d_name, ".") == 0 ||
strcmp(ent->d_name, "..") == 0)
continue;
//TODO check if path already finished in '/'
sprintf(child, "%s/%s", path, ent->d_name);
/* see if this child is excluded. */
if (iso_exclude_lookup(child))
if (iso_exclude_lookup(excludes, child))
continue;
iso_tree_radd_dir(new, child);
new = iso_tree_add_node(parent, child);
if (!new || !ISO_ISDIR(new)) {
if (!new)
behavior->error = 1;
continue;
}
iso_tree_radd_dir_aux( (struct iso_tree_node_dir *) new, child,
behavior, excludes);
}
closedir(dir);
return new;
return;
}
void
iso_tree_free(struct iso_tree_node *root)
void
iso_tree_radd_dir(struct iso_tree_node_dir *parent, const char *path,
struct iso_tree_radd_dir_behavior *behavior)
{
size_t i;
for (i=0; i < root->nchildren; i++) {
iso_tree_free(root->children[i]);
struct iso_tree_node_dir *dir;
struct iso_hash_table table = { {0,}, 0};
assert ( parent && path );
behavior->error = 0;
/* initialize exclude hash_table */
if ( behavior->excludes ) {
char *exclude;
int i = 0;
while ( (exclude = behavior->excludes[i++]) ) {
iso_exclude_add_path(&table, exclude);
}
}
free(root->name);
free(root->children);
free(root);
/* recurse into dir */
iso_tree_radd_dir_aux(parent, path, behavior, &table);
/* clear hashtable */
iso_exclude_empty(&table);
}
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);
printf("%s%s\n", sp, root->name);
if ( ISO_ISDIR(root) ) {
size_t i;
struct iso_tree_node_dir *dir;
dir = (struct iso_tree_node_dir *) root;
for (i=0; i < dir->nchildren; i++) {
iso_tree_print(dir->children[i], spaces+2);
}
}
}
@ -205,19 +645,18 @@ iso_tree_print_verbose(const struct iso_tree_node *root,
void *callback_data,
int spaces)
{
size_t i;
(S_ISDIR(root->attrib.st_mode) ? dir : file)
(ISO_ISDIR(root) ? 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);
if ( ISO_ISDIR(root) ) {
size_t i;
struct iso_tree_node_dir *dir_node;
dir_node = (struct iso_tree_node_dir *) root;
for (i=0; i < dir_node->nchildren; i++) {
iso_tree_print_verbose(dir_node->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);
}

149
libisofs/tree.h Executable file → Normal file
View File

@ -8,7 +8,7 @@
* (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
* are stored in UTF-8 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.
@ -20,95 +20,103 @@
#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;
/*
* Reference counter:
* - When created, refcount is set to 1
* - One node is automatically free when free the tree (i.e., dir) it
* belongs, and the tree is automatically freed when the volume it
* belongs is also freed.
* - If the user deon't add the tree to a volume, (s)he has to free the
* tree.
* - If the user doesn't add a node to a tree (dir), for example after
* taken it with iso_tree_node_take(), it should free the node when
* no more needed.
* - If the user wants an own ref, it should call iso_tree_node_ref()
* function to get that ref, and free the node when no more needed.
*/
int refcount;
struct iso_tree_node_dir *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). */
int hide_flags; /**< If the node is to be hidden in RR/ISO or
* Joilet tree */
enum tree_node_from procedence; /**< Procedence of the node. */
enum iso_tree_node_type type; /**< Type of the node. */
};
/**
* A node in the filesystem tree that represents a regular file
*/
struct iso_tree_node_file
{
struct iso_tree_node node;
int sort_weight; /**< It sorts the order in which the file data is
* written to the CD image. Higher weighting files
* are written at the beginning of image */
union {
char *path; /**< the path of the file on local filesystem */
uint32_t block; /**< If the file is from a previous session.
* Maybe we can put this in iso_tree_node later.
*/
} loc;
};
/**
* A node in the filesystem tree that represents a symbolic link
*/
struct iso_tree_node_symlink
{
struct iso_tree_node node;
char *dest; /**< Destination of the link */
};
/**
* A directory on the filesystem tree
*/
struct iso_tree_node_dir
{
struct iso_tree_node node;
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.
* Tree node that corresponds to some El-Torito artifact.
* This can be either the boot catalog or a bootable image.
*/
struct iso_tree_node *iso_tree_new_root(struct iso_volume *vol);
struct iso_tree_node_boot
{
struct iso_tree_node node;
unsigned int img:1; /*< 1 if img, 0 if catalog */
union {
char *path; /**< the path of the file on local filesystem */
uint32_t block; /**< If the file is from a previous session.*/
} loc;
};
/**
* 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.
* An iterator for directory children.
*/
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);
struct iso_tree_iter
{
struct iso_tree_node_dir *dir;
int index;
};
/**
* A function that prints verbose information about a directory.
@ -154,6 +162,9 @@ void iso_tree_print_verbose(const struct iso_tree_node *root,
void *callback_data,
int spaces);
#define ISO_ISDIR(n) S_ISDIR(n->attrib.st_mode)
#define ISO_ISDIR(n) (n->type == LIBISO_NODE_DIR)
#define ISO_ISREG(n) (n->type == LIBISO_NODE_FILE)
#define ISO_ISLNK(n) (n->type == LIBISO_NODE_SYMLINK)
#define ISO_ISBOOT(n) (n->type == LIBISO_NODE_BOOT)
#endif /* LIBISO_TREE_H */

424
libisofs/util.c Executable file → Normal file
View File

@ -16,9 +16,12 @@
#include <ctype.h>
#include <assert.h>
#include <errno.h>
#include <locale.h>
#include <langinfo.h>
#include <locale.h>
#include <limits.h>
#include "util.h"
#include "libisofs.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,
@ -35,32 +38,141 @@ int round_up(int n, int mul)
return div_up(n, mul) * mul;
}
/**
* Convert a string between charsets.
* This assumes '\0' means end-of-string, what is not necessarily true,
* but given there are lots of strdup around there, it will fail in other
* places anyway...
*/
char *
convert_str(const char *str, const char *icharset, const char *ocharset)
{
char *ret;
size_t inbytes;
size_t outbytes;
inbytes = strlen(str);
outbytes = (inbytes+1) * MB_LEN_MAX;
{
/* ensure enought space */
char out[outbytes];
char *src;
size_t n;
iconv_t conv = iconv_open(ocharset, icharset);
if (conv == (iconv_t)(-1)) {
printf("Can't convert from %s to %s\n", icharset, ocharset);
return NULL;
}
src = (char *)str;
ret = (char *)out;
n = iconv(conv, &src, &inbytes, &ret, &outbytes);
if (n == -1) {
/* error just return input stream */
perror("Convert error.");
printf("Maybe string %s is not encoded in %s\n", str, icharset);
iconv_close(conv);
return strdup(str);
}
iconv_close(conv);
*ret = '\0';
ret = strdup(out);
}
return ret;
}
/**
* Convert a str in a specified codeset to WCHAR_T.
* The result must be free() when no more needed
*/
static wchar_t *str2wchar(const char *str, const char *codeset)
{
iconv_t conv;
size_t inbytes;
size_t outbytes;
char *ret;
char *src;
wchar_t *wstr;
size_t n;
conv = iconv_open("WCHAR_T", codeset);
if (conv == (iconv_t)-1) {
perror("Invalid encodings\n");
return NULL;
}
inbytes = strlen(str);
outbytes = (inbytes + 1) * sizeof(wchar_t);
/* we are sure that numchars <= inbytes */
wstr = malloc(outbytes);
ret = (char *)wstr;
src = (char *)str;
n = iconv(conv, &src, &inbytes, &ret, &outbytes);
while (n == -1) {
if( errno != EINVAL ) {
/* error, should never occur */
iconv_close(conv);
perror("Convert error\n");
return NULL;
}
/* invalid input string charset, just log and ignore */
printf("String %s is not encoded in %s\n", str, codeset);
inbytes--;
if(!inbytes)
break;
n = iconv(conv, &src, &inbytes, &ret, &outbytes);
}
iconv_close(conv);
*( (wchar_t *)ret )='\0';
return wstr;
}
/* 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)
char *str2ascii(const char *src_arg, const char *icharset)
{
wchar_t wsrc_[NAME_BUFFER_SIZE];
char *src = (char*)wsrc_;
char *ret_;
wchar_t *wsrc_;
char *ret;
mbstate_t state;
char *ret_;
char *src;
iconv_t conv;
size_t numchars;
size_t outbytes;
size_t inbytes;
size_t n;
assert(icharset);
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)
* include the last NULL character.
*
* For now, just assume input to be in UTF-8, we can change
* this later.
*/
wsrc_ = str2wchar(src_arg, icharset);
if (!wsrc_)
return NULL;
src = (char *)wsrc_;
numchars = wcslen(wsrc_);
inbytes = numchars * sizeof(wchar_t);
@ -70,8 +182,10 @@ char *str2ascii(const char *src_arg)
/* initialize iconv */
conv = iconv_open("ASCII", "WCHAR_T");
if (conv == (iconv_t)-1)
if (conv == (iconv_t)-1) {
free(wsrc_);
return NULL;
}
n = iconv(conv, &src, &inbytes, &ret, &outbytes);
while(n == -1) {
@ -109,17 +223,18 @@ char *str2ascii(const char *src_arg)
iconv_close(conv);
*ret='\0';
free(wsrc_);
return ret_;
}
/* FIXME: C&P */
uint16_t *str2ucs(const char *src_arg)
uint16_t *str2ucs(const char *src_arg, const char *icharset)
{
wchar_t wsrc_[NAME_BUFFER_SIZE];
char *src = (char*)wsrc_;
wchar_t *wsrc_;
char *src;
char *ret_;
char *ret;
mbstate_t state;
iconv_t conv;
size_t outbytes;
size_t numchars;
@ -129,13 +244,17 @@ uint16_t *str2ucs(const char *src_arg)
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)
* include the last NULL character.
*/
wsrc_ = str2wchar(src_arg, icharset);
if (!wsrc_)
return calloc(2, 1); /* empty UCS string */
src = (char*)wsrc_;
numchars = wcslen(wsrc_);
inbytes = numchars * sizeof(wchar_t);
@ -190,7 +309,6 @@ uint16_t *str2ucs(const char *src_arg)
return (uint16_t*)ret_;
}
static int valid_d_char(char c)
{
return (c >= '0' && c <= '9') || (c >= 'A' && c <= 'Z') || (c == '_');
@ -218,9 +336,45 @@ static int valid_p_char(char c)
|| (c == '.') || (c == '_') || (c == '-');
}
static char *iso_dirid(const char *src, int size)
char *str2d_char(const char *str, const char *icharset) {
char *ret;
size_t len, i;
if (!str)
return NULL;
ret = str2ascii(str, icharset);
len = strlen(ret);
for (i = 0; i < len; ++i) {
char c = toupper(ret[i]);
ret[i] = valid_d_char(c) ? c : '_';
}
return ret;
}
char *str2a_char(const char *str, const char *icharset) {
char *ret;
size_t len, i;
if (!str)
return NULL;
ret = str2ascii(str, icharset);
len = strlen(ret);
for (i = 0; i < len; ++i) {
char c = toupper(ret[i]);
ret[i] = valid_a_char(c) ? c : '_';
}
return ret;
}
static char *iso_dirid(const char *src, int size, const char *icharset)
{
char *ret = str2ascii(src);
char *ret = str2ascii(src, icharset);
size_t len, i;
if (!ret)
@ -239,19 +393,46 @@ static char *iso_dirid(const char *src, int size)
return ret;
}
char *iso_1_dirid(const char *src)
char *iso_1_dirid(const char *src, const char *icharset)
{
return iso_dirid(src, 8);
return iso_dirid(src, 8, icharset);
}
char *iso_2_dirid(const char *src)
char *iso_2_dirid(const char *src, const char *icharset)
{
return iso_dirid(src, 31);
return iso_dirid(src, 31, icharset);
}
char *iso_1_fileid(const char *src_arg)
char *iso_r_dirid(const char *src, const char *icharset, int flags)
{
char *src = str2ascii(src_arg);
char *ret = str2ascii(src, icharset);
size_t size, len, i;
if (!ret)
return NULL;
size = flags & ECMA119_37_CHAR_FILENAMES ? 37 : 31;
len = strlen(ret);
if (len > size) {
ret[size] = '\0';
len = size;
}
if (flags & ECMA119_RELAXED_FILENAMES)
return ret;
for (i = 0; i < len; i++) {
char c = toupper(ret[i]);
ret[i] = valid_d_char(c) ? c : '_';
}
return ret;
}
char *iso_1_fileid(const char *src_arg, const char *icharset)
{
char *src = str2ascii(src_arg, icharset);
char *dest;
char *dot; /* Position of the last dot in the
filename, will be used to calculate
@ -300,9 +481,9 @@ char *iso_1_fileid(const char *src_arg)
return dest;
}
char *iso_2_fileid(const char *src_arg)
char *iso_2_fileid(const char *src_arg, const char *icharset)
{
char *src = str2ascii(src_arg);
char *src = str2ascii(src_arg, icharset);
char *dest;
char *dot;
int lname, lext, lnname, lnext, pos, i;
@ -318,7 +499,7 @@ char *iso_2_fileid(const char *src_arg)
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') {
if (dot == NULL || *(dot + 1) == '\0') {
lname = strlen(src);
lnname = (lname > 30) ? 30 : lname;
lext = lnext = 0;
@ -360,9 +541,89 @@ char *iso_2_fileid(const char *src_arg)
}
char *
iso_p_fileid(const char *src)
iso_r_fileid(const char *src_arg, const char *icharset, int flag)
{
char *ret = str2ascii(src);
char *src = str2ascii(src_arg, icharset);
char *dest;
char *dot;
int lname, lext, lnname, lnext, pos, i;
size_t max;
size_t size = flag & (1<<1) ? 37 : 33;
if (!src)
return NULL;
dest = malloc(size+1);
if (flag & ECMA119_RELAXED_FILENAMES) {
strncpy(dest, src, size);
dest[size] = '\0'; /* ensure 37 / 33 max length */
pos = strlen(dest);
pos = pos < (size == 37 ? 37 : 31) ? pos : (size == 37 ? 37 : 31);
if ( !(flag & ECMA119_OMIT_VERSION_NUMBERS) ) {
dest[pos++] = ';';
dest[pos++] = '1';
}
dest[pos] = '\0';
return dest;
}
/* no relaxed filenames */
dot = strrchr(src, '.');
max = (size == 37 ? 36 : 30);
/* 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 + 1) == '\0') {
lname = strlen(src);
lnname = (lname > max) ? max : lname;
lext = lnext = 0;
} else {
lext = strlen(dot + 1);
lname = strlen(src) - lext - 1;
lnext = (strlen(src) > max + 1 && lext > 3)
? (lname < max - 3 ? max - lname : 3) : lext;
lnname = (strlen(src) > max +1) ? max - 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 : '_';
}
if ( !(flag & ECMA119_OMIT_VERSION_NUMBERS) ) {
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, const char *icharset)
{
char *ret = str2ascii(src, icharset);
size_t i, len;
if (!ret)
@ -377,9 +638,9 @@ iso_p_fileid(const char *src)
}
uint16_t *
iso_j_id(const char *src_arg)
iso_j_id(const char *src_arg, const char *icharset)
{
uint16_t *j_str = str2ucs(src_arg);
uint16_t *j_str = str2ucs(src_arg, icharset);
size_t len = ucslen(j_str);
size_t n;
@ -420,7 +681,6 @@ void iso_bb(uint8_t *buf, uint32_t num, int bytes)
iso_msb(buf+bytes, num, bytes);
}
void iso_datetime_7(unsigned char *buf, time_t t)
{
static int tzsetup = 0;
@ -441,12 +701,12 @@ void iso_datetime_7(unsigned char *buf, time_t t)
buf[4] = tm.tm_min;
buf[5] = tm.tm_sec;
#ifdef HAVE_TM_GMTOFF
tzoffset = -tm.tm_gmtoff / 60 / 15;
tzoffset = tm.tm_gmtoff / 60 / 15;
#else
tzoffset = -timezone / 60 / 15;
tzoffset = timezone / 60 / 15;
#endif
if (tzoffset < -48)
tzoffset += 101;
if (tzoffset > 52)
tzoffset -= 101;
buf[6] = tzoffset;
}
@ -455,13 +715,12 @@ 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_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;
return timegm(&tm) - buf[6] * 60 * 15;
}
void iso_datetime_17(unsigned char *buf, time_t t)
@ -490,12 +749,12 @@ void iso_datetime_17(unsigned char *buf, time_t t)
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;
tzoffset = tm.tm_gmtoff / 60 / 15;
#else
tzoffset = -timezone / 60 / 15;
tzoffset = timezone / 60 / 15;
#endif
if (tzoffset < -48)
tzoffset += 101;
if (tzoffset > 52)
tzoffset -= 101;
buf[16] = tzoffset;
}
}
@ -513,7 +772,7 @@ time_t iso_datetime_read_17(const uint8_t *buf)
tm.tm_year -= 1900;
tm.tm_mon -= 1;
return mktime(&tm) - buf[16] * 60 * 60;
return timegm(&tm) - buf[16] * 60 * 15;
}
size_t ucslen(const uint16_t *str)
@ -574,11 +833,74 @@ uint32_t iso_read_msb(const uint8_t *buf, int bytes)
return ret;
}
uint32_t iso_read_bb(const uint8_t *buf, int bytes)
uint32_t iso_read_bb(const uint8_t *buf, int bytes, int *error)
{
uint32_t v1 = iso_read_lsb(buf, bytes);
uint32_t v2 = iso_read_msb(buf+bytes, bytes);
assert(v1 == v2);
if (error) {
uint32_t v2 = iso_read_msb(buf+bytes, bytes);
if (v1 != v2)
*error = 1;
}
return v1;
}
char *strcopy(const char *buf, size_t len)
{
char *str;
str = malloc( (len+1) * sizeof(char) );
strncpy(str, buf, len);
str[len] = '\0';
/* remove trailing spaces */
for (len = len-1; str[len] == ' ' && len > 0; --len)
str[len] = '\0';
return str;
}
char *ucs2str(const char *buf, size_t len)
{
size_t outbytes, inbytes;
char *str;
inbytes = len;
outbytes = (inbytes+1) * MB_LEN_MAX;
{
/* ensure enought space */
char out[outbytes];
char *src;
iconv_t conv;
size_t n;
/* convert to local charset */
setlocale(LC_CTYPE, "");
conv = iconv_open(nl_langinfo(CODESET), "UCS-2BE");
if (conv == (iconv_t)(-1)) {
printf("Can't convert from %s to %s\n", "UCS-2BE", nl_langinfo(CODESET));
return NULL;
}
src = (char *)buf;
str = (char *)out;
n = iconv(conv, &src, &inbytes, &str, &outbytes);
if (n == -1) {
/* error just return input stream */
perror("Convert error.");
printf("Maybe string is not encoded in UCS-2BE.\n");
iconv_close(conv);
return NULL;
}
iconv_close(conv);
*str = '\0';
/* remove trailing spaces */
for (len = strlen(out) - 1; out[len] == ' ' && len > 0; --len)
out[len] = '\0';
str = strdup(out);
}
return str;
}

50
libisofs/util.h Executable file → Normal file
View File

@ -30,19 +30,29 @@ extern inline int round_up(int n, int mul)
return div_up(n, mul) * mul;
}
char *convert_str(const char *str, const char *icharset, const char *ocharset);
wchar_t *towcs(const char *);
char *str2ascii(const char*);
uint16_t *str2ucs(const char*);
char *str2ascii(const char*, const char *);
uint16_t *str2ucs(const char *, const char *);
char *str2d_char(const char*, const char *);
char *str2a_char(const char*, const char *);
/**
* Create a level 1 directory identifier.
*/
char *iso_1_dirid(const char *src);
char *iso_1_dirid(const char *src, const char *);
/**
* Create a level 2 directory identifier.
*/
char *iso_2_dirid(const char *src);
char *iso_2_dirid(const char *src, const char *);
/**
* Create a directory identifier with relaxed constraints
*/
char *iso_r_dirid(const char *src, const char *icharset, int flags);
/**
* Create a level 1 file identifier that consists of a name, extension and
@ -51,7 +61,7 @@ char *iso_2_dirid(const char *src);
* 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);
char *iso_1_fileid(const char *src, const char *);
/**
* Create a level 2 file identifier that consists of a name, extension and
@ -60,7 +70,12 @@ char *iso_1_fileid(const char *src);
* 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);
char *iso_2_fileid(const char *src, const char *);
/**
* Create a file identifier with relaxed constraints.
*/
char *iso_r_fileid(const char *src, const char *icharset, int flags);
/**
* Create a Joliet file or directory identifier that consists of a name,
@ -73,7 +88,7 @@ char *iso_2_fileid(const char *src);
* @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);
uint16_t *iso_j_id(const char *src, const char *icharset);
/**
* FIXME: what are the requirements for these next two? Is this for RR?
@ -82,7 +97,7 @@ uint16_t *iso_j_id(const char *src);
* 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);
char *iso_p_fileid(const char *src, const char *);
/**
* Create a POSIX portable directory name.
@ -97,7 +112,12 @@ 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);
/**
* if error != NULL it will be set to 1 if LSB and MSB integers
* don't match
*/
uint32_t iso_read_bb(const uint8_t *buf, int bytes, int *error);
/** Records the date/time into a 7 byte buffer (9.1.5) */
void iso_datetime_7(uint8_t *buf, time_t t);
@ -118,4 +138,16 @@ size_t ucslen(const uint16_t *str);
*/
int ucscmp(const uint16_t *s1, const uint16_t *s2);
/**
* Copy up to \p len chars from \p buf and return this newly allocated
* string. The new string is null-terminated.
*/
char *strcopy(const char *buf, size_t len);
/**
* Convert a Joliet string with a length of \p len bytes to a new string
* in local charset.
*/
char *ucs2str(const char *buf, size_t len);
#endif /* LIBISO_UTIL_H */

274
libisofs/volume.c Executable file → Normal file
View File

@ -1,6 +1,7 @@
/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */
/* vim: set ts=8 sts=8 sw=8 noet : */
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include <libgen.h>
@ -9,6 +10,7 @@
#include "tree.h"
#include "util.h"
#include "volume.h"
#include "eltorito.h"
struct iso_volset*
iso_volset_new(struct iso_volume *vol, const char *id)
@ -23,6 +25,12 @@ iso_volset_new(struct iso_volume *vol, const char *id)
return volset;
}
void
iso_volset_ref(struct iso_volset *volset)
{
++volset->refcount;
}
void
iso_volset_free(struct iso_volset *volset)
{
@ -37,6 +45,16 @@ iso_volset_free(struct iso_volset *volset)
}
}
struct iso_volume *
iso_volset_get_volume(struct iso_volset *volset, int volnum)
{
assert(volset);
if (volnum < volset->volset_size)
return volset->volume[volnum];
else
return NULL;
}
struct iso_volume*
iso_volume_new(const char *volume_id,
const char *publisher_id,
@ -52,14 +70,14 @@ 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_tree_node_dir *root)
{
struct iso_volume *volume;
volume = calloc(1, sizeof(struct iso_volume));
volume->refcount = 1;
volume->root = root ? root : iso_tree_new_root(volume);
volume->root = root ? root : iso_tree_new_root();
if (volume_id != NULL)
volume->volume_id = strdup(volume_id);
@ -75,39 +93,166 @@ iso_volume_free(struct iso_volume *volume)
{
/* Only free if no references are in use. */
if (--volume->refcount < 1) {
iso_tree_free(volume->root);
iso_tree_free( (struct iso_tree_node*) volume->root);
free(volume->volume_id);
free(volume->publisher_id);
free(volume->data_preparer_id);
free(volume->system_id);
free(volume->application_id);
free(volume->copyright_file_id);
free(volume->abstract_file_id);
free(volume->biblio_file_id);
if (volume->bootcat)
el_torito_boot_catalog_free(volume->bootcat);
free(volume);
}
}
struct iso_tree_node *
struct iso_tree_node_dir *
iso_volume_get_root(const struct iso_volume *volume)
{
assert(volume);
return volume->root;
}
void iso_volume_set_volume_id(struct iso_volume *volume,
const char *volume_id)
{
assert(volume);
free(volume->volume_id);
volume->volume_id = strdup(volume_id);
}
const char *
iso_volume_get_volume_id(struct iso_volume *volume)
{
assert(volume);
return volume->volume_id;
}
void iso_volume_set_publisher_id(struct iso_volume *volume,
const char *publisher_id)
{
assert(volume);
free(volume->publisher_id);
volume->publisher_id = strdup(publisher_id);
}
const char *
iso_volume_get_publisher_id(struct iso_volume *volume)
{
assert(volume);
return volume->publisher_id;
}
void iso_volume_set_data_preparer_id(struct iso_volume *volume,
const char *data_preparer_id)
{
assert(volume);
free(volume->data_preparer_id);
volume->data_preparer_id = strdup(data_preparer_id);
}
const char *
iso_volume_get_data_preparer_id(struct iso_volume *volume)
{
assert(volume);
return volume->data_preparer_id;
}
void iso_volume_set_system_id(struct iso_volume *volume,
const char *system_id)
{
assert(volume);
free(volume->system_id);
volume->system_id = strdup(system_id);
}
const char *
iso_volume_get_system_id(struct iso_volume *volume)
{
assert(volume);
return volume->system_id;
}
void iso_volume_set_application_id(struct iso_volume *volume,
const char *application_id)
{
assert(volume);
free(volume->application_id);
volume->application_id = strdup(application_id);
}
const char *
iso_volume_get_application_id(struct iso_volume *volume)
{
assert(volume);
return volume->application_id;
}
void iso_volume_set_copyright_file_id(struct iso_volume *volume,
const char *copyright_file_id)
{
assert(volume);
free(volume->copyright_file_id);
volume->copyright_file_id = strdup(copyright_file_id);
}
const char *
iso_volume_get_copyright_file_id(struct iso_volume *volume)
{
assert(volume);
return volume->copyright_file_id;
}
void iso_volume_set_abstract_file_id(struct iso_volume *volume,
const char *abstract_file_id)
{
assert(volume);
free(volume->abstract_file_id);
volume->abstract_file_id = strdup(abstract_file_id);
}
const char *
iso_volume_get_abstract_file_id(struct iso_volume *volume)
{
assert(volume);
return volume->abstract_file_id;
}
void iso_volume_set_biblio_file_id(struct iso_volume *volume,
const char *biblio_file_id)
{
assert(volume);
free(volume->biblio_file_id);
volume->biblio_file_id = strdup(biblio_file_id);
}
const char *
iso_volume_get_biblio_file_id(struct iso_volume *volume)
{
assert(volume);
return volume->biblio_file_id;
}
struct iso_tree_node *
iso_tree_volume_path_to_node(struct iso_volume *volume, const char *path)
{
struct iso_tree_node *node;
struct iso_tree_node_dir *dir;
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, "/"))
dir = iso_volume_get_root(volume);
node = (struct iso_tree_node *)dir;
if (!strcmp(path, "/"))
return node;
if (!node->nchildren)
if (!dir->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 */
@ -115,12 +260,18 @@ iso_tree_volume_path_to_node(struct iso_volume *volume, const char *path)
while (component) {
size_t max;
size_t i;
if ( !ISO_ISDIR(node) ) {
node=NULL;
break;
}
dir = (struct iso_tree_node_dir *)node;
/* search among all the children of this directory if this path component exists */
max=node->nchildren;
max=dir->nchildren;
for (i=0; i < max; i++) {
if (!strcmp(component, node->children[i]->name)) {
node=node->children[i];
if (!strcmp(component, dir->children[i]->name)) {
node=dir->children[i];
break;
}
}
@ -135,54 +286,55 @@ iso_tree_volume_path_to_node(struct iso_volume *volume, const char *path)
}
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;
}
//
//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;
//}

10
libisofs/volume.h Executable file → Normal file
View File

@ -18,12 +18,20 @@ struct iso_volume
int refcount; /**< Number of used references to this
volume. */
struct iso_tree_node *root; /**< Root of the directory tree for the
struct iso_tree_node_dir *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. */
char *system_id; /**< Volume system identifier. */
char *application_id; /**< Volume application id */
char *copyright_file_id;
char *abstract_file_id;
char *biblio_file_id;
struct el_torito_boot_catalog *bootcat; /**< El-Torito boot catalog */
};
/**

33
test.sh Normal file
View File

@ -0,0 +1,33 @@
#!/bin/bash
TEST_ROOT=/tmp/libisofs_test
rm -rf $TEST_ROOT
#create test folders
mkdir -p $TEST_ROOT
mkdir -p $TEST_ROOT/dir1/dir11
chmod 755 $TEST_ROOT/dir1
chmod 755 $TEST_ROOT/dir1/dir11
touch $TEST_ROOT/dir1/dir11/a
echo "This file is to check correct file permissions. set them to 754" > $TEST_ROOT/dir1/permtest
chmod 754 $TEST_ROOT/dir1/permtest
mkdir -p $TEST_ROOT/dir2
ln -s $TEST_ROOT/dir1 "$TEST_ROOT/link to dir1"
echo "README file" > $TEST_ROOT/README
chmod 555 $TEST_ROOT/README
ln -s $TEST_ROOT/README "$TEST_ROOT/link to readme"
echo "No read file" > $TEST_ROOT/no_read
chmod 000 $TEST_ROOT/no_read
if ! make check
then
exit 1
fi
test/test

View File

@ -20,41 +20,49 @@
#define SECSIZE 2048
const char * const optstring = "JRL:h";
const char * const optstring = "JRL:b:hV:";
extern char *optarg;
extern int optind;
void usage()
void usage(char **argv)
{
printf("test [OPTIONS] DIRECTORY OUTPUT\n");
printf("%s [OPTIONS] DIRECTORY OUTPUT\n", argv[0]);
help();
}
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"
" -J Add Joliet support\n"
" -R Add Rock Ridge support\n"
" -V label Volume Label\n"
" -L <num> Set the ISO level (1 or 2)\n"
" -b file Specifies a boot image to add to image\n"
" -h Print this message\n"
);
}
int main(int argc, char **argv)
{
struct ecma119_source_opts opts;
struct iso_volset *volset;
struct iso_volume *volume;
struct iso_tree_node *root;
struct iso_tree_node_dir *root;
struct burn_source *src;
unsigned char buf[2048];
FILE *fd;
int c;
int constraints;
struct iso_tree_radd_dir_behavior behav = {0,0,0};
int level=1, flags=0;
char *boot_img = NULL;
char *volid = "VOLID";
while ((c = getopt(argc, argv, optstring)) != -1) {
switch(c) {
case 'h':
usage();
usage(argv);
help();
exit(0);
break;
@ -67,8 +75,14 @@ int main(int argc, char **argv)
case 'L':
level = atoi(optarg);
break;
case 'b':
boot_img = optarg;
break;
case 'V':
volid = optarg;
break;
case '?':
usage();
usage(argv);
exit(1);
break;
}
@ -76,32 +90,75 @@ int main(int argc, char **argv)
if (argc < 2) {
printf ("Please pass directory from which to build ISO\n");
usage();
usage(argv);
return 1;
}
if (argc < 3) {
printf ("Please supply output file\n");
usage();
usage(argv);
return 1;
}
if (!iso_init()) {
err(1, "Can't init libisofs");
}
iso_msgs_set_severities("NEVER", "ALL", "");
fd = fopen(argv[optind+1], "w");
if (!fd) {
err(1, "error opening output file");
}
root = iso_tree_radd_dir(NULL, argv[optind]);
root = iso_tree_new_root();
iso_tree_radd_dir(root, argv[optind], &behav);
if (!root) {
err(1, "error opening input directory");
}
volume = iso_volume_new_with_root( "VOLID", "PUBID", "PREPID", root );
volume = iso_volume_new_with_root(volid, "PUBID", "PREPID", root);
if ( boot_img ) {
/* adds El-Torito boot info. Tunned for isolinux */
struct el_torito_boot_image *bootimg;
struct iso_tree_node_dir *boot = (struct iso_tree_node_dir *)
iso_tree_volume_path_to_node(volume, "isolinux");
struct iso_tree_node *img = iso_tree_volume_path_to_node(volume, boot_img);
if (!img) {
err(1, "boot image patch is not valid");
}
bootimg = iso_volume_set_boot_image(volume, img, ELTORITO_NO_EMUL,
boot, "boot.cat");
el_torito_set_load_size(bootimg, 4);
el_torito_patch_isolinux_image(bootimg);
/* Or just this for hidden img
* iso_volume_set_boot_image_hidden(volume, boot_img, ELTORITO_NO_EMUL);
*/
}
volset = iso_volset_new( volume, "VOLSETID" );
/* some tests */
iso_volume_set_application_id(volume, "Libburnia");
iso_volume_set_copyright_file_id(volume, "LICENSE");
src = iso_source_new_ecma119(volset, 0, level, flags);
constraints = ECMA119_OMIT_VERSION_NUMBERS |
ECMA119_37_CHAR_FILENAMES | ECMA119_NO_DIR_REALOCATION |
ECMA119_RELAXED_FILENAMES;
memset(&opts, 0, sizeof(struct ecma119_source_opts));
opts.level = level;
opts.flags = flags;
opts.relaxed_constraints = 0;
opts.input_charset = NULL;
opts.ouput_charset = "UTF-8";
src = iso_source_new_ecma119(volset, &opts);
while (src->read(src, buf, 2048) == 2048) {
fwrite(buf, 1, 2048, fd);
}
fclose(fd);
iso_finish();
return 0;
}

157
test/iso_add.c Normal file
View File

@ -0,0 +1,157 @@
/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */
/* vim: set ts=8 sts=8 sw=8 noet : */
/*
* Little program that appends a dir to an existing image
*/
#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] IMAGE 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 ecma119_source_opts wopts;
struct ecma119_read_opts ropts;
struct data_source *rsrc;
struct iso_volset *volset;
struct iso_tree_node_dir *root;
struct burn_source *wsrc;
unsigned char buf[2048];
FILE *fd;
int c;
int constraints;
struct iso_tree_radd_dir_behavior behav = {0,0,0};
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 < optind + 1) {
printf ("Please supply old image file\n");
usage();
return 1;
}
if (argc < optind + 2) {
printf ("Please supply directory to add to image\n");
usage();
return 1;
}
if (argc < optind + 3) {
printf ("Please supply output file\n");
usage();
return 1;
}
if (!iso_init()) {
err(1, "Can't init libisofs");
}
iso_msgs_set_severities("NEVER", "ALL", "");
rsrc = data_source_from_file(argv[optind]);
if (rsrc == NULL) {
printf ("Can't open device\n");
return 1;
}
fd = fopen(argv[optind+2], "w");
if (!fd) {
err(1, "error opening output file");
}
ropts.block = 0;
ropts.norock = 0;
ropts.nojoliet = 0;
ropts.preferjoliet = 0;
ropts.mode = 0555;
ropts.uid = 0;
ropts.gid = 0;
volset = iso_volset_read(rsrc, &ropts);
if (volset == NULL) {
printf ("Error reading image\n");
return 1;
}
root = iso_volume_get_root(iso_volset_get_volume(volset, 0));
iso_tree_radd_dir(root, argv[optind+1], &behav);
constraints = ECMA119_OMIT_VERSION_NUMBERS |
ECMA119_37_CHAR_FILENAMES | ECMA119_NO_DIR_REALOCATION |
ECMA119_RELAXED_FILENAMES;
memset(&wopts, 0, sizeof(struct ecma119_source_opts));
wopts.level = level;
wopts.flags = flags;
wopts.relaxed_constraints = 0;//constraints;
wopts.input_charset = "UTF-8";
wopts.ouput_charset = "UTF-8";
wopts.ms_block = 0;
wopts.src = rsrc;
wsrc = iso_source_new_ecma119(volset, &wopts);
while (wsrc->read(wsrc, buf, 2048) == 2048) {
fwrite(buf, 1, 2048, fd);
}
fclose(fd);
iso_finish();
return 0;
}

302
test/iso_grow.c Normal file
View File

@ -0,0 +1,302 @@
/* -*- 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;
static struct data_source *libburn_data_source_new(struct burn_drive *d);
void usage()
{
printf("test [OPTIONS] DISC DIRECTORY\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 burn_drive_info *drives;
struct burn_drive *drive;
struct ecma119_source_opts wopts;
struct ecma119_read_opts ropts;
struct data_source *rsrc;
struct iso_volset *volset;
struct iso_tree_node_dir *root;
struct burn_source *wsrc;
int c;
struct iso_tree_radd_dir_behavior behav = {0,0,0};
int level=1, flags=0;
int ret = 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 < optind + 1) {
printf ("Please supply device name\n");
usage();
return 1;
}
if (argc < optind + 2) {
printf ("Please supply directory to add to disc\n");
usage();
return 1;
}
if (!iso_init()) {
err(1, "Can't init libisofs");
}
if (!burn_initialize()) {
err(1, "Can't init libburn");
}
iso_msgs_set_severities("NEVER", "ALL", "");
burn_msgs_set_severities("NEVER", "SORRY", "libburner : ");
printf("Reading from %s\n", argv[optind]);
if (burn_drive_scan_and_grab(&drives, argv[optind], 0) != 1) {
err(1, "Can't open device. Are you sure it is a valid drive?\n");
}
drive = drives[0].drive;
{
/* some check before going on */
enum burn_disc_status state;
int pno;
char name[80];
state = burn_disc_get_status(drive);
burn_disc_get_profile(drive, &pno, name);
// my drives report BURN_DISC_BLANK on a DVD+RW with data.
// is that correct?
if ( (pno != 0x1a) /*|| (state != BURN_DISC_FULL)*/ ) {
printf("You need to insert a DVD+RW with some data.\n");
printf("Profile: %x, state: %d.\n", pno, state);
ret = 1;
goto exit_cleanup;
}
}
rsrc = libburn_data_source_new(drive);
if (rsrc == NULL) {
printf ("Can't create data source.\n");
ret = 1;
goto exit_cleanup;
}
ropts.block = 0; /* image always start on first block */
ropts.norock = 0;
ropts.nojoliet = 0;
ropts.preferjoliet = 0;
ropts.mode = 0555;
ropts.uid = 0;
ropts.gid = 0;
volset = iso_volset_read(rsrc, &ropts);
if (volset == NULL) {
printf ("Error reading image\n");
ret = 1;
goto exit_cleanup;
}
printf("Image size: %d blocks.\n", ropts.size);
/* close source, no more needed */
data_source_free(rsrc);
root = iso_volume_get_root(iso_volset_get_volume(volset, 0));
/* add a new dir */
iso_tree_radd_dir(root, argv[optind+1], &behav);
memset(&wopts, 0, sizeof(struct ecma119_source_opts));
wopts.level = level;
wopts.flags = flags;
wopts.relaxed_constraints = 0;
wopts.input_charset = "UTF-8";
wopts.ouput_charset = "UTF-8";
/* round up to 32kb aligment = 16 block*/
wopts.ms_block = ((ropts.size + 15) / 16 ) * 16;
wopts.overwrite = calloc(32, 2048);
wsrc = iso_source_new_ecma119(volset, &wopts);
/* a. write the new image */
printf("Adding new data...\n");
{
struct burn_disc *target_disc;
struct burn_session *session;
struct burn_write_opts *burn_options;
struct burn_track *track;
struct burn_progress progress;
char reasons[BURN_REASONS_LEN];
target_disc = burn_disc_create();
session = burn_session_create();
burn_disc_add_session(target_disc, session, BURN_POS_END);
track = burn_track_create();
burn_track_set_source(track, wsrc);
burn_session_add_track(session, track, BURN_POS_END);
burn_options = burn_write_opts_new(drive);
burn_drive_set_speed(drive, 0, 0);
burn_write_opts_set_underrun_proof(burn_options, 1);
//mmm, check for 32K alignment?
burn_write_opts_set_start_byte(burn_options, wopts.ms_block * 2048);
if (burn_write_opts_auto_write_type(burn_options, target_disc,
reasons, 0) == BURN_WRITE_NONE) {
printf("Failed to find a suitable write mode:\n%s\n", reasons);
ret = 1;
goto exit_cleanup;
}
/* ok, write the new track */
burn_disc_write(burn_options, target_disc);
burn_write_opts_free(burn_options);
while (burn_drive_get_status(drive, NULL) == BURN_DRIVE_SPAWNING)
usleep(1002);
while (burn_drive_get_status(drive, &progress) != BURN_DRIVE_IDLE) {
printf("Writing: sector %d of %d\n", progress.sector, progress.sectors);
sleep(1);
}
}
/* b. write the new vol desc */
printf("Writing the new vol desc...\n");
if ( burn_random_access_write(drive, 0, wopts.overwrite, 32*2048, 0) != 1) {
printf("Ups, new vol desc write failed\n");
}
free(wopts.overwrite);
iso_volset_free(volset);
exit_cleanup:;
burn_drive_release(drives[0].drive, 0);
burn_finish();
iso_finish();
exit(ret);
}
struct disc_data_src {
struct burn_drive *d;
int nblocks;
};
static int
libburn_ds_read_block(struct data_source *src, int lba, unsigned char *buffer)
{
struct disc_data_src *data;
off_t data_count;
assert(src && buffer);
data = (struct disc_data_src*)src->data;
/* if (lba >= data->nblocks)
* return BLOCK_OUT_OF_FILE;
*/
if ( burn_read_data(data->d, (off_t) lba * (off_t) 2048, buffer, 2048,
&data_count, 0) < 0 ) {
return -1; //error
}
return 0;
}
static int
libburn_ds_get_size(struct data_source *src)
{
struct disc_data_src *data;
assert(src);
data = (struct disc_data_src*)src->data;
return data->nblocks;
}
static void
libburn_ds_free_data(struct data_source *src)
{
free(src->data);
}
static struct data_source *
libburn_data_source_new(struct burn_drive *d)
{
struct disc_data_src *data;
struct data_source *ret;
assert(d);
data = malloc(sizeof(struct disc_data_src));
data->d = d;
//should be filled with the size of disc (or track?)
data->nblocks = 0;
ret = malloc(sizeof(struct data_source));
ret->refcount = 1;
ret->read_block = libburn_ds_read_block;
ret->get_size = libburn_ds_get_size;
ret->free_data = libburn_ds_free_data;
ret->data = data;
return ret;
}

159
test/iso_ms.c Normal file
View File

@ -0,0 +1,159 @@
/* -*- 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] LSS NWA DISC 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 ecma119_source_opts wopts;
struct ecma119_read_opts ropts;
struct data_source *rsrc;
struct iso_volset *volset;
struct iso_volume *volume;
struct iso_tree_node_dir *root;
struct burn_source *wsrc;
unsigned char buf[2048];
FILE *fd;
int c;
int constraints;
struct iso_tree_radd_dir_behavior behav = {0,0,0};
int level=1, flags=0;
char *boot_img = NULL;
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 < optind + 2) {
printf ("Please supply last_sess_start next_sess_start\n");
usage();
return 1;
}
if (argc < optind + 3) {
printf ("Please supply device name\n");
usage();
return 1;
}
if (argc < optind + 4) {
printf ("Please supply directory to add to disc\n");
usage();
return 1;
}
if (argc < optind + 5) {
printf ("Please supply output file\n");
usage();
return 1;
}
if (!iso_init()) {
err(1, "Can't init libisofs");
}
iso_msgs_set_severities("NEVER", "ALL", "");
rsrc = data_source_from_file(argv[optind+2]);
if (rsrc == NULL) {
printf ("Can't open device\n");
return 1;
}
fd = fopen(argv[optind+4], "w");
if (!fd) {
err(1, "error opening output file");
}
ropts.block = atoi(argv[optind]);
ropts.norock = 0;
ropts.nojoliet = 0;
ropts.preferjoliet = 0;
ropts.mode = 0555;
ropts.uid = 0;
ropts.gid = 0;
volset = iso_volset_read(rsrc, &ropts);
if (volset == NULL) {
printf ("Error reading image\n");
return 1;
}
root = iso_volume_get_root(iso_volset_get_volume(volset, 0));
iso_tree_radd_dir(root, argv[optind+3], &behav);
constraints = ECMA119_OMIT_VERSION_NUMBERS |
ECMA119_37_CHAR_FILENAMES | ECMA119_NO_DIR_REALOCATION |
ECMA119_RELAXED_FILENAMES;
memset(&wopts, 0, sizeof(struct ecma119_source_opts));
wopts.level = level;
wopts.flags = flags;
wopts.relaxed_constraints = 0;//constraints;
wopts.input_charset = "UTF-8";
wopts.ouput_charset = "UTF-8";
wopts.ms_block = atoi(argv[optind+1]);
wsrc = iso_source_new_ecma119(volset, &wopts);
while (wsrc->read(wsrc, buf, 2048) == 2048) {
fwrite(buf, 1, 2048, fd);
}
fclose(fd);
iso_finish();
return 0;
}

163
test/iso_read.c Normal file
View File

@ -0,0 +1,163 @@
/*
* Little program that reads an existing ISO image and prints its
* contents to stdout.
*/
#include "libisofs.h"
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
static void
print_permissions(mode_t mode)
{
char perm[10];
//TODO suid, sticky...
perm[9] = '\0';
perm[8] = mode & S_IXOTH ? 'x' : '-';
perm[7] = mode & S_IWOTH ? 'w' : '-';
perm[6] = mode & S_IROTH ? 'r' : '-';
perm[5] = mode & S_IXGRP ? 'x' : '-';
perm[4] = mode & S_IWGRP ? 'w' : '-';
perm[3] = mode & S_IRGRP ? 'r' : '-';
perm[2] = mode & S_IXUSR ? 'x' : '-';
perm[1] = mode & S_IWUSR ? 'w' : '-';
perm[0] = mode & S_IRUSR ? 'r' : '-';
printf("[%s]",perm);
}
static void
print_dir(struct iso_tree_node_dir *dir, int level)
{
int i;
struct iso_tree_iter *iter;
struct iso_tree_node *node;
char sp[level * 2 + 1];
for (i = 0; i < level * 2; i += 2) {
sp[i] = '|';
sp[i+1] = ' ';
}
sp[level * 2-1] = '-';
sp[level * 2] = '\0';
iter = iso_tree_node_children(dir);
while ( (node = iso_tree_iter_next(iter)) != NULL ) {
if (LIBISO_ISDIR(node)) {
printf("%s+[D] ", sp);
print_permissions(iso_tree_node_get_permissions(node));
printf(" %s\n", iso_tree_node_get_name(node) );
print_dir((struct iso_tree_node_dir*)node, level+1);
} else if (LIBISO_ISREG(node)) {
printf("%s-[F] ", sp);
print_permissions(iso_tree_node_get_permissions(node));
printf(" %s\n", iso_tree_node_get_name(node) );
} else if (LIBISO_ISLNK(node)) {
printf("%s-[L] ", sp);
print_permissions(iso_tree_node_get_permissions(node));
printf(" %s -> %s \n", iso_tree_node_get_name(node),
iso_tree_node_symlink_get_dest((struct iso_tree_node_symlink*)node) );
} else {
/* boot catalog */
printf("%s-[C] ", sp);
print_permissions(iso_tree_node_get_permissions(node));
printf(" %s\n", iso_tree_node_get_name(node) );
}
}
iso_tree_iter_free(iter);
}
static void
check_el_torito(struct iso_volume *volume)
{
struct iso_tree_node *cat, *img;
if (iso_volume_get_boot_image(volume, &img, &cat)) {
printf("\nEL-TORITO INFORMATION\n");
printf("=====================\n\n");
printf("Catalog: %s\n", iso_tree_node_get_name(cat) );
printf("Image: %s\n", iso_tree_node_get_name(img) );
}
}
int main(int argc, char **argv)
{
struct ecma119_read_opts opts;
struct data_source *src;
struct iso_volset *volset;
struct iso_volume *volume;
if (argc != 2) {
printf ("You need to specify a valid ISO image\n");
return 1;
}
if (!iso_init()) {
err(1, "Can't init libisofs");
}
iso_msgs_set_severities("NEVER", "ALL", "");
src = data_source_from_file(argv[1]);
if (src == NULL) {
printf ("Can't open image\n");
return 1;
}
opts.block = 0;
opts.norock = 0;
opts.nojoliet = 0;
opts.preferjoliet = 1;
opts.mode = 0555;
opts.uid = 0;
opts.gid = 0;
volset = iso_volset_read(src, &opts);
if (volset == NULL) {
printf ("Error reading image\n");
return 1;
}
volume = iso_volset_get_volume(volset, 0);
printf("\nVOLUME INFORMATION\n");
printf("==================\n\n");
printf("Vol. id: %s\n", iso_volume_get_volume_id(volume));
printf("Publisher: %s\n", iso_volume_get_publisher_id(volume));
printf("Data preparer: %s\n", iso_volume_get_data_preparer_id(volume));
printf("System: %s\n", iso_volume_get_system_id(volume));
printf("Application: %s\n", iso_volume_get_application_id(volume));
printf("Copyright: %s\n", iso_volume_get_copyright_file_id(volume));
printf("Abstract: %s\n", iso_volume_get_abstract_file_id(volume));
printf("Biblio: %s\n", iso_volume_get_biblio_file_id(volume));
if (opts.hasRR)
printf("Rock Ridge Extensions are available.\n");
if (opts.hasJoliet)
printf("Joliet Extensions are available.\n");
printf("\nDIRECTORY TREE\n");
printf("==============\n");
print_dir(iso_volume_get_root(volume), 0);
check_el_torito(volume);
printf("\n\n");
data_source_free(src);
iso_volset_free(volset);
iso_finish();
return 0;
}

30
test/test.c Normal file
View File

@ -0,0 +1,30 @@
#include "test.h"
static void create_test_suite()
{
add_util_suite();
add_tree_suite();
add_exclude_suite();
add_file_hashtable_suite();
add_ecma119_tree_suite();
add_volume_suite();
add_data_source_suite();
add_isoread_suite();
}
int main(int argc, char **argv)
{
CU_pSuite pSuite = NULL;
/* initialize the CUnit test registry */
if (CUE_SUCCESS != CU_initialize_registry())
return CU_get_error();
create_test_suite();
/* Run all tests using the console interface */
CU_basic_set_mode(CU_BRM_VERBOSE);
CU_basic_run_tests();
CU_cleanup_registry();
return CU_get_error();
}

28
test/test.h Normal file
View File

@ -0,0 +1,28 @@
#ifndef TEST_H_
#define TEST_H_
#include <sys/types.h>
#include <stddef.h>
#include <stdio.h>
#include <CUnit/Basic.h>
#include "libisofs.h"
void add_tree_suite();
void add_exclude_suite();
void add_file_hashtable_suite();
void add_util_suite();
void add_ecma119_tree_suite();
void add_volume_suite();
void add_data_source_suite();
void add_isoread_suite();
#endif /*TEST_H_*/

75
test/test_data_source.c Normal file
View File

@ -0,0 +1,75 @@
/*
* Unit test for data_source implementation
*/
#include "test.h"
#include "libisofs.h"
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
void test_data_source_from_file()
{
int res;
struct data_source *ds;
unsigned char buffer[2048];
unsigned char rbuff[2048];
char filename[] = "/tmp/temp.iso.XXXXXX";
int fd = mkstemp(filename);
/* actually a failure here means that we couldn't test */
CU_ASSERT_NOT_EQUAL(fd, -1);
memset(buffer, 0x35, 2048);
write(fd, buffer, 2048);
memset(buffer, 0x20, 2048);
write(fd, buffer, 2048);
memset(buffer, 0xF2, 2048);
write(fd, buffer, 2048);
memset(buffer, 0x11, 2048);
write(fd, buffer, 2048);
memset(buffer, 0xAB, 2048);
write(fd, buffer, 2048);
close(fd);
ds = data_source_from_file(filename);
CU_ASSERT_PTR_NOT_NULL(ds);
/* check correct size reported */
CU_ASSERT_EQUAL(ds->get_size(ds), 5);
/* check reading */
res = ds->read_block(ds, 4, rbuff);
CU_ASSERT_EQUAL(res, 0);
CU_ASSERT_NSTRING_EQUAL(rbuff, buffer, 2048);
res = ds->read_block(ds, 1, rbuff);
CU_ASSERT_EQUAL(res, 0);
memset(buffer, 0x20, 2048);
CU_ASSERT_NSTRING_EQUAL(rbuff, buffer, 2048);
res = ds->read_block(ds, 0, rbuff);
CU_ASSERT_EQUAL(res, 0);
memset(buffer, 0x35, 2048);
CU_ASSERT_NSTRING_EQUAL(rbuff, buffer, 2048);
res = ds->read_block(ds, 3, rbuff);
CU_ASSERT_EQUAL(res, 0);
memset(buffer, 0x11, 2048);
CU_ASSERT_NSTRING_EQUAL(rbuff, buffer, 2048);
/* and a block outside file */
res = ds->read_block(ds, 5, rbuff);
CU_ASSERT_TRUE(res < 0);
data_source_free(ds);
unlink(filename);
}
void add_data_source_suite()
{
CU_pSuite pSuite = CU_add_suite("DataSourceSuite", NULL, NULL);
CU_add_test(pSuite, "test of data_source_from_file()", test_data_source_from_file);
}

122
test/test_ecma119_tree.c Normal file
View File

@ -0,0 +1,122 @@
/*
* Unit test for ecma119_tree.h
*/
#include "libisofs.h"
#include "tree.h"
#include "test.h"
#include "ecma119.h"
#include "ecma119_tree.h"
#include <assert.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <string.h>
#include <time.h>
struct ecma119_write_target t;
static void reset_write_target()
{
/* inititalize t with default values */
t.root = NULL;
t.joliet_root = NULL;
t.volset = NULL;
t.volnum = time(NULL);
t.now;
t.total_size = 0;
t.vol_space_size = 0;
t.rockridge = 0;
t.joliet = 0;
t.iso_level = 1;
t.eltorito = 0;
t.relaxed_constraints = 0;
t.catalog = NULL;
t.replace_mode = 0;
t.dir_mode = 0777;
t.file_mode = 0777;
t.gid = 0;
t.uid = 0;
t.input_charset = "UTF-8";
t.ouput_charset = "UTF-8";
t.cache_inodes = 0;
t.sort_files = 0;
t.ino = 0;
t.curblock = 0;
t.block_size = 2048;
t.path_table_size = 0;
t.path_table_size_joliet = 0;
t.l_path_table_pos = 0;
t.m_path_table_pos = 0;
t.l_path_table_pos_joliet = 0;
t.m_path_table_pos_joliet = 0;
t.total_dir_size = 0;
t.total_dir_size_joliet = 0;
t.dirlist = NULL;
t.pathlist = NULL;
t.dirlist_len = 0;
t.file_table = NULL;
t.filelist = NULL;
t.filelist_len = 0;
t.curfile = 0;
t.dirlist_joliet = NULL;
t.pathlist_joliet = NULL;
t.dirlist_len_joliet = 0;
t.state = ECMA119_WRITE_SYSTEM_AREA;
}
void test_create_tree_only_root()
{
struct iso_tree_node *root;
struct ecma119_tree_node *node;
reset_write_target();
root = (struct iso_tree_node*) iso_tree_new_root();
node = ecma119_tree_create(&t, root);
CU_ASSERT_PTR_NOT_NULL(node);
/* root has no name */
CU_ASSERT_PTR_NULL(node->iso_name);
CU_ASSERT_PTR_NULL(node->full_name);
/* target root has been set */
CU_ASSERT_PTR_EQUAL(t.root, node);
CU_ASSERT_PTR_EQUAL(node->target, &t);
CU_ASSERT_PTR_NULL(node->parent);
CU_ASSERT_EQUAL(node->attrib.st_mode, root->attrib.st_mode);
CU_ASSERT_EQUAL(node->attrib.st_uid, root->attrib.st_uid);
CU_ASSERT_EQUAL(node->attrib.st_gid, root->attrib.st_gid);
/* the node is a directory */
CU_ASSERT_EQUAL(node->type, ECMA119_DIR);
CU_ASSERT_EQUAL(node->info.dir.nchildren, 0);
iso_tree_free((struct iso_tree_node *)root);
ecma119_tree_free(node);
}
void add_ecma119_tree_suite()
{
CU_pSuite pSuite = CU_add_suite("Ecma119TreeSuite", NULL, NULL);
CU_add_test(pSuite, "test of ecma119_tree_create() with only root dir", test_create_tree_only_root);
//CU_add_test(pSuite, "test of create_dir()", test_create_dir);
}

33
test/test_exclude.c Normal file
View File

@ -0,0 +1,33 @@
/*
* Unit test for exclude.h
*/
#include "exclude.h"
#include "test.h"
#include <assert.h>
#include <stdio.h>
static void test_exclude()
{
struct iso_hash_table table = { {0,}, 0};
CU_ASSERT_FALSE( iso_exclude_lookup(&table, "/dir") );
CU_ASSERT_FALSE( iso_exclude_lookup(&table, "/otherdir") );
iso_exclude_add_path(&table, "/otherdir");
CU_ASSERT_TRUE( iso_exclude_lookup(&table, "/otherdir") );
CU_ASSERT_FALSE( iso_exclude_lookup(&table, "/dir") );
iso_exclude_add_path(&table, "/dir");
CU_ASSERT_TRUE( iso_exclude_lookup(&table, "/otherdir") );
CU_ASSERT_TRUE( iso_exclude_lookup(&table, "/dir") );
iso_exclude_empty(&table);
CU_ASSERT_FALSE( iso_exclude_lookup(&table, "/dir") );
CU_ASSERT_FALSE( iso_exclude_lookup(&table, "/otherdir") );
}
void add_exclude_suite()
{
CU_pSuite pSuite = CU_add_suite("ExcludeSuite", NULL, NULL);
CU_add_test(pSuite, "test of exclude", test_exclude);
}

314
test/test_file_hashtable.c Normal file
View File

@ -0,0 +1,314 @@
/*
* Unit test for file.h
*/
#include "test.h"
#include "file.h"
#include "tree.h"
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
static void test_iso_file_new()
{
struct iso_tree_node_file *file;
struct iso_file *iso;
file = calloc(1, sizeof(struct iso_tree_node_file) );
file->node.name = "fileName";
file->node.attrib.st_size = 12;
file->node.attrib.st_dev = 15;
file->node.attrib.st_ino = 204;
file->loc.path = "/tmp/filename";
file->sort_weight = 1;
iso = iso_file_new(file);
CU_ASSERT_PTR_NOT_NULL(iso);
CU_ASSERT_STRING_EQUAL(iso->path, "/tmp/filename");
CU_ASSERT_EQUAL(iso->size, 12);
CU_ASSERT_EQUAL(iso->ino, 0);
CU_ASSERT_EQUAL(iso->nlink, 1);
CU_ASSERT_EQUAL(iso->sort_weight, 1);
CU_ASSERT_EQUAL(iso->real_dev, 15);
CU_ASSERT_EQUAL(iso->real_ino, 204);
}
static void test_add_lookup()
{
struct iso_file_table *table;
struct iso_tree_node_file *file1;
struct iso_tree_node_file *file2;
struct iso_file *iso1;
struct iso_file *iso2;
struct iso_file *iso3;
int r;
table = iso_file_table_new(1);
CU_ASSERT_PTR_NOT_NULL( table );
CU_ASSERT_TRUE( table->cache_inodes );
CU_ASSERT_EQUAL(table->count, 0);
file1 = calloc(1, sizeof(struct iso_tree_node_file) );
file1->node.name = "fileName";
file1->node.attrib.st_dev = 15;
file1->node.attrib.st_ino = 204;
file1->loc.path = "/tmp/filename";
iso1 = iso_file_new(file1);
r = iso_file_table_add_file(table, iso1);
CU_ASSERT_EQUAL(r, 1);
CU_ASSERT_EQUAL(table->count, 1);
iso2 = iso_file_table_lookup(table, file1);
CU_ASSERT_PTR_NOT_NULL(iso2);
CU_ASSERT_PTR_EQUAL(iso2, iso1);
file2 = calloc(1, sizeof(struct iso_tree_node_file) );
file2->node.name = "fileName2";
file2->node.attrib.st_dev = 152;
file2->node.attrib.st_ino = 2042;
file2->loc.path = "/tmp/filename2";
iso3 = iso_file_new(file2);
r = iso_file_table_add_file(table, iso3);
CU_ASSERT_EQUAL(r, 1);
CU_ASSERT_EQUAL(table->count, 2);
/* treat to add the same file again */
r = iso_file_table_add_file(table, iso3);
CU_ASSERT_EQUAL(r, 0);
CU_ASSERT_EQUAL(table->count, 2);
iso2 = iso_file_table_lookup(table, file1);
CU_ASSERT_PTR_NOT_NULL(iso2);
CU_ASSERT_PTR_EQUAL(iso2, iso1);
iso2 = iso_file_table_lookup(table, file2);
CU_ASSERT_PTR_NOT_NULL(iso2);
CU_ASSERT_PTR_EQUAL(iso2, iso3);
iso3 = iso_file_new(file2);
r = iso_file_table_add_file(table, iso3);
CU_ASSERT_EQUAL(r, 0);
CU_ASSERT_EQUAL(table->count, 2);
iso_file_table_clear(table);
CU_ASSERT_EQUAL(table->count, 0);
iso2 = iso_file_table_lookup(table, file2);
CU_ASSERT_PTR_NULL(iso2);
free( file1 );
free( file2 );
free( table );
}
static void test_cache_inodes()
{
struct iso_file_table *table;
struct iso_tree_node_file *file1;
struct iso_tree_node_file *file2;
struct iso_file *iso1;
struct iso_file *iso2;
struct iso_file *iso3;
int r;
table = iso_file_table_new(1);
CU_ASSERT_PTR_NOT_NULL( table );
CU_ASSERT_TRUE( table->cache_inodes );
CU_ASSERT_EQUAL(table->count, 0);
file1 = calloc(1, sizeof(struct iso_tree_node_file) );
file1->node.name = "fileName";
file1->node.attrib.st_dev = 15;
file1->node.attrib.st_ino = 204;
file1->loc.path = "/tmp/filename";
iso1 = iso_file_new(file1);
r = iso_file_table_add_file(table, iso1);
CU_ASSERT_EQUAL(r, 1);
/* another file, different but with the same inode id */
file2 = calloc(1, sizeof(struct iso_tree_node_file) );
file2->node.name = "another file";
file2->node.attrib.st_dev = 15;
file2->node.attrib.st_ino = 204;
file2->loc.path = "/tmp/another";
iso2 = iso_file_new(file2);
/* ensure it's not added again... */
r = iso_file_table_add_file(table, iso2);
CU_ASSERT_EQUAL(r, 0);
/* ...and the lookup returns the first */
iso3 = iso_file_table_lookup(table, file2);
CU_ASSERT_PTR_EQUAL(iso1, iso3);
free(iso2);
free(file2);
/* and now a file with same inode but different device */
file2 = calloc(1, sizeof(struct iso_tree_node_file) );
file2->node.name = "different file";
file2->node.attrib.st_dev = 16; /* different dev id */
file2->node.attrib.st_ino = 204;
file2->loc.path = "/tmp/different";
iso2 = iso_file_new(file2);
r = iso_file_table_add_file(table, iso2);
CU_ASSERT_EQUAL(r, 1);
iso3 = iso_file_table_lookup(table, file2);
CU_ASSERT_PTR_NOT_EQUAL(iso3, iso1);
CU_ASSERT_PTR_EQUAL(iso3, iso2);
iso_file_table_clear(table);
free( file1 );
free( file2 );
free( table );
}
static void test_no_cache_inodes()
{
struct iso_file_table *table;
struct iso_tree_node_file *file1;
struct iso_tree_node_file *file2;
struct iso_tree_node_file *file3;
struct iso_file *iso1;
struct iso_file *iso2;
struct iso_file *iso3;
int r;
table = iso_file_table_new(0);
CU_ASSERT_PTR_NOT_NULL( table );
CU_ASSERT_FALSE( table->cache_inodes );
CU_ASSERT_EQUAL(table->count, 0);
file1 = calloc(1, sizeof(struct iso_tree_node_file) );
file1->node.name = "fileName";
file1->node.attrib.st_dev = 15;
file1->node.attrib.st_ino = 204;
file1->loc.path = "/tmp/filename";
iso1 = iso_file_new(file1);
r = iso_file_table_add_file(table, iso1);
CU_ASSERT_EQUAL(r, 1);
/* another file, different but with the same inode id */
file2 = calloc(1, sizeof(struct iso_tree_node_file) );
file2->node.name = "another file";
file2->node.attrib.st_dev = 15;
file2->node.attrib.st_ino = 204;
file2->loc.path = "/tmp/another";
iso2 = iso_file_new(file2);
/* ensure is added */
r = iso_file_table_add_file(table, iso2);
CU_ASSERT_EQUAL(r, 1);
iso3 = iso_file_table_lookup(table, file2);
CU_ASSERT_PTR_EQUAL(iso3, iso2);
/* and now a file with same inode and path */
file3 = calloc(1, sizeof(struct iso_tree_node_file) );
file3->node.name = "different file";
file3->node.attrib.st_dev = 15;
file3->node.attrib.st_ino = 204;
file3->loc.path = "/tmp/filename";
iso3 = iso_file_new(file3);
r = iso_file_table_add_file(table, iso3);
CU_ASSERT_EQUAL(r, 0);
iso3 = iso_file_table_lookup(table, file3);
CU_ASSERT_PTR_EQUAL(iso3, iso1);
iso_file_table_clear(table);
free(file1);
free(file2);
free(file3);
free(table);
}
static void test_prev_img_files()
{
struct iso_file_table *table;
struct iso_tree_node_file *file1;
struct iso_tree_node_file *file2;
struct iso_tree_node_file *file3;
struct iso_file *iso1;
struct iso_file *iso2;
struct iso_file *iso3;
int r;
table = iso_file_table_new(0);
CU_ASSERT_PTR_NOT_NULL( table );
CU_ASSERT_FALSE( table->cache_inodes );
CU_ASSERT_EQUAL(table->count, 0);
file1 = calloc(1, sizeof(struct iso_tree_node_file) );
file1->node.name = "fileName";
file1->node.procedence = LIBISO_PREVIMG;
file1->node.attrib.st_dev = 0;
file1->node.attrib.st_ino = 204;
file1->loc.block = 567;
iso1 = iso_file_new(file1);
r = iso_file_table_add_file(table, iso1);
CU_ASSERT_EQUAL(r, 1);
/* another file, different but with the same inode id */
file2 = calloc(1, sizeof(struct iso_tree_node_file) );
file2->node.name = "another file";
file2->node.procedence = LIBISO_PREVIMG;
file2->node.attrib.st_dev = 0;
file2->node.attrib.st_ino = 204;
file2->loc.block = 567;
iso2 = iso_file_new(file2);
/* ensure is not added */
r = iso_file_table_add_file(table, iso2);
CU_ASSERT_EQUAL(r, 0);
iso3 = iso_file_table_lookup(table, file2);
CU_ASSERT_PTR_EQUAL(iso3, iso1);
/* and now a file added new */
file3 = calloc(1, sizeof(struct iso_tree_node_file) );
file3->node.name = "different file";
file3->node.attrib.st_dev = 0;
file3->node.attrib.st_ino = 204;
file3->loc.path = "/tmp/filename";
iso3 = iso_file_new(file3);
/* assert it's added */
r = iso_file_table_add_file(table, iso3);
CU_ASSERT_EQUAL(r, 1);
iso1 = iso_file_table_lookup(table, file3);
CU_ASSERT_PTR_EQUAL(iso1, iso3);
iso_file_table_clear(table);
free(file1);
free(file2);
free(file3);
free(table);
}
void add_file_hashtable_suite()
{
CU_pSuite pSuite = CU_add_suite("FileHashtableSuite", NULL, NULL);
CU_add_test(pSuite, "iso_file_new()", test_iso_file_new);
CU_add_test(pSuite, "add and lookup", test_add_lookup);
CU_add_test(pSuite, "with cache_inodes", test_cache_inodes);
CU_add_test(pSuite, "with files from previous img", test_prev_img_files);
}

57
test/test_read.c Normal file
View File

@ -0,0 +1,57 @@
/*
* Unit test iso reading functions
*/
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include "test.h"
#include "ecma119_read_rr.h"
#include "util.h"
static void
test_read_rr_PX()
{
struct iso_read_info info;
struct susp_sys_user_entry px;
struct stat atts;
int res;
info.src = NULL; /* data source is not needed */
info.error = 0;
info.ino = 0;
memset(&atts, 0, sizeof(atts));
/* fill px struct */
px.sig[0] = 'P';
px.sig[1] = 'X';
px.version[0] = 1;
iso_bb(px.data.PX.uid, 33, 4);
iso_bb(px.data.PX.gid, 22, 4);
iso_bb(px.data.PX.links, 7, 4);
iso_bb(px.data.PX.mode, S_IFREG | 0555, 4);
/* a) test with RRIP 1.12 */
info.rr = RR_EXT_112;
px.len_sue[0] = 44;
iso_bb(px.data.PX.serial, 678, 4);
res = read_rr_PX(&info, &px, &atts);
CU_ASSERT_EQUAL(res, 0);
CU_ASSERT_EQUAL(atts.st_uid, 33);
CU_ASSERT_EQUAL(atts.st_gid, 22);
CU_ASSERT_EQUAL(atts.st_mode, (mode_t) S_IFREG | 0555);
CU_ASSERT_EQUAL(atts.st_nlink, 7);
CU_ASSERT_EQUAL(atts.st_ino, 678);
//TODO
}
void add_isoread_suite()
{
CU_pSuite pSuite = CU_add_suite("IsoReadSuite", NULL, NULL);
CU_add_test(pSuite, "test of read_rr_PX()", test_read_rr_PX);
}

1051
test/test_tree.c Normal file

File diff suppressed because it is too large Load Diff

387
test/test_util.c Normal file
View File

@ -0,0 +1,387 @@
/*
* Unit test for util.h
*
* This test utiliy functions
*
*/
#include <time.h>
#include <locale.h>
#include "test.h"
#include "util.h"
static void test_div_up()
{
CU_ASSERT_EQUAL( div_up(1, 2), 1 );
CU_ASSERT_EQUAL( div_up(2, 2), 1 );
CU_ASSERT_EQUAL( div_up(0, 2), 0 );
CU_ASSERT_EQUAL( div_up(-1, 2), 0 );
CU_ASSERT_EQUAL( div_up(3, 2), 2 );
}
static void test_round_up()
{
CU_ASSERT_EQUAL( round_up(1, 2), 2 );
CU_ASSERT_EQUAL( round_up(2, 2), 2 );
CU_ASSERT_EQUAL( round_up(0, 2), 0 );
CU_ASSERT_EQUAL( round_up(-1, 2), 0 );
CU_ASSERT_EQUAL( round_up(3, 2), 4 );
CU_ASSERT_EQUAL( round_up(15, 7), 21 );
CU_ASSERT_EQUAL( round_up(13, 7), 14 );
CU_ASSERT_EQUAL( round_up(14, 7), 14 );
}
static void test_iso_lsb_msb()
{
uint8_t buf[4];
uint32_t num;
num = 0x01020304;
iso_lsb(buf, num, 4);
CU_ASSERT_EQUAL( buf[0], 0x04 );
CU_ASSERT_EQUAL( buf[1], 0x03 );
CU_ASSERT_EQUAL( buf[2], 0x02 );
CU_ASSERT_EQUAL( buf[3], 0x01 );
iso_msb(buf, num, 4);
CU_ASSERT_EQUAL( buf[0], 0x01 );
CU_ASSERT_EQUAL( buf[1], 0x02 );
CU_ASSERT_EQUAL( buf[2], 0x03 );
CU_ASSERT_EQUAL( buf[3], 0x04 );
iso_lsb(buf, num, 2);
CU_ASSERT_EQUAL( buf[0], 0x04 );
CU_ASSERT_EQUAL( buf[1], 0x03 );
iso_msb(buf, num, 2);
CU_ASSERT_EQUAL( buf[0], 0x03 );
CU_ASSERT_EQUAL( buf[1], 0x04 );
}
static void test_iso_read_lsb_msb()
{
uint8_t buf[4];
uint32_t num;
buf[0] = 0x04;
buf[1] = 0x03;
buf[2] = 0x02;
buf[3] = 0x01;
num = iso_read_lsb(buf, 4);
CU_ASSERT_EQUAL(num, 0x01020304);
num = iso_read_msb(buf, 4);
CU_ASSERT_EQUAL(num, 0x04030201);
num = iso_read_lsb(buf, 2);
CU_ASSERT_EQUAL(num, 0x0304);
num = iso_read_msb(buf, 2);
CU_ASSERT_EQUAL(num, 0x0403);
}
static void test_iso_bb()
{
uint8_t buf[8];
uint32_t num;
num = 0x01020304;
iso_bb(buf, num, 4);
CU_ASSERT_EQUAL( buf[0], 0x04 );
CU_ASSERT_EQUAL( buf[1], 0x03 );
CU_ASSERT_EQUAL( buf[2], 0x02 );
CU_ASSERT_EQUAL( buf[3], 0x01 );
CU_ASSERT_EQUAL( buf[4], 0x01 );
CU_ASSERT_EQUAL( buf[5], 0x02 );
CU_ASSERT_EQUAL( buf[6], 0x03 );
CU_ASSERT_EQUAL( buf[7], 0x04 );
iso_bb(buf, num, 2);
CU_ASSERT_EQUAL( buf[0], 0x04 );
CU_ASSERT_EQUAL( buf[1], 0x03 );
CU_ASSERT_EQUAL( buf[2], 0x03 );
CU_ASSERT_EQUAL( buf[3], 0x04 );
}
static void test_iso_read_bb()
{
uint8_t buf[8];
uint32_t num;
int error = 0;
buf[0] = 0x04;
buf[1] = 0x03;
buf[2] = 0x02;
buf[3] = 0x01;
buf[4] = 0x01;
buf[5] = 0x02;
buf[6] = 0x03;
buf[7] = 0x04;
num = iso_read_bb(buf, 4, &error);
CU_ASSERT_EQUAL( num, 0x01020304 );
CU_ASSERT_FALSE(error);
num = iso_read_bb(buf, 4, NULL);
CU_ASSERT_EQUAL( num, 0x01020304 );
buf[2] = 3;
num = iso_read_bb(buf, 4, &error);
/* return the LSB */
CU_ASSERT_EQUAL( num, 0x01030304 );
CU_ASSERT_TRUE(error);
num = iso_read_bb(buf, 4, NULL);
/* return the LSB */
CU_ASSERT_EQUAL( num, 0x01030304 );
error = 0;
buf[3] = 0x04;
num = iso_read_bb(buf, 2, &error);
CU_ASSERT_EQUAL( num, 0x0304 );
CU_ASSERT_FALSE(error);
num = iso_read_bb(buf, 2, NULL);
CU_ASSERT_EQUAL( num, 0x0304 );
}
static void test_iso_datetime_7()
{
uint8_t buf[7];
time_t t, t2;
struct tm tp;
strptime("01-03-1976 13:27:45", "%d-%m-%Y %T", &tp);
t = mktime(&tp);
iso_datetime_7(buf, t);
CU_ASSERT_EQUAL( buf[0], 76 ); /* year since 1900 */
CU_ASSERT_EQUAL( buf[1], 3 ); /* month */
CU_ASSERT_EQUAL( buf[2], 1 ); /* day */
CU_ASSERT_EQUAL( buf[3], 13 ); /* hour */
CU_ASSERT_EQUAL( buf[4], 27 ); /* minute */
CU_ASSERT_EQUAL( buf[5], 45 ); /* second */
/* the offset depends on current timezone and it's not easy to test */
//CU_ASSERT_EQUAL( buf[6], 4 ); /* 15 min offset */
/* check that reading returns the same time */
t2 = iso_datetime_read_7(buf);
CU_ASSERT_EQUAL(t2, t);
//TODO check with differnt timezones for reading and writting
}
static void test_iso_1_dirid()
{
CU_ASSERT_STRING_EQUAL( iso_1_dirid("dir1", "UTF-8"), "DIR1" );
CU_ASSERT_STRING_EQUAL( iso_1_dirid("dIR1", "UTF-8"), "DIR1" );
CU_ASSERT_STRING_EQUAL( iso_1_dirid("DIR1", "UTF-8"), "DIR1" );
CU_ASSERT_STRING_EQUAL( iso_1_dirid("dirwithbigname", "UTF-8"), "DIRWITHB");
CU_ASSERT_STRING_EQUAL( iso_1_dirid("dirwith8", "UTF-8"), "DIRWITH8");
CU_ASSERT_STRING_EQUAL( iso_1_dirid("dir.1", "UTF-8"), "DIR_1");
CU_ASSERT_STRING_EQUAL( iso_1_dirid("4f<0KmM::xcvf", "UTF-8"), "4F_0KMM_");
}
static void test_iso_2_dirid()
{
CU_ASSERT_STRING_EQUAL( iso_2_dirid("dir1", "UTF-8"), "DIR1" );
CU_ASSERT_STRING_EQUAL( iso_2_dirid("dIR1", "UTF-8"), "DIR1" );
CU_ASSERT_STRING_EQUAL( iso_2_dirid("DIR1", "UTF-8"), "DIR1" );
CU_ASSERT_STRING_EQUAL( iso_2_dirid("dirwithbigname", "UTF-8"), "DIRWITHBIGNAME");
CU_ASSERT_STRING_EQUAL( iso_2_dirid("dirwith8", "UTF-8"), "DIRWITH8");
CU_ASSERT_STRING_EQUAL( iso_2_dirid("dir.1", "UTF-8"), "DIR_1");
CU_ASSERT_STRING_EQUAL( iso_2_dirid("4f<0KmM::xcvf", "UTF-8"), "4F_0KMM__XCVF");
CU_ASSERT_STRING_EQUAL( iso_2_dirid("directory with 31 characters ok", "UTF-8"), "DIRECTORY_WITH_31_CHARACTERS_OK");
CU_ASSERT_STRING_EQUAL( iso_2_dirid("directory with more than 31 characters", "UTF-8"), "DIRECTORY_WITH_MORE_THAN_31_CHA");
}
static void test_iso_r_dirid()
{
int flag;
/* 1. only ECMA119_37_CHAR_FILENAMES */
flag = ECMA119_37_CHAR_FILENAMES;
CU_ASSERT_STRING_EQUAL( iso_r_dirid("dir1", "UTF-8", flag), "DIR1");
CU_ASSERT_STRING_EQUAL( iso_r_dirid("dIR1", "UTF-8", flag), "DIR1");
CU_ASSERT_STRING_EQUAL( iso_r_dirid("DIR1", "UTF-8", flag), "DIR1");
CU_ASSERT_STRING_EQUAL( iso_r_dirid("dirwithbigname", "UTF-8", flag), "DIRWITHBIGNAME");
CU_ASSERT_STRING_EQUAL( iso_r_dirid("dirwith8", "UTF-8", flag), "DIRWITH8");
CU_ASSERT_STRING_EQUAL( iso_r_dirid("dir.1", "UTF-8", flag), "DIR_1");
CU_ASSERT_STRING_EQUAL( iso_r_dirid("4f<0KmM::xcvf", "UTF-8", flag), "4F_0KMM__XCVF");
CU_ASSERT_STRING_EQUAL( iso_r_dirid("directory with 31 characters ok", "UTF-8", flag), "DIRECTORY_WITH_31_CHARACTERS_OK");
CU_ASSERT_STRING_EQUAL( iso_r_dirid("directory with more than 37 characters", "UTF-8", flag), "DIRECTORY_WITH_MORE_THAN_37_CHARACTER");
CU_ASSERT_STRING_EQUAL( iso_r_dirid("directory with just 37 characters ok", "UTF-8", flag), "DIRECTORY_WITH_JUST_37_CHARACTERS__OK");
/* 2. only ECMA119_RELAXED_FILENAMES */
flag = ECMA119_RELAXED_FILENAMES;
CU_ASSERT_STRING_EQUAL( iso_r_dirid("dir1", "UTF-8", flag), "dir1");
CU_ASSERT_STRING_EQUAL( iso_r_dirid("dIR1", "UTF-8", flag), "dIR1");
CU_ASSERT_STRING_EQUAL( iso_r_dirid("DIR1", "UTF-8", flag), "DIR1");
CU_ASSERT_STRING_EQUAL( iso_r_dirid("dirwithbigname", "UTF-8", flag), "dirwithbigname");
CU_ASSERT_STRING_EQUAL( iso_r_dirid("dirwith8", "UTF-8", flag), "dirwith8");
CU_ASSERT_STRING_EQUAL( iso_r_dirid("dir.1", "UTF-8", flag), "dir.1");
CU_ASSERT_STRING_EQUAL( iso_r_dirid("4f<0KmM::xcvf", "UTF-8", flag), "4f<0KmM::xcvf");
CU_ASSERT_STRING_EQUAL( iso_r_dirid("directory with 31 characters ok", "UTF-8", flag), "directory with 31 characters ok");
CU_ASSERT_STRING_EQUAL( iso_r_dirid("directory with more than 37 characters", "UTF-8", flag), "directory with more than 37 cha");
CU_ASSERT_STRING_EQUAL( iso_r_dirid("directory with just 37 characters ok", "UTF-8", flag), "directory with just 37 characte");
/* 3. both ECMA119_RELAXED_FILENAMES and ECMA119_37_CHAR_FILENAMES */
flag = ECMA119_RELAXED_FILENAMES | ECMA119_37_CHAR_FILENAMES;
CU_ASSERT_STRING_EQUAL( iso_r_dirid("dir1", "UTF-8", flag), "dir1");
CU_ASSERT_STRING_EQUAL( iso_r_dirid("dIR1", "UTF-8", flag), "dIR1");
CU_ASSERT_STRING_EQUAL( iso_r_dirid("DIR1", "UTF-8", flag), "DIR1");
CU_ASSERT_STRING_EQUAL( iso_r_dirid("dirwithbigname", "UTF-8", flag), "dirwithbigname");
CU_ASSERT_STRING_EQUAL( iso_r_dirid("dirwith8", "UTF-8", flag), "dirwith8");
CU_ASSERT_STRING_EQUAL( iso_r_dirid("dir.1", "UTF-8", flag), "dir.1");
CU_ASSERT_STRING_EQUAL( iso_r_dirid("4f<0KmM::xcvf", "UTF-8", flag), "4f<0KmM::xcvf");
CU_ASSERT_STRING_EQUAL( iso_r_dirid("directory with 31 characters ok", "UTF-8", flag), "directory with 31 characters ok");
CU_ASSERT_STRING_EQUAL( iso_r_dirid("directory with more than 37 characters", "UTF-8", flag), "directory with more than 37 character");
CU_ASSERT_STRING_EQUAL( iso_r_dirid("directory with just 37 characters ok", "UTF-8", flag), "directory with just 37 characters ok");
}
static void test_iso_1_fileid()
{
CU_ASSERT_STRING_EQUAL( iso_1_fileid("file1", "UTF-8"), "FILE1.;1");
CU_ASSERT_STRING_EQUAL( iso_1_fileid("fILe1", "UTF-8"), "FILE1.;1");
CU_ASSERT_STRING_EQUAL( iso_1_fileid("FILE1", "UTF-8"), "FILE1.;1");
CU_ASSERT_STRING_EQUAL( iso_1_fileid(".EXT", "UTF-8"), ".EXT;1");
CU_ASSERT_STRING_EQUAL( iso_1_fileid("file.ext", "UTF-8"), "FILE.EXT;1");
CU_ASSERT_STRING_EQUAL( iso_1_fileid("fiLE.ext", "UTF-8"), "FILE.EXT;1");
CU_ASSERT_STRING_EQUAL( iso_1_fileid("file.EXt", "UTF-8"), "FILE.EXT;1");
CU_ASSERT_STRING_EQUAL( iso_1_fileid("FILE.EXT", "UTF-8"), "FILE.EXT;1");
CU_ASSERT_STRING_EQUAL( iso_1_fileid("bigfilename", "UTF-8"), "BIGFILEN.;1");
CU_ASSERT_STRING_EQUAL( iso_1_fileid("bigfilename.ext", "UTF-8"), "BIGFILEN.EXT;1");
CU_ASSERT_STRING_EQUAL( iso_1_fileid("bigfilename.e", "UTF-8"), "BIGFILEN.E;1");
CU_ASSERT_STRING_EQUAL( iso_1_fileid("file.bigext", "UTF-8"), "FILE.BIG;1");
CU_ASSERT_STRING_EQUAL( iso_1_fileid(".bigext", "UTF-8"), ".BIG;1");
CU_ASSERT_STRING_EQUAL( iso_1_fileid("bigfilename.bigext", "UTF-8"), "BIGFILEN.BIG;1");
CU_ASSERT_STRING_EQUAL( iso_1_fileid("file<:a.ext", "UTF-8"), "FILE__A.EXT;1");
CU_ASSERT_STRING_EQUAL( iso_1_fileid("file.<:a", "UTF-8"), "FILE.__A;1");
CU_ASSERT_STRING_EQUAL( iso_1_fileid("file<:a.--a", "UTF-8"), "FILE__A.__A;1");
CU_ASSERT_STRING_EQUAL( iso_1_fileid("file.ex1.ex2", "UTF-8"), "FILE_EX1.EX2;1");
CU_ASSERT_STRING_EQUAL( iso_1_fileid("file.ex1.ex2.ex3", "UTF-8"), "FILE_EX1.EX3;1");
CU_ASSERT_STRING_EQUAL( iso_1_fileid("fil.ex1.ex2.ex3", "UTF-8"), "FIL_EX1_.EX3;1");
}
static void test_iso_2_fileid()
{
CU_ASSERT_STRING_EQUAL( iso_2_fileid("file1", "UTF-8"), "FILE1.;1");
CU_ASSERT_STRING_EQUAL( iso_2_fileid("fILe1", "UTF-8"), "FILE1.;1");
CU_ASSERT_STRING_EQUAL( iso_2_fileid("FILE1", "UTF-8"), "FILE1.;1");
CU_ASSERT_STRING_EQUAL( iso_2_fileid(".EXT", "UTF-8"), ".EXT;1");
CU_ASSERT_STRING_EQUAL( iso_2_fileid("file.ext", "UTF-8"), "FILE.EXT;1");
CU_ASSERT_STRING_EQUAL( iso_2_fileid("fiLE.ext", "UTF-8"), "FILE.EXT;1");
CU_ASSERT_STRING_EQUAL( iso_2_fileid("file.EXt", "UTF-8"), "FILE.EXT;1");
CU_ASSERT_STRING_EQUAL( iso_2_fileid("FILE.EXT", "UTF-8"), "FILE.EXT;1");
CU_ASSERT_STRING_EQUAL( iso_2_fileid("bigfilename", "UTF-8"), "BIGFILENAME.;1");
CU_ASSERT_STRING_EQUAL( iso_2_fileid("bigfilename.ext", "UTF-8"), "BIGFILENAME.EXT;1");
CU_ASSERT_STRING_EQUAL( iso_2_fileid("bigfilename.e", "UTF-8"), "BIGFILENAME.E;1");
CU_ASSERT_STRING_EQUAL( iso_2_fileid("31 characters filename.extensio", "UTF-8"), "31_CHARACTERS_FILENAME.EXTENSIO;1");
CU_ASSERT_STRING_EQUAL( iso_2_fileid("32 characters filename.extension", "UTF-8"), "32_CHARACTERS_FILENAME.EXTENSIO;1");
CU_ASSERT_STRING_EQUAL( iso_2_fileid("more than 30 characters filename.extension", "UTF-8"), "MORE_THAN_30_CHARACTERS_FIL.EXT;1");
CU_ASSERT_STRING_EQUAL( iso_2_fileid("file.bigext", "UTF-8"), "FILE.BIGEXT;1");
CU_ASSERT_STRING_EQUAL( iso_2_fileid(".bigext", "UTF-8"), ".BIGEXT;1");
CU_ASSERT_STRING_EQUAL( iso_2_fileid("bigfilename.bigext", "UTF-8"), "BIGFILENAME.BIGEXT;1");
CU_ASSERT_STRING_EQUAL( iso_2_fileid("file<:a.ext", "UTF-8"), "FILE__A.EXT;1");
CU_ASSERT_STRING_EQUAL( iso_2_fileid("file.<:a", "UTF-8"), "FILE.__A;1");
CU_ASSERT_STRING_EQUAL( iso_2_fileid("file<:a.--a", "UTF-8"), "FILE__A.__A;1");
CU_ASSERT_STRING_EQUAL( iso_2_fileid("file.ex1.ex2", "UTF-8"), "FILE_EX1.EX2;1");
CU_ASSERT_STRING_EQUAL( iso_2_fileid("file.ex1.ex2.ex3", "UTF-8"), "FILE_EX1_EX2.EX3;1");
CU_ASSERT_STRING_EQUAL( iso_2_fileid("fil.ex1.ex2.ex3", "UTF-8"), "FIL_EX1_EX2.EX3;1");
CU_ASSERT_STRING_EQUAL( iso_2_fileid(".file.bigext", "UTF-8"), "_FILE.BIGEXT;1");
}
static void test_iso_r_fileid()
{
int flag;
/* 1. only ECMA119_OMIT_VERSION_NUMBERS */
flag = ECMA119_OMIT_VERSION_NUMBERS;
CU_ASSERT_STRING_EQUAL( iso_r_fileid("file1", "UTF-8", flag), "FILE1.");
CU_ASSERT_STRING_EQUAL( iso_r_fileid("fILe1", "UTF-8", flag), "FILE1.");
CU_ASSERT_STRING_EQUAL( iso_r_fileid("31 characters filename.extensio", "UTF-8", flag), "31_CHARACTERS_FILENAME.EXTENSIO");
CU_ASSERT_STRING_EQUAL( iso_r_fileid("it's 37 characters filename.extension", "UTF-8", flag), "IT_S_37_CHARACTERS_FILENAME.EXT");
/* 2. only ECMA119_37_CHAR_FILENAMES */
flag = ECMA119_37_CHAR_FILENAMES;
CU_ASSERT_STRING_EQUAL( iso_r_fileid("file1", "UTF-8", flag), "FILE1.");
CU_ASSERT_STRING_EQUAL( iso_r_fileid("fILe1", "UTF-8", flag), "FILE1.");
CU_ASSERT_STRING_EQUAL( iso_r_fileid("FILE1", "UTF-8", flag), "FILE1.");
CU_ASSERT_STRING_EQUAL( iso_r_fileid(".EXT", "UTF-8", flag), ".EXT");
CU_ASSERT_STRING_EQUAL( iso_r_fileid("file.ext", "UTF-8", flag), "FILE.EXT");
CU_ASSERT_STRING_EQUAL( iso_r_fileid("fiLE.ext", "UTF-8", flag), "FILE.EXT");
CU_ASSERT_STRING_EQUAL( iso_r_fileid("file.EXt", "UTF-8", flag), "FILE.EXT");
CU_ASSERT_STRING_EQUAL( iso_r_fileid("FILE.EXT", "UTF-8", flag), "FILE.EXT");
CU_ASSERT_STRING_EQUAL( iso_r_fileid("bigfilename", "UTF-8", flag), "BIGFILENAME.");
CU_ASSERT_STRING_EQUAL( iso_r_fileid("bigfilename.ext", "UTF-8", flag), "BIGFILENAME.EXT");
CU_ASSERT_STRING_EQUAL( iso_r_fileid("bigfilename.e", "UTF-8", flag), "BIGFILENAME.E");
CU_ASSERT_STRING_EQUAL( iso_r_fileid("file.bigext", "UTF-8", flag), "FILE.BIGEXT");
CU_ASSERT_STRING_EQUAL( iso_r_fileid("31 characters filename.extensio", "UTF-8", flag), "31_CHARACTERS_FILENAME.EXTENSIO");
CU_ASSERT_STRING_EQUAL( iso_r_fileid("it's 37 characters filename.extension", "UTF-8", flag), "IT_S_37_CHARACTERS_FILENAME.EXTENSION");
CU_ASSERT_STRING_EQUAL( iso_r_fileid("more than 37 characters filename.extension", "UTF-8", flag), "MORE_THAN_37_CHARACTERS_FILENAME.EXTE");
CU_ASSERT_STRING_EQUAL( iso_r_fileid("file.this is a 37 chars len extension", "UTF-8", flag), "FILE.THIS_IS_A_37_CHARS_LEN_EXTENSION");
CU_ASSERT_STRING_EQUAL( iso_r_fileid("file.this is a very very big extension", "UTF-8", flag), "FILE.THIS_IS_A_VERY_VERY_BIG_EXTENSIO");
CU_ASSERT_STRING_EQUAL( iso_r_fileid("fil.ex1.ex2.ex3", "UTF-8", flag), "FIL_EX1_EX2.EX3");
/* 3. only ECMA119_RELAXED_FILENAMES */
flag = ECMA119_RELAXED_FILENAMES;
CU_ASSERT_STRING_EQUAL( iso_r_fileid("file1", "UTF-8", flag), "file1;1");
CU_ASSERT_STRING_EQUAL( iso_r_fileid("fILe1", "UTF-8", flag), "fILe1;1");
CU_ASSERT_STRING_EQUAL( iso_r_fileid("FILE1", "UTF-8", flag), "FILE1;1");
CU_ASSERT_STRING_EQUAL( iso_r_fileid(".EXT", "UTF-8", flag), ".EXT;1");
CU_ASSERT_STRING_EQUAL( iso_r_fileid("file.ext", "UTF-8", flag), "file.ext;1");
CU_ASSERT_STRING_EQUAL( iso_r_fileid("fiLE.ext", "UTF-8", flag), "fiLE.ext;1");
CU_ASSERT_STRING_EQUAL( iso_r_fileid("file.EXt", "UTF-8", flag), "file.EXt;1");
CU_ASSERT_STRING_EQUAL( iso_r_fileid("FILE.EXT", "UTF-8", flag), "FILE.EXT;1");
CU_ASSERT_STRING_EQUAL( iso_r_fileid("bigfilename", "UTF-8", flag), "bigfilename;1");
CU_ASSERT_STRING_EQUAL( iso_r_fileid("bigfilename.ext", "UTF-8", flag), "bigfilename.ext;1");
CU_ASSERT_STRING_EQUAL( iso_r_fileid("bigfilename.e", "UTF-8", flag), "bigfilename.e;1");
CU_ASSERT_STRING_EQUAL( iso_r_fileid("file.bigext", "UTF-8", flag), "file.bigext;1");
CU_ASSERT_STRING_EQUAL( iso_r_fileid("31 characters filename.extensio", "UTF-8", flag), "31 characters filename.extensio;1");
CU_ASSERT_STRING_EQUAL( iso_r_fileid("it's 37 characters filename.extension", "UTF-8", flag), "it's 37 characters filename.ext;1");
CU_ASSERT_STRING_EQUAL( iso_r_fileid("file.this is a 37 chars len extension", "UTF-8", flag), "file.this is a 37 chars len ext;1");
CU_ASSERT_STRING_EQUAL( iso_r_fileid("fil.ex1.ex2.ex3", "UTF-8", flag), "fil.ex1.ex2.ex3;1");
CU_ASSERT_STRING_EQUAL( iso_r_fileid("file.<:a", "UTF-8", flag), "file.<:a;1");
/* 3. ECMA119_RELAXED_FILENAMES and ECMA119_OMIT_VERSION_NUMBERS*/
flag = ECMA119_RELAXED_FILENAMES | ECMA119_OMIT_VERSION_NUMBERS;
CU_ASSERT_STRING_EQUAL( iso_r_fileid("file1", "UTF-8", flag), "file1");
CU_ASSERT_STRING_EQUAL( iso_r_fileid("fILe1", "UTF-8", flag), "fILe1");
CU_ASSERT_STRING_EQUAL( iso_r_fileid("FILE1", "UTF-8", flag), "FILE1");
CU_ASSERT_STRING_EQUAL( iso_r_fileid(".EXT", "UTF-8", flag), ".EXT");
CU_ASSERT_STRING_EQUAL( iso_r_fileid("file.ext", "UTF-8", flag), "file.ext");
CU_ASSERT_STRING_EQUAL( iso_r_fileid("fiLE.ext", "UTF-8", flag), "fiLE.ext");
CU_ASSERT_STRING_EQUAL( iso_r_fileid("file.EXt", "UTF-8", flag), "file.EXt");
CU_ASSERT_STRING_EQUAL( iso_r_fileid("FILE.EXT", "UTF-8", flag), "FILE.EXT");
CU_ASSERT_STRING_EQUAL( iso_r_fileid("bigfilename", "UTF-8", flag), "bigfilename");
CU_ASSERT_STRING_EQUAL( iso_r_fileid("bigfilename.ext", "UTF-8", flag), "bigfilename.ext");
CU_ASSERT_STRING_EQUAL( iso_r_fileid("bigfilename.e", "UTF-8", flag), "bigfilename.e");
CU_ASSERT_STRING_EQUAL( iso_r_fileid("file.bigext", "UTF-8", flag), "file.bigext");
CU_ASSERT_STRING_EQUAL( iso_r_fileid("31 characters filename.extensio", "UTF-8", flag), "31 characters filename.extensio");
CU_ASSERT_STRING_EQUAL( iso_r_fileid("it's 37 characters filename.extension", "UTF-8", flag), "it's 37 characters filename.ext");
CU_ASSERT_STRING_EQUAL( iso_r_fileid("file.this is a 37 chars len extension", "UTF-8", flag), "file.this is a 37 chars len ext");
CU_ASSERT_STRING_EQUAL( iso_r_fileid("fil.ex1.ex2.ex3", "UTF-8", flag), "fil.ex1.ex2.ex3");
CU_ASSERT_STRING_EQUAL( iso_r_fileid("file.<:a", "UTF-8", flag), "file.<:a");
}
void add_util_suite()
{
CU_pSuite pSuite = CU_add_suite("UtilSuite", NULL, NULL);
CU_add_test(pSuite, "div_up()", test_div_up);
CU_add_test(pSuite, "round_up()", test_round_up);
CU_add_test(pSuite, "iso_lsb() and iso_msb()", test_iso_lsb_msb);
CU_add_test(pSuite, "iso_read_lsb() and iso_read_msb()", test_iso_read_lsb_msb);
CU_add_test(pSuite, "iso_bb()", test_iso_bb);
CU_add_test(pSuite, "iso_read_bb()", test_iso_read_bb);
CU_add_test(pSuite, "iso_datetime_7()", test_iso_datetime_7);
CU_add_test(pSuite, "iso_1_dirid()", test_iso_1_dirid);
CU_add_test(pSuite, "iso_2_dirid()", test_iso_2_dirid);
CU_add_test(pSuite, "iso_r_dirid()", test_iso_r_dirid);
CU_add_test(pSuite, "iso_1_fileid()", test_iso_1_fileid);
CU_add_test(pSuite, "iso_2_fileid()", test_iso_2_fileid);
CU_add_test(pSuite, "iso_r_fileid()", test_iso_r_fileid);
}

375
test/test_volume.c Normal file
View File

@ -0,0 +1,375 @@
/*
* Unit test for volume.h
*/
#include "libisofs.h"
#include "tree.h"
#include "test.h"
#include "volume.h"
#include <assert.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <string.h>
static void test_iso_volume_new()
{
struct iso_volume *volume;
volume = iso_volume_new("volume_id", "publisher_id", "data_preparer_id");
CU_ASSERT_PTR_NOT_NULL(volume);
CU_ASSERT_EQUAL(volume->refcount, 1);
/* a new root must be created */
CU_ASSERT_PTR_NOT_NULL(volume->root);
CU_ASSERT_STRING_EQUAL( volume->volume_id, "volume_id" );
CU_ASSERT_STRING_EQUAL( volume->publisher_id, "publisher_id" );
CU_ASSERT_STRING_EQUAL( volume->data_preparer_id, "data_preparer_id" );
CU_ASSERT_PTR_NULL(volume->system_id);
CU_ASSERT_PTR_NULL(volume->application_id);
CU_ASSERT_PTR_NULL(volume->copyright_file_id);
CU_ASSERT_PTR_NULL(volume->abstract_file_id);
CU_ASSERT_PTR_NULL(volume->biblio_file_id);
CU_ASSERT_PTR_NULL(volume->bootcat);
iso_volume_free(volume);
}
static void test_iso_volume_new_with_root()
{
struct iso_volume *volume;
struct iso_tree_node_dir *root;
root = iso_tree_new_root();
volume = iso_volume_new_with_root("volume_id", "publisher_id",
"data_preparer_id", root);
CU_ASSERT_PTR_NOT_NULL(volume);
CU_ASSERT_EQUAL(volume->refcount, 1);
CU_ASSERT_PTR_NOT_NULL(volume->root);
CU_ASSERT_PTR_EQUAL(volume->root, root);
CU_ASSERT_STRING_EQUAL( volume->volume_id, "volume_id" );
CU_ASSERT_STRING_EQUAL( volume->publisher_id, "publisher_id" );
CU_ASSERT_STRING_EQUAL( volume->data_preparer_id, "data_preparer_id" );
CU_ASSERT_PTR_NULL(volume->system_id);
CU_ASSERT_PTR_NULL(volume->application_id);
CU_ASSERT_PTR_NULL(volume->copyright_file_id);
CU_ASSERT_PTR_NULL(volume->abstract_file_id);
CU_ASSERT_PTR_NULL(volume->biblio_file_id);
CU_ASSERT_PTR_NULL(volume->bootcat);
iso_volume_free(volume);
}
static void test_iso_volume_get_root()
{
struct iso_volume *volume;
struct iso_tree_node_dir *root;
struct iso_tree_node_dir *root2;
root = iso_tree_new_root();
volume = iso_volume_new_with_root("volume_id", "publisher_id",
"data_preparer_id", root);
root2 = iso_volume_get_root(volume);
CU_ASSERT_PTR_NOT_NULL(root2);
CU_ASSERT_PTR_EQUAL(root2, volume->root);
CU_ASSERT_PTR_EQUAL(root2, root);
iso_volume_free(volume);
}
static void test_iso_volume_set_volume_id()
{
struct iso_volume *volume;
volume = iso_volume_new("volume_id", "publisher_id", "data_preparer_id");
CU_ASSERT_STRING_EQUAL( volume->volume_id, "volume_id" );
char *volid = "new volume id";
iso_volume_set_volume_id(volume, volid);
CU_ASSERT_STRING_EQUAL( volume->volume_id, "new volume id" );
/* check string was strdup'ed */
CU_ASSERT_PTR_NOT_EQUAL( volume->volume_id, volid );
iso_volume_free(volume);
}
static void test_iso_volume_get_volume_id()
{
struct iso_volume *volume;
volume = iso_volume_new("volume_id", "publisher_id", "data_preparer_id");
CU_ASSERT_STRING_EQUAL( iso_volume_get_volume_id(volume), "volume_id" );
char *volid = "new volume id";
iso_volume_set_volume_id(volume, volid);
CU_ASSERT_STRING_EQUAL( iso_volume_get_volume_id(volume), "new volume id" );
iso_volume_free(volume);
}
static void test_iso_volume_set_publisher_id()
{
struct iso_volume *volume;
volume = iso_volume_new("volume_id", "publisher_id", "data_preparer_id");
CU_ASSERT_STRING_EQUAL( volume->publisher_id, "publisher_id" );
char *pubid = "new publisher id";
iso_volume_set_publisher_id(volume, pubid);
CU_ASSERT_STRING_EQUAL( volume->publisher_id, "new publisher id" );
/* check string was strdup'ed */
CU_ASSERT_PTR_NOT_EQUAL( volume->publisher_id, pubid );
iso_volume_free(volume);
}
static void test_iso_volume_get_publisher_id()
{
struct iso_volume *volume;
volume = iso_volume_new("volume_id", "publisher_id", "data_preparer_id");
CU_ASSERT_STRING_EQUAL( iso_volume_get_publisher_id(volume), "publisher_id" );
char *pubid = "new publisher id";
iso_volume_set_publisher_id(volume, pubid);
CU_ASSERT_STRING_EQUAL( iso_volume_get_publisher_id(volume), "new publisher id" );
iso_volume_free(volume);
}
static void test_iso_volume_set_data_preparer_id()
{
struct iso_volume *volume;
volume = iso_volume_new("volume_id", "publisher_id", "data_preparer_id");
CU_ASSERT_STRING_EQUAL( volume->data_preparer_id, "data_preparer_id" );
char *dpid = "new data preparer id";
iso_volume_set_data_preparer_id(volume, dpid);
CU_ASSERT_STRING_EQUAL( volume->data_preparer_id, "new data preparer id" );
/* check string was strdup'ed */
CU_ASSERT_PTR_NOT_EQUAL( volume->data_preparer_id, dpid );
iso_volume_free(volume);
}
static void test_iso_volume_get_data_preparer_id()
{
struct iso_volume *volume;
volume = iso_volume_new("volume_id", "publisher_id", "data_preparer_id");
CU_ASSERT_STRING_EQUAL( iso_volume_get_data_preparer_id(volume), "data_preparer_id" );
char *dpid = "new data preparer id";
iso_volume_set_data_preparer_id(volume, dpid);
CU_ASSERT_STRING_EQUAL( iso_volume_get_data_preparer_id(volume), "new data preparer id" );
iso_volume_free(volume);
}
static void test_iso_volume_set_system_id()
{
struct iso_volume *volume;
volume = iso_volume_new("volume_id", "publisher_id", "data_preparer_id");
CU_ASSERT_PTR_NULL(volume->system_id);
char *sysid = "new system id";
iso_volume_set_system_id(volume, sysid);
CU_ASSERT_STRING_EQUAL( volume->system_id, "new system id" );
/* check string was strdup'ed */
CU_ASSERT_PTR_NOT_EQUAL( volume->system_id, sysid );
iso_volume_free(volume);
}
static void test_iso_volume_get_system_id()
{
struct iso_volume *volume;
volume = iso_volume_new("volume_id", "publisher_id", "data_preparer_id");
CU_ASSERT_PTR_NULL(iso_volume_get_system_id(volume));
char *sysid = "new system id";
iso_volume_set_system_id(volume, sysid);
CU_ASSERT_STRING_EQUAL( iso_volume_get_system_id(volume), "new system id" );
iso_volume_free(volume);
}
static void test_iso_volume_set_application_id()
{
struct iso_volume *volume;
volume = iso_volume_new("volume_id", "publisher_id", "data_preparer_id");
CU_ASSERT_PTR_NULL(volume->application_id);
char *appid = "new application id";
iso_volume_set_application_id(volume, appid);
CU_ASSERT_STRING_EQUAL( volume->application_id, "new application id" );
/* check string was strdup'ed */
CU_ASSERT_PTR_NOT_EQUAL( volume->application_id, appid );
iso_volume_free(volume);
}
static void test_iso_volume_get_application_id()
{
struct iso_volume *volume;
volume = iso_volume_new("volume_id", "publisher_id", "data_preparer_id");
CU_ASSERT_PTR_NULL(iso_volume_get_application_id(volume));
char *appid = "new application id";
iso_volume_set_application_id(volume, appid);
CU_ASSERT_STRING_EQUAL( iso_volume_get_application_id(volume), "new application id" );
iso_volume_free(volume);
}
static void test_iso_volume_set_copyright_file_id()
{
struct iso_volume *volume;
volume = iso_volume_new("volume_id", "publisher_id", "data_preparer_id");
CU_ASSERT_PTR_NULL(volume->copyright_file_id);
char *copid = "new copyright id";
iso_volume_set_copyright_file_id(volume, copid);
CU_ASSERT_STRING_EQUAL( volume->copyright_file_id, "new copyright id" );
/* check string was strdup'ed */
CU_ASSERT_PTR_NOT_EQUAL( volume->copyright_file_id, copid );
iso_volume_free(volume);
}
static void test_iso_volume_get_copyright_file_id()
{
struct iso_volume *volume;
volume = iso_volume_new("volume_id", "publisher_id", "data_preparer_id");
CU_ASSERT_PTR_NULL(iso_volume_get_copyright_file_id(volume));
char *copid = "new copyright id";
iso_volume_set_copyright_file_id(volume, copid);
CU_ASSERT_STRING_EQUAL( iso_volume_get_copyright_file_id(volume), "new copyright id" );
iso_volume_free(volume);
}
static void test_iso_volume_set_abstract_file_id()
{
struct iso_volume *volume;
volume = iso_volume_new("volume_id", "publisher_id", "data_preparer_id");
CU_ASSERT_PTR_NULL(volume->abstract_file_id);
char *absid = "new abstract id";
iso_volume_set_abstract_file_id(volume, absid);
CU_ASSERT_STRING_EQUAL( volume->abstract_file_id, "new abstract id" );
/* check string was strdup'ed */
CU_ASSERT_PTR_NOT_EQUAL( volume->abstract_file_id, absid );
iso_volume_free(volume);
}
static void test_iso_volume_get_abstract_file_id()
{
struct iso_volume *volume;
volume = iso_volume_new("volume_id", "publisher_id", "data_preparer_id");
CU_ASSERT_PTR_NULL(iso_volume_get_abstract_file_id(volume));
char *absid = "new abstract id";
iso_volume_set_abstract_file_id(volume, absid);
CU_ASSERT_STRING_EQUAL(iso_volume_get_abstract_file_id(volume), "new abstract id");
iso_volume_free(volume);
}
static void test_iso_volume_set_biblio_file_id()
{
struct iso_volume *volume;
volume = iso_volume_new("volume_id", "publisher_id", "data_preparer_id");
CU_ASSERT_PTR_NULL(volume->biblio_file_id);
char *bibid = "new biblio id";
iso_volume_set_biblio_file_id(volume, bibid);
CU_ASSERT_STRING_EQUAL( volume->biblio_file_id, "new biblio id" );
/* check string was strdup'ed */
CU_ASSERT_PTR_NOT_EQUAL( volume->biblio_file_id, bibid );
iso_volume_free(volume);
}
static void test_iso_volume_get_biblio_file_id()
{
struct iso_volume *volume;
volume = iso_volume_new("volume_id", "publisher_id", "data_preparer_id");
CU_ASSERT_PTR_NULL(iso_volume_get_biblio_file_id(volume));
char *bibid = "new biblio id";
iso_volume_set_biblio_file_id(volume, bibid);
CU_ASSERT_STRING_EQUAL(iso_volume_get_biblio_file_id(volume), "new biblio id");
iso_volume_free(volume);
}
static void test_iso_volset_new()
{
struct iso_volume *volume;
struct iso_volset *volset;
volume = iso_volume_new("volume_id", "publisher_id", "data_preparer_id");
volset = iso_volset_new(volume, "volset_id");
CU_ASSERT_PTR_NOT_NULL(volset);
CU_ASSERT_EQUAL(volset->refcount, 1);
CU_ASSERT_EQUAL(volset->volset_size, 1);
CU_ASSERT_PTR_NOT_NULL(volset->volume);
CU_ASSERT_PTR_NOT_NULL(volset->volume[0]);
CU_ASSERT_PTR_EQUAL(volset->volume[0], volume);
CU_ASSERT_STRING_EQUAL( volset->volset_id, "volset_id" );
iso_volset_free(volset);
}
void add_volume_suite()
{
CU_pSuite pSuite = CU_add_suite("VolumeSuite", NULL, NULL);
CU_add_test(pSuite, "test of iso_volume_new()", test_iso_volume_new);
CU_add_test(pSuite, "test of iso_volume_new_with_root()", test_iso_volume_new_with_root);
CU_add_test(pSuite, "test of iso_volume_get_root()", test_iso_volume_get_root);
CU_add_test(pSuite, "test of iso_volume_set_volume_id()", test_iso_volume_set_volume_id);
CU_add_test(pSuite, "test of iso_volume_get_volume_id()", test_iso_volume_get_volume_id);
CU_add_test(pSuite, "test of iso_volume_set_publisher_id()", test_iso_volume_set_publisher_id);
CU_add_test(pSuite, "test of iso_volume_get_publisher_id()", test_iso_volume_get_publisher_id);
CU_add_test(pSuite, "test of iso_volume_set_data_preparer_id()", test_iso_volume_set_data_preparer_id);
CU_add_test(pSuite, "test of iso_volume_get_data_preparer_id()", test_iso_volume_get_data_preparer_id);
CU_add_test(pSuite, "test of iso_volume_set_system_id()", test_iso_volume_set_system_id);
CU_add_test(pSuite, "test of iso_volume_get_system_id()", test_iso_volume_get_system_id);
CU_add_test(pSuite, "test of iso_volume_set_application_id()", test_iso_volume_set_application_id);
CU_add_test(pSuite, "test of iso_volume_get_application_id()", test_iso_volume_get_application_id);
CU_add_test(pSuite, "test of iso_volume_set_copyright_file_id()", test_iso_volume_set_copyright_file_id);
CU_add_test(pSuite, "test of iso_volume_get_copyright_file_id()", test_iso_volume_get_copyright_file_id);
CU_add_test(pSuite, "test of iso_volume_set_abstract_file_id()", test_iso_volume_set_abstract_file_id);
CU_add_test(pSuite, "test of iso_volume_get_abstract_file_id()", test_iso_volume_get_abstract_file_id);
CU_add_test(pSuite, "test of iso_volume_set_biblio_file_id()", test_iso_volume_set_biblio_file_id);
CU_add_test(pSuite, "test of iso_volume_get_biblio_file_id()", test_iso_volume_get_biblio_file_id);
CU_add_test(pSuite, "test of iso_volset_new()", test_iso_volset_new);
}