mirror of
https://github.com/RIOT-OS/RIOT.git
synced 2024-12-29 04:50:03 +01:00
64ba553d1f
Co-authored-by: Marian Buschsieweke <marian.buschsieweke@posteo.net>
256 lines
7.7 KiB
C
256 lines
7.7 KiB
C
/*
|
|
* Copyright (C) 2016 Eistec AB
|
|
*
|
|
* This file is subject to the terms and conditions of the GNU Lesser
|
|
* General Public License v2.1. See the file LICENSE in the top level
|
|
* directory for more details.
|
|
*/
|
|
|
|
/**
|
|
* @ingroup sys_fs_constfs
|
|
* @{
|
|
*
|
|
* @file
|
|
* @brief ConstFS implementation
|
|
*
|
|
* @author Joakim Nohlgård <joakim.nohlgard@eistec.se>
|
|
*
|
|
* @}
|
|
*/
|
|
|
|
/* Required for strnlen in string.h, when building with -std=c99 */
|
|
#define _DEFAULT_SOURCE 1
|
|
#include <stddef.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
#include <fcntl.h>
|
|
#include <errno.h>
|
|
|
|
#include "architecture.h"
|
|
#include "fs/constfs.h"
|
|
#include "vfs.h"
|
|
|
|
#define ENABLE_DEBUG 0
|
|
#include "debug.h"
|
|
|
|
/* File system operations */
|
|
static int constfs_stat(vfs_mount_t *mountp, const char *restrict name, struct stat *restrict buf);
|
|
static int constfs_statvfs(vfs_mount_t *mountp, const char *restrict path, struct statvfs *restrict buf);
|
|
|
|
/* File operations */
|
|
static int constfs_fstat(vfs_file_t *filp, struct stat *buf);
|
|
static off_t constfs_lseek(vfs_file_t *filp, off_t off, int whence);
|
|
static int constfs_open(vfs_file_t *filp, const char *name, int flags, mode_t mode);
|
|
static ssize_t constfs_read(vfs_file_t *filp, void *dest, size_t nbytes);
|
|
|
|
/* Directory operations */
|
|
static int constfs_opendir(vfs_DIR *dirp, const char *dirname);
|
|
static int constfs_readdir(vfs_DIR *dirp, vfs_dirent_t *entry);
|
|
|
|
static const vfs_file_system_ops_t constfs_fs_ops = {
|
|
.statvfs = constfs_statvfs,
|
|
.stat = constfs_stat,
|
|
};
|
|
|
|
static const vfs_file_ops_t constfs_file_ops = {
|
|
.fstat = constfs_fstat,
|
|
.lseek = constfs_lseek,
|
|
.open = constfs_open,
|
|
.read = constfs_read,
|
|
};
|
|
|
|
static const vfs_dir_ops_t constfs_dir_ops = {
|
|
.opendir = constfs_opendir,
|
|
.readdir = constfs_readdir,
|
|
};
|
|
|
|
const vfs_file_system_t constfs_file_system = {
|
|
.f_op = &constfs_file_ops,
|
|
.fs_op = &constfs_fs_ops,
|
|
.d_op = &constfs_dir_ops,
|
|
};
|
|
|
|
/**
|
|
* @internal
|
|
* @brief Fill a file information struct with information about the file
|
|
* pointed to by @p fp
|
|
*
|
|
* @param[in] fp file to query
|
|
* @param[out] buf output buffer
|
|
*/
|
|
static void _constfs_write_stat(const constfs_file_t *fp, struct stat *restrict buf);
|
|
|
|
static int constfs_stat(vfs_mount_t *mountp, const char *restrict name, struct stat *restrict buf)
|
|
{
|
|
(void) name;
|
|
/* Fill out some information about this file */
|
|
if (buf == NULL) {
|
|
return -EFAULT;
|
|
}
|
|
constfs_t *fs = mountp->private_data;
|
|
/* linear search through the files array */
|
|
for (size_t i = 0; i < fs->nfiles; ++i) {
|
|
DEBUG("constfs_stat ? \"%s\"\n", fs->files[i].path);
|
|
if (strcmp(fs->files[i].path, name) == 0) {
|
|
DEBUG("constfs_stat: Found :)\n");
|
|
_constfs_write_stat(&fs->files[i], buf);
|
|
buf->st_ino = i;
|
|
return 0;
|
|
}
|
|
}
|
|
DEBUG("constfs_stat: Not found :(\n");
|
|
return -ENOENT;
|
|
}
|
|
|
|
static int constfs_statvfs(vfs_mount_t *mountp, const char *restrict path, struct statvfs *restrict buf)
|
|
{
|
|
(void) path;
|
|
/* Fill out some information about this file system */
|
|
if (buf == NULL) {
|
|
return -EFAULT;
|
|
}
|
|
constfs_t *fs = mountp->private_data;
|
|
/* clear out the stat buffer first */
|
|
buf->f_bsize = sizeof(uint8_t); /* block size */
|
|
buf->f_frsize = sizeof(uint8_t); /* fundamental block size */
|
|
fsblkcnt_t f_blocks = 0;
|
|
for (size_t i = 0; i < fs->nfiles; ++i) {
|
|
f_blocks += fs->files[i].size;
|
|
}
|
|
buf->f_blocks = f_blocks; /* Blocks total */
|
|
buf->f_bfree = 0; /* Blocks free */
|
|
buf->f_bavail = 0; /* Blocks available to non-privileged processes */
|
|
buf->f_files = fs->nfiles; /* Total number of file serial numbers */
|
|
buf->f_ffree = 0; /* Total number of free file serial numbers */
|
|
buf->f_favail = 0; /* Number of file serial numbers available to non-privileged process */
|
|
buf->f_fsid = 0; /* File system id */
|
|
buf->f_flag = (ST_RDONLY | ST_NOSUID); /* File system flags */
|
|
buf->f_namemax = UINT8_MAX; /* Maximum file name length */
|
|
return 0;
|
|
}
|
|
|
|
static int constfs_fstat(vfs_file_t *filp, struct stat *buf)
|
|
{
|
|
constfs_file_t *fp = filp->private_data.ptr;
|
|
if (buf == NULL) {
|
|
return -EFAULT;
|
|
}
|
|
_constfs_write_stat(fp, buf);
|
|
return 0;
|
|
}
|
|
|
|
static off_t constfs_lseek(vfs_file_t *filp, off_t off, int whence)
|
|
{
|
|
constfs_file_t *fp = filp->private_data.ptr;
|
|
switch (whence) {
|
|
case SEEK_SET:
|
|
break;
|
|
case SEEK_CUR:
|
|
off += filp->pos;
|
|
break;
|
|
case SEEK_END:
|
|
off += fp->size;
|
|
break;
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
if (off < 0) {
|
|
/* the resulting file offset would be negative */
|
|
return -EINVAL;
|
|
}
|
|
/* POSIX allows seeking past the end of the file, even with O_RDONLY */
|
|
filp->pos = off;
|
|
return off;
|
|
}
|
|
|
|
static int constfs_open(vfs_file_t *filp, const char *name, int flags, mode_t mode)
|
|
{
|
|
(void) mode;
|
|
constfs_t *fs = filp->mp->private_data;
|
|
DEBUG("constfs_open: %p, \"%s\", 0x%x, 0%03lo\"\n", (void *)filp, name, flags, (unsigned long)mode);
|
|
/* We only support read access */
|
|
if ((flags & O_ACCMODE) != O_RDONLY) {
|
|
return -EROFS;
|
|
}
|
|
/* linear search through the files array */
|
|
for (size_t i = 0; i < fs->nfiles; ++i) {
|
|
DEBUG("constfs_open ? \"%s\"\n", fs->files[i].path);
|
|
if (strcmp(fs->files[i].path, name) == 0) {
|
|
DEBUG("constfs_open: Found :)\n");
|
|
filp->private_data.ptr = (void *)&fs->files[i];
|
|
return 0;
|
|
}
|
|
}
|
|
DEBUG("constfs_open: Not found :(\n");
|
|
return -ENOENT;
|
|
}
|
|
|
|
static ssize_t constfs_read(vfs_file_t *filp, void *dest, size_t nbytes)
|
|
{
|
|
constfs_file_t *fp = filp->private_data.ptr;
|
|
DEBUG("constfs_read: %p, %p, %" PRIuSIZE "\n", (void *)filp, dest, nbytes);
|
|
if ((size_t)filp->pos >= fp->size) {
|
|
/* Current offset is at or beyond end of file */
|
|
return 0;
|
|
}
|
|
|
|
if (nbytes > (fp->size - filp->pos)) {
|
|
nbytes = fp->size - filp->pos;
|
|
}
|
|
memcpy(dest, (const uint8_t *)fp->data + filp->pos, nbytes);
|
|
DEBUG("constfs_read: read %" PRIuSIZE " bytes\n", nbytes);
|
|
filp->pos += nbytes;
|
|
return nbytes;
|
|
}
|
|
|
|
static int constfs_opendir(vfs_DIR *dirp, const char *dirname)
|
|
{
|
|
DEBUG("constfs_opendir: %p, \"%s\"\n", (void *)dirp, dirname);
|
|
if (strncmp(dirname, "/", 2) != 0) {
|
|
/* We keep it simple and only support a flat file system, only a root directory */
|
|
return -ENOENT;
|
|
}
|
|
dirp->private_data.value = 0;
|
|
return 0;
|
|
}
|
|
|
|
static int constfs_readdir(vfs_DIR *dirp, vfs_dirent_t *entry)
|
|
{
|
|
DEBUG("constfs_readdir: %p, %p\n", (void *)dirp, (void *)entry);
|
|
constfs_t *fs = dirp->mp->private_data;
|
|
int filenum = dirp->private_data.value;
|
|
if ((size_t)filenum >= fs->nfiles) {
|
|
/* End of stream */
|
|
return 0;
|
|
}
|
|
const constfs_file_t *fp = &fs->files[filenum];
|
|
if (fp->path == NULL) {
|
|
return -EIO;
|
|
}
|
|
const char *filename = fp->path[0] == '/' ? fp->path + 1 : fp->path;
|
|
size_t len = strnlen(filename, VFS_NAME_MAX + 1);
|
|
if (len > VFS_NAME_MAX) {
|
|
/* name does not fit in vfs_dirent_t buffer */
|
|
/* skipping past the broken entry */
|
|
++filenum;
|
|
dirp->private_data.value = filenum;
|
|
return -EAGAIN;
|
|
}
|
|
/* copy the string, including terminating null */
|
|
memcpy(&entry->d_name[0], filename, len + 1);
|
|
entry->d_ino = filenum;
|
|
++filenum;
|
|
dirp->private_data.value = filenum;
|
|
return 1;
|
|
}
|
|
|
|
static void _constfs_write_stat(const constfs_file_t *fp, struct stat *restrict buf)
|
|
{
|
|
/* buffer is cleared by vfs already */
|
|
buf->st_nlink = 1;
|
|
buf->st_mode = S_IFREG | S_IRUSR | S_IRGRP | S_IROTH;
|
|
buf->st_size = fp->size;
|
|
buf->st_blocks = fp->size;
|
|
buf->st_blksize = sizeof(uint8_t);
|
|
}
|