1
2#include <linux/init.h>
3#include <linux/module.h>
4#include <linux/crypto.h>
5#include <linux/zlib.h>
6#include <linux/vmalloc.h>
7#include <linux/interrupt.h>
8#include <linux/mm.h>
9#include <linux/net.h>
10#include <linux/slab.h>
11
12#define DEFLATE_DEF_LEVEL		Z_DEFAULT_COMPRESSION
13#define DEFLATE_DEF_WINBITS		11
14#define DEFLATE_DEF_MEMLEVEL		MAX_MEM_LEVEL
15
16struct deflate_ctx {
17	struct z_stream_s comp_stream;
18	struct z_stream_s decomp_stream;
19};
20
21static int deflate_comp_init(struct deflate_ctx *ctx)
22{
23	int ret = 0;
24	struct z_stream_s *stream = &ctx->comp_stream;
25
26	stream->workspace = vmalloc(zlib_deflate_workspacesize());
27	if (!stream->workspace) {
28		ret = -ENOMEM;
29		goto out;
30	}
31	memset(stream->workspace, 0, zlib_deflate_workspacesize());
32	ret = zlib_deflateInit2(stream, DEFLATE_DEF_LEVEL, Z_DEFLATED,
33	                        -DEFLATE_DEF_WINBITS, DEFLATE_DEF_MEMLEVEL,
34	                        Z_DEFAULT_STRATEGY);
35	if (ret != Z_OK) {
36		ret = -EINVAL;
37		goto out_free;
38	}
39out:
40	return ret;
41out_free:
42	vfree(stream->workspace);
43	goto out;
44}
45
46static int deflate_decomp_init(struct deflate_ctx *ctx)
47{
48	int ret = 0;
49	struct z_stream_s *stream = &ctx->decomp_stream;
50
51	stream->workspace = kzalloc(zlib_inflate_workspacesize(), GFP_KERNEL);
52	if (!stream->workspace) {
53		ret = -ENOMEM;
54		goto out;
55	}
56	ret = zlib_inflateInit2(stream, -DEFLATE_DEF_WINBITS);
57	if (ret != Z_OK) {
58		ret = -EINVAL;
59		goto out_free;
60	}
61out:
62	return ret;
63out_free:
64	kfree(stream->workspace);
65	goto out;
66}
67
68static void deflate_comp_exit(struct deflate_ctx *ctx)
69{
70	zlib_deflateEnd(&ctx->comp_stream);
71	vfree(ctx->comp_stream.workspace);
72}
73
74static void deflate_decomp_exit(struct deflate_ctx *ctx)
75{
76	zlib_inflateEnd(&ctx->decomp_stream);
77	kfree(ctx->decomp_stream.workspace);
78}
79
80static int deflate_init(struct crypto_tfm *tfm)
81{
82	struct deflate_ctx *ctx = crypto_tfm_ctx(tfm);
83	int ret;
84
85	ret = deflate_comp_init(ctx);
86	if (ret)
87		goto out;
88	ret = deflate_decomp_init(ctx);
89	if (ret)
90		deflate_comp_exit(ctx);
91out:
92	return ret;
93}
94
95static void deflate_exit(struct crypto_tfm *tfm)
96{
97	struct deflate_ctx *ctx = crypto_tfm_ctx(tfm);
98
99	deflate_comp_exit(ctx);
100	deflate_decomp_exit(ctx);
101}
102
103static int deflate_compress(struct crypto_tfm *tfm, const u8 *src,
104			    unsigned int slen, u8 *dst, unsigned int *dlen)
105{
106	int ret = 0;
107	struct deflate_ctx *dctx = crypto_tfm_ctx(tfm);
108	struct z_stream_s *stream = &dctx->comp_stream;
109
110	ret = zlib_deflateReset(stream);
111	if (ret != Z_OK) {
112		ret = -EINVAL;
113		goto out;
114	}
115
116	stream->next_in = (u8 *)src;
117	stream->avail_in = slen;
118	stream->next_out = (u8 *)dst;
119	stream->avail_out = *dlen;
120
121	ret = zlib_deflate(stream, Z_FINISH);
122	if (ret != Z_STREAM_END) {
123		ret = -EINVAL;
124		goto out;
125	}
126	ret = 0;
127	*dlen = stream->total_out;
128out:
129	return ret;
130}
131
132static int deflate_decompress(struct crypto_tfm *tfm, const u8 *src,
133			      unsigned int slen, u8 *dst, unsigned int *dlen)
134{
135
136	int ret = 0;
137	struct deflate_ctx *dctx = crypto_tfm_ctx(tfm);
138	struct z_stream_s *stream = &dctx->decomp_stream;
139
140	ret = zlib_inflateReset(stream);
141	if (ret != Z_OK) {
142		ret = -EINVAL;
143		goto out;
144	}
145
146	stream->next_in = (u8 *)src;
147	stream->avail_in = slen;
148	stream->next_out = (u8 *)dst;
149	stream->avail_out = *dlen;
150
151	ret = zlib_inflate(stream, Z_SYNC_FLUSH);
152	if (ret == Z_OK && !stream->avail_in && stream->avail_out) {
153		u8 zerostuff = 0;
154		stream->next_in = &zerostuff;
155		stream->avail_in = 1;
156		ret = zlib_inflate(stream, Z_FINISH);
157	}
158	if (ret != Z_STREAM_END) {
159		ret = -EINVAL;
160		goto out;
161	}
162	ret = 0;
163	*dlen = stream->total_out;
164out:
165	return ret;
166}
167
168static struct crypto_alg alg = {
169	.cra_name		= "deflate",
170	.cra_flags		= CRYPTO_ALG_TYPE_COMPRESS,
171	.cra_ctxsize		= sizeof(struct deflate_ctx),
172	.cra_module		= THIS_MODULE,
173	.cra_list		= LIST_HEAD_INIT(alg.cra_list),
174	.cra_init		= deflate_init,
175	.cra_exit		= deflate_exit,
176	.cra_u			= { .compress = {
177	.coa_compress 		= deflate_compress,
178	.coa_decompress  	= deflate_decompress } }
179};
180
181static int __init deflate_mod_init(void)
182{
183	return crypto_register_alg(&alg);
184}
185
186static void __exit deflate_mod_fini(void)
187{
188	crypto_unregister_alg(&alg);
189}
190
191module_init(deflate_mod_init);
192module_exit(deflate_mod_fini);
193
194MODULE_LICENSE("GPL");
195MODULE_DESCRIPTION("Deflate Compression Algorithm for IPCOMP");
196MODULE_AUTHOR("James Morris <jmorris@intercode.com.au>");
197