aboutsummaryrefslogtreecommitdiff
path: root/src/std/file.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/std/file.c')
-rw-r--r--src/std/file.c152
1 files changed, 152 insertions, 0 deletions
diff --git a/src/std/file.c b/src/std/file.c
new file mode 100644
index 0000000..7701be4
--- /dev/null
+++ b/src/std/file.c
@@ -0,0 +1,152 @@
+#include "../../include/std/file.h"
+#include "../../include/std/mem.h"
+#include "../../include/std/log.h"
+#include <stdio.h>
+#include <errno.h>
+#include <assert.h>
+#include <string.h>
+
+#include <dirent.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <sys/mman.h>
+#include <unistd.h>
+
+#define SCANDIR_PATH_LENGTH 4096
+
+static int scan_dir_recursive_internal(char *dir_path, int path_length, scan_dir_callback_func callback_func, void *userdata) {
+ DIR *dir;
+ struct dirent *entry;
+ struct stat file_stats;
+ int filename_length;
+ int path_length_start;
+
+ path_length_start = path_length;
+ if((dir = opendir(dir_path)) == NULL) {
+ amal_log_perror("scan_dir_recursive");
+ return errno;
+ }
+
+ while((entry = readdir(dir)) != NULL) {
+ /*
+ Skip hidden files. Name will always at least include null terminator
+ so it's ok to not check filename length. And file names are never empty, right???
+ */
+ if(entry->d_name[0] == '.')
+ continue;
+
+ /* Reset path concat, because it's overwritten by recursive call to scan dir */
+ path_length = path_length_start;
+ filename_length = strlen(entry->d_name);
+ /* current path '/' filename '\0' */
+ if(path_length + 1 + filename_length + 1 > SCANDIR_PATH_LENGTH) {
+ amal_log_error("Filepath too long. Trying to append %s to %s", entry->d_name, dir_path);
+ return -1;
+ }
+
+ /* Replace '\0' with '/' */
+ dir_path[path_length] = '/';
+ ++path_length;
+ am_memcpy(&dir_path[path_length], entry->d_name, filename_length);
+ path_length += filename_length;
+ dir_path[path_length] = '\0';
+
+ if(stat(dir_path, &file_stats) == -1) {
+ amal_log_perror("scan_dir_recursive_internal");
+ goto cleanup;
+ }
+
+ if(S_ISDIR(file_stats.st_mode)) {
+ cleanup_if_error(scan_dir_recursive_internal(dir_path, path_length, callback_func, userdata));
+ } else if(S_ISREG(file_stats.st_mode)) {
+ if(!callback_func(dir_path, path_length, userdata))
+ goto cleanup_noerr;
+ } else if(S_ISLNK(file_stats.st_mode)) {
+ amal_log_warning("Symlink ignored: %s", entry->d_name);
+ } else {
+ /* Ignore block devices and all other types of files */
+ }
+ }
+
+ cleanup_noerr:
+ closedir(dir);
+ return 0;
+
+ cleanup:
+ closedir(dir);
+ return -1;
+}
+
+int scan_dir_recursive(const char *dir_path, scan_dir_callback_func callback_func, void *userdata) {
+ char scandir_path[SCANDIR_PATH_LENGTH];
+ int dir_path_length = strlen(dir_path) + 1; /* include null terminator */
+ am_memcpy(scandir_path, dir_path, dir_path_length);
+ return scan_dir_recursive_internal(scandir_path, dir_path_length - 1, callback_func, userdata);
+}
+
+static int mapped_file_mode_to_system_file_mode(MappedFileMode file_mode) {
+ switch(file_mode) {
+ case MAPPED_FILE_READ: return PROT_READ;
+ case MAPPED_FILE_WRITE: return PROT_WRITE;
+ case MAPPED_FILE_READ_WRITE: return PROT_READ | PROT_WRITE;
+ default:
+ assert(bool_false);
+ return PROT_READ;
+ }
+}
+
+int mapped_file_init(MappedFile *self, const char *filepath, MappedFileMode file_mode) {
+ struct stat file_stats;
+ int fd;
+ const char *memblock;
+ int map_file_system_mode;
+
+ self->file_data = NULL;
+ self->file_size = 0;
+ self->fd = -1;
+
+ fd = open(filepath, O_RDONLY);
+ if(fd == -1) return errno;
+ if(fstat(fd, &file_stats) == -1) {
+ amal_log_perror("map_file_read");
+ goto cleanup;
+ }
+
+ map_file_system_mode = mapped_file_mode_to_system_file_mode(file_mode);
+ memblock = mmap(NULL, file_stats.st_size, map_file_system_mode, MAP_PRIVATE, fd, 0);
+ if(memblock == MAP_FAILED) {
+ amal_log_perror("map_file_read");
+ goto cleanup;
+ }
+
+ self->file_data = memblock;
+ self->file_size = file_stats.st_size;
+ self->fd = fd;
+ return 0;
+
+ cleanup:
+ close(fd);
+ return -1;
+}
+
+int mapped_file_deinit(MappedFile *self) {
+ int result_munmap;
+ int result_close;
+
+ if(!self->file_data)
+ return 0;
+
+ result_munmap = munmap((void*)self->file_data, self->file_size);
+ if(result_munmap == -1)
+ amal_log_perror("mapped_file_deinit");
+ result_close = close(self->fd);
+ if(result_close == -1)
+ amal_log_perror("mapped_file_deinit");
+ self->file_data = NULL;
+ self->file_size = 0;
+ self->fd = 0;
+ if(result_munmap == -1 || result_close == -1)
+ return -1;
+ else
+ return 0;
+} \ No newline at end of file