1 /*
2 * Copyright 2018 Jonathan Dieter <jdieter@gmail.com>
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are met:
6 *
7 * 1. Redistributions of source code must retain the above copyright notice,
8 * this list of conditions and the following disclaimer.
9 *
10 * 2. Redistributions in binary form must reproduce the above copyright notice,
11 * this list of conditions and the following disclaimer in the documentation
12 * and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
15 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
18 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
19 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
20 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
21 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
22 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
23 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
24 * POSSIBILITY OF SUCH DAMAGE.
25 */
26
27 #include <stdlib.h>
28 #include <stdio.h>
29 #include <stdint.h>
30 #include <stdbool.h>
31 #include <string.h>
32 #include <math.h>
33 #include <zck.h>
34
35 #include "zck_private.h"
36 #include "comp/nocomp/nocomp.h"
37 #ifdef ZCHUNK_ZSTD
38 #include "comp/zstd/zstd.h"
39 #endif
40
41 #define BLK_SIZE 32768
42
43 static char unknown[] = "Unknown(\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0";
44
45 const static char *COMP_NAME[] = {
46 "no",
47 "Unknown (1)",
48 "zstd"
49 };
50
update_buzhash_bits(zckCtx * zck)51 static void update_buzhash_bits(zckCtx *zck) {
52 int s=1;
53 for(int i=0; i<zck->buzhash_match_bits; i++)
54 s *= 2;
55 s -= 1;
56 zck->buzhash_bitmask = s;
57 }
58
set_comp_type(zckCtx * zck,ssize_t type)59 static bool set_comp_type(zckCtx *zck, ssize_t type) {
60 VALIDATE_BOOL(zck);
61
62 zckComp *comp = &(zck->comp);
63
64 /* Cannot change compression type after compression has started */
65 if(comp->started) {
66 set_error(zck, "Unable to set compression type after initialization");
67 return false;
68 }
69
70 /* Set all values to 0 before setting compression type */
71 char *dc_data = comp->dc_data;
72 size_t dc_data_loc = comp->dc_data_loc;
73 size_t dc_data_size = comp->dc_data_size;
74 memset(comp, 0, sizeof(zckComp));
75 comp->dc_data = dc_data;
76 comp->dc_data_loc = dc_data_loc;
77 comp->dc_data_size = dc_data_size;
78
79 zck_log(ZCK_LOG_DEBUG, "Setting compression to %s",
80 zck_comp_name_from_type(type));
81 if(type == ZCK_COMP_NONE) {
82 return nocomp_setup(zck, comp);
83 #ifdef ZCHUNK_ZSTD
84 } else if(type == ZCK_COMP_ZSTD) {
85 return zstd_setup(zck, comp);
86 #endif
87 } else {
88 set_error(zck, "Unsupported compression type: %s",
89 zck_comp_name_from_type(type));
90 return false;
91 }
92 return true;
93 }
94
comp_read_from_dc(zckCtx * zck,zckComp * comp,char * dst,size_t dst_size)95 static size_t comp_read_from_dc(zckCtx *zck, zckComp *comp, char *dst,
96 size_t dst_size) {
97 VALIDATE_INT(zck);
98 ALLOCD_INT(zck, comp);
99 ALLOCD_INT(zck, dst);
100
101 size_t dl_size = dst_size;
102 if(dl_size > comp->dc_data_size - comp->dc_data_loc)
103 dl_size = comp->dc_data_size - comp->dc_data_loc;
104 memcpy(dst, comp->dc_data+comp->dc_data_loc, dl_size);
105 comp->dc_data_loc += dl_size;
106 if(dl_size > 0)
107 zck_log(ZCK_LOG_DEBUG, "Reading %lu bytes from decompressed buffer",
108 dl_size);
109 return dl_size;
110 }
111
comp_add_to_data(zckCtx * zck,zckComp * comp,const char * src,size_t src_size)112 static bool comp_add_to_data(zckCtx *zck, zckComp *comp, const char *src,
113 size_t src_size) {
114 VALIDATE_BOOL(zck);
115 ALLOCD_BOOL(zck, comp);
116 ALLOCD_BOOL(zck, src);
117
118 comp->data = zrealloc(comp->data, comp->data_size + src_size);
119 zck_log(ZCK_LOG_DEBUG, "Adding %lu bytes to compressed buffer",
120 src_size);
121 memcpy(comp->data + comp->data_size, src, src_size);
122 comp->data_size += src_size;
123 comp->data_loc += src_size;
124 return true;
125 }
126
comp_end_dchunk(zckCtx * zck,bool use_dict,size_t fd_size)127 static ssize_t comp_end_dchunk(zckCtx *zck, bool use_dict, size_t fd_size) {
128 VALIDATE_READ_INT(zck);
129
130 ssize_t rb = zck->comp.end_dchunk(zck, &(zck->comp), use_dict, fd_size);
131 if(validate_current_chunk(zck) < 1)
132 return -1;
133 zck->comp.data_loc = 0;
134 zck->comp.data_idx = zck->comp.data_idx->next;
135 if(!hash_init(zck, &(zck->check_chunk_hash), &(zck->chunk_hash_type)))
136 return -1;
137 return rb;
138 }
139
comp_write(zckCtx * zck,const char * src,const size_t src_size)140 static ssize_t comp_write(zckCtx *zck, const char *src, const size_t src_size) {
141 VALIDATE_WRITE_INT(zck);
142
143 if(src_size == 0)
144 return 0;
145
146 char *dst = NULL;
147 size_t dst_size = 0;
148 if(zck->comp.compress(zck, &(zck->comp), src, src_size, &dst,
149 &dst_size, 1) < 0)
150 return -1;
151 zck->comp.dc_data_size += src_size;
152
153 if(dst_size > 0 && !write_data(zck, zck->temp_fd, dst, dst_size)) {
154 free(dst);
155 return -1;
156 }
157 if(!index_add_to_chunk(zck, dst, dst_size, src_size)) {
158 free(dst);
159 return -1;
160 }
161 free(dst);
162 return src_size;
163 }
164
comp_init(zckCtx * zck)165 bool comp_init(zckCtx *zck) {
166 VALIDATE_BOOL(zck);
167
168 zckComp *comp = &(zck->comp);
169
170 if(zck->comp.started) {
171 set_error(zck, "Compression already initialized");
172 return false;
173 }
174 if((zck->comp.dict && zck->comp.dict_size == 0) ||
175 (zck->comp.dict == NULL && zck->comp.dict_size > 0)) {
176 set_error(zck, "Invalid dictionary configuration");
177 return false;
178 }
179 zck_log(ZCK_LOG_DEBUG, "Initializing %s compression",
180 zck_comp_name_from_type(comp->type));
181 if(!zck->comp.init(zck, &(zck->comp)))
182 return false;
183 if(zck->mode == ZCK_MODE_WRITE) {
184 if(zck->chunk_min_size == 0) {
185 zck->chunk_min_size = CHUNK_DEFAULT_MIN;
186 zck_log(ZCK_LOG_DEBUG, "Using default minimum chunk size of %lu",
187 zck->chunk_min_size);
188 }
189 if(zck->chunk_max_size == 0) {
190 zck->chunk_max_size = CHUNK_DEFAULT_MAX;
191 zck_log(ZCK_LOG_DEBUG, "Using default maximum chunk size of %lu",
192 zck->chunk_max_size);
193 }
194 if(zck->manual_chunk == 0) {
195 zck_log(ZCK_LOG_DEBUG, "Using buzhash algorithm for chunking");
196 zck->buzhash_width = DEFAULT_BUZHASH_WIDTH;
197 zck->buzhash_match_bits = DEFAULT_BUZHASH_BITS;
198 update_buzhash_bits(zck);
199 zck_log(ZCK_LOG_DEBUG, "Setting average chunk size to %lu",
200 zck->buzhash_bitmask + 1);
201 zck->chunk_auto_min = (zck->buzhash_bitmask + 1) / 4;
202 if(zck->chunk_auto_min < zck->chunk_min_size)
203 zck->chunk_auto_min = zck->chunk_min_size;
204 zck_log(ZCK_LOG_DEBUG, "Setting automatic minimum chunk size to %lu",
205 zck->chunk_auto_min);
206 zck->chunk_auto_max = (zck->buzhash_bitmask + 1) * 4;
207 if(zck->chunk_auto_max > zck->chunk_max_size)
208 zck->chunk_auto_max = zck->chunk_max_size;
209 zck_log(ZCK_LOG_DEBUG, "Setting automatic maximum chunk size to %lu",
210 zck->chunk_auto_max);
211 }
212 }
213
214 if(zck->temp_fd) {
215 if(zck->comp.dict) {
216 char *dst = NULL;
217 size_t dst_size = 0;
218
219 if(zck->comp.compress(zck, comp, zck->comp.dict,
220 zck->comp.dict_size, &dst, &dst_size, 0) < 0)
221 return false;
222 zck->comp.dc_data_size = zck->comp.dict_size;
223 if(!write_data(zck, zck->temp_fd, dst, dst_size)) {
224 free(dst);
225 return false;
226 }
227 if(!index_add_to_chunk(zck, dst, dst_size,
228 zck->comp.dict_size)) {
229 free(dst);
230 return false;
231 }
232 free(dst);
233 dst = NULL;
234 dst_size = 0;
235
236 if(!zck->comp.end_cchunk(zck, comp, &dst, &dst_size, 0))
237 return false;
238 zck->comp.dc_data_size = 0;
239 if(!write_data(zck, zck->temp_fd, dst, dst_size)) {
240 free(dst);
241 return false;
242 }
243 if(!index_add_to_chunk(zck, dst, dst_size, 0) ||
244 !index_finish_chunk(zck)) {
245 free(dst);
246 return false;
247 }
248 free(dst);
249 } else {
250 if(!index_finish_chunk(zck))
251 return false;
252 }
253 }
254 zck->comp.started = true;
255 return true;
256 }
257
comp_reset(zckCtx * zck)258 bool comp_reset(zckCtx *zck) {
259 ALLOCD_BOOL(zck, zck);
260
261 zck->comp.started = 0;
262 if(zck->comp.dc_data) {
263 free(zck->comp.dc_data);
264 zck->comp.dc_data = NULL;
265 zck->comp.dc_data_loc = 0;
266 zck->comp.dc_data_size = 0;
267 }
268 if(zck->comp.close == NULL)
269 return true;
270 return zck->comp.close(zck, &(zck->comp));
271 }
272
comp_reset_comp_data(zckCtx * zck)273 bool comp_reset_comp_data(zckCtx *zck) {
274 ALLOCD_BOOL(zck, zck);
275
276 if(zck->comp.data) {
277 free(zck->comp.data);
278 zck->comp.data = NULL;
279 zck->comp.data_size = 0;
280 zck->comp.data_loc = 0;
281 zck->comp.data_idx = NULL;
282 }
283 return true;
284 }
285
comp_close(zckCtx * zck)286 bool comp_close(zckCtx *zck) {
287 ALLOCD_BOOL(zck, zck);
288
289 zck_log(ZCK_LOG_DEBUG, "Closing compression");
290 comp_reset_comp_data(zck);
291 if(zck->comp.dict)
292 free(zck->comp.dict);
293 zck->comp.dict = NULL;
294 zck->comp.dict_size = 0;
295
296 return comp_reset(zck);
297 }
298
comp_ioption(zckCtx * zck,zck_ioption option,ssize_t value)299 bool comp_ioption(zckCtx *zck, zck_ioption option, ssize_t value) {
300 VALIDATE_BOOL(zck);
301
302 /* Cannot change compression parameters after compression has started */
303 if(zck && zck->comp.started) {
304 set_error(zck,
305 "Unable to set compression parameters after initialization");
306 return false;
307 }
308 if(option == ZCK_COMP_TYPE) {
309 return set_comp_type(zck, value);
310
311 /* Manual chunking */
312 } else if(option == ZCK_MANUAL_CHUNK) {
313 VALIDATE_WRITE_BOOL(zck);
314 if(value != 0) {
315 zck_log(ZCK_LOG_DEBUG, "Disabling automatic chunking");
316 zck->manual_chunk = 1;
317 } else {
318 zck_log(ZCK_LOG_DEBUG, "Enabling automatic chunking");
319 zck->manual_chunk = 0;
320 }
321 return true;
322
323 /* Minimum chunk size */
324 } else if(option == ZCK_CHUNK_MIN) {
325 VALIDATE_WRITE_BOOL(zck);
326 if(value < 1) {
327 set_error(zck, "Minimum chunk size must be > 0");
328 return false;
329 }
330 if(value > zck->chunk_max_size) {
331 set_error(zck, "Minimum chunk size must be <= maximum chunk size");
332 return false;
333 }
334 zck->chunk_min_size = value;
335 zck_log(ZCK_LOG_DEBUG, "Setting minimum chunk size to %li", value);
336 return true;
337
338 /* Maximum chunk size */
339 } else if(option == ZCK_CHUNK_MAX) {
340 VALIDATE_WRITE_BOOL(zck);
341 if(value < 1) {
342 set_error(zck, "Maximum chunk size must be > 0");
343 return false;
344 }
345 if(value < zck->chunk_min_size) {
346 set_error(zck, "Maximum chunk size must be >= minimum chunk size");
347 return false;
348 }
349 zck->chunk_max_size = value;
350 zck_log(ZCK_LOG_DEBUG, "Setting maximum chunk size to %li", value);
351 return true;
352
353 } else {
354 if(zck && zck->comp.set_parameter)
355 return zck->comp.set_parameter(zck, &(zck->comp), option, &value);
356
357 set_error(zck, "Unsupported compression parameter: %i",
358 option);
359 return false;
360 }
361 return true;
362 }
363
comp_soption(zckCtx * zck,zck_soption option,const void * value,size_t length)364 bool comp_soption(zckCtx *zck, zck_soption option, const void *value,
365 size_t length) {
366 VALIDATE_BOOL(zck);
367
368 /* Cannot change compression parameters after compression has started */
369 if(zck && zck->comp.started) {
370 set_error(zck,
371 "Unable to set compression parameters after initialization");
372 return false;
373 }
374 if(option == ZCK_COMP_DICT) {
375 zck->comp.dict = (char *)value;
376 zck->comp.dict_size = length;
377 } else {
378 if(zck && zck->comp.set_parameter)
379 return zck->comp.set_parameter(zck, &(zck->comp), option, value);
380
381 set_error(zck, "Unsupported compression parameter: %i", option);
382 return false;
383 }
384 return true;
385 }
386
comp_add_to_dc(zckCtx * zck,zckComp * comp,const char * src,size_t src_size)387 bool comp_add_to_dc(zckCtx *zck, zckComp *comp, const char *src,
388 size_t src_size) {
389 VALIDATE_BOOL(zck);
390 ALLOCD_BOOL(zck, comp);
391 ALLOCD_BOOL(zck, src);
392
393 /* Get rid of any already read data and allocate space for new data */
394 char *temp = zmalloc(comp->dc_data_size - comp->dc_data_loc + src_size);
395 if(comp->dc_data_loc != 0)
396 zck_log(ZCK_LOG_DEBUG, "Freeing %lu bytes from decompressed buffer",
397 comp->dc_data_loc);
398 zck_log(ZCK_LOG_DEBUG, "Adding %lu bytes to decompressed buffer",
399 src_size);
400 memcpy(temp, comp->dc_data + comp->dc_data_loc,
401 comp->dc_data_size - comp->dc_data_loc);
402 free(comp->dc_data);
403 comp->dc_data_size -= comp->dc_data_loc;
404 comp->dc_data_loc = 0;
405 comp->dc_data = temp;
406
407 /* Copy new uncompressed data into comp */
408 memcpy(comp->dc_data + comp->dc_data_size, src, src_size);
409 comp->dc_data_size += src_size;
410 return true;
411 }
412
comp_read(zckCtx * zck,char * dst,size_t dst_size,bool use_dict)413 ssize_t comp_read(zckCtx *zck, char *dst, size_t dst_size, bool use_dict) {
414 VALIDATE_READ_INT(zck);
415
416 if(!zck->comp.started) {
417 set_error(zck, "Compression hasn't been initialized yet");
418 return -1;
419 }
420
421 if(dst_size == 0)
422 return 0;
423
424 /* Read dictionary if it exists and hasn't been read yet */
425 if(use_dict && zck->index.first->length > 0 && zck->comp.dict == NULL &&
426 !import_dict(zck))
427 return -1;
428
429 size_t dc = 0;
430 char *src = zmalloc(dst_size - dc);
431 bool finished_rd = false;
432 bool finished_dc = false;
433 zck_log(ZCK_LOG_DEBUG, "Trying to read %lu bytes", dst_size);
434 while(dc < dst_size) {
435 /* Get bytes from decompressed buffer */
436 ssize_t rb = comp_read_from_dc(zck, &(zck->comp), dst+dc, dst_size-dc);
437 if(rb < 0)
438 goto read_error;
439 dc += rb;
440 if(dc == dst_size)
441 break;
442 if(rb > 0)
443 continue;
444 if(finished_dc || zck->comp.data_eof)
445 break;
446
447 /* Decompress compressed buffer into decompressed buffer */
448 size_t dc_data_size = zck->comp.dc_data_size;
449 size_t dc_data_loc = zck->comp.dc_data_loc;
450 if(zck->comp.data_size > 0 &&
451 !zck->comp.decompress(zck, &(zck->comp), use_dict))
452 goto read_error;
453
454 /* Check whether we decompressed more data */
455 if(zck->comp.dc_data_size != dc_data_size ||
456 zck->comp.dc_data_loc != dc_data_loc)
457 continue;
458
459 /* End decompression chunk if we're on a chunk boundary */
460 if(zck->comp.data_idx == NULL) {
461 zck->comp.data_idx = zck->index.first;
462 /* Skip first chunk if it's an empty dict */
463 if(zck->comp.data_idx->comp_length == 0)
464 zck->comp.data_idx = zck->comp.data_idx->next;
465 if(!hash_init(zck, &(zck->check_chunk_hash),
466 &(zck->chunk_hash_type)))
467 goto hash_error;
468 if(zck->comp.data_loc > 0) {
469 if(!hash_update(zck, &(zck->check_full_hash), zck->comp.data,
470 zck->comp.data_loc))
471 goto hash_error;
472 if(!hash_update(zck, &(zck->check_chunk_hash), zck->comp.data,
473 zck->comp.data_loc))
474 goto hash_error;
475 }
476 if(zck->comp.data_idx == NULL) {
477 free(src);
478 return 0;
479 }
480 }
481 if(zck->comp.data_loc == zck->comp.data_idx->comp_length) {
482 if(!comp_end_dchunk(zck, use_dict, zck->comp.data_idx->length)) {
483 free(src);
484 return -1;
485 }
486 if(zck->comp.data_idx == NULL)
487 zck->comp.data_eof = true;
488 continue;
489 }
490
491 /* If we finished reading and we've reached here, we're done
492 * decompressing */
493 if(finished_rd) {
494 finished_dc = true;
495 continue;
496 }
497
498 /* Make sure we don't read beyond current chunk length */
499 size_t rs = dst_size;
500 if(zck->comp.data_loc + rs > zck->comp.data_idx->comp_length)
501 rs = zck->comp.data_idx->comp_length - zck->comp.data_loc;
502
503 /* Decompressed buffer is empty, so read data from file and fill
504 * compressed buffer */
505 rb = read_data(zck, src, rs);
506 if(rb < 0)
507 goto read_error;
508 if(rb < rs) {
509 zck_log(ZCK_LOG_DDEBUG, "EOF");
510 finished_rd = true;
511 }
512 if(zck->check_chunk_hash.ctx == NULL)
513 if(!hash_init(zck, &(zck->check_chunk_hash),
514 &(zck->chunk_hash_type)))
515 goto hash_error;
516 if(!hash_update(zck, &(zck->check_full_hash), src, rb) ||
517 !hash_update(zck, &(zck->check_chunk_hash), src, rb) ||
518 !comp_add_to_data(zck, &(zck->comp), src, rb))
519 goto read_error;
520 }
521 free(src);
522 return dc;
523 read_error:
524 free(src);
525 return -1;
526 hash_error:
527 free(src);
528 return -2;
529 }
530
zck_comp_name_from_type(int comp_type)531 const char PUBLIC *zck_comp_name_from_type(int comp_type) {
532 if(comp_type > 2) {
533 snprintf(unknown+8, 21, "%i)", comp_type);
534 return unknown;
535 }
536 return COMP_NAME[comp_type];
537 }
538
zck_write(zckCtx * zck,const char * src,const size_t src_size)539 ssize_t PUBLIC zck_write(zckCtx *zck, const char *src, const size_t src_size) {
540 VALIDATE_WRITE_INT(zck);
541
542 zck_log(ZCK_LOG_DDEBUG, "Starting up");
543
544 if(src_size == 0)
545 return 0;
546
547 if(!zck->comp.started && !comp_init(zck))
548 return -1;
549
550 zck_log(ZCK_LOG_DDEBUG, "Starting up");
551
552 const char *loc = src;
553 size_t loc_size = src_size;
554 size_t loc_written = 0;
555
556 if(zck->manual_chunk) {
557 while(zck->comp.dc_data_size + loc_size > zck->chunk_max_size) {
558 loc_written = zck->chunk_max_size - zck->comp.dc_data_size;
559 if(comp_write(zck, loc, loc_written) != loc_written)
560 return -1;
561 loc_size -= loc_written;
562 loc += loc_written;
563 zck_log(ZCK_LOG_DDEBUG,
564 "Chunk has reached maximum size, forcing a new chunk");
565 if(zck_end_chunk(zck) < 0)
566 return -1;
567 }
568 if(comp_write(zck, loc, loc_size) != loc_size)
569 return -1;
570 else
571 return src_size;
572 } else {
573 for(size_t i=0; i<loc_size; ) {
574 if((buzhash_update(&(zck->buzhash), loc+i, zck->buzhash_width) &
575 zck->buzhash_bitmask) == 0 ||
576 zck->comp.dc_data_size + i >= zck->chunk_auto_max) {
577 if(comp_write(zck, loc, i) != i)
578 return -1;
579 loc += i;
580 loc_size -= i;
581 i = 0;
582 if(zck->comp.dc_data_size >= zck->chunk_max_size)
583 zck_log(ZCK_LOG_DDEBUG,
584 "Chunk has reached maximum size, forcing a new "
585 "chunk");
586 else
587 zck_log(ZCK_LOG_DDEBUG, "Automatically ending chunk");
588 if(zck->comp.dc_data_size < zck->chunk_auto_min) {
589 zck_log(ZCK_LOG_DDEBUG,
590 "Chunk too small, refusing to end chunk");
591 continue;
592 }
593 if(zck_end_chunk(zck) < 0)
594 return -1;
595 } else {
596 i++;
597 }
598 }
599 if(loc_size > 0 && comp_write(zck, loc, loc_size) != loc_size)
600 return -1;
601 return src_size;
602 }
603 }
604
zck_end_chunk(zckCtx * zck)605 ssize_t PUBLIC zck_end_chunk(zckCtx *zck) {
606 VALIDATE_WRITE_INT(zck);
607
608 if(!zck->comp.started && !comp_init(zck))
609 return -1;
610
611 if(zck->comp.dc_data_size < zck->chunk_min_size) {
612 zck_log(ZCK_LOG_DDEBUG, "Chunk too small, refusing to end chunk");
613 return zck->comp.dc_data_size;
614 }
615
616 buzhash_reset(&(zck->buzhash));
617 /* No point in compressing empty data */
618 if(zck->comp.dc_data_size == 0)
619 return 0;
620
621 size_t data_size = zck->comp.dc_data_size;
622 char *dst = NULL;
623 size_t dst_size = 0;
624 if(!zck->comp.end_cchunk(zck, &(zck->comp), &dst, &dst_size, 1))
625 return -1;
626 zck->comp.dc_data_size = 0;
627 if(dst_size > 0 && !write_data(zck, zck->temp_fd, dst, dst_size)) {
628 free(dst);
629 return -1;
630 }
631 if(!index_add_to_chunk(zck, dst, dst_size, 0)) {
632 free(dst);
633 return -1;
634 }
635 if(!index_finish_chunk(zck)) {
636 free(dst);
637 return -1;
638 }
639 zck_log(ZCK_LOG_DDEBUG, "Finished chunk size: %lu", data_size);
640 free(dst);
641 return data_size;
642 }
643
zck_read(zckCtx * zck,char * dst,size_t dst_size)644 ssize_t PUBLIC zck_read(zckCtx *zck, char *dst, size_t dst_size) {
645 VALIDATE_READ_INT(zck);
646 ALLOCD_INT(zck, dst);
647
648 return comp_read(zck, dst, dst_size, 1);
649 }
650
zck_get_chunk_comp_data(zckChunk * idx,char * dst,size_t dst_size)651 ssize_t PUBLIC zck_get_chunk_comp_data(zckChunk *idx, char *dst,
652 size_t dst_size) {
653 zckCtx *zck = NULL;
654 if(idx && idx->zck) {
655 VALIDATE_INT(idx->zck);
656 zck = idx->zck;
657 }
658 ALLOCD_INT(zck, idx);
659 ALLOCD_INT(zck, dst);
660
661 /* Make sure chunk size is valid */
662 if(zck_get_chunk_size(idx) < 0)
663 return -1;
664
665 /* If the chunk is empty, we're done */
666 if(zck_get_chunk_size(idx) == 0)
667 return 0;
668
669 /* Make sure requested chunk has a beginning */
670 if(zck_get_chunk_start(idx) < 0)
671 return -1;
672
673 /* Seek to beginning of requested chunk */
674 if(!seek_data(zck, zck_get_chunk_start(idx), SEEK_SET))
675 return -1;
676
677 /* Return read chunk */
678 return read_data(zck, dst, dst_size);
679 }
680
zck_get_chunk_data(zckChunk * idx,char * dst,size_t dst_size)681 ssize_t PUBLIC zck_get_chunk_data(zckChunk *idx, char *dst,
682 size_t dst_size) {
683 zckCtx *zck = NULL;
684 if(idx && idx->zck) {
685 VALIDATE_INT(idx->zck);
686 zck = idx->zck;
687 }
688 ALLOCD_INT(zck, idx);
689 ALLOCD_INT(zck, dst);
690
691 /* Make sure chunk size is valid */
692 if(zck_get_chunk_size(idx) < 0)
693 return -1;
694 /* If the chunk is empty, we're done */
695 if(zck_get_chunk_size(idx) == 0)
696 return 0;
697 /* Make sure requested chunk has a beginning */
698 if(zck_get_chunk_start(idx) < 0)
699 return -1;
700
701 /* Read dictionary if needed */
702 zckChunk *dict = zck_get_first_chunk(zck);
703 if(dict == NULL)
704 return -1;
705 if(zck_get_chunk_size(dict) > 0 && zck->comp.dict == NULL) {
706 if(zck_get_chunk_start(dict) < 0)
707 return -1;
708 if(!seek_data(zck, zck_get_chunk_start(dict), SEEK_SET))
709 return -1;
710 if(!comp_reset(zck))
711 return -1;
712 if(!comp_init(zck))
713 return -1;
714 if(!import_dict(zck))
715 return -1;
716 }
717
718 /* Seek to beginning of requested chunk */
719 if(!comp_reset_comp_data(zck))
720 return -1;
721 if(!comp_reset(zck))
722 return -1;
723 if(!comp_init(zck))
724 return -1;
725 if(!seek_data(zck, zck_get_chunk_start(idx), SEEK_SET))
726 return -1;
727 zck->comp.data_idx = idx;
728 return comp_read(zck, dst, dst_size, 1);
729 }
730