fs: ext2: Implementation of basic operations
Included operations: - mount - unmount - mkfs - statvfs Signed-off-by: Franciszek Zdobylak <fzdobylak@antmicro.com>
This commit is contained in:
parent
d7bcac091c
commit
1eccf55102
13 changed files with 1396 additions and 0 deletions
45
include/zephyr/fs/ext2.h
Normal file
45
include/zephyr/fs/ext2.h
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
/*
|
||||
* Copyright (c) 2023 Antmicro <www.antmicro.com>
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#ifndef ZEPHYR_INCLUDE_FS_EXT2_H_
|
||||
#define ZEPHYR_INCLUDE_FS_EXT2_H_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
/** @brief Configuration used to format ext2 file system.
|
||||
*
|
||||
* If a field is set to 0 then default value is used.
|
||||
* (In volume name the first cell of an array must be 0 to use default value.)
|
||||
*
|
||||
* @param block_size Requested size of block.
|
||||
* @param fs_size Requested size of file system. If 0 then whole available memory is used.
|
||||
* @param bytes_per_inode Requested memory for one inode. It is used to calculate number of inodes
|
||||
* in created file system.
|
||||
* @param uuid UUID for created file system. Used when set_uuid is true.
|
||||
* @param volume_name Name for created file system.
|
||||
* @param set_uuid If true then UUID from that structure is used in created file system.
|
||||
* If false then UUID (ver4) is generated.
|
||||
*/
|
||||
struct ext2_cfg {
|
||||
uint32_t block_size;
|
||||
uint32_t fs_size; /* Number of blocks that we want to take. */
|
||||
uint32_t bytes_per_inode;
|
||||
uint8_t uuid[16];
|
||||
uint8_t volume_name[17]; /* If first byte is 0 then name ext2" is given. */
|
||||
bool set_uuid;
|
||||
};
|
||||
|
||||
#define FS_EXT2_DECLARE_DEFAULT_CONFIG(name) \
|
||||
static struct ext2_cfg name = { \
|
||||
.block_size = 1024, \
|
||||
.fs_size = 0x800000, \
|
||||
.bytes_per_inode = 4096, \
|
||||
.volume_name = {'e', 'x', 't', '2', '\0'}, \
|
||||
.set_uuid = false, \
|
||||
}
|
||||
|
||||
|
||||
#endif /* ZEPHYR_INCLUDE_FS_EXT2_H_ */
|
||||
|
|
@ -56,6 +56,9 @@ enum {
|
|||
/** Identifier for in-tree LittleFS file system. */
|
||||
FS_LITTLEFS,
|
||||
|
||||
/** Identifier for in-tree Ext2 file system. */
|
||||
FS_EXT2,
|
||||
|
||||
/** Base identifier for external file systems. */
|
||||
FS_TYPE_EXTERNAL_BASE,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -14,10 +14,13 @@ if(CONFIG_FILE_SYSTEM)
|
|||
LFS_CONFIG=zephyr_lfs_config.h
|
||||
)
|
||||
|
||||
add_subdirectory_ifdef(CONFIG_FILE_SYSTEM_EXT2 ext2)
|
||||
|
||||
zephyr_library_link_libraries(FS)
|
||||
|
||||
target_link_libraries_ifdef(CONFIG_FAT_FILESYSTEM_ELM FS INTERFACE ELMFAT)
|
||||
target_link_libraries_ifdef(CONFIG_FILE_SYSTEM_LITTLEFS FS INTERFACE LITTLEFS)
|
||||
target_link_libraries_ifdef(CONFIG_FILE_SYSTEM_EXT2 FS INTERFACE EXT2)
|
||||
endif()
|
||||
|
||||
add_subdirectory_ifdef(CONFIG_FCB ./fcb)
|
||||
|
|
|
|||
|
|
@ -85,6 +85,7 @@ config FUSE_FS_ACCESS
|
|||
|
||||
rsource "Kconfig.fatfs"
|
||||
rsource "Kconfig.littlefs"
|
||||
rsource "ext2/Kconfig"
|
||||
|
||||
endif # FILE_SYSTEM
|
||||
|
||||
|
|
|
|||
15
subsys/fs/ext2/CMakeLists.txt
Normal file
15
subsys/fs/ext2/CMakeLists.txt
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
# Copyright (c) 2023 Antmicro
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
add_library(EXT2 INTERFACE)
|
||||
target_include_directories(EXT2 INTERFACE ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
|
||||
zephyr_library()
|
||||
zephyr_library_sources(
|
||||
ext2_ops.c
|
||||
ext2_impl.c
|
||||
ext2_disk_access.c
|
||||
)
|
||||
zephyr_library_sources_ifdef(CONFIG_FILE_SYSTEM_MKFS ext2_format.c)
|
||||
|
||||
zephyr_library_link_libraries(EXT2)
|
||||
44
subsys/fs/ext2/Kconfig
Normal file
44
subsys/fs/ext2/Kconfig
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
# Copyright (c) 2023 Antmicro
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
# Ext2 file system
|
||||
|
||||
config FILE_SYSTEM_EXT2
|
||||
bool "Ext2 file system support"
|
||||
depends on FILE_SYSTEM
|
||||
help
|
||||
Enable Ext2 file system support.
|
||||
|
||||
module = EXT2
|
||||
module-str = Ext2
|
||||
source "subsys/logging/Kconfig.template.log_config"
|
||||
|
||||
if FILE_SYSTEM_EXT2
|
||||
|
||||
menu "Ext2 file system Settings"
|
||||
visible if FILE_SYSTEM_EXT2
|
||||
|
||||
config EXT2_MAX_BLOCK_SIZE
|
||||
int "Maximum size of supported block"
|
||||
range 1024 4096
|
||||
default 4096
|
||||
help
|
||||
This flag is used to determine size of internal structures that
|
||||
are used to store fetched blocks.
|
||||
|
||||
config EXT2_MAX_BLOCK_COUNT
|
||||
int "Maximum number of blocks that might be used"
|
||||
default 10
|
||||
help
|
||||
This flag is used to determine size of internal structures that
|
||||
are used to store fetched blocks.
|
||||
|
||||
config EXT2_DISK_STARTING_SECTOR
|
||||
int "Ext2 starting sector"
|
||||
default 0
|
||||
help
|
||||
The current Ext2 implementation does not support GUID Partition Table. The starting sector
|
||||
of the file system must be specified by this option.
|
||||
|
||||
endmenu
|
||||
endif
|
||||
77
subsys/fs/ext2/ext2.h
Normal file
77
subsys/fs/ext2/ext2.h
Normal file
|
|
@ -0,0 +1,77 @@
|
|||
/*
|
||||
* Copyright (c) 2023 Antmicro <www.antmicro.com>
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#ifndef __EXT2_H__
|
||||
#define __EXT2_H__
|
||||
|
||||
#define EXT2_SUPERBLOCK_OFFSET 1024
|
||||
#define EXT2_MAGIC_NUMBER 0xEF53
|
||||
#define EXT2_MAX_FILE_NAME 255
|
||||
#define EXT2_ROOT_INODE 2
|
||||
#define EXT2_RESERVED_INODES 10
|
||||
#define EXT2_GOOD_OLD_INODE_SIZE 128
|
||||
|
||||
#define EXT2_FEATURE_INCOMPAT_COMPRESSION 0x0001 /* Disk/File compression is used */
|
||||
#define EXT2_FEATURE_INCOMPAT_FILETYPE 0x0002 /* Directory entries record the file type */
|
||||
#define EXT3_FEATURE_INCOMPAT_RECOVER 0x0004 /* Filesystem needs recovery */
|
||||
#define EXT3_FEATURE_INCOMPAT_JOURNAL_DEV 0x0008 /* Filesystem has a separate journal device */
|
||||
#define EXT2_FEATURE_INCOMPAT_META_BG 0x0010 /* Meta block groups */
|
||||
|
||||
#define EXT2_FEATURE_INCOMPAT_SUPPORTED (EXT2_FEATURE_INCOMPAT_FILETYPE)
|
||||
|
||||
#define EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER 0x0001 /* Sparse Superblock */
|
||||
#define EXT2_FEATURE_RO_COMPAT_LARGE_FILE 0x0002 /* Large file support, 64-bit file size */
|
||||
#define EXT2_FEATURE_RO_COMPAT_BTREE_DIR 0x0004 /* Binary tree sorted directory files */
|
||||
|
||||
#define EXT2_FEATURE_RO_COMPAT_SUPPORTED (0)
|
||||
|
||||
#define EXT2_INODE_BLOCKS 15 /* number of blocks referenced by inode i_block field */
|
||||
#define EXT2_INODE_BLOCK_DIRECT 12
|
||||
#define EXT2_INODE_BLOCK_1LVL 12
|
||||
#define EXT2_INODE_BLOCK_2LVL 13
|
||||
#define EXT2_INODE_BLOCK_3LVL 14
|
||||
|
||||
/* Inode mode flags */
|
||||
#define EXT2_S_IFMT 0xF000 /* format mask */
|
||||
#define EXT2_S_IFSOCK 0xC000 /* socket */
|
||||
#define EXT2_S_IFLNK 0xA000 /* symbolic link */
|
||||
#define EXT2_S_IFREG 0x8000 /* regular file */
|
||||
#define EXT2_S_IFBLK 0x6000 /* block device */
|
||||
#define EXT2_S_IFDIR 0x4000 /* directory */
|
||||
#define EXT2_S_IFCHR 0x2000 /* character device */
|
||||
#define EXT2_S_IFIFO 0x1000 /* fifo */
|
||||
|
||||
#define IS_REG_FILE(mode) (((mode) & EXT2_S_IFMT) == EXT2_S_IFREG)
|
||||
#define IS_DIR(mode) (((mode) & EXT2_S_IFMT) == EXT2_S_IFDIR)
|
||||
|
||||
/* Directory file type flags */
|
||||
#define EXT2_FT_UNKNOWN 0
|
||||
#define EXT2_FT_REG_FILE 1
|
||||
#define EXT2_FT_DIR 2
|
||||
#define EXT2_FT_CHRDEV 3
|
||||
#define EXT2_FT_BLKDEV 4
|
||||
#define EXT2_FT_FIFO 5
|
||||
#define EXT2_FT_SOCK 6
|
||||
#define EXT2_FT_SYMLINK 7
|
||||
#define EXT2_FT_MAX 8
|
||||
|
||||
/* Superblock status flags.
|
||||
* When file system is mounted the status is set to EXT2_ERROR_FS.
|
||||
* When file system is cleanly unmounted then flag is reset to EXT2_VALID_FS.
|
||||
*/
|
||||
#define EXT2_VALID_FS 0x0001 /* Unmounted cleanly */
|
||||
#define EXT2_ERROR_FS 0x0002 /* Errors detected */
|
||||
|
||||
/* Revision flags. */
|
||||
#define EXT2_GOOD_OLD_REV 0x0 /* Revision 0 */
|
||||
#define EXT2_DYNAMIC_REV 0x1 /* Revision 1 */
|
||||
|
||||
/* Strategy when error detected. */
|
||||
#define EXT2_ERRORS_CONTINUE 1 /* Continue as if nothing happened. */
|
||||
#define EXT2_ERRORS_RO 2 /* Mount read only. */
|
||||
#define EXT2_ERRORS_PANIC 3 /* Cause kernel panic. */
|
||||
|
||||
#endif /* __EXT2_H__ */
|
||||
171
subsys/fs/ext2/ext2_disk_access.c
Normal file
171
subsys/fs/ext2/ext2_disk_access.c
Normal file
|
|
@ -0,0 +1,171 @@
|
|||
/*
|
||||
* Copyright (c) 2023 Antmicro <www.antmicro.com>
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <zephyr/logging/log.h>
|
||||
#include <zephyr/device.h>
|
||||
#include <zephyr/storage/disk_access.h>
|
||||
|
||||
#include "ext2.h"
|
||||
#include "ext2_struct.h"
|
||||
|
||||
LOG_MODULE_DECLARE(ext2);
|
||||
|
||||
static struct disk_data {
|
||||
const char *name;
|
||||
uint32_t sector_size;
|
||||
uint32_t sector_count;
|
||||
} disk_data;
|
||||
|
||||
static int64_t disk_access_device_size(struct ext2_data *fs)
|
||||
{
|
||||
struct disk_data *disk = fs->backend;
|
||||
|
||||
return disk->sector_count * disk->sector_size;
|
||||
}
|
||||
|
||||
static int64_t disk_access_write_size(struct ext2_data *fs)
|
||||
{
|
||||
struct disk_data *disk = fs->backend;
|
||||
|
||||
return disk->sector_size;
|
||||
}
|
||||
|
||||
static int disk_read(const char *disk, uint8_t *buf, uint32_t start, uint32_t num)
|
||||
{
|
||||
int rc, loop = 0;
|
||||
|
||||
do {
|
||||
rc = disk_access_ioctl(disk, DISK_IOCTL_CTRL_SYNC, NULL);
|
||||
if (rc == 0) {
|
||||
rc = disk_access_read(disk, buf, start, num);
|
||||
LOG_DBG("disk read: (start:%d, num:%d) (ret: %d)", start, num, rc);
|
||||
}
|
||||
} while ((rc == -EBUSY) && (loop++ < 16));
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int disk_write(const char *disk, const uint8_t *buf, uint32_t start, uint32_t num)
|
||||
{
|
||||
int rc, loop = 0;
|
||||
|
||||
do {
|
||||
rc = disk_access_ioctl(disk, DISK_IOCTL_CTRL_SYNC, NULL);
|
||||
if (rc == 0) {
|
||||
rc = disk_access_write(disk, buf, start, num);
|
||||
LOG_DBG("disk write: (start:%d, num:%d) (ret: %d)", start, num, rc);
|
||||
}
|
||||
} while ((rc == -EBUSY) && (loop++ < 16));
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int disk_prepare_range(struct disk_data *disk, uint32_t addr, uint32_t size,
|
||||
uint32_t *s_start, uint32_t *s_count)
|
||||
{
|
||||
*s_start = CONFIG_EXT2_DISK_STARTING_SECTOR + addr / disk->sector_size;
|
||||
*s_count = size / disk->sector_size;
|
||||
|
||||
LOG_DBG("addr:0x%x size:0x%x -> sector_start:%d sector_count:%d",
|
||||
addr, size, *s_start, *s_count);
|
||||
|
||||
/* Check for overflow. */
|
||||
if (*s_count > UINT32_MAX - *s_start) {
|
||||
LOG_ERR("Requested range (%d:+%d) can't be accessed due to overflow.",
|
||||
*s_start, *s_count);
|
||||
return -ENOSPC;
|
||||
}
|
||||
|
||||
/* Cannot read or write outside the disk. */
|
||||
if (*s_start + *s_count > disk->sector_count) {
|
||||
LOG_ERR("Requested sectors: %d-%d are outside of disk (num_sectors: %d)",
|
||||
*s_start, *s_start + *s_count, disk->sector_count);
|
||||
return -ENOSPC;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int disk_access_read_block(struct ext2_data *fs, void *buf, uint32_t block)
|
||||
{
|
||||
int rc;
|
||||
struct disk_data *disk = fs->backend;
|
||||
uint32_t sector_start, sector_count;
|
||||
|
||||
rc = disk_prepare_range(disk, block * fs->block_size, fs->block_size,
|
||||
§or_start, §or_count);
|
||||
if (rc < 0) {
|
||||
return rc;
|
||||
}
|
||||
return disk_read(disk->name, buf, sector_start, sector_count);
|
||||
}
|
||||
|
||||
static int disk_access_write_block(struct ext2_data *fs, const void *buf, uint32_t block)
|
||||
{
|
||||
int rc;
|
||||
struct disk_data *disk = fs->backend;
|
||||
uint32_t sector_start, sector_count;
|
||||
|
||||
rc = disk_prepare_range(disk, block * fs->block_size, fs->block_size,
|
||||
§or_start, §or_count);
|
||||
if (rc < 0) {
|
||||
return rc;
|
||||
}
|
||||
return disk_write(disk->name, buf, sector_start, sector_count);
|
||||
}
|
||||
|
||||
static int disk_access_read_superblock(struct ext2_data *fs, struct ext2_disk_superblock *sb)
|
||||
{
|
||||
int rc;
|
||||
struct disk_data *disk = fs->backend;
|
||||
uint32_t sector_start, sector_count;
|
||||
|
||||
rc = disk_prepare_range(disk, EXT2_SUPERBLOCK_OFFSET, sizeof(struct ext2_disk_superblock),
|
||||
§or_start, §or_count);
|
||||
if (rc < 0) {
|
||||
return rc;
|
||||
}
|
||||
return disk_read(disk->name, (uint8_t *)sb, sector_start, sector_count);
|
||||
}
|
||||
static const struct ext2_backend_ops disk_access_ops = {
|
||||
.get_device_size = disk_access_device_size,
|
||||
.get_write_size = disk_access_write_size,
|
||||
.read_block = disk_access_read_block,
|
||||
.write_block = disk_access_write_block,
|
||||
.read_superblock = disk_access_read_superblock,
|
||||
};
|
||||
|
||||
int ext2_init_disk_access_backend(struct ext2_data *fs, const void *storage_dev, int flags)
|
||||
{
|
||||
int rc;
|
||||
uint32_t sector_size, sector_count;
|
||||
const char *name = (const char *)storage_dev;
|
||||
|
||||
rc = disk_access_init(name);
|
||||
if (rc < 0) {
|
||||
LOG_ERR("FAIL: unable to find disk %s: %d\n", name, rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
rc = disk_access_ioctl(name, DISK_IOCTL_GET_SECTOR_COUNT, §or_count);
|
||||
if (rc < 0) {
|
||||
LOG_ERR("Disk access (sector count) error: %d", rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
rc = disk_access_ioctl(name, DISK_IOCTL_GET_SECTOR_SIZE, §or_size);
|
||||
if (rc < 0) {
|
||||
LOG_ERR("Disk access (sector size) error: %d", rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
disk_data = (struct disk_data) {
|
||||
.name = storage_dev,
|
||||
.sector_size = sector_size,
|
||||
.sector_count = sector_count,
|
||||
};
|
||||
|
||||
fs->backend = &disk_data;
|
||||
fs->backend_ops = &disk_access_ops;
|
||||
return 0;
|
||||
}
|
||||
295
subsys/fs/ext2/ext2_format.c
Normal file
295
subsys/fs/ext2/ext2_format.c
Normal file
|
|
@ -0,0 +1,295 @@
|
|||
/*
|
||||
* Copyright (c) 2023 Antmicro <www.antmicro.com>
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <zephyr/logging/log.h>
|
||||
#include <zephyr/random/rand32.h>
|
||||
#include <zephyr/fs/ext2.h>
|
||||
|
||||
#include "ext2.h"
|
||||
#include "ext2_impl.h"
|
||||
#include "ext2_struct.h"
|
||||
|
||||
LOG_MODULE_DECLARE(ext2);
|
||||
|
||||
FS_EXT2_DECLARE_DEFAULT_CONFIG(ext2_default);
|
||||
|
||||
static void validate_config(struct ext2_cfg *cfg)
|
||||
{
|
||||
if (cfg->block_size == 0) {
|
||||
cfg->block_size = ext2_default.block_size;
|
||||
}
|
||||
|
||||
if (cfg->bytes_per_inode == 0) {
|
||||
cfg->bytes_per_inode = ext2_default.bytes_per_inode;
|
||||
}
|
||||
|
||||
if (cfg->volume_name[0] == '\0') {
|
||||
strcpy(cfg->volume_name, "ext2");
|
||||
}
|
||||
|
||||
if (!cfg->set_uuid) {
|
||||
/* Generate random UUID */
|
||||
sys_rand_get(cfg->uuid, 16);
|
||||
|
||||
/* Set version of UUID (ver. 4 variant 1) */
|
||||
cfg->uuid[6] = (cfg->uuid[6] & 0x0f) | 0x40;
|
||||
cfg->uuid[8] = (cfg->uuid[8] & 0x3f) | 0x80;
|
||||
}
|
||||
}
|
||||
|
||||
int ext2_format(struct ext2_data *fs, struct ext2_cfg *cfg)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
validate_config(cfg);
|
||||
LOG_INF("[Config] blk_sz:%d fs_sz:%d ino_bytes:%d uuid:'%s' vol:'%s'",
|
||||
cfg->block_size, cfg->fs_size, cfg->bytes_per_inode, cfg->uuid,
|
||||
cfg->volume_name);
|
||||
|
||||
uint32_t fs_memory = cfg->fs_size ? MIN(cfg->fs_size, fs->device_size) : fs->device_size;
|
||||
|
||||
/* Calculate value that will be stored in superblock field 's_log_block_size'. That value
|
||||
* tells how much we have to shift 1024 to obtain block size.
|
||||
* To obtain it we calculate: log(block_size) - 11
|
||||
*/
|
||||
uint8_t block_log_size = find_msb_set(cfg->block_size) - 11;
|
||||
|
||||
LOG_INF("[Memory] available:%lld requested:%d", fs->device_size, fs_memory);
|
||||
|
||||
if (fs_memory > fs->device_size) {
|
||||
LOG_ERR("No enough space on storage device");
|
||||
return -ENOSPC;
|
||||
}
|
||||
|
||||
uint32_t blocks_count = fs_memory / cfg->block_size;
|
||||
uint32_t blocks_per_group = cfg->block_size * 8;
|
||||
|
||||
/* Require at least 24 blocks to have at least 1 block per inode */
|
||||
if (blocks_count < 24) {
|
||||
LOG_ERR("Storage device too small to fit ext2 file system");
|
||||
return -ENOSPC;
|
||||
}
|
||||
|
||||
/* We want to have only 1 block group (and one extra block) */
|
||||
if (blocks_count > blocks_per_group + 1) {
|
||||
LOG_ERR("File systems with more than 1 block group are not supported.");
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
|
||||
/* Superblock, group descriptor table, 2 x bitmap. */
|
||||
uint32_t reserved_blocks = 4;
|
||||
|
||||
uint32_t mem_for_inodes = fs_memory - reserved_blocks * cfg->block_size;
|
||||
uint32_t mem_per_inode = cfg->bytes_per_inode + sizeof(struct ext2_disk_inode);
|
||||
|
||||
uint32_t inodes_per_block = cfg->block_size / sizeof(struct ext2_disk_inode);
|
||||
uint32_t inodes_count = mem_for_inodes / mem_per_inode;
|
||||
|
||||
if (inodes_count % inodes_per_block) {
|
||||
/* Increase inodes_count to use entire blocks that are reserved for inode table. */
|
||||
inodes_count += inodes_per_block - (inodes_count % inodes_per_block);
|
||||
}
|
||||
|
||||
uint32_t itable_blocks = inodes_count / inodes_per_block;
|
||||
uint32_t reserved_inodes = 10;
|
||||
|
||||
/* Used blocks:
|
||||
* Reserved blocks:
|
||||
* 0 - boot sector
|
||||
* 1 - superblock
|
||||
* 2 - block group descriptor table
|
||||
* 3 - block bitmap
|
||||
* 4 - inode bitmap
|
||||
*
|
||||
* Inode table blocks:
|
||||
* 5-x - inode table (x = 5 + inode_count / inodes per block )
|
||||
*
|
||||
* Other used blocks:
|
||||
* x+1 - root dir
|
||||
*/
|
||||
uint32_t used_blocks = reserved_blocks + itable_blocks + 1;
|
||||
uint32_t free_blocks = blocks_count - used_blocks - 1; /* boot sector block also counts */
|
||||
|
||||
LOG_INF("[Blocks] total:%d per_grp:%d reserved:%d",
|
||||
blocks_count, blocks_per_group, used_blocks);
|
||||
LOG_INF("[Inodes] total:%d reserved:%d itable_blocks:%d",
|
||||
inodes_count, reserved_inodes, itable_blocks);
|
||||
|
||||
uint32_t sb_offset;
|
||||
uint32_t sb_block_num, bg_block_num, bbitmap_block_num, ibitmap_block_num, in1_block_num,
|
||||
root_dir_blk_num;
|
||||
|
||||
if (cfg->block_size == 1024) {
|
||||
sb_offset = 0;
|
||||
sb_block_num = 1;
|
||||
bg_block_num = 2;
|
||||
bbitmap_block_num = 3;
|
||||
ibitmap_block_num = 4;
|
||||
in1_block_num = 5;
|
||||
root_dir_blk_num = used_blocks; /* last used block */
|
||||
} else {
|
||||
sb_offset = 1024;
|
||||
sb_block_num = 0;
|
||||
bg_block_num = 1;
|
||||
bbitmap_block_num = 2;
|
||||
ibitmap_block_num = 3;
|
||||
in1_block_num = 4;
|
||||
root_dir_blk_num = used_blocks; /* last used block */
|
||||
}
|
||||
|
||||
struct ext2_block *sb_block = ext2_get_block(fs, sb_block_num);
|
||||
struct ext2_block *bg_block = ext2_get_block(fs, bg_block_num);
|
||||
struct ext2_block *bbitmap_block = ext2_get_block(fs, bbitmap_block_num);
|
||||
struct ext2_block *ibitmap_block = ext2_get_block(fs, ibitmap_block_num);
|
||||
struct ext2_block *in1_block = ext2_get_block(fs, in1_block_num);
|
||||
struct ext2_block *root_dir_blk_block = ext2_get_block(fs, root_dir_blk_num);
|
||||
|
||||
if (root_dir_blk_block == NULL || in1_block == NULL || ibitmap_block == NULL ||
|
||||
bbitmap_block == NULL || bg_block == NULL || sb_block == NULL) {
|
||||
ret = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
struct ext2_disk_superblock *sb =
|
||||
(struct ext2_disk_superblock *)((uint8_t *)sb_block->data + sb_offset);
|
||||
|
||||
memset(sb, 0, 1024);
|
||||
sb->s_inodes_count = inodes_count;
|
||||
sb->s_blocks_count = blocks_count;
|
||||
sb->s_r_blocks_count = 0;
|
||||
sb->s_free_blocks_count = free_blocks;
|
||||
sb->s_free_inodes_count = inodes_count - reserved_inodes;
|
||||
sb->s_first_data_block = sb_block_num;
|
||||
sb->s_log_block_size = block_log_size;
|
||||
sb->s_log_frag_size = block_log_size;
|
||||
sb->s_blocks_per_group = cfg->block_size * 8;
|
||||
sb->s_frags_per_group = cfg->block_size * 8;
|
||||
sb->s_inodes_per_group = inodes_count;
|
||||
sb->s_mtime = 0;
|
||||
sb->s_wtime = 0;
|
||||
sb->s_mnt_count = 0;
|
||||
sb->s_max_mnt_count = -1;
|
||||
sb->s_magic = 0xEF53;
|
||||
sb->s_state = EXT2_VALID_FS;
|
||||
sb->s_errors = EXT2_ERRORS_RO;
|
||||
sb->s_minor_rev_level = 0;
|
||||
sb->s_lastcheck = 0;
|
||||
sb->s_checkinterval = 0;
|
||||
sb->s_creator_os = 5; /* Unknown OS */
|
||||
sb->s_rev_level = EXT2_DYNAMIC_REV;
|
||||
sb->s_def_resuid = 0;
|
||||
sb->s_def_resgid = 0;
|
||||
sb->s_first_ino = 11;
|
||||
sb->s_inode_size = sizeof(struct ext2_disk_inode);
|
||||
sb->s_block_group_nr = 0;
|
||||
sb->s_feature_compat = 0;
|
||||
sb->s_feature_incompat = EXT2_FEATURE_INCOMPAT_FILETYPE;
|
||||
sb->s_feature_ro_compat = 0;
|
||||
|
||||
memcpy(sb->s_uuid, cfg->uuid, 16);
|
||||
memcpy(sb->s_volume_name, cfg->uuid, 16);
|
||||
|
||||
sb->s_algo_bitmap = 0;
|
||||
sb->s_prealloc_blocks = 0;
|
||||
sb->s_prealloc_dir_blocks = 0;
|
||||
sb->s_journal_inum = 0;
|
||||
sb->s_journal_dev = 0;
|
||||
sb->s_last_orphan = 0;
|
||||
|
||||
/* Block descriptor table */
|
||||
|
||||
struct ext2_disk_bgroup *bg = (struct ext2_disk_bgroup *)bg_block->data;
|
||||
|
||||
memset(bg, 0, cfg->block_size);
|
||||
bg->bg_block_bitmap = bbitmap_block_num;
|
||||
bg->bg_inode_bitmap = ibitmap_block_num;
|
||||
bg->bg_inode_table = in1_block_num;
|
||||
bg->bg_free_blocks_count = free_blocks;
|
||||
bg->bg_free_inodes_count = inodes_count - reserved_inodes;
|
||||
bg->bg_used_dirs_count = 1;
|
||||
|
||||
/* Inode table */
|
||||
struct ext2_disk_inode *in1 = (struct ext2_disk_inode *)in1_block->data;
|
||||
|
||||
memset(in1, 0, cfg->block_size);
|
||||
in1[1].i_mode = 0x4000 | 0755;
|
||||
in1[1].i_uid = 0;
|
||||
in1[1].i_size = cfg->block_size;
|
||||
in1[1].i_atime = 0;
|
||||
in1[1].i_ctime = 0;
|
||||
in1[1].i_mtime = 0;
|
||||
in1[1].i_dtime = 0;
|
||||
in1[1].i_gid = 0;
|
||||
in1[1].i_links_count = 2;
|
||||
in1[1].i_blocks = cfg->block_size / 512;
|
||||
in1[1].i_flags = 0;
|
||||
in1[1].i_osd1 = 0;
|
||||
in1[1].i_generation = 0;
|
||||
in1[1].i_file_acl = 0;
|
||||
in1[1].i_dir_acl = 0;
|
||||
in1[1].i_faddr = 0;
|
||||
in1[1].i_block[0] = root_dir_blk_num;
|
||||
|
||||
/* Block bitmap */
|
||||
|
||||
uint8_t *bbitmap = bbitmap_block->data;
|
||||
|
||||
memset(bbitmap, 0, cfg->block_size);
|
||||
|
||||
int i = 0, blocks = used_blocks;
|
||||
int bits;
|
||||
uint16_t to_set;
|
||||
|
||||
while (blocks > 0) {
|
||||
bits = MIN(8, blocks);
|
||||
to_set = (1 << bits) - 1;
|
||||
bbitmap[i] = (uint8_t)to_set;
|
||||
blocks -= 8;
|
||||
i++;
|
||||
}
|
||||
|
||||
/* Inode bitmap */
|
||||
|
||||
uint8_t *ibitmap = ibitmap_block->data;
|
||||
|
||||
memset(ibitmap, 0, cfg->block_size);
|
||||
ibitmap[0] = 0xff;
|
||||
ibitmap[1] = 0x03;
|
||||
|
||||
memset(root_dir_blk_block->data, 0, cfg->block_size);
|
||||
|
||||
struct ext2_disk_dentry *de = (struct ext2_disk_dentry *)root_dir_blk_block->data;
|
||||
|
||||
de->de_inode = 2;
|
||||
de->de_rec_len = sizeof(struct ext2_disk_dentry) + 4;
|
||||
de->de_name_len = 1;
|
||||
de->de_file_type = EXT2_FT_DIR;
|
||||
memset(de->de_name, '.', 1);
|
||||
|
||||
de = (struct ext2_disk_dentry *)(((uint8_t *)de) + de->de_rec_len);
|
||||
de->de_inode = 2;
|
||||
de->de_rec_len = cfg->block_size - 12;
|
||||
de->de_name_len = 2;
|
||||
de->de_file_type = EXT2_FT_DIR;
|
||||
memset(de->de_name, '.', 2);
|
||||
|
||||
ext2_sync_block(fs, sb_block);
|
||||
ext2_sync_block(fs, bg_block);
|
||||
ext2_sync_block(fs, in1_block);
|
||||
ext2_sync_block(fs, bbitmap_block);
|
||||
ext2_sync_block(fs, ibitmap_block);
|
||||
ext2_sync_block(fs, root_dir_blk_block);
|
||||
|
||||
out:
|
||||
ext2_drop_block(sb_block);
|
||||
ext2_drop_block(bg_block);
|
||||
ext2_drop_block(in1_block);
|
||||
ext2_drop_block(bbitmap_block);
|
||||
ext2_drop_block(ibitmap_block);
|
||||
ext2_drop_block(root_dir_blk_block);
|
||||
return ret;
|
||||
}
|
||||
291
subsys/fs/ext2/ext2_impl.c
Normal file
291
subsys/fs/ext2/ext2_impl.c
Normal file
|
|
@ -0,0 +1,291 @@
|
|||
/*
|
||||
* Copyright (c) 2023 Antmicro <www.antmicro.com>
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <zephyr/kernel.h>
|
||||
#include <zephyr/init.h>
|
||||
#include <zephyr/fs/fs.h>
|
||||
#include <zephyr/logging/log.h>
|
||||
#include <zephyr/sys/util.h>
|
||||
|
||||
#include "ext2.h"
|
||||
#include "ext2_impl.h"
|
||||
#include "ext2_struct.h"
|
||||
|
||||
LOG_MODULE_REGISTER(ext2, CONFIG_EXT2_LOG_LEVEL);
|
||||
|
||||
static struct ext2_data __fs;
|
||||
static bool initialized;
|
||||
|
||||
#define BLOCK_MEMORY_BUFFER_SIZE (CONFIG_EXT2_MAX_BLOCK_COUNT * CONFIG_EXT2_MAX_BLOCK_SIZE)
|
||||
#define BLOCK_STRUCT_BUFFER_SIZE (CONFIG_EXT2_MAX_BLOCK_COUNT * sizeof(struct ext2_block))
|
||||
|
||||
/* Structures for blocks slab alocator */
|
||||
struct k_mem_slab ext2_block_memory_slab, ext2_block_struct_slab;
|
||||
char __aligned(sizeof(void *)) __ext2_block_memory_buffer[BLOCK_MEMORY_BUFFER_SIZE];
|
||||
char __aligned(sizeof(void *)) __ext2_block_struct_buffer[BLOCK_STRUCT_BUFFER_SIZE];
|
||||
|
||||
/* Initialize heap memory allocator */
|
||||
K_HEAP_DEFINE(ext2_heap, CONFIG_EXT2_HEAP_SIZE);
|
||||
|
||||
/* Helper functions --------------------------------------------------------- */
|
||||
|
||||
void *ext2_heap_alloc(size_t size)
|
||||
{
|
||||
return k_heap_alloc(&ext2_heap, size, K_NO_WAIT);
|
||||
}
|
||||
|
||||
void ext2_heap_free(void *ptr)
|
||||
{
|
||||
k_heap_free(&ext2_heap, ptr);
|
||||
}
|
||||
|
||||
/* Block operations --------------------------------------------------------- */
|
||||
|
||||
struct ext2_block *ext2_get_block(struct ext2_data *fs, uint32_t block)
|
||||
{
|
||||
int ret;
|
||||
struct ext2_block *b;
|
||||
|
||||
ret = k_mem_slab_alloc(&ext2_block_struct_slab, (void **)&b, K_NO_WAIT);
|
||||
if (ret < 0) {
|
||||
LOG_ERR("get block: alloc block struct error %d", ret);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ret = k_mem_slab_alloc(&ext2_block_memory_slab, (void **)&b->data, K_NO_WAIT);
|
||||
if (ret < 0) {
|
||||
LOG_ERR("get block: alloc block memory error %d", ret);
|
||||
goto fail_alloc;
|
||||
}
|
||||
|
||||
b->num = block;
|
||||
ret = fs->backend_ops->read_block(fs, b->data, block);
|
||||
if (ret < 0) {
|
||||
LOG_ERR("get block: read block error %d", ret);
|
||||
goto fail_read;
|
||||
}
|
||||
|
||||
return b;
|
||||
|
||||
fail_read:
|
||||
k_mem_slab_free(&ext2_block_memory_slab, (void **)&b->data);
|
||||
fail_alloc:
|
||||
k_mem_slab_free(&ext2_block_struct_slab, (void **)&b);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int ext2_sync_block(struct ext2_data *fs, struct ext2_block *b)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = fs->backend_ops->write_block(fs, b->data, b->num);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void ext2_drop_block(struct ext2_block *b)
|
||||
{
|
||||
if (b->data != NULL) {
|
||||
k_mem_slab_free(&ext2_block_memory_slab, (void **)&b->data);
|
||||
k_mem_slab_free(&ext2_block_struct_slab, (void **)&b);
|
||||
}
|
||||
}
|
||||
|
||||
void ext2_init_blocks_slab(struct ext2_data *fs)
|
||||
{
|
||||
memset(__ext2_block_memory_buffer, 0, BLOCK_MEMORY_BUFFER_SIZE);
|
||||
memset(__ext2_block_struct_buffer, 0, BLOCK_STRUCT_BUFFER_SIZE);
|
||||
|
||||
/* These calls will always succeed because sizes and memory buffers are properly aligned. */
|
||||
|
||||
k_mem_slab_init(&ext2_block_struct_slab, __ext2_block_struct_buffer,
|
||||
sizeof(struct ext2_block), CONFIG_EXT2_MAX_BLOCK_COUNT);
|
||||
|
||||
k_mem_slab_init(&ext2_block_memory_slab, __ext2_block_memory_buffer, fs->block_size,
|
||||
CONFIG_EXT2_MAX_BLOCK_COUNT);
|
||||
}
|
||||
|
||||
/* FS operations ------------------------------------------------------------ */
|
||||
|
||||
int ext2_init_storage(struct ext2_data **fsp, const void *storage_dev, int flags)
|
||||
{
|
||||
if (initialized) {
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
int ret = 0;
|
||||
struct ext2_data *fs = &__fs;
|
||||
int64_t dev_size, write_size;
|
||||
|
||||
*fsp = fs;
|
||||
|
||||
ret = ext2_init_disk_access_backend(fs, storage_dev, flags);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
dev_size = fs->backend_ops->get_device_size(fs);
|
||||
if (dev_size < 0) {
|
||||
ret = dev_size;
|
||||
goto err;
|
||||
}
|
||||
|
||||
write_size = fs->backend_ops->get_write_size(fs);
|
||||
if (write_size < 0) {
|
||||
ret = write_size;
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (write_size < 1024 && 1024 % write_size != 0) {
|
||||
ret = -EINVAL;
|
||||
LOG_ERR("expecting sector size that divides 1024 (got: %lld)", write_size);
|
||||
goto err;
|
||||
}
|
||||
|
||||
LOG_DBG("Device size: %lld", dev_size);
|
||||
LOG_DBG("Write size: %lld", write_size);
|
||||
|
||||
fs->device_size = dev_size;
|
||||
fs->write_size = write_size;
|
||||
|
||||
initialized = true;
|
||||
err:
|
||||
return ret;
|
||||
}
|
||||
|
||||
int ext2_verify_superblock(struct ext2_disk_superblock *sb)
|
||||
{
|
||||
/* Check if it is a valid Ext2 file system. */
|
||||
if (sb->s_magic != EXT2_MAGIC_NUMBER) {
|
||||
LOG_ERR("Wrong file system magic number (%x)", sb->s_magic);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* For now we don't support file systems with frag size different from block size */
|
||||
if (sb->s_log_block_size != sb->s_log_frag_size) {
|
||||
LOG_ERR("Filesystem with frag_size != block_size is not supported");
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
/* Support only second revision */
|
||||
if (sb->s_rev_level != EXT2_DYNAMIC_REV) {
|
||||
LOG_ERR("Filesystem with revision %d is not supported", sb->s_rev_level);
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
if (sb->s_inode_size != EXT2_GOOD_OLD_INODE_SIZE) {
|
||||
LOG_ERR("Filesystem with inode size %d is not supported", sb->s_inode_size);
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
/* Check if file system may contain errors. */
|
||||
if (sb->s_state == EXT2_ERROR_FS) {
|
||||
LOG_WRN("File system may contain errors.");
|
||||
switch (sb->s_errors) {
|
||||
case EXT2_ERRORS_CONTINUE:
|
||||
break;
|
||||
|
||||
case EXT2_ERRORS_RO:
|
||||
LOG_WRN("File system can be mounted read only");
|
||||
return -EROFS;
|
||||
|
||||
case EXT2_ERRORS_PANIC:
|
||||
LOG_ERR("File system can't be mounted");
|
||||
/* panic or return that fs is invalid */
|
||||
__ASSERT(sb->s_state == EXT2_VALID_FS, "Error detected in superblock");
|
||||
return -EINVAL;
|
||||
default:
|
||||
LOG_WRN("Unknown option for superblock s_errors field.");
|
||||
}
|
||||
}
|
||||
|
||||
if ((sb->s_feature_incompat & EXT2_FEATURE_INCOMPAT_FILETYPE) == 0) {
|
||||
LOG_ERR("File system without file type stored in de is not supported");
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
if ((sb->s_feature_incompat & ~EXT2_FEATURE_INCOMPAT_SUPPORTED) > 0) {
|
||||
LOG_ERR("File system can't be mounted. Incompat features %d not supported",
|
||||
(sb->s_feature_incompat & ~EXT2_FEATURE_INCOMPAT_SUPPORTED));
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
if ((sb->s_feature_ro_compat & ~EXT2_FEATURE_RO_COMPAT_SUPPORTED) > 0) {
|
||||
LOG_WRN("File system can be mounted read only. RO features %d detected.",
|
||||
(sb->s_feature_ro_compat & ~EXT2_FEATURE_RO_COMPAT_SUPPORTED));
|
||||
return -EROFS;
|
||||
}
|
||||
|
||||
LOG_DBG("ino_cnt:%d blk_cnt:%d blk_per_grp:%d ino_per_grp:%d free_ino:%d free_blk:%d "
|
||||
"blk_size:%d ino_size:%d mntc:%d",
|
||||
sb->s_inodes_count, sb->s_blocks_count, sb->s_blocks_per_group,
|
||||
sb->s_inodes_per_group, sb->s_free_inodes_count, sb->s_free_blocks_count,
|
||||
1024 << sb->s_log_block_size, sb->s_inode_size, sb->s_mnt_count);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ext2_init_fs(struct ext2_data *fs)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
/* Fetch superblock */
|
||||
if (fs->block_size == 1024) {
|
||||
fs->sblock_offset = 0;
|
||||
fs->sblock = ext2_get_block(fs, 1);
|
||||
} else {
|
||||
fs->sblock_offset = 1024;
|
||||
fs->sblock = ext2_get_block(fs, 0);
|
||||
}
|
||||
|
||||
if (fs->sblock == NULL) {
|
||||
ret = ENOENT;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!(fs->flags & EXT2_DATA_FLAGS_RO)) {
|
||||
/* Update sblock fields set during the successful mount. */
|
||||
EXT2_DATA_SBLOCK(fs)->s_state = EXT2_ERROR_FS;
|
||||
EXT2_DATA_SBLOCK(fs)->s_mnt_count += 1;
|
||||
|
||||
ret = ext2_sync_block(fs, fs->sblock);
|
||||
if (ret < 0) {
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
out:
|
||||
ext2_drop_block(fs->sblock);
|
||||
fs->sblock = NULL;
|
||||
return ret;
|
||||
}
|
||||
|
||||
int ext2_close_fs(struct ext2_data *fs)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if (!(fs->flags & EXT2_DATA_FLAGS_RO)) {
|
||||
EXT2_DATA_SBLOCK(fs)->s_state = EXT2_VALID_FS;
|
||||
|
||||
ret = ext2_sync_block(fs, fs->sblock);
|
||||
if (ret < 0) {
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
ext2_drop_block(fs->sblock);
|
||||
fs->sblock = NULL;
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
int ext2_close_struct(struct ext2_data *fs)
|
||||
{
|
||||
memset(fs, 0, sizeof(struct ext2_data));
|
||||
initialized = false;
|
||||
return 0;
|
||||
}
|
||||
104
subsys/fs/ext2/ext2_impl.h
Normal file
104
subsys/fs/ext2/ext2_impl.h
Normal file
|
|
@ -0,0 +1,104 @@
|
|||
/*
|
||||
* Copyright (c) 2023 Antmicro <www.antmicro.com>
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#ifndef __EXT2_IMPL_H__
|
||||
#define __EXT2_IMPL_H__
|
||||
|
||||
#include <zephyr/fs/fs.h>
|
||||
#include <zephyr/fs/ext2.h>
|
||||
|
||||
#include "ext2_struct.h"
|
||||
|
||||
/* Memory allocation for ext2 implementation */
|
||||
void *ext2_heap_alloc(size_t size);
|
||||
void ext2_heap_free(void *ptr);
|
||||
|
||||
/* Initialization of disk storage. */
|
||||
int ext2_init_disk_access_backend(struct ext2_data *fs, const void *storage_dev, int flags);
|
||||
|
||||
/**
|
||||
* @brief Get block from the disk.
|
||||
*/
|
||||
struct ext2_block *ext2_get_block(struct ext2_data *fs, uint32_t block);
|
||||
|
||||
/**
|
||||
* @brief Free the block structure.
|
||||
*/
|
||||
void ext2_drop_block(struct ext2_block *b);
|
||||
|
||||
/**
|
||||
* @brief Write block to the disk.
|
||||
*/
|
||||
int ext2_sync_block(struct ext2_data *fs, struct ext2_block *b);
|
||||
|
||||
void ext2_init_blocks_slab(struct ext2_data *fs);
|
||||
|
||||
/* FS operations */
|
||||
|
||||
/**
|
||||
* @brief Initialize structure with data needed to access the storage device
|
||||
*
|
||||
* @param fs File system data structure to initialize
|
||||
* @param storage_dev Pointer to storage
|
||||
* @param flags Additional flags (e.g. RO flag)
|
||||
*
|
||||
* @retval 0 on success
|
||||
* @retval -EINVAL when superblock of ext2 was not detected
|
||||
* @retval -ENOTSUP when described file system is not supported
|
||||
* @retval <0 other error
|
||||
*/
|
||||
int ext2_init_storage(struct ext2_data **fsp, const void *storage_dev, int flags);
|
||||
|
||||
/**
|
||||
* @brief Verify superblock of file system
|
||||
*
|
||||
* Checks if file system is supported by the implementation.
|
||||
* @retval 0 when superblock is valid
|
||||
* @retval -EROFS when superblock is not valid but file system may be mounted read only
|
||||
* @retval -EINVAL when superblock is not valid and file system cannot be mounted at all
|
||||
* @retval -ENOTSUP when superblock has some field set to value that we don't support
|
||||
*/
|
||||
int ext2_verify_superblock(struct ext2_disk_superblock *sb);
|
||||
|
||||
/**
|
||||
* @brief Initialize all data needed to perform operations on file system
|
||||
*
|
||||
* Fetches the superblock. Initializes structure fields.
|
||||
*/
|
||||
int ext2_init_fs(struct ext2_data *fs);
|
||||
|
||||
/**
|
||||
* @brief Clear the data used by file system implementation
|
||||
*
|
||||
*/
|
||||
int ext2_close_fs(struct ext2_data *fs);
|
||||
|
||||
/**
|
||||
* @brief Clear the data used to communicate with storage device
|
||||
*
|
||||
*/
|
||||
int ext2_close_struct(struct ext2_data *fs);
|
||||
|
||||
/**
|
||||
* @brief Create the ext2 file system
|
||||
*
|
||||
* This function uses functions stored in `ext2_data` structure to create new
|
||||
* file system on storage device.
|
||||
*
|
||||
* NOTICE: fs structure must be first initialized with `ext2_init_fs` function.
|
||||
*
|
||||
* After this function succeeds the `ext2_clean` function must be called.
|
||||
*
|
||||
* @param fs File system data (must be initialized before)
|
||||
*
|
||||
* @retval 0 on success
|
||||
* @retval -ENOSPC when storage device is too small for ext2 file system
|
||||
* @retval -ENOTSUP when storage device is too big (file systems with more than
|
||||
* 8192 blocks are not supported)
|
||||
*/
|
||||
int ext2_format(struct ext2_data *fs, struct ext2_cfg *cfg);
|
||||
|
||||
#endif /* __EXT2_IMPL_H__ */
|
||||
208
subsys/fs/ext2/ext2_ops.c
Normal file
208
subsys/fs/ext2/ext2_ops.c
Normal file
|
|
@ -0,0 +1,208 @@
|
|||
/*
|
||||
* Copyright (c) 2023 Antmicro <www.antmicro.com>
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <zephyr/init.h>
|
||||
#include <zephyr/fs/fs.h>
|
||||
#include <zephyr/fs/fs_sys.h>
|
||||
#include <zephyr/fs/ext2.h>
|
||||
#include <zephyr/logging/log.h>
|
||||
|
||||
#include "../fs_impl.h"
|
||||
#include "ext2.h"
|
||||
#include "ext2_impl.h"
|
||||
#include "ext2_struct.h"
|
||||
|
||||
LOG_MODULE_DECLARE(ext2);
|
||||
|
||||
/* File system level operations */
|
||||
|
||||
#ifdef CONFIG_FILE_SYSTEM_MKFS
|
||||
FS_EXT2_DECLARE_DEFAULT_CONFIG(ext2_default_cfg);
|
||||
#endif
|
||||
|
||||
static int ext2_mount(struct fs_mount_t *mountp)
|
||||
{
|
||||
int ret = 0;
|
||||
struct ext2_data *fs = NULL;
|
||||
struct ext2_disk_superblock *sb;
|
||||
#ifdef CONFIG_FILE_SYSTEM_MKFS
|
||||
bool do_format = false;
|
||||
bool possible_format = (mountp->flags & FS_MOUNT_FLAG_NO_FORMAT) == 0 &&
|
||||
(mountp->flags & FS_MOUNT_FLAG_READ_ONLY) == 0;
|
||||
#endif
|
||||
|
||||
/* Allocate superblock structure for temporary use */
|
||||
sb = ext2_heap_alloc(sizeof(struct ext2_disk_superblock));
|
||||
if (sb == NULL) {
|
||||
ret = -ENOMEM;
|
||||
goto err;
|
||||
}
|
||||
|
||||
ret = ext2_init_storage(&fs, mountp->storage_dev, mountp->flags);
|
||||
if (ret < 0) {
|
||||
goto err;
|
||||
}
|
||||
|
||||
fs->flags = 0;
|
||||
if (mountp->flags & FS_MOUNT_FLAG_READ_ONLY) {
|
||||
fs->flags |= EXT2_DATA_FLAGS_RO;
|
||||
}
|
||||
|
||||
ret = fs->backend_ops->read_superblock(fs, sb);
|
||||
if (ret < 0) {
|
||||
goto err;
|
||||
}
|
||||
|
||||
ret = ext2_verify_superblock(sb);
|
||||
if (ret == 0) {
|
||||
fs->block_size = 1024 << sb->s_log_block_size;
|
||||
|
||||
} else if (ret == -EROFS) {
|
||||
fs->block_size = 1024 << sb->s_log_block_size;
|
||||
fs->flags |= EXT2_DATA_FLAGS_RO;
|
||||
|
||||
#ifdef CONFIG_FILE_SYSTEM_MKFS
|
||||
} else if (ret == -EINVAL && possible_format) {
|
||||
do_format = true;
|
||||
fs->block_size = ext2_default_cfg.block_size;
|
||||
#endif
|
||||
|
||||
} else {
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (fs->block_size % fs->write_size != 0) {
|
||||
LOG_ERR("Blocks size isn't multiple of sector size. (bsz: %d, ssz: %d)",
|
||||
fs->block_size, fs->write_size);
|
||||
ret = -ENOTSUP;
|
||||
goto err;
|
||||
}
|
||||
|
||||
/* Temporary superblock won't be used anymore */
|
||||
ext2_heap_free(sb);
|
||||
|
||||
ext2_init_blocks_slab(fs);
|
||||
|
||||
#ifdef CONFIG_FILE_SYSTEM_MKFS
|
||||
if (do_format) {
|
||||
LOG_INF("Formatting the storage device");
|
||||
|
||||
ret = ext2_format(fs, &ext2_default_cfg);
|
||||
if (ret < 0) {
|
||||
goto err;
|
||||
}
|
||||
/* We don't need to verify superblock here again. Format has succeeded hence
|
||||
* superblock must be valid.
|
||||
*/
|
||||
}
|
||||
#endif
|
||||
|
||||
ret = ext2_init_fs(fs);
|
||||
if (ret < 0) {
|
||||
goto err;
|
||||
}
|
||||
|
||||
mountp->fs_data = fs;
|
||||
return 0;
|
||||
|
||||
err:
|
||||
ext2_heap_free(sb);
|
||||
ext2_close_struct(fs);
|
||||
return ret;
|
||||
}
|
||||
|
||||
#if defined(CONFIG_FILE_SYSTEM_MKFS)
|
||||
|
||||
static int ext2_mkfs(uintptr_t dev_id, void *vcfg, int flags)
|
||||
{
|
||||
int ret = 0;
|
||||
struct ext2_data *fs;
|
||||
struct ext2_cfg *cfg = vcfg;
|
||||
|
||||
if (cfg == NULL) {
|
||||
cfg = &ext2_default_cfg;
|
||||
}
|
||||
|
||||
ret = ext2_init_storage(&fs, (const void *)dev_id, flags);
|
||||
if (ret < 0) {
|
||||
LOG_ERR("Initialization of %ld device failed (%d)", dev_id, ret);
|
||||
goto out;
|
||||
}
|
||||
|
||||
fs->block_size = cfg->block_size;
|
||||
|
||||
ext2_init_blocks_slab(fs);
|
||||
|
||||
LOG_INF("Formatting the storage device");
|
||||
ret = ext2_format(fs, cfg);
|
||||
if (ret < 0) {
|
||||
LOG_ERR("Format of %ld device failed (%d)", dev_id, ret);
|
||||
}
|
||||
|
||||
out:
|
||||
ext2_close_struct(fs);
|
||||
return ret;
|
||||
}
|
||||
|
||||
#endif /* CONFIG_FILE_SYSTEM_MKFS */
|
||||
|
||||
static int ext2_unmount(struct fs_mount_t *mountp)
|
||||
{
|
||||
int ret;
|
||||
struct ext2_data *fs = mountp->fs_data;
|
||||
|
||||
ret = ext2_close_fs(fs);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = ext2_close_struct(fs);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
mountp->fs_data = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ext2_statvfs(struct fs_mount_t *mountp, const char *path, struct fs_statvfs *stat)
|
||||
{
|
||||
ARG_UNUSED(path);
|
||||
struct ext2_data *fs = mountp->fs_data;
|
||||
|
||||
stat->f_bsize = fs->block_size;
|
||||
stat->f_frsize = fs->block_size;
|
||||
stat->f_blocks = EXT2_DATA_SBLOCK(fs)->s_blocks_count;
|
||||
stat->f_bfree = EXT2_DATA_SBLOCK(fs)->s_free_blocks_count;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* File system interface */
|
||||
|
||||
static const struct fs_file_system_t ext2_fs = {
|
||||
.mount = ext2_mount,
|
||||
.unmount = ext2_unmount,
|
||||
.statvfs = ext2_statvfs,
|
||||
#if defined(CONFIG_FILE_SYSTEM_MKFS)
|
||||
.mkfs = ext2_mkfs,
|
||||
#endif
|
||||
};
|
||||
|
||||
static int ext2_init(void)
|
||||
{
|
||||
int rc = fs_register(FS_EXT2, &ext2_fs);
|
||||
|
||||
if (rc < 0) {
|
||||
LOG_WRN("Ext2 register error (%d)\n", rc);
|
||||
} else {
|
||||
LOG_DBG("Ext2 fs registered\n");
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
SYS_INIT(ext2_init, POST_KERNEL, 99);
|
||||
139
subsys/fs/ext2/ext2_struct.h
Normal file
139
subsys/fs/ext2/ext2_struct.h
Normal file
|
|
@ -0,0 +1,139 @@
|
|||
/*
|
||||
* Copyright (c) 2023 Antmicro <www.antmicro.com>
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#ifndef __EXT2_STRUCT_H__
|
||||
#define __EXT2_STRUCT_H__
|
||||
|
||||
#include <zephyr/kernel.h>
|
||||
#include "ext2.h"
|
||||
|
||||
/* Disk structures ---------------------------------------------------------- */
|
||||
|
||||
struct ext2_disk_superblock {
|
||||
uint32_t s_inodes_count;
|
||||
uint32_t s_blocks_count;
|
||||
uint32_t s_r_blocks_count;
|
||||
uint32_t s_free_blocks_count;
|
||||
uint32_t s_free_inodes_count;
|
||||
uint32_t s_first_data_block;
|
||||
uint32_t s_log_block_size;
|
||||
uint32_t s_log_frag_size;
|
||||
uint32_t s_blocks_per_group;
|
||||
uint32_t s_frags_per_group;
|
||||
uint32_t s_inodes_per_group;
|
||||
uint32_t s_mtime;
|
||||
uint32_t s_wtime;
|
||||
uint16_t s_mnt_count;
|
||||
uint16_t s_max_mnt_count;
|
||||
uint16_t s_magic;
|
||||
uint16_t s_state;
|
||||
uint16_t s_errors;
|
||||
uint16_t s_minor_rev_level;
|
||||
uint32_t s_lastcheck;
|
||||
uint32_t s_checkinterval;
|
||||
uint32_t s_creator_os;
|
||||
uint32_t s_rev_level;
|
||||
uint16_t s_def_resuid;
|
||||
uint16_t s_def_resgid;
|
||||
uint32_t s_first_ino;
|
||||
uint16_t s_inode_size;
|
||||
uint16_t s_block_group_nr;
|
||||
uint32_t s_feature_compat;
|
||||
uint32_t s_feature_incompat;
|
||||
uint32_t s_feature_ro_compat;
|
||||
uint8_t s_uuid[16];
|
||||
uint8_t s_volume_name[16];
|
||||
uint8_t s_last_mounted[64];
|
||||
uint32_t s_algo_bitmap;
|
||||
uint8_t s_prealloc_blocks;
|
||||
uint8_t s_prealloc_dir_blocks;
|
||||
uint8_t s_align[2];
|
||||
uint8_t s_journal_uuid[16];
|
||||
uint32_t s_journal_inum;
|
||||
uint32_t s_journal_dev;
|
||||
uint32_t s_last_orphan;
|
||||
uint8_t s_padding[788];
|
||||
};
|
||||
|
||||
struct ext2_disk_bgroup {
|
||||
uint32_t bg_block_bitmap;
|
||||
uint32_t bg_inode_bitmap;
|
||||
uint32_t bg_inode_table;
|
||||
uint16_t bg_free_blocks_count;
|
||||
uint16_t bg_free_inodes_count;
|
||||
uint16_t bg_used_dirs_count;
|
||||
uint16_t bg_pad;
|
||||
uint8_t bg_reserved[12];
|
||||
};
|
||||
|
||||
struct ext2_disk_inode {
|
||||
uint16_t i_mode;
|
||||
uint16_t i_uid;
|
||||
uint32_t i_size;
|
||||
uint32_t i_atime;
|
||||
uint32_t i_ctime;
|
||||
uint32_t i_mtime;
|
||||
uint32_t i_dtime;
|
||||
uint16_t i_gid;
|
||||
uint16_t i_links_count;
|
||||
uint32_t i_blocks;
|
||||
uint32_t i_flags;
|
||||
uint32_t i_osd1;
|
||||
uint32_t i_block[15];
|
||||
uint32_t i_generation;
|
||||
uint32_t i_file_acl;
|
||||
uint32_t i_dir_acl;
|
||||
uint32_t i_faddr;
|
||||
uint8_t i_osd2[12];
|
||||
};
|
||||
|
||||
struct ext2_disk_dentry {
|
||||
uint32_t de_inode;
|
||||
uint16_t de_rec_len;
|
||||
uint8_t de_name_len;
|
||||
uint8_t de_file_type;
|
||||
char de_name[];
|
||||
};
|
||||
|
||||
/* Program structures ------------------------------------------------------- */
|
||||
|
||||
struct ext2_block {
|
||||
uint32_t num;
|
||||
uint8_t *data;
|
||||
} __aligned(sizeof(void *));
|
||||
|
||||
#define EXT2_DATA_FLAGS_RO BIT(0)
|
||||
|
||||
/* Accessing superblock disk structure (it is at some offset in stored block) */
|
||||
#define EXT2_DATA_SBLOCK(fs) \
|
||||
((struct ext2_disk_superblock *)((uint8_t *)(fs)->sblock->data + (fs)->sblock_offset))
|
||||
|
||||
struct ext2_data;
|
||||
|
||||
struct ext2_backend_ops {
|
||||
int64_t (*get_device_size)(struct ext2_data *fs);
|
||||
int64_t (*get_write_size)(struct ext2_data *fs);
|
||||
int (*read_block)(struct ext2_data *fs, void *buf, uint32_t num);
|
||||
int (*write_block)(struct ext2_data *fs, const void *buf, uint32_t num);
|
||||
int (*read_superblock)(struct ext2_data *fs, struct ext2_disk_superblock *sb);
|
||||
int (*sync)(struct ext2_data *fs);
|
||||
};
|
||||
|
||||
struct ext2_data {
|
||||
struct ext2_block *sblock; /* superblock */
|
||||
|
||||
uint32_t sblock_offset;
|
||||
uint32_t block_size; /* fs block size */
|
||||
uint32_t write_size; /* dev minimal write size */
|
||||
uint64_t device_size;
|
||||
struct k_thread sync_thr;
|
||||
|
||||
void *backend; /* pointer to implementation specific resource */
|
||||
const struct ext2_backend_ops *backend_ops;
|
||||
uint8_t flags;
|
||||
};
|
||||
|
||||
#endif /* __EXT2_STRUCT_H__ */
|
||||
Loading…
Reference in a new issue