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 <stdint.h>
29 #include <stdbool.h>
30 #include <string.h>
31 #include <zck.h>
32 
33 #include "zck_private.h"
34 
create_chunk(zckCtx * zck)35 static bool create_chunk(zckCtx *zck) {
36     VALIDATE_BOOL(zck);
37 
38     clear_work_index(zck);
39     zck->work_index_item = zmalloc(sizeof(zckChunk));
40     if(!hash_init(zck, &(zck->work_index_hash), &(zck->chunk_hash_type)))
41         return false;
42     return true;
43 }
44 
finish_chunk(zckIndex * index,zckChunk * item,char * digest,bool valid,zckCtx * zck)45 static bool finish_chunk(zckIndex *index, zckChunk *item, char *digest,
46                         bool valid, zckCtx *zck) {
47     VALIDATE_BOOL(zck);
48     ALLOCD_BOOL(zck, index);
49     ALLOCD_BOOL(zck, item);
50 
51     item->digest = zmalloc(index->digest_size);
52     if(digest) {
53         memcpy(item->digest, digest, index->digest_size);
54         item->digest_size = index->digest_size;
55     }
56     item->start = index->length;
57     item->valid = valid;
58     item->zck = zck;
59     item->number = index->count;
60     if(index->first == NULL) {
61         index->first = item;
62     } else {
63         index->last->next = item;
64     }
65     index->last = item;
66     index->count += 1;
67     index->length += item->comp_length;
68     return true;
69 }
70 
index_create(zckCtx * zck)71 bool index_create(zckCtx *zck) {
72     VALIDATE_BOOL(zck);
73 
74     char *index;
75     size_t index_malloc = 0;
76     size_t index_size = 0;
77 
78 
79     zck->full_hash_digest = hash_finalize(zck, &(zck->full_hash));
80     if(zck->full_hash_digest == NULL)
81         return false;
82 
83     /* Set initial malloc size */
84     index_malloc  = MAX_COMP_SIZE * 2;
85 
86     /* Add digest size + MAX_COMP_SIZE bytes for length of each entry in
87      * index */
88     if(zck->index.first) {
89         zckChunk *tmp = zck->index.first;
90         while(tmp) {
91             index_malloc += zck->index.digest_size + MAX_COMP_SIZE*2;
92             tmp = tmp->next;
93         }
94     }
95 
96     /* Write index */
97     index = zmalloc(index_malloc);
98     compint_from_size(index+index_size, zck->index.hash_type, &index_size);
99     compint_from_size(index+index_size, zck->index.count, &index_size);
100     if(zck->index.first) {
101         zckChunk *tmp = zck->index.first;
102         while(tmp) {
103             /* Write digest */
104             memcpy(index+index_size, tmp->digest, zck->index.digest_size);
105             index_size += zck->index.digest_size;
106             /* Write compressed size */
107             compint_from_size(index+index_size, tmp->comp_length,
108                                   &index_size);
109             /* Write uncompressed size */
110             compint_from_size(index+index_size, tmp->length, &index_size);
111 
112             tmp = tmp->next;
113         }
114     }
115     /* Shrink index to actual size */
116     index = zrealloc(index, index_size);
117     zck->index_string = index;
118     zck->index_size = index_size;
119     zck_log(ZCK_LOG_DEBUG, "Generated index: %lu bytes", zck->index_size);
120     return true;
121 }
122 
index_new_chunk(zckCtx * zck,zckIndex * index,char * digest,int digest_size,size_t comp_size,size_t orig_size,zckChunk * src,bool finished)123 bool index_new_chunk(zckCtx *zck, zckIndex *index, char *digest,
124                      int digest_size, size_t comp_size, size_t orig_size,
125                      zckChunk *src, bool finished) {
126     VALIDATE_BOOL(zck);
127 
128     if(index == NULL) {
129         set_error(zck, "Invalid index");
130         return false;
131     }
132     if(digest_size == 0) {
133         set_error(zck, "Digest size 0 too small");
134         return false;
135     }
136     zckChunk *chk = zmalloc(sizeof(zckChunk));
137     index->digest_size = digest_size;
138     chk->comp_length = comp_size;
139     chk->length = orig_size;
140     chk->src = src;
141     return finish_chunk(index, chk, digest, finished, zck);
142 }
143 
index_add_to_chunk(zckCtx * zck,char * data,size_t comp_size,size_t orig_size)144 bool index_add_to_chunk(zckCtx *zck, char *data, size_t comp_size,
145                            size_t orig_size) {
146     VALIDATE_BOOL(zck);
147 
148     if(zck->work_index_item == NULL && !create_chunk(zck))
149         return false;
150 
151     zck->work_index_item->length += orig_size;
152     if(comp_size == 0)
153         return true;
154 
155     if(!hash_update(zck, &(zck->full_hash), data, comp_size))
156         return false;
157     if(!hash_update(zck, &(zck->work_index_hash), data, comp_size))
158         return false;
159 
160     zck->work_index_item->comp_length += comp_size;
161     return true;
162 }
163 
index_finish_chunk(zckCtx * zck)164 bool index_finish_chunk(zckCtx *zck) {
165     VALIDATE_BOOL(zck);
166 
167     if(zck->work_index_item == NULL && !create_chunk(zck))
168         return false;
169 
170     char *digest = NULL;
171     if(zck->work_index_item->length > 0) {
172         /* Finalize chunk checksum */
173         digest = hash_finalize(zck, &(zck->work_index_hash));
174         if(digest == NULL) {
175             set_fatal_error(zck,
176                             "Unable to calculate %s checksum for new chunk",
177                             zck_hash_name_from_type(zck->index.hash_type));
178             return false;
179         }
180     } else {
181         digest = zmalloc(zck->chunk_hash_type.digest_size);
182     }
183     if(!finish_chunk(&(zck->index), zck->work_index_item, digest, true, zck)) {
184         free(digest);
185         return false;
186     }
187 
188     free(digest);
189     zck->work_index_item = NULL;
190     hash_close(&(zck->work_index_hash));
191     return true;
192 }
193