GCC Code Coverage Report


Directory: ./
File: src/main.c
Date: 2026-11-08 20:01:15
Exec Total Coverage
Lines: 303 530 57.2%
Functions: 29 46 63.0%
Branches: 184 371 49.6%

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
22 #include <errno.h>
23 #include <error.h>
24
25 #define FUSE_USE_VERSION 27
26 #include <fuse.h>
27
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <pthread.h>
31
32 #include <string.h>
33 #include <inttypes.h>
34 #include <stdbool.h>
35
36 #include <limits.h>
37 #include <fcntl.h>
38
39 #include <sys/statvfs.h>
40 #include <sys/stat.h>
41
42 #include <gcrypt.h>
43
44 /* submodule includes */
45
46 #include "mem.h"
47 #include "config.h"
48 #include "ccrypt.h"
49 #include "dir.h"
50
51 /* project includes */
52
53 #include "stegfs.h"
54
55
56 /*
57 * standard file system functions (used by fuse)
58 */
59 static int fuse_stegfs_statfs(const char *, struct statvfs *);
60 static int fuse_stegfs_getattr(const char *, struct stat *);
61 static int fuse_stegfs_mkdir(const char *, mode_t);
62 static int fuse_stegfs_rmdir(const char *);
63 static int fuse_stegfs_readdir(const char *, void *, fuse_fill_dir_t, off_t, struct fuse_file_info *);
64 static int fuse_stegfs_unlink(const char *);
65 static int fuse_stegfs_read(const char *, char *, size_t, off_t , struct fuse_file_info *);
66 static int fuse_stegfs_write(const char *, const char *, size_t, off_t , struct fuse_file_info *);
67 static int fuse_stegfs_open(const char *, struct fuse_file_info *);
68 static int fuse_stegfs_release(const char *, struct fuse_file_info *);
69 static int fuse_stegfs_truncate(const char *, off_t);
70 static int fuse_stegfs_ftruncate(const char *, off_t, struct fuse_file_info *);
71 #ifdef STEGFS_FALLOCATE
72 static int fuse_stegfs_fallocate(const char *, int, off_t, off_t, struct fuse_file_info *);
73 #endif
74 static int fuse_stegfs_create(const char *, mode_t, struct fuse_file_info *);
75 static int fuse_stegfs_mknod(const char *, mode_t, dev_t);
76 static void fuse_stegfs_destroy(void *);
77 /*
78 * empty functions; required by fuse, but not used by stegfs
79 */
80 static int fuse_stegfs_readlink(const char *, char *, size_t);
81 static int fuse_stegfs_utime(const char *, struct utimbuf *);
82 static int fuse_stegfs_chmod(const char *, mode_t);
83 static int fuse_stegfs_chown(const char *, uid_t, gid_t);
84 static int fuse_stegfs_flush(const char *, struct fuse_file_info *);
85
86 static pthread_rwlock_t filesystem_lock = PTHREAD_RWLOCK_INITIALIZER;
87
88 typedef struct open_file_s
89 {
90 struct open_file_s *next;
91 stegfs_file_s *file;
92 char *path;
93 char *pass;
94 bool readable;
95 bool writable;
96 }
97 open_file_s;
98
99 static open_file_s *open_files;
100
101 16 static void filesystem_read_lock(void)
102 {
103 16 int result = pthread_rwlock_rdlock(&filesystem_lock);
104
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 16 times.
16 if (result)
105 ✗ die("Unable to read-lock filesystem state: %s", strerror(result));
106 16 }
107
108 60 static void filesystem_write_lock(void)
109 {
110 60 int result = pthread_rwlock_wrlock(&filesystem_lock);
111
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 60 times.
60 if (result)
112 ✗ die("Unable to write-lock filesystem state: %s", strerror(result));
113 60 }
114
115 76 static void filesystem_unlock(void)
116 {
117 76 int result = pthread_rwlock_unlock(&filesystem_lock);
118
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 76 times.
76 if (result)
119 ✗ die("Unable to unlock filesystem state: %s", strerror(result));
120 76 }
121
122 #define FUSE_CALLBACK(name, lock, parameters, arguments) \
123 static int dispatch_##name parameters \
124 { \
125 lock(); \
126 int result = fuse_stegfs_##name arguments; \
127 filesystem_unlock(); \
128 return result; \
129 }
130
131 ✗ FUSE_CALLBACK(statfs, filesystem_read_lock, (const char *path, struct statvfs *buf), (path, buf))
132 1 FUSE_CALLBACK(getattr, filesystem_read_lock, (const char *path, struct stat *buf), (path, buf))
133 ✗ FUSE_CALLBACK(mkdir, filesystem_write_lock, (const char *path, mode_t mode), (path, mode))
134 ✗ FUSE_CALLBACK(rmdir, filesystem_write_lock, (const char *path), (path))
135 ✗ FUSE_CALLBACK(readdir, filesystem_read_lock, (const char *path, void *buf, fuse_fill_dir_t filler, off_t offset, struct fuse_file_info *info), (path, buf, filler, offset, info))
136 1 FUSE_CALLBACK(unlink, filesystem_write_lock, (const char *path), (path))
137 15 FUSE_CALLBACK(read, filesystem_read_lock, (const char *path, char *buf, size_t size, off_t offset, struct fuse_file_info *info), (path, buf, size, offset, info))
138 11 FUSE_CALLBACK(write, filesystem_write_lock, (const char *path, const char *buf, size_t size, off_t offset, struct fuse_file_info *info), (path, buf, size, offset, info))
139 14 FUSE_CALLBACK(open, filesystem_write_lock, (const char *path, struct fuse_file_info *info), (path, info))
140 23 FUSE_CALLBACK(release, filesystem_write_lock, (const char *path, struct fuse_file_info *info), (path, info))
141 ✗ FUSE_CALLBACK(truncate, filesystem_write_lock, (const char *path, off_t offset), (path, offset))
142 1 FUSE_CALLBACK(ftruncate, filesystem_write_lock, (const char *path, off_t offset, struct fuse_file_info *info), (path, offset, info))
143 #ifdef STEGFS_FALLOCATE
144 FUSE_CALLBACK(fallocate, filesystem_write_lock, (const char *path, int mode, off_t offset, off_t length, struct fuse_file_info *info), (path, mode, offset, length, info))
145 #endif
146 10 FUSE_CALLBACK(create, filesystem_write_lock, (const char *path, mode_t mode, struct fuse_file_info *info), (path, mode, info))
147 ✗ FUSE_CALLBACK(mknod, filesystem_write_lock, (const char *path, mode_t mode, dev_t rdev), (path, mode, rdev))
148 ✗ FUSE_CALLBACK(readlink, filesystem_read_lock, (const char *path, char *buf, size_t size), (path, buf, size))
149 ✗ FUSE_CALLBACK(flush, filesystem_read_lock, (const char *path, struct fuse_file_info *info), (path, info))
150
151 #undef FUSE_CALLBACK
152
153 101 static open_file_s *open_file_lookup(const struct fuse_file_info *info)
154 {
155
5/8
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 101 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 79 times.
✓ Branch 7 taken 22 times.
103 if (!info || !info->fh)
156 return NULL;
157
3/6
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 82 times.
✗ Branch 5 not taken.
85 for (open_file_s *file = open_files; file; file = file->next)
158
5/6
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 1 times.
✓ Branch 4 taken 3 times.
✓ Branch 5 taken 79 times.
85 if ((uint64_t)(uintptr_t)file == info->fh)
159 return file;
160 return NULL;
161 }
162
163 38 static void open_file_attach(const char *path, struct fuse_file_info *info)
164 {
165
2/2
✓ Branch 0 taken 34 times.
✓ Branch 1 taken 4 times.
38 if (!info)
166 return;
167 34 open_file_s *file = m_calloc(1, sizeof *file);
168 34 file->path = m_strdup(path);
169 34 file->pass = dir_get_pass(path);
170 34 file->readable = (info->flags & O_ACCMODE) != O_WRONLY;
171 34 file->writable = (info->flags & O_ACCMODE) != O_RDONLY;
172 34 file->next = open_files;
173 34 open_files = file;
174 34 info->fh = (uint64_t)(uintptr_t)file;
175 34 stegfs_cache_s *cached = stegfs_cache_exists(path, NULL);
176
2/4
✓ Branch 0 taken 34 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 34 times.
✗ Branch 3 not taken.
34 if (cached && cached->file)
177 {
178 34 file->file = cached->file;
179 34 cached->file->open_count++;
180 }
181 }
182
183 34 static bool path_is_open_unlinked(const char *path)
184 {
185
2/2
✓ Branch 0 taken 34 times.
✓ Branch 1 taken 32 times.
66 for (open_file_s *file = open_files; file; file = file->next)
186
4/6
✓ Branch 0 taken 34 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
✓ Branch 3 taken 32 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 2 times.
34 if (file->file && file->file->unlinked && !strcmp(file->path, path))
187 return true;
188 return false;
189 }
190
191 34 static void open_file_detach(open_file_s *file, struct fuse_file_info *info)
192 {
193 34 open_file_s **link = &open_files;
194
2/4
✓ Branch 0 taken 34 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 34 times.
34 while (*link && *link != file)
195 ✗ link = &(*link)->next;
196
1/2
✓ Branch 0 taken 34 times.
✗ Branch 1 not taken.
34 if (*link)
197 34 *link = file->next;
198
1/2
✓ Branch 0 taken 34 times.
✗ Branch 1 not taken.
34 if (info)
199 34 info->fh = 0;
200 34 free(file->path);
201 34 free(file->pass);
202 34 free(file);
203 34 }
204
205 ✗ static void dispatch_destroy(void *ptr)
206 {
207 ✗ filesystem_write_lock();
208 fuse_stegfs_destroy(ptr);
209 ✗ filesystem_unlock();
210 ✗ }
211
212 9 static bool block_index_from_path(const char *path, uint64_t *index)
213 {
214 9 char *name = dir_get_name(path, PASSWORD_SEPARATOR);
215
2/4
✓ Branch 0 taken 9 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 9 times.
9 if (!name || !*name)
216 {
217 ✗ free(name);
218 ✗ return false;
219 }
220 9 char *end = NULL;
221 9 errno = 0;
222 9 uintmax_t value = strtoumax(name, &end, 10);
223
5/6
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 8 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 3 times.
✓ Branch 5 taken 5 times.
9 bool valid = !errno && end && !*end && value <= UINT64_MAX;
224 9 free(name);
225
2/2
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 4 times.
9 if (!valid)
226 return false;
227 5 *index = (uint64_t)value;
228 5 return true;
229 }
230
231 static struct fuse_operations fuse_stegfs_functions =
232 {
233 .statfs = dispatch_statfs,
234 .getattr = dispatch_getattr,
235 .mkdir = dispatch_mkdir,
236 .rmdir = dispatch_rmdir,
237 .readdir = dispatch_readdir,
238 .unlink = dispatch_unlink,
239 .read = dispatch_read,
240 .write = dispatch_write,
241 .open = dispatch_open,
242 .release = dispatch_release,
243 .truncate = dispatch_truncate,
244 .ftruncate = dispatch_ftruncate,
245 #ifdef STEGFS_FALLOCATE
246 .fallocate = dispatch_fallocate,
247 #endif
248 .create = dispatch_create,
249 .mknod = dispatch_mknod,
250 .destroy = dispatch_destroy,
251 .readlink = dispatch_readlink,
252 /*
253 * empty functions; required by fuse, but not used by stegfs
254 */
255 .utime = fuse_stegfs_utime,
256 .chmod = fuse_stegfs_chmod,
257 .chown = fuse_stegfs_chown,
258 .flush = dispatch_flush
259 };
260
261 ✗ extern bool is_stegfs(void)
262 {
263 ✗ return true;
264 }
265
266 2 static int fuse_stegfs_statfs(const char *path, struct statvfs *stvbuf)
267 {
268 2 errno = EXIT_SUCCESS;
269
270 (void)path;
271
272 2 stegfs_s file_system = stegfs_info();
273
274 2 stvbuf->f_bsize = SIZE_BYTE_BLOCK;
275 2 stvbuf->f_frsize = SIZE_BYTE_DATA;
276 2 stvbuf->f_blocks = file_system.size / SIZE_BYTE_BLOCK - 1;
277 2 stvbuf->f_bfree = file_system.blocks.used > 0
278 2 ? (stvbuf->f_blocks > file_system.blocks.used - 1
279 2 ? stvbuf->f_blocks - (file_system.blocks.used - 1) : 0)
280
2/4
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
2 : stvbuf->f_blocks;
281 2 stvbuf->f_bavail = stvbuf->f_bfree;
282 2 stvbuf->f_files = stvbuf->f_blocks;
283 2 stvbuf->f_ffree = stvbuf->f_bfree;
284 2 stvbuf->f_favail = stvbuf->f_bfree;
285 2 stvbuf->f_fsid = HASH_MAGIC_2;
286 2 stvbuf->f_flag = ST_NOSUID;
287 2 stvbuf->f_namemax = SYM_LENGTH;
288
289 2 return -errno;
290 }
291
292 4 static int fuse_stegfs_getattr(const char *path, struct stat *stbuf)
293 {
294 4 errno = EXIT_SUCCESS;
295
296 4 stegfs_s file_system = stegfs_info();
297 /* common attributes for root/files/directories */
298 4 stbuf->st_dev = (dev_t)HASH_MAGIC_2;
299 4 stbuf->st_ino = 0;
300 4 stbuf->st_mode = 0;//S_IFDIR | 0700;
301 4 stbuf->st_nlink = 0;
302 4 stbuf->st_uid = fuse_get_context()->uid;
303 4 stbuf->st_gid = fuse_get_context()->gid;
304 4 stbuf->st_atime = time(NULL);
305 4 stbuf->st_ctime = time(NULL);
306 4 stbuf->st_mtime = time(NULL);
307 4 stbuf->st_size = 0;
308 4 stbuf->st_blksize = 0;
309
310
2/4
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 4 times.
4 if (path_equals(DIR_SEPARATOR, path))
311 {
312 ✗ stbuf->st_mode = S_IFDIR | S_IRWXU;
313 ✗ stbuf->st_nlink = 2;
314 ✗ for (uint64_t i = 0; i < file_system.cache.ents; i++)
315 ✗ if (file_system.cache.child[i] && !file_system.cache.child[i]->file)
316 ✗ stbuf->st_nlink++;
317 }
318
4/6
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 3 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 3 times.
4 else if (file_system.show_bloc && path_equals(PATH_BLOC, path))
319 /* if path == /bloc/ */
320 ✗ stbuf->st_mode = S_IFDIR | S_IRUSR | S_IXUSR;
321
4/6
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 3 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 3 times.
✗ Branch 5 not taken.
4 else if (file_system.show_bloc && path_starts_with(PATH_BLOC, path))
322 3 {
323 /*
324 * if we’re looking at files in /bloc/ treat them as blocks (as
325 * that’s what we’re representing)
326 */
327 //stbuf->st_mode = S_IFBLK | S_IRUSR;
328 3 stbuf->st_mode = S_IFLNK | S_IRUSR;
329 3 stbuf->st_nlink = 1;
330
331 uint64_t ino;
332
1/2
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
3 uint64_t block_count = file_system.blocksize ? file_system.size / file_system.blocksize : 0;
333
4/4
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 1 times.
✓ Branch 4 taken 1 times.
3 if (!block_index_from_path(path, &ino) || ino >= block_count)
334 2 errno = ENOENT;
335
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 else if (file_system.blocks.file[ino])
336 1 stbuf->st_size = strlen(file_system.blocks.file[ino]);
337 }
338 else
339 {
340 stegfs_cache_s c;
341 1 memset(&c, 0x00, sizeof c);
342
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
1 if (stegfs_cache_exists(path, &c))
343 {
344 ✗ if (c.file)
345 {
346 ✗ for (unsigned i = 0; i < file_system.copies; i++)
347 ✗ if (c.file->inodes[i])
348 {
349 ✗ stbuf->st_ino = (ino_t)(c.file->inodes[i] % (file_system.size / SIZE_BYTE_BLOCK));
350 ✗ break;
351 }
352 /* it makes little sense (right now) to set this to anything else */
353 ✗ stbuf->st_mode = S_IFREG | S_IRUSR | S_IWUSR;
354 ✗ stbuf->st_ctime = c.file->time;
355 ✗ stbuf->st_mtime = c.file->time;
356 ✗ stbuf->st_size = c.file->size;
357 }
358 else
359 {
360 ✗ stbuf->st_mode = S_IFDIR | S_IRWXU;
361 ✗ stbuf->st_nlink = 2;
362 ✗ for (uint64_t i = 0; i < c.ents; i++)
363 ✗ if (c.child[i] && !c.child[i]->file)
364 ✗ stbuf->st_nlink++;
365 }
366 }
367 else
368 {
369
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
1 if (path_is_open_unlinked(path))
370 1 return errno = ENOENT, -errno;
371 stegfs_file_s file;
372 ✗ memset(&file, 0x00, sizeof file);
373 ✗ file.path = dir_get_path(path);
374 ✗ file.name = dir_get_name(path, PASSWORD_SEPARATOR);
375 ✗ file.pass = dir_get_pass(path);
376 ✗ if (stegfs_file_stat(&file))
377 {
378 ✗ for (unsigned i = 0; i < file_system.copies; i++)
379 ✗ if (file.inodes[i])
380 {
381 ✗ stbuf->st_ino = (ino_t)(file.inodes[i] % (file_system.size / SIZE_BYTE_BLOCK));
382 ✗ break;
383 }
384 ✗ stbuf->st_mode = S_IFREG | S_IRUSR | S_IWUSR;
385 ✗ stbuf->st_ctime = file.time;
386 ✗ stbuf->st_mtime = file.time;
387 ✗ stbuf->st_size = file.size;
388 }
389 else
390 ✗ errno = ENOENT;
391 ✗ free(file.path);
392 ✗ free(file.name);
393 ✗ free(file.pass);
394 }
395 }
396
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
3 if (S_ISREG(stbuf->st_mode))
397 {
398 ✗ stbuf->st_nlink = 1;
399 ✗ stbuf->st_blksize = SIZE_BYTE_DATA;
400 ✗ lldiv_t d = lldiv(stbuf->st_size, stbuf->st_blksize);
401 ✗ stbuf->st_blocks = d.quot + (d.rem > 0);
402 }
403
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
3 else if (S_ISDIR(stbuf->st_mode))
404 {
405 ✗ size_t hash_length = gcry_md_get_algo_dlen(file_system.hash);
406 ✗ uint8_t *hash_buffer = m_gcry_malloc_secure(hash_length);
407 ✗ gcry_md_hash_buffer(file_system.hash, hash_buffer, path, strlen(path));
408 ✗ memcpy(&(stbuf->st_ino), hash_buffer, sizeof stbuf->st_ino);
409 ✗ gcry_free(hash_buffer);
410 ✗ stbuf->st_ino %= (file_system.size / SIZE_BYTE_BLOCK);
411 ✗ stbuf->st_size = SIZE_BYTE_DATA;
412 }
413 3 return -errno;
414 }
415
416 8 static int fuse_stegfs_mkdir(const char *path, mode_t mode)
417 {
418 8 errno = EXIT_SUCCESS;
419
420 (void)mode;
421
422
2/2
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 7 times.
8 if (stegfs_cache_exists(path, NULL))
423 1 return errno = EEXIST, -errno;
424 7 stegfs_cache_add(path, NULL);
425
426 7 return -errno;
427 }
428
429 9 static int fuse_stegfs_rmdir(const char *path)
430 {
431 9 errno = EXIT_SUCCESS;
432
433 9 stegfs_s file_system = stegfs_info();
434
4/6
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 8 times.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
9 if (file_system.show_bloc && path_equals(path, PATH_BLOC))
435 1 return errno = EBUSY, -errno;
436
437 stegfs_cache_s *c = NULL;
438
2/2
✓ Branch 1 taken 7 times.
✓ Branch 2 taken 1 times.
8 if ((c = stegfs_cache_exists(path, NULL)))
439 {
440
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 7 times.
7 if (c->file)
441 ✗ errno = ENOTDIR;
442 else
443 {
444 bool empty = true;
445
2/2
✓ Branch 0 taken 7 times.
✓ Branch 1 taken 7 times.
14 for (uint64_t i = 0; i < c->ents; i++)
446
3/4
✗ Branch 0 not taken.
✓ Branch 1 taken 7 times.
✓ Branch 2 taken 5 times.
✓ Branch 3 taken 2 times.
7 if (c->child[i] && c->child[i]->name)
447 empty = false;
448
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 5 times.
7 if (!empty)
449 2 errno = ENOTEMPTY;
450 else
451 5 stegfs_cache_remove(path);
452 }
453 }
454 else
455 1 errno = ENOENT;
456
457 8 return -errno;
458 }
459
460 2 static int fuse_stegfs_readdir(const char *path, void *buf, fuse_fill_dir_t filler, off_t offset, struct fuse_file_info *info)
461 {
462 3 errno = EXIT_SUCCESS;
463
464 (void)info;
465
466
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (offset < 0)
467 1 return errno = EINVAL, -errno;
468 off_t cookie = 1;
469
3/4
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
✓ Branch 3 taken 1 times.
✗ Branch 4 not taken.
2 if (offset < cookie && filler(buf, ".", NULL, cookie))
470 return 0;
471 cookie++;
472
3/4
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
✓ Branch 3 taken 1 times.
✗ Branch 4 not taken.
2 if (offset < cookie && filler(buf, "..", NULL, cookie))
473 return 0;
474
475 2 stegfs_s file_system = stegfs_info();
476
477
2/4
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
2 if (path_equals(DIR_SEPARATOR, path))
478 {
479
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 1 times.
5 for (uint64_t i = 0; i < file_system.cache.ents; i++)
480
1/2
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
4 if (file_system.cache.child[i]->name
481
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
4 && (!file_system.cache.child[i]->file || !file_system.cache.child[i]->file->unlinked))
482 {
483 4 cookie = (off_t)i + 3;
484
4/4
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 1 times.
✓ Branch 3 taken 2 times.
✓ Branch 4 taken 1 times.
4 if (offset < cookie && filler(buf, file_system.cache.child[i]->name, NULL, cookie))
485 break;
486 }
487 }
488 ✗ else if (file_system.show_bloc && path_equals(PATH_BLOC, path))
489 {
490 ✗ for (uint64_t i = 0; i < file_system.size / SIZE_BYTE_BLOCK; i++)
491 ✗ if (file_system.blocks.in_use[i])
492 {
493 ✗ char b[21] = { 0x0 }; // max digits for UINT64_MAX
494 ✗ snprintf(b, sizeof b, "%ju", i);
495 ✗ cookie = (off_t)i + 3;
496 ✗ if (offset < cookie && filler(buf, b, NULL, cookie))
497 break;
498 }
499 }
500 else
501 {
502 stegfs_cache_s c;
503 ✗ memset(&c, 0x00, sizeof c);
504 ✗ if (stegfs_cache_exists(path, &c))
505 ✗ for (uint64_t i = 0; i < c.ents; i++)
506 ✗ if (c.child[i]->name
507 ✗ && (!c.child[i]->file || !c.child[i]->file->unlinked))
508 {
509 ✗ cookie = (off_t)i + 3;
510 ✗ if (offset < cookie && filler(buf, c.child[i]->name, NULL, cookie))
511 break;
512 }
513 }
514
515 2 return -errno;
516 }
517
518 7 static int fuse_stegfs_unlink(const char *path)
519 {
520 7 errno = EXIT_SUCCESS;
521 7 stegfs_cache_s *cached = stegfs_cache_exists(path, NULL);
522
3/4
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 6 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 1 times.
7 if (!cached && path_is_open_unlinked(path))
523 ✗ return errno = ENOENT, -errno;
524
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 5 times.
6 if (cached && !cached->file)
525 1 return errno = EISDIR, -errno;
526
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 4 times.
5 if (cached && cached->file && cached->file->open_count)
527 {
528 1 cached->file->unlinked = true;
529 1 stegfs_file_unlink(cached->file);
530 1 return 0;
531 }
532
1/2
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
4 if (cached && cached->file && !cached->file->inodes[0])
533 {
534 4 stegfs_cache_remove(path);
535 4 return 0;
536 }
537
538 stegfs_file_s file;
539 1 memset(&file, 0x00, sizeof file);
540 1 file.path = dir_get_path(path);
541 1 file.name = dir_get_name(path, PASSWORD_SEPARATOR);
542 1 file.pass = dir_get_pass(path);
543
544 1 bool exists = stegfs_file_stat(&file);
545
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (exists)
546 ✗ stegfs_file_delete(&file);
547 else
548 1 errno = ENOENT;
549
550 1 free(file.path);
551 1 free(file.name);
552 1 free(file.pass);
553
554 1 return -errno;
555 }
556
557 19 static int fuse_stegfs_read(const char *path, char *buf, size_t size, off_t offset, struct fuse_file_info *info)
558 {
559 20 errno = EXIT_SUCCESS;
560
561
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 19 times.
19 if (offset < 0)
562 1 return errno = EINVAL, -errno;
563 19 open_file_s *opened = open_file_lookup(info);
564
2/4
✓ Branch 0 taken 19 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 19 times.
19 if (!opened || strcmp(opened->path, path))
565 ✗ return errno = EBADF, -errno;
566
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 18 times.
19 if (!opened->readable)
567 1 return errno = EBADF, -errno;
568
569 18 stegfs_file_s *file = opened->file;
570
1/2
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
18 if (file)
571 {
572
3/4
✓ Branch 0 taken 16 times.
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 16 times.
✗ Branch 3 not taken.
18 if ((uint64_t)offset >= file->size || !size)
573 return 0;
574 16 uint64_t available = file->size - (uint64_t)offset;
575 16 if (size > available)
576 size = (size_t)available;
577
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 16 times.
16 if (!file->data)
578 ✗ return errno = EIO, -errno;
579 16 if (size > INT_MAX)
580 size = INT_MAX;
581 16 memcpy(buf, file->data + offset, size);
582 16 return size;
583 }
584
585 ✗ return errno = ENOENT, -errno;
586 }
587
588 17 static int fuse_stegfs_write(const char *path, const char *buf, size_t size, off_t offset, struct fuse_file_info *info)
589 {
590 17 errno = EXIT_SUCCESS;
591
592
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 16 times.
17 if (offset < 0)
593 1 return errno = EINVAL, -errno;
594
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 16 times.
16 if (size > INT_MAX)
595 ✗ return errno = EOVERFLOW, -errno;
596 16 if (size > UINT64_MAX - (uint64_t)offset)
597 return errno = EFBIG, -errno;
598 16 open_file_s *opened = open_file_lookup(info);
599
2/4
✓ Branch 0 taken 16 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 16 times.
16 if (!opened || strcmp(opened->path, path))
600 ✗ return errno = EBADF, -errno;
601
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 14 times.
16 if (!opened->writable)
602 2 return errno = EBADF, -errno;
603 14 uint64_t end = (uint64_t)offset + size;
604
605 14 stegfs_file_s *file = opened->file;
606
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 14 times.
14 if (!file)
607 ✗ return errno = EBADF, -errno;
608 14 uint64_t old_size = file->size;
609 14 uint64_t new_size = old_size > end ? old_size : end;
610 14 stegfs_file_s candidate = *file;
611 14 candidate.size = new_size;
612
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 14 times.
14 if (!stegfs_file_will_fit(&candidate))
613 ✗ return -errno;
614
2/2
✓ Branch 0 taken 13 times.
✓ Branch 1 taken 1 times.
14 if (new_size > old_size)
615 {
616 13 file->data = m_realloc(file->data, new_size);
617 13 memset(file->data + old_size, 0, new_size - old_size);
618 }
619 14 file->size = new_size;
620 14 file->time = time(NULL);
621 14 file->dirty = true;
622
1/2
✓ Branch 0 taken 14 times.
✗ Branch 1 not taken.
14 if (size)
623 14 memcpy(file->data + offset, buf, size);
624 14 return size;
625 }
626
627 26 static int fuse_stegfs_open(const char *path, struct fuse_file_info *info)
628 {
629 26 errno = EXIT_SUCCESS;
630
631 stegfs_cache_s *c = NULL;
632
3/4
✓ Branch 1 taken 25 times.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 25 times.
✗ Branch 4 not taken.
26 if ((c = stegfs_cache_exists(path, NULL)) && c->file)
633 21 {
634
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 25 times.
25 if (!info)
635 ✗ return errno = EINVAL, -errno;
636 25 open_file_s *opened = open_file_lookup(info);
637
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 22 times.
25 if (opened)
638
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
3 return strcmp(opened->path, path) ? (errno = EBADF, -errno) : 0;
639
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 22 times.
22 if (info->fh)
640 ✗ return errno = EBADF, -errno;
641
642 22 char *pass = dir_get_pass(path);
643
3/4
✓ Branch 0 taken 16 times.
✓ Branch 1 taken 6 times.
✓ Branch 2 taken 16 times.
✗ Branch 3 not taken.
22 if (!c->file->loaded && c->file->inodes[0])
644 {
645 16 free(c->file->pass);
646 16 c->file->pass = m_strdup(pass);
647
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 16 times.
16 if (!stegfs_file_read(c->file))
648 {
649 ✗ free(c->file->pass);
650 ✗ c->file->pass = NULL;
651 ✗ free(pass);
652 ✗ return errno = EACCES, -errno;
653 }
654 }
655
2/4
✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 6 times.
✗ Branch 3 not taken.
6 else if (c->file->loaded && c->file->pass
656
3/4
✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 5 times.
6 && strcmp(c->file->pass, pass ? pass : ""))
657 {
658 1 free(pass);
659 1 return errno = EACCES, -errno;
660 }
661 21 free(pass);
662 21 open_file_attach(path, info);
663 }
664 else
665 1 errno = c ? EISDIR : ENOENT;
666
667 22 return -errno;
668 }
669
670 ✗ static int fuse_stegfs_flush(const char *path, struct fuse_file_info *info)
671 {
672 ✗ errno = EXIT_SUCCESS;
673
674 ✗ open_file_s *opened = open_file_lookup(info);
675 ✗ if (!opened || strcmp(opened->path, path) || !opened->file)
676 ✗ return errno = EBADF, -errno;
677 ✗ stegfs_file_will_fit(opened->file);
678
679 ✗ return -errno;
680 }
681
682 ✗ static int fuse_stegfs_truncate(const char *path, off_t offset)
683 {
684 ✗ errno = EXIT_SUCCESS;
685
686 ✗ return fuse_stegfs_ftruncate(path, offset, NULL);
687 }
688
689 8 static int fuse_stegfs_ftruncate(const char *path, off_t offset, struct fuse_file_info *info)
690 {
691 8 errno = EXIT_SUCCESS;
692
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 7 times.
8 if (offset < 0)
693 1 return errno = EINVAL, -errno;
694
695 stegfs_file_s *file = NULL;
696
1/2
✓ Branch 0 taken 7 times.
✗ Branch 1 not taken.
7 if (info)
697 {
698 7 open_file_s *opened = open_file_lookup(info);
699
3/6
✓ Branch 0 taken 7 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 7 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 7 times.
7 if (!opened || strcmp(opened->path, path) || !opened->writable)
700 ✗ return errno = EBADF, -errno;
701 7 file = opened->file;
702 }
703 else
704 {
705 ✗ stegfs_cache_s *cached = stegfs_cache_exists(path, NULL);
706 ✗ file = cached ? cached->file : NULL;
707 }
708
1/2
✓ Branch 0 taken 7 times.
✗ Branch 1 not taken.
7 if (file)
709 {
710
1/6
✗ Branch 0 not taken.
✓ Branch 1 taken 7 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
7 if (!file->data && file->size && !stegfs_file_read(file))
711 ✗ return -errno;
712 7 stegfs_file_s candidate = *file;
713 7 candidate.size = (uint64_t)offset;
714
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 7 times.
7 if (!stegfs_file_will_fit(&candidate))
715 ✗ return -errno;
716 7 uint64_t old_size = file->size;
717 uint64_t new_size = (uint64_t)offset;
718
1/2
✓ Branch 0 taken 7 times.
✗ Branch 1 not taken.
7 if (new_size != old_size)
719 {
720
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 1 times.
7 if (new_size)
721 6 file->data = m_realloc(file->data, new_size);
722 else
723 {
724 1 free(file->data);
725 file->data = NULL;
726 }
727
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 4 times.
7 if (new_size > old_size)
728 3 memset(file->data + old_size, 0, new_size - old_size);
729 7 file->size = new_size;
730 7 file->time = time(NULL);
731 }
732 7 file->dirty = true;
733 7 return 0;
734 }
735 ✗ return errno = stegfs_cache_exists(path, NULL) ? EISDIR : ENOENT, -errno;
736 }
737
738 #ifdef STEGFS_FALLOCATE
739 static int fuse_stegfs_fallocate(const char *path, int mode, off_t offset, off_t length, struct fuse_file_info *info)
740 {
741 errno = EXIT_SUCCESS;
742
743 /* this isn't finished or working properly */
744
745 stegfs_cache_s *c = NULL;
746 while (true)
747 {
748 if ((c = stegfs_cache_exists(path, NULL)) && c->file)
749 {
750 uint64_t sz = offset + length;
751 switch (mode)
752 {
753 case -1: /* emulate truncate */
754 break;
755 case 0: /* make bigger (not smaller) */
756 if (sz < c->file->size)
757 sz = c->file->size;
758 break;
759 case FALLOC_FL_KEEP_SIZE: /* no size change */
760 case FALLOC_FL_PUNCH_HOLE:
761 return errno = EOPNOTSUPP, -errno;
762 case FALLOC_FL_COLLAPSE_RANGE: /* remove data from middle */
763 if (sz > c->file->size)
764 return errno = EINVAL, -errno;
765 case FALLOC_FL_ZERO_RANGE: /* does nothing here, but zeros later */
766 break;
767 }
768 char *buf = m_calloc(sz, sizeof(uint8_t));
769 fuse_stegfs_read(path, buf, sz, 0, info);
770 fuse_stegfs_unlink(path);
771 switch (mode)
772 {
773 case FALLOC_FL_COLLAPSE_RANGE:
774 memmove(buf + offset, buf + offset + length, sz - offset - length);
775 sz -= offset - length;
776 break;
777 case FALLOC_FL_ZERO_RANGE:
778 memset(buf + offset, 0x00, length);
779 break;
780 }
781 fuse_stegfs_write(path, buf, sz, 0, info);
782 free(buf);
783 return -errno;
784 }
785 else if (c && !c->file)
786 return errno = EISDIR, -errno;
787 else
788 stegfs_file_create(path, true);
789 }
790
791 return errno = EIO, -errno;
792 }
793 #endif
794
795 18 static int fuse_stegfs_create(const char *path, mode_t mode, struct fuse_file_info *info)
796 {
797 18 errno = EXIT_SUCCESS;
798
799 (void)mode;
800
2/2
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 17 times.
18 if (stegfs_cache_exists(path, NULL))
801 1 return errno = EEXIST, -errno;
802
803 17 stegfs_file_create(path, true);
804 17 open_file_attach(path, info);
805
806 17 return -errno;
807 }
808
809 ✗ static int fuse_stegfs_mknod(const char *path, mode_t mode, dev_t rdev)
810 {
811 ✗ errno = EXIT_SUCCESS;
812
813 (void)mode;
814 (void)rdev;
815
816 ✗ if (stegfs_cache_exists(path, NULL))
817 ✗ return errno = EEXIST, -errno;
818 ✗ stegfs_file_create(path, false);
819
820 ✗ return -errno;
821 }
822
823 34 static int fuse_stegfs_release(const char *path, struct fuse_file_info *info)
824 {
825 34 errno = EXIT_SUCCESS;
826
827 34 open_file_s *opened = open_file_lookup(info);
828
2/4
✓ Branch 0 taken 34 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 34 times.
34 if (!opened || strcmp(opened->path, path))
829 ✗ return errno = EBADF, -errno;
830
831 34 stegfs_file_s *file = opened->file;
832
1/2
✓ Branch 0 taken 34 times.
✗ Branch 1 not taken.
34 if (file)
833 {
834
1/2
✓ Branch 0 taken 34 times.
✗ Branch 1 not taken.
34 if (file->open_count)
835 34 file->open_count--;
836
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 33 times.
34 if (file->unlinked)
837 {
838
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 if (!file->open_count)
839 1 stegfs_file_delete(file);
840 }
841 else
842 {
843
2/2
✓ Branch 0 taken 19 times.
✓ Branch 1 taken 14 times.
33 if (file->dirty)
844 {
845 19 free(file->pass);
846
1/2
✓ Branch 0 taken 19 times.
✗ Branch 1 not taken.
38 file->pass = m_strdup(opened->pass ? opened->pass : "");
847
2/4
✓ Branch 1 taken 19 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 19 times.
✗ Branch 5 not taken.
19 if (stegfs_file_will_fit(file) && stegfs_file_write(file))
848 19 errno = EXIT_SUCCESS;
849 ✗ else if (!errno)
850 ✗ errno = EIO;
851 }
852
5/6
✓ Branch 0 taken 32 times.
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 32 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 31 times.
✓ Branch 6 taken 1 times.
33 if (!file->open_count && !file->dirty && !path_is_open_unlinked(path))
853 {
854 31 free(file->data);
855 31 file->data = NULL;
856 31 file->loaded = false;
857 31 free(file->pass);
858 31 file->pass = NULL;
859 }
860 }
861 }
862 34 open_file_detach(opened, info);
863
864 34 return -errno;
865 }
866
867 static void fuse_stegfs_destroy(void *ptr)
868 {
869 (void)ptr;
870
871 ✗ stegfs_deinit();
872 }
873
874 3 static int fuse_stegfs_readlink(const char *path, char *buf, size_t size)
875 {
876 3 errno = EXIT_SUCCESS;
877
878 3 stegfs_s file_system = stegfs_info();
879
3/6
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 3 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 3 times.
✗ Branch 5 not taken.
3 if (file_system.show_bloc && path_starts_with(PATH_BLOC, path))
880 3 {
881 uint64_t ino;
882
1/2
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
3 uint64_t block_count = file_system.blocksize ? file_system.size / file_system.blocksize : 0;
883
4/4
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 1 times.
✓ Branch 4 taken 1 times.
3 if (!block_index_from_path(path, &ino) || ino >= block_count)
884 2 errno = ENOENT;
885
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 else if (file_system.blocks.file[ino])
886 1 snprintf(buf, size, "%s", file_system.blocks.file[ino]);
887 else
888 ✗ errno = ENOENT;
889 }
890 else
891 {
892 (void)path;
893 (void)buf;
894 (void)size;
895 ✗ errno = ENOTSUP;
896 }
897
898 3 return -errno;
899 }
900
901 /*
902 * empty functions; required by fuse, but not used by stegfs; return ENOTSUP
903 */
904
905 ✗ static int fuse_stegfs_utime(const char *path, struct utimbuf *utime)
906 {
907 (void)path;
908 (void)utime;
909
910 ✗ return errno = ENOTSUP, -errno;
911 }
912
913 ✗ static int fuse_stegfs_chmod(const char *path, mode_t mode)
914 {
915 (void)path;
916 (void)mode;
917
918 ✗ return errno = ENOTSUP, -errno;
919 }
920
921 ✗ static int fuse_stegfs_chown(const char *path, uid_t uid, gid_t gid)
922 {
923 (void)path;
924 (void)uid;
925 (void)gid;
926
927 ✗ return errno = ENOTSUP, -errno;
928 }
929
930 ✗ int main(int argc, char **argv)
931 {
932 ✗ char **fargs = m_calloc(argc, sizeof( char * ));
933 ✗ fargs[0] = argv[0];
934
935 ✗ list_t args = list_init(config_named_compare, false, false);
936 ✗ list_add(args, &((config_named_s){ 'c', "cipher", _("algorithm"), _("Algorithm to use to encrypt data; use ‘list’ to show available cipher algorithms"), { CONFIG_ARG_REQ_STRING, { .string = NULL } }, false, false, false, false }));
937 ✗ list_add(args, &((config_named_s){ 's', "hash", _("algorithm"), _("Hash algorithm to generate key; use ‘list’ to show available hash algorithms"), { CONFIG_ARG_REQ_STRING, { .string = NULL } }, false, false, false, false }));
938 ✗ list_add(args, &((config_named_s){ 'm', "mode", _("mode"), _("The encryption mode to use; use ‘list’ to show available cipher modes"), { CONFIG_ARG_REQ_STRING, { .string = NULL } }, false, false, false, false }));
939 ✗ list_add(args, &((config_named_s){ 'a', "mac", _("mac"), _("The MAC algorithm to use; use ‘list’ to show available MACs"), { CONFIG_ARG_REQ_STRING, { .string = NULL } }, false, false, false, false }));
940 ✗ list_add(args, &((config_named_s){ 'i', "kdf-iterations", _("iterations"), _("Number of iterations the KDF should use"), { CONFIG_ARG_REQ_INTEGER, { .integer = 0 } }, false, false, false, false }));
941 ✗ list_add(args, &((config_named_s){ 'p', "paranoid", NULL, _("Enable paranoia mode"), { CONFIG_ARG_BOOLEAN, { .boolean = false } }, false, true, false, false }));
942 ✗ list_add(args, &((config_named_s){ 'x', "duplicates", "#", _("Number of times each file should be duplicated"), { CONFIG_ARG_REQ_INTEGER, { .integer = 0 } }, false, true, false, false }));
943 ✗ list_add(args, &((config_named_s){ 'b', "show-bloc", NULL, _("Expose the /bloc/ in-use block list directory"), { CONFIG_ARG_BOOLEAN, { .boolean = false } }, false, true, false, false }));
944 ✗ list_add(args, &((config_named_s){ 'd', NULL, NULL, _("Enable debug output (forces foreground and single-thread)"), { CONFIG_ARG_BOOLEAN, { .boolean = false } }, false, false, false, false }));
945 ✗ list_add(args, &((config_named_s){ 'f', NULL, NULL, _("Foreground operation"), { CONFIG_ARG_BOOLEAN, { .boolean = false } }, false, false, false, false }));
946 ✗ list_add(args, &((config_named_s){ 't', NULL, NULL, _("Disable multi-threaded operation (FUSE option -s)"), { CONFIG_ARG_BOOLEAN, { .boolean = false } }, false, false, false, false }));
947 ✗ list_add(args, &((config_named_s){ 'o', NULL, "opt,[opt...]", _("FUSE mount options--see FUSE documentation for details"), { CONFIG_ARG_LIST_STRING, { .list = NULL } }, false, false, false, false }));
948
949 ✗ list_t extra = list_default();
950 ✗ list_add(extra, &((config_unnamed_s){ "file system", { CONFIG_ARG_STRING, { .string = NULL } }, true, false }));
951 ✗ list_add(extra, &((config_unnamed_s){ "mount point", { CONFIG_ARG_STRING, { .string = NULL } }, true, false }));
952
953 ✗ list_t notes = list_default();
954 ✗ list_add(notes, _("It doesn't matter which order the file system and mount point are specified as stegfs will figure that out. All other options are passed to FUSE."));
955 ✗ list_add(notes, _("If you’re feeling extra paranoid you can now disable to stegfs file system header. This will also disable the checks when mounting and thus anything could happen ;-)"));
956
957 ✗ config_about_s about =
958 {
959 STEGFS_NAME,
960 STEGFS_VERSION,
961 PROJECT_URL,
962 NULL
963 };
964 ✗ config_init(about);
965 ✗ config_parse(argc, argv, args, extra, notes, false);
966
967 ✗ list_deinit(notes);
968
969 char *fs = NULL;
970 char *mnt = NULL;
971 ✗ struct stat s = { 0x0 };
972 ✗ stat(((config_unnamed_s *)list_get(extra, 0))->response.value.string, &s);
973 ✗ if (S_ISDIR(s.st_mode))
974 {
975 // 1st argument is directory, so it's the mount point
976 ✗ mnt = ((config_unnamed_s *)list_get(extra, 0))->response.value.string;
977 ✗ fs = ((config_unnamed_s *)list_get(extra, 1))->response.value.string;
978 }
979 else
980 {
981 ✗ fs = ((config_unnamed_s *)list_get(extra, 0))->response.value.string;
982 ✗ mnt = ((config_unnamed_s *)list_get(extra, 1))->response.value.string;
983 }
984 ✗ list_deinit(extra);
985
986 // config is actually read from the fs unless paranoid == true
987 enum gcry_cipher_algos cipher = DEFAULT_CIPHER;
988 enum gcry_cipher_modes mode = DEFAULT_MODE;
989 enum gcry_md_algos hash = DEFAULT_HASH;
990 enum gcry_mac_algos mac = DEFAULT_MAC;
991 uint64_t kdf_iters = DEFAULT_KDF_ITERATIONS;
992 ✗ bool paranoid = ((config_named_s *)list_get(args, 5))->response.value.boolean;
993 uint8_t duplicates = COPIES_DEFAULT;
994 ✗ bool show_bloc = ((config_named_s *)list_get(args, 7))->response.value.boolean;
995
996 ✗ if (paranoid)
997 {
998 ✗ cipher = cipher_id_from_name(((config_named_s *)list_get(args, 0))->response.value.string);
999 ✗ mode = mode_id_from_name(((config_named_s *)list_get(args, 1))->response.value.string);
1000 ✗ hash = hash_id_from_name(((config_named_s *)list_get(args, 2))->response.value.string);
1001 ✗ mac = mac_id_from_name(((config_named_s *)list_get(args, 3))->response.value.string);
1002 ✗ kdf_iters = ((config_named_s *)list_get(args, 4))->response.value.integer;
1003 ✗ duplicates = (uint8_t)((config_named_s *)list_get(args, 6))->response.value.integer;
1004 }
1005
1006 /*
1007 * deal with FUSE options
1008 */
1009
1010 ✗ bool debug = ((config_named_s *)list_get(args, 8))->response.value.boolean;
1011 ✗ bool foreground = debug || ((config_named_s *)list_get(args, 9))->response.value.boolean;
1012 ✗ bool single_thread = ((config_named_s *)list_get(args, 10))->response.value.boolean;
1013
1014 int fuse_argc = 3;
1015 ✗ char **fuse_argv = m_calloc(fuse_argc, sizeof (char *));
1016 ✗ fuse_argv[0] = argv[0];
1017 ✗ fuse_argv[1] = mnt;
1018 ✗ if (debug)
1019 {
1020 fuse_argc++;
1021 ✗ fuse_argv = m_realloc(fuse_argv, fuse_argc * sizeof (char *));
1022 ✗ fuse_argv[fuse_argc - 2] = "-d";
1023 }
1024 ✗ if (foreground)
1025 {
1026 ✗ fuse_argc++;
1027 ✗ fuse_argv = m_realloc(fuse_argv, fuse_argc * sizeof (char *));
1028 ✗ fuse_argv[fuse_argc - 2] = "-f";
1029 }
1030 ✗ if (single_thread)
1031 {
1032 ✗ fuse_argc++;
1033 ✗ fuse_argv = m_realloc(fuse_argv, fuse_argc * sizeof (char *));
1034 ✗ fuse_argv[fuse_argc - 2] = "-s";
1035 }
1036 ✗ list_t fuse_options = ((config_named_s *)list_get(args, 11))->response.value.list;
1037 ✗ iter_t iter = list_iterator(fuse_options);
1038 ✗ while (list_has_next(iter))
1039 {
1040 ✗ fuse_argc += 2;
1041 ✗ fuse_argv = m_realloc(fuse_argv, fuse_argc * sizeof (char *));
1042 ✗ fuse_argv[fuse_argc - 3] = "-o";
1043 ✗ fuse_argv[fuse_argc - 2] = (char *)list_get_next(iter);
1044 }
1045 ✗ fuse_argc--;
1046 ✗ fuse_argv[fuse_argc] = NULL;
1047
1048 ✗ list_deinit(args);
1049
1050 ✗ errno = EXIT_SUCCESS;
1051 ✗ switch (stegfs_init(fs, paranoid, cipher, mode, hash, mac, kdf_iters, duplicates, show_bloc))
1052 {
1053 ✗ case STEGFS_INIT_OKAY:
1054 ✗ goto done;
1055 ✗ case STEGFS_INIT_NOT_STEGFS:
1056 ✗ fprintf(stderr, "Not a stegfs partition!\n");
1057 ✗ break;
1058 ✗ case STEGFS_INIT_OLD_STEGFS:
1059 ✗ fprintf(stderr, "Previous (unsupported) version of stegfs!\n");
1060 ✗ break;
1061 ✗ case STEGFS_INIT_MISSING_TAG:
1062 ✗ fprintf(stderr, "Missing required stegfs metadata!\n");
1063 ✗ break;
1064 ✗ case STEGFS_INIT_INVALID_TAG:
1065 ✗ fprintf(stderr, "Invalid value for stegfs metadata!\n");
1066 ✗ break;
1067 ✗ case STEGFS_INIT_CORRUPT_TAG:
1068 ✗ fprintf(stderr, "Partition size mismatch! (Resizing not allowed!)\n");
1069 ✗ break;
1070 ✗ default:
1071 ✗ fprintf(stderr, "Unknown error initialising stegfs partition!\n");
1072 ✗ break;
1073 }
1074 return EXIT_FAILURE;
1075 done:
1076
1077 ✗ struct fuse_args f = FUSE_ARGS_INIT(fuse_argc, fuse_argv);
1078 ✗ fuse_opt_parse(&f, NULL, NULL, NULL);
1079 ✗ return fuse_main(f.argc, f.argv, &fuse_stegfs_functions, NULL);
1080 }
1081