fs: allow external file system implementations

The documentation claims that Zephyr supports external file system
implementations, and there's no reason not to do so.  Rework the API
to allow this.

Note that the file system type cannot legally be an enum anymore,
since we need to support file system types that don't have an
identifier assigned in that enum.  Rely on the implicit conversion of
enum values to int to preserve backwards compatibility.

Signed-off-by: Peter A. Bigot <pab@pabigot.com>
This commit is contained in:
Peter A. Bigot 2020-06-01 12:08:58 -05:00 committed by Carles Cufí
commit d0d3c4a3a7
4 changed files with 115 additions and 33 deletions

View file

@ -12,13 +12,15 @@ specific API or internal functions by introducing file system registration
mechanisms.
In Zephyr, any file system implementation or library can be plugged into or
pulled out through a file system registration API.
pulled out through a file system registration API. Each file system
implementation must have a globally unique integer identifier; use
:c:macro:`FS_TYPE_EXTERNAL_BASE` to avoid clashes with in-tree identifiers.
.. code-block:: c
int fs_register(enum fs_type type, const struct fs_file_system_t *fs);
int fs_register(int type, const struct fs_file_system_t *fs);
int fs_unregister(enum fs_type type, const struct fs_file_system_t *fs);
int fs_unregister(int type, const struct fs_file_system_t *fs);
Zephyr RTOS supports multiple instances of a file system by making use of
the mount point as the disk volume name, which is used by the file system library

View file

