source: mainline/uspace/lib/compress/gzip.c@ e37ddde1

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since e37ddde1 was e37ddde1, checked in by Martin Decky <martin@…>, 11 years ago

library for inflate decompression and gzip expansion

  • Property mode set to 100644
File size: 4.9 KB
Line 
1/*
2 * Copyright (c) 2014 Martin Decky
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * - The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29#include <sys/types.h>
30#include <errno.h>
31#include <mem.h>
32#include <malloc.h>
33#include <unistd.h>
34#include "gzip.h"
35#include "inflate.h"
36
37#define GZIP_ID1 UINT8_C(0x1f)
38#define GZIP_ID2 UINT8_C(0x8b)
39
40#define GZIP_METHOD_DEFLATE UINT8_C(0x08)
41
42#define GZIP_FLAGS_MASK UINT8_C(0x1f)
43#define GZIP_FLAG_FHCRC UINT8_C(1 << 1)
44#define GZIP_FLAG_FEXTRA UINT8_C(1 << 2)
45#define GZIP_FLAG_FNAME UINT8_C(1 << 3)
46#define GZIP_FLAG_FCOMMENT UINT8_C(1 << 4)
47
48typedef struct {
49 uint8_t id1;
50 uint8_t id2;
51 uint8_t method;
52 uint8_t flags;
53 uint32_t mtime;
54 uint8_t extra_flags;
55 uint8_t os;
56} __attribute__((packed)) gzip_header_t;
57
58typedef struct {
59 uint32_t crc32;
60 uint32_t size;
61} __attribute__((packed)) gzip_footer_t;
62
63/** Expand GZIP compressed data
64 *
65 * The routine allocates the output buffer based
66 * on the size encoded in the input stream. This
67 * effectively limits the size of the uncompressed
68 * data to 4 GiB (expanding input streams that actually
69 * encode more data will always fail).
70 *
71 * So far, no CRC is perfomed.
72 *
73 * @param[in] src Source data buffer.
74 * @param[in] srclen Source buffer size (bytes).
75 * @param[out] dest Destination data buffer.
76 * @param[out] destlen Destination buffer size (bytes).
77 *
78 * @return EOK on success.
79 * @return ENOENT on distance too large.
80 * @return EINVAL on invalid Huffman code, invalid deflate data,
81 * invalid compression method or invalid stream.
82 * @return ELIMIT on input buffer overrun.
83 * @return ENOMEM on output buffer overrun.
84 *
85 */
86int gzip_expand(void *src, size_t srclen, void **dest, size_t *destlen)
87{
88 gzip_header_t header;
89 gzip_footer_t footer;
90
91 if ((srclen < sizeof(header)) || (srclen < sizeof(footer)))
92 return EINVAL;
93
94 /* Decode header and footer */
95
96 memcpy(&header, src, sizeof(header));
97 memcpy(&footer, src + srclen - sizeof(footer), sizeof(footer));
98
99 if ((header.id1 != GZIP_ID1) ||
100 (header.id2 != GZIP_ID2) ||
101 (header.method != GZIP_METHOD_DEFLATE) ||
102 ((header.flags & (~GZIP_FLAGS_MASK)) != 0))
103 return EINVAL;
104
105 *destlen = footer.size;
106
107 /* Ignore extra metadata */
108
109 void *stream = src + sizeof(header);
110 size_t stream_length = srclen - sizeof(header) - sizeof(footer);
111
112 if ((header.flags & GZIP_FLAG_FEXTRA) != 0) {
113 uint16_t extra_length;
114
115 if (stream_length < sizeof(extra_length))
116 return EINVAL;
117
118 memcpy(&extra_length, stream, sizeof(extra_length));
119 stream += sizeof(extra_length);
120 stream_length -= sizeof(extra_length);
121
122 if (stream_length < extra_length)
123 return EINVAL;
124
125 stream += extra_length;
126 stream_length -= extra_length;
127 }
128
129 if ((header.flags & GZIP_FLAG_FNAME) != 0) {
130 while (*((uint8_t *) stream) != 0) {
131 if (stream_length == 0)
132 return EINVAL;
133
134 stream++;
135 stream_length--;
136 }
137
138 if (stream_length == 0)
139 return EINVAL;
140
141 stream++;
142 stream_length--;
143 }
144
145 if ((header.flags & GZIP_FLAG_FCOMMENT) != 0) {
146 while (*((uint8_t *) stream) != 0) {
147 if (stream_length == 0)
148 return EINVAL;
149
150 stream++;
151 stream_length--;
152 }
153
154 if (stream_length == 0)
155 return EINVAL;
156
157 stream++;
158 stream_length--;
159 }
160
161 if ((header.flags & GZIP_FLAG_FHCRC) != 0) {
162 if (stream_length < 2)
163 return EINVAL;
164
165 stream += 2;
166 stream_length -= 2;
167 }
168
169 /* Allocate output buffer and inflate the data */
170
171 *dest = malloc(*destlen);
172 if (*dest == NULL)
173 return ENOMEM;
174
175 int ret = inflate(stream, stream_length, *dest, *destlen);
176 if (ret != EOK) {
177 free(dest);
178 return ret;
179 }
180
181 return EOK;
182}
Note: See TracBrowser for help on using the repository browser.