1 // SPDX-License-Identifier: GPL-2.0+ OR Apache-2.0
2 /*
3 * Copyright (C) 2021 Gao Xiang <[email protected]>
4 */
5 #include <stdlib.h>
6 #include "config.h"
7 #ifdef HAVE_LIBLZMA
8 #include <lzma.h>
9 #include "erofs/config.h"
10 #include "erofs/print.h"
11 #include "erofs/internal.h"
12 #include "erofs/atomic.h"
13 #include "compressor.h"
14
15 struct erofs_liblzma_context {
16 lzma_options_lzma opt;
17 lzma_stream strm;
18 };
19
erofs_liblzma_compress_destsize(const struct erofs_compress * c,const void * src,unsigned int * srcsize,void * dst,unsigned int dstsize)20 static int erofs_liblzma_compress_destsize(const struct erofs_compress *c,
21 const void *src, unsigned int *srcsize,
22 void *dst, unsigned int dstsize)
23 {
24 struct erofs_liblzma_context *ctx = c->private_data;
25 lzma_stream *strm = &ctx->strm;
26
27 lzma_ret ret = lzma_microlzma_encoder(strm, &ctx->opt);
28 if (ret != LZMA_OK)
29 return -EFAULT;
30
31 strm->next_in = src;
32 strm->avail_in = *srcsize;
33 strm->next_out = dst;
34 strm->avail_out = dstsize;
35
36 ret = lzma_code(strm, LZMA_FINISH);
37 if (ret != LZMA_STREAM_END)
38 return -EBADMSG;
39
40 *srcsize = strm->total_in;
41 return strm->total_out;
42 }
43
erofs_compressor_liblzma_exit(struct erofs_compress * c)44 static int erofs_compressor_liblzma_exit(struct erofs_compress *c)
45 {
46 struct erofs_liblzma_context *ctx = c->private_data;
47
48 if (!ctx)
49 return -EINVAL;
50
51 lzma_end(&ctx->strm);
52 free(ctx);
53 return 0;
54 }
55
erofs_compressor_liblzma_setlevel(struct erofs_compress * c,int compression_level)56 static int erofs_compressor_liblzma_setlevel(struct erofs_compress *c,
57 int compression_level)
58 {
59 if (compression_level < 0)
60 compression_level = erofs_compressor_lzma.default_level;
61
62 if (compression_level > erofs_compressor_lzma.best_level) {
63 erofs_err("invalid compression level %d", compression_level);
64 return -EINVAL;
65 }
66 c->compression_level = compression_level;
67 return 0;
68 }
69
erofs_compressor_liblzma_setdictsize(struct erofs_compress * c,u32 dict_size)70 static int erofs_compressor_liblzma_setdictsize(struct erofs_compress *c,
71 u32 dict_size)
72 {
73 if (!dict_size) {
74 if (erofs_compressor_lzma.default_dictsize) {
75 dict_size = erofs_compressor_lzma.default_dictsize;
76 } else {
77 dict_size = min_t(u32, Z_EROFS_LZMA_MAX_DICT_SIZE,
78 cfg.c_mkfs_pclustersize_max << 3);
79 if (dict_size < 32768)
80 dict_size = 32768;
81 }
82 }
83
84 if (dict_size > Z_EROFS_LZMA_MAX_DICT_SIZE || dict_size < 4096) {
85 erofs_err("invalid dictionary size %u", dict_size);
86 return -EINVAL;
87 }
88 c->dict_size = dict_size;
89 return 0;
90 }
91
erofs_compressor_liblzma_init(struct erofs_compress * c)92 static int erofs_compressor_liblzma_init(struct erofs_compress *c)
93 {
94 struct erofs_liblzma_context *ctx;
95 u32 preset;
96
97 ctx = malloc(sizeof(*ctx));
98 if (!ctx)
99 return -ENOMEM;
100 ctx->strm = (lzma_stream)LZMA_STREAM_INIT;
101
102 if (c->compression_level < 0)
103 preset = LZMA_PRESET_DEFAULT;
104 else if (c->compression_level >= 100)
105 preset = (c->compression_level - 100) | LZMA_PRESET_EXTREME;
106 else
107 preset = c->compression_level;
108
109 if (lzma_lzma_preset(&ctx->opt, preset))
110 return -EINVAL;
111 ctx->opt.dict_size = c->dict_size;
112
113 c->private_data = ctx;
114 return 0;
115 }
116
117 const struct erofs_compressor erofs_compressor_lzma = {
118 .default_level = LZMA_PRESET_DEFAULT,
119 .best_level = 109,
120 .max_dictsize = Z_EROFS_LZMA_MAX_DICT_SIZE,
121 .init = erofs_compressor_liblzma_init,
122 .exit = erofs_compressor_liblzma_exit,
123 .setlevel = erofs_compressor_liblzma_setlevel,
124 .setdictsize = erofs_compressor_liblzma_setdictsize,
125 .compress_destsize = erofs_liblzma_compress_destsize,
126 };
127 #endif
128