Commit aef1c8513c1f8ae076e22ea2a57eff5835578e75
Committed by
Theodore Ts'o
1 parent
0d812f77b3
Exists in
master
and in
20 other branches
ext4: let ext4_truncate handle inline data correctly
Signed-off-by: Robin Dong <sanbai@taobao.com> Signed-off-by: Tao Ma <boyu.mt@taobao.com> Signed-off-by: "Theodore Ts'o" <tytso@mit.edu>
Showing 3 changed files with 107 additions and 0 deletions Side-by-side Diff
fs/ext4/inline.c
... | ... | @@ -1753,4 +1753,94 @@ |
1753 | 1753 | brelse(iloc.bh); |
1754 | 1754 | return error; |
1755 | 1755 | } |
1756 | + | |
1757 | +void ext4_inline_data_truncate(struct inode *inode, int *has_inline) | |
1758 | +{ | |
1759 | + handle_t *handle; | |
1760 | + int inline_size, value_len, needed_blocks; | |
1761 | + size_t i_size; | |
1762 | + void *value = NULL; | |
1763 | + struct ext4_xattr_ibody_find is = { | |
1764 | + .s = { .not_found = -ENODATA, }, | |
1765 | + }; | |
1766 | + struct ext4_xattr_info i = { | |
1767 | + .name_index = EXT4_XATTR_INDEX_SYSTEM, | |
1768 | + .name = EXT4_XATTR_SYSTEM_DATA, | |
1769 | + }; | |
1770 | + | |
1771 | + | |
1772 | + needed_blocks = ext4_writepage_trans_blocks(inode); | |
1773 | + handle = ext4_journal_start(inode, needed_blocks); | |
1774 | + if (IS_ERR(handle)) | |
1775 | + return; | |
1776 | + | |
1777 | + down_write(&EXT4_I(inode)->xattr_sem); | |
1778 | + if (!ext4_has_inline_data(inode)) { | |
1779 | + *has_inline = 0; | |
1780 | + ext4_journal_stop(handle); | |
1781 | + return; | |
1782 | + } | |
1783 | + | |
1784 | + if (ext4_orphan_add(handle, inode)) | |
1785 | + goto out; | |
1786 | + | |
1787 | + if (ext4_get_inode_loc(inode, &is.iloc)) | |
1788 | + goto out; | |
1789 | + | |
1790 | + down_write(&EXT4_I(inode)->i_data_sem); | |
1791 | + i_size = inode->i_size; | |
1792 | + inline_size = ext4_get_inline_size(inode); | |
1793 | + EXT4_I(inode)->i_disksize = i_size; | |
1794 | + | |
1795 | + if (i_size < inline_size) { | |
1796 | + /* Clear the content in the xattr space. */ | |
1797 | + if (inline_size > EXT4_MIN_INLINE_DATA_SIZE) { | |
1798 | + if (ext4_xattr_ibody_find(inode, &i, &is)) | |
1799 | + goto out_error; | |
1800 | + | |
1801 | + BUG_ON(is.s.not_found); | |
1802 | + | |
1803 | + value_len = le32_to_cpu(is.s.here->e_value_size); | |
1804 | + value = kmalloc(value_len, GFP_NOFS); | |
1805 | + if (!value) | |
1806 | + goto out_error; | |
1807 | + | |
1808 | + if (ext4_xattr_ibody_get(inode, i.name_index, i.name, | |
1809 | + value, value_len)) | |
1810 | + goto out_error; | |
1811 | + | |
1812 | + i.value = value; | |
1813 | + i.value_len = i_size > EXT4_MIN_INLINE_DATA_SIZE ? | |
1814 | + i_size - EXT4_MIN_INLINE_DATA_SIZE : 0; | |
1815 | + if (ext4_xattr_ibody_inline_set(handle, inode, &i, &is)) | |
1816 | + goto out_error; | |
1817 | + } | |
1818 | + | |
1819 | + /* Clear the content within i_blocks. */ | |
1820 | + if (i_size < EXT4_MIN_INLINE_DATA_SIZE) | |
1821 | + memset(ext4_raw_inode(&is.iloc)->i_block + i_size, 0, | |
1822 | + EXT4_MIN_INLINE_DATA_SIZE - i_size); | |
1823 | + | |
1824 | + EXT4_I(inode)->i_inline_size = i_size < | |
1825 | + EXT4_MIN_INLINE_DATA_SIZE ? | |
1826 | + EXT4_MIN_INLINE_DATA_SIZE : i_size; | |
1827 | + } | |
1828 | + | |
1829 | +out_error: | |
1830 | + up_write(&EXT4_I(inode)->i_data_sem); | |
1831 | +out: | |
1832 | + brelse(is.iloc.bh); | |
1833 | + up_write(&EXT4_I(inode)->xattr_sem); | |
1834 | + kfree(value); | |
1835 | + if (inode->i_nlink) | |
1836 | + ext4_orphan_del(handle, inode); | |
1837 | + | |
1838 | + inode->i_mtime = inode->i_ctime = ext4_current_time(inode); | |
1839 | + ext4_mark_inode_dirty(handle, inode); | |
1840 | + if (IS_SYNC(inode)) | |
1841 | + ext4_handle_sync(handle); | |
1842 | + | |
1843 | + ext4_journal_stop(handle); | |
1844 | + return; | |
1845 | +} |
fs/ext4/inode.c
... | ... | @@ -3594,6 +3594,14 @@ |
3594 | 3594 | if (inode->i_size == 0 && !test_opt(inode->i_sb, NO_AUTO_DA_ALLOC)) |
3595 | 3595 | ext4_set_inode_state(inode, EXT4_STATE_DA_ALLOC_CLOSE); |
3596 | 3596 | |
3597 | + if (ext4_has_inline_data(inode)) { | |
3598 | + int has_inline = 1; | |
3599 | + | |
3600 | + ext4_inline_data_truncate(inode, &has_inline); | |
3601 | + if (has_inline) | |
3602 | + return; | |
3603 | + } | |
3604 | + | |
3597 | 3605 | if (ext4_test_inode_flag(inode, EXT4_INODE_EXTENTS)) |
3598 | 3606 | ext4_ext_truncate(inode); |
3599 | 3607 | else |
fs/ext4/xattr.h
... | ... | @@ -190,6 +190,8 @@ |
190 | 190 | extern int ext4_try_to_evict_inline_data(handle_t *handle, |
191 | 191 | struct inode *inode, |
192 | 192 | int needed); |
193 | +extern void ext4_inline_data_truncate(struct inode *inode, int *has_inline); | |
194 | + | |
193 | 195 | # else /* CONFIG_EXT4_FS_XATTR */ |
194 | 196 | |
195 | 197 | static inline int |
... | ... | @@ -411,6 +413,13 @@ |
411 | 413 | { |
412 | 414 | return 0; |
413 | 415 | } |
416 | + | |
417 | +static inline void ext4_inline_data_truncate(struct inode *inode, | |
418 | + int *has_inline) | |
419 | +{ | |
420 | + return; | |
421 | +} | |
422 | + | |
414 | 423 | # endif /* CONFIG_EXT4_FS_XATTR */ |
415 | 424 | |
416 | 425 | #ifdef CONFIG_EXT4_FS_SECURITY |