chiark / gitweb /
journal/compress: return early in uncompress_startswith
[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         _cleanup_(lzma_end) lzma_stream s = LZMA_STREAM_INIT;
61         lzma_ret ret;
62         uint64_t space;
63
64         assert(src);
65         assert(src_size > 0);
66         assert(dst);
67         assert(dst_alloc_size);
68         assert(dst_size);
69         assert(*dst_alloc_size == 0 || *dst);
70
71         ret = lzma_stream_decoder(&s, UINT64_MAX, 0);
72         if (ret != LZMA_OK)
73                 return false;
74
75         space = MIN(src_size * 2, dst_max ?: (uint64_t) -1);
76         if (!greedy_realloc(dst, dst_alloc_size, space, 1))
77                 return false;
78
79         s.next_in = src;
80         s.avail_in = src_size;
81
82         s.next_out = *dst;
83         s.avail_out = space;
84
85         for (;;) {
86                 uint64_t used;
87
88                 ret = lzma_code(&s, LZMA_FINISH);
89
90                 if (ret == LZMA_STREAM_END)
91                         break;
92
93                 if (ret != LZMA_OK)
94                         return false;
95
96                 if (dst_max > 0 && (space - s.avail_out) >= dst_max)
97                         break;
98
99                 if (dst_max > 0 && space == dst_max)
100                         return false;
101
102                 used = space - s.avail_out;
103                 space = MIN(2 * space, dst_max ?: (uint64_t) -1);
104                 if (!greedy_realloc(dst, dst_alloc_size, space, 1))
105                         return false;
106
107                 s.avail_out = space - used;
108                 s.next_out = *dst + used;
109         }
110
111         *dst_size = space - s.avail_out;
112         return true;
113 }
114
115 bool uncompress_startswith(const void *src, uint64_t src_size,
116                            void **buffer, uint64_t *buffer_size,
117                            const void *prefix, uint64_t prefix_len,
118                            uint8_t extra) {
119
120         _cleanup_(lzma_end) lzma_stream s = LZMA_STREAM_INIT;
121         lzma_ret ret;
122
123         /* Checks whether the uncompressed blob starts with the
124          * mentioned prefix. The byte extra needs to follow the
125          * prefix */
126
127         assert(src);
128         assert(src_size > 0);
129         assert(buffer);
130         assert(buffer_size);
131         assert(prefix);
132         assert(*buffer_size == 0 || *buffer);
133
134         ret = lzma_stream_decoder(&s, UINT64_MAX, 0);
135         if (ret != LZMA_OK)
136                 return false;
137
138         if (!(greedy_realloc(buffer, buffer_size, prefix_len + 1, 1)))
139                 return false;
140
141         s.next_in = src;
142         s.avail_in = src_size;
143
144         s.next_out = *buffer;
145         s.avail_out = *buffer_size;
146
147         for (;;) {
148                 ret = lzma_code(&s, LZMA_FINISH);
149
150                 if (ret != LZMA_STREAM_END && ret != LZMA_OK)
151                         return false;
152
153                 if (*buffer_size - s.avail_out >= prefix_len + 1)
154                         return memcmp(*buffer, prefix, prefix_len) == 0 &&
155                                 ((const uint8_t*) *buffer)[prefix_len] == extra;
156
157                 if (ret == LZMA_STREAM_END)
158                         return false;
159
160                 s.avail_out += *buffer_size;
161
162                 if (!(greedy_realloc(buffer, buffer_size, *buffer_size * 2, 1)))
163                         return false;
164
165                 s.next_out = *buffer + *buffer_size - s.avail_out;
166         }
167 }
168
169 int compress_stream(int fdf, int fdt, uint32_t preset, off_t max_bytes) {
170         _cleanup_(lzma_end) lzma_stream s = LZMA_STREAM_INIT;
171         lzma_ret ret;
172
173         uint8_t buf[BUFSIZ], out[BUFSIZ];
174         lzma_action action = LZMA_RUN;
175
176         assert(fdf >= 0);
177         assert(fdt >= 0);
178
179         ret = lzma_easy_encoder(&s, preset, LZMA_CHECK_CRC64);
180         if (ret != LZMA_OK) {
181                 log_error("Failed to initialize XZ encoder: code %d", ret);
182                 return -EINVAL;
183         }
184
185         for (;;) {
186                 if (s.avail_in == 0 && action == LZMA_RUN) {
187                         size_t m = sizeof(buf);
188                         ssize_t n;
189
190                         if (max_bytes != -1 && m > (size_t) max_bytes)
191                                 m = max_bytes;
192
193                         n = read(fdf, buf, m);
194                         if (n < 0)
195                                 return -errno;
196                         if (n == 0)
197                                 action = LZMA_FINISH;
198                         else {
199                                 s.next_in = buf;
200                                 s.avail_in = n;
201
202                                 if (max_bytes != -1) {
203                                         assert(max_bytes >= n);
204                                         max_bytes -= n;
205                                 }
206                         }
207                 }
208
209                 if (s.avail_out == 0) {
210                         s.next_out = out;
211                         s.avail_out = sizeof(out);
212                 }
213
214                 ret = lzma_code(&s, action);
215                 if (ret != LZMA_OK && ret != LZMA_STREAM_END) {
216                         log_error("Compression failed: code %d", ret);
217                         return -EBADMSG;
218                 }
219
220                 if (s.avail_out == 0 || ret == LZMA_STREAM_END) {
221                         ssize_t n, k;
222
223                         n = sizeof(out) - s.avail_out;
224
225                         errno = 0;
226                         k = loop_write(fdt, out, n, false);
227                         if (k < 0)
228                                 return k;
229                         if (k != n)
230                                 return errno ? -errno : -EIO;
231
232                         if (ret == LZMA_STREAM_END) {
233                                 log_debug("Compression finished (%zu -> %zu bytes, %.1f%%)",
234                                           s.total_in, s.total_out,
235                                           (double) s.total_out / s.total_in * 100);
236
237                                 return 0;
238                         }
239                 }
240         }
241 }
242
243 int decompress_stream(int fdf, int fdt, off_t max_bytes) {
244         _cleanup_(lzma_end) lzma_stream s = LZMA_STREAM_INIT;
245         lzma_ret ret;
246
247         uint8_t buf[BUFSIZ], out[BUFSIZ];
248         lzma_action action = LZMA_RUN;
249
250         assert(fdf >= 0);
251         assert(fdt >= 0);
252
253         ret = lzma_stream_decoder(&s, UINT64_MAX, 0);
254         if (ret != LZMA_OK) {
255                 log_error("Failed to initialize XZ decoder: code %d", ret);
256                 return -EINVAL;
257         }
258
259         for (;;) {
260                 if (s.avail_in == 0 && action == LZMA_RUN) {
261                         ssize_t n;
262
263                         n = read(fdf, buf, sizeof(buf));
264                         if (n < 0)
265                                 return -errno;
266                         if (n == 0)
267                                 action = LZMA_FINISH;
268                         else {
269                                 s.next_in = buf;
270                                 s.avail_in = n;
271                         }
272                 }
273
274                 if (s.avail_out == 0) {
275                         s.next_out = out;
276                         s.avail_out = sizeof(out);
277                 }
278
279                 ret = lzma_code(&s, action);
280                 if (ret != LZMA_OK && ret != LZMA_STREAM_END) {
281                         log_error("Decompression failed: code %d", ret);
282                         return -EBADMSG;
283                 }
284
285                 if (s.avail_out == 0 || ret == LZMA_STREAM_END) {
286                         ssize_t n, k;
287
288                         n = sizeof(out) - s.avail_out;
289
290                         if (max_bytes != -1) {
291                                 if (max_bytes < n)
292                                         return -E2BIG;
293
294                                 max_bytes -= n;
295                         }
296
297                         errno = 0;
298                         k = loop_write(fdt, out, n, false);
299                         if (k < 0)
300                                 return k;
301                         if (k != n)
302                                 return errno ? -errno : -EIO;
303
304                         if (ret == LZMA_STREAM_END) {
305                                 log_debug("Decompression finished (%zu -> %zu bytes, %.1f%%)",
306                                           s.total_in, s.total_out,
307                                           (double) s.total_out / s.total_in * 100);
308
309                                 return 0;
310                         }
311                 }
312         }
313 }