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