summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMark Brown <broonie@kernel.org>2026-10-30 10:54:13 +0000
committerMark Brown <broonie@kernel.org>2026-10-30 10:54:13 +0000
commit2f3fc992562134fbfcb97c23d5caeb14358f019a (patch)
treeef309ac60886b408fc7f0ffa8b207e354a376c8e
parent69c598196a097aceb15ecbd8fef593cc54bc3966 (diff)
parent564c7753647731d19170914c1c637d3b33d88d3b (diff)
downloadlinux-next-2f3fc992562134fbfcb97c23d5caeb14358f019a.tar.gz
linux-next-2f3fc992562134fbfcb97c23d5caeb14358f019a.zip
Merge branch 'ntfs-next' of https://git.kernel.org/pub/scm/linux/kernel/git/linkinjeon/ntfs.git
-rw-r--r--fs/ntfs/index.c41
-rw-r--r--fs/ntfs/index.h1
-rw-r--r--fs/ntfs/inode.c38
-rw-r--r--fs/ntfs/namei.c6
4 files changed, 73 insertions, 13 deletions
diff --git a/fs/ntfs/index.c b/fs/ntfs/index.c
index 66411a712b14..322cb371dbb3 100644
--- a/fs/ntfs/index.c
+++ b/fs/ntfs/index.c
@@ -14,6 +14,8 @@
* Copyright (c) 2007-2021 Jean-Pierre Andre
*/
+#include <linux/overflow.h>
+
#include "collate.h"
#include "index.h"
#include "ntfs.h"
@@ -255,6 +257,32 @@ static s64 ntfs_ie_get_vcn(struct index_entry *ie)
return le64_to_cpup(ntfs_ie_get_vcn_addr(ie));
}
+static int ntfs_index_entry_vcn_inconsistent(const struct ntfs_volume *vol,
+ struct index_entry *ie,
+ u8 vcn_size_bits, u64 inum)
+{
+ s64 vcn, pos;
+
+ if (le16_to_cpu(ie->length) <
+ sizeof(struct index_entry_header) + sizeof(s64)) {
+ ntfs_error(vol->sb,
+ "Index entry with node flag has no VCN in inode %llu.",
+ (unsigned long long)inum);
+ return -EIO;
+ }
+
+ vcn = ntfs_ie_get_vcn(ie);
+ if (vcn < 0 ||
+ check_shl_overflow(vcn, vcn_size_bits, &pos)) {
+ ntfs_error(vol->sb,
+ "Invalid index entry VCN %lld in inode %llu.",
+ vcn, (unsigned long long)inum);
+ return -EIO;
+ }
+
+ return 0;
+}
+
static struct index_entry *ntfs_ie_get_first(struct index_header *ih)
{
return (struct index_entry *)((u8 *)ih + le32_to_cpu(ih->entries_offset));
@@ -326,10 +354,13 @@ static int ntfs_index_header_inconsistent(struct ntfs_volume *vol,
int ntfs_index_entries_inconsistent(const struct ntfs_volume *vol,
const struct index_header *ih,
+ u32 block_size,
__le32 collation_rule, u64 inum)
{
struct index_entry *ie;
u8 *index_end = (u8 *)ih + le32_to_cpu(ih->index_length);
+ u8 vcn_size_bits = vol->cluster_size <= block_size ?
+ vol->cluster_size_bits : NTFS_BLOCK_SIZE_BITS;
for (ie = ntfs_ie_get_first((struct index_header *)ih);
; ie = ntfs_ie_get_next(ie)) {
@@ -348,6 +379,11 @@ int ntfs_index_entries_inconsistent(const struct ntfs_volume *vol,
return -EIO;
}
+ if ((ie->flags & INDEX_ENTRY_NODE) &&
+ ntfs_index_entry_vcn_inconsistent(vol, ie,
+ vcn_size_bits, inum))
+ return -EIO;
+
if (ntfs_ie_end(ie))
break;
@@ -566,7 +602,8 @@ int ntfs_index_block_inconsistent(struct ntfs_volume *vol,
return -EIO;
}
- if (ntfs_index_entries_inconsistent(vol, &ib->index, cr, inum))
+ if (ntfs_index_entries_inconsistent(vol, &ib->index, block_size,
+ cr, inum))
return -EIO;
return 0;
}
@@ -1547,9 +1584,9 @@ static int ntfs_ih_insert(struct index_header *ih, struct index_entry *orig_ie,
ie_node = ntfs_ie_get_by_pos(ih, pos);
old_vcn = ntfs_ie_get_vcn(ie_node);
ntfs_ie_set_vcn(ie_node, new_vcn);
+ ntfs_ie_set_vcn(ie, old_vcn);
ntfs_ie_insert(ih, ie, ie_node);
- ntfs_ie_set_vcn(ie_node, old_vcn);
out:
kfree(ie);
return ret;
diff --git a/fs/ntfs/index.h b/fs/ntfs/index.h
index a898e348f2c9..bc2f1bbdfba4 100644
--- a/fs/ntfs/index.h
+++ b/fs/ntfs/index.h
@@ -98,6 +98,7 @@ int ntfs_index_block_inconsistent(struct ntfs_volume *vol,
__le32 cr, u64 inum);
int ntfs_index_entries_inconsistent(const struct ntfs_volume *vol,
const struct index_header *ih,
+ u32 block_size,
__le32 collation_rule, u64 inum);
struct ntfs_index_context *ntfs_index_ctx_get(struct ntfs_inode *ni, __le16 *name,
u32 name_len);
diff --git a/fs/ntfs/inode.c b/fs/ntfs/inode.c
index 206db4e99ef1..62dd5bd5bb81 100644
--- a/fs/ntfs/inode.c
+++ b/fs/ntfs/inode.c
@@ -1034,6 +1034,7 @@ view_index_meta:
le16_to_cpu(a->data.resident.value_offset));
if (ntfs_index_root_inconsistent(ni->vol, a, ir, ni->mft_no) ||
ntfs_index_entries_inconsistent(ni->vol, &ir->index,
+ le32_to_cpu(ir->index_block_size),
ir->collation_rule, ni->mft_no)) {
ntfs_error(vi->i_sb, "Directory index is corrupt.");
goto unm_err_out;
@@ -1684,6 +1685,7 @@ static int ntfs_read_locked_index_inode(struct inode *base_vi, struct inode *vi)
ir = (struct index_root *)((u8 *)a + le16_to_cpu(a->data.resident.value_offset));
if (ntfs_index_root_inconsistent(vol, a, ir, ni->mft_no) ||
ntfs_index_entries_inconsistent(vol, &ir->index,
+ le32_to_cpu(ir->index_block_size),
ir->collation_rule, ni->mft_no)) {
ntfs_error(vi->i_sb, "Index is corrupt.");
goto unm_err_out;
@@ -2554,8 +2556,12 @@ int ntfs_show_options(struct seq_file *sf, struct dentry *root)
return 0;
}
-int ntfs_extend_initialized_size(struct inode *vi, const loff_t offset,
- const loff_t new_size)
+/*
+ * The caller must hold @vi's mapping invalidate lock.
+ */
+static int __ntfs_extend_initialized_size(struct inode *vi,
+ const loff_t offset,
+ const loff_t new_size)
{
struct ntfs_inode *ni = NTFS_I(vi);
struct ntfs_inode *mrec_ni = ntfs_base_inode(ni);
@@ -2563,10 +2569,11 @@ int ntfs_extend_initialized_size(struct inode *vi, const loff_t offset,
unsigned long flags;
int err = 0;
+ lockdep_assert_held_write(&vi->i_mapping->invalidate_lock);
+
if (!NInoNonResident(ni))
return -EINVAL;
- filemap_invalidate_lock(vi->i_mapping);
read_lock_irqsave(&ni->size_lock, flags);
old_init_size = ni->initialized_size;
read_unlock_irqrestore(&ni->size_lock, flags);
@@ -2599,9 +2606,17 @@ int ntfs_extend_initialized_size(struct inode *vi, const loff_t offset,
mutex_lock(&mrec_ni->mrec_lock);
err = ntfs_attr_set_initialized_size(ni, new_size);
mutex_unlock(&mrec_ni->mrec_lock);
- if (err)
- truncate_setsize(vi, old_init_size);
out:
+ return err;
+}
+
+int ntfs_extend_initialized_size(struct inode *vi, const loff_t offset,
+ const loff_t new_size)
+{
+ int err;
+
+ filemap_invalidate_lock(vi->i_mapping);
+ err = __ntfs_extend_initialized_size(vi, offset, new_size);
filemap_invalidate_unlock(vi->i_mapping);
return err;
}
@@ -3715,11 +3730,11 @@ s64 ntfs_inode_attr_pread(struct inode *vi, s64 pos, s64 count, u8 *buf)
}
}
isize = i_size_read(vi);
- if (pos > isize) {
+ if (pos < 0 || pos > isize) {
mutex_unlock(&mrec_ni->mrec_lock);
return -EINVAL;
}
- if (pos + count > isize)
+ if (count > isize - pos)
count = isize - pos;
if (!NInoNonResident(ni)) {
@@ -4087,8 +4102,13 @@ s64 ntfs_inode_attr_pwrite(struct inode *vi, s64 pos, s64 count, u8 *buf, bool s
mutex_unlock(&mrec_ni->mrec_lock);
/* Attribute writes bypass iomap's delayed-allocation preparation. */
if (ntfs_inode_is_named_stream(ni)) {
- /* A failed write must not expose any newly initialized data. */
- ret = ntfs_extend_initialized_size(vi, pos + count, pos + count);
+ /*
+ * A failed write must not expose any newly initialized
+ * data. The stream ioctl caller holds the mapping
+ * invalidate lock.
+ */
+ ret = __ntfs_extend_initialized_size(vi, pos + count,
+ pos + count);
modified |= ni->initialized_size != old_init_size;
if (!ret)
ret = ntfs_attr_fallocate(ni, pos, count, true);
diff --git a/fs/ntfs/namei.c b/fs/ntfs/namei.c
index 62b01646c262..b46fe44b63b5 100644
--- a/fs/ntfs/namei.c
+++ b/fs/ntfs/namei.c
@@ -472,8 +472,10 @@ static struct ntfs_inode *__ntfs_create(const struct mnt_idmap *idmap, struct in
#ifdef CONFIG_NTFS_FS_POSIX_ACL
if (!S_ISLNK(mode) && (sb->s_flags & SB_POSIXACL)) {
err = ntfs_init_acl(idmap, vi, dir);
- if (err)
- goto err_out;
+ if (err) {
+ iput(vi);
+ return ERR_PTR(err);
+ }
} else
#endif
{