#include #include #include "vfs/vfs.h" #include "spinlock/spinlock.h" #include "util/util.h" #include "fs/portlfs/portlfs.h" #include "storedev/storedev.h" #include "baseimg/baseimg.h" #include "dlmalloc/malloc.h" #include "std/string.h" #include "kprintf.h" #include "errors.h" #include "hshtb.h" VfsTable VFS_TABLE; VfsBusyObjs VFS_BUSY_VOBJS; #define CHECK_BUSY_VFSOBJ() \ char *tmpbuf = dlmalloc(VFS_MOUNTPOINT_LABEL_MAX+1+VFS_PATH_MAX); \ \ memset(tmpbuf, 0, VFS_MOUNTPOINT_LABEL_MAX+1+VFS_PATH_MAX); \ strcat(tmpbuf, mountpoint); \ strcat(tmpbuf, ":"); \ strcat(tmpbuf, path); \ \ bool busy = false; \ VfsObj *vobj, *vobjtmp; \ spinlock_acquire(&VFS_BUSY_VOBJS.spinlock); \ LL_FOREACH_SAFE(VFS_BUSY_VOBJS.vobjs, vobj, vobjtmp) { \ if (strcmp(vobj->path, tmpbuf) == 0) { \ busy = true; \ break; \ } \ } \ spinlock_release(&VFS_BUSY_VOBJS.spinlock); \ \ dlfree(tmpbuf); void vfs_init_littlefs(VfsMountPoint *mp, bool format) { struct lfs_config *cfg = dlmalloc(sizeof(*cfg)); memset(cfg, 0, sizeof(*cfg)); cfg->context = mp; cfg->read = &portlfs_read; cfg->prog = &portlfs_prog; cfg->erase = &portlfs_erase; cfg->sync = &portlfs_sync; cfg->block_size = LITTLEFS_BLOCK_SIZE; cfg->block_count = mp->backingsd->capacity(mp->backingsd) / LITTLEFS_BLOCK_SIZE; cfg->read_size = LITTLEFS_BLOCK_SIZE; cfg->prog_size = LITTLEFS_BLOCK_SIZE; cfg->block_cycles = 16; cfg->cache_size = LITTLEFS_BLOCK_SIZE; cfg->lookahead_size = LITTLEFS_BLOCK_SIZE; cfg->read_buffer = NULL; cfg->prog_buffer = NULL; cfg->lookahead_buffer = NULL; cfg->name_max = 0; cfg->file_max = 0; cfg->attr_max = 0; if (format) { lfs_format(&mp->fs.littlefs.instance, cfg); } int err = lfs_mount(&mp->fs.littlefs.instance, cfg); if (err < 0) { ERR("vfs", "Little FS mount failed %d\n", err); } mp->cleanup = &littlefs_cleanup; mp->open = &littlefs_open; mp->stat = &littlefs_stat; mp->fetchdirent = &littlefs_fetchdirent; mp->mkdir = &littlefs_mkdir; mp->delete = &littlefs_delete; } int32_t vfs_stat(char *mountpoint, const char *path, FsStat *stat) { VfsMountPoint *mp = NULL; spinlock_acquire(&VFS_TABLE.spinlock); HSHTB_GET(VFS_TABLE.mountpoints, label, mountpoint, mp); spinlock_release(&VFS_TABLE.spinlock); if (mp == NULL) { return E_NOENTRY; } return mp->stat(mp, path, stat); } int32_t vfs_mkdir(char *mountpoint, const char *path) { VfsMountPoint *mp = NULL; spinlock_acquire(&VFS_TABLE.spinlock); HSHTB_GET(VFS_TABLE.mountpoints, label, mountpoint, mp); spinlock_release(&VFS_TABLE.spinlock); if (mp == NULL) { return E_NOENTRY; } return mp->mkdir(mp, path); } int32_t vfs_fetchdirent(char *mountpoint, const char *path, FsDirent *direntbuf, size_t idx) { VfsMountPoint *mp = NULL; spinlock_acquire(&VFS_TABLE.spinlock); HSHTB_GET(VFS_TABLE.mountpoints, label, mountpoint, mp); spinlock_release(&VFS_TABLE.spinlock); if (mp == NULL) { return E_NOENTRY; } return mp->fetchdirent(mp, path, direntbuf, idx); } int32_t vfs_delete(char *mountpoint, const char *path) { CHECK_BUSY_VFSOBJ() if (busy) { return E_NOTYET; } VfsMountPoint *mp = NULL; spinlock_acquire(&VFS_TABLE.spinlock); HSHTB_GET(VFS_TABLE.mountpoints, label, mountpoint, mp); spinlock_release(&VFS_TABLE.spinlock); if (mp == NULL) { return E_NOENTRY; } return mp->delete(mp, path); } int32_t vfs_mount(char *mountpoint, int32_t fstype, StoreDev *backingsd, bool format) { VfsMountPoint *mp = NULL; spinlock_acquire(&VFS_TABLE.spinlock); HSHTB_ALLOC(VFS_TABLE.mountpoints, label, mountpoint, mp); spinlock_release(&VFS_TABLE.spinlock); if (mp == NULL) { return E_NOMEMORY; } memcpy(mp->label, mountpoint, strlen(mountpoint)); mp->backingsd = backingsd; mp->fstype = fstype; switch (fstype) { case VFS_LITTLEFS: vfs_init_littlefs(mp, format); break; default: return E_UNKNOWN_FSTYPE; } return E_OK; } int32_t vfs_unmount(char *mountpoint) { VfsMountPoint *mp = NULL; spinlock_acquire(&VFS_TABLE.spinlock); HSHTB_GET(VFS_TABLE.mountpoints, label, mountpoint, mp); spinlock_release(&VFS_TABLE.spinlock); if (mp == NULL) { return E_NOENTRY; } spinlock_acquire(&mp->spinlock); int32_t err = mp->cleanup(mp); if (err != E_OK) { spinlock_release(&mp->spinlock); return err; } memset(mp, 0, sizeof(*mp)); spinlock_release(&mp->spinlock); return E_OK; } VfsObj *vfs_open(char *mountpoint, const char *path, uint32_t flags) { CHECK_BUSY_VFSOBJ() if (busy) { return NULL; } VfsMountPoint *mp = NULL; spinlock_acquire(&VFS_TABLE.spinlock); HSHTB_GET(VFS_TABLE.mountpoints, label, mountpoint, mp); spinlock_release(&VFS_TABLE.spinlock); if (mp == NULL) { return NULL; } VfsObj *vobj1 = mp->open(mp, path, flags); if (vobj1 == NULL) { return NULL; } strcat(vobj1->path, mountpoint); strcat(vobj1->path, ":"); strcat(vobj1->path, path); spinlock_acquire(&VFS_BUSY_VOBJS.spinlock); LL_APPEND(VFS_BUSY_VOBJS.vobjs, vobj1); spinlock_release(&VFS_BUSY_VOBJS.spinlock); return vobj1; } void vfs_close(VfsObj *vobj) { spinlock_acquire(&VFS_BUSY_VOBJS.spinlock); LL_REMOVE(VFS_BUSY_VOBJS.vobjs, vobj); spinlock_release(&VFS_BUSY_VOBJS.spinlock); vobj->cleanup(vobj); } void vfs_init(void) { memset(&VFS_TABLE, 0, sizeof(VFS_TABLE)); spinlock_init(&VFS_TABLE.spinlock); memset(&VFS_BUSY_VOBJS, 0, sizeof(VFS_BUSY_VOBJS)); spinlock_init(&VFS_BUSY_VOBJS.spinlock); { RamSdInitExtra extra = { .capacity = baseimg_getsize(), .preallocbuffer = (uint8_t *)baseimg_getaddr() }; StoreDev *backingsd = storedev_create(STOREDEV_RAMSD, "ramsd-0", &extra); if (backingsd == NULL) { return; } vfs_mount("base", VFS_LITTLEFS, backingsd, false); } { RamSdInitExtra extra = { .capacity = (1024*1024*10) }; StoreDev *backingsd = storedev_create(STOREDEV_RAMSD, "ramsd-1", &extra); if (backingsd == NULL) { return; } vfs_mount("temp", VFS_LITTLEFS, backingsd, true); } LOG("vfs", "init\n"); for (size_t i = 0; i < LEN(VFS_TABLE.mountpoints); i++) { if (VFS_TABLE.mountpoints[i]._hshtbstate != HSHTB_TAKEN) continue; VfsMountPoint *vmp = &VFS_TABLE.mountpoints[i]; LOG("vfs", "mount point %s: %s, backing device: %s\n", vmp->label, vfs_strings[vmp->fstype], storedev_strings[vmp->backingsd->sdtype]); } }