13e0a4e85SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later 287bf54bbSSean Purcell /* 387bf54bbSSean Purcell * Squashfs - a compressed read only filesystem for Linux 487bf54bbSSean Purcell * 587bf54bbSSean Purcell * Copyright (c) 2016-present, Facebook, Inc. 687bf54bbSSean Purcell * All rights reserved. 787bf54bbSSean Purcell * 887bf54bbSSean Purcell * zstd_wrapper.c 987bf54bbSSean Purcell */ 1087bf54bbSSean Purcell 1187bf54bbSSean Purcell #include <linux/mutex.h> 1293e72b3cSPhilippe Liard #include <linux/bio.h> 1387bf54bbSSean Purcell #include <linux/slab.h> 1487bf54bbSSean Purcell #include <linux/zstd.h> 1587bf54bbSSean Purcell #include <linux/vmalloc.h> 1687bf54bbSSean Purcell 1787bf54bbSSean Purcell #include "squashfs_fs.h" 1887bf54bbSSean Purcell #include "squashfs_fs_sb.h" 1987bf54bbSSean Purcell #include "squashfs.h" 2087bf54bbSSean Purcell #include "decompressor.h" 2187bf54bbSSean Purcell #include "page_actor.h" 2287bf54bbSSean Purcell 2387bf54bbSSean Purcell struct workspace { 2487bf54bbSSean Purcell void *mem; 2587bf54bbSSean Purcell size_t mem_size; 2687bf54bbSSean Purcell size_t window_size; 2787bf54bbSSean Purcell }; 2887bf54bbSSean Purcell 2987bf54bbSSean Purcell static void *zstd_init(struct squashfs_sb_info *msblk, void *buff) 3087bf54bbSSean Purcell { 3187bf54bbSSean Purcell struct workspace *wksp = kmalloc(sizeof(*wksp), GFP_KERNEL); 3287bf54bbSSean Purcell 3387bf54bbSSean Purcell if (wksp == NULL) 3487bf54bbSSean Purcell goto failed; 3587bf54bbSSean Purcell wksp->window_size = max_t(size_t, 3687bf54bbSSean Purcell msblk->block_size, SQUASHFS_METADATA_SIZE); 37*cf30f6a5SNick Terrell wksp->mem_size = zstd_dstream_workspace_bound(wksp->window_size); 3887bf54bbSSean Purcell wksp->mem = vmalloc(wksp->mem_size); 3987bf54bbSSean Purcell if (wksp->mem == NULL) 4087bf54bbSSean Purcell goto failed; 4187bf54bbSSean Purcell 4287bf54bbSSean Purcell return wksp; 4387bf54bbSSean Purcell 4487bf54bbSSean Purcell failed: 4587bf54bbSSean Purcell ERROR("Failed to allocate zstd workspace\n"); 4687bf54bbSSean Purcell kfree(wksp); 4787bf54bbSSean Purcell return ERR_PTR(-ENOMEM); 4887bf54bbSSean Purcell } 4987bf54bbSSean Purcell 5087bf54bbSSean Purcell 5187bf54bbSSean Purcell static void zstd_free(void *strm) 5287bf54bbSSean Purcell { 5387bf54bbSSean Purcell struct workspace *wksp = strm; 5487bf54bbSSean Purcell 5587bf54bbSSean Purcell if (wksp) 5687bf54bbSSean Purcell vfree(wksp->mem); 5787bf54bbSSean Purcell kfree(wksp); 5887bf54bbSSean Purcell } 5987bf54bbSSean Purcell 6087bf54bbSSean Purcell 6187bf54bbSSean Purcell static int zstd_uncompress(struct squashfs_sb_info *msblk, void *strm, 6293e72b3cSPhilippe Liard struct bio *bio, int offset, int length, 6387bf54bbSSean Purcell struct squashfs_page_actor *output) 6487bf54bbSSean Purcell { 6587bf54bbSSean Purcell struct workspace *wksp = strm; 66*cf30f6a5SNick Terrell zstd_dstream *stream; 6787bf54bbSSean Purcell size_t total_out = 0; 6893e72b3cSPhilippe Liard int error = 0; 69*cf30f6a5SNick Terrell zstd_in_buffer in_buf = { NULL, 0, 0 }; 70*cf30f6a5SNick Terrell zstd_out_buffer out_buf = { NULL, 0, 0 }; 7193e72b3cSPhilippe Liard struct bvec_iter_all iter_all = {}; 7293e72b3cSPhilippe Liard struct bio_vec *bvec = bvec_init_iter_all(&iter_all); 7387bf54bbSSean Purcell 74*cf30f6a5SNick Terrell stream = zstd_init_dstream(wksp->window_size, wksp->mem, wksp->mem_size); 7587bf54bbSSean Purcell 7687bf54bbSSean Purcell if (!stream) { 7787bf54bbSSean Purcell ERROR("Failed to initialize zstd decompressor\n"); 7893e72b3cSPhilippe Liard return -EIO; 7987bf54bbSSean Purcell } 8087bf54bbSSean Purcell 8187bf54bbSSean Purcell out_buf.size = PAGE_SIZE; 8287bf54bbSSean Purcell out_buf.dst = squashfs_first_page(output); 8387bf54bbSSean Purcell 8493e72b3cSPhilippe Liard for (;;) { 8593e72b3cSPhilippe Liard size_t zstd_err; 8687bf54bbSSean Purcell 8793e72b3cSPhilippe Liard if (in_buf.pos == in_buf.size) { 8893e72b3cSPhilippe Liard const void *data; 8993e72b3cSPhilippe Liard int avail; 9093e72b3cSPhilippe Liard 9193e72b3cSPhilippe Liard if (!bio_next_segment(bio, &iter_all)) { 9293e72b3cSPhilippe Liard error = -EIO; 9393e72b3cSPhilippe Liard break; 9493e72b3cSPhilippe Liard } 9593e72b3cSPhilippe Liard 9693e72b3cSPhilippe Liard avail = min(length, ((int)bvec->bv_len) - offset); 97fbc27241SChristoph Hellwig data = bvec_virt(bvec); 9887bf54bbSSean Purcell length -= avail; 9993e72b3cSPhilippe Liard in_buf.src = data + offset; 10087bf54bbSSean Purcell in_buf.size = avail; 10187bf54bbSSean Purcell in_buf.pos = 0; 10287bf54bbSSean Purcell offset = 0; 10387bf54bbSSean Purcell } 10487bf54bbSSean Purcell 10587bf54bbSSean Purcell if (out_buf.pos == out_buf.size) { 10687bf54bbSSean Purcell out_buf.dst = squashfs_next_page(output); 10787bf54bbSSean Purcell if (out_buf.dst == NULL) { 10887bf54bbSSean Purcell /* Shouldn't run out of pages 10987bf54bbSSean Purcell * before stream is done. 11087bf54bbSSean Purcell */ 11193e72b3cSPhilippe Liard error = -EIO; 11293e72b3cSPhilippe Liard break; 11387bf54bbSSean Purcell } 11487bf54bbSSean Purcell out_buf.pos = 0; 11587bf54bbSSean Purcell out_buf.size = PAGE_SIZE; 11687bf54bbSSean Purcell } 11787bf54bbSSean Purcell 11887bf54bbSSean Purcell total_out -= out_buf.pos; 119*cf30f6a5SNick Terrell zstd_err = zstd_decompress_stream(stream, &out_buf, &in_buf); 12087bf54bbSSean Purcell total_out += out_buf.pos; /* add the additional data produced */ 12193e72b3cSPhilippe Liard if (zstd_err == 0) 12293e72b3cSPhilippe Liard break; 12387bf54bbSSean Purcell 124*cf30f6a5SNick Terrell if (zstd_is_error(zstd_err)) { 12587bf54bbSSean Purcell ERROR("zstd decompression error: %d\n", 126*cf30f6a5SNick Terrell (int)zstd_get_error_code(zstd_err)); 12793e72b3cSPhilippe Liard error = -EIO; 12893e72b3cSPhilippe Liard break; 12993e72b3cSPhilippe Liard } 13087bf54bbSSean Purcell } 13187bf54bbSSean Purcell 13293e72b3cSPhilippe Liard squashfs_finish_page(output); 13387bf54bbSSean Purcell 13493e72b3cSPhilippe Liard return error ? error : total_out; 13587bf54bbSSean Purcell } 13687bf54bbSSean Purcell 13787bf54bbSSean Purcell const struct squashfs_decompressor squashfs_zstd_comp_ops = { 13887bf54bbSSean Purcell .init = zstd_init, 13987bf54bbSSean Purcell .free = zstd_free, 14087bf54bbSSean Purcell .decompress = zstd_uncompress, 14187bf54bbSSean Purcell .id = ZSTD_COMPRESSION, 14287bf54bbSSean Purcell .name = "zstd", 14387bf54bbSSean Purcell .supported = 1 14487bf54bbSSean Purcell }; 145