1 /* -*- indent-tabs-mode: nil -*-
2 *
3 * Copyright 2011-2015 Kubo Takehiro <kubo@jiubao.org>
4 *
5 * Redistribution and use in source and binary forms, with or without modification, are
6 * permitted provided that the following conditions are met:
7 *
8 * 1. Redistributions of source code must retain the above copyright notice, this list of
9 * conditions and the following disclaimer.
10 *
11 * 2. Redistributions in binary form must reproduce the above copyright notice, this list
12 * of conditions and the following disclaimer in the documentation and/or other materials
13 * provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS OR IMPLIED
16 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
17 * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> OR
18 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
19 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
20 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
21 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
22 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
23 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 *
25 * The views and conclusions contained in the software and documentation are those of the
26 * authors and should not be interpreted as representing official policies, either expressed
27 * or implied, of the authors.
28 *
29 */
30 #ifdef HAVE_CONFIG_H
31 #include "config.h"
32 #endif
33
34 #include <stdlib.h>
35 #include <string.h>
36 #include <errno.h>
37 #include <snappy-c.h>
38 #ifdef WIN32
39 #include <winsock2.h>
40 #else
41 #include <arpa/inet.h>
42 #endif
43 #include "snzip.h"
44
45 #define SNAPPY_JAVA_MAGIC "\x82SNAPPY\x00"
46 #define SNAPPY_JAVA_MAGIC_LEN 8
47 #define SNAPPY_JAVA_FILE_VERSION 1
48 #define DEFAULT_BLOCK_SIZE (32 * 1024) // Use 32kb for the default block size
49
50 typedef struct {
51 char magic[SNAPPY_JAVA_MAGIC_LEN];
52 int version;
53 int compatible_version;
54 } snappy_java_header_t;
55
snappy_java_compress(FILE * infp,FILE * outfp,size_t block_size)56 static int snappy_java_compress(FILE *infp, FILE *outfp, size_t block_size)
57 {
58 snappy_java_header_t header;
59 work_buffer_t wb;
60 size_t uncompressed_length;
61 int err = 1;
62
63 wb.c = NULL;
64 wb.uc = NULL;
65
66 if (block_size == 0) {
67 block_size = DEFAULT_BLOCK_SIZE;
68 }
69
70 /* write the file header */
71 memcpy(header.magic, SNAPPY_JAVA_MAGIC, SNAPPY_JAVA_MAGIC_LEN);
72 header.version = SNZ_TO_BE32(SNAPPY_JAVA_FILE_VERSION);
73 header.compatible_version = SNZ_TO_BE32(SNAPPY_JAVA_FILE_VERSION);
74
75 if (fwrite(&header, sizeof(header), 1, outfp) != 1) {
76 print_error("Failed to write a file: %s\n", strerror(errno));
77 goto cleanup;
78 }
79
80 /* write file body */
81 work_buffer_init(&wb, block_size);
82 while ((uncompressed_length = fread(wb.uc, 1, wb.uclen, infp)) > 0) {
83 size_t compressed_length = wb.clen;
84
85 trace("read %lu bytes.\n", (unsigned long)uncompressed_length);
86
87 /* compress the block. */
88 snappy_compress(wb.uc, uncompressed_length, wb.c, &compressed_length);
89 trace("compressed_legnth is %lu.\n", (unsigned long)compressed_length);
90
91 /* write the compressed length. */
92 putc((compressed_length >> 24), outfp);
93 putc((compressed_length >> 16), outfp);
94 putc((compressed_length >> 8), outfp);
95 putc((compressed_length >> 0), outfp);
96 trace("write 4 bytes for compressed data length.\n");
97
98 /* write the compressed data. */
99 if (fwrite(wb.c, compressed_length, 1, outfp) != 1) {
100 print_error("Failed to write a file: %s\n", strerror(errno));
101 goto cleanup;
102 }
103 trace("write %ld bytes for compressed data.\n", (long)compressed_length);
104 }
105 if (!feof(infp)) {
106 /* fread() failed. */
107 print_error("Failed to read a file: %s\n", strerror(errno));
108 goto cleanup;
109 }
110 err = 0;
111 cleanup:
112 work_buffer_free(&wb);
113 return err;
114 }
115
snappy_java_uncompress(FILE * infp,FILE * outfp,int skip_magic)116 static int snappy_java_uncompress(FILE *infp, FILE *outfp, int skip_magic)
117 {
118 snappy_java_header_t header;
119 work_buffer_t wb;
120 int err = 1;
121 int outfd;
122
123 wb.c = NULL;
124 wb.uc = NULL;
125
126 if (skip_magic) {
127 /* read header except magic */
128 if (fread(&header.version, sizeof(header) - sizeof(header.magic), 1, infp) != 1) {
129 print_error("Failed to read a file: %s\n", strerror(errno));
130 goto cleanup;
131 }
132 } else {
133 /* read header */
134 if (fread(&header, sizeof(header), 1, infp) != 1) {
135 print_error("Failed to read a file: %s\n", strerror(errno));
136 goto cleanup;
137 }
138
139 /* check magic */
140 if (memcmp(header.magic, SNAPPY_JAVA_MAGIC, SNAPPY_JAVA_MAGIC_LEN) != 0) {
141 print_error("This is not a snappy-java file.\n");
142 goto cleanup;
143 }
144 }
145
146 /* check rest header */
147 header.version = SNZ_FROM_BE32(header.version);
148 if (header.version != SNAPPY_JAVA_FILE_VERSION) {
149 print_error("Unknown snappy-java version %d\n", header.version);
150 goto cleanup;
151 }
152
153 header.compatible_version = SNZ_FROM_BE32(header.compatible_version);
154 if (header.compatible_version != SNAPPY_JAVA_FILE_VERSION) {
155 print_error("Unknown snappy-java compatible version %d\n", header.compatible_version);
156 goto cleanup;
157 }
158
159 /* Use a file descriptor 'outfd' instead of the stdio file pointer 'outfp'
160 * to reduce the number of write system calls.
161 */
162 fflush(outfp);
163 outfd = fileno(outfp);
164
165 /* read body */
166 work_buffer_init(&wb, DEFAULT_BLOCK_SIZE);
167 for (;;) {
168 /* read the compressed length in a block */
169 size_t compressed_length = 0;
170 size_t uncompressed_length = wb.uclen;
171 int idx;
172
173 for (idx = 3; idx >= 0; idx--) {
174 int chr = getc(infp);
175 if (chr == -1) {
176 if (idx == 3) {
177 /* read all blocks */
178 err = 0;
179 goto cleanup;
180 }
181 print_error("Unexpected end of file.\n");
182 goto cleanup;
183 }
184 compressed_length |= (chr << (idx * 8));
185 }
186
187 trace("read 4 bytes (compressed_length = %ld)\n", (long)compressed_length);
188 if (compressed_length == 0) {
189 print_error("Invalid compressed length %ld\n", (long)compressed_length);
190 goto cleanup;
191 }
192 if (compressed_length > wb.clen) {
193 work_buffer_resize(&wb, compressed_length, 0);
194 }
195
196 /* read the compressed data */
197 if (fread(wb.c, compressed_length, 1, infp) != 1) {
198 if (feof(infp)) {
199 print_error("Unexpected end of file\n");
200 } else {
201 print_error("Failed to read a file: %s\n", strerror(errno));
202 }
203 goto cleanup;
204 }
205 trace("read %ld bytes.\n", (long)(compressed_length));
206
207 /* check the uncompressed length */
208 err = snappy_uncompressed_length(wb.c, compressed_length, &uncompressed_length);
209 if (err != 0) {
210 print_error("Invalid data: GetUncompressedLength failed %d\n", err);
211 goto cleanup;
212 }
213 err = 1;
214 if (uncompressed_length > wb.uclen) {
215 work_buffer_resize(&wb, 0, uncompressed_length);
216 }
217
218 /* uncompress and write */
219 if (snappy_uncompress(wb.c, compressed_length, wb.uc, &uncompressed_length)) {
220 print_error("Invalid data: RawUncompress failed\n");
221 goto cleanup;
222 }
223 if (write_full(outfd, wb.uc, uncompressed_length) != uncompressed_length) {
224 print_error("Failed to write a file: %s\n", strerror(errno));
225 goto cleanup;
226 }
227 trace("write %ld bytes\n", (long)uncompressed_length);
228 }
229 cleanup:
230 work_buffer_free(&wb);
231 return err;
232 }
233
234 stream_format_t snappy_java_format = {
235 "snappy-java",
236 "https://github.com/xerial/snappy-java",
237 "snappy",
238 snappy_java_compress,
239 snappy_java_uncompress,
240 };
241