Sorted the data file content extents by ECMA-119 tree rather than

by the red-black tree which shall consolidate files with identical
source object. Discovered and repaired a flaw in transitivity of
iso_stream_cmp_ino().
This commit is contained in:
stacker 2015-07-27 13:50:57 +02:00
parent 94f8503b57
commit 6947bfe5ec
17 changed files with 585 additions and 87 deletions

View File

@ -88,6 +88,8 @@ void ecma119_image_free(Ecma119Image *t)
iso_image_unref(t->image);
if (t->files != NULL)
iso_rbtree_destroy(t->files, iso_file_src_free);
if (t->ecma119_hidden_list != NULL)
iso_filesrc_list_destroy(&(t->ecma119_hidden_list));
if (t->buffer != NULL)
iso_ring_buffer_free(t->buffer);
@ -2343,6 +2345,7 @@ int ecma119_image_new(IsoImage *src, IsoWriteOpts *in_opts, Ecma119Image **img)
if (ret < 0) {
goto target_cleanup;
}
target->ecma119_hidden_list = NULL;
target->image = src;
iso_image_ref(src);
@ -4252,3 +4255,83 @@ ex:
}
static
void ecma119_filesrc_array(Ecma119Node *dir,
int (*include_item)(void *),
IsoFileSrc **filelist, size_t *size, int just_count)
{
size_t i;
Ecma119Node *child;
for (i = 0; i < dir->info.dir->nchildren; i++) {
child = dir->info.dir->children[i];
if (child->type == ECMA119_DIR) {
ecma119_filesrc_array(child, include_item, filelist, size,
just_count);
} else if (child->type == ECMA119_FILE) {
if (include_item != NULL)
if (!include_item((void *) child->info.file))
continue;
if (just_count) {
(*size)++;
} else {
if (!child->info.file->taken) {
filelist[*size] = child->info.file;
child->info.file->taken = 1;
(*size)++;
}
}
}
}
}
static
void hidden_filesrc_array(Ecma119Image *t,
int (*include_item)(void *),
IsoFileSrc **filelist, size_t *size, int just_count)
{
struct iso_filesrc_list_item *item;
for (item = t->ecma119_hidden_list; item != NULL; item = item->next) {
if (include_item != NULL)
if (!include_item((void *) item->src))
continue;
if (just_count) {
(*size)++;
} else {
if (!item->src->taken) {
filelist[*size] = item->src;
item->src->taken = 1;
(*size)++;
}
}
}
}
IsoFileSrc **iso_ecma119_to_filesrc_array(Ecma119Image *t,
int (*include_item)(void *),
size_t *size)
{
IsoFileSrc **filelist = NULL;
/* Count nodes */
*size = 0;
ecma119_filesrc_array(t->root, include_item, filelist, size, 1);
hidden_filesrc_array(t, include_item, filelist, size, 1);
LIBISO_ALLOC_MEM_VOID(filelist, IsoFileSrc *, *size + 1);
/* Fill array */
*size = 0;
ecma119_filesrc_array(t->root, include_item, filelist, size, 0);
hidden_filesrc_array(t, include_item, filelist, size, 0);
filelist[*size] = NULL;
return filelist;
ex: /* LIBISO_ALLOC_MEM failed */
*size = 0;
return NULL;
}

View File

@ -702,6 +702,8 @@ struct ecma119_image
/* tree of files sources */
IsoRBTree *files;
struct iso_filesrc_list_item *ecma119_hidden_list;
unsigned int checksum_idx_counter;
void *checksum_ctx;
off_t checksum_counter;

View File

