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