]> git.ipfire.org Git - thirdparty/libarchive.git/commitdiff
Hack up the lzma decoder to at least compile; there aren't
authorTim Kientzle <kientzle@gmail.com>
Sat, 8 Nov 2008 21:44:40 +0000 (16:44 -0500)
committerTim Kientzle <kientzle@gmail.com>
Sat, 8 Nov 2008 21:44:40 +0000 (16:44 -0500)
any tests for it yet, so I'm not sure if it actually works.

SVN-Revision: 239

libarchive/archive_read_support_compression_lzma.c

index 85b886840ce78365a941ab9af5d33cb296af32ac..0f8e4b36fcbc2b69060d5b500ab1c37b1fe70d6b 100644 (file)
@@ -1,5 +1,5 @@
 /*-
- * Copyright (c) 2008 Miklos Vajna
+ * Copyright (c) 2003-2008 Tim Kientzle and Miklos Vajna
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
 
 #include "archive_platform.h"
 
-__FBSDID("FIXME");
-
+__FBSDID("$FreeBSD$");
 
 #ifdef HAVE_ERRNO_H
 #include <errno.h>
 #endif
+#include <stdio.h>
 #ifdef HAVE_STDLIB_H
 #include <stdlib.h>
 #endif
@@ -48,33 +48,52 @@ __FBSDID("FIXME");
 #include "archive_private.h"
 #include "archive_read_private.h"
 
-#ifdef HAVE_LZMADEC_H
+#if HAVE_LZMADEC_H
 struct private_data {
        lzmadec_stream   stream;
-       unsigned char   *uncompressed_buffer;
-       size_t           uncompressed_buffer_size;
-       unsigned char   *read_next;
+       unsigned char   *out_block;
+       size_t           out_block_size;
        int64_t          total_out;
        char             eof; /* True = found end of compressed data. */
 };
 
-static int     finish(struct archive_read *);
-static ssize_t read_ahead(struct archive_read *, const void **, size_t);
-static ssize_t read_consume(struct archive_read *, size_t);
-static int     drive_decompressor(struct archive_read *a, struct private_data *);
+/* Lzma source */
+static ssize_t lzma_source_read(struct archive_read_source *, const void **);
+static int     lzma_source_close(struct archive_read_source *);
 #endif
 
-/* These two functions are defined even if we lack the library.  See below. */
-static int     bid(const void *, size_t);
-static int     init(struct archive_read *, const void *, size_t);
+/*
+ * Note that we can detect lzma archives even if we can't decompress
+ * them.  (In fact, we like detecting them because we can give better
+ * error messages.)  So the bid framework here gets compiled even
+ * if lzmadec is unavailable.
+ */
+static int     lzma_reader_bid(struct archive_reader *, const void *, size_t);
+static struct archive_read_source *lzma_reader_init(struct archive_read *,
+    struct archive_reader *, struct archive_read_source *,
+    const void *, size_t);
+static int     lzma_reader_free(struct archive_reader *);
 
 int
 archive_read_support_compression_lzma(struct archive *_a)
 {
        struct archive_read *a = (struct archive_read *)_a;
-       if (__archive_read_register_compression(a, bid, init) != NULL)
-               return (ARCHIVE_OK);
-       return (ARCHIVE_FATAL);
+       struct archive_reader *reader = __archive_read_get_reader(a);
+
+       if (reader == NULL)
+               return (ARCHIVE_FATAL);
+
+       reader->data = NULL;
+       reader->bid = lzma_reader_bid;
+       reader->init = lzma_reader_init;
+       reader->free = lzma_reader_free;
+       return (ARCHIVE_OK);
+}
+
+static int
+lzma_reader_free(struct archive_reader *self){
+       (void)self; /* UNUSED */
+       return (ARCHIVE_OK);
 }
 
 /*
@@ -85,11 +104,13 @@ archive_read_support_compression_lzma(struct archive *_a)
  * from verifying as much as we would like.
  */
 static int
-bid(const void *buff, size_t len)
+lzma_reader_bid(struct archive_reader *self, const void *buff, size_t len)
 {
        const unsigned char *buffer;
        int bits_checked;
 
+       (void)self; /* UNUSED */
+
        if (len < 1)
                return (0);
 
@@ -102,7 +123,6 @@ bid(const void *buff, size_t len)
        return (bits_checked);
 }
 