@ -365,6 +365,35 @@ void ecma119_node_free(Ecma119Node *node)
free(node);
}
static
int add_to_hidden_list(Ecma119Image *image, IsoFileSrc *src)
{
int ret;
struct iso_filesrc_list_item *item;
LIBISO_ALLOC_MEM(item, struct iso_filesrc_list_item, 1);
item->src = src;
item->next = image->ecma119_hidden_list;
image->ecma119_hidden_list = item;
ret = ISO_SUCCESS;
ex:
return ret;
}
int iso_filesrc_list_destroy(struct iso_filesrc_list_item **start_item)
{
struct iso_filesrc_list_item *item, *next;
for (item = *start_item; item != NULL; item = next) {
next = item->next;
LIBISO_FREE_MEM(item);
}
return ISO_SUCCESS;
}
/**
* @param flag
* bit0= iso is in a hidden directory. Thus hide it.
@ -430,6 +459,9 @@ int create_tree(Ecma119Image *image, IsoNode *iso, Ecma119Node **tree,
case LIBISO_FILE:
if (hidden) {
ret = create_file_src(image, (IsoFile *) iso, &src);
if (ret <= 0)
goto ex;
ret = add_to_hidden_list(image, src);
} else {
ret = create_file(image, (IsoFile*)iso, &node);
}
@ -470,6 +502,9 @@ int create_tree(Ecma119Image *image, IsoNode *iso, Ecma119Node **tree,
if (image->eltorito) {
if (hidden) {
ret = el_torito_catalog_file_src_create(image, &src);
if (ret <= 0)
goto ex;
ret = add_to_hidden_list(image, src);
} else {
ret = create_boot_cat(image, (IsoBoot*)iso, &node);
}

View File

@ -79,6 +79,17 @@ struct ecma119_node
} info;
};
/* For recording files which are hidden in ECMA-119 */
struct iso_filesrc_list_item
{
IsoFileSrc *src;
struct iso_filesrc_list_item *next;
};
int iso_filesrc_list_destroy(struct iso_filesrc_list_item **start_item);
/**
*
*/

View File

