xref: /openbmc/linux/fs/squashfs/zstd_wrapper.c (revision 87bf54bb)
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