187bf54bbSSean Purcell /* 287bf54bbSSean Purcell * Squashfs - a compressed read only filesystem for Linux 387bf54bbSSean Purcell * 487bf54bbSSean Purcell * Copyright (c) 2016-present, Facebook, Inc. 587bf54bbSSean Purcell * All rights reserved. 687bf54bbSSean Purcell * 787bf54bbSSean Purcell * This program is free software; you can redistribute it and/or 887bf54bbSSean Purcell * modify it under the terms of the GNU General Public License 987bf54bbSSean Purcell * as published by the Free Software Foundation; either version 2, 1087bf54bbSSean Purcell * or (at your option) any later version. 1187bf54bbSSean Purcell * 1287bf54bbSSean Purcell * This program is distributed in the hope that it will be useful, 1387bf54bbSSean Purcell * but WITHOUT ANY WARRANTY; without even the implied warranty of 1487bf54bbSSean Purcell * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 1587bf54bbSSean Purcell * GNU General Public License for more details. 1687bf54bbSSean Purcell * 1787bf54bbSSean Purcell * zstd_wrapper.c 1887bf54bbSSean Purcell */ 1987bf54bbSSean Purcell 2087bf54bbSSean Purcell #include <linux/mutex.h> 2187bf54bbSSean Purcell #include <linux/buffer_head.h> 2287bf54bbSSean Purcell #include <linux/slab.h> 2387bf54bbSSean Purcell #include <linux/zstd.h> 2487bf54bbSSean Purcell #include <linux/vmalloc.h> 2587bf54bbSSean Purcell 2687bf54bbSSean Purcell #include "squashfs_fs.h" 2787bf54bbSSean Purcell #include "squashfs_fs_sb.h" 2887bf54bbSSean Purcell #include "squashfs.h" 2987bf54bbSSean Purcell #include "decompressor.h" 3087bf54bbSSean Purcell #include "page_actor.h" 3187bf54bbSSean Purcell 3287bf54bbSSean Purcell struct workspace { 3387bf54bbSSean Purcell void *mem; 3487bf54bbSSean Purcell size_t mem_size; 3587bf54bbSSean Purcell size_t window_size; 3687bf54bbSSean Purcell }; 3787bf54bbSSean Purcell 3887bf54bbSSean Purcell static void *zstd_init(struct squashfs_sb_info *msblk, void *buff) 3987bf54bbSSean Purcell { 4087bf54bbSSean Purcell struct workspace *wksp = kmalloc(sizeof(*wksp), GFP_KERNEL); 4187bf54bbSSean Purcell 4287bf54bbSSean Purcell if (wksp == NULL) 4387bf54bbSSean Purcell goto failed; 4487bf54bbSSean Purcell wksp->window_size = max_t(size_t, 4587bf54bbSSean Purcell msblk->block_size, SQUASHFS_METADATA_SIZE); 4687bf54bbSSean Purcell wksp->mem_size = ZSTD_DStreamWorkspaceBound(wksp->window_size); 4787bf54bbSSean Purcell wksp->mem = vmalloc(wksp->mem_size); 4887bf54bbSSean Purcell if (wksp->mem == NULL) 4987bf54bbSSean Purcell goto failed; 5087bf54bbSSean Purcell 5187bf54bbSSean Purcell return wksp; 5287bf54bbSSean Purcell 5387bf54bbSSean Purcell failed: 5487bf54bbSSean Purcell ERROR("Failed to allocate zstd workspace\n"); 5587bf54bbSSean Purcell kfree(wksp); 5687bf54bbSSean Purcell return ERR_PTR(-ENOMEM); 5787bf54bbSSean Purcell } 5887bf54bbSSean Purcell 5987bf54bbSSean Purcell 6087bf54bbSSean Purcell static void zstd_free(void *strm) 6187bf54bbSSean Purcell { 6287bf54bbSSean Purcell struct workspace *wksp = strm; 6387bf54bbSSean Purcell 6487bf54bbSSean Purcell if (wksp) 6587bf54bbSSean Purcell vfree(wksp->mem); 6687bf54bbSSean Purcell kfree(wksp); 6787bf54bbSSean Purcell } 6887bf54bbSSean Purcell 6987bf54bbSSean Purcell 7087bf54bbSSean Purcell static int zstd_uncompress(struct squashfs_sb_info *msblk, void *strm, 7187bf54bbSSean Purcell struct buffer_head **bh, int b, int offset, int length, 7287bf54bbSSean Purcell struct squashfs_page_actor *output) 7387bf54bbSSean Purcell { 7487bf54bbSSean Purcell struct workspace *wksp = strm; 7587bf54bbSSean Purcell ZSTD_DStream *stream; 7687bf54bbSSean Purcell size_t total_out = 0; 7787bf54bbSSean Purcell size_t zstd_err; 7887bf54bbSSean Purcell int k = 0; 7987bf54bbSSean Purcell ZSTD_inBuffer in_buf = { NULL, 0, 0 }; 8087bf54bbSSean Purcell ZSTD_outBuffer out_buf = { NULL, 0, 0 }; 8187bf54bbSSean Purcell 8287bf54bbSSean Purcell stream = ZSTD_initDStream(wksp->window_size, wksp->mem, wksp->mem_size); 8387bf54bbSSean Purcell 8487bf54bbSSean Purcell if (!stream) { 8587bf54bbSSean Purcell ERROR("Failed to initialize zstd decompressor\n"); 8687bf54bbSSean Purcell goto out; 8787bf54bbSSean Purcell } 8887bf54bbSSean Purcell 8987bf54bbSSean Purcell out_buf.size = PAGE_SIZE; 9087bf54bbSSean Purcell out_buf.dst = squashfs_first_page(output); 9187bf54bbSSean Purcell 9287bf54bbSSean Purcell do { 9387bf54bbSSean Purcell if (in_buf.pos == in_buf.size && k < b) { 9487bf54bbSSean Purcell int avail = min(length, msblk->devblksize - offset); 9587bf54bbSSean Purcell 9687bf54bbSSean Purcell length -= avail; 9787bf54bbSSean Purcell in_buf.src = bh[k]->b_data + offset; 9887bf54bbSSean Purcell in_buf.size = avail; 9987bf54bbSSean Purcell in_buf.pos = 0; 10087bf54bbSSean Purcell offset = 0; 10187bf54bbSSean Purcell } 10287bf54bbSSean Purcell 10387bf54bbSSean Purcell if (out_buf.pos == out_buf.size) { 10487bf54bbSSean Purcell out_buf.dst = squashfs_next_page(output); 10587bf54bbSSean Purcell if (out_buf.dst == NULL) { 10687bf54bbSSean Purcell /* Shouldn't run out of pages 10787bf54bbSSean Purcell * before stream is done. 10887bf54bbSSean Purcell */ 10987bf54bbSSean Purcell squashfs_finish_page(output); 11087bf54bbSSean Purcell goto out; 11187bf54bbSSean Purcell } 11287bf54bbSSean Purcell out_buf.pos = 0; 11387bf54bbSSean Purcell out_buf.size = PAGE_SIZE; 11487bf54bbSSean Purcell } 11587bf54bbSSean Purcell 11687bf54bbSSean Purcell total_out -= out_buf.pos; 11787bf54bbSSean Purcell zstd_err = ZSTD_decompressStream(stream, &out_buf, &in_buf); 11887bf54bbSSean Purcell total_out += out_buf.pos; /* add the additional data produced */ 11987bf54bbSSean Purcell 12087bf54bbSSean Purcell if (in_buf.pos == in_buf.size && k < b) 12187bf54bbSSean Purcell put_bh(bh[k++]); 12287bf54bbSSean Purcell } while (zstd_err != 0 && !ZSTD_isError(zstd_err)); 12387bf54bbSSean Purcell 12487bf54bbSSean Purcell squashfs_finish_page(output); 12587bf54bbSSean Purcell 12687bf54bbSSean Purcell if (ZSTD_isError(zstd_err)) { 12787bf54bbSSean Purcell ERROR("zstd decompression error: %d\n", 12887bf54bbSSean Purcell (int)ZSTD_getErrorCode(zstd_err)); 12987bf54bbSSean Purcell goto out; 13087bf54bbSSean Purcell } 13187bf54bbSSean Purcell 13287bf54bbSSean Purcell if (k < b) 13387bf54bbSSean Purcell goto out; 13487bf54bbSSean Purcell 13587bf54bbSSean Purcell return (int)total_out; 13687bf54bbSSean Purcell 13787bf54bbSSean Purcell out: 13887bf54bbSSean Purcell for (; k < b; k++) 13987bf54bbSSean Purcell put_bh(bh[k]); 14087bf54bbSSean Purcell 14187bf54bbSSean Purcell return -EIO; 14287bf54bbSSean Purcell } 14387bf54bbSSean Purcell 14487bf54bbSSean Purcell const struct squashfs_decompressor squashfs_zstd_comp_ops = { 14587bf54bbSSean Purcell .init = zstd_init, 14687bf54bbSSean Purcell .free = zstd_free, 14787bf54bbSSean Purcell .decompress = zstd_uncompress, 14887bf54bbSSean Purcell .id = ZSTD_COMPRESSION, 14987bf54bbSSean Purcell .name = "zstd", 15087bf54bbSSean Purcell .supported = 1 15187bf54bbSSean Purcell }; 152