/**
* @file vfs.c
* An implementation of filesystem abstraction
*
* Copyright (C) 2018 Clyne Sullivan
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#include "vfs.h"
#include
#include
// +1 vol for stdio, +3 fd's for stdout, in, err
#define VFS_MAX_VOLS (1 + 8)
#define VFS_MAX_FILES (3 + 10)
static vfs_volume vfs_volumes[VFS_MAX_VOLS];
static vfs_file vfs_files[VFS_MAX_FILES];
#define VFS_PID_CHECK(fpid) { \
uint32_t pid = task_getpid(); \
if (pid != fpid && fpid != 0) \
return 0; }
void vfs_svc(uint32_t n, uint32_t *ret, uint32_t *args)
{
switch (n) {
case 0:
*((int *)ret) = vfs_mount((vfs_volume_funcs *)args[0],
args[1]);
break;
case 1:
*((int *)ret) = vfs_open((const char *)args[0], args[1]);
break;
case 2:
*((int *)ret) = vfs_close(args[0]);
break;
case 3:
*((int *)ret) = vfs_read(args[0], args[1], (uint8_t *)args[2]);
break;
case 4:
*((int *)ret) = vfs_write(args[0], args[1], (const uint8_t *)args[2]);
break;
default:
break;
}
}
void vfs_init(void)
{
// Mark all volumes and files as empty
for (int i = 0; i < VFS_MAX_VOLS; i++)
vfs_volumes[i].flags = 0;
for (int i = 0; i < VFS_MAX_FILES; i++)
vfs_files[i].flags = 0;
vfs_mount(&stdio_funcs, 0);
// Order of opening here may be important TODO confirm
vfs_open(" in", VFS_FILE_READ);
vfs_open(" out", VFS_FILE_WRITE);
vfs_open(" err", VFS_FILE_WRITE);
}
int vfs_mount(const vfs_volume_funcs *funcs, uint32_t flags)
{
for (int i = 0; i < VFS_MAX_VOLS; i++) {
if (!(vfs_volumes[i].flags && VFS_MOUNTED)) {
vfs_volumes[i].flags = VFS_MOUNTED | flags;
vfs_volumes[i].funcs = funcs;
return i;
}
}
return -1;
}
int vfs_get_drive(const char *path)
{
// Validate parameters
if (path[0] == '\0')
return -1;
// Default to 'A' if no drive specified (A gives stdio)
if (path[1] == '\0' || path[1] != ':' ||
path[2] == '\0' || path[2] != '/')
return 0;
// Find chosen drive
int drive = -1;
for (int i = 0; i < VFS_MAX_VOLS; i++) {
if (path[0] == ('a' + i) || path[0] == ('A' + i)) {
drive = i;
break;
}
}
if (drive == -1 || !(vfs_volumes[drive].flags && VFS_MOUNTED))
return -1;
return drive;
}
int vfs_open(const char *path, uint32_t flags)
{
int drive = vfs_get_drive(path);
if (drive == -1)
return -1;
if (vfs_volumes[drive].funcs->open == 0)
return -1;
// Find available file handle
int file = -1;
for (int i = 0; i < VFS_MAX_FILES; i++) {
if (!(vfs_files[i].flags & VFS_FILE_OPEN)) {
file = i;
break;
}
}
if (file == -1)
return -1;
void *fsinfo = vfs_volumes[drive].funcs->open(path + 3);
if (fsinfo == 0)
return -1;
vfs_files[file].flags = VFS_FILE_OPEN | flags;
vfs_files[file].vol = drive;
vfs_files[file].pid = task_getpid();
vfs_files[file].info.pos = 0;
vfs_files[file].info.fsinfo = fsinfo;
return file;
}
int vfs_close(int fd)
{
if (fd < 0 || fd > VFS_MAX_FILES)
return -1;
if (vfs_volumes[vfs_files[fd].vol].funcs->close == 0)
return -1;
VFS_PID_CHECK(vfs_files[fd].pid);
if (!(vfs_files[fd].flags & VFS_FILE_OPEN))
return 0;
// TODO care
/*int ret =*/ vfs_volumes[vfs_files[fd].vol].funcs->close(
&vfs_files[fd].info);
vfs_files[fd].flags = 0;
vfs_files[fd].pid = 0;
return 0;
}
uint32_t vfs_read(int fd, uint32_t count, uint8_t *buffer)
{
if (fd < 0 || fd > VFS_MAX_FILES || count == 0 || buffer == 0)
return 0;
if (!(vfs_files[fd].flags & VFS_FILE_OPEN))
return -1;
if (vfs_volumes[vfs_files[fd].vol].funcs->read == 0)
return -1;
VFS_PID_CHECK(vfs_files[fd].pid);
if ((!(vfs_files[fd].flags & VFS_FILE_READ)) || (vfs_files[fd].flags &
VFS_EOF))
return 0;
uint32_t ret = vfs_volumes[vfs_files[fd].vol].funcs->read(
&vfs_files[fd].info, count, buffer);
if (ret < count)
vfs_files[fd].flags |= VFS_EOF;
return ret;
}
uint32_t vfs_write(int fd, uint32_t count, const uint8_t *buffer)
{
if (fd < 0 || fd > VFS_MAX_FILES || count == 0 || buffer == 0)
return 0;
if (!(vfs_files[fd].flags & VFS_FILE_OPEN))
return -1;
if (vfs_volumes[vfs_files[fd].vol].funcs->write == 0)
return -1;
VFS_PID_CHECK(vfs_files[fd].pid);
// TODO append?
if ((!(vfs_files[fd].flags & VFS_FILE_WRITE)) || (vfs_files[fd].flags &
VFS_EOF))
return 0;
uint32_t ret = vfs_volumes[vfs_files[fd].vol].funcs->write(
&vfs_files[fd].info, count, buffer);
if (ret < count)
vfs_files[fd].flags |= VFS_EOF;
return ret;
}
int vfs_seek(int fd, int32_t offset, int whence)
{
if (fd < 0 || fd > VFS_MAX_FILES)
return -1;
if (!(vfs_files[fd].flags & VFS_FILE_OPEN))
return -1;
if (vfs_volumes[vfs_files[fd].vol].funcs->seek == 0)
return -1;
VFS_PID_CHECK(vfs_files[fd].pid);
return vfs_volumes[vfs_files[fd].vol].funcs->seek(&vfs_files[fd].info,
offset, whence);
}
int32_t vfs_tell(int fd)
{
if (fd < 0 || fd > VFS_MAX_FILES)
return -1;
if (!(vfs_files[fd].flags & VFS_FILE_OPEN))
return -1;
VFS_PID_CHECK(vfs_files[fd].pid);
return (int32_t)vfs_files[fd].info.pos;
}