xref: /openbmc/linux/fs/squashfs/lz4_wrapper.c (revision f268eedd)
120c8ccb1SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
29c06a46fSPhillip Lougher /*
39c06a46fSPhillip Lougher  * Copyright (c) 2013, 2014
49c06a46fSPhillip Lougher  * Phillip Lougher <phillip@squashfs.org.uk>
59c06a46fSPhillip Lougher  */
69c06a46fSPhillip Lougher 
793e72b3cSPhilippe Liard #include <linux/bio.h>
89c06a46fSPhillip Lougher #include <linux/mutex.h>
99c06a46fSPhillip Lougher #include <linux/slab.h>
109c06a46fSPhillip Lougher #include <linux/vmalloc.h>
119c06a46fSPhillip Lougher #include <linux/lz4.h>
129c06a46fSPhillip Lougher 
139c06a46fSPhillip Lougher #include "squashfs_fs.h"
149c06a46fSPhillip Lougher #include "squashfs_fs_sb.h"
159c06a46fSPhillip Lougher #include "squashfs.h"
169c06a46fSPhillip Lougher #include "decompressor.h"
179c06a46fSPhillip Lougher #include "page_actor.h"
189c06a46fSPhillip Lougher 
199c06a46fSPhillip Lougher #define LZ4_LEGACY	1
209c06a46fSPhillip Lougher 
219c06a46fSPhillip Lougher struct lz4_comp_opts {
229c06a46fSPhillip Lougher 	__le32 version;
239c06a46fSPhillip Lougher 	__le32 flags;
249c06a46fSPhillip Lougher };
259c06a46fSPhillip Lougher 
269c06a46fSPhillip Lougher struct squashfs_lz4 {
279c06a46fSPhillip Lougher 	void *input;
289c06a46fSPhillip Lougher 	void *output;
299c06a46fSPhillip Lougher };
309c06a46fSPhillip Lougher 
319c06a46fSPhillip Lougher 
lz4_comp_opts(struct squashfs_sb_info * msblk,void * buff,int len)329c06a46fSPhillip Lougher static void *lz4_comp_opts(struct squashfs_sb_info *msblk,
339c06a46fSPhillip Lougher 	void *buff, int len)
349c06a46fSPhillip Lougher {
359c06a46fSPhillip Lougher 	struct lz4_comp_opts *comp_opts = buff;
369c06a46fSPhillip Lougher 
379c06a46fSPhillip Lougher 	/* LZ4 compressed filesystems always have compression options */
389c06a46fSPhillip Lougher 	if (comp_opts == NULL || len < sizeof(*comp_opts))
399c06a46fSPhillip Lougher 		return ERR_PTR(-EIO);
409c06a46fSPhillip Lougher 
419c06a46fSPhillip Lougher 	if (le32_to_cpu(comp_opts->version) != LZ4_LEGACY) {
429c06a46fSPhillip Lougher 		/* LZ4 format currently used by the kernel is the 'legacy'
439c06a46fSPhillip Lougher 		 * format */
449c06a46fSPhillip Lougher 		ERROR("Unknown LZ4 version\n");
459c06a46fSPhillip Lougher 		return ERR_PTR(-EINVAL);
469c06a46fSPhillip Lougher 	}
479c06a46fSPhillip Lougher 
489c06a46fSPhillip Lougher 	return NULL;
499c06a46fSPhillip Lougher }
509c06a46fSPhillip Lougher 
519c06a46fSPhillip Lougher 
lz4_init(struct squashfs_sb_info * msblk,void * buff)529c06a46fSPhillip Lougher static void *lz4_init(struct squashfs_sb_info *msblk, void *buff)
539c06a46fSPhillip Lougher {
549c06a46fSPhillip Lougher 	int block_size = max_t(int, msblk->block_size, SQUASHFS_METADATA_SIZE);
559c06a46fSPhillip Lougher 	struct squashfs_lz4 *stream;
569c06a46fSPhillip Lougher 
579c06a46fSPhillip Lougher 	stream = kzalloc(sizeof(*stream), GFP_KERNEL);
589c06a46fSPhillip Lougher 	if (stream == NULL)
599c06a46fSPhillip Lougher 		goto failed;
609c06a46fSPhillip Lougher 	stream->input = vmalloc(block_size);
619c06a46fSPhillip Lougher 	if (stream->input == NULL)
629c06a46fSPhillip Lougher 		goto failed2;
639c06a46fSPhillip Lougher 	stream->output = vmalloc(block_size);
649c06a46fSPhillip Lougher 	if (stream->output == NULL)
659c06a46fSPhillip Lougher 		goto failed3;
669c06a46fSPhillip Lougher 
679c06a46fSPhillip Lougher 	return stream;
689c06a46fSPhillip Lougher 
699c06a46fSPhillip Lougher failed3:
709c06a46fSPhillip Lougher 	vfree(stream->input);
719c06a46fSPhillip Lougher failed2:
729c06a46fSPhillip Lougher 	kfree(stream);
739c06a46fSPhillip Lougher failed:
749c06a46fSPhillip Lougher 	ERROR("Failed to initialise LZ4 decompressor\n");
759c06a46fSPhillip Lougher 	return ERR_PTR(-ENOMEM);
769c06a46fSPhillip Lougher }
779c06a46fSPhillip Lougher 
789c06a46fSPhillip Lougher 
lz4_free(void * strm)799c06a46fSPhillip Lougher static void lz4_free(void *strm)
809c06a46fSPhillip Lougher {
819c06a46fSPhillip Lougher 	struct squashfs_lz4 *stream = strm;
829c06a46fSPhillip Lougher 
839c06a46fSPhillip Lougher 	if (stream) {
849c06a46fSPhillip Lougher 		vfree(stream->input);
859c06a46fSPhillip Lougher 		vfree(stream->output);
869c06a46fSPhillip Lougher 	}
879c06a46fSPhillip Lougher 	kfree(stream);
889c06a46fSPhillip Lougher }
899c06a46fSPhillip Lougher 
909c06a46fSPhillip Lougher 
lz4_uncompress(struct squashfs_sb_info * msblk,void * strm,struct bio * bio,int offset,int length,struct squashfs_page_actor * output)919c06a46fSPhillip Lougher static int lz4_uncompress(struct squashfs_sb_info *msblk, void *strm,
9293e72b3cSPhilippe Liard 	struct bio *bio, int offset, int length,
939c06a46fSPhillip Lougher 	struct squashfs_page_actor *output)
949c06a46fSPhillip Lougher {
9593e72b3cSPhilippe Liard 	struct bvec_iter_all iter_all = {};
9693e72b3cSPhilippe Liard 	struct bio_vec *bvec = bvec_init_iter_all(&iter_all);
979c06a46fSPhillip Lougher 	struct squashfs_lz4 *stream = strm;
989c06a46fSPhillip Lougher 	void *buff = stream->input, *data;
9993e72b3cSPhilippe Liard 	int bytes = length, res;
1009c06a46fSPhillip Lougher 
10193e72b3cSPhilippe Liard 	while (bio_next_segment(bio, &iter_all)) {
10293e72b3cSPhilippe Liard 		int avail = min(bytes, ((int)bvec->bv_len) - offset);
10393e72b3cSPhilippe Liard 
104fbc27241SChristoph Hellwig 		data = bvec_virt(bvec);
10593e72b3cSPhilippe Liard 		memcpy(buff, data + offset, avail);
1069c06a46fSPhillip Lougher 		buff += avail;
1079c06a46fSPhillip Lougher 		bytes -= avail;
1089c06a46fSPhillip Lougher 		offset = 0;
1099c06a46fSPhillip Lougher 	}
1109c06a46fSPhillip Lougher 
111d21b5ff1SSven Schmidt 	res = LZ4_decompress_safe(stream->input, stream->output,
112d21b5ff1SSven Schmidt 		length, output->length);
113d21b5ff1SSven Schmidt 
114d21b5ff1SSven Schmidt 	if (res < 0)
1159c06a46fSPhillip Lougher 		return -EIO;
1169c06a46fSPhillip Lougher 
117d21b5ff1SSven Schmidt 	bytes = res;
1189c06a46fSPhillip Lougher 	data = squashfs_first_page(output);
1199c06a46fSPhillip Lougher 	buff = stream->output;
1209c06a46fSPhillip Lougher 	while (data) {
12109cbfeafSKirill A. Shutemov 		if (bytes <= PAGE_SIZE) {
122*f268eeddSPhillip Lougher 			if (!IS_ERR(data))
1239c06a46fSPhillip Lougher 				memcpy(data, buff, bytes);
1249c06a46fSPhillip Lougher 			break;
1259c06a46fSPhillip Lougher 		}
126*f268eeddSPhillip Lougher 		if (!IS_ERR(data))
12709cbfeafSKirill A. Shutemov 			memcpy(data, buff, PAGE_SIZE);
12809cbfeafSKirill A. Shutemov 		buff += PAGE_SIZE;
12909cbfeafSKirill A. Shutemov 		bytes -= PAGE_SIZE;
1309c06a46fSPhillip Lougher 		data = squashfs_next_page(output);
1319c06a46fSPhillip Lougher 	}
1329c06a46fSPhillip Lougher 	squashfs_finish_page(output);
1339c06a46fSPhillip Lougher 
134d21b5ff1SSven Schmidt 	return res;
1359c06a46fSPhillip Lougher }
1369c06a46fSPhillip Lougher 
1379c06a46fSPhillip Lougher const struct squashfs_decompressor squashfs_lz4_comp_ops = {
1389c06a46fSPhillip Lougher 	.init = lz4_init,
1399c06a46fSPhillip Lougher 	.comp_opts = lz4_comp_opts,
1409c06a46fSPhillip Lougher 	.free = lz4_free,
1419c06a46fSPhillip Lougher 	.decompress = lz4_uncompress,
1429c06a46fSPhillip Lougher 	.id = LZ4_COMPRESSION,
1439c06a46fSPhillip Lougher 	.name = "lz4",
144*f268eeddSPhillip Lougher 	.alloc_buffer = 0,
1459c06a46fSPhillip Lougher 	.supported = 1
1469c06a46fSPhillip Lougher };
147