1
0
mirror of https://github.com/RIOT-OS/RIOT.git synced 2025-12-20 03:53:49 +01:00
RIOT/pkg/xipfs/fs/xipfs_fs.c
2025-07-23 12:44:38 +02:00

791 lines
18 KiB
C

/*
* Copyright (C) 2024 Université de Lille
*
* 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_xipfs
* @{
*
* @file
* @brief xipfs integration with vfs
*
* @author Damien Amara <damien.amara@univ-lille.fr>
* @author Gregory Guche <gregory.guche@univ-lille.fr>
*
* @}
*/
/*
* The following define is required in order to use strnlen(3)
* since glibc 2.10. Refer to the SYNOPSIS section of the
* strnlen(3) manual and the feature_test_macros(7) manual for
* more information
*/
#define _POSIX_C_SOURCE 200809L
/*
* libc includes
*/
#include <assert.h>
#include <errno.h>
#include <fcntl.h>
#include <mutex.h>
#include <stddef.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <ctype.h>
/*
* RIOT includes
*/
#define ENABLE_DEBUG 0
#include "debug.h"
#include "fs/xipfs_fs.h"
#include "periph/flashpage.h"
#include "saul_reg.h"
/*
* xipfs includes
*/
#include "include/buffer.h"
#include "include/errno.h"
#include "include/file.h"
#include "include/flash.h"
#include "include/fs.h"
#include "include/path.h"
#ifndef RIOT_OS
#include "include/xipfs.h"
#endif
/*
* The eXecute In Place File System only makes sense if the
* non-volatile memory of the target MCU is addressable
*/
#ifndef MODULE_PERIPH_FLASHPAGE_IN_ADDRESS_SPACE
#error "sys/fs/xipfs: the target MCU has no addressable NVM"
#endif /* !MODULE_PERIPH_FLASHPAGE_IN_ADDRESS_SPACE */
/*
* Helper functions
*/
/**
* @internal
*
* @pre dir must be a pointer that references an accessible
* memory region
*
* @pre path must be a pointer that references a path which is
* accessible, null-terminated, starts with a slash, normalized,
* and shorter than XIPFS_PATH_MAX
*
* @brief Copy the directory name component of path, including
* the final slash, into the memory region pointed to by dir
*
* @param dir A pointer to a memory region that respects the
* preconditions for storing the directory name component
*
* @param path A pointer to a path that respects the
* preconditions
*/
static void dirname(char *dir, const char *path)
{
const char *end;
size_t len, i;
assert(dir != NULL);
assert(path != NULL);
assert(path[0] == '/');
if (path[1] == '\0') {
dir[0] = '/';
dir[1] = '\0';
return;
}
len = strnlen(path, XIPFS_PATH_MAX);
assert(len < XIPFS_PATH_MAX);
end = path + len - 1;
if (*end == '/') {
/* skip the trailing slash if the
* path ends with one */
end--;
}
while (end > path && *end != '/') {
/* skip all characters that are not
* slashes */
end--;
}
if (end != path) {
for (i = 0; path + i <= end; i++) {
/* copy the characters of the directory
* name component until end */
dir[i] = path[i];
}
dir[i] = '\0';
} else {
/* no slashes found, except for the root */
dir[0] = '/';
dir[1] = '\0';
}
}
/**
* @internal
*
* @warning This function provides a workaround for xipfs-
* specific functions that need to retrieve the xipfs mount
* point structure directly, bypassing the VFS layer, as these
* functions are not available in the VFS abstraction
*
* @brief Retrieves the xipfs mount point structure from a
* specified path within the mount point
*
* @param path A path within the mount point for retrieving the
* corresponding xipfs mount point structure
*
* @param mp A pointer to an accessible memory region for
* storing the xipfs mount point structure
*
* @return Returns zero if the function succeeds or a negative
* value otherwise
*/
static int get_xipfs_mp(const char *path, xipfs_mount_t *xipfs_mp)
{
char dir[XIPFS_PATH_MAX];
size_t count, len;
int fd, ret;
if (path[0] != '/') {
return -EINVAL;
}
len = strnlen(path, XIPFS_PATH_MAX);
if (len == XIPFS_PATH_MAX) {
return -ENAMETOOLONG;
}
dirname(dir, path);
if (len + strlen(".xipfs_infos") + 1 > XIPFS_PATH_MAX) {
return -ENAMETOOLONG;
}
(void)strcat(dir, ".xipfs_infos");
if ((ret = vfs_open(dir, O_RDONLY, 0)) < 0) {
/* not a xipfs mount point */
return ret;
}
fd = ret;
count = sizeof(*xipfs_mp);
while (count > 0) {
ret = vfs_read(fd, xipfs_mp, count);
if (ret < 0) {
/* error */
return ret;
}
count -= ret;
xipfs_mp += ret;
if (ret == 0) {
/* EOF */
break;
}
}
assert(count == 0);
(void)vfs_close(fd);
return 0;
}
/**
* @internal
*
* @pre full_path must be a pointer that references a path which
* is accessible, null-terminated, starts with a slash,
* normalized, and shorter than XIPFS_PATH_MAX
*
* @brief Returns a pointer to the first character of the
* relative path derived from the absolute path retrieved from
* the vfs_mp mount point structure
*
* @param vfs_mp A pointer to a memory region containing an
* accessible VFS mount point structure
*
* @param full_path A pointer to a path that respects the
* preconditions
*/
static const char *get_rel_path(xipfs_mount_t *mp,
const char *full_path)
{
const char *p1, *p2;
assert(mp != NULL);
assert(full_path != NULL);
p1 = mp->mount_path;
p2 = full_path;
while (*p1 != '\0') {
if (*p1++ != *p2++) {
return NULL;
}
}
return p2;
}
static inline xipfs_mount_t *_get_xipfs_mount_t(vfs_mount_t *vfs_mp)
{
vfs_xipfs_mount_t *vfs_xipfs_mp;
xipfs_mount_t *xipfs_mp;
vfs_xipfs_mp = (vfs_xipfs_mount_t *)(uintptr_t)vfs_mp;
xipfs_mp = (xipfs_mount_t *)(uintptr_t)&vfs_xipfs_mp->magic;
return xipfs_mp;
}
/*
* Operations on open files
*/
static int _close(vfs_file_t *filp)
{
xipfs_file_desc_t *descp;
xipfs_mount_t *mp;
int ret;
mp = _get_xipfs_mount_t(filp->mp);
descp = (xipfs_file_desc_t *)(uintptr_t)&filp->private_data.ptr;
mutex_lock(mp->mutex);
ret = xipfs_close(mp, descp);
mutex_unlock(mp->mutex);
return ret;
}
static int _fstat(vfs_file_t *filp, struct stat *buf)
{
xipfs_file_desc_t *descp;
xipfs_mount_t *mp;
int ret;
mp = _get_xipfs_mount_t(filp->mp);
descp = (xipfs_file_desc_t *)(uintptr_t)&filp->private_data.ptr;
mutex_lock(mp->mutex);
ret = xipfs_fstat(mp, descp, buf);
mutex_unlock(mp->mutex);
return ret;
}
static off_t _lseek(vfs_file_t *filp, off_t off, int whence)
{
xipfs_file_desc_t *descp;
xipfs_mount_t *mp;
int ret;
mp = _get_xipfs_mount_t(filp->mp);
descp = (xipfs_file_desc_t *)(uintptr_t)&filp->private_data.ptr;
mutex_lock(mp->mutex);
ret = xipfs_lseek(mp, descp, off, whence);
mutex_unlock(mp->mutex);
return ret;
}
static int _open(vfs_file_t *filp, const char *name, int flags,
mode_t mode)
{
xipfs_file_desc_t *descp;
xipfs_mount_t *mp;
int ret;
mp = _get_xipfs_mount_t(filp->mp);
descp = (xipfs_file_desc_t *)(uintptr_t)&filp->private_data.ptr;
mutex_lock(mp->mutex);
ret = xipfs_open(mp, descp, name, flags, mode);
mutex_unlock(mp->mutex);
return ret;
}
static ssize_t _read(vfs_file_t *filp, void *dest, size_t nbytes)
{
xipfs_file_desc_t *descp;
xipfs_mount_t *mp;
int ret;
mp = _get_xipfs_mount_t(filp->mp);
descp = (xipfs_file_desc_t *)(uintptr_t)&filp->private_data.ptr;
mutex_lock(mp->mutex);
ret = xipfs_read(mp, descp, dest, nbytes);
mutex_unlock(mp->mutex);
return ret;
}
static ssize_t _write(vfs_file_t *filp, const void *src, size_t nbytes)
{
xipfs_file_desc_t *descp;
xipfs_mount_t *mp;
int ret;
mp = _get_xipfs_mount_t(filp->mp);
descp = (xipfs_file_desc_t *)(uintptr_t)&filp->private_data.ptr;
mutex_lock(mp->mutex);
ret = xipfs_write(mp, descp, src, nbytes);
mutex_unlock(mp->mutex);
return ret;
}
static int _fsync(vfs_file_t *filp)
{
xipfs_file_desc_t *descp;
xipfs_mount_t *mp;
int ret;
mp = _get_xipfs_mount_t(filp->mp);
descp = (xipfs_file_desc_t *)(uintptr_t)&filp->private_data.ptr;
mutex_lock(mp->mutex);
ret = xipfs_fsync(mp, descp, filp->pos);
mutex_unlock(mp->mutex);
return ret;
}
/*
* Operations on open directories
*/
static int _opendir(vfs_DIR *dirp, const char *dirname)
{
xipfs_dir_desc_t *descp;
xipfs_mount_t *mp;
int ret;
mp = _get_xipfs_mount_t(dirp->mp);
descp = (xipfs_dir_desc_t *)(uintptr_t)&dirp->private_data.ptr;
mutex_lock(mp->mutex);
ret = xipfs_opendir(mp, descp, dirname);
mutex_unlock(mp->mutex);
return ret;
}
static int _readdir(vfs_DIR *dirp, vfs_dirent_t *entry)
{
xipfs_dir_desc_t *descp;
xipfs_dirent_t *direntp;
xipfs_mount_t *mp;
int ret;
mp = _get_xipfs_mount_t(dirp->mp);
descp = (xipfs_dir_desc_t *)(uintptr_t)&dirp->private_data.ptr;
direntp = (xipfs_dirent_t *)(uintptr_t)entry->d_name;
mutex_lock(mp->mutex);
ret = xipfs_readdir(mp, descp, direntp);
mutex_unlock(mp->mutex);
return ret;
}
static int _closedir(vfs_DIR *dirp)
{
xipfs_dir_desc_t *descp;
xipfs_mount_t *mp;
int ret;
mp = _get_xipfs_mount_t(dirp->mp);
descp = (xipfs_dir_desc_t *)(uintptr_t)&dirp->private_data.ptr;
mutex_lock(mp->mutex);
ret = xipfs_closedir(mp, descp);
mutex_unlock(mp->mutex);
return ret;
}
/*
* Operations on mounted file systems
*/
static int _format(vfs_mount_t *vfs_mp)
{
xipfs_mount_t *mp;
int ret;
mp = _get_xipfs_mount_t(vfs_mp);
mutex_lock(mp->execution_mutex);
mutex_lock(mp->mutex);
ret = xipfs_format(mp);
mutex_lock(mp->mutex);
mutex_lock(mp->execution_mutex);
return ret;
}
static int _mount(vfs_mount_t *vfs_mp)
{
xipfs_mount_t *mp;
int ret;
mp = _get_xipfs_mount_t(vfs_mp);
mutex_lock(mp->mutex);
ret = xipfs_mount(mp);
mutex_unlock(mp->mutex);
return ret;
}
static int _umount(vfs_mount_t *vfs_mp)
{
(void)vfs_mp;
/* nothing to do */
return 0;
}
static int _unlink(vfs_mount_t *vfs_mp, const char *name)
{
xipfs_mount_t *mp;
int ret;
mp = _get_xipfs_mount_t(vfs_mp);
mutex_lock(mp->execution_mutex);
mutex_lock(mp->mutex);
ret = xipfs_unlink(mp, name);
mutex_unlock(mp->mutex);
mutex_lock(mp->execution_mutex);
return ret;
}
static int _mkdir(vfs_mount_t *vfs_mp, const char *name, mode_t mode)
{
xipfs_mount_t *mp;
int ret;
mp = _get_xipfs_mount_t(vfs_mp);
mutex_lock(mp->mutex);
ret = xipfs_mkdir(mp, name, mode);
mutex_unlock(mp->mutex);
return ret;
}
static int _rmdir(vfs_mount_t *vfs_mp, const char *name)
{
xipfs_mount_t *mp;
int ret;
mp = _get_xipfs_mount_t(vfs_mp);
mutex_lock(mp->execution_mutex);
mutex_lock(mp->mutex);
ret = xipfs_rmdir(mp, name);
mutex_unlock(mp->mutex);
mutex_unlock(mp->execution_mutex);
return ret;
}
static int _rename(vfs_mount_t *vfs_mp, const char *from_path,
const char *to_path)
{
xipfs_mount_t *mp;
int ret;
mp = _get_xipfs_mount_t(vfs_mp);
mutex_lock(mp->mutex);
ret = xipfs_rename(mp, from_path, to_path);
mutex_unlock(mp->mutex);
return ret;
}
static int _stat(vfs_mount_t *vfs_mp, const char *restrict path,
struct stat *restrict buf)
{
xipfs_mount_t *mp;
int ret;
mp = _get_xipfs_mount_t(vfs_mp);
mutex_lock(mp->mutex);
ret = xipfs_stat(mp, path, buf);
mutex_unlock(mp->mutex);
return ret;
}
static int _statvfs(vfs_mount_t *vfs_mp, const char *restrict path,
struct statvfs *restrict buf)
{
struct xipfs_statvfs xipfs_buf;
xipfs_mount_t *mp;
int ret;
mp = _get_xipfs_mount_t(vfs_mp);
mutex_lock(mp->mutex);
ret = xipfs_statvfs(mp, path, &xipfs_buf);
mutex_unlock(mp->mutex);
(void)memset(buf, 0, sizeof(*buf));
buf->f_bsize = xipfs_buf.f_bsize;
buf->f_frsize = xipfs_buf.f_frsize;
buf->f_blocks = xipfs_buf.f_blocks;
buf->f_bfree = xipfs_buf.f_bfree;
buf->f_bavail = xipfs_buf.f_bavail;
buf->f_flag = xipfs_buf.f_flag;
buf->f_namemax = xipfs_buf.f_namemax;
return ret;
}
/*
* xipfs-specific functions
*/
int xipfs_extended_driver_new_file(const char *full_path, uint32_t size, uint32_t exec)
{
xipfs_mount_t mp;
const char *path;
int ret;
if (full_path == NULL) {
return -EFAULT;
}
if ((ret = get_xipfs_mp(full_path, &mp)) < 0) {
return ret;
}
if ((path = get_rel_path(&mp, full_path)) == NULL) {
return -EIO;
}
mutex_lock(mp.mutex);
ret = xipfs_new_file(&mp, path, size, exec);
mutex_unlock(mp.mutex);
return ret;
}
static int get_temperature(void) {
phydat_t physical_data;
saul_reg_t *device = saul_reg_find_type(SAUL_SENSE_TEMP);
int res = saul_reg_read(device, &physical_data);
if (res < 0) {
return res;
}
return physical_data.val[0];
}
static int get_led(int pos) {
phydat_t physical_data;
saul_reg_t *device = saul_reg_find_nth(pos);
int res = saul_reg_read(device, &physical_data);
if (res < 0) {
return res;
}
return physical_data.val[0];
}
static int set_led(int pos, int val) {
phydat_t physical_data;
saul_reg_t *device = saul_reg_find_nth(pos);
physical_data.val[0] = val;
int res = saul_reg_write(device, &physical_data);
return res;
}
static int get_file_size(const char *full_path, size_t *size) {
xipfs_mount_t mp;
const char *path;
int ret;
struct stat buf;
if ((full_path == NULL) || (size == NULL)) {
return -EFAULT;
}
if ((ret = get_xipfs_mp(full_path, &mp)) < 0) {
return ret;
}
if ((path = get_rel_path(&mp, full_path)) == NULL) {
return -EIO;
}
*size = 0;
ret = xipfs_stat(&mp, path, &buf);
if (ret < 0) {
return ret;
}
*size = buf.st_size;
return 0;
}
static ssize_t copy_file(const char *full_path, void *buf, size_t nbyte) {
xipfs_mount_t mp;
const char *path;
xipfs_file_desc_t desc;
int ret;
size_t file_size;
ret = get_file_size(full_path, &file_size);
if (ret < 0) {
return ret;
}
if (nbyte > file_size) {
nbyte = file_size;
}
if (full_path == NULL) {
return -EFAULT;
}
if ((ret = get_xipfs_mp(full_path, &mp)) < 0) {
return ret;
}
if ((path = get_rel_path(&mp, full_path)) == NULL) {
return -EIO;
}
ret = xipfs_open(&mp, &desc, path, O_RDONLY, 0);
if (ret < 0) {
return ret;
}
ret = xipfs_read(&mp, &desc, buf, nbyte);
if (ret < 0) {
return ret;
}
ret = xipfs_close(&mp, &desc);
return nbyte;
}
static const void *xipfs_user_syscalls_table[XIPFS_USER_SYSCALL_MAX] = {
[ XIPFS_USER_SYSCALL_PRINTF] = vprintf,
[ XIPFS_USER_SYSCALL_GET_TEMP] = get_temperature,
[ XIPFS_USER_SYSCALL_ISPRINT] = isprint,
[ XIPFS_USER_SYSCALL_STRTOL] = strtol,
[ XIPFS_USER_SYSCALL_GET_LED] = get_led,
[ XIPFS_USER_SYSCALL_SET_LED] = set_led,
[ XIPFS_USER_SYSCALL_COPY_FILE] = copy_file,
[XIPFS_USER_SYSCALL_GET_FILE_SIZE] = get_file_size,
[ XIPFS_USER_SYSCALL_MEMSET] = memset
};
int xipfs_extended_driver_execv(const char *full_path, char *const argv[])
{
xipfs_mount_t mp;
const char *path;
int ret;
if (full_path == NULL) {
return -EFAULT;
}
if ((ret = get_xipfs_mp(full_path, &mp)) < 0) {
return ret;
}
if ((path = get_rel_path(&mp, full_path)) == NULL) {
return -EIO;
}
mutex_lock(mp.execution_mutex);
ret = xipfs_execv(&mp, path, argv, xipfs_user_syscalls_table);
mutex_unlock(mp.execution_mutex);
return ret;
}
/*
* File system driver structures
*/
static const vfs_file_ops_t xipfs_file_ops = {
.close = _close,
.fstat = _fstat,
.lseek = _lseek,
.open = _open,
.read = _read,
.write = _write,
.fsync = _fsync,
};
static const vfs_dir_ops_t xipfs_dir_ops = {
.opendir = _opendir,
.readdir = _readdir,
.closedir = _closedir,
};
static const vfs_file_system_ops_t xipfs_fs_ops = {
.format = _format,
.mount = _mount,
.umount = _umount,
.unlink = _unlink,
.mkdir = _mkdir,
.rmdir = _rmdir,
.rename = _rename,
.stat = _stat,
.statvfs = _statvfs,
};
const vfs_file_system_t xipfs_file_system = {
.fs_op = &xipfs_fs_ops,
.f_op = &xipfs_file_ops,
.d_op = &xipfs_dir_ops,
};
int xipfs_construct_from_flashpage(mtd_flashpage_t *flashpage, const char *path,
mutex_t *execution_mutex, mutex_t *mutex,
vfs_xipfs_mount_t *vfs_xipfs_mount) {
if ( (flashpage == NULL) || (path == NULL) || (path[0] == '\0')
|| (execution_mutex == NULL) || (mutex == NULL) ) {
return -EINVAL;
}
vfs_xipfs_mount->vfs_mp.fs = &xipfs_file_system;
vfs_xipfs_mount->vfs_mp.mount_point = path;
vfs_xipfs_mount->magic = XIPFS_MAGIC;
vfs_xipfs_mount->mount_path = path;
vfs_xipfs_mount->page_num = flashpage->base.sector_count;
vfs_xipfs_mount->page_addr = (void *)(
(unsigned char *)XIPFS_NVM_BASE
+ (flashpage->offset * XIPFS_NVM_PAGE_SIZE)
);
vfs_xipfs_mount->execution_mutex = execution_mutex;
mutex_init(vfs_xipfs_mount->execution_mutex);
vfs_xipfs_mount->mutex = mutex;
mutex_init(vfs_xipfs_mount->mutex);
return 0;
}