initial codebase upload
This commit is contained in:
parent
abd4c064fb
commit
cccde99e80
21 changed files with 1299 additions and 0 deletions
53
.gitignore
vendored
Normal file
53
.gitignore
vendored
Normal file
|
@ -0,0 +1,53 @@
|
|||
# ---> C
|
||||
# Prerequisites
|
||||
*.d
|
||||
|
||||
# Object files
|
||||
*.o
|
||||
*.ko
|
||||
*.obj
|
||||
*.elf
|
||||
|
||||
# Linker output
|
||||
*.ilk
|
||||
*.map
|
||||
*.exp
|
||||
|
||||
# Precompiled Headers
|
||||
*.gch
|
||||
*.pch
|
||||
|
||||
# Libraries
|
||||
*.lib
|
||||
*.a
|
||||
*.la
|
||||
*.lo
|
||||
|
||||
# Shared objects (inc. Windows DLLs)
|
||||
*.dll
|
||||
*.so
|
||||
*.so.*
|
||||
*.dylib
|
||||
|
||||
# Executables
|
||||
*.exe
|
||||
*.out
|
||||
*.app
|
||||
*.i*86
|
||||
*.x86_64
|
||||
*.hex
|
||||
|
||||
# Debug files
|
||||
*.dSYM/
|
||||
*.su
|
||||
*.idb
|
||||
*.pdb
|
||||
|
||||
# Kernel Module Compile Results
|
||||
*.mod*
|
||||
*.cmd
|
||||
.tmp_versions/
|
||||
modules.order
|
||||
Module.symvers
|
||||
Mkfile.old
|
||||
dkms.conf
|
70
Makefile
Normal file
70
Makefile
Normal file
|
@ -0,0 +1,70 @@
|
|||
CC ?= gcc
|
||||
DEBUG ?= 0
|
||||
|
||||
CFLAGS = -Wall -Werror -Wextra -Wpedantic -std=c99 -Ofast
|
||||
|
||||
ifeq ($(DEBUG), 1)
|
||||
CFLAGS += -g -DDEBUG
|
||||
endif
|
||||
|
||||
SRCDIR = ./src
|
||||
INCDIR = ./include
|
||||
OBJDIR = ./obj
|
||||
BINDIR = ./bin
|
||||
|
||||
SRC = $(wildcard $(SRCDIR)/*.c)
|
||||
OBJ = $(SRC:$(SRCDIR)/%.c=$(OBJDIR)/%.o)
|
||||
DEP = $(OBJ:.o=.d)
|
||||
|
||||
PROGNAME_RELEASE = polonium
|
||||
PROGNAME_DEBUG = polonium_debug
|
||||
|
||||
ifeq ($(DEBUG), 1)
|
||||
EXECUTABLE = $(PROGNAME_DEBUG)
|
||||
else
|
||||
EXECUTABLE = $(PROGNAME_RELEASE)
|
||||
endif
|
||||
|
||||
EXECPATH = $(BINDIR)/$(EXECUTABLE)
|
||||
EXECPATH_RELEASE = $(BINDIR)/$(PROGNAME_RELEASE)
|
||||
EXECPATH_DEBUG = $(BINDIR)/$(PROGNAME_DEBUG)
|
||||
|
||||
all: $(EXECPATH)
|
||||
|
||||
$(EXECPATH): $(OBJ)
|
||||
@mkdir -p $(@D)
|
||||
$(CC) $(CFLAGS) $^ -o $@
|
||||
ifeq ($(DEBUG), 0)
|
||||
strip $@*
|
||||
endif
|
||||
|
||||
-include $(DEP)
|
||||
|
||||
$(OBJDIR)/%.o: $(SRCDIR)/%.c
|
||||
@mkdir -p $(@D)
|
||||
$(CC) $(CFLAGS) -I$(INCDIR) -MMD -MP -c $< -o $@
|
||||
|
||||
define rmfiles_if_exist
|
||||
@for file in $1; do \
|
||||
if [ -e $$file ]; then \
|
||||
echo "Removing file $$file"; \
|
||||
rm $$file; \
|
||||
fi; \
|
||||
done
|
||||
endef
|
||||
|
||||
define rmdir_if_empty
|
||||
@if [ -d "$1" ]; then \
|
||||
if [ -z "$$(find "$1" -mindepth 1 -maxdepth 1 -print -quit)" ]; then \
|
||||
echo "Removing empty directory $1"; \
|
||||
rm -r $1; \
|
||||
fi; \
|
||||
fi
|
||||
endef
|
||||
|
||||
clean:
|
||||
$(call rmfiles_if_exist,$(EXECPATH_RELEASE) $(EXECPATH_DEBUG) $(DEP) $(OBJ))
|
||||
$(call rmdir_if_empty,$(BINDIR))
|
||||
$(call rmdir_if_empty,$(OBJDIR))
|
||||
|
||||
.PHONY: all clean
|
18
include/MTPRNG.h
Normal file
18
include/MTPRNG.h
Normal file
|
@ -0,0 +1,18 @@
|
|||
#ifndef _MTPRNG_H
|
||||
#define _MTPRNG_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <time.h>
|
||||
|
||||
#include "common.h"
|
||||
|
||||
#include "random_seed.h"
|
||||
|
||||
void mt_seed(uint32_t* seed_ptr);
|
||||
uint32_t mt_next(void);
|
||||
uint32_t mt_randint(uint32_t min, uint32_t max);
|
||||
|
||||
#define mt_randomize() (mt_seed(NULL))
|
||||
|
||||
#endif /* _MTPRNG_H */
|
12
include/atoumax_base10.h
Normal file
12
include/atoumax_base10.h
Normal file
|
@ -0,0 +1,12 @@
|
|||
#ifndef _ATOUMAX_BASE10_H
|
||||
#define _ATOUMAX_BASE10_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "common.h"
|
||||
|
||||
/* functions definitions */
|
||||
uintmax_t atoumax_base10(const char* s);
|
||||
|
||||
#endif /* _ATOUMAX_BASE10_H */
|
102
include/common.h
Normal file
102
include/common.h
Normal file
|
@ -0,0 +1,102 @@
|
|||
#ifndef _COMMON_H
|
||||
#define _COMMON_H
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
/* enums */
|
||||
enum configurations_bitshift {
|
||||
C_BITSHIFT_CONFIRM = 0,
|
||||
C_BITSHIFT_CONTENTS,
|
||||
C_BITSHIFT_LINE_ENDINGS,
|
||||
C_BITSHIFT_PRINTABLE,
|
||||
C_BITSHIFT_CUSTOM_SEED
|
||||
};
|
||||
|
||||
enum configurations {
|
||||
C_CONFIRM = 1 << C_BITSHIFT_CONFIRM,
|
||||
C_CONTENTS = 1 << C_BITSHIFT_CONTENTS,
|
||||
C_LINE_ENDINGS = 1 << C_BITSHIFT_LINE_ENDINGS,
|
||||
C_PRINTABLE = 1 << C_BITSHIFT_PRINTABLE,
|
||||
C_CUSTOM_SEED = 1 << C_BITSHIFT_CUSTOM_SEED
|
||||
};
|
||||
|
||||
/* macros: procedures */
|
||||
#define FPRINTF_MACRO(stream, ...) do { \
|
||||
fflush(stdout); \
|
||||
fprintf(stream, __VA_ARGS__); \
|
||||
} while (0)
|
||||
/* // Intel A80486DX2-66: doesn't compile on my system
|
||||
// TODO: remove this multi-line comment before merging into main branch
|
||||
#define PERROR_MACRO(s) do { \
|
||||
fflush(stdout); \
|
||||
int errnum = errno; \
|
||||
size_t err_msg_size = 256; \
|
||||
char *err_msg = NULL; \
|
||||
int ret = -1; \
|
||||
do { \
|
||||
err_msg_size *= 2; \
|
||||
char* new_err_msg = realloc(err_msg, err_msg_size); \
|
||||
if (new_err_msg == NULL) { \
|
||||
free(err_msg); \
|
||||
break; \
|
||||
} \
|
||||
err_msg = new_err_msg; \
|
||||
ret = strerror_r(errnum, err_msg, err_msg_size); \
|
||||
} while (ret == -1 && errno == ERANGE); \
|
||||
if (ret == 0) { \
|
||||
fprintf(stderr, "%s:%d: %s: %s\n", __FILE__, __LINE__, (s), err_msg); \
|
||||
} \
|
||||
free(err_msg); \
|
||||
} while (0)
|
||||
*/
|
||||
#define PERROR_MACRO(s) do { \
|
||||
int errnum = errno; \
|
||||
char* err_msg = strerror(errnum); \
|
||||
fflush(stdout); \
|
||||
fprintf(stderr, "%s:%d: %s: %s\n", __FILE__, __LINE__, (s), err_msg); \
|
||||
} while (0)
|
||||
#define FATAL_ERROR_PRINT(...) \
|
||||
FPRINTF_MACRO(stderr, __VA_ARGS__); \
|
||||
putc('\n', stderr)
|
||||
#define FATAL_ERROR(...) do { \
|
||||
FATAL_ERROR_PRINT(__VA_ARGS__); \
|
||||
exit(EXIT_FAILURE); \
|
||||
} while (0)
|
||||
#define FATAL_ERROR_RET(...) do { \
|
||||
FATAL_ERROR_PRINT(__VA_ARGS__); \
|
||||
return EXIT_FAILURE; \
|
||||
} while (0)
|
||||
|
||||
/* macros: lambdas */
|
||||
#define STRINGIZE(x) #x
|
||||
#define INT2STR(x) STRINGIZE(x)
|
||||
#define YES_NO(x) ((x) ? "yes" : "no")
|
||||
#define READ_CONFIG(x) (config & (x))
|
||||
#define SET_CONFIG(x) \
|
||||
config |= (x)
|
||||
#define CLEAR_CONFIG(x) \
|
||||
config &= ~(x)
|
||||
#define LOOP_ACTION_CONFIG(f, x) \
|
||||
f(x); \
|
||||
continue
|
||||
|
||||
/* macros: definitions */
|
||||
#ifndef SIZE_T_C
|
||||
# if defined(UINT64_MAX) && SIZE_MAX == UINT64_MAX
|
||||
# define SIZE_T_C(x) UINT64_C(x)
|
||||
# elif defined(UINT32_MAX) && SIZE_MAX == UINT32_MAX
|
||||
# define SIZE_T_C(x) UINT32_C(x)
|
||||
# else
|
||||
# define SIZE_T_C(x) ((size_t) x)
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#ifndef SIZE_MAX
|
||||
# define SIZE_MAX (~SIZE_T_C(0))
|
||||
#endif
|
||||
|
||||
#endif /* _COMMON_H */
|
61
include/corrupter.h
Normal file
61
include/corrupter.h
Normal file
|
@ -0,0 +1,61 @@
|
|||
#ifndef _CORRUPTER_H
|
||||
#define _CORRUPTER_H
|
||||
|
||||
#include <ctype.h>
|
||||
#include <errno.h>
|
||||
#include <inttypes.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "common.h"
|
||||
|
||||
#include "MTPRNG.h"
|
||||
#include "file_boundaries.h"
|
||||
#include "file_type.h"
|
||||
|
||||
/* structures */
|
||||
struct _corrupter_result {
|
||||
bool error;
|
||||
size_t hit_counter,
|
||||
file_size,
|
||||
damaged_bytes;
|
||||
};
|
||||
|
||||
struct _corrupter_param {
|
||||
FILE* file;
|
||||
uint16_t probability;
|
||||
uint8_t threshold;
|
||||
size_t passes;
|
||||
uint8_t config;
|
||||
uint32_t* seed;
|
||||
file_type_t type;
|
||||
};
|
||||
|
||||
/* typedefs */
|
||||
typedef struct _corrupter_param Corrupter_Param;
|
||||
typedef struct _corrupter_result Corrupter_Result;
|
||||
typedef unsigned char byte;
|
||||
|
||||
/* macros: procedures */
|
||||
#ifdef DEBUG
|
||||
# define DBG_EXPECT(text, condition) do { \
|
||||
if (!(condition)) \
|
||||
die("[debug] Fail: %s", text); \
|
||||
} while (0)
|
||||
#else
|
||||
# define DBG_EXPECT(text, condition) do { \
|
||||
if (!(condition)) \
|
||||
abort(); \
|
||||
} while (0)
|
||||
#endif
|
||||
|
||||
/* macros: lambdas */
|
||||
#define FLIP_BIT(x, n) ((x) ^ (1 << (n)))
|
||||
|
||||
/* functions definitions */
|
||||
Corrupter_Result* corrupt_file(Corrupter_Param* param);
|
||||
|
||||
#endif /* _CORRUPTER_H */
|
18
include/endianness_tools.h
Normal file
18
include/endianness_tools.h
Normal file
|
@ -0,0 +1,18 @@
|
|||
#ifndef _ENDIANNESS_TOOLS_H
|
||||
#define _ENDIANNESS_TOOLS_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
enum endiannesses {
|
||||
UNSUPPORTED_ENDIANNESS,
|
||||
DETECTED_LITTLE_ENDIAN,
|
||||
DETECTED_BIG_ENDIAN
|
||||
};
|
||||
|
||||
typedef enum endiannesses endianness_t;
|
||||
|
||||
endianness_t detect_endianness(void);
|
||||
void reorder_b32(uint32_t* ptr);
|
||||
|
||||
#endif /* _ENDIANNESS_TOOLS_H */
|
11
include/ends_with.h
Normal file
11
include/ends_with.h
Normal file
|
@ -0,0 +1,11 @@
|
|||
#ifndef _ENDS_WITH_H
|
||||
#define _ENDS_WITH_H
|
||||
|
||||
#include <ctype.h>
|
||||
#include <errno.h>
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
|
||||
bool ends_with_casefold(const char* str, const char* substr);
|
||||
|
||||
#endif /* _ENDS_WITH_H */
|
26
include/file_boundaries.h
Normal file
26
include/file_boundaries.h
Normal file
|
@ -0,0 +1,26 @@
|
|||
#ifndef _FILE_BOUNDARIES_H
|
||||
#define _FILE_BOUNDARIES_H
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "common.h"
|
||||
|
||||
#include "endianness_tools.h"
|
||||
|
||||
/* structures */
|
||||
struct _file_boundaries {
|
||||
bool invalid_file;
|
||||
off_t start, end;
|
||||
};
|
||||
|
||||
/* typedefs */
|
||||
typedef struct _file_boundaries file_boundaries_t;
|
||||
|
||||
/* function definitions */
|
||||
file_boundaries_t* determine_boundaries_BMP(FILE* file);
|
||||
file_boundaries_t* determine_boundaries_WAV(FILE* file);
|
||||
|
||||
#endif /* _FILE_BOUNDARIES_H */
|
29
include/file_type.h
Normal file
29
include/file_type.h
Normal file
|
@ -0,0 +1,29 @@
|
|||
#ifndef _FILE_TYPE_H
|
||||
#define _FILE_TYPE_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "common.h"
|
||||
|
||||
#include "ends_with.h"
|
||||
|
||||
/* enums */
|
||||
enum file_types {
|
||||
FILE_TYPE_AUTO, // default value
|
||||
FILE_TYPE_BINARY,
|
||||
FILE_TYPE_TEXT,
|
||||
FILE_TYPE_BMP,
|
||||
FILE_TYPE_WAV
|
||||
};
|
||||
|
||||
/* typedefs */
|
||||
typedef enum file_types file_type_t;
|
||||
|
||||
/* function definitions */
|
||||
file_type_t determine_file_type(FILE* f, const char* file_name);
|
||||
const char* file_type_to_string(file_type_t type);
|
||||
|
||||
#endif /* _FILE_TYPE_H */
|
19
include/random_seed.h
Normal file
19
include/random_seed.h
Normal file
|
@ -0,0 +1,19 @@
|
|||
#ifndef _RANDOM_SEED_H
|
||||
#define _RANDOM_SEED_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <time.h>
|
||||
|
||||
#include "sys_id.h"
|
||||
#if defined(SYS_NT)
|
||||
# include <process.h>
|
||||
# include <windows.h>
|
||||
#elif defined(SYS_UNIX_GENERAL)
|
||||
# include <sys/types.h>
|
||||
# include <unistd.h>
|
||||
#endif
|
||||
|
||||
/* function definitions */
|
||||
uint32_t mt_generate_random_seed(void);
|
||||
|
||||
#endif /* _RANDOM_SEED_H */
|
23
include/sys_id.h
Normal file
23
include/sys_id.h
Normal file
|
@ -0,0 +1,23 @@
|
|||
#ifndef _SYS_ID_H
|
||||
#define _SYS_ID_H
|
||||
|
||||
/* macros: definitions */
|
||||
// system identification
|
||||
#if defined(__MINGW32__) || defined(__MINGW64__)
|
||||
# define SYS_NT
|
||||
# define SYS_UNIX
|
||||
#elif defined(_WIN32)
|
||||
# define SYS_NT
|
||||
#elif defined(__linux__)
|
||||
# define SYS_LINUX
|
||||
#elif defined(__APPLE__)
|
||||
# define SYS_MAC
|
||||
#elif defined(__unix__)
|
||||
# define SYS_UNIX
|
||||
#endif
|
||||
|
||||
#if defined(SYS_UNIX) || defined(SYS_LINUX) || defined(SYS_MAC)
|
||||
# define SYS_UNIX_GENERAL
|
||||
#endif
|
||||
|
||||
#endif /* _SYS_ID_H */
|
70
src/MTPRNG.c
Normal file
70
src/MTPRNG.c
Normal file
|
@ -0,0 +1,70 @@
|
|||
/*
|
||||
* A basic implementation of Mersenne twister PRNG by ChatGPT and
|
||||
* Intel A80486DX2-66
|
||||
*
|
||||
* TODO: verify code
|
||||
* TODO: extract all PRNG parameters
|
||||
*/
|
||||
|
||||
#include "MTPRNG.h"
|
||||
#include "random_seed.h"
|
||||
|
||||
/* static macros */
|
||||
#define N SIZE_T_C(624)
|
||||
#define M SIZE_T_C(397)
|
||||
#define MATRIX_A UINT32_C(0x9908B0DF)
|
||||
#define UPPER_MASK UINT32_C(0x80000000)
|
||||
#define LOWER_MASK UINT32_C(0x7FFFFFFF)
|
||||
|
||||
/* cached operations */
|
||||
static size_t N_sub_1 = (size_t) N - (size_t) 1;
|
||||
static size_t M_sub_1 = (size_t) M - (size_t) 1;
|
||||
static size_t N_sub_M = (size_t) N - (size_t) M;
|
||||
static size_t M_sub_N = (size_t) M - (size_t) N;
|
||||
|
||||
/* global variables */
|
||||
static uint32_t mt[N];
|
||||
static size_t mti;
|
||||
|
||||
void mt_seed(uint32_t* seed_ptr) {
|
||||
uint32_t seed = seed_ptr == NULL ? (uint32_t) (mt_generate_random_seed())
|
||||
: *seed_ptr;
|
||||
|
||||
uint32_t x = seed;
|
||||
for (mti = 0; mti < N; mti++) {
|
||||
mt[mti] = (x = (UINT32_C(1812433253) * (x ^ (x >> 30)) + mti));
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t mt_next(void) {
|
||||
uint32_t y;
|
||||
if (mti >= N) {
|
||||
size_t kk;
|
||||
for (kk = 0; kk < N_sub_M; kk++) {
|
||||
y = (mt[kk] & UPPER_MASK) | (mt[kk + 1] & LOWER_MASK);
|
||||
mt[kk] = mt[kk + (size_t) M] ^ (y >> 1) ^ ((y & 1) * MATRIX_A);
|
||||
}
|
||||
for (; kk < N_sub_1; kk++) {
|
||||
y = (mt[kk] & UPPER_MASK) | (mt[kk + 1] & LOWER_MASK);
|
||||
mt[kk] = mt[kk + M_sub_N] ^ (y >> 1) ^ ((y & 1) *
|
||||
MATRIX_A);
|
||||
}
|
||||
y = (mt[N_sub_1] & UPPER_MASK) | (mt[0] & LOWER_MASK);
|
||||
mt[N_sub_1] = mt[M_sub_1] ^ (y >> 1) ^ ((y & 1) * MATRIX_A);
|
||||
mti = 0;
|
||||
}
|
||||
|
||||
y = mt[mti];
|
||||
++mti;
|
||||
|
||||
y ^= (y >> 11);
|
||||
y ^= (y << 7 ) & UINT32_C(0x9D2C5680);
|
||||
y ^= (y << 15) & UINT32_C(0xEFC60000);
|
||||
y ^= (y >> 18);
|
||||
|
||||
return y;
|
||||
}
|
||||
|
||||
uint32_t mt_randint(uint32_t min, uint32_t max) {
|
||||
return min + (mt_next() % (max - min + 1));
|
||||
}
|
22
src/atoumax_base10.c
Normal file
22
src/atoumax_base10.c
Normal file
|
@ -0,0 +1,22 @@
|
|||
#include "atoumax_base10.h"
|
||||
|
||||
/* function implementations */
|
||||
uintmax_t atoumax_base10(const char* s) {
|
||||
uintmax_t result = 0;
|
||||
for (const char* p = s; *p != '\0'; ++p) {
|
||||
char c = *p;
|
||||
|
||||
if (c < '0' || c > '9') {
|
||||
FATAL_ERROR("Character '%c' in string '%s' is not a base 10 digit",
|
||||
c, s);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
uintmax_t digit = c - '0';
|
||||
if ((uintmax_t) (result + digit) < result) {
|
||||
FATAL_ERROR("Integer overflow (passed string = '%s')", s);
|
||||
}
|
||||
result = (result * 10) + digit;
|
||||
}
|
||||
return result;
|
||||
}
|
166
src/corrupter.c
Normal file
166
src/corrupter.c
Normal file
|
@ -0,0 +1,166 @@
|
|||
#include "corrupter.h"
|
||||
|
||||
/* cached operations */
|
||||
const size_t UINT16_MAX_PLUS_1 = UINT16_MAX + 1;
|
||||
|
||||
/* function definitions */
|
||||
static bool get_chance(uint16_t desired_chance);
|
||||
static bool is_line_ending(byte c);
|
||||
|
||||
/* function implementations */
|
||||
static bool get_chance(uint16_t desired_chance) {
|
||||
/* TODO: remove this multi-line comment before merging into main branch
|
||||
// algorithm v1
|
||||
// calculate the cumulative distribution function (CDF)
|
||||
uint8_t cdf[UINT8_MAX_PLUS_1];
|
||||
memset(cdf, 0, sizeof(cdf));
|
||||
for (uint8_t i = 0; i < UINT8_MAX; i++)
|
||||
cdf[i] = cdf[i] + ((i - 1) <= desired_chance);
|
||||
|
||||
// generate a random number in the range of 0 to the total weight
|
||||
uint8_t random_number = mt_next() & UINT8_MAX;
|
||||
|
||||
// use the CDF to determine the outcome
|
||||
for (uint8_t i = 0; i < UINT8_MAX; i++)
|
||||
if (random_number < cdf[i])
|
||||
return true;
|
||||
return (random_number < cdf[UINT8_MAX] ||
|
||||
random_number < cdf[UINT8_MAX_PLUS_1]);
|
||||
*/
|
||||
|
||||
// algorithm v2
|
||||
if (desired_chance == UINT16_MAX)
|
||||
return true;
|
||||
|
||||
uint16_t cdf[UINT16_MAX_PLUS_1];
|
||||
memset(cdf, 0, sizeof(cdf));
|
||||
for (uint16_t i = 0; i < UINT16_MAX; i++) {
|
||||
cdf[i] = (i <= desired_chance) ? i + 1 : cdf[i - 1];
|
||||
}
|
||||
|
||||
uint16_t random_number = mt_next() & UINT16_MAX;
|
||||
return random_number < cdf[desired_chance];
|
||||
}
|
||||
|
||||
static bool is_line_ending(byte c) {
|
||||
return (c == '\n' || c == '\r');
|
||||
}
|
||||
|
||||
Corrupter_Result* corrupt_file(Corrupter_Param* param) {
|
||||
Corrupter_Result* result = malloc(sizeof(Corrupter_Result));
|
||||
if (result == NULL)
|
||||
return NULL;
|
||||
|
||||
// copy and cast parameters
|
||||
FILE* file = param->file;
|
||||
uint8_t probability = param->probability;
|
||||
off_t threshold = (off_t) param->threshold;
|
||||
uint8_t config = param->config;
|
||||
|
||||
// refuse to operate on non-seekable streams
|
||||
if (fseeko(file, 0L, SEEK_SET) != 0 || ftello(file) != 0) {
|
||||
errno = ESPIPE;
|
||||
result->error = true;
|
||||
return result;
|
||||
}
|
||||
|
||||
// determine file size
|
||||
off_t start = 0, end;
|
||||
fseeko(file, 0L, SEEK_END);
|
||||
end = ftello(file);
|
||||
|
||||
// determine file boundaries
|
||||
const char* failed_function = NULL;
|
||||
file_boundaries_t* boundaries = NULL;
|
||||
|
||||
switch (param->type) {
|
||||
case FILE_TYPE_BMP:
|
||||
boundaries = determine_boundaries_BMP(file);
|
||||
failed_function = "determine_boundaries_BMP";
|
||||
goto File_Type_General_Case;
|
||||
case FILE_TYPE_WAV:
|
||||
boundaries = determine_boundaries_WAV(file);
|
||||
failed_function = "determine_boundaries_WAV";
|
||||
File_Type_General_Case:
|
||||
if (boundaries == NULL) {
|
||||
PERROR_MACRO(failed_function);
|
||||
exit(EXIT_FAILURE);
|
||||
return NULL;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
// output data to result
|
||||
result->file_size = end;
|
||||
result->damaged_bytes = 0;
|
||||
|
||||
// initialize the PRNG
|
||||
mt_seed(param->seed);
|
||||
|
||||
for (size_t pass = 0; pass < param->passes; pass++) {
|
||||
// display information
|
||||
printf("Pass: %zu/%zu", pass + 1, param->passes);
|
||||
fflush(stdout);
|
||||
|
||||
rewind(file);
|
||||
|
||||
// iterate over the file contents
|
||||
for (off_t i = start; i < end; i++) {
|
||||
fseeko(file, i, SEEK_SET);
|
||||
byte byte_value;
|
||||
if (fread(&byte_value, sizeof(byte), 1, file) != 1) {
|
||||
result->error = true;
|
||||
fclose(file);
|
||||
return result;
|
||||
}
|
||||
|
||||
if (READ_CONFIG(C_PRINTABLE) && !isprint(byte_value))
|
||||
continue;
|
||||
else if (READ_CONFIG(C_LINE_ENDINGS) && is_line_ending(byte_value))
|
||||
continue;
|
||||
|
||||
// generate bit mask
|
||||
unsigned char damage_left = (unsigned char) param->threshold;
|
||||
static bool bit_mask[CHAR_BIT];
|
||||
for (unsigned char bit = 0; bit < CHAR_BIT; bit++) {
|
||||
if (get_chance(probability) && (damage_left > 0)) {
|
||||
bit_mask[bit] = true;
|
||||
damage_left--;
|
||||
} else {
|
||||
bit_mask[bit] = false;
|
||||
}
|
||||
}
|
||||
|
||||
bool damaged_byte = false;
|
||||
for (unsigned char bit = 0; bit < threshold; bit++) {
|
||||
if (bit_mask[bit] == false)
|
||||
continue;
|
||||
|
||||
byte_value = FLIP_BIT(byte_value, bit);
|
||||
result->hit_counter++;
|
||||
|
||||
damaged_byte = true;
|
||||
|
||||
// write the modified byte back to the file
|
||||
fseeko(file, i, SEEK_SET);
|
||||
if (fwrite(&byte_value, sizeof(byte), 1, file) == 1)
|
||||
continue;
|
||||
|
||||
// on error
|
||||
result->error = true;
|
||||
fclose(file);
|
||||
return result;
|
||||
}
|
||||
|
||||
if (damaged_byte)
|
||||
result->damaged_bytes++;
|
||||
}
|
||||
|
||||
puts(" [OK]");
|
||||
}
|
||||
|
||||
result->error = false;
|
||||
return result;
|
||||
}
|
24
src/endianness_tools.c
Normal file
24
src/endianness_tools.c
Normal file
|
@ -0,0 +1,24 @@
|
|||
#include "endianness_tools.h"
|
||||
|
||||
#define ORDER_NATIVE_U32 0x01234567
|
||||
#define ORDER_LITTLE_ENDIAN_S4 "\x67\x45\x23\x01"
|
||||
#define ORDER_BIG_ENDIAN_S4 "\x01\x23\x45\x67"
|
||||
#define ifeq_b32_ret(lhs, rhs, value) if (!memcmp(lhs, rhs, 4)) return value;
|
||||
|
||||
endianness_t detect_endianness(void) {
|
||||
volatile uint32_t native_order_value = ORDER_NATIVE_U32;
|
||||
uint8_t* as_bytes = (uint8_t*)&native_order_value;
|
||||
|
||||
ifeq_b32_ret(as_bytes, ORDER_LITTLE_ENDIAN_S4, DETECTED_LITTLE_ENDIAN);
|
||||
ifeq_b32_ret(as_bytes, ORDER_BIG_ENDIAN_S4, DETECTED_BIG_ENDIAN );
|
||||
return UNSUPPORTED_ENDIANNESS;
|
||||
}
|
||||
|
||||
void reorder_b32(uint32_t* ptr) {
|
||||
if (ptr == NULL)
|
||||
return;
|
||||
|
||||
uint32_t result = ((*ptr >> 24) & 0xFF) | ((*ptr >> 8) & 0xFF00) |
|
||||
((*ptr << 8) & 0xFF0000) | ((*ptr << 24) & 0xFF000000);
|
||||
*ptr = result;
|
||||
}
|
25
src/ends_with.c
Normal file
25
src/ends_with.c
Normal file
|
@ -0,0 +1,25 @@
|
|||
#include "ends_with.h"
|
||||
|
||||
/* function implementations */
|
||||
bool ends_with_casefold(const char* str, const char* substr) {
|
||||
if (str == NULL || substr == NULL) {
|
||||
errno = EINVAL;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (str[0] == '\0')
|
||||
return substr[0] == '\0';
|
||||
|
||||
size_t str_len = strlen(str);
|
||||
size_t substr_len = strlen(substr);
|
||||
|
||||
if (str_len < substr_len)
|
||||
return false;
|
||||
|
||||
for (const char* p = str + str_len - substr_len, * q = substr;
|
||||
*p != '\0' && *q != '\0'; p++, q++) {
|
||||
if (tolower(*p) != tolower(*q))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
84
src/file_boundaries.c
Normal file
84
src/file_boundaries.c
Normal file
|
@ -0,0 +1,84 @@
|
|||
#include "file_boundaries.h"
|
||||
|
||||
/* macros: definitions */
|
||||
#define WAV_CHUNK_ID "RIFF"
|
||||
#define WAV_SUBCHUNK_DATA_ID "data"
|
||||
|
||||
/* macros: lambdas */
|
||||
#define DETERMINE_BOUNDARIES_FREAD_ERROR_HANDLING \
|
||||
if (feof(file)) { \
|
||||
boundaries->invalid_file = true; \
|
||||
return boundaries; \
|
||||
} \
|
||||
\
|
||||
PERROR_MACRO("fread"); \
|
||||
exit(EXIT_FAILURE); \
|
||||
return NULL
|
||||
|
||||
/* function implementations */
|
||||
file_boundaries_t* determine_boundaries_BMP(FILE* file) {
|
||||
rewind(file);
|
||||
FATAL_ERROR("Feature 'determine_boundaries_BMP' not available. Coming "
|
||||
"soon!");
|
||||
return false;
|
||||
}
|
||||
|
||||
file_boundaries_t* determine_boundaries_WAV(FILE* file) {
|
||||
file_boundaries_t* boundaries = malloc(sizeof(file_boundaries_t));
|
||||
if (boundaries == NULL) {
|
||||
PERROR_MACRO("fatal: malloc @ determine_boundaries_WAV");
|
||||
exit(EXIT_FAILURE);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// prepare
|
||||
endianness_t endianness = detect_endianness();
|
||||
if (endianness == UNSUPPORTED_ENDIANNESS) {
|
||||
FATAL_ERROR("Unsupported endianness of the machine");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bool need_to_reorder_values = endianness == DETECTED_BIG_ENDIAN;
|
||||
|
||||
rewind(file);
|
||||
|
||||
char chunk_id[4];
|
||||
uint32_t chunk_size;
|
||||
|
||||
if (fread(chunk_id, sizeof(char), 4, file) != 4) {
|
||||
DETERMINE_BOUNDARIES_FREAD_ERROR_HANDLING;
|
||||
}
|
||||
|
||||
if (strncmp(chunk_id, WAV_CHUNK_ID, 4)) {
|
||||
boundaries->invalid_file = true;
|
||||
return boundaries;
|
||||
}
|
||||
|
||||
// Read the next 4 bytes (chunk size) and skip the rest of the RIFF chunk
|
||||
if (fread(&chunk_size, sizeof(uint32_t), 1, file) != 1) {
|
||||
DETERMINE_BOUNDARIES_FREAD_ERROR_HANDLING;
|
||||
}
|
||||
|
||||
fseeko(file, (off_t) chunk_size - 4, SEEK_CUR);
|
||||
|
||||
while (true) {
|
||||
if (fread(chunk_id, 1, 4, file) != 4 ||
|
||||
fread(&chunk_size, sizeof(uint32_t), 1, file) != 1) {
|
||||
DETERMINE_BOUNDARIES_FREAD_ERROR_HANDLING;
|
||||
}
|
||||
|
||||
if (need_to_reorder_values)
|
||||
reorder_b32(&chunk_size);
|
||||
|
||||
if (!strncmp(chunk_id, WAV_SUBCHUNK_DATA_ID, 4)) {
|
||||
boundaries->start = ftello(file);
|
||||
boundaries->end = ftello(file) + (off_t) chunk_size;
|
||||
break;
|
||||
}
|
||||
|
||||
fseeko(file, chunk_size, SEEK_CUR);
|
||||
}
|
||||
|
||||
boundaries->invalid_file = false;
|
||||
return boundaries;
|
||||
}
|
66
src/file_type.c
Normal file
66
src/file_type.c
Normal file
|
@ -0,0 +1,66 @@
|
|||
#include "file_type.h"
|
||||
|
||||
/* macros: lambdas */
|
||||
#define FILE_TYPE_CHECK_RET(ext, type) \
|
||||
if (ends_with_casefold(file_name, ext) && is_valid_file(file, type)) \
|
||||
return true
|
||||
#define FILE_TYPE_FREAD_ERROR_HANDLING \
|
||||
if (feof(f)) \
|
||||
return false; \
|
||||
\
|
||||
PERROR_MACRO("fread"); \
|
||||
exit(EXIT_FAILURE)
|
||||
|
||||
/* function definitions */
|
||||
static bool header_check(FILE* f, const char* id, size_t length);
|
||||
static bool is_valid_file(FILE* f, file_type_t type);
|
||||
|
||||
/* function implementations */
|
||||
static bool header_check(FILE* f, const char* id, size_t length) {
|
||||
rewind(f);
|
||||
|
||||
for (size_t i = 0; i < length; i++) {
|
||||
char c;
|
||||
if (fread(&c, sizeof(char), 1, f) != 1) {
|
||||
FILE_TYPE_FREAD_ERROR_HANDLING;
|
||||
}
|
||||
|
||||
if (c != id[i])
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool is_valid_file(FILE* f, file_type_t type) {
|
||||
switch (type) {
|
||||
case FILE_TYPE_BMP: return header_check(f, "BM", 2);
|
||||
case FILE_TYPE_WAV: return header_check(f, "RIFF", 4);
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
file_type_t determine_file_type(FILE* file, const char* file_name) {
|
||||
if (ends_with_casefold(file_name, ".txt"))
|
||||
return FILE_TYPE_TEXT;
|
||||
|
||||
FILE_TYPE_CHECK_RET(".bmp", FILE_TYPE_BMP);
|
||||
FILE_TYPE_CHECK_RET(".wav", FILE_TYPE_WAV);
|
||||
|
||||
return FILE_TYPE_BINARY;
|
||||
}
|
||||
|
||||
const char* file_type_to_string(file_type_t type) {
|
||||
switch (type) {
|
||||
case FILE_TYPE_AUTO:
|
||||
case FILE_TYPE_BINARY:
|
||||
return "binary";
|
||||
case FILE_TYPE_BMP:
|
||||
return "Windows Bitmap";
|
||||
case FILE_TYPE_WAV:
|
||||
return "Waveform Audio File Format";
|
||||
default:
|
||||
return "(unknown)";
|
||||
}
|
||||
}
|
364
src/main.c
Normal file
364
src/main.c
Normal file
|
@ -0,0 +1,364 @@
|
|||
// TODO: if user requires, only damage contents of PNG, BMP, WAV
|
||||
// PNG: work only with pixels
|
||||
// BMP: work only with pixels
|
||||
// WAV: work only with samples
|
||||
|
||||
#include <ctype.h>
|
||||
#include <inttypes.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "sys_id.h"
|
||||
#ifdef SYS_UNIX_GENERAL
|
||||
# include <sys/types.h>
|
||||
# include <sys/stat.h>
|
||||
#endif
|
||||
|
||||
#include "common.h"
|
||||
|
||||
#include "MTPRNG.h"
|
||||
#include "atoumax_base10.h"
|
||||
#include "corrupter.h"
|
||||
#include "file_type.h"
|
||||
|
||||
/* enums */
|
||||
enum arg_destinations {
|
||||
ARG_NO_DEST,
|
||||
ARG_DEST_POSSIBILITY,
|
||||
ARG_DEST_THRESHOLD,
|
||||
ARG_DEST_PASSES,
|
||||
ARG_DEST_SEED
|
||||
};
|
||||
|
||||
/* constant values: arguments */
|
||||
const char* ARGS_HELP[] = { "-h", "--help" };
|
||||
const char* ARGS_PROBABILITY[] = { "-p", "--probability" };
|
||||
const char* ARGS_THRESHOLD[] = { "-t", "--threshold" };
|
||||
const char* ARGS_PASSES[] = { "-n", "--passes" };
|
||||
const char* ARGS_CONTENTS[] = { "-c", "--contents" };
|
||||
const char* ARGS_LINE_ENDINGS[] = { "-l", "--line-endings" };
|
||||
const char* ARGS_PRINTABLE[] = { "-P", "--printable" };
|
||||
const char* ARGS_SEED[] = { "-s", "--seed" };
|
||||
|
||||
/* global variables */
|
||||
uint32_t PRNG_seed_value;
|
||||
uint8_t config = C_CONFIRM;
|
||||
|
||||
/* macros: procedures */
|
||||
#define DOES_NOT_EXIST \
|
||||
if (errno == ENOENT) { \
|
||||
FPRINTF_MACRO(stderr, "Error: File %s doesn't exist\n", file_path); \
|
||||
return EXIT_FAILURE; \
|
||||
}
|
||||
|
||||
/* macros: lambdas */
|
||||
#define ARG_MATCH(s, args2) (!strcmp(s, args2[0]) || !strcmp(s, args2[1]))
|
||||
|
||||
/* default values */
|
||||
static uint16_t probability = 100;
|
||||
static uint8_t threshold = 2;
|
||||
static size_t passes = 1;
|
||||
|
||||
/* function definitions */
|
||||
static char* correct_slashes(const char* path);
|
||||
static char* my_basename(const char* raw_path);
|
||||
static void parse_value(uint8_t destination, const char* arg);
|
||||
|
||||
/* function implementations */
|
||||
static char* correct_slashes(const char* path) {
|
||||
char* new_path = strdup(path);
|
||||
if (new_path == NULL)
|
||||
return NULL;
|
||||
|
||||
char* ptr = new_path;
|
||||
while (*ptr != '\0') {
|
||||
if (*ptr == '\\')
|
||||
*ptr = '/';
|
||||
ptr++;
|
||||
}
|
||||
|
||||
return new_path;
|
||||
}
|
||||
|
||||
static char* my_basename(const char* raw_path) {
|
||||
char* path = correct_slashes(raw_path);
|
||||
if (path == NULL)
|
||||
return NULL;
|
||||
|
||||
char* base = malloc(FILENAME_MAX * sizeof(char));
|
||||
if (base == NULL) {
|
||||
free(path);
|
||||
PERROR_MACRO("malloc");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
size_t fname_len = strlen(path);
|
||||
|
||||
const char* last_slash = strrchr(path, '/');
|
||||
if (last_slash != NULL)
|
||||
fname_len = strlen(last_slash + 1);
|
||||
|
||||
memcpy(base, last_slash + 1, fname_len);
|
||||
base[fname_len] = '\0';
|
||||
|
||||
return base;
|
||||
}
|
||||
|
||||
static void parse_value(uint8_t destination, const char* arg) {
|
||||
if (destination == ARG_DEST_SEED && !strcmp(arg, "random"))
|
||||
return;
|
||||
|
||||
uintmax_t result = atoumax_base10(arg);
|
||||
switch (destination) {
|
||||
case ARG_DEST_POSSIBILITY:
|
||||
if (result == 0 || result > UINT8_MAX) {
|
||||
FATAL_ERROR("Chance value should be in range [1..%" PRIu8 "]",
|
||||
UINT8_MAX);
|
||||
return;
|
||||
}
|
||||
|
||||
probability = result;
|
||||
break;
|
||||
case ARG_DEST_THRESHOLD:
|
||||
if (result < 1 || result > CHAR_BIT) {
|
||||
FATAL_ERROR("Damage value should be in range [1.."
|
||||
INT2STR(CHAR_BIT) "]");
|
||||
return;
|
||||
}
|
||||
|
||||
threshold = result;
|
||||
break;
|
||||
case ARG_DEST_PASSES:
|
||||
if (result == 0) {
|
||||
FATAL_ERROR("The number of passes can't be less than 1");
|
||||
return;
|
||||
}
|
||||
|
||||
passes = result;
|
||||
break;
|
||||
case ARG_DEST_SEED:
|
||||
SET_CONFIG(C_CUSTOM_SEED);
|
||||
PRNG_seed_value = result;
|
||||
break;
|
||||
default:
|
||||
FATAL_ERROR("Unknown argument destination (value = %" PRIu16 ")",
|
||||
destination);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
puts("Polonium: a file corrupter\n");
|
||||
|
||||
if (argc < 2 || ARG_MATCH(argv[argc - 1], ARGS_HELP)) {
|
||||
char* program_name = my_basename(argv[0]);
|
||||
printf(
|
||||
"Usage: %s <file to corrupt> [parameters] [options]\n"
|
||||
"\n"
|
||||
"Both parameters and options are optional.\n"
|
||||
"\n"
|
||||
"[parameters]:\n"
|
||||
" --probability : Determines the likelihood of a neutron "
|
||||
"radiation event\n"
|
||||
" occurring. The higher the value, the more "
|
||||
"likely the bits are\n"
|
||||
" to be corrupted.\n"
|
||||
" Value range: [1..%" PRIu16 "]\n"
|
||||
" Default value: %" PRIu16 "\n"
|
||||
"\n"
|
||||
" --threshold : Controls the amount of change allowed for each "
|
||||
"byte. The\n"
|
||||
" higher the value, the more extensive the "
|
||||
"modifications will\n"
|
||||
" be.\n"
|
||||
" Value range: [1.." INT2STR(CHAR_BIT) "]\n"
|
||||
" Default value: %" PRIu8 "\n"
|
||||
"\n"
|
||||
" --passes : This parameter determines the number of times "
|
||||
"the file will\n"
|
||||
" be processed. The higher the value, the longer "
|
||||
"the overall\n"
|
||||
" processing time will be.\n"
|
||||
" Value range: [1..%" PRIuMAX "]\n"
|
||||
" Default value: %" PRIuMAX "\n"
|
||||
"\n"
|
||||
"[options]:\n"
|
||||
" --noconfirm : (Caution!) Do not ask user to confirm "
|
||||
"deletion\n"
|
||||
"\n"
|
||||
" -c, --contents : Only corrupt contents of files (PNG, BMP, WAV)\n"
|
||||
"\n"
|
||||
" -l,\n"
|
||||
" --line-endings : Preserve line endings\n"
|
||||
"\n"
|
||||
" -p,\n"
|
||||
" --printable : Work only with printable ASCII characters\n"
|
||||
"\n"
|
||||
" --seed : Specify a 32-bit seed for the PRNG. If you "
|
||||
"want to keep it\n"
|
||||
" random, set the option to `random`.\n",
|
||||
program_name, UINT16_MAX, probability, threshold,
|
||||
(uintmax_t) SIZE_MAX, passes);
|
||||
free(program_name);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
const char* file_path = argv[1];
|
||||
file_type_t file_type = FILE_TYPE_AUTO;
|
||||
|
||||
if (argc > 2) {
|
||||
uint8_t arg_destination = ARG_NO_DEST;
|
||||
bool read_off_value = false;
|
||||
|
||||
for (int i = 2; i < argc; ++i) {
|
||||
bool last_arg = i == (argc - 1);
|
||||
|
||||
const char* arg = argv[i];
|
||||
|
||||
if (read_off_value) {
|
||||
parse_value(arg_destination, arg);
|
||||
read_off_value = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ARG_MATCH(arg, ARGS_PROBABILITY))
|
||||
arg_destination = ARG_DEST_POSSIBILITY;
|
||||
else if (ARG_MATCH(arg, ARGS_THRESHOLD))
|
||||
arg_destination = ARG_DEST_THRESHOLD;
|
||||
else if (ARG_MATCH(arg, ARGS_PASSES))
|
||||
arg_destination = ARG_DEST_PASSES;
|
||||
else if (ARG_MATCH(arg, ARGS_SEED))
|
||||
arg_destination = ARG_DEST_SEED;
|
||||
else if (!strcmp(arg, "--noconfirm")) {
|
||||
LOOP_ACTION_CONFIG(CLEAR_CONFIG, C_CONFIRM);
|
||||
} else if (ARG_MATCH(arg, ARGS_CONTENTS)) {
|
||||
LOOP_ACTION_CONFIG(SET_CONFIG, C_CONTENTS);
|
||||
} else if (ARG_MATCH(arg, ARGS_LINE_ENDINGS)) {
|
||||
LOOP_ACTION_CONFIG(SET_CONFIG, C_LINE_ENDINGS);
|
||||
} else if (ARG_MATCH(arg, ARGS_PRINTABLE)) {
|
||||
LOOP_ACTION_CONFIG(SET_CONFIG, C_PRINTABLE);
|
||||
} else
|
||||
FATAL_ERROR_RET("Unknown command line parameter '%s'.\n"
|
||||
"Please invoke the program with argument %s or "
|
||||
"%s to display help.\n",
|
||||
arg, ARGS_HELP[0], ARGS_HELP[1]);
|
||||
|
||||
if (last_arg)
|
||||
FATAL_ERROR_RET("Please provide the value.");
|
||||
else {
|
||||
read_off_value = true;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef SYS_UNIX_GENERAL
|
||||
// check if the file is a directory
|
||||
struct stat path_stat;
|
||||
if (stat(file_path, &path_stat) != 0) {
|
||||
if (errno == ENOENT) {
|
||||
DOES_NOT_EXIST;
|
||||
}
|
||||
PERROR_MACRO("stat");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
if (S_ISDIR(path_stat.st_mode)) {
|
||||
FPRINTF_MACRO(stderr, "Error: %s is a directory\n", file_path);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
#endif
|
||||
|
||||
// open the file
|
||||
FILE* file = fopen(file_path, "rb+");
|
||||
if (file == NULL) {
|
||||
#ifndef SYS_UNIX_GENERAL
|
||||
DOES_NOT_EXIST;
|
||||
#endif
|
||||
PERROR_MACRO("fopen");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
if (READ_CONFIG(C_CONTENTS))
|
||||
// FIXME: interprets *.txt files as binary
|
||||
file_type = determine_file_type(file, file_path);
|
||||
|
||||
printf("Configuration:\n"
|
||||
"> Only damaging file contents (PNG, BMP, WAV): %s\n"
|
||||
"> Preserving line endings: %s\n"
|
||||
"> Operating on printable ASCII characters exclusively: %s\n"
|
||||
"> Custom seed: %s\n"
|
||||
"> File type: %s\n"
|
||||
"\n",
|
||||
YES_NO(READ_CONFIG(C_CONTENTS)),
|
||||
YES_NO(READ_CONFIG(C_LINE_ENDINGS)),
|
||||
YES_NO(READ_CONFIG(C_PRINTABLE)),
|
||||
YES_NO(READ_CONFIG(C_CUSTOM_SEED)),
|
||||
file_type_to_string(file_type));
|
||||
|
||||
printf("Parameters:\n"
|
||||
"> Probability: %" PRIu8 "\n"
|
||||
"> Threshold: %" PRIu8 "\n"
|
||||
"> Passes: %" PRIuMAX "\n"
|
||||
"\n", probability, threshold, (uintmax_t) passes);
|
||||
|
||||
if (READ_CONFIG(C_CONFIRM)) {
|
||||
printf("Are you sure? (Y/n): ");
|
||||
fflush(stdout);
|
||||
if ((getchar()) != (int) 'Y') {
|
||||
printf("File corruption aborted.\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
fflush(stdout);
|
||||
|
||||
Corrupter_Param param = {
|
||||
.file = file,
|
||||
|
||||
// parameters
|
||||
.probability = probability,
|
||||
.threshold = threshold,
|
||||
.passes = passes,
|
||||
|
||||
.config = config,
|
||||
|
||||
// configuration
|
||||
.seed = READ_CONFIG(C_CUSTOM_SEED) ? &PRNG_seed_value : NULL,
|
||||
.type = file_type
|
||||
};
|
||||
|
||||
Corrupter_Result* result = corrupt_file(¶m);
|
||||
if (result == NULL) {
|
||||
PERROR_MACRO("corrupt_file memory allocation");
|
||||
return EXIT_FAILURE;
|
||||
} else if (result->error) {
|
||||
free(result);
|
||||
PERROR_MACRO("corrupt_file");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
putchar('\n');
|
||||
|
||||
if (result->damaged_bytes) {
|
||||
size_t dmg = result->damaged_bytes, fsize = result->file_size,
|
||||
passes = param.passes;
|
||||
printf("Byte hit counter: %" PRIuMAX " / %" PRIuMAX " = %.3Lf%%\n",
|
||||
(uintmax_t) dmg, (uintmax_t) (fsize * passes),
|
||||
((long double) dmg * 100.l) / (long double) (fsize * passes));
|
||||
} else {
|
||||
puts("No bytes were damaged");
|
||||
}
|
||||
|
||||
free(result);
|
||||
|
||||
// finish working with the file
|
||||
fclose(file);
|
||||
|
||||
puts("Done");
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
36
src/random_seed.c
Normal file
36
src/random_seed.c
Normal file
|
@ -0,0 +1,36 @@
|
|||
#include "random_seed.h"
|
||||
|
||||
/* function implementations */
|
||||
uint32_t mt_generate_random_seed(void) {
|
||||
uint32_t seed = (uint32_t) time(NULL);
|
||||
|
||||
#if defined(_POSIX_TIMERS) && (_POSIX_TIMERS > 0)
|
||||
struct timespec ts;
|
||||
clock_gettime(CLOCK_REALTIME, &ts);
|
||||
seed ^= (uint32_t) ts.tv_nsec;
|
||||
#endif
|
||||
|
||||
#if defined(SYS_NT)
|
||||
seed ^= (uint32_t) GetCurrentProcessId();
|
||||
#elif defined(SYS_UNIX_GENERAL)
|
||||
seed ^= (uint32_t) getpid();
|
||||
#endif
|
||||
|
||||
#if defined(SYS_NT)
|
||||
LARGE_INTEGER freq, count;
|
||||
QueryPerformanceFrequency(&freq);
|
||||
QueryPerformanceCounter(&count);
|
||||
seed ^= (uint32_t) (count.QuadPart * 1000 / freq.QuadPart);
|
||||
#elif defined(SYS_MAC)
|
||||
uint64_t uptime_usec;
|
||||
size_t len = sizeof(uptime_usec);
|
||||
sysctlbyname("kern.boottime", &uptime_usec, &len, NULL, 0);
|
||||
seed ^= (uint32_t) ((uptime_usec / 1000) & 0xFFFFFFFF);
|
||||
#elif defined(SYS_LINUX)
|
||||
struct timespec ts;
|
||||
clock_gettime(CLOCK_BOOTTIME, &ts);
|
||||
seed ^= (uint32_t) ((ts.tv_sec * 1000 + ts.tv_nsec / 1000000) & 0xFFFFFFFF);
|
||||
#endif
|
||||
|
||||
return seed;
|
||||
}
|
Loading…
Reference in a new issue