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
ziso_decide_v2_usage(off_t orig_size)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
ziso_decide_bs_log2(off_t orig_size)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
ziso_running_destroy(ZisofsFilterRuntime ** running,int flag)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
ziso_running_new(ZisofsFilterRuntime ** running,off_t orig_size,int flag)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
ziso_block_pointer_mgt(uint64_t num,int mode)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
ziso_discard_bpt(IsoStream * stream,int flag)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
ziso_stream_close_flag(IsoStream * stream,int flag)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
ziso_stream_close(IsoStream * stream)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
ziso_stream_open_flag(IsoStream * stream,int flag)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
ziso_stream_open(IsoStream * stream)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
ziso_stream_measure_size(IsoStream * stream,int flag)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
ziso_stream_compress(IsoStream * stream,void * buf,size_t desired)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
ziso_algo_to_num(uint8_t zisofs_algo[2])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
ziso_num_to_algo(uint8_t num,uint8_t zisofs_algo[2])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
ziso_parse_zisofs_head(IsoStream * stream,uint8_t * ziso_algo_num,int * header_size_div4,int * block_size_log2,uint64_t * uncompressed_size,int flag)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
ziso_stream_uncompress(IsoStream * stream,void * buf,size_t desired)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
ziso_stream_get_size(IsoStream * stream)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
ziso_stream_is_repeatable(IsoStream * stream)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
ziso_stream_get_id(IsoStream * stream,unsigned int * fs_id,dev_t * dev_id,ino_t * ino_id)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
ziso_stream_free(IsoStream * stream)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
ziso_update_size(IsoStream * stream)1196 int ziso_update_size(IsoStream *stream)
1197 {
1198     /* By principle size is determined only once */
1199     return 1;
1200 }
1201 
1202 
1203 static
ziso_get_input_stream(IsoStream * stream,int flag)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
ziso_clone_stream(IsoStream * old_stream,IsoStream ** new_stream,int flag)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
ziso_cmp_ino(IsoStream * s1,IsoStream * s2)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
ziso_uncompress_cmp_ino(IsoStream * s1,IsoStream * s2)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
ziso_filter_free(FilterContext * filter)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
ziso_filter_get_filter(FilterContext * filter,IsoStream * original,IsoStream ** filtered,int flag)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
ziso_filter_get_compressor(FilterContext * filter,IsoStream * original,IsoStream ** filtered)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
ziso_filter_get_uncompressor(FilterContext * filter,IsoStream * original,IsoStream ** filtered)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
ziso_create_context(FilterContext ** filter,int flag)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  */
ziso_add_filter(IsoFile * file,int flag)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 */
iso_file_add_zisofs_filter(IsoFile * file,int flag)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 */
iso_zisofs_get_refcounts(off_t * ziso_count,off_t * osiz_count,int flag)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 
ziso_add_osiz_filter(IsoFile * file,uint8_t zisofs_algo[2],uint8_t header_size_div4,uint8_t block_size_log2,uint64_t uncompressed_size,int flag)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 */
ziso_is_zisofs_stream(IsoStream * stream,int * stream_type,uint8_t zisofs_algo[2],int * header_size_div4,int * block_size_log2,uint64_t * uncompressed_size,int flag)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 */
iso_zisofs_set_params(struct iso_zisofs_ctrl * params,int flag)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 */
iso_zisofs_get_params(struct iso_zisofs_ctrl * params,int flag)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 */
iso_stream_get_zisofs_par(IsoStream * stream,int * stream_type,uint8_t zisofs_algo[2],uint8_t * algo_num,int * block_size_log2,int flag)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 */
iso_stream_zisofs_discard_bpt(IsoStream * stream,int flag)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 */
iso_zisofs_ctrl_susp_z2(int enable)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 }
1813