xref: /linux/crypto/deflate.c (revision 184e56e77c06a7eef68a021e9d4b11a11a8ab096)
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