From: Ana Bobillo <[email protected]> Inode numbers start at 1, so the block group of inode n is (n - 1) / s_inodes_per_group, as ext4fs_read_inode() computes it. The write path computes n / s_inodes_per_group instead, in ext4fs_delete_file(), for the new and the parent inode in ext4fs_write(), and in ext4fs_get_new_inode_no(). For the last inode of a group this gives the next group: ext4fs_delete_file() then clears a bit in the byte before that group's inode bitmap and zeroes an inode in the wrong inode table, and for the last group of the filesystem the index is past the end of fs->inode_bmaps[] and the group descriptor table.
Add ext4fs_inode_to_group(), next to ext4fs_blk_to_group(), which computes the group that way, and use it in all four places. The inode numbers given to ext4fs_delete_file() and taken as the parent inode come from directory entries on disk, so it also checks the inode as Linux ext4_valid_inum() does: one past the inode count or the last block group, or a reserved inode other than the root, such as the journal's, is reported and refused rather than having its blocks released and its inode zeroed. When ext4fs_get_new_inode_no() finds no free inode after the last one it allocated, it goes on past the last group's inode bitmap. Make it stop at the inode count instead. Signed-off-by: Ana Bobillo <[email protected]> --- Changes in v2: - Add ext4fs_inode_to_group() and use it for every inode bitmap index, so a bad parent inode is now reported too - Reject reserved inodes other than the root, as ext4_valid_inum() does fs/ext4/ext4_common.c | 49 +++++++++++++++++++++++++++++++++++++++---- fs/ext4/ext4_common.h | 1 + fs/ext4/ext4_write.c | 17 ++++++++++----- 3 files changed, 58 insertions(+), 9 deletions(-) diff --git a/fs/ext4/ext4_common.c b/fs/ext4/ext4_common.c index 650597873f8..273e6628b8d 100644 --- a/fs/ext4/ext4_common.c +++ b/fs/ext4/ext4_common.c @@ -998,6 +998,41 @@ err: return -EINVAL; } +/** + * ext4fs_inode_to_group() - get the block group of an inode + * + * The inode number may come from disk, so check it before it is used to + * index the inode bitmaps and group descriptors: as Linux ext4_valid_inum() + * does, it must be the root or a non-reserved inode within the inode count, + * and its block group must be inside the filesystem. + * + * @fs: filesystem + * @inodeno: inode number + * Return: block group of @inodeno, or -EINVAL if it is not a valid inode + */ +int ext4fs_inode_to_group(struct ext_filesystem *fs, int inodeno) +{ + struct ext2_sblock *sblock = &ext4fs_root->sblock; + uint32_t first_ino = EXT2_GOOD_OLD_FIRST_INO; + uint32_t group; + + if (le32_to_cpu(sblock->revision_level) != EXT2_GOOD_OLD_REV) + first_ino = le32_to_cpu(sblock->first_inode); + if (inodeno != EXT2_ROOT_INO && + (inodeno < 1 || (uint32_t)inodeno < first_ino || + (uint32_t)inodeno > le32_to_cpu(sblock->total_inodes))) + goto err; + + group = (inodeno - 1) / le32_to_cpu(sblock->inodes_per_group); + if (group >= fs->no_blkgrp) + goto err; + + return group; +err: + printf("Invalid inode number %d\n", inodeno); + return -EINVAL; +} + uint32_t ext4fs_get_new_blk_no(void) { short i; @@ -1131,7 +1166,7 @@ int ext4fs_get_new_inode_no(void) { short i; short status; - unsigned int ibmap_idx; + int ibmap_idx; static int prev_inode_bitmap_index = -1; unsigned int inodes_per_grp = le32_to_cpu(ext4fs_root->sblock.inodes_per_group); struct ext_filesystem *fs = get_fs(); @@ -1191,8 +1226,14 @@ int ext4fs_get_new_inode_no(void) } else { restart: fs->curr_inode_no++; - /* get the blockbitmap index respective to blockno */ - ibmap_idx = fs->curr_inode_no / inodes_per_grp; + /* no free inode left after the last one allocated */ + if (fs->curr_inode_no > + le32_to_cpu(ext4fs_root->sblock.total_inodes)) + goto fail; + /* get the inode bitmap index respective to inode no */ + ibmap_idx = ext4fs_inode_to_group(fs, fs->curr_inode_no); + if (ibmap_idx < 0) + goto fail; struct ext2_block_group *bgd = ext4fs_get_group_descriptor(fs, ibmap_idx); uint16_t bg_flags = ext4fs_bg_get_flags(bgd); @@ -1210,7 +1251,7 @@ restart: if (ext4fs_set_inode_bmap(fs->curr_inode_no, fs->inode_bmaps[ibmap_idx], ibmap_idx) != 0) { - debug("going for restart for the block no %d %u\n", + debug("going for restart for the block no %d %d\n", fs->curr_inode_no, ibmap_idx); goto restart; } diff --git a/fs/ext4/ext4_common.h b/fs/ext4/ext4_common.h index eecc11d9995..eda04b5b3a3 100644 --- a/fs/ext4/ext4_common.h +++ b/fs/ext4/ext4_common.h @@ -64,6 +64,7 @@ uint16_t ext4fs_checksum_update(unsigned int i); int ext4fs_get_parent_inode_num(const char *dirname, char *dname, int flags); int ext4fs_update_parent_dentry(char *filename, int file_type); int ext4fs_blk_to_group(struct ext_filesystem *fs, uint64_t blknr); +int ext4fs_inode_to_group(struct ext_filesystem *fs, int inodeno); uint32_t ext4fs_get_new_blk_no(void); int ext4fs_get_new_inode_no(void); void ext4fs_reset_block_bmap(long int blockno, unsigned char *buffer, diff --git a/fs/ext4/ext4_write.c b/fs/ext4/ext4_write.c index ea7843775b3..0cbbdc6c934 100644 --- a/fs/ext4/ext4_write.c +++ b/fs/ext4/ext4_write.c @@ -473,6 +473,9 @@ static int ext4fs_delete_file(int inodeno) char *journal_buffer = zalloc(fs->blksz); if (!journal_buffer) return -ENOMEM; + ibmap_idx = ext4fs_inode_to_group(fs, inodeno); + if (ibmap_idx < 0) + goto fail; status = ext4fs_read_inode(ext4fs_root, inodeno, &inode); if (status == 0) goto fail; @@ -539,7 +542,6 @@ static int ext4fs_delete_file(int inodeno) /* release inode */ /* from the inode no to blockno */ inodes_per_block = fs->blksz / fs->inodesz; - ibmap_idx = inodeno / inode_per_grp; /* get the block no */ inodeno--; @@ -892,7 +894,8 @@ int ext4fs_write(const char *fname, const char *buffer, long int blkoff; struct ext2_sblock *sblock = &(ext4fs_root->sblock); unsigned int inodes_per_block; - unsigned int ibmap_idx; + int ibmap_idx; + int parent_ibmap_idx; struct ext2_block_group *bgd = NULL; struct ext_filesystem *fs = get_fs(); ALLOC_CACHE_ALIGN_BUFFER(char, filename, 256); @@ -929,6 +932,9 @@ int ext4fs_write(const char *fname, const char *buffer, parent_inodeno = ext4fs_get_parent_inode_num(fname, filename, F_FILE); if (parent_inodeno == -1) goto fail; + parent_ibmap_idx = ext4fs_inode_to_group(fs, parent_inodeno); + if (parent_ibmap_idx < 0) + goto fail; if (ext4fs_iget(parent_inodeno, g_parent_inode)) goto fail; /* do not mess up a directory using hash trees */ @@ -1020,7 +1026,9 @@ int ext4fs_write(const char *fname, const char *buffer, temp_ptr = zalloc(fs->blksz); if (!temp_ptr) goto fail; - ibmap_idx = inodeno / le32_to_cpu(ext4fs_root->sblock.inodes_per_group); + ibmap_idx = ext4fs_inode_to_group(fs, inodeno); + if (ibmap_idx < 0) + goto fail; inodeno--; bgd = ext4fs_get_group_descriptor(fs, ibmap_idx); itable_blkno = ext4fs_bg_get_inode_table_id(bgd, fs) + @@ -1041,9 +1049,8 @@ int ext4fs_write(const char *fname, const char *buffer, /* FIXME: Deallocate data blocks */ goto fail; } - ibmap_idx = parent_inodeno / le32_to_cpu(ext4fs_root->sblock.inodes_per_group); parent_inodeno--; - bgd = ext4fs_get_group_descriptor(fs, ibmap_idx); + bgd = ext4fs_get_group_descriptor(fs, parent_ibmap_idx); parent_itable_blkno = ext4fs_bg_get_inode_table_id(bgd, fs) + (parent_inodeno % le32_to_cpu(sblock->inodes_per_group)) / inodes_per_block;
