226 lines
6.2 KiB
C
226 lines
6.2 KiB
C
#include <stdint.h>
|
|
#include <stdbool.h>
|
|
#include "fs/fatfs/fat_context.h"
|
|
#include "fs/fatfs/fat_filelib.h"
|
|
#include "sysdefs/fs.h"
|
|
#include "vfs/vfs.h"
|
|
#include "dlmalloc/malloc.h"
|
|
#include "std/string.h"
|
|
#include "spinlock/spinlock.h"
|
|
#include "util/util.h"
|
|
#include "errors.h"
|
|
#include "kprintf.h"
|
|
|
|
// REF: https://github.com/ultraembedded/fat_io_lib/blob/master/examples/sd_card_generic/sd.c
|
|
|
|
int32_t fatfs_cleanup(struct VfsMountPoint *vmp) {
|
|
fl_shutdown(&vmp->fs.fatfs.instance);
|
|
return E_OK;
|
|
}
|
|
|
|
void fatfs_vobj_cleanup(struct VfsObj *vobj) {
|
|
if (vobj->extra != NULL) {
|
|
fl_fclose(&vobj->vmp->fs.fatfs.instance, vobj->extra);
|
|
}
|
|
dlfree(vobj);
|
|
}
|
|
|
|
int32_t fatfs_vobj_read(struct VfsObj *vobj, uint8_t *const buffer, size_t n, size_t off) {
|
|
if (!(vobj->flags & VFS_FLAG_READ)) {
|
|
return E_INVALIDOPER;
|
|
}
|
|
|
|
spinlock_acquire(&vobj->spinlock);
|
|
|
|
int ok = fl_fseek(&vobj->vmp->fs.fatfs.instance, vobj->extra, off, SEEK_SET);
|
|
if (ok < 0) {
|
|
spinlock_release(&vobj->spinlock);
|
|
return E_BADIO;
|
|
}
|
|
|
|
ok = fl_fread(&vobj->vmp->fs.fatfs.instance, buffer, 1, n, vobj->extra);
|
|
if (ok < 0) {
|
|
spinlock_release(&vobj->spinlock);
|
|
return E_BADIO;
|
|
}
|
|
|
|
spinlock_release(&vobj->spinlock);
|
|
return E_OK;
|
|
}
|
|
|
|
int32_t fatfs_vobj_write(struct VfsObj *vobj, const uint8_t *const buffer, size_t n, size_t off) {
|
|
if (!(vobj->flags & VFS_FLAG_WRITE)) {
|
|
return E_INVALIDOPER;
|
|
}
|
|
|
|
spinlock_acquire(&vobj->spinlock);
|
|
|
|
int ok = fl_fseek(&vobj->vmp->fs.fatfs.instance, vobj->extra, off, SEEK_SET);
|
|
if (ok < 0) {
|
|
spinlock_release(&vobj->spinlock);
|
|
return E_BADIO;
|
|
}
|
|
|
|
ok = fl_fwrite(&vobj->vmp->fs.fatfs.instance, buffer, 1, n, vobj->extra);
|
|
if (ok < 0) {
|
|
spinlock_release(&vobj->spinlock);
|
|
return E_BADIO;
|
|
}
|
|
|
|
spinlock_release(&vobj->spinlock);
|
|
return E_OK;
|
|
}
|
|
|
|
struct VfsObj *fatfs_open(struct VfsMountPoint *vmp, const char *path, uint32_t flags) {
|
|
VfsObj *vobj = dlmalloc(sizeof(*vobj));
|
|
if (vobj == NULL) {
|
|
return NULL;
|
|
}
|
|
memset(vobj, 0, sizeof(*vobj));
|
|
spinlock_init(&vobj->spinlock);
|
|
|
|
char *mods = dlmalloc(4);
|
|
memset(mods, 0, 4);
|
|
|
|
spinlock_acquire(&vmp->spinlock);
|
|
|
|
FatFs *fs = &vmp->fs.fatfs;
|
|
|
|
if ((flags & VFS_FLAG_READ) && !(flags & VFS_FLAG_WRITE) && !(flags & VFS_FLAG_MAKE)) {
|
|
strcpy(mods, "rb");
|
|
} else if (!(flags & VFS_FLAG_READ) && (flags & VFS_FLAG_WRITE) && !(flags & VFS_FLAG_MAKE)) {
|
|
strcpy(mods, "wb");
|
|
} else if ((flags & VFS_FLAG_READ) && (flags & VFS_FLAG_WRITE) && !(flags & VFS_FLAG_MAKE)) {
|
|
strcpy(mods, "rb+");
|
|
} else if (!(flags & VFS_FLAG_READ) && (flags & VFS_FLAG_WRITE) && (flags & VFS_FLAG_MAKE)) {
|
|
strcpy(mods, "wb");
|
|
} else if ((flags & VFS_FLAG_READ) && (flags & VFS_FLAG_WRITE) && (flags & VFS_FLAG_MAKE)) {
|
|
strcpy(mods, "wb+");
|
|
}
|
|
|
|
void *f = fl_fopen(&fs->instance, path, mods);
|
|
|
|
dlfree(mods);
|
|
|
|
if (f == NULL) {
|
|
dlfree(vobj);
|
|
spinlock_release(&vmp->spinlock);
|
|
return NULL;
|
|
}
|
|
|
|
vobj->flags = flags;
|
|
vobj->extra = f;
|
|
vobj->vmp = vmp;
|
|
vobj->cleanup = &fatfs_vobj_cleanup;
|
|
vobj->read = &fatfs_vobj_read;
|
|
vobj->write = &fatfs_vobj_write;
|
|
|
|
spinlock_release(&vmp->spinlock);
|
|
return vobj;
|
|
}
|
|
|
|
int32_t fatfs_stat(struct VfsMountPoint *vmp, const char *path, FsStat *statbuf) {
|
|
spinlock_acquire(&vmp->spinlock);
|
|
|
|
if (fl_is_dir(&vmp->fs.fatfs.instance, path)) {
|
|
FL_DIR dirstat;
|
|
if (fl_opendir(&vmp->fs.fatfs.instance, path, &dirstat) == NULL) {
|
|
spinlock_release(&vmp->spinlock);
|
|
return E_NOENTRY;
|
|
}
|
|
|
|
statbuf->type = FSSTAT_DIR;
|
|
statbuf->size = 0;
|
|
fl_dirent dirent;
|
|
while (fl_readdir(&vmp->fs.fatfs.instance, &dirstat, &dirent) == 0) {
|
|
statbuf->size++;
|
|
}
|
|
fl_closedir(&vmp->fs.fatfs.instance, &dirstat);
|
|
} else {
|
|
FL_FILE *f = fl_fopen(&vmp->fs.fatfs.instance, path, "r");
|
|
statbuf->type = FSSTAT_FILE;
|
|
statbuf->size = f->filelength;
|
|
fl_fclose(&vmp->fs.fatfs.instance, f);
|
|
}
|
|
|
|
spinlock_release(&vmp->spinlock);
|
|
return E_OK;
|
|
}
|
|
|
|
int32_t fatfs_fetchdirent(struct VfsMountPoint *vmp, const char *path, FsDirent *direntbuf, size_t idx) {
|
|
spinlock_acquire(&vmp->spinlock);
|
|
|
|
FL_DIR dirstat;
|
|
FL_DIR *d = fl_opendir(&vmp->fs.fatfs.instance, path, &dirstat);
|
|
if (d == NULL) {
|
|
spinlock_release(&vmp->spinlock);
|
|
return E_BADIO;
|
|
}
|
|
|
|
fl_dirent dirent;
|
|
size_t i = 0;
|
|
while (fl_readdir(&vmp->fs.fatfs.instance, d, &dirent) == 0) {
|
|
if (i == idx) {
|
|
direntbuf->stat.type = dirent.is_dir ? FSSTAT_DIR : FSSTAT_FILE;
|
|
direntbuf->stat.size = dirent.is_dir ? 0 : dirent.size;
|
|
strncpy(direntbuf->name, dirent.filename, sizeof(direntbuf->name));
|
|
break;
|
|
}
|
|
i++;
|
|
}
|
|
fl_closedir(&vmp->fs.fatfs.instance, d);
|
|
|
|
spinlock_release(&vmp->spinlock);
|
|
return E_OK;
|
|
}
|
|
|
|
int32_t fatfs_mkdir(struct VfsMountPoint *vmp, const char *path) {
|
|
spinlock_acquire(&vmp->spinlock);
|
|
int err = fl_createdirectory(&vmp->fs.fatfs.instance, path);
|
|
spinlock_release(&vmp->spinlock);
|
|
return err == 0 ? E_OK : E_BADIO;
|
|
}
|
|
|
|
int32_t fatfs_delete(struct VfsMountPoint *vmp, const char *path) {
|
|
spinlock_acquire(&vmp->spinlock);
|
|
int err = fl_remove(&vmp->fs.fatfs.instance, path);
|
|
spinlock_release(&vmp->spinlock);
|
|
return err == 0 ? E_OK : E_BADIO;
|
|
}
|
|
|
|
int portfatfs_diskio_read(struct fat_ctx *ctx, uint32_t sector, uint8_t *buffer, uint32_t sector_count) {
|
|
VfsMountPoint *vmp = ctx->extra;
|
|
|
|
if (sector_count == 0) {
|
|
return 0;
|
|
}
|
|
|
|
uint64_t byteaddr = (uint64_t)sector * FAT_SECTOR_SIZE;
|
|
ptrdiff_t sector1 = byteaddr / vmp->backingsd->sectorsize;
|
|
ptrdiff_t sector_off = byteaddr % vmp->backingsd->sectorsize;
|
|
|
|
int32_t ret = vmp->backingsd->read(vmp->backingsd, (uint8_t *const)buffer, sector1, sector_off, sector_count * FAT_SECTOR_SIZE);
|
|
if (ret != E_OK) {
|
|
return 0;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
int portfatfs_diskio_write(struct fat_ctx *ctx, uint32_t sector, uint8_t *buffer, uint32_t sector_count) {
|
|
VfsMountPoint *vmp = ctx->extra;
|
|
|
|
if (sector_count == 0) {
|
|
return 0;
|
|
}
|
|
|
|
uint64_t byteaddr = (uint64_t)sector * FAT_SECTOR_SIZE;
|
|
ptrdiff_t sector1 = byteaddr / vmp->backingsd->sectorsize;
|
|
ptrdiff_t sector_off = byteaddr % vmp->backingsd->sectorsize;
|
|
|
|
int32_t ret = vmp->backingsd->write(vmp->backingsd, (const uint8_t *const)buffer, sector1, sector_off, sector_count * FAT_SECTOR_SIZE);
|
|
if (ret != E_OK) {
|
|
return 0;
|
|
}
|
|
return 1;
|
|
}
|