sys/vfs: A virtual file system (VFS) layer for RIOT
The VFS layer provides file system abstractions to allow using a unified interface to access files from mounted file systems.
This commit is contained in:
parent
5e27bf4f31
commit
dcc37329df
|
@ -597,6 +597,10 @@ ifneq (,$(filter emcute,$(USEMODULE)))
|
|||
USEMODULE += xtimer
|
||||
endif
|
||||
|
||||
ifneq (,$(filter constfs,$(USEMODULE)))
|
||||
USEMODULE += vfs
|
||||
endif
|
||||
|
||||
# include package dependencies
|
||||
-include $(USEPKG:%=$(RIOTPKG)/%/Makefile.dep)
|
||||
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
# Introduction
|
||||
|
||||
This tool creates a .c file including all data from a local directory as data
|
||||
structures that can be mounted using constfs.
|
||||
|
||||
# Usage
|
||||
|
||||
mkconstfs.py /path/to/files /
|
||||
|
||||
#include "vfs.h"
|
||||
#include "fs/constfs.h"
|
||||
extern const vfs_mount_t _constfs;
|
||||
|
||||
[...]
|
||||
|
||||
vfs_mount((vfs_mount_t *)&_constfs);
|
|
@ -0,0 +1,91 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
import codecs
|
||||
import os
|
||||
import sys
|
||||
|
||||
FILE_TYPE = "static const uint8_t"
|
||||
|
||||
def mkconstfs(root_path, mount_point, constfs_name):
|
||||
print("/* This file was automatically generated by mkconstfs */")
|
||||
print("#include \"fs/constfs.h\"")
|
||||
print("")
|
||||
|
||||
for dirname, subdir_list, file_list in os.walk(root_path):
|
||||
target_dirname = os.path.join("/", dirname[len(root_path):])
|
||||
for fname in file_list:
|
||||
local_fname = os.path.join(dirname, fname)
|
||||
target_fname = os.path.join(target_dirname, fname)
|
||||
print_file_data(local_fname, target_fname)
|
||||
|
||||
print("\nstatic const constfs_file_t _files[] = {")
|
||||
|
||||
for mangled_name, target_name, _ in files:
|
||||
print(" {")
|
||||
print(" .path = \"%s\"," % target_name)
|
||||
print(" .data = %s," % mangled_name)
|
||||
print(" .size = sizeof(%s)" % mangled_name)
|
||||
print(" },")
|
||||
print("};")
|
||||
|
||||
print("""
|
||||
static const constfs_t _fs_data = {
|
||||
.files = _files,
|
||||
.nfiles = sizeof(_files) / sizeof(_files[0]),
|
||||
};
|
||||
|
||||
vfs_mount_t %s = {
|
||||
.fs = &constfs_file_system,
|
||||
.mount_point = \"%s\",
|
||||
.private_data = (void *)&_fs_data,
|
||||
};
|
||||
""" % (constfs_name, mount_point))
|
||||
|
||||
def mangle_name(fname):
|
||||
fname = fname.replace("/", "__")
|
||||
fname = fname.replace(".", "__")
|
||||
|
||||
return fname
|
||||
|
||||
def print_file_data(local_fname, target_fname):
|
||||
mangled_name = mangle_name(target_fname)
|
||||
print(FILE_TYPE, mangled_name, "[] = {", end="")
|
||||
|
||||
line_length = 8
|
||||
nread = 0
|
||||
with open(local_fname, 'rb') as f:
|
||||
byte = f.read(1)
|
||||
while byte:
|
||||
if nread == 0:
|
||||
print("\n ", end="")
|
||||
elif nread % line_length == 0:
|
||||
print(",\n ", end="")
|
||||
else:
|
||||
print(", ", end="")
|
||||
nread += 1
|
||||
print ("0x" + codecs.encode(byte, 'hex').decode('ascii'), end="")
|
||||
# Do stuff with byte.
|
||||
byte = f.read(1)
|
||||
|
||||
print("\n};")
|
||||
|
||||
files.append((mangled_name, target_fname, nread))
|
||||
|
||||
files = []
|
||||
|
||||
if __name__=="__main__":
|
||||
mountpoint = "/"
|
||||
constfs_name = "_constfs"
|
||||
|
||||
if len(sys.argv) < 2:
|
||||
print("usage: mkconstfs.py <path> [mountpoint] [constfs_name]")
|
||||
exit(1)
|
||||
|
||||
path = sys.argv[1]
|
||||
if len(sys.argv) > 2:
|
||||
mountpoint = sys.argv[2]
|
||||
|
||||
if len(sys.argv) > 3:
|
||||
constfs_name = sys.argv[3]
|
||||
|
||||
mkconstfs(path, mountpoint, constfs_name)
|
|
@ -112,6 +112,10 @@ ifneq (,$(filter emcute,$(USEMODULE)))
|
|||
DIRS += net/application_layer/emcute
|
||||
endif
|
||||
|
||||
ifneq (,$(filter constfs,$(USEMODULE)))
|
||||
DIRS += fs/constfs
|
||||
endif
|
||||
|
||||
DIRS += $(dir $(wildcard $(addsuffix /Makefile, ${USEMODULE})))
|
||||
|
||||
include $(RIOTBASE)/Makefile.base
|
||||
|
|
|
@ -32,6 +32,10 @@ ifneq (,$(filter oneway_malloc,$(USEMODULE)))
|
|||
USEMODULE_INCLUDES += $(RIOTBASE)/sys/oneway-malloc/include
|
||||
endif
|
||||
|
||||
ifneq (,$(filter vfs,$(USEMODULE)))
|
||||
USEMODULE_INCLUDES += $(RIOTBASE)/sys/posix/include
|
||||
endif
|
||||
|
||||
ifneq (,$(filter cpp11-compat,$(USEMODULE)))
|
||||
USEMODULE_INCLUDES += $(RIOTBASE)/sys/cpp11-compat/include
|
||||
# make sure cppsupport.o is linked explicitly because __dso_handle is not
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
MODULE=constfs
|
||||
include $(RIOTBASE)/Makefile.base
|
|
@ -0,0 +1,319 @@
|
|||
/*
|
||||
* 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 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 "fs/constfs.h"
|
||||
#include "vfs.h"
|
||||
|
||||
#define ENABLE_DEBUG (0)
|
||||
#include "debug.h"
|
||||
|
||||
/* File system operations */
|
||||
static int constfs_mount(vfs_mount_t *mountp);
|
||||
static int constfs_umount(vfs_mount_t *mountp);
|
||||
static int constfs_unlink(vfs_mount_t *mountp, const char *name);
|
||||
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_close(vfs_file_t *filp);
|
||||
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, const char *abs_path);
|
||||
static ssize_t constfs_read(vfs_file_t *filp, void *dest, size_t nbytes);
|
||||
static ssize_t constfs_write(vfs_file_t *filp, const void *src, size_t nbytes);
|
||||
|
||||
/* Directory operations */
|
||||
static int constfs_opendir(vfs_DIR *dirp, const char *dirname, const char *abs_path);
|
||||
static int constfs_readdir(vfs_DIR *dirp, vfs_dirent_t *entry);
|
||||
static int constfs_closedir(vfs_DIR *dirp);
|
||||
|
||||
static const vfs_file_system_ops_t constfs_fs_ops = {
|
||||
.mount = constfs_mount,
|
||||
.umount = constfs_umount,
|
||||
.unlink = constfs_unlink,
|
||||
.statvfs = constfs_statvfs,
|
||||
.stat = constfs_stat,
|
||||
};
|
||||
|
||||
static const vfs_file_ops_t constfs_file_ops = {
|
||||
.close = constfs_close,
|
||||
.fstat = constfs_fstat,
|
||||
.lseek = constfs_lseek,
|
||||
.open = constfs_open,
|
||||
.read = constfs_read,
|
||||
.write = constfs_write,
|
||||
};
|
||||
|
||||
static const vfs_dir_ops_t constfs_dir_ops = {
|
||||
.opendir = constfs_opendir,
|
||||
.readdir = constfs_readdir,
|
||||
.closedir = constfs_closedir,
|
||||
};
|
||||
|
||||
|
||||
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_mount(vfs_mount_t *mountp)
|
||||
{
|
||||
/* perform any extra initialization here */
|
||||
(void) mountp; /* prevent warning: unused parameter */
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int constfs_umount(vfs_mount_t *mountp)
|
||||
{
|
||||
/* free resources and perform any clean up here */
|
||||
(void) mountp; /* prevent warning: unused parameter */
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int constfs_unlink(vfs_mount_t *mountp, const char *name)
|
||||
{
|
||||
/* Removing files is prohibited */
|
||||
(void) mountp; /* prevent warning: unused parameter */
|
||||
(void) name; /* prevent warning: unused parameter */
|
||||
return -EROFS;
|
||||
}
|
||||
|
||||
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 */
|
||||
memset(buf, 0, sizeof(*buf));
|
||||
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_close(vfs_file_t *filp)
|
||||
{
|
||||
/* perform any necessary clean ups */
|
||||
(void) filp; /* prevent warning: unused parameter */
|
||||
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, const char *abs_path)
|
||||
{
|
||||
(void) mode;
|
||||
(void) abs_path;
|
||||
constfs_t *fs = filp->mp->private_data;
|
||||
DEBUG("constfs_open: %p, \"%s\", 0x%x, 0%03lo, \"%s\"\n", (void *)filp, name, flags, (unsigned long)mode, abs_path);
|
||||
/* 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, %lu\n", (void *)filp, dest, (unsigned long)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, fp->data + filp->pos, nbytes);
|
||||
DEBUG("constfs_read: read %d bytes\n", nbytes);
|
||||
filp->pos += nbytes;
|
||||
return nbytes;
|
||||
}
|
||||
|
||||
static ssize_t constfs_write(vfs_file_t *filp, const void *src, size_t nbytes)
|
||||
{
|
||||
DEBUG("constfs_write: %p, %p, %lu\n", (void *)filp, src, (unsigned long)nbytes);
|
||||
/* Read only file system */
|
||||
DEBUG("constfs_write: read only FS\n");
|
||||
/* prevent warning: unused parameter */
|
||||
(void) filp;
|
||||
(void) src;
|
||||
(void) nbytes;
|
||||
return -EBADF;
|
||||
}
|
||||
|
||||
static int constfs_opendir(vfs_DIR *dirp, const char *dirname, const char *abs_path)
|
||||
{
|
||||
(void) abs_path;
|
||||
DEBUG("constfs_opendir: %p, \"%s\", \"%s\"\n", (void *)dirp, dirname, abs_path);
|
||||
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;
|
||||
}
|
||||
size_t len = strnlen(fp->path, 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], fp->path, len + 1);
|
||||
entry->d_ino = filenum;
|
||||
++filenum;
|
||||
dirp->private_data.value = filenum;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int constfs_closedir(vfs_DIR *dirp)
|
||||
{
|
||||
/* Just an example, it's not necessary to define closedir if there is
|
||||
* nothing to clean up */
|
||||
(void) dirp;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void _constfs_write_stat(const constfs_file_t *fp, struct stat *restrict buf)
|
||||
{
|
||||
/* clear out the stat buffer first */
|
||||
memset(buf, 0, sizeof(*buf));
|
||||
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);
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @defgroup fs File systems
|
||||
* @brief File system libraries
|
||||
*/
|
|
@ -0,0 +1,66 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @defgroup fs_constfs ConstFS static file system
|
||||
* @ingroup fs
|
||||
* @brief Constant file system resident in arrays
|
||||
*
|
||||
* This is an example of how to implement a simple file system driver for the
|
||||
* RIOT VFS layer. The implementation uses an array of @c constfs_file_t objects
|
||||
* as its storage back-end.
|
||||
*
|
||||
* @{
|
||||
* @file
|
||||
* @brief ConstFS public API
|
||||
* @author Joakim Nohlgård <joakim.nohlgard@eistec.se>
|
||||
*/
|
||||
|
||||
#ifndef CONSTFS_H_
|
||||
#define CONSTFS_H_
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "vfs.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief A file in ConstFS (file name + contents)
|
||||
*/
|
||||
typedef struct {
|
||||
const char *path; /**< file system relative path to file */
|
||||
const size_t size; /**< length of @c data */
|
||||
const uint8_t *data; /**< pointer to file contents */
|
||||
} constfs_file_t;
|
||||
|
||||
/**
|
||||
* @brief ConstFS file system superblock
|
||||
*/
|
||||
typedef struct {
|
||||
const size_t nfiles; /**< Number of files */
|
||||
const constfs_file_t *files; /**< Files array */
|
||||
} constfs_t;
|
||||
|
||||
/**
|
||||
* @brief ConstFS file system driver
|
||||
*
|
||||
* For use with vfs_mount
|
||||
*/
|
||||
extern const vfs_file_system_t constfs_file_system;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
/** @} */
|
|
@ -0,0 +1,834 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @defgroup sys_vfs Virtual File System (VFS) layer
|
||||
* @ingroup sys
|
||||
* @brief Provides an interface for accessing files and directories from
|
||||
* different devices and file systems
|
||||
*
|
||||
* This layer is modeled as a mix between POSIX syscalls (e.g. open) and the
|
||||
* Linux VFS layer implementation, with major reductions in the feature set, in
|
||||
* order to fit the resource constrained platforms that RIOT targets.
|
||||
*
|
||||
* The overall design goals are:
|
||||
* - Provide implementations for all newlib "file" syscalls
|
||||
* - Keep it simple, do not add every possible file operation from Linux VFS.
|
||||
* - Easy to map existing file system implementations for resource constrained systems onto the VFS layer API
|
||||
* - Avoid keeping a central `enum` of all file system drivers that has to be kept up to date with external packages etc.
|
||||
* - Use POSIX `<errno.h>` numbers as negative return codes for errors, avoid the global `errno` variable.
|
||||
* - Only absolute paths to files (no per-process working directory)
|
||||
* - No dynamic memory allocation
|
||||
*
|
||||
*
|
||||
* The API should be easy to understand for users who are familiar with the
|
||||
* POSIX file functions (open, close, read, write, fstat, lseek etc.)
|
||||
*
|
||||
* The VFS layer keeps track of mounted file systems and open files, the
|
||||
* `vfs_open` function searches the array of mounted file systems and dispatches
|
||||
* the call to the file system instance with the longest matching mount point prefix.
|
||||
* Subsequent calls to `vfs_read`, `vfs_write`, etc will do a look up in the
|
||||
* table of open files and dispatch the call to the correct file system driver
|
||||
* for handling.
|
||||
*
|
||||
* `vfs_mount` takes a string containing the mount point, a file system driver
|
||||
* specification (`struct file_system`), and an opaque pointer that only the FS
|
||||
* driver knows how to use, which can be used to keep driver parameters in order
|
||||
* to allow dynamic handling of multiple devices.
|
||||
*
|
||||
* @todo VFS layer reference counting and locking for open files and
|
||||
* simultaneous access.
|
||||
*
|
||||
* @{
|
||||
* @file
|
||||
* @brief VFS layer API declarations
|
||||
* @author Joakim Nohlgård <joakim.nohlgard@eistec.se>
|
||||
*/
|
||||
|
||||
#ifndef VFS_H_
|
||||
#define VFS_H_
|
||||
|
||||
#include <stdint.h>
|
||||
/* The stdatomic.h in GCC gives compilation errors with C++
|
||||
* see: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=60932
|
||||
*/
|
||||
#ifdef __cplusplus
|
||||
#include <atomic>
|
||||
/* Make atomic_int available without namespace specifier */
|
||||
using std::atomic_int;
|
||||
#else
|
||||
#include <stdatomic.h> /* for atomic_int */
|
||||
#endif
|
||||
#include <sys/stat.h> /* for struct stat */
|
||||
#include <sys/types.h> /* for off_t etc. */
|
||||
#include <sys/statvfs.h> /* for struct statvfs */
|
||||
|
||||
#include "kernel_types.h"
|
||||
#include "clist.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
/* restrict is a C99 keyword, not valid in C++, but GCC and Clang have the
|
||||
* __restrict__ extension keyword which can be used instead */
|
||||
#define restrict __restrict__
|
||||
/* If the above is not supported by the compiler, you can replace it with an
|
||||
* empty definition instead: */
|
||||
/* #define restrict */
|
||||
#endif
|
||||
|
||||
#ifndef VFS_MAX_OPEN_FILES
|
||||
/**
|
||||
* @brief Maximum number of simultaneous open files
|
||||
*/
|
||||
#define VFS_MAX_OPEN_FILES (16)
|
||||
#endif
|
||||
|
||||
#ifndef VFS_DIR_BUFFER_SIZE
|
||||
/**
|
||||
* @brief Size of buffer space in vfs_DIR
|
||||
*
|
||||
* This space is needed to avoid dynamic memory allocations for some file
|
||||
* systems where a single pointer is not enough space for its directory stream
|
||||
* state, e.g. SPIFFS.
|
||||
*
|
||||
* Guidelines:
|
||||
*
|
||||
* SPIFFS requires a sizeof(spiffs_DIR) (6-16 bytes, depending on target
|
||||
* platform and configuration) buffer for its DIR struct.
|
||||
*
|
||||
* @attention File system developers: If your file system requires a buffer for
|
||||
* DIR streams that is larger than a single pointer or @c int variable, ensure
|
||||
* that you have a preprocessor check in your header file (so that it is
|
||||
* impossible to attempt to mount the file system without running into a
|
||||
* compiler error):
|
||||
*
|
||||
* @attention @code
|
||||
* #if VFS_DIR_BUFFER_SIZE < 123
|
||||
* #error VFS_DIR_BUFFER_SIZE is too small, at least 123 bytes is required
|
||||
* #endif
|
||||
* @endcode
|
||||
*
|
||||
* @attention Put the check in the public header file (.h), do not put the check in the
|
||||
* implementation (.c) file.
|
||||
*/
|
||||
#define VFS_DIR_BUFFER_SIZE (12)
|
||||
#endif
|
||||
|
||||
#ifndef VFS_NAME_MAX
|
||||
/**
|
||||
* @brief Maximum length of the name in a @c vfs_dirent_t (not including terminating null)
|
||||
*
|
||||
* Maximum number of bytes in a filename (not including terminating null).
|
||||
*
|
||||
* Similar to the POSIX macro NAME_MAX
|
||||
*/
|
||||
#define VFS_NAME_MAX (31)
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Used with vfs_bind to bind to any available fd number
|
||||
*/
|
||||
#define VFS_ANY_FD (-1)
|
||||
|
||||
/* Forward declarations */
|
||||
/**
|
||||
* @brief struct @c vfs_file_ops typedef
|
||||
*/
|
||||
typedef struct vfs_file_ops vfs_file_ops_t;
|
||||
|
||||
/**
|
||||
* @brief struct @c vfs_dir_ops typedef
|
||||
*/
|
||||
typedef struct vfs_dir_ops vfs_dir_ops_t;
|
||||
|
||||
/**
|
||||
* @brief struct @c vfs_file_system_ops typedef
|
||||
*/
|
||||
typedef struct vfs_file_system_ops vfs_file_system_ops_t;
|
||||
|
||||
/**
|
||||
* @brief struct @c vfs_mount_struct typedef
|
||||
*/
|
||||
/* not struct vfs_mount because of name collision with the function */
|
||||
typedef struct vfs_mount_struct vfs_mount_t;
|
||||
|
||||
/**
|
||||
* @brief A file system driver
|
||||
*/
|
||||
typedef struct {
|
||||
const vfs_file_ops_t *f_op; /**< File operations table */
|
||||
const vfs_dir_ops_t *d_op; /**< Directory operations table */
|
||||
const vfs_file_system_ops_t *fs_op; /**< File system operations table */
|
||||
} vfs_file_system_t;
|
||||
|
||||
/**
|
||||
* @brief A mounted file system
|
||||
*/
|
||||
struct vfs_mount_struct {
|
||||
clist_node_t list_entry; /**< List entry for the _vfs_mount_list list */
|
||||
const vfs_file_system_t *fs; /**< The file system driver for the mount point */
|
||||
const char *mount_point; /**< Mount point, e.g. "/mnt/cdrom" */
|
||||
size_t mount_point_len; /**< Length of mount_point string (set by vfs_mount) */
|
||||
atomic_int open_files; /**< Number of currently open files */
|
||||
void *private_data; /**< File system driver private data, implementation defined */
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Information about an open file
|
||||
*
|
||||
* Similar to, but not equal to, <tt>struct file</tt> in Linux
|
||||
*/
|
||||
typedef struct {
|
||||
const vfs_file_ops_t *f_op; /**< File operations table */
|
||||
vfs_mount_t *mp; /**< Pointer to mount table entry */
|
||||
int flags; /**< File flags */
|
||||
off_t pos; /**< Current position in the file */
|
||||
kernel_pid_t pid; /**< PID of the process that opened the file */
|
||||
union {
|
||||
void *ptr; /**< pointer to private data */
|
||||
int value; /**< alternatively, you can use private_data as an int */
|
||||
} private_data; /**< File system driver private data, implementation defined */
|
||||
} vfs_file_t;
|
||||
|
||||
/**
|
||||
* @brief Internal representation of a file system directory entry
|
||||
*
|
||||
* Used by opendir, readdir, closedir
|
||||
*
|
||||
* @attention This structure should be treated as an opaque blob and must not be
|
||||
* modified by user code. The contents should only be used by file system drivers.
|
||||
*/
|
||||
typedef struct {
|
||||
const vfs_dir_ops_t *d_op; /**< Directory operations table */
|
||||
vfs_mount_t *mp; /**< Pointer to mount table entry */
|
||||
union {
|
||||
void *ptr; /**< pointer to private data */
|
||||
int value; /**< alternatively, you can use private_data as an int */
|
||||
uint8_t buffer[VFS_DIR_BUFFER_SIZE]; /**< Buffer space, in case a single pointer is not enough */
|
||||
} private_data; /**< File system driver private data, implementation defined */
|
||||
} vfs_DIR;
|
||||
|
||||
/**
|
||||
* @brief User facing directory entry
|
||||
*
|
||||
* Used to hold the output from readdir
|
||||
*
|
||||
* @note size, modification time, and other information is part of the file
|
||||
* status, not the directory entry.
|
||||
*/
|
||||
typedef struct {
|
||||
ino_t d_ino; /**< file serial number, unique for the file system ("inode" in Linux) */
|
||||
char d_name[VFS_NAME_MAX + 1]; /**< file name, relative to its containing directory */
|
||||
} vfs_dirent_t;
|
||||
|
||||
/**
|
||||
* @brief Operations on open files
|
||||
*
|
||||
* Similar, but not equal, to struct file_operations in Linux
|
||||
*/
|
||||
struct vfs_file_ops {
|
||||
/**
|
||||
* @brief Close an open file
|
||||
*
|
||||
* This function must perform any necessary clean ups and flush any internal
|
||||
* buffers in the file system driver.
|
||||
*
|
||||
* If an error occurs, the file will still be considered closed by the VFS
|
||||
* layer. Therefore, the proper clean up must still be performed by the file
|
||||
* system driver before returning any error code.
|
||||
*
|
||||
* @note This implementation does not consider @c -EINTR a special return code,
|
||||
* the file is still considered closed.
|
||||
*
|
||||
* @param[in] filp pointer to open file
|
||||
*
|
||||
* @return 0 on success
|
||||
* @return <0 on error, the file is considered closed anyway
|
||||
*/
|
||||
int (*close) (vfs_file_t *filp);
|
||||
|
||||
/**
|
||||
* @brief Query/set options on an open file
|
||||
*
|
||||
* @param[in] filp pointer to open file
|
||||
* @param[in] cmd fcntl command, see man 3p fcntl
|
||||
* @param[in] arg argument to fcntl command, see man 3p fcntl
|
||||
*
|
||||
* @return 0 on success
|
||||
* @return <0 on error
|
||||
*/
|
||||
int (*fcntl) (vfs_file_t *filp, int cmd, int arg);
|
||||
|
||||
/**
|
||||
* @brief Get status of an open file
|
||||
*
|
||||
* @param[in] filp pointer to open file
|
||||
* @param[out] buf pointer to stat struct to fill
|
||||
*
|
||||
* @return 0 on success
|
||||
* @return <0 on error
|
||||
*/
|
||||
int (*fstat) (vfs_file_t *filp, struct stat *buf);
|
||||
|
||||
/**
|
||||
* @brief Seek to position in file
|
||||
*
|
||||
* @p whence determines the function of the seek and should be set to one of
|
||||
* the following values:
|
||||
*
|
||||
* - @c SEEK_SET: Seek to absolute offset @p off
|
||||
* - @c SEEK_CUR: Seek to current location + @p off
|
||||
* - @c SEEK_END: Seek to end of file + @p off
|
||||
*
|
||||
* @param[in] filp pointer to open file
|
||||
* @param[in] off seek offset
|
||||
* @param[in] whence determines the seek method, see detailed description
|
||||
*
|
||||
* @return the new seek location in the file on success
|
||||
* @return <0 on error
|
||||
*/
|
||||
off_t (*lseek) (vfs_file_t *filp, off_t off, int whence);
|
||||
|
||||
/**
|
||||
* @brief Attempt to open a file in the file system at rel_path
|
||||
*
|
||||
* A file system driver should perform the necessary checks for file
|
||||
* existence etc in this function.
|
||||
*
|
||||
* The VFS layer will initialize the contents of @p *filp so that
|
||||
* @c filp->f_op points to the mounted file system's @c vfs_file_ops_t.
|
||||
* @c filp->private_data.ptr will be initialized to NULL, @c filp->pos will
|
||||
* be set to 0.
|
||||
*
|
||||
* @note @p name is an absolute path inside the file system, @p abs_path is
|
||||
* the path to the file in the VFS, example:
|
||||
* @p abs_path = "/mnt/hd/foo/bar", @p name = "/foo/bar"
|
||||
*
|
||||
* @note @p name and @p abs_path may point to different locations within the
|
||||
* same const char array and the strings may overlap
|
||||
*
|
||||
* @param[in] filp pointer to open file
|
||||
* @param[in] name null-terminated name of the file to open, relative to the file system root, including a leading slash
|
||||
* @param[in] flags flags for opening, see man 2 open, man 3p open
|
||||
* @param[in] mode mode for creating a new file, see man 2 open, man 3p open
|
||||
* @param[in] abs_path null-terminated name of the file to open, relative to the VFS root ("/")
|
||||
*
|
||||
* @return 0 on success
|
||||
* @return <0 on error
|
||||
*/
|
||||
int (*open) (vfs_file_t *filp, const char *name, int flags, mode_t mode, const char *abs_path);
|
||||
|
||||
/**
|
||||
* @brief Read bytes from an open file
|
||||
*
|
||||
* @param[in] filp pointer to open file
|
||||
* @param[in] dest pointer to destination buffer
|
||||
* @param[in] nbytes maximum number of bytes to read
|
||||
*
|
||||
* @return number of bytes read on success
|
||||
* @return <0 on error
|
||||
*/
|
||||
ssize_t (*read) (vfs_file_t *filp, void *dest, size_t nbytes);
|
||||
|
||||
/**
|
||||
* @brief Write bytes to an open file
|
||||
*
|
||||
* @param[in] filp pointer to open file
|
||||
* @param[in] src pointer to source buffer
|
||||
* @param[in] nbytes maximum number of bytes to write
|
||||
*
|
||||
* @return number of bytes written on success
|
||||
* @return <0 on error
|
||||
*/
|
||||
ssize_t (*write) (vfs_file_t *filp, const void *src, size_t nbytes);
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Operations on open directories
|
||||
*/
|
||||
struct vfs_dir_ops {
|
||||
/**
|
||||
* @brief Open a directory for reading with readdir
|
||||
*
|
||||
* @param[in] dirp pointer to open directory
|
||||
* @param[in] name null-terminated name of the dir to open, relative to the file system root, including a leading slash
|
||||
* @param[in] abs_path null-terminated name of the dir to open, relative to the VFS root ("/")
|
||||
*
|
||||
* @return 0 on success
|
||||
* @return <0 on error
|
||||
*/
|
||||
int (*opendir) (vfs_DIR *dirp, const char *dirname, const char *abs_path);
|
||||
|
||||
/**
|
||||
* @brief Read a single entry from the open directory dirp and advance the
|
||||
* read position by one
|
||||
*
|
||||
* @p entry will be populated with information about the next entry in the
|
||||
* directory stream @p dirp
|
||||
*
|
||||
* If @p entry was updated successfully, @c readdir shall return 1.
|
||||
*
|
||||
* If the end of stream was reached, @c readdir shall return 0 and @p entry
|
||||
* shall remain untouched.
|
||||
*
|
||||
* @param[in] dirp pointer to open directory
|
||||
* @param[out] entry directory entry information
|
||||
*
|
||||
* @return 1 if @p entry was updated
|
||||
* @return 0 if @p dirp has reached the end of the directory index
|
||||
* @return <0 on error
|
||||
*/
|
||||
int (*readdir) (vfs_DIR *dirp, vfs_dirent_t *entry);
|
||||
|
||||
/**
|
||||
* @brief Close an open directory
|
||||
*
|
||||
* @param[in] dirp pointer to open directory
|
||||
*
|
||||
* @return 0 on success
|
||||
* @return <0 on error, the directory stream dirp should be considered invalid
|
||||
*/
|
||||
int (*closedir) (vfs_DIR *dirp);
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Operations on mounted file systems
|
||||
*
|
||||
* Similar, but not equal, to struct super_operations in Linux
|
||||
*/
|
||||
struct vfs_file_system_ops {
|
||||
/**
|
||||
* @brief Perform any extra processing needed after mounting a file system
|
||||
*
|
||||
* If this call returns an error, the whole vfs_mount call will signal a
|
||||
* failure.
|
||||
*
|
||||
* All fields of @p mountp will be initialized by vfs_mount beforehand,
|
||||
* @c private_data will be initialized to NULL.
|
||||
*
|
||||
* @param[in] mountp file system mount being mounted
|
||||
*
|
||||
* @return 0 on success
|
||||
* @return <0 on error
|
||||
*/
|
||||
int (*mount) (vfs_mount_t *mountp);
|
||||
|
||||
/**
|
||||
* @brief Perform the necessary clean up for unmounting a file system
|
||||
*
|
||||
* @param[in] mountp file system mount being unmounted
|
||||
*
|
||||
* @return 0 on success
|
||||
* @return <0 on error
|
||||
*/
|
||||
int (*umount) (vfs_mount_t *mountp);
|
||||
|
||||
/**
|
||||
* @brief Rename a file
|
||||
*
|
||||
* The file @p from_path will be renamed to @p to_path
|
||||
*
|
||||
* @note it is not possible to rename files across different file system
|
||||
*
|
||||
* @param[in] mountp file system mount to operate on
|
||||
* @param[in] from_path absolute path to existing file
|
||||
* @param[in] to_path absolute path to destination
|
||||
*
|
||||
* @return 0 on success
|
||||
* @return <0 on error
|
||||
*/
|
||||
int (*rename) (vfs_mount_t *mountp, const char *from_path, const char *to_path);
|
||||
|
||||
/**
|
||||
* @brief Unlink (delete) a file from the file system
|
||||
*
|
||||
* @param[in] mountp file system mount to operate on
|
||||
* @param[in] name name of the file to delete
|
||||
*
|
||||
* @return 0 on success
|
||||
* @return <0 on error
|
||||
*/
|
||||
int (*unlink) (vfs_mount_t *mountp, const char *name);
|
||||
|
||||
/**
|
||||
* @brief Create a directory on the file system
|
||||
*
|
||||
* @param[in] mountp file system mount to operate on
|
||||
* @param[in] name name of the directory to create
|
||||
* @param[in] mode file creation mode bits
|
||||
*
|
||||
* @return 0 on success
|
||||
* @return <0 on error
|
||||
*/
|
||||
int (*mkdir) (vfs_mount_t *mountp, const char *name, mode_t mode);
|
||||
|
||||
/**
|
||||
* @brief Remove a directory from the file system
|
||||
*
|
||||
* Only empty directories may be removed.
|
||||
*
|
||||
* @param[in] mountp file system mount to operate on
|
||||
* @param[in] name name of the directory to remove
|
||||
*
|
||||
* @return 0 on success
|
||||
* @return <0 on error
|
||||
*/
|
||||
int (*rmdir) (vfs_mount_t *mountp, const char *name);
|
||||
|
||||
/**
|
||||
* @brief Get file status
|
||||
*
|
||||
* @param[in] mountp file system mount to operate on
|
||||
* @param[in] path path to file being queried
|
||||
* @param[out] buf pointer to stat struct to fill
|
||||
*
|
||||
* @return 0 on success
|
||||
* @return <0 on error
|
||||
*/
|
||||
int (*stat) (vfs_mount_t *mountp, const char *restrict path, struct stat *restrict buf);
|
||||
|
||||
/**
|
||||
* @brief Get file system status
|
||||
*
|
||||
* @p path is only passed for consistency against the POSIX statvfs function.
|
||||
* @c vfs_statvfs calls this function only when it has determined that
|
||||
* @p path belongs to this file system. @p path is a file system relative
|
||||
* path and does not necessarily name an existing file.
|
||||
*
|
||||
* @param[in] mountp file system mount to operate on
|
||||
* @param[in] path path to a file on the file system being queried
|
||||
* @param[out] buf pointer to statvfs struct to fill
|
||||
*
|
||||
* @return 0 on success
|
||||
* @return <0 on error
|
||||
*/
|
||||
int (*statvfs) (vfs_mount_t *mountp, const char *restrict path, struct statvfs *restrict buf);
|
||||
|
||||
/**
|
||||
* @brief Get file system status of an open file
|
||||
*
|
||||
* @p path is only passed for consistency against the POSIX statvfs function.
|
||||
* @c vfs_statvfs calls this function only when it has determined that
|
||||
* @p path belongs to this file system. @p path is a file system relative
|
||||
* path and does not necessarily name an existing file.
|
||||
*
|
||||
* @param[in] mountp file system mount to operate on
|
||||
* @param[in] filp pointer to an open file on the file system being queried
|
||||
* @param[out] buf pointer to statvfs struct to fill
|
||||
*
|
||||
* @return 0 on success
|
||||
* @return <0 on error
|
||||
*/
|
||||
int (*fstatvfs) (vfs_mount_t *mountp, vfs_file_t *filp, struct statvfs *buf);
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Close an open file
|
||||
*
|
||||
* @param[in] fd fd number to close
|
||||
*
|
||||
* @return 0 on success
|
||||
* @return <0 on error
|
||||
*/
|
||||
int vfs_close(int fd);
|
||||
|
||||
/**
|
||||
* @brief Query/set options on an open file
|
||||
*
|
||||
* @param[in] fd fd number to operate on
|
||||
* @param[in] cmd fcntl command, see man 3p fcntl
|
||||
* @param[in] arg argument to fcntl command, see man 3p fcntl
|
||||
*
|
||||
* @return 0 on success
|
||||
* @return <0 on error
|
||||
*/
|
||||
int vfs_fcntl(int fd, int cmd, int arg);
|
||||
|
||||
/**
|
||||
* @brief Get status of an open file
|
||||
*
|
||||
* @param[in] fd fd number obtained from vfs_open
|
||||
* @param[out] buf pointer to stat struct to fill
|
||||
*
|
||||
* @return 0 on success
|
||||
* @return <0 on error
|
||||
*/
|
||||
int vfs_fstat(int fd, struct stat *buf);
|
||||
|
||||
/**
|
||||
* @brief Get file system status of the file system containing an open file
|
||||
*
|
||||
* @param[in] fd fd number obtained from vfs_open
|
||||
* @param[out] buf pointer to statvfs struct to fill
|
||||
*
|
||||
* @return 0 on success
|
||||
* @return <0 on error
|
||||
*/
|
||||
int vfs_fstatvfs(int fd, struct statvfs *buf);
|
||||
|
||||
/**
|
||||
* @brief Seek to position in file
|
||||
*
|
||||
* @p whence determines the function of the seek and should be set to one of
|
||||
* the following values:
|
||||
*
|
||||
* - @c SEEK_SET: Seek to absolute offset @p off
|
||||
* - @c SEEK_CUR: Seek to current location + @p off
|
||||
* - @c SEEK_END: Seek to end of file + @p off
|
||||
*
|
||||
* @param[in] fd fd number obtained from vfs_open
|
||||
* @param[in] off seek offset
|
||||
* @param[in] whence determines the seek method, see detailed description
|
||||
*
|
||||
* @return the new seek location in the file on success
|
||||
* @return <0 on error
|
||||
*/
|
||||
off_t vfs_lseek(int fd, off_t off, int whence);
|
||||
|
||||
/**
|
||||
* @brief Open a file
|
||||
*
|
||||
* @param[in] name file name to open
|
||||
* @param[in] flags flags for opening, see man 3p open
|
||||
* @param[in] mode file mode
|
||||
*
|
||||
* @return fd number on success (>= 0)
|
||||
* @return <0 on error
|
||||
*/
|
||||
int vfs_open(const char *name, int flags, mode_t mode);
|
||||
|
||||
/**
|
||||
* @brief Read bytes from an open file
|
||||
*
|
||||
* @param[in] fd fd number obtained from vfs_open
|
||||
* @param[out] dest destination buffer to hold the file contents
|
||||
* @param[in] count maximum number of bytes to read
|
||||
*
|
||||
* @return number of bytes read on success
|
||||
* @return <0 on error
|
||||
*/
|
||||
ssize_t vfs_read(int fd, void *dest, size_t count);
|
||||
|
||||
/**
|
||||
* @brief Write bytes to an open file
|
||||
*
|
||||
* @param[in] fd fd number obtained from vfs_open
|
||||
* @param[in] src pointer to source buffer
|
||||
* @param[in] count maximum number of bytes to write
|
||||
*
|
||||
* @return number of bytes written on success
|
||||
* @return <0 on error
|
||||
*/
|
||||
ssize_t vfs_write(int fd, const void *src, size_t count);
|
||||
|
||||
/**
|
||||
* @brief Open a directory for reading with readdir
|
||||
*
|
||||
* The data in @c *dirp will be initialized by @c vfs_opendir
|
||||
*
|
||||
* @param[out] dirp pointer to directory stream struct for storing the state
|
||||
* @param[in] dirname null-terminated name of the dir to open, absolute file system path
|
||||
*
|
||||
* @return 0 on success
|
||||
* @return <0 on error
|
||||
*/
|
||||
int vfs_opendir(vfs_DIR *dirp, const char *dirname);
|
||||
|
||||
/**
|
||||
* @brief Read a single entry from the open directory dirp and advance the
|
||||
* read position by one
|
||||
*
|
||||
* @p entry will be populated with information about the next entry in the
|
||||
* directory stream @p dirp
|
||||
*
|
||||
* @attention Calling vfs_readdir on an uninitialized @c vfs_DIR is forbidden
|
||||
* and may lead to file system corruption and random system failures.
|
||||
*
|
||||
* @param[in] dirp pointer to open directory
|
||||
* @param[out] entry directory entry information
|
||||
*
|
||||
* @return 0 on success
|
||||
* @return <0 on error
|
||||
*/
|
||||
int vfs_readdir(vfs_DIR *dirp, vfs_dirent_t *entry);
|
||||
|
||||
/**
|
||||
* @brief Close an open directory
|
||||
*
|
||||
* @attention Calling vfs_closedir on an uninitialized @c vfs_DIR is forbidden
|
||||
* and may lead to file system corruption and random system failures.
|
||||
*
|
||||
* @param[in] dirp pointer to open directory
|
||||
*
|
||||
* @return 0 on success
|
||||
* @return <0 on error, the directory stream dirp should be considered invalid
|
||||
*/
|
||||
int vfs_closedir(vfs_DIR *dirp);
|
||||
|
||||
/**
|
||||
* @brief Mount a file system
|
||||
*
|
||||
* @p mountp should have been populated in advance with a file system driver,
|
||||
* a mount point, and private_data (if the file system driver uses one).
|
||||
*
|
||||
* @param[in] mountp pointer to the mount structure of the file system to mount
|
||||
*
|
||||
* @return 0 on success
|
||||
* @return <0 on error
|
||||
*/
|
||||
int vfs_mount(vfs_mount_t *mountp);
|
||||
|
||||
/**
|
||||
* @brief Rename a file
|
||||
*
|
||||
* The file @p from_path will be renamed to @p to_path
|
||||
*
|
||||
* @note it is not possible to rename files across different file system
|
||||
*
|
||||
* @param[in] from_path absolute path to existing file
|
||||
* @param[in] to_path absolute path to destination
|
||||
*
|
||||
* @return 0 on success
|
||||
* @return <0 on error
|
||||
*/
|
||||
int vfs_rename(const char *from_path, const char *to_path);
|
||||
|
||||
/**
|
||||
* @brief Unmount a mounted file system
|
||||
*
|
||||
* This will fail if there are any open files on the mounted file system
|
||||
*
|
||||
* @param[in] mountp pointer to the mount structure of the file system to unmount
|
||||
*
|
||||
* @return 0 on success
|
||||
* @return <0 on error
|
||||
*/
|
||||
int vfs_umount(vfs_mount_t *mountp);
|
||||
|
||||
/**
|
||||
* @brief Unlink (delete) a file from a mounted file system
|
||||
*
|
||||
* @param[in] name name of file to delete
|
||||
*
|
||||
* @return 0 on success
|
||||
* @return <0 on error
|
||||
*/
|
||||
int vfs_unlink(const char *name);
|
||||
|
||||
/**
|
||||
* @brief Create a directory on the file system
|
||||
*
|
||||
* @param[in] name name of the directory to create
|
||||
* @param[in] mode file creation mode bits
|
||||
*
|
||||
* @return 0 on success
|
||||
* @return <0 on error
|
||||
*/
|
||||
int vfs_mkdir(const char *name, mode_t mode);
|
||||
|
||||
/**
|
||||
* @brief Remove a directory from the file system
|
||||
*
|
||||
* Only empty directories may be removed.
|
||||
*
|
||||
* @param[in] name name of the directory to remove
|
||||
*
|
||||
* @return 0 on success
|
||||
* @return <0 on error
|
||||
*/
|
||||
int vfs_rmdir(const char *name);
|
||||
|
||||
/**
|
||||
* @brief Get file status
|
||||
*
|
||||
* @param[in] path path to file being queried
|
||||
* @param[out] buf pointer to stat struct to fill
|
||||
*
|
||||
* @return 0 on success
|
||||
* @return <0 on error
|
||||
*/
|
||||
int vfs_stat(const char *restrict path, struct stat *restrict buf);
|
||||
|
||||
/**
|
||||
* @brief Get file system status
|
||||
*
|
||||
* @p path can be any path that resolves to the file system being queried, it
|
||||
* does not have to be an existing file.
|
||||
*
|
||||
* @param[in] path path to a file on the file system being queried
|
||||
* @param[out] buf pointer to statvfs struct to fill
|
||||
*
|
||||
* @return 0 on success
|
||||
* @return <0 on error
|
||||
*/
|
||||
int vfs_statvfs(const char *restrict path, struct statvfs *restrict buf);
|
||||
|
||||
/**
|
||||
* @brief Allocate a new file descriptor and give it file operations
|
||||
*
|
||||
* The new fd will be initialized with pointers to the given @p f_op file
|
||||
* operations table and @p private_data.
|
||||
*
|
||||
* This function can be used to give file-like functionality to devices, e.g. UART.
|
||||
*
|
||||
* @p private_data can be used for passing instance information to the file
|
||||
* operation handlers in @p f_op.
|
||||
*
|
||||
* @param[in] fd Desired fd number, use VFS_ANY_FD for any available fd
|
||||
* @param[in] flags not implemented yet
|
||||
* @param[in] f_op pointer to file operations table
|
||||
* @param[in] private_data opaque pointer to private data
|
||||
*
|
||||
* @return fd number on success (>= 0)
|
||||
* @return <0 on error
|
||||
*/
|
||||
int vfs_bind(int fd, int flags, const vfs_file_ops_t *f_op, void *private_data);
|
||||
|
||||
/**
|
||||
* @brief Normalize a path
|
||||
*
|
||||
* Normalizing a path means to remove all relative components ("..", ".") and
|
||||
* any double slashes.
|
||||
*
|
||||
* @note @p buf is allowed to overlap @p path if @p &buf[0] <= @p &path[0]
|
||||
*
|
||||
* @attention @p path must be an absolute path (starting with @c / )
|
||||
*
|
||||
* @param[out] buf buffer to store normalized path
|
||||
* @param[in] path path to normalize
|
||||
* @param[in] buflen available space in @p buf
|
||||
*
|
||||
* @return number of path components in the normalized path on success
|
||||
* @return <0 on error
|
||||
*/
|
||||
int vfs_normalize_path(char *buf, const char *path, size_t buflen);
|
||||
|
||||
/**
|
||||
* @brief Iterate through all mounted file systems
|
||||
*
|
||||
* @attention Not thread safe! Do not mix calls to this function with other
|
||||
* calls which modify the mount table, such as vfs_mount() and vfs_umount()
|
||||
*
|
||||
* Set @p cur to @c NULL to start from the beginning
|
||||
*
|
||||
* @see @c sc_vfs.c (@c df command) for a usage example
|
||||
*
|
||||
* @param[in] cur current iterator value
|
||||
*
|
||||
* @return Pointer to next mounted file system in list after @p cur
|
||||
* @return NULL if @p cur is the last element in the list
|
||||
*/
|
||||
const vfs_mount_t *vfs_iterate_mounts(const vfs_mount_t *cur);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
/** @} */
|
|
@ -61,6 +61,9 @@ endif
|
|||
ifneq (,$(filter sntp,$(USEMODULE)))
|
||||
SRC += sc_sntp.c
|
||||
endif
|
||||
ifneq (,$(filter vfs,$(USEMODULE)))
|
||||
SRC += sc_vfs.c
|
||||
endif
|
||||
|
||||
# TODO
|
||||
# Conditional building not possible at the moment due to
|
||||
|
|
|
@ -0,0 +1,424 @@
|
|||
/*
|
||||
* 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_shell_commands
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
* @brief Shell commands for the VFS module
|
||||
*
|
||||
* @author Joakim Nohlgård <joakim.nohlgard@eistec.se>
|
||||
*
|
||||
* @}
|
||||
*/
|
||||
|
||||
#if MODULE_VFS
|
||||
#include <stdint.h>
|
||||
#include <inttypes.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
#include "vfs.h"
|
||||
|
||||
#define SHELL_VFS_BUFSIZE 256
|
||||
static uint8_t _shell_vfs_data_buffer[SHELL_VFS_BUFSIZE];
|
||||
|
||||
static void _ls_usage(char **argv)
|
||||
{
|
||||
printf("%s <path>\n", argv[0]);
|
||||
puts("list files in <path>");
|
||||
}
|
||||
|
||||
static void _vfs_usage(char **argv)
|
||||
{
|
||||
printf("%s <r|w> <path> [bytes] [offset]\n", argv[0]);
|
||||
printf("%s ls <path>\n", argv[0]);
|
||||
printf("%s cp <src> <dest>\n", argv[0]);
|
||||
printf("%s mv <src> <dest>\n", argv[0]);
|
||||
printf("%s rm <file>\n", argv[0]);
|
||||
printf("%s df [path]\n", argv[0]);
|
||||
puts("r: Read [bytes] bytes at [offset] in file <path>");
|
||||
puts("w: not implemented yet");
|
||||
puts( |