Commit c37336b0 authored by Evgeniy Dushistov's avatar Evgeniy Dushistov Committed by Linus Torvalds

[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: default avatarEvgeniy Dushistov <dushistov@mail.ru>
Signed-off-by: default avatarAndrew Morton <akpm@osdl.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@osdl.org>
parent 08fb306f
...@@ -169,18 +169,20 @@ static void ufs_clear_frag(struct inode *inode, struct buffer_head *bh) ...@@ -169,18 +169,20 @@ static void ufs_clear_frag(struct inode *inode, struct buffer_head *bh)
static struct buffer_head * static struct buffer_head *
ufs_clear_frags(struct inode *inode, sector_t beg, 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; sector_t end = beg + n;
res = sb_getblk(inode->i_sb, beg); for (; beg < end; ++beg) {
ufs_clear_frag(inode, res);
for (++beg; beg < end; ++beg) {
bh = sb_getblk(inode->i_sb, beg); bh = sb_getblk(inode->i_sb, beg);
ufs_clear_frag(inode, bh); ufs_clear_frag(inode, bh);
if (want != beg)
brelse(bh); brelse(bh);
else
res = bh;
} }
BUG_ON(!res);
return res; return res;
} }
...@@ -265,7 +267,9 @@ ufs_inode_getfrag(struct inode *inode, unsigned int fragment, ...@@ -265,7 +267,9 @@ ufs_inode_getfrag(struct inode *inode, unsigned int fragment,
lastfrag = ufsi->i_lastfrag; 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, tmp = ufs_new_fragments (inode, p, fragment - blockoff,
goal, required + blockoff, goal, required + blockoff,
err, locked_page); err, locked_page);
...@@ -277,13 +281,15 @@ ufs_inode_getfrag(struct inode *inode, unsigned int fragment, ...@@ -277,13 +281,15 @@ ufs_inode_getfrag(struct inode *inode, unsigned int fragment,
tmp = ufs_new_fragments(inode, p, fragment - (blockoff - lastblockoff), tmp = ufs_new_fragments(inode, p, fragment - (blockoff - lastblockoff),
fs32_to_cpu(sb, *p), required + (blockoff - lastblockoff), fs32_to_cpu(sb, *p), required + (blockoff - lastblockoff),
err, locked_page); err, locked_page);
} } else /* (lastblock > block) */ {
/* /*
* We will allocate new block before last allocated block * We will allocate new block before last allocated block
*/ */
else /* (lastblock > block) */ { if (block) {
if (lastblock && (tmp = fs32_to_cpu(sb, ufsi->i_u1.i_data[lastblock-1]))) tmp = fs32_to_cpu(sb, ufsi->i_u1.i_data[block-1]);
if (tmp)
goal = tmp + uspi->s_fpb; goal = tmp + uspi->s_fpb;
}
tmp = ufs_new_fragments(inode, p, fragment - blockoff, tmp = ufs_new_fragments(inode, p, fragment - blockoff,
goal, uspi->s_fpb, err, locked_page); goal, uspi->s_fpb, err, locked_page);
} }
...@@ -296,7 +302,7 @@ ufs_inode_getfrag(struct inode *inode, unsigned int fragment, ...@@ -296,7 +302,7 @@ ufs_inode_getfrag(struct inode *inode, unsigned int fragment,
} }
if (!phys) { if (!phys) {
result = ufs_clear_frags(inode, tmp + blockoff, required); result = ufs_clear_frags(inode, tmp, required, tmp + blockoff);
} else { } else {
*phys = tmp + blockoff; *phys = tmp + blockoff;
result = NULL; result = NULL;
...@@ -383,7 +389,7 @@ ufs_inode_getblock(struct inode *inode, struct buffer_head *bh, ...@@ -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; goal = tmp + uspi->s_fpb;
else else
goal = bh->b_blocknr + uspi->s_fpb; goal = bh->b_blocknr + uspi->s_fpb;
...@@ -397,7 +403,8 @@ ufs_inode_getblock(struct inode *inode, struct buffer_head *bh, ...@@ -397,7 +403,8 @@ ufs_inode_getblock(struct inode *inode, struct buffer_head *bh,
if (!phys) { if (!phys) {
result = ufs_clear_frags(inode, tmp + blockoff, uspi->s_fpb); result = ufs_clear_frags(inode, tmp, uspi->s_fpb,
tmp + blockoff);
} else { } else {
*phys = tmp + blockoff; *phys = tmp + blockoff;
*new = 1; *new = 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