-
 #ifndef HAVE_LZMADEC_H
 
 /*
@@ -111,9 +131,12 @@ bid(const void *buff, size_t len)
  * and emit a useful message.
  */
 static int
-init(struct archive_read *a, const void *buff, size_t n)
+lzma_reader_init(struct archive_read *a, struct archive_reader *reader,
+    struct archive_read_source *upstream, const void *buff, size_t n)
 {
        (void)a;        /* UNUSED */
+       (void)reader;   /* UNUSED */
+       (void)upstream; /* UNUSED */
        (void)buff;     /* UNUSED */
        (void)n;        /* UNUSED */
 
@@ -128,37 +151,43 @@ init(struct archive_read *a, const void *buff, size_t n)
 /*
  * Setup the callbacks.
  */
-static int
-init(struct archive_read *a, const void *buff, size_t n)
+static struct archive_read_source *
+lzma_reader_init(struct archive_read *a, struct archive_reader *reader,
+    struct archive_read_source *upstream, const void *buff, size_t n)
 {
+       static const size_t out_block_size = 64 * 1024;
+       void *out_block;
+       struct archive_read_source *self;
        struct private_data *state;
        int ret;
 
+       (void)reader; /* UNUSED */
+
        a->archive.compression_code = ARCHIVE_COMPRESSION_LZMA;
        a->archive.compression_name = "lzma";
 
-       state = (struct private_data *)malloc(sizeof(*state));
-       if (state == NULL) {
+       self = calloc(sizeof(*self), 1);
+       state = (struct private_data *)calloc(sizeof(*state), 1);
+       out_block = (unsigned char *)malloc(out_block_size);
+       if (self == NULL || state == NULL || out_block == NULL) {
                archive_set_error(&a->archive, ENOMEM,
                    "Can't allocate data for %s decompression",
                    a->archive.compression_name);
-               return (ARCHIVE_FATAL);
+               free(out_block);
+               free(state);
+               free(self);
+               return (NULL);
        }
-       memset(state, 0, sizeof(*state));
 
-       state->uncompressed_buffer_size = 64 * 1024;
-       state->uncompressed_buffer = (unsigned char *)malloc(state->uncompressed_buffer_size);
-       state->stream.next_out = state->uncompressed_buffer;
-       state->read_next = state->uncompressed_buffer;
-       state->stream.avail_out = state->uncompressed_buffer_size;
 
-       if (state->uncompressed_buffer == NULL) {
-               archive_set_error(&a->archive, ENOMEM,
-                   "Can't allocate %s decompression buffers",
-                   a->archive.compression_name);
-               free(state);
-               return (ARCHIVE_FATAL);
-       }
+       self->archive = a;
+       self->data = state;
+       state->out_block_size = out_block_size;
+       state->out_block = out_block;
+       self->upstream = upstream;
+       self->read = lzma_source_read;
+       self->skip = NULL; /* not supported */
+       self->close = lzma_source_close;
 
        /*
         * A bug in lzmadec.h: stream.next_in should be marked 'const'
@@ -166,27 +195,22 @@ init(struct archive_read *a, const void *buff, size_t n)
         * next_in pointer, only reads it).  The result: this ugly
         * cast to remove 'const'.
         */
-       state->stream.next_in = (uint8_t *)(uintptr_t)(const void *)buff;
+       state->stream.next_in = (char *)(uintptr_t)(const void *)buff;
        state->stream.avail_in = n;
 
-       a->decompressor->read_ahead = read_ahead;
-       a->decompressor->consume = read_consume;
-       a->decompressor->skip = NULL; /* not supported */
-       a->decompressor->finish = finish;
+       state->stream.next_out = state->out_block;
+       state->stream.avail_out = state->out_block_size;
 
        /* Initialize compression library. */
        ret = lzmadec_init(&(state->stream));
-       if (ret == LZMADEC_OK) {
-               a->decompressor->data = state;
-               return (ARCHIVE_OK);
-       }
+
+       if (ret == LZMADEC_OK)
+               return (self);
 
        /* Library setup failed: Clean up. */
        archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
            "Internal error initializing %s library",
            a->archive.compression_name);
-       free(state->uncompressed_buffer);
-       free(state);
 
        /* Override the error message if we know what really went wrong. */
        switch (ret) {
@@ -198,174 +222,113 @@ init(struct archive_read *a, const void *buff, size_t n)
        case LZMADEC_MEM_ERROR:
                archive_set_error(&a->archive, ENOMEM,
                    "Internal error initializing compression library: "
-                   "not enough memory");
+                   "out of memory");
                break;
        }
 
-       return (ARCHIVE_FATAL);
+       free(state->out_block);
+       free(state);
+       free(self);
+       return (NULL);
 }
 
 /*
- * Return a block of data from the decompression buffer.  Decompress more
- * as necessary.
+ * Return the next block of decompressed data.
  */
 static ssize_t
