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
zstd_init(struct squashfs_sb_info * msblk,void * buff)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);
37cf30f6a5SNick 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
zstd_free(void * strm)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
zstd_uncompress(struct squashfs_sb_info * msblk,void * strm,struct bio * bio,int offset,int length,struct squashfs_page_actor * output)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;
66cf30f6a5SNick Terrell zstd_dstream *stream;
6787bf54bbSSean Purcell size_t total_out = 0;
6893e72b3cSPhilippe Liard int error = 0;
69cf30f6a5SNick Terrell zstd_in_buffer in_buf = { NULL, 0, 0 };
70cf30f6a5SNick 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
74cf30f6a5SNick 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);
83*f268eeddSPhillip Lougher if (IS_ERR(out_buf.dst)) {
84*f268eeddSPhillip Lougher error = PTR_ERR(out_buf.dst);
85*f268eeddSPhillip Lougher goto finish;
86*f268eeddSPhillip Lougher }
8787bf54bbSSean Purcell
8893e72b3cSPhilippe Liard for (;;) {
8993e72b3cSPhilippe Liard size_t zstd_err;
9087bf54bbSSean Purcell
9193e72b3cSPhilippe Liard if (in_buf.pos == in_buf.size) {
9293e72b3cSPhilippe Liard const void *data;
9393e72b3cSPhilippe Liard int avail;
9493e72b3cSPhilippe Liard
9593e72b3cSPhilippe Liard if (!bio_next_segment(bio, &iter_all)) {
9693e72b3cSPhilippe Liard error = -EIO;
9793e72b3cSPhilippe Liard break;
9893e72b3cSPhilippe Liard }
9993e72b3cSPhilippe Liard
10093e72b3cSPhilippe Liard avail = min(length, ((int)bvec->bv_len) - offset);
101fbc27241SChristoph Hellwig data = bvec_virt(bvec);
10287bf54bbSSean Purcell length -= avail;
10393e72b3cSPhilippe Liard in_buf.src = data + offset;
10487bf54bbSSean Purcell in_buf.size = avail;
10587bf54bbSSean Purcell in_buf.pos = 0;
10687bf54bbSSean Purcell offset = 0;
10787bf54bbSSean Purcell }
10887bf54bbSSean Purcell
10987bf54bbSSean Purcell if (out_buf.pos == out_buf.size) {
11087bf54bbSSean Purcell out_buf.dst = squashfs_next_page(output);
111*f268eeddSPhillip Lougher if (IS_ERR(out_buf.dst)) {
112*f268eeddSPhillip Lougher error = PTR_ERR(out_buf.dst);
113*f268eeddSPhillip Lougher break;
114*f268eeddSPhillip Lougher } else if (out_buf.dst == NULL) {
11587bf54bbSSean Purcell /* Shouldn't run out of pages
11687bf54bbSSean Purcell * before stream is done.
11787bf54bbSSean Purcell */
11893e72b3cSPhilippe Liard error = -EIO;
11993e72b3cSPhilippe Liard break;
12087bf54bbSSean Purcell }
12187bf54bbSSean Purcell out_buf.pos = 0;
12287bf54bbSSean Purcell out_buf.size = PAGE_SIZE;
12387bf54bbSSean Purcell }
12487bf54bbSSean Purcell
12587bf54bbSSean Purcell total_out -= out_buf.pos;
126cf30f6a5SNick Terrell zstd_err = zstd_decompress_stream(stream, &out_buf, &in_buf);
12787bf54bbSSean Purcell total_out += out_buf.pos; /* add the additional data produced */
12893e72b3cSPhilippe Liard if (zstd_err == 0)
12993e72b3cSPhilippe Liard break;
13087bf54bbSSean Purcell
131cf30f6a5SNick Terrell if (zstd_is_error(zstd_err)) {
13287bf54bbSSean Purcell ERROR("zstd decompression error: %d\n",
133cf30f6a5SNick Terrell (int)zstd_get_error_code(zstd_err));
13493e72b3cSPhilippe Liard error = -EIO;
13593e72b3cSPhilippe Liard break;
13693e72b3cSPhilippe Liard }
13787bf54bbSSean Purcell }
13887bf54bbSSean Purcell
139*f268eeddSPhillip Lougher finish:
140*f268eeddSPhillip Lougher
14193e72b3cSPhilippe Liard squashfs_finish_page(output);
14287bf54bbSSean Purcell
14393e72b3cSPhilippe Liard return error ? error : total_out;
14487bf54bbSSean Purcell }
14587bf54bbSSean Purcell
14687bf54bbSSean Purcell const struct squashfs_decompressor squashfs_zstd_comp_ops = {
14787bf54bbSSean Purcell .init = zstd_init,
14887bf54bbSSean Purcell .free = zstd_free,
14987bf54bbSSean Purcell .decompress = zstd_uncompress,
15087bf54bbSSean Purcell .id = ZSTD_COMPRESSION,
15187bf54bbSSean Purcell .name = "zstd",
152*f268eeddSPhillip Lougher .alloc_buffer = 1,
15387bf54bbSSean Purcell .supported = 1
15487bf54bbSSean Purcell };
155