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