Root/crypto/zlib.c

1/*
2 * Cryptographic API.
3 *
4 * Zlib algorithm
5 *
6 * Copyright 2008 Sony Corporation
7 *
8 * Based on deflate.c, which is
9 * Copyright (c) 2003 James Morris <jmorris@intercode.com.au>
10 *
11 * This program is free software; you can redistribute it and/or modify it
12 * under the terms of the GNU General Public License as published by the Free
13 * Software Foundation; either version 2 of the License, or (at your option)
14 * any later version.
15 *
16 * FIXME: deflate transforms will require up to a total of about 436k of kernel
17 * memory on i386 (390k for compression, the rest for decompression), as the
18 * current zlib kernel code uses a worst case pre-allocation system by default.
19 * This needs to be fixed so that the amount of memory required is properly
20 * related to the winbits and memlevel parameters.
21 */
22
23#define pr_fmt(fmt) "%s: " fmt, __func__
24
25#include <linux/init.h>
26#include <linux/module.h>
27#include <linux/zlib.h>
28#include <linux/vmalloc.h>
29#include <linux/interrupt.h>
30#include <linux/mm.h>
31#include <linux/net.h>
32#include <linux/slab.h>
33
34#include <crypto/internal/compress.h>
35
36#include <net/netlink.h>
37
38
39struct zlib_ctx {
40    struct z_stream_s comp_stream;
41    struct z_stream_s decomp_stream;
42    int decomp_windowBits;
43};
44
45
46static void zlib_comp_exit(struct zlib_ctx *ctx)
47{
48    struct z_stream_s *stream = &ctx->comp_stream;
49
50    if (stream->workspace) {
51        zlib_deflateEnd(stream);
52        vfree(stream->workspace);
53        stream->workspace = NULL;
54    }
55}
56
57static void zlib_decomp_exit(struct zlib_ctx *ctx)
58{
59    struct z_stream_s *stream = &ctx->decomp_stream;
60
61    if (stream->workspace) {
62        zlib_inflateEnd(stream);
63        kfree(stream->workspace);
64        stream->workspace = NULL;
65    }
66}
67
68static int zlib_init(struct crypto_tfm *tfm)
69{
70    return 0;
71}
72
73static void zlib_exit(struct crypto_tfm *tfm)
74{
75    struct zlib_ctx *ctx = crypto_tfm_ctx(tfm);
76
77    zlib_comp_exit(ctx);
78    zlib_decomp_exit(ctx);
79}
80
81
82static int zlib_compress_setup(struct crypto_pcomp *tfm, void *params,
83                   unsigned int len)
84{
85    struct zlib_ctx *ctx = crypto_tfm_ctx(crypto_pcomp_tfm(tfm));
86    struct z_stream_s *stream = &ctx->comp_stream;
87    struct nlattr *tb[ZLIB_COMP_MAX + 1];
88    size_t workspacesize;
89    int ret;
90
91    ret = nla_parse(tb, ZLIB_COMP_MAX, params, len, NULL);
92    if (ret)
93        return ret;
94
95    zlib_comp_exit(ctx);
96
97    workspacesize = zlib_deflate_workspacesize();
98    stream->workspace = vmalloc(workspacesize);
99    if (!stream->workspace)
100        return -ENOMEM;
101
102    memset(stream->workspace, 0, workspacesize);
103    ret = zlib_deflateInit2(stream,
104                tb[ZLIB_COMP_LEVEL]
105                    ? nla_get_u32(tb[ZLIB_COMP_LEVEL])
106                    : Z_DEFAULT_COMPRESSION,
107                tb[ZLIB_COMP_METHOD]
108                    ? nla_get_u32(tb[ZLIB_COMP_METHOD])
109                    : Z_DEFLATED,
110                tb[ZLIB_COMP_WINDOWBITS]
111                    ? nla_get_u32(tb[ZLIB_COMP_WINDOWBITS])
112                    : MAX_WBITS,
113                tb[ZLIB_COMP_MEMLEVEL]
114                    ? nla_get_u32(tb[ZLIB_COMP_MEMLEVEL])
115                    : DEF_MEM_LEVEL,
116                tb[ZLIB_COMP_STRATEGY]
117                    ? nla_get_u32(tb[ZLIB_COMP_STRATEGY])
118                    : Z_DEFAULT_STRATEGY);
119    if (ret != Z_OK) {
120        vfree(stream->workspace);
121        stream->workspace = NULL;
122        return -EINVAL;
123    }
124
125    return 0;
126}
127
128static int zlib_compress_init(struct crypto_pcomp *tfm)
129{
130    int ret;
131    struct zlib_ctx *dctx = crypto_tfm_ctx(crypto_pcomp_tfm(tfm));
132    struct z_stream_s *stream = &dctx->comp_stream;
133
134    ret = zlib_deflateReset(stream);
135    if (ret != Z_OK)
136        return -EINVAL;
137
138    return 0;
139}
140
141static int zlib_compress_update(struct crypto_pcomp *tfm,
142                struct comp_request *req)
143{
144    int ret;
145    struct zlib_ctx *dctx = crypto_tfm_ctx(crypto_pcomp_tfm(tfm));
146    struct z_stream_s *stream = &dctx->comp_stream;
147
148    pr_debug("avail_in %u, avail_out %u\n", req->avail_in, req->avail_out);
149    stream->next_in = req->next_in;
150    stream->avail_in = req->avail_in;
151    stream->next_out = req->next_out;
152    stream->avail_out = req->avail_out;
153
154    ret = zlib_deflate(stream, Z_NO_FLUSH);
155    switch (ret) {
156    case Z_OK:
157        break;
158
159    case Z_BUF_ERROR:
160        pr_debug("zlib_deflate could not make progress\n");
161        return -EAGAIN;
162
163    default:
164        pr_debug("zlib_deflate failed %d\n", ret);
165        return -EINVAL;
166    }
167
168    ret = req->avail_out - stream->avail_out;
169    pr_debug("avail_in %u, avail_out %u (consumed %u, produced %u)\n",
170         stream->avail_in, stream->avail_out,
171         req->avail_in - stream->avail_in, ret);
172    req->next_in = stream->next_in;
173    req->avail_in = stream->avail_in;
174    req->next_out = stream->next_out;
175    req->avail_out = stream->avail_out;
176    return ret;
177}
178
179static int zlib_compress_final(struct crypto_pcomp *tfm,
180                   struct comp_request *req)
181{
182    int ret;
183    struct zlib_ctx *dctx = crypto_tfm_ctx(crypto_pcomp_tfm(tfm));
184    struct z_stream_s *stream = &dctx->comp_stream;
185
186    pr_debug("avail_in %u, avail_out %u\n", req->avail_in, req->avail_out);
187    stream->next_in = req->next_in;
188    stream->avail_in = req->avail_in;
189    stream->next_out = req->next_out;
190    stream->avail_out = req->avail_out;
191
192    ret = zlib_deflate(stream, Z_FINISH);
193    if (ret != Z_STREAM_END) {
194        pr_debug("zlib_deflate failed %d\n", ret);
195        return -EINVAL;
196    }
197
198    ret = req->avail_out - stream->avail_out;
199    pr_debug("avail_in %u, avail_out %u (consumed %u, produced %u)\n",
200         stream->avail_in, stream->avail_out,
201         req->avail_in - stream->avail_in, ret);
202    req->next_in = stream->next_in;
203    req->avail_in = stream->avail_in;
204    req->next_out = stream->next_out;
205    req->avail_out = stream->avail_out;
206    return ret;
207}
208
209
210static int zlib_decompress_setup(struct crypto_pcomp *tfm, void *params,
211                 unsigned int len)
212{
213    struct zlib_ctx *ctx = crypto_tfm_ctx(crypto_pcomp_tfm(tfm));
214    struct z_stream_s *stream = &ctx->decomp_stream;
215    struct nlattr *tb[ZLIB_DECOMP_MAX + 1];
216    int ret = 0;
217
218    ret = nla_parse(tb, ZLIB_DECOMP_MAX, params, len, NULL);
219    if (ret)
220        return ret;
221
222    zlib_decomp_exit(ctx);
223
224    ctx->decomp_windowBits = tb[ZLIB_DECOMP_WINDOWBITS]
225                 ? nla_get_u32(tb[ZLIB_DECOMP_WINDOWBITS])
226                 : DEF_WBITS;
227
228    stream->workspace = kzalloc(zlib_inflate_workspacesize(), GFP_KERNEL);
229    if (!stream->workspace)
230        return -ENOMEM;
231
232    ret = zlib_inflateInit2(stream, ctx->decomp_windowBits);
233    if (ret != Z_OK) {
234        kfree(stream->workspace);
235        stream->workspace = NULL;
236        return -EINVAL;
237    }
238
239    return 0;
240}
241
242static int zlib_decompress_init(struct crypto_pcomp *tfm)
243{
244    int ret;
245    struct zlib_ctx *dctx = crypto_tfm_ctx(crypto_pcomp_tfm(tfm));
246    struct z_stream_s *stream = &dctx->decomp_stream;
247
248    ret = zlib_inflateReset(stream);
249    if (ret != Z_OK)
250        return -EINVAL;
251
252    return 0;
253}
254
255static int zlib_decompress_update(struct crypto_pcomp *tfm,
256                  struct comp_request *req)
257{
258    int ret;
259    struct zlib_ctx *dctx = crypto_tfm_ctx(crypto_pcomp_tfm(tfm));
260    struct z_stream_s *stream = &dctx->decomp_stream;
261
262    pr_debug("avail_in %u, avail_out %u\n", req->avail_in, req->avail_out);
263    stream->next_in = req->next_in;
264    stream->avail_in = req->avail_in;
265    stream->next_out = req->next_out;
266    stream->avail_out = req->avail_out;
267
268    ret = zlib_inflate(stream, Z_SYNC_FLUSH);
269    switch (ret) {
270    case Z_OK:
271    case Z_STREAM_END:
272        break;
273
274    case Z_BUF_ERROR:
275        pr_debug("zlib_inflate could not make progress\n");
276        return -EAGAIN;
277
278    default:
279        pr_debug("zlib_inflate failed %d\n", ret);
280        return -EINVAL;
281    }
282
283    ret = req->avail_out - stream->avail_out;
284    pr_debug("avail_in %u, avail_out %u (consumed %u, produced %u)\n",
285         stream->avail_in, stream->avail_out,
286         req->avail_in - stream->avail_in, ret);
287    req->next_in = stream->next_in;
288    req->avail_in = stream->avail_in;
289    req->next_out = stream->next_out;
290    req->avail_out = stream->avail_out;
291    return ret;
292}
293
294static int zlib_decompress_final(struct crypto_pcomp *tfm,
295                 struct comp_request *req)
296{
297    int ret;
298    struct zlib_ctx *dctx = crypto_tfm_ctx(crypto_pcomp_tfm(tfm));
299    struct z_stream_s *stream = &dctx->decomp_stream;
300
301    pr_debug("avail_in %u, avail_out %u\n", req->avail_in, req->avail_out);
302    stream->next_in = req->next_in;
303    stream->avail_in = req->avail_in;
304    stream->next_out = req->next_out;
305    stream->avail_out = req->avail_out;
306
307    if (dctx->decomp_windowBits < 0) {
308        ret = zlib_inflate(stream, Z_SYNC_FLUSH);
309        /*
310         * Work around a bug in zlib, which sometimes wants to taste an
311         * extra byte when being used in the (undocumented) raw deflate
312         * mode. (From USAGI).
313         */
314        if (ret == Z_OK && !stream->avail_in && stream->avail_out) {
315            const void *saved_next_in = stream->next_in;
316            u8 zerostuff = 0;
317
318            stream->next_in = &zerostuff;
319            stream->avail_in = 1;
320            ret = zlib_inflate(stream, Z_FINISH);
321            stream->next_in = saved_next_in;
322            stream->avail_in = 0;
323        }
324    } else
325        ret = zlib_inflate(stream, Z_FINISH);
326    if (ret != Z_STREAM_END) {
327        pr_debug("zlib_inflate failed %d\n", ret);
328        return -EINVAL;
329    }
330
331    ret = req->avail_out - stream->avail_out;
332    pr_debug("avail_in %u, avail_out %u (consumed %u, produced %u)\n",
333         stream->avail_in, stream->avail_out,
334         req->avail_in - stream->avail_in, ret);
335    req->next_in = stream->next_in;
336    req->avail_in = stream->avail_in;
337    req->next_out = stream->next_out;
338    req->avail_out = stream->avail_out;
339    return ret;
340}
341
342
343static struct pcomp_alg zlib_alg = {
344    .compress_setup = zlib_compress_setup,
345    .compress_init = zlib_compress_init,
346    .compress_update = zlib_compress_update,
347    .compress_final = zlib_compress_final,
348    .decompress_setup = zlib_decompress_setup,
349    .decompress_init = zlib_decompress_init,
350    .decompress_update = zlib_decompress_update,
351    .decompress_final = zlib_decompress_final,
352
353    .base = {
354        .cra_name = "zlib",
355        .cra_flags = CRYPTO_ALG_TYPE_PCOMPRESS,
356        .cra_ctxsize = sizeof(struct zlib_ctx),
357        .cra_module = THIS_MODULE,
358        .cra_init = zlib_init,
359        .cra_exit = zlib_exit,
360    }
361};
362
363static int __init zlib_mod_init(void)
364{
365    return crypto_register_pcomp(&zlib_alg);
366}
367
368static void __exit zlib_mod_fini(void)
369{
370    crypto_unregister_pcomp(&zlib_alg);
371}
372
373module_init(zlib_mod_init);
374module_exit(zlib_mod_fini);
375
376MODULE_LICENSE("GPL");
377MODULE_DESCRIPTION("Zlib Compression Algorithm");
378MODULE_AUTHOR("Sony Corporation");
379

Archive Download this file



interactive