#define AL_LOG_SECTION "file" #include #include #include "../error.h" #include "file.h" static bool lock_file_internal(s32 fd, size_t size) { struct flock l = { .l_type = F_WRLCK, .l_whence = SEEK_SET, .l_start = 0, .l_len = size }; if (fcntl(fd, F_SETLKW, &l) == -1) { log_error("fcntl(F_SETLKW, F_WRLCK) failed (%s).", nn_strerror(errno)); close(fd); return false; } return true; } static bool unlock_file_internal(s32 fd, size_t size) { struct flock l = { .l_type = F_ULOCK, .l_whence = SEEK_SET, .l_start = 0, .l_len = size }; if (fcntl(fd, F_SETLKW, &l) == -1) { log_error("fcntl(F_SETLKW, F_ULOCK) failed (%s).", nn_strerror(errno)); return false; } return true; } static bool open_file_linux(s32 *fd, str *path, s32 flags) { char *c_str = al_str_to_c_str(path); bool success = (*fd = open(c_str, flags, 0644)) != -1; al_free(c_str); return success; } bool nn_file_open(struct nn_file *file, str *path, s32 flags) { file->flags = flags; s32 oflags = (flags & NNWT_FILE_READONLY) ? O_RDONLY : O_RDWR; if (flags & NNWT_FILE_CREATE) { oflags |= O_CREAT; } if (!open_file_linux(&file->fd, path, oflags)) { log_error("open(%.*s) failed (%d: %s).", al_str_x(path), errno, nn_strerror(errno)); return false; } struct stat sb; s32 res = fstat(file->fd, &sb); if (res == -1) { log_error("fstat() failed (%s).", nn_strerror(errno)); close(file->fd); return false; } file->size = sb.st_size; if (flags & NNWT_FILE_LOCK && !lock_file_internal(file->fd, file->size)) { close(file->fd); return false; } al_str_clone(&file->path, path); return true; } bool nn_file_exists(str *path) { char *c_str = al_str_to_c_str(path); s32 ret = access(c_str, F_OK); al_free(c_str); return ret == 0; } bool nn_file_create(str *path) { s32 fd; bool opened = open_file_linux(&fd, path, O_RDWR | O_CREAT); if (opened) close(fd); return opened; } off_t nn_file_seek(struct nn_file *file, off_t offset, s32 whence) { off_t res = lseek(file->fd, offset, whence); if (res == (off_t)-1) { log_error("lseek(%zd, %d) failed (%s).", offset, whence, nn_strerror(errno)); } return res; } bool nn_file_truncate(struct nn_file *file, off_t size) { s32 ret = ftruncate(file->fd, size); if (ret != 0) { log_error("ftruncate(%zd) failed (%s).", size, nn_strerror(errno)); return false; } file->size = size; return true; } bool nn_file_flush(struct nn_file *file) { s32 ret = fsync(file->fd); if (ret != 0) { log_error("fsync() failed (%s).", nn_strerror(errno)); return false; } return true; } #define file_io_loop(call, fail_case) \ size_t bc = 0; \ ssize_t res; \ for (;;) { \ res = call(file->fd, buf, size - bc); \ if (fail_case) { \ log_error(""#call"() failed (%s).", nn_strerror(errno)); \ return false; \ } \ if ((bc += res) >= size) break; \ } \ return true bool nn_file_write(struct nn_file *file, void *buf, size_t size) { file_io_loop(write, (res == -1)); } bool nn_file_read(struct nn_file *file, void *buf, size_t size) { file_io_loop(read, (res == -1)); } void *nn_file_mmap(struct nn_file *file) { if (file->size == 0) return NULL; s32 flags = PROT_READ | ((file->flags & NNWT_FILE_READONLY) ? 0 : PROT_WRITE); void *map = mmap(NULL, file->size, flags, MAP_SHARED, file->fd, 0L); if (map == MAP_FAILED) { log_error("mmap(%.*s) failed (%s).", al_str_x(&file->path), nn_strerror(errno)); return NULL; } return map; } void *nn_file_mremap(struct nn_file *file, size_t size, void *old_map) { void *map = mremap(old_map, file->size, size, MREMAP_MAYMOVE); if (map == MAP_FAILED) { log_error("mremap(%p, %zu, %zu) failed (%s).", old_map, file->size, size, nn_strerror(errno)); return NULL; } return map; } void nn_file_munmap(struct nn_file *file, void *map) { s32 ret = munmap(map, file->size); if (ret != 0) { log_error("munmap(%p, %zu) failed (%s).", map, file->size, nn_strerror(errno)); } } void nn_file_close(struct nn_file *file) { if (file->flags & NNWT_FILE_LOCK) { unlock_file_internal(file->fd, file->size); } close(file->fd); al_str_free(&file->path); } bool nn_dir_open(struct nn_dir *dir, str *path) { char *c_str = al_str_to_c_str(path); dir->dir = opendir(c_str); al_free(c_str); if (!dir->dir) { log_error("opendir(%.*s) failed (%s).", al_str_x(path), nn_strerror(errno)); return false; } al_str_clone(&dir->path, path); return true; } void nn_dir_close(struct nn_dir *dir) { closedir(dir->dir); al_str_free(&dir->path); } bool nn_dir_exists(str *path) { DIR *dir; char *c_str = al_str_to_c_str(path); dir = opendir(c_str); al_free(c_str); if (!dir) { if (errno != ENOENT) { log_error("opendir(%.*s) failed (%s).", al_str_x(path), nn_strerror(errno)); } return false; } closedir(dir); return true; } bool nn_dir_create(str *path) { char *c_str = al_str_to_c_str(path); s32 ret = mkdir(c_str, 0755); al_free(c_str); if (ret == -1) { log_error("mkdir(%.*s) failed (%s).", al_str_x(path), nn_strerror(errno)); return false; } return true; } bool nn_dir_read(struct nn_dir *dir, struct nn_dir_entry *entry) { errno = 0; if (!(entry->entry = readdir(dir->dir))) { if (errno) { log_error("readdir(%.*s) failed (%s).", al_str_x(&dir->path), nn_strerror(errno)); } return false; } switch (entry->entry->d_type) { case DT_UNKNOWN: entry->type = NNWT_ENTRY_FILE; break; case DT_REG: case DT_DIR: entry->type = NNWT_ENTRY_DIR; break; default: entry->type = NNWT_ENTRY_OTHER; break; } return true; } void nn_dir_entry_get_name(struct nn_dir_entry *entry, str *name) { al_str_clone(name, &al_str_cr(entry->entry->d_name)); } const char *nn_dir_entry_get_os_name(struct nn_dir_entry *entry) { return entry->entry->d_name; } void nn_dir_entry_get_path(struct nn_dir_entry *entry, struct nn_dir *dir, str *path) { al_str_clone(path, &dir->path); al_str_cat(path, &al_str_c("/")); al_str_cat(path, &al_str_cr(entry->entry->d_name)); }