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