Mercurial > molko
view libmlk-core/mlk/core/vfs-dir.c @ 641:fcd124e513ea
core: reintroduce VFS
author | David Demelier <markand@malikania.fr> |
---|---|
date | Sun, 01 Oct 2023 09:18:01 +0200 |
parents | 9850089c9671 |
children |
line wrap: on
line source
/* * vfs-dir.c -- VFS subsystem for directories * * Copyright (c) 2020-2023 David Demelier <markand@malikania.fr> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include <assert.h> #include <errno.h> #include <limits.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include "alloc.h" #include "err.h" #include "util.h" #include "vfs-dir.h" #include "vfs.h" #define MLK_VFS_DIR_FILE(self) \ MLK_CONTAINER_OF(self, struct mlk_vfs_dir_file, file) #define MLK_VFS_DIR(self) \ MLK_CONTAINER_OF(self, struct mlk_vfs_dir, vfs) static inline void normalize(char *path) { size_t len; len = strlen(path); for (char *p = path; *p; ++p) if (*p == '\\') *p = '/'; while (len && path[len - 1] == '/') path[--len] = 0; } /* * TODO * * Add an optional POSIX implementation based on open(2) if function is * available. */ static FILE * makefile(const char *path, const char *mode) { FILE *fp; const char *fmode = NULL; int r = 0, w = 0, e = 0, t = 0, seek = 0, flags, whence; long offset; size_t len; /* * Determine which read-write mode we need, if unset by the user assumes * read-only for convenience. * * Conversion as following: * * | mode | fopen mode | fseek? | tewr | * |-------+------------+---------+-------------| * | r | rb | no | 0001 (0x01) | * | w | r+b | yes, 0 | 0010 (0x02) | * | we | r+b | yes, -1 | 0110 (0x06) | * | rw | r+b | yes, 0 | 0011 (0x03) | * | rwe | r+b | yes, -1 | 0111 (0x07) | * | wt | wb | no | 1010 (0x0a) | * | rwt | w+b | no | 1011 (0x0b) | * * Notes: * * Standard C function fopen does not have support to open a file for * writing only at the beginning (without destroying content) so we have * to read-write with a call to fseek if at-end is requested. The mode * 'a' is never applicable because it always append to the file no * matter fseek position. * * We could use open(2) POSIX function but that would make the code less * portable. */ len = strlen(mode); r = strcspn(mode, "r") != len; w = strcspn(mode, "w") != len; e = strcspn(mode, "e") != len; t = strcspn(mode, "t") != len; /* For simplicity, convert the individual open mode into a bitmask. */ flags = r << 0 | w << 1 | e << 2 | t << 3; switch (flags) { case 0x01: fmode = "rb"; break; case 0x02: case 0x03: fmode = "r+b"; seek = 1; offset = 0; whence = SEEK_SET; break; case 0x06: case 0x07: fmode = "r+b"; seek = 1; offset = 0; whence = SEEK_END; break; case 0x0a: fmode = "wb"; break; case 0x0b: fmode = "w+b"; break; default: mlk_errf("incompatible modes specified"); return NULL; } if (!(fp = fopen(path, fmode))) { mlk_errf("%s", strerror(errno)); return NULL; } if (seek && fseek(fp, offset, whence) < 0) { fclose(fp); mlk_errf("%s", strerror(errno)); return NULL; } return fp; } static size_t file_read(struct mlk_vfs_file *self, void *buf, size_t bufsz) { return mlk_vfs_dir_file_read(MLK_VFS_DIR_FILE(self), buf, bufsz); } static size_t file_write(struct mlk_vfs_file *self, const void *buf, size_t bufsz) { return mlk_vfs_dir_file_write(MLK_VFS_DIR_FILE(self), buf, bufsz); } static int file_flush(struct mlk_vfs_file *self) { return mlk_vfs_dir_file_flush(MLK_VFS_DIR_FILE(self)); } static void file_finish(struct mlk_vfs_file *self) { mlk_vfs_dir_file_finish(MLK_VFS_DIR_FILE(self)); } static struct mlk_vfs_file * vfs_open(struct mlk_vfs *self, const char *entry, const char *mode) { return mlk_vfs_dir_open(MLK_VFS_DIR(self), entry, mode); } void mlk_vfs_dir_init(struct mlk_vfs_dir *dir, const char *path) { assert(dir); assert(path); mlk_util_strlcpy(dir->path, path, sizeof (dir->path)); /* Remove terminator and switch to UNIX paths. */ normalize(dir->path); dir->vfs.data = NULL; dir->vfs.open = vfs_open; dir->vfs.finish = NULL; } struct mlk_vfs_file * mlk_vfs_dir_open(struct mlk_vfs_dir *dir, const char *entry, const char *mode) { struct mlk_vfs_dir_file *file; file = mlk_alloc_new0(1, sizeof (*file)); snprintf(file->path, sizeof (file->path), "%s/%s", dir->path, entry); if (!(file->handle = makefile(file->path, mode))) { mlk_alloc_free(file); return NULL; } file->file.read = file_read; file->file.write = file_write; file->file.flush = file_flush; file->file.finish = file_finish; return &file->file; } void mlk_vfs_dir_finish(struct mlk_vfs_dir *dir) { assert(dir); /* Kept for future use. */ (void)dir; } size_t mlk_vfs_dir_file_read(struct mlk_vfs_dir_file *file, void *buf, size_t bufsz) { assert(file); assert(buf); size_t rv; rv = fread(buf, 1, bufsz, file->handle); if (ferror(file->handle)) return -1; return rv; } size_t mlk_vfs_dir_file_write(struct mlk_vfs_dir_file *file, const void *buf, size_t bufsz) { assert(file); assert(buf); size_t rv; rv = fwrite(buf, 1, bufsz, file->handle); if (ferror(file->handle)) return -1; return rv; } int mlk_vfs_dir_file_flush(struct mlk_vfs_dir_file *file) { assert(file); return fflush(file->handle) == EOF ? -1 : 0; } void mlk_vfs_dir_file_finish(struct mlk_vfs_dir_file *file) { assert(file); fclose(file->handle); mlk_alloc_free(file); }