summaryrefslogtreecommitdiffstats
path: root/fs/ext4/inode.c
diff options
context:
space:
mode:
Diffstat (limited to 'fs/ext4/inode.c')
-rw-r--r--fs/ext4/inode.c290
1 files changed, 211 insertions, 79 deletions
diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c
index 5cf82d03968c..c774bdc22759 100644
--- a/fs/ext4/inode.c
+++ b/fs/ext4/inode.c
@@ -144,16 +144,12 @@ static int ext4_meta_trans_blocks(struct inode *inode, int lblocks,
/*
* Test whether an inode is a fast symlink.
+ * A fast symlink has its symlink data stored in ext4_inode_info->i_data.
*/
int ext4_inode_is_fast_symlink(struct inode *inode)
{
- int ea_blocks = EXT4_I(inode)->i_file_acl ?
- EXT4_CLUSTER_SIZE(inode->i_sb) >> 9 : 0;
-
- if (ext4_has_inline_data(inode))
- return 0;
-
- return (S_ISLNK(inode->i_mode) && inode->i_blocks - ea_blocks == 0);
+ return S_ISLNK(inode->i_mode) && inode->i_size &&
+ (inode->i_size < EXT4_N_BLOCKS * 4);
}
/*
@@ -189,6 +185,8 @@ void ext4_evict_inode(struct inode *inode)
{
handle_t *handle;
int err;
+ int extra_credits = 3;
+ struct ext4_xattr_inode_array *ea_inode_array = NULL;
trace_ext4_evict_inode(inode);
@@ -213,7 +211,8 @@ void ext4_evict_inode(struct inode *inode)
*/
if (inode->i_ino != EXT4_JOURNAL_INO &&
ext4_should_journal_data(inode) &&
- (S_ISLNK(inode->i_mode) || S_ISREG(inode->i_mode))) {
+ (S_ISLNK(inode->i_mode) || S_ISREG(inode->i_mode)) &&
+ inode->i_data.nrpages) {
journal_t *journal = EXT4_SB(inode->i_sb)->s_journal;
tid_t commit_tid = EXT4_I(inode)->i_datasync_tid;
@@ -238,8 +237,12 @@ void ext4_evict_inode(struct inode *inode)
* protection against it
*/
sb_start_intwrite(inode->i_sb);
+
+ if (!IS_NOQUOTA(inode))
+ extra_credits += EXT4_MAXQUOTAS_DEL_BLOCKS(inode->i_sb);
+
handle = ext4_journal_start(inode, EXT4_HT_TRUNCATE,
- ext4_blocks_for_truncate(inode)+3);
+ ext4_blocks_for_truncate(inode)+extra_credits);
if (IS_ERR(handle)) {
ext4_std_error(inode->i_sb, PTR_ERR(handle));
/*
@@ -254,6 +257,16 @@ void ext4_evict_inode(struct inode *inode)
if (IS_SYNC(inode))
ext4_handle_sync(handle);
+
+ /*
+ * Set inode->i_size to 0 before calling ext4_truncate(). We need
+ * special handling of symlinks here because i_size is used to
+ * determine whether ext4_inode_info->i_data contains symlink data or
+ * block mappings. Setting i_size to 0 will remove its fast symlink
+ * status. Erase i_data so that it becomes a valid empty block map.
+ */
+ if (ext4_inode_is_fast_symlink(inode))
+ memset(EXT4_I(inode)->i_data, 0, sizeof(EXT4_I(inode)->i_data));
inode->i_size = 0;
err = ext4_mark_inode_dirty(handle, inode);
if (err) {
@@ -271,25 +284,17 @@ void ext4_evict_inode(struct inode *inode)
}
}
- /*
- * ext4_ext_truncate() doesn't reserve any slop when it
- * restarts journal transactions; therefore there may not be
- * enough credits left in the handle to remove the inode from
- * the orphan list and set the dtime field.
- */
- if (!ext4_handle_has_enough_credits(handle, 3)) {
- err = ext4_journal_extend(handle, 3);
- if (err > 0)
- err = ext4_journal_restart(handle, 3);
- if (err != 0) {
- ext4_warning(inode->i_sb,
- "couldn't extend journal (err %d)", err);
- stop_handle:
- ext4_journal_stop(handle);
- ext4_orphan_del(NULL, inode);
- sb_end_intwrite(inode->i_sb);
- goto no_delete;
- }
+ /* Remove xattr references. */
+ err = ext4_xattr_delete_inode(handle, inode, &ea_inode_array,
+ extra_credits);
+ if (err) {
+ ext4_warning(inode->i_sb, "xattr delete (err %d)", err);
+stop_handle:
+ ext4_journal_stop(handle);
+ ext4_orphan_del(NULL, inode);
+ sb_end_intwrite(inode->i_sb);
+ ext4_xattr_inode_array_free(ea_inode_array);
+ goto no_delete;
}
/*
@@ -317,6 +322,7 @@ void ext4_evict_inode(struct inode *inode)
ext4_free_inode(handle, inode);
ext4_journal_stop(handle);
sb_end_intwrite(inode->i_sb);
+ ext4_xattr_inode_array_free(ea_inode_array);
return;
no_delete:
ext4_clear_inode(inode); /* We must guarantee clearing of inode... */
@@ -710,7 +716,7 @@ out_sem:
if (map->m_flags & EXT4_MAP_NEW &&
!(map->m_flags & EXT4_MAP_UNWRITTEN) &&
!(flags & EXT4_GET_BLOCKS_ZERO) &&
- !IS_NOQUOTA(inode) &&
+ !ext4_is_quota_file(inode) &&
ext4_should_order_data(inode)) {
if (flags & EXT4_GET_BLOCKS_IO_SUBMIT)
ret = ext4_jbd2_inode_add_wait(handle, inode);
@@ -886,7 +892,7 @@ static int ext4_dio_get_block_unwritten_async(struct inode *inode,
/*
* Get block function for non-AIO DIO writes when we create unwritten extent if
* blocks are not allocated yet. The extent will be converted to written
- * after IO is complete from ext4_ext_direct_IO() function.
+ * after IO is complete by ext4_direct_IO_write().
*/
static int ext4_dio_get_block_unwritten_sync(struct inode *inode,
sector_t iblock, struct buffer_head *bh_result, int create)
@@ -901,7 +907,7 @@ static int ext4_dio_get_block_unwritten_sync(struct inode *inode,
/*
* Mark inode as having pending DIO writes to unwritten extents.
- * ext4_ext_direct_IO() checks this flag and converts extents to
+ * ext4_direct_IO_write() checks this flag and converts extents to
* written.
*/
if (!ret && buffer_unwritten(bh_result))
@@ -1009,6 +1015,50 @@ struct buffer_head *ext4_bread(handle_t *handle, struct inode *inode,
return ERR_PTR(-EIO);
}
+/* Read a contiguous batch of blocks. */
+int ext4_bread_batch(struct inode *inode, ext4_lblk_t block, int bh_count,
+ bool wait, struct buffer_head **bhs)
+{
+ int i, err;
+
+ for (i = 0; i < bh_count; i++) {
+ bhs[i] = ext4_getblk(NULL, inode, block + i, 0 /* map_flags */);
+ if (IS_ERR(bhs[i])) {
+ err = PTR_ERR(bhs[i]);
+ bh_count = i;
+ goto out_brelse;
+ }
+ }
+
+ for (i = 0; i < bh_count; i++)
+ /* Note that NULL bhs[i] is valid because of holes. */
+ if (bhs[i] && !buffer_uptodate(bhs[i]))
+ ll_rw_block(REQ_OP_READ, REQ_META | REQ_PRIO, 1,
+ &bhs[i]);
+
+ if (!wait)
+ return 0;
+
+ for (i = 0; i < bh_count; i++)
+ if (bhs[i])
+ wait_on_buffer(bhs[i]);
+
+ for (i = 0; i < bh_count; i++) {
+ if (bhs[i] && !buffer_uptodate(bhs[i])) {
+ err = -EIO;
+ goto out_brelse;
+ }
+ }
+ return 0;
+
+out_brelse:
+ for (i = 0; i < bh_count; i++) {
+ brelse(bhs[i]);
+ bhs[i] = NULL;
+ }
+ return err;
+}
+
int ext4_walk_page_buffers(handle_t *handle,
struct buffer_head *head,
unsigned from,
@@ -4712,7 +4762,7 @@ struct inode *ext4_iget(struct super_block *sb, unsigned long ino)
if (ext4_has_feature_64bit(sb))
ei->i_file_acl |=
((__u64)le16_to_cpu(raw_inode->i_file_acl_high)) << 32;
- inode->i_size = ext4_isize(raw_inode);
+ inode->i_size = ext4_isize(sb, raw_inode);
if ((size = i_size_read(inode)) < 0) {
EXT4_ERROR_INODE(inode, "bad i_size value: %lld", size);
ret = -EFSCORRUPTED;
@@ -4846,6 +4896,15 @@ struct inode *ext4_iget(struct super_block *sb, unsigned long ino)
}
brelse(iloc.bh);
ext4_set_inode_flags(inode);
+
+ if (ei->i_flags & EXT4_EA_INODE_FL) {
+ ext4_xattr_inode_set_class(inode);
+
+ inode_lock(inode);
+ inode->i_flags |= S_NOQUOTA;
+ inode_unlock(inode);
+ }
+
unlock_new_inode(inode);
return inode;
@@ -5037,7 +5096,7 @@ static int ext4_do_update_inode(handle_t *handle,
raw_inode->i_file_acl_high =
cpu_to_le16(ei->i_file_acl >> 32);
raw_inode->i_file_acl_lo = cpu_to_le32(ei->i_file_acl);
- if (ei->i_disksize != ext4_isize(raw_inode)) {
+ if (ei->i_disksize != ext4_isize(inode->i_sb, raw_inode)) {
ext4_isize_set(raw_inode, ei->i_disksize);
need_datasync = 1;
}
@@ -5287,7 +5346,14 @@ int ext4_setattr(struct dentry *dentry, struct iattr *attr)
error = PTR_ERR(handle);
goto err_out;
}
+
+ /* dquot_transfer() calls back ext4_get_inode_usage() which
+ * counts xattr inode references.
+ */
+ down_read(&EXT4_I(inode)->xattr_sem);
error = dquot_transfer(inode, attr);
+ up_read(&EXT4_I(inode)->xattr_sem);
+
if (error) {
ext4_journal_stop(handle);
return error;
@@ -5307,6 +5373,14 @@ int ext4_setattr(struct dentry *dentry, struct iattr *attr)
loff_t oldsize = inode->i_size;
int shrink = (attr->ia_size <= inode->i_size);
+ if (ext4_encrypted_inode(inode)) {
+ error = fscrypt_get_encryption_info(inode);
+ if (error)
+ return error;
+ if (!fscrypt_has_encryption_key(inode))
+ return -ENOKEY;
+ }
+
if (!(ext4_test_inode_flag(inode, EXT4_INODE_EXTENTS))) {
struct ext4_sb_info *sbi = EXT4_SB(inode->i_sb);
@@ -5628,22 +5702,16 @@ ext4_reserve_inode_write(handle_t *handle, struct inode *inode,
return err;
}
-/*
- * Expand an inode by new_extra_isize bytes.
- * Returns 0 on success or negative error number on failure.
- */
-static int ext4_expand_extra_isize(struct inode *inode,
- unsigned int new_extra_isize,
- struct ext4_iloc iloc,
- handle_t *handle)
+static int __ext4_expand_extra_isize(struct inode *inode,
+ unsigned int new_extra_isize,
+ struct ext4_iloc *iloc,
+ handle_t *handle, int *no_expand)
{
struct ext4_inode *raw_inode;
struct ext4_xattr_ibody_header *header;
+ int error;
- if (EXT4_I(inode)->i_extra_isize >= new_extra_isize)
- return 0;
-
- raw_inode = ext4_raw_inode(&iloc);
+ raw_inode = ext4_raw_inode(iloc);
header = IHDR(inode, raw_inode);
@@ -5658,8 +5726,98 @@ static int ext4_expand_extra_isize(struct inode *inode,
}
/* try to expand with EAs present */
- return ext4_expand_extra_isize_ea(inode, new_extra_isize,
- raw_inode, handle);
+ error = ext4_expand_extra_isize_ea(inode, new_extra_isize,
+ raw_inode, handle);
+ if (error) {
+ /*
+ * Inode size expansion failed; don't try again
+ */
+ *no_expand = 1;
+ }
+
+ return error;
+}
+
+/*
+ * Expand an inode by new_extra_isize bytes.
+ * Returns 0 on success or negative error number on failure.
+ */
+static int ext4_try_to_expand_extra_isize(struct inode *inode,
+ unsigned int new_extra_isize,
+ struct ext4_iloc iloc,
+ handle_t *handle)
+{
+ int no_expand;
+ int error;
+
+ if (ext4_test_inode_state(inode, EXT4_STATE_NO_EXPAND))
+ return -EOVERFLOW;
+
+ /*
+ * In nojournal mode, we can immediately attempt to expand
+ * the inode. When journaled, we first need to obtain extra
+ * buffer credits since we may write into the EA block
+ * with this same handle. If journal_extend fails, then it will
+ * only result in a minor loss of functionality for that inode.
+ * If this is felt to be critical, then e2fsck should be run to
+ * force a large enough s_min_extra_isize.
+ */
+ if (ext4_handle_valid(handle) &&
+ jbd2_journal_extend(handle,
+ EXT4_DATA_TRANS_BLOCKS(inode->i_sb)) != 0)
+ return -ENOSPC;
+
+ if (ext4_write_trylock_xattr(inode, &no_expand) == 0)
+ return -EBUSY;
+
+ error = __ext4_expand_extra_isize(inode, new_extra_isize, &iloc,
+ handle, &no_expand);
+ ext4_write_unlock_xattr(inode, &no_expand);
+
+ return error;
+}
+
+int ext4_expand_extra_isize(struct inode *inode,
+ unsigned int new_extra_isize,
+ struct ext4_iloc *iloc)
+{
+ handle_t *handle;
+ int no_expand;
+ int error, rc;
+
+ if (ext4_test_inode_state(inode, EXT4_STATE_NO_EXPAND)) {
+ brelse(iloc->bh);
+ return -EOVERFLOW;
+ }
+
+ handle = ext4_journal_start(inode, EXT4_HT_INODE,
+ EXT4_DATA_TRANS_BLOCKS(inode->i_sb));
+ if (IS_ERR(handle)) {
+ error = PTR_ERR(handle);
+ brelse(iloc->bh);
+ return error;
+ }
+
+ ext4_write_lock_xattr(inode, &no_expand);
+
+ BUFFER_TRACE(iloc.bh, "get_write_access");
+ error = ext4_journal_get_write_access(handle, iloc->bh);
+ if (error) {
+ brelse(iloc->bh);
+ goto out_stop;
+ }
+
+ error = __ext4_expand_extra_isize(inode, new_extra_isize, iloc,
+ handle, &no_expand);
+
+ rc = ext4_mark_iloc_dirty(handle, inode, iloc);
+ if (!error)
+ error = rc;
+
+ ext4_write_unlock_xattr(inode, &no_expand);
+out_stop:
+ ext4_journal_stop(handle);
+ return error;
}
/*
@@ -5679,44 +5837,18 @@ int ext4_mark_inode_dirty(handle_t *handle, struct inode *inode)
{
struct ext4_iloc iloc;
struct ext4_sb_info *sbi = EXT4_SB(inode->i_sb);
- static unsigned int mnt_count;
- int err, ret;
+ int err;
might_sleep();
trace_ext4_mark_inode_dirty(inode, _RET_IP_);
err = ext4_reserve_inode_write(handle, inode, &iloc);
if (err)
return err;
- if (EXT4_I(inode)->i_extra_isize < sbi->s_want_extra_isize &&
- !ext4_test_inode_state(inode, EXT4_STATE_NO_EXPAND)) {
- /*
- * In nojournal mode, we can immediately attempt to expand
- * the inode. When journaled, we first need to obtain extra
- * buffer credits since we may write into the EA block
- * with this same handle. If journal_extend fails, then it will
- * only result in a minor loss of functionality for that inode.
- * If this is felt to be critical, then e2fsck should be run to
- * force a large enough s_min_extra_isize.
- */
- if (!ext4_handle_valid(handle) ||
- jbd2_journal_extend(handle,
- EXT4_DATA_TRANS_BLOCKS(inode->i_sb)) == 0) {
- ret = ext4_expand_extra_isize(inode,
- sbi->s_want_extra_isize,
- iloc, handle);
- if (ret) {
- if (mnt_count !=
- le16_to_cpu(sbi->s_es->s_mnt_count)) {
- ext4_warning(inode->i_sb,
- "Unable to expand inode %lu. Delete"
- " some EAs or run e2fsck.",
- inode->i_ino);
- mnt_count =
- le16_to_cpu(sbi->s_es->s_mnt_count);
- }
- }
- }
- }
+
+ if (EXT4_I(inode)->i_extra_isize < sbi->s_want_extra_isize)
+ ext4_try_to_expand_extra_isize(inode, sbi->s_want_extra_isize,
+ iloc, handle);
+
return ext4_mark_iloc_dirty(handle, inode, &iloc);
}
OpenPOWER on IntegriCloud