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

Archive Download this file



interactive