Merge tag 'ext4_for_linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tytso/ext4
Pull ext4 updates from Ted Ts'o: "For this cycle we add support for the shutdown ioctl, which is primarily used for testing, but which can be useful on production systems when a scratch volume is being destroyed and the data on it doesn't need to be saved. This found (and we fixed) a number of bugs with ext4's recovery to corrupted file system --- the bugs increased the amount of data that could be potentially lost, and in the case of the inline data feature, could cause the kernel to BUG. Also included are a number of other bug fixes, including in ext4's fscrypt, DAX, inline data support" * tag 'ext4_for_linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tytso/ext4: (26 commits) ext4: rename EXT4_IOC_GOINGDOWN to EXT4_IOC_SHUTDOWN ext4: fix fencepost in s_first_meta_bg validation ext4: don't BUG when truncating encrypted inodes on the orphan list ext4: do not use stripe_width if it is not set ext4: fix stripe-unaligned allocations dax: assert that i_rwsem is held exclusive for writes ext4: fix DAX write locking ext4: add EXT4_IOC_GOINGDOWN ioctl ext4: add shutdown bit and check for it ext4: rename s_resize_flags to s_ext4_flags ext4: return EROFS if device is r/o and journal replay is needed ext4: preserve the needs_recovery flag when the journal is aborted jbd2: don't leak modified metadata buffers on an aborted journal ext4: fix inline data error paths ext4: move halfmd4 into hash.c directly ext4: fix use-after-iput when fscrypt contexts are inconsistent jbd2: fix use after free in kjournald2() ext4: fix data corruption in data=journal mode ext4: trim allocation requests to group size ext4: replace BUG_ON with WARN_ON in mb_find_extent() ...
... | ... | @@ -215,6 +215,9 @@ static void ext4_write_inline_data(struct inode *inode, struct ext4_iloc *iloc, |
struct ext4_inode *raw_inode; | ||
int cp_len = 0; | ||
if (unlikely(ext4_forced_shutdown(EXT4_SB(inode->i_sb)))) | ||
return; | ||
BUG_ON(!EXT4_I(inode)->i_inline_off); | ||
BUG_ON(pos + len > EXT4_I(inode)->i_inline_size); | ||
... | ... | @@ -381,7 +384,7 @@ out: |
static int ext4_prepare_inline_data(handle_t *handle, struct inode *inode, | ||
unsigned int len) | ||
{ | ||
int ret, size; | ||
int ret, size, no_expand; | ||
struct ext4_inode_info *ei = EXT4_I(inode); | ||
if (!ext4_test_inode_state(inode, EXT4_STATE_MAY_INLINE_DATA)) | ||
... | ... | @@ -391,15 +394,14 @@ static int ext4_prepare_inline_data(handle_t *handle, struct inode *inode, |
if (size < len) | ||
return -ENOSPC; | ||
down_write(&EXT4_I(inode)->xattr_sem); | ||
ext4_write_lock_xattr(inode, &no_expand); | ||
if (ei->i_inline_off) | ||
ret = ext4_update_inline_data(handle, inode, len); | ||
else | ||
ret = ext4_create_inline_data(handle, inode, len); | ||
up_write(&EXT4_I(inode)->xattr_sem); | ||
ext4_write_unlock_xattr(inode, &no_expand); | ||
return ret; | ||
} | ||
... | ... | @@ -533,7 +535,7 @@ static int ext4_convert_inline_data_to_extent(struct address_space *mapping, |
struct inode *inode, | ||
unsigned flags) | ||
{ | ||
int ret, needed_blocks; | ||
int ret, needed_blocks, no_expand; | ||
handle_t *handle = NULL; | ||
int retries = 0, sem_held = 0; | ||
struct page *page = NULL; | ||
... | ... | @@ -573,7 +575,7 @@ retry: |
goto out; | ||
} | ||
down_write(&EXT4_I(inode)->xattr_sem); | ||
ext4_write_lock_xattr(inode, &no_expand); | ||
sem_held = 1; | ||
/* If some one has already done this for us, just exit. */ | ||
if (!ext4_has_inline_data(inode)) { | ||
... | ... | @@ -610,7 +612,7 @@ retry: |
put_page(page); | ||
page = NULL; | ||
ext4_orphan_add(handle, inode); | ||
up_write(&EXT4_I(inode)->xattr_sem); | ||
ext4_write_unlock_xattr(inode, &no_expand); | ||
sem_held = 0; | ||
ext4_journal_stop(handle); | ||
handle = NULL; | ||
... | ... | @@ -636,7 +638,7 @@ out: |
put_page(page); | ||
} | ||
if (sem_held) | ||
up_write(&EXT4_I(inode)->xattr_sem); | ||
ext4_write_unlock_xattr(inode, &no_expand); | ||
if (handle) | ||
ext4_journal_stop(handle); | ||
brelse(iloc.bh); | ||
... | ... | @@ -729,7 +731,7 @@ convert: |
int ext4_write_inline_data_end(struct inode *inode, loff_t pos, unsigned len, | ||
unsigned copied, struct page *page) | ||
{ | ||
int ret; | ||
int ret, no_expand; | ||
void *kaddr; | ||
struct ext4_iloc iloc; | ||
... | ... | @@ -747,7 +749,7 @@ int ext4_write_inline_data_end(struct inode *inode, loff_t pos, unsigned len, |
goto out; | ||
} | ||
down_write(&EXT4_I(inode)->xattr_sem); | ||
ext4_write_lock_xattr(inode, &no_expand); | ||
BUG_ON(!ext4_has_inline_data(inode)); | ||
kaddr = kmap_atomic(page); | ||
... | ... | @@ -757,7 +759,7 @@ int ext4_write_inline_data_end(struct inode *inode, loff_t pos, unsigned len, |
/* clear page dirty so that writepages wouldn't work for us. */ | ||
ClearPageDirty(page); | ||
up_write(&EXT4_I(inode)->xattr_sem); | ||
ext4_write_unlock_xattr(inode, &no_expand); | ||
brelse(iloc.bh); | ||
out: | ||
return copied; | ||
... | ... | @@ -768,7 +770,7 @@ ext4_journalled_write_inline_data(struct inode *inode, |
unsigned len, | ||
struct page *page) | ||
{ | ||
int ret; | ||
int ret, no_expand; | ||
void *kaddr; | ||
struct ext4_iloc iloc; | ||
... | ... | @@ -778,11 +780,11 @@ ext4_journalled_write_inline_data(struct inode *inode, |
return NULL; | ||
} | ||
down_write(&EXT4_I(inode)->xattr_sem); | ||
ext4_write_lock_xattr(inode, &no_expand); | ||
kaddr = kmap_atomic(page); | ||
ext4_write_inline_data(inode, &iloc, kaddr, 0, len); | ||
kunmap_atomic(kaddr); | ||
up_write(&EXT4_I(inode)->xattr_sem); | ||
ext4_write_unlock_xattr(inode, &no_expand); | ||
return iloc.bh; | ||
} | ||
... | ... | @@ -944,8 +946,15 @@ int ext4_da_write_inline_data_end(struct inode *inode, loff_t pos, |
struct page *page) | ||
{ | ||
int i_size_changed = 0; | ||
int ret; | ||
copied = ext4_write_inline_data_end(inode, pos, len, copied, page); | ||
ret = ext4_write_inline_data_end(inode, pos, len, copied, page); | ||
if (ret < 0) { | ||
unlock_page(page); | ||
put_page(page); | ||
return ret; | ||
} | ||
copied = ret; | ||
/* | ||
* No need to use i_size_read() here, the i_size | ||
... | ... | @@ -1043,7 +1052,6 @@ static int ext4_add_dirent_to_inline(handle_t *handle, |
dir->i_mtime = dir->i_ctime = current_time(dir); | ||
ext4_update_dx_flag(dir); | ||
dir->i_version++; | ||
ext4_mark_inode_dirty(handle, dir); | ||
return 1; | ||
} | ||
... | ... | @@ -1259,7 +1267,7 @@ out: |
int ext4_try_add_inline_entry(handle_t *handle, struct ext4_filename *fname, | ||
struct inode *dir, struct inode *inode) | ||
{ | ||
int ret, inline_size; | ||
int ret, inline_size, no_expand; | ||
void *inline_start; | ||
struct ext4_iloc iloc; | ||
... | ... | @@ -1267,7 +1275,7 @@ int ext4_try_add_inline_entry(handle_t *handle, struct ext4_filename *fname, |
if (ret) | ||
return ret; | ||
down_write(&EXT4_I(dir)->xattr_sem); | ||
ext4_write_lock_xattr(dir, &no_expand); | ||
if (!ext4_has_inline_data(dir)) | ||
goto out; | ||
... | ... | @@ -1312,8 +1320,8 @@ int ext4_try_add_inline_entry(handle_t *handle, struct ext4_filename *fname, |
ret = ext4_convert_inline_data_nolock(handle, dir, &iloc); | ||
out: | ||
ext4_write_unlock_xattr(dir, &no_expand); | ||
ext4_mark_inode_dirty(handle, dir); | ||
up_write(&EXT4_I(dir)->xattr_sem); | ||
brelse(iloc.bh); | ||
return ret; | ||
} | ||
... | ... | @@ -1673,7 +1681,7 @@ int ext4_delete_inline_entry(handle_t *handle, |
struct buffer_head *bh, | ||
int *has_inline_data) | ||
{ | ||
int err, inline_size; | ||
int err, inline_size, no_expand; | ||
struct ext4_iloc iloc; | ||
void *inline_start; | ||
... | ... | @@ -1681,7 +1689,7 @@ int ext4_delete_inline_entry(handle_t *handle, |
if (err) | ||
return err; | ||
down_write(&EXT4_I(dir)->xattr_sem); | ||
ext4_write_lock_xattr(dir, &no_expand); | ||
if (!ext4_has_inline_data(dir)) { | ||
*has_inline_data = 0; | ||
goto out; | ||
... | ... | @@ -1709,13 +1717,11 @@ int ext4_delete_inline_entry(handle_t *handle, |
if (err) | ||
goto out; | ||
err = ext4_mark_inode_dirty(handle, dir); | ||
if (unlikely(err)) | ||
goto out; | ||
ext4_show_inline_dir(dir, iloc.bh, inline_start, inline_size); | ||
out: | ||
up_write(&EXT4_I(dir)->xattr_sem); | ||
ext4_write_unlock_xattr(dir, &no_expand); | ||
if (likely(err == 0)) | ||
err = ext4_mark_inode_dirty(handle, dir); | ||
brelse(iloc.bh); | ||
if (err != -ENOENT) | ||
ext4_std_error(dir->i_sb, err); | ||
... | ... | @@ -1814,11 +1820,11 @@ out: |
int ext4_destroy_inline_data(handle_t *handle, struct inode *inode) | ||
{ | ||
int ret; | ||
int ret, no_expand; | ||
down_write(&EXT4_I(inode)->xattr_sem); | ||
ext4_write_lock_xattr(inode, &no_expand); | ||
ret = ext4_destroy_inline_data_nolock(handle, inode); | ||
up_write(&EXT4_I(inode)->xattr_sem); | ||
ext4_write_unlock_xattr(inode, &no_expand); | ||
return ret; | ||
} | ||
... | ... | @@ -1900,10 +1906,10 @@ out: |
return error; | ||
} | ||
void ext4_inline_data_truncate(struct inode *inode, int *has_inline) | ||
int ext4_inline_data_truncate(struct inode *inode, int *has_inline) | ||
{ | ||
handle_t *handle; | ||
int inline_size, value_len, needed_blocks; | ||
int inline_size, value_len, needed_blocks, no_expand, err = 0; | ||
size_t i_size; | ||
void *value = NULL; | ||
struct ext4_xattr_ibody_find is = { | ||
... | ... | @@ -1918,19 +1924,19 @@ void ext4_inline_data_truncate(struct inode *inode, int *has_inline) |
needed_blocks = ext4_writepage_trans_blocks(inode); | ||
handle = ext4_journal_start(inode, EXT4_HT_INODE, needed_blocks); | ||
if (IS_ERR(handle)) | ||
return; | ||
return PTR_ERR(handle); | ||
down_write(&EXT4_I(inode)->xattr_sem); | ||
ext4_write_lock_xattr(inode, &no_expand); | ||
if (!ext4_has_inline_data(inode)) { | ||
*has_inline = 0; | ||
ext4_journal_stop(handle); | ||
return; | ||
return 0; | ||
} | ||
if (ext4_orphan_add(handle, inode)) | ||
if ((err = ext4_orphan_add(handle, inode)) != 0) | ||
goto out; | ||
if (ext4_get_inode_loc(inode, &is.iloc)) | ||
if ((err = ext4_get_inode_loc(inode, &is.iloc)) != 0) | ||
goto out; | ||
down_write(&EXT4_I(inode)->i_data_sem); | ||
... | ... | @@ -1941,24 +1947,29 @@ void ext4_inline_data_truncate(struct inode *inode, int *has_inline) |
if (i_size < inline_size) { | ||
/* Clear the content in the xattr space. */ | ||
if (inline_size > EXT4_MIN_INLINE_DATA_SIZE) { | ||
if (ext4_xattr_ibody_find(inode, &i, &is)) | ||
if ((err = ext4_xattr_ibody_find(inode, &i, &is)) != 0) | ||
goto out_error; | ||
BUG_ON(is.s.not_found); | ||
value_len = le32_to_cpu(is.s.here->e_value_size); | ||
value = kmalloc(value_len, GFP_NOFS); | ||
if (!value) | ||
if (!value) { | ||
err = -ENOMEM; | ||
goto out_error; | ||
} | ||
if (ext4_xattr_ibody_get(inode, i.name_index, i.name, | ||
value, value_len)) | ||
err = ext4_xattr_ibody_get(inode, i.name_index, | ||
i.name, value, value_len); | ||
if (err <= 0) | ||
goto out_error; | ||
i.value = value; | ||
i.value_len = i_size > EXT4_MIN_INLINE_DATA_SIZE ? | ||
i_size - EXT4_MIN_INLINE_DATA_SIZE : 0; | ||
if (ext4_xattr_ibody_inline_set(handle, inode, &i, &is)) | ||
err = ext4_xattr_ibody_inline_set(handle, inode, | ||
&i, &is); | ||
if (err) | ||
goto out_error; | ||
} | ||
... | ... | @@ -1978,23 +1989,24 @@ out_error: |
< |