@ -229,12 +229,23 @@ int shall_be_written(void *arg)
return f->no_write ? 0 : 1;
}
static
int shall_be_written_if_not_taken(void *arg)
{
IsoFileSrc *f = (IsoFileSrc *)arg;
return f->no_write || f->taken ? 0 : 1;
}
int filesrc_writer_pre_compute(IsoImageWriter *writer)
{
size_t i, size, is_external;
Ecma119Image *t;
IsoFileSrc **filelist;
int (*inc_item)(void *);
size_t omitted_count;
IsoFileSrc **iso_ecma119_to_filesrc_array(Ecma119Image *t,
int (*include_item)(void *),
size_t *size);
if (writer == NULL) {
return ISO_ASSERT_FAILURE;
@ -257,7 +268,16 @@ int filesrc_writer_pre_compute(IsoImageWriter *writer)
}
/* store the filesrcs in a array */
filelist = (IsoFileSrc**)iso_rbtree_to_array(t->files, inc_item, &size);
filelist = (IsoFileSrc**) iso_ecma119_to_filesrc_array(t, inc_item, &size);
omitted_count = iso_rbtree_count_array(t->files, (size_t) 0,
shall_be_written_if_not_taken);
if (omitted_count > 0) {
iso_msg_submit(t->image->id, ISO_NOT_REPRODUCIBLE, 0,
"Cannot arrange content of data files in surely reproducible way");
LIBISO_FREE_MEM(filelist);
filelist = (IsoFileSrc**)iso_rbtree_to_array(
t->files, inc_item, &size);
}
if (filelist == NULL) {
return ISO_OUT_OF_MEM;
}

View File

@ -32,6 +32,10 @@ struct Iso_File_Src
*/
unsigned int no_write :1;
/* Is 1 if the object was already put into the filelist array.
*/
unsigned int taken :1;
unsigned int checksum_index :31;
/** File Sections of the file in the image */

View File

@ -655,14 +655,41 @@ IsoStreamIface extf_stream_class = {
static
int extf_cmp_ino(IsoStream *s1, IsoStream *s2)
{
int i;
ExternalFilterStreamData *data1, *data2;
IsoExternalFilterCommand *cmd1, *cmd2;
/* This function may rely on being called by iso_stream_cmp_ino()
only with s1, s2 which both point to it as their .cmp_ino() function.
It would be a programming error to let any other than extf_stream_class
point to extf_cmp_ino(). This fallback endangers transitivity of
iso_stream_cmp_ino().
*/
if (s1->class != &extf_stream_class || s2->class != &extf_stream_class)
return iso_stream_cmp_ino(s1, s2, 1);
data1 = (ExternalFilterStreamData*) s1->data;
data2 = (ExternalFilterStreamData*) s2->data;
if (data1->cmd != data2->cmd)
return (data1->cmd < data2->cmd ? -1 : 1);
cmd1 = data1->cmd;
cmd2 = data2->cmd;
if (cmd1 != cmd2) {
if (strcmp(cmd1->name, cmd2->name) != 0)
return strcmp(cmd1->name, cmd2->name);
if (strcmp(cmd1->path, cmd2->path) != 0)
return strcmp(cmd1->path, cmd2->path);
if (cmd1->argc != cmd2->argc)
return cmd1->argc < cmd2->argc ? -1 : 1;
for (i = 0; i < cmd1->argc; i++) {
if (strcmp(cmd1->argv[i], cmd2->argv[i]) != 0)
return strcmp(cmd1->argv[i], cmd2->argv[i]);
}
if (cmd1->behavior != cmd2->behavior)
return cmd1->behavior < cmd2->behavior ? -1 : 1;
if (strcmp(cmd1->suffix, cmd2->suffix) != 0)
return strcmp(cmd1->suffix, cmd2->suffix);
}
/* Both streams apply the same treatment to their input streams */
return iso_stream_cmp_ino(data1->orig, data2->orig, 0);
}

View File

@ -573,6 +573,9 @@ int gzip_clone_stream(IsoStream *old_stream, IsoStream **new_stream, int flag)
static
int gzip_cmp_ino(IsoStream *s1, IsoStream *s2);
static
int gzip_uncompress_cmp_ino(IsoStream *s1, IsoStream *s2);
IsoStreamIface gzip_stream_compress_class = {
4,
@ -603,17 +606,44 @@ IsoStreamIface gzip_stream_uncompress_class = {
gzip_stream_free,
gzip_update_size,
gzip_get_input_stream,
gzip_cmp_ino,
gzip_uncompress_cmp_ino,
gzip_clone_stream
};
static
int gzip_cmp_ino(IsoStream *s1, IsoStream *s2)
{
/* This function may rely on being called by iso_stream_cmp_ino()
only with s1, s2 which both point to it as their .cmp_ino() function.
It would be a programming error to let any other than
gzip_stream_compress_class point to gzip_cmp_ino().
This fallback endangers transitivity of iso_stream_cmp_ino().
*/
if (s1->class != s2->class || (s1->class != &gzip_stream_compress_class &&
s2->class != &gzip_stream_compress_class))
return iso_stream_cmp_ino(s1, s2, 1);
/* Both streams apply the same treatment to their input streams */
return iso_stream_cmp_ino(iso_stream_get_input_stream(s1, 0),
iso_stream_get_input_stream(s2, 0), 0);
}
static
int gzip_uncompress_cmp_ino(IsoStream *s1, IsoStream *s2)
{
/* This function may rely on being called by iso_stream_cmp_ino()
only with s1, s2 which both point to it as their .cmp_ino() function.
It would be a programming error to let any other than
gzip_stream_uncompress_class point to gzip_uncompress_cmp_ino().
*/
if (s1->class != s2->class ||
(s1->class != &gzip_stream_uncompress_class &&
s2->class != &gzip_stream_uncompress_class))
return iso_stream_cmp_ino(s1, s2, 1);
/* Both streams apply the same treatment to their input streams */
return iso_stream_cmp_ino(iso_stream_get_input_stream(s1, 0),
iso_stream_get_input_stream(s2, 0), 0);
}

View File

@ -838,6 +838,9 @@ no_mem:
static
int ziso_cmp_ino(IsoStream *s1, IsoStream *s2);
static
int ziso_uncompress_cmp_ino(IsoStream *s1, IsoStream *s2);
IsoStreamIface ziso_stream_compress_class = {
4,
@ -868,7 +871,7 @@ IsoStreamIface ziso_stream_uncompress_class = {
ziso_stream_free,
ziso_update_size,
ziso_get_input_stream,
ziso_cmp_ino,
ziso_uncompress_cmp_ino,
ziso_clone_stream
};
@ -876,9 +879,36 @@ IsoStreamIface ziso_stream_uncompress_class = {
static
int ziso_cmp_ino(IsoStream *s1, IsoStream *s2)
{
/* This function may rely on being called by iso_stream_cmp_ino()
only with s1, s2 which both point to it as their .cmp_ino() function.
It would be a programming error to let any other than
ziso_stream_compress_class point to ziso_cmp_ino().
*/
if (s1->class != s2->class || (s1->class != &ziso_stream_compress_class &&
s2->class != &ziso_stream_uncompress_class))
iso_stream_cmp_ino(s1, s2, 1);
/* Both streams apply the same treatment to their input streams */
return iso_stream_cmp_ino(iso_stream_get_input_stream(s1, 0),
iso_stream_get_input_stream(s2, 0), 0);
}
static
int ziso_uncompress_cmp_ino(IsoStream *s1, IsoStream *s2)
{
/* This function may rely on being called by iso_stream_cmp_ino()
only with s1, s2 which both point to it as their .cmp_ino() function.
It would be a programming error to let any other than
ziso_stream_uncompress_class point to ziso_uncompress_cmp_ino().
This fallback endangers transitivity of iso_stream_cmp_ino().
*/
if (s1->class != s2->class ||
(s1->class != &ziso_stream_uncompress_class &&
s2->class != &ziso_stream_uncompress_class))
iso_stream_cmp_ino(s1, s2, 1);
/* Both streams apply the same treatment to their input streams */
return iso_stream_cmp_ino(iso_stream_get_input_stream(s1, 0),
iso_stream_get_input_stream(s2, 0), 0);
}

View File

@ -34,6 +34,7 @@
#include <langinfo.h>
#include <limits.h>
#include <stdio.h>
#include <ctype.h>
/* Enable this and write the correct absolute path into the include statement
@ -1439,6 +1440,18 @@ int iso_file_source_new_ifs(IsoImageFilesystem *fs, IsoFileSource *parent,
memset(&atts, 0, sizeof(struct stat));
atts.st_nlink = 1;
#ifdef Libisofs_for_bsd_inst_isoS
/* >>> ??? see read_rr_TF : shall libisofs follow a Linux inconsistency ? */
/* Set preliminary file type */
if (record->flags[0] & 0x02) {
atts.st_mode = S_IFDIR;
} else {
atts.st_mode = S_IFREG;
}
#endif /* Libisofs_for_bsd_inst_isoS */
/*
* First of all, check for unsupported ECMA-119 features
*/
@ -1915,6 +1928,18 @@ if (name != NULL && !namecont) {
name[len-2] = '\0';
}
}
#ifdef Libisofs_for_bsd_inst_isoS
{ char *cpt;
for (cpt = name; *cpt != 0; cpt++)
if (isupper(*cpt))
*cpt = tolower(*cpt);
}
#endif /* Libisofs_for_bsd_inst_isoS */
}
}
@ -6320,35 +6345,76 @@ int iso_file_get_old_image_sections(IsoFile *file, int *section_count,
return 0;
}
/* Rank two IsoFileSource by their eventual old image LBAs.
Other IsoFileSource classes will be ranked only roughly.
/* Rank two IsoFileSource by their eventual old image LBAs if still non-zero.
Other IsoFileSource classes and zeroized LBAs will be ranked only roughly.
flag bit0 preserves transitivity of the caller by evaluating ifs_class with
non-zero block address as smaller than anything else.
flag bit1 could harm reproducibility of ISO image output.
@param flag bit0= if s1 exor s2 is of applicable class, then enforce
a valid test result by comparing classes
bit1= if both are applicable but also have sections[].block == 0
then enforce a valid test result by comparing object addresses.
*/
int iso_ifs_sections_cmp(IsoFileSource *s1, IsoFileSource *s2, int *cmp_ret,
int flag)
{
int i;
ImageFileSourceData *d1, *d2;
ImageFileSourceData *d1 = NULL, *d2 = NULL;
IsoFileSourceIface *class1 = NULL, *class2 = NULL;
if (s1->class != s2->class) {
*cmp_ret = (s1->class < s2->class ? -1 : 1);
return 0;
/* Newly created IsoFileSrc from imported IsoFile (e.g. boot image)
is not an applicable source. It must be kept from causing a decision
with other non-applicables.
*/
if (s1 != NULL) {
class1 = (IsoFileSourceIface *) s1->class;
if (class1 == &ifs_class) {
d1 = (ImageFileSourceData *) s1->data;
if (d1->nsections > 0)
if (d1->sections[0].block == 0)
class1 = NULL;
}
}
if (s1->class != &ifs_class) {
if (s2 != NULL) {
class2 = (IsoFileSourceIface *) s2->class;
if (class2 == &ifs_class) {
d2 = (ImageFileSourceData *) s2->data;
if (d2->nsections > 0)
if (d2->sections[0].block == 0)
class2 = NULL;
}
}
if (class1 != &ifs_class && class2 != &ifs_class) {
*cmp_ret = 0;
return 0;
}
d1 = s1->data;
d2 = s2->data;
if (d1->nsections < 1)
return 0;
if (d1->sections[0].size < 1)
return 0;
for (i = 0; i < d1->nsections; i++) {
if (i >= d2->nsections) {
*cmp_ret = 1;
if (class1 != class2) {
*cmp_ret = (class1 == &ifs_class ? -1 : 1);
if (flag & 1)
return 1;
}
return 0;
}
if (d1->nsections != d2->nsections) {
*cmp_ret = d1->nsections < d2->nsections ? -1 : 1;
return 1;
}
if (d1->nsections == 0) {
*cmp_ret = 0;
return 1;
}
if (d1->sections[0].size < 1 || d2->sections[0].size < 1) {
if (d1->sections[0].size > d2->sections[0].size)
*cmp_ret = 1;
else if (d1->sections[0].size < d2->sections[0].size)
*cmp_ret = -1;
else
*cmp_ret = 0;
return 1;
}
for (i = 0; i < d1->nsections; i++) {
if (d1->sections[i].block != d2->sections[i].block) {
*cmp_ret = (d1->sections[i].block < d2->sections[i].block ? -1 : 1);
return 1;
@ -6358,10 +6424,6 @@ int iso_ifs_sections_cmp(IsoFileSource *s1, IsoFileSource *s2, int *cmp_ret,
return 1;
}
}
if (i < d2->nsections) {
*cmp_ret = -1;
return 1;
}
*cmp_ret = 0;
return 1;
}

View File

@ -985,7 +985,7 @@ struct IsoStream_Iface
* get_input_stream() added.
* A filter stream must have version 2 at least.
* Version 3 (since 0.6.20)
* compare() added.
* cmp_ino() added.
* A filter stream should have version 3 at least.
* Version 4 (since 1.0.2)
* clone_stream() added.
@ -1107,11 +1107,15 @@ struct IsoStream_Iface
* produce the same output. If in any doubt, then this comparison should
* indicate no match. A match might allow hardlinking of IsoFile objects.
*
* If this function cannot accept one of the given stream types, then
* the decision must be delegated to
* iso_stream_cmp_ino(s1, s2, 1);
* This is also appropriate if one has reason to implement stream.cmp_ino()
* without having an own special comparison algorithm.
* A pointer value of NULL is permissible. In this case, function
* iso_stream_cmp_ino() will decide on its own.
*
* If not NULL, this function .cmp_ino() will be called by
* iso_stream_cmp_ino() if both compared streams point to it, and if not
* flag bit0 of iso_stream_cmp_ino() prevents it.
* So a .cmp_ino() function must be able to compare any pair of streams
* which name it as their .cmp_ino(). A fallback to iso_stream_cmp_ino(,,1)
* would endanger transitivity of iso_stream_cmp_ino(,,0).
*
* With filter streams, the decision whether the underlying chains of
* streams match, should be delegated to
@ -1123,16 +1127,9 @@ struct IsoStream_Iface
* cmp_ino(A,A) == 0
* cmp_ino(A,B) == -cmp_ino(B,A)
* if cmp_ino(A,B) == 0 && cmp_ino(B,C) == 0 then cmp_ino(A,C) == 0
* Most tricky is the demand for transitivity:
* if cmp_ino(A,B) < 0 && cmp_ino(B,C) < 0 then cmp_ino(A,C) < 0
*
* A big hazard to the last constraint are tests which do not apply to some
* types of streams.Thus it is mandatory to let iso_stream_cmp_ino(s1,s2,1)
* decide in this case.
*
* A function s1.(*cmp_ino)() must only accept stream s2 if function
* s2.(*cmp_ino)() would accept s1. Best is to accept only the own stream
* type or to have the same function for a family of similar stream types.
*
* @param s1
* The first stream to compare. Expect foreign stream types.
* @param s2
@ -6639,9 +6636,7 @@ char *iso_stream_get_source_path(IsoStream *stream, int flag);
* @return
* -1 if s1 is smaller s2 , 0 if s1 matches s2 , 1 if s1 is larger s2
* @param flag
* bit0= do not use s1->class->compare() even if available
* (e.g. because iso_stream_cmp_ino(0 is called as fallback
* from said stream->class->compare())
* bit0= do not use s1->class->cmp_ino() even if available
*
* @since 0.6.20
*/
@ -8313,6 +8308,10 @@ int iso_conv_name_chars(IsoWriteOpts *opts, char *name, size_t name_len,
(WARNING, HIGH, -408) */
#define ISO_INTVL_READ_PROBLEM 0xD030FE68
/** Cannot arrange content of data files in surely reproducible way
(NOTE, HIGH, -409) */
#define ISO_NOT_REPRODUCIBLE 0xB030FE67
/* Internal developer note:
Place new error codes directly above this comment.
@ -8510,5 +8509,14 @@ struct burn_source {
#define Libisofs_with_rrip_rR yes
*/
/* Experiment : bring representation of BSD installation ISOs near to
their representation by the Linux kernel.
Rock Ridge TF has ctime in CREATE rather than ATTRIBUTES.
Linux accepts this, but not for directories.
Some files only have ECMA-119 names, which Linux maps
to lowercase.
#define Libisofs_for_bsd_inst_isoS yes
*/
#endif /*LIBISO_LIBISOFS_H_*/

View File

@ -38,6 +38,7 @@
#include "util.h"
#include "node.h"
#include "stream.h"
/*
@ -232,6 +233,7 @@ void iso_finish()
{
libiso_msgs_destroy(&libiso_msgr, 0);
iso_node_xinfo_dispose_cloners(0);
iso_stream_destroy_cmpranks(0);
}
int iso_set_abort_severity(char *severity)
@ -527,6 +529,8 @@ const char *iso_error_to_msg(int errcode)
return "Malformed description string for interval reader";
case ISO_INTVL_READ_PROBLEM:
return "Unreadable file, premature EOF, or failure to seek for interval reader";
case ISO_NOT_REPRODUCIBLE:
return "Cannot arrange content of data files in surely reproducible way";
default:
return "Unknown error";
}

View File

@ -211,10 +211,29 @@ int read_rr_TF(struct susp_sys_user_entry *tf, struct stat *st)
/* 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!! */
#ifdef Libisofs_for_bsd_inst_isoS
/* FreeBSD installation ISOs represent ctime by Creation time rather
* than by Attributes time. If both are given, then Attribute time
* will win. Linux 3.16 does not do this for directories.
*/
/* >>> ??? shall libisofs follow a Linux inconsistency ? */
if ((st->st_mode & S_IFMT) != S_IFDIR) {
if (tf->len_sue[0] < 5 + (nts+1) * s) {
/* RR TF entry too short. */
return ISO_WRONG_RR;
}
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;
}
#endif /* Libisofs_for_bsd_inst_isoS */
++nts;
}

View File

@ -164,15 +164,6 @@ IsoStream *fsrc_get_input_stream(IsoStream *stream, int flag)
return NULL;
}
static
int fsrc_cmp_ino(IsoStream *s1, IsoStream *s2)
{
int ret;
ret = iso_stream_cmp_ino(s1, s2, 1);
return ret;
}
int fsrc_clone_stream(IsoStream *old_stream, IsoStream **new_stream,
int flag)
{
@ -227,7 +218,7 @@ IsoStreamIface fsrc_stream_class = {
fsrc_free,
fsrc_update_size,
fsrc_get_input_stream,
fsrc_cmp_ino,
NULL,
fsrc_clone_stream
};
@ -448,15 +439,6 @@ IsoStream* cut_out_get_input_stream(IsoStream *stream, int flag)
return NULL;
}
static
int cut_out_cmp_ino(IsoStream *s1, IsoStream *s2)
{
int ret;
ret = iso_stream_cmp_ino(s1, s2, 1);
return ret;
}
static
int cut_out_clone_stream(IsoStream *old_stream, IsoStream **new_stream,
int flag)
@ -517,7 +499,7 @@ IsoStreamIface cut_out_stream_class = {
cut_out_free,
cut_out_update_size,
cut_out_get_input_stream,
cut_out_cmp_ino,
NULL,
cut_out_clone_stream
};
@ -698,15 +680,6 @@ IsoStream* mem_get_input_stream(IsoStream *stream, int flag)
return NULL;
}
static
int mem_cmp_ino(IsoStream *s1, IsoStream *s2)
{
int ret;
ret = iso_stream_cmp_ino(s1, s2, 1);
return ret;
}
static
int mem_clone_stream(IsoStream *old_stream, IsoStream **new_stream,
int flag)
@ -763,7 +736,7 @@ IsoStreamIface mem_stream_class = {
mem_free,
mem_update_size,
mem_get_input_stream,
mem_cmp_ino,
NULL,
mem_clone_stream
};
@ -972,18 +945,99 @@ int iso_stream_cmp_ifs_sections(IsoStream *s1, IsoStream *s2, int *cmp_ret,
{
int ret;
FSrcStreamData *fssd1, *fssd2;
IsoFileSource *src1, *src2;
if (s1->class != &fsrc_stream_class || s2->class != &fsrc_stream_class)
/* Must keep any suspect in the game to preserve transitivity of the
calling function by ranking applicable streams lower than
non-applicable. ones.
*/
if (s1->class != &fsrc_stream_class && s2->class != &fsrc_stream_class)
return 0;
/* Compare eventual image data section LBA and sizes */
fssd1= (FSrcStreamData *) s1->data;
fssd2= (FSrcStreamData *) s2->data;
ret = iso_ifs_sections_cmp(fssd1->src, fssd2->src, cmp_ret, 0);
if (s1->class == &fsrc_stream_class) {
fssd1= (FSrcStreamData *) s1->data;
src1 = fssd1->src;
} else {
src1 = NULL;
}
if (s2->class == &fsrc_stream_class) {
fssd2= (FSrcStreamData *) s2->data;
src2 = fssd2->src;
} else {
src2 = NULL;
}
ret = iso_ifs_sections_cmp(src1, src2, cmp_ret, 1);
if (ret <= 0)
return 0;
return 1;
}
/* Maintain and exploit a list of stream compare functions seen by
iso_stream_cmp_ino(). This is needed to separate stream comparison
families in order to keep iso_stream_cmp_ino() transitive while
alternative stream->class->cmp_ino() decide inside the families.
*/
struct iso_streamcmprank {
int (*cmp_func)(IsoStream *s1, IsoStream *s2);
struct iso_streamcmprank *next;
};
static struct iso_streamcmprank *streamcmpranks = NULL;
static
int iso_get_streamcmprank(int (*cmp_func)(IsoStream *s1, IsoStream *s2),
int flag)
{
int idx;
struct iso_streamcmprank *cpr, *last_cpr = NULL;
idx = 0;
for (cpr = streamcmpranks; cpr != NULL; cpr = cpr->next) {
if (cpr->cmp_func == cmp_func)
break;
idx++;
last_cpr = cpr;
}
if (cpr != NULL)
return idx;
LIBISO_ALLOC_MEM_VOID(cpr, struct iso_streamcmprank, 1);
cpr->cmp_func = cmp_func;
cpr->next = NULL;
if (last_cpr != NULL)
last_cpr->next = cpr;
if (streamcmpranks == NULL)
streamcmpranks = cpr;
return idx;
ex:;
return -1;
}
static
int iso_cmp_streamcmpranks(int (*cf1)(IsoStream *s1, IsoStream *s2),
int (*cf2)(IsoStream *s1, IsoStream *s2))
{
int rank1, rank2;
rank1 = iso_get_streamcmprank(cf1, 0);
rank2 = iso_get_streamcmprank(cf2, 0);
return rank1 < rank2 ? -1 : 1;
}
int iso_stream_destroy_cmpranks(int flag)
{
struct iso_streamcmprank *cpr, *next;
for (cpr = streamcmpranks; cpr != NULL; cpr = next) {
next = cpr->next;
LIBISO_FREE_MEM(cpr);
}
streamcmpranks = NULL;
return ISO_SUCCESS;
}
/* API */
int iso_stream_cmp_ino(IsoStream *s1, IsoStream *s2, int flag)
{
@ -1008,13 +1062,72 @@ int iso_stream_cmp_ino(IsoStream *s1, IsoStream *s2, int flag)
if (s2 == NULL)
return 1;
/* This stays transitive by the fact that
iso_stream_cmp_ifs_sections() is transitive,
returns > 0 if s1 or s2 are applicable,
ret is -1 if s1 is applicable but s2 is not,
ret is 1 if s1 is not applicable but s2 is.
Proof:
Be A the set of applicable streams, S and G transitive and
antisymmetric relations in respect to outcome {-1, 0, 1}.
The combined relation R shall be defined by
I. R(a,b) = S(a,b) if a in A or b in A, else G(a,b)
Further S shall have the property
II. S(a,b) = -1 if a in A and b not in A
Then R can be proven to be transitive:
By enumerating the 8 combinations of a,b,c being in A or not, we get
5 cases of pure S or pure G. Three cases are mixed:
a,b not in A, c in A : G(a,b) == -1, S(b,c) == -1 -> S(a,c) == -1
Impossible because S(b,c) == -1 contradicts II.
a,c not in A, b in A : S(a,b) == -1, S(b,c) == -1 -> G(a,c) == -1
Impossible because S(a,b) == -1 contradicts II.
b,c not in A, a in A : S(a,b) == -1, G(b,c) == -1 -> S(a,c) == -1
Always true because S(a,c) == -1 by definition II.
*/
if (iso_stream_cmp_ifs_sections(s1, s2, &ret, 0) > 0)
return ret; /* Both are unfiltered from loaded ISO filesystem */
if (s1->class->version >= 3 && !(flag & 1)) {
/* Filters may have smarter methods to compare themselves with others */
ret = s1->class->cmp_ino(s1, s2);
return ret;
if (!(flag & 1)) {
/* Filters may have smarter methods to compare themselves with others.
Transitivity is ensured by ranking mixed pairs by the rank of their
comparison functions, and by ranking streams with .cmp_ino lower
than streams without.
(One could merge (class->version < 3) and (cmp_ino == NULL).)
Here we define S for "and" rather than "or"
I. R(a,b) = S(a,b) if a in A and b in A, else G(a,b)
and the function ranking in case of "exor" makes sure that
II. G(a,b) = -1 if a in A and b not in A
Again we get three mixed cases:
a not in A, b,c in A : G(a,b) == -1, S(b,c) == -1 -> G(a,c) == -1
Impossible because G(a,b) == -1 contradicts II.
b not in A, a,c in A : G(a,b) == -1, G(b,c) == -1 -> S(a,c) == -1
Impossible because G(b,c) == -1 contradicts II.
c not in A, a,b in A : S(a,b) == -1, G(b,c) == -1 -> G(a,c) == -1
Always true because G(a,c) == -1 by definition II.
*/
if ((s1->class->version >= 3) ^ (s2->class->version >= 3)) {
/* One of both has no own com_ino function. Rank it as larger. */
return s1->class->version >= 3 ? -1 : 1;
} else if (s1->class->version >= 3) {
if (s1->class->cmp_ino == s2->class->cmp_ino) {
if (s1->class->cmp_ino == NULL) {
/* Both are NULL. No decision by .cmp_ino(). */;
} else {
/* Both are compared by the same function */
ret = s1->class->cmp_ino(s1, s2);
return ret;
}
} else {
/* Not the same cmp_ino() function. Decide by list rank of
function while building the list on the fly.
*/
ret = iso_cmp_streamcmpranks(s1->class->cmp_ino,
s2->class->cmp_ino);
return ret;
}
}
}
iso_stream_get_id(s1, &fs_id1, &dev_id1, &ino_id1);

View File

@ -112,4 +112,13 @@ int iso_stream_clone_filter_common(IsoStream *old_stream,
IsoStream **new_stream,
IsoStream **new_input, int flag);
/**
* Dispose the internal list of stream class cmp_ino() functions. It is
* a static global of stream.c, created and used by iso_stream_cmp_ino().
* This function is supposed to be called by iso_finish() only.
*/
int iso_stream_destroy_cmpranks(int flag);
#endif /*STREAM_H_*/

View File

@ -407,6 +407,12 @@ size_t iso_rbtree_get_size(IsoRBTree *tree);
void **iso_rbtree_to_array(IsoRBTree *tree, int (*include_item)(void *),
size_t *size);
/** Predict the size of the array which gets returned by iso_rbtree_to_array().
*/
size_t iso_rbtree_count_array(IsoRBTree *tree, size_t initial_count,
int (*include_item)(void *));
/**
* Create a new hash table.
*

View File

@ -308,3 +308,38 @@ void ** iso_rbtree_to_array(IsoRBTree *tree, int (*include_item)(void *),
return array;
}
static
size_t rbtree_count_array_aux(struct iso_rbnode *root, size_t pos,
int (*include_item)(void *))
{
if (root == NULL) {
return pos;
}
pos = rbtree_count_array_aux(root->ch[0], pos, include_item);
if (include_item == NULL || include_item(root->data)) {
/*
{
IsoFileSrc* src = (IsoFileSrc*) root->data;
fprintf(stderr, "libisofs_DEBUG: rbtree_count_array_aux : not taken : '%s'\n",
iso_stream_get_source_path(src->stream, 0));
}
*/
pos++;
}
pos = rbtree_count_array_aux(root->ch[1], pos, include_item);
return pos;
}
size_t iso_rbtree_count_array(IsoRBTree *tree, size_t initial_count,
int (*include_item)(void *))
{
size_t pos;
pos = rbtree_count_array_aux(tree->root, initial_count, include_item);
return pos;
}