Commit a8fad984 authored by Al Viro's avatar Al Viro

ufs_truncate_blocks(): fix the case when size is in the last direct block

The logics when deciding whether we need to do anything with direct blocks
is broken when new size is within the last direct block.  It's better to
find the path to the last byte _not_ to be removed and use that instead
of the path to the beginning of the first block to be freed...
Signed-off-by: default avatarAl Viro <viro@zeniv.linux.org.uk>
parent 289dec5b
...@@ -886,7 +886,6 @@ static inline void free_data(struct to_free *ctx, u64 from, unsigned count) ...@@ -886,7 +886,6 @@ static inline void free_data(struct to_free *ctx, u64 from, unsigned count)
ctx->to = from + count; ctx->to = from + count;
} }
#define DIRECT_BLOCK ((inode->i_size + uspi->s_bsize - 1) >> uspi->s_bshift)
#define DIRECT_FRAGMENT ((inode->i_size + uspi->s_fsize - 1) >> uspi->s_fshift) #define DIRECT_FRAGMENT ((inode->i_size + uspi->s_fsize - 1) >> uspi->s_fshift)
static void ufs_trunc_direct(struct inode *inode) static void ufs_trunc_direct(struct inode *inode)
...@@ -1124,19 +1123,24 @@ static void ufs_truncate_blocks(struct inode *inode) ...@@ -1124,19 +1123,24 @@ static void ufs_truncate_blocks(struct inode *inode)
struct super_block *sb = inode->i_sb; struct super_block *sb = inode->i_sb;
struct ufs_sb_private_info *uspi = UFS_SB(sb)->s_uspi; struct ufs_sb_private_info *uspi = UFS_SB(sb)->s_uspi;
unsigned offsets[4]; unsigned offsets[4];
int depth = ufs_block_to_path(inode, DIRECT_BLOCK, offsets); int depth;
int depth2; int depth2;
unsigned i; unsigned i;
struct ufs_buffer_head *ubh[3]; struct ufs_buffer_head *ubh[3];
void *p; void *p;
u64 block; u64 block;
if (inode->i_size) {
sector_t last = (inode->i_size - 1) >> uspi->s_bshift;
depth = ufs_block_to_path(inode, last, offsets);
if (!depth) if (!depth)
return; return;
} else {
depth = 1;
}
/* find the last non-zero in offsets[] */
for (depth2 = depth - 1; depth2; depth2--) for (depth2 = depth - 1; depth2; depth2--)
if (offsets[depth2]) if (offsets[depth2] != uspi->s_apb - 1)
break; break;
mutex_lock(&ufsi->truncate_mutex); mutex_lock(&ufsi->truncate_mutex);
...@@ -1145,9 +1149,8 @@ static void ufs_truncate_blocks(struct inode *inode) ...@@ -1145,9 +1149,8 @@ static void ufs_truncate_blocks(struct inode *inode)
offsets[0] = UFS_IND_BLOCK; offsets[0] = UFS_IND_BLOCK;
} else { } else {
/* get the blocks that should be partially emptied */ /* get the blocks that should be partially emptied */
p = ufs_get_direct_data_ptr(uspi, ufsi, offsets[0]); p = ufs_get_direct_data_ptr(uspi, ufsi, offsets[0]++);
for (i = 0; i < depth2; i++) { for (i = 0; i < depth2; i++) {
offsets[i]++; /* next branch is fully freed */
block = ufs_data_ptr_to_cpu(sb, p); block = ufs_data_ptr_to_cpu(sb, p);
if (!block) if (!block)
break; break;
...@@ -1158,7 +1161,7 @@ static void ufs_truncate_blocks(struct inode *inode) ...@@ -1158,7 +1161,7 @@ static void ufs_truncate_blocks(struct inode *inode)
write_sequnlock(&ufsi->meta_lock); write_sequnlock(&ufsi->meta_lock);
break; break;
} }
p = ubh_get_data_ptr(uspi, ubh[i], offsets[i + 1]); p = ubh_get_data_ptr(uspi, ubh[i], offsets[i + 1]++);
} }
while (i--) while (i--)
free_branch_tail(inode, offsets[i + 1], ubh[i], depth - i - 1); free_branch_tail(inode, offsets[i + 1], ubh[i], depth - i - 1);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment