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 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61
#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);
	wksp->mem_size = ZSTD_DStreamWorkspaceBound(wksp->window_size);
	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 66 67
	struct squashfs_page_actor *output)
{
	struct workspace *wksp = strm;
	ZSTD_DStream *stream;
	size_t total_out = 0;
68
	int error = 0;
S
Sean Purcell 已提交
69 70
	ZSTD_inBuffer in_buf = { NULL, 0, 0 };
	ZSTD_outBuffer 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 74 75 76 77

	stream = ZSTD_initDStream(wksp->window_size, wksp->mem, wksp->mem_size);

	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 97
		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);
			data = page_address(bvec->bv_page) + bvec->bv_offset;
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 119 120
			}
			out_buf.pos = 0;
			out_buf.size = PAGE_SIZE;
		}

		total_out -= out_buf.pos;
		zstd_err = ZSTD_decompressStream(stream, &out_buf, &in_buf);
		total_out += out_buf.pos; /* add the additional data produced */
121 122 123 124 125 126 127 128 129
		if (zstd_err == 0)
			break;

		if (ZSTD_isError(zstd_err)) {
			ERROR("zstd decompression error: %d\n",
					(int)ZSTD_getErrorCode(zstd_err));
			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
};