mirror of
https://github.com/AuxXxilium/linux_dsm_epyc7002.git
synced 2024-11-24 04:40:51 +07:00
udf: Fix incorrect final NOT_ALLOCATED (hole) extent length
In some cases, using the 'truncate' command to extend a UDF file results
in a mismatch between the length of the file's extents (specifically, due
to incorrect length of the final NOT_ALLOCATED extent) and the information
(file) length. The discrepancy can prevent other operating systems
(i.e., Windows 10) from opening the file.
Two particular errors have been observed when extending a file:
1. The final extent is larger than it should be, having been rounded up
to a multiple of the block size.
B. The final extent is not shorter than it should be, due to not having
been updated when the file's information length was increased.
[JK: simplified udf_do_extend_final_block(), fixed up some types]
Fixes: 2c948b3f86
("udf: Avoid IO in udf_clear_inode")
CC: stable@vger.kernel.org
Signed-off-by: Steven J. Magnani <steve@digidescorp.com>
Link: https://lore.kernel.org/r/1561948775-5878-1-git-send-email-steve@digidescorp.com
Signed-off-by: Jan Kara <jack@suse.cz>
This commit is contained in:
parent
90f15ac9fa
commit
fa33cdbf3e
@ -470,13 +470,15 @@ static struct buffer_head *udf_getblk(struct inode *inode, udf_pblk_t block,
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Extend the file by 'blocks' blocks, return the number of extents added */
|
/* Extend the file with new blocks totaling 'new_block_bytes',
|
||||||
|
* return the number of extents added
|
||||||
|
*/
|
||||||
static int udf_do_extend_file(struct inode *inode,
|
static int udf_do_extend_file(struct inode *inode,
|
||||||
struct extent_position *last_pos,
|
struct extent_position *last_pos,
|
||||||
struct kernel_long_ad *last_ext,
|
struct kernel_long_ad *last_ext,
|
||||||
sector_t blocks)
|
loff_t new_block_bytes)
|
||||||
{
|
{
|
||||||
sector_t add;
|
uint32_t add;
|
||||||
int count = 0, fake = !(last_ext->extLength & UDF_EXTENT_LENGTH_MASK);
|
int count = 0, fake = !(last_ext->extLength & UDF_EXTENT_LENGTH_MASK);
|
||||||
struct super_block *sb = inode->i_sb;
|
struct super_block *sb = inode->i_sb;
|
||||||
struct kernel_lb_addr prealloc_loc = {};
|
struct kernel_lb_addr prealloc_loc = {};
|
||||||
@ -486,7 +488,7 @@ static int udf_do_extend_file(struct inode *inode,
|
|||||||
|
|
||||||
/* The previous extent is fake and we should not extend by anything
|
/* The previous extent is fake and we should not extend by anything
|
||||||
* - there's nothing to do... */
|
* - there's nothing to do... */
|
||||||
if (!blocks && fake)
|
if (!new_block_bytes && fake)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
iinfo = UDF_I(inode);
|
iinfo = UDF_I(inode);
|
||||||
@ -517,13 +519,12 @@ static int udf_do_extend_file(struct inode *inode,
|
|||||||
/* Can we merge with the previous extent? */
|
/* Can we merge with the previous extent? */
|
||||||
if ((last_ext->extLength & UDF_EXTENT_FLAG_MASK) ==
|
if ((last_ext->extLength & UDF_EXTENT_FLAG_MASK) ==
|
||||||
EXT_NOT_RECORDED_NOT_ALLOCATED) {
|
EXT_NOT_RECORDED_NOT_ALLOCATED) {
|
||||||
add = ((1 << 30) - sb->s_blocksize -
|
add = (1 << 30) - sb->s_blocksize -
|
||||||
(last_ext->extLength & UDF_EXTENT_LENGTH_MASK)) >>
|
(last_ext->extLength & UDF_EXTENT_LENGTH_MASK);
|
||||||
sb->s_blocksize_bits;
|
if (add > new_block_bytes)
|
||||||
if (add > blocks)
|
add = new_block_bytes;
|
||||||
add = blocks;
|
new_block_bytes -= add;
|
||||||
blocks -= add;
|
last_ext->extLength += add;
|
||||||
last_ext->extLength += add << sb->s_blocksize_bits;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fake) {
|
if (fake) {
|
||||||
@ -544,28 +545,27 @@ static int udf_do_extend_file(struct inode *inode,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Managed to do everything necessary? */
|
/* Managed to do everything necessary? */
|
||||||
if (!blocks)
|
if (!new_block_bytes)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
/* All further extents will be NOT_RECORDED_NOT_ALLOCATED */
|
/* All further extents will be NOT_RECORDED_NOT_ALLOCATED */
|
||||||
last_ext->extLocation.logicalBlockNum = 0;
|
last_ext->extLocation.logicalBlockNum = 0;
|
||||||
last_ext->extLocation.partitionReferenceNum = 0;
|
last_ext->extLocation.partitionReferenceNum = 0;
|
||||||
add = (1 << (30-sb->s_blocksize_bits)) - 1;
|
add = (1 << 30) - sb->s_blocksize;
|
||||||
last_ext->extLength = EXT_NOT_RECORDED_NOT_ALLOCATED |
|
last_ext->extLength = EXT_NOT_RECORDED_NOT_ALLOCATED | add;
|
||||||
(add << sb->s_blocksize_bits);
|
|
||||||
|
|
||||||
/* Create enough extents to cover the whole hole */
|
/* Create enough extents to cover the whole hole */
|
||||||
while (blocks > add) {
|
while (new_block_bytes > add) {
|
||||||
blocks -= add;
|
new_block_bytes -= add;
|
||||||
err = udf_add_aext(inode, last_pos, &last_ext->extLocation,
|
err = udf_add_aext(inode, last_pos, &last_ext->extLocation,
|
||||||
last_ext->extLength, 1);
|
last_ext->extLength, 1);
|
||||||
if (err)
|
if (err)
|
||||||
return err;
|
return err;
|
||||||
count++;
|
count++;
|
||||||
}
|
}
|
||||||
if (blocks) {
|
if (new_block_bytes) {
|
||||||
last_ext->extLength = EXT_NOT_RECORDED_NOT_ALLOCATED |
|
last_ext->extLength = EXT_NOT_RECORDED_NOT_ALLOCATED |
|
||||||
(blocks << sb->s_blocksize_bits);
|
new_block_bytes;
|
||||||
err = udf_add_aext(inode, last_pos, &last_ext->extLocation,
|
err = udf_add_aext(inode, last_pos, &last_ext->extLocation,
|
||||||
last_ext->extLength, 1);
|
last_ext->extLength, 1);
|
||||||
if (err)
|
if (err)
|
||||||
@ -596,6 +596,24 @@ static int udf_do_extend_file(struct inode *inode,
|
|||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Extend the final block of the file to final_block_len bytes */
|
||||||
|
static void udf_do_extend_final_block(struct inode *inode,
|
||||||
|
struct extent_position *last_pos,
|
||||||
|
struct kernel_long_ad *last_ext,
|
||||||
|
uint32_t final_block_len)
|
||||||
|
{
|
||||||
|
struct super_block *sb = inode->i_sb;
|
||||||
|
uint32_t added_bytes;
|
||||||
|
|
||||||
|
added_bytes = final_block_len -
|
||||||
|
(last_ext->extLength & (sb->s_blocksize - 1));
|
||||||
|
last_ext->extLength += added_bytes;
|
||||||
|
UDF_I(inode)->i_lenExtents += added_bytes;
|
||||||
|
|
||||||
|
udf_write_aext(inode, last_pos, &last_ext->extLocation,
|
||||||
|
last_ext->extLength, 1);
|
||||||
|
}
|
||||||
|
|
||||||
static int udf_extend_file(struct inode *inode, loff_t newsize)
|
static int udf_extend_file(struct inode *inode, loff_t newsize)
|
||||||
{
|
{
|
||||||
|
|
||||||
@ -605,10 +623,12 @@ static int udf_extend_file(struct inode *inode, loff_t newsize)
|
|||||||
int8_t etype;
|
int8_t etype;
|
||||||
struct super_block *sb = inode->i_sb;
|
struct super_block *sb = inode->i_sb;
|
||||||
sector_t first_block = newsize >> sb->s_blocksize_bits, offset;
|
sector_t first_block = newsize >> sb->s_blocksize_bits, offset;
|
||||||
|
unsigned long partial_final_block;
|
||||||
int adsize;
|
int adsize;
|
||||||
struct udf_inode_info *iinfo = UDF_I(inode);
|
struct udf_inode_info *iinfo = UDF_I(inode);
|
||||||
struct kernel_long_ad extent;
|
struct kernel_long_ad extent;
|
||||||
int err;
|
int err = 0;
|
||||||
|
int within_final_block;
|
||||||
|
|
||||||
if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_SHORT)
|
if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_SHORT)
|
||||||
adsize = sizeof(struct short_ad);
|
adsize = sizeof(struct short_ad);
|
||||||
@ -618,18 +638,8 @@ static int udf_extend_file(struct inode *inode, loff_t newsize)
|
|||||||
BUG();
|
BUG();
|
||||||
|
|
||||||
etype = inode_bmap(inode, first_block, &epos, &eloc, &elen, &offset);
|
etype = inode_bmap(inode, first_block, &epos, &eloc, &elen, &offset);
|
||||||
|
within_final_block = (etype != -1);
|
||||||
|
|
||||||
/* File has extent covering the new size (could happen when extending
|
|
||||||
* inside a block)? */
|
|
||||||
if (etype != -1)
|
|
||||||
return 0;
|
|
||||||
if (newsize & (sb->s_blocksize - 1))
|
|
||||||
offset++;
|
|
||||||
/* Extended file just to the boundary of the last file block? */
|
|
||||||
if (offset == 0)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
/* Truncate is extending the file by 'offset' blocks */
|
|
||||||
if ((!epos.bh && epos.offset == udf_file_entry_alloc_offset(inode)) ||
|
if ((!epos.bh && epos.offset == udf_file_entry_alloc_offset(inode)) ||
|
||||||
(epos.bh && epos.offset == sizeof(struct allocExtDesc))) {
|
(epos.bh && epos.offset == sizeof(struct allocExtDesc))) {
|
||||||
/* File has no extents at all or has empty last
|
/* File has no extents at all or has empty last
|
||||||
@ -643,7 +653,22 @@ static int udf_extend_file(struct inode *inode, loff_t newsize)
|
|||||||
&extent.extLength, 0);
|
&extent.extLength, 0);
|
||||||
extent.extLength |= etype << 30;
|
extent.extLength |= etype << 30;
|
||||||
}
|
}
|
||||||
err = udf_do_extend_file(inode, &epos, &extent, offset);
|
|
||||||
|
partial_final_block = newsize & (sb->s_blocksize - 1);
|
||||||
|
|
||||||
|
/* File has extent covering the new size (could happen when extending
|
||||||
|
* inside a block)?
|
||||||
|
*/
|
||||||
|
if (within_final_block) {
|
||||||
|
/* Extending file within the last file block */
|
||||||
|
udf_do_extend_final_block(inode, &epos, &extent,
|
||||||
|
partial_final_block);
|
||||||
|
} else {
|
||||||
|
loff_t add = ((loff_t)offset << sb->s_blocksize_bits) |
|
||||||
|
partial_final_block;
|
||||||
|
err = udf_do_extend_file(inode, &epos, &extent, add);
|
||||||
|
}
|
||||||
|
|
||||||
if (err < 0)
|
if (err < 0)
|
||||||
goto out;
|
goto out;
|
||||||
err = 0;
|
err = 0;
|
||||||
@ -745,6 +770,7 @@ static sector_t inode_getblk(struct inode *inode, sector_t block,
|
|||||||
/* Are we beyond EOF? */
|
/* Are we beyond EOF? */
|
||||||
if (etype == -1) {
|
if (etype == -1) {
|
||||||
int ret;
|
int ret;
|
||||||
|
loff_t hole_len;
|
||||||
isBeyondEOF = true;
|
isBeyondEOF = true;
|
||||||
if (count) {
|
if (count) {
|
||||||
if (c)
|
if (c)
|
||||||
@ -760,7 +786,8 @@ static sector_t inode_getblk(struct inode *inode, sector_t block,
|
|||||||
startnum = (offset > 0);
|
startnum = (offset > 0);
|
||||||
}
|
}
|
||||||
/* Create extents for the hole between EOF and offset */
|
/* Create extents for the hole between EOF and offset */
|
||||||
ret = udf_do_extend_file(inode, &prev_epos, laarr, offset);
|
hole_len = (loff_t)offset << inode->i_blkbits;
|
||||||
|
ret = udf_do_extend_file(inode, &prev_epos, laarr, hole_len);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
*err = ret;
|
*err = ret;
|
||||||
newblock = 0;
|
newblock = 0;
|
||||||
|
Loading…
Reference in New Issue
Block a user