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
deflate_alloc_stream(void)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
deflate_free_stream(void * ctx)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
deflate_compress_one(struct acomp_req * req,struct deflate_stream * ds)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
deflate_compress(struct acomp_req * req)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
deflate_decompress_one(struct acomp_req * req,struct deflate_stream * ds)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
deflate_decompress(struct acomp_req * req)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
deflate_init(struct crypto_acomp * tfm)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
deflate_mod_init(void)240 static int __init deflate_mod_init(void)
241 {
242 return crypto_register_acomp(&acomp);
243 }
244
deflate_mod_fini(void)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