-read_ahead(struct archive_read *a, const void **p, size_t min)
+lzma_source_read(struct archive_read_source *self, const void **p)
 {
        struct private_data *state;
-       size_t read_avail, was_avail;
+       size_t read_avail, decompressed;
+       const void *read_buf;
        int ret;
 
-       state = (struct private_data *)a->decompressor->data;
-       if (!a->client_reader) {
-               archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER,
-                   "No read callback is registered?  "
-                   "This is probably an internal programming error.");
-               return (ARCHIVE_FATAL);
-       }
+       state = (struct private_data *)self->data;
+       read_avail = 0;
 
-       read_avail = state->stream.next_out - state->read_next;
+       /* Empty our output buffer. */
+       state->stream.next_out = state->out_block;
+       state->stream.avail_out = state->out_block_size;
 
-       if (read_avail + state->stream.avail_out < min) {
-               memmove(state->uncompressed_buffer, state->read_next,
-                   read_avail);
-               state->read_next = state->uncompressed_buffer;
-               state->stream.next_out = state->read_next + read_avail;
-               state->stream.avail_out
-                   = state->uncompressed_buffer_size - read_avail;
-       }
+       /* Try to fill the output buffer. */
+       for (;;) {
+               /* If the last upstream block is done, get another one. */
+               if (state->stream.avail_in == 0) {
+                       ret = (self->upstream->read)(self->upstream,
+                           &read_buf);
+                       /* stream.next_in is really const, but lzmadec
+                        * doesn't declare it so. <sigh> */
+                       state->stream.next_in
+                           = (unsigned char *)(uintptr_t)read_buf;
+                       if (ret < 0)
+                               return (ARCHIVE_FATAL);
+                       /* There is no more data, return whatever we have. */
+                       if (ret == 0) {
+                               *p = state->out_block;
+                               decompressed = state->stream.next_out
+                                   - state->out_block;
+                               state->total_out += decompressed;
+                               return (decompressed);
+                       }
+                       state->stream.avail_in = ret;
+               }
 
-       while (read_avail < min &&              /* Haven't satisfied min. */
-           read_avail < state->uncompressed_buffer_size) { /* !full */
-               was_avail = read_avail;
-               if ((ret = drive_decompressor(a, state)) < ARCHIVE_OK)
-                       return (ret);
-               if (ret == ARCHIVE_EOF)
-                       break; /* Break on EOF even if we haven't met min. */
-               read_avail = state->stream.next_out - state->read_next;
-               if (was_avail == read_avail) /* No progress? */
+               /* Decompress as much as we can in one pass. */
+               ret = lzmadec_decode(&(state->stream),
+                   state->stream.avail_in == 0);
+               switch (ret) {
+               case LZMADEC_STREAM_END: /* Found end of stream. */
+                       /* TODO: Peek ahead to see if there's another
+                        * stream so we can mimic the behavior of gunzip
+                        * on concatenated streams. */
+                       state->eof = 1;
+               case LZMADEC_OK: /* Decompressor made some progress. */
+                       /* If we filled our buffer, update stats and return. */
+                       if (state->eof || state->stream.avail_out == 0) {
+                               *p = state->out_block;
+                               decompressed = state->stream.next_out
+                                   - state->out_block;
+                               state->total_out += decompressed;
+                               return (decompressed);
+                       }
                        break;
+               default:
+                       /* Return an error. */
+                       archive_set_error(&self->archive->archive,
+                           ARCHIVE_ERRNO_MISC,
+                           "%s decompression failed",
+                           self->archive->archive.compression_name);
+                       return (ARCHIVE_FATAL);
+               }
        }
-
-       *p = state->read_next;
-       return (read_avail);
-}
-
-/*
- * Mark a previously-returned block of data as read.
- */
-static ssize_t
-read_consume(struct archive_read *a, size_t n)
-{
-       struct private_data *state;
-
-       state = (struct private_data *)a->decompressor->data;
-       a->archive.file_position += n;
-       state->read_next += n;
-       if (state->read_next > state->stream.next_out)
-               __archive_errx(1, "Request to consume too many "
-                   "bytes from lzma decompressor");
-       return (n);
 }
 
 /*
  * Clean up the decompressor.
  */
 static int
