diff --git a/subsys/fs/ext2/ext2.h b/subsys/fs/ext2/ext2.h index 1154369645e..394229fa2e2 100644 --- a/subsys/fs/ext2/ext2.h +++ b/subsys/fs/ext2/ext2.h @@ -44,6 +44,30 @@ #define EXT2_S_IFCHR 0x2000 /* character device */ #define EXT2_S_IFIFO 0x1000 /* fifo */ +#define EXT2_S_IRUSR 0x100 /* owner may read */ +#define EXT2_S_IWUSR 0x080 /* owner may write */ +#define EXT2_S_IXUSR 0x040 /* owner may execute */ +#define EXT2_S_IRGRP 0x020 /* group members may read */ +#define EXT2_S_IWGRP 0x010 /* group members may write */ +#define EXT2_S_IXGRP 0x008 /* group members may execute */ +#define EXT2_S_IROTH 0x004 /* others may read */ +#define EXT2_S_IWOTH 0x002 /* others may write */ +#define EXT2_S_IXOTH 0x001 /* others may execute */ + +/* Default file mode: rw-r--r-- */ +#define EXT2_DEF_FILE_MODE \ + (EXT2_S_IFREG | \ + EXT2_S_IRUSR | EXT2_S_IWUSR | \ + EXT2_S_IRGRP | \ + EXT2_S_IROTH) + +/* Default dir mode: rwxr-xr-x */ +#define EXT2_DEF_DIR_MODE \ + (EXT2_S_IFDIR | \ + EXT2_S_IRUSR | EXT2_S_IWUSR | EXT2_S_IXUSR | \ + EXT2_S_IRGRP | EXT2_S_IXGRP | \ + EXT2_S_IROTH | EXT2_S_IXOTH) + #define IS_REG_FILE(mode) (((mode) & EXT2_S_IFMT) == EXT2_S_IFREG) #define IS_DIR(mode) (((mode) & EXT2_S_IFMT) == EXT2_S_IFDIR) diff --git a/subsys/fs/ext2/ext2_bitmap.c b/subsys/fs/ext2/ext2_bitmap.c index 9843736bd82..ee6e9ed7654 100644 --- a/subsys/fs/ext2/ext2_bitmap.c +++ b/subsys/fs/ext2/ext2_bitmap.c @@ -27,6 +27,8 @@ int ext2_bitmap_set(uint8_t *bm, uint32_t index, uint32_t size) return -EINVAL; } + __ASSERT((bm[idx] & BIT(off)) == 0, "Bit %d set in bitmap", index); + LOG_DBG("Bitmap %d: %x", idx, bm[idx]); bm[idx] |= BIT(off); LOG_DBG("Bitmap %d: %x", idx, bm[idx]); @@ -46,6 +48,8 @@ int ext2_bitmap_unset(uint8_t *bm, uint32_t index, uint32_t size) return -EINVAL; } + __ASSERT(bm[idx] & BIT(off), "Bit %d not set in bitmap", index); + LOG_DBG("Bitmap %d: %x", idx, bm[idx]); bm[idx] &= ~BIT(off); LOG_DBG("Bitmap %d: %x", idx, bm[idx]); @@ -67,3 +71,18 @@ int32_t ext2_bitmap_find_free(uint8_t *bm, uint32_t size) } return -ENOSPC; } + +uint32_t bitmap_count_set(uint8_t *bm, uint32_t size) +{ + int32_t count = 0; + + for (uint32_t i = 0; i < size; i += 8) { + uint8_t val = bm[i / 8]; + + for (int b = 0; b < 8 && i + b < size; ++b) { + count += (val >> b) & BIT(0); + } + + } + return count; +} diff --git a/subsys/fs/ext2/ext2_bitmap.h b/subsys/fs/ext2/ext2_bitmap.h index 3f297693b7a..330798f3f0f 100644 --- a/subsys/fs/ext2/ext2_bitmap.h +++ b/subsys/fs/ext2/ext2_bitmap.h @@ -51,4 +51,14 @@ int ext2_bitmap_unset(uint8_t *bm, uint32_t index, uint32_t size); */ int32_t ext2_bitmap_find_free(uint8_t *bm, uint32_t size); +/** + * @brief Helper function to count bits set in bitmap + * + * @param bm Pointer to bitmap + * @param size Size of bitmap in bits + * + * @retval Number of set bits in bitmap; + */ +uint32_t bitmap_count_set(uint8_t *bm, uint32_t size); + #endif /* __EXT2_BITMAP_H__ */ diff --git a/subsys/fs/ext2/ext2_diskops.c b/subsys/fs/ext2/ext2_diskops.c index ffafb08bef7..4a7986e4226 100644 --- a/subsys/fs/ext2/ext2_diskops.c +++ b/subsys/fs/ext2/ext2_diskops.c @@ -605,19 +605,20 @@ int64_t ext2_alloc_block(struct ext2_data *fs) return rc; } - int r = ext2_bitmap_find_free(BGROUP_BLOCK_BITMAP(fs->bgroup), fs->block_size); + int bitmap_slot = ext2_bitmap_find_free(BGROUP_BLOCK_BITMAP(fs->bgroup), fs->block_size); - if (r < 0) { - LOG_WRN("Cannot find free block in group %d (rc: %d)", group, r); - return r; + if (bitmap_slot < 0) { + LOG_WRN("Cannot find free block in group %d (rc: %d)", group, bitmap_slot); + return bitmap_slot; } - /* Add 1 because bitmaps describe blocks except the first one */ - int32_t total = group * EXT2_DATA_SBLOCK(fs)->s_blocks_per_group + r + 1; + /* 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)", r, group, total); + LOG_DBG("Found free block %d in group %d (total: %d)", bitmap_slot, group, total); - rc = ext2_bitmap_set(BGROUP_BLOCK_BITMAP(fs->bgroup), r, fs->block_size); + rc = ext2_bitmap_set(BGROUP_BLOCK_BITMAP(fs->bgroup), bitmap_slot, fs->block_size); if (rc < 0) { return rc; } @@ -667,7 +668,7 @@ int32_t ext2_alloc_inode(struct ext2_data *fs) return r; } - /* Add 1 because bitmaps describe blocks except the first one */ + /* Add 1 because inodes are counted from 1 not 0. */ int32_t total = group * EXT2_DATA_SBLOCK(fs)->s_inodes_per_group + r + 1; LOG_DBG("Found free inode %d in group %d (total: %d)", r, group, total); diff --git a/subsys/fs/ext2/ext2_format.c b/subsys/fs/ext2/ext2_format.c index 8f8329afb11..102a9181bfb 100644 --- a/subsys/fs/ext2/ext2_format.c +++ b/subsys/fs/ext2/ext2_format.c @@ -12,7 +12,7 @@ #include "ext2_impl.h" #include "ext2_struct.h" -LOG_MODULE_DECLARE(ext2); +LOG_MODULE_DECLARE(ext2, LOG_LEVEL_DBG); FS_EXT2_DECLARE_DEFAULT_CONFIG(ext2_default); @@ -40,6 +40,57 @@ static void validate_config(struct ext2_cfg *cfg) } } +static void set_bitmap_padding(uint8_t *bitmap, uint32_t nelems, struct ext2_cfg *cfg) +{ + uint32_t used_bytes = nelems / 8 + (nelems % 8 != 0); + + LOG_DBG("Set bitmap padding: %d bytes", nelems); + memset(bitmap, 0x00, used_bytes); + + /* Set padding in block-bitmap block */ + if (nelems % 8) { + bitmap[used_bytes - 1] = (0xff << (nelems % 8)) & 0xff; + LOG_DBG("last byte: %02x", (0xff << (nelems % 8)) & 0xff); + } + memset(bitmap + used_bytes, 0xff, cfg->block_size - used_bytes); +} + +static void set_bitmap_bits(uint8_t *bitmap, uint32_t to_set) +{ + int i = 0, bits; + uint16_t set_value; + + while (to_set > 0) { + bits = MIN(8, to_set); + set_value = (1 << bits) - 1; + bitmap[i] = (uint8_t)set_value; + to_set -= bits; + i++; + } +} + +static void default_directory_inode(struct ext2_disk_inode *in, uint32_t nblocks, + struct ext2_cfg *cfg) +{ + LOG_DBG("Set directory inode: %p", in); + in->i_mode = EXT2_S_IFDIR; + in->i_uid = 0; + in->i_size = nblocks * cfg->block_size; + in->i_atime = 0; + in->i_ctime = 0; + in->i_mtime = 0; + in->i_dtime = 0; + in->i_gid = 0; + in->i_blocks = nblocks * cfg->block_size / 512; + in->i_flags = 0; + in->i_osd1 = 0; + in->i_generation = 0; + in->i_file_acl = 0; + in->i_dir_acl = 0; + in->i_faddr = 0; + memset(in->i_block, 0, EXT2_INODE_BLOCKS * sizeof(uint32_t)); +} + int ext2_format(struct ext2_data *fs, struct ext2_cfg *cfg) { int ret = 0; @@ -64,92 +115,89 @@ int ext2_format(struct ext2_data *fs, struct ext2_cfg *cfg) return -ENOSPC; } + uint32_t blocks_count = fs_memory / cfg->block_size; uint32_t blocks_per_group = cfg->block_size * 8; + uint32_t inodes_per_block = cfg->block_size / sizeof(struct ext2_disk_inode); + uint32_t mem_per_inode = cfg->bytes_per_inode + sizeof(struct ext2_disk_inode); - /* Require at least 24 blocks to have at least 1 block per inode */ + /* 24 block should be enough to fit minimal file system. */ 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) { + uint32_t sb_offset; + uint32_t first_data_block; + uint32_t occupied_blocks; + + if (cfg->block_size == 1024) { + /* Superblock is stored in 1st block */ + sb_offset = 0; + first_data_block = 1; + occupied_blocks = 2; + } else { + /* Superblock is stored in 0th block */ + sb_offset = 1024; + first_data_block = 0; + occupied_blocks = 1; + } + + /* Reserve blocks for block group and bitmaps. */ + uint32_t bg_block_num = occupied_blocks++; + uint32_t bbitmap_block_num = occupied_blocks++; + uint32_t ibitmap_block_num = occupied_blocks++; + + /* We want to have only 1 block group (that starts with first data block) */ + if (blocks_count > blocks_per_group + first_data_block) { 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 mem_for_inodes = fs_memory - occupied_blocks * cfg->block_size; uint32_t inodes_count = mem_for_inodes / mem_per_inode; + /* Align indes_count to use last block of inode table entirely. */ 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; + uint32_t used_inodes = EXT2_RESERVED_INODES; + uint32_t lost_found_inode = 1 + used_inodes++; /* We count inodes from 1. */ - /* 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 */ + /* First unoccupied block will be the start of inode table. */ + uint32_t itable_block_num = occupied_blocks; - 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); + occupied_blocks += 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; + /* Two next block after inode table will be the blocks for '/' and 'lost+found' dirs */ + uint32_t root_dir_blk_num = occupied_blocks++; + uint32_t lost_found_dir_blk_num = occupied_blocks++; - 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 */ - } + LOG_INF("root: %d l+f: %d", root_dir_blk_num, lost_found_dir_blk_num); - struct ext2_block *sb_block = ext2_get_block(fs, sb_block_num); + /* All blocks available for writes after creating file system. */ + uint32_t free_blocks = blocks_count - occupied_blocks; + + /* Blocks that will be described in bitmaps. */ + uint32_t used_blocks = occupied_blocks - first_data_block; + + LOG_INF("[Blocks] total:%d per_grp:%d occupied:%d used:%d", + blocks_count, blocks_per_group, occupied_blocks, used_blocks); + LOG_INF("[Inodes] total:%d used:%d itable_blocks:%d", + inodes_count, used_inodes, itable_blocks); + + struct ext2_block *sb_block = ext2_get_block(fs, first_data_block); 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); + struct ext2_block *itable_block1, *itable_block2, *root_dir_blk, *lost_found_dir_blk; - if (root_dir_blk_block == NULL || in1_block == NULL || ibitmap_block == NULL || - bbitmap_block == NULL || bg_block == NULL || sb_block == NULL) { + itable_block1 = itable_block2 = root_dir_blk = lost_found_dir_blk = NULL; + + if (ibitmap_block == NULL || bbitmap_block == NULL || + bg_block == NULL || sb_block == NULL) { ret = -ENOMEM; goto out; } @@ -162,8 +210,8 @@ int ext2_format(struct ext2_data *fs, struct ext2_cfg *cfg) 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_free_inodes_count = inodes_count - used_inodes; + sb->s_first_data_block = first_data_block; 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; @@ -189,10 +237,6 @@ int ext2_format(struct ext2_data *fs, struct ext2_cfg *cfg) 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; @@ -200,6 +244,11 @@ int ext2_format(struct ext2_data *fs, struct ext2_cfg *cfg) sb->s_journal_dev = 0; sb->s_last_orphan = 0; + memcpy(sb->s_uuid, cfg->uuid, 16); + strcpy(sb->s_volume_name, cfg->volume_name); + + sb_block->flags |= EXT2_BLOCK_DIRTY; + /* Block descriptor table */ struct ext2_disk_bgroup *bg = (struct ext2_disk_bgroup *)bg_block->data; @@ -207,88 +256,141 @@ int ext2_format(struct ext2_data *fs, struct ext2_cfg *cfg) 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_inode_table = itable_block_num; bg->bg_free_blocks_count = free_blocks; - bg->bg_free_inodes_count = inodes_count - reserved_inodes; - bg->bg_used_dirs_count = 1; + bg->bg_free_inodes_count = inodes_count - used_inodes; + bg->bg_used_dirs_count = 2; /* '/' and 'lost+found' */ - /* 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; + bg_block->flags |= EXT2_BLOCK_DIRTY; /* 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++; - } + /* In bitmap we describe blocks starting from s_first_data_block. */ + set_bitmap_padding(bbitmap, blocks_count - sb->s_first_data_block, cfg); + set_bitmap_bits(bbitmap, used_blocks); + bbitmap_block->flags |= EXT2_BLOCK_DIRTY; /* 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); - - sb_block->flags |= EXT2_BLOCK_DIRTY; - bg_block->flags |= EXT2_BLOCK_DIRTY; - in1_block->flags |= EXT2_BLOCK_DIRTY; - bbitmap_block->flags |= EXT2_BLOCK_DIRTY; + set_bitmap_padding(ibitmap, inodes_count, cfg); + set_bitmap_bits(ibitmap, used_inodes); ibitmap_block->flags |= EXT2_BLOCK_DIRTY; - root_dir_blk_block->flags |= EXT2_BLOCK_DIRTY; + + /* Inode table */ + /* Zero inode table */ + for (int i = 0; i < itable_blocks; i++) { + struct ext2_block *blk = ext2_get_block(fs, itable_block_num + i); + + memset(blk->data, 0, cfg->block_size); + blk->flags |= EXT2_BLOCK_DIRTY; + ext2_drop_block(fs, blk); + } + + struct ext2_disk_inode *in; + int inode_offset; + + /* Set inode 2 ('/' directory) */ + itable_block1 = ext2_get_block(fs, itable_block_num); + in = (struct ext2_disk_inode *)itable_block1->data; + inode_offset = EXT2_ROOT_INODE - 1; + default_directory_inode(&in[inode_offset], 1, cfg); + + in[inode_offset].i_mode = EXT2_DEF_DIR_MODE; + in[inode_offset].i_links_count = 3; /* 2 from itself and 1 from child directory */ + in[inode_offset].i_block[0] = root_dir_blk_num; + itable_block1->flags |= EXT2_BLOCK_DIRTY; + + /* Set inode for 'lost+found' directory */ + inode_offset = (lost_found_inode - 1) % inodes_per_block; /* We count inodes from 1 */ + + LOG_DBG("Inode offset: %d", inode_offset); + + if (inodes_per_block < lost_found_inode) { + /* We need to fetch new inode table block */ + uint32_t block_num = itable_block_num + lost_found_inode / inodes_per_block; + + itable_block2 = ext2_get_block(fs, block_num); + in = (struct ext2_disk_inode *)itable_block2->data; + } + + default_directory_inode(&in[inode_offset], 1, cfg); + in[inode_offset].i_mode = EXT2_DEF_DIR_MODE; + in[inode_offset].i_links_count = 2; /* 1 from itself and 1 from parent directory */ + in[inode_offset].i_block[0] = lost_found_dir_blk_num; + if (itable_block2) { + itable_block2->flags |= EXT2_BLOCK_DIRTY; + } + + struct ext2_disk_dentry *de; + uint32_t current_size; + + /* Contents of '/' directory */ + LOG_DBG("Root dir blk: %d", root_dir_blk_num); + root_dir_blk = ext2_get_block(fs, root_dir_blk_num); + if (root_dir_blk == NULL) { + ret = ENOMEM; + goto out; + } + memset(root_dir_blk->data, 0, cfg->block_size); + + current_size = 0; + + de = (struct ext2_disk_dentry *)root_dir_blk->data; + ext2_fill_direntry(de, ".", 1, EXT2_ROOT_INODE, EXT2_FT_DIR); + current_size += de->de_rec_len; + + de = EXT2_NEXT_DISK_DIRENTRY(de); + ext2_fill_direntry(de, "..", 2, EXT2_ROOT_INODE, EXT2_FT_DIR); + current_size += de->de_rec_len; + + de = EXT2_NEXT_DISK_DIRENTRY(de); + ext2_fill_direntry(de, "lost+found", strlen("lost+found"), lost_found_inode, EXT2_FT_DIR); + current_size += de->de_rec_len; + + /* This was the last entry so add padding until end of block */ + de->de_rec_len += cfg->block_size - current_size; + + root_dir_blk->flags |= EXT2_BLOCK_DIRTY; + + /* Contents of 'lost+found' directory */ + LOG_DBG("Lost found dir blk: %d", lost_found_dir_blk_num); + lost_found_dir_blk = ext2_get_block(fs, lost_found_dir_blk_num); + if (lost_found_dir_blk == NULL) { + ret = ENOMEM; + goto out; + } + memset(lost_found_dir_blk->data, 0, cfg->block_size); + + current_size = 0; + + de = (struct ext2_disk_dentry *)lost_found_dir_blk->data; + ext2_fill_direntry(de, ".", 1, lost_found_inode, EXT2_FT_DIR); + current_size += de->de_rec_len; + + de = EXT2_NEXT_DISK_DIRENTRY(de); + ext2_fill_direntry(de, "..", 2, EXT2_ROOT_INODE, EXT2_FT_DIR); + current_size += de->de_rec_len; + + /* This was the last entry so add padding until end of block */ + de->de_rec_len += cfg->block_size - current_size; + + 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_file_type == EXT2_FT_DIR ? 'd' : 'f'); + + lost_found_dir_blk->flags |= EXT2_BLOCK_DIRTY; + out: ext2_drop_block(fs, sb_block); ext2_drop_block(fs, bg_block); - ext2_drop_block(fs, in1_block); ext2_drop_block(fs, bbitmap_block); ext2_drop_block(fs, ibitmap_block); - ext2_drop_block(fs, root_dir_blk_block); + ext2_drop_block(fs, itable_block1); + ext2_drop_block(fs, itable_block2); + ext2_drop_block(fs, root_dir_blk); + ext2_drop_block(fs, lost_found_dir_blk); + fs->backend_ops->sync(fs); return ret; } diff --git a/subsys/fs/ext2/ext2_impl.c b/subsys/fs/ext2/ext2_impl.c index de3e96cbc6c..a0661b7a29c 100644 --- a/subsys/fs/ext2/ext2_impl.c +++ b/subsys/fs/ext2/ext2_impl.c @@ -14,6 +14,7 @@ #include "ext2_impl.h" #include "ext2_struct.h" #include "ext2_diskops.h" +#include "ext2_bitmap.h" LOG_MODULE_REGISTER(ext2, CONFIG_EXT2_LOG_LEVEL); @@ -309,6 +310,30 @@ int ext2_init_fs(struct ext2_data *fs) EXT2_DATA_SBLOCK(fs)->s_mnt_count += 1; fs->sblock->flags |= EXT2_BLOCK_DIRTY; } + ret = ext2_fetch_block_group(fs, 0); + if (ret < 0) { + goto out; + } + + ret = ext2_fetch_bg_ibitmap(fs->bgroup); + ret = ext2_fetch_bg_bbitmap(fs->bgroup); + + /* Validate superblock */ + uint32_t set; + struct ext2_disk_superblock *sb = EXT2_DATA_SBLOCK(fs); + uint32_t fs_blocks = sb->s_blocks_count - sb->s_first_data_block; + + set = bitmap_count_set(BGROUP_BLOCK_BITMAP(fs->bgroup), fs_blocks); + + LOG_INF("Set: %ld Should: %ld", set, fs_blocks - sb->s_free_blocks_count); + + __ASSERT(set == sb->s_blocks_count - sb->s_free_blocks_count - sb->s_first_data_block, + "Number of used blocks should be equal to bits set in bitmap"); + + set = bitmap_count_set(BGROUP_INODE_BITMAP(fs->bgroup), sb->s_inodes_count); + + __ASSERT(set == sb->s_inodes_count - sb->s_free_inodes_count, + "Number of used inodes should be equal to bits set in bitmap"); return 0; out: ext2_drop_block(fs, fs->sblock); @@ -340,6 +365,7 @@ int ext2_close_fs(struct ext2_data *fs) ext2_drop_block(fs, fs->bgroup->block); } ext2_drop_block(fs, fs->sblock); + ret = fs->backend_ops->sync(fs); fs->sblock = NULL; return ret; } @@ -856,6 +882,27 @@ static int ext2_create_inode(struct ext2_data *fs, struct ext2_inode *parent, return rc; } +void ext2_fill_direntry(struct ext2_disk_dentry *de, const char *name, uint8_t namelen, + uint32_t ino, uint8_t filetype) +{ + uint32_t reclen = sizeof(struct ext2_disk_dentry) + namelen; + + /* Align reclen to 4 bytes. */ + reclen = ROUND_UP(reclen, 4); + + __ASSERT(namelen <= EXT2_MAX_FILE_NAME, "Name length to long"); + + de->de_inode = ino; + de->de_rec_len = reclen; + de->de_name_len = (uint8_t)namelen; + de->de_file_type = filetype; + memcpy(de->de_name, name, namelen); + + 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_file_type == EXT2_FT_DIR ? 'd' : 'f'); +} + static int ext2_add_direntry(struct ext2_inode *dir, struct ext2_disk_dentry *entry) { LOG_DBG("Adding entry: {in=%d type=%d name_len=%d} to directory (in=%d)", @@ -958,10 +1005,8 @@ int ext2_create_file(struct ext2_inode *parent, struct ext2_inode *new_inode, goto out; } - entry->de_inode = new_inode->i_id; - entry->de_name_len = args->name_len; - entry->de_file_type = EXT2_FT_REG_FILE; - memcpy(entry->de_name, args->path + args->name_pos, entry->de_name_len); + ext2_fill_direntry(entry, args->path + args->name_pos, args->name_len, new_inode->i_id, + EXT2_FT_REG_FILE); rc = ext2_add_direntry(parent, entry); if (rc < 0) { @@ -1004,10 +1049,8 @@ int ext2_create_dir(struct ext2_inode *parent, struct ext2_inode *new_inode, goto out; } - entry->de_inode = new_inode->i_id; - entry->de_name_len = args->name_len; - entry->de_file_type = EXT2_FT_DIR; - memcpy(entry->de_name, args->path + args->name_pos, entry->de_name_len); + ext2_fill_direntry(entry, args->path + args->name_pos, args->name_len, new_inode->i_id, + EXT2_FT_DIR); rc = ext2_add_direntry(parent, entry); if (rc < 0) { diff --git a/subsys/fs/ext2/ext2_impl.h b/subsys/fs/ext2/ext2_impl.h index ed0b543d3d6..4391274c090 100644 --- a/subsys/fs/ext2/ext2_impl.h +++ b/subsys/fs/ext2/ext2_impl.h @@ -229,6 +229,23 @@ int ext2_inode_sync(struct ext2_inode *inode); */ int ext2_get_direntry(struct ext2_dir *dir, struct fs_dirent *ent); +/** + * @brief Fill in the directory structure with given attributes + * + * Fills in all the fields of directory entry. Automatically calculates de_rec_len field. + * + * NOTE: if you need to adjust the size (e.g. when this entry is last in the block) + * then just update the size after this function returns. + * + * @param de Structure to fill in + * @param name Name of direntry + * @param namelen Length of name + * @param ino Inode associated with that entry + * @param filetype File type of that entry + */ +void ext2_fill_direntry(struct ext2_disk_dentry *de, const char *name, uint8_t len, uint32_t ino, + uint8_t filetype); + /** * @brief Create a file * diff --git a/subsys/fs/ext2/ext2_struct.h b/subsys/fs/ext2/ext2_struct.h index 481616719eb..9304c1d3148 100644 --- a/subsys/fs/ext2/ext2_struct.h +++ b/subsys/fs/ext2/ext2_struct.h @@ -101,6 +101,8 @@ struct ext2_disk_dentry { /* Program structures ------------------------------------------------------- */ #define EXT2_BLOCK_NUM_SIZE (sizeof(uint32_t)) +#define EXT2_NEXT_DISK_DIRENTRY(de) \ + ((struct ext2_disk_dentry *)(((uint8_t *)(de)) + (de)->de_rec_len)) #define EXT2_BLOCK_ASSIGNED BIT(0) #define EXT2_BLOCK_DIRTY BIT(1)