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) 2010 LG Electronics
68c2ecf20Sopenharmony_ci * Chan Jeong <chan.jeong@lge.com>
78c2ecf20Sopenharmony_ci *
88c2ecf20Sopenharmony_ci * lzo_wrapper.c
98c2ecf20Sopenharmony_ci */
108c2ecf20Sopenharmony_ci
118c2ecf20Sopenharmony_ci#include <linux/mutex.h>
128c2ecf20Sopenharmony_ci#include <linux/bio.h>
138c2ecf20Sopenharmony_ci#include <linux/slab.h>
148c2ecf20Sopenharmony_ci#include <linux/vmalloc.h>
158c2ecf20Sopenharmony_ci#include <linux/lzo.h>
168c2ecf20Sopenharmony_ci
178c2ecf20Sopenharmony_ci#include "squashfs_fs.h"
188c2ecf20Sopenharmony_ci#include "squashfs_fs_sb.h"
198c2ecf20Sopenharmony_ci#include "squashfs.h"
208c2ecf20Sopenharmony_ci#include "decompressor.h"
218c2ecf20Sopenharmony_ci#include "page_actor.h"
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_cistruct squashfs_lzo {
248c2ecf20Sopenharmony_ci	void	*input;
258c2ecf20Sopenharmony_ci	void	*output;
268c2ecf20Sopenharmony_ci};
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_cistatic void *lzo_init(struct squashfs_sb_info *msblk, void *buff)
298c2ecf20Sopenharmony_ci{
308c2ecf20Sopenharmony_ci	int block_size = max_t(int, msblk->block_size, SQUASHFS_METADATA_SIZE);
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_ci	struct squashfs_lzo *stream = kzalloc(sizeof(*stream), GFP_KERNEL);
338c2ecf20Sopenharmony_ci	if (stream == NULL)
348c2ecf20Sopenharmony_ci		goto failed;
358c2ecf20Sopenharmony_ci	stream->input = vmalloc(block_size);
368c2ecf20Sopenharmony_ci	if (stream->input == NULL)
378c2ecf20Sopenharmony_ci		goto failed;
388c2ecf20Sopenharmony_ci	stream->output = vmalloc(block_size);
398c2ecf20Sopenharmony_ci	if (stream->output == NULL)
408c2ecf20Sopenharmony_ci		goto failed2;
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_ci	return stream;
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_cifailed2:
458c2ecf20Sopenharmony_ci	vfree(stream->input);
468c2ecf20Sopenharmony_cifailed:
478c2ecf20Sopenharmony_ci	ERROR("Failed to allocate lzo workspace\n");
488c2ecf20Sopenharmony_ci	kfree(stream);
498c2ecf20Sopenharmony_ci	return ERR_PTR(-ENOMEM);
508c2ecf20Sopenharmony_ci}
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_cistatic void lzo_free(void *strm)
548c2ecf20Sopenharmony_ci{
558c2ecf20Sopenharmony_ci	struct squashfs_lzo *stream = strm;
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_ci	if (stream) {
588c2ecf20Sopenharmony_ci		vfree(stream->input);
598c2ecf20Sopenharmony_ci		vfree(stream->output);
608c2ecf20Sopenharmony_ci	}
618c2ecf20Sopenharmony_ci	kfree(stream);
628c2ecf20Sopenharmony_ci}
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_cistatic int lzo_uncompress(struct squashfs_sb_info *msblk, void *strm,
668c2ecf20Sopenharmony_ci	struct bio *bio, int offset, int length,
678c2ecf20Sopenharmony_ci	struct squashfs_page_actor *output)
688c2ecf20Sopenharmony_ci{
698c2ecf20Sopenharmony_ci	struct bvec_iter_all iter_all = {};
708c2ecf20Sopenharmony_ci	struct bio_vec *bvec = bvec_init_iter_all(&iter_all);
718c2ecf20Sopenharmony_ci	struct squashfs_lzo *stream = strm;
728c2ecf20Sopenharmony_ci	void *buff = stream->input, *data;
738c2ecf20Sopenharmony_ci	int bytes = length, res;
748c2ecf20Sopenharmony_ci	size_t out_len = output->length;
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_ci	while (bio_next_segment(bio, &iter_all)) {
778c2ecf20Sopenharmony_ci		int avail = min(bytes, ((int)bvec->bv_len) - offset);
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_ci		data = page_address(bvec->bv_page) + bvec->bv_offset;
808c2ecf20Sopenharmony_ci		memcpy(buff, data + offset, avail);
818c2ecf20Sopenharmony_ci		buff += avail;
828c2ecf20Sopenharmony_ci		bytes -= avail;
838c2ecf20Sopenharmony_ci		offset = 0;
848c2ecf20Sopenharmony_ci	}
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_ci	res = lzo1x_decompress_safe(stream->input, (size_t)length,
878c2ecf20Sopenharmony_ci					stream->output, &out_len);
888c2ecf20Sopenharmony_ci	if (res != LZO_E_OK)
898c2ecf20Sopenharmony_ci		goto failed;
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_ci	res = bytes = (int)out_len;
928c2ecf20Sopenharmony_ci	data = squashfs_first_page(output);
938c2ecf20Sopenharmony_ci	buff = stream->output;
948c2ecf20Sopenharmony_ci	while (data) {
958c2ecf20Sopenharmony_ci		if (bytes <= PAGE_SIZE) {
968c2ecf20Sopenharmony_ci			memcpy(data, buff, bytes);
978c2ecf20Sopenharmony_ci			break;
988c2ecf20Sopenharmony_ci		} else {
998c2ecf20Sopenharmony_ci			memcpy(data, buff, PAGE_SIZE);
1008c2ecf20Sopenharmony_ci			buff += PAGE_SIZE;
1018c2ecf20Sopenharmony_ci			bytes -= PAGE_SIZE;
1028c2ecf20Sopenharmony_ci			data = squashfs_next_page(output);
1038c2ecf20Sopenharmony_ci		}
1048c2ecf20Sopenharmony_ci	}
1058c2ecf20Sopenharmony_ci	squashfs_finish_page(output);
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_ci	return res;
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_cifailed:
1108c2ecf20Sopenharmony_ci	return -EIO;
1118c2ecf20Sopenharmony_ci}
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_ciconst struct squashfs_decompressor squashfs_lzo_comp_ops = {
1148c2ecf20Sopenharmony_ci	.init = lzo_init,
1158c2ecf20Sopenharmony_ci	.free = lzo_free,
1168c2ecf20Sopenharmony_ci	.decompress = lzo_uncompress,
1178c2ecf20Sopenharmony_ci	.id = LZO_COMPRESSION,
1188c2ecf20Sopenharmony_ci	.name = "lzo",
1198c2ecf20Sopenharmony_ci	.supported = 1
1208c2ecf20Sopenharmony_ci};
121