xref: /openbmc/linux/lib/xz/xz_dec_test.c (revision e5451c8f8330e03ad3cfa16048b4daf961af434f)
1*24fa0402SLasse Collin /*
2*24fa0402SLasse Collin  * XZ decoder tester
3*24fa0402SLasse Collin  *
4*24fa0402SLasse Collin  * Author: Lasse Collin <lasse.collin@tukaani.org>
5*24fa0402SLasse Collin  *
6*24fa0402SLasse Collin  * This file has been put into the public domain.
7*24fa0402SLasse Collin  * You can do whatever you want with this file.
8*24fa0402SLasse Collin  */
9*24fa0402SLasse Collin 
10*24fa0402SLasse Collin #include <linux/kernel.h>
11*24fa0402SLasse Collin #include <linux/module.h>
12*24fa0402SLasse Collin #include <linux/fs.h>
13*24fa0402SLasse Collin #include <linux/uaccess.h>
14*24fa0402SLasse Collin #include <linux/crc32.h>
15*24fa0402SLasse Collin #include <linux/xz.h>
16*24fa0402SLasse Collin 
17*24fa0402SLasse Collin /* Maximum supported dictionary size */
18*24fa0402SLasse Collin #define DICT_MAX (1 << 20)
19*24fa0402SLasse Collin 
20*24fa0402SLasse Collin /* Device name to pass to register_chrdev(). */
21*24fa0402SLasse Collin #define DEVICE_NAME "xz_dec_test"
22*24fa0402SLasse Collin 
23*24fa0402SLasse Collin /* Dynamically allocated device major number */
24*24fa0402SLasse Collin static int device_major;
25*24fa0402SLasse Collin 
26*24fa0402SLasse Collin /*
27*24fa0402SLasse Collin  * We reuse the same decoder state, and thus can decode only one
28*24fa0402SLasse Collin  * file at a time.
29*24fa0402SLasse Collin  */
30*24fa0402SLasse Collin static bool device_is_open;
31*24fa0402SLasse Collin 
32*24fa0402SLasse Collin /* XZ decoder state */
33*24fa0402SLasse Collin static struct xz_dec *state;
34*24fa0402SLasse Collin 
35*24fa0402SLasse Collin /*
36*24fa0402SLasse Collin  * Return value of xz_dec_run(). We need to avoid calling xz_dec_run() after
37*24fa0402SLasse Collin  * it has returned XZ_STREAM_END, so we make this static.
38*24fa0402SLasse Collin  */
39*24fa0402SLasse Collin static enum xz_ret ret;
40*24fa0402SLasse Collin 
41*24fa0402SLasse Collin /*
42*24fa0402SLasse Collin  * Input and output buffers. The input buffer is used as a temporary safe
43*24fa0402SLasse Collin  * place for the data coming from the userspace.
44*24fa0402SLasse Collin  */
45*24fa0402SLasse Collin static uint8_t buffer_in[1024];
46*24fa0402SLasse Collin static uint8_t buffer_out[1024];
47*24fa0402SLasse Collin 
48*24fa0402SLasse Collin /*
49*24fa0402SLasse Collin  * Structure to pass the input and output buffers to the XZ decoder.
50*24fa0402SLasse Collin  * A few of the fields are never modified so we initialize them here.
51*24fa0402SLasse Collin  */
52*24fa0402SLasse Collin static struct xz_buf buffers = {
53*24fa0402SLasse Collin 	.in = buffer_in,
54*24fa0402SLasse Collin 	.out = buffer_out,
55*24fa0402SLasse Collin 	.out_size = sizeof(buffer_out)
56*24fa0402SLasse Collin };
57*24fa0402SLasse Collin 
58*24fa0402SLasse Collin /*
59*24fa0402SLasse Collin  * CRC32 of uncompressed data. This is used to give the user a simple way
60*24fa0402SLasse Collin  * to check that the decoder produces correct output.
61*24fa0402SLasse Collin  */
62*24fa0402SLasse Collin static uint32_t crc;
63*24fa0402SLasse Collin 
xz_dec_test_open(struct inode * i,struct file * f)64*24fa0402SLasse Collin static int xz_dec_test_open(struct inode *i, struct file *f)
65*24fa0402SLasse Collin {
66*24fa0402SLasse Collin 	if (device_is_open)
67*24fa0402SLasse Collin 		return -EBUSY;
68*24fa0402SLasse Collin 
69*24fa0402SLasse Collin 	device_is_open = true;
70*24fa0402SLasse Collin 
71*24fa0402SLasse Collin 	xz_dec_reset(state);
72*24fa0402SLasse Collin 	ret = XZ_OK;
73*24fa0402SLasse Collin 	crc = 0xFFFFFFFF;
74*24fa0402SLasse Collin 
75*24fa0402SLasse Collin 	buffers.in_pos = 0;
76*24fa0402SLasse Collin 	buffers.in_size = 0;
77*24fa0402SLasse Collin 	buffers.out_pos = 0;
78*24fa0402SLasse Collin 
79*24fa0402SLasse Collin 	printk(KERN_INFO DEVICE_NAME ": opened\n");
80*24fa0402SLasse Collin 	return 0;
81*24fa0402SLasse Collin }
82*24fa0402SLasse Collin 
xz_dec_test_release(struct inode * i,struct file * f)83*24fa0402SLasse Collin static int xz_dec_test_release(struct inode *i, struct file *f)
84*24fa0402SLasse Collin {
85*24fa0402SLasse Collin 	device_is_open = false;
86*24fa0402SLasse Collin 
87*24fa0402SLasse Collin 	if (ret == XZ_OK)
88*24fa0402SLasse Collin 		printk(KERN_INFO DEVICE_NAME ": input was truncated\n");
89*24fa0402SLasse Collin 
90*24fa0402SLasse Collin 	printk(KERN_INFO DEVICE_NAME ": closed\n");
91*24fa0402SLasse Collin 	return 0;
92*24fa0402SLasse Collin }
93*24fa0402SLasse Collin 
94*24fa0402SLasse Collin /*
95*24fa0402SLasse Collin  * Decode the data given to us from the userspace. CRC32 of the uncompressed
96*24fa0402SLasse Collin  * data is calculated and is printed at the end of successful decoding. The
97*24fa0402SLasse Collin  * uncompressed data isn't stored anywhere for further use.
98*24fa0402SLasse Collin  *
99*24fa0402SLasse Collin  * The .xz file must have exactly one Stream and no Stream Padding. The data
100*24fa0402SLasse Collin  * after the first Stream is considered to be garbage.
101*24fa0402SLasse Collin  */
xz_dec_test_write(struct file * file,const char __user * buf,size_t size,loff_t * pos)102*24fa0402SLasse Collin static ssize_t xz_dec_test_write(struct file *file, const char __user *buf,
103*24fa0402SLasse Collin 				 size_t size, loff_t *pos)
104*24fa0402SLasse Collin {
105*24fa0402SLasse Collin 	size_t remaining;
106*24fa0402SLasse Collin 
107*24fa0402SLasse Collin 	if (ret != XZ_OK) {
108*24fa0402SLasse Collin 		if (size > 0)
109*24fa0402SLasse Collin 			printk(KERN_INFO DEVICE_NAME ": %zu bytes of "
110*24fa0402SLasse Collin 					"garbage at the end of the file\n",
111*24fa0402SLasse Collin 					size);
112*24fa0402SLasse Collin 
113*24fa0402SLasse Collin 		return -ENOSPC;
114*24fa0402SLasse Collin 	}
115*24fa0402SLasse Collin 
116*24fa0402SLasse Collin 	printk(KERN_INFO DEVICE_NAME ": decoding %zu bytes of input\n",
117*24fa0402SLasse Collin 			size);
118*24fa0402SLasse Collin 
119*24fa0402SLasse Collin 	remaining = size;
120*24fa0402SLasse Collin 	while ((remaining > 0 || buffers.out_pos == buffers.out_size)
121*24fa0402SLasse Collin 			&& ret == XZ_OK) {
122*24fa0402SLasse Collin 		if (buffers.in_pos == buffers.in_size) {
123*24fa0402SLasse Collin 			buffers.in_pos = 0;
124*24fa0402SLasse Collin 			buffers.in_size = min(remaining, sizeof(buffer_in));
125*24fa0402SLasse Collin 			if (copy_from_user(buffer_in, buf, buffers.in_size))
126*24fa0402SLasse Collin 				return -EFAULT;
127*24fa0402SLasse Collin 
128*24fa0402SLasse Collin 			buf += buffers.in_size;
129*24fa0402SLasse Collin 			remaining -= buffers.in_size;
130*24fa0402SLasse Collin 		}
131*24fa0402SLasse Collin 
132*24fa0402SLasse Collin 		buffers.out_pos = 0;
133*24fa0402SLasse Collin 		ret = xz_dec_run(state, &buffers);
134*24fa0402SLasse Collin 		crc = crc32(crc, buffer_out, buffers.out_pos);
135*24fa0402SLasse Collin 	}
136*24fa0402SLasse Collin 
137*24fa0402SLasse Collin 	switch (ret) {
138*24fa0402SLasse Collin 	case XZ_OK:
139*24fa0402SLasse Collin 		printk(KERN_INFO DEVICE_NAME ": XZ_OK\n");
140*24fa0402SLasse Collin 		return size;
141*24fa0402SLasse Collin 
142*24fa0402SLasse Collin 	case XZ_STREAM_END:
143*24fa0402SLasse Collin 		printk(KERN_INFO DEVICE_NAME ": XZ_STREAM_END, "
144*24fa0402SLasse Collin 				"CRC32 = 0x%08X\n", ~crc);
145*24fa0402SLasse Collin 		return size - remaining - (buffers.in_size - buffers.in_pos);
146*24fa0402SLasse Collin 
147*24fa0402SLasse Collin 	case XZ_MEMLIMIT_ERROR:
148*24fa0402SLasse Collin 		printk(KERN_INFO DEVICE_NAME ": XZ_MEMLIMIT_ERROR\n");
149*24fa0402SLasse Collin 		break;
150*24fa0402SLasse Collin 
151*24fa0402SLasse Collin 	case XZ_FORMAT_ERROR:
152*24fa0402SLasse Collin 		printk(KERN_INFO DEVICE_NAME ": XZ_FORMAT_ERROR\n");
153*24fa0402SLasse Collin 		break;
154*24fa0402SLasse Collin 
155*24fa0402SLasse Collin 	case XZ_OPTIONS_ERROR:
156*24fa0402SLasse Collin 		printk(KERN_INFO DEVICE_NAME ": XZ_OPTIONS_ERROR\n");
157*24fa0402SLasse Collin 		break;
158*24fa0402SLasse Collin 
159*24fa0402SLasse Collin 	case XZ_DATA_ERROR:
160*24fa0402SLasse Collin 		printk(KERN_INFO DEVICE_NAME ": XZ_DATA_ERROR\n");
161*24fa0402SLasse Collin 		break;
162*24fa0402SLasse Collin 
163*24fa0402SLasse Collin 	case XZ_BUF_ERROR:
164*24fa0402SLasse Collin 		printk(KERN_INFO DEVICE_NAME ": XZ_BUF_ERROR\n");
165*24fa0402SLasse Collin 		break;
166*24fa0402SLasse Collin 
167*24fa0402SLasse Collin 	default:
168*24fa0402SLasse Collin 		printk(KERN_INFO DEVICE_NAME ": Bug detected!\n");
169*24fa0402SLasse Collin 		break;
170*24fa0402SLasse Collin 	}
171*24fa0402SLasse Collin 
172*24fa0402SLasse Collin 	return -EIO;
173*24fa0402SLasse Collin }
174*24fa0402SLasse Collin 
175*24fa0402SLasse Collin /* Allocate the XZ decoder state and register the character device. */
xz_dec_test_init(void)176*24fa0402SLasse Collin static int __init xz_dec_test_init(void)
177*24fa0402SLasse Collin {
178*24fa0402SLasse Collin 	static const struct file_operations fileops = {
179*24fa0402SLasse Collin 		.owner = THIS_MODULE,
180*24fa0402SLasse Collin 		.open = &xz_dec_test_open,
181*24fa0402SLasse Collin 		.release = &xz_dec_test_release,
182*24fa0402SLasse Collin 		.write = &xz_dec_test_write
183*24fa0402SLasse Collin 	};
184*24fa0402SLasse Collin 
185*24fa0402SLasse Collin 	state = xz_dec_init(XZ_PREALLOC, DICT_MAX);
186*24fa0402SLasse Collin 	if (state == NULL)
187*24fa0402SLasse Collin 		return -ENOMEM;
188*24fa0402SLasse Collin 
189*24fa0402SLasse Collin 	device_major = register_chrdev(0, DEVICE_NAME, &fileops);
190*24fa0402SLasse Collin 	if (device_major < 0) {
191*24fa0402SLasse Collin 		xz_dec_end(state);
192*24fa0402SLasse Collin 		return device_major;
193*24fa0402SLasse Collin 	}
194*24fa0402SLasse Collin 
195*24fa0402SLasse Collin 	printk(KERN_INFO DEVICE_NAME ": module loaded\n");
196*24fa0402SLasse Collin 	printk(KERN_INFO DEVICE_NAME ": Create a device node with "
197*24fa0402SLasse Collin 			"'mknod " DEVICE_NAME " c %d 0' and write .xz files "
198*24fa0402SLasse Collin 			"to it.\n", device_major);
199*24fa0402SLasse Collin 	return 0;
200*24fa0402SLasse Collin }
201*24fa0402SLasse Collin 
xz_dec_test_exit(void)202*24fa0402SLasse Collin static void __exit xz_dec_test_exit(void)
203*24fa0402SLasse Collin {
204*24fa0402SLasse Collin 	unregister_chrdev(device_major, DEVICE_NAME);
205*24fa0402SLasse Collin 	xz_dec_end(state);
206*24fa0402SLasse Collin 	printk(KERN_INFO DEVICE_NAME ": module unloaded\n");
207*24fa0402SLasse Collin }
208*24fa0402SLasse Collin 
209*24fa0402SLasse Collin module_init(xz_dec_test_init);
210*24fa0402SLasse Collin module_exit(xz_dec_test_exit);
211*24fa0402SLasse Collin 
212*24fa0402SLasse Collin MODULE_DESCRIPTION("XZ decompressor tester");
213*24fa0402SLasse Collin MODULE_VERSION("1.0");
214*24fa0402SLasse Collin MODULE_AUTHOR("Lasse Collin <lasse.collin@tukaani.org>");
215*24fa0402SLasse Collin 
216*24fa0402SLasse Collin /*
217*24fa0402SLasse Collin  * This code is in the public domain, but in Linux it's simplest to just
218*24fa0402SLasse Collin  * say it's GPL and consider the authors as the copyright holders.
219*24fa0402SLasse Collin  */
220*24fa0402SLasse Collin MODULE_LICENSE("GPL");
221