xref: /openbmc/linux/fs/squashfs/lz4_wrapper.c (revision 09cbfeaf)
19c06a46fSPhillip Lougher /*
29c06a46fSPhillip Lougher  * Copyright (c) 2013, 2014
39c06a46fSPhillip Lougher  * Phillip Lougher <phillip@squashfs.org.uk>
49c06a46fSPhillip Lougher  *
59c06a46fSPhillip Lougher  * This work is licensed under the terms of the GNU GPL, version 2. See
69c06a46fSPhillip Lougher  * the COPYING file in the top-level directory.
79c06a46fSPhillip Lougher  */
89c06a46fSPhillip Lougher 
99c06a46fSPhillip Lougher #include <linux/buffer_head.h>
109c06a46fSPhillip Lougher #include <linux/mutex.h>
119c06a46fSPhillip Lougher #include <linux/slab.h>
129c06a46fSPhillip Lougher #include <linux/vmalloc.h>
139c06a46fSPhillip Lougher #include <linux/lz4.h>
149c06a46fSPhillip Lougher 
159c06a46fSPhillip Lougher #include "squashfs_fs.h"
169c06a46fSPhillip Lougher #include "squashfs_fs_sb.h"
179c06a46fSPhillip Lougher #include "squashfs.h"
189c06a46fSPhillip Lougher #include "decompressor.h"
199c06a46fSPhillip Lougher #include "page_actor.h"
209c06a46fSPhillip Lougher 
219c06a46fSPhillip Lougher #define LZ4_LEGACY	1
229c06a46fSPhillip Lougher 
239c06a46fSPhillip Lougher struct lz4_comp_opts {
249c06a46fSPhillip Lougher 	__le32 version;
259c06a46fSPhillip Lougher 	__le32 flags;
269c06a46fSPhillip Lougher };
279c06a46fSPhillip Lougher 
289c06a46fSPhillip Lougher struct squashfs_lz4 {
299c06a46fSPhillip Lougher 	void *input;
309c06a46fSPhillip Lougher 	void *output;
319c06a46fSPhillip Lougher };
329c06a46fSPhillip Lougher 
339c06a46fSPhillip Lougher 
349c06a46fSPhillip Lougher static void *lz4_comp_opts(struct squashfs_sb_info *msblk,
359c06a46fSPhillip Lougher 	void *buff, int len)
369c06a46fSPhillip Lougher {
379c06a46fSPhillip Lougher 	struct lz4_comp_opts *comp_opts = buff;
389c06a46fSPhillip Lougher 
399c06a46fSPhillip Lougher 	/* LZ4 compressed filesystems always have compression options */
409c06a46fSPhillip Lougher 	if (comp_opts == NULL || len < sizeof(*comp_opts))
419c06a46fSPhillip Lougher 		return ERR_PTR(-EIO);
429c06a46fSPhillip Lougher 
439c06a46fSPhillip Lougher 	if (le32_to_cpu(comp_opts->version) != LZ4_LEGACY) {
449c06a46fSPhillip Lougher 		/* LZ4 format currently used by the kernel is the 'legacy'
459c06a46fSPhillip Lougher 		 * format */
469c06a46fSPhillip Lougher 		ERROR("Unknown LZ4 version\n");
479c06a46fSPhillip Lougher 		return ERR_PTR(-EINVAL);
489c06a46fSPhillip Lougher 	}
499c06a46fSPhillip Lougher 
509c06a46fSPhillip Lougher 	return NULL;
519c06a46fSPhillip Lougher }
529c06a46fSPhillip Lougher 
539c06a46fSPhillip Lougher 
549c06a46fSPhillip Lougher static void *lz4_init(struct squashfs_sb_info *msblk, void *buff)
559c06a46fSPhillip Lougher {
569c06a46fSPhillip Lougher 	int block_size = max_t(int, msblk->block_size, SQUASHFS_METADATA_SIZE);
579c06a46fSPhillip Lougher 	struct squashfs_lz4 *stream;
589c06a46fSPhillip Lougher 
599c06a46fSPhillip Lougher 	stream = kzalloc(sizeof(*stream), GFP_KERNEL);
609c06a46fSPhillip Lougher 	if (stream == NULL)
619c06a46fSPhillip Lougher 		goto failed;
629c06a46fSPhillip Lougher 	stream->input = vmalloc(block_size);
639c06a46fSPhillip Lougher 	if (stream->input == NULL)
649c06a46fSPhillip Lougher 		goto failed2;
659c06a46fSPhillip Lougher 	stream->output = vmalloc(block_size);
669c06a46fSPhillip Lougher 	if (stream->output == NULL)
679c06a46fSPhillip Lougher 		goto failed3;
689c06a46fSPhillip Lougher 
699c06a46fSPhillip Lougher 	return stream;
709c06a46fSPhillip Lougher 
719c06a46fSPhillip Lougher failed3:
729c06a46fSPhillip Lougher 	vfree(stream->input);
739c06a46fSPhillip Lougher failed2:
749c06a46fSPhillip Lougher 	kfree(stream);
759c06a46fSPhillip Lougher failed:
769c06a46fSPhillip Lougher 	ERROR("Failed to initialise LZ4 decompressor\n");
779c06a46fSPhillip Lougher 	return ERR_PTR(-ENOMEM);
789c06a46fSPhillip Lougher }
799c06a46fSPhillip Lougher 
809c06a46fSPhillip Lougher 
819c06a46fSPhillip Lougher static void lz4_free(void *strm)
829c06a46fSPhillip Lougher {
839c06a46fSPhillip Lougher 	struct squashfs_lz4 *stream = strm;
849c06a46fSPhillip Lougher 
859c06a46fSPhillip Lougher 	if (stream) {
869c06a46fSPhillip Lougher 		vfree(stream->input);
879c06a46fSPhillip Lougher 		vfree(stream->output);
889c06a46fSPhillip Lougher 	}
899c06a46fSPhillip Lougher 	kfree(stream);
909c06a46fSPhillip Lougher }
919c06a46fSPhillip Lougher 
929c06a46fSPhillip Lougher 
939c06a46fSPhillip Lougher static int lz4_uncompress(struct squashfs_sb_info *msblk, void *strm,
949c06a46fSPhillip Lougher 	struct buffer_head **bh, int b, int offset, int length,
959c06a46fSPhillip Lougher 	struct squashfs_page_actor *output)
969c06a46fSPhillip Lougher {
979c06a46fSPhillip Lougher 	struct squashfs_lz4 *stream = strm;
989c06a46fSPhillip Lougher 	void *buff = stream->input, *data;
999c06a46fSPhillip Lougher 	int avail, i, bytes = length, res;
1009c06a46fSPhillip Lougher 	size_t dest_len = output->length;
1019c06a46fSPhillip Lougher 
1029c06a46fSPhillip Lougher 	for (i = 0; i < b; i++) {
1039c06a46fSPhillip Lougher 		avail = min(bytes, msblk->devblksize - offset);
1049c06a46fSPhillip Lougher 		memcpy(buff, bh[i]->b_data + offset, avail);
1059c06a46fSPhillip Lougher 		buff += avail;
1069c06a46fSPhillip Lougher 		bytes -= avail;
1079c06a46fSPhillip Lougher 		offset = 0;
1089c06a46fSPhillip Lougher 		put_bh(bh[i]);
1099c06a46fSPhillip Lougher 	}
1109c06a46fSPhillip Lougher 
1119c06a46fSPhillip Lougher 	res = lz4_decompress_unknownoutputsize(stream->input, length,
1129c06a46fSPhillip Lougher 					stream->output, &dest_len);
1139c06a46fSPhillip Lougher 	if (res)
1149c06a46fSPhillip Lougher 		return -EIO;
1159c06a46fSPhillip Lougher 
1169c06a46fSPhillip Lougher 	bytes = dest_len;
1179c06a46fSPhillip Lougher 	data = squashfs_first_page(output);
1189c06a46fSPhillip Lougher 	buff = stream->output;
1199c06a46fSPhillip Lougher 	while (data) {
12009cbfeafSKirill A. Shutemov 		if (bytes <= PAGE_SIZE) {
1219c06a46fSPhillip Lougher 			memcpy(data, buff, bytes);
1229c06a46fSPhillip Lougher 			break;
1239c06a46fSPhillip Lougher 		}
12409cbfeafSKirill A. Shutemov 		memcpy(data, buff, PAGE_SIZE);
12509cbfeafSKirill A. Shutemov 		buff += PAGE_SIZE;
12609cbfeafSKirill A. Shutemov 		bytes -= PAGE_SIZE;
1279c06a46fSPhillip Lougher 		data = squashfs_next_page(output);
1289c06a46fSPhillip Lougher 	}
1299c06a46fSPhillip Lougher 	squashfs_finish_page(output);
1309c06a46fSPhillip Lougher 
1319c06a46fSPhillip Lougher 	return dest_len;
1329c06a46fSPhillip Lougher }
1339c06a46fSPhillip Lougher 
1349c06a46fSPhillip Lougher const struct squashfs_decompressor squashfs_lz4_comp_ops = {
1359c06a46fSPhillip Lougher 	.init = lz4_init,
1369c06a46fSPhillip Lougher 	.comp_opts = lz4_comp_opts,
1379c06a46fSPhillip Lougher 	.free = lz4_free,
1389c06a46fSPhillip Lougher 	.decompress = lz4_uncompress,
1399c06a46fSPhillip Lougher 	.id = LZ4_COMPRESSION,
1409c06a46fSPhillip Lougher 	.name = "lz4",
1419c06a46fSPhillip Lougher 	.supported = 1
1429c06a46fSPhillip Lougher };
143