diff options
author | Aneesh Kumar K.V <aneesh.kumar@linux.vnet.ibm.com> | 2009-06-05 00:56:49 -0400 |
---|---|---|
committer | Theodore Ts'o <tytso@mit.edu> | 2009-06-05 00:56:49 -0400 |
commit | f8514083cd61daef12fba5ef883ad9352c450428 (patch) | |
tree | 46cbf0c28ea112229c3ee9209750c8017f1b9385 | |
parent | 1938a150c25bf7c2c47182e753a1038945b70b0e (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>
-rw-r--r-- | fs/ext4/inode.c | 128 |
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 | ||
1552 | static 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 | } |