mirror of
https://github.com/RIOT-OS/RIOT.git
synced 2024-12-29 04:50:03 +01:00
Merge #18682
18682: pkg/lwext4: add lightweight implementation of the ext2/3/4 filesystem r=benpicco a=benpicco Co-authored-by: Benjamin Valentin <benjamin.valentin@ml-pa.com>
This commit is contained in:
commit
d4422a074a
@ -72,6 +72,11 @@ ifneq (,$(filter fatfs_vfs,$(USEMODULE)))
|
||||
USEMODULE += vfs
|
||||
endif
|
||||
|
||||
ifneq (,$(filter lwext%_vfs,$(USEMODULE)))
|
||||
USEPKG += lwext4
|
||||
USEMODULE += vfs
|
||||
endif
|
||||
|
||||
ifneq (,$(filter nimble_%,$(USEMODULE)))
|
||||
USEPKG += nimble
|
||||
endif
|
||||
|
@ -14,7 +14,7 @@ endif
|
||||
|
||||
# default to using littlefs2 on the virtual flash if no other fs was selected
|
||||
ifneq (,$(filter vfs_default,$(USEMODULE)))
|
||||
ifeq (,$(filter spiffs littlefs fatfs_vfs,$(USEMODULE)))
|
||||
ifeq (,$(filter lwext%_vfs spiffs littlefs fatfs_vfs,$(USEMODULE)))
|
||||
USEMODULE += littlefs2
|
||||
endif
|
||||
USEMODULE += mtd
|
||||
|
@ -35,7 +35,7 @@ mtd_native_dev_t mtd0_dev = {
|
||||
mtd_dev_t *mtd0 = &mtd0_dev.base;
|
||||
#endif
|
||||
|
||||
#ifdef MODULE_VFS
|
||||
#ifdef MODULE_VFS_DEFAULT
|
||||
#include "vfs_default.h"
|
||||
|
||||
/*
|
||||
@ -62,8 +62,12 @@ VFS_AUTO_MOUNT(spiffs, VFS_MTD(mtd0_dev), VFS_DEFAULT_NVM(0), 0);
|
||||
#elif defined(MODULE_FATFS_VFS)
|
||||
VFS_AUTO_MOUNT(fatfs, VFS_MTD(mtd0_dev), VFS_DEFAULT_NVM(0), 0);
|
||||
|
||||
/* ext2/3/4 support */
|
||||
#elif defined(MODULE_LWEXT4)
|
||||
VFS_AUTO_MOUNT(lwext4, VFS_MTD(mtd0_dev), VFS_DEFAULT_NVM(0), 0);
|
||||
|
||||
#endif
|
||||
#endif /* MODULE_VFS */
|
||||
#endif /* MODULE_VFS_DEFAULT */
|
||||
|
||||
/**
|
||||
* Nothing to initialize at the moment.
|
||||
|
@ -60,22 +60,26 @@ void _native_LED_RED_TOGGLE(void);
|
||||
* @{
|
||||
*/
|
||||
#ifndef MTD_PAGE_SIZE
|
||||
#ifdef MODULE_FATFS
|
||||
#if defined(MODULE_FATFS) || defined(MODULE_LWEXT4)
|
||||
#define MTD_PAGE_SIZE (512)
|
||||
#else
|
||||
#define MTD_PAGE_SIZE (256)
|
||||
#endif
|
||||
#endif
|
||||
#ifndef MTD_SECTOR_SIZE
|
||||
#ifdef MODULE_FATFS
|
||||
#if defined(MODULE_FATFS) || defined(MODULE_LWEXT4)
|
||||
#define MTD_SECTOR_SIZE (512)
|
||||
#else
|
||||
#define MTD_SECTOR_SIZE (4096)
|
||||
#endif
|
||||
#endif
|
||||
#ifndef MTD_SECTOR_NUM
|
||||
#if defined(MODULE_FATFS) || defined(MODULE_LWEXT4)
|
||||
#define MTD_SECTOR_NUM (32768)
|
||||
#else
|
||||
#define MTD_SECTOR_NUM (2048)
|
||||
#endif
|
||||
#endif
|
||||
/** Advertised write size. While the file system backend supports single byte
|
||||
* granularity, this can be increased to mimic other media. */
|
||||
#ifndef MTD_WRITE_SIZE
|
||||
|
@ -21,6 +21,9 @@ endif
|
||||
# default to using littlefs2 on the external flash and fatfs on SD card
|
||||
ifneq (,$(filter vfs_default,$(USEMODULE)))
|
||||
USEPKG += littlefs2
|
||||
USEMODULE += fatfs_vfs
|
||||
# if ext2/3/4 is used, don't use FAT
|
||||
ifeq (,$(filter lwext%_vfs,$(USEMODULE)))
|
||||
USEMODULE += fatfs_vfs
|
||||
endif
|
||||
USEMODULE += mtd
|
||||
endif
|
||||
|
@ -87,6 +87,12 @@ static mtd_sam0_sdhc_t sdhc_dev = {
|
||||
mtd_dev_t *mtd2 = (mtd_dev_t *)&sdhc_dev;
|
||||
|
||||
#ifdef MODULE_VFS_DEFAULT
|
||||
/* default to FAT */
|
||||
#if defined(MODULE_FATFS_VFS)
|
||||
VFS_AUTO_MOUNT(fatfs, VFS_MTD(sdhc_dev), VFS_DEFAULT_SD(0), 1);
|
||||
/* but also support ext2/3/4 */
|
||||
#elif defined(MODULE_LWEXT4)
|
||||
VFS_AUTO_MOUNT(lwext4, VFS_MTD(sdhc_dev), VFS_DEFAULT_SD(0), 1);
|
||||
#endif
|
||||
#endif /* MODULE_VFS_DEFAULT */
|
||||
#endif /* MODULE_SAM0_SDHC */
|
||||
|
@ -273,6 +273,11 @@ PSEUDOMODULES += l2filter_whitelist
|
||||
PSEUDOMODULES += libstdcpp
|
||||
PSEUDOMODULES += log
|
||||
PSEUDOMODULES += lora
|
||||
PSEUDOMODULES += lwext4_no_gpl
|
||||
PSEUDOMODULES += lwext2_vfs
|
||||
PSEUDOMODULES += lwext3_vfs
|
||||
PSEUDOMODULES += lwext4_vfs
|
||||
PSEUDOMODULES += lwext4_vfs_format
|
||||
## @defgroup pseudomodule_libc_gettimeofday libc_gettimeofday
|
||||
## @brief Includes implementation of gettimeofday()
|
||||
##
|
||||
|
13
pkg/lwext4/Makefile
Normal file
13
pkg/lwext4/Makefile
Normal file
@ -0,0 +1,13 @@
|
||||
PKG_NAME=lwext4
|
||||
PKG_URL=https://github.com/gkostka/lwext4.git
|
||||
PKG_VERSION=58bcf89a121b72d4fb66334f1693d3b30e4cb9c5
|
||||
PKG_LICENSE=GPLv2
|
||||
|
||||
CFLAGS += -Wno-cast-align
|
||||
CFLAGS += -Wno-error=format
|
||||
CFLAGS += -Wno-unused-parameter -Wno-unused-variable
|
||||
|
||||
include $(RIOTBASE)/pkg/pkg.mk
|
||||
|
||||
all:
|
||||
$(QQ)"$(MAKE)" -C $(PKG_SOURCE_DIR)/src -f $(RIOTBASE)/Makefile.base MODULE=$(PKG_NAME)
|
5
pkg/lwext4/Makefile.dep
Normal file
5
pkg/lwext4/Makefile.dep
Normal file
@ -0,0 +1,5 @@
|
||||
USEMODULE += lwext_fs
|
||||
|
||||
ifneq (,$(filter vfs_auto_format,$(USEMODULE)))
|
||||
DEFAULT_MODULE += lwext4_vfs_format
|
||||
endif
|
23
pkg/lwext4/Makefile.include
Normal file
23
pkg/lwext4/Makefile.include
Normal file
@ -0,0 +1,23 @@
|
||||
INCLUDES += -I$(PKGDIRBASE)/lwext4/include
|
||||
|
||||
DIRS += $(RIOTPKG)/lwext4/fs
|
||||
|
||||
CFLAGS += -DCONFIG_USE_DEFAULT_CFG=1
|
||||
CFLAGS += -DCONFIG_HAVE_OWN_OFLAGS=0
|
||||
|
||||
# select ext2/3/4 feature level based on module name
|
||||
ifneq (,$(filter lwext4_vfs,$(USEMODULE)))
|
||||
CFLAGS += -DCONFIG_EXT_FEATURE_SET_LVL=F_SET_EXT4
|
||||
endif
|
||||
ifneq (,$(filter lwext3_vfs,$(USEMODULE)))
|
||||
CFLAGS += -DCONFIG_EXT_FEATURE_SET_LVL=F_SET_EXT3
|
||||
endif
|
||||
ifneq (,$(filter lwext2_vfs,$(USEMODULE)))
|
||||
CFLAGS += -DCONFIG_EXT_FEATURE_SET_LVL=F_SET_EXT2
|
||||
endif
|
||||
|
||||
# Disable GPL-only features
|
||||
ifneq (,$(filter lwext4_no_gpl,$(USEMODULE)))
|
||||
CFLAGS += -DCONFIG_EXTENTS_ENABLE=0
|
||||
CFLAGS += -DCONFIG_XATTR_ENABLE=0
|
||||
endif
|
19
pkg/lwext4/doc.txt
Normal file
19
pkg/lwext4/doc.txt
Normal file
@ -0,0 +1,19 @@
|
||||
/**
|
||||
* @defgroup pkg_lwext4 lightweight ext2/3/4 implementation
|
||||
* @ingroup pkg
|
||||
* @ingroup sys_fs
|
||||
* @brief Provides a lightweight implementation of the ext2/3/4
|
||||
* filesystem with optional journaling transactions & recovery
|
||||
*
|
||||
* Lwext4 is an excellent choice for SD/MMC card, USB flash drive
|
||||
* or any other wear leveled memory types. However it is not good
|
||||
* for raw flash devices.
|
||||
*
|
||||
* Some of the source files are licensed under GPLv2.
|
||||
* It makes whole lwext4 GPLv2 licensed. To use library as a BSD3,
|
||||
* GPLv2 licensed source files must be disabled.
|
||||
* To do so, enable the `lwext4_no_gpl` module.
|
||||
* This will disable support for extends and extended attributes.
|
||||
*
|
||||
* @see https://github.com/gkostka/lwext4
|
||||
*/
|
3
pkg/lwext4/fs/Makefile
Normal file
3
pkg/lwext4/fs/Makefile
Normal file
@ -0,0 +1,3 @@
|
||||
MODULE := lwext_fs
|
||||
|
||||
include $(RIOTBASE)/Makefile.base
|
519
pkg/lwext4/fs/lwext4_fs.c
Normal file
519
pkg/lwext4/fs/lwext4_fs.c
Normal file
@ -0,0 +1,519 @@
|
||||
/*
|
||||
* Copyright (C) 2022 ML!PA Consulting GmbH
|
||||
*
|
||||
* 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_lwext4
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
* @brief lwEXT4 integration with vfs
|
||||
*
|
||||
* @author Benjamin Valentin <benjamin.valentin@ml-pa.com>
|
||||
*
|
||||
* @}
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <inttypes.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "fs/lwext4_fs.h"
|
||||
#include <ext4_super.h>
|
||||
#include <ext4_mkfs.h>
|
||||
|
||||
#define ENABLE_DEBUG 0
|
||||
#include <debug.h>
|
||||
|
||||
/**
|
||||
* @brief file system block size to use when formatting a new file system
|
||||
*/
|
||||
#ifndef CONFIG_EXT_BLOCKSIZE_DEFAULT
|
||||
#define CONFIG_EXT_BLOCKSIZE_DEFAULT (1024)
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Automatic mountpoints
|
||||
*/
|
||||
XFA_USE(vfs_mount_t, vfs_mountpoints_xfa);
|
||||
|
||||
/**
|
||||
* @brief Number of automatic mountpoints
|
||||
*/
|
||||
#define MOUNTPOINTS_NUMOF XFA_LEN(vfs_mount_t, vfs_mountpoints_xfa)
|
||||
|
||||
struct ext4_mountpoint *ext4_get_mount(const char *path)
|
||||
{
|
||||
size_t strlen_path = strlen(path);
|
||||
for (unsigned i = 0; i < MOUNTPOINTS_NUMOF; ++i) {
|
||||
/* lwext4 wants terminating '/' for mountpoint, but VFS does not */
|
||||
size_t strlen_mp = strlen(vfs_mountpoints_xfa[i].mount_point) - 1;
|
||||
|
||||
if (strlen_mp < strlen_path &&
|
||||
strncmp(path, vfs_mountpoints_xfa[i].mount_point, strlen_mp) == 0) {
|
||||
lwext4_desc_t *fs = vfs_mountpoints_xfa[i].private_data;
|
||||
return &fs->mp;
|
||||
}
|
||||
}
|
||||
|
||||
DEBUG("lwext4: no mountpoint found for '%s'\n", path);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int _noop(struct ext4_blockdev *bdev)
|
||||
{
|
||||
(void)bdev;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int blockdev_bread(struct ext4_blockdev *bdev, void *buf, uint64_t blk_id,
|
||||
uint32_t blk_cnt)
|
||||
{
|
||||
mtd_dev_t *dev = bdev->bdif->p_user;
|
||||
|
||||
uint32_t page = blk_id * dev->pages_per_sector;
|
||||
uint32_t size = blk_cnt * dev->pages_per_sector * dev->page_size;
|
||||
|
||||
assert(blk_id <= UINT32_MAX);
|
||||
|
||||
DEBUG("lwext4: read %"PRIu32" bytes from page %"PRIu32"\n", size, page);
|
||||
|
||||
return -mtd_read_page(dev, buf, page, 0, size);
|
||||
}
|
||||
|
||||
static int blockdev_bwrite(struct ext4_blockdev *bdev, const void *buf,
|
||||
uint64_t blk_id, uint32_t blk_cnt)
|
||||
{
|
||||
mtd_dev_t *dev = bdev->bdif->p_user;
|
||||
|
||||
uint32_t page = blk_id * dev->pages_per_sector;
|
||||
uint32_t size = blk_cnt * dev->pages_per_sector * dev->page_size;
|
||||
|
||||
assert(blk_id <= UINT32_MAX);
|
||||
|
||||
DEBUG("lwext4: erase %"PRIu32" sectors starting with %"PRIu64"\n", blk_cnt, blk_id);
|
||||
int res = mtd_erase_sector(dev, blk_id, blk_cnt);
|
||||
if (res) {
|
||||
return -res;
|
||||
}
|
||||
|
||||
DEBUG("lwext4: write %"PRIu32" bytes to page %"PRIu32"\n", size, page);
|
||||
|
||||
return -mtd_write_page_raw(dev, buf, page, 0, size);
|
||||
}
|
||||
|
||||
static int prepare(lwext4_desc_t *fs, const char *mount_point)
|
||||
{
|
||||
mtd_dev_t *dev = fs->dev;
|
||||
struct ext4_blockdev_iface *iface = &fs->iface;
|
||||
|
||||
memset(&fs->mp, 0, sizeof(fs->mp));
|
||||
memset(&fs->bdev, 0, sizeof(fs->bdev));
|
||||
memset(&fs->iface, 0, sizeof(fs->iface));
|
||||
|
||||
strncpy(fs->mp.name, mount_point, CONFIG_EXT4_MAX_MP_NAME);
|
||||
strcat(fs->mp.name, "/");
|
||||
|
||||
int res = mtd_init(dev);
|
||||
if (res) {
|
||||
return res;
|
||||
}
|
||||
|
||||
iface->open = _noop;
|
||||
iface->bread = blockdev_bread;
|
||||
iface->bwrite = blockdev_bwrite;
|
||||
iface->close = _noop;
|
||||
iface->lock = _noop;
|
||||
iface->unlock = _noop;
|
||||
|
||||
iface->p_user = dev;
|
||||
iface->ph_bcnt = dev->sector_count;
|
||||
iface->ph_bsize = dev->pages_per_sector * dev->page_size;
|
||||
iface->ph_bbuf = malloc(iface->ph_bsize);
|
||||
|
||||
fs->bdev.bdif = iface;
|
||||
fs->bdev.part_size = iface->ph_bcnt * iface->ph_bsize;
|
||||
|
||||
return -ext4_block_init(&fs->bdev);
|
||||
}
|
||||
|
||||
static mutex_t _lwext4_mutex;
|
||||
static void _lock(void)
|
||||
{
|
||||
mutex_lock(&_lwext4_mutex);
|
||||
}
|
||||
|
||||
static void _unlock(void)
|
||||
{
|
||||
mutex_unlock(&_lwext4_mutex);
|
||||
}
|
||||
|
||||
static const struct ext4_lock _lwext4_os_lock = {
|
||||
.lock = _lock,
|
||||
.unlock = _unlock,
|
||||
};
|
||||
|
||||
static int _mount(vfs_mount_t *mountp)
|
||||
{
|
||||
/* if one of the lines below fail to compile you probably need to adjust
|
||||
vfs buffer sizes ;) */
|
||||
static_assert(VFS_DIR_BUFFER_SIZE >= sizeof(ext4_dir),
|
||||
"ext4_dir must fit in VFS_DIR_BUFFER_SIZE");
|
||||
static_assert(VFS_FILE_BUFFER_SIZE >= sizeof(ext4_file),
|
||||
"ext4_file must fit in VFS_FILE_BUFFER_SIZE");
|
||||
|
||||
lwext4_desc_t *fs = mountp->private_data;
|
||||
struct ext4_mountpoint *mp = &fs->mp;
|
||||
struct ext4_bcache *bc = &mp->bc;
|
||||
|
||||
if (mp->mounted) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int res = prepare(fs, mountp->mount_point);
|
||||
if (res) {
|
||||
return res;
|
||||
}
|
||||
|
||||
res = ext4_fs_init(&mp->fs, &fs->bdev, false);
|
||||
DEBUG("lwext4 mount: %s\n", strerror(res));
|
||||
|
||||
if (res != EOK) {
|
||||
return -res;
|
||||
}
|
||||
|
||||
size_t bsize = ext4_sb_get_block_size(&mp->fs.sb);
|
||||
ext4_block_set_lb_size(&fs->bdev, bsize);
|
||||
|
||||
res = ext4_bcache_init_dynamic(bc, CONFIG_BLOCK_DEV_CACHE_SIZE, bsize);
|
||||
if (res != EOK) {
|
||||
return -res;
|
||||
}
|
||||
|
||||
assert(bsize == bc->itemsize);
|
||||
|
||||
bc->bdev = &fs->bdev;
|
||||
fs->bdev.bc = bc;
|
||||
fs->bdev.fs = &mp->fs;
|
||||
|
||||
/*Bind block cache to block device*/
|
||||
res = ext4_block_bind_bcache(bc->bdev, bc);
|
||||
if (res != EOK) {
|
||||
ext4_bcache_cleanup(bc);
|
||||
ext4_block_fini(bc->bdev);
|
||||
ext4_bcache_fini_dynamic(bc);
|
||||
return -res;
|
||||
}
|
||||
|
||||
mp->os_locks = &_lwext4_os_lock;
|
||||
mp->mounted = true;
|
||||
|
||||
res = ext4_recover(fs->mp.name);
|
||||
if (res != EOK && res != ENOTSUP) {
|
||||
DEBUG("ext4_recover: rc = %d\n", res);
|
||||
return -res;
|
||||
}
|
||||
|
||||
res = ext4_journal_start(fs->mp.name);
|
||||
if (res != EOK) {
|
||||
DEBUG("ext4_journal_start: rc = %d\n", res);
|
||||
return -res;
|
||||
}
|
||||
|
||||
ext4_cache_write_back(fs->mp.name, 1);
|
||||
|
||||
return -res;
|
||||
}
|
||||
|
||||
static int _umount(vfs_mount_t *mountp)
|
||||
{
|
||||
lwext4_desc_t *fs = mountp->private_data;
|
||||
struct ext4_mountpoint *mp = &fs->mp;
|
||||
|
||||
int res;
|
||||
|
||||
ext4_cache_write_back(fs->mp.name, 0);
|
||||
|
||||
res = ext4_journal_stop(fs->mp.name);
|
||||
if (res != EOK) {
|
||||
DEBUG("ext4_journal_stop: fail %d", res);
|
||||
return false;
|
||||
}
|
||||
|
||||
res = ext4_fs_fini(&mp->fs);
|
||||
if (res != EOK) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
ext4_bcache_cleanup(mp->fs.bdev->bc);
|
||||
ext4_bcache_fini_dynamic(mp->fs.bdev->bc);
|
||||
|
||||
res = ext4_block_fini(mp->fs.bdev);
|
||||
|
||||
if (fs->iface.ph_bbuf) {
|
||||
free(fs->iface.ph_bbuf);
|
||||
fs->iface.ph_bbuf = NULL;
|
||||
}
|
||||
|
||||
out:
|
||||
mp->mounted = false;
|
||||
mp->fs.bdev->fs = NULL;
|
||||
|
||||
return -res;
|
||||
}
|
||||
|
||||
#ifdef MODULE_LWEXT4_VFS_FORMAT
|
||||
static int _format(vfs_mount_t *mountp)
|
||||
{
|
||||
lwext4_desc_t *fs = mountp->private_data;
|
||||
struct ext4_mountpoint *mp = &fs->mp;
|
||||
|
||||
if (mp->mounted) {
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
int res = prepare(fs, mountp->mount_point);
|
||||
if (res) {
|
||||
return res;
|
||||
}
|
||||
|
||||
struct ext4_mkfs_info info = {
|
||||
.block_size = CONFIG_EXT_BLOCKSIZE_DEFAULT,
|
||||
.journal = CONFIG_JOURNALING_ENABLE,
|
||||
};
|
||||
assert(fs->dev->pages_per_sector * fs->dev->page_size <= info.block_size);
|
||||
|
||||
res = ext4_mkfs(&mp->fs, &fs->bdev, &info, CONFIG_EXT_FEATURE_SET_LVL);
|
||||
|
||||
free(fs->iface.ph_bbuf);
|
||||
fs->iface.ph_bbuf = NULL;
|
||||
|
||||
return -res;
|
||||
}
|
||||
#endif /* MODULE_LWEXT4_VFS_FORMAT */
|
||||
|
||||
static int _mkdir(vfs_mount_t *mountp, const char *name, mode_t mode)
|
||||
{
|
||||
(void)mountp;
|
||||
|
||||
int res = ext4_dir_mk(name);
|
||||
if (res != EOK) {
|
||||
return -res;
|
||||
}
|
||||
|
||||
if (mode) {
|
||||
return -ext4_mode_set(name, mode);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int _rmdir(vfs_mount_t *mountp, const char *name)
|
||||
{
|
||||
(void)mountp;
|
||||
|
||||
return -ext4_dir_rm(name);
|
||||
}
|
||||
|
||||
static int _statvfs(vfs_mount_t *mountp, const char *restrict path, struct statvfs *restrict buf)
|
||||
{
|
||||
(void)path;
|
||||
|
||||
struct ext4_mount_stats stats;
|
||||
lwext4_desc_t *fs = mountp->private_data;
|
||||
|
||||
int res = ext4_mount_point_stats(fs->mp.name, &stats);
|
||||
if (res) {
|
||||
return -res;
|
||||
}
|
||||
|
||||
buf->f_blocks = stats.blocks_count;
|
||||
buf->f_bfree = stats.free_blocks_count;
|
||||
buf->f_bavail = stats.free_blocks_count;
|
||||
buf->f_bsize = stats.block_size;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline ext4_file * _get_ext4_file(vfs_file_t *f)
|
||||
{
|
||||
/* The buffer in `private_data` is part of a union that also contains a
|
||||
* pointer, so the alignment is fine. Adding an intermediate cast to
|
||||
* uintptr_t to silence -Wcast-align
|
||||
*/
|
||||
return (ext4_file *)(uintptr_t)f->private_data.buffer;
|
||||
}
|
||||
|
||||
static int _open(vfs_file_t *filp, const char *name, int flags, mode_t mode)
|
||||
{
|
||||
int res;
|
||||
|
||||
ext4_file *file = _get_ext4_file(filp);
|
||||
|
||||
res = ext4_fopen2(file, name, flags);
|
||||
if (res != EOK) {
|
||||
return -res;
|
||||
}
|
||||
|
||||
file->mp = ext4_get_mount(name);
|
||||
|
||||
if (mode && (flags & O_CREAT)) {
|
||||
return -ext4_mode_set(name, mode);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t _read(vfs_file_t *filp, void *dest, size_t nbytes)
|
||||
{
|
||||
ext4_file *file = _get_ext4_file(filp);
|
||||
|
||||
int res = ext4_fread(file, dest, nbytes, &nbytes);
|
||||
|
||||
if (res != EOK) {
|
||||
return -res;
|
||||
} else {
|
||||
return nbytes;
|
||||
}
|
||||
}
|
||||
|
||||
static ssize_t _write(vfs_file_t *filp, const void *src, size_t nbytes)
|
||||
{
|
||||
ext4_file *file = _get_ext4_file(filp);
|
||||
|
||||
int res = ext4_fwrite(file, src, nbytes, &nbytes);
|
||||
|
||||
if (res != EOK) {
|
||||
return -res;
|
||||
} else {
|
||||
return nbytes;
|
||||
}
|
||||
}
|
||||
|
||||
static off_t _lseek(vfs_file_t *filp, off_t off, int whence)
|
||||
{
|
||||
ext4_file *file = _get_ext4_file(filp);
|
||||
int res = ext4_fseek(file, off, whence);
|
||||
if (res) {
|
||||
return -res;
|
||||
}
|
||||
|
||||
return ext4_ftell(file);
|
||||
}
|
||||
|
||||
static int _fstat(vfs_file_t *filp, struct stat *buf)
|
||||
{
|
||||
ext4_file *file = _get_ext4_file(filp);
|
||||
|
||||
buf->st_ino = file->inode;
|
||||
buf->st_size = file->fsize;
|
||||
buf->st_mode = S_IFREG;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int _fsync(vfs_file_t *filp)
|
||||
{
|
||||
return -ext4_cache_flush(filp->mp->mount_point);
|
||||
}
|
||||
|
||||
static int _close(vfs_file_t *filp)
|
||||
{
|
||||
ext4_file *file = _get_ext4_file(filp);
|
||||
|
||||
return ext4_fclose(file);
|
||||
}
|
||||
|
||||
static int _unlink(vfs_mount_t *mountp, const char *name)
|
||||
{
|
||||
(void)mountp;
|
||||
return -ext4_fremove(name);
|
||||
}
|
||||
|
||||
static inline ext4_dir *_get_ext4_dir(vfs_DIR *dirp)
|
||||
{
|
||||
return (ext4_dir *)(uintptr_t)dirp->private_data.buffer;
|
||||
}
|
||||
|
||||
static int _rename(vfs_mount_t *mountp, const char *from_path, const char *to_path)
|
||||
{
|
||||
(void)mountp;
|
||||
return -ext4_frename(from_path, to_path);
|
||||
}
|
||||
|
||||
static int _opendir(vfs_DIR *dirp, const char *dirname)
|
||||
{
|
||||
ext4_dir *dir = _get_ext4_dir(dirp);
|
||||
|
||||
/* lwext4 doesn't like an empty path (relative to mount point) */
|
||||
if (strcmp(dirp->mp->mount_point, dirname) == 0) {
|
||||
lwext4_desc_t *fs = dirp->mp->private_data;
|
||||
dirname = fs->mp.name;
|
||||
}
|
||||
|
||||
return -ext4_dir_open(dir, dirname);
|
||||
}
|
||||
|
||||
static int _readdir(vfs_DIR *dirp, vfs_dirent_t *entry)
|
||||
{
|
||||
ext4_dir *dir = _get_ext4_dir(dirp);
|
||||
|
||||
const ext4_direntry *dirent = ext4_dir_entry_next(dir);
|
||||
if (dirent == NULL) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
strncpy(entry->d_name, (char *)dirent->name, sizeof(entry->d_name));
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int _closedir(vfs_DIR *dirp)
|
||||
{
|
||||
ext4_dir *dir = _get_ext4_dir(dirp);
|
||||
|
||||
return -ext4_dir_close(dir);
|
||||
}
|
||||
|
||||
static const vfs_file_system_ops_t lwext4_fs_ops = {
|
||||
#ifdef MODULE_LWEXT4_VFS_FORMAT
|
||||
.format = _format,
|
||||
#endif
|
||||
.mount = _mount,
|
||||
.umount = _umount,
|
||||
.unlink = _unlink,
|
||||
.mkdir = _mkdir,
|
||||
.rmdir = _rmdir,
|
||||
.rename = _rename,
|
||||
.stat = vfs_sysop_stat_from_fstat,
|
||||
.statvfs = _statvfs,
|
||||
};
|
||||
|
||||
static const vfs_file_ops_t lwext4_file_ops = {
|
||||
.open = _open,
|
||||
.close = _close,
|
||||
.read = _read,
|
||||
.write = _write,
|
||||
.lseek = _lseek,
|
||||
.fstat = _fstat,
|
||||
.fsync = _fsync,
|
||||
};
|
||||
|
||||
static const vfs_dir_ops_t lwext4_dir_ops = {
|
||||
.opendir = _opendir,
|
||||
.readdir = _readdir,
|
||||
.closedir = _closedir,
|
||||
};
|
||||
|
||||
const vfs_file_system_t lwext4_file_system = {
|
||||
.fs_op = &lwext4_fs_ops,
|
||||
.f_op = &lwext4_file_ops,
|
||||
.d_op = &lwext4_dir_ops,
|
||||
.flags = VFS_FS_FLAG_WANT_ABS_PATH,
|
||||
};
|
BIN
pkg/lwext4/patches/0001-make-struct-ext4_mountpoint-public.patch
Normal file
BIN
pkg/lwext4/patches/0001-make-struct-ext4_mountpoint-public.patch
Normal file
Binary file not shown.
58
sys/include/fs/lwext4_fs.h
Normal file
58
sys/include/fs/lwext4_fs.h
Normal file
@ -0,0 +1,58 @@
|
||||
/*
|
||||
* Copyright (C) 2022 ML!PA Consulting GmbH
|
||||
*
|
||||
* 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_lwext4 lwEXT4 integration
|
||||
* @ingroup pkg_lwext4
|
||||
* @brief RIOT integration of lwEXT4
|
||||
*
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
* @brief lwext4 integration with vfs
|
||||
*
|
||||
* @author Benjamin Valentin <benjamin.valentin@ml-pa.com>
|
||||
*/
|
||||
|
||||
#ifndef FS_LWEXT4_FS_H
|
||||
#define FS_LWEXT4_FS_H
|
||||
|
||||
#include <stdalign.h>
|
||||
|
||||
#include "vfs.h"
|
||||
#include "mtd.h"
|
||||
#include "mutex.h"
|
||||
#include "ext4.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief lwext4 descriptor for vfs integration
|
||||
*/
|
||||
typedef struct {
|
||||
struct ext4_mountpoint mp; /**< lwext4 mountpoint struct */
|
||||
struct ext4_blockdev bdev; /**< lwext4 block device struct */
|
||||
struct ext4_blockdev_iface iface; /**< lwext4 block device interface */
|
||||
|
||||
mtd_dev_t *dev; /**< mtd device to use */
|
||||
mutex_t lock; /**< mutex */
|
||||
} lwext4_desc_t;
|
||||
|
||||
/**
|
||||
* @brief The littlefs vfs driver
|
||||
*/
|
||||
extern const vfs_file_system_t lwext4_file_system;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* FS_LWEXT4_FS_H */
|
||||
/** @} */
|
@ -89,8 +89,11 @@ extern "C" {
|
||||
#ifndef _MAX
|
||||
#define _MAX(a, b) ((a) > (b) ? (a) : (b))
|
||||
#endif
|
||||
#ifndef MAX4
|
||||
#define MAX4(a, b, c, d) _MAX(_MAX((a), (b)), _MAX((c),(d)))
|
||||
#ifndef MAX5
|
||||
/**
|
||||
* @brief MAX5 Function to get the largest of 5 values
|
||||
*/
|
||||
#define MAX5(a, b, c, d, e) _MAX(_MAX(_MAX((a), (b)), _MAX((c),(d))), (e))
|
||||
#endif
|
||||
/** @} */
|
||||
|
||||
@ -174,6 +177,19 @@ extern "C" {
|
||||
#endif
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @brief VFS parameters for lwext4
|
||||
* @{
|
||||
*/
|
||||
#if defined(MODULE_LWEXT4) || DOXYGEN
|
||||
#define LWEXT4_VFS_DIR_BUFFER_SIZE (308) /**< sizeof(ext4_dir) */
|
||||
#define LWEXT4_VFS_FILE_BUFFER_SIZE (32) /**< sizeof(ext4_file) */
|
||||
#else
|
||||
#define LWEXT4_VFS_DIR_BUFFER_SIZE (1)
|
||||
#define LWEXT4_VFS_FILE_BUFFER_SIZE (1)
|
||||
#endif
|
||||
/** @} */
|
||||
|
||||
#ifndef VFS_MAX_OPEN_FILES
|
||||
/**
|
||||
* @brief Maximum number of simultaneous open files
|
||||
@ -209,10 +225,11 @@ extern "C" {
|
||||
* @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 MAX4(FATFS_VFS_DIR_BUFFER_SIZE, \
|
||||
#define VFS_DIR_BUFFER_SIZE MAX5(FATFS_VFS_DIR_BUFFER_SIZE, \
|
||||
LITTLEFS_VFS_DIR_BUFFER_SIZE, \
|
||||
LITTLEFS2_VFS_DIR_BUFFER_SIZE, \
|
||||
SPIFFS_VFS_DIR_BUFFER_SIZE \
|
||||
SPIFFS_VFS_DIR_BUFFER_SIZE, \
|
||||
LWEXT4_VFS_DIR_BUFFER_SIZE \
|
||||
)
|
||||
#endif
|
||||
|
||||
@ -236,10 +253,11 @@ extern "C" {
|
||||
* @attention Put the check in the public header file (.h), do not put the check in the
|
||||
* implementation (.c) file.
|
||||
*/
|
||||
#define VFS_FILE_BUFFER_SIZE MAX4(FATFS_VFS_FILE_BUFFER_SIZE, \
|
||||
#define VFS_FILE_BUFFER_SIZE MAX5(FATFS_VFS_FILE_BUFFER_SIZE, \
|
||||
LITTLEFS_VFS_FILE_BUFFER_SIZE, \
|
||||
LITTLEFS2_VFS_FILE_BUFFER_SIZE,\
|
||||
SPIFFS_VFS_FILE_BUFFER_SIZE \
|
||||
SPIFFS_VFS_FILE_BUFFER_SIZE, \
|
||||
LWEXT4_VFS_FILE_BUFFER_SIZE \
|
||||
)
|
||||
#endif
|
||||
|
||||
@ -316,6 +334,11 @@ typedef struct vfs_mount_struct vfs_mount_t;
|
||||
*/
|
||||
extern const vfs_file_ops_t mtd_vfs_ops;
|
||||
|
||||
/**
|
||||
* @brief File system always wants the full VFS path
|
||||
*/
|
||||
#define VFS_FS_FLAG_WANT_ABS_PATH (1 << 0)
|
||||
|
||||
/**
|
||||
* @brief A file system driver
|
||||
*/
|
||||
@ -323,6 +346,7 @@ 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 */
|
||||
const uint32_t flags; /**< File system flags */
|
||||
} vfs_file_system_t;
|
||||
|
||||
/**
|
||||
|
@ -41,6 +41,9 @@
|
||||
#if IS_USED(MODULE_SPIFFS)
|
||||
#include "fs/spiffs_fs.h"
|
||||
#endif
|
||||
#if IS_USED(MODULE_LWEXT4)
|
||||
#include "fs/lwext4_fs.h"
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
|
@ -1085,8 +1085,13 @@ static inline int _find_mount(vfs_mount_t **mountpp, const char *name, const cha
|
||||
atomic_fetch_add(&mountp->open_files, 1);
|
||||
mutex_unlock(&_mount_mutex);
|
||||
*mountpp = mountp;
|
||||
|
||||
if (rel_path != NULL) {
|
||||
*rel_path = name + longest_match;
|
||||
if (mountp->fs->flags & VFS_FS_FLAG_WANT_ABS_PATH) {
|
||||
*rel_path = name;
|
||||
} else {
|
||||
*rel_path = name + longest_match;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user