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