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 struct crypto_acomp_streams deflate_streams = { 52 .alloc_ctx = deflate_alloc_stream, 53 .cfree_ctx = kvfree, 54 }; 55 56 static int deflate_compress_one(struct acomp_req *req, 57 struct deflate_stream *ds) 58 { 59 struct z_stream_s *stream = &ds->stream; 60 struct acomp_walk walk; 61 int ret; 62 63 ret = acomp_walk_virt(&walk, req); 64 if (ret) 65 return ret; 66 67 do { 68 unsigned int dcur; 69 70 dcur = acomp_walk_next_dst(&walk); 71 if (!dcur) 72 return -ENOSPC; 73 74 stream->avail_out = dcur; 75 stream->next_out = walk.dst.virt.addr; 76 77 do { 78 int flush = Z_FINISH; 79 unsigned int scur; 80 81 stream->avail_in = 0; 82 stream->next_in = NULL; 83 84 scur = acomp_walk_next_src(&walk); 85 if (scur) { 86 if (acomp_walk_more_src(&walk, scur)) 87 flush = Z_NO_FLUSH; 88 stream->avail_in = scur; 89 stream->next_in = walk.src.virt.addr; 90 } 91 92 ret = zlib_deflate(stream, flush); 93 94 if (scur) { 95 scur -= stream->avail_in; 96 acomp_walk_done_src(&walk, scur); 97 } 98 } while (ret == Z_OK && stream->avail_out); 99 100 acomp_walk_done_dst(&walk, dcur); 101 } while (ret == Z_OK); 102 103 if (ret != Z_STREAM_END) 104 return -EINVAL; 105 106 req->dlen = stream->total_out; 107 return 0; 108 } 109 110 static int deflate_compress(struct acomp_req *req) 111 { 112 struct crypto_acomp_stream *s; 113 struct deflate_stream *ds; 114 struct acomp_req *r2; 115 int err; 116 117 s = crypto_acomp_lock_stream_bh(&deflate_streams); 118 ds = s->ctx; 119 120 err = zlib_deflateInit2(&ds->stream, DEFLATE_DEF_LEVEL, Z_DEFLATED, 121 -DEFLATE_DEF_WINBITS, DEFLATE_DEF_MEMLEVEL, 122 Z_DEFAULT_STRATEGY); 123 if (err != Z_OK) { 124 err = -EINVAL; 125 goto out; 126 } 127 128 err = deflate_compress_one(req, ds); 129 req->base.err = err; 130 131 list_for_each_entry(r2, &req->base.list, base.list) { 132 zlib_deflateReset(&ds->stream); 133 r2->base.err = deflate_compress_one(r2, ds); 134 } 135 136 out: 137 crypto_acomp_unlock_stream_bh(s); 138 139 return err; 140 } 141 142 static int deflate_decompress_one(struct acomp_req *req, 143 struct deflate_stream *ds) 144 { 145 struct z_stream_s *stream = &ds->stream; 146 bool out_of_space = false; 147 struct acomp_walk walk; 148 int ret; 149 150 ret = acomp_walk_virt(&walk, req); 151 if (ret) 152 return ret; 153 154 do { 155 unsigned int scur; 156 157 stream->avail_in = 0; 158 stream->next_in = NULL; 159 160 scur = acomp_walk_next_src(&walk); 161 if (scur) { 162 stream->avail_in = scur; 163 stream->next_in = walk.src.virt.addr; 164 } 165 166 do { 167 unsigned int dcur; 168 169 dcur = acomp_walk_next_dst(&walk); 170 if (!dcur) { 171 out_of_space = true; 172 break; 173 } 174 175 stream->avail_out = dcur; 176 stream->next_out = walk.dst.virt.addr; 177 178 ret = zlib_inflate(stream, Z_NO_FLUSH); 179 180 dcur -= stream->avail_out; 181 acomp_walk_done_dst(&walk, dcur); 182 } while (ret == Z_OK && stream->avail_in); 183 184 if (scur) 185 acomp_walk_done_src(&walk, scur); 186 187 if (out_of_space) 188 return -ENOSPC; 189 } while (ret == Z_OK); 190 191 if (ret != Z_STREAM_END) 192 return -EINVAL; 193 194 req->dlen = stream->total_out; 195 return 0; 196 } 197 198 static int deflate_decompress(struct acomp_req *req) 199 { 200 struct crypto_acomp_stream *s; 201 struct deflate_stream *ds; 202 struct acomp_req *r2; 203 int err; 204 205 s = crypto_acomp_lock_stream_bh(&deflate_streams); 206 ds = s->ctx; 207 208 err = zlib_inflateInit2(&ds->stream, -DEFLATE_DEF_WINBITS); 209 if (err != Z_OK) { 210 err = -EINVAL; 211 goto out; 212 } 213 214 err = deflate_decompress_one(req, ds); 215 req->base.err = err; 216 217 list_for_each_entry(r2, &req->base.list, base.list) { 218 zlib_inflateReset(&ds->stream); 219 r2->base.err = deflate_decompress_one(r2, ds); 220 } 221 222 out: 223 crypto_acomp_unlock_stream_bh(s); 224 225 return err; 226 } 227 228 static int deflate_init(struct crypto_acomp *tfm) 229 { 230 int ret; 231 232 mutex_lock(&deflate_stream_lock); 233 ret = crypto_acomp_alloc_streams(&deflate_streams); 234 mutex_unlock(&deflate_stream_lock); 235 236 return ret; 237 } 238 239 static struct acomp_alg acomp = { 240 .compress = deflate_compress, 241 .decompress = deflate_decompress, 242 .init = deflate_init, 243 .base.cra_name = "deflate", 244 .base.cra_driver_name = "deflate-generic", 245 .base.cra_flags = CRYPTO_ALG_REQ_CHAIN, 246 .base.cra_module = THIS_MODULE, 247 }; 248 249 static int __init deflate_mod_init(void) 250 { 251 return crypto_register_acomp(&acomp); 252 } 253 254 static void __exit deflate_mod_fini(void) 255 { 256 crypto_unregister_acomp(&acomp); 257 crypto_acomp_free_streams(&deflate_streams); 258 } 259 260 subsys_initcall(deflate_mod_init); 261 module_exit(deflate_mod_fini); 262 263 MODULE_LICENSE("GPL"); 264 MODULE_DESCRIPTION("Deflate Compression Algorithm for IPCOMP"); 265 MODULE_AUTHOR("James Morris <jmorris@intercode.com.au>"); 266 MODULE_AUTHOR("Ard Biesheuvel <ardb@kernel.org>"); 267 MODULE_AUTHOR("Herbert Xu <herbert@gondor.apana.org.au>"); 268 MODULE_ALIAS_CRYPTO("deflate"); 269 MODULE_ALIAS_CRYPTO("deflate-generic"); 270