summaryrefslogtreecommitdiff
path: root/fs
diff options
context:
space:
mode:
authorGreg Kroah-Hartman <gregkh@linuxfoundation.org>2026-07-04 13:45:09 +0200
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2026-07-04 13:45:09 +0200
commit8ca1f4c6fb1462ee120730ea75c19da10d2f2d6f (patch)
treefa6bebc72c42a94b66d4b3676371753d31e66702 /fs
parent9372da540bfdcfa345309043e57b335c900a8ad9 (diff)
parent199c9959d3a9b53f346c221757fc7ac507fbac50 (diff)
downloadlinux-rolling-stable.tar.gz
linux-rolling-stable.zip
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'fs')
-rw-r--r--fs/crypto/fscrypt_private.h52
-rw-r--r--fs/crypto/inline_crypt.c8
-rw-r--r--fs/crypto/keyring.c23
-rw-r--r--fs/crypto/keysetup.c118
-rw-r--r--fs/exfat/dir.c4
-rw-r--r--fs/f2fs/acl.c18
-rw-r--r--fs/f2fs/checkpoint.c14
-rw-r--r--fs/f2fs/data.c49
-rw-r--r--fs/f2fs/extent_cache.c19
-rw-r--r--fs/f2fs/f2fs.h1
-rw-r--r--fs/f2fs/file.c11
-rw-r--r--fs/f2fs/gc.c56
-rw-r--r--fs/f2fs/inode.c26
-rw-r--r--fs/f2fs/node.c8
-rw-r--r--fs/gfs2/super.c1
-rw-r--r--fs/nfs/client.c1
-rw-r--r--fs/nfs/flexfilelayout/flexfilelayout.c4
-rw-r--r--fs/nfs/nfs4proc.c5
-rw-r--r--fs/nfs/pnfs.c2
-rw-r--r--fs/nfs/pnfs_nfs.c4
-rw-r--r--fs/nfsd/nfs2acl.c17
-rw-r--r--fs/nfsd/nfs3acl.c17
-rw-r--r--fs/nfsd/nfs4layouts.c12
-rw-r--r--fs/nfsd/nfs4proc.c19
-rw-r--r--fs/nfsd/nfs4recover.c3
-rw-r--r--fs/nfsd/nfs4state.c1
-rw-r--r--fs/nfsd/nfs4xdr.c3
-rw-r--r--fs/nfsd/vfs.c6
-rw-r--r--fs/ntfs/file.c17
-rw-r--r--fs/ntfs/super.c19
-rw-r--r--fs/ntfs/volume.h2
-rw-r--r--fs/ntfs3/xattr.c12
-rw-r--r--fs/ocfs2/suballoc.c22
-rw-r--r--fs/smb/server/smbacl.c4
-rw-r--r--fs/userfaultfd.c2
35 files changed, 395 insertions, 185 deletions
diff --git a/fs/crypto/fscrypt_private.h b/fs/crypto/fscrypt_private.h
index 8d3c278a7591..4263cac24b32 100644
--- a/fs/crypto/fscrypt_private.h
+++ b/fs/crypto/fscrypt_private.h
@@ -236,7 +236,7 @@ struct fscrypt_symlink_data {
* @tfm: crypto API transform object
* @blk_key: key for blk-crypto
*
- * Normally only one of the fields will be non-NULL.
+ * Only one of the fields is non-NULL.
*/
struct fscrypt_prepared_key {
struct crypto_sync_skcipher *tfm;
@@ -245,6 +245,15 @@ struct fscrypt_prepared_key {
#endif
};
+/* An entry in the linked list ->mk_mode_keys */
+struct fscrypt_mode_key {
+ struct fscrypt_prepared_key key;
+ struct list_head link;
+ u8 hkdf_context;
+ u8 mode_num;
+ u8 data_unit_bits;
+};
+
/*
* fscrypt_inode_info - the "encryption key" for an inode
*
@@ -430,20 +439,12 @@ int fscrypt_derive_sw_secret(struct super_block *sb,
* @prep_key, depending on which encryption implementation the file will use.
*/
static inline bool
-fscrypt_is_key_prepared(struct fscrypt_prepared_key *prep_key,
+fscrypt_is_key_prepared(const struct fscrypt_prepared_key *prep_key,
const struct fscrypt_inode_info *ci)
{
- /*
- * The two smp_load_acquire()'s here pair with the smp_store_release()'s
- * in fscrypt_prepare_inline_crypt_key() and fscrypt_prepare_key().
- * I.e., in some cases (namely, if this prep_key is a per-mode
- * encryption key) another task can publish blk_key or tfm concurrently,
- * executing a RELEASE barrier. We need to use smp_load_acquire() here
- * to safely ACQUIRE the memory the other task published.
- */
if (fscrypt_using_inline_encryption(ci))
- return smp_load_acquire(&prep_key->blk_key) != NULL;
- return smp_load_acquire(&prep_key->tfm) != NULL;
+ return prep_key->blk_key != NULL;
+ return prep_key->tfm != NULL;
}
#else /* CONFIG_FS_ENCRYPTION_INLINE_CRYPT */
@@ -486,10 +487,10 @@ fscrypt_derive_sw_secret(struct super_block *sb,
}
static inline bool
-fscrypt_is_key_prepared(struct fscrypt_prepared_key *prep_key,
+fscrypt_is_key_prepared(const struct fscrypt_prepared_key *prep_key,
const struct fscrypt_inode_info *ci)
{
- return smp_load_acquire(&prep_key->tfm) != NULL;
+ return prep_key->tfm != NULL;
}
#endif /* !CONFIG_FS_ENCRYPTION_INLINE_CRYPT */
@@ -577,8 +578,8 @@ struct fscrypt_master_key {
/*
* Active and structural reference counts. An active ref guarantees
* that the struct continues to exist, continues to be in the keyring
- * ->s_master_keys, and that any embedded subkeys (e.g.
- * ->mk_direct_keys) that have been prepared continue to exist.
+ * ->s_master_keys, and that any non-file-scoped subkeys (e.g.
+ * ->mk_mode_keys) that have been prepared continue to exist.
* A structural ref only guarantees that the struct continues to exist.
*
* There is one active ref associated with ->mk_present being true, and
@@ -632,12 +633,21 @@ struct fscrypt_master_key {
spinlock_t mk_decrypted_inodes_lock;
/*
- * Per-mode encryption keys for the various types of encryption policies
- * that use them. Allocated and derived on-demand.
+ * A list of 'struct fscrypt_mode_key' for the (hkdf_context, mode_num,
+ * data_unit_bits, inlinecrypt) combinations that are in use for this
+ * master key, for hkdf_context in [HKDF_CONTEXT_DIRECT_KEY,
+ * HKDF_CONTEXT_IV_INO_LBLK_32_KEY, HKDF_CONTEXT_IV_INO_LBLK_64_KEY].
+ *
+ * This is a linked list and not a hash table because in practice
+ * there's just a single encryption policy per master key, using
+ * _at most_ 2 nodes in this list. Per-file keys don't use this at all.
+ *
+ * This list is append-only until the master key is fully removed, at
+ * which time the list is cleared. Before then,
+ * fscrypt_mode_key_setup_mutex synchronizes appends, and searches use
+ * the RCU read lock together with ->mk_sem held for read.
*/
- struct fscrypt_prepared_key mk_direct_keys[FSCRYPT_MODE_MAX + 1];
- struct fscrypt_prepared_key mk_iv_ino_lblk_64_keys[FSCRYPT_MODE_MAX + 1];
- struct fscrypt_prepared_key mk_iv_ino_lblk_32_keys[FSCRYPT_MODE_MAX + 1];
+ struct list_head mk_mode_keys;
/* Hash key for inode numbers. Initialized only when needed. */
siphash_key_t mk_ino_hash_key;
diff --git a/fs/crypto/inline_crypt.c b/fs/crypto/inline_crypt.c
index 37d42d357925..47324062fee5 100644
--- a/fs/crypto/inline_crypt.c
+++ b/fs/crypto/inline_crypt.c
@@ -198,13 +198,7 @@ int fscrypt_prepare_inline_crypt_key(struct fscrypt_prepared_key *prep_key,
goto fail;
}
- /*
- * Pairs with the smp_load_acquire() in fscrypt_is_key_prepared().
- * I.e., here we publish ->blk_key with a RELEASE barrier so that
- * concurrent tasks can ACQUIRE it. Note that this concurrency is only
- * possible for per-mode keys, not for per-file keys.
- */
- smp_store_release(&prep_key->blk_key, blk_key);
+ prep_key->blk_key = blk_key;
return 0;
fail:
diff --git a/fs/crypto/keyring.c b/fs/crypto/keyring.c
index be8e6e8011f2..5fe0d985a58d 100644
--- a/fs/crypto/keyring.c
+++ b/fs/crypto/keyring.c
@@ -87,14 +87,14 @@ void fscrypt_put_master_key(struct fscrypt_master_key *mk)
void fscrypt_put_master_key_activeref(struct super_block *sb,
struct fscrypt_master_key *mk)
{
- size_t i;
+ struct fscrypt_mode_key *node, *tmp;
if (!refcount_dec_and_test(&mk->mk_active_refs))
return;
/*
* No active references left, so complete the full removal of this
* fscrypt_master_key struct by removing it from the keyring and
- * destroying any subkeys embedded in it.
+ * destroying any non-file-scoped subkeys.
*/
if (WARN_ON_ONCE(!sb->s_master_keys))
@@ -110,13 +110,16 @@ void fscrypt_put_master_key_activeref(struct super_block *sb,
WARN_ON_ONCE(mk->mk_present);
WARN_ON_ONCE(!list_empty(&mk->mk_decrypted_inodes));
- for (i = 0; i <= FSCRYPT_MODE_MAX; i++) {
- fscrypt_destroy_prepared_key(
- sb, &mk->mk_direct_keys[i]);
- fscrypt_destroy_prepared_key(
- sb, &mk->mk_iv_ino_lblk_64_keys[i]);
- fscrypt_destroy_prepared_key(
- sb, &mk->mk_iv_ino_lblk_32_keys[i]);
+ /*
+ * Destroy any non-file-scoped subkeys. Since ->mk_active_refs == 0,
+ * they're no longer referenced by any inodes. Nor can key setup run
+ * and use them again. So they're no longer needed. (This implies no
+ * concurrent readers, so we don't need list_del_rcu() for example.)
+ */
+ list_for_each_entry_safe(node, tmp, &mk->mk_mode_keys, link) {
+ fscrypt_destroy_prepared_key(sb, &node->key);
+ list_del(&node->link);
+ kfree(node);
}
memzero_explicit(&mk->mk_ino_hash_key,
sizeof(mk->mk_ino_hash_key));
@@ -445,6 +448,8 @@ static int add_new_master_key(struct super_block *sb,
INIT_LIST_HEAD(&mk->mk_decrypted_inodes);
spin_lock_init(&mk->mk_decrypted_inodes_lock);
+ INIT_LIST_HEAD(&mk->mk_mode_keys);
+
if (mk_spec->type == FSCRYPT_KEY_SPEC_TYPE_IDENTIFIER) {
err = allocate_master_key_users_keyring(mk);
if (err)
diff --git a/fs/crypto/keysetup.c b/fs/crypto/keysetup.c
index ce327bfdada4..f905f9f94bdd 100644
--- a/fs/crypto/keysetup.c
+++ b/fs/crypto/keysetup.c
@@ -163,13 +163,7 @@ int fscrypt_prepare_key(struct fscrypt_prepared_key *prep_key,
tfm = fscrypt_allocate_skcipher(ci->ci_mode, raw_key, ci->ci_inode);
if (IS_ERR(tfm))
return PTR_ERR(tfm);
- /*
- * Pairs with the smp_load_acquire() in fscrypt_is_key_prepared().
- * I.e., here we publish ->tfm with a RELEASE barrier so that
- * concurrent tasks can ACQUIRE it. Note that this concurrency is only
- * possible for per-mode keys, not for per-file keys.
- */
- smp_store_release(&prep_key->tfm, tfm);
+ prep_key->tfm = tfm;
return 0;
}
@@ -190,9 +184,37 @@ int fscrypt_set_per_file_enc_key(struct fscrypt_inode_info *ci,
return fscrypt_prepare_key(&ci->ci_enc_key, raw_key, ci);
}
+/*
+ * Find the fscrypt_prepared_key (if any) for a particular (mk, hkdf_context,
+ * mode_num, data_unit_bits, inlinecrypt) combination.
+ *
+ * The caller must hold ->mk_sem for reading and ->mk_present must be true,
+ * ensuring that ->mk_mode_keys is still append-only.
+ */
+static struct fscrypt_prepared_key *
+fscrypt_find_mode_key(struct fscrypt_master_key *mk, u8 hkdf_context,
+ u8 mode_num, const struct fscrypt_inode_info *ci)
+{
+ struct fscrypt_mode_key *node;
+
+ /*
+ * The RCU read lock here is used only to synchronize with concurrent
+ * list_add_tail_rcu(). Concurrent deletions are impossible here, so
+ * returning a pointer to a node without taking any refcount is safe.
+ */
+ guard(rcu)();
+ list_for_each_entry_rcu(node, &mk->mk_mode_keys, link) {
+ if (node->hkdf_context == hkdf_context &&
+ node->mode_num == mode_num &&
+ node->data_unit_bits == ci->ci_data_unit_bits &&
+ fscrypt_is_key_prepared(&node->key, ci))
+ return &node->key;
+ }
+ return NULL;
+}
+
static int setup_per_mode_enc_key(struct fscrypt_inode_info *ci,
struct fscrypt_master_key *mk,
- struct fscrypt_prepared_key *keys,
u8 hkdf_context, bool include_fs_uuid)
{
const struct inode *inode = ci->ci_inode;
@@ -200,7 +222,8 @@ static int setup_per_mode_enc_key(struct fscrypt_inode_info *ci,
struct fscrypt_mode *mode = ci->ci_mode;
const u8 mode_num = mode - fscrypt_modes;
struct fscrypt_prepared_key *prep_key;
- u8 mode_key[FSCRYPT_MAX_RAW_KEY_SIZE];
+ struct fscrypt_mode_key *new_node;
+ u8 raw_mode_key[FSCRYPT_MAX_RAW_KEY_SIZE];
u8 hkdf_info[sizeof(mode_num) + sizeof(sb->s_uuid)];
unsigned int hkdf_infolen = 0;
bool use_hw_wrapped_key = false;
@@ -223,48 +246,56 @@ static int setup_per_mode_enc_key(struct fscrypt_inode_info *ci,
use_hw_wrapped_key = true;
}
- prep_key = &keys[mode_num];
- if (fscrypt_is_key_prepared(prep_key, ci)) {
+ prep_key = fscrypt_find_mode_key(mk, hkdf_context, mode_num, ci);
+ if (prep_key) {
ci->ci_enc_key = *prep_key;
return 0;
}
- mutex_lock(&fscrypt_mode_key_setup_mutex);
+ guard(mutex)(&fscrypt_mode_key_setup_mutex);
- if (fscrypt_is_key_prepared(prep_key, ci))
- goto done_unlock;
+ prep_key = fscrypt_find_mode_key(mk, hkdf_context, mode_num, ci);
+ if (prep_key) {
+ ci->ci_enc_key = *prep_key;
+ return 0;
+ }
+
+ new_node = kzalloc_obj(*new_node);
+ if (!new_node)
+ return -ENOMEM;
+ new_node->hkdf_context = hkdf_context;
+ new_node->mode_num = mode_num;
+ new_node->data_unit_bits = ci->ci_data_unit_bits;
+ prep_key = &new_node->key;
if (use_hw_wrapped_key) {
err = fscrypt_prepare_inline_crypt_key(prep_key,
mk->mk_secret.bytes,
mk->mk_secret.size, true,
ci);
- if (err)
- goto out_unlock;
- goto done_unlock;
+ } else {
+ static_assert(sizeof(mode_num) == 1);
+ static_assert(sizeof(sb->s_uuid) == 16);
+ static_assert(sizeof(hkdf_info) == 17);
+ hkdf_info[hkdf_infolen++] = mode_num;
+ if (include_fs_uuid) {
+ memcpy(&hkdf_info[hkdf_infolen], &sb->s_uuid,
+ sizeof(sb->s_uuid));
+ hkdf_infolen += sizeof(sb->s_uuid);
+ }
+ fscrypt_hkdf_expand(&mk->mk_secret.hkdf, hkdf_context,
+ hkdf_info, hkdf_infolen, raw_mode_key,
+ mode->keysize);
+ err = fscrypt_prepare_key(prep_key, raw_mode_key, ci);
+ memzero_explicit(raw_mode_key, mode->keysize);
}
-
- BUILD_BUG_ON(sizeof(mode_num) != 1);
- BUILD_BUG_ON(sizeof(sb->s_uuid) != 16);
- BUILD_BUG_ON(sizeof(hkdf_info) != 17);
- hkdf_info[hkdf_infolen++] = mode_num;
- if (include_fs_uuid) {
- memcpy(&hkdf_info[hkdf_infolen], &sb->s_uuid,
- sizeof(sb->s_uuid));
- hkdf_infolen += sizeof(sb->s_uuid);
+ if (err) {
+ kfree(new_node);
+ return err;
}
- fscrypt_hkdf_expand(&mk->mk_secret.hkdf, hkdf_context, hkdf_info,
- hkdf_infolen, mode_key, mode->keysize);
- err = fscrypt_prepare_key(prep_key, mode_key, ci);
- memzero_explicit(mode_key, mode->keysize);
- if (err)
- goto out_unlock;
-done_unlock:
+ list_add_tail_rcu(&new_node->link, &mk->mk_mode_keys);
ci->ci_enc_key = *prep_key;
- err = 0;
-out_unlock:
- mutex_unlock(&fscrypt_mode_key_setup_mutex);
- return err;
+ return 0;
}
/*
@@ -311,8 +342,8 @@ static int fscrypt_setup_iv_ino_lblk_32_key(struct fscrypt_inode_info *ci,
{
int err;
- err = setup_per_mode_enc_key(ci, mk, mk->mk_iv_ino_lblk_32_keys,
- HKDF_CONTEXT_IV_INO_LBLK_32_KEY, true);
+ err = setup_per_mode_enc_key(ci, mk, HKDF_CONTEXT_IV_INO_LBLK_32_KEY,
+ true);
if (err)
return err;
@@ -364,8 +395,8 @@ static int fscrypt_setup_v2_file_key(struct fscrypt_inode_info *ci,
* encryption key. This ensures that the master key is
* consistently used only for HKDF, avoiding key reuse issues.
*/
- err = setup_per_mode_enc_key(ci, mk, mk->mk_direct_keys,
- HKDF_CONTEXT_DIRECT_KEY, false);
+ err = setup_per_mode_enc_key(ci, mk, HKDF_CONTEXT_DIRECT_KEY,
+ false);
} else if (ci->ci_policy.v2.flags &
FSCRYPT_POLICY_FLAG_IV_INO_LBLK_64) {
/*
@@ -374,9 +405,8 @@ static int fscrypt_setup_v2_file_key(struct fscrypt_inode_info *ci,
* the IVs. This format is optimized for use with inline
* encryption hardware compliant with the UFS standard.
*/
- err = setup_per_mode_enc_key(ci, mk, mk->mk_iv_ino_lblk_64_keys,
- HKDF_CONTEXT_IV_INO_LBLK_64_KEY,
- true);
+ err = setup_per_mode_enc_key(
+ ci, mk, HKDF_CONTEXT_IV_INO_LBLK_64_KEY, true);
} else if (ci->ci_policy.v2.flags &
FSCRYPT_POLICY_FLAG_IV_INO_LBLK_32) {
err = fscrypt_setup_iv_ino_lblk_32_key(ci, mk);
diff --git a/fs/exfat/dir.c b/fs/exfat/dir.c
index ac008ccaa97d..561fd2349218 100644
--- a/fs/exfat/dir.c
+++ b/fs/exfat/dir.c
@@ -1027,12 +1027,12 @@ rewind:
continue;
}
- brelse(bh);
if (entry_type == TYPE_EXTEND) {
unsigned short entry_uniname[16], unichar;
if (step != DIRENT_STEP_NAME ||
name_len >= MAX_NAME_LENGTH) {
+ brelse(bh);
step = DIRENT_STEP_FILE;
continue;
}
@@ -1043,6 +1043,7 @@ rewind:
uniname += EXFAT_FILE_NAME_LEN;
len = exfat_extract_uni_name(ep, entry_uniname);
+ brelse(bh);
name_len += len;
unichar = *(uniname+len);
@@ -1061,6 +1062,7 @@ rewind:
continue;
}
+ brelse(bh);
if (entry_type &
(TYPE_CRITICAL_SEC | TYPE_BENIGN_SEC)) {
if (step == DIRENT_STEP_SECD) {
diff --git a/fs/f2fs/acl.c b/fs/f2fs/acl.c
index fa8d81a30fb9..d3253549173e 100644
--- a/fs/f2fs/acl.c
+++ b/fs/f2fs/acl.c
@@ -47,6 +47,7 @@ static inline int f2fs_acl_count(size_t size)
static struct posix_acl *f2fs_acl_from_disk(const char *value, size_t size)
{
int i, count;
+ int err = -EINVAL;
struct posix_acl *acl;
struct f2fs_acl_header *hdr = (struct f2fs_acl_header *)value;
struct f2fs_acl_entry *entry = (struct f2fs_acl_entry *)(hdr + 1);
@@ -70,8 +71,11 @@ static struct posix_acl *f2fs_acl_from_disk(const char *value, size_t size)
for (i = 0; i < count; i++) {
- if ((char *)entry > end)
+ if (unlikely((char *)entry +
+ sizeof(struct f2fs_acl_entry_short) > end)) {
+ err = -EFSCORRUPTED;
goto fail;
+ }
acl->a_entries[i].e_tag = le16_to_cpu(entry->e_tag);
acl->a_entries[i].e_perm = le16_to_cpu(entry->e_perm);
@@ -86,6 +90,11 @@ static struct posix_acl *f2fs_acl_from_disk(const char *value, size_t size)
break;
case ACL_USER:
+ if (unlikely((char *)entry +
+ sizeof(struct f2fs_acl_entry) > end)) {
+ err = -EFSCORRUPTED;
+ goto fail;
+ }
acl->a_entries[i].e_uid =
make_kuid(&init_user_ns,
le32_to_cpu(entry->e_id));
@@ -93,6 +102,11 @@ static struct posix_acl *f2fs_acl_from_disk(const char *value, size_t size)
sizeof(struct f2fs_acl_entry));
break;
case ACL_GROUP:
+ if (unlikely((char *)entry +
+ sizeof(struct f2fs_acl_entry) > end)) {
+ err = -EFSCORRUPTED;
+ goto fail;
+ }
acl->a_entries[i].e_gid =
make_kgid(&init_user_ns,
le32_to_cpu(entry->e_id));
@@ -108,7 +122,7 @@ static struct posix_acl *f2fs_acl_from_disk(const char *value, size_t size)
return acl;
fail:
posix_acl_release(acl);
- return ERR_PTR(-EINVAL);
+ return ERR_PTR(err);
}
static void *f2fs_acl_to_disk(struct f2fs_sb_info *sbi,
diff --git a/fs/f2fs/checkpoint.c b/fs/f2fs/checkpoint.c
index c00a6b6ebcbd..064f5b537423 100644
--- a/fs/f2fs/checkpoint.c
+++ b/fs/f2fs/checkpoint.c
@@ -943,6 +943,7 @@ int f2fs_recover_orphan_inodes(struct f2fs_sb_info *sbi)
for (i = 0; i < orphan_blocks; i++) {
struct folio *folio;
struct f2fs_orphan_block *orphan_blk;
+ unsigned int entry_count;
folio = f2fs_get_meta_folio(sbi, start_blk + i);
if (IS_ERR(folio)) {
@@ -951,7 +952,18 @@ int f2fs_recover_orphan_inodes(struct f2fs_sb_info *sbi)
}
orphan_blk = folio_address(folio);
- for (j = 0; j < le32_to_cpu(orphan_blk->entry_count); j++) {
+ entry_count = le32_to_cpu(orphan_blk->entry_count);
+ if (entry_count > F2FS_ORPHANS_PER_BLOCK) {
+ f2fs_err(sbi, "invalid orphan inode entry count %u",
+ entry_count);
+ set_sbi_flag(sbi, SBI_NEED_FSCK);
+ f2fs_handle_error(sbi, ERROR_INCONSISTENT_ORPHAN);
+ err = -EFSCORRUPTED;
+ f2fs_folio_put(folio, true);
+ goto out;
+ }
+
+ for (j = 0; j < entry_count; j++) {
nid_t ino = le32_to_cpu(orphan_blk->ino[j]);
err = recover_orphan_inode(sbi, ino);
diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c
index 8d4f1e75dee3..e24b74859427 100644
--- a/fs/f2fs/data.c
+++ b/fs/f2fs/data.c
@@ -2495,7 +2495,7 @@ static int f2fs_read_data_large_folio(struct inode *inode,
unsigned nrpages;
struct f2fs_folio_state *ffs;
int ret = 0;
- bool folio_in_bio;
+ bool folio_in_bio = false;
if (!IS_IMMUTABLE(inode) || f2fs_compressed_file(inode)) {
if (folio)
@@ -2611,18 +2611,17 @@ submit_and_realloc:
}
trace_f2fs_read_folio(folio, DATA);
err_out:
- if (!folio_in_bio) {
+ if (!folio_in_bio)
folio_end_read(folio, !ret);
- if (ret)
- return ret;
- }
+ if (ret)
+ goto out;
if (rac) {
folio = readahead_folio(rac);
goto next_folio;
}
out:
f2fs_submit_read_bio(F2FS_I_SB(inode), bio, DATA);
- if (ret) {
+ if (ret && folio_in_bio) {
/* Wait bios and clear uptodate. */
folio_lock(folio);
folio_clear_uptodate(folio);
@@ -3822,13 +3821,14 @@ unlock_out:
static int prepare_atomic_write_begin(struct f2fs_sb_info *sbi,
struct folio *folio, loff_t pos, unsigned int len,
- block_t *blk_addr, bool *node_changed, bool *use_cow)
+ block_t *blk_addr, bool *node_changed)
{
struct inode *inode = folio->mapping->host;
struct inode *cow_inode = F2FS_I(inode)->cow_inode;
pgoff_t index = folio->index;
int err = 0;
block_t ori_blk_addr = NULL_ADDR;
+ bool cow_has_reserved_block = false;
/* If pos is beyond the end of file, reserve a new block in COW inode */
if ((pos & PAGE_MASK) >= i_size_read(inode))
@@ -3836,12 +3836,14 @@ static int prepare_atomic_write_begin(struct f2fs_sb_info *sbi,
/* Look for the block in COW inode first */
err = __find_data_block(cow_inode, index, blk_addr);
- if (err) {
+ if (err)
return err;
- } else if (*blk_addr != NULL_ADDR) {
- *use_cow = true;
+
+ if (__is_valid_data_blkaddr(*blk_addr))
return 0;
- }
+
+ if (*blk_addr == NEW_ADDR)
+ cow_has_reserved_block = true;
if (is_inode_flag_set(inode, FI_ATOMIC_REPLACE))
goto reserve_block;
@@ -3853,10 +3855,13 @@ static int prepare_atomic_write_begin(struct f2fs_sb_info *sbi,
reserve_block:
/* Finally, we should reserve a new block in COW inode for the update */
- err = __reserve_data_block(cow_inode, index, blk_addr, node_changed);
- if (err)
- return err;
- inc_atomic_write_cnt(inode);
+ if (!cow_has_reserved_block) {
+ err = __reserve_data_block(cow_inode, index, blk_addr,
+ node_changed);
+ if (err)
+ return err;
+ inc_atomic_write_cnt(inode);
+ }
if (ori_blk_addr != NULL_ADDR)
*blk_addr = ori_blk_addr;
@@ -3873,7 +3878,6 @@ static int f2fs_write_begin(const struct kiocb *iocb,
struct folio *folio;
pgoff_t index = pos >> PAGE_SHIFT;
bool need_balance = false;
- bool use_cow = false;
block_t blkaddr = NULL_ADDR;
int err = 0;
@@ -3936,7 +3940,7 @@ repeat:
if (f2fs_is_atomic_file(inode))
err = prepare_atomic_write_begin(sbi, folio, pos, len,
- &blkaddr, &need_balance, &use_cow);
+ &blkaddr, &need_balance);
else
err = prepare_write_begin(sbi, folio, pos, len,
&blkaddr, &need_balance);
@@ -3976,8 +3980,15 @@ repeat:
err = -EFSCORRUPTED;
goto put_folio;
}
- f2fs_submit_page_read(use_cow ? F2FS_I(inode)->cow_inode :
- inode,
+ /*
+ * Although the block may be stored in the COW inode, the folio
+ * belongs to @inode and its data was encrypted (or not) using
+ * @inode's context (see f2fs_encrypt_one_page()). Read with
+ * @inode so the post-read decryption decision matches the
+ * folio's owner; otherwise an unencrypted @inode whose COW inode
+ * is encrypted hits a NULL ->i_crypt_info on decryption.
+ */
+ f2fs_submit_page_read(inode,
NULL, /* can't write to fsverity files */
folio, blkaddr, 0, true);
diff --git a/fs/f2fs/extent_cache.c b/fs/f2fs/extent_cache.c
index d2e006420f04..61f6b9714366 100644
--- a/fs/f2fs/extent_cache.c
+++ b/fs/f2fs/extent_cache.c
@@ -119,10 +119,9 @@ static bool __may_extent_tree(struct inode *inode, enum extent_type type)
if (!__init_may_extent_tree(inode, type))
return false;
- if (is_inode_flag_set(inode, FI_NO_EXTENT))
- return false;
-
if (type == EX_READ) {
+ if (is_inode_flag_set(inode, FI_NO_EXTENT))
+ return false;
if (is_inode_flag_set(inode, FI_COMPRESSED_FILE) &&
!f2fs_sb_has_readonly(F2FS_I_SB(inode)))
return false;
@@ -645,14 +644,10 @@ static unsigned int __destroy_extent_node(struct inode *inode,
while (atomic_read(&et->node_cnt)) {
write_lock(&et->lock);
- if (!is_inode_flag_set(inode, FI_NO_EXTENT))
- set_inode_flag(inode, FI_NO_EXTENT);
node_cnt += __free_extent_tree(sbi, et, nr_shrink);
write_unlock(&et->lock);
}
- f2fs_bug_on(sbi, atomic_read(&et->node_cnt));
-
return node_cnt;
}
@@ -691,12 +686,12 @@ static void __update_extent_tree_range(struct inode *inode,
write_lock(&et->lock);
- if (is_inode_flag_set(inode, FI_NO_EXTENT)) {
- write_unlock(&et->lock);
- return;
- }
-
if (type == EX_READ) {
+ if (is_inode_flag_set(inode, FI_NO_EXTENT)) {
+ write_unlock(&et->lock);
+ return;
+ }
+
prev = et->largest;
dei.len = 0;
diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h
index 91f506e7c9cf..069595fc5e1a 100644
--- a/fs/f2fs/f2fs.h
+++ b/fs/f2fs/f2fs.h
@@ -1583,6 +1583,7 @@ enum node_type {
NODE_TYPE_INODE,
NODE_TYPE_XATTR,
NODE_TYPE_NON_INODE,
+ NODE_TYPE_NON_IXNODE, /* non inode and xnode */
};
/* a threshold of maximum elapsed time in critical region to print tracepoint */
diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c
index fb12c5c9affd..ade4fde2ef0e 100644
--- a/fs/f2fs/file.c
+++ b/fs/f2fs/file.c
@@ -1098,6 +1098,8 @@ int f2fs_setattr(struct mnt_idmap *idmap, struct dentry *dentry,
return -EPERM;
if ((attr->ia_valid & ATTR_SIZE)) {
+ if (mapping_large_folio_support(inode->i_mapping))
+ return -EOPNOTSUPP;
if (!f2fs_is_compress_backend_ready(inode) ||
IS_DEVICE_ALIASING(inode))
return -EOPNOTSUPP;
@@ -1914,8 +1916,15 @@ static int f2fs_expand_inode_data(struct inode *inode, loff_t offset,
if (f2fs_is_pinned_file(inode)) {
block_t sec_blks = CAP_BLKS_PER_SEC(sbi);
- block_t sec_len = roundup(map.m_len, sec_blks);
+ block_t sec_len;
+ if (map.m_lblk % sec_blks) {
+ map.m_lblk = rounddown(map.m_lblk, sec_blks);
+ map.m_len = pg_end - map.m_lblk;
+ if (off_end)
+ map.m_len++;
+ }
+ sec_len = roundup(map.m_len, sec_blks);
map.m_len = sec_blks;
next_alloc:
f2fs_down_write(&sbi->pin_sem);
diff --git a/fs/f2fs/gc.c b/fs/f2fs/gc.c
index ba93010924c0..11228349b003 100644
--- a/fs/f2fs/gc.c
+++ b/fs/f2fs/gc.c
@@ -1220,8 +1220,8 @@ static bool is_alive(struct f2fs_sb_info *sbi, struct f2fs_summary *sum,
static int ra_data_block(struct inode *inode, pgoff_t index)
{
struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
- struct address_space *mapping = f2fs_is_cow_file(inode) ?
- F2FS_I(inode)->atomic_inode->i_mapping : inode->i_mapping;
+ struct address_space *mapping = inode->i_mapping;
+ struct inode *atomic_inode = NULL;
struct dnode_of_data dn;
struct folio *folio, *efolio;
struct f2fs_io_info fio = {
@@ -1236,9 +1236,22 @@ static int ra_data_block(struct inode *inode, pgoff_t index)
};
int err = 0;
+ f2fs_down_read(&F2FS_I(inode)->i_sem);
+ if (f2fs_is_cow_file(inode)) {
+ atomic_inode = igrab(F2FS_I(inode)->atomic_inode);
+ if (!atomic_inode) {
+ f2fs_up_read(&F2FS_I(inode)->i_sem);
+ return -EBUSY;
+ }
+ mapping = atomic_inode->i_mapping;
+ }
+ f2fs_up_read(&F2FS_I(inode)->i_sem);
+
folio = f2fs_grab_cache_folio(mapping, index, true);
- if (IS_ERR(folio))
- return PTR_ERR(folio);
+ if (IS_ERR(folio)) {
+ err = PTR_ERR(folio);
+ goto out_iput;
+ }
if (f2fs_lookup_read_extent_cache_block(inode, index,
&dn.data_blkaddr)) {
@@ -1299,11 +1312,16 @@ got_it:
f2fs_update_iostat(sbi, inode, FS_DATA_READ_IO, F2FS_BLKSIZE);
f2fs_update_iostat(sbi, NULL, FS_GDATA_READ_IO, F2FS_BLKSIZE);
+ if (atomic_inode)
+ iput(atomic_inode);
return 0;
put_encrypted_page:
f2fs_put_page(fio.encrypted_page, true);
put_folio:
f2fs_folio_put(folio, true);
+out_iput:
+ if (atomic_inode)
+ iput(atomic_inode);
return err;
}
@@ -1314,8 +1332,8 @@ put_folio:
static int move_data_block(struct inode *inode, block_t bidx,
int gc_type, unsigned int segno, int off)
{
- struct address_space *mapping = f2fs_is_cow_file(inode) ?
- F2FS_I(inode)->atomic_inode->i_mapping : inode->i_mapping;
+ struct address_space *mapping = inode->i_mapping;
+ struct inode *atomic_inode = NULL;
struct f2fs_io_info fio = {
.sbi = F2FS_I_SB(inode),
.ino = inode->i_ino,
@@ -1337,10 +1355,23 @@ static int move_data_block(struct inode *inode, block_t bidx,
(fio.sbi->gc_mode != GC_URGENT_HIGH) ?
CURSEG_ALL_DATA_ATGC : CURSEG_COLD_DATA;
+ f2fs_down_read(&F2FS_I(inode)->i_sem);
+ if (f2fs_is_cow_file(inode)) {
+ atomic_inode = igrab(F2FS_I(inode)->atomic_inode);
+ if (!atomic_inode) {
+ f2fs_up_read(&F2FS_I(inode)->i_sem);
+ return -EBUSY;
+ }
+ mapping = atomic_inode->i_mapping;
+ }
+ f2fs_up_read(&F2FS_I(inode)->i_sem);
+
/* do not read out */
folio = f2fs_grab_cache_folio(mapping, bidx, false);
- if (IS_ERR(folio))
- return PTR_ERR(folio);
+ if (IS_ERR(folio)) {
+ err = PTR_ERR(folio);
+ goto out_iput;
+ }
if (!check_valid_map(F2FS_I_SB(inode), segno, off)) {
err = -ENOENT;
@@ -1468,11 +1499,10 @@ up_out:
put_out:
f2fs_put_dnode(&dn);
out:
- if (!folio_test_uptodate(folio))
- __folio_set_dropbehind(folio);
- folio_unlock(folio);
- folio_end_dropbehind(folio);
- folio_put(folio);
+ f2fs_folio_put(folio, true);
+out_iput:
+ if (atomic_inode)
+ iput(atomic_inode);
return err;
}
diff --git a/fs/f2fs/inode.c b/fs/f2fs/inode.c
index c6dcda447882..5f0cf81e49f0 100644
--- a/fs/f2fs/inode.c
+++ b/fs/f2fs/inode.c
@@ -324,9 +324,9 @@ static bool sanity_check_inode(struct inode *inode, struct folio *node_folio)
}
if (f2fs_sb_has_flexible_inline_xattr(sbi) &&
- f2fs_has_inline_xattr(inode) &&
- (fi->i_inline_xattr_size < MIN_INLINE_XATTR_SIZE ||
- fi->i_inline_xattr_size > MAX_INLINE_XATTR_SIZE)) {
+ (fi->i_inline_xattr_size > MAX_INLINE_XATTR_SIZE ||
+ (f2fs_has_inline_xattr(inode) &&
+ fi->i_inline_xattr_size < MIN_INLINE_XATTR_SIZE))) {
f2fs_warn(sbi, "%s: inode (ino=%llx) has corrupted i_inline_xattr_size: %d, min: %zu, max: %lu",
__func__, inode->i_ino, fi->i_inline_xattr_size,
MIN_INLINE_XATTR_SIZE, MAX_INLINE_XATTR_SIZE);
@@ -561,8 +561,13 @@ static int do_read_inode(struct inode *inode)
static bool is_meta_ino(struct f2fs_sb_info *sbi, unsigned int ino)
{
- return ino == F2FS_NODE_INO(sbi) || ino == F2FS_META_INO(sbi) ||
- ino == F2FS_COMPRESS_INO(sbi);
+ if (ino == F2FS_NODE_INO(sbi) || ino == F2FS_META_INO(sbi))
+ return true;
+#ifdef CONFIG_F2FS_FS_COMPRESSION
+ if (test_opt(sbi, COMPRESS_CACHE) && ino == F2FS_COMPRESS_INO(sbi))
+ return true;
+#endif
+ return false;
}
struct inode *f2fs_iget(struct super_block *sb, unsigned long ino)
@@ -858,10 +863,15 @@ void f2fs_evict_inode(struct inode *inode)
f2fs_abort_atomic_write(inode, true);
if (fi->cow_inode && f2fs_is_cow_file(fi->cow_inode)) {
- clear_inode_flag(fi->cow_inode, FI_COW_FILE);
- F2FS_I(fi->cow_inode)->atomic_inode = NULL;
- iput(fi->cow_inode);
+ struct inode *cow_inode = fi->cow_inode;
+
+ f2fs_down_write(&F2FS_I(cow_inode)->i_sem);
+ clear_inode_flag(cow_inode, FI_COW_FILE);
+ F2FS_I(cow_inode)->atomic_inode = NULL;
fi->cow_inode = NULL;
+ f2fs_up_write(&F2FS_I(cow_inode)->i_sem);
+
+ iput(cow_inode);
}
trace_f2fs_evict_inode(inode);
diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c
index 4e5bd9e4cfc3..01d860e7e169 100644
--- a/fs/f2fs/node.c
+++ b/fs/f2fs/node.c
@@ -1541,6 +1541,10 @@ int f2fs_sanity_check_node_footer(struct f2fs_sb_info *sbi,
if (is_inode)
goto out_err;
break;
+ case NODE_TYPE_NON_IXNODE:
+ if (is_inode || is_xnode)
+ goto out_err;
+ break;
default:
break;
}
@@ -1634,7 +1638,7 @@ static struct folio *f2fs_get_node_folio_ra(struct folio *parent, int start)
struct f2fs_sb_info *sbi = F2FS_F_SB(parent);
nid_t nid = get_nid(parent, start, false);
- return __get_node_folio(sbi, nid, parent, start, NODE_TYPE_REGULAR);
+ return __get_node_folio(sbi, nid, parent, start, NODE_TYPE_NON_IXNODE);
}
static void flush_inline_data(struct f2fs_sb_info *sbi, nid_t ino)
@@ -1875,7 +1879,7 @@ int f2fs_write_single_node_folio(struct folio *node_folio, int sync_mode,
}
if (!__write_node_folio(node_folio, false, false, NULL,
- &wbc, false, FS_GC_NODE_IO, NULL))
+ &wbc, false, io_type, NULL))
err = -EAGAIN;
goto release_folio;
out_folio:
diff --git a/fs/gfs2/super.c b/fs/gfs2/super.c
index a2ea121331f1..4d854556b529 100644
--- a/fs/gfs2/super.c
+++ b/fs/gfs2/super.c
@@ -643,6 +643,7 @@ restart:
gfs2_delete_debugfs_file(sdp);
gfs2_sys_fs_del(sdp);
+ rcu_barrier();
free_sbd(sdp);
}
diff --git a/fs/nfs/client.c b/fs/nfs/client.c
index be02bb227741..0781b15e7e05 100644
--- a/fs/nfs/client.c
+++ b/fs/nfs/client.c
@@ -1074,6 +1074,7 @@ struct nfs_server *nfs_alloc_server(void)
server->io_stats = nfs_alloc_iostats();
if (!server->io_stats) {
+ ida_free(&s_sysfs_ids, server->s_sysfs_id);
kfree(server);
return NULL;
}
diff --git a/fs/nfs/flexfilelayout/flexfilelayout.c b/fs/nfs/flexfilelayout/flexfilelayout.c
index 8b1559171fe3..e22a8e0daf2c 100644
--- a/fs/nfs/flexfilelayout/flexfilelayout.c
+++ b/fs/nfs/flexfilelayout/flexfilelayout.c
@@ -551,6 +551,10 @@ ff_layout_alloc_lseg(struct pnfs_layout_hdr *lh,
if (!p)
goto out_err_free;
fh_count = be32_to_cpup(p);
+ if (fh_count == 0) {
+ rc = -EINVAL;
+ goto out_err_free;
+ }
dss_info->fh_versions =
kzalloc_objs(struct nfs_fh, fh_count, gfp_flags);
diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c
index a9b8d482d289..c354b7b90293 100644
--- a/fs/nfs/nfs4proc.c
+++ b/fs/nfs/nfs4proc.c
@@ -5302,10 +5302,9 @@ static struct dentry *nfs4_proc_mkdir(struct inode *dir, struct dentry *dentry,
do {
alias = _nfs4_proc_mkdir(dir, dentry, sattr, label, &err);
trace_nfs4_mkdir(dir, &dentry->d_name, err);
+ err = nfs4_handle_exception(NFS_SERVER(dir), err, &exception);
if (err)
- alias = ERR_PTR(nfs4_handle_exception(NFS_SERVER(dir),
- err,
- &exception));
+ alias = ERR_PTR(err);
} while (exception.retry);
nfs4_label_release_security(label);
diff --git a/fs/nfs/pnfs.c b/fs/nfs/pnfs.c
index 743467e9ba20..aee523134c0f 100644
--- a/fs/nfs/pnfs.c
+++ b/fs/nfs/pnfs.c
@@ -2229,11 +2229,11 @@ lookup_again:
dprintk("%s wait for layoutreturn\n", __func__);
lseg = ERR_PTR(pnfs_prepare_to_retry_layoutget(lo));
if (!IS_ERR(lseg)) {
- pnfs_put_layout_hdr(lo);
dprintk("%s retrying\n", __func__);
trace_pnfs_update_layout(ino, pos, count, iomode, lo,
lseg,
PNFS_UPDATE_LAYOUT_RETRY);
+ pnfs_put_layout_hdr(lo);
goto lookup_again;
}
trace_pnfs_update_layout(ino, pos, count, iomode, lo, lseg,
diff --git a/fs/nfs/pnfs_nfs.c b/fs/nfs/pnfs_nfs.c
index 12632a706da8..0ff43dbcb7cd 100644
--- a/fs/nfs/pnfs_nfs.c
+++ b/fs/nfs/pnfs_nfs.c
@@ -1075,14 +1075,14 @@ nfs4_decode_mp_ds_addr(struct net *net, struct xdr_stream *xdr, gfp_t gfp_flags)
/* r_netid */
nlen = xdr_stream_decode_string_dup(xdr, &netid, XDR_MAX_NETOBJ,
gfp_flags);
- if (unlikely(nlen < 0))
+ if (unlikely(nlen <= 0))
goto out_err;
/* r_addr: ip/ip6addr with port in dec octets - see RFC 5665 */
/* port is ".ABC.DEF", 8 chars max */
rlen = xdr_stream_decode_string_dup(xdr, &buf, INET6_ADDRSTRLEN +
IPV6_SCOPE_ID_LEN + 8, gfp_flags);
- if (unlikely(rlen < 0))
+ if (unlikely(rlen <= 0))
goto out_free_netid;
/* replace port '.' with '-' */
diff --git a/fs/nfsd/nfs2acl.c b/fs/nfsd/nfs2acl.c
index 0ac538c76180..76305b86c1a9 100644
--- a/fs/nfsd/nfs2acl.c
+++ b/fs/nfsd/nfs2acl.c
@@ -131,10 +131,7 @@ static __be32 nfsacld_proc_setacl(struct svc_rqst *rqstp)
resp->status = fh_getattr(fh, &resp->stat);
out:
- /* argp->acl_{access,default} may have been allocated in
- nfssvc_decode_setaclargs. */
- posix_acl_release(argp->acl_access);
- posix_acl_release(argp->acl_default);
+ /* argp->acl_{access,default} are released in nfsaclsvc_release_setacl. */
return rpc_success;
out_drop_lock:
@@ -310,6 +307,16 @@ static void nfsaclsvc_release_access(struct svc_rqst *rqstp)
fh_put(&resp->fh);
}
+static void nfsaclsvc_release_setacl(struct svc_rqst *rqstp)
+{
+ struct nfsd3_setaclargs *argp = rqstp->rq_argp;
+ struct nfsd_attrstat *resp = rqstp->rq_resp;
+
+ fh_put(&resp->fh);
+ posix_acl_release(argp->acl_access);
+ posix_acl_release(argp->acl_default);
+}
+
#define ST 1 /* status*/
#define AT 21 /* attributes */
#define pAT (1+AT) /* post attributes - conditional */
@@ -343,7 +350,7 @@ static const struct svc_procedure nfsd_acl_procedures2[5] = {
.pc_func = nfsacld_proc_setacl,
.pc_decode = nfsaclsvc_decode_setaclargs,
.pc_encode = nfssvc_encode_attrstatres,
- .pc_release = nfssvc_release_attrstat,
+ .pc_release = nfsaclsvc_release_setacl,
.pc_argsize = sizeof(struct nfsd3_setaclargs),
.pc_argzero = sizeof(struct nfsd3_setaclargs),
.pc_ressize = sizeof(struct nfsd_attrstat),
diff --git a/fs/nfsd/nfs3acl.c b/fs/nfsd/nfs3acl.c
index 7b5433bd3019..e87731380be8 100644
--- a/fs/nfsd/nfs3acl.c
+++ b/fs/nfsd/nfs3acl.c
@@ -118,10 +118,7 @@ out_drop_lock:
out_errno:
resp->status = nfserrno(error);
out:
- /* argp->acl_{access,default} may have been allocated in
- nfs3svc_decode_setaclargs. */
- posix_acl_release(argp->acl_access);
- posix_acl_release(argp->acl_default);
+ /* argp->acl_{access,default} are released in nfs3svc_release_setacl. */
return rpc_success;
}
@@ -223,6 +220,16 @@ static void nfs3svc_release_getacl(struct svc_rqst *rqstp)
posix_acl_release(resp->acl_default);
}
+static void nfs3svc_release_setacl(struct svc_rqst *rqstp)
+{
+ struct nfsd3_setaclargs *argp = rqstp->rq_argp;
+ struct nfsd3_attrstat *resp = rqstp->rq_resp;
+
+ fh_put(&resp->fh);
+ posix_acl_release(argp->acl_access);
+ posix_acl_release(argp->acl_default);
+}
+
#define ST 1 /* status*/
#define AT 21 /* attributes */
#define pAT (1+AT) /* post attributes - conditional */
@@ -256,7 +263,7 @@ static const struct svc_procedure nfsd_acl_procedures3[3] = {
.pc_func = nfsd3_proc_setacl,
.pc_decode = nfs3svc_decode_setaclargs,
.pc_encode = nfs3svc_encode_setaclres,
- .pc_release = nfs3svc_release_fhandle,
+ .pc_release = nfs3svc_release_setacl,
.pc_argsize = sizeof(struct nfsd3_setaclargs),
.pc_argzero = sizeof(struct nfsd3_setaclargs),
.pc_ressize = sizeof(struct nfsd3_attrstat),
diff --git a/fs/nfsd/nfs4layouts.c b/fs/nfsd/nfs4layouts.c
index 69e41105efdd..8e2929cffa7c 100644
--- a/fs/nfsd/nfs4layouts.c
+++ b/fs/nfsd/nfs4layouts.c
@@ -264,10 +264,12 @@ nfsd4_alloc_layout_stateid(struct nfsd4_compound_state *cstate,
ls->ls_file = find_any_file(fp);
BUG_ON(!ls->ls_file);
+ ls->ls_fenced = false;
+ ls->ls_fence_delay = 0;
+ INIT_DELAYED_WORK(&ls->ls_fence_work, nfsd4_layout_fence_worker);
+
if (nfsd4_layout_setlease(ls)) {
- nfsd_file_put(ls->ls_file);
- put_nfs4_file(fp);
- kmem_cache_free(nfs4_layout_stateid_cache, ls);
+ nfs4_put_stid(stp);
return NULL;
}
@@ -280,10 +282,6 @@ nfsd4_alloc_layout_stateid(struct nfsd4_compound_state *cstate,
list_add(&ls->ls_perfile, &fp->fi_lo_states);
spin_unlock(&fp->fi_lock);
- ls->ls_fenced = false;
- ls->ls_fence_delay = 0;
- INIT_DELAYED_WORK(&ls->ls_fence_work, nfsd4_layout_fence_worker);
-
trace_nfsd_layoutstate_alloc(&ls->ls_stid.sc_stateid);
return ls;
}
diff --git a/fs/nfsd/nfs4proc.c b/fs/nfsd/nfs4proc.c
index ab39ec885440..8561540ab2db 100644
--- a/fs/nfsd/nfs4proc.c
+++ b/fs/nfsd/nfs4proc.c
@@ -253,7 +253,7 @@ nfsd4_create_file(struct svc_rqst *rqstp, struct svc_fh *fhp,
.na_iattr = iap,
.na_seclabel = &open->op_label,
};
- struct dentry *parent, *child;
+ struct dentry *parent, *child = ERR_PTR(-EINVAL);
__u32 v_mtime, v_atime;
struct inode *inode;
__be32 status;
@@ -277,10 +277,14 @@ nfsd4_create_file(struct svc_rqst *rqstp, struct svc_fh *fhp,
if (open->op_acl) {
if (open->op_dpacl || open->op_pacl) {
status = nfserr_inval;
- goto out_write;
+ goto out;
+ }
+ if (is_create_with_attrs(open)) {
+ status = nfsd4_acl_to_attr(NF4REG, open->op_acl,
+ &attrs);
+ if (status)
+ goto out;
}
- if (is_create_with_attrs(open))
- nfsd4_acl_to_attr(NF4REG, open->op_acl, &attrs);
} else if (is_create_with_attrs(open)) {
/* The dpacl and pacl will get released by nfsd_attrs_free(). */
attrs.na_dpacl = open->op_dpacl;
@@ -293,7 +297,7 @@ nfsd4_create_file(struct svc_rqst *rqstp, struct svc_fh *fhp,
&QSTR_LEN(open->op_fname, open->op_fnamelen));
if (IS_ERR(child)) {
status = nfserrno(PTR_ERR(child));
- goto out_write;
+ goto out;
}
if (d_really_is_negative(child)) {
@@ -407,7 +411,6 @@ set_attr:
out:
end_creating(child);
nfsd_attrs_free(&attrs);
-out_write:
fh_drop_write(fhp);
return status;
}
@@ -837,7 +840,7 @@ nfsd4_create(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
goto out_aftermask;
if (create->cr_acl) {
- if (create->cr_dpacl || create->cr_pacl) {
+ if (attrs.na_dpacl || attrs.na_pacl) {
status = nfserr_inval;
goto out_aftermask;
}
@@ -1470,7 +1473,7 @@ void nfsd4_async_copy_reaper(struct nfsd_net *nn)
list_for_each_safe(pos, next, &clp->async_copies) {
copy = list_entry(pos, struct nfsd4_copy, copies);
if (test_bit(NFSD4_COPY_F_OFFLOAD_DONE, &copy->cp_flags)) {
- if (--copy->cp_ttl) {
+ if (!--copy->cp_ttl) {
list_del_init(&copy->copies);
list_add(&copy->copies, &reaplist);
}
diff --git a/fs/nfsd/nfs4recover.c b/fs/nfsd/nfs4recover.c
index b338473d6e52..6ea25a52d2f4 100644
--- a/fs/nfsd/nfs4recover.c
+++ b/fs/nfsd/nfs4recover.c
@@ -718,7 +718,8 @@ __cld_pipe_inprogress_downcall(const struct cld_msg_v2 __user *cmsg,
return PTR_ERR(namecopy);
name.data = namecopy;
name.len = namelen;
- get_user(princhashlen, &ci->cc_princhash.cp_len);
+ if (get_user(princhashlen, &ci->cc_princhash.cp_len))
+ return -EFAULT;
if (princhashlen > 0) {
princhashcopy = memdup_user(
&ci->cc_princhash.cp_data,
diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index 6837b63d9864..d875e98d4dcb 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -5169,6 +5169,7 @@ retry:
/* Replace unconfirmed owners without checking for replay. */
release_openowner(oo);
oo = NULL;
+ goto retry;
}
if (oo) {
if (new)
diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c
index 2a0946c630e1..2da83053fa10 100644
--- a/fs/nfsd/nfs4xdr.c
+++ b/fs/nfsd/nfs4xdr.c
@@ -2008,10 +2008,11 @@ static __be32 nfsd4_decode_secinfo_no_name(struct nfsd4_compoundargs *argp,
union nfsd4_op_u *u)
{
struct nfsd4_secinfo_no_name *sin = &u->secinfo_no_name;
+
+ sin->sin_exp = NULL;
if (xdr_stream_decode_u32(argp->xdr, &sin->sin_style) < 0)
return nfserr_bad_xdr;
- sin->sin_exp = NULL;
return nfs_ok;
}
diff --git a/fs/nfsd/vfs.c b/fs/nfsd/vfs.c
index eafdf7b7890f..2c557a998961 100644
--- a/fs/nfsd/vfs.c
+++ b/fs/nfsd/vfs.c
@@ -1508,8 +1508,10 @@ nfsd_vfs_write(struct svc_rqst *rqstp, struct svc_fh *fhp,
nfsd_stats_io_write_add(nn, exp, *cnt);
fsnotify_modify(file);
host_err = filemap_check_wb_err(file->f_mapping, since);
- if (host_err < 0)
+ if (host_err < 0) {
+ commit_reset_write_verifier(nn, rqstp, host_err);
goto out_nfserr;
+ }
if (stable && fhp->fh_use_wgather) {
host_err = wait_for_concurrent_writes(file);
@@ -1689,6 +1691,8 @@ nfsd_commit(struct svc_rqst *rqstp, struct svc_fh *fhp, struct nfsd_file *nf,
nfsd_copy_write_verifier(verf, nn);
err2 = filemap_check_wb_err(nf->nf_file->f_mapping,
since);
+ if (err2 < 0)
+ commit_reset_write_verifier(nn, rqstp, err2);
err = nfserrno(err2);
break;
case -EINVAL:
diff --git a/fs/ntfs/file.c b/fs/ntfs/file.c
index e8bea22b81a7..264cf8404385 100644
--- a/fs/ntfs/file.c
+++ b/fs/ntfs/file.c
@@ -707,12 +707,21 @@ static int ntfs_ioctl_get_volume_label(struct file *filp, unsigned long arg)
{
struct ntfs_volume *vol = NTFS_SB(file_inode(filp)->i_sb);
char __user *buf = (char __user *)arg;
+ char label[FSLABEL_MAX];
+ ssize_t len;
+ mutex_lock(&vol->volume_label_lock);
if (!vol->volume_label) {
- if (copy_to_user(buf, "", 1))
- return -EFAULT;
- } else if (copy_to_user(buf, vol->volume_label,
- MIN(FSLABEL_MAX, strlen(vol->volume_label) + 1)))
+ label[0] = '\0';
+ len = 0;
+ } else {
+ len = strscpy(label, vol->volume_label, sizeof(label));
+ if (len == -E2BIG)
+ len = FSLABEL_MAX - 1;
+ }
+ mutex_unlock(&vol->volume_label_lock);
+
+ if (copy_to_user(buf, label, len + 1))
return -EFAULT;
return 0;
}
diff --git a/fs/ntfs/super.c b/fs/ntfs/super.c
index 9e321cc2febe..cb880cb6e388 100644
--- a/fs/ntfs/super.c
+++ b/fs/ntfs/super.c
@@ -460,17 +460,23 @@ int ntfs_write_volume_label(struct ntfs_volume *vol, char *label)
ret = ntfs_resident_attr_record_add(vol_ni, AT_VOLUME_NAME, AT_UNNAMED, 0,
(u8 *)uname, uname_len * sizeof(__le16), 0);
out:
- mutex_unlock(&vol_ni->mrec_lock);
- kvfree(uname);
-
if (ret >= 0) {
- kfree(vol->volume_label);
+ char *old_label;
+
+ mutex_lock(&vol->volume_label_lock);
+ old_label = vol->volume_label;
vol->volume_label = new_label;
+ mutex_unlock(&vol->volume_label_lock);
+
+ kfree(old_label);
mark_inode_dirty_sync(vol->vol_ino);
ret = 0;
- } else {
- kfree(new_label);
}
+ mutex_unlock(&vol_ni->mrec_lock);
+ kvfree(uname);
+
+ if (ret < 0)
+ kfree(new_label);
return ret;
}
@@ -2631,6 +2637,7 @@ static int ntfs_init_fs_context(struct fs_context *fc)
NVolSetCaseSensitive(vol);
init_rwsem(&vol->mftbmp_lock);
init_rwsem(&vol->lcnbmp_lock);
+ mutex_init(&vol->volume_label_lock);
fc->s_fs_info = vol;
fc->ops = &ntfs_context_ops;
diff --git a/fs/ntfs/volume.h b/fs/ntfs/volume.h
index af41427ec622..99e642f475f7 100644
--- a/fs/ntfs/volume.h
+++ b/fs/ntfs/volume.h
@@ -72,6 +72,7 @@
* @vol_flags: Volume flags.
* @major_ver: Ntfs major version of volume.
* @minor_ver: Ntfs minor version of volume.
+ * @volume_label_lock: protects @volume_label.
* @volume_label: volume label.
* @root_ino: The VFS inode of the root directory.
* @secure_ino: The VFS inode of $Secure (NTFS3.0+ only, otherwise NULL).
@@ -133,6 +134,7 @@ struct ntfs_volume {
struct inode *logfile_ino;
struct inode *lcnbmp_ino;
struct rw_semaphore lcnbmp_lock;
+ struct mutex volume_label_lock;
struct inode *vol_ino;
__le16 vol_flags;
u8 major_ver;
diff --git a/fs/ntfs3/xattr.c b/fs/ntfs3/xattr.c
index 9eeac0ab2b71..372cf2f2e89b 100644
--- a/fs/ntfs3/xattr.c
+++ b/fs/ntfs3/xattr.c
@@ -851,6 +851,12 @@ out:
return err;
}
+static bool ntfs_is_reserved_lxattr(const char *name)
+{
+ return !strcmp(name, "$LXUID") || !strcmp(name, "$LXGID") ||
+ !strcmp(name, "$LXMOD") || !strcmp(name, "$LXDEV");
+}
+
/*
* ntfs_setxattr - inode_operations::setxattr
*/
@@ -955,6 +961,12 @@ set_new_fa:
goto out;
}
+ /* Do not allow non privileged users to change $LXUID/$LXGID... */
+ if (ntfs_is_reserved_lxattr(name) && !capable(CAP_SYS_ADMIN)) {
+ err = -EPERM;
+ goto out;
+ }
+
/* Deal with NTFS extended attribute. */
err = ntfs_set_ea(inode, name, strlen(name), value, size, flags, 0,
NULL);
diff --git a/fs/ocfs2/suballoc.c b/fs/ocfs2/suballoc.c
index d284e0e37252..a4a2b87a45fe 100644
--- a/fs/ocfs2/suballoc.c
+++ b/fs/ocfs2/suballoc.c
@@ -231,8 +231,16 @@ static int ocfs2_validate_gd_parent(struct super_block *sb,
int resize)
{
unsigned int max_bits;
+ unsigned int max_bitmap_bits;
+ unsigned int max_bitmap_size;
+ int suballocator;
struct ocfs2_group_desc *gd = (struct ocfs2_group_desc *)bh->b_data;
+ suballocator = le64_to_cpu(di->i_blkno) != OCFS2_SB(sb)->bitmap_blkno;
+ max_bitmap_size = ocfs2_group_bitmap_size(sb, suballocator,
+ OCFS2_SB(sb)->s_feature_incompat);
+ max_bitmap_bits = max_bitmap_size * 8;
+
if (di->i_blkno != gd->bg_parent_dinode) {
do_error("Group descriptor #%llu has bad parent pointer (%llu, expected %llu)\n",
(unsigned long long)bh->b_blocknr,
@@ -240,6 +248,20 @@ static int ocfs2_validate_gd_parent(struct super_block *sb,
(unsigned long long)le64_to_cpu(di->i_blkno));
}
+ if (le16_to_cpu(gd->bg_size) > max_bitmap_size) {
+ do_error("Group descriptor #%llu has bitmap size %u but physical max of %u\n",
+ (unsigned long long)bh->b_blocknr,
+ le16_to_cpu(gd->bg_size),
+ max_bitmap_size);
+ }
+
+ if (le16_to_cpu(gd->bg_bits) > max_bitmap_bits) {
+ do_error("Group descriptor #%llu has bit count %u but physical max of %u\n",
+ (unsigned long long)bh->b_blocknr,
+ le16_to_cpu(gd->bg_bits),
+ max_bitmap_bits);
+ }
+
max_bits = le16_to_cpu(di->id2.i_chain.cl_cpg) * le16_to_cpu(di->id2.i_chain.cl_bpc);
if (le16_to_cpu(gd->bg_bits) > max_bits) {
do_error("Group descriptor #%llu has bit count of %u\n",
diff --git a/fs/smb/server/smbacl.c b/fs/smb/server/smbacl.c
index 664b1b4a3233..340ea98fa494 100644
--- a/fs/smb/server/smbacl.c
+++ b/fs/smb/server/smbacl.c
@@ -1477,7 +1477,9 @@ int smb_check_perm_dacl(struct ksmbd_conn *conn, const struct path *path,
break;
aces_size -= ace_size;
- if (ace->sid.num_subauth > SID_MAX_SUB_AUTHORITIES)
+ if (ace->sid.num_subauth > SID_MAX_SUB_AUTHORITIES ||
+ ace_size < offsetof(struct smb_ace, sid) + CIFS_SID_BASE_SIZE +
+ sizeof(__le32) * ace->sid.num_subauth)
break;
if (!compare_sids(&sid, &ace->sid) ||
diff --git a/fs/userfaultfd.c b/fs/userfaultfd.c
index 4b53dc4a3266..390e4b7d9cb9 100644
--- a/fs/userfaultfd.c
+++ b/fs/userfaultfd.c
@@ -786,6 +786,8 @@ void mremap_userfaultfd_fail(struct vm_userfaultfd_ctx *vm_ctx)
if (!ctx)
return;
+ atomic_dec(&ctx->mmap_changing);
+ VM_WARN_ON_ONCE(atomic_read(&ctx->mmap_changing) < 0);
userfaultfd_ctx_put(ctx);
}