]> git.ipfire.org Git - people/ms/pakfire.git/blame - src/libpakfire/compress.c
compress: Fix potential leak of cookie
[people/ms/pakfire.git] / src / libpakfire / compress.c
CommitLineData
c352b776
MT
1/*#############################################################################
2# #
3# Pakfire - The IPFire package management system #
4# Copyright (C) 2021 Pakfire development team #
5# #
6# This program is free software: you can redistribute it and/or modify #
7# it under the terms of the GNU General Public License as published by #
8# the Free Software Foundation, either version 3 of the License, or #
9# (at your option) any later version. #
10# #
11# This program is distributed in the hope that it will be useful, #
12# but WITHOUT ANY WARRANTY; without even the implied warranty of #
13# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
14# GNU General Public License for more details. #
15# #
16# You should have received a copy of the GNU General Public License #
17# along with this program. If not, see <http://www.gnu.org/licenses/>. #
18# #
19#############################################################################*/
20
64f977a3 21#include <errno.h>
c352b776 22#include <stdio.h>
99018421 23#include <stdlib.h>
c352b776
MT
24#include <string.h>
25
26#include <lzma.h>
9476d502 27#include <zstd.h>
c352b776
MT
28
29#include <pakfire/compress.h>
30
31// Read up to N bytes for analyze the magic
32#define MAX_MAGIC_LENGTH 6
33
5cd454df
MT
34// Compression/Decompression buffer size
35#define BUFFER_SIZE 64 * 1024
36
0bed1e1d
MT
37// Settings for XZ
38#define XZ_COMPRESSION_LEVEL 6
39
9476d502
MT
40// Settings for ZSTD
41#define ZSTD_COMPRESSION_LEVEL 7
42
c352b776
MT
43const struct compressor {
44 char magic[MAX_MAGIC_LENGTH];
45 size_t magic_length;
46 FILE* (*open)(FILE* f, const char* mode);
47} compressors[] = {
48 // XZ
49 { { 0xFD, '7', 'z', 'X', 'Z', 0x00 }, 6, pakfire_xzfopen, },
60732b6e
MT
50 // ZSTD
51 { { 0x28, 0xb5, 0x2f, 0xfd }, 4, pakfire_zstdfopen, },
52 // End
c352b776
MT
53 { "", 0, NULL, },
54};
55
c352b776
MT
56// Try to guess the compression
57FILE* pakfire_xfopen(FILE* f, const char* mode) {
58 char buffer[MAX_MAGIC_LENGTH];
59
64f977a3
MT
60 if (!f) {
61 errno = EBADFD;
c352b776 62 return NULL;
64f977a3
MT
63 }
64
65 if (!mode) {
66 errno = EINVAL;
67 return NULL;
68 }
69
70 // This only works for reading files
71 if (*mode != 'r') {
72 errno = ENOTSUP;
73 return NULL;
74 }
c352b776
MT
75
76 fpos_t pos;
77
78 // Store the position
79 int r = fgetpos(f, &pos);
80 if (r < 0)
81 return NULL;
82
83 // Read a couple of bytes
84 size_t bytes_read = fread(buffer, 1, sizeof(buffer), f);
85
86 // Reset position
87 r = fsetpos(f, &pos);
88 if (r < 0)
89 return NULL;
90
91 // Check if we could read anything
92 if (!bytes_read || bytes_read < sizeof(buffer))
93 return f;
94
95 // Analyze magic
96 for (const struct compressor* c = compressors; c->open; c++) {
97 // Check if we have read enough data
98 if (bytes_read < c->magic_length)
99 continue;
100
101 // Compare the magic value
102 r = memcmp(c->magic, buffer, c->magic_length);
103 if (r)
104 continue;
105
106 // We found a match!
107 return c->open(f, mode);
108 }
109
110 // Nothing seems to match
49492077 111 errno = ENOTSUP;
c352b776
MT
112 return f;
113}
114
5cd454df
MT
115struct xz_cookie {
116 FILE* f;
117 char mode;
118 lzma_stream stream;
119 int done;
120
121 uint8_t buffer[BUFFER_SIZE];
122};
123
c352b776
MT
124static ssize_t xz_read(void* data, char* buffer, size_t size) {
125 struct xz_cookie* cookie = (struct xz_cookie*)data;
126 if (!cookie)
127 return -1;
128
48187ff3
MT
129 // Do not read when mode is "w"
130 if (cookie->mode == 'w')
6923552a 131 return -1;
48187ff3 132
c352b776
MT
133 lzma_action action = LZMA_RUN;
134
135 // Set output to allocated buffer
136 cookie->stream.next_out = (uint8_t *)buffer;
137 cookie->stream.avail_out = size;
138
139 while (1) {
140 // Read something when the input buffer is empty
141 if (cookie->stream.avail_in == 0) {
142 cookie->stream.next_in = cookie->buffer;
143 cookie->stream.avail_in = fread(cookie->buffer,
5cd454df 144 1, sizeof(cookie->buffer), cookie->f);
c352b776
MT
145
146 // Break if the input file could not be read
147 if (ferror(cookie->f))
148 return -1;
149
150 // Finish after we have reached the end of the input file
5e12758c 151 if (feof(cookie->f))
c352b776 152 cookie->done = 1;
c352b776
MT
153 }
154
155 lzma_ret ret = lzma_code(&cookie->stream, action);
156
157 // If the stream has ended, we just send the
158 // remaining output and mark that we are done.
159 if (ret == LZMA_STREAM_END) {
160 cookie->done = 1;
161 return size - cookie->stream.avail_out;
162 }
163
164 // Break on all other unexpected errors
165 if (ret != LZMA_OK)
166 return -1;
167
168 // When we have read enough to fill the entire output buffer, we return
169 if (cookie->stream.avail_out == 0)
170 return size;
171
172 if (cookie->done)
173 return -1;
174 }
175}
176
0bed1e1d
MT
177static ssize_t xz_write(void* data, const char* buffer, size_t size) {
178 struct xz_cookie* cookie = (struct xz_cookie*)data;
179 if (!cookie)
180 return -1;
181
182 // Do not write when mode is "r"
183 if (cookie->mode == 'r')
6923552a 184 return -1;
0bed1e1d
MT
185
186 // Return nothing when there is no input
187 if (size == 0)
188 return 0;
189
190 // Set input to allocated buffer
9d816df3
MT
191 cookie->stream.next_in = (uint8_t *)buffer;
192 cookie->stream.avail_in = size;
0bed1e1d
MT
193
194 while (1) {
195 cookie->stream.next_out = cookie->buffer;
196 cookie->stream.avail_out = sizeof(cookie->buffer);
197
198 lzma_ret ret = lzma_code(&cookie->stream, LZMA_RUN);
199 if (ret != LZMA_OK)
200 return -1;
201
202 size_t bytes_to_write = sizeof(cookie->buffer) - cookie->stream.avail_out;
203 if (bytes_to_write) {
204 size_t bytes_written = fwrite(cookie->buffer, 1, bytes_to_write, cookie->f);
205
206 if (bytes_written != bytes_to_write)
207 return -1;
208 }
209
210 // Report that all data has been written
211 if (cookie->stream.avail_in == 0)
212 return size;
213 }
214}
215
c352b776
MT
216static int xz_close(void* data) {
217 struct xz_cookie* cookie = (struct xz_cookie*)data;
99018421
MT
218 if (!cookie)
219 return -1;
220
221 if (cookie->mode == 'w') {
222 while (1) {
223 cookie->stream.next_out = cookie->buffer;
224 cookie->stream.avail_out = sizeof(cookie->buffer);
225
226 lzma_ret ret = lzma_code(&cookie->stream, LZMA_FINISH);
227 if (ret != LZMA_OK && ret != LZMA_STREAM_END)
228 return -1;
229
230 size_t bytes_to_write = sizeof(cookie->buffer) - cookie->stream.avail_out;
231 if (bytes_to_write) {
232 size_t bytes_written = fwrite(cookie->buffer, 1, bytes_to_write, cookie->f);
233
234 if (bytes_written != bytes_to_write)
235 return -1;
236 }
237
238 if (ret == LZMA_STREAM_END)
239 break;
240 }
241 }
c352b776 242
c352b776
MT
243 lzma_end(&cookie->stream);
244
245 // Close input file
99018421
MT
246 int r = fclose(cookie->f);
247 free(cookie);
c352b776 248
99018421 249 return r;
c352b776
MT
250}
251
99018421
MT
252static cookie_io_functions_t xz_functions = {
253 .read = xz_read,
254 .write = xz_write,
255 .seek = NULL,
256 .close = xz_close,
257};
258
c352b776 259FILE* pakfire_xzfopen(FILE* f, const char* mode) {
99018421
MT
260 lzma_ret ret;
261
48187ff3
MT
262 if (!f) {
263 errno = EBADFD;
264 return NULL;
265 }
266
267 if (!mode) {
268 errno = EINVAL;
269 return NULL;
270 }
271
99018421
MT
272 struct xz_cookie* cookie = calloc(1, sizeof(*cookie));
273 if (!cookie)
274 return NULL;
c352b776 275
99018421
MT
276 cookie->f = f;
277 cookie->mode = *mode;
0bed1e1d 278
99018421 279 switch (cookie->mode) {
0bed1e1d 280 case 'r':
99018421 281 ret = lzma_stream_decoder(&cookie->stream, UINT64_MAX, 0);
0bed1e1d
MT
282 break;
283
284 case 'w':
99018421 285 ret = lzma_easy_encoder(&cookie->stream, XZ_COMPRESSION_LEVEL, LZMA_CHECK_SHA256);
0bed1e1d
MT
286 break;
287
288 default:
289 errno = ENOTSUP;
2d4af759 290 goto ERROR;
0bed1e1d
MT
291 }
292
c352b776 293 if (ret != LZMA_OK)
2d4af759 294 goto ERROR;
c352b776 295
99018421 296 return fopencookie(cookie, mode, xz_functions);
2d4af759
MT
297
298ERROR:
299 free(cookie);
300 return NULL;
c352b776 301}
9476d502
MT
302
303// ZSTD
304
305struct zstd_cookie {
306 FILE* f;
307 char mode;
308 int done;
309
310 // Encoder
311 ZSTD_CStream* cstream;
312 ZSTD_inBuffer in;
313
314 // Decoder
315 ZSTD_DStream* dstream;
316 ZSTD_outBuffer out;
317
318 uint8_t buffer[BUFFER_SIZE];
319};
320
321static ssize_t zstd_read(void* data, char* buffer, size_t size) {
322 struct zstd_cookie* cookie = (struct zstd_cookie*)data;
323 if (!cookie)
324 return -1;
325
326 // Do not read when mode is "w"
327 if (cookie->mode == 'w')
328 return -1;
329
330 if (cookie->done)
331 return 0;
332
333 size_t r = 0;
334
335 // Configure output buffer
336 cookie->out.dst = buffer;
337 cookie->out.pos = 0;
338 cookie->out.size = size;
339
340 while (1) {
341 if (!feof(cookie->f) && (cookie->in.pos == cookie->in.size)) {
342 cookie->in.pos = 0;
343 cookie->in.size = fread(cookie->buffer, 1, sizeof(cookie->buffer), cookie->f);
344 }
345
346 if (r || cookie->in.size)
347 r = ZSTD_decompressStream(cookie->dstream, &cookie->out, &cookie->in);
348
349 if (r == 0 && feof(cookie->f)) {
350 cookie->done = 1;
351 return cookie->out.pos;
352 }
353
354 if (ZSTD_isError(r))
355 return -1;
356
357 // Buffer full
358 if (cookie->out.pos == size)
359 return size;
360 }
361}
362
99a1527a
MT
363static ssize_t zstd_write(void* data, const char* buffer, size_t size) {
364 struct zstd_cookie* cookie = (struct zstd_cookie*)data;
365 if (!cookie)
366 return -1;
367
368 // Do not write when mode is "r"
369 if (cookie->mode == 'r')
370 return -1;
371
372 // Return nothing when there is no input
373 if (size == 0)
374 return 0;
375
376 // Configure input buffer
377 cookie->in.src = buffer;
378 cookie->in.pos = 0;
379 cookie->in.size = size;
380
381 while (1) {
382 cookie->out.pos = 0;
383
384 size_t r = ZSTD_compressStream(cookie->cstream, &cookie->out, &cookie->in);
385 if (ZSTD_isError(r))
386 return -1;
387
388 if (cookie->out.pos > 0) {
389 size_t bytes_written = fwrite(cookie->buffer, 1, cookie->out.pos, cookie->f);
390
391 if (bytes_written != cookie->out.pos)
392 return -1;
393 }
394
395 // Return when all input has been written
396 if (cookie->in.pos == size)
397 return size;
398 }
399}
400
9476d502
MT
401static int zstd_close(void* data) {
402 struct zstd_cookie* cookie = (struct zstd_cookie*)data;
403 if (!cookie)
404 return -1;
405
99a1527a
MT
406 if (cookie->mode == 'w') {
407 while (1) {
408 // Reset output buffer
409 cookie->out.pos = 0;
410
411 size_t r = ZSTD_endStream(cookie->cstream, &cookie->out);
412 if (ZSTD_isError(r))
413 return -1;
414
415 if (cookie->out.pos > 0) {
416 size_t bytes_written = fwrite(cookie->buffer, 1, cookie->out.pos, cookie->f);
417
418 if (bytes_written != cookie->out.pos)
419 return -1;
420 }
421
422 if (r == 0)
423 break;
424 }
425 }
9476d502
MT
426
427 int r = fclose(cookie->f);
428
429 // Free everything
430 if (cookie->cstream)
431 ZSTD_freeCStream(cookie->cstream);
432 if (cookie->dstream)
433 ZSTD_freeDStream(cookie->dstream);
434 free(cookie);
435
436 return r;
437}
438
439static cookie_io_functions_t zstd_functions = {
440 .read = zstd_read,
99a1527a 441 .write = zstd_write,
9476d502
MT
442 .seek = NULL,
443 .close = zstd_close,
444};
445
446FILE* pakfire_zstdfopen(FILE* f, const char* mode) {
447 if (!f) {
448 errno = EBADFD;
449 return NULL;
450 }
451
452 if (!mode) {
453 errno = EINVAL;
454 return NULL;
455 }
456
457 struct zstd_cookie* cookie = calloc(1, sizeof(*cookie));
458 if (!cookie)
459 return NULL;
460
461 cookie->f = f;
462 cookie->mode = *mode;
463
464 size_t r;
465 switch (cookie->mode) {
466 case 'r':
467 // Allocate stream
468 cookie->dstream = ZSTD_createDStream();
469 if (!cookie->dstream)
470 goto ERROR;
471
472 // Initialize stream
473 r = ZSTD_initDStream(cookie->dstream);
474 if (ZSTD_isError(r))
475 goto ERROR;
476
477 cookie->in.src = cookie->buffer;
478 cookie->in.pos = 0;
479 cookie->in.size = 0;
480 break;
481
482 case 'w':
483 // Allocate stream
484 cookie->cstream = ZSTD_createCStream();
485 if (!cookie->cstream)
486 goto ERROR;
487
488 // Initialize stream
489 r = ZSTD_initCStream(cookie->cstream, ZSTD_COMPRESSION_LEVEL);
490 if (ZSTD_isError(r))
491 goto ERROR;
492
493 cookie->out.dst = cookie->buffer;
494 cookie->out.pos = 0;
495 cookie->out.size = sizeof(cookie->buffer);
496 break;
497
498 default:
499 errno = ENOTSUP;
500 goto ERROR;
501 }
502
503 return fopencookie(cookie, mode, zstd_functions);
504
505ERROR:
506 if (cookie->cstream)
507 ZSTD_freeCStream(cookie->cstream);
508 if (cookie->dstream)
509 ZSTD_freeDStream(cookie->dstream);
510 free(cookie);
511
512 return NULL;
513}