[PATCH] ufs: write to hole in big file
On UFS, this scenario: open(O_TRUNC) lseek(1024 * 1024 * 80) write("A") lseek(1024 * 2) write("A") may cause access to invalid address. This happened because of "goal" is calculated in wrong way in block allocation path, as I see this problem exists also in 2.4. We use construction like this i_data[lastfrag], i_data array of pointers to direct blocks, indirect and so on, it has ceratain size ~20 elements, and lastfrag may have value for example 40000. Also this patch fixes related to handling such scenario issues, wrong zeroing metadata, in case of block(not fragment) allocation, and wrong goal calculation, when we allocate block Signed-off-by: Evgeniy Dushistov <dushistov@mail.ru> Signed-off-by: Andrew Morton <akpm@osdl.org> Signed-off-by: Linus Torvalds <torvalds@osdl.org>
This commit is contained in:
parent
08fb306fe6
commit
c37336b078
1 changed files with 21 additions and 14 deletions
|
@ -169,18 +169,20 @@ static void ufs_clear_frag(struct inode *inode, struct buffer_head *bh)
|
|||
|
||||
static struct buffer_head *
|
||||
ufs_clear_frags(struct inode *inode, sector_t beg,
|
||||
unsigned int n)
|
||||
unsigned int n, sector_t want)
|
||||
{
|
||||
struct buffer_head *res, *bh;
|
||||
struct buffer_head *res = NULL, *bh;
|
||||
sector_t end = beg + n;
|
||||
|
||||
res = sb_getblk(inode->i_sb, beg);
|
||||
ufs_clear_frag(inode, res);
|
||||
for (++beg; beg < end; ++beg) {
|
||||
for (; beg < end; ++beg) {
|
||||
bh = sb_getblk(inode->i_sb, beg);
|
||||
ufs_clear_frag(inode, bh);
|
||||
brelse(bh);
|
||||
if (want != beg)
|
||||
brelse(bh);
|
||||
else
|
||||
res = bh;
|
||||
}
|
||||
BUG_ON(!res);
|
||||
return res;
|
||||
}
|
||||
|
||||
|
@ -265,7 +267,9 @@ ufs_inode_getfrag(struct inode *inode, unsigned int fragment,
|
|||
lastfrag = ufsi->i_lastfrag;
|
||||
|
||||
}
|
||||
goal = fs32_to_cpu(sb, ufsi->i_u1.i_data[lastblock]) + uspi->s_fpb;
|
||||
tmp = fs32_to_cpu(sb, ufsi->i_u1.i_data[lastblock]);
|
||||
if (tmp)
|
||||
goal = tmp + uspi->s_fpb;
|
||||
tmp = ufs_new_fragments (inode, p, fragment - blockoff,
|
||||
goal, required + blockoff,
|
||||
err, locked_page);
|
||||
|
@ -277,13 +281,15 @@ ufs_inode_getfrag(struct inode *inode, unsigned int fragment,
|
|||
tmp = ufs_new_fragments(inode, p, fragment - (blockoff - lastblockoff),
|
||||
fs32_to_cpu(sb, *p), required + (blockoff - lastblockoff),
|
||||
err, locked_page);
|
||||
}
|
||||
} else /* (lastblock > block) */ {
|
||||
/*
|
||||
* We will allocate new block before last allocated block
|
||||
*/
|
||||
else /* (lastblock > block) */ {
|
||||
if (lastblock && (tmp = fs32_to_cpu(sb, ufsi->i_u1.i_data[lastblock-1])))
|
||||
goal = tmp + uspi->s_fpb;
|
||||
if (block) {
|
||||
tmp = fs32_to_cpu(sb, ufsi->i_u1.i_data[block-1]);
|
||||
if (tmp)
|
||||
goal = tmp + uspi->s_fpb;
|
||||
}
|
||||
tmp = ufs_new_fragments(inode, p, fragment - blockoff,
|
||||
goal, uspi->s_fpb, err, locked_page);
|
||||
}
|
||||
|
@ -296,7 +302,7 @@ ufs_inode_getfrag(struct inode *inode, unsigned int fragment,
|
|||
}
|
||||
|
||||
if (!phys) {
|
||||
result = ufs_clear_frags(inode, tmp + blockoff, required);
|
||||
result = ufs_clear_frags(inode, tmp, required, tmp + blockoff);
|
||||
} else {
|
||||
*phys = tmp + blockoff;
|
||||
result = NULL;
|
||||
|
@ -383,7 +389,7 @@ ufs_inode_getblock(struct inode *inode, struct buffer_head *bh,
|
|||
}
|
||||
}
|
||||
|
||||
if (block && (tmp = fs32_to_cpu(sb, ((__fs32*)bh->b_data)[block-1]) + uspi->s_fpb))
|
||||
if (block && (tmp = fs32_to_cpu(sb, ((__fs32*)bh->b_data)[block-1])))
|
||||
goal = tmp + uspi->s_fpb;
|
||||
else
|
||||
goal = bh->b_blocknr + uspi->s_fpb;
|
||||
|
@ -397,7 +403,8 @@ ufs_inode_getblock(struct inode *inode, struct buffer_head *bh,
|
|||
|
||||
|
||||
if (!phys) {
|
||||
result = ufs_clear_frags(inode, tmp + blockoff, uspi->s_fpb);
|
||||
result = ufs_clear_frags(inode, tmp, uspi->s_fpb,
|
||||
tmp + blockoff);
|
||||
} else {
|
||||
*phys = tmp + blockoff;
|
||||
*new = 1;
|
||||
|
|
Loading…
Reference in a new issue