source: mainline/boot/generic/src/gzip.c@ 39916d6

Last change on this file since 39916d6 was d7f7a4a, checked in by Jiří Zárevúcky <zarevucky.jiri@…>, 3 years ago

Replace some license headers with SPDX identifier

Headers are replaced using tools/transorm-copyright.sh only
when it can be matched verbatim with the license header used
throughout most of the codebase.

  • Property mode set to 100644
File size: 4.5 KB
Line 
1/*
2 * SPDX-FileCopyrightText: 2014 Martin Decky
3 *
4 * SPDX-License-Identifier: BSD-3-Clause
5 */
6
7// XXX: This file is a duplicate of the same in uspace/lib/compress
8
9#include <stdint.h>
10#include <stddef.h>
11#include <errno.h>
12#include <memstr.h>
13#include <byteorder.h>
14#include <gzip.h>
15#include <inflate.h>
16
17#define GZIP_ID1 UINT8_C(0x1f)
18#define GZIP_ID2 UINT8_C(0x8b)
19
20#define GZIP_METHOD_DEFLATE UINT8_C(0x08)
21
22#define GZIP_FLAGS_MASK UINT8_C(0x1f)
23#define GZIP_FLAG_FHCRC UINT8_C(1 << 1)
24#define GZIP_FLAG_FEXTRA UINT8_C(1 << 2)
25#define GZIP_FLAG_FNAME UINT8_C(1 << 3)
26#define GZIP_FLAG_FCOMMENT UINT8_C(1 << 4)
27
28typedef struct {
29 uint8_t id1;
30 uint8_t id2;
31 uint8_t method;
32 uint8_t flags;
33 uint32_t mtime;
34 uint8_t extra_flags;
35 uint8_t os;
36} __attribute__((packed)) gzip_header_t;
37
38typedef struct {
39 uint32_t crc32;
40 uint32_t size;
41} __attribute__((packed)) gzip_footer_t;
42
43/** Check GZIP signature
44 *
45 * Checks whether the source buffer start with a GZIP signature.
46 *
47 * @param[in] src Source data buffer.
48 * @param[in] srclen Source buffer size (bytes).
49 *
50 * @return True if GZIP signature found.
51 * @return False if no GZIP signature found.
52 *
53 */
54bool gzip_check(const void *src, size_t srclen)
55{
56 if ((srclen < (sizeof(gzip_header_t) + sizeof(gzip_footer_t))))
57 return false;
58
59 gzip_header_t header;
60 memcpy(&header, src, sizeof(header));
61
62 if ((header.id1 != GZIP_ID1) ||
63 (header.id2 != GZIP_ID2) ||
64 (header.method != GZIP_METHOD_DEFLATE) ||
65 ((header.flags & (~GZIP_FLAGS_MASK)) != 0))
66 return false;
67
68 return true;
69}
70
71/** Get uncompressed size
72 *
73 * Note that the uncompressed size is read from the GZIP footer
74 * (and not calculated by acutally decompressing the GZip archive).
75 * Thus the source of the GZip archive needs to be trusted.
76 *
77 * @param[in] src Source data buffer.
78 * @param[out] srclen Source buffer size (bytes).
79 *
80 * @return Uncompressed size.
81 *
82 */
83size_t gzip_size(const void *src, size_t srclen)
84{
85 if (!gzip_check(src, srclen))
86 return 0;
87
88 gzip_footer_t footer;
89 memcpy(&footer, src + srclen - sizeof(footer), sizeof(footer));
90
91 return uint32_t_le2host(footer.size);
92}
93
94/** Expand GZIP compressed data
95 *
96 * The routine compares the output buffer size with
97 * the size encoded in the input stream. This
98 * effectively limits the size of the uncompressed
99 * data to 4 GiB (expanding input streams that actually
100 * encode more data will always fail).
101 *
102 * So far, no CRC is perfomed.
103 *
104 * @param[in] src Source data buffer.
105 * @param[in] srclen Source buffer size (bytes).
106 * @param[out] dest Destination data buffer.
107 * @param[out] destlen Destination buffer size (bytes).
108 *
109 * @return EOK on success.
110 * @return ENOENT on distance too large.
111 * @return EINVAL on invalid Huffman code, invalid deflate data,
112 * invalid compression method or invalid stream.
113 * @return ELIMIT on input buffer overrun.
114 * @return ENOMEM on output buffer overrun.
115 *
116 */
117int gzip_expand(const void *src, size_t srclen, void *dest, size_t destlen)
118{
119 if (!gzip_check(src, srclen))
120 return EINVAL;
121
122 /* Decode header and footer */
123
124 gzip_header_t header;
125 memcpy(&header, src, sizeof(header));
126
127 gzip_footer_t footer;
128 memcpy(&footer, src + srclen - sizeof(footer), sizeof(footer));
129
130 if (destlen != uint32_t_le2host(footer.size))
131 return EINVAL;
132
133 /* Ignore extra metadata */
134
135 const void *stream = src + sizeof(header);
136 size_t stream_length = srclen - sizeof(header) - sizeof(footer);
137
138 if ((header.flags & GZIP_FLAG_FEXTRA) != 0) {
139 uint16_t extra_length;
140
141 if (stream_length < sizeof(extra_length))
142 return EINVAL;
143
144 memcpy(&extra_length, stream, sizeof(extra_length));
145 stream += sizeof(extra_length);
146 stream_length -= sizeof(extra_length);
147
148 if (stream_length < extra_length)
149 return EINVAL;
150
151 stream += extra_length;
152 stream_length -= extra_length;
153 }
154
155 if ((header.flags & GZIP_FLAG_FNAME) != 0) {
156 while (*((uint8_t *) stream) != 0) {
157 if (stream_length == 0)
158 return EINVAL;
159
160 stream++;
161 stream_length--;
162 }
163
164 if (stream_length == 0)
165 return EINVAL;
166
167 stream++;
168 stream_length--;
169 }
170
171 if ((header.flags & GZIP_FLAG_FCOMMENT) != 0) {
172 while (*((uint8_t *) stream) != 0) {
173 if (stream_length == 0)
174 return EINVAL;
175
176 stream++;
177 stream_length--;
178 }
179
180 if (stream_length == 0)
181 return EINVAL;
182
183 stream++;
184 stream_length--;
185 }
186
187 if ((header.flags & GZIP_FLAG_FHCRC) != 0) {
188 if (stream_length < 2)
189 return EINVAL;
190
191 stream += 2;
192 stream_length -= 2;
193 }
194
195 /* Inflate the data */
196 return inflate(stream, stream_length, dest, destlen);
197}
Note: See TracBrowser for help on using the repository browser.