* Idea from Pierre del Perugia <delperug@gla.ecoledoc.ibp.fr>
*/
+/*
+ * Modified to avoid infinite loop on 2006 by
+ * Evgeniy Dushistov <dushistov@mail.ru>
+ */
+
#include <linux/errno.h>
#include <linux/fs.h>
#include <linux/ufs_fs.h>
#include "swab.h"
#include "util.h"
-#undef UFS_TRUNCATE_DEBUG
-
-#ifdef UFS_TRUNCATE_DEBUG
-#define UFSD(x) printk("(%s, %d), %s: ", __FILE__, __LINE__, __FUNCTION__); printk x;
-#else
-#define UFSD(x)
-#endif
-
/*
* Secure deletion currently doesn't work. It interacts very badly
* with buffers shared with memory mappings, and for that reason
#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)
-#define DATA_BUFFER_USED(bh) \
- (atomic_read(&bh->b_count)>1 || buffer_locked(bh))
static int ufs_trunc_direct (struct inode * inode)
{
struct ufs_inode_info *ufsi = UFS_I(inode);
struct super_block * sb;
struct ufs_sb_private_info * uspi;
- struct buffer_head * bh;
- u32 * p;
+ __fs32 * p;
unsigned frag1, frag2, frag3, frag4, block1, block2;
unsigned frag_to_free, free_count;
- unsigned i, j, tmp;
+ unsigned i, tmp;
int retry;
- UFSD(("ENTER\n"))
+ UFSD("ENTER\n");
sb = inode->i_sb;
uspi = UFS_SB(sb)->s_uspi;
block2 = ufs_fragstoblks (frag3);
}
- UFSD(("frag1 %u, frag2 %u, block1 %u, block2 %u, frag3 %u, frag4 %u\n", frag1, frag2, block1, block2, frag3, frag4))
+ UFSD("frag1 %u, frag2 %u, block1 %u, block2 %u, frag3 %u, frag4 %u\n", frag1, frag2, block1, block2, frag3, frag4);
if (frag1 >= frag2)
goto next1;
tmp = fs32_to_cpu(sb, *p);
if (!tmp )
ufs_panic (sb, "ufs_trunc_direct", "internal error");
+ frag2 -= frag1;
frag1 = ufs_fragnum (frag1);
- frag2 = ufs_fragnum (frag2);
- for (j = frag1; j < frag2; j++) {
- bh = sb_find_get_block (sb, tmp + j);
- if ((bh && DATA_BUFFER_USED(bh)) || tmp != fs32_to_cpu(sb, *p)) {
- retry = 1;
- brelse (bh);
- goto next1;
- }
- bforget (bh);
- }
- inode->i_blocks -= (frag2-frag1) << uspi->s_nspfshift;
+
+ ufs_free_fragments(inode, tmp + frag1, frag2);
mark_inode_dirty(inode);
- ufs_free_fragments (inode, tmp + frag1, frag2 - frag1);
frag_to_free = tmp + frag1;
next1:
tmp = fs32_to_cpu(sb, *p);
if (!tmp)
continue;
- for (j = 0; j < uspi->s_fpb; j++) {
- bh = sb_find_get_block(sb, tmp + j);
- if ((bh && DATA_BUFFER_USED(bh)) || tmp != fs32_to_cpu(sb, *p)) {
- retry = 1;
- brelse (bh);
- goto next2;
- }
- bforget (bh);
- }
+
*p = 0;
- inode->i_blocks -= uspi->s_nspb;
- mark_inode_dirty(inode);
+
if (free_count == 0) {
frag_to_free = tmp;
free_count = uspi->s_fpb;
frag_to_free = tmp;
free_count = uspi->s_fpb;
}
-next2:;
+ mark_inode_dirty(inode);
}
if (free_count > 0)
if (!tmp )
ufs_panic(sb, "ufs_truncate_direct", "internal error");
frag4 = ufs_fragnum (frag4);
- for (j = 0; j < frag4; j++) {
- bh = sb_find_get_block (sb, tmp + j);
- if ((bh && DATA_BUFFER_USED(bh)) || tmp != fs32_to_cpu(sb, *p)) {
- retry = 1;
- brelse (bh);
- goto next1;
- }
- bforget (bh);
- }
+
*p = 0;
- inode->i_blocks -= frag4 << uspi->s_nspfshift;
- mark_inode_dirty(inode);
+
ufs_free_fragments (inode, tmp, frag4);
+ mark_inode_dirty(inode);
next3:
- UFSD(("EXIT\n"))
+ UFSD("EXIT\n");
return retry;
}
-static int ufs_trunc_indirect (struct inode * inode, unsigned offset, u32 * p)
+static int ufs_trunc_indirect (struct inode * inode, unsigned offset, __fs32 *p)
{
struct super_block * sb;
struct ufs_sb_private_info * uspi;
struct ufs_buffer_head * ind_ubh;
- struct buffer_head * bh;
- u32 * ind;
- unsigned indirect_block, i, j, tmp;
+ __fs32 * ind;
+ unsigned indirect_block, i, tmp;
unsigned frag_to_free, free_count;
int retry;
- UFSD(("ENTER\n"))
+ UFSD("ENTER\n");
sb = inode->i_sb;
uspi = UFS_SB(sb)->s_uspi;
tmp = fs32_to_cpu(sb, *ind);
if (!tmp)
continue;
- for (j = 0; j < uspi->s_fpb; j++) {
- bh = sb_find_get_block(sb, tmp + j);
- if ((bh && DATA_BUFFER_USED(bh)) || tmp != fs32_to_cpu(sb, *ind)) {
- retry = 1;
- brelse (bh);
- goto next;
- }
- bforget (bh);
- }
+
*ind = 0;
ubh_mark_buffer_dirty(ind_ubh);
if (free_count == 0) {
frag_to_free = tmp;
free_count = uspi->s_fpb;
}
- inode->i_blocks -= uspi->s_nspb;
+
mark_inode_dirty(inode);
-next:;
}
if (free_count > 0) {
if (*ubh_get_addr32(ind_ubh,i))
break;
if (i >= uspi->s_apb) {
- if (ubh_max_bcount(ind_ubh) != 1) {
- retry = 1;
- }
- else {
- tmp = fs32_to_cpu(sb, *p);
- *p = 0;
- inode->i_blocks -= uspi->s_nspb;
- mark_inode_dirty(inode);
- ufs_free_blocks (inode, tmp, uspi->s_fpb);
- ubh_bforget(ind_ubh);
- ind_ubh = NULL;
- }
+ tmp = fs32_to_cpu(sb, *p);
+ *p = 0;
+
+ ufs_free_blocks (inode, tmp, uspi->s_fpb);
+ mark_inode_dirty(inode);
+ ubh_bforget(ind_ubh);
+ ind_ubh = NULL;
}
if (IS_SYNC(inode) && ind_ubh && ubh_buffer_dirty(ind_ubh)) {
- ubh_wait_on_buffer (ind_ubh);
- ubh_ll_rw_block (WRITE, 1, &ind_ubh);
+ ubh_ll_rw_block(SWRITE, ind_ubh);
ubh_wait_on_buffer (ind_ubh);
}
ubh_brelse (ind_ubh);
- UFSD(("EXIT\n"))
+ UFSD("EXIT\n");
return retry;
}
-static int ufs_trunc_dindirect (struct inode * inode, unsigned offset, u32 * p)
+static int ufs_trunc_dindirect (struct inode *inode, unsigned offset, __fs32 *p)
{
struct super_block * sb;
struct ufs_sb_private_info * uspi;
struct ufs_buffer_head * dind_bh;
unsigned i, tmp, dindirect_block;
- u32 * dind;
+ __fs32 * dind;
int retry = 0;
- UFSD(("ENTER\n"))
+ UFSD("ENTER\n");
sb = inode->i_sb;
uspi = UFS_SB(sb)->s_uspi;
if (*ubh_get_addr32 (dind_bh, i))
break;
if (i >= uspi->s_apb) {
- if (ubh_max_bcount(dind_bh) != 1)
- retry = 1;
- else {
- tmp = fs32_to_cpu(sb, *p);
- *p = 0;
- inode->i_blocks -= uspi->s_nspb;
- mark_inode_dirty(inode);
- ufs_free_blocks (inode, tmp, uspi->s_fpb);
- ubh_bforget(dind_bh);
- dind_bh = NULL;
- }
+ tmp = fs32_to_cpu(sb, *p);
+ *p = 0;
+
+ ufs_free_blocks(inode, tmp, uspi->s_fpb);
+ mark_inode_dirty(inode);
+ ubh_bforget(dind_bh);
+ dind_bh = NULL;
}
if (IS_SYNC(inode) && dind_bh && ubh_buffer_dirty(dind_bh)) {
- ubh_wait_on_buffer (dind_bh);
- ubh_ll_rw_block (WRITE, 1, &dind_bh);
+ ubh_ll_rw_block(SWRITE, dind_bh);
ubh_wait_on_buffer (dind_bh);
}
ubh_brelse (dind_bh);
- UFSD(("EXIT\n"))
+ UFSD("EXIT\n");
return retry;
}
struct ufs_sb_private_info * uspi;
struct ufs_buffer_head * tind_bh;
unsigned tindirect_block, tmp, i;
- u32 * tind, * p;
+ __fs32 * tind, * p;
int retry;
- UFSD(("ENTER\n"))
+ UFSD("ENTER\n");
sb = inode->i_sb;
uspi = UFS_SB(sb)->s_uspi;
if (*ubh_get_addr32 (tind_bh, i))
break;
if (i >= uspi->s_apb) {
- if (ubh_max_bcount(tind_bh) != 1)
- retry = 1;
- else {
- tmp = fs32_to_cpu(sb, *p);
- *p = 0;
- inode->i_blocks -= uspi->s_nspb;
- mark_inode_dirty(inode);
- ufs_free_blocks (inode, tmp, uspi->s_fpb);
- ubh_bforget(tind_bh);
- tind_bh = NULL;
- }
+ tmp = fs32_to_cpu(sb, *p);
+ *p = 0;
+
+ ufs_free_blocks(inode, tmp, uspi->s_fpb);
+ mark_inode_dirty(inode);
+ ubh_bforget(tind_bh);
+ tind_bh = NULL;
}
if (IS_SYNC(inode) && tind_bh && ubh_buffer_dirty(tind_bh)) {
- ubh_wait_on_buffer (tind_bh);
- ubh_ll_rw_block (WRITE, 1, &tind_bh);
+ ubh_ll_rw_block(SWRITE, tind_bh);
ubh_wait_on_buffer (tind_bh);
}
ubh_brelse (tind_bh);
- UFSD(("EXIT\n"))
+ UFSD("EXIT\n");
return retry;
}
-
-void ufs_truncate (struct inode * inode)
+
+static int ufs_alloc_lastblock(struct inode *inode)
+{
+ int err = 0;
+ struct address_space *mapping = inode->i_mapping;
+ struct ufs_sb_private_info *uspi = UFS_SB(inode->i_sb)->s_uspi;
+ unsigned lastfrag, i, end;
+ struct page *lastpage;
+ struct buffer_head *bh;
+
+ lastfrag = (i_size_read(inode) + uspi->s_fsize - 1) >> uspi->s_fshift;
+
+ if (!lastfrag)
+ goto out;
+
+ 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;
+
+
+ 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);
+ }
+
+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;
- struct ufs_sb_private_info * uspi;
- struct buffer_head * bh;
- unsigned offset;
- int err, retry;
+ struct super_block *sb = inode->i_sb;
+ struct ufs_sb_private_info *uspi = UFS_SB(sb)->s_uspi;
+ int retry, err = 0;
- UFSD(("ENTER\n"))
- sb = inode->i_sb;
- uspi = UFS_SB(sb)->s_uspi;
+ UFSD("ENTER\n");
- if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode) || S_ISLNK(inode->i_mode)))
- return;
+ if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode) ||
+ S_ISLNK(inode->i_mode)))
+ return -EINVAL;
if (IS_APPEND(inode) || IS_IMMUTABLE(inode))
- return;
+ return -EPERM;
+
+ err = ufs_alloc_lastblock(inode);
+
+ if (err) {
+ i_size_write(inode, old_i_size);
+ goto out;
+ }
+
+ block_truncate_page(inode->i_mapping, inode->i_size, ufs_getfrag_block);
+
lock_kernel();
while (1) {
retry = ufs_trunc_direct(inode);
retry |= ufs_trunc_indirect (inode, UFS_IND_BLOCK,
- (u32 *) &ufsi->i_u1.i_data[UFS_IND_BLOCK]);
+ (__fs32 *) &ufsi->i_u1.i_data[UFS_IND_BLOCK]);
retry |= ufs_trunc_dindirect (inode, UFS_IND_BLOCK + uspi->s_apb,
- (u32 *) &ufsi->i_u1.i_data[UFS_DIND_BLOCK]);
+ (__fs32 *) &ufsi->i_u1.i_data[UFS_DIND_BLOCK]);
retry |= ufs_trunc_tindirect (inode);
if (!retry)
break;
blk_run_address_space(inode->i_mapping);
yield();
}
- offset = inode->i_size & uspi->s_fshift;
- if (offset) {
- bh = ufs_bread (inode, inode->i_size >> uspi->s_fshift, 0, &err);
- if (bh) {
- memset (bh->b_data + offset, 0, uspi->s_fsize - offset);
- mark_buffer_dirty (bh);
- brelse (bh);
- }
- }
- inode->i_mtime = inode->i_ctime = CURRENT_TIME;
+
+ inode->i_mtime = inode->i_ctime = CURRENT_TIME_SEC;
ufsi->i_lastfrag = DIRECT_FRAGMENT;
unlock_kernel();
mark_inode_dirty(inode);
- UFSD(("EXIT\n"))
+out:
+ UFSD("EXIT: err %d\n", err);
+ return err;
}
+
+
+/*
+ * 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);
+}
+
+struct inode_operations ufs_file_inode_operations = {
+ .setattr = ufs_setattr,
+};