18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Squashfs - a compressed read only filesystem for Linux
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009
68c2ecf20Sopenharmony_ci * Phillip Lougher <phillip@squashfs.org.uk>
78c2ecf20Sopenharmony_ci *
88c2ecf20Sopenharmony_ci * decompressor.c
98c2ecf20Sopenharmony_ci */
108c2ecf20Sopenharmony_ci
118c2ecf20Sopenharmony_ci#include <linux/types.h>
128c2ecf20Sopenharmony_ci#include <linux/mutex.h>
138c2ecf20Sopenharmony_ci#include <linux/slab.h>
148c2ecf20Sopenharmony_ci#include <linux/buffer_head.h>
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_ci#include "squashfs_fs.h"
178c2ecf20Sopenharmony_ci#include "squashfs_fs_sb.h"
188c2ecf20Sopenharmony_ci#include "decompressor.h"
198c2ecf20Sopenharmony_ci#include "squashfs.h"
208c2ecf20Sopenharmony_ci#include "page_actor.h"
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_ci/*
238c2ecf20Sopenharmony_ci * This file (and decompressor.h) implements a decompressor framework for
248c2ecf20Sopenharmony_ci * Squashfs, allowing multiple decompressors to be easily supported
258c2ecf20Sopenharmony_ci */
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_cistatic const struct squashfs_decompressor squashfs_lzma_unsupported_comp_ops = {
288c2ecf20Sopenharmony_ci	NULL, NULL, NULL, NULL, LZMA_COMPRESSION, "lzma", 0
298c2ecf20Sopenharmony_ci};
308c2ecf20Sopenharmony_ci
318c2ecf20Sopenharmony_ci#ifndef CONFIG_SQUASHFS_LZ4
328c2ecf20Sopenharmony_cistatic const struct squashfs_decompressor squashfs_lz4_comp_ops = {
338c2ecf20Sopenharmony_ci	NULL, NULL, NULL, NULL, LZ4_COMPRESSION, "lz4", 0
348c2ecf20Sopenharmony_ci};
358c2ecf20Sopenharmony_ci#endif
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_ci#ifndef CONFIG_SQUASHFS_LZO
388c2ecf20Sopenharmony_cistatic const struct squashfs_decompressor squashfs_lzo_comp_ops = {
398c2ecf20Sopenharmony_ci	NULL, NULL, NULL, NULL, LZO_COMPRESSION, "lzo", 0
408c2ecf20Sopenharmony_ci};
418c2ecf20Sopenharmony_ci#endif
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_ci#ifndef CONFIG_SQUASHFS_XZ
448c2ecf20Sopenharmony_cistatic const struct squashfs_decompressor squashfs_xz_comp_ops = {
458c2ecf20Sopenharmony_ci	NULL, NULL, NULL, NULL, XZ_COMPRESSION, "xz", 0
468c2ecf20Sopenharmony_ci};
478c2ecf20Sopenharmony_ci#endif
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_ci#ifndef CONFIG_SQUASHFS_ZLIB
508c2ecf20Sopenharmony_cistatic const struct squashfs_decompressor squashfs_zlib_comp_ops = {
518c2ecf20Sopenharmony_ci	NULL, NULL, NULL, NULL, ZLIB_COMPRESSION, "zlib", 0
528c2ecf20Sopenharmony_ci};
538c2ecf20Sopenharmony_ci#endif
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_ci#ifndef CONFIG_SQUASHFS_ZSTD
568c2ecf20Sopenharmony_cistatic const struct squashfs_decompressor squashfs_zstd_comp_ops = {
578c2ecf20Sopenharmony_ci	NULL, NULL, NULL, NULL, ZSTD_COMPRESSION, "zstd", 0
588c2ecf20Sopenharmony_ci};
598c2ecf20Sopenharmony_ci#endif
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_cistatic const struct squashfs_decompressor squashfs_unknown_comp_ops = {
628c2ecf20Sopenharmony_ci	NULL, NULL, NULL, NULL, 0, "unknown", 0
638c2ecf20Sopenharmony_ci};
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_cistatic const struct squashfs_decompressor *decompressor[] = {
668c2ecf20Sopenharmony_ci	&squashfs_zlib_comp_ops,
678c2ecf20Sopenharmony_ci	&squashfs_lz4_comp_ops,
688c2ecf20Sopenharmony_ci	&squashfs_lzo_comp_ops,
698c2ecf20Sopenharmony_ci	&squashfs_xz_comp_ops,
708c2ecf20Sopenharmony_ci	&squashfs_lzma_unsupported_comp_ops,
718c2ecf20Sopenharmony_ci	&squashfs_zstd_comp_ops,
728c2ecf20Sopenharmony_ci	&squashfs_unknown_comp_ops
738c2ecf20Sopenharmony_ci};
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_ciconst struct squashfs_decompressor *squashfs_lookup_decompressor(int id)
778c2ecf20Sopenharmony_ci{
788c2ecf20Sopenharmony_ci	int i;
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_ci	for (i = 0; decompressor[i]->id; i++)
818c2ecf20Sopenharmony_ci		if (id == decompressor[i]->id)
828c2ecf20Sopenharmony_ci			break;
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_ci	return decompressor[i];
858c2ecf20Sopenharmony_ci}
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_cistatic void *get_comp_opts(struct super_block *sb, unsigned short flags)
898c2ecf20Sopenharmony_ci{
908c2ecf20Sopenharmony_ci	struct squashfs_sb_info *msblk = sb->s_fs_info;
918c2ecf20Sopenharmony_ci	void *buffer = NULL, *comp_opts;
928c2ecf20Sopenharmony_ci	struct squashfs_page_actor *actor = NULL;
938c2ecf20Sopenharmony_ci	int length = 0;
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_ci	/*
968c2ecf20Sopenharmony_ci	 * Read decompressor specific options from file system if present
978c2ecf20Sopenharmony_ci	 */
988c2ecf20Sopenharmony_ci	if (SQUASHFS_COMP_OPTS(flags)) {
998c2ecf20Sopenharmony_ci		buffer = kmalloc(PAGE_SIZE, GFP_KERNEL);
1008c2ecf20Sopenharmony_ci		if (buffer == NULL) {
1018c2ecf20Sopenharmony_ci			comp_opts = ERR_PTR(-ENOMEM);
1028c2ecf20Sopenharmony_ci			goto out;
1038c2ecf20Sopenharmony_ci		}
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_ci		actor = squashfs_page_actor_init(&buffer, 1, 0);
1068c2ecf20Sopenharmony_ci		if (actor == NULL) {
1078c2ecf20Sopenharmony_ci			comp_opts = ERR_PTR(-ENOMEM);
1088c2ecf20Sopenharmony_ci			goto out;
1098c2ecf20Sopenharmony_ci		}
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_ci		length = squashfs_read_data(sb,
1128c2ecf20Sopenharmony_ci			sizeof(struct squashfs_super_block), 0, NULL, actor);
1138c2ecf20Sopenharmony_ci
1148c2ecf20Sopenharmony_ci		if (length < 0) {
1158c2ecf20Sopenharmony_ci			comp_opts = ERR_PTR(length);
1168c2ecf20Sopenharmony_ci			goto out;
1178c2ecf20Sopenharmony_ci		}
1188c2ecf20Sopenharmony_ci	}
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_ci	comp_opts = squashfs_comp_opts(msblk, buffer, length);
1218c2ecf20Sopenharmony_ci
1228c2ecf20Sopenharmony_ciout:
1238c2ecf20Sopenharmony_ci	kfree(actor);
1248c2ecf20Sopenharmony_ci	kfree(buffer);
1258c2ecf20Sopenharmony_ci	return comp_opts;
1268c2ecf20Sopenharmony_ci}
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_civoid *squashfs_decompressor_setup(struct super_block *sb, unsigned short flags)
1308c2ecf20Sopenharmony_ci{
1318c2ecf20Sopenharmony_ci	struct squashfs_sb_info *msblk = sb->s_fs_info;
1328c2ecf20Sopenharmony_ci	void *stream, *comp_opts = get_comp_opts(sb, flags);
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_ci	if (IS_ERR(comp_opts))
1358c2ecf20Sopenharmony_ci		return comp_opts;
1368c2ecf20Sopenharmony_ci
1378c2ecf20Sopenharmony_ci	stream = squashfs_decompressor_create(msblk, comp_opts);
1388c2ecf20Sopenharmony_ci	if (IS_ERR(stream))
1398c2ecf20Sopenharmony_ci		kfree(comp_opts);
1408c2ecf20Sopenharmony_ci
1418c2ecf20Sopenharmony_ci	return stream;
1428c2ecf20Sopenharmony_ci}
143