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