Commit c226964e authored by Zhao Mengmeng's avatar Zhao Mengmeng Committed by Jan Kara
Browse files

udf: refactor inode_bmap() to handle error



Refactor inode_bmap() to handle error since udf_next_aext() can return
error now. On situations like ftruncate, udf_extend_file() can now
detect errors and bail out early without resorting to checking for
particular offsets and assuming internal behavior of these functions.

Reported-by: default avatar <syzbot+7a4842f0b1801230a989@syzkaller.appspotmail.com>
Closes: https://syzkaller.appspot.com/bug?extid=7a4842f0b1801230a989


Tested-by: default avatar <syzbot+7a4842f0b1801230a989@syzkaller.appspotmail.com>
Signed-off-by: default avatarZhao Mengmeng <zhaomengmeng@kylinos.cn>
Suggested-by: default avatarJan Kara <jack@suse.cz>
Signed-off-by: default avatarJan Kara <jack@suse.cz>
Link: https://patch.msgid.link/20241001115425.266556-4-zhaomzhao@126.com
parent b405c1e5
Loading
Loading
Loading
Loading
+8 −5
Original line number Diff line number Diff line
@@ -246,6 +246,7 @@ int udf_fiiter_init(struct udf_fileident_iter *iter, struct inode *dir,
{
	struct udf_inode_info *iinfo = UDF_I(dir);
	int err = 0;
	int8_t etype;

	iter->dir = dir;
	iter->bh[0] = iter->bh[1] = NULL;
@@ -265,9 +266,9 @@ int udf_fiiter_init(struct udf_fileident_iter *iter, struct inode *dir,
		goto out;
	}

	if (inode_bmap(dir, iter->pos >> dir->i_blkbits, &iter->epos,
		       &iter->eloc, &iter->elen, &iter->loffset) !=
	    (EXT_RECORDED_ALLOCATED >> 30)) {
	err = inode_bmap(dir, iter->pos >> dir->i_blkbits, &iter->epos,
			 &iter->eloc, &iter->elen, &iter->loffset, &etype);
	if (err <= 0 || etype != (EXT_RECORDED_ALLOCATED >> 30)) {
		if (pos == dir->i_size)
			return 0;
		udf_err(dir->i_sb,
@@ -463,6 +464,7 @@ int udf_fiiter_append_blk(struct udf_fileident_iter *iter)
	sector_t block;
	uint32_t old_elen = iter->elen;
	int err;
	int8_t etype;

	if (WARN_ON_ONCE(iinfo->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB))
		return -EINVAL;
@@ -477,8 +479,9 @@ int udf_fiiter_append_blk(struct udf_fileident_iter *iter)
		udf_fiiter_update_elen(iter, old_elen);
		return err;
	}
	if (inode_bmap(iter->dir, block, &iter->epos, &iter->eloc, &iter->elen,
		       &iter->loffset) != (EXT_RECORDED_ALLOCATED >> 30)) {
	err = inode_bmap(iter->dir, block, &iter->epos, &iter->eloc, &iter->elen,
		   &iter->loffset, &etype);
	if (err <= 0 || etype != (EXT_RECORDED_ALLOCATED >> 30)) {
		udf_err(iter->dir->i_sb,
			"block %llu not allocated in directory (ino %lu)\n",
			(unsigned long long)block, iter->dir->i_ino);
+25 −15
Original line number Diff line number Diff line
@@ -404,7 +404,7 @@ struct udf_map_rq {

static int udf_map_block(struct inode *inode, struct udf_map_rq *map)
{
	int err;
	int ret;
	struct udf_inode_info *iinfo = UDF_I(inode);

	if (WARN_ON_ONCE(iinfo->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB))
@@ -416,18 +416,24 @@ static int udf_map_block(struct inode *inode, struct udf_map_rq *map)
		uint32_t elen;
		sector_t offset;
		struct extent_position epos = {};
		int8_t etype;

		down_read(&iinfo->i_data_sem);
		if (inode_bmap(inode, map->lblk, &epos, &eloc, &elen, &offset)
				== (EXT_RECORDED_ALLOCATED >> 30)) {
		ret = inode_bmap(inode, map->lblk, &epos, &eloc, &elen, &offset,
				 &etype);
		if (ret < 0)
			goto out_read;
		if (ret > 0 && etype == (EXT_RECORDED_ALLOCATED >> 30)) {
			map->pblk = udf_get_lb_pblock(inode->i_sb, &eloc,
							offset);
			map->oflags |= UDF_BLK_MAPPED;
			ret = 0;
		}
out_read:
		up_read(&iinfo->i_data_sem);
		brelse(epos.bh);

		return 0;
		return ret;
	}

	down_write(&iinfo->i_data_sem);
@@ -438,9 +444,9 @@ static int udf_map_block(struct inode *inode, struct udf_map_rq *map)
	if (((loff_t)map->lblk) << inode->i_blkbits >= iinfo->i_lenExtents)
		udf_discard_prealloc(inode);
	udf_clear_extent_cache(inode);
	err = inode_getblk(inode, map);
	ret = inode_getblk(inode, map);
	up_write(&iinfo->i_data_sem);
	return err;
	return ret;
}

static int __udf_get_block(struct inode *inode, sector_t block,
@@ -662,8 +668,10 @@ static int udf_extend_file(struct inode *inode, loff_t newsize)
	 */
	udf_discard_prealloc(inode);

	etype = inode_bmap(inode, first_block, &epos, &eloc, &elen, &offset);
	within_last_ext = (etype != -1);
	err = inode_bmap(inode, first_block, &epos, &eloc, &elen, &offset, &etype);
	if (err < 0)
		goto out;
	within_last_ext = (err == 1);
	/* We don't expect extents past EOF... */
	WARN_ON_ONCE(within_last_ext &&
		     elen > ((loff_t)offset + 1) << inode->i_blkbits);
@@ -2401,13 +2409,15 @@ int8_t udf_delete_aext(struct inode *inode, struct extent_position epos)
	return (elen >> 30);
}

int8_t inode_bmap(struct inode *inode, sector_t block,
		  struct extent_position *pos, struct kernel_lb_addr *eloc,
		  uint32_t *elen, sector_t *offset)
/*
 * Returns 1 on success, -errno on error, 0 on hit EOF.
 */
int inode_bmap(struct inode *inode, sector_t block, struct extent_position *pos,
	       struct kernel_lb_addr *eloc, uint32_t *elen, sector_t *offset,
	       int8_t *etype)
{
	unsigned char blocksize_bits = inode->i_sb->s_blocksize_bits;
	loff_t lbcount = 0, bcount = (loff_t) block << blocksize_bits;
	int8_t etype;
	struct udf_inode_info *iinfo;
	int err = 0;

@@ -2419,13 +2429,13 @@ int8_t inode_bmap(struct inode *inode, sector_t block,
	}
	*elen = 0;
	do {
		err = udf_next_aext(inode, pos, eloc, elen, &etype, 1);
		err = udf_next_aext(inode, pos, eloc, elen, etype, 1);
		if (err <= 0) {
			if (err == 0) {
				*offset = (bcount - lbcount) >> blocksize_bits;
				iinfo->i_lenExtents = lbcount;
			}
			return -1;
			return err;
		}
		lbcount += *elen;
	} while (lbcount <= bcount);
@@ -2433,5 +2443,5 @@ int8_t inode_bmap(struct inode *inode, sector_t block,
	udf_update_extent_cache(inode, lbcount - *elen, pos);
	*offset = (bcount + *elen - lbcount) >> blocksize_bits;

	return etype;
	return 1;
}
+4 −2
Original line number Diff line number Diff line
@@ -282,9 +282,11 @@ static uint32_t udf_try_read_meta(struct inode *inode, uint32_t block,
	sector_t ext_offset;
	struct extent_position epos = {};
	uint32_t phyblock;
	int8_t etype;
	int err = 0;

	if (inode_bmap(inode, block, &epos, &eloc, &elen, &ext_offset) !=
						(EXT_RECORDED_ALLOCATED >> 30))
	err = inode_bmap(inode, block, &epos, &eloc, &elen, &ext_offset, &etype);
	if (err <= 0 || etype != (EXT_RECORDED_ALLOCATED >> 30))
		phyblock = 0xFFFFFFFF;
	else {
		map = &UDF_SB(sb)->s_partmaps[partition];
+4 −2
Original line number Diff line number Diff line
@@ -214,10 +214,12 @@ int udf_truncate_extents(struct inode *inode)
	else
		BUG();

	etype = inode_bmap(inode, first_block, &epos, &eloc, &elen, &offset);
	ret = inode_bmap(inode, first_block, &epos, &eloc, &elen, &offset, &etype);
	if (ret < 0)
		return ret;
	byte_offset = (offset << sb->s_blocksize_bits) +
		(inode->i_size & (sb->s_blocksize - 1));
	if (etype == -1) {
	if (ret == 0) {
		/* We should extend the file? */
		WARN_ON(byte_offset);
		return 0;
+3 −2
Original line number Diff line number Diff line
@@ -157,8 +157,9 @@ extern struct buffer_head *udf_bread(struct inode *inode, udf_pblk_t block,
extern int udf_setsize(struct inode *, loff_t);
extern void udf_evict_inode(struct inode *);
extern int udf_write_inode(struct inode *, struct writeback_control *wbc);
extern int8_t inode_bmap(struct inode *, sector_t, struct extent_position *,
			 struct kernel_lb_addr *, uint32_t *, sector_t *);
extern int inode_bmap(struct inode *inode, sector_t block,
		      struct extent_position *pos, struct kernel_lb_addr *eloc,
		      uint32_t *elen, sector_t *offset, int8_t *etype);
int udf_get_block(struct inode *, sector_t, struct buffer_head *, int);
extern int udf_setup_indirect_aext(struct inode *inode, udf_pblk_t block,
				   struct extent_position *epos);