"Fossies" - the Fresh Open Source Software Archive

Member "xorriso-1.5.4/libisofs/filters/zisofs.c" (30 Jan 2021, 56026 Bytes) of package /linux/misc/xorriso-1.5.4.pl02.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 "zisofs.c" see the Fossies "Dox" file reference documentation and the last Fossies "Diffs" side-by-side code changes report: 1.5.2_vs_1.5.4.

    1 /*
    2  * Copyright (c) 2009 - 2020 Thomas Schmitt
    3  * 
    4  * This file is part of the libisofs project; you can redistribute it and/or 
    5  * modify it under the terms of the GNU General Public License version 2 
    6  * or later as published by the Free Software Foundation. 
    7  * See COPYING file for details.
    8  *
    9  * It implements a filter facility which can pipe a IsoStream into zisofs
   10  * compression resp. uncompression, read its output and forward it as IsoStream
   11  * output to an IsoFile.
   12  * The zisofs format was invented by H. Peter Anvin. See doc/zisofs_format.txt
   13  * It is writeable and readable by zisofs-tools, readable by Linux kernels.
   14  *
   15  */
   16 
   17 #ifdef HAVE_CONFIG_H
   18 #include "../config.h"
   19 #endif
   20 
   21 #include "../libisofs.h"
   22 #include "../filter.h"
   23 #include "../fsource.h"
   24 #include "../util.h"
   25 #include "../stream.h"
   26 #include "../messages.h"
   27 
   28 #include <sys/types.h>
   29 #include <sys/time.h>
   30 #include <sys/wait.h>
   31 #include <unistd.h>
   32 #include <stdio.h>
   33 #include <errno.h>
   34 #include <string.h>
   35 
   36 #ifdef Libisofs_with_zliB
   37 #include <zlib.h>
   38 #else
   39 /* If zlib is not available then this code is a dummy */
   40 #endif
   41 
   42 
   43 /*
   44  * A filter that encodes or decodes the content of zisofs compressed files.
   45  */
   46 
   47 
   48 /* The lowest size of a file which shall not be represented by zisofs v1 */
   49 #define ISO_ZISOFS_V1_LIMIT 4294967296
   50 
   51 /* zisofs2: Test value for small mixed-version ISOs: 1 million
   52             ISO_ZISOFS_V1_LIMIT 1000000
   53 */
   54 
   55 /* Minimum and maximum blocks sizes for version 1 and 2 */
   56 #define ISO_ZISOFS_V1_MIN_LOG2   15
   57 #define ISO_ZISOFS_V1_MAX_LOG2   17
   58 #define ISO_ZISOFS_V2_MIN_LOG2   15
   59 #define ISO_ZISOFS_V2_MAX_LOG2   20
   60 
   61 
   62 /* ------------------- Defaults of runtime parameters ------------------ */
   63 
   64 /* Limit for overall count of allocated block pointers:
   65    2 exp 25 = 256 MiB blocklist buffer = 4 TiB uncompressed at 128 KiB
   66 */
   67 #define ISO_ZISOFS_MAX_BLOCKS_T 0x2000000
   68 
   69 /* Function to account for global block pointers */
   70 static uint64_t ziso_block_pointer_mgt(uint64_t num, int mode);
   71 
   72 /* Limit for single files:
   73    2 exp 25 = 256 MiB blocklist buffer = 4 TiB uncompressed at 128 KiB
   74 */
   75 #define ISO_ZISOFS_MAX_BLOCKS_F 0x2000000
   76 
   77 /* The number of blocks from which on the block pointer list shall be discarded
   78  * on iso_stream_close() of a compressing stream. This means that the pointers
   79  * have to be determined again on next ziso_stream_compress(), so that adding
   80  * a zisofs compression filter and writing the compressed stream needs in the
   81  * sum three read runs of the input stream.
   82  * <= 0 disables this file size based discarding.
   83  */ 
   84 #define ISO_ZISOFS_MANY_BLOCKS 0
   85 
   86 /* A ratio describing the part of the maximum number of block pointers which
   87  * shall be kept free by intermediate discarding of block pointers. See above
   88  * ISO_ZISOFS_MANY_BLOCKS. -1.0 disables this feature.
   89  */
   90 #define ISO_ZISOFS_KBF_RATIO  -1.0 
   91 
   92 
   93 /* --------------------------- Runtime parameters ------------------------- */
   94 
   95 /* Sizes to be used for compression. Decompression learns from input header. */
   96 static uint8_t ziso_block_size_log2 = 15;
   97 
   98 static int ziso_v2_enabled = 0;
   99 static int ziso_v2_block_size_log2 = 17;
  100 
  101 static int64_t ziso_block_number_target = -1;
  102 
  103 static int64_t ziso_max_total_blocks = ISO_ZISOFS_MAX_BLOCKS_T;
  104 static int64_t ziso_max_file_blocks = ISO_ZISOFS_MAX_BLOCKS_F;
  105 
  106 static int64_t ziso_many_block_limit = ISO_ZISOFS_MANY_BLOCKS;
  107 static double ziso_keep_blocks_free_ratio = ISO_ZISOFS_KBF_RATIO;
  108 
  109 /* Discard block pointers on last stream close even if the size constraints
  110  * are not met. To be set to 1 at block pointer overflow. To be set to 0
  111  * when all compression filters are deleted.
  112  */
  113 static int ziso_early_bpt_discard = 0;
  114 
  115 /* 1 = produce Z2 entries for zisofs2 , 0 = produce ZF for zisofs2
  116  * This is used as extern variable in rockridge.c
  117  */
  118 int iso_zisofs2_enable_susp_z2 = 0;
  119 
  120 
  121 static
  122 int ziso_decide_v2_usage(off_t orig_size)
  123 {
  124     if (ziso_v2_enabled > 1 ||
  125         (ziso_v2_enabled == 1 && orig_size >= (off_t) ISO_ZISOFS_V1_LIMIT))
  126         return 1;
  127     return 0;
  128 }
  129 
  130 static
  131 int ziso_decide_bs_log2(off_t orig_size)
  132 {       
  133     int bs_log2, bs_log2_min, i;
  134     off_t bs;
  135 
  136     if (ziso_decide_v2_usage(orig_size)) {
  137         bs_log2 = ziso_v2_block_size_log2;
  138         bs_log2_min = ISO_ZISOFS_V2_MIN_LOG2;
  139     } else {
  140         bs_log2 = ziso_block_size_log2;
  141         bs_log2_min = ISO_ZISOFS_V1_MIN_LOG2;
  142     }
  143     if (ziso_block_number_target <= 0)
  144         return bs_log2;
  145 
  146     for (i = bs_log2_min; i < bs_log2; i++) {
  147         bs = (1 << i);
  148         if (orig_size / bs + !!(orig_size % bs) + 1 <=
  149             ziso_block_number_target)
  150             return i;
  151     }
  152     return bs_log2;
  153 }
  154 
  155 
  156 /* --------------------------- ZisofsFilterRuntime ------------------------- */
  157 
  158 
  159 /* Individual runtime properties exist only as long as the stream is opened.
  160  */
  161 typedef struct
  162 {
  163     int state; /* processing: 0= header, 1= block pointers, 2= data blocks */
  164 
  165     int zisofs_version; /* 1 or 2 */
  166 
  167     int block_size;
  168     int64_t block_pointer_fill;
  169     int64_t block_pointer_rpos;
  170     uint64_t *block_pointers; /* These are in use only with uncompression.
  171                                  Compression streams hold the pointer in
  172                                  their persistent data.
  173                                */
  174 
  175     char *read_buffer;
  176     char *block_buffer;
  177     int buffer_size;
  178     int buffer_fill;
  179     int buffer_rpos;
  180 
  181     off_t block_counter;
  182     off_t in_counter;
  183     off_t out_counter;
  184 
  185     int error_ret;
  186 
  187 } ZisofsFilterRuntime;
  188 
  189 
  190 static
  191 int ziso_running_destroy(ZisofsFilterRuntime **running, int flag)
  192 {
  193     ZisofsFilterRuntime *o= *running;
  194     if (o == NULL)
  195         return 0;
  196     if (o->block_pointers != NULL) {
  197         ziso_block_pointer_mgt((uint64_t) o->block_pointer_fill, 2);
  198         free(o->block_pointers);
  199     }
  200     if (o->read_buffer != NULL)
  201         free(o->read_buffer);
  202     if (o->block_buffer != NULL)
  203         free(o->block_buffer);
  204     free((char *) o);
  205     *running = NULL;
  206     return 1;
  207 }
  208 
  209 
  210 /*
  211  * @param flag bit0= do not set block_size, do not allocate buffers
  212  */
  213 static
  214 int ziso_running_new(ZisofsFilterRuntime **running, off_t orig_size,
  215                      int flag)
  216 {
  217     ZisofsFilterRuntime *o;
  218     *running = o = calloc(sizeof(ZisofsFilterRuntime), 1);
  219     if (o == NULL) {
  220         return ISO_OUT_OF_MEM;
  221     }
  222     o->state = 0;
  223     o->block_size= 0;
  224     o->zisofs_version = 0;
  225     o->block_pointer_fill = 0;
  226     o->block_pointer_rpos = 0;
  227     o->block_pointers = NULL;
  228     o->read_buffer = NULL;
  229     o->block_buffer = NULL;
  230     o->buffer_size = 0;
  231     o->buffer_fill = 0;
  232     o->buffer_rpos = 0;
  233     o->block_counter = 0;
  234     o->in_counter = 0;
  235     o->out_counter = 0;
  236     o->error_ret = 0;
  237 
  238     if (flag & 1)
  239         return 1;
  240 
  241     o->block_size = (1 << ziso_decide_bs_log2(orig_size));
  242 #ifdef Libisofs_with_zliB
  243     o->buffer_size = compressBound((uLong) o->block_size);
  244 #else
  245     o->buffer_size = 2 * o->block_size;
  246 #endif
  247     o->read_buffer = calloc(o->block_size, 1);
  248     o->block_buffer = calloc(o->buffer_size, 1);
  249     if (o->block_buffer == NULL || o->read_buffer == NULL)
  250         goto failed;
  251     return 1;
  252 failed:
  253     ziso_running_destroy(running, 0);
  254     return -1;
  255 }
  256 
  257 
  258 /* --------------------------- Resource accounting ------------------------- */
  259 
  260 /* @param mode  0= inquire whether num block pointers would fit
  261                 1= register num block pointers
  262                 2= unregister num block_pointers
  263                 3= return number of accounted block pointers
  264    @return      if not mode 3: 0= does not fit , 1= fits
  265 */
  266 static
  267 uint64_t ziso_block_pointer_mgt(uint64_t num, int mode)
  268 {
  269     static uint64_t global_count = 0;
  270     static int underrun = 0;
  271 
  272     if (mode == 2) {
  273         if (global_count < num) {
  274             if (underrun < 3)
  275                 iso_msg_submit(-1, ISO_ZISOFS_BPT_UNDERRUN, 0, 
  276                             "Prevented global block pointer counter underrun");
  277             underrun++;
  278             global_count = 0;
  279         } else {
  280             global_count -= num;
  281         }
  282     } else if (mode == 3) {
  283         return global_count;
  284     } else {
  285        if (global_count + num > (uint64_t) ziso_max_total_blocks)
  286            return 0;
  287        if (mode == 1)
  288            global_count += num;
  289     }
  290     return 1;
  291 }
  292 
  293 
  294 /* ---------------------------- ZisofsFilterStreamData --------------------- */
  295 
  296 /* The first 8 bytes of a zisofs compressed data file */
  297 static unsigned char zisofs_magic[9] =
  298                               {0x37, 0xE4, 0x53, 0x96, 0xC9, 0xDB, 0xD6, 0x07};
  299 
  300 /* The first 8 bytes of a zisofs2 compressed data file */
  301 static unsigned char zisofs2_magic[9] =
  302                               {0xEF, 0x22, 0x55, 0xA1, 0xBC, 0x1B, 0x95, 0xA0};
  303 
  304 /* Counts the number of active compression filters */
  305 static off_t ziso_ref_count = 0;
  306 
  307 /* Counts the number of active uncompression filters */
  308 static off_t ziso_osiz_ref_count = 0;
  309 
  310 
  311 #ifdef Libisofs_with_zliB
  312 /* Parameter for compress2() , see <zlib.h> */
  313 
  314 static int ziso_compression_level = 6;
  315 
  316 #endif /* Libisofs_with_zliB */
  317 
  318 
  319 /*
  320  * The common data payload of an individual Zisofs Filter IsoStream
  321  * IMPORTANT: Any change must be reflected by ziso_clone_stream().
  322  */
  323 typedef struct
  324 {
  325     IsoStream *orig;
  326 
  327     off_t size; /* -1 means that the size is unknown yet */
  328 
  329     ZisofsFilterRuntime *running; /* is non-NULL when open */
  330 
  331     ino_t id;
  332 
  333 } ZisofsFilterStreamData;
  334 
  335 
  336 /*
  337  * The data payload of an individual Zisofs Filter Compressor IsoStream
  338  * IMPORTANT: Any change must be reflected by ziso_clone_stream().
  339  */
  340 typedef struct
  341 {   
  342     ZisofsFilterStreamData std;
  343 
  344     uint64_t orig_size;
  345     uint64_t *block_pointers; /* Cache for output block addresses. They get
  346                                  written before the data and so need 2 passes.
  347                                  This cache avoids surplus passes.
  348                                */
  349     uint64_t block_pointer_counter;
  350     uint64_t open_counter;
  351     int block_pointers_dropped;
  352 
  353 } ZisofsComprStreamData;
  354 
  355 
  356 /*
  357  * The data payload of an individual Zisofs Filter Uncompressor IsoStream
  358  * IMPORTANT: Any change must be reflected by ziso_clone_stream().
  359  */
  360 typedef struct
  361 {
  362     ZisofsFilterStreamData std;
  363 
  364     uint8_t zisofs_algo_num;
  365     unsigned char header_size_div4;
  366     unsigned char block_size_log2;
  367 
  368 } ZisofsUncomprStreamData;
  369 
  370 
  371 /* Each individual ZisofsFilterStreamData needs a unique id number. */
  372 /* >>> This is very suboptimal:
  373        The counter can rollover.
  374 */
  375 static ino_t ziso_ino_id = 0;
  376 
  377 
  378 /*
  379  * Methods for the IsoStreamIface of an Zisofs Filter object.
  380  */
  381 
  382 static
  383 int ziso_stream_uncompress(IsoStream *stream, void *buf, size_t desired);
  384 
  385 static
  386 int ziso_stream_compress(IsoStream *stream, void *buf, size_t desired);
  387 
  388 
  389 /*
  390  * @param flag  bit0= discard even if the size conditions are not met
  391                 bit1= check for open_counter == 1 rather than == 0
  392  */
  393 static
  394 int ziso_discard_bpt(IsoStream *stream, int flag)
  395 {
  396     ZisofsFilterStreamData *data;
  397     ZisofsComprStreamData *cstd = NULL;
  398     int block_size;
  399     double max_blocks, free_blocks;
  400 
  401     data = stream->data;
  402     if (stream->class->read == &ziso_stream_compress)
  403         cstd = (ZisofsComprStreamData *) data;
  404     if (cstd == NULL)
  405         return 0;
  406 
  407     block_size = (1 << ziso_decide_bs_log2(cstd->orig_size));
  408     max_blocks = ziso_max_file_blocks;
  409     if (max_blocks < 1.0)
  410         max_blocks = 1.0;
  411     free_blocks = ziso_max_total_blocks -
  412                   ziso_block_pointer_mgt((uint64_t) 0, 3);
  413     if (cstd->block_pointers == NULL) {
  414         return 0;
  415     } else if (cstd->open_counter != !!(flag & 2)) {
  416         return 0;
  417     } else if (!((flag & 1) || ziso_early_bpt_discard)) {
  418         if (ziso_many_block_limit <= 0 ||
  419             cstd->orig_size / block_size + !!(cstd->orig_size % block_size)
  420             + 1 < (uint64_t) ziso_many_block_limit)
  421             if (ziso_keep_blocks_free_ratio < 0.0 ||
  422                 free_blocks / max_blocks >= ziso_keep_blocks_free_ratio)
  423                 return 0;
  424     }
  425     ziso_block_pointer_mgt(cstd->block_pointer_counter, 2);
  426     free((char *) cstd->block_pointers);
  427     cstd->block_pointers_dropped = 1;
  428     cstd->block_pointers = NULL;
  429     cstd->block_pointer_counter = 0;
  430     return 1;
  431 }
  432 
  433 /*
  434  * @param flag  bit0= original stream is not open
  435  *              bit1= do not destroy large
  436  *                    ZisofsComprStreamData->block_pointers
  437  */
  438 static
  439 int ziso_stream_close_flag(IsoStream *stream, int flag)
  440 {
  441     ZisofsFilterStreamData *data;
  442     ZisofsComprStreamData *cstd = NULL;
  443 
  444     if (stream == NULL) {
  445         return ISO_NULL_POINTER;
  446     }
  447     data = stream->data;
  448     if (stream->class->read == &ziso_stream_compress)
  449         cstd = (ZisofsComprStreamData *) data;
  450 
  451     if (cstd != NULL && !(flag & 2))
  452         ziso_discard_bpt(stream, 2);
  453 
  454     if (data->running == NULL) {
  455         return 1;
  456     }
  457     ziso_running_destroy(&(data->running), 0);
  458     if (flag & 1)
  459         return 1;
  460     if (cstd != NULL)
  461         if (cstd->open_counter > 0)
  462             cstd->open_counter--;
  463     return iso_stream_close(data->orig);
  464 }
  465 
  466 
  467 static
  468 int ziso_stream_close(IsoStream *stream)
  469 {
  470     return ziso_stream_close_flag(stream, 0);
  471 }
  472 
  473 
  474 /*
  475  * @param flag  bit0= do not run .get_size() if size is < 0
  476  */
  477 static
  478 int ziso_stream_open_flag(IsoStream *stream, int flag)
  479 {
  480     ZisofsFilterStreamData *data;
  481     ZisofsComprStreamData *cstd;
  482     ZisofsFilterRuntime *running = NULL;
  483     int ret;
  484     off_t orig_size = 0;
  485 
  486     if (stream == NULL) {
  487         return ISO_NULL_POINTER;
  488     }
  489     data = (ZisofsFilterStreamData*) stream->data;
  490     if (data->running != NULL) {
  491         return ISO_FILE_ALREADY_OPENED;
  492     }
  493     if (data->size < 0 && !(flag & 1)) {
  494         /* Do the size determination run now, so that the size gets cached
  495            and .get_size() will not fail on an opened stream.
  496         */
  497         stream->class->get_size(stream);
  498     }
  499     orig_size = data->size;
  500     if (stream->class->read == &ziso_stream_compress) {
  501         cstd = (ZisofsComprStreamData *) data;
  502         cstd->open_counter++;
  503         orig_size = cstd->orig_size;
  504     }
  505     if (orig_size < 0)
  506         return ISO_ZISOFS_UNKNOWN_SIZE;
  507 
  508     ret = ziso_running_new(&running, orig_size,
  509                            (stream->class->read == &ziso_stream_uncompress));
  510     if (ret < 0) {
  511         return ret;
  512     }
  513     data->running = running;
  514 
  515     ret = iso_stream_open(data->orig);
  516     if (ret < 0) {
  517         return ret;
  518     }
  519     return 1;
  520 }
  521 
  522 
  523 static
  524 int ziso_stream_open(IsoStream *stream)
  525 {
  526     return ziso_stream_open_flag(stream, 0);
  527 }
  528 
  529 
  530 /* @param flag bit0= stream is already open
  531                bit1= close stream with flag bit1
  532  */
  533 static
  534 off_t ziso_stream_measure_size(IsoStream *stream, int flag)
  535 {
  536     int ret, ret_close;
  537     off_t count = 0;
  538     ZisofsFilterStreamData *data;
  539     char buf[64 * 1024];
  540     size_t bufsize = 64 * 1024;
  541 
  542     if (stream == NULL)
  543         return ISO_NULL_POINTER;
  544     data = stream->data;
  545 
  546     /* Run filter command and count output bytes */
  547     if (!(flag & 1)) {
  548         ret = ziso_stream_open_flag(stream, 1);
  549         if (ret < 0)
  550             return ret;
  551     }
  552     if (stream->class->read == &ziso_stream_uncompress) {
  553         /* It is enough to read the header part of a compressed file */
  554         ret = ziso_stream_uncompress(stream, buf, 0);
  555         count = data->size;
  556     } else {
  557         /* The size of the compression result has to be counted */
  558         while (1) {
  559             ret = stream->class->read(stream, buf, bufsize);
  560             if (ret <= 0)
  561         break;
  562             count += ret;
  563         }
  564     }
  565     ret_close = ziso_stream_close_flag(stream, flag & 2);
  566     if (ret < 0)
  567         return ret;
  568     if (ret_close < 0)
  569         return ret_close;
  570 
  571     data->size = count;
  572     return count;
  573 }
  574 
  575 
  576 static
  577 int ziso_stream_compress(IsoStream *stream, void *buf, size_t desired)
  578 {
  579 
  580 #ifdef Libisofs_with_zliB
  581 
  582     int ret, todo, i;
  583     ZisofsComprStreamData *data;
  584     ZisofsFilterRuntime *rng;
  585     size_t fill = 0;
  586     off_t orig_size, next_pt, measure_ret;
  587     char *cbuf = buf;
  588     uLongf buf_len;
  589     uint64_t *copy_base, num_blocks = 0;
  590 
  591     if (stream == NULL) {
  592         return ISO_NULL_POINTER;
  593     }
  594     data = stream->data;
  595     rng= data->std.running;
  596     if (rng == NULL) {
  597         return ISO_FILE_NOT_OPENED;
  598     }
  599     if (rng->error_ret < 0) {
  600         return rng->error_ret;
  601     }
  602 
  603     if (data->block_pointers_dropped) {
  604         /* The list was dropped after measurement of compressed size. But this
  605          * run of the function expects it as already filled with pointer
  606          * values. So now they have to be re-computed by extra runs of this
  607          * function in the course of compressed size measurement.
  608          */
  609         data->block_pointers_dropped = 0;
  610         measure_ret = ziso_stream_measure_size(stream, 1 | 2); 
  611         if (measure_ret < 0)
  612             return (rng->error_ret = measure_ret);
  613 
  614         /* Stream was closed. Open it again, without any size determination. */
  615         ret = ziso_stream_open_flag(stream, 1);
  616         if (ret < 0)
  617             return ret;
  618     }
  619 
  620     while (1) {
  621         if (rng->state == 0) {
  622             /* Delivering file header */
  623 
  624             if (rng->buffer_fill == 0) {
  625                 orig_size = iso_stream_get_size(data->std.orig);
  626                 num_blocks = orig_size / rng->block_size +
  627                              1 + !!(orig_size % rng->block_size);
  628                 if (num_blocks > (uint64_t) ziso_max_file_blocks)
  629                     return (rng->error_ret = ISO_ZISOFS_TOO_LARGE);
  630                 if (ziso_block_pointer_mgt((uint64_t) num_blocks, 0) == 0) {
  631                     ziso_early_bpt_discard = 1;
  632                     return (rng->error_ret = ISO_ZISOFS_TOO_MANY_PTR);
  633                 }
  634                 if (orig_size != (off_t) data->orig_size)
  635                     return (rng->error_ret = ISO_FILTER_WRONG_INPUT);
  636                 if (ziso_decide_v2_usage(orig_size)) {
  637                     rng->zisofs_version = 2;
  638                     memcpy(rng->block_buffer, zisofs2_magic, 8);
  639                     rng->block_buffer[8] = 0;    /* @hdr_version */
  640                     rng->block_buffer[9] = 6;    /* @hdr_size */
  641                     rng->block_buffer[10] = 1;   /* @alg_id */
  642                     rng->block_buffer[11] = ziso_decide_bs_log2(orig_size);
  643                     iso_lsb64((uint8_t *) (rng->block_buffer + 12),
  644                               (uint64_t) orig_size);
  645                     memset(rng->block_buffer + 20, 0, 4);
  646                     rng->buffer_fill = 24;
  647                 } else {
  648                     if (orig_size >= (off_t) ISO_ZISOFS_V1_LIMIT) {
  649                         return (rng->error_ret = ISO_ZISOFS_TOO_LARGE);
  650                     }
  651                     rng->zisofs_version = 1;
  652                     memcpy(rng->block_buffer, zisofs_magic, 8);
  653                     iso_lsb((unsigned char *) (rng->block_buffer + 8),
  654                             (uint32_t) orig_size, 4);
  655                     rng->block_buffer[12] = 4;
  656                     rng->block_buffer[13] = ziso_decide_bs_log2(orig_size);
  657                     rng->block_buffer[14] = rng->block_buffer[15] = 0;
  658                     rng->buffer_fill = 16;
  659                 }
  660                 rng->buffer_rpos = 0;
  661             } else if (rng->buffer_rpos >= rng->buffer_fill) {
  662                 rng->buffer_fill = rng->buffer_rpos = 0;
  663                 rng->state = 1; /* header is delivered */
  664             }
  665         }
  666         if (rng->state == 1) {
  667             /* Delivering block pointers */;
  668 
  669             if (rng->block_pointer_fill == 0 || data->block_pointers == NULL) {
  670                 /* Initialize block pointer writing */
  671                 rng->block_pointer_rpos = 0;
  672                 num_blocks = data->orig_size / rng->block_size
  673                              + 1 + !!(data->orig_size % rng->block_size);
  674                 if (rng->block_pointer_fill > 0 &&
  675                     (int64_t) num_blocks != rng->block_pointer_fill)
  676                     return (rng->error_ret = ISO_FILTER_WRONG_INPUT);
  677                 rng->block_pointer_fill = num_blocks;
  678                 if (data->block_pointers == NULL) {
  679                     /* On the first pass, create pointer array with all 0s */
  680                     if (ziso_block_pointer_mgt(num_blocks, 1) == 0) {
  681                         rng->block_pointer_fill = 0;
  682                         ziso_early_bpt_discard = 1;
  683                         return (rng->error_ret = ISO_ZISOFS_TOO_MANY_PTR);
  684                     }
  685                     data->block_pointers = calloc(rng->block_pointer_fill, 8);
  686                     if (data->block_pointers == NULL) {
  687                         ziso_block_pointer_mgt(num_blocks, 2);
  688                         rng->block_pointer_fill = 0;
  689                         return (rng->error_ret = ISO_OUT_OF_MEM);
  690                     }
  691                     data->block_pointer_counter = rng->block_pointer_fill;
  692                 }
  693             }
  694 
  695             if (rng->buffer_rpos >= rng->buffer_fill) {
  696                 if (rng->block_pointer_rpos >= rng->block_pointer_fill) {
  697                     rng->buffer_fill = rng->buffer_rpos = 0;
  698                     rng->block_counter = 0;
  699                     if (rng->zisofs_version == 1)
  700                         data->block_pointers[0] = 16 +
  701                                                   rng->block_pointer_fill * 4;
  702                     else
  703                         data->block_pointers[0] = 24 +
  704                                                   rng->block_pointer_fill * 8;
  705                     rng->state = 2; /* block pointers are delivered */
  706                 } else {
  707                     /* Provide a buffer full of block pointers */
  708                     /* data->block_pointers was filled by ziso_stream_open() */
  709                     todo = rng->block_pointer_fill - rng->block_pointer_rpos;
  710                     copy_base = data->block_pointers + rng->block_pointer_rpos;
  711                     if (rng->zisofs_version == 1) {
  712                         if (todo * 4 > rng->buffer_size)
  713                             todo = rng->buffer_size / 4;
  714                         for (i = 0; i < todo; i++)
  715                             iso_lsb((unsigned char *) (rng->block_buffer +
  716                                                        4 * i), 
  717                                     (uint32_t) (copy_base[i] & 0xffffffff), 4);
  718                         rng->buffer_fill = todo * 4;
  719                     } else {
  720                         if (todo * 8 > rng->buffer_size)
  721                             todo = rng->buffer_size / 8;
  722                         for (i = 0; i < todo; i++)
  723                             iso_lsb64((uint8_t *) rng->block_buffer + 8 * i,
  724                                       copy_base[i]);
  725                         rng->buffer_fill = todo * 8;
  726                     }
  727                     rng->buffer_rpos = 0;
  728                     rng->block_pointer_rpos += todo;
  729                 }
  730             }
  731         }
  732         if (rng->state == 2 && rng->buffer_rpos >= rng->buffer_fill) {
  733             /* Delivering data blocks */;
  734 
  735             ret = iso_stream_read(data->std.orig, rng->read_buffer,
  736                                   rng->block_size);
  737             if (ret > 0) {
  738                 rng->in_counter += ret;
  739                 if ((uint64_t) rng->in_counter > data->orig_size) {
  740                     /* Input size became larger */
  741                     return (rng->error_ret = ISO_FILTER_WRONG_INPUT);
  742                 }
  743                 /* Check whether all 0 : represent as 0-length block */;
  744                 for (i = 0; i < ret; i++)
  745                     if (rng->read_buffer[i])
  746                 break;
  747                 if (i >= ret) { /* All 0-bytes. Bypass compression. */
  748                     buf_len = 0;
  749                 } else {
  750                     buf_len = rng->buffer_size;
  751                     ret = compress2((Bytef *) rng->block_buffer, &buf_len,
  752                                     (Bytef *) rng->read_buffer, (uLong) ret,
  753                                     ziso_compression_level);
  754                     if (ret != Z_OK) {
  755                         return (rng->error_ret = ISO_ZLIB_COMPR_ERR);
  756                     }
  757                 }
  758                 rng->buffer_fill = buf_len;
  759                 rng->buffer_rpos = 0;
  760 
  761                 next_pt = data->block_pointers[rng->block_counter] + buf_len;
  762 
  763                 if (data->std.size >= 0 && next_pt > data->std.size) {
  764                     /* Compression yields more bytes than on first run */
  765                     return (rng->error_ret = ISO_FILTER_WRONG_INPUT);
  766                 }
  767 
  768                 /* Check or record check block pointer */
  769                 rng->block_counter++;
  770                 if (data->block_pointers[rng->block_counter] > 0) {
  771                     if ((uint64_t) next_pt !=
  772                         data->block_pointers[rng->block_counter]) {
  773                         /* block pointers mismatch , content has changed */
  774                         return (rng->error_ret = ISO_FILTER_WRONG_INPUT);
  775                     }
  776                 } else {
  777                     data->block_pointers[rng->block_counter] = next_pt;
  778                 }
  779 
  780             } else if (ret == 0) {
  781                 rng->state = 3;
  782                 if ((uint64_t) rng->in_counter != data->orig_size) {
  783                     /* Input size shrunk */
  784                     return (rng->error_ret = ISO_FILTER_WRONG_INPUT);
  785                 }
  786                 return fill;
  787             } else
  788                 return (rng->error_ret = ret);
  789             if (rng->buffer_fill == 0) {
  790     continue;
  791             }
  792         }
  793         if (rng->state == 3 && rng->buffer_rpos >= rng->buffer_fill) {
  794             return 0; /* EOF */
  795         }
  796 
  797         /* Transfer from rng->block_buffer to buf */
  798         todo = desired - fill;
  799         if (todo > rng->buffer_fill - rng->buffer_rpos)
  800             todo = rng->buffer_fill - rng->buffer_rpos;
  801         memcpy(cbuf + fill, rng->block_buffer + rng->buffer_rpos, todo);
  802         fill += todo;
  803         rng->buffer_rpos += todo;
  804         rng->out_counter += todo;
  805 
  806         if (fill >= desired) {
  807            return fill;
  808         }
  809     }
  810     return ISO_FILE_READ_ERROR; /* should never be hit */
  811 
  812 #else
  813 
  814     return ISO_ZLIB_NOT_ENABLED;
  815 
  816 #endif
  817 
  818 }
  819 
  820 
  821 #ifdef Libisofs_with_zliB
  822 
  823 static
  824 int ziso_algo_to_num(uint8_t zisofs_algo[2])
  825 {
  826     if (zisofs_algo[0] == 'p' && zisofs_algo[1] == 'z')
  827         return 0;
  828     if (zisofs_algo[0] == 'P' && zisofs_algo[1] == 'Z')
  829         return 1;
  830     if (zisofs_algo[0] == 'X' && zisofs_algo[1] == 'Z')
  831         return 2;
  832     if (zisofs_algo[0] == 'L' && zisofs_algo[1] == '4')
  833         return 3;
  834     if (zisofs_algo[0] == 'Z' && zisofs_algo[1] == 'D')
  835         return 4;
  836     if (zisofs_algo[0] == 'B' && zisofs_algo[1] == '2')
  837         return 5;
  838     return -1;
  839 }
  840 
  841 #endif /* Libisofs_with_zliB */
  842 
  843 static
  844 int ziso_num_to_algo(uint8_t num, uint8_t zisofs_algo[2])
  845 {
  846     if (num == 0) {
  847         zisofs_algo[0] = 'p';
  848         zisofs_algo[1] = 'z';
  849         return 1;
  850     } else if (num == 1) {
  851         zisofs_algo[0] = 'P';
  852         zisofs_algo[1] = 'Z';
  853         return 1;
  854     } else if (num == 2) {
  855         zisofs_algo[0] = 'X';
  856         zisofs_algo[1] = 'Z';
  857         return 2;
  858     } else if (num == 3) {
  859         zisofs_algo[0] = 'L';
  860         zisofs_algo[1] = '4';
  861         return 2;
  862     } else if (num == 4) {
  863         zisofs_algo[0] = 'Z';
  864         zisofs_algo[1] = 'D';
  865         return 2;
  866     } else if (num == 5) {
  867         zisofs_algo[0] = 'B';
  868         zisofs_algo[1] = '2';
  869         return 2;
  870     }
  871     return -1;
  872 }
  873 
  874 
  875 /* @param flag bit0= recognize zisofs2 only if ziso_v2_enabled
  876                bit1= do not accept algorithms which libisofs does not support
  877 */
  878 static
  879 int ziso_parse_zisofs_head(IsoStream *stream, uint8_t *ziso_algo_num,
  880                            int *header_size_div4, int *block_size_log2,
  881                            uint64_t *uncompressed_size, int flag)
  882 {
  883     int ret, consumed = 0, i;
  884     char zisofs_head[24];
  885     char waste_word[4];
  886 
  887     ret = iso_stream_read(stream, zisofs_head, 8);
  888     if (ret < 0)
  889         return ret;
  890     if (ret != 8)
  891         return ISO_ZISOFS_WRONG_INPUT;
  892     consumed = 8;
  893     if (memcmp(zisofs_head, zisofs_magic, 8) == 0) {
  894         *ziso_algo_num = 0;
  895         ret = iso_stream_read(stream, zisofs_head + 8, 8);
  896         if (ret < 0)
  897             return ret;
  898         if (ret != 8)
  899             return ISO_ZISOFS_WRONG_INPUT;
  900         consumed += 8;
  901         *header_size_div4 = ((unsigned char *) zisofs_head)[12];
  902         *block_size_log2 = ((unsigned char *) zisofs_head)[13];
  903         *uncompressed_size = iso_read_lsb(((uint8_t *) zisofs_head) + 8, 4);
  904         if (*header_size_div4 < 4 ||
  905             *block_size_log2 < ISO_ZISOFS_V1_MIN_LOG2 ||
  906             *block_size_log2 > ISO_ZISOFS_V1_MAX_LOG2)
  907             return ISO_ZISOFS_WRONG_INPUT;
  908     } else if (memcmp(zisofs_head, zisofs2_magic, 8) == 0 &&
  909                !(ziso_v2_enabled == 0 && (flag & 1))) {
  910         ret = iso_stream_read(stream, zisofs_head + 8, 16);
  911         if (ret < 0)
  912             return ret;
  913         if (ret != 16)
  914             return ISO_ZISOFS_WRONG_INPUT;
  915         consumed += 16;
  916         *ziso_algo_num = zisofs_head[10];
  917         *header_size_div4 = ((unsigned char *) zisofs_head)[9];
  918         *block_size_log2 = ((unsigned char *) zisofs_head)[11];
  919         *uncompressed_size = iso_read_lsb64(((uint8_t *) zisofs_head) + 12);
  920         if (*header_size_div4 < 4 ||
  921             *block_size_log2 < ISO_ZISOFS_V2_MIN_LOG2 ||
  922             *block_size_log2 > ISO_ZISOFS_V2_MAX_LOG2 ||
  923             (*ziso_algo_num != 1 && (flag & 2)))
  924             return ISO_ZISOFS_WRONG_INPUT;
  925     } else {
  926         return ISO_ZISOFS_WRONG_INPUT;
  927     }
  928     for (i = consumed; i < *header_size_div4; i++) {
  929        /* Skip surplus header words */
  930        ret = iso_stream_read(stream, waste_word, 4);
  931        if (ret < 0)
  932            return ret;
  933        if (ret != 4)
  934            return ISO_ZISOFS_WRONG_INPUT; 
  935     }
  936     return 1;
  937 }
  938 
  939 
  940 /* Note: A call with desired==0 directly after .open() only checks the file
  941          head and loads the uncompressed size from that head.
  942 */
  943 static
  944 int ziso_stream_uncompress(IsoStream *stream, void *buf, size_t desired)
  945 {
  946 
  947 #ifdef Libisofs_with_zliB
  948 
  949     int ret, todo, header_size, bs_log2, block_max = 1, blpt_size;
  950     ZisofsFilterStreamData *data;
  951     ZisofsFilterRuntime *rng;
  952     ZisofsUncomprStreamData *nstd;
  953     size_t fill = 0;
  954     char *cbuf = buf;
  955     uLongf buf_len;
  956     uint64_t uncompressed_size;
  957     int64_t i;
  958     uint8_t algo_num, *rpt, *wpt;
  959 
  960     if (stream == NULL) {
  961         return ISO_NULL_POINTER;
  962     }
  963     data = stream->data;
  964     nstd = stream->data;
  965     rng= data->running;
  966     if (rng == NULL) {
  967         return ISO_FILE_NOT_OPENED;
  968     }
  969     if (rng->error_ret < 0) {
  970         return rng->error_ret;
  971     }
  972 
  973     while (1) {
  974         if (rng->state == 0) {
  975             /* Reading file header */
  976             ret = ziso_parse_zisofs_head(data->orig, &algo_num, &header_size,
  977                                          &bs_log2, &uncompressed_size, 2);
  978             if (ret < 0)
  979                 return (rng->error_ret = ret);
  980             if (algo_num == 0)
  981                 blpt_size = 4;
  982             else
  983                 blpt_size = 8;
  984             nstd->header_size_div4 = header_size;
  985             header_size *= 4;
  986             data->size = uncompressed_size;
  987             nstd->block_size_log2 = bs_log2;
  988             rng->block_size = 1 << bs_log2;
  989 
  990             if (desired == 0)
  991                 return 0;
  992 
  993             /* Create and read pointer array */
  994             rng->block_pointer_rpos = 0;
  995             rng->block_pointer_fill = data->size / rng->block_size
  996                                      + 1 + !!(data->size % rng->block_size);
  997             if (rng->block_pointer_fill > ziso_max_file_blocks) {
  998                 rng->block_pointer_fill = 0;
  999                 return (rng->error_ret = ISO_ZISOFS_TOO_LARGE);
 1000             }
 1001             if (ziso_block_pointer_mgt((uint64_t) rng->block_pointer_fill, 1)
 1002                 == 0)
 1003                 return ISO_ZISOFS_TOO_MANY_PTR;
 1004             rng->block_pointers = calloc(rng->block_pointer_fill, 8);
 1005             if (rng->block_pointers == NULL) {
 1006                 ziso_block_pointer_mgt((uint64_t) rng->block_pointer_fill, 2);
 1007                 rng->block_pointer_fill = 0;
 1008                 return (rng->error_ret = ISO_OUT_OF_MEM);
 1009             }
 1010             ret = iso_stream_read(data->orig, rng->block_pointers,
 1011                                   rng->block_pointer_fill * blpt_size);
 1012             if (ret < 0)
 1013                 return (rng->error_ret = ret);
 1014             if (algo_num == 0) {
 1015                 /* Spread 4 byte little-endian pointer values over 8 byte */
 1016                 rpt = ((uint8_t *) rng->block_pointers)
 1017                       + rng->block_pointer_fill * 4;
 1018                 wpt = ((uint8_t *) rng->block_pointers)
 1019                       + rng->block_pointer_fill * 8;
 1020         while (rpt > ((uint8_t *) rng->block_pointers) + 4) {
 1021                     rpt -= 4;
 1022                     wpt -= 8;
 1023                     memcpy(wpt, rpt, 4);
 1024                     memset(wpt + 4, 0, 4);
 1025                 }
 1026                 memset(((uint8_t *) rng->block_pointers) + 4, 0, 4);
 1027             }
 1028             if (ret != rng->block_pointer_fill * blpt_size)
 1029                return (rng->error_ret = ISO_ZISOFS_WRONG_INPUT);
 1030             for (i = 0; i < rng->block_pointer_fill; i++) {
 1031                  rng->block_pointers[i] =
 1032                          iso_read_lsb64((uint8_t *) (rng->block_pointers + i));
 1033                  if (i > 0)
 1034                      if ((int) (rng->block_pointers[i] -
 1035                                 rng->block_pointers[i - 1])
 1036                          > block_max)
 1037                          block_max = rng->block_pointers[i]
 1038                                      - rng->block_pointers[i - 1];
 1039             }
 1040 
 1041             rng->read_buffer = calloc(block_max, 1);
 1042             rng->block_buffer = calloc(rng->block_size, 1);
 1043             if (rng->read_buffer == NULL || rng->block_buffer == NULL)
 1044                 return (rng->error_ret = ISO_OUT_OF_MEM);
 1045             rng->state = 2; /* block pointers are read */
 1046             rng->buffer_fill = rng->buffer_rpos = 0;
 1047         }
 1048 
 1049         if (rng->state == 2 && rng->buffer_rpos >= rng->buffer_fill) {
 1050             /* Delivering data blocks */;
 1051             i = ++(rng->block_pointer_rpos);
 1052             if (i >= rng->block_pointer_fill) {
 1053                 if (rng->out_counter == data->size) {
 1054                     rng->state = 3;
 1055                     rng->block_pointer_rpos--;
 1056                     return fill;
 1057                 }
 1058                 /* More data blocks needed than announced */
 1059                 return (rng->error_ret = ISO_FILTER_WRONG_INPUT);
 1060             }
 1061             todo = rng->block_pointers[i] - rng->block_pointers[i- 1];
 1062             if (todo == 0) {
 1063                 memset(rng->block_buffer, 0, rng->block_size);
 1064                 rng->buffer_fill = rng->block_size;
 1065                 if (rng->out_counter + rng->buffer_fill > data->size &&
 1066                     i == rng->block_pointer_fill - 1)
 1067                     rng->buffer_fill = data->size - rng->out_counter;
 1068             } else {
 1069                 ret = iso_stream_read(data->orig, rng->read_buffer, todo);
 1070                 if (ret > 0) {
 1071                     rng->in_counter += ret;
 1072                     buf_len = rng->block_size;
 1073                     ret = uncompress((Bytef *) rng->block_buffer, &buf_len,
 1074                                      (Bytef *) rng->read_buffer, (uLong) ret);
 1075                     if (ret != Z_OK)
 1076                         return (rng->error_ret = ISO_ZLIB_COMPR_ERR);
 1077                     rng->buffer_fill = buf_len;
 1078                     if ((int) buf_len < rng->block_size &&
 1079                         i != rng->block_pointer_fill - 1)
 1080                         return (rng->error_ret = ISO_ZISOFS_WRONG_INPUT);
 1081                 } else if(ret == 0) {
 1082                     rng->state = 3;
 1083                     if (rng->out_counter != data->size) {
 1084                         /* Input size shrunk */
 1085                         return (rng->error_ret = ISO_FILTER_WRONG_INPUT);
 1086                     }
 1087                     return fill;
 1088                 } else
 1089                     return (rng->error_ret = ret);
 1090             }
 1091             rng->buffer_rpos = 0;
 1092 
 1093             if (rng->out_counter + rng->buffer_fill > data->size) {
 1094                 /* Uncompression yields more bytes than announced by header */
 1095                 return (rng->error_ret = ISO_FILTER_WRONG_INPUT);
 1096             }
 1097         }
 1098         if (rng->state == 3 && rng->buffer_rpos >= rng->buffer_fill) {
 1099             return 0; /* EOF */
 1100         }
 1101 
 1102         /* Transfer from rng->block_buffer to buf */
 1103         todo = desired - fill;
 1104         if (todo > rng->buffer_fill - rng->buffer_rpos)
 1105             todo = rng->buffer_fill - rng->buffer_rpos;
 1106         memcpy(cbuf + fill, rng->block_buffer + rng->buffer_rpos, todo);
 1107         fill += todo;
 1108         rng->buffer_rpos += todo;
 1109         rng->out_counter += todo;
 1110 
 1111         if (fill >= desired) {
 1112            return fill;
 1113         }
 1114     }
 1115     return (rng->error_ret = ISO_FILE_READ_ERROR); /* should never be hit */
 1116 
 1117 #else
 1118 
 1119     return ISO_ZLIB_NOT_ENABLED;
 1120 
 1121 #endif
 1122 
 1123 }
 1124 
 1125 
 1126 static
 1127 off_t ziso_stream_get_size(IsoStream *stream)
 1128 {
 1129     off_t ret;
 1130     ZisofsFilterStreamData *data;
 1131 
 1132     if (stream == NULL)
 1133         return ISO_NULL_POINTER;
 1134     data = stream->data;
 1135     if (data->size >= 0)
 1136         return data->size;
 1137     ret = ziso_stream_measure_size(stream, 0);
 1138     return ret;
 1139 }
 1140 
 1141 
 1142 static
 1143 int ziso_stream_is_repeatable(IsoStream *stream)
 1144 {
 1145     /* Only repeatable streams are accepted as orig */
 1146     return 1;
 1147 }
 1148 
 1149 
 1150 static
 1151 void ziso_stream_get_id(IsoStream *stream, unsigned int *fs_id, 
 1152                         dev_t *dev_id, ino_t *ino_id)
 1153 {
 1154     ZisofsFilterStreamData *data;
 1155 
 1156     data = stream->data;
 1157     *fs_id = ISO_FILTER_FS_ID;
 1158     *dev_id = ISO_FILTER_ZISOFS_DEV_ID;
 1159     *ino_id = data->id;
 1160 }
 1161 
 1162 
 1163 static
 1164 void ziso_stream_free(IsoStream *stream)
 1165 {
 1166     ZisofsFilterStreamData *data;
 1167     ZisofsComprStreamData *nstd;
 1168 
 1169     if (stream == NULL) {
 1170         return;
 1171     }
 1172     data = stream->data;
 1173     if (data->running != NULL) {
 1174         ziso_stream_close(stream);
 1175     }
 1176     if (stream->class->read == &ziso_stream_uncompress) {
 1177         if (--ziso_osiz_ref_count < 0)
 1178             ziso_osiz_ref_count = 0;
 1179     } else {
 1180         nstd = stream->data;
 1181         if (nstd->block_pointers != NULL) {
 1182             ziso_block_pointer_mgt(nstd->block_pointer_counter, 2);
 1183             free((char *) nstd->block_pointers);
 1184         }
 1185         if (--ziso_ref_count < 0)
 1186             ziso_ref_count = 0;
 1187         if (ziso_ref_count == 0)
 1188             ziso_early_bpt_discard = 0;
 1189     }
 1190     iso_stream_unref(data->orig);
 1191     free(data);
 1192 }
 1193 
 1194 
 1195 static
 1196 int ziso_update_size(IsoStream *stream)
 1197 {
 1198     /* By principle size is determined only once */
 1199     return 1;
 1200 }
 1201 
 1202 
 1203 static
 1204 IsoStream *ziso_get_input_stream(IsoStream *stream, int flag)
 1205 {
 1206     ZisofsFilterStreamData *data;
 1207 
 1208     if (stream == NULL) {
 1209         return NULL;
 1210     }
 1211     data = stream->data;
 1212     return data->orig;
 1213 }
 1214 
 1215 static
 1216 int ziso_clone_stream(IsoStream *old_stream, IsoStream **new_stream, int flag)
 1217 {
 1218     int ret;
 1219     IsoStream *new_input_stream = NULL, *stream = NULL;
 1220     ZisofsFilterStreamData *stream_data, *old_stream_data;
 1221     ZisofsUncomprStreamData *uncompr, *old_uncompr;
 1222     ZisofsComprStreamData *compr, *old_compr;
 1223 
 1224     if (flag)
 1225         return ISO_STREAM_NO_CLONE; /* unknown option required */
 1226 
 1227     ret = iso_stream_clone_filter_common(old_stream, &stream,
 1228                                          &new_input_stream, 0);
 1229     if (ret < 0)
 1230         return ret;
 1231 
 1232     if (old_stream->class->read == &ziso_stream_uncompress) {
 1233         uncompr = calloc(1, sizeof(ZisofsUncomprStreamData));
 1234         if (uncompr == NULL)
 1235             goto no_mem;
 1236         stream_data = (ZisofsFilterStreamData *) uncompr;
 1237         old_uncompr = (ZisofsUncomprStreamData *) old_stream->data;
 1238         uncompr->zisofs_algo_num = old_uncompr->zisofs_algo_num;
 1239         uncompr->header_size_div4 = old_uncompr->header_size_div4;
 1240         uncompr->block_size_log2 = old_uncompr->block_size_log2;
 1241     } else {
 1242         compr = calloc(1, sizeof(ZisofsComprStreamData));
 1243         if (compr == NULL)
 1244             goto no_mem;
 1245         stream_data = (ZisofsFilterStreamData *) compr;
 1246         old_compr = (ZisofsComprStreamData *) old_stream->data;
 1247         compr->orig_size = old_compr->orig_size;
 1248         compr->block_pointers = NULL;
 1249         compr->block_pointer_counter = 0;
 1250         compr->open_counter = 0;
 1251         if (old_compr->block_pointers != NULL ||
 1252             old_compr->block_pointers_dropped)
 1253             compr->block_pointers_dropped = 1;
 1254         else
 1255             compr->block_pointers_dropped = 0;
 1256     }
 1257     old_stream_data = (ZisofsFilterStreamData *) old_stream->data;
 1258     stream_data->orig = new_input_stream;
 1259     stream_data->size = old_stream_data->size;
 1260     stream_data->running = NULL;
 1261     stream_data->id = ++ziso_ino_id;
 1262     stream->data = stream_data;
 1263     *new_stream = stream;
 1264     return ISO_SUCCESS;
 1265 no_mem:
 1266     if (new_input_stream != NULL)
 1267         iso_stream_unref(new_input_stream);
 1268     if (stream != NULL)
 1269         iso_stream_unref(stream);
 1270     return ISO_OUT_OF_MEM;
 1271 }
 1272 
 1273 
 1274 static
 1275 int ziso_cmp_ino(IsoStream *s1, IsoStream *s2);
 1276 
 1277 static
 1278 int ziso_uncompress_cmp_ino(IsoStream *s1, IsoStream *s2);
 1279 
 1280 
 1281 IsoStreamIface ziso_stream_compress_class = {
 1282     4,
 1283     "ziso",
 1284     ziso_stream_open,
 1285     ziso_stream_close,
 1286     ziso_stream_get_size,
 1287     ziso_stream_compress,
 1288     ziso_stream_is_repeatable,
 1289     ziso_stream_get_id,
 1290     ziso_stream_free,
 1291     ziso_update_size,
 1292     ziso_get_input_stream,
 1293     ziso_cmp_ino,
 1294     ziso_clone_stream
 1295 };
 1296 
 1297 
 1298 IsoStreamIface ziso_stream_uncompress_class = {
 1299     4,
 1300     "osiz",
 1301     ziso_stream_open,
 1302     ziso_stream_close,
 1303     ziso_stream_get_size,
 1304     ziso_stream_uncompress,
 1305     ziso_stream_is_repeatable,
 1306     ziso_stream_get_id,
 1307     ziso_stream_free,
 1308     ziso_update_size,
 1309     ziso_get_input_stream,
 1310     ziso_uncompress_cmp_ino,
 1311     ziso_clone_stream
 1312 };
 1313 
 1314 
 1315 static
 1316 int ziso_cmp_ino(IsoStream *s1, IsoStream *s2)
 1317 {
 1318     /* This function may rely on being called by iso_stream_cmp_ino()
 1319        only with s1, s2 which both point to it as their .cmp_ino() function. 
 1320        It would be a programming error to let any other than
 1321        ziso_stream_compress_class point to ziso_cmp_ino().
 1322     */
 1323     if (s1->class != s2->class || (s1->class != &ziso_stream_compress_class &&
 1324                                    s2->class != &ziso_stream_uncompress_class))
 1325         iso_stream_cmp_ino(s1, s2, 1);
 1326 
 1327     /* Both streams apply the same treatment to their input streams */
 1328     return iso_stream_cmp_ino(iso_stream_get_input_stream(s1, 0),
 1329                               iso_stream_get_input_stream(s2, 0), 0);
 1330 }
 1331 
 1332 
 1333 static
 1334 int ziso_uncompress_cmp_ino(IsoStream *s1, IsoStream *s2)
 1335 {
 1336     /* This function may rely on being called by iso_stream_cmp_ino()
 1337        only with s1, s2 which both point to it as their .cmp_ino() function. 
 1338        It would be a programming error to let any other than
 1339        ziso_stream_uncompress_class point to ziso_uncompress_cmp_ino().
 1340        This fallback endangers transitivity of iso_stream_cmp_ino().
 1341     */
 1342     if (s1->class != s2->class ||
 1343         (s1->class != &ziso_stream_uncompress_class &&
 1344          s2->class != &ziso_stream_uncompress_class))
 1345         iso_stream_cmp_ino(s1, s2, 1);
 1346 
 1347     /* Both streams apply the same treatment to their input streams */
 1348     return iso_stream_cmp_ino(iso_stream_get_input_stream(s1, 0),
 1349                               iso_stream_get_input_stream(s2, 0), 0);
 1350 }
 1351 
 1352 
 1353 /* ------------------------------------------------------------------------- */
 1354 
 1355 
 1356 
 1357 #ifdef Libisofs_with_zliB
 1358 
 1359 static
 1360 void ziso_filter_free(FilterContext *filter)
 1361 {
 1362     /* no data are allocated */;
 1363 }
 1364 
 1365 
 1366 /*
 1367  * @param flag bit1= Install a decompression filter
 1368  */
 1369 static
 1370 int ziso_filter_get_filter(FilterContext *filter, IsoStream *original, 
 1371                            IsoStream **filtered, int flag)
 1372 {
 1373     IsoStream *str;
 1374     ZisofsFilterStreamData *data;
 1375     ZisofsComprStreamData *cnstd = NULL;
 1376     ZisofsUncomprStreamData *unstd = NULL;
 1377 
 1378     if (filter == NULL || original == NULL || filtered == NULL) {
 1379         return ISO_NULL_POINTER;
 1380     }
 1381 
 1382     str = calloc(sizeof(IsoStream), 1);
 1383     if (str == NULL) {
 1384         return ISO_OUT_OF_MEM;
 1385     }
 1386     if (flag & 2) {
 1387         unstd = calloc(sizeof(ZisofsUncomprStreamData), 1);
 1388         data = (ZisofsFilterStreamData *) unstd;
 1389     } else {
 1390         cnstd = calloc(sizeof(ZisofsComprStreamData), 1);
 1391         data = (ZisofsFilterStreamData *) cnstd;
 1392     }
 1393     if (data == NULL) {
 1394         free(str);
 1395         return ISO_OUT_OF_MEM;
 1396     }
 1397 
 1398     /* These data items are not owned by this filter object */
 1399     data->id = ++ziso_ino_id;
 1400     data->orig = original;
 1401     data->size = -1;
 1402     data->running = NULL;
 1403 
 1404     /* get reference to the source */
 1405     iso_stream_ref(data->orig);
 1406 
 1407     str->refcount = 1;
 1408     str->data = data;
 1409     if (flag & 2) {
 1410         unstd->zisofs_algo_num = 0;
 1411         unstd->header_size_div4 = 0;
 1412         unstd->block_size_log2 = 0;
 1413         str->class = &ziso_stream_uncompress_class;
 1414         ziso_osiz_ref_count++;
 1415     } else {
 1416         cnstd->orig_size = iso_stream_get_size(original);
 1417         cnstd->block_pointers = NULL;
 1418         cnstd->block_pointer_counter = 0;
 1419         cnstd->open_counter = 0;
 1420         cnstd->block_pointers_dropped = 0;
 1421         str->class = &ziso_stream_compress_class;
 1422         ziso_ref_count++;
 1423     }
 1424 
 1425     *filtered = str;
 1426 
 1427     return ISO_SUCCESS;
 1428 }
 1429 
 1430 
 1431 /* To be called by iso_file_add_filter().
 1432  * The FilterContext input parameter is not furtherly needed for the 
 1433  * emerging IsoStream.
 1434  */
 1435 static
 1436 int ziso_filter_get_compressor(FilterContext *filter, IsoStream *original, 
 1437                                IsoStream **filtered)
 1438 {
 1439     return ziso_filter_get_filter(filter, original, filtered, 0);
 1440 }
 1441 
 1442 static
 1443 int ziso_filter_get_uncompressor(FilterContext *filter, IsoStream *original, 
 1444                                  IsoStream **filtered)
 1445 {
 1446     return ziso_filter_get_filter(filter, original, filtered, 2);
 1447 }
 1448 
 1449 
 1450 /* Produce a parameter object suitable for iso_file_add_filter().
 1451  * It may be disposed by free() after all those calls are made.
 1452  *
 1453  * This is quite a dummy as it does not carry individual data.
 1454  * @param flag bit1= Install a decompression filter
 1455  */
 1456 static
 1457 int ziso_create_context(FilterContext **filter, int flag)
 1458 {
 1459     FilterContext *f;
 1460     
 1461     *filter = f = calloc(1, sizeof(FilterContext));
 1462     if (f == NULL) {
 1463         return ISO_OUT_OF_MEM;
 1464     }
 1465     f->refcount = 1;
 1466     f->version = 0;
 1467     f->data = NULL;
 1468     f->free = ziso_filter_free;
 1469     if (flag & 2)
 1470         f->get_filter = ziso_filter_get_uncompressor;
 1471     else
 1472         f->get_filter = ziso_filter_get_compressor;
 1473     return ISO_SUCCESS;
 1474 }
 1475 
 1476 #endif /* Libisofs_with_zliB */
 1477 
 1478 /*
 1479  * @param flag bit0= if_block_reduction rather than if_reduction
 1480  *             bit1= Install a decompression filter
 1481  *             bit2= only inquire availability of zisofs filtering
 1482  *             bit3= do not inquire size
 1483  */
 1484 int ziso_add_filter(IsoFile *file, int flag)
 1485 {
 1486 
 1487 #ifdef Libisofs_with_zliB
 1488 
 1489     int ret;
 1490     FilterContext *f = NULL;
 1491     IsoStream *stream;
 1492     off_t original_size = 0, filtered_size = 0;
 1493 
 1494     if (flag & 4)
 1495         return 2;
 1496 
 1497     original_size = iso_file_get_size(file);
 1498     if (!(flag & 2)) {
 1499         if (original_size <= 0 || ((flag & 1) && original_size <= 2048)) {
 1500             return 2;
 1501         }
 1502         if (original_size >= (off_t) ISO_ZISOFS_V1_LIMIT && !ziso_v2_enabled) {
 1503             return ISO_ZISOFS_TOO_LARGE;
 1504         }
 1505     }
 1506 
 1507     ret = ziso_create_context(&f, flag & 2);
 1508     if (ret < 0) {
 1509         return ret;
 1510     }
 1511     ret = iso_file_add_filter(file, f, 0);
 1512     free(f);
 1513     if (ret < 0) {
 1514         return ret;
 1515     }
 1516     if (flag & 8) /* size will be filled in by caller */
 1517         return ISO_SUCCESS;
 1518 
 1519     /* Run a full filter process getsize so that the size is cached */
 1520     stream = iso_file_get_stream(file);
 1521     filtered_size = iso_stream_get_size(stream);
 1522     if (filtered_size < 0) {
 1523         iso_file_remove_filter(file, 0);
 1524         return filtered_size;
 1525     }
 1526     if ((filtered_size >= original_size ||
 1527         ((flag & 1) && filtered_size / 2048 >= original_size / 2048))
 1528         && !(flag & 2)){
 1529         ret = iso_file_remove_filter(file, 0);
 1530         if (ret < 0) {
 1531             return ret;
 1532         }
 1533         return 2;
 1534     }
 1535     return ISO_SUCCESS;
 1536 
 1537 #else
 1538 
 1539     return ISO_ZLIB_NOT_ENABLED;
 1540 
 1541 #endif /* ! Libisofs_with_zliB */
 1542 
 1543 }
 1544 
 1545 
 1546 /* API function */
 1547 int iso_file_add_zisofs_filter(IsoFile *file, int flag)
 1548 {
 1549     return ziso_add_filter(file, flag & ~8);
 1550 }
 1551 
 1552 
 1553 /* API function */
 1554 int iso_zisofs_get_refcounts(off_t *ziso_count, off_t *osiz_count, int flag)
 1555 {
 1556     *ziso_count = ziso_ref_count;
 1557     *osiz_count = ziso_osiz_ref_count;
 1558     return ISO_SUCCESS;
 1559 }
 1560 
 1561 
 1562 int ziso_add_osiz_filter(IsoFile *file, uint8_t zisofs_algo[2],
 1563                          uint8_t header_size_div4, uint8_t block_size_log2,
 1564                          uint64_t uncompressed_size, int flag)
 1565 {   
 1566 
 1567 #ifdef Libisofs_with_zliB
 1568 
 1569     int ret;
 1570     ZisofsUncomprStreamData *unstd;
 1571 
 1572     ret = ziso_add_filter(file, 2 | 8);
 1573     if (ret < 0)
 1574         return ret;
 1575     unstd = iso_file_get_stream(file)->data;
 1576     ret = ziso_algo_to_num(zisofs_algo);
 1577     if (ret < 0)
 1578         return ISO_ZISOFS_WRONG_INPUT;
 1579     unstd->zisofs_algo_num = ret;
 1580     unstd->header_size_div4 = header_size_div4;
 1581     unstd->block_size_log2 = block_size_log2;
 1582     unstd->std.size = uncompressed_size;
 1583     return ISO_SUCCESS;
 1584     
 1585 #else
 1586 
 1587     return ISO_ZLIB_NOT_ENABLED;
 1588     
 1589 #endif /* ! Libisofs_with_zliB */
 1590     
 1591 }   
 1592 
 1593 
 1594 
 1595 /* Determine stream type : 1=ziso , -1=osiz , 0=other , 2=ziso_by_content
 1596    and eventual ZF field parameters
 1597    @param flag bit0= allow ziso_by_content which is based on content reading
 1598                bit1= do not inquire stream->class for filters
 1599                bit2= recognize zisofs2 by magic only if ziso_v2_enabled
 1600 */
 1601 int ziso_is_zisofs_stream(IsoStream *stream, int *stream_type,
 1602                           uint8_t zisofs_algo[2],
 1603                           int *header_size_div4, int *block_size_log2,
 1604                           uint64_t *uncompressed_size, int flag)
 1605 {
 1606     int ret, close_ret, algo_ret;
 1607     ZisofsFilterStreamData *data;
 1608     ZisofsComprStreamData *cnstd;
 1609     ZisofsUncomprStreamData *unstd;
 1610     uint8_t algo_num;
 1611 
 1612     *stream_type = 0; 
 1613     if (stream->class == &ziso_stream_compress_class && !(flag & 2)) {
 1614         *stream_type = 1;
 1615         cnstd = stream->data;
 1616         *uncompressed_size = cnstd->orig_size;
 1617         *block_size_log2 = ziso_decide_bs_log2((off_t) *uncompressed_size);
 1618         if (ziso_decide_v2_usage((off_t) *uncompressed_size)) {
 1619           zisofs_algo[0] = 'P';
 1620           zisofs_algo[1] = 'Z';
 1621           *header_size_div4 = 6;
 1622         } else if (*uncompressed_size < (uint64_t) ISO_ZISOFS_V1_LIMIT) {
 1623           zisofs_algo[0] = 'p';
 1624           zisofs_algo[1] = 'z';
 1625           *header_size_div4 = 4;
 1626         } else {
 1627           return 0;
 1628         }
 1629         return 1;
 1630     } else if(stream->class == &ziso_stream_uncompress_class && !(flag & 2)) {
 1631         *stream_type = -1;
 1632         data = stream->data;
 1633         unstd = stream->data;
 1634         ret = ziso_num_to_algo(unstd->zisofs_algo_num, zisofs_algo);
 1635         if (ret < 0) 
 1636             return ISO_ZISOFS_WRONG_INPUT;
 1637         *header_size_div4 = unstd->header_size_div4;
 1638         *block_size_log2 = unstd->block_size_log2;
 1639         *uncompressed_size = data->size;
 1640         return 1;
 1641     }
 1642     if (!(flag & 1))
 1643         return 0;
 1644 
 1645     ret = iso_stream_open(stream);
 1646     if (ret < 0) 
 1647         return ret;
 1648     ret = ziso_parse_zisofs_head(stream, &algo_num, header_size_div4,
 1649                                  block_size_log2, uncompressed_size,
 1650                                  (flag >> 2) & 1);
 1651     if (ret == 1) {
 1652         *stream_type = 2;
 1653         algo_ret = ziso_num_to_algo(algo_num, zisofs_algo);
 1654     } else {
 1655         ret = 0;
 1656         algo_ret = 1;
 1657     }
 1658     close_ret = iso_stream_close(stream);
 1659     if (algo_ret < 0) 
 1660         return ISO_ZISOFS_WRONG_INPUT;
 1661     if (close_ret < 0) 
 1662         return close_ret;
 1663 
 1664     return ret;
 1665 }
 1666 
 1667 
 1668 /* API */
 1669 int iso_zisofs_set_params(struct iso_zisofs_ctrl *params, int flag)
 1670 {
 1671 
 1672 #ifdef Libisofs_with_zliB
 1673 
 1674     if (params->version < 0 || params->version > 1)
 1675        return ISO_WRONG_ARG_VALUE;
 1676 
 1677     if (params->compression_level < 0 || params->compression_level > 9 ||
 1678         params->block_size_log2 < ISO_ZISOFS_V1_MIN_LOG2 ||
 1679         params->block_size_log2  > ISO_ZISOFS_V1_MAX_LOG2) {
 1680         return ISO_WRONG_ARG_VALUE;
 1681     }
 1682     if (params->version >= 1)
 1683         if (params->v2_enabled < 0 || params->v2_enabled > 2 ||
 1684             (params->v2_block_size_log2 != 0 &&
 1685              (params->v2_block_size_log2 < ISO_ZISOFS_V2_MIN_LOG2 ||
 1686               params->v2_block_size_log2 > ISO_ZISOFS_V2_MAX_LOG2)))
 1687             return ISO_WRONG_ARG_VALUE;
 1688     if (ziso_ref_count > 0) {
 1689         return ISO_ZISOFS_PARAM_LOCK;
 1690     }
 1691     ziso_compression_level = params->compression_level;
 1692     ziso_block_size_log2 = params->block_size_log2;
 1693 
 1694     if (params->version == 0)
 1695         return 1;
 1696 
 1697     ziso_v2_enabled = params->v2_enabled;
 1698     if (params->v2_block_size_log2 > 0)
 1699         ziso_v2_block_size_log2 = params->v2_block_size_log2;
 1700     if (params->max_total_blocks > 0)
 1701         ziso_max_total_blocks = params->max_total_blocks;
 1702     if (params->max_file_blocks > 0)
 1703         ziso_max_file_blocks = params->max_file_blocks;
 1704     if (params->block_number_target != 0)
 1705         ziso_block_number_target = params->block_number_target;
 1706     if (params->bpt_discard_file_blocks != 0)
 1707         ziso_many_block_limit = params->bpt_discard_file_blocks;
 1708     if (params->bpt_discard_free_ratio != 0.0)
 1709         ziso_keep_blocks_free_ratio = params->bpt_discard_free_ratio;
 1710 
 1711     return 1;
 1712     
 1713 #else
 1714 
 1715     return ISO_ZLIB_NOT_ENABLED;
 1716     
 1717 #endif /* ! Libisofs_with_zliB */
 1718     
 1719 }
 1720 
 1721 
 1722 /* API */
 1723 int iso_zisofs_get_params(struct iso_zisofs_ctrl *params, int flag)
 1724 {
 1725 
 1726 #ifdef Libisofs_with_zliB
 1727 
 1728     if (params->version < 0 || params->version > 1)
 1729        return ISO_WRONG_ARG_VALUE;
 1730 
 1731     params->compression_level = ziso_compression_level;
 1732     params->block_size_log2 = ziso_block_size_log2;
 1733     if (params->version == 1) {
 1734         params->v2_enabled = ziso_v2_enabled;
 1735         params->v2_block_size_log2 = ziso_v2_block_size_log2;
 1736         params->max_total_blocks = ziso_max_total_blocks;
 1737         params->current_total_blocks = ziso_block_pointer_mgt((uint64_t) 0, 3);
 1738         params->max_file_blocks = ziso_max_file_blocks;
 1739         params->block_number_target = ziso_block_number_target;
 1740         params->bpt_discard_file_blocks = ziso_many_block_limit;
 1741         params->bpt_discard_free_ratio = ziso_keep_blocks_free_ratio;
 1742     }
 1743     return 1;
 1744 
 1745 #else
 1746 
 1747     return ISO_ZLIB_NOT_ENABLED;
 1748     
 1749 #endif /* ! Libisofs_with_zliB */
 1750     
 1751 }
 1752 
 1753 
 1754 /* API */
 1755 int iso_stream_get_zisofs_par(IsoStream *stream, int *stream_type,
 1756                               uint8_t zisofs_algo[2], uint8_t* algo_num,
 1757                               int *block_size_log2, int flag)
 1758 {
 1759 
 1760 #ifdef Libisofs_with_zliB
 1761 
 1762     uint64_t uncompressed_size;
 1763     int header_size_div4, ret;
 1764     
 1765     if (stream == NULL)
 1766         return ISO_NULL_POINTER;
 1767     ret = ziso_is_zisofs_stream(stream, stream_type, zisofs_algo,
 1768                                 &header_size_div4, block_size_log2,
 1769                                 &uncompressed_size, 0);
 1770     if (ret <= 0 || (*stream_type != -1 && *stream_type != 1))
 1771         return 0;
 1772     *algo_num = ziso_algo_to_num(zisofs_algo);
 1773     return 1;
 1774 
 1775 #else
 1776 
 1777     return ISO_ZLIB_NOT_ENABLED;
 1778     
 1779 #endif /* ! Libisofs_with_zliB */
 1780 
 1781 }
 1782 
 1783 
 1784 /* API */
 1785 int iso_stream_zisofs_discard_bpt(IsoStream *stream, int flag)
 1786 {
 1787 
 1788 #ifdef Libisofs_with_zliB
 1789 
 1790     int ret;
 1791 
 1792     if (stream == NULL)
 1793         return ISO_NULL_POINTER;
 1794     ret = ziso_discard_bpt(stream, 1);
 1795     return ret;
 1796 
 1797 #else
 1798 
 1799     return ISO_ZLIB_NOT_ENABLED;
 1800     
 1801 #endif /* ! Libisofs_with_zliB */
 1802 
 1803 }
 1804 
 1805 
 1806 /* API */
 1807 int iso_zisofs_ctrl_susp_z2(int enable)
 1808 {
 1809     if (enable == 0 || enable == 1)
 1810         iso_zisofs2_enable_susp_z2 = enable;
 1811     return iso_zisofs2_enable_susp_z2;
 1812 }