chiark / gitweb /
import: split out compression logic, so that we can share it with between import...
[elogind.git] / src / import / import-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 2015 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 "util.h"
23 #include "import-compress.h"
24
25 void import_compress_free(ImportCompress *c) {
26         assert(c);
27
28         if (c->type == IMPORT_COMPRESS_XZ)
29                 lzma_end(&c->xz);
30         else if (c->type == IMPORT_COMPRESS_GZIP)
31                 inflateEnd(&c->gzip);
32         else if (c->type == IMPORT_COMPRESS_BZIP2)
33                 BZ2_bzDecompressEnd(&c->bzip2);
34
35         c->type = IMPORT_COMPRESS_UNKNOWN;
36 }
37
38 int import_uncompress_detect(ImportCompress *c, const void *data, size_t size) {
39         static const uint8_t xz_signature[] = {
40                 0xfd, '7', 'z', 'X', 'Z', 0x00
41         };
42         static const uint8_t gzip_signature[] = {
43                 0x1f, 0x8b
44         };
45         static const uint8_t bzip2_signature[] = {
46                 'B', 'Z', 'h'
47         };
48
49         int r;
50
51         assert(c);
52
53         if (c->type != IMPORT_COMPRESS_UNKNOWN)
54                 return 1;
55
56         if (size < MAX3(sizeof(xz_signature),
57                         sizeof(gzip_signature),
58                         sizeof(bzip2_signature)))
59                 return 0;
60
61         assert(data);
62
63         if (memcmp(data, xz_signature, sizeof(xz_signature)) == 0) {
64                 lzma_ret xzr;
65
66                 xzr = lzma_stream_decoder(&c->xz, UINT64_MAX, LZMA_TELL_UNSUPPORTED_CHECK);
67                 if (xzr != LZMA_OK)
68                         return -EIO;
69
70                 c->type = IMPORT_COMPRESS_XZ;
71
72         } else if (memcmp(data, gzip_signature, sizeof(gzip_signature)) == 0) {
73                 r = inflateInit2(&c->gzip, 15+16);
74                 if (r != Z_OK)
75                         return -EIO;
76
77                 c->type = IMPORT_COMPRESS_GZIP;
78
79         } else if (memcmp(data, bzip2_signature, sizeof(bzip2_signature)) == 0) {
80                 r = BZ2_bzDecompressInit(&c->bzip2, 0, 0);
81                 if (r != BZ_OK)
82                         return -EIO;
83
84                 c->type = IMPORT_COMPRESS_BZIP2;
85         } else
86                 c->type = IMPORT_COMPRESS_UNCOMPRESSED;
87
88         return 1;
89 }
90
91 int import_uncompress(ImportCompress *c, const void *data, size_t size, ImportCompressCallback callback, void *userdata) {
92         int r;
93
94         assert(c);
95         assert(callback);
96
97         r = import_uncompress_detect(c, data, size);
98         if (r <= 0)
99                 return r;
100
101         if (size <= 0)
102                 return 1;
103
104         assert(data);
105
106         switch (c->type) {
107
108         case IMPORT_COMPRESS_UNCOMPRESSED:
109                 r = callback(data, size, userdata);
110                 if (r < 0)
111                         return r;
112
113                 break;
114
115         case IMPORT_COMPRESS_XZ:
116                 c->xz.next_in = data;
117                 c->xz.avail_in = size;
118
119                 while (c->xz.avail_in > 0) {
120                         uint8_t buffer[16 * 1024];
121                         lzma_ret lzr;
122
123                         c->xz.next_out = buffer;
124                         c->xz.avail_out = sizeof(buffer);
125
126                         lzr = lzma_code(&c->xz, LZMA_RUN);
127                         if (lzr != LZMA_OK && lzr != LZMA_STREAM_END)
128                                 return -EIO;
129
130                         r = callback(buffer, sizeof(buffer) - c->xz.avail_out, userdata);
131                         if (r < 0)
132                                 return r;
133                 }
134
135                 break;
136
137         case IMPORT_COMPRESS_GZIP:
138                 c->gzip.next_in = (void*) data;
139                 c->gzip.avail_in = size;
140
141                 while (c->gzip.avail_in > 0) {
142                         uint8_t buffer[16 * 1024];
143
144                         c->gzip.next_out = buffer;
145                         c->gzip.avail_out = sizeof(buffer);
146
147                         r = inflate(&c->gzip, Z_NO_FLUSH);
148                         if (r != Z_OK && r != Z_STREAM_END)
149                                 return -EIO;
150
151                         r = callback(buffer, sizeof(buffer) - c->gzip.avail_out, userdata);
152                         if (r < 0)
153                                 return r;
154                 }
155
156                 break;
157
158         case IMPORT_COMPRESS_BZIP2:
159                 c->bzip2.next_in = (void*) data;
160                 c->bzip2.avail_in = size;
161
162                 while (c->bzip2.avail_in > 0) {
163                         uint8_t buffer[16 * 1024];
164
165                         c->bzip2.next_out = (char*) buffer;
166                         c->bzip2.avail_out = sizeof(buffer);
167
168                         r = BZ2_bzDecompress(&c->bzip2);
169                         if (r != BZ_OK && r != BZ_STREAM_END)
170                                 return -EIO;
171
172                         r = callback(buffer, sizeof(buffer) - c->bzip2.avail_out, userdata);
173                         if (r < 0)
174                                 return r;
175                 }
176
177                 break;
178
179         default:
180                 assert_not_reached("Unknown compression");
181         }
182
183         return 1;
184 }
185
186 static const char* const import_compress_type_table[_IMPORT_COMPRESS_TYPE_MAX] = {
187         [IMPORT_COMPRESS_UNKNOWN] = "unknown",
188         [IMPORT_COMPRESS_UNCOMPRESSED] = "uncompressed",
189         [IMPORT_COMPRESS_XZ] = "xz",
190         [IMPORT_COMPRESS_GZIP] = "gzip",
191         [IMPORT_COMPRESS_BZIP2] = "bzip2",
192 };
193
194 DEFINE_STRING_TABLE_LOOKUP(import_compress_type, ImportCompressType);