zstd_wrapper.c 3.0 KB
Newer Older
1
// SPDX-License-Identifier: GPL-2.0-or-later
S
Sean Purcell 已提交
2 3 4 5 6 7 8 9 10 11
/*
 * Squashfs - a compressed read only filesystem for Linux
 *
 * Copyright (c) 2016-present, Facebook, Inc.
 * All rights reserved.
 *
 * zstd_wrapper.c
 */

#include <linux/mutex.h>
12
#include <linux/bio.h>
S
Sean Purcell 已提交
13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
#include <linux/slab.h>
#include <linux/zstd.h>
#include <linux/vmalloc.h>

#include "squashfs_fs.h"
#include "squashfs_fs_sb.h"
#include "squashfs.h"
#include "decompressor.h"
#include "page_actor.h"

struct workspace {
	void *mem;
	size_t mem_size;
	size_t window_size;
};

static void *zstd_init(struct squashfs_sb_info *msblk, void *buff)
{
	struct workspace *wksp = kmalloc(sizeof(*wksp), GFP_KERNEL);

	if (wksp == NULL)
		goto failed;
	wksp->window_size = max_t(size_t,
			msblk->block_size, SQUASHFS_METADATA_SIZE);
N
Nick Terrell 已提交
37
	wksp->mem_size = zstd_dstream_workspace_bound(wksp->window_size);
S
Sean Purcell 已提交
38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61
	wksp->mem = vmalloc(wksp->mem_size);
	if (wksp->mem == NULL)
		goto failed;

	return wksp;

failed:
	ERROR("Failed to allocate zstd workspace\n");
	kfree(wksp);
	return ERR_PTR(-ENOMEM);
}


static void zstd_free(void *strm)
{
	struct workspace *wksp = strm;

	if (wksp)
		vfree(wksp->mem);
	kfree(wksp);
}


static int zstd_uncompress(struct squashfs_sb_info *msblk, void *strm,
62
	struct bio *bio, int offset, int length,
S
Sean Purcell 已提交
63 64 65
	struct squashfs_page_actor *output)
{
	struct workspace *wksp = strm;
N
Nick Terrell 已提交
66
	zstd_dstream *stream;
S
Sean Purcell 已提交
67
	size_t total_out = 0;
68
	int error = 0;
N
Nick Terrell 已提交
69 70
	zstd_in_buffer in_buf = { NULL, 0, 0 };
	zstd_out_buffer out_buf = { NULL, 0, 0 };
71 72
	struct bvec_iter_all iter_all = {};
	struct bio_vec *bvec = bvec_init_iter_all(&iter_all);
S
Sean Purcell 已提交
73

N
Nick Terrell 已提交
74
	stream = zstd_init_dstream(wksp->window_size, wksp->mem, wksp->mem_size);
S
Sean Purcell 已提交
75 76 77

	if (!stream) {
		ERROR("Failed to initialize zstd decompressor\n");
78
		return -EIO;
S
Sean Purcell 已提交
79 80 81 82 83
	}

	out_buf.size = PAGE_SIZE;
	out_buf.dst = squashfs_first_page(output);

84 85
	for (;;) {
		size_t zstd_err;
S
Sean Purcell 已提交
86

87 88 89 90 91 92 93 94 95 96
		if (in_buf.pos == in_buf.size) {
			const void *data;
			int avail;

			if (!bio_next_segment(bio, &iter_all)) {
				error = -EIO;
				break;
			}

			avail = min(length, ((int)bvec->bv_len) - offset);
C
Christoph Hellwig 已提交
97
			data = bvec_virt(bvec);
S
Sean Purcell 已提交
98
			length -= avail;
99
			in_buf.src = data + offset;
S
Sean Purcell 已提交
100 101 102 103 104 105 106 107 108 109 110
			in_buf.size = avail;
			in_buf.pos = 0;
			offset = 0;
		}

		if (out_buf.pos == out_buf.size) {
			out_buf.dst = squashfs_next_page(output);
			if (out_buf.dst == NULL) {
				/* Shouldn't run out of pages
				 * before stream is done.
				 */
111 112
				error = -EIO;
				break;
S
Sean Purcell 已提交
113 114 115 116 117 118
			}
			out_buf.pos = 0;
			out_buf.size = PAGE_SIZE;
		}

		total_out -= out_buf.pos;
N
Nick Terrell 已提交
119
		zstd_err = zstd_decompress_stream(stream, &out_buf, &in_buf);
S
Sean Purcell 已提交
120
		total_out += out_buf.pos; /* add the additional data produced */
121 122 123
		if (zstd_err == 0)
			break;

N
Nick Terrell 已提交
124
		if (zstd_is_error(zstd_err)) {
125
			ERROR("zstd decompression error: %d\n",
N
Nick Terrell 已提交
126
					(int)zstd_get_error_code(zstd_err));
127 128 129
			error = -EIO;
			break;
		}
S
Sean Purcell 已提交
130 131
	}

132
	squashfs_finish_page(output);
S
Sean Purcell 已提交
133

134
	return error ? error : total_out;
S
Sean Purcell 已提交
135 136 137 138 139 140 141 142 143 144
}

const struct squashfs_decompressor squashfs_zstd_comp_ops = {
	.init = zstd_init,
	.free = zstd_free,
	.decompress = zstd_uncompress,
	.id = ZSTD_COMPRESSION,
	.name = "zstd",
	.supported = 1
};