xref: /linux/crypto/deflate.c (revision b4ada0618eed0fbd1b1630f73deb048c592b06a1)
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 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 
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 
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 
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 
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 
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 
240 static int __init deflate_mod_init(void)
241 {
242 	return crypto_register_acomp(&acomp);
243 }
244 
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