@ -45,12 +45,30 @@ enum fs_dir_entry_type {
FS_DIR_ENTRY_DIR
};
enum fs_type {
/** @brief Enumeration to uniquely identify file system types.
*
* Zephyr supports in-tree file systems and external ones. Each
* requires a unique identifier used to register the file system
* implementation and to associate a mount point with the file system
* type. This anonymous enum defines global identifiers for the
* in-tree file systems.
*
* External file systems should be registered using unique identifiers
* starting at @c FS_TYPE_EXTERNAL_BASE. It is the responsibility of
* applications that use external file systems to ensure that these
* identifiers are unique if multiple file system implementations are
* used by the application.
*/
enum {
/** Identifier for in-tree FatFS file system. */
FS_FATFS = 0,
FS_LITTLEFS,
FS_TYPE_END,
};
/** Identifier for in-tree LittleFS file system. */
FS_LITTLEFS,
/** Base identifier for external file systems. */
FS_TYPE_EXTERNAL_BASE,
};
/**
* @brief File system mount info structure
@ -65,7 +83,7 @@ enum fs_type {
*/
struct fs_mount_t {
sys_dnode_t node;
enum fs_type type;
int type;
const char *mnt_point;
void *fs_data;
void *storage_dev;
@ -494,7 +512,7 @@ int fs_statvfs(const char *path, struct fs_statvfs *stat);
* @retval 0 Success
* @retval -ERRNO errno code if error
*/
int fs_register(enum fs_type type, const struct fs_file_system_t *fs);
int fs_register(int type, const struct fs_file_system_t *fs);
/**
* @brief Unregister a file system
@ -507,7 +525,7 @@ int fs_register(enum fs_type type, const struct fs_file_system_t *fs);
* @retval 0 Success
* @retval -ERRNO errno code if error
*/
int fs_unregister(enum fs_type type, const struct fs_file_system_t *fs);
int fs_unregister(int type, const struct fs_file_system_t *fs);
/**
* @}

View file

@ -28,6 +28,14 @@ config FAT_FILESYSTEM_ELM
help
Use the ELM FAT File system implementation.
config FILE_SYSTEM_MAX_TYPES
int "Maximum number of distinct file system types allowed"
default 2
help
Zephyr provides several file system types including FatFS and
LittleFS, but it is possible to define additional ones and
register them. A slot is required for each type.
menu "FatFs Settings"
visible if FAT_FILESYSTEM_ELM

View file

@ -1,5 +1,6 @@
/*
* Copyright (c) 2018 Intel Corporation.
* Copyright (c) 2020 Peter Bigot Consulting, LLC
*
* SPDX-License-Identifier: Apache-2.0
*/
@ -23,8 +24,57 @@ static sys_dlist_t fs_mnt_list;
/* lock to protect mount list operations */
static struct k_mutex mutex;
/* file system map table */
static const struct fs_file_system_t *fs_map[FS_TYPE_END];
/* Maps an identifier used in mount points to the file system
* implementation.
*/
struct registry_entry {
int type;
const struct fs_file_system_t *fstp;
};
static struct registry_entry registry[CONFIG_FILE_SYSTEM_MAX_TYPES];
static inline void registry_clear_entry(struct registry_entry *ep)
{
ep->fstp = NULL;
}
static int registry_add(int type,
const struct fs_file_system_t *fstp)
{
int rv = -ENOSPC;
for (size_t i = 0; i < ARRAY_SIZE(registry); ++i) {
struct registry_entry *ep = &registry[i];
if (ep->fstp == NULL) {
ep->type = type;
ep->fstp = fstp;
rv = 0;
break;
}
}
return rv;
}
static struct registry_entry *registry_find(int type)
{
for (size_t i = 0; i < ARRAY_SIZE(registry); ++i) {
struct registry_entry *ep = &registry[i];
if ((ep->fstp != NULL) && (ep->type == type)) {
return ep;
}
}
return NULL;
}
static const struct fs_file_system_t *fs_type_get(int type)
{
struct registry_entry *ep = registry_find(type);
return (ep != NULL) ? ep->fstp : NULL;
}
static int fs_get_mnt_point(struct fs_mount_t **mnt_pntp,
const char *name, size_t *match_len)
@ -536,15 +586,15 @@ int fs_mount(struct fs_mount_t *mp)
}
k_mutex_lock(&mutex, K_FOREVER);
/* Check if requested file system is registered */
if (mp->type >= FS_TYPE_END || fs_map[mp->type] == NULL) {
LOG_ERR("requested file system not registered!!");
/* Get file system information */
fs = fs_type_get(mp->type);
if (fs == NULL) {
LOG_ERR("requested file system type not registered!!");
rc = -ENOENT;
goto mount_err;
}
/* Get fs interface from file system map */
fs = fs_map[mp->type];
mp->mountp_len = strlen(mp->mnt_point);
if ((mp->mnt_point[0] != '/') ||
@ -664,39 +714,43 @@ int fs_readmount(int *number, const char **name)
}
/* Register File system */
int fs_register(enum fs_type type, const struct fs_file_system_t *fs)
int fs_register(int type, const struct fs_file_system_t *fs)
{
int rc = 0;
k_mutex_lock(&mutex, K_FOREVER);
if (type >= FS_TYPE_END) {
LOG_ERR("failed to register File system!!");
rc = -EINVAL;
goto reg_err;
if (fs_type_get(type) != NULL) {
rc = -EALREADY;
} else {
rc = registry_add(type, fs);
}
fs_map[type] = fs;
LOG_DBG("fs registered of type(%u)", type);
reg_err:
k_mutex_unlock(&mutex);
LOG_DBG("fs register %d: %d", type, rc);
return rc;
}
/* Unregister File system */
int fs_unregister(enum fs_type type, const struct fs_file_system_t *fs)
int fs_unregister(int type, const struct fs_file_system_t *fs)
{
int rc = 0;
struct registry_entry *ep;
k_mutex_lock(&mutex, K_FOREVER);
if ((type >= FS_TYPE_END) ||
(fs_map[type] != fs)) {
LOG_ERR("failed to unregister File system!!");
ep = registry_find(type);
if ((ep == NULL) || (ep->fstp != fs)) {
rc = -EINVAL;
goto unreg_err;
} else {
registry_clear_entry(ep);
}
fs_map[type] = NULL;
LOG_DBG("fs unregistered of type(%u)", type);
unreg_err:
k_mutex_unlock(&mutex);
LOG_DBG("fs unregister %d: %d", type, rc);
return rc;
}