1 /*
2 * Copyright (c) 2010, 2013
3 * Phillip Lougher <phillip@squashfs.org.uk>
4 *
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License
7 * as published by the Free Software Foundation; either version 2,
8 * or (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18 *
19 * lzma_xz_wrapper.c
20 *
21 * Support for LZMA1 compression using XZ Utils liblzma http://tukaani.org/xz/
22 */
23
24 #include <stdio.h>
25 #include <string.h>
26 #include <lzma.h>
27
28 #include "squashfs_fs.h"
29 #include "compressor.h"
30
31 #define LZMA_PROPS_SIZE 5
32 #define LZMA_UNCOMP_SIZE 8
33 #define LZMA_HEADER_SIZE (LZMA_PROPS_SIZE + LZMA_UNCOMP_SIZE)
34
35 #define LZMA_OPTIONS 5
36 #define MEMLIMIT (32 * 1024 * 1024)
37
lzma_compress(void * dummy,void * dest,void * src,int size,int block_size,int * error)38 static int lzma_compress(void *dummy, void *dest, void *src, int size,
39 int block_size, int *error)
40 {
41 unsigned char *d = (unsigned char *) dest;
42 lzma_options_lzma opt;
43 lzma_stream strm = LZMA_STREAM_INIT;
44 int res;
45
46 lzma_lzma_preset(&opt, LZMA_OPTIONS);
47 opt.dict_size = block_size;
48
49 res = lzma_alone_encoder(&strm, &opt);
50 if(res != LZMA_OK) {
51 lzma_end(&strm);
52 goto failed;
53 }
54
55 strm.next_out = dest;
56 strm.avail_out = block_size;
57 strm.next_in = src;
58 strm.avail_in = size;
59
60 res = lzma_code(&strm, LZMA_FINISH);
61 lzma_end(&strm);
62
63 if(res == LZMA_STREAM_END) {
64 /*
65 * Fill in the 8 byte little endian uncompressed size field in
66 * the LZMA header. 8 bytes is excessively large for squashfs
67 * but this is the standard LZMA header and which is expected by
68 * the kernel code
69 */
70
71 d[LZMA_PROPS_SIZE] = size & 255;
72 d[LZMA_PROPS_SIZE + 1] = (size >> 8) & 255;
73 d[LZMA_PROPS_SIZE + 2] = (size >> 16) & 255;
74 d[LZMA_PROPS_SIZE + 3] = (size >> 24) & 255;
75 d[LZMA_PROPS_SIZE + 4] = 0;
76 d[LZMA_PROPS_SIZE + 5] = 0;
77 d[LZMA_PROPS_SIZE + 6] = 0;
78 d[LZMA_PROPS_SIZE + 7] = 0;
79
80 return (int) strm.total_out;
81 }
82
83 if(res == LZMA_OK)
84 /*
85 * Output buffer overflow. Return out of buffer space
86 */
87 return 0;
88
89 failed:
90 /*
91 * All other errors return failure, with the compressor
92 * specific error code in *error
93 */
94 *error = res;
95 return -1;
96 }
97
98
lzma_uncompress(void * dest,void * src,int size,int outsize,int * error)99 static int lzma_uncompress(void *dest, void *src, int size, int outsize,
100 int *error)
101 {
102 lzma_stream strm = LZMA_STREAM_INIT;
103 int uncompressed_size = 0, res;
104 unsigned char lzma_header[LZMA_HEADER_SIZE];
105
106 res = lzma_alone_decoder(&strm, MEMLIMIT);
107 if(res != LZMA_OK) {
108 lzma_end(&strm);
109 goto failed;
110 }
111
112 memcpy(lzma_header, src, LZMA_HEADER_SIZE);
113 uncompressed_size = lzma_header[LZMA_PROPS_SIZE] |
114 (lzma_header[LZMA_PROPS_SIZE + 1] << 8) |
115 (lzma_header[LZMA_PROPS_SIZE + 2] << 16) |
116 (lzma_header[LZMA_PROPS_SIZE + 3] << 24);
117
118 if(uncompressed_size > outsize) {
119 res = 0;
120 goto failed;
121 }
122
123 memset(lzma_header + LZMA_PROPS_SIZE, 255, LZMA_UNCOMP_SIZE);
124
125 strm.next_out = dest;
126 strm.avail_out = outsize;
127 strm.next_in = lzma_header;
128 strm.avail_in = LZMA_HEADER_SIZE;
129
130 res = lzma_code(&strm, LZMA_RUN);
131
132 if(res != LZMA_OK || strm.avail_in != 0) {
133 lzma_end(&strm);
134 goto failed;
135 }
136
137 strm.next_in = src + LZMA_HEADER_SIZE;
138 strm.avail_in = size - LZMA_HEADER_SIZE;
139
140 res = lzma_code(&strm, LZMA_FINISH);
141 lzma_end(&strm);
142
143 if(res == LZMA_STREAM_END || (res == LZMA_OK &&
144 strm.total_out >= uncompressed_size && strm.avail_in == 0))
145 return uncompressed_size;
146
147 failed:
148 *error = res;
149 return -1;
150 }
151
152
153 struct compressor lzma_comp_ops = {
154 .init = NULL,
155 .compress = lzma_compress,
156 .uncompress = lzma_uncompress,
157 .options = NULL,
158 .usage = NULL,
159 .id = LZMA_COMPRESSION,
160 .name = "lzma",
161 .supported = 1
162 };
163
164