Squash  0.7.0
file.c
1 /* Copyright (c) 2015 The Squash Authors
2  *
3  * Permission is hereby granted, free of charge, to any person
4  * obtaining a copy of this software and associated documentation
5  * files (the "Software"), to deal in the Software without
6  * restriction, including without limitation the rights to use, copy,
7  * modify, merge, publish, distribute, sublicense, and/or sell copies
8  * of the Software, and to permit persons to whom the Software is
9  * furnished to do so, subject to the following conditions:
10  *
11  * The above copyright notice and this permission notice shall be
12  * included in all copies or substantial portions of the Software.
13  *
14  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
18  * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
19  * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21  * SOFTWARE.
22  *
23  * Authors:
24  * Evan Nemerson <evan@nemerson.com>
25  */
26 
27 #define _FILE_OFFSET_BITS 64
28 #define _POSIX_C_SOURCE 200112L
29 
30 #include "internal.h"
31 
32 #ifndef SQUASH_FILE_BUF_SIZE
33 # define SQUASH_FILE_BUF_SIZE ((size_t) (1024 * 1024))
34 #endif
35 
36 #include <sys/types.h>
37 #include <sys/stat.h>
38 #include <sys/mman.h>
39 #include <unistd.h>
40 
41 /* #define SQUASH_MMAP_IO */
42 
47 typedef struct SquashMappedFile_s {
48  uint8_t* data;
49  size_t length;
50  size_t map_length;
51  size_t window_offset;
52  FILE* fp;
53  bool writable;
54 } SquashMappedFile;
55 
56 static const SquashMappedFile squash_mapped_file_empty = { MAP_FAILED, 0 };
57 
58 struct _SquashFile {
59  FILE* fp;
60  bool eof;
61  SquashStream* stream;
62  SquashStatus last_status;
63  SquashCodec* codec;
64  SquashOptions* options;
65  uint8_t buf[SQUASH_FILE_BUF_SIZE];
66 #if defined(SQUASH_MMAP_IO)
67  SquashMappedFile map;
68 #endif
69 };
70 
71 #if defined(__GNUC__)
72 __attribute__ ((__const__))
73 #endif
74 static size_t
75 squash_npot (size_t v) {
76  v--;
77  v |= v >> 1;
78  v |= v >> 2;
79  v |= v >> 4;
80  v |= v >> 8;
81  v |= v >> 16;
82 #if SIZE_MAX > UINT32_MAX
83  v |= v >> 32;
84 #endif
85  v++;
86  return v;
87 }
88 
89 static bool
90 squash_mapped_file_init_full (SquashMappedFile* mapped, FILE* fp, size_t length, bool length_is_suggestion, bool writable) {
91  assert (mapped != NULL);
92  assert (fp != NULL);
93 
94  if (mapped->data != MAP_FAILED)
95  munmap (mapped->data - mapped->window_offset, mapped->length + mapped->window_offset);
96 
97  int fd = fileno (fp);
98  if (fd == -1)
99  return false;
100 
101  int ires;
102  struct stat fp_stat;
103 
104  ires = fstat (fd, &fp_stat);
105  if (ires == -1 || !S_ISREG(fp_stat.st_mode) || (!writable && fp_stat.st_size == 0))
106  return false;
107 
108  off_t offset = ftello (fp);
109  if (offset < 0)
110  return false;
111 
112  if (writable) {
113  ires = ftruncate (fd, offset + (off_t) length);
114  if (ires == -1)
115  return false;
116  } else {
117  const size_t remaining = fp_stat.st_size - (size_t) offset;
118  if (remaining > 0) {
119  if (length == 0 || (length > remaining && length_is_suggestion)) {
120  length = remaining;
121  } else if (length > remaining) {
122  return false;
123  }
124  } else {
125  return false;
126  }
127  }
128  mapped->length = length;
129 
130  const size_t page_size = (size_t) sysconf (_SC_PAGE_SIZE);
131  mapped->window_offset = (size_t) offset % page_size;
132  mapped->map_length = length + mapped->window_offset;
133 
134  mapped->data = mmap (NULL, mapped->map_length, writable ? PROT_READ | PROT_WRITE : PROT_READ, MAP_SHARED, fd, offset - mapped->window_offset);
135  if (mapped->data == MAP_FAILED)
136  return false;
137 
138  mapped->data += mapped->window_offset;
139  mapped->fp = fp;
140  mapped->writable = writable;
141 
142  return true;
143 }
144 
145 static bool
146 squash_mapped_file_init (SquashMappedFile* mapped, FILE* fp, size_t length, bool writable) {
147  return squash_mapped_file_init_full (mapped, fp, length, false, writable);
148 }
149 
150 static void
151 squash_mapped_file_destroy (SquashMappedFile* mapped, bool success) {
152  if (mapped->data != MAP_FAILED) {
153  munmap (mapped->data - mapped->window_offset, mapped->length + mapped->window_offset);
154  mapped->data = MAP_FAILED;
155 
156  if (success) {
157  fseeko (mapped->fp, mapped->length, SEEK_CUR);
158  if (mapped->writable) {
159  ftruncate (fileno (mapped->fp), ftello (mapped->fp));
160  }
161  }
162  }
163 }
164 
212 SquashFile*
213 squash_file_open (const char* codec, const char* filename, const char* mode, ...) {
214  va_list ap;
215  SquashOptions* options;
216  SquashCodec* codec_i;
217 
218  assert (filename != NULL);
219  assert (mode != NULL);
220  assert (codec != NULL);
221 
222  codec_i = squash_get_codec (codec);
223  if (codec_i == NULL)
224  return NULL;
225 
226  va_start (ap, mode);
227  options = squash_options_newv (codec_i, ap);
228  va_end (ap);
229 
230  return squash_file_open_codec_with_options (codec_i, filename, mode, options);
231 }
232 
243 SquashFile*
244 squash_file_open_codec (SquashCodec* codec, const char* filename, const char* mode, ...) {
245  va_list ap;
246  SquashOptions* options;
247 
248  assert (filename != NULL);
249  assert (mode != NULL);
250  assert (codec != NULL);
251 
252  va_start (ap, mode);
253  options = squash_options_newv (codec, ap);
254  va_end (ap);
255 
256  return squash_file_open_codec_with_options (codec, filename, mode, options);
257 }
258 
269 SquashFile*
270 squash_file_open_with_options (const char* codec, const char* filename, const char* mode, SquashOptions* options) {
271  assert (filename != NULL);
272  assert (mode != NULL);
273  assert (codec != NULL);
274 
275  SquashCodec* codec_i = squash_get_codec (codec);
276  if (codec_i == NULL)
277  return NULL;
278 
279  return squash_file_open_codec_with_options (codec_i, filename, mode, options);
280 }
281 
292 SquashFile*
293 squash_file_open_codec_with_options (SquashCodec* codec, const char* filename, const char* mode, SquashOptions* options) {
294  assert (filename != NULL);
295  assert (mode != NULL);
296  assert (codec != NULL);
297 
298  FILE* fp = fopen (filename, mode);
299  if (fp == NULL)
300  return NULL;
301 
302  return squash_file_steal_codec_with_options (codec, fp, options);
303 }
304 
305 
317 SquashFile*
318 squash_file_steal (const char* codec, FILE* fp, ...) {
319  va_list ap;
320  SquashOptions* options;
321 
322  assert (fp != NULL);
323  assert (codec != NULL);
324 
325  SquashCodec* codec_i = squash_get_codec (codec);
326  if (codec_i == NULL)
327  return NULL;
328  va_start (ap, fp);
329  options = squash_options_newv (codec_i, ap);
330  va_end (ap);
331 
332  return squash_file_steal_codec_with_options (codec_i, fp, options);
333 }
334 
346 SquashFile*
347 squash_file_steal_codec (SquashCodec* codec, FILE* fp, ...) {
348  va_list ap;
349  SquashOptions* options;
350 
351  assert (fp != NULL);
352  assert (codec != NULL);
353 
354  va_start (ap, fp);
355  options = squash_options_newv (codec, ap);
356  va_end (ap);
357 
358  return squash_file_steal_codec_with_options (codec, fp, options);
359 }
360 
372 SquashFile*
373 squash_file_steal_with_options (const char* codec, FILE* fp, SquashOptions* options) {
374  assert (fp != NULL);
375  assert (codec != NULL);
376 
377  SquashCodec* codec_i = squash_get_codec (codec);
378  if (codec_i == NULL)
379  return NULL;
380 
381  return squash_file_steal_codec_with_options (codec_i, fp, options);
382 }
383 
395 SquashFile*
396 squash_file_steal_codec_with_options (SquashCodec* codec, FILE* fp, SquashOptions* options) {
397  assert (fp != NULL);
398  assert (codec != NULL);
399 
400  SquashFile* file = malloc (sizeof (SquashFile));
401  if (file == NULL)
402  return NULL;
403 
404  file->fp = fp;
405  file->eof = false;
406  file->stream = NULL;
407  file->last_status = SQUASH_OK;
408  file->codec = codec;
409  file->options = (options != NULL) ? squash_object_ref (options) : NULL;
410 #if defined(SQUASH_MMAP_IO)
411  file->map = squash_mapped_file_empty;
412 #endif
413 
414  return file;
415 }
416 
439 squash_file_read (SquashFile* file,
440  size_t* decompressed_length,
441  uint8_t decompressed[SQUASH_ARRAY_PARAM(*decompressed_length)]) {
443 
444  assert (file != NULL);
445  assert (decompressed_length != NULL);
446  assert (decompressed != NULL);
447 
448  if (file->stream == NULL) {
449  file->stream = squash_codec_create_stream_with_options (file->codec, SQUASH_STREAM_DECOMPRESS, file->options);
450  if (file->stream == NULL)
451  return squash_error (SQUASH_FAILED);
452  }
453  SquashStream* stream = file->stream;
454 
455  assert (stream->next_out == NULL);
456  assert (stream->avail_out == 0);
457 
458  if (stream->state == SQUASH_STREAM_STATE_FINISHED) {
459  *decompressed_length = 0;
460  return SQUASH_END_OF_STREAM;
461  }
462 
463  file->stream->next_out = decompressed;
464  file->stream->avail_out = *decompressed_length;
465 
466  while (stream->avail_out != 0) {
467  if (file->last_status < 0) {
468  res = file->last_status;
469  break;
470  }
471 
472  if (stream->state == SQUASH_STREAM_STATE_FINISHED)
473  break;
474 
475  if (file->last_status == SQUASH_PROCESSING) {
476  if (stream->state < SQUASH_STREAM_STATE_FINISHING) {
477  res = file->last_status = squash_stream_process (stream);
478  } else {
479  res = file->last_status = squash_stream_finish (stream);
480  }
481 
482  continue;
483  }
484 
485  assert (file->last_status == SQUASH_OK);
486 
487 #if defined(SQUASH_MMAP_IO)
488  if (file->map.data != MAP_FAILED)
489  squash_mapped_file_destroy (&(file->map), true);
490 
491  if (squash_mapped_file_init_full(&(file->map), file->fp, SQUASH_FILE_BUF_SIZE, true, false)) {
492  stream->next_in = file->map.data;
493  stream->avail_in = file->map.length;
494  } else
495 #endif
496  {
497  stream->next_in = file->buf;
498  stream->avail_in = fread (file->buf, 1, SQUASH_FILE_BUF_SIZE, file->fp);
499  }
500 
501  if (stream->avail_in == 0) {
502  if (feof (file->fp)) {
503  res = file->last_status = squash_stream_finish (stream);
504  } else {
505  res = SQUASH_IO;
506  break;
507  }
508  } else {
509  res = file->last_status = squash_stream_process (stream);
510  }
511  }
512 
513  *decompressed_length = (stream->next_out - decompressed);
514  stream->next_out = 0;
515  stream->avail_out = 0;
516 
517  return res;
518 }
519 
520 static SquashStatus
521 squash_file_write_internal (SquashFile* file,
522  size_t uncompressed_length,
523  const uint8_t uncompressed[SQUASH_ARRAY_PARAM(uncompressed_length)],
524  SquashOperation operation) {
525  SquashStatus res;
526 
527  assert (file != NULL);
528 
529  if (file->stream == NULL) {
530  file->stream = squash_codec_create_stream_with_options (file->codec, SQUASH_STREAM_COMPRESS, file->options);
531  if (file->stream == NULL)
532  return squash_error (SQUASH_FAILED);
533  }
534 
535  assert (file->stream->next_in == NULL);
536  assert (file->stream->avail_in == 0);
537  assert (file->stream->next_out == NULL);
538  assert (file->stream->avail_out == 0);
539 
540  file->stream->next_in = uncompressed;
541  file->stream->avail_in = uncompressed_length;
542 
543  file->stream->next_in = uncompressed;
544  file->stream->avail_in = uncompressed_length;
545 
546  do {
547  file->stream->next_out = file->buf;
548  file->stream->avail_out = SQUASH_FILE_BUF_SIZE;
549 
550  switch (operation) {
552  res = squash_stream_process (file->stream);
553  break;
555  res = squash_stream_flush (file->stream);
556  break;
558  res = squash_stream_finish (file->stream);
559  break;
561  squash_assert_unreachable ();
562  break;
563  }
564 
565  if (res > 0 && file->stream->avail_out != SQUASH_FILE_BUF_SIZE) {
566  size_t bytes_written = fwrite (file->buf, 1, SQUASH_FILE_BUF_SIZE - file->stream->avail_out, file->fp);
567  if (bytes_written != SQUASH_FILE_BUF_SIZE - file->stream->avail_out)
568  return SQUASH_IO;
569  }
570  } while (res == SQUASH_PROCESSING);
571 
572  file->stream->next_in = NULL;
573  file->stream->avail_in = 0;
574  file->stream->next_out = NULL;
575  file->stream->avail_out = 0;
576 
577  return res;
578 }
579 
601 squash_file_write (SquashFile* file,
602  size_t uncompressed_length,
603  const uint8_t uncompressed[SQUASH_ARRAY_PARAM(uncompressed_length)]) {
604  return squash_file_write_internal (file, uncompressed_length, uncompressed, SQUASH_OPERATION_PROCESS);
605 }
606 
619 squash_file_flush (SquashFile* file) {
620  SquashStatus res = squash_file_write_internal (file, 0, NULL, SQUASH_OPERATION_FLUSH);
621  fflush (file->fp);
622  return res;
623 }
624 
631 bool
632 squash_file_eof (SquashFile* file) {
633  return (file->stream->state == SQUASH_STREAM_STATE_FINISHED) && (feof (file->fp));
634 }
635 
655 squash_splice (const char* codec, SquashStreamType stream_type, FILE* fp_out, FILE* fp_in, size_t length, ...) {
656  assert (fp_in != NULL);
657  assert (fp_out != NULL);
658  assert (stream_type == SQUASH_STREAM_COMPRESS || stream_type == SQUASH_STREAM_DECOMPRESS);
659 
660  SquashCodec* codec_i = squash_get_codec (codec);
661  if (codec_i == NULL)
663 
664  SquashOptions* options = NULL;
665  va_list ap;
666  va_start (ap, length);
667  options = squash_options_newv (codec_i, ap);
668  va_end (ap);
669 
670  return squash_splice_codec_with_options (codec_i, stream_type, fp_out, fp_in, length, options);
671 }
672 
692 squash_splice_codec (SquashCodec* codec, SquashStreamType stream_type, FILE* fp_out, FILE* fp_in, size_t length, ...) {
693  assert (fp_in != NULL);
694  assert (fp_out != NULL);
695  assert (stream_type == SQUASH_STREAM_COMPRESS || stream_type == SQUASH_STREAM_DECOMPRESS);
696  assert (codec != NULL);
697 
698  SquashOptions* options = NULL;
699  va_list ap;
700  va_start (ap, length);
701  options = squash_options_newv (codec, ap);
702  va_end (ap);
703 
704  return squash_splice_codec_with_options (codec, stream_type, fp_out, fp_in, length, options);
705 }
706 
726 squash_splice_with_options (const char* codec, SquashStreamType stream_type, FILE* fp_out, FILE* fp_in, size_t length, SquashOptions* options) {
727  assert (fp_in != NULL);
728  assert (fp_out != NULL);
729  assert (stream_type == SQUASH_STREAM_COMPRESS || stream_type == SQUASH_STREAM_DECOMPRESS);
730 
731  SquashCodec* codec_i = squash_get_codec (codec);
732  if (codec_i == NULL)
734 
735  return squash_splice_codec_with_options (codec_i, stream_type, fp_out, fp_in, length, options);
736 }
737 
738 static SquashStatus
739 squash_splice_map (FILE* fp_in, FILE* fp_out, size_t length, SquashStreamType stream_type, SquashCodec* codec, SquashOptions* options) {
741  SquashMappedFile mapped_in = squash_mapped_file_empty;
742  SquashMappedFile mapped_out = squash_mapped_file_empty;
743 
744  if (stream_type == SQUASH_STREAM_COMPRESS) {
745  if (!squash_mapped_file_init (&mapped_in, fp_in, length, false))
746  goto cleanup;
747 
748  const size_t max_output_length = squash_codec_get_max_compressed_size(codec, mapped_in.length);
749  if (!squash_mapped_file_init (&mapped_out, fp_out, max_output_length, true))
750  goto cleanup;
751 
752  res = squash_codec_compress_with_options (codec, &mapped_out.length, mapped_out.data, mapped_in.length, mapped_in.data, options);
753  if (res != SQUASH_OK)
754  goto cleanup;
755 
756  squash_mapped_file_destroy (&mapped_in, true);
757  squash_mapped_file_destroy (&mapped_out, true);
758  } else {
759  if (!squash_mapped_file_init (&mapped_in, fp_in, 0, false))
760  goto cleanup;
761 
762  const SquashCodecInfo codec_info = squash_codec_get_info (codec);
763  const bool knows_uncompressed = ((codec_info & SQUASH_CODEC_INFO_KNOWS_UNCOMPRESSED_SIZE) == SQUASH_CODEC_INFO_KNOWS_UNCOMPRESSED_SIZE);
764 
765  size_t max_output_length = knows_uncompressed ?
766  squash_codec_get_uncompressed_size(codec, mapped_in.length, mapped_in.data) :
767  squash_npot (mapped_in.length) << 3;
768 
769  do {
770  if (!squash_mapped_file_init (&mapped_out, fp_out, max_output_length, true))
771  goto cleanup;
772 
773  res = squash_codec_decompress_with_options (codec, &mapped_out.length, mapped_out.data, mapped_in.length, mapped_in.data, options);
774  if (res == SQUASH_OK) {
775  squash_mapped_file_destroy (&mapped_in, true);
776  squash_mapped_file_destroy (&mapped_out, true);
777  } else {
778  max_output_length <<= 1;
779  }
780  } while (!knows_uncompressed && res == SQUASH_BUFFER_FULL);
781  }
782 
783  cleanup:
784 
785  squash_mapped_file_destroy (&mapped_in, false);
786  squash_mapped_file_destroy (&mapped_out, false);
787 
788  return res;
789 }
790 
791 static SquashStatus
792 squash_splice_stream (FILE* fp_in,
793  FILE* fp_out,
794  size_t length,
795  SquashStreamType stream_type,
796  SquashCodec* codec,
797  SquashOptions* options) {
799  SquashFile* file = NULL;
800  size_t remaining = length;
801  uint8_t* data = NULL;
802  size_t data_length = 0;
803 #if defined(SQUASH_MMAP_IO)
804  bool first_block = true;
805  SquashMappedFile map = squash_mapped_file_empty;
806 
807  if (stream_type == SQUASH_STREAM_COMPRESS) {
808  file = squash_file_steal_codec_with_options (fp_out, codec, options);
809  assert (file != NULL);
810 
811  while (length == 0 || remaining != 0) {
812  const size_t req_size = (length == 0 || remaining > SQUASH_FILE_BUF_SIZE) ? SQUASH_FILE_BUF_SIZE : remaining;
813  if (!squash_mapped_file_init_full(&map, fp_in, req_size, true, false)) {
814  if (first_block)
815  goto nomap;
816  else {
817  break;
818  }
819  } else {
820  first_block = false;
821  }
822 
823  res = squash_file_write (file, map.length, map.data);
824  if (res != SQUASH_OK)
825  goto cleanup;
826 
827  if (length != 0)
828  remaining -= map.length;
829  squash_mapped_file_destroy (&map, true);
830  }
831  } else { /* stream_type == SQUASH_STREAM_DECOMPRESS */
832  file = squash_file_steal_codec_with_options (fp_in, codec, options);
833  assert (file != NULL);
834 
835  while (length == 0 || remaining > 0) {
836  const size_t req_size = (length == 0 || remaining > SQUASH_FILE_BUF_SIZE) ? SQUASH_FILE_BUF_SIZE : remaining;
837  if (!squash_mapped_file_init_full(&map, fp_out, req_size, true, true)) {
838  if (first_block)
839  goto nomap;
840  else {
841  break;
842  }
843  } else {
844  first_block = false;
845  }
846 
847  res = squash_file_read (file, &map.length, map.data);
848  if (res < 0)
849  goto cleanup;
850 
851  if (res == SQUASH_END_OF_STREAM) {
852  assert (map.length == 0);
853  res = SQUASH_OK;
854  squash_mapped_file_destroy (&map, true);
855  goto cleanup;
856  }
857 
858  squash_mapped_file_destroy (&map, true);
859  }
860  }
861 
862  nomap:
863 #endif /* defined(SQUASH_MMAP_IO) */
864 
865  if (res != SQUASH_OK) {
866  file = squash_file_steal_codec_with_options (codec, (stream_type == SQUASH_STREAM_COMPRESS ? fp_out : fp_in), options);
867  if (file == NULL) {
868  res = squash_error (SQUASH_FAILED);
869  goto cleanup;
870  }
871 
872  data = malloc (SQUASH_FILE_BUF_SIZE);
873  if (data == NULL) {
874  res = squash_error (SQUASH_MEMORY);
875  goto cleanup;
876  }
877 
878  if (stream_type == SQUASH_STREAM_COMPRESS) {
879  while (length == 0 || remaining != 0) {
880  const size_t req_size = (length == 0 || remaining > SQUASH_FILE_BUF_SIZE) ? SQUASH_FILE_BUF_SIZE : remaining;
881 
882  data_length = fread (data, 1, req_size, fp_in);
883  if (data_length == 0) {
884  res = feof (fp_in) ? SQUASH_OK : squash_error (SQUASH_IO);
885  goto cleanup;
886  }
887 
888  res = squash_file_write (file, data_length, data);
889  if (res != SQUASH_OK)
890  goto cleanup;
891 
892  if (remaining != 0) {
893  assert (data_length <= remaining);
894  remaining -= data_length;
895  }
896  }
897  } else {
898  while (length == 0 || remaining != 0) {
899  data_length = (length == 0 || remaining > SQUASH_FILE_BUF_SIZE) ? SQUASH_FILE_BUF_SIZE : remaining;
900  res = squash_file_read (file, &data_length, data);
901  if (res < 0) {
902  break;
903  } else if (res == SQUASH_PROCESSING) {
904  res = SQUASH_OK;
905  }
906 
907  if (data_length > 0) {
908  size_t bytes_written = fwrite (data, 1, data_length, fp_out);
909  /* fprintf (stderr, "%s:%d: bytes_written: %zu\n", __FILE__, __LINE__, data_length); */
910  assert (bytes_written == data_length);
911  if (bytes_written == 0) {
912  res = squash_error (SQUASH_IO);
913  break;
914  }
915 
916  if (remaining != 0) {
917  assert (data_length <= remaining);
918  remaining -= data_length;
919  }
920  }
921 
922  if (res == SQUASH_END_OF_STREAM) {
923  res = SQUASH_OK;
924  break;
925  }
926  }
927  }
928  }
929 
930  cleanup:
931 
932  squash_file_free (file, NULL);
933 #if defined(SQUASH_MMAP_IO)
934  squash_mapped_file_destroy (&map, false);
935 #endif
936  free (data);
937 
938  return res;
939 }
940 
961  SquashStreamType stream_type,
962  FILE* fp_out,
963  FILE* fp_in,
964  size_t length,
965  SquashOptions* options) {
967 
968  assert (fp_in != NULL);
969  assert (fp_out != NULL);
970  assert (stream_type == SQUASH_STREAM_COMPRESS || stream_type == SQUASH_STREAM_DECOMPRESS);
971  assert (codec != NULL);
972 
973  if (codec->impl.create_stream == NULL)
974  res = squash_splice_map (fp_in, fp_out, length, stream_type, codec, options);
975 
976  if (res != SQUASH_OK)
977  return squash_splice_stream (fp_in, fp_out, length, stream_type, codec, options);
978 
979  return res;
980 }
981 
1003 squash_file_close (SquashFile* file) {
1004  FILE* fp = NULL;
1005  SquashStatus res = squash_file_free (file, &fp);
1006  if (fp != NULL)
1007  fclose (fp);
1008  return res;
1009 }
1010 
1026 squash_file_free (SquashFile* file, FILE** fp) {
1027  SquashStatus res = SQUASH_OK;
1028 
1029  if (file == NULL)
1030  return SQUASH_OK;
1031 
1032  if (file->stream != NULL && file->stream->stream_type == SQUASH_STREAM_COMPRESS)
1033  res = squash_file_write_internal (file, 0, NULL, SQUASH_OPERATION_FINISH);
1034 
1035  fflush (file->fp);
1036 
1037 #if defined(SQUASH_MMAP_IO)
1038  squash_mapped_file_destroy (&(file->map), false);
1039 #endif
1040 
1041  if (fp != NULL)
1042  *fp = file->fp;
1043 
1044  squash_object_unref (file->stream);
1045  squash_object_unref (file->options);
1046  free (file);
1047 
1048  return res;
1049 }
1050 
SquashStream * squash_codec_create_stream_with_options(SquashCodec *codec, SquashStreamType stream_type, SquashOptions *options)
Create a new stream with existing SquashOptions.
Definition: codec.c:485
SquashStatus squash_stream_finish(SquashStream *stream)
Finish writing to a stream.
Definition: stream.c:743
SquashCodec * squash_get_codec(const char *codec)
Retrieve a SquashCodec.
Definition: context.c:149
Operation failed.
Definition: status.h:41
SquashCodecInfo
Information about the codec.
Definition: codec.h:36
Reached the end of the stream while decoding.
Definition: status.h:39
Operation partially completed.
Definition: status.h:38
A decompression stream.
Definition: stream.h:40
SquashOptions * squash_options_newv(SquashCodec *codec, va_list options)
Create a new group of options from a variadic list.
Definition: options.c:560
size_t squash_codec_get_uncompressed_size(SquashCodec *codec, size_t compressed_length, const uint8_t compressed[SQUASH_ARRAY_PARAM(compressed_length)])
Get the uncompressed size of the compressed buffer.
Definition: codec.c:383
SquashStatus squash_splice_codec_with_options(SquashCodec *codec, SquashStreamType stream_type, FILE *fp_out, FILE *fp_in, size_t length, SquashOptions *options)
compress or decompress the contents of one file to another
Definition: file.c:960
bool squash_file_eof(SquashFile *file)
Determine whether the file has reached the end of file.
Definition: file.c:632
Insufficient space in buffer.
Definition: status.h:46
SquashFile * squash_file_open(const char *codec, const char *filename, const char *mode,...)
Open a file.
Definition: file.c:213
Continue processing the stream normally.
Definition: stream.h:52
void * squash_object_unref(void *obj)
Decrement the reference count on an object.
Definition: object.c:255
size_t squash_codec_get_max_compressed_size(SquashCodec *codec, size_t uncompressed_length)
Get the maximum buffer size necessary to store compressed data.
Definition: codec.c:433
SquashStatus squash_file_read(SquashFile *file, size_t *decompressed_length, uint8_t decompressed[SQUASH_ARRAY_PARAM(*decompressed_length)])
Read from a compressed file.
Definition: file.c:439
SquashStatus squash_codec_compress_with_options(SquashCodec *codec, size_t *compressed_length, uint8_t compressed[SQUASH_ARRAY_PARAM(*compressed_length)], size_t uncompressed_length, const uint8_t uncompressed[SQUASH_ARRAY_PARAM(uncompressed_length)], SquashOptions *options)
Compress a buffer with an existing SquashOptions.
Definition: codec.c:545
Not enough memory is available.
Definition: status.h:45
SquashStatus squash_codec_decompress_with_options(SquashCodec *codec, size_t *decompressed_length, uint8_t decompressed[SQUASH_ARRAY_PARAM(*decompressed_length)], size_t compressed_length, const uint8_t compressed[SQUASH_ARRAY_PARAM(compressed_length)], SquashOptions *options)
Decompress a buffer with an existing SquashOptions.
Definition: codec.c:698
SquashStatus squash_stream_flush(SquashStream *stream)
Flush a stream.
Definition: stream.c:732
SquashStatus squash_file_flush(SquashFile *file)
immediately write any buffered data to a file
Definition: file.c:619
SquashFile * squash_file_open_codec_with_options(SquashCodec *codec, const char *filename, const char *mode, SquashOptions *options)
Open a file using a codec instance with the specified options.
Definition: file.c:293
SquashFile * squash_file_open_with_options(const char *codec, const char *filename, const char *mode, SquashOptions *options)
Open a file with the specified options.
Definition: file.c:270
SquashStatus
Status codes.
Definition: status.h:36
Flush the stream.
Definition: stream.h:53
A compression stream.
Definition: stream.h:39
SquashFile * squash_file_steal_codec(SquashCodec *codec, FILE *fp,...)
Open an existing stdio file using a codec instance.
Definition: file.c:347
SquashOperation
Operations to perform on a stream.
Definition: stream.h:51
SquashStatus squash_file_close(SquashFile *file)
Close a file.
Definition: file.c:1003
SquashStatus squash_error(SquashStatus status)
Emit an error.
Definition: status.c:173
SquashStatus squash_splice_with_options(const char *codec, SquashStreamType stream_type, FILE *fp_out, FILE *fp_in, size_t length, SquashOptions *options)
compress or decompress the contents of one file to another
Definition: file.c:726
SquashFile * squash_file_steal_codec_with_options(SquashCodec *codec, FILE *fp, SquashOptions *options)
Open an existing stdio file using a codec instance with the specified options.
Definition: file.c:396
void * squash_object_ref(void *obj)
Increment the reference count on an object.
Definition: object.c:206
SquashStatus squash_splice(const char *codec, SquashStreamType stream_type, FILE *fp_out, FILE *fp_in, size_t length,...)
compress or decompress the contents of one file to another
Definition: file.c:655
SquashStatus squash_stream_process(SquashStream *stream)
Process a stream.
Definition: stream.c:717
SquashStatus squash_file_free(SquashFile *file, FILE **fp)
Free a file.
Definition: file.c:1026
Finish processing the stream.
Definition: stream.h:54
SquashFile * squash_file_steal_with_options(const char *codec, FILE *fp, SquashOptions *options)
Open an existing stdio file with the specified options.
Definition: file.c:373
SquashFile * squash_file_open_codec(SquashCodec *codec, const char *filename, const char *mode,...)
Open a file using a codec instance.
Definition: file.c:244
SquashCodecInfo squash_codec_get_info(SquashCodec *codec)
Get a bitmask of information about the codec.
Definition: codec.c:1002
Operation completed successfully.
Definition: status.h:37
The compressed data encodes the length of the uncompressed data without having to decompress it...
Definition: codec.h:43
SquashStatus squash_splice_codec(SquashCodec *codec, SquashStreamType stream_type, FILE *fp_out, FILE *fp_in, size_t length,...)
compress or decompress the contents of one file to another
Definition: file.c:692
SquashStreamType
Stream type.
Definition: stream.h:38
One or more of the parameters were not valid.
Definition: status.h:43
SquashFile * squash_file_steal(const char *codec, FILE *fp,...)
Open an existing stdio file.
Definition: file.c:318
SquashStatus squash_file_write(SquashFile *file, size_t uncompressed_length, const uint8_t uncompressed[SQUASH_ARRAY_PARAM(uncompressed_length)])
Write data to a compressed file.
Definition: file.c:601