summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--fs/crypto/policy.c1
-rw-r--r--fs/ext4/acl.c13
-rw-r--r--fs/ext4/ialloc.c52
-rw-r--r--fs/ext4/super.c3
-rw-r--r--fs/ext4/xattr.c46
-rw-r--r--fs/ext4/xattr.h5
-rw-r--r--include/linux/fscrypt_common.h3
7 files changed, 89 insertions, 34 deletions
diff --git a/fs/crypto/policy.c b/fs/crypto/policy.c
index 210976e7a269..94becf5a1519 100644
--- a/fs/crypto/policy.c
+++ b/fs/crypto/policy.c
@@ -260,6 +260,7 @@ int fscrypt_inherit_context(struct inode *parent, struct inode *child,
memcpy(ctx.master_key_descriptor, ci->ci_master_key,
FS_KEY_DESCRIPTOR_SIZE);
get_random_bytes(ctx.nonce, FS_KEY_DERIVATION_NONCE_SIZE);
+ BUILD_BUG_ON(sizeof(ctx) != FSCRYPT_SET_CONTEXT_MAX_SIZE);
res = parent->i_sb->s_cop->set_context(child, &ctx,
sizeof(ctx), fs_data);
if (res)
diff --git a/fs/ext4/acl.c b/fs/ext4/acl.c
index 8db03e5c78bc..09441ae07a5b 100644
--- a/fs/ext4/acl.c
+++ b/fs/ext4/acl.c
@@ -183,7 +183,7 @@ ext4_get_acl(struct inode *inode, int type)
*/
static int
__ext4_set_acl(handle_t *handle, struct inode *inode, int type,
- struct posix_acl *acl)
+ struct posix_acl *acl, int xattr_flags)
{
int name_index;
void *value = NULL;
@@ -218,7 +218,7 @@ __ext4_set_acl(handle_t *handle, struct inode *inode, int type,
}
error = ext4_xattr_set_handle(handle, inode, name_index, "",
- value, size, 0);
+ value, size, xattr_flags);
kfree(value);
if (!error)
@@ -238,7 +238,8 @@ ext4_set_acl(struct inode *inode, struct posix_acl *acl, int type)
if (error)
return error;
retry:
- error = ext4_xattr_set_credits(inode, acl_size, &credits);
+ error = ext4_xattr_set_credits(inode, acl_size, false /* is_create */,
+ &credits);
if (error)
return error;
@@ -246,7 +247,7 @@ retry:
if (IS_ERR(handle))
return PTR_ERR(handle);
- error = __ext4_set_acl(handle, inode, type, acl);
+ error = __ext4_set_acl(handle, inode, type, acl, 0 /* xattr_flags */);
ext4_journal_stop(handle);
if (error == -ENOSPC && ext4_should_retry_alloc(inode->i_sb, &retries))
goto retry;
@@ -271,13 +272,13 @@ ext4_init_acl(handle_t *handle, struct inode *inode, struct inode *dir)
if (default_acl) {
error = __ext4_set_acl(handle, inode, ACL_TYPE_DEFAULT,
- default_acl);
+ default_acl, XATTR_CREATE);
posix_acl_release(default_acl);
}
if (acl) {
if (!error)
error = __ext4_set_acl(handle, inode, ACL_TYPE_ACCESS,
- acl);
+ acl, XATTR_CREATE);
posix_acl_release(acl);
}
return error;
diff --git a/fs/ext4/ialloc.c b/fs/ext4/ialloc.c
index 0c79e3efcaf7..507bfb3344d4 100644
--- a/fs/ext4/ialloc.c
+++ b/fs/ext4/ialloc.c
@@ -766,11 +766,13 @@ struct inode *__ext4_new_inode(handle_t *handle, struct inode *dir,
if (!dir || !dir->i_nlink)
return ERR_PTR(-EPERM);
- if (unlikely(ext4_forced_shutdown(EXT4_SB(dir->i_sb))))
+ sb = dir->i_sb;
+ sbi = EXT4_SB(sb);
+
+ if (unlikely(ext4_forced_shutdown(sbi)))
return ERR_PTR(-EIO);
- if ((ext4_encrypted_inode(dir) ||
- DUMMY_ENCRYPTION_ENABLED(EXT4_SB(dir->i_sb))) &&
+ if ((ext4_encrypted_inode(dir) || DUMMY_ENCRYPTION_ENABLED(sbi)) &&
(S_ISREG(mode) || S_ISDIR(mode) || S_ISLNK(mode)) &&
!(i_flags & EXT4_EA_INODE_FL)) {
err = fscrypt_get_encryption_info(dir);
@@ -778,19 +780,55 @@ struct inode *__ext4_new_inode(handle_t *handle, struct inode *dir,
return ERR_PTR(err);
if (!fscrypt_has_encryption_key(dir))
return ERR_PTR(-ENOKEY);
- if (!handle)
- nblocks += EXT4_DATA_TRANS_BLOCKS(dir->i_sb);
encrypt = 1;
}
- sb = dir->i_sb;
+ if (!handle && sbi->s_journal && !(i_flags & EXT4_EA_INODE_FL)) {
+#ifdef CONFIG_EXT4_FS_POSIX_ACL
+ struct posix_acl *p = get_acl(dir, ACL_TYPE_DEFAULT);
+
+ if (p) {
+ int acl_size = p->a_count * sizeof(ext4_acl_entry);
+
+ nblocks += (S_ISDIR(mode) ? 2 : 1) *
+ __ext4_xattr_set_credits(sb, NULL /* inode */,
+ NULL /* block_bh */, acl_size,
+ true /* is_create */);
+ posix_acl_release(p);
+ }
+#endif
+
+#ifdef CONFIG_SECURITY
+ {
+ int num_security_xattrs = 1;
+
+#ifdef CONFIG_INTEGRITY
+ num_security_xattrs++;
+#endif
+ /*
+ * We assume that security xattrs are never
+ * more than 1k. In practice they are under
+ * 128 bytes.
+ */
+ nblocks += num_security_xattrs *
+ __ext4_xattr_set_credits(sb, NULL /* inode */,
+ NULL /* block_bh */, 1024,
+ true /* is_create */);
+ }
+#endif
+ if (encrypt)
+ nblocks += __ext4_xattr_set_credits(sb,
+ NULL /* inode */, NULL /* block_bh */,
+ FSCRYPT_SET_CONTEXT_MAX_SIZE,
+ true /* is_create */);
+ }
+
ngroups = ext4_get_groups_count(sb);
trace_ext4_request_inode(dir, mode);
inode = new_inode(sb);
if (!inode)
return ERR_PTR(-ENOMEM);
ei = EXT4_I(inode);
- sbi = EXT4_SB(sb);
/*
* Initialize owners and quota early so that we don't have to account
diff --git a/fs/ext4/super.c b/fs/ext4/super.c
index 56c971807df5..f666042a3d58 100644
--- a/fs/ext4/super.c
+++ b/fs/ext4/super.c
@@ -1194,7 +1194,8 @@ static int ext4_set_context(struct inode *inode, const void *ctx, size_t len,
if (res)
return res;
retry:
- res = ext4_xattr_set_credits(inode, len, &credits);
+ res = ext4_xattr_set_credits(inode, len, false /* is_create */,
+ &credits);
if (res)
return res;
diff --git a/fs/ext4/xattr.c b/fs/ext4/xattr.c
index 34fa37e7744c..cff4f41ced61 100644
--- a/fs/ext4/xattr.c
+++ b/fs/ext4/xattr.c
@@ -830,11 +830,10 @@ static void ext4_xattr_inode_free_quota(struct inode *inode, size_t len)
dquot_free_inode(inode);
}
-static int __ext4_xattr_set_credits(struct inode *inode,
- struct buffer_head *block_bh,
- size_t value_len)
+int __ext4_xattr_set_credits(struct super_block *sb, struct inode *inode,
+ struct buffer_head *block_bh, size_t value_len,
+ bool is_create)
{
- struct super_block *sb = inode->i_sb;
int credits;
int blocks;
@@ -860,7 +859,7 @@ static int __ext4_xattr_set_credits(struct inode *inode,
* In case of inline data, we may push out the data to a block,
* so we need to reserve credits for this eventuality
*/
- if (ext4_has_inline_data(inode))
+ if (inode && ext4_has_inline_data(inode))
credits += ext4_writepage_trans_blocks(inode) + 1;
/* We are done if ea_inode feature is not enabled. */
@@ -882,19 +881,23 @@ static int __ext4_xattr_set_credits(struct inode *inode,
/* Blocks themselves. */
credits += blocks;
- /* Dereference ea_inode holding old xattr value.
- * Old ea_inode, inode map, block bitmap, group descriptor.
- */
- credits += 4;
+ if (!is_create) {
+ /* Dereference ea_inode holding old xattr value.
+ * Old ea_inode, inode map, block bitmap, group descriptor.
+ */
+ credits += 4;
- /* Data blocks for old ea_inode. */
- blocks = XATTR_SIZE_MAX >> sb->s_blocksize_bits;
+ /* Data blocks for old ea_inode. */
+ blocks = XATTR_SIZE_MAX >> sb->s_blocksize_bits;
- /* Indirection block or one level of extent tree for old ea_inode. */
- blocks += 1;
+ /* Indirection block or one level of extent tree for old
+ * ea_inode.
+ */
+ blocks += 1;
- /* Block bitmap and group descriptor updates for each block. */
- credits += blocks * 2;
+ /* Block bitmap and group descriptor updates for each block. */
+ credits += blocks * 2;
+ }
/* We may need to clone the existing xattr block in which case we need
* to increment ref counts for existing ea_inodes referenced by it.
@@ -2263,7 +2266,9 @@ ext4_xattr_set_handle(handle_t *handle, struct inode *inode, int name_index,
goto cleanup;
}
- credits = __ext4_xattr_set_credits(inode, bh, value_len);
+ credits = __ext4_xattr_set_credits(inode->i_sb, inode, bh,
+ value_len,
+ flags & XATTR_CREATE);
brelse(bh);
if (!ext4_handle_has_enough_credits(handle, credits)) {
@@ -2370,7 +2375,8 @@ cleanup:
return error;
}
-int ext4_xattr_set_credits(struct inode *inode, size_t value_len, int *credits)
+int ext4_xattr_set_credits(struct inode *inode, size_t value_len,
+ bool is_create, int *credits)
{
struct buffer_head *bh;
int err;
@@ -2386,7 +2392,8 @@ int ext4_xattr_set_credits(struct inode *inode, size_t value_len, int *credits)
if (IS_ERR(bh)) {
err = PTR_ERR(bh);
} else {
- *credits = __ext4_xattr_set_credits(inode, bh, value_len);
+ *credits = __ext4_xattr_set_credits(inode->i_sb, inode, bh,
+ value_len, is_create);
brelse(bh);
err = 0;
}
@@ -2417,7 +2424,8 @@ ext4_xattr_set(struct inode *inode, int name_index, const char *name,
return error;
retry:
- error = ext4_xattr_set_credits(inode, value_len, &credits);
+ error = ext4_xattr_set_credits(inode, value_len, flags & XATTR_CREATE,
+ &credits);
if (error)
return error;
diff --git a/fs/ext4/xattr.h b/fs/ext4/xattr.h
index 26119a67c8c3..0d2dde1fa87a 100644
--- a/fs/ext4/xattr.h
+++ b/fs/ext4/xattr.h
@@ -153,7 +153,10 @@ extern int ext4_xattr_get(struct inode *, int, const char *, void *, size_t);
extern int ext4_xattr_set(struct inode *, int, const char *, const void *, size_t, int);
extern int ext4_xattr_set_handle(handle_t *, struct inode *, int, const char *, const void *, size_t, int);
extern int ext4_xattr_set_credits(struct inode *inode, size_t value_len,
- int *credits);
+ bool is_create, int *credits);
+extern int __ext4_xattr_set_credits(struct super_block *sb, struct inode *inode,
+ struct buffer_head *block_bh, size_t value_len,
+ bool is_create);
extern int ext4_xattr_delete_inode(handle_t *handle, struct inode *inode,
struct ext4_xattr_inode_array **array,
diff --git a/include/linux/fscrypt_common.h b/include/linux/fscrypt_common.h
index 0a30c106c1e5..82beaf70e7e2 100644
--- a/include/linux/fscrypt_common.h
+++ b/include/linux/fscrypt_common.h
@@ -83,6 +83,9 @@ struct fscrypt_operations {
unsigned (*max_namelen)(struct inode *);
};
+/* Maximum value for the third parameter of fscrypt_operations.set_context(). */
+#define FSCRYPT_SET_CONTEXT_MAX_SIZE 28
+
static inline bool fscrypt_dummy_context_enabled(struct inode *inode)
{
if (inode->i_sb->s_cop->dummy_context &&