Remove unistd.h
[helenos.git] / uspace / lib / compress / gzip.c
blobe0fbf6e9f2879d312eb759d9b0f291d380873973
1 /*
2 * Copyright (c) 2014 Martin Decky
3 * All rights reserved.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
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.
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.
29 #include <sys/types.h>
30 #include <stddef.h>
31 #include <errno.h>
32 #include <mem.h>
33 #include <malloc.h>
34 #include <byteorder.h>
35 #include "gzip.h"
36 #include "inflate.h"
38 #define GZIP_ID1 UINT8_C(0x1f)
39 #define GZIP_ID2 UINT8_C(0x8b)
41 #define GZIP_METHOD_DEFLATE UINT8_C(0x08)
43 #define GZIP_FLAGS_MASK UINT8_C(0x1f)
44 #define GZIP_FLAG_FHCRC UINT8_C(1 << 1)
45 #define GZIP_FLAG_FEXTRA UINT8_C(1 << 2)
46 #define GZIP_FLAG_FNAME UINT8_C(1 << 3)
47 #define GZIP_FLAG_FCOMMENT UINT8_C(1 << 4)
49 typedef struct {
50 uint8_t id1;
51 uint8_t id2;
52 uint8_t method;
53 uint8_t flags;
54 uint32_t mtime;
55 uint8_t extra_flags;
56 uint8_t os;
57 } __attribute__((packed)) gzip_header_t;
59 typedef struct {
60 uint32_t crc32;
61 uint32_t size;
62 } __attribute__((packed)) gzip_footer_t;
64 /** Expand GZIP compressed data
66 * The routine allocates the output buffer based
67 * on the size encoded in the input stream. This
68 * effectively limits the size of the uncompressed
69 * data to 4 GiB (expanding input streams that actually
70 * encode more data will always fail).
72 * So far, no CRC is perfomed.
74 * @param[in] src Source data buffer.
75 * @param[in] srclen Source buffer size (bytes).
76 * @param[out] dest Destination data buffer.
77 * @param[out] destlen Destination buffer size (bytes).
79 * @return EOK on success.
80 * @return ENOENT on distance too large.
81 * @return EINVAL on invalid Huffman code, invalid deflate data,
82 * invalid compression method or invalid stream.
83 * @return ELIMIT on input buffer overrun.
84 * @return ENOMEM on output buffer overrun.
87 int gzip_expand(void *src, size_t srclen, void **dest, size_t *destlen)
89 gzip_header_t header;
90 gzip_footer_t footer;
92 if ((srclen < sizeof(header)) || (srclen < sizeof(footer)))
93 return EINVAL;
95 /* Decode header and footer */
97 memcpy(&header, src, sizeof(header));
98 memcpy(&footer, src + srclen - sizeof(footer), sizeof(footer));
100 if ((header.id1 != GZIP_ID1) ||
101 (header.id2 != GZIP_ID2) ||
102 (header.method != GZIP_METHOD_DEFLATE) ||
103 ((header.flags & (~GZIP_FLAGS_MASK)) != 0))
104 return EINVAL;
106 *destlen = uint32_t_le2host(footer.size);
108 /* Ignore extra metadata */
110 void *stream = src + sizeof(header);
111 size_t stream_length = srclen - sizeof(header) - sizeof(footer);
113 if ((header.flags & GZIP_FLAG_FEXTRA) != 0) {
114 uint16_t extra_length;
116 if (stream_length < sizeof(extra_length))
117 return EINVAL;
119 memcpy(&extra_length, stream, sizeof(extra_length));
120 stream += sizeof(extra_length);
121 stream_length -= sizeof(extra_length);
123 if (stream_length < extra_length)
124 return EINVAL;
126 stream += extra_length;
127 stream_length -= extra_length;
130 if ((header.flags & GZIP_FLAG_FNAME) != 0) {
131 while (*((uint8_t *) stream) != 0) {
132 if (stream_length == 0)
133 return EINVAL;
135 stream++;
136 stream_length--;
139 if (stream_length == 0)
140 return EINVAL;
142 stream++;
143 stream_length--;
146 if ((header.flags & GZIP_FLAG_FCOMMENT) != 0) {
147 while (*((uint8_t *) stream) != 0) {
148 if (stream_length == 0)
149 return EINVAL;
151 stream++;
152 stream_length--;
155 if (stream_length == 0)
156 return EINVAL;
158 stream++;
159 stream_length--;
162 if ((header.flags & GZIP_FLAG_FHCRC) != 0) {
163 if (stream_length < 2)
164 return EINVAL;
166 stream += 2;
167 stream_length -= 2;
170 /* Allocate output buffer and inflate the data */
172 *dest = malloc(*destlen);
173 if (*dest == NULL)
174 return ENOMEM;
176 int ret = inflate(stream, stream_length, *dest, *destlen);
177 if (ret != EOK) {
178 free(dest);
179 return ret;
182 return EOK;