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 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 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 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 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 10493e72b3cSPhilippe Liard data = page_address(bvec->bv_page) + bvec->bv_offset; 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) { 1229c06a46fSPhillip Lougher memcpy(data, buff, bytes); 1239c06a46fSPhillip Lougher break; 1249c06a46fSPhillip Lougher } 12509cbfeafSKirill A. Shutemov memcpy(data, buff, PAGE_SIZE); 12609cbfeafSKirill A. Shutemov buff += PAGE_SIZE; 12709cbfeafSKirill A. Shutemov bytes -= PAGE_SIZE; 1289c06a46fSPhillip Lougher data = squashfs_next_page(output); 1299c06a46fSPhillip Lougher } 1309c06a46fSPhillip Lougher squashfs_finish_page(output); 1319c06a46fSPhillip Lougher 132d21b5ff1SSven Schmidt return res; 1339c06a46fSPhillip Lougher } 1349c06a46fSPhillip Lougher 1359c06a46fSPhillip Lougher const struct squashfs_decompressor squashfs_lz4_comp_ops = { 1369c06a46fSPhillip Lougher .init = lz4_init, 1379c06a46fSPhillip Lougher .comp_opts = lz4_comp_opts, 1389c06a46fSPhillip Lougher .free = lz4_free, 1399c06a46fSPhillip Lougher .decompress = lz4_uncompress, 1409c06a46fSPhillip Lougher .id = LZ4_COMPRESSION, 1419c06a46fSPhillip Lougher .name = "lz4", 1429c06a46fSPhillip Lougher .supported = 1 1439c06a46fSPhillip Lougher }; 144