GCC Code Coverage Report


Directory: ./
File: src/stegfs.c
Date: 2026-11-08 20:01:15
Exec Total Coverage
Lines: 663 763 86.9%
Functions: 26 27 96.3%
Branches: 378 545 69.4%

Line Branch Exec Source
1 /*
2 * stegfs ~ a steganographic file system for unix-like systems
3 * Copyright © 2007-2026, albinoloverats ~ Software Development
4 * email: stegfs@albinoloverats.net
5 *
6 * This program is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 *
19 */
20
21 #include <errno.h>
22 #include <error.h>
23
24 #include <fcntl.h>
25 #include <unistd.h>
26 #include <stdio.h>
27
28 #include <sys/stat.h>
29 #include <sys/mman.h>
30 #include <netinet/in.h>
31
32 #include <gcrypt.h>
33
34 /* submodule includes */
35
36 #include "common.h"
37 #include "mem.h"
38 #include "ccrypt.h"
39 #include "tlv.h"
40 #include "dir.h"
41
42 /* project includes */
43
44 #include "stegfs.h"
45
46
47 #define normalize(I) ((I)%(file_system.size/file_system.blocksize))
48
49
50 static version_e parse_version(const char *v);
51
52 static bool block_read(uint64_t, stegfs_block_s *, gcry_cipher_hd_t, const char * const restrict);
53 static bool block_write(uint64_t, stegfs_block_s, gcry_cipher_hd_t, const char * const restrict);
54 static void block_delete(uint64_t);
55
56 static bool block_in_use(uint64_t, const char * const restrict);
57 static uint64_t block_assign(const char * const restrict);
58
59 static gcry_cipher_hd_t init_cipher(const stegfs_file_s * const restrict, uint8_t);
60 static gcry_mac_hd_t init_mac(const stegfs_file_s * const restrict, uint8_t);
61
62
63 static stegfs_s file_system = { .handle = -1 };
64 static bool file_system_mapped;
65 static bool file_system_cache_ready;
66
67 121 static void cache_free(stegfs_cache_s *cache)
68 {
69
1/2
✓ Branch 0 taken 121 times.
✗ Branch 1 not taken.
121 if (!cache)
70 return;
71
4/4
✓ Branch 0 taken 87 times.
✓ Branch 1 taken 86 times.
✓ Branch 2 taken 52 times.
✓ Branch 3 taken 35 times.
173 for (uint64_t i = 0; cache->child && i < cache->ents; i++)
72 {
73 52 stegfs_cache_s *child = cache->child[i];
74
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 52 times.
52 if (!child)
75 ✗ continue;
76 52 cache_free(child);
77 52 free(child);
78 }
79 121 free(cache->child);
80 121 free(cache->name);
81
2/2
✓ Branch 0 taken 46 times.
✓ Branch 1 taken 75 times.
121 if (cache->file)
82 {
83 46 free(cache->file->path);
84 46 free(cache->file->name);
85 46 free(cache->file->pass);
86 46 free(cache->file->data);
87
2/2
✓ Branch 0 taken 2944 times.
✓ Branch 1 taken 46 times.
2990 for (unsigned i = 0; i < COPIES_MAX; i++)
88 2944 free(cache->file->blocks[i]);
89 46 free(cache->file);
90 }
91 121 memset(cache, 0, sizeof *cache);
92 }
93
94 64 static void file_system_clear(void)
95 {
96
2/2
✓ Branch 0 taken 58 times.
✓ Branch 1 taken 6 times.
64 if (file_system_cache_ready)
97 58 cache_free(&file_system.cache);
98
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 62 times.
64 if (file_system.blocks.file)
99 {
100
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 uint64_t blocks = file_system.blocksize ? file_system.size / file_system.blocksize : 0;
101
2/2
✓ Branch 0 taken 1024 times.
✓ Branch 1 taken 2 times.
1026 for (uint64_t i = 0; i < blocks; i++)
102 1024 free(file_system.blocks.file[i]);
103 2 free(file_system.blocks.file);
104 }
105 64 free(file_system.blocks.in_use);
106
2/2
✓ Branch 0 taken 58 times.
✓ Branch 1 taken 6 times.
64 if (file_system_mapped)
107 58 munmap(file_system.memory, file_system.size);
108
2/2
✓ Branch 0 taken 59 times.
✓ Branch 1 taken 5 times.
64 if (file_system.handle >= 0)
109 59 close(file_system.handle);
110 64 memset(&file_system, 0, sizeof file_system);
111 64 file_system.handle = -1;
112 64 file_system_mapped = false;
113 64 file_system_cache_ready = false;
114 64 }
115
116 19 static stegfs_init_e parse_superblock(const stegfs_block_s *block)
117 {
118
2/4
✓ Branch 0 taken 19 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 19 times.
19 if ((block->hash[0] == HASH_MAGIC_201001_0 || htonll(block->hash[0]) == HASH_MAGIC_201001_0)
119 ✗ && (block->hash[1] == HASH_MAGIC_201001_1 || htonll(block->hash[1]) == HASH_MAGIC_201001_1)
120 ✗ && (block->hash[2] == HASH_MAGIC_201001_2 || htonll(block->hash[2]) == HASH_MAGIC_201001_2))
121 return STEGFS_INIT_OLD_STEGFS;
122
123
3/4
✓ Branch 0 taken 18 times.
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
19 if (ntohll(block->hash[0]) != HASH_MAGIC_0 || ntohll(block->hash[1]) != HASH_MAGIC_1)
124 return STEGFS_INIT_NOT_STEGFS;
125
126 uint64_t tags;
127 size_t offset;
128
1/3
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
18 switch (ntohll(block->hash[2]))
129 {
130 case HASH_MAGIC_201508_2:
131 tags = 8;
132 offset = 0;
133 break;
134 18 case HASH_MAGIC_2:
135 18 memcpy(&tags, block->data, sizeof tags);
136 18 tags = ntohll(tags);
137 offset = sizeof tags;
138 break;
139 default:
140 return STEGFS_INIT_NOT_STEGFS;
141 }
142
2/2
✓ Branch 0 taken 17 times.
✓ Branch 1 taken 1 times.
18 if (tags > TAG_MAX || offset > sizeof block->data)
143 return STEGFS_INIT_CORRUPT_TAG;
144
145 17 tlv_t tlv = tlv_init();
146
2/2
✓ Branch 0 taken 160 times.
✓ Branch 1 taken 16 times.
176 for (uint64_t i = 0; i < tags; i++)
147 {
148
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 160 times.
160 if (sizeof block->data - offset < sizeof(uint8_t) + sizeof(uint16_t))
149 1 goto corrupt;
150 tlv_entry_s entry;
151 160 memcpy(&entry.tag, block->data + offset, sizeof entry.tag);
152 160 offset += sizeof entry.tag;
153 160 memcpy(&entry.length, block->data + offset, sizeof entry.length);
154
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 159 times.
160 offset += sizeof entry.length;
155 entry.length = ntohs(entry.length);
156
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 159 times.
160 if (entry.length > sizeof block->data - offset)
157 1 goto corrupt;
158 159 entry.value = (byte_t *)block->data + offset;
159 159 offset += entry.length;
160
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 159 times.
159 if (!tlv_append(tlv, entry))
161 ✗ goto corrupt;
162 }
163
164
1/2
✓ Branch 1 taken 16 times.
✗ Branch 2 not taken.
16 if (!tlv_has_tag(tlv, TAG_STEGFS)
165
1/2
✓ Branch 1 taken 16 times.
✗ Branch 2 not taken.
16 || tlv_length_of(tlv, TAG_STEGFS) != strlen(STEGFS_NAME)
166
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 16 times.
16 || memcmp(tlv_value_of(tlv, TAG_STEGFS), STEGFS_NAME, strlen(STEGFS_NAME)))
167 ✗ goto invalid;
168
169
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 16 times.
16 if (!tlv_has_tag(tlv, TAG_VERSION))
170 ✗ goto missing;
171 16 uint16_t version_length = tlv_length_of(tlv, TAG_VERSION);
172 16 char *version_string = m_calloc((size_t)version_length + 1, sizeof *version_string);
173 16 memcpy(version_string, tlv_value_of(tlv, TAG_VERSION), version_length);
174 16 file_system.version = parse_version(version_string);
175 16 free(version_string);
176
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 15 times.
16 if (file_system.version != VERSION_2015_08 && file_system.version != VERSION_202X_XX)
177 {
178 1 tlv_deinit(tlv);
179 1 return STEGFS_INIT_OLD_STEGFS;
180 }
181
182 #define READ_ALGORITHM_TAG(tag, destination, default_value, convert, none_value) \
183 do { \
184 if (tlv_has_tag(tlv, tag)) { \
185 uint16_t length = tlv_length_of(tlv, tag); \
186 if (!length) goto invalid; \
187 char *name = m_calloc((size_t)length + 1, sizeof *name); \
188 memcpy(name, tlv_value_of(tlv, tag), length); \
189 destination = convert(name); \
190 free(name); \
191 } else destination = default_value; \
192 if (destination == none_value) goto invalid; \
193 } while (0)
194
4/6
✓ Branch 1 taken 15 times.
✗ Branch 2 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 15 times.
✓ Branch 8 taken 1 times.
✓ Branch 9 taken 14 times.
15 READ_ALGORITHM_TAG(TAG_CIPHER, file_system.cipher, DEFAULT_CIPHER, cipher_id_from_name, GCRY_CIPHER_NONE);
195
3/6
✓ Branch 1 taken 14 times.
✗ Branch 2 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 14 times.
✗ Branch 8 not taken.
✓ Branch 9 taken 14 times.
14 READ_ALGORITHM_TAG(TAG_MODE, file_system.mode, DEFAULT_MODE, mode_id_from_name, GCRY_CIPHER_MODE_NONE);
196
3/6
✓ Branch 1 taken 14 times.
✗ Branch 2 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 14 times.
✗ Branch 8 not taken.
✓ Branch 9 taken 14 times.
14 READ_ALGORITHM_TAG(TAG_HASH, file_system.hash, DEFAULT_HASH, hash_id_from_name, GCRY_MD_NONE);
197
1/2
✓ Branch 0 taken 14 times.
✗ Branch 1 not taken.
14 if (file_system.version >= VERSION_202X_XX)
198
4/6
✓ Branch 1 taken 14 times.
✗ Branch 2 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 14 times.
✓ Branch 8 taken 1 times.
✓ Branch 9 taken 13 times.
14 READ_ALGORITHM_TAG(TAG_MAC, file_system.mac, DEFAULT_MAC, mac_id_from_name, GCRY_MAC_NONE);
199 else
200 ✗ file_system.mac = DEFAULT_MAC;
201 #undef READ_ALGORITHM_TAG
202
203
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 13 times.
13 if (!tlv_has_tag(tlv, TAG_DUPLICATION))
204 ✗ file_system.copies = COPIES_DEFAULT;
205
1/2
✓ Branch 0 taken 13 times.
✗ Branch 1 not taken.
13 else if (file_system.version >= VERSION_202X_XX)
206 {
207
2/2
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 12 times.
13 if (tlv_length_of(tlv, TAG_DUPLICATION) != sizeof(uint8_t))
208 1 goto invalid;
209 12 file_system.copies = *(uint8_t *)tlv_value_of(tlv, TAG_DUPLICATION);
210 }
211 else
212 {
213 uint32_t copies;
214 ✗ if (tlv_length_of(tlv, TAG_DUPLICATION) != sizeof copies)
215 ✗ goto invalid;
216 ✗ memcpy(&copies, tlv_value_of(tlv, TAG_DUPLICATION), sizeof copies);
217 ✗ file_system.copies = ntohl(copies);
218 }
219
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 10 times.
12 if (!file_system.copies || file_system.copies > COPIES_MAX)
220 2 goto invalid;
221
222 uint32_t value;
223
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 10 times.
10 if (!tlv_has_tag(tlv, TAG_BLOCKSIZE))
224 ✗ goto missing;
225
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 10 times.
10 if (tlv_length_of(tlv, TAG_BLOCKSIZE) != sizeof value)
226 ✗ goto invalid;
227
2/2
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 9 times.
10 memcpy(&value, tlv_value_of(tlv, TAG_BLOCKSIZE), sizeof value);
228 10 file_system.blocksize = ntohl(value);
229
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 9 times.
10 if (file_system.blocksize != sizeof(stegfs_block_s))
230 1 goto corrupt;
231
232
2/2
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 8 times.
9 if (!tlv_has_tag(tlv, TAG_HEADER_OFFSET))
233 1 goto missing;
234
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 8 times.
8 if (tlv_length_of(tlv, TAG_HEADER_OFFSET) != sizeof value)
235 ✗ goto invalid;
236
1/2
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
8 memcpy(&value, tlv_value_of(tlv, TAG_HEADER_OFFSET), sizeof value);
237 8 file_system.head_offset = ntohl(value);
238 8 uint64_t header_offset = (uint64_t)file_system.head_offset;
239
1/2
✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
8 if (header_offset > sizeof block->data
240 16 || header_offset < (file_system.copies + 1) * sizeof(uint64_t)
241
2/2
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 7 times.
8 + gcry_mac_get_algo_maclen(file_system.mac))
242 1 goto corrupt;
243
244
1/2
✓ Branch 1 taken 7 times.
✗ Branch 2 not taken.
7 if (tlv_has_tag(tlv, TAG_KDF))
245 {
246 uint64_t kdf;
247
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 7 times.
7 if (tlv_length_of(tlv, TAG_KDF) != sizeof kdf)
248 ✗ goto invalid;
249 7 memcpy(&kdf, tlv_value_of(tlv, TAG_KDF), sizeof kdf);
250 7 file_system.kdf_iterations = ntohll(kdf);
251 }
252 else
253 ✗ file_system.kdf_iterations = DEFAULT_KDF_ITERATIONS;
254
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 6 times.
7 if (!file_system.kdf_iterations)
255 1 goto invalid;
256
257
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
6 if (ntohll(block->next) != file_system.size / file_system.blocksize)
258 ✗ goto corrupt;
259 6 tlv_deinit(tlv);
260 6 return STEGFS_INIT_OKAY;
261
262 1 missing:
263 1 tlv_deinit(tlv);
264 1 return STEGFS_INIT_MISSING_TAG;
265 6 invalid:
266 6 tlv_deinit(tlv);
267 6 return STEGFS_INIT_INVALID_TAG;
268 3 corrupt:
269 3 tlv_deinit(tlv);
270 3 return STEGFS_INIT_CORRUPT_TAG;
271 }
272 59 extern stegfs_init_e stegfs_init(const char * const restrict fs, bool paranoid, enum gcry_cipher_algos cipher, enum gcry_cipher_modes mode, enum gcry_md_algos hash, enum gcry_mac_algos mac, uint64_t kdf, uint32_t dups, bool show_bloc)
273 {
274
3/4
✓ Branch 0 taken 57 times.
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 57 times.
59 if (file_system_mapped || file_system.handle >= 0)
275 2 file_system_clear();
276 59 memset(&file_system, 0, sizeof file_system);
277 59 file_system.handle = -1;
278
2/4
✓ Branch 0 taken 59 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 59 times.
✗ Branch 4 not taken.
59 if (!fs || (file_system.handle = open(fs, O_RDWR, S_IRUSR | S_IWUSR)) < 0)
279 return STEGFS_INIT_UNKNOWN;
280
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 59 times.
59 if (lockf(file_system.handle, F_LOCK, 0) < 0)
281 ✗ goto unknown;
282 59 off_t size = lseek(file_system.handle, 0, SEEK_END);
283
3/4
✓ Branch 0 taken 58 times.
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 58 times.
59 if (size < (off_t)(2 * SIZE_BYTE_BLOCK) || size % SIZE_BYTE_BLOCK)
284 1 goto corrupt_image;
285 58 file_system.size = (uint64_t)size;
286
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 58 times.
58 if ((file_system.memory = mmap(NULL, file_system.size, PROT_READ | PROT_WRITE, MAP_SHARED, file_system.handle, 0)) == MAP_FAILED)
287 {
288 ✗ file_system.memory = NULL;
289 ✗ goto unknown;
290 }
291 58 file_system_mapped = true;
292
293 58 file_system.cache.name = m_strdup(DIR_SEPARATOR);
294 58 file_system.cache.ents = 0;
295 58 file_system.cache.child = NULL;
296 58 file_system.cache.file = NULL;
297 58 file_system_cache_ready = true;
298
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 56 times.
58 if ((file_system.show_bloc = show_bloc))
299 2 stegfs_cache_add(PATH_BLOC, NULL);
300
2/2
✓ Branch 0 taken 39 times.
✓ Branch 1 taken 19 times.
58 if (paranoid)
301 {
302 39 file_system.cipher = cipher;
303 39 file_system.mode = mode;
304 39 file_system.hash = hash;
305 39 file_system.mac = mac;
306 39 file_system.blocksize = SIZE_BYTE_BLOCK;
307 39 file_system.head_offset = OFFSET_BYTE_HEAD;
308 39 file_system.kdf_iterations = kdf;
309 39 file_system.copies = dups;
310 39 goto done;
311 }
312
313 stegfs_block_s block;
314 19 memcpy(&block, file_system.memory, sizeof block);
315 19 stegfs_init_e status = parse_superblock(&block);
316
2/2
✓ Branch 0 taken 13 times.
✓ Branch 1 taken 6 times.
19 if (status != STEGFS_INIT_OKAY)
317 {
318 13 file_system_clear();
319 13 return status;
320 }
321
322 6 done:
323
2/4
✓ Branch 0 taken 45 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 45 times.
45 if (file_system.blocksize != SIZE_BYTE_BLOCK || file_system.size % file_system.blocksize)
324 {
325 ✗ file_system_clear();
326 ✗ return STEGFS_INIT_CORRUPT_TAG;
327 }
328
1/2
✓ Branch 0 taken 45 times.
✗ Branch 1 not taken.
45 if (!file_system.copies || file_system.copies > COPIES_MAX
329
1/2
✓ Branch 0 taken 45 times.
✗ Branch 1 not taken.
45 || (uint64_t)file_system.head_offset > SIZE_BYTE_DATA
330
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 45 times.
45 || !file_system.kdf_iterations)
331 {
332 ✗ file_system_clear();
333 ✗ return STEGFS_INIT_INVALID_TAG;
334 }
335 45 file_system.blocks.used = 1;
336 45 file_system.blocks.in_use = m_calloc(file_system.size / file_system.blocksize, sizeof( bool ));
337
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 43 times.
45 if (file_system.show_bloc)
338 2 file_system.blocks.file = m_calloc(file_system.size / file_system.blocksize, sizeof( char * ));
339
340 45 init_crypto();
341 45 return STEGFS_INIT_OKAY;
342
343 corrupt_image:
344 1 file_system_clear();
345 1 return STEGFS_INIT_CORRUPT_TAG;
346 ✗ unknown:
347 ✗ file_system_clear();
348 ✗ return STEGFS_INIT_UNKNOWN;
349 }
350
351 16 static version_e parse_version(const char *v)
352 {
353 static char *versions[] =
354 {
355 "Unknown",
356 "2010.01", /* unsupported */
357 "2015.08",
358 "202X.XX",
359 "Current"
360 };
361
2/2
✓ Branch 0 taken 34 times.
✓ Branch 1 taken 1 times.
35 for (version_e i = VERSION_CURRENT; i > VERSION_UNKNOWN; i--)
362
2/2
✓ Branch 0 taken 19 times.
✓ Branch 1 taken 15 times.
34 if (!strcmp(v, versions[i]))
363 return i;
364 return VERSION_UNKNOWN;
365 }
366
367 48 extern void stegfs_deinit(void)
368 {
369 48 file_system_clear();
370 48 }
371
372 38 extern stegfs_s stegfs_info(void)
373 {
374 38 return file_system;
375 }
376
377 70 extern bool stegfs_file_will_fit(stegfs_file_s *file)
378 {
379
3/6
✓ Branch 0 taken 70 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 70 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 70 times.
✗ Branch 5 not taken.
70 if (!file || !file_system.blocksize || file_system.head_offset > SIZE_BYTE_DATA
380
1/2
✓ Branch 0 taken 70 times.
✗ Branch 1 not taken.
70 || !file_system.copies
381
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 70 times.
70 || file_system.copies > COPIES_MAX)
382 ✗ return errno = EINVAL, false;
383 stegfs_block_s block;
384 70 uint64_t inline_capacity = sizeof block.data - file_system.head_offset;
385
2/2
✓ Branch 0 taken 22 times.
✓ Branch 1 taken 48 times.
70 uint64_t remainder = file->size > inline_capacity ? file->size - inline_capacity : 0;
386 70 uint64_t blocks_needed = remainder / SIZE_BYTE_DATA + (remainder % SIZE_BYTE_DATA != 0);
387 if (blocks_needed > UINT64_MAX / file_system.copies)
388 return errno = EFBIG, false;
389 70 blocks_needed *= file_system.copies;
390
391 70 uint64_t total_blocks = file_system.size / file_system.blocksize;
392
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 70 times.
70 if (file_system.blocks.used > total_blocks)
393 ✗ return errno = EIO, false;
394 uint64_t old_data_blocks = 0;
395 uint64_t old_inodes = 0;
396
2/2
✓ Branch 0 taken 138 times.
✓ Branch 1 taken 70 times.
208 for (unsigned i = 0; i < file_system.copies; i++)
397 {
398 138 old_inodes += file->inodes[i] != 0;
399
2/2
✓ Branch 0 taken 34 times.
✓ Branch 1 taken 104 times.
138 if (file->blocks[i])
400 {
401
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 34 times.
34 if (old_data_blocks > UINT64_MAX - file->blocks[i][0])
402 ✗ return errno = EFBIG, false;
403 34 old_data_blocks += file->blocks[i][0];
404 }
405 }
406 uint64_t new_data_blocks = blocks_needed;
407 70 uint64_t available = total_blocks - file_system.blocks.used;
408 140 uint64_t required = (file_system.copies - old_inodes)
409
2/2
✓ Branch 0 taken 18 times.
✓ Branch 1 taken 52 times.
70 + (new_data_blocks > old_data_blocks ? new_data_blocks - old_data_blocks : 0);
410
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 67 times.
70 if (required > available)
411
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 2 times.
4 return errno = old_inodes ? ENOSPC : EFBIG, false;
412 67 return errno = EXIT_SUCCESS, true;
413 }
414
415 43 extern void stegfs_file_create(const char * const restrict path, bool write)
416 {
417 stegfs_file_s file;
418 43 memset(&file, 0, sizeof file);
419 43 file.path = dir_get_path(path);
420 43 file.name = dir_get_name(path, PASSWORD_SEPARATOR);
421 43 file.pass = dir_get_pass(path);
422 43 file.time = time(NULL);
423 43 file.loaded = true;
424 43 file.dirty = write;
425 43 stegfs_cache_add(NULL, &file);
426 43 free(file.path);
427 43 free(file.name);
428 43 free(file.pass);
429 43 return;
430 }
431
432 ✗ extern void stegfs_directory_create(const char * const restrict path)
433 {
434 ✗ stegfs_cache_add(path, NULL);
435 ✗ }
436
437 88 extern bool stegfs_file_stat_aux(stegfs_file_s *file, bool quick)
438 {
439 /*
440 * figure out where the files’ inode blocks are
441 */
442 gcry_md_hd_t hash;
443 88 gcry_md_open(&hash, GCRY_MD_SHA512, GCRY_MD_FLAG_SECURE);
444 88 gcry_md_write(hash, file->path, strlen(file->path));
445 88 gcry_md_write(hash, file->name, strlen(file->name));
446 uint8_t digest[64];
447 88 memcpy(digest, gcry_md_read(hash, GCRY_MD_SHA512), sizeof digest);
448 88 size_t len = gcry_md_get_algo_dlen(GCRY_MD_SHA512);
449 /*
450 * calculate inode values; must be done here, so we have all of
451 * them and not just the first that we can read (I wonder if there
452 * is a better way than rotating the data…)
453 */
454
2/2
✓ Branch 0 taken 176 times.
✓ Branch 1 taken 88 times.
264 for (unsigned i = 0; i < file_system.copies; i++)
455 {
456 176 memcpy(&file->inodes[i], digest, sizeof file->inodes[i]);
457 176 uint8_t b = digest[0];
458 176 memmove(digest, digest + 1, len - 1);
459 176 digest[len - 1] = b;
460 }
461 88 gcry_md_close(hash);
462 /*
463 * read file inode, pray for success, then see if we can get a
464 * complete copy of the file
465 */
466 88 unsigned available_inodes = file_system.copies;
467 unsigned corrupt_copies = 0;
468 bool found = false;
469
2/2
✓ Branch 0 taken 137 times.
✓ Branch 1 taken 48 times.
185 for (unsigned i = 0; i < file_system.copies; i++)
470 {
471 137 gcry_cipher_hd_t cipher_handle = init_cipher(file, i);
472 stegfs_block_s inode;
473 //memset(&inode, 0x00, sizeof inode);
474
2/2
✓ Branch 1 taken 50 times.
✓ Branch 2 taken 87 times.
137 if (block_read(file->inodes[i], &inode, cipher_handle, file->path))
475 {
476
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 50 times.
50 if ((file->size = ntohll(inode.next)) > file_system.size)
477 {
478 ✗ available_inodes--;
479 ✗ gcry_cipher_close(cipher_handle);
480 5 continue;
481 }
482 50 uint64_t inode_block = normalize(file->inodes[i]);
483
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 47 times.
50 if (!file_system.blocks.in_use[inode_block])
484 {
485 3 file_system.blocks.in_use[inode_block] = true;
486 3 file_system.blocks.used++;
487 }
488
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 50 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
50 if (file_system.show_bloc && !file_system.blocks.file[inode_block])
489 ✗ m_asprintf(&file_system.blocks.file[inode_block], "../%s/%s", file->path, file->name);
490
2/2
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 45 times.
50 if (!quick && found)
491 {
492 5 gcry_cipher_close(cipher_handle);
493 5 continue;
494 }
495
496 uint64_t first[SIZE_LONG_DATA];
497 45 memcpy(first, inode.data, sizeof first);
498 45 file->time = htonll(first[0]);
499
2/2
✓ Branch 0 taken 90 times.
✓ Branch 1 taken 45 times.
135 for (unsigned j = 0, l = 1; j < file_system.copies; j++, l++)
500 {
501 90 gcry_cipher_hd_t another_cipher = init_cipher(file, j);
502 90 lldiv_t d = lldiv(file->size - (file->size < (sizeof inode.data - file_system.head_offset) ? file->size : (sizeof inode.data - file_system.head_offset)), SIZE_BYTE_DATA);
503 90 uint64_t blocks = d.quot + (d.rem > 0);
504 90 file->blocks[j] = m_realloc(file->blocks[j], (blocks + 2) * sizeof blocks);
505 //memset(file->blocks[j], 0x00, blocks * sizeof blocks);
506 90 file->blocks[j][0] = blocks;
507
2/2
✓ Branch 0 taken 42 times.
✓ Branch 1 taken 48 times.
90 if (blocks)
508 {
509 /* first full block of file data */
510 42 file->blocks[j][1] = htonll(first[l]);
511 42 uint64_t data_block = normalize(file->blocks[j][1]);
512
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 38 times.
42 if (!file_system.blocks.in_use[data_block])
513 {
514 4 file_system.blocks.in_use[data_block] = true;
515 4 file_system.blocks.used++;
516 }
517
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 42 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
42 if (file_system.show_bloc && !file_system.blocks.file[data_block])
518 ✗ m_asprintf(&file_system.blocks.file[data_block], "../%s/%s", file->path, file->name);
519 }
520 /*
521 * traverse file block tree; whilst the
522 * whole block is read, the actual file
523 * data is discarded
524 */
525
2/2
✓ Branch 0 taken 64 times.
✓ Branch 1 taken 87 times.
151 for (uint64_t k = 2 ; k <= blocks; k++)
526 {
527 stegfs_block_s block;
528 //memset(&block, 0x00, sizeof block);
529
2/2
✓ Branch 1 taken 61 times.
✓ Branch 2 taken 3 times.
64 if (block_read(file->blocks[j][k - 1], &block, another_cipher, file->path))
530 {
531 61 uint64_t data_block = normalize(file->blocks[j][k - 1]);
532
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 59 times.
61 if (!file_system.blocks.in_use[data_block])
533 {
534 2 file_system.blocks.in_use[data_block] = true;
535 2 file_system.blocks.used++;
536 }
537
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 61 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
61 if (file_system.show_bloc && !file_system.blocks.file[data_block])
538 ✗ m_asprintf(&file_system.blocks.file[data_block], "../%s/%s", file->path, file->name);
539 61 file->blocks[j][k] = ntohll(block.next);
540 }
541 else
542 {
543 3 corrupt_copies++;
544 3 break;
545 }
546 }
547 90 gcry_cipher_close(another_cipher);
548 }
549
2/2
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 40 times.
45 if (quick)
550 break;
551
1/2
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
5 if (corrupt_copies < file_system.copies)
552 found = true;
553 }
554 else
555 87 available_inodes--;
556 92 gcry_cipher_close(cipher_handle);
557 }
558 /*
559 * as long as there’s a valid inode and one complete copy we’re
560 * good
561 */
562
4/4
✓ Branch 0 taken 45 times.
✓ Branch 1 taken 43 times.
✓ Branch 2 taken 44 times.
✓ Branch 3 taken 1 times.
88 if (available_inodes && corrupt_copies < file_system.copies)
563 {
564
1/2
✓ Branch 0 taken 44 times.
✗ Branch 1 not taken.
44 if (!file->unlinked)
565 44 stegfs_cache_add(NULL, file);
566 return true;
567 }
568
2/2
✓ Branch 0 taken 88 times.
✓ Branch 1 taken 44 times.
132 for (unsigned i = 0; i < file_system.copies; i++)
569
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 84 times.
88 if (file->blocks[i])
570 {
571
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 4 times.
8 for (uint64_t j = 1; j < file->blocks[i][0]; j++)
572 {
573 4 file_system.blocks.in_use[normalize(file->blocks[i][j])] = false;
574
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
4 if (file_system.show_bloc)
575 {
576 ✗ free(file_system.blocks.file[normalize(file->blocks[i][j])]);
577 ✗ file_system.blocks.file[normalize(file->blocks[i][j])] = NULL;
578 }
579
1/2
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
4 if (file_system.blocks.used > 1)
580 4 file_system.blocks.used--;
581 }
582 4 free(file->blocks[i]);
583 4 file->blocks[i] = NULL;
584 }
585 44 return errno = ENOENT, false;
586 }
587
588 34 extern bool stegfs_file_read(stegfs_file_s *file)
589 {
590
2/2
✓ Branch 1 taken 32 times.
✓ Branch 2 taken 2 times.
34 if (!stegfs_file_stat(file, true))
591 return false;
592 32 size_t mac_length = gcry_mac_get_algo_maclen(file_system.mac);
593 32 uint8_t *mac_data = m_gcry_calloc_secure(mac_length, sizeof( uint8_t ));
594 /*
595 * read the start of the file data
596 */
597
2/2
✓ Branch 0 taken 30 times.
✓ Branch 1 taken 2 times.
32 if (file->size)
598 30 file->data = m_realloc(file->data, file->size);
599 else
600 {
601 2 free(file->data);
602 file->data = NULL;
603 }
604 bool inode_found = false;
605
4/4
✓ Branch 0 taken 64 times.
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 33 times.
✓ Branch 3 taken 31 times.
65 for (unsigned i = 0, c = 0; i < file_system.copies && !c; i++)
606 {
607 33 gcry_cipher_hd_t cipher_handle = init_cipher(file, i);
608 stegfs_block_s inode;
609 //memset(&inode, 0x00, sizeof inode);
610
2/2
✓ Branch 1 taken 32 times.
✓ Branch 2 taken 1 times.
33 if (block_read(file->inodes[i], &inode, cipher_handle, file->path))
611 {
612 32 size_t inline_size = file->size < (sizeof inode.data - file_system.head_offset)
613 ? file->size : (sizeof inode.data - file_system.head_offset);
614
2/2
✓ Branch 0 taken 30 times.
✓ Branch 1 taken 2 times.
32 if (inline_size)
615 30 memcpy(file->data, inode.data + file_system.head_offset, inline_size);
616 32 memcpy(mac_data, inode.data + ((file_system.copies + 1) * sizeof( uint64_t )), mac_length);
617 inode_found = true;
618 c = 1;
619 }
620 33 gcry_cipher_close(cipher_handle);
621 }
622
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 32 times.
32 if (!inode_found)
623 {
624 ✗ gcry_free(mac_data);
625 ✗ return errno = EIO, false;
626 }
627 /*
628 * and then the rest of it
629 */
630 stegfs_block_s block;
631 bool read_successfully = false;
632
1/2
✓ Branch 0 taken 33 times.
✗ Branch 1 not taken.
33 for (unsigned i = 0, corrupt_copies = 0; i < file_system.copies; i++)
633 {
634 33 lldiv_t d = lldiv(file->size - (file->size < (sizeof block.data - file_system.head_offset) ? file->size : (sizeof block.data - file_system.head_offset)), SIZE_BYTE_DATA);
635 33 uint64_t blocks = d.quot + (d.rem > 0);
636
2/4
✓ Branch 0 taken 33 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 33 times.
33 if (!file->blocks[i] || file->blocks[i][0] != blocks)
637 1 continue; /* this copy is corrupt; try the next */
638 bool failed = false;
639 33 gcry_cipher_hd_t cipher_handle = init_cipher(file, i);
640 33 gcry_mac_hd_t mac_handle = init_mac(file, i);
641
3/4
✓ Branch 0 taken 32 times.
✓ Branch 1 taken 32 times.
✓ Branch 2 taken 32 times.
✗ Branch 3 not taken.
64 for (uint64_t j = 1, k = 0; j <= file->blocks[i][0] && file->blocks[i][j]; j++, k++)
642 {
643 /*
644 * we should be largely confident that we’ll be
645 * able to read the complete file as otherwise the
646 * stat would have failed
647 */
648 //memset(&block, 0x00, sizeof block);
649
2/2
✓ Branch 1 taken 31 times.
✓ Branch 2 taken 1 times.
32 if (block_read(file->blocks[i][j], &block, cipher_handle, file->path))
650 {
651 size_t l = sizeof block.data;
652
2/2
✓ Branch 0 taken 11 times.
✓ Branch 1 taken 20 times.
31 if ((l + k * sizeof block.data) > (file->size - (sizeof block.data - file_system.head_offset)))
653 11 l = l - ((l + k * sizeof block.data) - (file->size - (sizeof block.data - file_system.head_offset)));
654 31 memcpy(file->data + (sizeof block.data - file_system.head_offset) + k * sizeof block.data, block.data, l);
655 31 gcry_mac_write(mac_handle, block.data, sizeof block.data);
656 }
657 else
658 {
659 failed = true;
660 1 corrupt_copies++;
661 1 break;
662 }
663 }
664 33 gcry_cipher_close(cipher_handle);
665 /* compare generated MAC with stored MAC */
666
3/4
✓ Branch 0 taken 31 times.
✓ Branch 1 taken 2 times.
✓ Branch 3 taken 2 times.
✗ Branch 4 not taken.
33 if (file_system.version >= VERSION_202X_XX && gcry_mac_verify(mac_handle, mac_data, mac_length) == GPG_ERR_CHECKSUM)
667 failed = true;
668 33 gcry_mac_close(mac_handle);
669
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 32 times.
33 if (failed)
670 1 continue;
671 32 file->loaded = true;
672 32 file->dirty = false;
673
1/2
✓ Branch 0 taken 32 times.
✗ Branch 1 not taken.
32 if (!file->unlinked)
674 32 stegfs_cache_add(NULL, file);
675 32 read_successfully = corrupt_copies < file_system.copies;
676 32 break;
677 }
678 32 gcry_free(mac_data);
679
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 32 times.
32 if (read_successfully)
680 return true;
681 /*
682 * somehow we failed to read a complete copy of the file, despite
683 * knowing that a complete copy existed when stat’d
684 */
685 ✗ return errno = EIO, false;
686 }
687
688 47 extern bool stegfs_file_write(stegfs_file_s *file)
689 {
690 stegfs_block_s block;
691 47 lldiv_t d = lldiv(file->size - (file->size < (sizeof block.data - file_system.head_offset) ? file->size : (sizeof block.data - file_system.head_offset)), SIZE_BYTE_DATA);
692 47 uint64_t blocks = d.quot + (d.rem > 0);
693 uint64_t z = file->size;
694 47 size_t mac_length = gcry_mac_get_algo_maclen(file_system.mac);
695 47 uint8_t *mac_data = m_gcry_calloc_secure(mac_length, sizeof( uint8_t ));
696
697
2/2
✓ Branch 1 taken 40 times.
✓ Branch 2 taken 7 times.
47 if (!stegfs_file_stat(file, true))
698 {
699
2/2
✓ Branch 0 taken 80 times.
✓ Branch 1 taken 39 times.
119 for (unsigned i = 0; i < file_system.copies; i++)
700 {
701 /*
702 * allocate inodes, mark as in use (inode locations
703 * are calculated in stegfs_file_stat)
704 */
705 80 file_system.blocks.in_use[normalize(file->inodes[i])] = true;
706
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 78 times.
80 if (file_system.show_bloc)
707 2 m_asprintf(&file_system.blocks.file[normalize(file->inodes[i])], "../%s/%s", file->path, file->name);
708 80 file_system.blocks.used++;
709 /*
710 * note-to-self: allocate 2 more blocks than is
711 * necessary so that block[0] indicates how many
712 * blocks there are (needed) * and block[last] is
713 * kept 0x00 as an “end of chain” guard
714 */
715 80 file->blocks[i] = m_calloc(blocks + 2, sizeof blocks);
716 80 file->blocks[i][0] = blocks;
717
2/2
✓ Branch 0 taken 589 times.
✓ Branch 1 taken 79 times.
668 for (uint64_t j = 1; j <= blocks; j++)
718
2/2
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 588 times.
589 if (!(file->blocks[i][j] = block_assign(file->path)))
719 {
720 /* failed to allocate space; free what we had claimed */
721
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 1 times.
3 for (unsigned k = 0; k <= i; k++)
722 {
723 2 block_delete(file->inodes[k]);
724
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 if (file->blocks[k])
725 {
726
2/2
✓ Branch 0 taken 30 times.
✓ Branch 1 taken 2 times.
32 for (uint64_t l = 1; l <= file->blocks[k][0]; l++)
727 30 block_delete(file->blocks[k][l]);
728 2 free(file->blocks[k]);
729 2 file->blocks[k] = NULL;
730 }
731 }
732 1 return errno = ENOSPC, false;
733 }
734
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 588 times.
588 else if (file_system.show_bloc)
735 ✗ m_asprintf(&file_system.blocks.file[normalize(file->blocks[i][j])], "../%s/%s", file->path, file->name);
736 }
737 }
738 46 file->size = z; /* stat can cause size to be reset to 0 */
739
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 45 times.
46 if (blocks > file->blocks[0][0]) /* need more blocks than we have */
740 {
741
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 for (unsigned i = 0; i < file_system.copies; i++)
742 {
743 1 uint64_t old_blocks = file->blocks[i][0];
744 1 file->blocks[i] = m_realloc(file->blocks[i], (blocks + 2) * sizeof blocks);
745 1 memset(file->blocks[i] + old_blocks + 1, 0,
746 1 (blocks - old_blocks + 1) * sizeof *file->blocks[i]);
747
1/2
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
4 for (uint64_t j = old_blocks + 1; j <= blocks; j++)
748
2/2
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 3 times.
4 if (!(file->blocks[i][j] = block_assign(file->path)))
749 {
750 /* failed to allocate space; release only newly claimed blocks */
751
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
2 for (unsigned k = 0; k <= i; k++)
752
2/2
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 1 times.
6 for (uint64_t l = file->blocks[k][0] + 1; l <= blocks; l++)
753 {
754
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 2 times.
5 if (file->blocks[k][l])
755 3 block_delete(file->blocks[k][l]);
756 5 file->blocks[k][l] = 0;
757 }
758 1 return errno = ENOSPC, false;
759 }
760
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
3 else if (file_system.show_bloc)
761 ✗ m_asprintf(&file_system.blocks.file[normalize(file->blocks[i][j])], "../%s/%s", file->path, file->name);
762 }
763 ✗ for (unsigned i = 0; i < file_system.copies; i++)
764 ✗ file->blocks[i][0] = blocks;
765 }
766
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 44 times.
45 else if (blocks < file->blocks[0][0]) /* have more blocks than we need */
767
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 1 times.
3 for (unsigned i = 0; i < file_system.copies; i++)
768 {
769
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 2 times.
8 for (uint64_t j = blocks + 1; j <= file->blocks[i][0]; j++)
770 6 block_delete(file->blocks[i][j]);
771 2 file->blocks[i] = m_realloc(file->blocks[i], (blocks + 2) * sizeof blocks);
772 2 file->blocks[i][0] = blocks;
773 }
774 /*
775 * write the data
776 */
777
2/2
✓ Branch 0 taken 90 times.
✓ Branch 1 taken 45 times.
135 for (unsigned i = 0; i < file_system.copies; i++)
778 {
779 90 gcry_cipher_hd_t cipher_handle = init_cipher(file, i);
780 90 gcry_mac_hd_t mac_handle = init_mac(file, i);
781
2/2
✓ Branch 0 taken 568 times.
✓ Branch 1 taken 90 times.
658 for (uint64_t j = 1, k = 0; j <= blocks; j++, k++)
782 {
783 size_t l = sizeof block.data;
784
2/2
✓ Branch 0 taken 24 times.
✓ Branch 1 taken 544 times.
568 if ((l + k * sizeof block.data) > (file->size - (sizeof block.data - file_system.head_offset)))
785 24 l = l - ((l + k * sizeof block.data) - (file->size - (sizeof block.data - file_system.head_offset)));
786 568 gcry_create_nonce(&block, sizeof block);
787 568 memcpy(block.data, file->data + (sizeof block.data - file_system.head_offset) + k * sizeof block.data, l);
788 568 block.next = htonll(file->blocks[i][j + 1]);
789
2/2
✓ Branch 0 taken 284 times.
✓ Branch 1 taken 284 times.
568 if (!i)
790 284 gcry_mac_write(mac_handle, block.data, sizeof block.data);
791
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 568 times.
568 if (!block_write(file->blocks[i][j], block, cipher_handle, file->path))
792 {
793 /* see below (where inode blocks are written) */
794 ✗ for (unsigned k = 0; k <= i; k++)
795 ✗ for (uint64_t l = 1; l <= j; l++)
796 ✗ block_delete(file->blocks[k][l]);
797 ✗ gcry_cipher_close(cipher_handle);
798 ✗ gcry_mac_close(mac_handle);
799 ✗ return false;
800 }
801 }
802 /* store the calculated MAC for read verification */
803
2/2
✓ Branch 0 taken 45 times.
✓ Branch 1 taken 45 times.
90 if (!i)
804 45 gcry_mac_read(mac_handle, mac_data, &mac_length);
805 90 gcry_mac_close(mac_handle);
806 90 gcry_cipher_close(cipher_handle);
807 }
808 /*
809 * write file inode blocks
810 */
811 stegfs_block_s inode;
812 45 gcry_create_nonce(&inode, sizeof inode);
813 uint64_t first[SIZE_LONG_DATA];
814
2/2
✓ Branch 0 taken 15 times.
✓ Branch 1 taken 30 times.
45 if (blocks)
815
2/2
✓ Branch 0 taken 30 times.
✓ Branch 1 taken 15 times.
45 for (unsigned i = 0, j = 1; i < file_system.copies; i++, j++)
816 30 first[j] = htonll(file->blocks[i][1]);
817 else
818 30 gcry_create_nonce(first, sizeof first);
819 45 first[0] = htonll(file->time);
820 45 memcpy(inode.data, first, sizeof first);
821 45 memcpy(inode.data + ((file_system.copies + 1) * sizeof( uint64_t )), mac_data, mac_length);
822 45 gcry_free(mac_data);
823
3/4
✓ Branch 0 taken 43 times.
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 43 times.
✗ Branch 3 not taken.
45 if (file->data && file->size)
824 43 memcpy(inode.data + file_system.head_offset, file->data, file->size < (sizeof inode.data - file_system.head_offset) ? file->size : (sizeof inode.data - file_system.head_offset));
825 45 inode.next = htonll(file->size);
826
2/2
✓ Branch 0 taken 90 times.
✓ Branch 1 taken 45 times.
135 for (unsigned i = 0; i < file_system.copies; i++)
827 {
828 90 gcry_cipher_hd_t cipher_handle = init_cipher(file, i);
829
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 90 times.
90 if (!block_write(file->inodes[i], inode, cipher_handle, file->path))
830 {
831 ✗ for (unsigned j = 0; j <= i; j++)
832 /*
833 * it’s likely that if a write failed above,
834 * it won’t work here either, but at least
835 * the block will be marked as available
836 * (in fact if a call to write fails it’s
837 * likely all subsequent write will fail
838 * too)
839 */
840 ✗ block_delete(file->inodes[j]);
841 ✗ stegfs_file_delete(file);
842 ✗ gcry_cipher_close(cipher_handle);
843 ✗ return false;
844 }
845 90 gcry_cipher_close(cipher_handle);
846 }
847
848 45 file->loaded = true;
849 45 file->dirty = false;
850 45 stegfs_cache_add(NULL, file);
851 45 return true;
852 }
853
854 1 extern void stegfs_file_unlink(stegfs_file_s *file)
855 {
856 stegfs_block_s block;
857 1 lldiv_t d = lldiv(file->size - (file->size < (sizeof block.data - file_system.head_offset)
858 1 ? file->size : (sizeof block.data - file_system.head_offset)), SIZE_BYTE_DATA);
859 1 uint64_t blocks = d.quot + (d.rem > 0);
860
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 1 times.
3 for (unsigned i = 0; i < file_system.copies; i++)
861 {
862
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 if (file->inodes[i])
863 {
864 2 block_delete(file->inodes[i]);
865
2/6
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 2 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
2 for (uint64_t j = 1; file->blocks[i] && j <= blocks && file->blocks[i][j]; j++)
866 ✗ block_delete(file->blocks[i][j]);
867 }
868 2 file->inodes[i] = 0;
869 2 free(file->blocks[i]);
870 2 file->blocks[i] = NULL;
871 }
872 1 file->dirty = false;
873 1 }
874
875 2 extern void stegfs_file_delete(stegfs_file_s *file)
876 {
877
3/4
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 1 times.
2 if (!file->unlinked && !stegfs_file_stat(file))
878 ✗ goto rfc;
879 stegfs_block_s block;
880 2 lldiv_t d = lldiv(file->size - (file->size < (sizeof block.data - file_system.head_offset) ? file->size : (sizeof block.data - file_system.head_offset)), SIZE_BYTE_DATA);
881 2 uint64_t blocks = d.quot + (d.rem > 0);
882
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 2 times.
6 for (unsigned i = 0; i < file_system.copies; i++)
883 {
884
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 2 times.
4 if (!file->inodes[i])
885 2 continue;
886 2 block_delete(file->inodes[i]);
887
2/6
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 2 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
2 for (uint64_t j = 1; file->blocks[i] && j <= blocks && file->blocks[i][j]; j++)
888 ✗ block_delete(file->blocks[i][j]);
889 }
890 2 rfc:
891
2/4
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 2 times.
2 char *p = m_strdupf("%s/%s", path_equals(file->path, DIR_SEPARATOR) ? "" : file->path, file->name);
892
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
2 if (file->unlinked)
893 1 stegfs_cache_remove_file(file);
894 else
895 stegfs_cache_remove(p);
896 2 free(p);
897 2 return;
898 }
899
900 /*
901 * block functions
902 */
903
904 266 static bool block_read(uint64_t bid, stegfs_block_s *block, gcry_cipher_hd_t cipher, const char * const restrict path)
905 {
906 266 errno = EXIT_SUCCESS;
907 266 bid %= (file_system.size / file_system.blocksize);
908
2/4
✓ Branch 0 taken 266 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 266 times.
266 if (!bid || (bid * file_system.blocksize + file_system.blocksize > file_system.size))
909 ✗ return errno = EINVAL, false;
910 266 memcpy(block, file_system.memory + (bid * file_system.blocksize), sizeof( stegfs_block_s ));
911 266 size_t hash_length = gcry_md_get_algo_dlen(file_system.hash);
912 266 uint8_t *hash_buffer = m_gcry_malloc_secure(hash_length);
913 /* ignore path check in root */
914
3/4
✓ Branch 0 taken 266 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
✓ Branch 3 taken 264 times.
266 if (!path_equals(path, DIR_SEPARATOR))
915 {
916 /* check path hash */
917 2 gcry_md_hash_buffer(file_system.hash, hash_buffer, path, strlen(path));
918
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 if (memcmp(block->path, hash_buffer, hash_length))
919 {
920 2 gcry_free(hash_buffer);
921 2 return false;
922 }
923 }
924 #ifdef __DEBUG__
925 (void)cipher;
926 #else
927 /* decrypt block, but not the path */
928 void *ptr = block;
929 264 ptr += sizeof block->path;
930 264 gcry_cipher_decrypt(cipher, ptr, file_system.blocksize - sizeof block->path, NULL, 0);
931 #endif
932 /* check data hash */
933 264 gcry_md_hash_buffer(file_system.hash, hash_buffer, block->data, sizeof block->data);
934
2/2
✓ Branch 0 taken 90 times.
✓ Branch 1 taken 174 times.
264 if (memcmp(block->hash, hash_buffer, hash_length))
935 {
936 90 gcry_free(hash_buffer);
937 90 return false;
938 }
939 174 gcry_free(hash_buffer);
940 174 return true;
941 }
942
943 658 static bool block_write(uint64_t bid, stegfs_block_s block, gcry_cipher_hd_t cipher, const char * const restrict path)
944 {
945 658 errno = EXIT_SUCCESS;
946 658 bid %= (file_system.size / file_system.blocksize);
947
2/4
✓ Branch 0 taken 658 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 658 times.
658 if (!bid || (bid * file_system.blocksize + file_system.blocksize > file_system.size))
948 ✗ return errno = EINVAL, false;
949 658 gcry_create_nonce((void *)block.path, sizeof block.path);
950 658 size_t hash_length = gcry_md_get_algo_dlen(file_system.hash);
951 658 uint8_t *hash_buffer = m_gcry_malloc_secure(hash_length);
952
2/4
✓ Branch 0 taken 658 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 658 times.
658 if (!path_equals(path, DIR_SEPARATOR))
953 {
954 /* compute path hash */
955 ✗ gcry_md_hash_buffer(file_system.hash, hash_buffer, path, strlen(path));
956 ✗ memcpy(block.path, hash_buffer, hash_length > sizeof block.path ? sizeof block.path : hash_length);
957 }
958 /* compute data hash (includes 0x00 after EOF) */
959 658 gcry_md_hash_buffer(file_system.hash, hash_buffer, block.data, sizeof block.data);
960 658 memcpy(block.hash, hash_buffer, hash_length > sizeof block.hash ? sizeof block.hash : hash_length);
961 658 gcry_free(hash_buffer);
962 #ifdef __DEBUG__
963 (void)cipher;
964 #else
965 /* encrypt the data, but not the path */
966 void *ptr = &block;
967 ptr += sizeof block.path;
968 658 gcry_cipher_encrypt(cipher, ptr, file_system.blocksize - sizeof block.path, NULL, 0);
969 #endif
970 /*
971 * TODO: When ECC, sizeof block.data must be SIZE_BYTE_DATA
972 * (1,976) - 56 == 1920 as shown by:
973 * 2,048 ÷ 256 = 8 (number of ECC blocks per FS block)
974 * 249 × 8 = 1,992 (total capacity of FS block)
975 * 1,992 - 32 - 32 - 8 = 1,920 (capacity of FS block.data)
976 */
977
978 658 memcpy(file_system.memory + (bid * file_system.blocksize), &block, sizeof block);
979 //msync(file_system.memory + (bid * file_system.blocksize), sizeof block, MS_SYNC);
980 658 return true;
981 }
982
983 45 static void block_delete(uint64_t bid)
984 {
985 45 bid %= (file_system.size / file_system.blocksize);
986
3/4
✓ Branch 0 taken 43 times.
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 43 times.
✗ Branch 3 not taken.
45 if (!bid || !file_system.blocks.in_use[bid])
987 return;
988 stegfs_block_s block;
989 43 memcpy(&block, file_system.memory + (bid * file_system.blocksize), sizeof block);
990 43 gcry_create_nonce(&block, file_system.blocksize);
991
1/2
✓ Branch 0 taken 43 times.
✗ Branch 1 not taken.
43 if (bid * file_system.blocksize + file_system.blocksize > file_system.size)
992 return;
993 43 memcpy(file_system.memory + (bid * file_system.blocksize), &block, sizeof block);
994 //msync(file_system.memory + (bid * file_system.blocksize), sizeof block, MS_SYNC);
995 43 file_system.blocks.in_use[bid] = false;
996
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 43 times.
43 if (file_system.show_bloc)
997 {
998 ✗ free(file_system.blocks.file[bid]);
999 ✗ file_system.blocks.file[bid] = NULL;
1000 }
1001 43 file_system.blocks.used--;
1002 43 return;
1003 }
1004
1005 3291 static bool block_in_use(uint64_t bid, const char * const restrict path)
1006 {
1007 3291 bid %= (file_system.size / file_system.blocksize);
1008
2/2
✓ Branch 0 taken 3282 times.
✓ Branch 1 taken 9 times.
3291 if (!bid)
1009 return true; /* superblock always in use */
1010 /*
1011 * check if the block is in the cache
1012 */
1013
2/2
✓ Branch 0 taken 591 times.
✓ Branch 1 taken 2691 times.
3282 if (file_system.blocks.in_use[bid])
1014 return true;
1015 /*
1016 * block not found in cache; check if this might belong to a file
1017 * in this directory, or any parent directory
1018 */
1019 #ifndef __DEBUG__
1020 591 size_t hash_length = gcry_md_get_algo_dlen(file_system.hash);
1021 591 uint8_t *hash_buffer = m_gcry_malloc_secure(hash_length);
1022 591 char *p = NULL;
1023 591 uint16_t hierarchy = dir_get_deep(path);
1024
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 591 times.
591 for (uint16_t i = 1; i < hierarchy; i++)
1025 {
1026 ✗ char *e = dir_get_part(path, i);
1027 ✗ char *previous = p;
1028 ✗ m_asprintf(&p, "%s/%s", previous ? : "", e ? : "");
1029 ✗ free(previous);
1030 ✗ if (e)
1031 ✗ free(e);
1032 ✗ gcry_md_hash_buffer(file_system.hash, hash_buffer, p, strlen(p));
1033 ✗ if (!memcmp(hash_buffer, file_system.memory + (bid * file_system.blocksize), hash_length))
1034 {
1035 ✗ gcry_free(hash_buffer);
1036 /*
1037 * block detected as being used by a file that exists
1038 * closer to the root of the system; mark it as such
1039 */
1040 ✗ file_system.blocks.in_use[bid] = true;
1041 ✗ file_system.blocks.used++;
1042 ✗ free(p);
1043 ✗ return true;
1044 }
1045 }
1046 591 free(p);
1047 591 gcry_free(hash_buffer);
1048 #else
1049 (void)path;
1050 #endif
1051 591 return false;
1052 }
1053
1054 /*
1055 * NB block_assign will return a block number in the range of 0 to UINT64_MAX
1056 * so that when the value is stored in the file system it’s not mostly 0’s
1057 * - we don’t want an attacker to know that most of some block of cipher
1058 * text is 0’s - translation into the valid range is done as necessary by
1059 * block_ functions
1060 */
1061 593 static uint64_t block_assign(const char * const restrict path)
1062 {
1063 uint64_t block;
1064 uint64_t tries = 0;
1065 do
1066 {
1067 3293 block = ((uint64_t)(uint32_t)lrand48() << 32) | (uint32_t)lrand48();
1068 /* eventually “timeout” after trying as many blocks as exists */
1069
2/2
✓ Branch 0 taken 3291 times.
✓ Branch 1 taken 2 times.
3293 if ((++tries) > file_system.size / file_system.blocksize)
1070 return 0;
1071 }
1072
2/2
✓ Branch 1 taken 2700 times.
✓ Branch 2 taken 591 times.
3291 while (block_in_use(block, path));
1073 591 file_system.blocks.in_use[normalize(block)] = true;
1074 591 file_system.blocks.used++;
1075 591 return block;
1076 }
1077
1078 473 static gcry_cipher_hd_t init_cipher(const stegfs_file_s * const restrict file, uint8_t ivi)
1079 {
1080 /* obtain handles */
1081 gcry_cipher_hd_t cipher;
1082 473 gcry_cipher_open(&cipher, file_system.cipher, file_system.mode, GCRY_CIPHER_SECURE);
1083 gcry_md_hd_t hash;
1084 473 gcry_md_open(&hash, file_system.hash, GCRY_MD_FLAG_SECURE);
1085 473 size_t hash_length = gcry_md_get_algo_dlen(file_system.hash);
1086 gcry_md_hd_t salt;
1087 473 gcry_md_open(&salt, file_system.hash, GCRY_MD_FLAG_SECURE);
1088 473 size_t salt_length = gcry_md_get_algo_dlen(file_system.hash);
1089 /* create the initial key for the encryption algorithm */
1090 473 size_t key_length = gcry_cipher_get_algo_keylen(file_system.cipher);
1091 /* allocate space for whichever is larger */
1092 473 uint8_t *key_data = m_gcry_calloc_secure(key_length > hash_length ? key_length : hash_length, sizeof( uint8_t ));
1093
2/2
✓ Branch 0 taken 451 times.
✓ Branch 1 taken 22 times.
473 if (file_system.version < VERSION_202X_XX)
1094 {
1095 451 gcry_md_write(hash, file->path, strlen(file->path));
1096 451 gcry_md_write(hash, file->name, strlen(file->name));
1097
1/2
✓ Branch 0 taken 451 times.
✗ Branch 1 not taken.
451 if (file->pass)
1098 451 gcry_md_write(hash, file->pass, strlen(file->pass));
1099 else
1100 ✗ gcry_md_write(hash, "", strlen(""));
1101 451 memcpy(key_data, gcry_md_read(hash, file_system.hash), key_length);
1102 }
1103 else
1104 {
1105 22 gcry_md_write(hash, file->name, strlen(file->name));
1106
1/2
✓ Branch 0 taken 22 times.
✗ Branch 1 not taken.
22 if (file->pass)
1107 22 gcry_md_write(hash, file->pass, strlen(file->pass));
1108 else
1109 ✗ gcry_md_write(hash, "", strlen(""));
1110 /* salt & kdf */
1111 22 gcry_md_write(salt, file->path, strlen(file->path));
1112 22 const uint8_t *hash_data = gcry_md_read(hash, file_system.hash);
1113 22 const uint8_t *salt_data = gcry_md_read(salt, file_system.hash);
1114 22 gcry_kdf_derive(hash_data, hash_length, GCRY_KDF_PBKDF2, file_system.hash, salt_data, salt_length, file_system.kdf_iterations, key_length, key_data);
1115 }
1116 473 gcry_cipher_setkey(cipher, key_data, key_length);
1117 473 gcry_free(key_data);
1118 473 gcry_md_close(salt);
1119 /* create the iv for the encryption algorithm */
1120 473 gcry_md_reset(hash);
1121 473 size_t iv_length = gcry_cipher_get_algo_blklen(file_system.cipher);
1122 /* allocate space for whichever is larger */
1123 473 uint8_t *iv = m_gcry_calloc_secure(iv_length > hash_length ? iv_length : hash_length, sizeof( uint8_t ));
1124
1/2
✓ Branch 0 taken 473 times.
✗ Branch 1 not taken.
473 if (file->pass)
1125 473 gcry_md_write(hash, file->pass, strlen(file->pass));
1126 else
1127 ✗ gcry_md_write(hash, "", strlen(""));
1128 473 gcry_md_write(hash, file->name, strlen(file->name));
1129 473 gcry_md_write(hash, file->path, strlen(file->path));
1130 473 gcry_md_write(hash, &ivi, sizeof ivi);
1131 473 memcpy(iv, gcry_md_read(hash, file_system.hash), iv_length);
1132 473 gcry_cipher_setiv(cipher, iv, iv_length);
1133 473 gcry_md_close(hash);
1134 473 return cipher;
1135 }
1136
1137 123 static gcry_mac_hd_t init_mac(const stegfs_file_s * const restrict file, uint8_t ivi)
1138 {
1139 /* obtain handles */
1140 gcry_mac_hd_t mac;
1141 123 gcry_mac_open(&mac, file_system.mac, GCRY_MAC_FLAG_SECURE, NULL);
1142 123 size_t mac_key_length = gcry_mac_get_algo_keylen(file_system.mac);
1143 gcry_md_hd_t hash;
1144 123 gcry_md_open(&hash, file_system.hash, GCRY_MD_FLAG_SECURE);
1145 123 size_t hash_length = gcry_md_get_algo_dlen(file_system.hash);
1146 gcry_md_hd_t salt;
1147 123 gcry_md_open(&salt, file_system.hash, GCRY_MD_FLAG_SECURE);
1148 123 size_t salt_length = gcry_md_get_algo_dlen(file_system.hash);
1149 /* allocate space for whichever is larger */
1150 123 gcry_md_write(hash, file->name, strlen(file->name));
1151
1/2
✓ Branch 0 taken 123 times.
✗ Branch 1 not taken.
123 if (file->pass)
1152 123 gcry_md_write(hash, file->pass, strlen(file->pass));
1153 else
1154 ✗ gcry_md_write(hash, "", strlen(""));
1155 /* salt & kdf */
1156 123 gcry_md_write(salt, file->path, strlen(file->path));
1157 123 const uint8_t *hash_data = gcry_md_read(hash, file_system.hash);
1158 123 const uint8_t *salt_data = gcry_md_read(salt, file_system.hash);
1159 /* initialise the mac */
1160 123 uint8_t *mac_key_data = m_gcry_calloc_secure(mac_key_length, sizeof( byte_t ));
1161 123 gcry_kdf_derive(hash_data, hash_length, GCRY_KDF_PBKDF2, file_system.hash, salt_data, salt_length, file_system.kdf_iterations, mac_key_length, mac_key_data);
1162 123 gcry_mac_setkey(mac, mac_key_data, mac_key_length);
1163 123 gcry_free(mac_key_data);
1164 123 gcry_md_close(salt);
1165 /* create the iv for the encryption algorithm */
1166 123 gcry_md_reset(hash);
1167 123 size_t iv_length = gcry_cipher_get_algo_blklen(file_system.cipher);
1168 /* allocate space for whichever is larger */
1169 123 uint8_t *iv = m_gcry_calloc_secure(iv_length > hash_length ? iv_length : hash_length, sizeof( uint8_t ));
1170
1/2
✓ Branch 0 taken 123 times.
✗ Branch 1 not taken.
123 if (file->pass)
1171 123 gcry_md_write(hash, file->pass, strlen(file->pass));
1172 else
1173 ✗ gcry_md_write(hash, "", strlen(""));
1174 123 gcry_md_write(hash, file->name, strlen(file->name));
1175 123 gcry_md_write(hash, file->path, strlen(file->path));
1176 123 gcry_md_write(hash, &ivi, sizeof ivi);
1177 123 memcpy(iv, gcry_md_read(hash, file_system.hash), iv_length);
1178 123 const char *mac_name = mac_name_from_id(file_system.mac);
1179
2/4
✓ Branch 0 taken 123 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 123 times.
123 if (!strncmp("GMAC", mac_name, strlen("GMAC")) || !strncmp("POLY1305", mac_name, strlen("POLY1305")))
1180 ✗ gcry_mac_setiv(mac, iv, iv_length);
1181 123 gcry_md_close(hash);
1182 123 return mac;
1183 }
1184
1185 /*
1186 * cache functions
1187 */
1188
1189 /*
1190 * add path (directory) or file to the cache; if adding a file, the path
1191 * can be null as it will be taken from the file object
1192 */
1193 173 extern void stegfs_cache_add(const char * const restrict path, stegfs_file_s *file)
1194 {
1195 stegfs_cache_s *ptr = NULL;//&(file_system.cache);
1196 char *p = NULL;
1197
2/2
✓ Branch 0 taken 9 times.
✓ Branch 1 taken 164 times.
173 if (path)
1198 9 p = m_strdup(path);
1199 else
1200
3/4
✓ Branch 0 taken 164 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 4 times.
✓ Branch 3 taken 160 times.
168 p = m_strdupf("%s/%s", path_equals(file->path, DIR_SEPARATOR) ? "" : file->path, file->name);
1201
2/2
✓ Branch 1 taken 118 times.
✓ Branch 2 taken 55 times.
173 if ((ptr = stegfs_cache_exists(p, NULL)))
1202 118 goto c2a3; /* already in cache */
1203
1204 ptr = &file_system.cache;
1205 55 char *name = dir_get_name(p, PASSWORD_SEPARATOR);
1206 55 uint16_t hierarchy = dir_get_deep(p);
1207
2/2
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 55 times.
63 for (uint16_t i = 1; i < hierarchy; i++)
1208 {
1209 8 char *e = dir_get_part(p, i);
1210 bool found = false;
1211
1/2
✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
8 for (uint64_t j = 0; j < ptr->ents; j++)
1212
2/4
✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 8 times.
8 if (ptr->child[j]->name && !strcmp(ptr->child[j]->name, e))
1213 {
1214 ptr = ptr->child[j];
1215 found = true;
1216 break;
1217 }
1218 if (!found)
1219 {
1220 /*
1221 * use existing, NULL entry in array if available
1222 */
1223 uint64_t k = ptr->ents;
1224 bool reused = false;
1225 ✗ for (uint64_t j = 0; j < ptr->ents; j++)
1226 ✗ if (!ptr->child[j]->name)
1227 {
1228 k = j;
1229 reused = true;
1230 ✗ goto c2a1;
1231 }
1232 ✗ ptr->child = m_realloc(ptr->child, (k + 1) * sizeof(stegfs_cache_s *));
1233 ✗ ptr->ents++;
1234 c2a1:
1235 if (reused)
1236 ✗ free(ptr->child[k]);
1237 ✗ ptr->child[k] = m_calloc(sizeof( stegfs_cache_s ), sizeof( uint8_t ));
1238 ✗ ptr->child[k]->name = m_strdup(e);
1239 ✗ ptr = ptr->child[k];
1240 }
1241 8 free(e);
1242 }
1243 /* ditto */
1244 55 uint64_t j = ptr->ents;
1245 bool reused = false;
1246
2/2
✓ Branch 0 taken 51 times.
✓ Branch 1 taken 52 times.
103 for (uint64_t i = 0; i < ptr->ents; i++)
1247
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 48 times.
51 if (!ptr->child[i]->name)
1248 {
1249 j = i;
1250 reused = true;
1251 3 goto c2a2;
1252 }
1253 52 ptr->child = m_realloc(ptr->child, (j + 1) * sizeof(stegfs_cache_s *));
1254 52 ptr->ents++;
1255 c2a2:
1256 if (reused)
1257 3 free(ptr->child[j]);
1258 55 ptr->child[j] = m_calloc(sizeof( stegfs_cache_s ), sizeof( uint8_t ));
1259 55 ptr = ptr->child[j];
1260 55 ptr->name = name;
1261 173 c2a3:
1262
4/4
✓ Branch 0 taken 164 times.
✓ Branch 1 taken 9 times.
✓ Branch 2 taken 80 times.
✓ Branch 3 taken 84 times.
173 if (file && file != ptr->file)
1263 {
1264
2/2
✓ Branch 0 taken 46 times.
✓ Branch 1 taken 34 times.
80 if (!ptr->file)
1265 46 ptr->file = m_calloc(sizeof( stegfs_file_s ), sizeof( uint8_t ));
1266 /* set path and name */
1267 80 free(ptr->file->path);
1268 80 free(ptr->file->name);
1269 80 free(ptr->file->pass);
1270 80 ptr->file->path = NULL;
1271 80 ptr->file->name = NULL;
1272 80 ptr->file->pass = NULL;
1273 80 m_asprintf(&ptr->file->path, "%s", file->path);
1274 80 m_asprintf(&ptr->file->name, "%s", file->name);
1275 80 m_asprintf(&ptr->file->pass, "%s", file->pass);
1276 /* set inodes */
1277
2/2
✓ Branch 0 taken 160 times.
✓ Branch 1 taken 80 times.
240 for (unsigned i = 0; i < file_system.copies; i++)
1278 160 ptr->file->inodes[i] = file->inodes[i];
1279 /* set time and size */
1280 80 ptr->file->time = file->time;
1281 80 ptr->file->size = file->size;
1282 80 ptr->file->loaded = file->loaded;
1283 80 ptr->file->dirty = file->dirty;
1284
2/2
✓ Branch 0 taken 35 times.
✓ Branch 1 taken 45 times.
80 if (ptr->file->size)
1285 {
1286 /* copy data */
1287
2/2
✓ Branch 0 taken 15 times.
✓ Branch 1 taken 20 times.
35 if (file->data)
1288 {
1289 15 ptr->file->data = m_realloc(ptr->file->data, ptr->file->size);
1290 15 memcpy(ptr->file->data, file->data, ptr->file->size);
1291 }
1292 /* copy blocks */
1293 stegfs_block_s block;
1294 35 lldiv_t d = lldiv(file->size - (file->size < (sizeof block.data - file_system.head_offset) ? file->size : (sizeof block.data - file_system.head_offset)), SIZE_BYTE_DATA);
1295 35 uint64_t blocks = d.quot + (d.rem > 0);
1296
2/2
✓ Branch 0 taken 70 times.
✓ Branch 1 taken 35 times.
105 for (unsigned i = 0; i < file_system.copies; i++)
1297 {
1298 70 free(ptr->file->blocks[i]);
1299 70 ptr->file->blocks[i] = m_calloc(blocks + 2, sizeof blocks);
1300 70 ptr->file->blocks[i][0] = blocks;
1301
4/6
✓ Branch 0 taken 114 times.
✓ Branch 1 taken 70 times.
✓ Branch 2 taken 114 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 114 times.
✗ Branch 5 not taken.
184 for (uint64_t j = 1 ; j <= blocks && file->blocks[i] && file->blocks[i][j]; j++)
1302 114 ptr->file->blocks[i][j] = file->blocks[i][j];
1303 }
1304 }
1305 else
1306 {
1307 45 free(ptr->file->data);
1308 45 ptr->file->data = NULL;
1309
2/2
✓ Branch 0 taken 90 times.
✓ Branch 1 taken 45 times.
135 for (unsigned i = 0; i < file_system.copies; i++)
1310 {
1311 90 free(ptr->file->blocks[i]);
1312 90 ptr->file->blocks[i] = NULL;
1313 }
1314 }
1315 }
1316 173 free(p);
1317 // if (name)
1318 // free(name);
1319 173 return;
1320 }
1321
1322 /*
1323 * Check whether a given path is in the cache, copy the file object if it’s
1324 * a file, and return a pointer to the original
1325 */
1326 308 extern stegfs_cache_s *stegfs_cache_exists(const char * const restrict path, stegfs_cache_s *entry)
1327 {
1328 stegfs_cache_s *ptr = &(file_system.cache);
1329 308 char *name = dir_get_name(path, PASSWORD_SEPARATOR);
1330 308 uint16_t hierarchy = dir_get_deep(path);
1331
2/2
✓ Branch 0 taken 26 times.
✓ Branch 1 taken 308 times.
334 for (uint16_t i = 1; i < hierarchy; i++)
1332 {
1333 bool found = false;
1334 26 char *e = dir_get_part(path, i);
1335
1/2
✓ Branch 0 taken 26 times.
✗ Branch 1 not taken.
26 for (uint64_t j = 0; j < ptr->ents; j++)
1336
2/4
✓ Branch 0 taken 26 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 26 times.
26 if (ptr->child[j]->name && !strcmp(ptr->child[j]->name, e))
1337 {
1338 ptr = ptr->child[j];
1339 found = true;
1340 break;
1341 }
1342 26 free(e);
1343
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 26 times.
26 if (!found)
1344 ✗ goto done;
1345 }
1346
2/2
✓ Branch 0 taken 549 times.
✓ Branch 1 taken 84 times.
633 for (uint64_t i = 0; i < ptr->ents; i++)
1347
4/4
✓ Branch 0 taken 539 times.
✓ Branch 1 taken 10 times.
✓ Branch 2 taken 229 times.
✓ Branch 3 taken 310 times.
549 if (ptr->child[i]->name && !strcmp(ptr->child[i]->name, name)
1348
4/4
✓ Branch 0 taken 220 times.
✓ Branch 1 taken 9 times.
✓ Branch 2 taken 215 times.
✓ Branch 3 taken 5 times.
229 && (!ptr->child[i]->file || !ptr->child[i]->file->unlinked))
1349 {
1350 224 free(name);
1351
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 224 times.
224 if (entry)
1352 ✗ memcpy(entry, ptr->child[i], sizeof( stegfs_cache_s ));
1353 224 return ptr->child[i];
1354 }
1355 84 done:
1356
1/2
✓ Branch 0 taken 84 times.
✗ Branch 1 not taken.
84 if (name)
1357 84 free(name);
1358 return NULL;
1359 }
1360
1361 11 static void cache_remove(const char * const restrict path, stegfs_file_s * const file)
1362 {
1363 stegfs_cache_s *ptr = &(file_system.cache);
1364 11 char *name = dir_get_name(path, PASSWORD_SEPARATOR);
1365 11 uint16_t hierarchy = dir_get_deep(path);
1366
2/2
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 11 times.
19 for (uint16_t i = 1; i < hierarchy; i++)
1367 {
1368 bool found = false;
1369 8 char *e = dir_get_part(path, i);
1370
1/2
✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
8 for (uint64_t j = 0; j < ptr->ents; j++)
1371
2/4
✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 8 times.
8 if (ptr->child[j]->name && !strcmp(ptr->child[j]->name, e))
1372 {
1373 ptr = ptr->child[j];
1374 found = true;
1375 break;
1376 }
1377 8 free(e);
1378
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 8 times.
8 if (!found)
1379 ✗ goto done;
1380 }
1381 bool found = false;
1382
1/2
✓ Branch 0 taken 11 times.
✗ Branch 1 not taken.
11 for (uint64_t i = 0; i < ptr->ents; i++)
1383
2/4
✓ Branch 0 taken 11 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 11 times.
✗ Branch 3 not taken.
11 if (ptr->child[i]->name && !strcmp(ptr->child[i]->name, name)
1384
3/4
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 10 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 1 times.
11 && (!file || ptr->child[i]->file == file))
1385 {
1386 ptr = ptr->child[i];
1387 found = true;
1388 break;
1389 }
1390 if (!found)
1391 ✗ goto done;
1392 11 cache_free(ptr);
1393 11 done:
1394
1/2
✓ Branch 0 taken 11 times.
✗ Branch 1 not taken.
11 if (name)
1395 11 free(name);
1396 11 return;
1397 }
1398
1399 9 extern void stegfs_cache_remove(const char * const restrict path)
1400 {
1401 10 cache_remove(path, NULL);
1402 9 }
1403
1404 1 extern void stegfs_cache_remove_file(stegfs_file_s * const restrict file)
1405 {
1406
2/4
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 1 times.
1 char *path = m_strdupf("%s/%s", path_equals(file->path, DIR_SEPARATOR) ? "" : file->path, file->name);
1407 1 cache_remove(path, file);
1408 1 free(path);
1409 1 }
1410