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(struct_size(ctx, workspace, size), GFP_KERNEL); 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 168 dcur = acomp_walk_next_dst(&walk); 169 if (!dcur) { 170 out_of_space = true; 171 break; 172 } 173 174 stream->avail_out = dcur; 175 stream->next_out = walk.dst.virt.addr; 176 177 ret = zlib_inflate(stream, Z_NO_FLUSH); 178 179 dcur -= stream->avail_out; 180 acomp_walk_done_dst(&walk, dcur); 181 } while (ret == Z_OK && stream->avail_in); 182 183 if (scur) 184 acomp_walk_done_src(&walk, scur); 185 186 if (out_of_space) 187 return -ENOSPC; 188 } while (ret == Z_OK); 189 190 if (ret != Z_STREAM_END) 191 return -EINVAL; 192 193 req->dlen = stream->total_out; 194 return 0; 195 } 196 197 static int deflate_decompress(struct acomp_req *req) 198 { 199 struct crypto_acomp_stream *s; 200 struct deflate_stream *ds; 201 int err; 202 203 s = crypto_acomp_lock_stream_bh(&deflate_streams); 204 ds = s->ctx; 205 206 err = zlib_inflateInit2(&ds->stream, -DEFLATE_DEF_WINBITS); 207 if (err != Z_OK) { 208 err = -EINVAL; 209 goto out; 210 } 211 212 err = deflate_decompress_one(req, ds); 213 214 out: 215 crypto_acomp_unlock_stream_bh(s); 216 217 return err; 218 } 219 220 static int deflate_init(struct crypto_acomp *tfm) 221 { 222 int ret; 223 224 mutex_lock(&deflate_stream_lock); 225 ret = crypto_acomp_alloc_streams(&deflate_streams); 226 mutex_unlock(&deflate_stream_lock); 227 228 return ret; 229 } 230 231 static struct acomp_alg acomp = { 232 .compress = deflate_compress, 233 .decompress = deflate_decompress, 234 .init = deflate_init, 235 .base.cra_name = "deflate", 236 .base.cra_driver_name = "deflate-generic", 237 .base.cra_flags = CRYPTO_ALG_REQ_VIRT, 238 .base.cra_module = THIS_MODULE, 239 }; 240 241 static int __init deflate_mod_init(void) 242 { 243 return crypto_register_acomp(&acomp); 244 } 245 246 static void __exit deflate_mod_fini(void) 247 { 248 crypto_unregister_acomp(&acomp); 249 crypto_acomp_free_streams(&deflate_streams); 250 } 251 252 module_init(deflate_mod_init); 253 module_exit(deflate_mod_fini); 254 255 MODULE_LICENSE("GPL"); 256 MODULE_DESCRIPTION("Deflate Compression Algorithm for IPCOMP"); 257 MODULE_AUTHOR("James Morris <jmorris@intercode.com.au>"); 258 MODULE_AUTHOR("Ard Biesheuvel <ardb@kernel.org>"); 259 MODULE_AUTHOR("Herbert Xu <herbert@gondor.apana.org.au>"); 260 MODULE_ALIAS_CRYPTO("deflate"); 261 MODULE_ALIAS_CRYPTO("deflate-generic"); 262