initial codebase upload

This commit is contained in:
Intel A80486DX2-66 2024-07-07 00:48:23 +03:00
parent abd4c064fb
commit cccde99e80
Signed by: 80486DX2-66
GPG key ID: 83631EF27054609B
21 changed files with 1299 additions and 0 deletions

53
.gitignore vendored Normal file
View 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
View 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
View 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
View 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
View 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
View 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 */

View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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(&param);
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
View 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;
}