2005-04-16 18:20:36 -04:00
|
|
|
/*
|
|
|
|
* linux/fs/ufs/truncate.c
|
|
|
|
*
|
|
|
|
* Copyright (C) 1998
|
|
|
|
* Daniel Pirkl <daniel.pirkl@email.cz>
|
|
|
|
* Charles University, Faculty of Mathematics and Physics
|
|
|
|
*
|
|
|
|
* from
|
|
|
|
*
|
|
|
|
* linux/fs/ext2/truncate.c
|
|
|
|
*
|
|
|
|
* Copyright (C) 1992, 1993, 1994, 1995
|
|
|
|
* Remy Card (card@masi.ibp.fr)
|
|
|
|
* Laboratoire MASI - Institut Blaise Pascal
|
|
|
|
* Universite Pierre et Marie Curie (Paris VI)
|
|
|
|
*
|
|
|
|
* from
|
|
|
|
*
|
|
|
|
* linux/fs/minix/truncate.c
|
|
|
|
*
|
|
|
|
* Copyright (C) 1991, 1992 Linus Torvalds
|
|
|
|
*
|
|
|
|
* Big-endian to little-endian byte-swapping/bitmaps by
|
|
|
|
* David S. Miller (davem@caip.rutgers.edu), 1995
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Real random numbers for secure rm added 94/02/18
|
|
|
|
* Idea from Pierre del Perugia <delperug@gla.ecoledoc.ibp.fr>
|
|
|
|
*/
|
|
|
|
|
2006-02-03 06:04:06 -05:00
|
|
|
/*
|
2007-02-12 03:54:32 -05:00
|
|
|
* Adoptation to use page cache and UFS2 write support by
|
|
|
|
* Evgeniy Dushistov <dushistov@mail.ru>, 2006-2007
|
2006-02-03 06:04:06 -05:00
|
|
|
*/
|
|
|
|
|
2005-04-16 18:20:36 -04:00
|
|
|
#include <linux/errno.h>
|
|
|
|
#include <linux/fs.h>
|
|
|
|
#include <linux/fcntl.h>
|
|
|
|
#include <linux/time.h>
|
|
|
|
#include <linux/stat.h>
|
|
|
|
#include <linux/string.h>
|
|
|
|
#include <linux/smp_lock.h>
|
|
|
|
#include <linux/buffer_head.h>
|
|
|
|
#include <linux/blkdev.h>
|
|
|
|
#include <linux/sched.h>
|
|
|
|
|
2008-02-08 07:21:31 -05:00
|
|
|
#include "ufs_fs.h"
|
2007-10-17 02:26:51 -04:00
|
|
|
#include "ufs.h"
|
2005-04-16 18:20:36 -04:00
|
|
|
#include "swab.h"
|
|
|
|
#include "util.h"
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Secure deletion currently doesn't work. It interacts very badly
|
|
|
|
* with buffers shared with memory mappings, and for that reason
|
|
|
|
* can't be done in the truncate() routines. It should instead be
|
|
|
|
* done separately in "release()" before calling the truncate routines
|
|
|
|
* that will release the actual file blocks.
|
|
|
|
*
|
|
|
|
* Linus
|
|
|
|
*/
|
|
|
|
|
|
|
|
#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)
|
|
|
|
|
|
|
|
|
2007-02-12 03:54:32 -05:00
|
|
|
static int ufs_trunc_direct(struct inode *inode)
|
2005-04-16 18:20:36 -04:00
|
|
|
{
|
|
|
|
struct ufs_inode_info *ufsi = UFS_I(inode);
|
|
|
|
struct super_block * sb;
|
|
|
|
struct ufs_sb_private_info * uspi;
|
2007-02-12 03:54:32 -05:00
|
|
|
void *p;
|
|
|
|
u64 frag1, frag2, frag3, frag4, block1, block2;
|
2005-04-16 18:20:36 -04:00
|
|
|
unsigned frag_to_free, free_count;
|
2006-02-03 06:04:06 -05:00
|
|
|
unsigned i, tmp;
|
2005-04-16 18:20:36 -04:00
|
|
|
int retry;
|
|
|
|
|
[PATCH] ufs: zeroize the rest of block in truncate
This patch fix behaviour in such test scenario:
lseek(fd, BIG_OFFSET)
write(fd, buf, sizeof(buf))
truncate(BIG_OFFSET)
truncate(BIG_OFFSET + sizeof(buf))
read(fd, buf...)
Because of if file big enough(BIG_OFFSET) we start allocate space by block,
ordinary block size > page size, so we should zeroize the rest of block in
truncate(except last framgnet, about which VFS should care), to not get
garbage, when we extend file.
Also patch corrects conversion from pointer to block to physical block number,
this helps in case of not common used UFS types.
And add to debug output inode number.
Signed-off-by: Evgeniy Dushistov <dushistov@mail.ru>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2007-03-16 17:38:09 -04:00
|
|
|
UFSD("ENTER: ino %lu\n", inode->i_ino);
|
2005-04-16 18:20:36 -04:00
|
|
|
|
|
|
|
sb = inode->i_sb;
|
|
|
|
uspi = UFS_SB(sb)->s_uspi;
|
|
|
|
|
|
|
|
frag_to_free = 0;
|
|
|
|
free_count = 0;
|
|
|
|
retry = 0;
|
|
|
|
|
|
|
|
frag1 = DIRECT_FRAGMENT;
|
|
|
|
frag4 = min_t(u32, UFS_NDIR_FRAGMENT, ufsi->i_lastfrag);
|
|
|
|
frag2 = ((frag1 & uspi->s_fpbmask) ? ((frag1 | uspi->s_fpbmask) + 1) : frag1);
|
|
|
|
frag3 = frag4 & ~uspi->s_fpbmask;
|
|
|
|
block1 = block2 = 0;
|
|
|
|
if (frag2 > frag3) {
|
|
|
|
frag2 = frag4;
|
|
|
|
frag3 = frag4 = 0;
|
2007-02-12 03:54:32 -05:00
|
|
|
} else if (frag2 < frag3) {
|
2005-04-16 18:20:36 -04:00
|
|
|
block1 = ufs_fragstoblks (frag2);
|
|
|
|
block2 = ufs_fragstoblks (frag3);
|
|
|
|
}
|
|
|
|
|
[PATCH] ufs: zeroize the rest of block in truncate
This patch fix behaviour in such test scenario:
lseek(fd, BIG_OFFSET)
write(fd, buf, sizeof(buf))
truncate(BIG_OFFSET)
truncate(BIG_OFFSET + sizeof(buf))
read(fd, buf...)
Because of if file big enough(BIG_OFFSET) we start allocate space by block,
ordinary block size > page size, so we should zeroize the rest of block in
truncate(except last framgnet, about which VFS should care), to not get
garbage, when we extend file.
Also patch corrects conversion from pointer to block to physical block number,
this helps in case of not common used UFS types.
And add to debug output inode number.
Signed-off-by: Evgeniy Dushistov <dushistov@mail.ru>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2007-03-16 17:38:09 -04:00
|
|
|
UFSD("ino %lu, frag1 %llu, frag2 %llu, block1 %llu, block2 %llu,"
|
|
|
|
" frag3 %llu, frag4 %llu\n", inode->i_ino,
|
2007-02-12 03:54:32 -05:00
|
|
|
(unsigned long long)frag1, (unsigned long long)frag2,
|
|
|
|
(unsigned long long)block1, (unsigned long long)block2,
|
|
|
|
(unsigned long long)frag3, (unsigned long long)frag4);
|
2005-04-16 18:20:36 -04:00
|
|
|
|
|
|
|
if (frag1 >= frag2)
|
|
|
|
goto next1;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Free first free fragments
|
|
|
|
*/
|
2007-02-12 03:54:32 -05:00
|
|
|
p = ufs_get_direct_data_ptr(uspi, ufsi, ufs_fragstoblks(frag1));
|
|
|
|
tmp = ufs_data_ptr_to_cpu(sb, p);
|
2005-04-16 18:20:36 -04:00
|
|
|
if (!tmp )
|
|
|
|
ufs_panic (sb, "ufs_trunc_direct", "internal error");
|
2007-01-29 16:19:55 -05:00
|
|
|
frag2 -= frag1;
|
2005-04-16 18:20:36 -04:00
|
|
|
frag1 = ufs_fragnum (frag1);
|
2006-02-03 06:04:06 -05:00
|
|
|
|
2007-01-29 16:19:55 -05:00
|
|
|
ufs_free_fragments(inode, tmp + frag1, frag2);
|
2006-06-25 08:47:26 -04:00
|
|
|
mark_inode_dirty(inode);
|
2005-04-16 18:20:36 -04:00
|
|
|
frag_to_free = tmp + frag1;
|
|
|
|
|
|
|
|
next1:
|
|
|
|
/*
|
|
|
|
* Free whole blocks
|
|
|
|
*/
|
|
|
|
for (i = block1 ; i < block2; i++) {
|
2007-02-12 03:54:32 -05:00
|
|
|
p = ufs_get_direct_data_ptr(uspi, ufsi, i);
|
|
|
|
tmp = ufs_data_ptr_to_cpu(sb, p);
|
2005-04-16 18:20:36 -04:00
|
|
|
if (!tmp)
|
|
|
|
continue;
|
2007-02-12 03:54:32 -05:00
|
|
|
ufs_data_ptr_clear(uspi, p);
|
2006-06-25 08:47:26 -04:00
|
|
|
|
2005-04-16 18:20:36 -04:00
|
|
|
if (free_count == 0) {
|
|
|
|
frag_to_free = tmp;
|
|
|
|
free_count = uspi->s_fpb;
|
|
|
|
} else if (free_count > 0 && frag_to_free == tmp - free_count)
|
|
|
|
free_count += uspi->s_fpb;
|
|
|
|
else {
|
|
|
|
ufs_free_blocks (inode, frag_to_free, free_count);
|
|
|
|
frag_to_free = tmp;
|
|
|
|
free_count = uspi->s_fpb;
|
|
|
|
}
|
2006-06-25 08:47:26 -04:00
|
|
|
mark_inode_dirty(inode);
|
2005-04-16 18:20:36 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
if (free_count > 0)
|
|
|
|
ufs_free_blocks (inode, frag_to_free, free_count);
|
|
|
|
|
|
|
|
if (frag3 >= frag4)
|
|
|
|
goto next3;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Free last free fragments
|
|
|
|
*/
|
2007-02-12 03:54:32 -05:00
|
|
|
p = ufs_get_direct_data_ptr(uspi, ufsi, ufs_fragstoblks(frag3));
|
|
|
|
tmp = ufs_data_ptr_to_cpu(sb, p);
|
2005-04-16 18:20:36 -04:00
|
|
|
if (!tmp )
|
|
|
|
ufs_panic(sb, "ufs_truncate_direct", "internal error");
|
|
|
|
frag4 = ufs_fragnum (frag4);
|
2007-02-12 03:54:32 -05:00
|
|
|
ufs_data_ptr_clear(uspi, p);
|
2006-06-25 08:47:26 -04:00
|
|
|
|
2005-04-16 18:20:36 -04:00
|
|
|
ufs_free_fragments (inode, tmp, frag4);
|
2006-06-25 08:47:26 -04:00
|
|
|
mark_inode_dirty(inode);
|
2005-04-16 18:20:36 -04:00
|
|
|
next3:
|
|
|
|
|
[PATCH] ufs: zeroize the rest of block in truncate
This patch fix behaviour in such test scenario:
lseek(fd, BIG_OFFSET)
write(fd, buf, sizeof(buf))
truncate(BIG_OFFSET)
truncate(BIG_OFFSET + sizeof(buf))
read(fd, buf...)
Because of if file big enough(BIG_OFFSET) we start allocate space by block,
ordinary block size > page size, so we should zeroize the rest of block in
truncate(except last framgnet, about which VFS should care), to not get
garbage, when we extend file.
Also patch corrects conversion from pointer to block to physical block number,
this helps in case of not common used UFS types.
And add to debug output inode number.
Signed-off-by: Evgeniy Dushistov <dushistov@mail.ru>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2007-03-16 17:38:09 -04:00
|
|
|
UFSD("EXIT: ino %lu\n", inode->i_ino);
|
2005-04-16 18:20:36 -04:00
|
|
|
return retry;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2007-02-12 03:54:32 -05:00
|
|
|
static int ufs_trunc_indirect(struct inode *inode, u64 offset, void *p)
|
2005-04-16 18:20:36 -04:00
|
|
|
{
|
|
|
|
struct super_block * sb;
|
|
|
|
struct ufs_sb_private_info * uspi;
|
|
|
|
struct ufs_buffer_head * ind_ubh;
|
2007-02-12 03:54:32 -05:00
|
|
|
void *ind;
|
|
|
|
u64 tmp, indirect_block, i, frag_to_free;
|
|
|
|
unsigned free_count;
|
2005-04-16 18:20:36 -04:00
|
|
|
int retry;
|
|
|
|
|
2007-02-12 03:54:32 -05:00
|
|
|
UFSD("ENTER: ino %lu, offset %llu, p: %p\n",
|
|
|
|
inode->i_ino, (unsigned long long)offset, p);
|
|
|
|
|
|
|
|
BUG_ON(!p);
|
2005-04-16 18:20:36 -04:00
|
|
|
|
|
|
|
sb = inode->i_sb;
|
|
|
|
uspi = UFS_SB(sb)->s_uspi;
|
|
|
|
|
|
|
|
frag_to_free = 0;
|
|
|
|
free_count = 0;
|
|
|
|
retry = 0;
|
|
|
|
|
2007-02-12 03:54:32 -05:00
|
|
|
tmp = ufs_data_ptr_to_cpu(sb, p);
|
2005-04-16 18:20:36 -04:00
|
|
|
if (!tmp)
|
|
|
|
return 0;
|
|
|
|
ind_ubh = ubh_bread(sb, tmp, uspi->s_bsize);
|
2007-02-12 03:54:32 -05:00
|
|
|
if (tmp != ufs_data_ptr_to_cpu(sb, p)) {
|
2005-04-16 18:20:36 -04:00
|
|
|
ubh_brelse (ind_ubh);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
if (!ind_ubh) {
|
2007-02-12 03:54:32 -05:00
|
|
|
ufs_data_ptr_clear(uspi, p);
|
2005-04-16 18:20:36 -04:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
indirect_block = (DIRECT_BLOCK > offset) ? (DIRECT_BLOCK - offset) : 0;
|
|
|
|
for (i = indirect_block; i < uspi->s_apb; i++) {
|
2007-02-12 03:54:32 -05:00
|
|
|
ind = ubh_get_data_ptr(uspi, ind_ubh, i);
|
|
|
|
tmp = ufs_data_ptr_to_cpu(sb, ind);
|
2005-04-16 18:20:36 -04:00
|
|
|
if (!tmp)
|
|
|
|
continue;
|
2006-02-03 06:04:06 -05:00
|
|
|
|
2007-02-12 03:54:32 -05:00
|
|
|
ufs_data_ptr_clear(uspi, ind);
|
2005-04-16 18:20:36 -04:00
|
|
|
ubh_mark_buffer_dirty(ind_ubh);
|
|
|
|
if (free_count == 0) {
|
|
|
|
frag_to_free = tmp;
|
|
|
|
free_count = uspi->s_fpb;
|
|
|
|
} else if (free_count > 0 && frag_to_free == tmp - free_count)
|
|
|
|
free_count += uspi->s_fpb;
|
|
|
|
else {
|
|
|
|
ufs_free_blocks (inode, frag_to_free, free_count);
|
|
|
|
frag_to_free = tmp;
|
|
|
|
free_count = uspi->s_fpb;
|
|
|
|
}
|
2006-06-25 08:47:26 -04:00
|
|
|
|
2005-04-16 18:20:36 -04:00
|
|
|
mark_inode_dirty(inode);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (free_count > 0) {
|
|
|
|
ufs_free_blocks (inode, frag_to_free, free_count);
|
|
|
|
}
|
|
|
|
for (i = 0; i < uspi->s_apb; i++)
|
2007-02-12 03:54:32 -05:00
|
|
|
if (!ufs_is_data_ptr_zero(uspi,
|
|
|
|
ubh_get_data_ptr(uspi, ind_ubh, i)))
|
2005-04-16 18:20:36 -04:00
|
|
|
break;
|
|
|
|
if (i >= uspi->s_apb) {
|
2007-02-12 03:54:32 -05:00
|
|
|
tmp = ufs_data_ptr_to_cpu(sb, p);
|
|
|
|
ufs_data_ptr_clear(uspi, p);
|
2006-06-25 08:47:26 -04:00
|
|
|
|
2006-06-25 08:47:18 -04:00
|
|
|
ufs_free_blocks (inode, tmp, uspi->s_fpb);
|
2006-06-25 08:47:26 -04:00
|
|
|
mark_inode_dirty(inode);
|
2006-06-25 08:47:18 -04:00
|
|
|
ubh_bforget(ind_ubh);
|
|
|
|
ind_ubh = NULL;
|
2005-04-16 18:20:36 -04:00
|
|
|
}
|
|
|
|
if (IS_SYNC(inode) && ind_ubh && ubh_buffer_dirty(ind_ubh)) {
|
2006-06-25 08:47:31 -04:00
|
|
|
ubh_ll_rw_block(SWRITE, ind_ubh);
|
2005-04-16 18:20:36 -04:00
|
|
|
ubh_wait_on_buffer (ind_ubh);
|
|
|
|
}
|
|
|
|
ubh_brelse (ind_ubh);
|
|
|
|
|
[PATCH] ufs: zeroize the rest of block in truncate
This patch fix behaviour in such test scenario:
lseek(fd, BIG_OFFSET)
write(fd, buf, sizeof(buf))
truncate(BIG_OFFSET)
truncate(BIG_OFFSET + sizeof(buf))
read(fd, buf...)
Because of if file big enough(BIG_OFFSET) we start allocate space by block,
ordinary block size > page size, so we should zeroize the rest of block in
truncate(except last framgnet, about which VFS should care), to not get
garbage, when we extend file.
Also patch corrects conversion from pointer to block to physical block number,
this helps in case of not common used UFS types.
And add to debug output inode number.
Signed-off-by: Evgeniy Dushistov <dushistov@mail.ru>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2007-03-16 17:38:09 -04:00
|
|
|
UFSD("EXIT: ino %lu\n", inode->i_ino);
|
2005-04-16 18:20:36 -04:00
|
|
|
|
|
|
|
return retry;
|
|
|
|
}
|
|
|
|
|
2007-02-12 03:54:32 -05:00
|
|
|
static int ufs_trunc_dindirect(struct inode *inode, u64 offset, void *p)
|
2005-04-16 18:20:36 -04:00
|
|
|
{
|
|
|
|
struct super_block * sb;
|
|
|
|
struct ufs_sb_private_info * uspi;
|
2007-02-12 03:54:32 -05:00
|
|
|
struct ufs_buffer_head *dind_bh;
|
|
|
|
u64 i, tmp, dindirect_block;
|
|
|
|
void *dind;
|
2005-04-16 18:20:36 -04:00
|
|
|
int retry = 0;
|
|
|
|
|
[PATCH] ufs: zeroize the rest of block in truncate
This patch fix behaviour in such test scenario:
lseek(fd, BIG_OFFSET)
write(fd, buf, sizeof(buf))
truncate(BIG_OFFSET)
truncate(BIG_OFFSET + sizeof(buf))
read(fd, buf...)
Because of if file big enough(BIG_OFFSET) we start allocate space by block,
ordinary block size > page size, so we should zeroize the rest of block in
truncate(except last framgnet, about which VFS should care), to not get
garbage, when we extend file.
Also patch corrects conversion from pointer to block to physical block number,
this helps in case of not common used UFS types.
And add to debug output inode number.
Signed-off-by: Evgeniy Dushistov <dushistov@mail.ru>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2007-03-16 17:38:09 -04:00
|
|
|
UFSD("ENTER: ino %lu\n", inode->i_ino);
|
2005-04-16 18:20:36 -04:00
|
|
|
|
|
|
|
sb = inode->i_sb;
|
|
|
|
uspi = UFS_SB(sb)->s_uspi;
|
|
|
|
|
|
|
|
dindirect_block = (DIRECT_BLOCK > offset)
|
|
|
|
? ((DIRECT_BLOCK - offset) >> uspi->s_apbshift) : 0;
|
|
|
|
retry = 0;
|
|
|
|
|
2007-02-12 03:54:32 -05:00
|
|
|
tmp = ufs_data_ptr_to_cpu(sb, p);
|
2005-04-16 18:20:36 -04:00
|
|
|
if (!tmp)
|
|
|
|
return 0;
|
|
|
|
dind_bh = ubh_bread(sb, tmp, uspi->s_bsize);
|
2007-02-12 03:54:32 -05:00
|
|
|
if (tmp != ufs_data_ptr_to_cpu(sb, p)) {
|
2005-04-16 18:20:36 -04:00
|
|
|
ubh_brelse (dind_bh);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
if (!dind_bh) {
|
2007-02-12 03:54:32 -05:00
|
|
|
ufs_data_ptr_clear(uspi, p);
|
2005-04-16 18:20:36 -04:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (i = dindirect_block ; i < uspi->s_apb ; i++) {
|
2007-02-12 03:54:32 -05:00
|
|
|
dind = ubh_get_data_ptr(uspi, dind_bh, i);
|
|
|
|
tmp = ufs_data_ptr_to_cpu(sb, dind);
|
2005-04-16 18:20:36 -04:00
|
|
|
if (!tmp)
|
|
|
|
continue;
|
|
|
|
retry |= ufs_trunc_indirect (inode, offset + (i << uspi->s_apbshift), dind);
|
|
|
|
ubh_mark_buffer_dirty(dind_bh);
|
|
|
|
}
|
|
|
|
|
|
|
|
for (i = 0; i < uspi->s_apb; i++)
|
2007-02-12 03:54:32 -05:00
|
|
|
if (!ufs_is_data_ptr_zero(uspi,
|
|
|
|
ubh_get_data_ptr(uspi, dind_bh, i)))
|
2005-04-16 18:20:36 -04:00
|
|
|
break;
|
|
|
|
if (i >= uspi->s_apb) {
|
2007-02-12 03:54:32 -05:00
|
|
|
tmp = ufs_data_ptr_to_cpu(sb, p);
|
|
|
|
ufs_data_ptr_clear(uspi, p);
|
2006-06-25 08:47:26 -04:00
|
|
|
|
|
|
|
ufs_free_blocks(inode, tmp, uspi->s_fpb);
|
2006-06-25 08:47:18 -04:00
|
|
|
mark_inode_dirty(inode);
|
|
|
|
ubh_bforget(dind_bh);
|
|
|
|
dind_bh = NULL;
|
2005-04-16 18:20:36 -04:00
|
|
|
}
|
|
|
|
if (IS_SYNC(inode) && dind_bh && ubh_buffer_dirty(dind_bh)) {
|
2006-06-25 08:47:31 -04:00
|
|
|
ubh_ll_rw_block(SWRITE, dind_bh);
|
2005-04-16 18:20:36 -04:00
|
|
|
ubh_wait_on_buffer (dind_bh);
|
|
|
|
}
|
|
|
|
ubh_brelse (dind_bh);
|
|
|
|
|
[PATCH] ufs: zeroize the rest of block in truncate
This patch fix behaviour in such test scenario:
lseek(fd, BIG_OFFSET)
write(fd, buf, sizeof(buf))
truncate(BIG_OFFSET)
truncate(BIG_OFFSET + sizeof(buf))
read(fd, buf...)
Because of if file big enough(BIG_OFFSET) we start allocate space by block,
ordinary block size > page size, so we should zeroize the rest of block in
truncate(except last framgnet, about which VFS should care), to not get
garbage, when we extend file.
Also patch corrects conversion from pointer to block to physical block number,
this helps in case of not common used UFS types.
And add to debug output inode number.
Signed-off-by: Evgeniy Dushistov <dushistov@mail.ru>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2007-03-16 17:38:09 -04:00
|
|
|
UFSD("EXIT: ino %lu\n", inode->i_ino);
|
2005-04-16 18:20:36 -04:00
|
|
|
|
|
|
|
return retry;
|
|
|
|
}
|
|
|
|
|
2007-02-12 03:54:32 -05:00
|
|
|
static int ufs_trunc_tindirect(struct inode *inode)
|
2005-04-16 18:20:36 -04:00
|
|
|
{
|
2007-02-12 03:54:32 -05:00
|
|
|
struct super_block *sb = inode->i_sb;
|
|
|
|
struct ufs_sb_private_info *uspi = UFS_SB(sb)->s_uspi;
|
2005-04-16 18:20:36 -04:00
|
|
|
struct ufs_inode_info *ufsi = UFS_I(inode);
|
|
|
|
struct ufs_buffer_head * tind_bh;
|
2007-02-12 03:54:32 -05:00
|
|
|
u64 tindirect_block, tmp, i;
|
|
|
|
void *tind, *p;
|
2005-04-16 18:20:36 -04:00
|
|
|
int retry;
|
|
|
|
|
[PATCH] ufs: zeroize the rest of block in truncate
This patch fix behaviour in such test scenario:
lseek(fd, BIG_OFFSET)
write(fd, buf, sizeof(buf))
truncate(BIG_OFFSET)
truncate(BIG_OFFSET + sizeof(buf))
read(fd, buf...)
Because of if file big enough(BIG_OFFSET) we start allocate space by block,
ordinary block size > page size, so we should zeroize the rest of block in
truncate(except last framgnet, about which VFS should care), to not get
garbage, when we extend file.
Also patch corrects conversion from pointer to block to physical block number,
this helps in case of not common used UFS types.
And add to debug output inode number.
Signed-off-by: Evgeniy Dushistov <dushistov@mail.ru>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2007-03-16 17:38:09 -04:00
|
|
|
UFSD("ENTER: ino %lu\n", inode->i_ino);
|
2005-04-16 18:20:36 -04:00
|
|
|
|
|
|
|
retry = 0;
|
|
|
|
|
|
|
|
tindirect_block = (DIRECT_BLOCK > (UFS_NDADDR + uspi->s_apb + uspi->s_2apb))
|
|
|
|
? ((DIRECT_BLOCK - UFS_NDADDR - uspi->s_apb - uspi->s_2apb) >> uspi->s_2apbshift) : 0;
|
2007-02-12 03:54:32 -05:00
|
|
|
|
|
|
|
p = ufs_get_direct_data_ptr(uspi, ufsi, UFS_TIND_BLOCK);
|
|
|
|
if (!(tmp = ufs_data_ptr_to_cpu(sb, p)))
|
2005-04-16 18:20:36 -04:00
|
|
|
return 0;
|
|
|
|
tind_bh = ubh_bread (sb, tmp, uspi->s_bsize);
|
2007-02-12 03:54:32 -05:00
|
|
|
if (tmp != ufs_data_ptr_to_cpu(sb, p)) {
|
2005-04-16 18:20:36 -04:00
|
|
|
ubh_brelse (tind_bh);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
if (!tind_bh) {
|
2007-02-12 03:54:32 -05:00
|
|
|
ufs_data_ptr_clear(uspi, p);
|
2005-04-16 18:20:36 -04:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (i = tindirect_block ; i < uspi->s_apb ; i++) {
|
2007-03-16 17:38:09 -04:00
|
|
|
tind = ubh_get_data_ptr(uspi, tind_bh, i);
|
2005-04-16 18:20:36 -04:00
|
|
|
retry |= ufs_trunc_dindirect(inode, UFS_NDADDR +
|
|
|
|
uspi->s_apb + ((i + 1) << uspi->s_2apbshift), tind);
|
|
|
|
ubh_mark_buffer_dirty(tind_bh);
|
|
|
|
}
|
|
|
|
for (i = 0; i < uspi->s_apb; i++)
|
2007-02-12 03:54:32 -05:00
|
|
|
if (!ufs_is_data_ptr_zero(uspi,
|
|
|
|
ubh_get_data_ptr(uspi, tind_bh, i)))
|
2005-04-16 18:20:36 -04:00
|
|
|
break;
|
|
|
|
if (i >= uspi->s_apb) {
|
2007-02-12 03:54:32 -05:00
|
|
|
tmp = ufs_data_ptr_to_cpu(sb, p);
|
|
|
|
ufs_data_ptr_clear(uspi, p);
|
2006-06-25 08:47:26 -04:00
|
|
|
|
|
|
|
ufs_free_blocks(inode, tmp, uspi->s_fpb);
|
2006-06-25 08:47:18 -04:00
|
|
|
mark_inode_dirty(inode);
|
|
|
|
ubh_bforget(tind_bh);
|
|
|
|
tind_bh = NULL;
|
2005-04-16 18:20:36 -04:00
|
|
|
}
|
|
|
|
if (IS_SYNC(inode) && tind_bh && ubh_buffer_dirty(tind_bh)) {
|
2006-06-25 08:47:31 -04:00
|
|
|
ubh_ll_rw_block(SWRITE, tind_bh);
|
2005-04-16 18:20:36 -04:00
|
|
|
ubh_wait_on_buffer (tind_bh);
|
|
|
|
}
|
|
|
|
ubh_brelse (tind_bh);
|
|
|
|
|
[PATCH] ufs: zeroize the rest of block in truncate
This patch fix behaviour in such test scenario:
lseek(fd, BIG_OFFSET)
write(fd, buf, sizeof(buf))
truncate(BIG_OFFSET)
truncate(BIG_OFFSET + sizeof(buf))
read(fd, buf...)
Because of if file big enough(BIG_OFFSET) we start allocate space by block,
ordinary block size > page size, so we should zeroize the rest of block in
truncate(except last framgnet, about which VFS should care), to not get
garbage, when we extend file.
Also patch corrects conversion from pointer to block to physical block number,
this helps in case of not common used UFS types.
And add to debug output inode number.
Signed-off-by: Evgeniy Dushistov <dushistov@mail.ru>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2007-03-16 17:38:09 -04:00
|
|
|
UFSD("EXIT: ino %lu\n", inode->i_ino);
|
2005-04-16 18:20:36 -04:00
|
|
|
return retry;
|
|
|
|
}
|
2006-07-01 07:36:24 -04:00
|
|
|
|
|
|
|
static int ufs_alloc_lastblock(struct inode *inode)
|
2005-04-16 18:20:36 -04:00
|
|
|
{
|
2006-07-01 07:36:24 -04:00
|
|
|
int err = 0;
|
[PATCH] ufs: zeroize the rest of block in truncate
This patch fix behaviour in such test scenario:
lseek(fd, BIG_OFFSET)
write(fd, buf, sizeof(buf))
truncate(BIG_OFFSET)
truncate(BIG_OFFSET + sizeof(buf))
read(fd, buf...)
Because of if file big enough(BIG_OFFSET) we start allocate space by block,
ordinary block size > page size, so we should zeroize the rest of block in
truncate(except last framgnet, about which VFS should care), to not get
garbage, when we extend file.
Also patch corrects conversion from pointer to block to physical block number,
this helps in case of not common used UFS types.
And add to debug output inode number.
Signed-off-by: Evgeniy Dushistov <dushistov@mail.ru>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2007-03-16 17:38:09 -04:00
|
|
|
struct super_block *sb = inode->i_sb;
|
2006-07-01 07:36:24 -04:00
|
|
|
struct address_space *mapping = inode->i_mapping;
|
[PATCH] ufs: zeroize the rest of block in truncate
This patch fix behaviour in such test scenario:
lseek(fd, BIG_OFFSET)
write(fd, buf, sizeof(buf))
truncate(BIG_OFFSET)
truncate(BIG_OFFSET + sizeof(buf))
read(fd, buf...)
Because of if file big enough(BIG_OFFSET) we start allocate space by block,
ordinary block size > page size, so we should zeroize the rest of block in
truncate(except last framgnet, about which VFS should care), to not get
garbage, when we extend file.
Also patch corrects conversion from pointer to block to physical block number,
this helps in case of not common used UFS types.
And add to debug output inode number.
Signed-off-by: Evgeniy Dushistov <dushistov@mail.ru>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2007-03-16 17:38:09 -04:00
|
|
|
struct ufs_sb_private_info *uspi = UFS_SB(sb)->s_uspi;
|
2007-02-12 03:54:32 -05:00
|
|
|
unsigned i, end;
|
|
|
|
sector_t lastfrag;
|
2006-07-01 07:36:24 -04:00
|
|
|
struct page *lastpage;
|
|
|
|
struct buffer_head *bh;
|
[PATCH] ufs: zeroize the rest of block in truncate
This patch fix behaviour in such test scenario:
lseek(fd, BIG_OFFSET)
write(fd, buf, sizeof(buf))
truncate(BIG_OFFSET)
truncate(BIG_OFFSET + sizeof(buf))
read(fd, buf...)
Because of if file big enough(BIG_OFFSET) we start allocate space by block,
ordinary block size > page size, so we should zeroize the rest of block in
truncate(except last framgnet, about which VFS should care), to not get
garbage, when we extend file.
Also patch corrects conversion from pointer to block to physical block number,
this helps in case of not common used UFS types.
And add to debug output inode number.
Signed-off-by: Evgeniy Dushistov <dushistov@mail.ru>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2007-03-16 17:38:09 -04:00
|
|
|
u64 phys64;
|
2006-07-01 07:36:24 -04:00
|
|
|
|
|
|
|
lastfrag = (i_size_read(inode) + uspi->s_fsize - 1) >> uspi->s_fshift;
|
|
|
|
|
[PATCH] ufs: truncate correction
1) When we allocated last fragment in ufs_truncate, we read page, check
if block mapped to address, and if not trying to allocate it. This is
wrong behaviour, fragment may be NOT allocated, but mapped, this
happened because of "block map" function not checked allocated fragment
or not, it just take address of the first fragment in the block, add
offset of fragment and return result, this is correct behaviour in
almost all situation except call from ufs_truncate.
2) Almost all implementation of UFS, which I can investigate have such
"defect": if you have full disk, and try truncate file, for example 3GB
to 2MB, and have hole in this region, truncate return -ENOSPC. I tried
evade from this problem, but "block allocation" algorithm is tied to
right value of i_lastfrag, and fix of this corner case may slow down of
ordinaries scenarios, so this patch makes behavior of "truncate"
operations similar to what other UFS implementations do.
Signed-off-by: Evgeniy Dushistov <dushistov@mail.ru>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-08-27 04:23:46 -04:00
|
|
|
if (!lastfrag)
|
2006-07-01 07:36:24 -04:00
|
|
|
goto out;
|
[PATCH] ufs: truncate correction
1) When we allocated last fragment in ufs_truncate, we read page, check
if block mapped to address, and if not trying to allocate it. This is
wrong behaviour, fragment may be NOT allocated, but mapped, this
happened because of "block map" function not checked allocated fragment
or not, it just take address of the first fragment in the block, add
offset of fragment and return result, this is correct behaviour in
almost all situation except call from ufs_truncate.
2) Almost all implementation of UFS, which I can investigate have such
"defect": if you have full disk, and try truncate file, for example 3GB
to 2MB, and have hole in this region, truncate return -ENOSPC. I tried
evade from this problem, but "block allocation" algorithm is tied to
right value of i_lastfrag, and fix of this corner case may slow down of
ordinaries scenarios, so this patch makes behavior of "truncate"
operations similar to what other UFS implementations do.
Signed-off-by: Evgeniy Dushistov <dushistov@mail.ru>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-08-27 04:23:46 -04:00
|
|
|
|
2006-07-01 07:36:24 -04:00
|
|
|
lastfrag--;
|
|
|
|
|
|
|
|
lastpage = ufs_get_locked_page(mapping, lastfrag >>
|
|
|
|
(PAGE_CACHE_SHIFT - inode->i_blkbits));
|
|
|
|
if (IS_ERR(lastpage)) {
|
|
|
|
err = -EIO;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
end = lastfrag & ((1 << (PAGE_CACHE_SHIFT - inode->i_blkbits)) - 1);
|
|
|
|
bh = page_buffers(lastpage);
|
|
|
|
for (i = 0; i < end; ++i)
|
|
|
|
bh = bh->b_this_page;
|
|
|
|
|
[PATCH] ufs: truncate correction
1) When we allocated last fragment in ufs_truncate, we read page, check
if block mapped to address, and if not trying to allocate it. This is
wrong behaviour, fragment may be NOT allocated, but mapped, this
happened because of "block map" function not checked allocated fragment
or not, it just take address of the first fragment in the block, add
offset of fragment and return result, this is correct behaviour in
almost all situation except call from ufs_truncate.
2) Almost all implementation of UFS, which I can investigate have such
"defect": if you have full disk, and try truncate file, for example 3GB
to 2MB, and have hole in this region, truncate return -ENOSPC. I tried
evade from this problem, but "block allocation" algorithm is tied to
right value of i_lastfrag, and fix of this corner case may slow down of
ordinaries scenarios, so this patch makes behavior of "truncate"
operations similar to what other UFS implementations do.
Signed-off-by: Evgeniy Dushistov <dushistov@mail.ru>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-08-27 04:23:46 -04:00
|
|
|
|
|
|
|
err = ufs_getfrag_block(inode, lastfrag, bh, 1);
|
|
|
|
|
|
|
|
if (unlikely(err))
|
|
|
|
goto out_unlock;
|
|
|
|
|
|
|
|
if (buffer_new(bh)) {
|
|
|
|
clear_buffer_new(bh);
|
|
|
|
unmap_underlying_metadata(bh->b_bdev,
|
|
|
|
bh->b_blocknr);
|
|
|
|
/*
|
|
|
|
* we do not zeroize fragment, because of
|
|
|
|
* if it maped to hole, it already contains zeroes
|
|
|
|
*/
|
|
|
|
set_buffer_uptodate(bh);
|
|
|
|
mark_buffer_dirty(bh);
|
|
|
|
set_page_dirty(lastpage);
|
2006-07-01 07:36:24 -04:00
|
|
|
}
|
[PATCH] ufs: truncate correction
1) When we allocated last fragment in ufs_truncate, we read page, check
if block mapped to address, and if not trying to allocate it. This is
wrong behaviour, fragment may be NOT allocated, but mapped, this
happened because of "block map" function not checked allocated fragment
or not, it just take address of the first fragment in the block, add
offset of fragment and return result, this is correct behaviour in
almost all situation except call from ufs_truncate.
2) Almost all implementation of UFS, which I can investigate have such
"defect": if you have full disk, and try truncate file, for example 3GB
to 2MB, and have hole in this region, truncate return -ENOSPC. I tried
evade from this problem, but "block allocation" algorithm is tied to
right value of i_lastfrag, and fix of this corner case may slow down of
ordinaries scenarios, so this patch makes behavior of "truncate"
operations similar to what other UFS implementations do.
Signed-off-by: Evgeniy Dushistov <dushistov@mail.ru>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-08-27 04:23:46 -04:00
|
|
|
|
[PATCH] ufs: zeroize the rest of block in truncate
This patch fix behaviour in such test scenario:
lseek(fd, BIG_OFFSET)
write(fd, buf, sizeof(buf))
truncate(BIG_OFFSET)
truncate(BIG_OFFSET + sizeof(buf))
read(fd, buf...)
Because of if file big enough(BIG_OFFSET) we start allocate space by block,
ordinary block size > page size, so we should zeroize the rest of block in
truncate(except last framgnet, about which VFS should care), to not get
garbage, when we extend file.
Also patch corrects conversion from pointer to block to physical block number,
this helps in case of not common used UFS types.
And add to debug output inode number.
Signed-off-by: Evgeniy Dushistov <dushistov@mail.ru>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2007-03-16 17:38:09 -04:00
|
|
|
if (lastfrag >= UFS_IND_FRAGMENT) {
|
|
|
|
end = uspi->s_fpb - ufs_fragnum(lastfrag) - 1;
|
|
|
|
phys64 = bh->b_blocknr + 1;
|
|
|
|
for (i = 0; i < end; ++i) {
|
|
|
|
bh = sb_getblk(sb, i + phys64);
|
|
|
|
lock_buffer(bh);
|
|
|
|
memset(bh->b_data, 0, sb->s_blocksize);
|
|
|
|
set_buffer_uptodate(bh);
|
|
|
|
mark_buffer_dirty(bh);
|
|
|
|
unlock_buffer(bh);
|
|
|
|
sync_dirty_buffer(bh);
|
|
|
|
brelse(bh);
|
|
|
|
}
|
|
|
|
}
|
2006-07-01 07:36:24 -04:00
|
|
|
out_unlock:
|
|
|
|
ufs_put_locked_page(lastpage);
|
|
|
|
out:
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
int ufs_truncate(struct inode *inode, loff_t old_i_size)
|
|
|
|
{
|
|
|
|
struct ufs_inode_info *ufsi = UFS_I(inode);
|
|
|
|
struct super_block *sb = inode->i_sb;
|
|
|
|
struct ufs_sb_private_info *uspi = UFS_SB(sb)->s_uspi;
|
|
|
|
int retry, err = 0;
|
2005-04-16 18:20:36 -04:00
|
|
|
|
2007-02-12 03:54:32 -05:00
|
|
|
UFSD("ENTER: ino %lu, i_size: %llu, old_i_size: %llu\n",
|
|
|
|
inode->i_ino, (unsigned long long)i_size_read(inode),
|
|
|
|
(unsigned long long)old_i_size);
|
2005-04-16 18:20:36 -04:00
|
|
|
|
2006-07-01 07:36:24 -04:00
|
|
|
if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode) ||
|
|
|
|
S_ISLNK(inode->i_mode)))
|
|
|
|
return -EINVAL;
|
2005-04-16 18:20:36 -04:00
|
|
|
if (IS_APPEND(inode) || IS_IMMUTABLE(inode))
|
2006-07-01 07:36:24 -04:00
|
|
|
return -EPERM;
|
|
|
|
|
[PATCH] ufs: truncate correction
1) When we allocated last fragment in ufs_truncate, we read page, check
if block mapped to address, and if not trying to allocate it. This is
wrong behaviour, fragment may be NOT allocated, but mapped, this
happened because of "block map" function not checked allocated fragment
or not, it just take address of the first fragment in the block, add
offset of fragment and return result, this is correct behaviour in
almost all situation except call from ufs_truncate.
2) Almost all implementation of UFS, which I can investigate have such
"defect": if you have full disk, and try truncate file, for example 3GB
to 2MB, and have hole in this region, truncate return -ENOSPC. I tried
evade from this problem, but "block allocation" algorithm is tied to
right value of i_lastfrag, and fix of this corner case may slow down of
ordinaries scenarios, so this patch makes behavior of "truncate"
operations similar to what other UFS implementations do.
Signed-off-by: Evgeniy Dushistov <dushistov@mail.ru>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-08-27 04:23:46 -04:00
|
|
|
err = ufs_alloc_lastblock(inode);
|
2006-07-01 07:36:24 -04:00
|
|
|
|
[PATCH] ufs: truncate correction
1) When we allocated last fragment in ufs_truncate, we read page, check
if block mapped to address, and if not trying to allocate it. This is
wrong behaviour, fragment may be NOT allocated, but mapped, this
happened because of "block map" function not checked allocated fragment
or not, it just take address of the first fragment in the block, add
offset of fragment and return result, this is correct behaviour in
almost all situation except call from ufs_truncate.
2) Almost all implementation of UFS, which I can investigate have such
"defect": if you have full disk, and try truncate file, for example 3GB
to 2MB, and have hole in this region, truncate return -ENOSPC. I tried
evade from this problem, but "block allocation" algorithm is tied to
right value of i_lastfrag, and fix of this corner case may slow down of
ordinaries scenarios, so this patch makes behavior of "truncate"
operations similar to what other UFS implementations do.
Signed-off-by: Evgeniy Dushistov <dushistov@mail.ru>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-08-27 04:23:46 -04:00
|
|
|
if (err) {
|
|
|
|
i_size_write(inode, old_i_size);
|
|
|
|
goto out;
|
2006-07-01 07:36:24 -04:00
|
|
|
}
|
2006-02-03 06:04:06 -05:00
|
|
|
|
2006-07-01 07:36:24 -04:00
|
|
|
block_truncate_page(inode->i_mapping, inode->i_size, ufs_getfrag_block);
|
2006-02-03 06:04:06 -05:00
|
|
|
|
2005-04-16 18:20:36 -04:00
|
|
|
lock_kernel();
|
|
|
|
while (1) {
|
|
|
|
retry = ufs_trunc_direct(inode);
|
2007-02-12 03:54:32 -05:00
|
|
|
retry |= ufs_trunc_indirect(inode, UFS_IND_BLOCK,
|
|
|
|
ufs_get_direct_data_ptr(uspi, ufsi,
|
|
|
|
UFS_IND_BLOCK));
|
|
|
|
retry |= ufs_trunc_dindirect(inode, UFS_IND_BLOCK + uspi->s_apb,
|
|
|
|
ufs_get_direct_data_ptr(uspi, ufsi,
|
|
|
|
UFS_DIND_BLOCK));
|
2005-04-16 18:20:36 -04:00
|
|
|
retry |= ufs_trunc_tindirect (inode);
|
|
|
|
if (!retry)
|
|
|
|
break;
|
|
|
|
if (IS_SYNC(inode) && (inode->i_state & I_DIRTY))
|
|
|
|
ufs_sync_inode (inode);
|
|
|
|
blk_run_address_space(inode->i_mapping);
|
|
|
|
yield();
|
|
|
|
}
|
2006-02-03 06:04:06 -05:00
|
|
|
|
2005-04-16 18:20:36 -04:00
|
|
|
inode->i_mtime = inode->i_ctime = CURRENT_TIME_SEC;
|
[PATCH] ufs: truncate correction
1) When we allocated last fragment in ufs_truncate, we read page, check
if block mapped to address, and if not trying to allocate it. This is
wrong behaviour, fragment may be NOT allocated, but mapped, this
happened because of "block map" function not checked allocated fragment
or not, it just take address of the first fragment in the block, add
offset of fragment and return result, this is correct behaviour in
almost all situation except call from ufs_truncate.
2) Almost all implementation of UFS, which I can investigate have such
"defect": if you have full disk, and try truncate file, for example 3GB
to 2MB, and have hole in this region, truncate return -ENOSPC. I tried
evade from this problem, but "block allocation" algorithm is tied to
right value of i_lastfrag, and fix of this corner case may slow down of
ordinaries scenarios, so this patch makes behavior of "truncate"
operations similar to what other UFS implementations do.
Signed-off-by: Evgeniy Dushistov <dushistov@mail.ru>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-08-27 04:23:46 -04:00
|
|
|
ufsi->i_lastfrag = DIRECT_FRAGMENT;
|
2005-04-16 18:20:36 -04:00
|
|
|
unlock_kernel();
|
|
|
|
mark_inode_dirty(inode);
|
2006-07-01 07:36:24 -04:00
|
|
|
out:
|
|
|
|
UFSD("EXIT: err %d\n", err);
|
|
|
|
return err;
|
2005-04-16 18:20:36 -04:00
|
|
|
}
|
2006-07-01 07:36:24 -04:00
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* We don't define our `inode->i_op->truncate', and call it here,
|
|
|
|
* because of:
|
|
|
|
* - there is no way to know old size
|
|
|
|
* - there is no way inform user about error, if it happens in `truncate'
|
|
|
|
*/
|
|
|
|
static int ufs_setattr(struct dentry *dentry, struct iattr *attr)
|
|
|
|
{
|
|
|
|
struct inode *inode = dentry->d_inode;
|
|
|
|
unsigned int ia_valid = attr->ia_valid;
|
|
|
|
int error;
|
|
|
|
|
|
|
|
error = inode_change_ok(inode, attr);
|
|
|
|
if (error)
|
|
|
|
return error;
|
|
|
|
|
|
|
|
if (ia_valid & ATTR_SIZE &&
|
|
|
|
attr->ia_size != i_size_read(inode)) {
|
|
|
|
loff_t old_i_size = inode->i_size;
|
|
|
|
error = vmtruncate(inode, attr->ia_size);
|
|
|
|
if (error)
|
|
|
|
return error;
|
|
|
|
error = ufs_truncate(inode, old_i_size);
|
|
|
|
if (error)
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
return inode_setattr(inode, attr);
|
|
|
|
}
|
|
|
|
|
2007-02-12 03:55:40 -05:00
|
|
|
const struct inode_operations ufs_file_inode_operations = {
|
2006-07-01 07:36:24 -04:00
|
|
|
.setattr = ufs_setattr,
|
|
|
|
};
|