aboutsummaryrefslogtreecommitdiffstats
path: root/fs/ext4/inode.c
diff options
context:
space:
mode:
authorAneesh Kumar K.V <aneesh.kumar@linux.vnet.ibm.com>2009-06-05 00:56:49 -0400
committerTheodore Ts'o <tytso@mit.edu>2009-06-05 00:56:49 -0400
commitf8514083cd61daef12fba5ef883ad9352c450428 (patch)
tree46cbf0c28ea112229c3ee9209750c8017f1b9385 /fs/ext4/inode.c
parent1938a150c25bf7c2c47182e753a1038945b70b0e (diff)
ext4: truncate the file properly if we fail to copy data from userspace
In generic_perform_write if we fail to copy the user data we don't update the inode->i_size. We should truncate the file in the above case so that we don't have blocks allocated outside inode->i_size. Add the inode to orphan list in the same transaction as block allocation This ensures that if we crash in between the recovery would do the truncate. Signed-off-by: Aneesh Kumar K.V <aneesh.kumar@linux.vnet.ibm.com> CC: Jan Kara <jack@suse.cz> Signed-off-by: "Theodore Ts'o" <tytso@mit.edu>
Diffstat (limited to 'fs/ext4/inode.c')
-rw-r--r--fs/ext4/inode.c128
1 files changed, 102 insertions, 26 deletions
diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c
index 8d215881172f..2c10d346f7a3 100644
--- a/fs/ext4/inode.c
+++ b/fs/ext4/inode.c
@@ -1549,6 +1549,52 @@ static int write_end_fn(handle_t *handle, struct buffer_head *bh)
1549 return ext4_handle_dirty_metadata(handle, NULL, bh); 1549 return ext4_handle_dirty_metadata(handle, NULL, bh);
1550} 1550}
1551 1551
1552static int ext4_generic_write_end(struct file *file,
1553 struct address_space *mapping,
1554 loff_t pos, unsigned len, unsigned copied,
1555 struct page *page, void *fsdata)
1556{
1557 int i_size_changed = 0;
1558 struct inode *inode = mapping->host;
1559 handle_t *handle = ext4_journal_current_handle();
1560
1561 copied = block_write_end(file, mapping, pos, len, copied, page, fsdata);
1562
1563 /*
1564 * No need to use i_size_read() here, the i_size
1565 * cannot change under us because we hold i_mutex.
1566 *
1567 * But it's important to update i_size while still holding page lock:
1568 * page writeout could otherwise come in and zero beyond i_size.
1569 */
1570 if (pos + copied > inode->i_size) {
1571 i_size_write(inode, pos + copied);
1572 i_size_changed = 1;
1573 }
1574
1575 if (pos + copied > EXT4_I(inode)->i_disksize) {
1576 /* We need to mark inode dirty even if
1577 * new_i_size is less that inode->i_size
1578 * bu greater than i_disksize.(hint delalloc)
1579 */
1580 ext4_update_i_disksize(inode, (pos + copied));
1581 i_size_changed = 1;
1582 }
1583 unlock_page(page);
1584 page_cache_release(page);
1585
1586 /*
1587 * Don't mark the inode dirty under page lock. First, it unnecessarily
1588 * makes the holding time of page lock longer. Second, it forces lock
1589 * ordering of page lock and transaction start for journaling
1590 * filesystems.
1591 */
1592 if (i_size_changed)
1593 ext4_mark_inode_dirty(handle, inode);
1594
1595 return copied;
1596}
1597
1552/* 1598/*
1553 * We need to pick up the new inode size which generic_commit_write gave us 1599 * We need to pick up the new inode size which generic_commit_write gave us
1554 * `file' can be NULL - eg, when called from page_symlink(). 1600 * `file' can be NULL - eg, when called from page_symlink().
@@ -1572,21 +1618,15 @@ static int ext4_ordered_write_end(struct file *file,
1572 ret = ext4_jbd2_file_inode(handle, inode); 1618 ret = ext4_jbd2_file_inode(handle, inode);
1573 1619
1574 if (ret == 0) { 1620 if (ret == 0) {
1575 loff_t new_i_size; 1621 ret2 = ext4_generic_write_end(file, mapping, pos, len, copied,
1576
1577 new_i_size = pos + copied;
1578 if (new_i_size > EXT4_I(inode)->i_disksize) {
1579 ext4_update_i_disksize(inode, new_i_size);
1580 /* We need to mark inode dirty even if
1581 * new_i_size is less that inode->i_size
1582 * bu greater than i_disksize.(hint delalloc)
1583 */
1584 ext4_mark_inode_dirty(handle, inode);
1585 }
1586
1587 ret2 = generic_write_end(file, mapping, pos, len, copied,
1588 page, fsdata); 1622 page, fsdata);
1589 copied = ret2; 1623 copied = ret2;
1624 if (pos + len > inode->i_size)
1625 /* if we have allocated more blocks and copied
1626 * less. We will have blocks allocated outside
1627 * inode->i_size. So truncate them
1628 */
1629 ext4_orphan_add(handle, inode);
1590 if (ret2 < 0) 1630 if (ret2 < 0)
1591 ret = ret2; 1631 ret = ret2;
1592 } 1632 }
@@ -1594,6 +1634,18 @@ static int ext4_ordered_write_end(struct file *file,
1594 if (!ret) 1634 if (!ret)
1595 ret = ret2; 1635 ret = ret2;
1596 1636
1637 if (pos + len > inode->i_size) {
1638 vmtruncate(inode, inode->i_size);
1639 /*
1640 * If vmtruncate failed early the inode might still be
1641 * on the orphan list; we need to make sure the inode
1642 * is removed from the orphan list in that case.
1643 */
1644 if (inode->i_nlink)
1645 ext4_orphan_del(NULL, inode);
1646 }
1647
1648
1597 return ret ? ret : copied; 1649 return ret ? ret : copied;
1598} 1650}
1599 1651
@@ -1605,25 +1657,21 @@ static int ext4_writeback_write_end(struct file *file,
1605 handle_t *handle = ext4_journal_current_handle(); 1657 handle_t *handle = ext4_journal_current_handle();
1606 struct inode *inode = mapping->host; 1658 struct inode *inode = mapping->host;
1607 int ret = 0, ret2; 1659 int ret = 0, ret2;
1608 loff_t new_i_size;
1609 1660
1610 trace_mark(ext4_writeback_write_end, 1661 trace_mark(ext4_writeback_write_end,
1611 "dev %s ino %lu pos %llu len %u copied %u", 1662 "dev %s ino %lu pos %llu len %u copied %u",
1612 inode->i_sb->s_id, inode->i_ino, 1663 inode->i_sb->s_id, inode->i_ino,
1613 (unsigned long long) pos, len, copied); 1664 (unsigned long long) pos, len, copied);
1614 new_i_size = pos + copied; 1665 ret2 = ext4_generic_write_end(file, mapping, pos, len, copied,
1615 if (new_i_size > EXT4_I(inode)->i_disksize) {
1616 ext4_update_i_disksize(inode, new_i_size);
1617 /* We need to mark inode dirty even if
1618 * new_i_size is less that inode->i_size
1619 * bu greater than i_disksize.(hint delalloc)
1620 */
1621 ext4_mark_inode_dirty(handle, inode);
1622 }
1623
1624 ret2 = generic_write_end(file, mapping, pos, len, copied,
1625 page, fsdata); 1666 page, fsdata);
1626 copied = ret2; 1667 copied = ret2;
1668 if (pos + len > inode->i_size)
1669 /* if we have allocated more blocks and copied
1670 * less. We will have blocks allocated outside
1671 * inode->i_size. So truncate them
1672 */
1673 ext4_orphan_add(handle, inode);
1674
1627 if (ret2 < 0) 1675 if (ret2 < 0)
1628 ret = ret2; 1676 ret = ret2;
1629 1677
@@ -1631,6 +1679,17 @@ static int ext4_writeback_write_end(struct file *file,
1631 if (!ret) 1679 if (!ret)
1632 ret = ret2; 1680 ret = ret2;
1633 1681
1682 if (pos + len > inode->i_size) {
1683 vmtruncate(inode, inode->i_size);
1684 /*
1685 * If vmtruncate failed early the inode might still be
1686 * on the orphan list; we need to make sure the inode
1687 * is removed from the orphan list in that case.
1688 */
1689 if (inode->i_nlink)
1690 ext4_orphan_del(NULL, inode);
1691 }
1692
1634 return ret ? ret : copied; 1693 return ret ? ret : copied;
1635} 1694}
1636 1695
@@ -1675,10 +1734,27 @@ static int ext4_journalled_write_end(struct file *file,
1675 } 1734 }
1676 1735
1677 unlock_page(page); 1736 unlock_page(page);
1737 page_cache_release(page);
1738 if (pos + len > inode->i_size)
1739 /* if we have allocated more blocks and copied
1740 * less. We will have blocks allocated outside
1741 * inode->i_size. So truncate them
1742 */
1743 ext4_orphan_add(handle, inode);
1744
1678 ret2 = ext4_journal_stop(handle); 1745 ret2 = ext4_journal_stop(handle);
1679 if (!ret) 1746 if (!ret)
1680 ret = ret2; 1747 ret = ret2;
1681 page_cache_release(page); 1748 if (pos + len > inode->i_size) {
1749 vmtruncate(inode, inode->i_size);
1750 /*
1751 * If vmtruncate failed early the inode might still be
1752 * on the orphan list; we need to make sure the inode
1753 * is removed from the orphan list in that case.
1754 */
1755 if (inode->i_nlink)
1756 ext4_orphan_del(NULL, inode);
1757 }
1682 1758
1683 return ret ? ret : copied; 1759 return ret ? ret : copied;
1684} 1760}