-finish(struct archive_read *a)
+lzma_source_close(struct archive_read_source *self)
 {
        struct private_data *state;
        int ret;
 
-       state = (struct private_data *)a->decompressor->data;
+       state = (struct private_data *)self->data;
        ret = ARCHIVE_OK;
        switch (lzmadec_end(&(state->stream))) {
        case LZMADEC_OK:
                break;
        default:
-               archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+               archive_set_error(&(self->archive->archive),
+                   ARCHIVE_ERRNO_MISC,
                    "Failed to clean up %s compressor",
-                   a->archive.compression_name);
+                   self->archive->archive.compression_name);
                ret = ARCHIVE_FATAL;
        }
 
-       free(state->uncompressed_buffer);
+       free(state->out_block);
        free(state);
-
-       a->decompressor->data = NULL;
+       free(self);
        return (ret);
 }
 
-/*
- * Utility function to pull data through decompressor, reading input
- * blocks as necessary.
- */
-static int
-drive_decompressor(struct archive_read *a, struct private_data *state)
-{
-       ssize_t ret;
-       int decompressed, total_decompressed;
-       uint8_t *output;
-       const void *read_buf;
-
-       if (state->eof)
-               return (ARCHIVE_EOF);
-       total_decompressed = 0;
-       for (;;) {
-               if (state->stream.avail_in == 0) {
-                       read_buf = state->stream.next_in;
-                       ret = (a->client_reader)(&a->archive, a->client_data,
-                           &read_buf);
-                       state->stream.next_in = (void *)(uintptr_t)read_buf;
-                       if (ret < 0) {
-                               /*
-                                * TODO: Find a better way to handle
-                                * this read failure.
-                                */
-                               goto fatal;
-                       }
-                       if (ret == 0  &&  total_decompressed == 0) {
-                               archive_set_error(&a->archive, EIO,
-                                   "Premature end of %s compressed data",
-                                   a->archive.compression_name);
-                               return (ARCHIVE_FATAL);
-                       }
-                       a->archive.raw_position += ret;
-                       state->stream.avail_in = ret;
-               }
-
-               {
-                       output = state->stream.next_out;
-
-                       /* Decompress some data. */
-                       ret = lzmadec_decode(&(state->stream), state->stream.avail_in == 0);
-                       decompressed = state->stream.next_out - output;
-
-                       /* Accumulate the total bytes of output. */
-                       state->total_out += decompressed;
-                       total_decompressed += decompressed;
-
-                       switch (ret) {
-                       case LZMADEC_OK: /* Decompressor made some progress. */
-                               if (decompressed > 0)
-                                       return (ARCHIVE_OK);
-                               break;
-                       case LZMADEC_STREAM_END: /* Found end of stream. */
-                               state->eof = 1;
-                               return (ARCHIVE_OK);
-                       default:
-                               /* Any other return value is an error. */
-                               goto fatal;
-                       }
-               }
-       }
-       return (ARCHIVE_OK);
-
-       /* Return a fatal error. */
-fatal:
-       archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
-           "%s decompression failed", a->archive.compression_name);
-       return (ARCHIVE_FATAL);
-}
-
 #endif /* HAVE_LZMADEC_H */