linux/crypto/zlib.c
<<
>>
Prefs
   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