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, true); 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 int err; 115 116 s = crypto_acomp_lock_stream_bh(&deflate_streams); 117 ds = s->ctx; 118 119 err = zlib_deflateInit2(&ds->stream, DEFLATE_DEF_LEVEL, Z_DEFLATED, 120 -DEFLATE_DEF_WINBITS, DEFLATE_DEF_MEMLEVEL, 121 Z_DEFAULT_STRATEGY); 122 if (err != Z_OK) { 123 err = -EINVAL; 124 goto out; 125 } 126 127 err = deflate_compress_one(req, ds); 128 129 out: 130 crypto_acomp_unlock_stream_bh(s); 131 132 return err; 133 } 134 135 static int deflate_decompress_one(struct acomp_req *req, 136 struct deflate_stream *ds) 137 { 138 struct z_stream_s *stream = &ds->stream; 139 bool out_of_space = false; 140 struct acomp_walk walk; 141 int ret; 142 143 ret = acomp_walk_virt(&walk, req, true); 144 if (ret) 145 return ret; 146 147 do { 148 unsigned int scur; 149 150 stream->avail_in = 0; 151 stream->next_in = NULL; 152 153 scur = acomp_walk_next_src(&walk); 154 if (scur) { 155 stream->avail_in = scur; 156 stream->next_in = walk.src.virt.addr; 157 } 158 159 do { 160 unsigned int dcur; 161 162 dcur = acomp_walk_next_dst(&walk); 163 if (!dcur) { 164 out_of_space = true; 165 break; 166 } 167 168 stream->avail_out = dcur; 169 stream->next_out = walk.dst.virt.addr; 170 171 ret = zlib_inflate(stream, Z_NO_FLUSH); 172 173 dcur -= stream->avail_out; 174 acomp_walk_done_dst(&walk, dcur); 175 } while (ret == Z_OK && stream->avail_in); 176 177 if (scur) 178 acomp_walk_done_src(&walk, scur); 179 180 if (out_of_space) 181 return -ENOSPC; 182 } while (ret == Z_OK); 183 184 if (ret != Z_STREAM_END) 185 return -EINVAL; 186 187 req->dlen = stream->total_out; 188 return 0; 189 } 190 191 static int deflate_decompress(struct acomp_req *req) 192 { 193 struct crypto_acomp_stream *s; 194 struct deflate_stream *ds; 195 int err; 196 197 s = crypto_acomp_lock_stream_bh(&deflate_streams); 198 ds = s->ctx; 199 200 err = zlib_inflateInit2(&ds->stream, -DEFLATE_DEF_WINBITS); 201 if (err != Z_OK) { 202 err = -EINVAL; 203 goto out; 204 } 205 206 err = deflate_decompress_one(req, ds); 207 208 out: 209 crypto_acomp_unlock_stream_bh(s); 210 211 return err; 212 } 213 214 static int deflate_init(struct crypto_acomp *tfm) 215 { 216 int ret; 217 218 mutex_lock(&deflate_stream_lock); 219 ret = crypto_acomp_alloc_streams(&deflate_streams); 220 mutex_unlock(&deflate_stream_lock); 221 222 return ret; 223 } 224 225 static struct acomp_alg acomp = { 226 .compress = deflate_compress, 227 .decompress = deflate_decompress, 228 .init = deflate_init, 229 .base.cra_name = "deflate", 230 .base.cra_driver_name = "deflate-generic", 231 .base.cra_flags = CRYPTO_ALG_REQ_VIRT, 232 .base.cra_module = THIS_MODULE, 233 }; 234 235 static int __init deflate_mod_init(void) 236 { 237 return crypto_register_acomp(&acomp); 238 } 239 240 static void __exit deflate_mod_fini(void) 241 { 242 crypto_unregister_acomp(&acomp); 243 crypto_acomp_free_streams(&deflate_streams); 244 } 245 246 module_init(deflate_mod_init); 247 module_exit(deflate_mod_fini); 248 249 MODULE_LICENSE("GPL"); 250 MODULE_DESCRIPTION("Deflate Compression Algorithm for IPCOMP"); 251 MODULE_AUTHOR("James Morris <jmorris@intercode.com.au>"); 252 MODULE_AUTHOR("Ard Biesheuvel <ardb@kernel.org>"); 253 MODULE_AUTHOR("Herbert Xu <herbert@gondor.apana.org.au>"); 254 MODULE_ALIAS_CRYPTO("deflate"); 255 MODULE_ALIAS_CRYPTO("deflate-generic"); 256