mirror of
https://github.com/torvalds/linux.git
synced 2024-11-21 19:41:42 +00:00
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: syzbot+7a4842f0b1801230a989@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=7a4842f0b1801230a989 Tested-by: syzbot+7a4842f0b1801230a989@syzkaller.appspotmail.com Signed-off-by: Zhao Mengmeng <zhaomengmeng@kylinos.cn> Suggested-by: Jan Kara <jack@suse.cz> Signed-off-by: Jan Kara <jack@suse.cz> Link: https://patch.msgid.link/20241001115425.266556-4-zhaomzhao@126.com
This commit is contained in:
parent
b405c1e58b
commit
c226964ec7
@ -246,6 +246,7 @@ int udf_fiiter_init(struct udf_fileident_iter *iter, struct inode *dir,
|
|||||||
{
|
{
|
||||||
struct udf_inode_info *iinfo = UDF_I(dir);
|
struct udf_inode_info *iinfo = UDF_I(dir);
|
||||||
int err = 0;
|
int err = 0;
|
||||||
|
int8_t etype;
|
||||||
|
|
||||||
iter->dir = dir;
|
iter->dir = dir;
|
||||||
iter->bh[0] = iter->bh[1] = NULL;
|
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;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (inode_bmap(dir, iter->pos >> dir->i_blkbits, &iter->epos,
|
err = inode_bmap(dir, iter->pos >> dir->i_blkbits, &iter->epos,
|
||||||
&iter->eloc, &iter->elen, &iter->loffset) !=
|
&iter->eloc, &iter->elen, &iter->loffset, &etype);
|
||||||
(EXT_RECORDED_ALLOCATED >> 30)) {
|
if (err <= 0 || etype != (EXT_RECORDED_ALLOCATED >> 30)) {
|
||||||
if (pos == dir->i_size)
|
if (pos == dir->i_size)
|
||||||
return 0;
|
return 0;
|
||||||
udf_err(dir->i_sb,
|
udf_err(dir->i_sb,
|
||||||
@ -463,6 +464,7 @@ int udf_fiiter_append_blk(struct udf_fileident_iter *iter)
|
|||||||
sector_t block;
|
sector_t block;
|
||||||
uint32_t old_elen = iter->elen;
|
uint32_t old_elen = iter->elen;
|
||||||
int err;
|
int err;
|
||||||
|
int8_t etype;
|
||||||
|
|
||||||
if (WARN_ON_ONCE(iinfo->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB))
|
if (WARN_ON_ONCE(iinfo->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB))
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
@ -477,8 +479,9 @@ int udf_fiiter_append_blk(struct udf_fileident_iter *iter)
|
|||||||
udf_fiiter_update_elen(iter, old_elen);
|
udf_fiiter_update_elen(iter, old_elen);
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
if (inode_bmap(iter->dir, block, &iter->epos, &iter->eloc, &iter->elen,
|
err = inode_bmap(iter->dir, block, &iter->epos, &iter->eloc, &iter->elen,
|
||||||
&iter->loffset) != (EXT_RECORDED_ALLOCATED >> 30)) {
|
&iter->loffset, &etype);
|
||||||
|
if (err <= 0 || etype != (EXT_RECORDED_ALLOCATED >> 30)) {
|
||||||
udf_err(iter->dir->i_sb,
|
udf_err(iter->dir->i_sb,
|
||||||
"block %llu not allocated in directory (ino %lu)\n",
|
"block %llu not allocated in directory (ino %lu)\n",
|
||||||
(unsigned long long)block, iter->dir->i_ino);
|
(unsigned long long)block, iter->dir->i_ino);
|
||||||
|
@ -404,7 +404,7 @@ struct udf_map_rq {
|
|||||||
|
|
||||||
static int udf_map_block(struct inode *inode, struct udf_map_rq *map)
|
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);
|
struct udf_inode_info *iinfo = UDF_I(inode);
|
||||||
|
|
||||||
if (WARN_ON_ONCE(iinfo->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB))
|
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;
|
uint32_t elen;
|
||||||
sector_t offset;
|
sector_t offset;
|
||||||
struct extent_position epos = {};
|
struct extent_position epos = {};
|
||||||
|
int8_t etype;
|
||||||
|
|
||||||
down_read(&iinfo->i_data_sem);
|
down_read(&iinfo->i_data_sem);
|
||||||
if (inode_bmap(inode, map->lblk, &epos, &eloc, &elen, &offset)
|
ret = inode_bmap(inode, map->lblk, &epos, &eloc, &elen, &offset,
|
||||||
== (EXT_RECORDED_ALLOCATED >> 30)) {
|
&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,
|
map->pblk = udf_get_lb_pblock(inode->i_sb, &eloc,
|
||||||
offset);
|
offset);
|
||||||
map->oflags |= UDF_BLK_MAPPED;
|
map->oflags |= UDF_BLK_MAPPED;
|
||||||
|
ret = 0;
|
||||||
}
|
}
|
||||||
|
out_read:
|
||||||
up_read(&iinfo->i_data_sem);
|
up_read(&iinfo->i_data_sem);
|
||||||
brelse(epos.bh);
|
brelse(epos.bh);
|
||||||
|
|
||||||
return 0;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
down_write(&iinfo->i_data_sem);
|
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)
|
if (((loff_t)map->lblk) << inode->i_blkbits >= iinfo->i_lenExtents)
|
||||||
udf_discard_prealloc(inode);
|
udf_discard_prealloc(inode);
|
||||||
udf_clear_extent_cache(inode);
|
udf_clear_extent_cache(inode);
|
||||||
err = inode_getblk(inode, map);
|
ret = inode_getblk(inode, map);
|
||||||
up_write(&iinfo->i_data_sem);
|
up_write(&iinfo->i_data_sem);
|
||||||
return err;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int __udf_get_block(struct inode *inode, sector_t block,
|
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);
|
udf_discard_prealloc(inode);
|
||||||
|
|
||||||
etype = inode_bmap(inode, first_block, &epos, &eloc, &elen, &offset);
|
err = inode_bmap(inode, first_block, &epos, &eloc, &elen, &offset, &etype);
|
||||||
within_last_ext = (etype != -1);
|
if (err < 0)
|
||||||
|
goto out;
|
||||||
|
within_last_ext = (err == 1);
|
||||||
/* We don't expect extents past EOF... */
|
/* We don't expect extents past EOF... */
|
||||||
WARN_ON_ONCE(within_last_ext &&
|
WARN_ON_ONCE(within_last_ext &&
|
||||||
elen > ((loff_t)offset + 1) << inode->i_blkbits);
|
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);
|
return (elen >> 30);
|
||||||
}
|
}
|
||||||
|
|
||||||
int8_t inode_bmap(struct inode *inode, sector_t block,
|
/*
|
||||||
struct extent_position *pos, struct kernel_lb_addr *eloc,
|
* Returns 1 on success, -errno on error, 0 on hit EOF.
|
||||||
uint32_t *elen, sector_t *offset)
|
*/
|
||||||
|
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;
|
unsigned char blocksize_bits = inode->i_sb->s_blocksize_bits;
|
||||||
loff_t lbcount = 0, bcount = (loff_t) block << blocksize_bits;
|
loff_t lbcount = 0, bcount = (loff_t) block << blocksize_bits;
|
||||||
int8_t etype;
|
|
||||||
struct udf_inode_info *iinfo;
|
struct udf_inode_info *iinfo;
|
||||||
int err = 0;
|
int err = 0;
|
||||||
|
|
||||||
@ -2419,13 +2429,13 @@ int8_t inode_bmap(struct inode *inode, sector_t block,
|
|||||||
}
|
}
|
||||||
*elen = 0;
|
*elen = 0;
|
||||||
do {
|
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) {
|
||||||
if (err == 0) {
|
if (err == 0) {
|
||||||
*offset = (bcount - lbcount) >> blocksize_bits;
|
*offset = (bcount - lbcount) >> blocksize_bits;
|
||||||
iinfo->i_lenExtents = lbcount;
|
iinfo->i_lenExtents = lbcount;
|
||||||
}
|
}
|
||||||
return -1;
|
return err;
|
||||||
}
|
}
|
||||||
lbcount += *elen;
|
lbcount += *elen;
|
||||||
} while (lbcount <= bcount);
|
} 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);
|
udf_update_extent_cache(inode, lbcount - *elen, pos);
|
||||||
*offset = (bcount + *elen - lbcount) >> blocksize_bits;
|
*offset = (bcount + *elen - lbcount) >> blocksize_bits;
|
||||||
|
|
||||||
return etype;
|
return 1;
|
||||||
}
|
}
|
||||||
|
@ -282,9 +282,11 @@ static uint32_t udf_try_read_meta(struct inode *inode, uint32_t block,
|
|||||||
sector_t ext_offset;
|
sector_t ext_offset;
|
||||||
struct extent_position epos = {};
|
struct extent_position epos = {};
|
||||||
uint32_t phyblock;
|
uint32_t phyblock;
|
||||||
|
int8_t etype;
|
||||||
|
int err = 0;
|
||||||
|
|
||||||
if (inode_bmap(inode, block, &epos, &eloc, &elen, &ext_offset) !=
|
err = inode_bmap(inode, block, &epos, &eloc, &elen, &ext_offset, &etype);
|
||||||
(EXT_RECORDED_ALLOCATED >> 30))
|
if (err <= 0 || etype != (EXT_RECORDED_ALLOCATED >> 30))
|
||||||
phyblock = 0xFFFFFFFF;
|
phyblock = 0xFFFFFFFF;
|
||||||
else {
|
else {
|
||||||
map = &UDF_SB(sb)->s_partmaps[partition];
|
map = &UDF_SB(sb)->s_partmaps[partition];
|
||||||
|
@ -214,10 +214,12 @@ int udf_truncate_extents(struct inode *inode)
|
|||||||
else
|
else
|
||||||
BUG();
|
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) +
|
byte_offset = (offset << sb->s_blocksize_bits) +
|
||||||
(inode->i_size & (sb->s_blocksize - 1));
|
(inode->i_size & (sb->s_blocksize - 1));
|
||||||
if (etype == -1) {
|
if (ret == 0) {
|
||||||
/* We should extend the file? */
|
/* We should extend the file? */
|
||||||
WARN_ON(byte_offset);
|
WARN_ON(byte_offset);
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -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 int udf_setsize(struct inode *, loff_t);
|
||||||
extern void udf_evict_inode(struct inode *);
|
extern void udf_evict_inode(struct inode *);
|
||||||
extern int udf_write_inode(struct inode *, struct writeback_control *wbc);
|
extern int udf_write_inode(struct inode *, struct writeback_control *wbc);
|
||||||
extern int8_t inode_bmap(struct inode *, sector_t, struct extent_position *,
|
extern int inode_bmap(struct inode *inode, sector_t block,
|
||||||
struct kernel_lb_addr *, uint32_t *, sector_t *);
|
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);
|
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,
|
extern int udf_setup_indirect_aext(struct inode *inode, udf_pblk_t block,
|
||||||
struct extent_position *epos);
|
struct extent_position *epos);
|
||||||
|
Loading…
Reference in New Issue
Block a user