fs: ext2: Use disk structs to access data on disk

This commits changes how data on the disk is accessed. There are disk
structures which are packed and their fields are stored in little endian
byte order. To use data in the program structures it has to be translated
from little endian to cpu endianness.

Signed-off-by: Franciszek Zdobylak <fzdobylak@antmicro.com>
This commit is contained in:
Franciszek Zdobylak 2023-05-22 11:24:06 +02:00 committed by Anas Nashif
commit 1eb104795e
7 changed files with 902 additions and 545 deletions

View file

@ -9,6 +9,7 @@
#include <zephyr/fs/fs.h> #include <zephyr/fs/fs.h>
#include <zephyr/logging/log.h> #include <zephyr/logging/log.h>
#include <zephyr/sys/util.h> #include <zephyr/sys/util.h>
#include <zephyr/sys/byteorder.h>
#include <stdint.h> #include <stdint.h>
@ -28,6 +29,318 @@ static inline uint32_t get_ngroups(struct ext2_data *fs);
/* Array of zeros to be used in inode block calculation */ /* Array of zeros to be used in inode block calculation */
static const uint32_t zero_offsets[MAX_OFFSETS_SIZE]; static const uint32_t zero_offsets[MAX_OFFSETS_SIZE];
static void fill_sblock(struct ext2_superblock *sb, struct ext2_disk_superblock *disk_sb)
{
sb->s_inodes_count = sys_le32_to_cpu(disk_sb->s_inodes_count);
sb->s_blocks_count = sys_le32_to_cpu(disk_sb->s_blocks_count);
sb->s_free_blocks_count = sys_le32_to_cpu(disk_sb->s_free_blocks_count);
sb->s_free_inodes_count = sys_le32_to_cpu(disk_sb->s_free_inodes_count);
sb->s_first_data_block = sys_le32_to_cpu(disk_sb->s_first_data_block);
sb->s_log_block_size = sys_le32_to_cpu(disk_sb->s_log_block_size);
sb->s_log_frag_size = sys_le32_to_cpu(disk_sb->s_log_frag_size);
sb->s_blocks_per_group = sys_le32_to_cpu(disk_sb->s_blocks_per_group);
sb->s_frags_per_group = sys_le32_to_cpu(disk_sb->s_frags_per_group);
sb->s_inodes_per_group = sys_le32_to_cpu(disk_sb->s_inodes_per_group);
sb->s_mnt_count = sys_le16_to_cpu(disk_sb->s_mnt_count);
sb->s_max_mnt_count = sys_le16_to_cpu(disk_sb->s_max_mnt_count);
sb->s_magic = sys_le16_to_cpu(disk_sb->s_magic);
sb->s_state = sys_le16_to_cpu(disk_sb->s_state);
sb->s_errors = sys_le16_to_cpu(disk_sb->s_errors);
sb->s_creator_os = sys_le32_to_cpu(disk_sb->s_creator_os);
sb->s_rev_level = sys_le32_to_cpu(disk_sb->s_rev_level);
sb->s_first_ino = sys_le32_to_cpu(disk_sb->s_first_ino);
sb->s_inode_size = sys_le16_to_cpu(disk_sb->s_inode_size);
sb->s_block_group_nr = sys_le16_to_cpu(disk_sb->s_block_group_nr);
sb->s_feature_compat = sys_le32_to_cpu(disk_sb->s_feature_compat);
sb->s_feature_incompat = sys_le32_to_cpu(disk_sb->s_feature_incompat);
sb->s_feature_ro_compat = sys_le32_to_cpu(disk_sb->s_feature_ro_compat);
}
static void fill_disk_sblock(struct ext2_disk_superblock *disk_sb, struct ext2_superblock *sb)
{
disk_sb->s_inodes_count = sys_cpu_to_le32(sb->s_inodes_count);
disk_sb->s_blocks_count = sys_cpu_to_le32(sb->s_blocks_count);
disk_sb->s_free_blocks_count = sys_cpu_to_le32(sb->s_free_blocks_count);
disk_sb->s_free_inodes_count = sys_cpu_to_le32(sb->s_free_inodes_count);
disk_sb->s_first_data_block = sys_cpu_to_le32(sb->s_first_data_block);
disk_sb->s_log_block_size = sys_cpu_to_le32(sb->s_log_block_size);
disk_sb->s_log_frag_size = sys_cpu_to_le32(sb->s_log_frag_size);
disk_sb->s_blocks_per_group = sys_cpu_to_le32(sb->s_blocks_per_group);
disk_sb->s_frags_per_group = sys_cpu_to_le32(sb->s_frags_per_group);
disk_sb->s_inodes_per_group = sys_cpu_to_le32(sb->s_inodes_per_group);
disk_sb->s_mnt_count = sys_cpu_to_le16(sb->s_mnt_count);
disk_sb->s_max_mnt_count = sys_cpu_to_le16(sb->s_max_mnt_count);
disk_sb->s_magic = sys_cpu_to_le16(sb->s_magic);
disk_sb->s_state = sys_cpu_to_le16(sb->s_state);
disk_sb->s_errors = sys_cpu_to_le16(sb->s_errors);
disk_sb->s_creator_os = sys_cpu_to_le32(sb->s_creator_os);
disk_sb->s_rev_level = sys_cpu_to_le32(sb->s_rev_level);
disk_sb->s_first_ino = sys_cpu_to_le32(sb->s_first_ino);
disk_sb->s_inode_size = sys_cpu_to_le16(sb->s_inode_size);
disk_sb->s_block_group_nr = sys_cpu_to_le16(sb->s_block_group_nr);
disk_sb->s_feature_compat = sys_cpu_to_le32(sb->s_feature_compat);
disk_sb->s_feature_incompat = sys_cpu_to_le32(sb->s_feature_incompat);
disk_sb->s_feature_ro_compat = sys_cpu_to_le32(sb->s_feature_ro_compat);
}
static void fill_bgroup(struct ext2_bgroup *bg, struct ext2_disk_bgroup *disk_bg)
{
bg->bg_block_bitmap = sys_le32_to_cpu(disk_bg->bg_block_bitmap);
bg->bg_inode_bitmap = sys_le32_to_cpu(disk_bg->bg_inode_bitmap);
bg->bg_inode_table = sys_le32_to_cpu(disk_bg->bg_inode_table);
bg->bg_free_blocks_count = sys_le16_to_cpu(disk_bg->bg_free_blocks_count);
bg->bg_free_inodes_count = sys_le16_to_cpu(disk_bg->bg_free_inodes_count);
bg->bg_used_dirs_count = sys_le16_to_cpu(disk_bg->bg_used_dirs_count);
}
static void fill_disk_bgroup(struct ext2_disk_bgroup *disk_bg, struct ext2_bgroup *bg)
{
disk_bg->bg_block_bitmap = sys_cpu_to_le32(bg->bg_block_bitmap);
disk_bg->bg_inode_bitmap = sys_cpu_to_le32(bg->bg_inode_bitmap);
disk_bg->bg_inode_table = sys_cpu_to_le32(bg->bg_inode_table);
disk_bg->bg_free_blocks_count = sys_cpu_to_le16(bg->bg_free_blocks_count);
disk_bg->bg_free_inodes_count = sys_cpu_to_le16(bg->bg_free_inodes_count);
disk_bg->bg_used_dirs_count = sys_cpu_to_le16(bg->bg_used_dirs_count);
}
static void fill_inode(struct ext2_inode *inode, struct ext2_disk_inode *dino)
{
inode->i_mode = sys_le16_to_cpu(dino->i_mode);
inode->i_size = sys_le32_to_cpu(dino->i_size);
inode->i_links_count = sys_le16_to_cpu(dino->i_links_count);
inode->i_blocks = sys_le32_to_cpu(dino->i_blocks);
for (int i = 0; i < EXT2_INODE_BLOCKS; i++) {
inode->i_block[i] = sys_le32_to_cpu(dino->i_block[i]);
}
}
static void fill_disk_inode(struct ext2_disk_inode *dino, struct ext2_inode *inode)
{
dino->i_mode = sys_cpu_to_le16(inode->i_mode);
dino->i_size = sys_cpu_to_le32(inode->i_size);
dino->i_links_count = sys_cpu_to_le16(inode->i_links_count);
dino->i_blocks = sys_cpu_to_le32(inode->i_blocks);
for (int i = 0; i < EXT2_INODE_BLOCKS; i++) {
dino->i_block[i] = sys_cpu_to_le32(inode->i_block[i]);
}
}
struct ext2_direntry *ext2_fetch_direntry(struct ext2_disk_direntry *disk_de)
{
if (disk_de->de_name_len > EXT2_MAX_FILE_NAME) {
return NULL;
}
uint32_t prog_rec_len = sizeof(struct ext2_direntry) + disk_de->de_name_len;
struct ext2_direntry *de = k_heap_alloc(&direntry_heap, prog_rec_len, K_FOREVER);
__ASSERT(de != NULL, "allocated direntry can't be NULL");
de->de_inode = sys_le32_to_cpu(disk_de->de_inode);
de->de_rec_len = sys_le16_to_cpu(disk_de->de_rec_len);
de->de_name_len = disk_de->de_name_len;
de->de_file_type = disk_de->de_file_type;
memcpy(de->de_name, disk_de->de_name, de->de_name_len);
return de;
}
void ext2_write_direntry(struct ext2_disk_direntry *disk_de, struct ext2_direntry *de)
{
disk_de->de_inode = sys_le32_to_cpu(de->de_inode);
disk_de->de_rec_len = sys_le16_to_cpu(de->de_rec_len);
disk_de->de_name_len = de->de_name_len;
disk_de->de_file_type = de->de_file_type;
memcpy(disk_de->de_name, de->de_name, de->de_name_len);
}
uint32_t ext2_get_disk_direntry_inode(struct ext2_disk_direntry *de)
{
return sys_le32_to_cpu(de->de_inode);
}
uint32_t ext2_get_disk_direntry_reclen(struct ext2_disk_direntry *de)
{
return sys_le16_to_cpu(de->de_rec_len);
}
uint8_t ext2_get_disk_direntry_namelen(struct ext2_disk_direntry *de)
{
return de->de_name_len;
}
uint8_t ext2_get_disk_direntry_type(struct ext2_disk_direntry *de)
{
return de->de_file_type;
}
void ext2_set_disk_direntry_inode(struct ext2_disk_direntry *de, uint32_t inode)
{
de->de_inode = sys_cpu_to_le32(inode);
}
void ext2_set_disk_direntry_reclen(struct ext2_disk_direntry *de, uint16_t reclen)
{
de->de_rec_len = sys_cpu_to_le16(reclen);
}
void ext2_set_disk_direntry_namelen(struct ext2_disk_direntry *de, uint8_t namelen)
{
de->de_name_len = namelen;
}
void ext2_set_disk_direntry_type(struct ext2_disk_direntry *de, uint8_t type)
{
de->de_file_type = type;
}
void ext2_set_disk_direntry_name(struct ext2_disk_direntry *de, const char *name, size_t len)
{
memcpy(de->de_name, name, len);
}
int ext2_fetch_superblock(struct ext2_data *fs)
{
struct ext2_block *b;
uint32_t sblock_offset;
if (fs->block_size == 1024) {
sblock_offset = 0;
b = ext2_get_block(fs, 1);
} else {
sblock_offset = 1024;
b = ext2_get_block(fs, 0);
}
if (b == NULL) {
return -ENOENT;
}
struct ext2_disk_superblock *disk_sb =
(struct ext2_disk_superblock *)(b->data + sblock_offset);
fill_sblock(&fs->sblock, disk_sb);
ext2_drop_block(b);
return 0;
}
static inline uint32_t get_ngroups(struct ext2_data *fs)
{
uint32_t ngroups =
fs->sblock.s_blocks_count / fs->sblock.s_blocks_per_group;
if (fs->sblock.s_blocks_count % fs->sblock.s_blocks_per_group != 0) {
/* there is one more group if the last group is incomplete */
ngroups += 1;
}
return ngroups;
}
int ext2_fetch_block_group(struct ext2_data *fs, uint32_t group)
{
struct ext2_bgroup *bg = &fs->bgroup;
/* Check if block group is cached */
if (group == bg->num) {
return 0;
}
uint32_t ngroups = get_ngroups(fs);
LOG_DBG("ngroups:%d", ngroups);
LOG_DBG("cur_group:%d fetch_group:%d", bg->num, group);
if (group > ngroups) {
return -ERANGE;
}
uint32_t groups_per_block = fs->block_size / sizeof(struct ext2_disk_bgroup);
uint32_t block = group / groups_per_block;
uint32_t offset = group % groups_per_block;
uint32_t global_block = fs->sblock.s_first_data_block + 1 + block;
struct ext2_block *b = ext2_get_block(fs, global_block);
if (b == NULL) {
return -ENOENT;
}
struct ext2_disk_bgroup *disk_bg = ((struct ext2_disk_bgroup *)b->data) + offset;
fill_bgroup(bg, disk_bg);
/* Drop unused block */
ext2_drop_block(b);
/* Invalidate previously fetched blocks */
ext2_drop_block(bg->inode_table);
ext2_drop_block(bg->inode_bitmap);
ext2_drop_block(bg->block_bitmap);
bg->inode_table = bg->inode_bitmap = bg->block_bitmap = NULL;
bg->fs = fs;
bg->num = group;
LOG_DBG("[BG:%d] itable:%d free_blk:%d free_ino:%d useddirs:%d bbitmap:%d ibitmap:%d",
group, bg->bg_inode_table,
bg->bg_free_blocks_count,
bg->bg_free_inodes_count,
bg->bg_used_dirs_count,
bg->bg_block_bitmap,
bg->bg_inode_bitmap);
return 0;
}
int ext2_fetch_bg_itable(struct ext2_bgroup *bg, uint32_t block)
{
if (bg->inode_table && bg->inode_table_block == block) {
return 0;
}
struct ext2_data *fs = bg->fs;
uint32_t global_block = bg->bg_inode_table + block;
ext2_drop_block(bg->inode_table);
bg->inode_table = ext2_get_block(fs, global_block);
if (bg->inode_table == NULL) {
return -ENOENT;
}
bg->inode_table_block = block;
return 0;
}
int ext2_fetch_bg_ibitmap(struct ext2_bgroup *bg)
{
if (bg->inode_bitmap) {
return 0;
}
struct ext2_data *fs = bg->fs;
uint32_t global_block = bg->bg_inode_bitmap;
bg->inode_bitmap = ext2_get_block(fs, global_block);
if (bg->inode_bitmap == NULL) {
return -ENOENT;
}
return 0;
}
int ext2_fetch_bg_bbitmap(struct ext2_bgroup *bg)
{
if (bg->block_bitmap) {
return 0;
}
struct ext2_data *fs = bg->fs;
uint32_t global_block = bg->bg_block_bitmap;
bg->block_bitmap = ext2_get_block(fs, global_block);
if (bg->block_bitmap == NULL) {
return -ENOENT;
}
return 0;
}
/** /**
* @brief Fetch block group and inode table of given inode. * @brief Fetch block group and inode table of given inode.
* *
@ -36,8 +349,8 @@ static const uint32_t zero_offsets[MAX_OFFSETS_SIZE];
static int32_t get_itable_entry(struct ext2_data *fs, uint32_t ino) static int32_t get_itable_entry(struct ext2_data *fs, uint32_t ino)
{ {
int rc; int rc;
uint32_t ino_group = (ino - 1) / EXT2_DATA_SBLOCK(fs)->s_inodes_per_group; uint32_t ino_group = (ino - 1) / fs->sblock.s_inodes_per_group;
uint32_t ino_index = (ino - 1) % EXT2_DATA_SBLOCK(fs)->s_inodes_per_group; uint32_t ino_index = (ino - 1) % fs->sblock.s_inodes_per_group;
LOG_DBG("ino_group:%d ino_index:%d", ino_group, ino_index); LOG_DBG("ino_group:%d ino_index:%d", ino_group, ino_index);
@ -46,7 +359,7 @@ static int32_t get_itable_entry(struct ext2_data *fs, uint32_t ino)
return rc; return rc;
} }
uint32_t inode_size = EXT2_DATA_SBLOCK(fs)->s_inode_size; uint32_t inode_size = fs->sblock.s_inode_size;
uint32_t inodes_per_block = fs->block_size / inode_size; uint32_t inodes_per_block = fs->block_size / inode_size;
uint32_t block_index = ino_index / inodes_per_block; uint32_t block_index = ino_index / inodes_per_block;
@ -74,18 +387,14 @@ int ext2_fetch_inode(struct ext2_data *fs, uint32_t ino, struct ext2_inode *inod
struct ext2_disk_inode *dino = &BGROUP_INODE_TABLE(&fs->bgroup)[itable_offset]; struct ext2_disk_inode *dino = &BGROUP_INODE_TABLE(&fs->bgroup)[itable_offset];
fill_inode(inode, dino);
/* Copy needed data into inode structure */ /* Copy needed data into inode structure */
inode->i_fs = fs; inode->i_fs = fs;
inode->flags = 0; inode->flags = 0;
inode->i_id = ino; inode->i_id = ino;
inode->i_mode = dino->i_mode;
inode->i_size = dino->i_size;
inode->i_links_count = dino->i_links_count;
inode->i_blocks = dino->i_blocks;
memcpy(inode->i_block, dino->i_block, sizeof(uint32_t) * EXT2_INODE_BLOCKS);
LOG_DBG("mode:%d size:%d links:%d", dino->i_mode, dino->i_size, dino->i_links_count); LOG_DBG("mode:%d size:%d links:%d", dino->i_mode, dino->i_size, dino->i_links_count);
return 0; return 0;
} }
@ -116,7 +425,9 @@ static int fetch_level_blocks(struct ext2_inode *inode, uint32_t offsets[4], int
if (lvl == 0) { if (lvl == 0) {
block = inode->i_block[offsets[0]]; block = inode->i_block[offsets[0]];
} else { } else {
block = ((uint32_t *)inode->blocks[lvl - 1]->data)[offsets[lvl]]; uint32_t *list = (uint32_t *)inode->blocks[lvl - 1]->data;
block = sys_le32_to_cpu(list[offsets[lvl]]);
} }
if (block == 0) { if (block == 0) {
@ -228,10 +539,12 @@ static int64_t delete_blocks(struct ext2_data *fs, uint32_t block_num, int lvl,
start_blk = offsets[0]; start_blk = offsets[0];
} else { } else {
uint32_t block_num = sys_le32_to_cpu(list[offsets[0]]);
/* We don't remove all blocks referenced by current block. We have to use /* We don't remove all blocks referenced by current block. We have to use
* offsets to decide which part of next block we want to remove. * offsets to decide which part of next block we want to remove.
*/ */
if (list[offsets[0]] == 0) { if (block_num == 0) {
LOG_ERR("Inode block that references other blocks must be nonzero"); LOG_ERR("Inode block that references other blocks must be nonzero");
fs->flags |= EXT2_DATA_FLAGS_ERR; fs->flags |= EXT2_DATA_FLAGS_ERR;
removed = -EINVAL; removed = -EINVAL;
@ -242,7 +555,7 @@ static int64_t delete_blocks(struct ext2_data *fs, uint32_t block_num, int lvl,
start_blk = offsets[0] + 1; start_blk = offsets[0] + 1;
/* Remove desired part of lower level block. */ /* Remove desired part of lower level block. */
rem = delete_blocks(fs, list[offsets[0]], lvl - 1, &offsets[1]); rem = delete_blocks(fs, block_num, lvl - 1, &offsets[1]);
if (rem < 0) { if (rem < 0) {
removed = rem; removed = rem;
goto out; goto out;
@ -252,10 +565,12 @@ static int64_t delete_blocks(struct ext2_data *fs, uint32_t block_num, int lvl,
/* Iterate over blocks that will be entirely deleted */ /* Iterate over blocks that will be entirely deleted */
for (uint32_t i = start_blk; i < fs->block_size / EXT2_BLOCK_NUM_SIZE; ++i) { for (uint32_t i = start_blk; i < fs->block_size / EXT2_BLOCK_NUM_SIZE; ++i) {
if (list[i] == 0) { uint32_t block_num = sys_le32_to_cpu(list[i]);
if (block_num == 0) {
continue; continue;
} }
rem = delete_blocks(fs, list[i], lvl - 1, zero_offsets); rem = delete_blocks(fs, block_num, lvl - 1, zero_offsets);
if (rem < 0) { if (rem < 0) {
removed = rem; removed = rem;
goto out; goto out;
@ -396,124 +711,6 @@ int64_t ext2_inode_remove_blocks(struct ext2_inode *inode, uint32_t first)
} }
return removed; return removed;
} }
static inline uint32_t get_ngroups(struct ext2_data *fs)
{
uint32_t ngroups =
EXT2_DATA_SBLOCK(fs)->s_blocks_count / EXT2_DATA_SBLOCK(fs)->s_blocks_per_group;
if (EXT2_DATA_SBLOCK(fs)->s_blocks_count % EXT2_DATA_SBLOCK(fs)->s_blocks_per_group != 0) {
/* there is one more group if the last group is incomplete */
ngroups += 1;
}
return ngroups;
}
int ext2_fetch_block_group(struct ext2_data *fs, uint32_t group)
{
struct ext2_bgroup *bg = &fs->bgroup;
/* Check if block group is cached */
if (bg->block && group == bg->num) {
return 0;
}
uint32_t ngroups = get_ngroups(fs);
LOG_DBG("ngroups:%d", ngroups);
LOG_DBG("cur_group:%d fetch_group:%d", bg->num, group);
if (group > ngroups) {
return -ERANGE;
}
uint32_t groups_per_block = fs->block_size / sizeof(struct ext2_disk_bgroup);
uint32_t block = group / groups_per_block;
uint32_t offset = group % groups_per_block;
/* fetch block group block if not cached */
if ((bg->block == NULL) || (block != bg->block->num)) {
uint32_t global_block = EXT2_DATA_SBLOCK(fs)->s_first_data_block + 1 + block;
ext2_drop_block(bg->block);
bg->block = ext2_get_block(fs, global_block);
if (bg->block == NULL) {
return -ENOENT;
}
}
/* Invalidate previously fetched blocks */
ext2_drop_block(bg->inode_table);
ext2_drop_block(bg->inode_bitmap);
ext2_drop_block(bg->block_bitmap);
bg->inode_table = bg->inode_bitmap = bg->block_bitmap = NULL;
bg->fs = fs;
bg->num = group;
bg->num_in_block = offset;
LOG_DBG("[BG:%d] itable:%d free_blk:%d free_ino:%d useddirs:%d bbitmap:%d ibitmap:%d",
group, current_disk_bgroup(bg)->bg_inode_table,
current_disk_bgroup(bg)->bg_free_blocks_count,
current_disk_bgroup(bg)->bg_free_inodes_count,
current_disk_bgroup(bg)->bg_used_dirs_count,
current_disk_bgroup(bg)->bg_block_bitmap,
current_disk_bgroup(bg)->bg_inode_bitmap);
return 0;
}
int ext2_fetch_bg_itable(struct ext2_bgroup *bg, uint32_t block)
{
if (bg->inode_table && bg->inode_table_block == block) {
return 0;
}
struct ext2_data *fs = bg->fs;
uint32_t global_block = current_disk_bgroup(bg)->bg_inode_table + block;
ext2_drop_block(bg->inode_table);
bg->inode_table = ext2_get_block(fs, global_block);
if (bg->inode_table == NULL) {
return -ENOENT;
}
bg->inode_table_block = block;
return 0;
}
int ext2_fetch_bg_ibitmap(struct ext2_bgroup *bg)
{
if (bg->inode_bitmap) {
return 0;
}
struct ext2_data *fs = bg->fs;
uint32_t global_block = current_disk_bgroup(bg)->bg_inode_bitmap;
bg->inode_bitmap = ext2_get_block(fs, global_block);
if (bg->inode_bitmap == NULL) {
return -ENOENT;
}
return 0;
}
int ext2_fetch_bg_bbitmap(struct ext2_bgroup *bg)
{
if (bg->block_bitmap) {
return 0;
}
struct ext2_data *fs = bg->fs;
uint32_t global_block = current_disk_bgroup(bg)->bg_block_bitmap;
bg->block_bitmap = ext2_get_block(fs, global_block);
if (bg->block_bitmap == NULL) {
return -ENOENT;
}
return 0;
}
static int alloc_level_blocks(struct ext2_inode *inode) static int alloc_level_blocks(struct ext2_inode *inode)
{ {
int ret = 0; int ret = 0;
@ -526,6 +723,7 @@ static int alloc_level_blocks(struct ext2_inode *inode)
block = &inode->i_block[inode->offsets[lvl]]; block = &inode->i_block[inode->offsets[lvl]];
} else { } else {
block = &((uint32_t *)inode->blocks[lvl - 1]->data)[inode->offsets[lvl]]; block = &((uint32_t *)inode->blocks[lvl - 1]->data)[inode->offsets[lvl]];
*block = sys_le32_to_cpu(*block);
} }
if (*block == 0) { if (*block == 0) {
@ -535,7 +733,7 @@ static int alloc_level_blocks(struct ext2_inode *inode)
} }
/* Update block from higher level. */ /* Update block from higher level. */
*block = inode->blocks[lvl]->num; *block = sys_cpu_to_le32(inode->blocks[lvl]->num);
if (lvl > 0) { if (lvl > 0) {
ret = ext2_write_block(fs, inode->blocks[lvl-1]); ret = ext2_write_block(fs, inode->blocks[lvl-1]);
if (ret < 0) { if (ret < 0) {
@ -560,6 +758,83 @@ static int alloc_level_blocks(struct ext2_inode *inode)
return ret; return ret;
} }
int ext2_commit_superblock(struct ext2_data *fs)
{
int ret;
struct ext2_block *b;
uint32_t sblock_offset;
if (fs->block_size == 1024) {
sblock_offset = 0;
b = ext2_get_block(fs, 1);
} else {
sblock_offset = 1024;
b = ext2_get_block(fs, 0);
}
if (b == NULL) {
return -ENOENT;
}
struct ext2_disk_superblock *disk_sb =
(struct ext2_disk_superblock *)(b->data + sblock_offset);
fill_disk_sblock(disk_sb, &fs->sblock);
ret = ext2_write_block(fs, b);
if (ret < 0) {
return ret;
}
ext2_drop_block(b);
return 0;
}
int ext2_commit_bg(struct ext2_data *fs)
{
int ret;
struct ext2_bgroup *bg = &fs->bgroup;
uint32_t groups_per_block = fs->block_size / sizeof(struct ext2_disk_bgroup);
uint32_t block = bg->num / groups_per_block;
uint32_t offset = bg->num % groups_per_block;
uint32_t global_block = fs->sblock.s_first_data_block + 1 + block;
struct ext2_block *b = ext2_get_block(fs, global_block);
if (b == NULL) {
return -ENOENT;
}
struct ext2_disk_bgroup *disk_bg = ((struct ext2_disk_bgroup *)b->data) + offset;
fill_disk_bgroup(disk_bg, bg);
ret = ext2_write_block(fs, b);
if (ret < 0) {
return ret;
}
ext2_drop_block(b);
return 0;
}
int ext2_commit_inode(struct ext2_inode *inode)
{
struct ext2_data *fs = inode->i_fs;
int32_t itable_offset = get_itable_entry(fs, inode->i_id);
if (itable_offset < 0) {
return itable_offset;
}
/* get pointer to proper inode in fetched block */
struct ext2_disk_inode *dino = &BGROUP_INODE_TABLE(&fs->bgroup)[itable_offset];
/* fill dinode */
fill_disk_inode(dino, inode);
return ext2_write_block(fs, fs->bgroup.inode_table);
}
int ext2_commit_inode_block(struct ext2_inode *inode) int ext2_commit_inode_block(struct ext2_inode *inode)
{ {
if (!(inode->flags & INODE_FETCHED_BLOCK)) { if (!(inode->flags & INODE_FETCHED_BLOCK)) {
@ -592,6 +867,79 @@ int ext2_clear_inode(struct ext2_data *fs, uint32_t ino)
return ret; return ret;
} }
int64_t ext2_alloc_block(struct ext2_data *fs)
{
int rc, bitmap_slot;
uint32_t group = 0, set;
int32_t total;
rc = ext2_fetch_block_group(fs, group);
if (rc < 0) {
return rc;
}
LOG_DBG("Free blocks: %d", fs->bgroup.bg_free_blocks_count);
while ((rc >= 0) && (fs->bgroup.bg_free_blocks_count == 0)) {
group++;
rc = ext2_fetch_block_group(fs, group);
if (rc == -ERANGE) {
/* reached last group */
return -ENOSPC;
}
}
if (rc < 0) {
return rc;
}
rc = ext2_fetch_bg_bbitmap(&fs->bgroup);
if (rc < 0) {
return rc;
}
bitmap_slot = ext2_bitmap_find_free(BGROUP_BLOCK_BITMAP(&fs->bgroup), fs->block_size);
if (bitmap_slot < 0) {
LOG_WRN("Cannot find free block in group %d (rc: %d)", group, bitmap_slot);
return bitmap_slot;
}
/* In bitmap blocks are counted from s_first_data_block hence we have to add this offset. */
total = group * fs->sblock.s_blocks_per_group + bitmap_slot + fs->sblock.s_first_data_block;
LOG_DBG("Found free block %d in group %d (total: %d)", bitmap_slot, group, total);
rc = ext2_bitmap_set(BGROUP_BLOCK_BITMAP(&fs->bgroup), bitmap_slot, fs->block_size);
if (rc < 0) {
return rc;
}
fs->bgroup.bg_free_blocks_count -= 1;
fs->sblock.s_free_blocks_count -= 1;
set = ext2_bitmap_count_set(BGROUP_BLOCK_BITMAP(&fs->bgroup), fs->sblock.s_blocks_count);
if (set != (fs->sblock.s_blocks_count - fs->sblock.s_free_blocks_count)) {
error_behavior(fs, "Wrong number of used blocks in superblock and bitmap");
return -EINVAL;
}
rc = ext2_commit_superblock(fs);
if (rc < 0) {
LOG_DBG("super block write returned: %d", rc);
return -EIO;
}
rc = ext2_commit_bg(fs);
if (rc < 0) {
LOG_DBG("block group write returned: %d", rc);
return -EIO;
}
rc = ext2_write_block(fs, fs->bgroup.block_bitmap);
if (rc < 0) {
LOG_DBG("block bitmap write returned: %d", rc);
return -EIO;
}
return total;
}
static int check_zero_inode(struct ext2_data *fs, uint32_t ino) static int check_zero_inode(struct ext2_data *fs, uint32_t ino)
{ {
int32_t itable_offset = get_itable_entry(fs, ino); int32_t itable_offset = get_itable_entry(fs, ino);
@ -610,113 +958,15 @@ static int check_zero_inode(struct ext2_data *fs, uint32_t ino)
return 0; return 0;
} }
int ext2_commit_inode(struct ext2_inode *inode)
{
struct ext2_data *fs = inode->i_fs;
int32_t itable_offset = get_itable_entry(fs, inode->i_id);
if (itable_offset < 0) {
return itable_offset;
}
/* get pointer to proper inode in fetched block */
struct ext2_disk_inode *dino = &BGROUP_INODE_TABLE(&fs->bgroup)[itable_offset];
/* fill dinode */
dino->i_size = inode->i_size;
dino->i_mode = inode->i_mode;
dino->i_links_count = inode->i_links_count;
dino->i_blocks = inode->i_blocks;
memcpy(dino->i_block, inode->i_block, sizeof(uint32_t) * 15);
return ext2_write_block(fs, fs->bgroup.inode_table);
}
int64_t ext2_alloc_block(struct ext2_data *fs)
{
int rc;
uint32_t group = 0;
rc = ext2_fetch_block_group(fs, group);
if (rc < 0) {
return rc;
}
LOG_DBG("Free blocks: %d", current_disk_bgroup(&fs->bgroup)->bg_free_blocks_count);
while ((rc >= 0) && (current_disk_bgroup(&fs->bgroup)->bg_free_blocks_count == 0)) {
group++;
rc = ext2_fetch_block_group(fs, group);
if (rc == -ERANGE) {
/* reached last group */
return -ENOSPC;
}
}
if (rc < 0) {
return rc;
}
rc = ext2_fetch_bg_bbitmap(&fs->bgroup);
if (rc < 0) {
return rc;
}
int bitmap_slot = ext2_bitmap_find_free(BGROUP_BLOCK_BITMAP(&fs->bgroup), fs->block_size);
if (bitmap_slot < 0) {
LOG_WRN("Cannot find free block in group %d (rc: %d)", group, bitmap_slot);
return bitmap_slot;
}
/* In bitmap blocks are counted from s_first_data_block hence we have to add this offset. */
int32_t total = group * EXT2_DATA_SBLOCK(fs)->s_blocks_per_group
+ bitmap_slot + EXT2_DATA_SBLOCK(fs)->s_first_data_block;
LOG_DBG("Found free block %d in group %d (total: %d)", bitmap_slot, group, total);
rc = ext2_bitmap_set(BGROUP_BLOCK_BITMAP(&fs->bgroup), bitmap_slot, fs->block_size);
if (rc < 0) {
return rc;
}
current_disk_bgroup(&fs->bgroup)->bg_free_blocks_count -= 1;
EXT2_DATA_SBLOCK(fs)->s_free_blocks_count -= 1;
struct ext2_disk_superblock *sb = EXT2_DATA_SBLOCK(fs);
uint32_t set = ext2_bitmap_count_set(BGROUP_BLOCK_BITMAP(&fs->bgroup), sb->s_blocks_count);
if (set != (sb->s_blocks_count - sb->s_free_blocks_count)) {
error_behavior(fs, "Wrong number of used blocks in superblock and bitmap");
return -EINVAL;
}
rc = ext2_write_block(fs, fs->sblock);
if (rc < 0) {
LOG_DBG("super block write returned: %d", rc);
return -EIO;
}
rc = ext2_write_block(fs, fs->bgroup.block);
if (rc < 0) {
LOG_DBG("block group write returned: %d", rc);
return -EIO;
}
rc = ext2_write_block(fs, fs->bgroup.block_bitmap);
if (rc < 0) {
LOG_DBG("block bitmap write returned: %d", rc);
return -EIO;
}
return total;
}
int32_t ext2_alloc_inode(struct ext2_data *fs) int32_t ext2_alloc_inode(struct ext2_data *fs)
{ {
int rc; int rc, r;
uint32_t group = 0; uint32_t group = 0, set;
int32_t global_idx;
rc = ext2_fetch_block_group(fs, group); rc = ext2_fetch_block_group(fs, group);
while (current_disk_bgroup(&fs->bgroup)->bg_free_inodes_count == 0 && rc >= 0) { while (fs->bgroup.bg_free_inodes_count == 0 && rc >= 0) {
group++; group++;
rc = ext2_fetch_block_group(fs, group); rc = ext2_fetch_block_group(fs, group);
if (rc == -ERANGE) { if (rc == -ERANGE) {
@ -729,23 +979,22 @@ int32_t ext2_alloc_inode(struct ext2_data *fs)
return rc; return rc;
} }
LOG_DBG("Free inodes (bg): %d", current_disk_bgroup(&fs->bgroup)->bg_free_inodes_count); LOG_DBG("Free inodes (bg): %d", fs->bgroup.bg_free_inodes_count);
LOG_DBG("Free inodes (sb): %d", EXT2_DATA_SBLOCK(fs)->s_free_inodes_count); LOG_DBG("Free inodes (sb): %d", fs->sblock.s_free_inodes_count);
rc = ext2_fetch_bg_ibitmap(&fs->bgroup); rc = ext2_fetch_bg_ibitmap(&fs->bgroup);
if (rc < 0) { if (rc < 0) {
return rc; return rc;
} }
int r = ext2_bitmap_find_free(BGROUP_INODE_BITMAP(&fs->bgroup), fs->block_size); r = ext2_bitmap_find_free(BGROUP_INODE_BITMAP(&fs->bgroup), fs->block_size);
if (r < 0) { if (r < 0) {
LOG_DBG("Cannot find free inode in group %d (rc: %d)", group, r); LOG_DBG("Cannot find free inode in group %d (rc: %d)", group, r);
return r; return r;
} }
/* Add 1 because inodes are counted from 1 not 0. */ /* Add 1 because inodes are counted from 1 not 0. */
int32_t global_idx = group * EXT2_DATA_SBLOCK(fs)->s_inodes_per_group + r + 1; global_idx = group * fs->sblock.s_inodes_per_group + r + 1;
/* Inode table entry for found inode must be cleared. */ /* Inode table entry for found inode must be cleared. */
if (check_zero_inode(fs, global_idx) != 0) { if (check_zero_inode(fs, global_idx) != 0) {
@ -760,23 +1009,22 @@ int32_t ext2_alloc_inode(struct ext2_data *fs)
return rc; return rc;
} }
current_disk_bgroup(&fs->bgroup)->bg_free_inodes_count -= 1; fs->bgroup.bg_free_inodes_count -= 1;
EXT2_DATA_SBLOCK(fs)->s_free_inodes_count -= 1; fs->sblock.s_free_inodes_count -= 1;
struct ext2_disk_superblock *sb = EXT2_DATA_SBLOCK(fs); set = ext2_bitmap_count_set(BGROUP_INODE_BITMAP(&fs->bgroup), fs->sblock.s_inodes_count);
uint32_t set = ext2_bitmap_count_set(BGROUP_INODE_BITMAP(&fs->bgroup), sb->s_inodes_count);
if (set != sb->s_inodes_count - sb->s_free_inodes_count) { if (set != fs->sblock.s_inodes_count - fs->sblock.s_free_inodes_count) {
error_behavior(fs, "Wrong number of used inodes in superblock and bitmap"); error_behavior(fs, "Wrong number of used inodes in superblock and bitmap");
return -EINVAL; return -EINVAL;
} }
rc = ext2_write_block(fs, fs->sblock); rc = ext2_commit_superblock(fs);
if (rc < 0) { if (rc < 0) {
LOG_DBG("super block write returned: %d", rc); LOG_DBG("super block write returned: %d", rc);
return -EIO; return -EIO;
} }
rc = ext2_write_block(fs, fs->bgroup.block); rc = ext2_commit_bg(fs);
if (rc < 0) { if (rc < 0) {
LOG_DBG("block group write returned: %d", rc); LOG_DBG("block group write returned: %d", rc);
return -EIO; return -EIO;
@ -787,8 +1035,8 @@ int32_t ext2_alloc_inode(struct ext2_data *fs)
return -EIO; return -EIO;
} }
LOG_DBG("Free inodes (bg): %d", current_disk_bgroup(&fs->bgroup)->bg_free_inodes_count); LOG_DBG("Free inodes (bg): %d", fs->bgroup.bg_free_inodes_count);
LOG_DBG("Free inodes (sb): %d", EXT2_DATA_SBLOCK(fs)->s_free_inodes_count); LOG_DBG("Free inodes (sb): %d", fs->sblock.s_free_inodes_count);
return global_idx; return global_idx;
} }
@ -798,11 +1046,12 @@ int ext2_free_block(struct ext2_data *fs, uint32_t block)
LOG_DBG("Free block %d", block); LOG_DBG("Free block %d", block);
/* Block bitmaps tracks blocks starting from s_first_data_block. */ /* Block bitmaps tracks blocks starting from s_first_data_block. */
block -= EXT2_DATA_SBLOCK(fs)->s_first_data_block; block -= fs->sblock.s_first_data_block;
int rc; int rc;
uint32_t group = block / EXT2_DATA_SBLOCK(fs)->s_blocks_per_group; uint32_t group = block / fs->sblock.s_blocks_per_group;
uint32_t off = block % EXT2_DATA_SBLOCK(fs)->s_blocks_per_group; uint32_t off = block % fs->sblock.s_blocks_per_group;
uint32_t set;
rc = ext2_fetch_block_group(fs, group); rc = ext2_fetch_block_group(fs, group);
if (rc < 0) { if (rc < 0) {
@ -819,23 +1068,22 @@ int ext2_free_block(struct ext2_data *fs, uint32_t block)
return rc; return rc;
} }
current_disk_bgroup(&fs->bgroup)->bg_free_blocks_count += 1; fs->bgroup.bg_free_blocks_count += 1;
EXT2_DATA_SBLOCK(fs)->s_free_blocks_count += 1; fs->sblock.s_free_blocks_count += 1;
struct ext2_disk_superblock *sb = EXT2_DATA_SBLOCK(fs); set = ext2_bitmap_count_set(BGROUP_BLOCK_BITMAP(&fs->bgroup), fs->sblock.s_blocks_count);
uint32_t set = ext2_bitmap_count_set(BGROUP_BLOCK_BITMAP(&fs->bgroup), sb->s_blocks_count);
if (set != sb->s_blocks_count - sb->s_free_blocks_count) { if (set != fs->sblock.s_blocks_count - fs->sblock.s_free_blocks_count) {
error_behavior(fs, "Wrong number of used blocks in superblock and bitmap"); error_behavior(fs, "Wrong number of used blocks in superblock and bitmap");
return -EINVAL; return -EINVAL;
} }
rc = ext2_write_block(fs, fs->sblock); rc = ext2_commit_superblock(fs);
if (rc < 0) { if (rc < 0) {
LOG_DBG("super block write returned: %d", rc); LOG_DBG("super block write returned: %d", rc);
return -EIO; return -EIO;
} }
rc = ext2_write_block(fs, fs->bgroup.block); rc = ext2_commit_bg(fs);
if (rc < 0) { if (rc < 0) {
LOG_DBG("block group write returned: %d", rc); LOG_DBG("block group write returned: %d", rc);
return -EIO; return -EIO;
@ -853,8 +1101,9 @@ int ext2_free_inode(struct ext2_data *fs, uint32_t ino, bool directory)
LOG_DBG("Free inode %d", ino); LOG_DBG("Free inode %d", ino);
int rc; int rc;
uint32_t group = (ino - 1) / EXT2_DATA_SBLOCK(fs)->s_inodes_per_group; uint32_t group = (ino - 1) / fs->sblock.s_inodes_per_group;
uint32_t bitmap_off = (ino - 1) % EXT2_DATA_SBLOCK(fs)->s_inodes_per_group; uint32_t bitmap_off = (ino - 1) % fs->sblock.s_inodes_per_group;
uint32_t set;
rc = ext2_fetch_block_group(fs, group); rc = ext2_fetch_block_group(fs, group);
if (rc < 0) { if (rc < 0) {
@ -876,29 +1125,28 @@ int ext2_free_inode(struct ext2_data *fs, uint32_t ino, bool directory)
return rc; return rc;
} }
current_disk_bgroup(&fs->bgroup)->bg_free_inodes_count += 1; fs->bgroup.bg_free_inodes_count += 1;
EXT2_DATA_SBLOCK(fs)->s_free_inodes_count += 1; fs->sblock.s_free_inodes_count += 1;
if (directory) { if (directory) {
current_disk_bgroup(&fs->bgroup)->bg_used_dirs_count -= 1; fs->bgroup.bg_used_dirs_count -= 1;
} }
struct ext2_disk_superblock *sb = EXT2_DATA_SBLOCK(fs); set = ext2_bitmap_count_set(BGROUP_INODE_BITMAP(&fs->bgroup), fs->sblock.s_inodes_count);
uint32_t set = ext2_bitmap_count_set(BGROUP_INODE_BITMAP(&fs->bgroup), sb->s_inodes_count);
if (set != sb->s_inodes_count - sb->s_free_inodes_count) { if (set != fs->sblock.s_inodes_count - fs->sblock.s_free_inodes_count) {
error_behavior(fs, "Wrong number of used inodes in superblock and bitmap"); error_behavior(fs, "Wrong number of used inodes in superblock and bitmap");
return -EINVAL; return -EINVAL;
} }
LOG_INF("Inode %d is free", ino); LOG_INF("Inode %d is free", ino);
rc = ext2_write_block(fs, fs->sblock); rc = ext2_commit_superblock(fs);
if (rc < 0) { if (rc < 0) {
LOG_DBG("super block write returned: %d", rc); LOG_DBG("super block write returned: %d", rc);
return -EIO; return -EIO;
} }
rc = ext2_write_block(fs, fs->bgroup.block); rc = ext2_commit_bg(fs);
if (rc < 0) { if (rc < 0) {
LOG_DBG("block group write returned: %d", rc); LOG_DBG("block group write returned: %d", rc);
return -EIO; return -EIO;

View file

@ -11,6 +11,13 @@
#include "ext2_struct.h" #include "ext2_struct.h"
/** @brief Fetch superblock into buffer in fs structure.
*
* @retval 0 on success
* @retval <0 error
*/
int ext2_fetch_superblock(struct ext2_data *fs);
/** /**
* @brief Fetch inode into given buffer. * @brief Fetch inode into given buffer.
* *
@ -38,8 +45,6 @@ int ext2_fetch_inode_block(struct ext2_inode *inode, uint32_t block);
* @brief Fetch block group into buffer in fs structure. * @brief Fetch block group into buffer in fs structure.
* *
* If the group was already fetched then this function has no effect. * If the group was already fetched then this function has no effect.
* If the group was not fetched but the block that holds this group is present in
* the internal buffer, then the block isn't fetched again.
* *
* @param fs File system data * @param fs File system data
* @param group Block group number * @param group Block group number
@ -52,8 +57,8 @@ int ext2_fetch_block_group(struct ext2_data *fs, uint32_t group);
/* /*
* @brief Fetch one block of inode table into internal buffer * @brief Fetch one block of inode table into internal buffer
* *
* If the block of inode table was already fetched then this function has no * If the block of inode table was already fetched then this function does nothing and returns
* effect. * with success.
* *
* @param bg Block group structure * @param bg Block group structure
* @param block Number of inode table block to fetch (relative to start of the inode table) * @param block Number of inode table block to fetch (relative to start of the inode table)
@ -127,6 +132,31 @@ int ext2_commit_inode(struct ext2_inode *inode);
*/ */
int ext2_commit_inode_block(struct ext2_inode *inode); int ext2_commit_inode_block(struct ext2_inode *inode);
/**
* @brief Commit changes made to superblock structure.
*
* The changes made to program structure are copied to disk representation and written to the
* backing storage.
*
* @param fs File system data struct
*
* @retval 0 on success
* @retval <0 error
*/
int ext2_commit_superblock(struct ext2_data *fs);
/**
* @brief Commit changes made to block group structure.
*
* The changes made to program structure are copied to disk representation and written to the
* backing storage.
*
* @param fs File system data struct
*
* @retval 0 on success
* @retval <0 error
*/
int ext2_commit_bg(struct ext2_data *fs);
/* Operations that reserve or free the block or inode in the file system. They /* Operations that reserve or free the block or inode in the file system. They
* mark an inode or block as used in the bitmap and change free inode/block * mark an inode or block as used in the bitmap and change free inode/block
@ -181,4 +211,29 @@ int ext2_free_block(struct ext2_data *fs, uint32_t block);
*/ */
int ext2_free_inode(struct ext2_data *fs, uint32_t ino, bool directory); int ext2_free_inode(struct ext2_data *fs, uint32_t ino, bool directory);
/**
* @brief Allocate directory entry filled with data from disk directory entry.
*
* NOTE: This function never fails.
*
* Returns structure allocated on direntry_heap.
*/
struct ext2_direntry *ext2_fetch_direntry(struct ext2_disk_direntry *disk_de);
/**
* @brief Write the data from program directory entry to disk structure.
*/
void ext2_write_direntry(struct ext2_disk_direntry *disk_de, struct ext2_direntry *de);
uint32_t ext2_get_disk_direntry_inode(struct ext2_disk_direntry *de);
uint32_t ext2_get_disk_direntry_reclen(struct ext2_disk_direntry *de);
uint8_t ext2_get_disk_direntry_namelen(struct ext2_disk_direntry *de);
uint8_t ext2_get_disk_direntry_type(struct ext2_disk_direntry *de);
void ext2_set_disk_direntry_inode(struct ext2_disk_direntry *de, uint32_t inode);
void ext2_set_disk_direntry_reclen(struct ext2_disk_direntry *de, uint16_t reclen);
void ext2_set_disk_direntry_namelen(struct ext2_disk_direntry *de, uint8_t namelen);
void ext2_set_disk_direntry_type(struct ext2_disk_direntry *de, uint8_t type);
void ext2_set_disk_direntry_name(struct ext2_disk_direntry *de, const char *name, size_t len);
#endif /* __EXT2_DISKOPS_H__ */ #endif /* __EXT2_DISKOPS_H__ */

View file

@ -7,10 +7,12 @@
#include <zephyr/logging/log.h> #include <zephyr/logging/log.h>
#include <zephyr/random/rand32.h> #include <zephyr/random/rand32.h>
#include <zephyr/fs/ext2.h> #include <zephyr/fs/ext2.h>
#include <zephyr/sys/byteorder.h>
#include "ext2.h" #include "ext2.h"
#include "ext2_impl.h" #include "ext2_impl.h"
#include "ext2_struct.h" #include "ext2_struct.h"
#include "ext2_diskops.h"
LOG_MODULE_DECLARE(ext2, LOG_LEVEL_DBG); LOG_MODULE_DECLARE(ext2, LOG_LEVEL_DBG);
@ -73,21 +75,21 @@ static void default_directory_inode(struct ext2_disk_inode *in, uint32_t nblocks
struct ext2_cfg *cfg) struct ext2_cfg *cfg)
{ {
LOG_DBG("Set directory inode: %p", in); LOG_DBG("Set directory inode: %p", in);
in->i_mode = EXT2_S_IFDIR; in->i_mode = sys_cpu_to_le16(EXT2_DEF_DIR_MODE);
in->i_uid = 0; in->i_uid = 0;
in->i_size = nblocks * cfg->block_size; in->i_size = sys_cpu_to_le32(nblocks * cfg->block_size);
in->i_atime = 0; in->i_atime = 0;
in->i_ctime = 0; in->i_ctime = 0;
in->i_mtime = 0; in->i_mtime = 0;
in->i_dtime = 0; in->i_dtime = 0;
in->i_gid = 0; in->i_gid = 0;
in->i_blocks = nblocks * cfg->block_size / 512; in->i_blocks = sys_cpu_to_le32(nblocks * cfg->block_size / 512);
in->i_flags = 0; in->i_flags = 0;
in->i_osd1 = 0; in->i_osd1 = 0;
in->i_generation = 0; in->i_generation = 0;
in->i_file_acl = 0; in->i_file_acl = 0;
in->i_dir_acl = 0; in->i_dir_acl = 0;
in->i_faddr = 0; in->i_faddr = 0;
memset(in->i_block, 0, EXT2_INODE_BLOCKS * sizeof(uint32_t)); memset(in->i_block, 0, EXT2_INODE_BLOCKS * sizeof(uint32_t));
} }
@ -206,43 +208,43 @@ int ext2_format(struct ext2_data *fs, struct ext2_cfg *cfg)
(struct ext2_disk_superblock *)((uint8_t *)sb_block->data + sb_offset); (struct ext2_disk_superblock *)((uint8_t *)sb_block->data + sb_offset);
memset(sb, 0, 1024); memset(sb, 0, 1024);
sb->s_inodes_count = inodes_count; sb->s_inodes_count = sys_cpu_to_le32(inodes_count);
sb->s_blocks_count = blocks_count; sb->s_blocks_count = sys_cpu_to_le32(blocks_count);
sb->s_r_blocks_count = 0; sb->s_r_blocks_count = sys_cpu_to_le32(0);
sb->s_free_blocks_count = free_blocks; sb->s_free_blocks_count = sys_cpu_to_le32(free_blocks);
sb->s_free_inodes_count = inodes_count - used_inodes; sb->s_free_inodes_count = sys_cpu_to_le32(inodes_count - used_inodes);
sb->s_first_data_block = first_data_block; sb->s_first_data_block = sys_cpu_to_le32(first_data_block);
sb->s_log_block_size = block_log_size; sb->s_log_block_size = sys_cpu_to_le32(block_log_size);
sb->s_log_frag_size = block_log_size; sb->s_log_frag_size = sys_cpu_to_le32(block_log_size);
sb->s_blocks_per_group = cfg->block_size * 8; sb->s_blocks_per_group = sys_cpu_to_le32(cfg->block_size * 8);
sb->s_frags_per_group = cfg->block_size * 8; sb->s_frags_per_group = sys_cpu_to_le32(cfg->block_size * 8);
sb->s_inodes_per_group = inodes_count; sb->s_inodes_per_group = sys_cpu_to_le32(inodes_count);
sb->s_mtime = 0; sb->s_mtime = sys_cpu_to_le32(0);
sb->s_wtime = 0; sb->s_wtime = sys_cpu_to_le32(0);
sb->s_mnt_count = 0; sb->s_mnt_count = sys_cpu_to_le32(0);
sb->s_max_mnt_count = -1; sb->s_max_mnt_count = sys_cpu_to_le32(-1);
sb->s_magic = 0xEF53; sb->s_magic = sys_cpu_to_le32(0xEF53);
sb->s_state = EXT2_VALID_FS; sb->s_state = sys_cpu_to_le32(EXT2_VALID_FS);
sb->s_errors = EXT2_ERRORS_RO; sb->s_errors = sys_cpu_to_le32(EXT2_ERRORS_RO);
sb->s_minor_rev_level = 0; sb->s_minor_rev_level = sys_cpu_to_le32(0);
sb->s_lastcheck = 0; sb->s_lastcheck = sys_cpu_to_le32(0);
sb->s_checkinterval = 0; sb->s_checkinterval = sys_cpu_to_le32(0);
sb->s_creator_os = 5; /* Unknown OS */ sb->s_creator_os = sys_cpu_to_le32(5); /* Unknown OS */
sb->s_rev_level = EXT2_DYNAMIC_REV; sb->s_rev_level = sys_cpu_to_le32(EXT2_DYNAMIC_REV);
sb->s_def_resuid = 0; sb->s_def_resuid = sys_cpu_to_le32(0);
sb->s_def_resgid = 0; sb->s_def_resgid = sys_cpu_to_le32(0);
sb->s_first_ino = 11; sb->s_first_ino = sys_cpu_to_le32(11);
sb->s_inode_size = sizeof(struct ext2_disk_inode); sb->s_inode_size = sys_cpu_to_le32(sizeof(struct ext2_disk_inode));
sb->s_block_group_nr = 0; sb->s_block_group_nr = sys_cpu_to_le32(0);
sb->s_feature_compat = 0; sb->s_feature_compat = sys_cpu_to_le32(0);
sb->s_feature_incompat = EXT2_FEATURE_INCOMPAT_FILETYPE; sb->s_feature_incompat = sys_cpu_to_le32(EXT2_FEATURE_INCOMPAT_FILETYPE);
sb->s_feature_ro_compat = 0; sb->s_feature_ro_compat = sys_cpu_to_le32(0);
sb->s_algo_bitmap = 0; sb->s_algo_bitmap = sys_cpu_to_le32(0);
sb->s_prealloc_blocks = 0; sb->s_prealloc_blocks = sys_cpu_to_le32(0);
sb->s_prealloc_dir_blocks = 0; sb->s_prealloc_dir_blocks = sys_cpu_to_le32(0);
sb->s_journal_inum = 0; sb->s_journal_inum = sys_cpu_to_le32(0);
sb->s_journal_dev = 0; sb->s_journal_dev = sys_cpu_to_le32(0);
sb->s_last_orphan = 0; sb->s_last_orphan = sys_cpu_to_le32(0);
memcpy(sb->s_uuid, cfg->uuid, 16); memcpy(sb->s_uuid, cfg->uuid, 16);
strcpy(sb->s_volume_name, cfg->volume_name); strcpy(sb->s_volume_name, cfg->volume_name);
@ -257,12 +259,12 @@ int ext2_format(struct ext2_data *fs, struct ext2_cfg *cfg)
struct ext2_disk_bgroup *bg = (struct ext2_disk_bgroup *)bg_block->data; struct ext2_disk_bgroup *bg = (struct ext2_disk_bgroup *)bg_block->data;
memset(bg, 0, cfg->block_size); memset(bg, 0, cfg->block_size);
bg->bg_block_bitmap = bbitmap_block_num; bg->bg_block_bitmap = sys_cpu_to_le32(bbitmap_block_num);
bg->bg_inode_bitmap = ibitmap_block_num; bg->bg_inode_bitmap = sys_cpu_to_le32(ibitmap_block_num);
bg->bg_inode_table = itable_block_num; bg->bg_inode_table = sys_cpu_to_le32(itable_block_num);
bg->bg_free_blocks_count = free_blocks; bg->bg_free_blocks_count = sys_cpu_to_le16(free_blocks);
bg->bg_free_inodes_count = inodes_count - used_inodes; bg->bg_free_inodes_count = sys_cpu_to_le16(inodes_count - used_inodes);
bg->bg_used_dirs_count = 2; /* '/' and 'lost+found' */ bg->bg_used_dirs_count = sys_cpu_to_le16(2); /* '/' and 'lost+found' */
if (ext2_write_block(fs, bg_block) < 0) { if (ext2_write_block(fs, bg_block) < 0) {
ret = -EIO; ret = -EIO;
@ -313,9 +315,8 @@ int ext2_format(struct ext2_data *fs, struct ext2_cfg *cfg)
inode_offset = EXT2_ROOT_INODE - 1; inode_offset = EXT2_ROOT_INODE - 1;
default_directory_inode(&in[inode_offset], 1, cfg); default_directory_inode(&in[inode_offset], 1, cfg);
in[inode_offset].i_mode = EXT2_DEF_DIR_MODE; in[inode_offset].i_links_count = sys_cpu_to_le16(3); /* 2 from itself, 1 from child */
in[inode_offset].i_links_count = 3; /* 2 from itself and 1 from child directory */ in[inode_offset].i_block[0] = sys_cpu_to_le32(root_dir_blk_num);
in[inode_offset].i_block[0] = root_dir_blk_num;
if (ext2_write_block(fs, itable_block1) < 0) { if (ext2_write_block(fs, itable_block1) < 0) {
ret = -EIO; ret = -EIO;
goto out; goto out;
@ -335,9 +336,8 @@ int ext2_format(struct ext2_data *fs, struct ext2_cfg *cfg)
} }
default_directory_inode(&in[inode_offset], 1, cfg); default_directory_inode(&in[inode_offset], 1, cfg);
in[inode_offset].i_mode = EXT2_DEF_DIR_MODE; in[inode_offset].i_links_count = sys_cpu_to_le16(2); /* 1 from itself, 1 from parent */
in[inode_offset].i_links_count = 2; /* 1 from itself and 1 from parent directory */ in[inode_offset].i_block[0] = sys_cpu_to_le32(lost_found_dir_blk_num);
in[inode_offset].i_block[0] = lost_found_dir_blk_num;
if (itable_block2) { if (itable_block2) {
if (ext2_write_block(fs, itable_block2) < 0) { if (ext2_write_block(fs, itable_block2) < 0) {
ret = -EIO; ret = -EIO;
@ -345,8 +345,9 @@ int ext2_format(struct ext2_data *fs, struct ext2_cfg *cfg)
} }
} }
struct ext2_disk_dentry *de; struct ext2_disk_direntry *disk_de;
uint32_t current_size; struct ext2_direntry *de;
uint32_t de_offset;
/* Contents of '/' directory */ /* Contents of '/' directory */
LOG_DBG("Root dir blk: %d", root_dir_blk_num); LOG_DBG("Root dir blk: %d", root_dir_blk_num);
@ -357,22 +358,33 @@ int ext2_format(struct ext2_data *fs, struct ext2_cfg *cfg)
} }
memset(root_dir_blk->data, 0, cfg->block_size); memset(root_dir_blk->data, 0, cfg->block_size);
current_size = 0; de_offset = 0;
de = (struct ext2_disk_dentry *)root_dir_blk->data; disk_de = EXT2_DISK_DIRENTRY_BY_OFFSET(root_dir_blk->data, de_offset);
ext2_fill_direntry(de, ".", 1, EXT2_ROOT_INODE, EXT2_FT_DIR); de = ext2_create_direntry(".", 1, EXT2_ROOT_INODE, EXT2_FT_DIR);
current_size += de->de_rec_len; ext2_write_direntry(disk_de, de);
de = EXT2_NEXT_DISK_DIRENTRY(de); de_offset += de->de_rec_len;
ext2_fill_direntry(de, "..", 2, EXT2_ROOT_INODE, EXT2_FT_DIR); k_heap_free(&direntry_heap, de);
current_size += de->de_rec_len;
de = EXT2_NEXT_DISK_DIRENTRY(de); disk_de = EXT2_DISK_DIRENTRY_BY_OFFSET(root_dir_blk->data, de_offset);
ext2_fill_direntry(de, "lost+found", strlen("lost+found"), lost_found_inode, EXT2_FT_DIR); de = ext2_create_direntry("..", 2, EXT2_ROOT_INODE, EXT2_FT_DIR);
current_size += de->de_rec_len; ext2_write_direntry(disk_de, de);
de_offset += de->de_rec_len;
k_heap_free(&direntry_heap, de);
char *name = "lost+found";
disk_de = EXT2_DISK_DIRENTRY_BY_OFFSET(root_dir_blk->data, de_offset);
de = ext2_create_direntry(name, strlen(name), lost_found_inode, EXT2_FT_DIR);
de_offset += de->de_rec_len;
/* This was the last entry so add padding until end of block */ /* This was the last entry so add padding until end of block */
de->de_rec_len += cfg->block_size - current_size; de->de_rec_len += cfg->block_size - de_offset;
ext2_write_direntry(disk_de, de);
k_heap_free(&direntry_heap, de);
if (ext2_write_block(fs, root_dir_blk) < 0) { if (ext2_write_block(fs, root_dir_blk) < 0) {
ret = -EIO; ret = -EIO;
@ -388,22 +400,24 @@ int ext2_format(struct ext2_data *fs, struct ext2_cfg *cfg)
} }
memset(lost_found_dir_blk->data, 0, cfg->block_size); memset(lost_found_dir_blk->data, 0, cfg->block_size);
current_size = 0; de_offset = 0;
de = (struct ext2_disk_dentry *)lost_found_dir_blk->data; disk_de = EXT2_DISK_DIRENTRY_BY_OFFSET(lost_found_dir_blk->data, de_offset);
ext2_fill_direntry(de, ".", 1, lost_found_inode, EXT2_FT_DIR); de = ext2_create_direntry(".", 1, lost_found_inode, EXT2_FT_DIR);
current_size += de->de_rec_len; ext2_write_direntry(disk_de, de);
de = EXT2_NEXT_DISK_DIRENTRY(de); de_offset += de->de_rec_len;
ext2_fill_direntry(de, "..", 2, EXT2_ROOT_INODE, EXT2_FT_DIR); k_heap_free(&direntry_heap, de);
current_size += de->de_rec_len;
disk_de = EXT2_DISK_DIRENTRY_BY_OFFSET(lost_found_dir_blk->data, de_offset);
de = ext2_create_direntry("..", 2, EXT2_ROOT_INODE, EXT2_FT_DIR);
de_offset += de->de_rec_len;
/* This was the last entry so add padding until end of block */ /* This was the last entry so add padding until end of block */
de->de_rec_len += cfg->block_size - current_size; de->de_rec_len += cfg->block_size - de_offset;
LOG_DBG("Initialized directory entry %p{%s(%d) %d %d %c}", ext2_write_direntry(disk_de, de);
de, de->de_name, de->de_name_len, de->de_inode, de->de_rec_len, k_heap_free(&direntry_heap, de);
de->de_file_type == EXT2_FT_DIR ? 'd' : 'f');
if (ext2_write_block(fs, lost_found_dir_blk) < 0) { if (ext2_write_block(fs, lost_found_dir_blk) < 0) {
ret = -EIO; ret = -EIO;

View file

@ -9,6 +9,7 @@
#include <zephyr/fs/fs.h> #include <zephyr/fs/fs.h>
#include <zephyr/logging/log.h> #include <zephyr/logging/log.h>
#include <zephyr/sys/util.h> #include <zephyr/sys/util.h>
#include <zephyr/sys/byteorder.h>
#include "ext2.h" #include "ext2.h"
#include "ext2_impl.h" #include "ext2_impl.h"
@ -40,12 +41,12 @@ void error_behavior(struct ext2_data *fs, const char *msg)
LOG_ERR("File system corrupted: %s", msg); LOG_ERR("File system corrupted: %s", msg);
/* If file system is not initialized panic */ /* If file system is not initialized panic */
if (!fs->sblock) { if (!initialized) {
LOG_ERR("File system data not found. Panic..."); LOG_ERR("File system data not found. Panic...");
k_panic(); k_panic();
} }
switch (EXT2_DATA_SBLOCK(fs)->s_errors) { switch (fs->sblock.s_errors) {
case EXT2_ERRORS_CONTINUE: case EXT2_ERRORS_CONTINUE:
/* Do nothing */ /* Do nothing */
break; break;
@ -193,6 +194,7 @@ int ext2_init_storage(struct ext2_data **fsp, const void *storage_dev, int flags
*fsp = fs; *fsp = fs;
fs->open_inodes = 0; fs->open_inodes = 0;
fs->flags = 0; fs->flags = 0;
fs->bgroup.num = -1;
ret = ext2_init_disk_access_backend(fs, storage_dev, flags); ret = ext2_init_disk_access_backend(fs, storage_dev, flags);
if (ret < 0) { if (ret < 0) {
@ -228,35 +230,35 @@ err:
return ret; return ret;
} }
int ext2_verify_superblock(struct ext2_disk_superblock *sb) int ext2_verify_disk_superblock(struct ext2_disk_superblock *sb)
{ {
/* Check if it is a valid Ext2 file system. */ /* Check if it is a valid Ext2 file system. */
if (sb->s_magic != EXT2_MAGIC_NUMBER) { if (sys_le16_to_cpu(sb->s_magic) != EXT2_MAGIC_NUMBER) {
LOG_ERR("Wrong file system magic number (%x)", sb->s_magic); LOG_ERR("Wrong file system magic number (%x)", sb->s_magic);
return -EINVAL; return -EINVAL;
} }
/* For now we don't support file systems with frag size different from block size */ /* 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) { if (sys_le32_to_cpu(sb->s_log_block_size) != sb->s_log_frag_size) {
LOG_ERR("Filesystem with frag_size != block_size is not supported"); LOG_ERR("Filesystem with frag_size != block_size is not supported");
return -ENOTSUP; return -ENOTSUP;
} }
/* Support only second revision */ /* Support only second revision */
if (sb->s_rev_level != EXT2_DYNAMIC_REV) { if (sys_le32_to_cpu(sb->s_rev_level) != EXT2_DYNAMIC_REV) {
LOG_ERR("Filesystem with revision %d is not supported", sb->s_rev_level); LOG_ERR("Filesystem with revision %d is not supported", sb->s_rev_level);
return -ENOTSUP; return -ENOTSUP;
} }
if (sb->s_inode_size != EXT2_GOOD_OLD_INODE_SIZE) { if (sys_le16_to_cpu(sb->s_inode_size) != EXT2_GOOD_OLD_INODE_SIZE) {
LOG_ERR("Filesystem with inode size %d is not supported", sb->s_inode_size); LOG_ERR("Filesystem with inode size %d is not supported", sb->s_inode_size);
return -ENOTSUP; return -ENOTSUP;
} }
/* Check if file system may contain errors. */ /* Check if file system may contain errors. */
if (sb->s_state == EXT2_ERROR_FS) { if (sys_le16_to_cpu(sb->s_state) == EXT2_ERROR_FS) {
LOG_WRN("File system may contain errors."); LOG_WRN("File system may contain errors.");
switch (sb->s_errors) { switch (sys_le16_to_cpu(sb->s_errors)) {
case EXT2_ERRORS_CONTINUE: case EXT2_ERRORS_CONTINUE:
break; break;
@ -272,18 +274,18 @@ int ext2_verify_superblock(struct ext2_disk_superblock *sb)
} }
} }
if ((sb->s_feature_incompat & EXT2_FEATURE_INCOMPAT_FILETYPE) == 0) { if ((sys_le32_to_cpu(sb->s_feature_incompat) & EXT2_FEATURE_INCOMPAT_FILETYPE) == 0) {
LOG_ERR("File system without file type stored in de is not supported"); LOG_ERR("File system without file type stored in de is not supported");
return -ENOTSUP; return -ENOTSUP;
} }
if ((sb->s_feature_incompat & ~EXT2_FEATURE_INCOMPAT_SUPPORTED) > 0) { if ((sys_le32_to_cpu(sb->s_feature_incompat) & ~EXT2_FEATURE_INCOMPAT_SUPPORTED) > 0) {
LOG_ERR("File system can't be mounted. Incompat features %d not supported", LOG_ERR("File system can't be mounted. Incompat features %d not supported",
(sb->s_feature_incompat & ~EXT2_FEATURE_INCOMPAT_SUPPORTED)); (sb->s_feature_incompat & ~EXT2_FEATURE_INCOMPAT_SUPPORTED));
return -ENOTSUP; return -ENOTSUP;
} }
if ((sb->s_feature_ro_compat & ~EXT2_FEATURE_RO_COMPAT_SUPPORTED) > 0) { if ((sys_le32_to_cpu(sb->s_feature_ro_compat) & ~EXT2_FEATURE_RO_COMPAT_SUPPORTED) > 0) {
LOG_WRN("File system can be mounted read only. RO features %d detected.", LOG_WRN("File system can be mounted read only. RO features %d detected.",
(sb->s_feature_ro_compat & ~EXT2_FEATURE_RO_COMPAT_SUPPORTED)); (sb->s_feature_ro_compat & ~EXT2_FEATURE_RO_COMPAT_SUPPORTED));
return -EROFS; return -EROFS;
@ -291,9 +293,15 @@ int ext2_verify_superblock(struct ext2_disk_superblock *sb)
LOG_DBG("ino_cnt:%d blk_cnt:%d blk_per_grp:%d ino_per_grp:%d free_ino:%d free_blk:%d " 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", "blk_size:%d ino_size:%d mntc:%d",
sb->s_inodes_count, sb->s_blocks_count, sb->s_blocks_per_group, sys_le32_to_cpu(sb->s_inodes_count),
sb->s_inodes_per_group, sb->s_free_inodes_count, sb->s_free_blocks_count, sys_le32_to_cpu(sb->s_blocks_count),
1024 << sb->s_log_block_size, sb->s_inode_size, sb->s_mnt_count); sys_le32_to_cpu(sb->s_blocks_per_group),
sys_le32_to_cpu(sb->s_inodes_per_group),
sys_le32_to_cpu(sb->s_free_inodes_count),
sys_le32_to_cpu(sb->s_free_blocks_count),
sys_le32_to_cpu(1024 << sb->s_log_block_size),
sys_le16_to_cpu(sb->s_inode_size),
sys_le16_to_cpu(sb->s_mnt_count));
return 0; return 0;
} }
@ -302,40 +310,37 @@ int ext2_init_fs(struct ext2_data *fs)
int ret = 0; int ret = 0;
/* Fetch superblock */ /* Fetch superblock */
if (fs->block_size == 1024) { ret = ext2_fetch_superblock(fs);
fs->sblock_offset = 0; if (ret < 0) {
fs->sblock = ext2_get_block(fs, 1); return ret;
} 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)) { if (!(fs->flags & EXT2_DATA_FLAGS_RO)) {
/* Update sblock fields set during the successful mount. */ /* Update sblock fields set during the successful mount. */
EXT2_DATA_SBLOCK(fs)->s_state = EXT2_ERROR_FS; fs->sblock.s_state = EXT2_ERROR_FS;
EXT2_DATA_SBLOCK(fs)->s_mnt_count += 1; fs->sblock.s_mnt_count += 1;
ret = ext2_write_block(fs, fs->sblock); ret = ext2_commit_superblock(fs);
if (ret < 0) { if (ret < 0) {
goto out; return ret;
} }
} }
ret = ext2_fetch_block_group(fs, 0); ret = ext2_fetch_block_group(fs, 0);
if (ret < 0) { if (ret < 0) {
goto out; return ret;
} }
ret = ext2_fetch_bg_ibitmap(&fs->bgroup); ret = ext2_fetch_bg_ibitmap(&fs->bgroup);
if (ret < 0) {
return ret;
}
ret = ext2_fetch_bg_bbitmap(&fs->bgroup); ret = ext2_fetch_bg_bbitmap(&fs->bgroup);
if (ret < 0) {
return ret;
}
/* Validate superblock */ /* Validate superblock */
uint32_t set; uint32_t set;
struct ext2_disk_superblock *sb = EXT2_DATA_SBLOCK(fs); struct ext2_superblock *sb = &fs->sblock;
uint32_t fs_blocks = sb->s_blocks_count - sb->s_first_data_block; uint32_t fs_blocks = sb->s_blocks_count - sb->s_first_data_block;
set = ext2_bitmap_count_set(BGROUP_BLOCK_BITMAP(&fs->bgroup), fs_blocks); set = ext2_bitmap_count_set(BGROUP_BLOCK_BITMAP(&fs->bgroup), fs_blocks);
@ -352,10 +357,6 @@ int ext2_init_fs(struct ext2_data *fs)
return -EINVAL; return -EINVAL;
} }
return 0; return 0;
out:
ext2_drop_block(fs->sblock);
fs->sblock = NULL;
return ret;
} }
int ext2_close_fs(struct ext2_data *fs) int ext2_close_fs(struct ext2_data *fs)
@ -371,10 +372,10 @@ int ext2_close_fs(struct ext2_data *fs)
/* To save file system as correct it must be writable and without errors */ /* To save file system as correct it must be writable and without errors */
if (!(fs->flags & (EXT2_DATA_FLAGS_RO | EXT2_DATA_FLAGS_ERR))) { if (!(fs->flags & (EXT2_DATA_FLAGS_RO | EXT2_DATA_FLAGS_ERR))) {
EXT2_DATA_SBLOCK(fs)->s_state = EXT2_VALID_FS; fs->sblock.s_state = EXT2_VALID_FS;
ret = ext2_write_block(fs, fs->sblock); ret = ext2_commit_superblock(fs);
if (ret < 0) { if (ret < 0) {
return -EIO; return ret;
} }
} }
@ -386,8 +387,6 @@ int ext2_close_fs(struct ext2_data *fs)
if (fs->backend_ops->sync(fs) < 0) { if (fs->backend_ops->sync(fs) < 0) {
return -EIO; return -EIO;
} }
ext2_drop_block(fs->sblock);
fs->sblock = NULL;
return 0; return 0;
} }
@ -565,7 +564,9 @@ static int64_t find_dir_entry(struct ext2_inode *inode, const char *name, size_t
{ {
int rc; int rc;
uint32_t block, block_off, offset = 0; uint32_t block, block_off, offset = 0;
int64_t ino = -1;
struct ext2_data *fs = inode->i_fs; struct ext2_data *fs = inode->i_fs;
struct ext2_direntry *de;
while (offset < inode->i_size) { while (offset < inode->i_size) {
block = offset / fs->block_size; block = offset / fs->block_size;
@ -576,24 +577,31 @@ static int64_t find_dir_entry(struct ext2_inode *inode, const char *name, size_t
return rc; return rc;
} }
struct ext2_disk_dentry *de = struct ext2_disk_direntry *disk_de =
(struct ext2_disk_dentry *)(inode_current_block_mem(inode) + block_off); EXT2_DISK_DIRENTRY_BY_OFFSET(inode_current_block_mem(inode), block_off);
de = ext2_fetch_direntry(disk_de);
if (de == NULL) {
return -EINVAL;
}
if (len == de->de_name_len && strncmp(de->de_name, name, len) == 0) { if (len == de->de_name_len && strncmp(de->de_name, name, len) == 0) {
ino = de->de_inode;
if (r_offset) { if (r_offset) {
/* Return offset*/ /* Return offset*/
*r_offset = offset; *r_offset = offset;
} }
goto success;
return (int64_t)de->de_inode;
} }
/* move to the next directory entry */
/* move to next directory entry */
offset += de->de_rec_len; offset += de->de_rec_len;
k_heap_free(&direntry_heap, de);
} }
return -EINVAL; return -EINVAL;
success:
k_heap_free(&direntry_heap, de);
return (int64_t)ino;
} }
/* Inode operations --------------------------------------------------------- */ /* Inode operations --------------------------------------------------------- */
@ -810,13 +818,14 @@ int ext2_get_direntry(struct ext2_file *dir, struct fs_dirent *ent)
return rc; return rc;
} }
struct ext2_disk_dentry *de =
(struct ext2_disk_dentry *)(inode_current_block_mem(dir->f_inode) + block_off);
struct ext2_inode *inode = NULL; struct ext2_inode *inode = NULL;
struct ext2_disk_direntry *disk_de =
EXT2_DISK_DIRENTRY_BY_OFFSET(inode_current_block_mem(dir->f_inode), block_off);
struct ext2_direntry *de = ext2_fetch_direntry(disk_de);
LOG_DBG("inode=%d name_len=%d rec_len=%d", de->de_inode, de->de_name_len, de->de_rec_len); LOG_DBG("inode=%d name_len=%d rec_len=%d", de->de_inode, de->de_name_len, de->de_rec_len);
if (de->de_name_len > EXT2_MAX_FILE_NAME) { if (de == NULL) {
LOG_ERR("Read directory entry name too long"); LOG_ERR("Read directory entry name too long");
return -EINVAL; return -EINVAL;
} }
@ -852,6 +861,7 @@ int ext2_get_direntry(struct ext2_file *dir, struct fs_dirent *ent)
dir->f_off += de->de_rec_len; dir->f_off += de->de_rec_len;
out: out:
k_heap_free(&direntry_heap, de);
ext2_inode_drop(inode); ext2_inode_drop(inode);
return ret; return ret;
} }
@ -882,8 +892,8 @@ static int ext2_create_inode(struct ext2_data *fs, struct ext2_inode *parent,
/* Block group current block is already fetched. We don't have to do it again. /* Block group current block is already fetched. We don't have to do it again.
* (It was done above in ext2_alloc_inode function.) * (It was done above in ext2_alloc_inode function.)
*/ */
current_disk_bgroup(&fs->bgroup)->bg_used_dirs_count += 1; fs->bgroup.bg_used_dirs_count += 1;
rc = ext2_write_block(fs, fs->bgroup.block); rc = ext2_commit_bg(fs);
if (rc < 0) { if (rc < 0) {
return rc; return rc;
} }
@ -893,16 +903,20 @@ static int ext2_create_inode(struct ext2_data *fs, struct ext2_inode *parent,
return rc; return rc;
} }
void ext2_fill_direntry(struct ext2_disk_dentry *de, const char *name, uint8_t namelen, struct ext2_direntry *ext2_create_direntry(const char *name, uint8_t namelen, uint32_t ino,
uint32_t ino, uint8_t filetype) uint8_t filetype)
{ {
uint32_t reclen = sizeof(struct ext2_disk_dentry) + namelen; __ASSERT(namelen <= EXT2_MAX_FILE_NAME, "Name length to long");
uint32_t prog_rec_len = sizeof(struct ext2_direntry) + namelen;
struct ext2_direntry *de = k_heap_alloc(&direntry_heap, prog_rec_len, K_FOREVER);
/* Size of future disk structure. */
uint32_t reclen = sizeof(struct ext2_disk_direntry) + namelen;
/* Align reclen to 4 bytes. */ /* Align reclen to 4 bytes. */
reclen = ROUND_UP(reclen, 4); reclen = ROUND_UP(reclen, 4);
__ASSERT(namelen <= EXT2_MAX_FILE_NAME, "Name length to long");
de->de_inode = ino; de->de_inode = ino;
de->de_rec_len = reclen; de->de_rec_len = reclen;
de->de_name_len = (uint8_t)namelen; de->de_name_len = (uint8_t)namelen;
@ -912,16 +926,17 @@ void ext2_fill_direntry(struct ext2_disk_dentry *de, const char *name, uint8_t n
LOG_DBG("Initialized directory entry %p{%s(%d) %d %d %c}", LOG_DBG("Initialized directory entry %p{%s(%d) %d %d %c}",
de, de->de_name, de->de_name_len, de->de_inode, de->de_rec_len, de, de->de_name, de->de_name_len, de->de_inode, de->de_rec_len,
de->de_file_type == EXT2_FT_DIR ? 'd' : 'f'); de->de_file_type == EXT2_FT_DIR ? 'd' : 'f');
return de;
} }
static int ext2_add_direntry(struct ext2_inode *dir, struct ext2_disk_dentry *entry) static int ext2_add_direntry(struct ext2_inode *dir, struct ext2_direntry *entry)
{ {
LOG_DBG("Adding entry: {in=%d type=%d name_len=%d} to directory (in=%d)", LOG_DBG("Adding entry: {in=%d type=%d name_len=%d} to directory (in=%d)",
entry->de_inode, entry->de_file_type, entry->de_name_len, dir->i_id); entry->de_inode, entry->de_file_type, entry->de_name_len, dir->i_id);
int rc = 0; int rc = 0;
uint32_t block_size = dir->i_fs->block_size; uint32_t block_size = dir->i_fs->block_size;
uint32_t entry_size = sizeof(struct ext2_disk_dentry) + entry->de_name_len; uint32_t entry_size = sizeof(struct ext2_disk_direntry) + entry->de_name_len;
if (entry_size > block_size) { if (entry_size > block_size) {
return -EINVAL; return -EINVAL;
@ -937,31 +952,33 @@ static int ext2_add_direntry(struct ext2_inode *dir, struct ext2_disk_dentry *en
} }
uint32_t offset = 0; uint32_t offset = 0;
uint16_t reclen;
struct ext2_disk_direntry *de = 0;
struct ext2_disk_dentry *de = 0;
/* loop must be executed at least once, because block_size > 0 */ /* loop must be executed at least once, because block_size > 0 */
while (offset < block_size) { while (offset < block_size) {
de = (struct ext2_disk_dentry *)(inode_current_block_mem(dir) + offset); de = EXT2_DISK_DIRENTRY_BY_OFFSET(inode_current_block_mem(dir), offset);
if (offset + de->de_rec_len == block_size) { reclen = ext2_get_disk_direntry_reclen(de);
if (offset + reclen == block_size) {
break; break;
} }
offset += de->de_rec_len; offset += reclen;
} }
uint32_t occupied = sizeof(struct ext2_disk_dentry) + de->de_name_len; uint32_t occupied = sizeof(struct ext2_disk_direntry) + ext2_get_disk_direntry_namelen(de);
/* Align to 4 bytes */ /* Align to 4 bytes */
occupied = ROUND_UP(occupied, 4); occupied = ROUND_UP(occupied, 4);
LOG_DBG("Occupied: %d total: %d needed: %d", occupied, de->de_rec_len, entry_size); LOG_DBG("Occupied: %d total: %d needed: %d", occupied, reclen, entry_size);
if (de->de_rec_len - occupied >= entry_size) { if (reclen - occupied >= entry_size) {
/* Entry fits into current block */ /* Entry fits into current block */
offset += occupied; offset += occupied;
de->de_rec_len = occupied;
entry->de_rec_len = block_size - offset; entry->de_rec_len = block_size - offset;
ext2_set_disk_direntry_reclen(de, occupied);
} else { } else {
LOG_DBG("Allocating new block for directory"); LOG_DBG("Allocating new block for directory");
@ -991,7 +1008,9 @@ static int ext2_add_direntry(struct ext2_inode *dir, struct ext2_disk_dentry *en
entry->de_inode, entry->de_file_type, entry->de_rec_len, entry->de_name_len, entry->de_inode, entry->de_file_type, entry->de_rec_len, entry->de_name_len,
inode_current_block(dir)->num, dir->i_id); inode_current_block(dir)->num, dir->i_id);
memcpy(inode_current_block_mem(dir) + offset, entry, entry_size);
de = EXT2_DISK_DIRENTRY_BY_OFFSET(inode_current_block_mem(dir), offset);
ext2_write_direntry(de, entry);
rc = ext2_commit_inode_block(dir); rc = ext2_commit_inode_block(dir);
return rc; return rc;
@ -1001,6 +1020,7 @@ int ext2_create_file(struct ext2_inode *parent, struct ext2_inode *new_inode,
struct ext2_lookup_args *args) struct ext2_lookup_args *args)
{ {
int rc, ret = 0; int rc, ret = 0;
struct ext2_direntry *entry;
struct ext2_data *fs = parent->i_fs; struct ext2_data *fs = parent->i_fs;
rc = ext2_create_inode(fs, args->inode, new_inode, FS_DIR_ENTRY_FILE); rc = ext2_create_inode(fs, args->inode, new_inode, FS_DIR_ENTRY_FILE);
@ -1008,15 +1028,7 @@ int ext2_create_file(struct ext2_inode *parent, struct ext2_inode *new_inode,
return rc; return rc;
} }
size_t dentry_size = sizeof(struct ext2_disk_dentry) + args->name_len; entry = ext2_create_direntry(args->path + args->name_pos, args->name_len, new_inode->i_id,
struct ext2_disk_dentry *entry = k_heap_alloc(&direntry_heap, dentry_size, K_NO_WAIT);
if (entry == NULL) {
ret = -ENOMEM;
goto out;
}
ext2_fill_direntry(entry, args->path + args->name_pos, args->name_len, new_inode->i_id,
EXT2_FT_REG_FILE); EXT2_FT_REG_FILE);
rc = ext2_add_direntry(parent, entry); rc = ext2_add_direntry(parent, entry);
@ -1041,6 +1053,8 @@ int ext2_create_dir(struct ext2_inode *parent, struct ext2_inode *new_inode,
struct ext2_lookup_args *args) struct ext2_lookup_args *args)
{ {
int rc, ret = 0; int rc, ret = 0;
struct ext2_direntry *entry;
struct ext2_disk_direntry *disk_de;
struct ext2_data *fs = parent->i_fs; struct ext2_data *fs = parent->i_fs;
uint32_t block_size = parent->i_fs->block_size; uint32_t block_size = parent->i_fs->block_size;
@ -1052,15 +1066,7 @@ int ext2_create_dir(struct ext2_inode *parent, struct ext2_inode *new_inode,
/* Directory must have at least one block */ /* Directory must have at least one block */
new_inode->i_size = block_size; new_inode->i_size = block_size;
size_t dentry_size = sizeof(struct ext2_disk_dentry) + args->name_len; entry = ext2_create_direntry(args->path + args->name_pos, args->name_len, new_inode->i_id,
struct ext2_disk_dentry *entry = k_heap_alloc(&direntry_heap, dentry_size, K_NO_WAIT);
if (entry == NULL) {
ret = -ENOMEM;
goto out;
}
ext2_fill_direntry(entry, args->path + args->name_pos, args->name_len, new_inode->i_id,
EXT2_FT_DIR); EXT2_FT_DIR);
rc = ext2_add_direntry(parent, entry); rc = ext2_add_direntry(parent, entry);
@ -1072,12 +1078,11 @@ int ext2_create_dir(struct ext2_inode *parent, struct ext2_inode *new_inode,
/* Successfully added to directory */ /* Successfully added to directory */
new_inode->i_links_count += 1; new_inode->i_links_count += 1;
/* Add "." directory entry */ k_heap_free(&direntry_heap, entry);
entry->de_inode = new_inode->i_id;
entry->de_name_len = 1; /* Create "." directory entry */
entry->de_file_type = EXT2_FT_DIR; entry = ext2_create_direntry(".", 1, new_inode->i_id, EXT2_FT_DIR);
entry->de_rec_len = block_size; entry->de_rec_len = block_size;
memcpy(entry->de_name, ".", 1);
/* It has to be inserted manually */ /* It has to be inserted manually */
rc = ext2_fetch_inode_block(new_inode, 0); rc = ext2_fetch_inode_block(new_inode, 0);
@ -1086,15 +1091,15 @@ int ext2_create_dir(struct ext2_inode *parent, struct ext2_inode *new_inode,
goto out; goto out;
} }
memcpy(inode_current_block_mem(new_inode), entry, sizeof(struct ext2_disk_dentry) + 1); disk_de = EXT2_DISK_DIRENTRY_BY_OFFSET(inode_current_block_mem(new_inode), 0);
ext2_write_direntry(disk_de, entry);
new_inode->i_links_count += 1; new_inode->i_links_count += 1;
k_heap_free(&direntry_heap, entry);
/* Add ".." directory entry */ /* Add ".." directory entry */
entry->de_inode = parent->i_id; entry = ext2_create_direntry("..", 2, parent->i_id, EXT2_FT_DIR);
entry->de_name_len = 2;
entry->de_file_type = EXT2_FT_DIR;
memcpy(entry->de_name, "..", 2);
rc = ext2_add_direntry(new_inode, entry); rc = ext2_add_direntry(new_inode, entry);
if (rc < 0) { if (rc < 0) {
@ -1144,10 +1149,11 @@ static int ext2_del_direntry(struct ext2_inode *parent, uint32_t offset)
} }
if (blk_off == 0) { if (blk_off == 0) {
struct ext2_disk_dentry *de = struct ext2_disk_direntry *de =
(struct ext2_disk_dentry *)(inode_current_block_mem(parent)); EXT2_DISK_DIRENTRY_BY_OFFSET(inode_current_block_mem(parent), 0);
uint16_t reclen = ext2_get_disk_direntry_reclen(de);
if (de->de_rec_len == block_size) { if (reclen == block_size) {
/* Remove whole block */ /* Remove whole block */
uint32_t last_blk = parent->i_size / block_size - 1; uint32_t last_blk = parent->i_size / block_size - 1;
@ -1170,14 +1176,13 @@ static int ext2_del_direntry(struct ext2_inode *parent, uint32_t offset)
return rc; return rc;
} }
} else { } else {
/* Move next entry to beginning of block */ /* Move next entry to beginning of block */
struct ext2_disk_dentry *next = (struct ext2_disk_dentry *) struct ext2_disk_direntry *next =
(inode_current_block_mem(parent) + de->de_rec_len); EXT2_DISK_DIRENTRY_BY_OFFSET(inode_current_block_mem(parent), reclen);
uint32_t new_size = de->de_rec_len + next->de_rec_len; uint16_t next_reclen = ext2_get_disk_direntry_reclen(next);
memmove(de, next, next->de_rec_len); memmove(de, next, next_reclen);
de->de_rec_len = new_size; ext2_set_disk_direntry_reclen(de, reclen + next_reclen);
rc = ext2_commit_inode_block(parent); rc = ext2_commit_inode_block(parent);
if (rc < 0) { if (rc < 0) {
@ -1188,21 +1193,24 @@ static int ext2_del_direntry(struct ext2_inode *parent, uint32_t offset)
} else { } else {
/* Entry inside the block */ /* Entry inside the block */
uint32_t cur = 0; uint32_t cur = 0;
uint16_t reclen;
struct ext2_disk_dentry *de = struct ext2_disk_direntry *de =
(struct ext2_disk_dentry *)(inode_current_block_mem(parent)); EXT2_DISK_DIRENTRY_BY_OFFSET(inode_current_block_mem(parent), 0);
reclen = ext2_get_disk_direntry_reclen(de);
/* find previous entry */ /* find previous entry */
while (cur + de->de_rec_len < blk_off) { while (cur + reclen < blk_off) {
cur += de->de_rec_len; cur += reclen;
de = (struct ext2_disk_dentry *)(inode_current_block_mem(parent) + cur); de = EXT2_DISK_DIRENTRY_BY_OFFSET(inode_current_block_mem(parent), cur);
reclen = ext2_get_disk_direntry_reclen(de);
} }
struct ext2_disk_dentry *del_entry = struct ext2_disk_direntry *del_entry =
(struct ext2_disk_dentry *)(inode_current_block_mem(parent) + blk_off); EXT2_DISK_DIRENTRY_BY_OFFSET(inode_current_block_mem(parent), blk_off);
uint16_t del_reclen = ext2_get_disk_direntry_reclen(del_entry);
de->de_rec_len += del_entry->de_rec_len;
ext2_set_disk_direntry_reclen(de, reclen + del_reclen);
rc = ext2_commit_inode_block(parent); rc = ext2_commit_inode_block(parent);
if (rc < 0) { if (rc < 0) {
return rc; return rc;
@ -1245,15 +1253,15 @@ static int can_unlink(struct ext2_inode *inode)
/* If directory check if it is empty */ /* If directory check if it is empty */
uint32_t offset = 0; uint32_t offset = 0;
struct ext2_disk_dentry *de; struct ext2_disk_direntry *de;
/* Get first entry */ /* Get first entry */
de = (struct ext2_disk_dentry *)(inode_current_block_mem(inode)); de = EXT2_DISK_DIRENTRY_BY_OFFSET(inode_current_block_mem(inode), 0);
offset += de->de_rec_len; offset += ext2_get_disk_direntry_reclen(de);
/* Get second entry */ /* Get second entry */
de = (struct ext2_disk_dentry *)(inode_current_block_mem(inode) + de->de_rec_len); de = EXT2_DISK_DIRENTRY_BY_OFFSET(inode_current_block_mem(inode), offset);
offset += de->de_rec_len; offset += ext2_get_disk_direntry_reclen(de);
uint32_t block_size = inode->i_fs->block_size; uint32_t block_size = inode->i_fs->block_size;
@ -1304,7 +1312,7 @@ int ext2_replace_file(struct ext2_lookup_args *args_from, struct ext2_lookup_arg
LOG_DBG("Inode: %d Inode to replace: %d", args_from->inode->i_id, args_to->inode->i_id); LOG_DBG("Inode: %d Inode to replace: %d", args_from->inode->i_id, args_to->inode->i_id);
int rc = 0; int rc = 0;
struct ext2_disk_dentry *de; struct ext2_disk_direntry *de;
uint32_t block_size = args_from->parent->i_fs->block_size; uint32_t block_size = args_from->parent->i_fs->block_size;
uint32_t from_offset = args_from->offset; uint32_t from_offset = args_from->offset;
@ -1316,16 +1324,21 @@ int ext2_replace_file(struct ext2_lookup_args *args_from, struct ext2_lookup_arg
return rc; return rc;
} }
de = (struct ext2_disk_dentry *)(inode_current_block_mem(args_from->parent) + from_blk_off); de = EXT2_DISK_DIRENTRY_BY_OFFSET(inode_current_block_mem(args_from->parent), from_blk_off);
/* record file type */ /* record file type */
uint8_t file_type = de->de_file_type; uint8_t file_type = ext2_get_disk_direntry_type(de);
de->de_inode = args_to->inode->i_id; /* NOTE: Replace the inode number in removed entry with inode of file that will be replaced
* with new one. Thanks to that we can use the function that unlinks directory entry to get
* rid of old directory entry and link to inode that will no longer be referenced by the
* directory entry after it is replaced with moved file.
*/
ext2_set_disk_direntry_inode(de, args_to->inode->i_id);
rc = ext2_inode_unlink(args_from->parent, args_to->inode, args_from->offset); rc = ext2_inode_unlink(args_from->parent, args_to->inode, args_from->offset);
if (rc < 0) { if (rc < 0) {
/* restore made changes */ /* restore the old inode number */
de->de_inode = args_from->inode->i_id; ext2_set_disk_direntry_inode(de, args_from->inode->i_id);
return rc; return rc;
} }
@ -1338,17 +1351,16 @@ int ext2_replace_file(struct ext2_lookup_args *args_from, struct ext2_lookup_arg
return rc; return rc;
} }
de = (struct ext2_disk_dentry *)(inode_current_block_mem(args_to->parent) + to_blk_off); de = EXT2_DISK_DIRENTRY_BY_OFFSET(inode_current_block_mem(args_to->parent), to_blk_off);
/* change inode of new entry */ /* change inode of new entry */
de->de_inode = args_from->inode->i_id; ext2_set_disk_direntry_inode(de, args_from->inode->i_id);
de->de_file_type = file_type; ext2_set_disk_direntry_type(de, file_type);
rc = ext2_commit_inode_block(args_to->parent); rc = ext2_commit_inode_block(args_to->parent);
if (rc < 0) { if (rc < 0) {
return rc; return rc;
} }
return 0; return 0;
} }
@ -1370,15 +1382,18 @@ int ext2_move_file(struct ext2_lookup_args *args_from, struct ext2_lookup_args *
return rc; return rc;
} }
struct ext2_disk_dentry *de; struct ext2_disk_direntry *de;
de = (struct ext2_disk_dentry *)(inode_current_block_mem(fparent) + blk_off); de = EXT2_DISK_DIRENTRY_BY_OFFSET(inode_current_block_mem(fparent), blk_off);
uint16_t reclen = ext2_get_disk_direntry_reclen(de);
/* If new name fits in old entry, then just copy it there */ /* If new name fits in old entry, then just copy it there */
if (de->de_rec_len - sizeof(struct ext2_disk_dentry) >= args_to->name_len) { if (reclen - sizeof(struct ext2_disk_direntry) >= args_to->name_len) {
LOG_DBG("Old entry is modified to hold new name"); LOG_DBG("Old entry is modified to hold new name");
de->de_name_len = args_to->name_len; ext2_set_disk_direntry_namelen(de, args_to->name_len);
memcpy(de->de_name, args_to->path + args_to->name_pos, args_to->name_len); ext2_set_disk_direntry_name(de, args_to->path + args_to->name_pos,
args_to->name_len);
rc = ext2_commit_inode_block(fparent); rc = ext2_commit_inode_block(fparent);
return rc; return rc;
@ -1394,22 +1409,16 @@ int ext2_move_file(struct ext2_lookup_args *args_from, struct ext2_lookup_args *
return rc; return rc;
} }
struct ext2_disk_dentry *old_de, *new_de; struct ext2_disk_direntry *old_de;
struct ext2_direntry *new_de;
old_de = (struct ext2_disk_dentry *)(inode_current_block_mem(fparent) + blk_off); old_de = EXT2_DISK_DIRENTRY_BY_OFFSET(inode_current_block_mem(fparent), blk_off);
size_t dentry_size = sizeof(struct ext2_disk_dentry) + args_to->name_len; uint32_t inode = ext2_get_disk_direntry_inode(old_de);
uint8_t file_type = ext2_get_disk_direntry_type(old_de);
new_de = k_heap_alloc(&direntry_heap, dentry_size, K_NO_WAIT); new_de = ext2_create_direntry(args_to->path + args_to->name_pos, args_to->name_len, inode,
if (new_de == NULL) { file_type);
ret = -ENOMEM;
goto out;
}
new_de->de_inode = old_de->de_inode;
new_de->de_name_len = args_to->name_len;
new_de->de_file_type = old_de->de_file_type;
memcpy(new_de->de_name, args_to->path + args_to->name_pos, args_to->name_len);
rc = ext2_add_direntry(tparent, new_de); rc = ext2_add_direntry(tparent, new_de);
if (rc < 0) { if (rc < 0) {

View file

@ -12,6 +12,8 @@
#include "ext2_struct.h" #include "ext2_struct.h"
extern struct k_heap direntry_heap;
void error_behavior(struct ext2_data *fs, const char *msg); void error_behavior(struct ext2_data *fs, const char *msg);
/* Memory allocation for ext2 implementation */ /* Memory allocation for ext2 implementation */
@ -78,7 +80,7 @@ int ext2_init_storage(struct ext2_data **fsp, const void *storage_dev, int flags
* @retval -EINVAL when superblock is not valid and file system cannot be mounted at all * @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 * @retval -ENOTSUP when superblock has some field set to value that we don't support
*/ */
int ext2_verify_superblock(struct ext2_disk_superblock *sb); int ext2_verify_disk_superblock(struct ext2_disk_superblock *sb);
/** /**
* @brief Initialize all data needed to perform operations on file system * @brief Initialize all data needed to perform operations on file system
@ -235,20 +237,21 @@ int ext2_inode_sync(struct ext2_inode *inode);
int ext2_get_direntry(struct ext2_file *dir, struct fs_dirent *ent); int ext2_get_direntry(struct ext2_file *dir, struct fs_dirent *ent);
/** /**
* @brief Fill in the directory structure with given attributes * @brief Create a directory entry with given attributes
* *
* Fills in all the fields of directory entry. Automatically calculates de_rec_len field. * Automatically calculates and sets de_rec_len field.
* *
* NOTE: if you need to adjust the size (e.g. when this entry is last in the block) * NOTE: if you need to adjust the size (e.g. when this entry is the last one in the block)
* then just update the size after this function returns. * then just update the size after this function returns.
* *
* @param de Structure to fill in
* @param name Name of direntry * @param name Name of direntry
* @param namelen Length of name * @param namelen Length of name
* @param ino Inode associated with that entry * @param ino Inode associated with that entry
* @param filetype File type of that entry * @param filetype File type of that entry
*
* @returns structure allocated on direntry_heap filled with given data
*/ */
void ext2_fill_direntry(struct ext2_disk_dentry *de, const char *name, uint8_t len, uint32_t ino, struct ext2_direntry *ext2_create_direntry(const char *name, uint8_t namelen, uint32_t ino,
uint8_t filetype); uint8_t filetype);
/** /**

View file

@ -390,7 +390,7 @@ static int ext2_mount(struct fs_mount_t *mountp)
goto err; goto err;
} }
ret = ext2_verify_superblock(&superblock); ret = ext2_verify_disk_superblock(&superblock);
if (ret == 0) { if (ret == 0) {
fs->block_size = 1024 << superblock.s_log_block_size; fs->block_size = 1024 << superblock.s_log_block_size;
@ -614,8 +614,8 @@ static int ext2_statvfs(struct fs_mount_t *mountp, const char *path, struct fs_s
stat->f_bsize = fs->block_size; stat->f_bsize = fs->block_size;
stat->f_frsize = fs->block_size; stat->f_frsize = fs->block_size;
stat->f_blocks = EXT2_DATA_SBLOCK(fs)->s_blocks_count; stat->f_blocks = fs->sblock.s_blocks_count;
stat->f_bfree = EXT2_DATA_SBLOCK(fs)->s_free_blocks_count; stat->f_bfree = fs->sblock.s_free_blocks_count;
return 0; return 0;
} }

View file

@ -56,7 +56,7 @@ struct ext2_disk_superblock {
uint32_t s_journal_dev; uint32_t s_journal_dev;
uint32_t s_last_orphan; uint32_t s_last_orphan;
uint8_t s_padding[788]; uint8_t s_padding[788];
}; } __packed;
struct ext2_disk_bgroup { struct ext2_disk_bgroup {
uint32_t bg_block_bitmap; uint32_t bg_block_bitmap;
@ -67,7 +67,7 @@ struct ext2_disk_bgroup {
uint16_t bg_used_dirs_count; uint16_t bg_used_dirs_count;
uint16_t bg_pad; uint16_t bg_pad;
uint8_t bg_reserved[12]; uint8_t bg_reserved[12];
}; } __packed;
struct ext2_disk_inode { struct ext2_disk_inode {
uint16_t i_mode; uint16_t i_mode;
@ -88,24 +88,47 @@ struct ext2_disk_inode {
uint32_t i_dir_acl; uint32_t i_dir_acl;
uint32_t i_faddr; uint32_t i_faddr;
uint8_t i_osd2[12]; uint8_t i_osd2[12];
}; } __packed;
struct ext2_disk_dentry { struct ext2_disk_direntry {
uint32_t de_inode; uint32_t de_inode;
uint16_t de_rec_len; uint16_t de_rec_len;
uint8_t de_name_len; uint8_t de_name_len;
uint8_t de_file_type; uint8_t de_file_type;
char de_name[]; char de_name[];
}; } __packed;
/* Max size of directory entry */
#define MAX_DIRENTRY_SIZE (sizeof(struct ext2_disk_dentry) + UINT8_MAX)
/* Program structures ------------------------------------------------------- */ /* Program structures ------------------------------------------------------- */
struct ext2_superblock {
uint32_t s_inodes_count;
uint32_t s_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;
uint16_t s_mnt_count;
uint16_t s_max_mnt_count;
uint16_t s_magic;
uint16_t s_state;
uint16_t s_errors;
uint32_t s_creator_os;
uint32_t s_rev_level;
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;
};
#define EXT2_BLOCK_NUM_SIZE (sizeof(uint32_t)) #define EXT2_BLOCK_NUM_SIZE (sizeof(uint32_t))
#define EXT2_NEXT_DISK_DIRENTRY(de) \ #define EXT2_DISK_DIRENTRY_BY_OFFSET(addr, offset) \
((struct ext2_disk_dentry *)(((uint8_t *)(de)) + (de)->de_rec_len)) ((struct ext2_disk_direntry *)(((uint8_t *)(addr)) + (offset)))
#define EXT2_BLOCK_ASSIGNED BIT(0) #define EXT2_BLOCK_ASSIGNED BIT(0)
@ -115,7 +138,6 @@ struct ext2_block {
uint8_t *data; uint8_t *data;
} __aligned(sizeof(void *)); } __aligned(sizeof(void *));
#define BGROUP_BLOCK(bg) ((struct ext2_disk_bgroup *)(bg)->block->data)
#define BGROUP_INODE_TABLE(bg) ((struct ext2_disk_inode *)(bg)->inode_table->data) #define BGROUP_INODE_TABLE(bg) ((struct ext2_disk_inode *)(bg)->inode_table->data)
#define BGROUP_INODE_BITMAP(bg) ((uint8_t *)(bg)->inode_bitmap->data) #define BGROUP_INODE_BITMAP(bg) ((uint8_t *)(bg)->inode_bitmap->data)
#define BGROUP_BLOCK_BITMAP(bg) ((uint8_t *)(bg)->block_bitmap->data) #define BGROUP_BLOCK_BITMAP(bg) ((uint8_t *)(bg)->block_bitmap->data)
@ -123,21 +145,20 @@ struct ext2_block {
struct ext2_bgroup { struct ext2_bgroup {
struct ext2_data *fs; /* pointer to file system data */ struct ext2_data *fs; /* pointer to file system data */
struct ext2_block *block; /* block of current block group */
struct ext2_block *inode_table; /* fetched block of inode table */ struct ext2_block *inode_table; /* fetched block of inode table */
struct ext2_block *inode_bitmap; /* inode bitmap */ struct ext2_block *inode_bitmap; /* inode bitmap */
struct ext2_block *block_bitmap; /* block bitmap */ struct ext2_block *block_bitmap; /* block bitmap */
uint32_t num; /* number of described block group */ int32_t num; /* number of described block group */
uint32_t num_in_block; /* number of group in fetched block */
uint32_t bgroup_block; /* number of fetched block (relative) */
uint32_t inode_table_block; /* number of fetched block (relative) */ uint32_t inode_table_block; /* number of fetched block (relative) */
};
static inline struct ext2_disk_bgroup *current_disk_bgroup(struct ext2_bgroup *bg) uint32_t bg_block_bitmap;
{ uint32_t bg_inode_bitmap;
return &BGROUP_BLOCK(bg)[bg->num_in_block]; uint32_t bg_inode_table;
} uint16_t bg_free_blocks_count;
uint16_t bg_free_inodes_count;
uint16_t bg_used_dirs_count;
};
/* Flags for inode */ /* Flags for inode */
#define INODE_FETCHED_BLOCK BIT(0) #define INODE_FETCHED_BLOCK BIT(0)
@ -171,6 +192,17 @@ static inline uint8_t *inode_current_block_mem(struct ext2_inode *inode)
return (uint8_t *)inode_current_block(inode)->data; return (uint8_t *)inode_current_block(inode)->data;
} }
struct ext2_direntry {
uint32_t de_inode;
uint16_t de_rec_len;
uint8_t de_name_len;
uint8_t de_file_type;
char de_name[];
};
/* Max size of directory entry that could be allocated from heap. */
#define MAX_DIRENTRY_SIZE (sizeof(struct ext2_direntry) + UINT8_MAX)
/* Structure common for files and directories representation */ /* Structure common for files and directories representation */
struct ext2_file { struct ext2_file {
struct ext2_inode *f_inode; struct ext2_inode *f_inode;
@ -181,10 +213,6 @@ struct ext2_file {
#define EXT2_DATA_FLAGS_RO BIT(0) #define EXT2_DATA_FLAGS_RO BIT(0)
#define EXT2_DATA_FLAGS_ERR BIT(1) #define EXT2_DATA_FLAGS_ERR BIT(1)
/* 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_data;
struct ext2_backend_ops { struct ext2_backend_ops {
@ -199,8 +227,8 @@ struct ext2_backend_ops {
#define MAX_INODES (CONFIG_MAX_FILES + 2) #define MAX_INODES (CONFIG_MAX_FILES + 2)
struct ext2_data { struct ext2_data {
struct ext2_block *sblock; /* superblock */ struct ext2_superblock sblock; /* superblock */
struct ext2_bgroup bgroup; /* block group */ struct ext2_bgroup bgroup; /* block group */
int32_t open_inodes; int32_t open_inodes;
int32_t open_files; int32_t open_files;