Skip to content

Commit

Permalink
[PATCH] ufs: write to hole in big file
Browse files Browse the repository at this point in the history
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 <[email protected]>
Signed-off-by: Andrew Morton <[email protected]>
Signed-off-by: Linus Torvalds <[email protected]>
  • Loading branch information
Dushistov authored and Linus Torvalds committed Aug 27, 2006
1 parent 08fb306 commit c37336b
Showing 1 changed file with 21 additions and 14 deletions.
35 changes: 21 additions & 14 deletions fs/ufs/inode.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

Expand Down Expand Up @@ -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);
Expand All @@ -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);
}
Expand All @@ -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;
Expand Down Expand Up @@ -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;
Expand All @@ -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;
Expand Down

0 comments on commit c37336b

Please sign in to comment.