#include "../../include/std/file.h" #include "../../include/std/mem.h" #include "../../include/std/log.h" #include "../../include/std/alloc.h" #include #include #include #include #include #include #include #include #include #include #include #define SCANDIR_PATH_LENGTH 4096 #define MAX_FILE_SIZE 1024*1024*48 /* 48 mb */ #if 0 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) { int error; error = errno; amal_log_perror("scan_dir_recursive"); return error; } 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; } } static int mapped_file_mode_to_open_mode(MappedFileMode file_mode) { switch(file_mode) { case MAPPED_FILE_READ: return O_RDONLY; case MAPPED_FILE_WRITE: return O_WRONLY; case MAPPED_FILE_READ_WRITE: return O_RDWR; default: assert(bool_false); return O_RDONLY; } } 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, mapped_file_mode_to_open_mode(file_mode)); if(fd == -1) return errno; if(fstat(fd, &file_stats) == -1) { amal_log_perror(filepath); 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_SHARED, fd, 0); if(memblock == MAP_FAILED) { amal_log_perror(filepath); 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; } #endif int read_whole_file(const char *filepath, Buffer *data_result) { FILE *file; int result; usize bytes_read; long size; file = fopen(filepath, "rb"); if(!file) { int error = errno; amal_log_error("read_whole_file: %s failed, error: %s", filepath, strerror(error)); return error; } result = fseek(file, 0, SEEK_END); if(result != 0) { result = ferror(file); goto cleanup; } size = ftell(file); if(size == -1) { int error = errno; result = error; amal_log_error("read_whole_file: %s failed, error: %s", filepath, strerror(error)); goto cleanup; } result = fseek(file, 0, SEEK_SET); if(result != 0) { result = -1; goto cleanup; } if(size > MAX_FILE_SIZE) { amal_log_error("File %s is too large (larger than 48 megabytes)", filepath); result = -1; goto cleanup; } assert(data_result->capacity == 0 && data_result->size == 0); cleanup_if_error(buffer_append_empty(data_result, size)); bytes_read = fread(data_result->data, 1, data_result->size, file); if(bytes_read != (unsigned long)size) result = -1; cleanup: fclose(file); return result; } int file_get_canonical_path(const char *filepath, char **result_path, usize *result_path_size) { char result_path_tmp[PATH_MAX]; if(!realpath(filepath, result_path_tmp)) { int ret; ret = errno; amal_log_error("file_get_canonical_path: realpath failed for path %s, error: %s", filepath, strerror(ret)); return ret; } *result_path_size = strnlen(result_path_tmp, PATH_MAX); return_if_error(am_malloc(*result_path_size + 1, (void**)result_path)); am_memcpy(*result_path, result_path_tmp, *result_path_size); (*result_path)[*result_path_size] = '\0'; return 0; } BufferView file_get_parent_directory(BufferView filepath) { int i; for(i = filepath.size - 1; i >= 0; --i) { if(filepath.data[i] == '/') return create_buffer_view(filepath.data, i); } return create_buffer_view(".", 1); } BufferView file_get_name(BufferView filepath) { int i; for(i = filepath.size - 1; i >= 0; --i) { if(filepath.data[i] == '/') return create_buffer_view(filepath.data + i, filepath.size - i); } return filepath; }