X-Git-Url: https://git.openpandora.org/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=fs%2Fufs%2Ftruncate.c;h=ea11d04c41a07f07d6ed2151561b5dfc2b22cdd8;hb=aa43f77939c97bf9d3580c6a5e71a5a40290e451;hp=61d2e35012a462e2cb6be0f9fc27bd10e20db0b1;hpb=2fc2991175bf77395e6b15fe6b2304d3bf72da40;p=pandora-kernel.git diff --git a/fs/ufs/truncate.c b/fs/ufs/truncate.c index 61d2e35012a4..ea11d04c41a0 100644 --- a/fs/ufs/truncate.c +++ b/fs/ufs/truncate.c @@ -29,6 +29,11 @@ * Idea from Pierre del Perugia */ +/* + * Modified to avoid infinite loop on 2006 by + * Evgeniy Dushistov + */ + #include #include #include @@ -44,14 +49,6 @@ #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 @@ -65,22 +62,19 @@ #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; __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; @@ -103,7 +97,7 @@ static int ufs_trunc_direct (struct inode * inode) 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; @@ -117,18 +111,9 @@ static int ufs_trunc_direct (struct inode * inode) ufs_panic (sb, "ufs_trunc_direct", "internal error"); 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; - mark_inode_dirty(inode); + ufs_free_fragments (inode, tmp + frag1, frag2 - frag1); + mark_inode_dirty(inode); frag_to_free = tmp + frag1; next1: @@ -140,18 +125,9 @@ 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; @@ -162,7 +138,7 @@ next1: frag_to_free = tmp; free_count = uspi->s_fpb; } -next2:; + mark_inode_dirty(inode); } if (free_count > 0) @@ -179,22 +155,14 @@ next2:; 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; } @@ -204,13 +172,12 @@ 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; __fs32 * ind; - unsigned indirect_block, i, j, tmp; + 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; @@ -238,15 +205,7 @@ static int ufs_trunc_indirect (struct inode * inode, unsigned offset, __fs32 *p) 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) { @@ -259,9 +218,8 @@ static int ufs_trunc_indirect (struct inode * inode, unsigned offset, __fs32 *p) frag_to_free = tmp; free_count = uspi->s_fpb; } - inode->i_blocks -= uspi->s_nspb; + mark_inode_dirty(inode); -next:; } if (free_count > 0) { @@ -271,26 +229,21 @@ next:; 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_ll_rw_block (SWRITE, 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; } @@ -304,7 +257,7 @@ static int ufs_trunc_dindirect (struct inode *inode, unsigned offset, __fs32 *p) __fs32 * dind; int retry = 0; - UFSD(("ENTER\n")) + UFSD("ENTER\n"); sb = inode->i_sb; uspi = UFS_SB(sb)->s_uspi; @@ -339,25 +292,21 @@ static int ufs_trunc_dindirect (struct inode *inode, unsigned offset, __fs32 *p) 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_ll_rw_block (SWRITE, 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; } @@ -372,7 +321,7 @@ static int ufs_trunc_tindirect (struct inode * inode) __fs32 * tind, * p; int retry; - UFSD(("ENTER\n")) + UFSD("ENTER\n"); sb = inode->i_sb; uspi = UFS_SB(sb)->s_uspi; @@ -403,45 +352,101 @@ static int ufs_trunc_tindirect (struct inode * inode) 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_ll_rw_block (SWRITE, 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); @@ -457,18 +462,46 @@ void ufs_truncate (struct inode * inode) 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_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, +};