15e5c12b0Sopenharmony_ci/**
25e5c12b0Sopenharmony_ci * compress.c
35e5c12b0Sopenharmony_ci *
45e5c12b0Sopenharmony_ci * Copyright (c) 2020 Google Inc.
55e5c12b0Sopenharmony_ci *   Robin Hsu <robinhsu@google.com>
65e5c12b0Sopenharmony_ci *  : add sload compression support
75e5c12b0Sopenharmony_ci *
85e5c12b0Sopenharmony_ci * This program is free software; you can redistribute it and/or modify
95e5c12b0Sopenharmony_ci * it under the terms of the GNU General Public License version 2 as
105e5c12b0Sopenharmony_ci * published by the Free Software Foundation.
115e5c12b0Sopenharmony_ci */
125e5c12b0Sopenharmony_ci
135e5c12b0Sopenharmony_ci/* for config.h for general environment (non-Android) */
145e5c12b0Sopenharmony_ci#include "f2fs.h"
155e5c12b0Sopenharmony_ci
165e5c12b0Sopenharmony_ci#include "compress.h"
175e5c12b0Sopenharmony_ci#ifdef HAVE_LIBLZO2
185e5c12b0Sopenharmony_ci#include <lzo/lzo1x.h>	/* for lzo1x_1_15_compress() */
195e5c12b0Sopenharmony_ci#endif
205e5c12b0Sopenharmony_ci#ifdef HAVE_LIBLZ4
215e5c12b0Sopenharmony_ci#include <lz4.h>	/* for LZ4_compress_fast_extState() */
225e5c12b0Sopenharmony_ci#endif
235e5c12b0Sopenharmony_ci
245e5c12b0Sopenharmony_ci/*
255e5c12b0Sopenharmony_ci * macro/constants borrowed from kernel header (GPL-2.0):
265e5c12b0Sopenharmony_ci * include/linux/lzo.h, and include/linux/lz4.h
275e5c12b0Sopenharmony_ci */
285e5c12b0Sopenharmony_ci#ifdef HAVE_LIBLZO2
295e5c12b0Sopenharmony_ci#define lzo1x_worst_compress(x)		((x) + (x) / 16 + 64 + 3 + 2)
305e5c12b0Sopenharmony_ci#define LZO_WORK_SIZE			ALIGN_UP(LZO1X_1_15_MEM_COMPRESS, 8)
315e5c12b0Sopenharmony_ci#endif
325e5c12b0Sopenharmony_ci#ifdef HAVE_LIBLZ4
335e5c12b0Sopenharmony_ci#define LZ4_MEMORY_USAGE		14
345e5c12b0Sopenharmony_ci#define LZ4_MAX_INPUT_SIZE		0x7E000000 /* 2 113 929 216 bytes */
355e5c12b0Sopenharmony_ci#define LZ4_MEM_COMPRESS		sizeof(LZ4_stream_t)
365e5c12b0Sopenharmony_ci#define LZ4_ACCELERATION_DEFAULT	1
375e5c12b0Sopenharmony_ci#define LZ4_WORK_SIZE			ALIGN_UP(LZ4_MEM_COMPRESS, 8)
385e5c12b0Sopenharmony_ci#endif
395e5c12b0Sopenharmony_ci
405e5c12b0Sopenharmony_ci#if defined(HAVE_LIBLZO2) || defined(HAVE_LIBLZ4)
415e5c12b0Sopenharmony_cistatic void reset_cc(struct compress_ctx *cc)
425e5c12b0Sopenharmony_ci{
435e5c12b0Sopenharmony_ci	memset(cc->rbuf, 0, cc->cluster_size * F2FS_BLKSIZE);
445e5c12b0Sopenharmony_ci	memset(cc->cbuf->cdata, 0, cc->cluster_size * F2FS_BLKSIZE
455e5c12b0Sopenharmony_ci			- F2FS_BLKSIZE);
465e5c12b0Sopenharmony_ci}
475e5c12b0Sopenharmony_ci#endif
485e5c12b0Sopenharmony_ci
495e5c12b0Sopenharmony_ci#ifdef HAVE_LIBLZO2
505e5c12b0Sopenharmony_cistatic void lzo_compress_init(struct compress_ctx *cc)
515e5c12b0Sopenharmony_ci{
525e5c12b0Sopenharmony_ci	size_t size = cc->cluster_size * F2FS_BLKSIZE;
535e5c12b0Sopenharmony_ci	size_t alloc = size + lzo1x_worst_compress(size)
545e5c12b0Sopenharmony_ci			+ COMPRESS_HEADER_SIZE + LZO_WORK_SIZE;
555e5c12b0Sopenharmony_ci	cc->private = malloc(alloc);
565e5c12b0Sopenharmony_ci	ASSERT(cc->private);
575e5c12b0Sopenharmony_ci	cc->rbuf = (char *) cc->private + LZO_WORK_SIZE;
585e5c12b0Sopenharmony_ci	cc->cbuf = (struct compress_data *)((char *) cc->rbuf + size);
595e5c12b0Sopenharmony_ci}
605e5c12b0Sopenharmony_ci
615e5c12b0Sopenharmony_cistatic int lzo_compress(struct compress_ctx *cc)
625e5c12b0Sopenharmony_ci{
635e5c12b0Sopenharmony_ci	int ret = lzo1x_1_15_compress(cc->rbuf, cc->rlen, cc->cbuf->cdata,
645e5c12b0Sopenharmony_ci			(lzo_uintp)(&cc->clen), cc->private);
655e5c12b0Sopenharmony_ci	cc->cbuf->clen = cpu_to_le32(cc->clen);
665e5c12b0Sopenharmony_ci	return ret;
675e5c12b0Sopenharmony_ci}
685e5c12b0Sopenharmony_ci#endif
695e5c12b0Sopenharmony_ci
705e5c12b0Sopenharmony_ci#ifdef HAVE_LIBLZ4
715e5c12b0Sopenharmony_cistatic void lz4_compress_init(struct compress_ctx *cc)
725e5c12b0Sopenharmony_ci{
735e5c12b0Sopenharmony_ci	size_t size = cc->cluster_size * F2FS_BLKSIZE;
745e5c12b0Sopenharmony_ci	size_t alloc = size + LZ4_COMPRESSBOUND(size)
755e5c12b0Sopenharmony_ci			+ COMPRESS_HEADER_SIZE + LZ4_WORK_SIZE;
765e5c12b0Sopenharmony_ci	cc->private = malloc(alloc);
775e5c12b0Sopenharmony_ci	ASSERT(cc->private);
785e5c12b0Sopenharmony_ci	cc->rbuf = (char *) cc->private + LZ4_WORK_SIZE;
795e5c12b0Sopenharmony_ci	cc->cbuf = (struct compress_data *)((char *) cc->rbuf + size);
805e5c12b0Sopenharmony_ci}
815e5c12b0Sopenharmony_ci
825e5c12b0Sopenharmony_cistatic int lz4_compress(struct compress_ctx *cc)
835e5c12b0Sopenharmony_ci{
845e5c12b0Sopenharmony_ci	cc->clen = LZ4_compress_fast_extState(cc->private, cc->rbuf,
855e5c12b0Sopenharmony_ci			(char *)cc->cbuf->cdata, cc->rlen,
865e5c12b0Sopenharmony_ci			cc->rlen - F2FS_BLKSIZE * c.compress.min_blocks -
875e5c12b0Sopenharmony_ci			COMPRESS_HEADER_SIZE,
885e5c12b0Sopenharmony_ci			LZ4_ACCELERATION_DEFAULT);
895e5c12b0Sopenharmony_ci
905e5c12b0Sopenharmony_ci	if (!cc->clen)
915e5c12b0Sopenharmony_ci		return 1;
925e5c12b0Sopenharmony_ci
935e5c12b0Sopenharmony_ci	cc->cbuf->clen = cpu_to_le32(cc->clen);
945e5c12b0Sopenharmony_ci	return 0;
955e5c12b0Sopenharmony_ci}
965e5c12b0Sopenharmony_ci#endif
975e5c12b0Sopenharmony_ci
985e5c12b0Sopenharmony_ciconst char *supported_comp_names[] = {
995e5c12b0Sopenharmony_ci	"lzo",
1005e5c12b0Sopenharmony_ci	"lz4",
1015e5c12b0Sopenharmony_ci	"",
1025e5c12b0Sopenharmony_ci};
1035e5c12b0Sopenharmony_ci
1045e5c12b0Sopenharmony_cicompress_ops supported_comp_ops[] = {
1055e5c12b0Sopenharmony_ci#ifdef HAVE_LIBLZO2
1065e5c12b0Sopenharmony_ci	{lzo_compress_init, lzo_compress, reset_cc},
1075e5c12b0Sopenharmony_ci#else
1085e5c12b0Sopenharmony_ci	{NULL, NULL, NULL},
1095e5c12b0Sopenharmony_ci#endif
1105e5c12b0Sopenharmony_ci#ifdef HAVE_LIBLZ4
1115e5c12b0Sopenharmony_ci	{lz4_compress_init, lz4_compress, reset_cc},
1125e5c12b0Sopenharmony_ci#else
1135e5c12b0Sopenharmony_ci	{NULL, NULL, NULL},
1145e5c12b0Sopenharmony_ci#endif
1155e5c12b0Sopenharmony_ci};
1165e5c12b0Sopenharmony_ci
1175e5c12b0Sopenharmony_ci/* linked list */
1185e5c12b0Sopenharmony_citypedef struct _ext_t {
1195e5c12b0Sopenharmony_ci	const char *ext;
1205e5c12b0Sopenharmony_ci	struct _ext_t *next;
1215e5c12b0Sopenharmony_ci} ext_t;
1225e5c12b0Sopenharmony_ci
1235e5c12b0Sopenharmony_cistatic ext_t *extension_list;
1245e5c12b0Sopenharmony_ci
1255e5c12b0Sopenharmony_cistatic bool ext_found(const char *ext)
1265e5c12b0Sopenharmony_ci{
1275e5c12b0Sopenharmony_ci	ext_t *p = extension_list;
1285e5c12b0Sopenharmony_ci
1295e5c12b0Sopenharmony_ci	while (p != NULL && strcmp(ext, p->ext))
1305e5c12b0Sopenharmony_ci		p = p->next;
1315e5c12b0Sopenharmony_ci	return (p != NULL);
1325e5c12b0Sopenharmony_ci}
1335e5c12b0Sopenharmony_ci
1345e5c12b0Sopenharmony_cistatic const char *get_ext(const char *path)
1355e5c12b0Sopenharmony_ci{
1365e5c12b0Sopenharmony_ci	char *p = strrchr(path, '.');
1375e5c12b0Sopenharmony_ci
1385e5c12b0Sopenharmony_ci	return p == NULL ? path + strlen(path) : p + 1;
1395e5c12b0Sopenharmony_ci}
1405e5c12b0Sopenharmony_ci
1415e5c12b0Sopenharmony_cistatic bool ext_do_filter(const char *path)
1425e5c12b0Sopenharmony_ci{
1435e5c12b0Sopenharmony_ci	return (ext_found(get_ext(path)) == true) ^
1445e5c12b0Sopenharmony_ci		(c.compress.filter == COMPR_FILTER_ALLOW);
1455e5c12b0Sopenharmony_ci}
1465e5c12b0Sopenharmony_ci
1475e5c12b0Sopenharmony_cistatic void ext_filter_add(const char *ext)
1485e5c12b0Sopenharmony_ci{
1495e5c12b0Sopenharmony_ci	ext_t *node;
1505e5c12b0Sopenharmony_ci
1515e5c12b0Sopenharmony_ci	ASSERT(ext != NULL);
1525e5c12b0Sopenharmony_ci	if (ext_found(ext))
1535e5c12b0Sopenharmony_ci		return; /* ext was already registered */
1545e5c12b0Sopenharmony_ci	node = malloc(sizeof(ext_t));
1555e5c12b0Sopenharmony_ci	ASSERT(node != NULL);
1565e5c12b0Sopenharmony_ci	node->ext = ext;
1575e5c12b0Sopenharmony_ci	node->next = extension_list;
1585e5c12b0Sopenharmony_ci	extension_list = node;
1595e5c12b0Sopenharmony_ci}
1605e5c12b0Sopenharmony_ci
1615e5c12b0Sopenharmony_cistatic void ext_filter_destroy(void)
1625e5c12b0Sopenharmony_ci{
1635e5c12b0Sopenharmony_ci	ext_t *p;
1645e5c12b0Sopenharmony_ci
1655e5c12b0Sopenharmony_ci	while (extension_list != NULL) {
1665e5c12b0Sopenharmony_ci		p = extension_list;
1675e5c12b0Sopenharmony_ci		extension_list = p->next;
1685e5c12b0Sopenharmony_ci		free(p);
1695e5c12b0Sopenharmony_ci	}
1705e5c12b0Sopenharmony_ci}
1715e5c12b0Sopenharmony_ci
1725e5c12b0Sopenharmony_cifilter_ops ext_filter = {
1735e5c12b0Sopenharmony_ci	.add = ext_filter_add,
1745e5c12b0Sopenharmony_ci	.destroy = ext_filter_destroy,
1755e5c12b0Sopenharmony_ci	.filter = ext_do_filter,
1765e5c12b0Sopenharmony_ci};
177