udf: Fix preallocation discarding at indirect extent boundary
commit cfe4c1b25dd6d2f056afc00b7c98bcb3dd0b1fc3 upstream. When preallocation extent is the first one in the extent block, the code would corrupt extent tree header instead. Fix the problem and use udf_delete_aext() for deleting extent to avoid some code duplication. CC: stable@vger.kernel.org Signed-off-by: Jan Kara <jack@suse.cz> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
parent
0905c78f62
commit
4d835efd56
@ -120,60 +120,41 @@ void udf_truncate_tail_extent(struct inode *inode)
|
||||
|
||||
void udf_discard_prealloc(struct inode *inode)
|
||||
{
|
||||
struct extent_position epos = { NULL, 0, {0, 0} };
|
||||
struct extent_position epos = {};
|
||||
struct extent_position prev_epos = {};
|
||||
struct kernel_lb_addr eloc;
|
||||
uint32_t elen;
|
||||
uint64_t lbcount = 0;
|
||||
int8_t etype = -1, netype;
|
||||
int adsize;
|
||||
struct udf_inode_info *iinfo = UDF_I(inode);
|
||||
|
||||
if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB ||
|
||||
inode->i_size == iinfo->i_lenExtents)
|
||||
return;
|
||||
|
||||
if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_SHORT)
|
||||
adsize = sizeof(struct short_ad);
|
||||
else if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_LONG)
|
||||
adsize = sizeof(struct long_ad);
|
||||
else
|
||||
adsize = 0;
|
||||
|
||||
epos.block = iinfo->i_location;
|
||||
|
||||
/* Find the last extent in the file */
|
||||
while ((netype = udf_next_aext(inode, &epos, &eloc, &elen, 1)) != -1) {
|
||||
etype = netype;
|
||||
while ((netype = udf_next_aext(inode, &epos, &eloc, &elen, 0)) != -1) {
|
||||
brelse(prev_epos.bh);
|
||||
prev_epos = epos;
|
||||
if (prev_epos.bh)
|
||||
get_bh(prev_epos.bh);
|
||||
|
||||
etype = udf_next_aext(inode, &epos, &eloc, &elen, 1);
|
||||
lbcount += elen;
|
||||
}
|
||||
if (etype == (EXT_NOT_RECORDED_ALLOCATED >> 30)) {
|
||||
epos.offset -= adsize;
|
||||
lbcount -= elen;
|
||||
extent_trunc(inode, &epos, &eloc, etype, elen, 0);
|
||||
if (!epos.bh) {
|
||||
iinfo->i_lenAlloc =
|
||||
epos.offset -
|
||||
udf_file_entry_alloc_offset(inode);
|
||||
mark_inode_dirty(inode);
|
||||
} else {
|
||||
struct allocExtDesc *aed =
|
||||
(struct allocExtDesc *)(epos.bh->b_data);
|
||||
aed->lengthAllocDescs =
|
||||
cpu_to_le32(epos.offset -
|
||||
sizeof(struct allocExtDesc));
|
||||
if (!UDF_QUERY_FLAG(inode->i_sb, UDF_FLAG_STRICT) ||
|
||||
UDF_SB(inode->i_sb)->s_udfrev >= 0x0201)
|
||||
udf_update_tag(epos.bh->b_data, epos.offset);
|
||||
else
|
||||
udf_update_tag(epos.bh->b_data,
|
||||
sizeof(struct allocExtDesc));
|
||||
mark_buffer_dirty_inode(epos.bh, inode);
|
||||
}
|
||||
udf_delete_aext(inode, prev_epos);
|
||||
udf_free_blocks(inode->i_sb, inode, &eloc, 0,
|
||||
DIV_ROUND_UP(elen, 1 << inode->i_blkbits));
|
||||
}
|
||||
/* This inode entry is in-memory only and thus we don't have to mark
|
||||
* the inode dirty */
|
||||
iinfo->i_lenExtents = lbcount;
|
||||
brelse(epos.bh);
|
||||
brelse(prev_epos.bh);
|
||||
}
|
||||
|
||||
static void udf_update_alloc_ext_desc(struct inode *inode,
|
||||
|
Loading…
Reference in New Issue
Block a user