"Fossies" - the Fresh Open Source Software Archive 
Member "libisofs-1.5.4/libisofs/filters/gzip.c" (8 Jul 2020, 20011 Bytes) of package /linux/misc/libisofs-1.5.4.tar.gz:
As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style:
standard) with prefixed line numbers and
code folding option.
Alternatively you can here
view or
download the uninterpreted source code file.
For more information about "gzip.c" see the
Fossies "Dox" file reference documentation.
1 /*
2 * Copyright (c) 2009 - 2011 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 gzip
10 * compression resp. uncompression, read its output and forward it as IsoStream
11 * output to an IsoFile.
12 * The gzip compression is done via zlib by Jean-loup Gailly and Mark Adler
13 * who state in <zlib.h>:
14 * "The data format used by the zlib library is described by RFCs (Request for
15 * Comments) 1950 to 1952 in the files http://www.ietf.org/rfc/rfc1950.txt
16 * (zlib format), rfc1951.txt (deflate format) and rfc1952.txt (gzip format)."
17 *
18 */
19
20 #ifdef HAVE_CONFIG_H
21 #include "../config.h"
22 #endif
23
24 #include "../libisofs.h"
25 #include "../filter.h"
26 #include "../fsource.h"
27 #include "../util.h"
28 #include "../stream.h"
29
30 #include <sys/types.h>
31 #include <sys/time.h>
32 #include <sys/wait.h>
33 #include <unistd.h>
34 #include <stdio.h>
35 #include <errno.h>
36 #include <string.h>
37
38 #ifdef Libisofs_with_zliB
39 #include <zlib.h>
40 #else
41 /* If zlib is not available then this code is a dummy */
42 #endif
43
44
45 /*
46 * A filter that encodes or decodes the content of gzip compressed files.
47 */
48
49
50 /* --------------------------- GzipFilterRuntime ------------------------- */
51
52
53 /* Individual runtime properties exist only as long as the stream is opened.
54 */
55 typedef struct
56 {
57
58 #ifdef Libisofs_with_zliB
59
60 z_stream strm; /* The zlib processing context */
61
62 #endif
63
64 char *in_buffer;
65 char *out_buffer;
66 int in_buffer_size;
67 int out_buffer_size;
68 char *rpt; /* out_buffer + read_bytes */
69
70 off_t in_counter;
71 off_t out_counter;
72
73 int do_flush; /* flush mode for deflate() changes at end of input */
74
75 int error_ret;
76
77 } GzipFilterRuntime;
78
79 #ifdef Libisofs_with_zliB
80
81 static
82 int gzip_running_destroy(GzipFilterRuntime **running, int flag)
83 {
84 GzipFilterRuntime *o= *running;
85 if (o == NULL)
86 return 0;
87 if (o->in_buffer != NULL)
88 free(o->in_buffer);
89 if (o->out_buffer != NULL)
90 free(o->out_buffer);
91 free((char *) o);
92 *running = NULL;
93 return 1;
94 }
95
96
97 static
98 int gzip_running_new(GzipFilterRuntime **running, int flag)
99 {
100 GzipFilterRuntime *o;
101
102 *running = o = calloc(sizeof(GzipFilterRuntime), 1);
103 if (o == NULL) {
104 return ISO_OUT_OF_MEM;
105 }
106 memset(&(o->strm), 0, sizeof(o->strm));
107 o->in_buffer = NULL;
108 o->out_buffer = NULL;
109 o->in_buffer_size = 0;
110 o->out_buffer_size = 0;
111 o->rpt = NULL;
112 o->in_counter = 0;
113 o->out_counter = 0;
114 o->do_flush = Z_NO_FLUSH;
115 o->error_ret = 1;
116
117 o->in_buffer_size= 2048;
118 o->out_buffer_size= 2048;
119 o->in_buffer = calloc(o->in_buffer_size, 1);
120 o->out_buffer = calloc(o->out_buffer_size, 1);
121 if (o->in_buffer == NULL || o->out_buffer == NULL)
122 goto failed;
123 o->rpt = o->out_buffer;
124 return 1;
125 failed:
126 gzip_running_destroy(running, 0);
127 return -1;
128 }
129 #endif /* Libisofs_with_zliB */
130
131
132 /* ---------------------------- GzipFilterStreamData --------------------- */
133
134
135 /* Counts the number of active compression filters */
136 static off_t gzip_ref_count = 0;
137
138 /* Counts the number of active uncompression filters */
139 static off_t gunzip_ref_count = 0;
140
141
142 #ifdef Libisofs_with_zliB
143 /* Parameter for deflateInit2() , see <zlib.h> */
144
145 /* ??? get this from zisofs.c ziso_compression_level ?
146 * ??? have an own global parameter API ?
147 * For now level 6 seems to be a fine compromise between speed and size.
148 */
149 static int gzip_compression_level = 6;
150
151 #endif /* Libisofs_with_zliB */
152
153
154 /*
155 * The data payload of an individual Gzip Filter IsoStream
156 */
157 /* IMPORTANT: Any change must be reflected by gzip_clone_stream() */
158 typedef struct
159 {
160 IsoStream *orig;
161
162 off_t size; /* -1 means that the size is unknown yet */
163
164 GzipFilterRuntime *running; /* is non-NULL when open */
165
166 ino_t id;
167
168 } GzipFilterStreamData;
169
170
171
172 #ifdef Libisofs_with_zliB
173
174 /* Each individual GzipFilterStreamData needs a unique id number. */
175 /* >>> This is very suboptimal:
176 The counter can rollover.
177 */
178 static ino_t gzip_ino_id = 0;
179
180 #endif /* Libisofs_with_zliB */
181
182
183 static
184 int gzip_stream_uncompress(IsoStream *stream, void *buf, size_t desired);
185
186
187 /*
188 * Methods for the IsoStreamIface of a Gzip Filter object.
189 */
190
191 /*
192 * @param flag bit0= original stream is not open
193 */
194 static
195 int gzip_stream_close_flag(IsoStream *stream, int flag)
196 {
197
198 #ifdef Libisofs_with_zliB
199
200 GzipFilterStreamData *data;
201
202 if (stream == NULL) {
203 return ISO_NULL_POINTER;
204 }
205 data = stream->data;
206
207 if (data->running == NULL) {
208 return 1;
209 }
210 if (stream->class->read == &gzip_stream_uncompress) {
211 inflateEnd(&(data->running->strm));
212 } else {
213 deflateEnd(&(data->running->strm));
214 }
215 gzip_running_destroy(&(data->running), 0);
216
217 if (flag & 1)
218 return 1;
219 return iso_stream_close(data->orig);
220
221 #else
222
223 return ISO_ZLIB_NOT_ENABLED;
224
225 #endif
226
227 }
228
229
230 static
231 int gzip_stream_close(IsoStream *stream)
232 {
233 return gzip_stream_close_flag(stream, 0);
234 }
235
236
237 /*
238 * @param flag bit0= do not run .get_size() if size is < 0
239 */
240 static
241 int gzip_stream_open_flag(IsoStream *stream, int flag)
242 {
243
244 #ifdef Libisofs_with_zliB
245
246 GzipFilterStreamData *data;
247 GzipFilterRuntime *running = NULL;
248 int ret;
249 z_stream *strm;
250
251 if (stream == NULL) {
252 return ISO_NULL_POINTER;
253 }
254 data = (GzipFilterStreamData*) stream->data;
255 if (data->running != NULL) {
256 return ISO_FILE_ALREADY_OPENED;
257 }
258 if (data->size < 0 && !(flag & 1)) {
259 /* Do the size determination run now, so that the size gets cached
260 and .get_size() will not fail on an opened stream.
261 */
262 stream->class->get_size(stream);
263 }
264
265 ret = gzip_running_new(&running,
266 stream->class->read == &gzip_stream_uncompress);
267 if (ret < 0) {
268 return ret;
269 }
270 data->running = running;
271
272 /* Start up zlib compression context */
273 strm = &(running->strm);
274 strm->zalloc = Z_NULL;
275 strm->zfree = Z_NULL;
276 strm->opaque = Z_NULL;
277 if (stream->class->read == &gzip_stream_uncompress) {
278 ret = inflateInit2(strm, 15 | 16);
279 } else {
280 ret = deflateInit2(strm, gzip_compression_level, Z_DEFLATED,
281 15 | 16, 8, Z_DEFAULT_STRATEGY);
282 }
283 if (ret != Z_OK)
284 return ISO_ZLIB_COMPR_ERR;
285 strm->next_out = (Bytef *) running->out_buffer;
286 strm->avail_out = running->out_buffer_size;
287
288 /* Open input stream */
289 ret = iso_stream_open(data->orig);
290 if (ret < 0) {
291 return ret;
292 }
293
294 return 1;
295
296 #else
297
298 return ISO_ZLIB_NOT_ENABLED;
299
300 #endif
301
302 }
303
304
305 static
306 int gzip_stream_open(IsoStream *stream)
307 {
308 return gzip_stream_open_flag(stream, 0);
309 }
310
311
312 /*
313 * @param flag bit1= uncompress rather than compress
314 */
315 static
316 int gzip_stream_convert(IsoStream *stream, void *buf, size_t desired, int flag)
317 {
318
319 #ifdef Libisofs_with_zliB
320
321 int ret, todo, cnv_ret, c_bytes;
322 GzipFilterStreamData *data;
323 GzipFilterRuntime *rng;
324 size_t fill = 0;
325 z_stream *strm;
326
327 if (stream == NULL) {
328 return ISO_NULL_POINTER;
329 }
330 data = stream->data;
331 rng= data->running;
332 if (rng == NULL) {
333 return ISO_FILE_NOT_OPENED;
334 }
335 strm = &(rng->strm);
336 if (rng->error_ret < 0) {
337 return rng->error_ret;
338 } else if (rng->error_ret == 0) {
339 if (rng->out_buffer_size - strm->avail_out
340 - (rng->rpt - rng->out_buffer) <= 0)
341 return 0;
342 }
343
344 while (1) {
345
346 /* Transfer eventual converted bytes from strm to buf */
347 c_bytes = rng->out_buffer_size - strm->avail_out
348 - (rng->rpt - rng->out_buffer);
349 if (c_bytes > 0) {
350 todo = desired - fill;
351 if (todo > c_bytes)
352 todo = c_bytes;
353 memcpy(((char *) buf) + fill, rng->rpt, todo);
354 rng->rpt += todo;
355 fill += todo;
356 rng->out_counter += todo;
357 }
358
359 if (fill >= desired || rng->error_ret == 0)
360 return fill;
361
362 /* All buffered out data are consumed now */
363 rng->rpt = rng->out_buffer;
364 strm->next_out = (Bytef *) rng->out_buffer;
365 strm->avail_out = rng->out_buffer_size;
366
367 if (strm->avail_in == 0) {
368 /* All pending input is consumed. Get new input. */
369 ret = iso_stream_read(data->orig, rng->in_buffer,
370 rng->in_buffer_size);
371 if (ret < 0)
372 return (rng->error_ret = ret);
373 if (ret == 0) {
374 if (flag & 2) {
375 /* Early input EOF */
376 return (rng->error_ret = ISO_ZLIB_EARLY_EOF);
377 } else {
378 /* Tell zlib by the next call that it is over */
379 rng->do_flush = Z_FINISH;
380 }
381 }
382 strm->next_in = (Bytef *) rng->in_buffer;
383 strm->avail_in = ret;
384 rng->in_counter += ret;
385 }
386
387 /* Submit input and fetch output until input is consumed */
388 while (1) {
389 if (flag & 2) {
390 cnv_ret = inflate(strm, rng->do_flush);
391 } else {
392 cnv_ret = deflate(strm, rng->do_flush);
393 }
394 if (cnv_ret == Z_STREAM_ERROR || cnv_ret == Z_BUF_ERROR) {
395 return (rng->error_ret = ISO_ZLIB_COMPR_ERR);
396 }
397 if ((int) strm->avail_out < rng->out_buffer_size)
398 break; /* output is available */
399 if (strm->avail_in == 0) /* all pending input consumed */
400 break;
401 }
402 if (cnv_ret == Z_STREAM_END)
403 rng->error_ret = 0;
404 }
405 return fill;
406
407 #else
408
409 return ISO_ZLIB_NOT_ENABLED;
410
411 #endif
412
413 }
414
415 static
416 int gzip_stream_compress(IsoStream *stream, void *buf, size_t desired)
417 {
418 return gzip_stream_convert(stream, buf, desired, 0);
419 }
420
421 static
422 int gzip_stream_uncompress(IsoStream *stream, void *buf, size_t desired)
423 {
424 return gzip_stream_convert(stream, buf, desired, 2);
425 }
426
427
428 static
429 off_t gzip_stream_get_size(IsoStream *stream)
430 {
431 int ret, ret_close;
432 off_t count = 0;
433 GzipFilterStreamData *data;
434 char buf[64 * 1024];
435 size_t bufsize = 64 * 1024;
436
437 if (stream == NULL) {
438 return ISO_NULL_POINTER;
439 }
440 data = stream->data;
441
442 if (data->size >= 0) {
443 return data->size;
444 }
445
446 /* Run filter command and count output bytes */
447 ret = gzip_stream_open_flag(stream, 1);
448 if (ret < 0) {
449 return ret;
450 }
451 while (1) {
452 ret = stream->class->read(stream, buf, bufsize);
453 if (ret <= 0)
454 break;
455 count += ret;
456 }
457 ret_close = gzip_stream_close(stream);
458 if (ret < 0)
459 return ret;
460 if (ret_close < 0)
461 return ret_close;
462
463 data->size = count;
464 return count;
465 }
466
467
468 static
469 int gzip_stream_is_repeatable(IsoStream *stream)
470 {
471 /* Only repeatable streams are accepted as orig */
472 return 1;
473 }
474
475
476 static
477 void gzip_stream_get_id(IsoStream *stream, unsigned int *fs_id,
478 dev_t *dev_id, ino_t *ino_id)
479 {
480 GzipFilterStreamData *data;
481
482 data = stream->data;
483 *fs_id = ISO_FILTER_FS_ID;
484 *dev_id = ISO_FILTER_GZIP_DEV_ID;
485 *ino_id = data->id;
486 }
487
488
489 static
490 void gzip_stream_free(IsoStream *stream)
491 {
492 GzipFilterStreamData *data;
493
494 if (stream == NULL) {
495 return;
496 }
497 data = stream->data;
498 if (data->running != NULL) {
499 gzip_stream_close(stream);
500 }
501 if (stream->class->read == &gzip_stream_uncompress) {
502 if (--gunzip_ref_count < 0)
503 gunzip_ref_count = 0;
504 } else {
505 if (--gzip_ref_count < 0)
506 gzip_ref_count = 0;
507 }
508 iso_stream_unref(data->orig);
509 free(data);
510 }
511
512
513 static
514 int gzip_update_size(IsoStream *stream)
515 {
516 /* By principle size is determined only once */
517 return 1;
518 }
519
520
521 static
522 IsoStream *gzip_get_input_stream(IsoStream *stream, int flag)
523 {
524 GzipFilterStreamData *data;
525
526 if (stream == NULL) {
527 return NULL;
528 }
529 data = stream->data;
530 return data->orig;
531 }
532
533
534 static
535 int gzip_clone_stream(IsoStream *old_stream, IsoStream **new_stream, int flag)
536 {
537
538 #ifdef Libisofs_with_zliB
539
540 int ret;
541 IsoStream *new_input_stream, *stream;
542 GzipFilterStreamData *stream_data, *old_stream_data;
543
544 if (flag)
545 return ISO_STREAM_NO_CLONE; /* unknown option required */
546
547 stream_data = calloc(1, sizeof(GzipFilterStreamData));
548 if (stream_data == NULL)
549 return ISO_OUT_OF_MEM;
550 ret = iso_stream_clone_filter_common(old_stream, &stream,
551 &new_input_stream, 0);
552 if (ret < 0) {
553 free((char *) stream_data);
554 return ret;
555 }
556 old_stream_data = (GzipFilterStreamData *) old_stream->data;
557 stream_data->orig = new_input_stream;
558 stream_data->size = old_stream_data->size;
559 stream_data->running = NULL;
560 stream_data->id = ++gzip_ino_id;
561 stream->data = stream_data;
562 *new_stream = stream;
563 return ISO_SUCCESS;
564
565 #else /* Libisofs_with_zliB */
566
567 return ISO_STREAM_NO_CLONE;
568
569 #endif /* ! Libisofs_with_zliB */
570
571 }
572
573 static
574 int gzip_cmp_ino(IsoStream *s1, IsoStream *s2);
575
576 static
577 int gzip_uncompress_cmp_ino(IsoStream *s1, IsoStream *s2);
578
579
580 IsoStreamIface gzip_stream_compress_class = {
581 4,
582 "gzip",
583 gzip_stream_open,
584 gzip_stream_close,
585 gzip_stream_get_size,
586 gzip_stream_compress,
587 gzip_stream_is_repeatable,
588 gzip_stream_get_id,
589 gzip_stream_free,
590 gzip_update_size,
591 gzip_get_input_stream,
592 gzip_cmp_ino,
593 gzip_clone_stream
594 };
595
596
597 IsoStreamIface gzip_stream_uncompress_class = {
598 4,
599 "pizg",
600 gzip_stream_open,
601 gzip_stream_close,
602 gzip_stream_get_size,
603 gzip_stream_uncompress,
604 gzip_stream_is_repeatable,
605 gzip_stream_get_id,
606 gzip_stream_free,
607 gzip_update_size,
608 gzip_get_input_stream,
609 gzip_uncompress_cmp_ino,
610 gzip_clone_stream
611 };
612
613
614 static
615 int gzip_cmp_ino(IsoStream *s1, IsoStream *s2)
616 {
617 /* This function may rely on being called by iso_stream_cmp_ino()
618 only with s1, s2 which both point to it as their .cmp_ino() function.
619 It would be a programming error to let any other than
620 gzip_stream_compress_class point to gzip_cmp_ino().
621 This fallback endangers transitivity of iso_stream_cmp_ino().
622 */
623 if (s1->class != s2->class || (s1->class != &gzip_stream_compress_class &&
624 s2->class != &gzip_stream_compress_class))
625 return iso_stream_cmp_ino(s1, s2, 1);
626
627 /* Both streams apply the same treatment to their input streams */
628 return iso_stream_cmp_ino(iso_stream_get_input_stream(s1, 0),
629 iso_stream_get_input_stream(s2, 0), 0);
630 }
631
632
633 static
634 int gzip_uncompress_cmp_ino(IsoStream *s1, IsoStream *s2)
635 {
636 /* This function may rely on being called by iso_stream_cmp_ino()
637 only with s1, s2 which both point to it as their .cmp_ino() function.
638 It would be a programming error to let any other than
639 gzip_stream_uncompress_class point to gzip_uncompress_cmp_ino().
640 */
641 if (s1->class != s2->class ||
642 (s1->class != &gzip_stream_uncompress_class &&
643 s2->class != &gzip_stream_uncompress_class))
644 return iso_stream_cmp_ino(s1, s2, 1);
645
646 /* Both streams apply the same treatment to their input streams */
647 return iso_stream_cmp_ino(iso_stream_get_input_stream(s1, 0),
648 iso_stream_get_input_stream(s2, 0), 0);
649 }
650
651
652 /* ------------------------------------------------------------------------- */
653
654
655 #ifdef Libisofs_with_zliB
656
657 static
658 void gzip_filter_free(FilterContext *filter)
659 {
660 /* no data are allocated */;
661 }
662
663 /*
664 * @param flag bit1= Install a decompression filter
665 */
666 static
667 int gzip_filter_get_filter(FilterContext *filter, IsoStream *original,
668 IsoStream **filtered, int flag)
669 {
670 IsoStream *str;
671 GzipFilterStreamData *data;
672
673 if (filter == NULL || original == NULL || filtered == NULL) {
674 return ISO_NULL_POINTER;
675 }
676
677 str = calloc(sizeof(IsoStream), 1);
678 if (str == NULL) {
679 return ISO_OUT_OF_MEM;
680 }
681 data = calloc(sizeof(GzipFilterStreamData), 1);
682 if (data == NULL) {
683 free(str);
684 return ISO_OUT_OF_MEM;
685 }
686
687 /* These data items are not owned by this filter object */
688 data->id = ++gzip_ino_id;
689 data->orig = original;
690 data->size = -1;
691 data->running = NULL;
692
693 /* get reference to the source */
694 iso_stream_ref(data->orig);
695
696 str->refcount = 1;
697 str->data = data;
698 if (flag & 2) {
699 str->class = &gzip_stream_uncompress_class;
700 gunzip_ref_count++;
701 } else {
702 str->class = &gzip_stream_compress_class;
703 gzip_ref_count++;
704 }
705
706 *filtered = str;
707
708 return ISO_SUCCESS;
709 }
710
711 /* To be called by iso_file_add_filter().
712 * The FilterContext input parameter is not furtherly needed for the
713 * emerging IsoStream.
714 */
715 static
716 int gzip_filter_get_compressor(FilterContext *filter, IsoStream *original,
717 IsoStream **filtered)
718 {
719 return gzip_filter_get_filter(filter, original, filtered, 0);
720 }
721
722 static
723 int gzip_filter_get_uncompressor(FilterContext *filter, IsoStream *original,
724 IsoStream **filtered)
725 {
726 return gzip_filter_get_filter(filter, original, filtered, 2);
727 }
728
729
730 /* Produce a parameter object suitable for iso_file_add_filter().
731 * It may be disposed by free() after all those calls are made.
732 *
733 * This is quite a dummy as it does not carry individual data.
734 * @param flag bit1= Install a decompression filter
735 */
736 static
737 int gzip_create_context(FilterContext **filter, int flag)
738 {
739 FilterContext *f;
740
741 *filter = f = calloc(1, sizeof(FilterContext));
742 if (f == NULL) {
743 return ISO_OUT_OF_MEM;
744 }
745 f->refcount = 1;
746 f->version = 0;
747 f->data = NULL;
748 f->free = gzip_filter_free;
749 if (flag & 2)
750 f->get_filter = gzip_filter_get_uncompressor;
751 else
752 f->get_filter = gzip_filter_get_compressor;
753 return ISO_SUCCESS;
754 }
755
756 #endif /* Libisofs_with_zliB */
757
758 /*
759 * @param flag bit0= if_block_reduction rather than if_reduction
760 * bit1= Install a decompression filter
761 * bit2= only inquire availability of gzip filtering
762 * bit3= do not inquire size
763 */
764 int gzip_add_filter(IsoFile *file, int flag)
765 {
766
767 #ifdef Libisofs_with_zliB
768
769 int ret;
770 FilterContext *f = NULL;
771 IsoStream *stream;
772 off_t original_size = 0, filtered_size = 0;
773
774 if (flag & 4)
775 return 2;
776
777 original_size = iso_file_get_size(file);
778
779 ret = gzip_create_context(&f, flag & 2);
780 if (ret < 0) {
781 return ret;
782 }
783 ret = iso_file_add_filter(file, f, 0);
784 free(f);
785 if (ret < 0) {
786 return ret;
787 }
788 if (flag & 8) /* size will be filled in by caller */
789 return ISO_SUCCESS;
790
791 /* Run a full filter process getsize so that the size is cached */
792 stream = iso_file_get_stream(file);
793 filtered_size = iso_stream_get_size(stream);
794 if (filtered_size < 0) {
795 iso_file_remove_filter(file, 0);
796 return filtered_size;
797 }
798 if ((filtered_size >= original_size ||
799 ((flag & 1) && filtered_size / 2048 >= original_size / 2048))
800 && !(flag & 2)){
801 ret = iso_file_remove_filter(file, 0);
802 if (ret < 0) {
803 return ret;
804 }
805 return 2;
806 }
807 return ISO_SUCCESS;
808
809 #else
810
811 return ISO_ZLIB_NOT_ENABLED;
812
813 #endif /* ! Libisofs_with_zliB */
814
815 }
816
817
818 /* API function */
819 int iso_file_add_gzip_filter(IsoFile *file, int flag)
820 {
821 return gzip_add_filter(file, flag & ~8);
822 }
823
824
825 /* API function */
826 int iso_gzip_get_refcounts(off_t *gzip_count, off_t *gunzip_count, int flag)
827 {
828 *gzip_count = gzip_ref_count;
829 *gunzip_count = gunzip_ref_count;
830 return ISO_SUCCESS;
831 }
832