chiark / gitweb /
network-internal: initialize _cleanup_ variable
[elogind.git] / src / journal / compress.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /***
4   This file is part of systemd.
5
6   Copyright 2011 Lennart Poettering
7
8   systemd is free software; you can redistribute it and/or modify it
9   under the terms of the GNU Lesser General Public License as published by
10   the Free Software Foundation; either version 2.1 of the License, or
11   (at your option) any later version.
12
13   systemd is distributed in the hope that it will be useful, but
14   WITHOUT ANY WARRANTY; without even the implied warranty of
15   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16   Lesser General Public License for more details.
17
18   You should have received a copy of the GNU Lesser General Public License
19   along with systemd; If not, see <http://www.gnu.org/licenses/>.
20 ***/
21
22 #include <assert.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <unistd.h>
26 #include <lzma.h>
27
28 #include "compress.h"
29 #include "macro.h"
30 #include "util.h"
31
32 bool compress_blob(const void *src, uint64_t src_size, void *dst, uint64_t *dst_size) {
33         lzma_ret ret;
34         size_t out_pos = 0;
35
36         assert(src);
37         assert(src_size > 0);
38         assert(dst);
39         assert(dst_size);
40
41         /* Returns false if we couldn't compress the data or the
42          * compressed result is longer than the original */
43
44         ret = lzma_easy_buffer_encode(LZMA_PRESET_DEFAULT, LZMA_CHECK_NONE, NULL,
45                                       src, src_size, dst, &out_pos, src_size);
46         if (ret != LZMA_OK)
47                 return false;
48
49         /* Is it actually shorter? */
50         if (out_pos == src_size)
51                 return false;
52
53         *dst_size = out_pos;
54         return true;
55 }
56
57 bool uncompress_blob(const void *src, uint64_t src_size,
58                      void **dst, uint64_t *dst_alloc_size, uint64_t* dst_size, uint64_t dst_max) {
59
60         lzma_stream s = LZMA_STREAM_INIT;
61         lzma_ret ret;
62         uint64_t space;
63         bool b = false;
64
65         assert(src);
66         assert(src_size > 0);
67         assert(dst);
68         assert(dst_alloc_size);
69         assert(dst_size);
70         assert(*dst_alloc_size == 0 || *dst);
71
72         ret = lzma_stream_decoder(&s, UINT64_MAX, 0);
73         if (ret != LZMA_OK)
74                 return false;
75
76         if (*dst_alloc_size <= src_size) {
77                 void *p;
78
79                 p = realloc(*dst, src_size*2);
80                 if (!p)
81                         return false;
82
83                 *dst = p;
84                 *dst_alloc_size = src_size*2;
85         }
86
87         s.next_in = src;
88         s.avail_in = src_size;
89
90         s.next_out = *dst;
91         space = dst_max > 0 ? MIN(*dst_alloc_size, dst_max) : *dst_alloc_size;
92         s.avail_out = space;
93
94         for (;;) {
95                 void *p;
96
97                 ret = lzma_code(&s, LZMA_FINISH);
98
99                 if (ret == LZMA_STREAM_END)
100                         break;
101
102                 if (ret != LZMA_OK)
103                         goto fail;
104
105                 if (dst_max > 0 && (space - s.avail_out) >= dst_max)
106                         break;
107
108                 p = realloc(*dst, space*2);
109                 if (!p)
110                         goto fail;
111
112                 s.next_out = (uint8_t*) p + ((uint8_t*) s.next_out - (uint8_t*) *dst);
113                 s.avail_out += space;
114
115                 space *= 2;
116
117                 *dst = p;
118                 *dst_alloc_size = space;
119         }
120
121         *dst_size = space - s.avail_out;
122         b = true;
123
124 fail:
125         lzma_end(&s);
126
127         return b;
128 }
129
130 bool uncompress_startswith(const void *src, uint64_t src_size,
131                            void **buffer, uint64_t *buffer_size,
132                            const void *prefix, uint64_t prefix_len,
133                            uint8_t extra) {
134
135         lzma_stream s = LZMA_STREAM_INIT;
136         lzma_ret ret;
137         bool b = false;
138
139         /* Checks whether the uncompressed blob starts with the
140          * mentioned prefix. The byte extra needs to follow the
141          * prefix */
142
143         assert(src);
144         assert(src_size > 0);
145         assert(buffer);
146         assert(buffer_size);
147         assert(prefix);
148         assert(*buffer_size == 0 || *buffer);
149
150         ret = lzma_stream_decoder(&s, UINT64_MAX, 0);
151         if (ret != LZMA_OK)
152                 return false;
153
154         if (*buffer_size <= prefix_len) {
155                 void *p;
156
157                 p = realloc(*buffer, prefix_len*2);
158                 if (!p)
159                         return false;
160
161                 *buffer = p;
162                 *buffer_size = prefix_len*2;
163         }
164
165         s.next_in = src;
166         s.avail_in = src_size;
167
168         s.next_out = *buffer;
169         s.avail_out = *buffer_size;
170
171         for (;;) {
172                 void *p;
173
174                 ret = lzma_code(&s, LZMA_FINISH);
175
176                 if (ret != LZMA_STREAM_END && ret != LZMA_OK)
177                         goto fail;
178
179                 if ((*buffer_size - s.avail_out > prefix_len) &&
180                     memcmp(*buffer, prefix, prefix_len) == 0 &&
181                     ((const uint8_t*) *buffer)[prefix_len] == extra)
182                         break;
183
184                 if (ret == LZMA_STREAM_END)
185                         goto fail;
186
187                 p = realloc(*buffer, *buffer_size*2);
188                 if (!p)
189                         goto fail;
190
191                 s.next_out = (uint8_t*) p + ((uint8_t*) s.next_out - (uint8_t*) *buffer);
192                 s.avail_out += *buffer_size;
193
194                 *buffer = p;
195                 *buffer_size *= 2;
196         }
197
198         b = true;
199
200 fail:
201         lzma_end(&s);
202
203         return b;
204 }
205
206 int compress_stream(int fdf, int fdt, uint32_t preset, off_t max_bytes) {
207         _cleanup_(lzma_end) lzma_stream s = LZMA_STREAM_INIT;
208         lzma_ret ret;
209
210         uint8_t buf[BUFSIZ], out[BUFSIZ];
211         lzma_action action = LZMA_RUN;
212
213         assert(fdf >= 0);
214         assert(fdt >= 0);
215
216         ret = lzma_easy_encoder(&s, preset, LZMA_CHECK_CRC64);
217         if (ret != LZMA_OK) {
218                 log_error("Failed to initialize XZ encoder: code %d", ret);
219                 return -EINVAL;
220         }
221
222         for (;;) {
223                 if (s.avail_in == 0 && action == LZMA_RUN) {
224                         size_t m = sizeof(buf);
225                         ssize_t n;
226
227                         if (max_bytes != -1 && m > (size_t) max_bytes)
228                                 m = max_bytes;
229
230                         n = read(fdf, buf, m);
231                         if (n < 0)
232                                 return -errno;
233                         if (n == 0)
234                                 action = LZMA_FINISH;
235                         else {
236                                 s.next_in = buf;
237                                 s.avail_in = n;
238
239                                 if (max_bytes != -1) {
240                                         assert(max_bytes >= n);
241                                         max_bytes -= n;
242                                 }
243                         }
244                 }
245
246                 if (s.avail_out == 0) {
247                         s.next_out = out;
248                         s.avail_out = sizeof(out);
249                 }
250
251                 ret = lzma_code(&s, action);
252                 if (ret != LZMA_OK && ret != LZMA_STREAM_END) {
253                         log_error("Compression failed: code %d", ret);
254                         return -EBADMSG;
255                 }
256
257                 if (s.avail_out == 0 || ret == LZMA_STREAM_END) {
258                         ssize_t n, k;
259
260                         n = sizeof(out) - s.avail_out;
261
262                         errno = 0;
263                         k = loop_write(fdt, out, n, false);
264                         if (k < 0)
265                                 return k;
266                         if (k != n)
267                                 return errno ? -errno : -EIO;
268
269                         if (ret == LZMA_STREAM_END) {
270                                 log_debug("Compression finished (%zu -> %zu bytes, %.1f%%)",
271                                           s.total_in, s.total_out,
272                                           (double) s.total_out / s.total_in * 100);
273
274                                 return 0;
275                         }
276                 }
277         }
278 }
279
280 int decompress_stream(int fdf, int fdt, off_t max_bytes) {
281         _cleanup_(lzma_end) lzma_stream s = LZMA_STREAM_INIT;
282         lzma_ret ret;
283
284         uint8_t buf[BUFSIZ], out[BUFSIZ];
285         lzma_action action = LZMA_RUN;
286
287         assert(fdf >= 0);
288         assert(fdt >= 0);
289
290         ret = lzma_stream_decoder(&s, UINT64_MAX, 0);
291         if (ret != LZMA_OK) {
292                 log_error("Failed to initialize XZ decoder: code %d", ret);
293                 return -EINVAL;
294         }
295
296         for (;;) {
297                 if (s.avail_in == 0 && action == LZMA_RUN) {
298                         ssize_t n;
299
300                         n = read(fdf, buf, sizeof(buf));
301                         if (n < 0)
302                                 return -errno;
303                         if (n == 0)
304                                 action = LZMA_FINISH;
305                         else {
306                                 s.next_in = buf;
307                                 s.avail_in = n;
308                         }
309                 }
310
311                 if (s.avail_out == 0) {
312                         s.next_out = out;
313                         s.avail_out = sizeof(out);
314                 }
315
316                 ret = lzma_code(&s, action);
317                 if (ret != LZMA_OK && ret != LZMA_STREAM_END) {
318                         log_error("Decompression failed: code %d", ret);
319                         return -EBADMSG;
320                 }
321
322                 if (s.avail_out == 0 || ret == LZMA_STREAM_END) {
323                         ssize_t n, k;
324
325                         n = sizeof(out) - s.avail_out;
326
327                         if (max_bytes != -1) {
328                                 if (max_bytes < n)
329                                         return -E2BIG;
330
331                                 max_bytes -= n;
332                         }
333
334                         errno = 0;
335                         k = loop_write(fdt, out, n, false);
336                         if (k < 0)
337                                 return k;
338                         if (k != n)
339                                 return errno ? -errno : -EIO;
340
341                         if (ret == LZMA_STREAM_END) {
342                                 log_debug("Decompression finished (%zu -> %zu bytes, %.1f%%)",
343                                           s.total_in, s.total_out,
344                                           (double) s.total_out / s.total_in * 100);
345
346                                 return 0;
347                         }
348                 }
349         }
350 }