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