mirror of
https://gitlab.com/80486DX2-66/gists
synced 2025-05-31 08:31:41 +05:30
reverse-ramdisk.c: revision 2
This commit is contained in:
@@ -1,24 +1,15 @@
|
|||||||
/*
|
/*
|
||||||
* reverse-ramdisk.c
|
* reverse-ramdisk.c: revision 2
|
||||||
*
|
*
|
||||||
* C programming idea: Handling temporary files like memory allocations
|
* C programming idea: Handling temporary files like memory allocations
|
||||||
* (allocating -> creating empty file, using -> locking for R/W,
|
* (allocating -> creating empty file, using -> locking for R/W,
|
||||||
* freeing -> deleting).
|
* freeing -> deleting).
|
||||||
*
|
*
|
||||||
* 20% AI, 80% human (the code is tested and reviewed)
|
|
||||||
*
|
|
||||||
* XXX: The current result is quick and dirty. Not for educational or
|
|
||||||
* production purposes.
|
|
||||||
*
|
|
||||||
* Compile with set macro TEST (using -DTEST) to set macro TEST as defined,
|
* Compile with set macro TEST (using -DTEST) to set macro TEST as defined,
|
||||||
* and/or with set macro DEBUG (using -DDEBUG) to enable debug mode
|
* and/or with set macro DEBUG (using -DDEBUG) to enable debug mode
|
||||||
*
|
*
|
||||||
* TODO:XXX: `tf_free`: Do not shift the array, only empty entry
|
* TODO: Make it possible to reuse non-last elements
|
||||||
* TODO:XXX: `tf_free`, `tf_write`, `tf_read`: Do not use `ID` as `index`, but
|
* FIXME: First file not being deleted from file system
|
||||||
* look for it in TempFile structs
|
|
||||||
* TODO:XXX: Set freed memory pointers to NULL
|
|
||||||
* TODO:XXX: Add more error handling
|
|
||||||
* TODO: Test: Automate the test verification
|
|
||||||
*
|
*
|
||||||
* Author: Intel A80486DX2-66
|
* Author: Intel A80486DX2-66
|
||||||
* License: Creative Commons Zero 1.0 Universal or Unlicense
|
* License: Creative Commons Zero 1.0 Universal or Unlicense
|
||||||
@@ -33,22 +24,64 @@
|
|||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
#if _POSIX_VERSION >= 200112L
|
/* macros: definitions */
|
||||||
# include <fcntl.h>
|
#ifdef __unix__
|
||||||
# include <unistd.h>
|
# include <unistd.h>
|
||||||
# define IS_POSIX 1
|
|
||||||
#else
|
|
||||||
# define IS_POSIX 0
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if _POSIX_VERSION >= 200112L
|
||||||
|
# include <fcntl.h>
|
||||||
|
# define IS_POSIX
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* typedefs */
|
||||||
|
typedef bool tf_error_t;
|
||||||
|
#ifdef IS_POSIX
|
||||||
|
typedef int tf_file_t;
|
||||||
|
#else
|
||||||
|
typedef FILE* tf_file_t;
|
||||||
|
#endif
|
||||||
|
typedef unsigned char uintmin_t;
|
||||||
|
|
||||||
|
/* structures */
|
||||||
|
struct _Temp_File {
|
||||||
|
bool locked;
|
||||||
|
char* file_path;
|
||||||
|
tf_file_t file;
|
||||||
|
size_t size;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* typedefs: structures */
|
||||||
|
typedef struct _Temp_File temp_file_t;
|
||||||
|
|
||||||
|
/* macros: definitions */
|
||||||
|
#define TEMP_FILES_SUCCESS true
|
||||||
|
#define TEMP_FILES_FAILURE false
|
||||||
|
#define TEMP_FILES_ALLOC_FAILURE SIZE_MAX
|
||||||
|
|
||||||
|
#define TEMP_FILES_POSIX_PERMISSIONS (S_IRUSR | S_IWUSR)
|
||||||
|
|
||||||
|
#ifdef IS_POSIX
|
||||||
|
# define FCNTL_SUPPORTED
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* macros: lambdas */
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
# define LINE_FAIL(x) printf("failed on line %d\n", __LINE__ + x)
|
# define LINE_FAIL(x) printf("failed on line %d\n", __LINE__ + x)
|
||||||
#else
|
#else
|
||||||
# define LINE_FAIL(x)
|
# define LINE_FAIL(x)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if defined(DEBUG) || defined(TEST)
|
#ifdef IS_POSIX
|
||||||
|
# define TEMP_FILES_SEEK_MACRO lseek
|
||||||
|
#else
|
||||||
|
# define TEMP_FILES_SEEK_MACRO fseek
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* macros: procedures */
|
||||||
|
#if defined(DEBUG)
|
||||||
# define DBG_PRINT(...) do { \
|
# define DBG_PRINT(...) do { \
|
||||||
|
fputs("[debug] ", stdout); \
|
||||||
printf(__VA_ARGS__); \
|
printf(__VA_ARGS__); \
|
||||||
fflush(stdout); \
|
fflush(stdout); \
|
||||||
} while (0)
|
} while (0)
|
||||||
@@ -61,29 +94,44 @@
|
|||||||
exit(EXIT_FAILURE); \
|
exit(EXIT_FAILURE); \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
typedef struct {
|
/* macros: procedures: internal */
|
||||||
bool locked;
|
#define TEMP_FILES_CHECK_AND_LOCK \
|
||||||
int ID;
|
if (temp_files[n].locked) { \
|
||||||
char* file_path;
|
errno = EBUSY; \
|
||||||
#if IS_POSIX
|
return TEMP_FILES_FAILURE; \
|
||||||
int
|
} \
|
||||||
#else
|
temp_files[n].locked = true
|
||||||
FILE*
|
#define TEMP_FILES_RELEASE \
|
||||||
#endif
|
temp_files[n].locked = false
|
||||||
file;
|
|
||||||
} TempFile;
|
|
||||||
|
|
||||||
TempFile* temp_files = NULL;
|
/* global variables */
|
||||||
|
temp_file_t* temp_files = NULL;
|
||||||
size_t num_temp_files = 0;
|
size_t num_temp_files = 0;
|
||||||
|
|
||||||
int tf_alloc(size_t n, size_t type_size);
|
/* function definitions */
|
||||||
int tf_free(int ID);
|
static size_t tf_alloc(size_t items, size_t size);
|
||||||
int tf_write(int ID, size_t offset, void* src, size_t data_size);
|
static tf_error_t tf_free(size_t n);
|
||||||
int tf_read(int ID, size_t offset, void* dest, size_t data_size);
|
static tf_error_t tf_read(size_t n, size_t offset, void* dest,
|
||||||
void tf_finish(void);
|
size_t data_size);
|
||||||
|
static tf_error_t tf_wipe(tf_file_t file, size_t items, size_t size);
|
||||||
|
static tf_error_t tf_write(size_t n, size_t offset, void* src,
|
||||||
|
size_t data_size);
|
||||||
|
static void tf_finish(void);
|
||||||
|
/* function definitions: internal */
|
||||||
|
#ifdef FCNTL_SUPPORTED
|
||||||
|
static void tf_fs_lock(int file);
|
||||||
|
static void tf_fs_unlock(int file);
|
||||||
|
#else
|
||||||
|
# define tf_fs_lock(...)
|
||||||
|
# define tf_fs_unlock(...)
|
||||||
|
#endif
|
||||||
|
|
||||||
int tf_alloc(size_t n, size_t type_size) {
|
/* function implementations */
|
||||||
DBG_PRINT("tf_alloc(%zu, %zu)\n", n, type_size);
|
static size_t tf_alloc(size_t items, size_t size) {
|
||||||
|
if ((num_temp_files + 1) == TEMP_FILES_ALLOC_FAILURE) {
|
||||||
|
errno = ENOSPC;
|
||||||
|
return TEMP_FILES_ALLOC_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
// Create an empty file
|
// Create an empty file
|
||||||
size_t len_digit =
|
size_t len_digit =
|
||||||
@@ -91,254 +139,157 @@ int tf_alloc(size_t n, size_t type_size) {
|
|||||||
1
|
1
|
||||||
:
|
:
|
||||||
(size_t) floor(log10((double) num_temp_files)) + 1,
|
(size_t) floor(log10((double) num_temp_files)) + 1,
|
||||||
file_path_len = len_digit + strlen("tf_.tmp");
|
file_path_len = len_digit + strlen("tf_.tmp");
|
||||||
char* file_path = malloc((file_path_len + 1) * sizeof(char));
|
char* file_path = malloc((file_path_len + 1) * sizeof(char));
|
||||||
if (file_path == NULL) {
|
if (file_path == NULL) {
|
||||||
LINE_FAIL(-2);
|
LINE_FAIL(-2);
|
||||||
return -1;
|
return TEMP_FILES_ALLOC_FAILURE;
|
||||||
}
|
}
|
||||||
|
|
||||||
int res = snprintf(file_path, file_path_len + 1, "tf_%" PRIuMAX ".tmp",
|
int res = snprintf(file_path, file_path_len + 1, "tf_%" PRIuMAX ".tmp",
|
||||||
(uintmax_t) num_temp_files);
|
(uintmax_t) num_temp_files);
|
||||||
if ((size_t) res != file_path_len) {
|
if ((size_t) res != file_path_len) {
|
||||||
LINE_FAIL(-2);
|
LINE_FAIL(-3);
|
||||||
return -1;
|
return TEMP_FILES_ALLOC_FAILURE;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if IS_POSIX
|
tf_file_t file;
|
||||||
int file = open(file_path, O_RDWR | O_CREAT, 0666);
|
|
||||||
if (file == -1) {
|
for (uintmin_t i = 0; i < 2; i++) {
|
||||||
|
file =
|
||||||
|
#ifdef IS_POSIX
|
||||||
|
open(file_path, O_RDWR | O_CREAT | O_EXCL,
|
||||||
|
TEMP_FILES_POSIX_PERMISSIONS);
|
||||||
|
if (file == -1) {
|
||||||
#else
|
#else
|
||||||
FILE* file = fopen(file_path, "w+b");
|
fopen(file_path, "w+b");
|
||||||
if (file == NULL) {
|
if (file == NULL) {
|
||||||
#endif
|
#endif
|
||||||
free(file_path);
|
if (i < 1) {
|
||||||
LINE_FAIL(-2);
|
unlink(file_path);
|
||||||
return -1;
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
free(file_path);
|
||||||
|
LINE_FAIL(-16);
|
||||||
|
return TEMP_FILES_ALLOC_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Allocate/reallocate memory for all TempFiles
|
// Allocate/reallocate memory for all TempFiles
|
||||||
temp_files = temp_files == NULL ?
|
temp_files = temp_files == NULL ?
|
||||||
malloc(sizeof(TempFile))
|
malloc(sizeof(temp_file_t))
|
||||||
:
|
:
|
||||||
realloc(temp_files, (num_temp_files + 1) * sizeof(TempFile));
|
realloc(temp_files, (num_temp_files + 1) * sizeof(temp_file_t));
|
||||||
if (temp_files == NULL) {
|
if (temp_files == NULL) {
|
||||||
free(file_path);
|
free(file_path);
|
||||||
#if IS_POSIX
|
#ifdef IS_POSIX
|
||||||
close(file);
|
|
||||||
#else
|
|
||||||
fclose(file);
|
|
||||||
#endif
|
|
||||||
LINE_FAIL(-2);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Assign the ID, file path, file handler
|
|
||||||
temp_files[num_temp_files].locked = false;
|
|
||||||
temp_files[num_temp_files].ID = num_temp_files;
|
|
||||||
temp_files[num_temp_files].file_path = file_path;
|
|
||||||
temp_files[num_temp_files].file = file;
|
|
||||||
|
|
||||||
// Increment the number of temporary files
|
|
||||||
num_temp_files++;
|
|
||||||
|
|
||||||
return temp_files[num_temp_files - 1].ID;
|
|
||||||
}
|
|
||||||
|
|
||||||
int tf_free(int ID) {
|
|
||||||
DBG_PRINT("tf_free(%d)\n", ID);
|
|
||||||
|
|
||||||
size_t index = (size_t) ID;
|
|
||||||
if (index >= num_temp_files) {
|
|
||||||
errno = EINVAL;
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (temp_files[index].locked) {
|
|
||||||
errno = EBUSY;
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
temp_files[index].locked = true;
|
|
||||||
|
|
||||||
#if IS_POSIX
|
|
||||||
close
|
|
||||||
#else
|
|
||||||
fclose
|
|
||||||
#endif
|
|
||||||
(temp_files[index].file);
|
|
||||||
|
|
||||||
// Delete the file
|
|
||||||
if (remove(temp_files[index].file_path) != 0) {
|
|
||||||
temp_files[index].locked = false;
|
|
||||||
LINE_FAIL(-2);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
free(temp_files[index].file_path);
|
|
||||||
temp_files[index].file_path = NULL;
|
|
||||||
|
|
||||||
// Shift the remaining temporary files in the array
|
|
||||||
for (size_t i = index; i < num_temp_files - 1; i++)
|
|
||||||
temp_files[i] = temp_files[i + 1];
|
|
||||||
|
|
||||||
// Reallocate memory for the temp_files array
|
|
||||||
if (--num_temp_files > 0) {
|
|
||||||
DBG_PRINT("num_temp_files = %zu\n", num_temp_files);
|
|
||||||
TempFile* new_temp_files_ptr = realloc(temp_files, num_temp_files *
|
|
||||||
sizeof(TempFile));
|
|
||||||
if (new_temp_files_ptr == NULL) {
|
|
||||||
LINE_FAIL(-3);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
temp_files = new_temp_files_ptr;
|
|
||||||
} else {
|
|
||||||
free(temp_files);
|
|
||||||
temp_files = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
temp_files[index].locked = false;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int tf_write(int ID, size_t offset, void* src, size_t data_size) {
|
|
||||||
DBG_PRINT("tf_write(%d, %zu, %p, %zu)\n", ID, offset, src, data_size);
|
|
||||||
|
|
||||||
size_t index = (size_t) ID;
|
|
||||||
if (index >= num_temp_files) {
|
|
||||||
errno = EINVAL;
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (temp_files[index].locked) {
|
|
||||||
errno = EBUSY;
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
temp_files[index].locked = true;
|
|
||||||
|
|
||||||
#if IS_POSIX
|
|
||||||
// Check file handler for -1
|
|
||||||
int file = temp_files[index].file;
|
|
||||||
if (file == -1)
|
|
||||||
#else
|
|
||||||
// Check file handler for NULL
|
|
||||||
FILE* file = temp_files[index].file;
|
|
||||||
if (file == NULL)
|
|
||||||
#endif
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
// Set the position
|
|
||||||
if (
|
|
||||||
#if IS_POSIX
|
|
||||||
lseek
|
|
||||||
#else
|
|
||||||
fseek
|
|
||||||
#endif
|
|
||||||
(file, offset, SEEK_SET) == -1) {
|
|
||||||
LINE_FAIL(-1);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Write the data to the file
|
|
||||||
#if IS_POSIX
|
|
||||||
ssize_t
|
|
||||||
#else
|
|
||||||
size_t
|
|
||||||
#endif
|
|
||||||
bytes_written =
|
|
||||||
#if IS_POSIX
|
|
||||||
write(file, src, data_size);
|
|
||||||
#else
|
|
||||||
fwrite(src, 1, data_size, file);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (
|
|
||||||
#if IS_POSIX
|
|
||||||
(size_t)
|
|
||||||
#endif
|
|
||||||
bytes_written != data_size) {
|
|
||||||
temp_files[index].locked = false;
|
|
||||||
errno = EIO;
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
#if IS_POSIX
|
|
||||||
if (fsync(file) == -1) {
|
|
||||||
temp_files[index].locked = false;
|
|
||||||
LINE_FAIL(-2);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
fflush(file);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
temp_files[index].locked = false;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int tf_read(int ID, size_t offset, void* dest, size_t data_size) {
|
|
||||||
DBG_PRINT("tf_read(%d, %zu, %p, %zu)\n", ID, offset, dest, data_size);
|
|
||||||
|
|
||||||
size_t index = (size_t) ID;
|
|
||||||
if (index >= num_temp_files) {
|
|
||||||
errno = EINVAL;
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (temp_files[index].locked) {
|
|
||||||
errno = EBUSY;
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
temp_files[index].locked = true;
|
|
||||||
|
|
||||||
#if IS_POSIX
|
|
||||||
int
|
|
||||||
#else
|
|
||||||
FILE*
|
|
||||||
#endif
|
|
||||||
file = temp_files[index].file;
|
|
||||||
if (file ==
|
|
||||||
#if IS_POSIX
|
|
||||||
-1
|
|
||||||
#else
|
|
||||||
NULL
|
|
||||||
#endif
|
|
||||||
) {
|
|
||||||
temp_files[index].locked = false;
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Read the data from the file
|
|
||||||
void* src = malloc(data_size);
|
|
||||||
if (src == NULL) {
|
|
||||||
temp_files[index].locked = false;
|
|
||||||
#if IS_POSIX
|
|
||||||
close
|
close
|
||||||
#else
|
#else
|
||||||
fclose
|
fclose
|
||||||
#endif
|
#endif
|
||||||
(file);
|
(file);
|
||||||
LINE_FAIL(-8);
|
LINE_FAIL(-8);
|
||||||
return -1;
|
return TEMP_FILES_ALLOC_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
tf_fs_lock(file);
|
||||||
|
|
||||||
|
tf_wipe(file, items, size);
|
||||||
|
|
||||||
|
temp_files[num_temp_files].locked = false;
|
||||||
|
temp_files[num_temp_files].file_path = file_path;
|
||||||
|
temp_files[num_temp_files].file = file;
|
||||||
|
temp_files[num_temp_files].size = items * size;
|
||||||
|
|
||||||
|
num_temp_files++;
|
||||||
|
|
||||||
|
return num_temp_files - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static tf_error_t tf_free(size_t n) {
|
||||||
|
TEMP_FILES_CHECK_AND_LOCK;
|
||||||
|
|
||||||
|
if ((n + 1) < num_temp_files) {
|
||||||
|
fputs("tf_free: [WARNING] Deallocating a non-last element; no action "
|
||||||
|
"taken.\n", stderr);
|
||||||
|
return TEMP_FILES_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
tf_wipe(temp_files[n].file, temp_files[n].size, 1);
|
||||||
|
tf_fs_unlock(temp_files[n].file);
|
||||||
|
|
||||||
|
#ifdef IS_POSIX
|
||||||
|
close(temp_files[n].file);
|
||||||
|
temp_files[n].file = -1;
|
||||||
|
#else
|
||||||
|
fclose(temp_files[n].file);
|
||||||
|
temp_files[n].file = NULL;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Delete the file
|
||||||
|
if (unlink(temp_files[n].file_path) == -1) {
|
||||||
|
temp_files[n].locked = false;
|
||||||
|
LINE_FAIL(-2);
|
||||||
|
return TEMP_FILES_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
free(temp_files[n].file_path);
|
||||||
|
temp_files[n].file_path = NULL;
|
||||||
|
temp_files[n].locked = false;
|
||||||
|
|
||||||
|
return TEMP_FILES_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
static tf_error_t tf_read(size_t n, size_t offset, void* dest,
|
||||||
|
size_t data_size) {
|
||||||
|
TEMP_FILES_CHECK_AND_LOCK;
|
||||||
|
|
||||||
|
tf_file_t file = temp_files[n].file;
|
||||||
|
if (file ==
|
||||||
|
#ifdef IS_POSIX
|
||||||
|
-1
|
||||||
|
#else
|
||||||
|
NULL
|
||||||
|
#endif
|
||||||
|
) {
|
||||||
|
temp_files[n].locked = false;
|
||||||
|
return TEMP_FILES_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read the data from the file
|
||||||
|
void* src = malloc(data_size);
|
||||||
|
if (src == NULL) {
|
||||||
|
temp_files[n].locked = false;
|
||||||
|
#ifdef IS_POSIX
|
||||||
|
close
|
||||||
|
#else
|
||||||
|
fclose
|
||||||
|
#endif
|
||||||
|
(file);
|
||||||
|
LINE_FAIL(-9);
|
||||||
|
return TEMP_FILES_FAILURE;
|
||||||
}
|
}
|
||||||
|
|
||||||
memset(src, 0, data_size); // clear destination
|
memset(src, 0, data_size); // clear destination
|
||||||
// Set the position
|
// Set the position
|
||||||
if (
|
if (
|
||||||
#if IS_POSIX
|
#ifdef IS_POSIX
|
||||||
lseek
|
lseek
|
||||||
#else
|
#else
|
||||||
fseek
|
fseek
|
||||||
#endif
|
#endif
|
||||||
(file, offset, SEEK_SET) == -1) {
|
(file, offset, SEEK_SET) == -1) {
|
||||||
free(src);
|
free(src);
|
||||||
temp_files[index].locked = false;
|
temp_files[n].locked = false;
|
||||||
LINE_FAIL(-3);
|
LINE_FAIL(-3);
|
||||||
return -1;
|
return TEMP_FILES_FAILURE;
|
||||||
}
|
}
|
||||||
|
|
||||||
// read bytes
|
// read bytes
|
||||||
#if IS_POSIX
|
#ifdef IS_POSIX
|
||||||
ssize_t bytes_read = read(file, src, data_size);
|
ssize_t bytes_read = read(file, src, data_size);
|
||||||
#else
|
#else
|
||||||
size_t bytes_read = fread(src, 1, data_size, file);
|
size_t bytes_read = fread(src, 1, data_size, file);
|
||||||
@@ -348,18 +299,18 @@ int tf_read(int ID, size_t offset, void* dest, size_t data_size) {
|
|||||||
free(src); // Free the allocated memory
|
free(src); // Free the allocated memory
|
||||||
|
|
||||||
if (
|
if (
|
||||||
#if IS_POSIX
|
#ifdef IS_POSIX
|
||||||
(size_t)
|
(size_t)
|
||||||
#endif
|
#endif
|
||||||
bytes_read != data_size) {
|
bytes_read != data_size) {
|
||||||
temp_files[index].locked = false;
|
temp_files[n].locked = false;
|
||||||
errno = EIO;
|
errno = EIO;
|
||||||
return -1;
|
return TEMP_FILES_FAILURE;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
printf("Read: ID = %d, src = %p, size = %zu -> '",
|
DBG_PRINT("Read: n = %" PRIuMAX ", src = %p, size = %" PRIuMAX " -> '",
|
||||||
ID, dest, data_size);
|
(uintmax_t) n, (void*) dest, (uintmax_t) data_size);
|
||||||
for (size_t i = 0; i < data_size; i++) {
|
for (size_t i = 0; i < data_size; i++) {
|
||||||
if (i > 0)
|
if (i > 0)
|
||||||
putc(' ', stdout);
|
putc(' ', stdout);
|
||||||
@@ -369,32 +320,195 @@ int tf_read(int ID, size_t offset, void* dest, size_t data_size) {
|
|||||||
fflush(stdout);
|
fflush(stdout);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
temp_files[index].locked = false;
|
TEMP_FILES_RELEASE;
|
||||||
|
|
||||||
return 0;
|
return TEMP_FILES_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
void tf_finish(void) {
|
static tf_error_t tf_wipe(tf_file_t file, size_t items, size_t size) {
|
||||||
DBG_PRINT("tf_finish()\n");
|
TEMP_FILES_SEEK_MACRO(file, 0, SEEK_SET);
|
||||||
|
|
||||||
for (size_t i = 0; i < num_temp_files; i++)
|
size_t n = items * size;
|
||||||
if (temp_files[i].file_path != NULL)
|
|
||||||
tf_free(temp_files[i].ID);
|
void* zeros = calloc(items, size);
|
||||||
|
if (zeros == NULL)
|
||||||
|
return TEMP_FILES_FAILURE;
|
||||||
|
|
||||||
|
for (size_t i = 0; i < n; i++) {
|
||||||
|
if (
|
||||||
|
#ifdef IS_POSIX
|
||||||
|
write(file, zeros, size)
|
||||||
|
#else
|
||||||
|
fwrite(zeros, size, 1, file)
|
||||||
|
#endif
|
||||||
|
!= 1) {
|
||||||
|
return TEMP_FILES_FAILURE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
free(zeros);
|
||||||
|
|
||||||
|
TEMP_FILES_SEEK_MACRO(file, 0, SEEK_SET);
|
||||||
|
|
||||||
|
return TEMP_FILES_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
static tf_error_t tf_write(size_t n, size_t offset, void* src,
|
||||||
|
size_t data_size) {
|
||||||
|
TEMP_FILES_CHECK_AND_LOCK;
|
||||||
|
|
||||||
|
#ifdef IS_POSIX
|
||||||
|
// Check file handler for -1
|
||||||
|
int file = temp_files[n].file;
|
||||||
|
if (file == -1)
|
||||||
|
#else
|
||||||
|
// Check file handler for NULL
|
||||||
|
FILE* file = temp_files[n].file;
|
||||||
|
if (file == NULL)
|
||||||
|
#endif
|
||||||
|
return TEMP_FILES_FAILURE;
|
||||||
|
|
||||||
|
// Set the position
|
||||||
|
if (TEMP_FILES_SEEK_MACRO(file, offset, SEEK_SET) == -1) {
|
||||||
|
LINE_FAIL(-1);
|
||||||
|
return TEMP_FILES_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write the data to the file
|
||||||
|
#ifdef IS_POSIX
|
||||||
|
ssize_t
|
||||||
|
#else
|
||||||
|
size_t
|
||||||
|
#endif
|
||||||
|
bytes_written =
|
||||||
|
#ifdef IS_POSIX
|
||||||
|
write(file, src, data_size);
|
||||||
|
#else
|
||||||
|
fwrite(src, 1, data_size, file);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (
|
||||||
|
#ifdef IS_POSIX
|
||||||
|
(size_t)
|
||||||
|
#endif
|
||||||
|
bytes_written != data_size) {
|
||||||
|
temp_files[n].locked = false;
|
||||||
|
errno = EIO;
|
||||||
|
return TEMP_FILES_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef IS_POSIX
|
||||||
|
if (fsync(file) == -1) {
|
||||||
|
temp_files[n].locked = false;
|
||||||
|
LINE_FAIL(-2);
|
||||||
|
return TEMP_FILES_FAILURE;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
fflush(file);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
TEMP_FILES_RELEASE;
|
||||||
|
|
||||||
|
return TEMP_FILES_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void tf_finish(void) {
|
||||||
|
for (size_t i = num_temp_files; i > 0; i--) {
|
||||||
|
size_t index = i - 1;
|
||||||
|
|
||||||
|
if (temp_files[index].file_path != NULL)
|
||||||
|
tf_free(index);
|
||||||
|
}
|
||||||
free(temp_files);
|
free(temp_files);
|
||||||
temp_files = NULL;
|
temp_files = NULL;
|
||||||
num_temp_files = 0;
|
num_temp_files = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* function implementations: internal */
|
||||||
|
#ifdef FCNTL_SUPPORTED
|
||||||
|
static void tf_fs_lock(int file) {
|
||||||
|
DBG_PRINT("Locking file...\n");
|
||||||
|
|
||||||
|
struct flock lock;
|
||||||
|
memset(&lock, 0, sizeof(lock));
|
||||||
|
lock.l_type = F_WRLCK;
|
||||||
|
lock.l_whence = SEEK_SET;
|
||||||
|
lock.l_start = 0;
|
||||||
|
lock.l_len = 0;
|
||||||
|
lock.l_pid = getpid();
|
||||||
|
|
||||||
|
if (fcntl(file, F_SETLK, &lock) == -1)
|
||||||
|
RETREAT("fcntl");
|
||||||
|
}
|
||||||
|
|
||||||
|
static void tf_fs_unlock(int file) {
|
||||||
|
struct flock lock;
|
||||||
|
memset(&lock, 0, sizeof(lock));
|
||||||
|
lock.l_type = F_UNLCK;
|
||||||
|
lock.l_whence = SEEK_SET;
|
||||||
|
lock.l_start = 0;
|
||||||
|
lock.l_len = 0;
|
||||||
|
lock.l_pid = getpid();
|
||||||
|
|
||||||
|
fcntl(file, F_SETLK, &lock);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef TEST
|
#ifdef TEST
|
||||||
|
/* macros: definitions */
|
||||||
|
# define ROUND_TRIP_DATA_FAILURE 0
|
||||||
|
# define ROUND_TRIP_DATA_SUCCESS 1
|
||||||
|
|
||||||
|
/* macros: lambdas */
|
||||||
|
# define ARRAY_SIZE(length, size) \
|
||||||
|
((size_t) length * (size_t) size)
|
||||||
|
|
||||||
|
/* macros: procedures */
|
||||||
|
# define ROUND_TRIP_DATA_MACRO(id, array, length, size) do { \
|
||||||
|
if (round_trip_data(id, array, ARRAY_SIZE(length, size)) == \
|
||||||
|
ROUND_TRIP_DATA_FAILURE) \
|
||||||
|
tests_failed += 1; \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
/* function definitions */
|
||||||
|
static int round_trip_data(size_t n, void* expected_data, size_t data_size);
|
||||||
|
|
||||||
|
/* function implementations */
|
||||||
|
static int round_trip_data(size_t n, void* expected_data, size_t data_size) {
|
||||||
|
if (tf_write(n, 0, expected_data, data_size) == TEMP_FILES_FAILURE)
|
||||||
|
RETREAT("tf_write");
|
||||||
|
|
||||||
|
void* actual_data = malloc(data_size);
|
||||||
|
if (actual_data == NULL)
|
||||||
|
RETREAT("malloc");
|
||||||
|
|
||||||
|
if (tf_read(n, 0, actual_data, data_size) == TEMP_FILES_FAILURE)
|
||||||
|
RETREAT("tf_read");
|
||||||
|
|
||||||
|
bool data_is_equal = memcmp(expected_data, actual_data, data_size) == 0;
|
||||||
|
|
||||||
|
free(actual_data);
|
||||||
|
|
||||||
|
if (data_is_equal)
|
||||||
|
return ROUND_TRIP_DATA_SUCCESS;
|
||||||
|
|
||||||
|
printf("Data verification failed for ID %" PRIuMAX "\n", (uintmax_t) n);
|
||||||
|
return ROUND_TRIP_DATA_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
int main(void) {
|
int main(void) {
|
||||||
DBG_PRINT("started\n");
|
DBG_PRINT("started\n");
|
||||||
|
|
||||||
|
#ifdef IS_POSIX
|
||||||
|
DBG_PRINT("POSIX features are supported\n");
|
||||||
|
#endif
|
||||||
|
|
||||||
#define ARRAY_1_LEN 4
|
#define ARRAY_1_LEN 4
|
||||||
#define ARRAY_2_LEN 16
|
#define ARRAY_2_LEN 16
|
||||||
|
|
||||||
int ID_1 = tf_alloc(ARRAY_1_LEN, sizeof(int));
|
size_t ID_1 = tf_alloc(ARRAY_1_LEN, sizeof(int));
|
||||||
int ID_2 = tf_alloc(ARRAY_2_LEN, sizeof(uint16_t));
|
size_t ID_2 = tf_alloc(ARRAY_2_LEN, sizeof(uint16_t));
|
||||||
if (ID_1 == -1 || ID_2 == -1)
|
if (ID_1 == TEMP_FILES_ALLOC_FAILURE || ID_2 == TEMP_FILES_ALLOC_FAILURE)
|
||||||
RETREAT("tf_alloc");
|
RETREAT("tf_alloc");
|
||||||
DBG_PRINT("allocated memory\n");
|
DBG_PRINT("allocated memory\n");
|
||||||
|
|
||||||
@@ -406,52 +520,27 @@ int main(void) {
|
|||||||
test_data_2[i] = 1 << i;
|
test_data_2[i] = 1 << i;
|
||||||
DBG_PRINT("initialized array 2\n");
|
DBG_PRINT("initialized array 2\n");
|
||||||
|
|
||||||
for (size_t i = 0; i < ARRAY_1_LEN; i++)
|
// Automatic testing
|
||||||
if (tf_write(ID_1, i * sizeof(int), &test_data_1[i], sizeof(int)) == -1)
|
uintmax_t tests_failed = 0;
|
||||||
RETREAT("tf_write");
|
|
||||||
DBG_PRINT("wrote array 1\n");
|
|
||||||
|
|
||||||
for (size_t i = 0; i < ARRAY_2_LEN; i++)
|
// Pause
|
||||||
if (tf_write(ID_2, i * sizeof(uint16_t), &test_data_2[i],
|
fputs("Press Enter to continue: ", stdout);
|
||||||
sizeof(uint16_t)) == -1)
|
getchar();
|
||||||
RETREAT("tf_write");
|
|
||||||
DBG_PRINT("wrote array 2\n");
|
|
||||||
|
|
||||||
// round-trip
|
ROUND_TRIP_DATA_MACRO(ID_1, test_data_1, ARRAY_1_LEN, sizeof(int));
|
||||||
test_data_1[0] = 111;
|
ROUND_TRIP_DATA_MACRO(ID_2, test_data_2, ARRAY_2_LEN, sizeof(uint16_t));
|
||||||
test_data_1[1] = 222;
|
|
||||||
test_data_1[2] = 333;
|
|
||||||
test_data_1[3] = 444;
|
|
||||||
DBG_PRINT("filled array 1 with garbage\n");
|
|
||||||
|
|
||||||
for (size_t i = 0; i < 16; i++)
|
|
||||||
test_data_2[i] ^= 1;
|
|
||||||
DBG_PRINT("filled array 2 with garbage\n");
|
|
||||||
|
|
||||||
for (size_t i = 0; i < ARRAY_1_LEN; i++)
|
|
||||||
if (tf_read(ID_1, i * sizeof(int), &test_data_1[i], sizeof(int)) == -1)
|
|
||||||
RETREAT("tf_read");
|
|
||||||
DBG_PRINT("restored array 1\n");
|
|
||||||
|
|
||||||
for (size_t i = 0; i < ARRAY_2_LEN; i++)
|
|
||||||
if (tf_read(ID_2, i * sizeof(uint16_t), &test_data_2[i],
|
|
||||||
sizeof(uint16_t)) == -1)
|
|
||||||
RETREAT("tf_read");
|
|
||||||
DBG_PRINT("restored array 2\n");
|
|
||||||
|
|
||||||
DBG_PRINT("Values (1): ");
|
|
||||||
for (size_t i = 0; i < ARRAY_1_LEN; i++)
|
|
||||||
DBG_PRINT("%d%c", test_data_1[i], i == (ARRAY_1_LEN - 1) ? '\n' : ' ');
|
|
||||||
DBG_PRINT("Values (2): ");
|
|
||||||
for (size_t i = 0; i < ARRAY_2_LEN; i++)
|
|
||||||
DBG_PRINT("%d%c", test_data_2[i], i == (ARRAY_2_LEN - 1) ? '\n' : ' ');
|
|
||||||
|
|
||||||
tf_free(ID_1);
|
tf_free(ID_1);
|
||||||
tf_free(ID_2);
|
|
||||||
DBG_PRINT("freed both files\n");
|
|
||||||
|
|
||||||
tf_finish();
|
tf_finish();
|
||||||
|
|
||||||
|
if (tests_failed != 0) {
|
||||||
|
printf("%" PRIuMAX " tests failed\n", tests_failed);
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
puts("Tests passed!");
|
||||||
|
|
||||||
return EXIT_SUCCESS;
|
return EXIT_SUCCESS;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
Reference in New Issue
Block a user