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 28 struct deflate_stream { 29 struct z_stream_s stream; 30 u8 workspace[]; 31 }; 32 33 static DEFINE_MUTEX(deflate_stream_lock); 34 35 static 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 51 static void deflate_free_stream(void *ctx) 52 { 53 kvfree(ctx); 54 } 55 56 static struct crypto_acomp_streams deflate_streams = { 57 .alloc_ctx = deflate_alloc_stream, 58 .free_ctx = deflate_free_stream, 59 }; 60 61 static 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 115 static 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 134 out: 135 crypto_acomp_unlock_stream_bh(s); 136 137 return err; 138 } 139 140 static 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 196 static 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 213 out: 214 crypto_acomp_unlock_stream_bh(s); 215 216 return err; 217 } 218 219 static 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 230 static 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 240 static int __init deflate_mod_init(void) 241 { 242 return crypto_register_acomp(&acomp); 243 } 244 245 static void __exit deflate_mod_fini(void) 246 { 247 crypto_unregister_acomp(&acomp); 248 crypto_acomp_free_streams(&deflate_streams); 249 } 250 251 module_init(deflate_mod_init); 252 module_exit(deflate_mod_fini); 253 254 MODULE_LICENSE("GPL"); 255 MODULE_DESCRIPTION("Deflate Compression Algorithm for IPCOMP"); 256 MODULE_AUTHOR("James Morris <jmorris@intercode.com.au>"); 257 MODULE_AUTHOR("Ard Biesheuvel <ardb@kernel.org>"); 258 MODULE_AUTHOR("Herbert Xu <herbert@gondor.apana.org.au>"); 259 MODULE_ALIAS_CRYPTO("deflate"); 260 MODULE_ALIAS_CRYPTO("deflate-generic"); 261