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
33#include <crypto/internal/compress.h>
34
35#include <net/netlink.h>
36
37
38struct zlib_ctx {
39    struct z_stream_s comp_stream;
40    struct z_stream_s decomp_stream;
41    int decomp_windowBits;
42};
43
44
45static void zlib_comp_exit(struct zlib_ctx *ctx)
46{
47    struct z_stream_s *stream = &ctx->comp_stream;
48
49    if (stream->workspace) {
50        zlib_deflateEnd(stream);
51        vfree(stream->workspace);
52        stream->workspace = NULL;
53    }
54}
55
56static void zlib_decomp_exit(struct zlib_ctx *ctx)
57{
58    struct z_stream_s *stream = &ctx->decomp_stream;
59
60    if (stream->workspace) {
61        zlib_inflateEnd(stream);
62        vfree(stream->workspace);
63        stream->workspace = NULL;
64    }
65}
66
67static int zlib_init(struct crypto_tfm *tfm)
68{
69    return 0;
70}
71
72static void zlib_exit(struct crypto_tfm *tfm)
73{
74    struct zlib_ctx *ctx = crypto_tfm_ctx(tfm);
75
76    zlib_comp_exit(ctx);
77    zlib_decomp_exit(ctx);
78}
79
80
81static int zlib_compress_setup(struct crypto_pcomp *tfm, void *params,
82                   unsigned int len)
83{
84    struct zlib_ctx *ctx = crypto_tfm_ctx(crypto_pcomp_tfm(tfm));
85    struct z_stream_s *stream = &ctx->comp_stream;
86    struct nlattr *tb[ZLIB_COMP_MAX + 1];
87    int window_bits, mem_level;
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    window_bits = tb[ZLIB_COMP_WINDOWBITS]
98                    ? nla_get_u32(tb[ZLIB_COMP_WINDOWBITS])
99                    : MAX_WBITS;
100    mem_level = tb[ZLIB_COMP_MEMLEVEL]
101                    ? nla_get_u32(tb[ZLIB_COMP_MEMLEVEL])
102                    : DEF_MEM_LEVEL;
103
104    workspacesize = zlib_deflate_workspacesize(window_bits, mem_level);
105    stream->workspace = vzalloc(workspacesize);
106    if (!stream->workspace)
107        return -ENOMEM;
108
109    ret = zlib_deflateInit2(stream,
110                tb[ZLIB_COMP_LEVEL]
111                    ? nla_get_u32(tb[ZLIB_COMP_LEVEL])
112                    : Z_DEFAULT_COMPRESSION,
113                tb[ZLIB_COMP_METHOD]
114                    ? nla_get_u32(tb[ZLIB_COMP_METHOD])
115                    : Z_DEFLATED,
116                window_bits,
117                mem_level,
118                tb[ZLIB_COMP_STRATEGY]
119                    ? nla_get_u32(tb[ZLIB_COMP_STRATEGY])
120                    : Z_DEFAULT_STRATEGY);
121    if (ret != Z_OK) {
122        vfree(stream->workspace);
123        stream->workspace = NULL;
124        return -EINVAL;
125    }
126
127    return 0;
128}
129
130static int zlib_compress_init(struct crypto_pcomp *tfm)
131{
132    int ret;
133    struct zlib_ctx *dctx = crypto_tfm_ctx(crypto_pcomp_tfm(tfm));
134    struct z_stream_s *stream = &dctx->comp_stream;
135
136    ret = zlib_deflateReset(stream);
137    if (ret != Z_OK)
138        return -EINVAL;
139
140    return 0;
141}
142
143static int zlib_compress_update(struct crypto_pcomp *tfm,
144                struct comp_request *req)
145{
146    int ret;
147    struct zlib_ctx *dctx = crypto_tfm_ctx(crypto_pcomp_tfm(tfm));
148    struct z_stream_s *stream = &dctx->comp_stream;
149
150    pr_debug("avail_in %u, avail_out %u\n", req->avail_in, req->avail_out);
151    stream->next_in = req->next_in;
152    stream->avail_in = req->avail_in;
153    stream->next_out = req->next_out;
154    stream->avail_out = req->avail_out;
155
156    ret = zlib_deflate(stream, Z_NO_FLUSH);
157    switch (ret) {
158    case Z_OK:
159        break;
160
161    case Z_BUF_ERROR:
162        pr_debug("zlib_deflate could not make progress\n");
163        return -EAGAIN;
164
165    default:
166        pr_debug("zlib_deflate failed %d\n", ret);
167        return -EINVAL;
168    }
169
170    ret = req->avail_out - stream->avail_out;
171    pr_debug("avail_in %lu, avail_out %lu (consumed %lu, produced %u)\n",
172         stream->avail_in, stream->avail_out,
173         req->avail_in - stream->avail_in, ret);
174    req->next_in = stream->next_in;
175    req->avail_in = stream->avail_in;
176    req->next_out = stream->next_out;
177    req->avail_out = stream->avail_out;
178    return ret;
179}
180
181static int zlib_compress_final(struct crypto_pcomp *tfm,
182                   struct comp_request *req)
183{
184    int ret;
185    struct zlib_ctx *dctx = crypto_tfm_ctx(crypto_pcomp_tfm(tfm));
186    struct z_stream_s *stream = &dctx->comp_stream;
187
188    pr_debug("avail_in %u, avail_out %u\n", req->avail_in, req->avail_out);
189    stream->next_in = req->next_in;
190    stream->avail_in = req->avail_in;
191    stream->next_out = req->next_out;
192    stream->avail_out = req->avail_out;
193
194    ret = zlib_deflate(stream, Z_FINISH);
195    if (ret != Z_STREAM_END) {
196        pr_debug("zlib_deflate failed %d\n", ret);
197        return -EINVAL;
198    }
199
200    ret = req->avail_out - stream->avail_out;
201    pr_debug("avail_in %lu, avail_out %lu (consumed %lu, produced %u)\n",
202         stream->avail_in, stream->avail_out,
203         req->avail_in - stream->avail_in, ret);
204    req->next_in = stream->next_in;
205    req->avail_in = stream->avail_in;
206    req->next_out = stream->next_out;
207    req->avail_out = stream->avail_out;
208    return ret;
209}
210
211
212static int zlib_decompress_setup(struct crypto_pcomp *tfm, void *params,
213                 unsigned int len)
214{
215    struct zlib_ctx *ctx = crypto_tfm_ctx(crypto_pcomp_tfm(tfm));
216    struct z_stream_s *stream = &ctx->decomp_stream;
217    struct nlattr *tb[ZLIB_DECOMP_MAX + 1];
218    int ret = 0;
219
220    ret = nla_parse(tb, ZLIB_DECOMP_MAX, params, len, NULL);
221    if (ret)
222        return ret;
223
224    zlib_decomp_exit(ctx);
225
226    ctx->decomp_windowBits = tb[ZLIB_DECOMP_WINDOWBITS]
227                 ? nla_get_u32(tb[ZLIB_DECOMP_WINDOWBITS])
228                 : DEF_WBITS;
229
230    stream->workspace = vzalloc(zlib_inflate_workspacesize());
231    if (!stream->workspace)
232        return -ENOMEM;
233
234    ret = zlib_inflateInit2(stream, ctx->decomp_windowBits);
235    if (ret != Z_OK) {
236        vfree(stream->workspace);
237        stream->workspace = NULL;
238        return -EINVAL;
239    }
240
241    return 0;
242}
243
244static int zlib_decompress_init(struct crypto_pcomp *tfm)
245{
246    int ret;
247    struct zlib_ctx *dctx = crypto_tfm_ctx(crypto_pcomp_tfm(tfm));
248    struct z_stream_s *stream = &dctx->decomp_stream;
249
250    ret = zlib_inflateReset(stream);
251    if (ret != Z_OK)
252        return -EINVAL;
253
254    return 0;
255}
256
257static int zlib_decompress_update(struct crypto_pcomp *tfm,
258                  struct comp_request *req)
259{
260    int ret;
261    struct zlib_ctx *dctx = crypto_tfm_ctx(crypto_pcomp_tfm(tfm));
262    struct z_stream_s *stream = &dctx->decomp_stream;
263
264    pr_debug("avail_in %u, avail_out %u\n", req->avail_in, req->avail_out);
265    stream->next_in = req->next_in;
266    stream->avail_in = req->avail_in;
267    stream->next_out = req->next_out;
268    stream->avail_out = req->avail_out;
269
270    ret = zlib_inflate(stream, Z_SYNC_FLUSH);
271    switch (ret) {
272    case Z_OK:
273    case Z_STREAM_END:
274        break;
275
276    case Z_BUF_ERROR:
277        pr_debug("zlib_inflate could not make progress\n");
278        return -EAGAIN;
279
280    default:
281        pr_debug("zlib_inflate failed %d\n", ret);
282        return -EINVAL;
283    }
284
285    ret = req->avail_out - stream->avail_out;
286    pr_debug("avail_in %lu, avail_out %lu (consumed %lu, produced %u)\n",
287         stream->avail_in, stream->avail_out,
288         req->avail_in - stream->avail_in, ret);
289    req->next_in = stream->next_in;
290    req->avail_in = stream->avail_in;
291    req->next_out = stream->next_out;
292    req->avail_out = stream->avail_out;
293    return ret;
294}
295
296static int zlib_decompress_final(struct crypto_pcomp *tfm,
297                 struct comp_request *req)
298{
299    int ret;
300    struct zlib_ctx *dctx = crypto_tfm_ctx(crypto_pcomp_tfm(tfm));
301    struct z_stream_s *stream = &dctx->decomp_stream;
302
303    pr_debug("avail_in %u, avail_out %u\n", req->avail_in, req->avail_out);
304    stream->next_in = req->next_in;
305    stream->avail_in = req->avail_in;
306    stream->next_out = req->next_out;
307    stream->avail_out = req->avail_out;
308
309    if (dctx->decomp_windowBits < 0) {
310        ret = zlib_inflate(stream, Z_SYNC_FLUSH);
311        /*
312         * Work around a bug in zlib, which sometimes wants to taste an
313         * extra byte when being used in the (undocumented) raw deflate
314         * mode. (From USAGI).
315         */
316        if (ret == Z_OK && !stream->avail_in && stream->avail_out) {
317            const void *saved_next_in = stream->next_in;
318            u8 zerostuff = 0;
319
320            stream->next_in = &zerostuff;
321            stream->avail_in = 1;
322            ret = zlib_inflate(stream, Z_FINISH);
323            stream->next_in = saved_next_in;
324            stream->avail_in = 0;
325        }
326    } else
327        ret = zlib_inflate(stream, Z_FINISH);
328    if (ret != Z_STREAM_END) {
329        pr_debug("zlib_inflate failed %d\n", ret);
330        return -EINVAL;
331    }
332
333    ret = req->avail_out - stream->avail_out;
334    pr_debug("avail_in %lu, avail_out %lu (consumed %lu, produced %u)\n",
335         stream->avail_in, stream->avail_out,
336         req->avail_in - stream->avail_in, ret);
337    req->next_in = stream->next_in;
338    req->avail_in = stream->avail_in;
339    req->next_out = stream->next_out;
340    req->avail_out = stream->avail_out;
341    return ret;
342}
343
344
345static struct pcomp_alg zlib_alg = {
346    .compress_setup = zlib_compress_setup,
347    .compress_init = zlib_compress_init,
348    .compress_update = zlib_compress_update,
349    .compress_final = zlib_compress_final,
350    .decompress_setup = zlib_decompress_setup,
351    .decompress_init = zlib_decompress_init,
352    .decompress_update = zlib_decompress_update,
353    .decompress_final = zlib_decompress_final,
354
355    .base = {
356        .cra_name = "zlib",
357        .cra_flags = CRYPTO_ALG_TYPE_PCOMPRESS,
358        .cra_ctxsize = sizeof(struct zlib_ctx),
359        .cra_module = THIS_MODULE,
360        .cra_init = zlib_init,
361        .cra_exit = zlib_exit,
362    }
363};
364
365static int __init zlib_mod_init(void)
366{
367    return crypto_register_pcomp(&zlib_alg);
368}
369
370static void __exit zlib_mod_fini(void)
371{
372    crypto_unregister_pcomp(&zlib_alg);
373}
374
375module_init(zlib_mod_init);
376module_exit(zlib_mod_fini);
377
378MODULE_LICENSE("GPL");
379MODULE_DESCRIPTION("Zlib Compression Algorithm");
380MODULE_AUTHOR("Sony Corporation");
381

Archive Download this file



interactive