xref: /kernel/linux/linux-5.10/lib/xz/xz_dec_test.c (revision 8c2ecf20)
18c2ecf20Sopenharmony_ci/*
28c2ecf20Sopenharmony_ci * XZ decoder tester
38c2ecf20Sopenharmony_ci *
48c2ecf20Sopenharmony_ci * Author: Lasse Collin <lasse.collin@tukaani.org>
58c2ecf20Sopenharmony_ci *
68c2ecf20Sopenharmony_ci * This file has been put into the public domain.
78c2ecf20Sopenharmony_ci * You can do whatever you want with this file.
88c2ecf20Sopenharmony_ci */
98c2ecf20Sopenharmony_ci
108c2ecf20Sopenharmony_ci#include <linux/kernel.h>
118c2ecf20Sopenharmony_ci#include <linux/module.h>
128c2ecf20Sopenharmony_ci#include <linux/fs.h>
138c2ecf20Sopenharmony_ci#include <linux/uaccess.h>
148c2ecf20Sopenharmony_ci#include <linux/crc32.h>
158c2ecf20Sopenharmony_ci#include <linux/xz.h>
168c2ecf20Sopenharmony_ci
178c2ecf20Sopenharmony_ci/* Maximum supported dictionary size */
188c2ecf20Sopenharmony_ci#define DICT_MAX (1 << 20)
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_ci/* Device name to pass to register_chrdev(). */
218c2ecf20Sopenharmony_ci#define DEVICE_NAME "xz_dec_test"
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_ci/* Dynamically allocated device major number */
248c2ecf20Sopenharmony_cistatic int device_major;
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_ci/*
278c2ecf20Sopenharmony_ci * We reuse the same decoder state, and thus can decode only one
288c2ecf20Sopenharmony_ci * file at a time.
298c2ecf20Sopenharmony_ci */
308c2ecf20Sopenharmony_cistatic bool device_is_open;
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_ci/* XZ decoder state */
338c2ecf20Sopenharmony_cistatic struct xz_dec *state;
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_ci/*
368c2ecf20Sopenharmony_ci * Return value of xz_dec_run(). We need to avoid calling xz_dec_run() after
378c2ecf20Sopenharmony_ci * it has returned XZ_STREAM_END, so we make this static.
388c2ecf20Sopenharmony_ci */
398c2ecf20Sopenharmony_cistatic enum xz_ret ret;
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_ci/*
428c2ecf20Sopenharmony_ci * Input and output buffers. The input buffer is used as a temporary safe
438c2ecf20Sopenharmony_ci * place for the data coming from the userspace.
448c2ecf20Sopenharmony_ci */
458c2ecf20Sopenharmony_cistatic uint8_t buffer_in[1024];
468c2ecf20Sopenharmony_cistatic uint8_t buffer_out[1024];
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_ci/*
498c2ecf20Sopenharmony_ci * Structure to pass the input and output buffers to the XZ decoder.
508c2ecf20Sopenharmony_ci * A few of the fields are never modified so we initialize them here.
518c2ecf20Sopenharmony_ci */
528c2ecf20Sopenharmony_cistatic struct xz_buf buffers = {
538c2ecf20Sopenharmony_ci	.in = buffer_in,
548c2ecf20Sopenharmony_ci	.out = buffer_out,
558c2ecf20Sopenharmony_ci	.out_size = sizeof(buffer_out)
568c2ecf20Sopenharmony_ci};
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_ci/*
598c2ecf20Sopenharmony_ci * CRC32 of uncompressed data. This is used to give the user a simple way
608c2ecf20Sopenharmony_ci * to check that the decoder produces correct output.
618c2ecf20Sopenharmony_ci */
628c2ecf20Sopenharmony_cistatic uint32_t crc;
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_cistatic int xz_dec_test_open(struct inode *i, struct file *f)
658c2ecf20Sopenharmony_ci{
668c2ecf20Sopenharmony_ci	if (device_is_open)
678c2ecf20Sopenharmony_ci		return -EBUSY;
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_ci	device_is_open = true;
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_ci	xz_dec_reset(state);
728c2ecf20Sopenharmony_ci	ret = XZ_OK;
738c2ecf20Sopenharmony_ci	crc = 0xFFFFFFFF;
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_ci	buffers.in_pos = 0;
768c2ecf20Sopenharmony_ci	buffers.in_size = 0;
778c2ecf20Sopenharmony_ci	buffers.out_pos = 0;
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_ci	printk(KERN_INFO DEVICE_NAME ": opened\n");
808c2ecf20Sopenharmony_ci	return 0;
818c2ecf20Sopenharmony_ci}
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_cistatic int xz_dec_test_release(struct inode *i, struct file *f)
848c2ecf20Sopenharmony_ci{
858c2ecf20Sopenharmony_ci	device_is_open = false;
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_ci	if (ret == XZ_OK)
888c2ecf20Sopenharmony_ci		printk(KERN_INFO DEVICE_NAME ": input was truncated\n");
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_ci	printk(KERN_INFO DEVICE_NAME ": closed\n");
918c2ecf20Sopenharmony_ci	return 0;
928c2ecf20Sopenharmony_ci}
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_ci/*
958c2ecf20Sopenharmony_ci * Decode the data given to us from the userspace. CRC32 of the uncompressed
968c2ecf20Sopenharmony_ci * data is calculated and is printed at the end of successful decoding. The
978c2ecf20Sopenharmony_ci * uncompressed data isn't stored anywhere for further use.
988c2ecf20Sopenharmony_ci *
998c2ecf20Sopenharmony_ci * The .xz file must have exactly one Stream and no Stream Padding. The data
1008c2ecf20Sopenharmony_ci * after the first Stream is considered to be garbage.
1018c2ecf20Sopenharmony_ci */
1028c2ecf20Sopenharmony_cistatic ssize_t xz_dec_test_write(struct file *file, const char __user *buf,
1038c2ecf20Sopenharmony_ci				 size_t size, loff_t *pos)
1048c2ecf20Sopenharmony_ci{
1058c2ecf20Sopenharmony_ci	size_t remaining;
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_ci	if (ret != XZ_OK) {
1088c2ecf20Sopenharmony_ci		if (size > 0)
1098c2ecf20Sopenharmony_ci			printk(KERN_INFO DEVICE_NAME ": %zu bytes of "
1108c2ecf20Sopenharmony_ci					"garbage at the end of the file\n",
1118c2ecf20Sopenharmony_ci					size);
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_ci		return -ENOSPC;
1148c2ecf20Sopenharmony_ci	}
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_ci	printk(KERN_INFO DEVICE_NAME ": decoding %zu bytes of input\n",
1178c2ecf20Sopenharmony_ci			size);
1188c2ecf20Sopenharmony_ci
1198c2ecf20Sopenharmony_ci	remaining = size;
1208c2ecf20Sopenharmony_ci	while ((remaining > 0 || buffers.out_pos == buffers.out_size)
1218c2ecf20Sopenharmony_ci			&& ret == XZ_OK) {
1228c2ecf20Sopenharmony_ci		if (buffers.in_pos == buffers.in_size) {
1238c2ecf20Sopenharmony_ci			buffers.in_pos = 0;
1248c2ecf20Sopenharmony_ci			buffers.in_size = min(remaining, sizeof(buffer_in));
1258c2ecf20Sopenharmony_ci			if (copy_from_user(buffer_in, buf, buffers.in_size))
1268c2ecf20Sopenharmony_ci				return -EFAULT;
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_ci			buf += buffers.in_size;
1298c2ecf20Sopenharmony_ci			remaining -= buffers.in_size;
1308c2ecf20Sopenharmony_ci		}
1318c2ecf20Sopenharmony_ci
1328c2ecf20Sopenharmony_ci		buffers.out_pos = 0;
1338c2ecf20Sopenharmony_ci		ret = xz_dec_run(state, &buffers);
1348c2ecf20Sopenharmony_ci		crc = crc32(crc, buffer_out, buffers.out_pos);
1358c2ecf20Sopenharmony_ci	}
1368c2ecf20Sopenharmony_ci
1378c2ecf20Sopenharmony_ci	switch (ret) {
1388c2ecf20Sopenharmony_ci	case XZ_OK:
1398c2ecf20Sopenharmony_ci		printk(KERN_INFO DEVICE_NAME ": XZ_OK\n");
1408c2ecf20Sopenharmony_ci		return size;
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_ci	case XZ_STREAM_END:
1438c2ecf20Sopenharmony_ci		printk(KERN_INFO DEVICE_NAME ": XZ_STREAM_END, "
1448c2ecf20Sopenharmony_ci				"CRC32 = 0x%08X\n", ~crc);
1458c2ecf20Sopenharmony_ci		return size - remaining - (buffers.in_size - buffers.in_pos);
1468c2ecf20Sopenharmony_ci
1478c2ecf20Sopenharmony_ci	case XZ_MEMLIMIT_ERROR:
1488c2ecf20Sopenharmony_ci		printk(KERN_INFO DEVICE_NAME ": XZ_MEMLIMIT_ERROR\n");
1498c2ecf20Sopenharmony_ci		break;
1508c2ecf20Sopenharmony_ci
1518c2ecf20Sopenharmony_ci	case XZ_FORMAT_ERROR:
1528c2ecf20Sopenharmony_ci		printk(KERN_INFO DEVICE_NAME ": XZ_FORMAT_ERROR\n");
1538c2ecf20Sopenharmony_ci		break;
1548c2ecf20Sopenharmony_ci
1558c2ecf20Sopenharmony_ci	case XZ_OPTIONS_ERROR:
1568c2ecf20Sopenharmony_ci		printk(KERN_INFO DEVICE_NAME ": XZ_OPTIONS_ERROR\n");
1578c2ecf20Sopenharmony_ci		break;
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_ci	case XZ_DATA_ERROR:
1608c2ecf20Sopenharmony_ci		printk(KERN_INFO DEVICE_NAME ": XZ_DATA_ERROR\n");
1618c2ecf20Sopenharmony_ci		break;
1628c2ecf20Sopenharmony_ci
1638c2ecf20Sopenharmony_ci	case XZ_BUF_ERROR:
1648c2ecf20Sopenharmony_ci		printk(KERN_INFO DEVICE_NAME ": XZ_BUF_ERROR\n");
1658c2ecf20Sopenharmony_ci		break;
1668c2ecf20Sopenharmony_ci
1678c2ecf20Sopenharmony_ci	default:
1688c2ecf20Sopenharmony_ci		printk(KERN_INFO DEVICE_NAME ": Bug detected!\n");
1698c2ecf20Sopenharmony_ci		break;
1708c2ecf20Sopenharmony_ci	}
1718c2ecf20Sopenharmony_ci
1728c2ecf20Sopenharmony_ci	return -EIO;
1738c2ecf20Sopenharmony_ci}
1748c2ecf20Sopenharmony_ci
1758c2ecf20Sopenharmony_ci/* Allocate the XZ decoder state and register the character device. */
1768c2ecf20Sopenharmony_cistatic int __init xz_dec_test_init(void)
1778c2ecf20Sopenharmony_ci{
1788c2ecf20Sopenharmony_ci	static const struct file_operations fileops = {
1798c2ecf20Sopenharmony_ci		.owner = THIS_MODULE,
1808c2ecf20Sopenharmony_ci		.open = &xz_dec_test_open,
1818c2ecf20Sopenharmony_ci		.release = &xz_dec_test_release,
1828c2ecf20Sopenharmony_ci		.write = &xz_dec_test_write
1838c2ecf20Sopenharmony_ci	};
1848c2ecf20Sopenharmony_ci
1858c2ecf20Sopenharmony_ci	state = xz_dec_init(XZ_PREALLOC, DICT_MAX);
1868c2ecf20Sopenharmony_ci	if (state == NULL)
1878c2ecf20Sopenharmony_ci		return -ENOMEM;
1888c2ecf20Sopenharmony_ci
1898c2ecf20Sopenharmony_ci	device_major = register_chrdev(0, DEVICE_NAME, &fileops);
1908c2ecf20Sopenharmony_ci	if (device_major < 0) {
1918c2ecf20Sopenharmony_ci		xz_dec_end(state);
1928c2ecf20Sopenharmony_ci		return device_major;
1938c2ecf20Sopenharmony_ci	}
1948c2ecf20Sopenharmony_ci
1958c2ecf20Sopenharmony_ci	printk(KERN_INFO DEVICE_NAME ": module loaded\n");
1968c2ecf20Sopenharmony_ci	printk(KERN_INFO DEVICE_NAME ": Create a device node with "
1978c2ecf20Sopenharmony_ci			"'mknod " DEVICE_NAME " c %d 0' and write .xz files "
1988c2ecf20Sopenharmony_ci			"to it.\n", device_major);
1998c2ecf20Sopenharmony_ci	return 0;
2008c2ecf20Sopenharmony_ci}
2018c2ecf20Sopenharmony_ci
2028c2ecf20Sopenharmony_cistatic void __exit xz_dec_test_exit(void)
2038c2ecf20Sopenharmony_ci{
2048c2ecf20Sopenharmony_ci	unregister_chrdev(device_major, DEVICE_NAME);
2058c2ecf20Sopenharmony_ci	xz_dec_end(state);
2068c2ecf20Sopenharmony_ci	printk(KERN_INFO DEVICE_NAME ": module unloaded\n");
2078c2ecf20Sopenharmony_ci}
2088c2ecf20Sopenharmony_ci
2098c2ecf20Sopenharmony_cimodule_init(xz_dec_test_init);
2108c2ecf20Sopenharmony_cimodule_exit(xz_dec_test_exit);
2118c2ecf20Sopenharmony_ci
2128c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("XZ decompressor tester");
2138c2ecf20Sopenharmony_ciMODULE_VERSION("1.0");
2148c2ecf20Sopenharmony_ciMODULE_AUTHOR("Lasse Collin <lasse.collin@tukaani.org>");
2158c2ecf20Sopenharmony_ci
2168c2ecf20Sopenharmony_ci/*
2178c2ecf20Sopenharmony_ci * This code is in the public domain, but in Linux it's simplest to just
2188c2ecf20Sopenharmony_ci * say it's GPL and consider the authors as the copyright holders.
2198c2ecf20Sopenharmony_ci */
2208c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
221