diff --git a/libisofs/ecma119.c b/libisofs/ecma119.c index 992e4dd..44763bd 100644 --- a/libisofs/ecma119.c +++ b/libisofs/ecma119.c @@ -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; +} + diff --git a/libisofs/ecma119.h b/libisofs/ecma119.h index a7b514d..c31d42e 100644 --- a/libisofs/ecma119.h +++ b/libisofs/ecma119.h @@ -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; diff --git a/libisofs/ecma119_tree.c b/libisofs/ecma119_tree.c index ec4c9d1..9e8ee52 100644 --- a/libisofs/ecma119_tree.c +++ b/libisofs/ecma119_tree.c @@ -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); } diff --git a/libisofs/ecma119_tree.h b/libisofs/ecma119_tree.h index d90cca8..6dba44b 100644 --- a/libisofs/ecma119_tree.h +++ b/libisofs/ecma119_tree.h @@ -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); + + /** * */ diff --git a/libisofs/filesrc.c b/libisofs/filesrc.c index a62a3d7..7213f29 100644 --- a/libisofs/filesrc.c +++ b/libisofs/filesrc.c @@ -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; } diff --git a/libisofs/filesrc.h b/libisofs/filesrc.h index 6e77ab9..5dbb742 100644 --- a/libisofs/filesrc.h +++ b/libisofs/filesrc.h @@ -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 */ diff --git a/libisofs/filters/external.c b/libisofs/filters/external.c index 85226ee..8975263 100644 --- a/libisofs/filters/external.c +++ b/libisofs/filters/external.c @@ -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); } diff --git a/libisofs/filters/gzip.c b/libisofs/filters/gzip.c index 69aa6a1..c30df0e 100644 --- a/libisofs/filters/gzip.c +++ b/libisofs/filters/gzip.c @@ -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); } diff --git a/libisofs/filters/zisofs.c b/libisofs/filters/zisofs.c index b4e4793..b3b6584 100644 --- a/libisofs/filters/zisofs.c +++ b/libisofs/filters/zisofs.c @@ -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); } diff --git a/libisofs/fs_image.c b/libisofs/fs_image.c index 3023d33..2724f61 100644 --- a/libisofs/fs_image.c +++ b/libisofs/fs_image.c @@ -34,6 +34,7 @@ #include #include #include +#include /* 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; } diff --git a/libisofs/libisofs.h b/libisofs/libisofs.h index ffe3cb1..c6c35f4 100644 --- a/libisofs/libisofs.h +++ b/libisofs/libisofs.h @@ -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_*/ diff --git a/libisofs/messages.c b/libisofs/messages.c index 3910623..78921b1 100644 --- a/libisofs/messages.c +++ b/libisofs/messages.c @@ -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"; } diff --git a/libisofs/rockridge_read.c b/libisofs/rockridge_read.c index 80db25e..a9f8290 100644 --- a/libisofs/rockridge_read.c +++ b/libisofs/rockridge_read.c @@ -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; } diff --git a/libisofs/stream.c b/libisofs/stream.c index aca704b..ff23595 100644 --- a/libisofs/stream.c +++ b/libisofs/stream.c @@ -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); diff --git a/libisofs/stream.h b/libisofs/stream.h index 913bf86..90d9ba9 100644 --- a/libisofs/stream.h +++ b/libisofs/stream.h @@ -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_*/ diff --git a/libisofs/util.h b/libisofs/util.h index e70b5e4..938d4ad 100644 --- a/libisofs/util.h +++ b/libisofs/util.h @@ -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. * diff --git a/libisofs/util_rbtree.c b/libisofs/util_rbtree.c index 0c18339..059bab1 100644 --- a/libisofs/util_rbtree.c +++ b/libisofs/util_rbtree.c @@ -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; +} +