|
@@ -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:
|
|
|
up_write(&EXT4_I(inode)->i_data_sem);
|
|
|
out:
|
|
|
brelse(is.iloc.bh);
|
|
|
- up_write(&EXT4_I(inode)->xattr_sem);
|
|
|
+ ext4_write_unlock_xattr(inode, &no_expand);
|
|
|
kfree(value);
|
|
|
if (inode->i_nlink)
|
|
|
ext4_orphan_del(handle, inode);
|
|
|
|
|
|
- inode->i_mtime = inode->i_ctime = current_time(inode);
|
|
|
- ext4_mark_inode_dirty(handle, inode);
|
|
|
- if (IS_SYNC(inode))
|
|
|
- ext4_handle_sync(handle);
|
|
|
-
|
|
|
+ if (err == 0) {
|
|
|
+ inode->i_mtime = inode->i_ctime = current_time(inode);
|
|
|
+ err = ext4_mark_inode_dirty(handle, inode);
|
|
|
+ if (IS_SYNC(inode))
|
|
|
+ ext4_handle_sync(handle);
|
|
|
+ }
|
|
|
ext4_journal_stop(handle);
|
|
|
- return;
|
|
|
+ return err;
|
|
|
}
|
|
|
|
|
|
int ext4_convert_inline_data(struct inode *inode)
|
|
|
{
|
|
|
- int error, needed_blocks;
|
|
|
+ int error, needed_blocks, no_expand;
|
|
|
handle_t *handle;
|
|
|
struct ext4_iloc iloc;
|
|
|
|
|
@@ -2016,15 +2028,10 @@ int ext4_convert_inline_data(struct inode *inode)
|
|
|
goto out_free;
|
|
|
}
|
|
|
|
|
|
- down_write(&EXT4_I(inode)->xattr_sem);
|
|
|
- if (!ext4_has_inline_data(inode)) {
|
|
|
- up_write(&EXT4_I(inode)->xattr_sem);
|
|
|
- goto out;
|
|
|
- }
|
|
|
-
|
|
|
- error = ext4_convert_inline_data_nolock(handle, inode, &iloc);
|
|
|
- up_write(&EXT4_I(inode)->xattr_sem);
|
|
|
-out:
|
|
|
+ ext4_write_lock_xattr(inode, &no_expand);
|
|
|
+ if (ext4_has_inline_data(inode))
|
|
|
+ error = ext4_convert_inline_data_nolock(handle, inode, &iloc);
|
|
|
+ ext4_write_unlock_xattr(inode, &no_expand);
|
|
|
ext4_journal_stop(handle);
|
|
|
out_free:
|
|
|
brelse(iloc.bh);
|