at v6.17 5.5 kB view raw
1// SPDX-License-Identifier: GPL-2.0-or-later 2/* 3 * Cryptographic API. 4 * 5 * Deflate algorithm (RFC 1951), implemented here primarily for use 6 * by IPCOMP (RFC 3173 & RFC 2394). 7 * 8 * Copyright (c) 2003 James Morris <jmorris@intercode.com.au> 9 * Copyright (c) 2023 Google, LLC. <ardb@kernel.org> 10 * Copyright (c) 2025 Herbert Xu <herbert@gondor.apana.org.au> 11 */ 12#include <crypto/internal/acompress.h> 13#include <crypto/scatterwalk.h> 14#include <linux/init.h> 15#include <linux/kernel.h> 16#include <linux/module.h> 17#include <linux/mutex.h> 18#include <linux/percpu.h> 19#include <linux/scatterlist.h> 20#include <linux/slab.h> 21#include <linux/spinlock.h> 22#include <linux/zlib.h> 23 24#define DEFLATE_DEF_LEVEL Z_DEFAULT_COMPRESSION 25#define DEFLATE_DEF_WINBITS 11 26#define DEFLATE_DEF_MEMLEVEL MAX_MEM_LEVEL 27 28struct deflate_stream { 29 struct z_stream_s stream; 30 u8 workspace[]; 31}; 32 33static DEFINE_MUTEX(deflate_stream_lock); 34 35static void *deflate_alloc_stream(void) 36{ 37 size_t size = max(zlib_inflate_workspacesize(), 38 zlib_deflate_workspacesize(-DEFLATE_DEF_WINBITS, 39 DEFLATE_DEF_MEMLEVEL)); 40 struct deflate_stream *ctx; 41 42 ctx = kvmalloc(sizeof(*ctx) + size, GFP_KERNEL); 43 if (!ctx) 44 return ERR_PTR(-ENOMEM); 45 46 ctx->stream.workspace = ctx->workspace; 47 48 return ctx; 49} 50 51static void deflate_free_stream(void *ctx) 52{ 53 kvfree(ctx); 54} 55 56static struct crypto_acomp_streams deflate_streams = { 57 .alloc_ctx = deflate_alloc_stream, 58 .free_ctx = deflate_free_stream, 59}; 60 61static int deflate_compress_one(struct acomp_req *req, 62 struct deflate_stream *ds) 63{ 64 struct z_stream_s *stream = &ds->stream; 65 struct acomp_walk walk; 66 int ret; 67 68 ret = acomp_walk_virt(&walk, req, true); 69 if (ret) 70 return ret; 71 72 do { 73 unsigned int dcur; 74 75 dcur = acomp_walk_next_dst(&walk); 76 if (!dcur) 77 return -ENOSPC; 78 79 stream->avail_out = dcur; 80 stream->next_out = walk.dst.virt.addr; 81 82 do { 83 int flush = Z_FINISH; 84 unsigned int scur; 85 86 stream->avail_in = 0; 87 stream->next_in = NULL; 88 89 scur = acomp_walk_next_src(&walk); 90 if (scur) { 91 if (acomp_walk_more_src(&walk, scur)) 92 flush = Z_NO_FLUSH; 93 stream->avail_in = scur; 94 stream->next_in = walk.src.virt.addr; 95 } 96 97 ret = zlib_deflate(stream, flush); 98 99 if (scur) { 100 scur -= stream->avail_in; 101 acomp_walk_done_src(&walk, scur); 102 } 103 } while (ret == Z_OK && stream->avail_out); 104 105 acomp_walk_done_dst(&walk, dcur); 106 } while (ret == Z_OK); 107 108 if (ret != Z_STREAM_END) 109 return -EINVAL; 110 111 req->dlen = stream->total_out; 112 return 0; 113} 114 115static int deflate_compress(struct acomp_req *req) 116{ 117 struct crypto_acomp_stream *s; 118 struct deflate_stream *ds; 119 int err; 120 121 s = crypto_acomp_lock_stream_bh(&deflate_streams); 122 ds = s->ctx; 123 124 err = zlib_deflateInit2(&ds->stream, DEFLATE_DEF_LEVEL, Z_DEFLATED, 125 -DEFLATE_DEF_WINBITS, DEFLATE_DEF_MEMLEVEL, 126 Z_DEFAULT_STRATEGY); 127 if (err != Z_OK) { 128 err = -EINVAL; 129 goto out; 130 } 131 132 err = deflate_compress_one(req, ds); 133 134out: 135 crypto_acomp_unlock_stream_bh(s); 136 137 return err; 138} 139 140static int deflate_decompress_one(struct acomp_req *req, 141 struct deflate_stream *ds) 142{ 143 struct z_stream_s *stream = &ds->stream; 144 bool out_of_space = false; 145 struct acomp_walk walk; 146 int ret; 147 148 ret = acomp_walk_virt(&walk, req, true); 149 if (ret) 150 return ret; 151 152 do { 153 unsigned int scur; 154 155 stream->avail_in = 0; 156 stream->next_in = NULL; 157 158 scur = acomp_walk_next_src(&walk); 159 if (scur) { 160 stream->avail_in = scur; 161 stream->next_in = walk.src.virt.addr; 162 } 163 164 do { 165 unsigned int dcur; 166 167 dcur = acomp_walk_next_dst(&walk); 168 if (!dcur) { 169 out_of_space = true; 170 break; 171 } 172 173 stream->avail_out = dcur; 174 stream->next_out = walk.dst.virt.addr; 175 176 ret = zlib_inflate(stream, Z_NO_FLUSH); 177 178 dcur -= stream->avail_out; 179 acomp_walk_done_dst(&walk, dcur); 180 } while (ret == Z_OK && stream->avail_in); 181 182 if (scur) 183 acomp_walk_done_src(&walk, scur); 184 185 if (out_of_space) 186 return -ENOSPC; 187 } while (ret == Z_OK); 188 189 if (ret != Z_STREAM_END) 190 return -EINVAL; 191 192 req->dlen = stream->total_out; 193 return 0; 194} 195 196static int deflate_decompress(struct acomp_req *req) 197{ 198 struct crypto_acomp_stream *s; 199 struct deflate_stream *ds; 200 int err; 201 202 s = crypto_acomp_lock_stream_bh(&deflate_streams); 203 ds = s->ctx; 204 205 err = zlib_inflateInit2(&ds->stream, -DEFLATE_DEF_WINBITS); 206 if (err != Z_OK) { 207 err = -EINVAL; 208 goto out; 209 } 210 211 err = deflate_decompress_one(req, ds); 212 213out: 214 crypto_acomp_unlock_stream_bh(s); 215 216 return err; 217} 218 219static int deflate_init(struct crypto_acomp *tfm) 220{ 221 int ret; 222 223 mutex_lock(&deflate_stream_lock); 224 ret = crypto_acomp_alloc_streams(&deflate_streams); 225 mutex_unlock(&deflate_stream_lock); 226 227 return ret; 228} 229 230static struct acomp_alg acomp = { 231 .compress = deflate_compress, 232 .decompress = deflate_decompress, 233 .init = deflate_init, 234 .base.cra_name = "deflate", 235 .base.cra_driver_name = "deflate-generic", 236 .base.cra_flags = CRYPTO_ALG_REQ_VIRT, 237 .base.cra_module = THIS_MODULE, 238}; 239 240static int __init deflate_mod_init(void) 241{ 242 return crypto_register_acomp(&acomp); 243} 244 245static void __exit deflate_mod_fini(void) 246{ 247 crypto_unregister_acomp(&acomp); 248 crypto_acomp_free_streams(&deflate_streams); 249} 250 251module_init(deflate_mod_init); 252module_exit(deflate_mod_fini); 253 254MODULE_LICENSE("GPL"); 255MODULE_DESCRIPTION("Deflate Compression Algorithm for IPCOMP"); 256MODULE_AUTHOR("James Morris <jmorris@intercode.com.au>"); 257MODULE_AUTHOR("Ard Biesheuvel <ardb@kernel.org>"); 258MODULE_AUTHOR("Herbert Xu <herbert@gondor.apana.org.au>"); 259MODULE_ALIAS_CRYPTO("deflate"); 260MODULE_ALIAS_CRYPTO("deflate-generic");