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 = 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        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,
 171                 req->avail_out - stream->avail_out);
 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 0;
 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        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,
 201                 req->avail_out - stream->avail_out);
 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 0;
 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        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,
 286                 req->avail_out - stream->avail_out);
 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 0;
 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        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,
 334                 req->avail_out - stream->avail_out);
 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 0;
 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