"Fossies" - the Fresh Open Source Software Archive

Member "libisofs-1.5.4/libisofs/stream.c" (14 Oct 2020, 32557 Bytes) of package /linux/misc/libisofs-1.5.4.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "stream.c" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 1.5.2_vs_1.5.4.

    1 /*
    2  * Copyright (c) 2007 Vreixo Formoso
    3  * Copyright (c) 2009 - 2015 Thomas Schmitt
    4  *
    5  * This file is part of the libisofs project; you can redistribute it and/or
    6  * modify it under the terms of the GNU General Public License version 2 
    7  * or later as published by the Free Software Foundation. 
    8  * See COPYING file for details.
    9  */
   10 
   11 #ifdef HAVE_CONFIG_H
   12 #include "../config.h"
   13 #endif
   14 
   15 #include "libisofs.h"
   16 #include "stream.h"
   17 #include "fsource.h"
   18 #include "util.h"
   19 #include "node.h"
   20 
   21 #include <stdlib.h>
   22 #include <string.h>
   23 #include <limits.h>
   24 #include <stdio.h>
   25 
   26 
   27 #ifndef PATH_MAX
   28 #define PATH_MAX Libisofs_default_path_maX
   29 #endif
   30 
   31 
   32 ino_t serial_id = (ino_t)1;
   33 ino_t mem_serial_id = (ino_t)1;
   34 ino_t cut_out_serial_id = (ino_t)1;
   35 
   36 static
   37 int fsrc_open(IsoStream *stream)
   38 {
   39     int ret;
   40     struct stat info;
   41     off_t esize;
   42     IsoFileSource *src;
   43     if (stream == NULL) {
   44         return ISO_NULL_POINTER;
   45     }
   46     src = ((FSrcStreamData*)stream->data)->src;
   47     ret = iso_file_source_stat(src, &info);
   48     if (ret < 0) {
   49         return ret;
   50     }
   51     ret = iso_file_source_open(src);
   52     if (ret < 0) {
   53         return ret;
   54     }
   55     esize = ((FSrcStreamData*)stream->data)->size;
   56     if (info.st_size == esize) {
   57         return ISO_SUCCESS;
   58     } else {
   59         return (esize > info.st_size) ? 3 : 2;
   60     }
   61 }
   62 
   63 static
   64 int fsrc_close(IsoStream *stream)
   65 {
   66     IsoFileSource *src;
   67     if (stream == NULL) {
   68         return ISO_NULL_POINTER;
   69     }
   70     src = ((FSrcStreamData*)stream->data)->src;
   71     return iso_file_source_close(src);
   72 }
   73 
   74 static
   75 off_t fsrc_get_size(IsoStream *stream)
   76 {
   77     FSrcStreamData *data;
   78     data = (FSrcStreamData*)stream->data;
   79 
   80     return data->size;
   81 }
   82 
   83 static
   84 int fsrc_read(IsoStream *stream, void *buf, size_t count)
   85 {
   86     IsoFileSource *src;
   87     if (stream == NULL) {
   88         return ISO_NULL_POINTER;
   89     }
   90     src = ((FSrcStreamData*)stream->data)->src;
   91     return iso_file_source_read(src, buf, count);
   92 }
   93 
   94 static
   95 int fsrc_is_repeatable(IsoStream *stream)
   96 {
   97     int ret;
   98     struct stat info;
   99     FSrcStreamData *data;
  100     if (stream == NULL) {
  101         return ISO_NULL_POINTER;
  102     }
  103     data = (FSrcStreamData*)stream->data;
  104 
  105     /* mode is not cached, this function is only useful for filters */
  106     ret = iso_file_source_stat(data->src, &info);
  107     if (ret < 0) {
  108         return ret;
  109     }
  110     if (S_ISREG(info.st_mode) || S_ISBLK(info.st_mode)) {
  111         return 1;
  112     } else {
  113         return 0;
  114     }
  115 }
  116 
  117 static
  118 void fsrc_get_id(IsoStream *stream, unsigned int *fs_id, dev_t *dev_id,
  119                 ino_t *ino_id)
  120 {
  121     FSrcStreamData *data;
  122     IsoFilesystem *fs;
  123 
  124     data = (FSrcStreamData*)stream->data;
  125     fs = iso_file_source_get_filesystem(data->src);
  126 
  127     *fs_id = fs->get_id(fs);
  128     *dev_id = data->dev_id;
  129     *ino_id = data->ino_id;
  130 }
  131 
  132 static
  133 void fsrc_free(IsoStream *stream)
  134 {
  135     FSrcStreamData *data;
  136     data = (FSrcStreamData*)stream->data;
  137     iso_file_source_unref(data->src);
  138     free(data);
  139 }
  140 
  141 static
  142 int fsrc_update_size(IsoStream *stream)
  143 {
  144     int ret;
  145     struct stat info;
  146     IsoFileSource *src;
  147 
  148     if (stream == NULL) {
  149         return ISO_NULL_POINTER;
  150     }
  151     src = ((FSrcStreamData*)stream->data)->src;
  152     ret = iso_file_source_stat(src, &info);
  153     if (ret < 0) {
  154         return ret;
  155     }
  156 
  157     ((FSrcStreamData*)stream->data)->size = info.st_size;
  158     return ISO_SUCCESS;
  159 }
  160 
  161 static 
  162 IsoStream *fsrc_get_input_stream(IsoStream *stream, int flag)
  163 {
  164     return NULL;
  165 }
  166 
  167 int fsrc_clone_stream(IsoStream *old_stream, IsoStream **new_stream,
  168                       int flag)
  169 {
  170     FSrcStreamData *data, *new_data;
  171     IsoStream *stream;
  172     int ret;
  173 
  174     if (flag)
  175         return ISO_STREAM_NO_CLONE; /* unknown option required */
  176 
  177     data = (FSrcStreamData*) old_stream->data;
  178     if (data->src->class->version < 2)
  179         return ISO_STREAM_NO_CLONE; /* No clone_src() method available */
  180 
  181     *new_stream = NULL;
  182     stream = calloc(1, sizeof(IsoStream));
  183     if (stream == NULL)
  184         return ISO_OUT_OF_MEM;
  185     new_data = calloc(1, sizeof(FSrcStreamData));
  186     if (new_data == NULL) {
  187         free((char *) stream);
  188         return ISO_OUT_OF_MEM;
  189     }
  190     *new_stream = stream;
  191     stream->class = old_stream->class;
  192     stream->refcount = 1;
  193     stream->data = new_data;
  194 
  195     ret = data->src->class->clone_src(data->src, &(new_data->src), 0);
  196     if (ret < 0) {
  197         free((char *) stream);
  198         free((char *) new_data);
  199         return ret;
  200     }
  201     new_data->dev_id = data->dev_id;
  202     new_data->ino_id = data->ino_id;
  203     new_data->size = data->size;
  204 
  205     return ISO_SUCCESS;
  206 }
  207 
  208 static
  209 IsoStreamIface fsrc_stream_class = {
  210     4, /* version */
  211     "fsrc",
  212     fsrc_open,
  213     fsrc_close,
  214     fsrc_get_size,
  215     fsrc_read,
  216     fsrc_is_repeatable,
  217     fsrc_get_id,
  218     fsrc_free,
  219     fsrc_update_size,
  220     fsrc_get_input_stream,
  221     NULL,
  222     fsrc_clone_stream
  223 };
  224 
  225 int iso_file_source_stream_new(IsoFileSource *src, IsoStream **stream)
  226 {
  227     int r;
  228     struct stat info;
  229     IsoStream *str;
  230     FSrcStreamData *data;
  231 
  232     if (src == NULL || stream == NULL) {
  233         return ISO_NULL_POINTER;
  234     }
  235 
  236     r = iso_file_source_stat(src, &info);
  237     if (r < 0) {
  238         return r;
  239     }
  240     if (S_ISDIR(info.st_mode)) {
  241         return ISO_FILE_IS_DIR;
  242     }
  243 
  244     /* check for read access to contents */
  245     r = iso_file_source_access(src);
  246     if (r < 0) {
  247         return r;
  248     }
  249 
  250     str = malloc(sizeof(IsoStream));
  251     if (str == NULL) {
  252         return ISO_OUT_OF_MEM;
  253     }
  254     data = malloc(sizeof(FSrcStreamData));
  255     if (data == NULL) {
  256         free(str);
  257         return ISO_OUT_OF_MEM;
  258     }
  259 
  260     /* take the ref to IsoFileSource */
  261     data->src = src;
  262     data->size = info.st_size;
  263 
  264     /* get the id numbers */
  265     {
  266         IsoFilesystem *fs;
  267         unsigned int fs_id;
  268         fs = iso_file_source_get_filesystem(data->src);
  269 
  270         fs_id = fs->get_id(fs);
  271         if (fs_id == 0) {
  272             /*
  273              * the filesystem implementation is unable to provide valid
  274              * st_dev and st_ino fields. Use serial_id.
  275              */
  276             data->dev_id = (dev_t) 0;
  277             data->ino_id = serial_id++;
  278         } else {
  279             data->dev_id = info.st_dev;
  280             data->ino_id = info.st_ino;
  281         }
  282     }
  283 
  284     str->refcount = 1;
  285     str->data = data;
  286     str->class = &fsrc_stream_class;
  287 
  288     *stream = str;
  289     return ISO_SUCCESS;
  290 }
  291 
  292 
  293 int iso_stream_get_src_zf(IsoStream *stream, uint8_t zisofs_algo[2],
  294                           int *header_size_div4, int *block_size_log2,
  295                           uint64_t *uncompressed_size, int flag)
  296 {
  297     int ret;
  298     FSrcStreamData *data;
  299     IsoFileSource *src;
  300 
  301     /* Intimate friendship with libisofs/fs_image.c */
  302     int iso_ifs_source_get_zf(IsoFileSource *src, uint8_t zisofs_algo[2],
  303                               int *header_size_div4, int *block_size_log2,
  304                               uint64_t *uncompressed_size, int flag);
  305 
  306     if (stream->class != &fsrc_stream_class)
  307         return 0;
  308     data = stream->data;
  309     src = data->src;
  310     ret = iso_ifs_source_get_zf(src, zisofs_algo, header_size_div4,
  311                                 block_size_log2, uncompressed_size, 0);
  312     return ret;
  313 }
  314 
  315 
  316 struct cut_out_stream
  317 {
  318     IsoFileSource *src;
  319 
  320     /* key for file identification inside filesystem */
  321     dev_t dev_id;
  322     ino_t ino_id;
  323     off_t offset; /**< offset where read begins */
  324     off_t size; /**< size of this file */
  325     off_t pos; /* position on the file for read */
  326 };
  327 
  328 static
  329 int cut_out_open(IsoStream *stream)
  330 {
  331     int ret;
  332     struct stat info;
  333     IsoFileSource *src;
  334     struct cut_out_stream *data;
  335 
  336     if (stream == NULL) {
  337         return ISO_NULL_POINTER;
  338     }
  339 
  340     data = stream->data;
  341     src = data->src;
  342     ret = iso_file_source_stat(data->src, &info);
  343     if (ret < 0) {
  344         return ret;
  345     }
  346     ret = iso_file_source_open(src);
  347     if (ret < 0) {
  348         return ret;
  349     }
  350 
  351     {
  352         off_t ret;
  353         if (data->offset > info.st_size) {
  354             /* file is smaller than expected */
  355             ret = iso_file_source_lseek(src, info.st_size, 0);
  356         } else {
  357             ret = iso_file_source_lseek(src, data->offset, 0);
  358         }
  359         if (ret < 0) {
  360             return (int) ret;
  361         }
  362     }
  363     data->pos = 0;
  364     if (data->offset + data->size > info.st_size) {
  365         return 3; /* file smaller than expected */
  366     } else {
  367         return ISO_SUCCESS;
  368     }
  369 }
  370 
  371 static
  372 int cut_out_close(IsoStream *stream)
  373 {
  374     IsoFileSource *src;
  375     if (stream == NULL) {
  376         return ISO_NULL_POINTER;
  377     }
  378     src = ((struct cut_out_stream*)stream->data)->src;
  379     return iso_file_source_close(src);
  380 }
  381 
  382 static
  383 off_t cut_out_get_size(IsoStream *stream)
  384 {
  385     struct cut_out_stream *data = stream->data;
  386     return data->size;
  387 }
  388 
  389 static
  390 int cut_out_read(IsoStream *stream, void *buf, size_t count)
  391 {
  392     struct cut_out_stream *data = stream->data;
  393     count = (size_t) MIN((size_t) (data->size - data->pos), count);
  394     if (count == 0) {
  395         return 0;
  396     }
  397     return iso_file_source_read(data->src, buf, count);
  398 }
  399 
  400 static
  401 int cut_out_is_repeatable(IsoStream *stream)
  402 {
  403     /* reg files are always repeatable */
  404     return 1;
  405 }
  406 
  407 static
  408 void cut_out_get_id(IsoStream *stream, unsigned int *fs_id, dev_t *dev_id,
  409                 ino_t *ino_id)
  410 {
  411     FSrcStreamData *data;
  412     IsoFilesystem *fs;
  413 
  414     data = (FSrcStreamData*)stream->data;
  415     fs = iso_file_source_get_filesystem(data->src);
  416 
  417     *fs_id = fs->get_id(fs);
  418     *dev_id = data->dev_id;
  419     *ino_id = data->ino_id;
  420 }
  421 
  422 static
  423 void cut_out_free(IsoStream *stream)
  424 {
  425     struct cut_out_stream *data = stream->data;
  426     iso_file_source_unref(data->src);
  427     free(data);
  428 }
  429 
  430 static
  431 int cut_out_update_size(IsoStream *stream)
  432 {
  433     return ISO_SUCCESS;
  434 }
  435 
  436 static 
  437 IsoStream* cut_out_get_input_stream(IsoStream *stream, int flag)
  438 {
  439     return NULL;
  440 }
  441 
  442 static
  443 int cut_out_clone_stream(IsoStream *old_stream, IsoStream **new_stream,
  444                       int flag)
  445 {
  446     struct cut_out_stream *data, *new_data;
  447     IsoStream *stream;
  448     int ret;
  449 
  450     if (flag)
  451         return ISO_STREAM_NO_CLONE; /* unknown option required */
  452 
  453     data = (struct cut_out_stream *) old_stream->data;
  454     if (data->src->class->version < 2)
  455         return ISO_STREAM_NO_CLONE; /* No clone_src() method available */
  456 
  457     *new_stream = NULL;
  458     stream = calloc(1, sizeof(IsoStream));
  459     if (stream == NULL)
  460         return ISO_OUT_OF_MEM;
  461     stream->refcount = 1;
  462     stream->class = old_stream->class;
  463     new_data = calloc(1, sizeof(struct cut_out_stream));
  464     if (new_data == NULL) {
  465         free((char *) stream);
  466         return ISO_OUT_OF_MEM;
  467     }
  468     ret = data->src->class->clone_src(data->src, &(new_data->src), 0);
  469     if (ret < 0) {
  470         free((char *) stream);
  471         free((char *) new_data);
  472         return ret;
  473     }
  474 
  475     new_data->dev_id = (dev_t) 0;
  476     new_data->ino_id = cut_out_serial_id++;
  477     new_data->offset = data->offset;
  478     new_data->size = data->size;
  479     new_data->pos = 0;
  480 
  481     stream->data = new_data;
  482     *new_stream = stream;
  483     return ISO_SUCCESS;
  484 }
  485 
  486 /*
  487  * TODO update cut out streams to deal with update_size(). Seems hard.
  488  */
  489 static
  490 IsoStreamIface cut_out_stream_class = {
  491     4, /* version */
  492     "cout",
  493     cut_out_open,
  494     cut_out_close,
  495     cut_out_get_size,
  496     cut_out_read,
  497     cut_out_is_repeatable,
  498     cut_out_get_id,
  499     cut_out_free,
  500     cut_out_update_size,
  501     cut_out_get_input_stream,
  502     NULL,
  503     cut_out_clone_stream
  504     
  505 };
  506 
  507 int iso_cut_out_stream_new(IsoFileSource *src, off_t offset, off_t size,
  508                            IsoStream **stream)
  509 {
  510     int r;
  511     struct stat info;
  512     IsoStream *str;
  513     struct cut_out_stream *data;
  514 
  515     if (src == NULL || stream == NULL) {
  516         return ISO_NULL_POINTER;
  517     }
  518     if (size == 0) {
  519         return ISO_WRONG_ARG_VALUE;
  520     }
  521 
  522     r = iso_file_source_stat(src, &info);
  523     if (r < 0) {
  524         return r;
  525     }
  526     if (!S_ISREG(info.st_mode)) {
  527         return ISO_WRONG_ARG_VALUE;
  528     }
  529     if (offset > info.st_size) {
  530         return ISO_FILE_OFFSET_TOO_BIG;
  531     }
  532 
  533     /* check for read access to contents */
  534     r = iso_file_source_access(src);
  535     if (r < 0) {
  536         return r;
  537     }
  538 
  539     str = malloc(sizeof(IsoStream));
  540     if (str == NULL) {
  541         return ISO_OUT_OF_MEM;
  542     }
  543     data = malloc(sizeof(struct cut_out_stream));
  544     if (data == NULL) {
  545         free(str);
  546         return ISO_OUT_OF_MEM;
  547     }
  548 
  549     /* take a new ref to IsoFileSource */
  550     data->src = src;
  551     iso_file_source_ref(src);
  552 
  553     data->offset = offset;
  554     data->size = MIN(info.st_size - offset, size);
  555 
  556     /* get the id numbers */
  557     data->dev_id = (dev_t) 0;
  558     data->ino_id = cut_out_serial_id++;
  559 
  560     str->refcount = 1;
  561     str->data = data;
  562     str->class = &cut_out_stream_class;
  563 
  564     *stream = str;
  565     return ISO_SUCCESS;
  566 }
  567 
  568 
  569 
  570 typedef struct
  571 {
  572     uint8_t *buf;
  573     ssize_t offset; /* -1 if stream closed */
  574     ino_t ino_id;
  575     size_t size;
  576 } MemStreamData;
  577 
  578 static
  579 int mem_open(IsoStream *stream)
  580 {
  581     MemStreamData *data;
  582     if (stream == NULL) {
  583         return ISO_NULL_POINTER;
  584     }
  585     data = (MemStreamData*)stream->data;
  586     if (data->offset != -1) {
  587         return ISO_FILE_ALREADY_OPENED;
  588     }
  589     data->offset = 0;
  590     return ISO_SUCCESS;
  591 }
  592 
  593 static
  594 int mem_close(IsoStream *stream)
  595 {
  596     MemStreamData *data;
  597     if (stream == NULL) {
  598         return ISO_NULL_POINTER;
  599     }
  600     data = (MemStreamData*)stream->data;
  601     if (data->offset == -1) {
  602         return ISO_FILE_NOT_OPENED;
  603     }
  604     data->offset = -1;
  605     return ISO_SUCCESS;
  606 }
  607 
  608 static
  609 off_t mem_get_size(IsoStream *stream)
  610 {
  611     MemStreamData *data;
  612     data = (MemStreamData*)stream->data;
  613 
  614     return (off_t)data->size;
  615 }
  616 
  617 static
  618 int mem_read(IsoStream *stream, void *buf, size_t count)
  619 {
  620     size_t len;
  621     MemStreamData *data;
  622     if (stream == NULL || buf == NULL) {
  623         return ISO_NULL_POINTER;
  624     }
  625     if (count == 0) {
  626         return ISO_WRONG_ARG_VALUE;
  627     }
  628     data = stream->data;
  629 
  630     if (data->offset == -1) {
  631         return ISO_FILE_NOT_OPENED;
  632     }
  633 
  634     if (data->offset >= (ssize_t) data->size) {
  635         return 0; /* EOF */
  636     }
  637 
  638     len = MIN(count, data->size - data->offset);
  639     memcpy(buf, data->buf + data->offset, len);
  640     data->offset += len;
  641     return len;
  642 }
  643 
  644 static
  645 int mem_is_repeatable(IsoStream *stream)
  646 {
  647     return 1;
  648 }
  649 
  650 static
  651 void mem_get_id(IsoStream *stream, unsigned int *fs_id, dev_t *dev_id,
  652                 ino_t *ino_id)
  653 {
  654     MemStreamData *data;
  655     data = (MemStreamData*)stream->data;
  656     *fs_id = ISO_MEM_FS_ID;
  657     *dev_id = 0;
  658     *ino_id = data->ino_id;
  659 }
  660 
  661 static
  662 void mem_free(IsoStream *stream)
  663 {
  664     MemStreamData *data;
  665     data = (MemStreamData*)stream->data;
  666     if (data->buf != NULL)
  667         free(data->buf);
  668     free(data);
  669 }
  670 
  671 static
  672 int mem_update_size(IsoStream *stream)
  673 {
  674     return ISO_SUCCESS;
  675 }
  676 
  677 static 
  678 IsoStream* mem_get_input_stream(IsoStream *stream, int flag)
  679 {
  680     return NULL;
  681 }
  682 
  683 static
  684 int mem_clone_stream(IsoStream *old_stream, IsoStream **new_stream,
  685                       int flag)
  686 {
  687     MemStreamData *data, *new_data;
  688     IsoStream *stream;
  689     uint8_t *new_buf = NULL;
  690 
  691     if (flag)
  692         return ISO_STREAM_NO_CLONE; /* unknown option required */
  693 
  694     *new_stream = NULL;
  695     stream = calloc(1, sizeof(IsoStream));
  696     if (stream == NULL)
  697         return ISO_OUT_OF_MEM;
  698     stream->refcount = 1;
  699     stream->class = old_stream->class;
  700     new_data = calloc(1, sizeof(MemStreamData));
  701     if (new_data == NULL) {
  702         free((char *) stream);
  703         return ISO_OUT_OF_MEM;
  704     }
  705     data = (MemStreamData *) old_stream->data;
  706     if (data->size > 0) {
  707         new_buf = calloc(1, data->size);
  708         if (new_buf == NULL) {
  709             free((char *) stream);
  710             free((char *) new_data);
  711             return ISO_OUT_OF_MEM;
  712         }
  713         memcpy(new_buf, data->buf, data->size);
  714     }
  715     new_data->buf = new_buf;
  716     new_data->offset = -1;
  717     new_data->ino_id = mem_serial_id++;
  718     new_data->size = data->size;
  719 
  720     stream->data = new_data;
  721     *new_stream = stream;
  722     return ISO_SUCCESS;
  723 }
  724 
  725 
  726 static
  727 IsoStreamIface mem_stream_class = {
  728     4, /* version */
  729     "mem ",
  730     mem_open,
  731     mem_close,
  732     mem_get_size,
  733     mem_read,
  734     mem_is_repeatable,
  735     mem_get_id,
  736     mem_free,
  737     mem_update_size,
  738     mem_get_input_stream,
  739     NULL,
  740     mem_clone_stream
  741 
  742 };
  743 
  744 /**
  745  * Create a stream for reading from a arbitrary memory buffer.
  746  * When the Stream refcount reach 0, the buffer is free(3).
  747  *
  748  * @return
  749  *      1 success, < 0 error
  750  */
  751 int iso_memory_stream_new(unsigned char *buf, size_t size, IsoStream **stream)
  752 {
  753     IsoStream *str;
  754     MemStreamData *data;
  755 
  756     if (buf == NULL || stream == NULL) {
  757         return ISO_NULL_POINTER;
  758     }
  759 
  760     str = malloc(sizeof(IsoStream));
  761     if (str == NULL) {
  762         return ISO_OUT_OF_MEM;
  763     }
  764     data = malloc(sizeof(MemStreamData));
  765     if (data == NULL) {
  766         free(str);
  767         return ISO_OUT_OF_MEM;
  768     }
  769 
  770     /* fill data */
  771     data->buf = buf;
  772     data->size = size;
  773     data->offset = -1;
  774     data->ino_id = mem_serial_id++;
  775 
  776     str->refcount = 1;
  777     str->data = data;
  778     str->class = &mem_stream_class;
  779 
  780     *stream = str;
  781     return ISO_SUCCESS;
  782 }
  783 
  784 void iso_stream_ref(IsoStream *stream)
  785 {
  786     ++stream->refcount;
  787 }
  788 
  789 void iso_stream_unref(IsoStream *stream)
  790 {
  791     if (--stream->refcount == 0) {
  792         stream->class->free(stream);
  793         free(stream);
  794     }
  795 }
  796 
  797 inline
  798 int iso_stream_open(IsoStream *stream)
  799 {
  800     return stream->class->open(stream);
  801 }
  802 
  803 inline
  804 int iso_stream_close(IsoStream *stream)
  805 {
  806     return stream->class->close(stream);
  807 }
  808 
  809 inline
  810 off_t iso_stream_get_size(IsoStream *stream)
  811 {
  812     return stream->class->get_size(stream);
  813 }
  814 
  815 inline
  816 int iso_stream_read(IsoStream *stream, void *buf, size_t count)
  817 {
  818     return stream->class->read(stream, buf, count);
  819 }
  820 
  821 inline
  822 int iso_stream_is_repeatable(IsoStream *stream)
  823 {
  824     return stream->class->is_repeatable(stream);
  825 }
  826 
  827 inline
  828 int iso_stream_update_size(IsoStream *stream)
  829 {
  830     IsoStreamIface* class = stream->class;
  831     return (class->version >= 1) ? class->update_size(stream) : 0;
  832 }
  833 
  834 inline
  835 void iso_stream_get_id(IsoStream *stream, unsigned int *fs_id, dev_t *dev_id,
  836                       ino_t *ino_id)
  837 {
  838     stream->class->get_id(stream, fs_id, dev_id, ino_id);
  839 }
  840 
  841 void iso_stream_get_file_name(IsoStream *stream, char *name)
  842 {
  843     char *type = stream->class->type;
  844 
  845     if (!strncmp(type, "fsrc", 4)) {
  846         FSrcStreamData *data = stream->data;
  847         char *path = iso_file_source_get_path(data->src);
  848         if (path == NULL) {
  849             name[0] = 0;
  850             return;
  851         }
  852         strncpy(name, path, PATH_MAX - 1);
  853         name[PATH_MAX - 1] = 0;
  854         free(path);
  855     } else if (!strncmp(type, "boot", 4)) {
  856         strcpy(name, "BOOT CATALOG");
  857     } else if (!strncmp(type, "mem ", 4)) {
  858         strcpy(name, "MEM SOURCE");
  859     } else if (!strncmp(type, "extf", 4)) {
  860         strcpy(name, "EXTERNAL FILTER");
  861     } else {
  862         strcpy(name, "UNKNOWN SOURCE");
  863     }
  864 }
  865 
  866 /* @param flag  bit0= Obtain most fundamental stream */
  867 IsoStream *iso_stream_get_input_stream(IsoStream *stream, int flag)
  868 {
  869     IsoStreamIface* class;
  870     IsoStream *result = NULL, *next;
  871 
  872     if (stream == NULL) {
  873         return NULL;
  874     }
  875     while (1) {
  876         class = stream->class;
  877         if (class->version < 2)
  878             return result;
  879         next = class->get_input_stream(stream, 0);
  880         if (next == NULL)
  881             return result;
  882         result = next;
  883         if (!(flag & 1))
  884             return result;
  885         stream = result;
  886     }
  887 }
  888 
  889 char *iso_stream_get_source_path(IsoStream *stream, int flag)
  890 {
  891     char *path = NULL, ivd[80], *raw_path = NULL;
  892 
  893     if (stream == NULL) {
  894         return NULL;
  895     }
  896     if (stream->class == &fsrc_stream_class) {
  897         FSrcStreamData *fsrc_data = stream->data;
  898 
  899         path = iso_file_source_get_path(fsrc_data->src);
  900     } else if (stream->class == &cut_out_stream_class) {
  901         struct cut_out_stream *cout_data = stream->data;
  902 
  903         raw_path = iso_file_source_get_path(cout_data->src);
  904         sprintf(ivd, " %.f %.f",
  905                 (double) cout_data->offset, (double) cout_data->size);
  906         path= calloc(strlen(raw_path) + strlen(ivd) + 1, 1);
  907         if (path == NULL) {
  908             goto ex;
  909         }
  910         strcpy(path, raw_path);
  911         strcat(path, ivd);
  912     }
  913 ex:;
  914     if (raw_path != NULL)
  915         free(raw_path);
  916     return path;
  917 }
  918 
  919 /*
  920    @param flag bit0= in case of filter stream do not dig for base stream
  921    @return 1 = ok , 0 = not an ISO image stream , <0 = error
  922 */
  923 int iso_stream_set_image_ino(IsoStream *stream, ino_t ino, int flag)
  924 {
  925     IsoStream *base_stream;
  926 
  927     if (stream == NULL) {
  928         return ISO_NULL_POINTER;
  929     }
  930     if (!(flag & 1)) {
  931         base_stream = iso_stream_get_input_stream(stream, 1);
  932         if (base_stream != NULL)
  933             stream = base_stream;
  934     }
  935     if (stream->class == &fsrc_stream_class) {
  936         FSrcStreamData *fsrc_data = stream->data;
  937         fsrc_data->ino_id = ino;
  938         return 1;
  939     }
  940    return 0;
  941 }
  942 
  943 int iso_stream_cmp_ifs_sections(IsoStream *s1, IsoStream *s2, int *cmp_ret,
  944                                 int flag)
  945 {   
  946     int ret;
  947     FSrcStreamData *fssd1, *fssd2;
  948     IsoFileSource *src1, *src2;
  949 
  950     /* Must keep any suspect in the game to preserve transitivity of the
  951        calling function by ranking applicable streams lower than
  952        non-applicable. ones.
  953     */
  954     if (s1->class != &fsrc_stream_class && s2->class != &fsrc_stream_class)
  955         return 0;
  956 
  957     /* Compare eventual image data section LBA and sizes */
  958     if (s1->class == &fsrc_stream_class) {
  959         fssd1= (FSrcStreamData *) s1->data;
  960         src1 = fssd1->src;
  961     } else {
  962         src1 = NULL;
  963     }
  964     if (s2->class == &fsrc_stream_class) {
  965         fssd2= (FSrcStreamData *) s2->data;
  966         src2 = fssd2->src;
  967     } else {
  968         src2 = NULL;
  969     }
  970     ret = iso_ifs_sections_cmp(src1, src2, cmp_ret, 1);
  971     if (ret <= 0)
  972         return 0;
  973     return 1;
  974 }
  975 
  976 
  977 /* Maintain and exploit a list of stream compare functions seen by
  978    iso_stream_cmp_ino(). This is needed to separate stream comparison
  979    families in order to keep iso_stream_cmp_ino() transitive while
  980    alternative stream->class->cmp_ino() decide inside the families.
  981 */
  982 struct iso_streamcmprank {
  983     int (*cmp_func)(IsoStream *s1, IsoStream *s2);
  984     struct iso_streamcmprank *next;
  985 };
  986 
  987 static struct iso_streamcmprank *streamcmpranks = NULL;
  988 
  989 static
  990 int iso_get_streamcmprank(int (*cmp_func)(IsoStream *s1, IsoStream *s2),
  991                           int flag)
  992 {
  993     int idx;
  994     struct iso_streamcmprank *cpr, *last_cpr = NULL;
  995 
  996     idx = 0;
  997     for (cpr = streamcmpranks; cpr != NULL; cpr = cpr->next) {
  998         if (cpr->cmp_func == cmp_func)
  999     break;
 1000         idx++;
 1001         last_cpr = cpr;
 1002     }
 1003     if (cpr != NULL)
 1004         return idx;
 1005     LIBISO_ALLOC_MEM_VOID(cpr, struct iso_streamcmprank, 1);
 1006     cpr->cmp_func = cmp_func;
 1007     cpr->next = NULL;
 1008     if (last_cpr != NULL)
 1009         last_cpr->next = cpr;
 1010     if (streamcmpranks == NULL)
 1011         streamcmpranks = cpr;
 1012     return idx;
 1013 ex:;
 1014     return -1;
 1015 }
 1016 
 1017 static
 1018 int iso_cmp_streamcmpranks(int (*cf1)(IsoStream *s1, IsoStream *s2),
 1019                            int (*cf2)(IsoStream *s1, IsoStream *s2))
 1020 {
 1021     int rank1, rank2;
 1022 
 1023     rank1 = iso_get_streamcmprank(cf1, 0);
 1024     rank2 = iso_get_streamcmprank(cf2, 0);
 1025     return rank1 < rank2 ? -1 : 1;
 1026 }
 1027 
 1028 int iso_stream_destroy_cmpranks(int flag)
 1029 {
 1030     struct iso_streamcmprank *cpr, *next;
 1031 
 1032     for (cpr = streamcmpranks; cpr != NULL; cpr = next) {
 1033         next = cpr->next;
 1034         LIBISO_FREE_MEM(cpr);
 1035     }
 1036     streamcmpranks = NULL;
 1037     return ISO_SUCCESS;
 1038 }
 1039 
 1040 
 1041 /* API */ 
 1042 int iso_stream_cmp_ino(IsoStream *s1, IsoStream *s2, int flag)
 1043 {
 1044     int ret;
 1045     unsigned int fs_id1, fs_id2;
 1046     dev_t dev_id1, dev_id2;
 1047     ino_t ino_id1, ino_id2;
 1048     off_t size1, size2;
 1049 
 1050 /*
 1051    #define Libisofs_stream_cmp_ino_debuG 1
 1052 */
 1053 #ifdef Libisofs_stream_cmp_ino_debuG
 1054     static int report_counter = 0;
 1055     static int debug = 1;
 1056 #endif /* Libisofs_stream_cmp_ino_debuG */
 1057 
 1058     if (s1 == s2)
 1059         return 0;
 1060     if (s1 == NULL)
 1061         return -1;
 1062     if (s2 == NULL)
 1063         return 1;
 1064 
 1065     /* This stays transitive by the fact that 
 1066        iso_stream_cmp_ifs_sections() is transitive,
 1067        returns > 0 if s1 or s2 are applicable,
 1068        ret is -1 if s1 is applicable but s2 is not,
 1069        ret is 1 if s1 is not applicable but s2 is.
 1070 
 1071        Proof:
 1072        Be A the set of applicable streams, S and G transitive and
 1073        antisymmetric relations in respect to outcome {-1, 0, 1}.
 1074        The combined relation R shall be defined by
 1075          I.   R(a,b) = S(a,b) if a in A or b in A, else G(a,b)
 1076        Further S shall have the property
 1077          II.  S(a,b) = -1 if a in A and b not in A
 1078        Then R can be proven to be transitive:
 1079        By enumerating the 8 combinations of a,b,c being in A or not, we get
 1080        5 cases of pure S or pure G. Three cases are mixed:
 1081          a,b not in A, c in A : G(a,b) == -1, S(b,c) == -1 -> S(a,c) == -1 
 1082              Impossible because S(b,c) == -1 contradicts II.
 1083          a,c not in A, b in A : S(a,b) == -1, S(b,c) == -1 -> G(a,c) == -1
 1084              Impossible because S(a,b) == -1 contradicts II.
 1085          b,c not in A, a in A : S(a,b) == -1, G(b,c) == -1 -> S(a,c) == -1
 1086              Always true because S(a,c) == -1 by definition II.
 1087     */
 1088     if (iso_stream_cmp_ifs_sections(s1, s2, &ret, 0) > 0)
 1089         return ret; /* Both are unfiltered from loaded ISO filesystem */
 1090 
 1091     if (!(flag & 1)) {
 1092        /* Filters may have smarter methods to compare themselves with others.
 1093           Transitivity is ensured by ranking mixed pairs by the rank of their
 1094           comparison functions, and by ranking streams with .cmp_ino lower
 1095           than streams without.
 1096           (One could merge (class->version < 3) and (cmp_ino == NULL).)
 1097 
 1098           Here we define S for "and" rather than "or"
 1099             I.   R(a,b) = S(a,b) if a in A and b in A, else G(a,b)
 1100           and the function ranking in case of "exor" makes sure that
 1101             II.  G(a,b) = -1 if a in A and b not in A
 1102           Again we get three mixed cases:
 1103             a not in A, b,c in A : G(a,b) == -1, S(b,c) == -1 -> G(a,c) == -1 
 1104                 Impossible because G(a,b) == -1 contradicts II.
 1105             b not in A, a,c in A : G(a,b) == -1, G(b,c) == -1 -> S(a,c) == -1
 1106                 Impossible because G(b,c) == -1 contradicts II.
 1107             c not in A, a,b in A : S(a,b) == -1, G(b,c) == -1 -> G(a,c) == -1
 1108                 Always true because G(a,c) == -1 by definition II.
 1109        */
 1110        if ((s1->class->version >= 3) ^ (s2->class->version >= 3)) {
 1111            /* One of both has no own com_ino function. Rank it as larger. */
 1112            return s1->class->version >= 3 ? -1 : 1;
 1113        } else if (s1->class->version >= 3) {
 1114            if (s1->class->cmp_ino == s2->class->cmp_ino) {
 1115                if (s1->class->cmp_ino == NULL) {
 1116                    /* Both are NULL. No decision by .cmp_ino(). */;
 1117                } else {
 1118                    /* Both are compared by the same function */
 1119                    ret = s1->class->cmp_ino(s1, s2);
 1120                    return ret;
 1121                }
 1122            } else {
 1123                /* Not the same cmp_ino() function. Decide by list rank of
 1124                   function while building the list on the fly.
 1125                */
 1126                ret = iso_cmp_streamcmpranks(s1->class->cmp_ino,
 1127                                             s2->class->cmp_ino);
 1128                return ret;
 1129            }
 1130        }
 1131     }
 1132 
 1133     iso_stream_get_id(s1, &fs_id1, &dev_id1, &ino_id1);
 1134     iso_stream_get_id(s2, &fs_id2, &dev_id2, &ino_id2);
 1135     if (fs_id1 < fs_id2) {
 1136         return -1;
 1137     } else if (fs_id1 > fs_id2) {
 1138         return 1;
 1139     }
 1140     /* files belong to the same fs */
 1141     if (dev_id1 > dev_id2) {
 1142         return -1;
 1143     } else if (dev_id1 < dev_id2) {
 1144         return 1;
 1145     } else if (ino_id1 < ino_id2) {
 1146         return -1;
 1147     } else if (ino_id1 > ino_id2) {
 1148         return 1;
 1149     }
 1150     size1 = iso_stream_get_size(s1);
 1151     size2 = iso_stream_get_size(s2);
 1152     if (size1 < size2) {
 1153 
 1154 #ifdef Libisofs_stream_cmp_ino_debuG
 1155         if (debug) {
 1156             if (report_counter < 5)
 1157                 fprintf(stderr,
 1158       "\n\nlibisofs_DEBUG : Program error: same ino but differing size\n\n\n");
 1159             else if (report_counter == 5)
 1160                 fprintf(stderr,
 1161       "\n\nlibisofs_DEBUG : Inode error: more of same ino but differing size\n\n\n");
 1162             report_counter++;
 1163         }
 1164 #endif /* Libisofs_stream_cmp_ino_debuG */
 1165 
 1166         return -1;
 1167     } else if (size1 > size2) {
 1168 
 1169 #ifdef Libisofs_stream_cmp_ino_debuG
 1170         if (debug) {
 1171             if (report_counter < 5)
 1172                 fprintf(stderr,
 1173       "\n\nlibisofs_DEBUG : Inode error: same ino but differing size\n\n\n");
 1174             else if (report_counter == 5)
 1175                 fprintf(stderr,
 1176       "\n\nlibisofs_DEBUG : Program error: more of same ino but differing size\n\n\n");
 1177             report_counter++;
 1178         }
 1179 #endif /* Libisofs_stream_cmp_ino_debuG */
 1180 
 1181         return 1;
 1182     }
 1183 
 1184     if (s1->class != s2->class)
 1185         return (s1->class < s2->class ? -1 : 1);
 1186     if (fs_id1 == 0 && dev_id1 == 0 && ino_id1 == 0) {
 1187         return (s1 < s2 ? -1 : 1);
 1188     }
 1189     return 0;
 1190 }
 1191 
 1192 
 1193 /**
 1194  * @return
 1195  *     1 ok, 0 EOF, < 0 error
 1196  */    
 1197 int iso_stream_read_buffer(IsoStream *stream, char *buf, size_t count,
 1198                            size_t *got)
 1199 {
 1200     ssize_t result;
 1201 
 1202     *got = 0;
 1203     do {
 1204         result = iso_stream_read(stream, buf + *got, count - *got);
 1205         if (result < 0) {
 1206             memset(buf + *got, 0, count - *got);
 1207             return result;
 1208         }
 1209         if (result == 0)
 1210             break;
 1211         *got += result;
 1212     } while (*got < count);
 1213 
 1214     if (*got < count) {
 1215         /* eof */
 1216         memset(buf + *got, 0, count - *got);
 1217         return 0;
 1218     }
 1219     return 1;
 1220 }
 1221 
 1222 /* @param flag bit0= dig out most original stream (e.g. because from old image)
 1223    @return 1=ok, md5 is valid,
 1224            0= not ok, 
 1225           <0 fatal error, abort 
 1226 */  
 1227 int iso_stream_make_md5(IsoStream *stream, char md5[16], int flag)
 1228 {
 1229     int ret, is_open = 0;
 1230     char * buffer = NULL;
 1231     void *ctx= NULL;
 1232     off_t file_size;
 1233     uint32_t b, nblocks;
 1234     size_t got_bytes;
 1235     IsoStream *input_stream;
 1236 
 1237     LIBISO_ALLOC_MEM(buffer, char, 2048);
 1238     if (flag & 1) {
 1239         while(1) {
 1240            input_stream = iso_stream_get_input_stream(stream, 0);
 1241            if (input_stream == NULL)
 1242         break;
 1243            stream = input_stream;
 1244         }
 1245     }
 1246 
 1247     if (! iso_stream_is_repeatable(stream))
 1248         {ret = 0; goto ex;}
 1249     ret = iso_md5_start(&ctx);
 1250     if (ret < 0)
 1251         goto ex;
 1252     ret = iso_stream_open(stream);
 1253     if (ret < 0)
 1254         goto ex;
 1255     is_open = 1;
 1256     file_size = iso_stream_get_size(stream);
 1257     nblocks = DIV_UP(file_size, 2048);
 1258     for (b = 0; b < nblocks; ++b) {
 1259         ret = iso_stream_read_buffer(stream, buffer, 2048, &got_bytes);
 1260         if (ret < 0) {
 1261             ret = 0;
 1262             goto ex;
 1263         }
 1264         /* Do not use got_bytes to stay closer to IsoFileSrc processing */
 1265         if (file_size - b * 2048 > 2048)
 1266             ret = 2048;
 1267         else
 1268             ret = file_size - b * 2048;
 1269         iso_md5_compute(ctx, buffer, ret);
 1270     }
 1271     ret = 1;
 1272 ex:;
 1273     if (is_open)
 1274         iso_stream_close(stream);
 1275     if (ctx != NULL)
 1276         iso_md5_end(&ctx, md5);
 1277     LIBISO_FREE_MEM(buffer);
 1278     return ret;
 1279 }
 1280 
 1281 /* API */
 1282 int iso_stream_clone(IsoStream *old_stream, IsoStream **new_stream, int flag)
 1283 {
 1284     int ret;
 1285 
 1286     if (old_stream->class->version < 4)
 1287         return ISO_STREAM_NO_CLONE;
 1288     ret = old_stream->class->clone_stream(old_stream, new_stream, 0);
 1289     return ret;
 1290 }
 1291 
 1292 int iso_stream_clone_filter_common(IsoStream *old_stream,
 1293                                    IsoStream **new_stream,
 1294                                    IsoStream **new_input, int flag)
 1295 {
 1296     IsoStream *stream, *input_stream;
 1297     int ret;
 1298 
 1299     *new_stream = NULL;
 1300     *new_input = NULL;
 1301     input_stream = iso_stream_get_input_stream(old_stream, 0);
 1302     if (input_stream == NULL)
 1303         return ISO_STREAM_NO_CLONE;
 1304     stream = calloc(1, sizeof(IsoStream));
 1305     if (stream == NULL)
 1306         return ISO_OUT_OF_MEM;
 1307     ret = iso_stream_clone(input_stream, new_input, 0);
 1308     if (ret < 0) {
 1309         free((char *) stream);
 1310         return ret;
 1311     }
 1312     stream->class = old_stream->class;
 1313     stream->refcount = 1;
 1314     stream->data = NULL;
 1315     *new_stream = stream;
 1316     return ISO_SUCCESS;
 1317 }
 1318