summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEvgeniy Dushistov <dushistov@mail.ru>2006-08-27 01:23:45 -0700
committerLinus Torvalds <torvalds@g5.osdl.org>2006-08-27 11:01:31 -0700
commitc37336b078ba9d2ff38c535b194996a7ad6e69f8 (patch)
tree36bb05432893a6db7620e283ea90e6be2c483363
parent08fb306fe63d98eb86e3b16f4cc21816fa47f18e (diff)
downloadlinux-3.10-c37336b078ba9d2ff38c535b194996a7ad6e69f8.tar.gz
linux-3.10-c37336b078ba9d2ff38c535b194996a7ad6e69f8.tar.bz2
linux-3.10-c37336b078ba9d2ff38c535b194996a7ad6e69f8.zip
[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>
-rw-r--r--fs/ufs/inode.c35
1 files changed, 21 insertions, 14 deletions
diff --git a/fs/ufs/inode.c b/fs/ufs/inode.c
index e7c8615beb6..30c6e8a9446 100644
--- a/fs/ufs/inode.c
+++ b/fs/ufs/inode.c
@@ -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 @@ repeat:
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 @@ repeat:
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 @@ repeat:
}
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 @@ repeat:
}
}
- 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 @@ repeat:
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;