]> git.ipfire.org Git - thirdparty/kernel/stable-queue.git/commitdiff
Fixes for all trees
authorSasha Levin <sashal@kernel.org>
Mon, 13 Jul 2026 20:32:38 +0000 (16:32 -0400)
committerSasha Levin <sashal@kernel.org>
Mon, 13 Jul 2026 20:32:38 +0000 (16:32 -0400)
Signed-off-by: Sasha Levin <sashal@kernel.org>
21 files changed:
queue-5.10/net-drop-the-lock-in-skb_may_tx_timestamp.patch [new file with mode: 0644]
queue-5.10/series
queue-5.15/net-drop-the-lock-in-skb_may_tx_timestamp.patch [new file with mode: 0644]
queue-5.15/series
queue-6.1/net-drop-the-lock-in-skb_may_tx_timestamp.patch [new file with mode: 0644]
queue-6.1/series
queue-6.12/fs-ntfs3-fix-missing-run-load-for-vcn0-in-attr_data_.patch [new file with mode: 0644]
queue-6.12/fs-ntfs3-fsync-files-by-syncing-parent-inodes.patch [new file with mode: 0644]
queue-6.12/fs-ntfs3-rename-ni_readpage_cmpr-into-ni_read_folio_.patch [new file with mode: 0644]
queue-6.12/fs-ntfs3-zero-fill-folios-beyond-i_valid-in-ntfs_rea.patch [new file with mode: 0644]
queue-6.12/series
queue-6.18/fs-ntfs3-fix-missing-run-load-for-vcn0-in-attr_data_.patch [new file with mode: 0644]
queue-6.18/fs-ntfs3-fsync-files-by-syncing-parent-inodes.patch [new file with mode: 0644]
queue-6.18/fs-ntfs3-rename-ni_readpage_cmpr-into-ni_read_folio_.patch [new file with mode: 0644]
queue-6.18/fs-ntfs3-zero-fill-folios-beyond-i_valid-in-ntfs_rea.patch [new file with mode: 0644]
queue-6.18/series
queue-6.6/fs-ntfs3-fix-missing-run-load-for-vcn0-in-attr_data_.patch [new file with mode: 0644]
queue-6.6/fs-ntfs3-fsync-files-by-syncing-parent-inodes.patch [new file with mode: 0644]
queue-6.6/fs-ntfs3-handle-attr_set_size-errors-when-truncating.patch [new file with mode: 0644]
queue-6.6/fs-ntfs3-zero-fill-folios-beyond-i_valid-in-ntfs_rea.patch [new file with mode: 0644]
queue-6.6/series

diff --git a/queue-5.10/net-drop-the-lock-in-skb_may_tx_timestamp.patch b/queue-5.10/net-drop-the-lock-in-skb_may_tx_timestamp.patch
new file mode 100644 (file)
index 0000000..ca71e5e
--- /dev/null
@@ -0,0 +1,118 @@
+From 2c26fb3783f69a5a373c823d4c2bc4028c855bd7 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 13 Jul 2026 10:50:17 +0800
+Subject: net: Drop the lock in skb_may_tx_timestamp()
+
+From: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
+
+commit 983512f3a87fd8dc4c94dfa6b596b6e57df5aad7 upstream.
+
+skb_may_tx_timestamp() may acquire sock::sk_callback_lock. The lock must
+not be taken in IRQ context, only softirq is okay. A few drivers receive
+the timestamp via a dedicated interrupt and complete the TX timestamp
+from that handler. This will lead to a deadlock if the lock is already
+write-locked on the same CPU.
+
+Taking the lock can be avoided. The socket (pointed by the skb) will
+remain valid until the skb is released. The ->sk_socket and ->file
+member will be set to NULL once the user closes the socket which may
+happen before the timestamp arrives.
+If we happen to observe the pointer while the socket is closing but
+before the pointer is set to NULL then we may use it because both
+pointer (and the file's cred member) are RCU freed.
+
+Drop the lock. Use READ_ONCE() to obtain the individual pointer. Add a
+matching WRITE_ONCE() where the pointer are cleared.
+
+Link: https://lore.kernel.org/all/20260205145104.iWinkXHv@linutronix.de
+Fixes: b245be1f4db1a ("net-timestamp: no-payload only sysctl")
+Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
+Reviewed-by: Willem de Bruijn <willemb@google.com>
+Reviewed-by: Jason Xing <kerneljasonxing@gmail.com>
+Reviewed-by: Eric Dumazet <edumazet@google.com>
+Link: https://patch.msgid.link/20260220183858.N4ERjFW6@linutronix.de
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+[ adapted sk_set_socket() in include/net/sock.h to fix the conflict from
+ not having commit 5d6b58c932ec ("net: lockless sock_i_ino()") and the
+ additional previous changes required by it.
+ It comes down to just now having the lines of
+    if (sock) {
+            WRITE_ONCE(sk->sk_uid, SOCK_INODE(sock)->i_uid);
+            WRITE_ONCE(sk->sk_ino, SOCK_INODE(sock)->i_ino);
+    }
+ below the changed line. ]
+Signed-off-by: Philo Lu <lulie@linux.alibaba.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ include/net/sock.h |  2 +-
+ net/core/skbuff.c  | 23 ++++++++++++++++++-----
+ net/socket.c       |  2 +-
+ 3 files changed, 20 insertions(+), 7 deletions(-)
+
+diff --git a/include/net/sock.h b/include/net/sock.h
+index f0e391afb511d2..b9c44e47cb7a78 100644
+--- a/include/net/sock.h
++++ b/include/net/sock.h
+@@ -1981,7 +1981,7 @@ static inline int sk_rx_queue_get(const struct sock *sk)
+ static inline void sk_set_socket(struct sock *sk, struct socket *sock)
+ {
+-      sk->sk_socket = sock;
++      WRITE_ONCE(sk->sk_socket, sock);
+ }
+ static inline wait_queue_head_t *sk_sleep(struct sock *sk)
+diff --git a/net/core/skbuff.c b/net/core/skbuff.c
+index e4a39e0f55f242..90a63c356affb2 100644
+--- a/net/core/skbuff.c
++++ b/net/core/skbuff.c
+@@ -4772,15 +4772,28 @@ static void __skb_complete_tx_timestamp(struct sk_buff *skb,
+ static bool skb_may_tx_timestamp(struct sock *sk, bool tsonly)
+ {
+-      bool ret;
++      struct socket *sock;
++      struct file *file;
++      bool ret = false;
+       if (likely(READ_ONCE(sysctl_tstamp_allow_data) || tsonly))
+               return true;
+-      read_lock_bh(&sk->sk_callback_lock);
+-      ret = sk->sk_socket && sk->sk_socket->file &&
+-            file_ns_capable(sk->sk_socket->file, &init_user_ns, CAP_NET_RAW);
+-      read_unlock_bh(&sk->sk_callback_lock);
++      /* The sk pointer remains valid as long as the skb is. The sk_socket and
++       * file pointer may become NULL if the socket is closed. Both structures
++       * (including file->cred) are RCU freed which means they can be accessed
++       * within a RCU read section.
++       */
++      rcu_read_lock();
++      sock = READ_ONCE(sk->sk_socket);
++      if (!sock)
++              goto out;
++      file = READ_ONCE(sock->file);
++      if (!file)
++              goto out;
++      ret = file_ns_capable(file, &init_user_ns, CAP_NET_RAW);
++out:
++      rcu_read_unlock();
+       return ret;
+ }
+diff --git a/net/socket.c b/net/socket.c
+index de838c3b004861..eb157ea2a3efbb 100644
+--- a/net/socket.c
++++ b/net/socket.c
+@@ -608,7 +608,7 @@ static void __sock_release(struct socket *sock, struct inode *inode)
+               iput(SOCK_INODE(sock));
+               return;
+       }
+-      sock->file = NULL;
++      WRITE_ONCE(sock->file, NULL);
+ }
+ /**
+-- 
+2.53.0
+
index d4ee4d685f604f59c85ea94458d9b1c072bf28a5..be8661d58be6062b5b502097fe859af8f0b400ec 100644 (file)
@@ -84,3 +84,4 @@ netfilter-ebtables-terminate-table-name-before-find_table_lock.patch
 bluetooth-bnep-pin-l2cap-connection-during-netdev-registration.patch
 bluetooth-fix-uaf-in-bt_accept_dequeue.patch
 bluetooth-l2cap-validate-option-length-before-reading-conf-opt-value.patch
+net-drop-the-lock-in-skb_may_tx_timestamp.patch
diff --git a/queue-5.15/net-drop-the-lock-in-skb_may_tx_timestamp.patch b/queue-5.15/net-drop-the-lock-in-skb_may_tx_timestamp.patch
new file mode 100644 (file)
index 0000000..b898994
--- /dev/null
@@ -0,0 +1,118 @@
+From eb2697900d0ebd2573f3179231aec11df803ca2f Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 13 Jul 2026 10:49:43 +0800
+Subject: net: Drop the lock in skb_may_tx_timestamp()
+
+From: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
+
+commit 983512f3a87fd8dc4c94dfa6b596b6e57df5aad7 upstream.
+
+skb_may_tx_timestamp() may acquire sock::sk_callback_lock. The lock must
+not be taken in IRQ context, only softirq is okay. A few drivers receive
+the timestamp via a dedicated interrupt and complete the TX timestamp
+from that handler. This will lead to a deadlock if the lock is already
+write-locked on the same CPU.
+
+Taking the lock can be avoided. The socket (pointed by the skb) will
+remain valid until the skb is released. The ->sk_socket and ->file
+member will be set to NULL once the user closes the socket which may
+happen before the timestamp arrives.
+If we happen to observe the pointer while the socket is closing but
+before the pointer is set to NULL then we may use it because both
+pointer (and the file's cred member) are RCU freed.
+
+Drop the lock. Use READ_ONCE() to obtain the individual pointer. Add a
+matching WRITE_ONCE() where the pointer are cleared.
+
+Link: https://lore.kernel.org/all/20260205145104.iWinkXHv@linutronix.de
+Fixes: b245be1f4db1a ("net-timestamp: no-payload only sysctl")
+Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
+Reviewed-by: Willem de Bruijn <willemb@google.com>
+Reviewed-by: Jason Xing <kerneljasonxing@gmail.com>
+Reviewed-by: Eric Dumazet <edumazet@google.com>
+Link: https://patch.msgid.link/20260220183858.N4ERjFW6@linutronix.de
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+[ adapted sk_set_socket() in include/net/sock.h to fix the conflict from
+ not having commit 5d6b58c932ec ("net: lockless sock_i_ino()") and the
+ additional previous changes required by it.
+ It comes down to just now having the lines of
+    if (sock) {
+            WRITE_ONCE(sk->sk_uid, SOCK_INODE(sock)->i_uid);
+            WRITE_ONCE(sk->sk_ino, SOCK_INODE(sock)->i_ino);
+    }
+ below the changed line. ]
+Signed-off-by: Philo Lu <lulie@linux.alibaba.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ include/net/sock.h |  2 +-
+ net/core/skbuff.c  | 23 ++++++++++++++++++-----
+ net/socket.c       |  2 +-
+ 3 files changed, 20 insertions(+), 7 deletions(-)
+
+diff --git a/include/net/sock.h b/include/net/sock.h
+index 962acf4a644da9..2b13a41cd99a99 100644
+--- a/include/net/sock.h
++++ b/include/net/sock.h
+@@ -2070,7 +2070,7 @@ static inline int sk_rx_queue_get(const struct sock *sk)
+ static inline void sk_set_socket(struct sock *sk, struct socket *sock)
+ {
+-      sk->sk_socket = sock;
++      WRITE_ONCE(sk->sk_socket, sock);
+ }
+ static inline wait_queue_head_t *sk_sleep(struct sock *sk)
+diff --git a/net/core/skbuff.c b/net/core/skbuff.c
+index 82add06ca935f1..6281cfed03bb29 100644
+--- a/net/core/skbuff.c
++++ b/net/core/skbuff.c
+@@ -4953,15 +4953,28 @@ static void __skb_complete_tx_timestamp(struct sk_buff *skb,
+ static bool skb_may_tx_timestamp(struct sock *sk, bool tsonly)
+ {
+-      bool ret;
++      struct socket *sock;
++      struct file *file;
++      bool ret = false;
+       if (likely(READ_ONCE(sysctl_tstamp_allow_data) || tsonly))
+               return true;
+-      read_lock_bh(&sk->sk_callback_lock);
+-      ret = sk->sk_socket && sk->sk_socket->file &&
+-            file_ns_capable(sk->sk_socket->file, &init_user_ns, CAP_NET_RAW);
+-      read_unlock_bh(&sk->sk_callback_lock);
++      /* The sk pointer remains valid as long as the skb is. The sk_socket and
++       * file pointer may become NULL if the socket is closed. Both structures
++       * (including file->cred) are RCU freed which means they can be accessed
++       * within a RCU read section.
++       */
++      rcu_read_lock();
++      sock = READ_ONCE(sk->sk_socket);
++      if (!sock)
++              goto out;
++      file = READ_ONCE(sock->file);
++      if (!file)
++              goto out;
++      ret = file_ns_capable(file, &init_user_ns, CAP_NET_RAW);
++out:
++      rcu_read_unlock();
+       return ret;
+ }
+diff --git a/net/socket.c b/net/socket.c
+index 24dd5f26585aaa..0c87e99c8e69ab 100644
+--- a/net/socket.c
++++ b/net/socket.c
+@@ -663,7 +663,7 @@ static void __sock_release(struct socket *sock, struct inode *inode)
+               iput(SOCK_INODE(sock));
+               return;
+       }
+-      sock->file = NULL;
++      WRITE_ONCE(sock->file, NULL);
+ }
+ /**
+-- 
+2.53.0
+
index 6ec9af3f1ddd3386017a21e8359202292d14a0cc..55ec3177fbe3b4acdbc82b3c74f9fae406f4316e 100644 (file)
@@ -113,3 +113,4 @@ netfilter-ebtables-terminate-table-name-before-find_table_lock.patch
 bluetooth-bnep-pin-l2cap-connection-during-netdev-registration.patch
 bluetooth-fix-uaf-in-bt_accept_dequeue.patch
 bluetooth-l2cap-validate-option-length-before-reading-conf-opt-value.patch
+net-drop-the-lock-in-skb_may_tx_timestamp.patch
diff --git a/queue-6.1/net-drop-the-lock-in-skb_may_tx_timestamp.patch b/queue-6.1/net-drop-the-lock-in-skb_may_tx_timestamp.patch
new file mode 100644 (file)
index 0000000..b5467fc
--- /dev/null
@@ -0,0 +1,118 @@
+From 42fbf615f12a32b47e6e317066a58ad5d10e0e08 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 13 Jul 2026 10:49:12 +0800
+Subject: net: Drop the lock in skb_may_tx_timestamp()
+
+From: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
+
+commit 983512f3a87fd8dc4c94dfa6b596b6e57df5aad7 upstream.
+
+skb_may_tx_timestamp() may acquire sock::sk_callback_lock. The lock must
+not be taken in IRQ context, only softirq is okay. A few drivers receive
+the timestamp via a dedicated interrupt and complete the TX timestamp
+from that handler. This will lead to a deadlock if the lock is already
+write-locked on the same CPU.
+
+Taking the lock can be avoided. The socket (pointed by the skb) will
+remain valid until the skb is released. The ->sk_socket and ->file
+member will be set to NULL once the user closes the socket which may
+happen before the timestamp arrives.
+If we happen to observe the pointer while the socket is closing but
+before the pointer is set to NULL then we may use it because both
+pointer (and the file's cred member) are RCU freed.
+
+Drop the lock. Use READ_ONCE() to obtain the individual pointer. Add a
+matching WRITE_ONCE() where the pointer are cleared.
+
+Link: https://lore.kernel.org/all/20260205145104.iWinkXHv@linutronix.de
+Fixes: b245be1f4db1a ("net-timestamp: no-payload only sysctl")
+Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
+Reviewed-by: Willem de Bruijn <willemb@google.com>
+Reviewed-by: Jason Xing <kerneljasonxing@gmail.com>
+Reviewed-by: Eric Dumazet <edumazet@google.com>
+Link: https://patch.msgid.link/20260220183858.N4ERjFW6@linutronix.de
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+[ adapted sk_set_socket() in include/net/sock.h to fix the conflict from
+ not having commit 5d6b58c932ec ("net: lockless sock_i_ino()") and the
+ additional previous changes required by it.
+ It comes down to just now having the lines of
+    if (sock) {
+            WRITE_ONCE(sk->sk_uid, SOCK_INODE(sock)->i_uid);
+            WRITE_ONCE(sk->sk_ino, SOCK_INODE(sock)->i_ino);
+    }
+ below the changed line. ]
+Signed-off-by: Philo Lu <lulie@linux.alibaba.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ include/net/sock.h |  2 +-
+ net/core/skbuff.c  | 23 ++++++++++++++++++-----
+ net/socket.c       |  2 +-
+ 3 files changed, 20 insertions(+), 7 deletions(-)
+
+diff --git a/include/net/sock.h b/include/net/sock.h
+index 1e7096e9bc4842..9fe621f9857401 100644
+--- a/include/net/sock.h
++++ b/include/net/sock.h
+@@ -2181,7 +2181,7 @@ static inline int sk_rx_queue_get(const struct sock *sk)
+ static inline void sk_set_socket(struct sock *sk, struct socket *sock)
+ {
+-      sk->sk_socket = sock;
++      WRITE_ONCE(sk->sk_socket, sock);
+ }
+ static inline wait_queue_head_t *sk_sleep(struct sock *sk)
+diff --git a/net/core/skbuff.c b/net/core/skbuff.c
+index f41dd20991c3df..6575a76a312e11 100644
+--- a/net/core/skbuff.c
++++ b/net/core/skbuff.c
+@@ -4948,15 +4948,28 @@ static void __skb_complete_tx_timestamp(struct sk_buff *skb,
+ static bool skb_may_tx_timestamp(struct sock *sk, bool tsonly)
+ {
+-      bool ret;
++      struct socket *sock;
++      struct file *file;
++      bool ret = false;
+       if (likely(READ_ONCE(sysctl_tstamp_allow_data) || tsonly))
+               return true;
+-      read_lock_bh(&sk->sk_callback_lock);
+-      ret = sk->sk_socket && sk->sk_socket->file &&
+-            file_ns_capable(sk->sk_socket->file, &init_user_ns, CAP_NET_RAW);
+-      read_unlock_bh(&sk->sk_callback_lock);
++      /* The sk pointer remains valid as long as the skb is. The sk_socket and
++       * file pointer may become NULL if the socket is closed. Both structures
++       * (including file->cred) are RCU freed which means they can be accessed
++       * within a RCU read section.
++       */
++      rcu_read_lock();
++      sock = READ_ONCE(sk->sk_socket);
++      if (!sock)
++              goto out;
++      file = READ_ONCE(sock->file);
++      if (!file)
++              goto out;
++      ret = file_ns_capable(file, &init_user_ns, CAP_NET_RAW);
++out:
++      rcu_read_unlock();
+       return ret;
+ }
+diff --git a/net/socket.c b/net/socket.c
+index f2b4cf9b09a322..8bf415a59e55c2 100644
+--- a/net/socket.c
++++ b/net/socket.c
+@@ -666,7 +666,7 @@ static void __sock_release(struct socket *sock, struct inode *inode)
+               iput(SOCK_INODE(sock));
+               return;
+       }
+-      sock->file = NULL;
++      WRITE_ONCE(sock->file, NULL);
+ }
+ /**
+-- 
+2.53.0
+
index 94226eef4804b4201b56cbf66bcca412a467ae64..e3c4afa67dc13c5b9f384b740ac2cf292162f0e1 100644 (file)
@@ -132,3 +132,4 @@ bluetooth-btmtksdio-fix-infinite-loop-in-btmtksdio_txrx_work.patch
 bluetooth-bnep-pin-l2cap-connection-during-netdev-registration.patch
 bluetooth-fix-uaf-in-bt_accept_dequeue.patch
 bluetooth-l2cap-validate-option-length-before-reading-conf-opt-value.patch
+net-drop-the-lock-in-skb_may_tx_timestamp.patch
diff --git a/queue-6.12/fs-ntfs3-fix-missing-run-load-for-vcn0-in-attr_data_.patch b/queue-6.12/fs-ntfs3-fix-missing-run-load-for-vcn0-in-attr_data_.patch
new file mode 100644 (file)
index 0000000..eef88c4
--- /dev/null
@@ -0,0 +1,72 @@
+From 7d191768fa33fb373e441a59bea2eba7fe5e7c33 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 19 Mar 2026 13:15:46 +0530
+Subject: fs/ntfs3: fix missing run load for vcn0 in
+ attr_data_get_block_locked()
+
+From: Deepanshu Kartikey <kartikey406@gmail.com>
+
+[ Upstream commit d7ea8495fd307b58f8867acd81a1b40075b1d3ba ]
+
+When a compressed or sparse attribute has its clusters frame-aligned,
+vcn is rounded down to the frame start using cmask, which can result
+in vcn != vcn0. In this case, vcn and vcn0 may reside in different
+attribute segments.
+
+The code already handles the case where vcn is in a different segment
+by loading its runs before allocation. However, it fails to load runs
+for vcn0 when vcn0 resides in a different segment than vcn. This causes
+run_lookup_entry() to return SPARSE_LCN for vcn0 since its segment was
+never loaded into the in-memory run list, triggering the WARN_ON(1).
+
+Fix this by adding a missing check for vcn0 after the existing vcn
+segment check. If vcn0 falls outside the current segment range
+[svcn, evcn1), find and load the attribute segment containing vcn0
+before performing the run lookup.
+
+The following scenario triggers the bug:
+  attr_data_get_block_locked()
+    vcn = vcn0 & cmask        <- vcn != vcn0 after frame alignment
+    load runs for vcn segment <- vcn0 segment not loaded!
+    attr_allocate_clusters()  <- allocation succeeds
+    run_lookup_entry(vcn0)    <- vcn0 not in run -> SPARSE_LCN
+    WARN_ON(1)                <- bug fires here!
+
+Reported-by: syzbot+c1e9aedbd913fadad617@syzkaller.appspotmail.com
+Closes: https://syzkaller.appspot.com/bug?extid=c1e9aedbd913fadad617
+Fixes: c380b52f6c57 ("fs/ntfs3: Change new sparse cluster processing")
+Signed-off-by: Deepanshu Kartikey <Kartikey406@gmail.com>
+Signed-off-by: Konstantin Komarov <almaz.alexandrovich@paragon-software.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ fs/ntfs3/attrib.c | 14 ++++++++++++++
+ 1 file changed, 14 insertions(+)
+
+diff --git a/fs/ntfs3/attrib.c b/fs/ntfs3/attrib.c
+index 4390c0e4bfb78a..940a8293abe9c0 100644
+--- a/fs/ntfs3/attrib.c
++++ b/fs/ntfs3/attrib.c
+@@ -1044,6 +1044,20 @@ int attr_data_get_block(struct ntfs_inode *ni, CLST vcn, CLST clen, CLST *lcn,
+                       if (err)
+                               goto out;
+               }
++
++              if (vcn0 < svcn || evcn1 <= vcn0) {
++                      struct ATTRIB *attr2;
++
++                      attr2 = ni_find_attr(ni, attr_b, &le_b, ATTR_DATA, NULL,
++                                             0, &vcn0, &mi);
++                      if (!attr2) {
++                              err = -EINVAL;
++                              goto out;
++                      }
++                      err = attr_load_runs(attr2, ni, run, NULL);
++                      if (err)
++                              goto out;
++              }
+       }
+       if (vcn + to_alloc > asize)
+-- 
+2.53.0
+
diff --git a/queue-6.12/fs-ntfs3-fsync-files-by-syncing-parent-inodes.patch b/queue-6.12/fs-ntfs3-fsync-files-by-syncing-parent-inodes.patch
new file mode 100644 (file)
index 0000000..098732e
--- /dev/null
@@ -0,0 +1,167 @@
+From be8b152f3289edbe3063864f24cabe86e3e30220 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 12 Dec 2025 14:12:18 +0300
+Subject: fs/ntfs3: fsync files by syncing parent inodes
+
+From: Konstantin Komarov <almaz.alexandrovich@paragon-software.com>
+
+[ Upstream commit dcd9d6a47199565d83d61a11bbf91fa2ade4d676 ]
+
+Some xfstests expect fsync() on a file or directory to also persist
+directory metadata up the parent chain. Using generic_file_fsync() is not
+sufficient for ntfs, because parent directories are not explicitly
+written out.
+
+Signed-off-by: Konstantin Komarov <almaz.alexandrovich@paragon-software.com>
+Stable-dep-of: d7ea8495fd30 ("fs/ntfs3: fix missing run load for vcn0 in attr_data_get_block_locked()")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ fs/ntfs3/dir.c     |  2 +-
+ fs/ntfs3/file.c    | 30 ++++++++++++++++++++++++---
+ fs/ntfs3/frecord.c | 51 ++++++++++++++++++++++++++++++++++++++++++++++
+ fs/ntfs3/ntfs_fs.h |  2 ++
+ 4 files changed, 81 insertions(+), 4 deletions(-)
+
+diff --git a/fs/ntfs3/dir.c b/fs/ntfs3/dir.c
+index 522ebc14b1fbd0..652b257e8b0782 100644
+--- a/fs/ntfs3/dir.c
++++ b/fs/ntfs3/dir.c
+@@ -625,7 +625,7 @@ const struct file_operations ntfs_dir_operations = {
+       .llseek         = generic_file_llseek,
+       .read           = generic_read_dir,
+       .iterate_shared = ntfs_readdir,
+-      .fsync          = generic_file_fsync,
++      .fsync          = ntfs_file_fsync,
+       .open           = ntfs_file_open,
+       .unlocked_ioctl = ntfs_ioctl,
+ #ifdef CONFIG_COMPAT
+diff --git a/fs/ntfs3/file.c b/fs/ntfs3/file.c
+index b7d87fd57063c5..13f7d8acecf901 100644
+--- a/fs/ntfs3/file.c
++++ b/fs/ntfs3/file.c
+@@ -1399,13 +1399,37 @@ static ssize_t ntfs_file_splice_write(struct pipe_inode_info *pipe,
+ /*
+  * ntfs_file_fsync - file_operations::fsync
+  */
+-static int ntfs_file_fsync(struct file *file, loff_t start, loff_t end, int datasync)
++int ntfs_file_fsync(struct file *file, loff_t start, loff_t end, int datasync)
+ {
+       struct inode *inode = file_inode(file);
+-      if (unlikely(ntfs3_forced_shutdown(inode->i_sb)))
++      struct super_block *sb = inode->i_sb;
++      struct ntfs_sb_info *sbi = sb->s_fs_info;
++      int err, ret;
++
++      if (unlikely(ntfs3_forced_shutdown(sb)))
+               return -EIO;
+-      return generic_file_fsync(file, start, end, datasync);
++      ret = file_write_and_wait_range(file, start, end);
++      if (ret)
++              return ret;
++
++      ret = write_inode_now(inode, !datasync);
++
++      if (!ret) {
++              ret = ni_write_parents(ntfs_i(inode), !datasync);
++      }
++
++      if (!ret) {
++              ntfs_set_state(sbi, NTFS_DIRTY_CLEAR);
++              ntfs_update_mftmirr(sbi, false);
++      }
++
++      err = sync_blockdev(sb->s_bdev);
++      if (unlikely(err && !ret))
++              ret = err;
++      if (!ret)
++              ret = blkdev_issue_flush(sb->s_bdev);
++      return ret;
+ }
+ // clang-format off
+diff --git a/fs/ntfs3/frecord.c b/fs/ntfs3/frecord.c
+index bb1feb80d5cb79..b6528d1e4e51fa 100644
+--- a/fs/ntfs3/frecord.c
++++ b/fs/ntfs3/frecord.c
+@@ -3144,6 +3144,57 @@ bool ni_is_dirty(struct inode *inode)
+       return false;
+ }
++/*
++ * ni_write_parents
++ *
++ * Helper function for ntfs_file_fsync.
++ */
++int ni_write_parents(struct ntfs_inode *ni, int sync)
++{
++      int err = 0;
++      struct ATTRIB *attr = NULL;
++      struct ATTR_LIST_ENTRY *le = NULL;
++      struct ntfs_sb_info *sbi = ni->mi.sbi;
++      struct super_block *sb = sbi->sb;
++
++      while ((attr = ni_find_attr(ni, attr, &le, ATTR_NAME, NULL, 0, NULL,
++                                  NULL))) {
++              struct inode *dir;
++              struct ATTR_FILE_NAME *fname;
++
++              fname = resident_data_ex(attr, SIZEOF_ATTRIBUTE_FILENAME);
++              if (!fname)
++                      continue;
++
++              /* Check simple case when parent inode equals current inode. */
++              if (ino_get(&fname->home) == ni->vfs_inode.i_ino) {
++                      if (MFT_REC_ROOT != ni->vfs_inode.i_ino) {
++                              ntfs_set_state(sbi, NTFS_DIRTY_ERROR);
++                              err = -EINVAL;
++                      }
++                      continue;
++              }
++
++              dir = ntfs_iget5(sb, &fname->home, NULL);
++              if (IS_ERR(dir)) {
++                      ntfs_inode_warn(
++                              &ni->vfs_inode,
++                              "failed to open parent directory r=%lx to write",
++                              (long)ino_get(&fname->home));
++                      continue;
++              }
++
++              if (!is_bad_inode(dir)) {
++                      int err2 = write_inode_now(dir, sync);
++                      if (!err)
++                              err = err2;
++              }
++              iput(dir);
++      }
++
++      return err;
++}
++
+ /*
+  * ni_update_parent
+  *
+diff --git a/fs/ntfs3/ntfs_fs.h b/fs/ntfs3/ntfs_fs.h
+index e860529e1ff996..7375ea351f0ea7 100644
+--- a/fs/ntfs3/ntfs_fs.h
++++ b/fs/ntfs3/ntfs_fs.h
+@@ -507,6 +507,7 @@ int ntfs_setattr(struct mnt_idmap *idmap, struct dentry *dentry,
+ int ntfs_file_open(struct inode *inode, struct file *file);
+ int ntfs_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo,
+               __u64 start, __u64 len);
++int ntfs_file_fsync(struct file *file, loff_t start, loff_t end, int datasync);
+ long ntfs_ioctl(struct file *filp, u32 cmd, unsigned long arg);
+ long ntfs_compat_ioctl(struct file *filp, u32 cmd, unsigned long arg);
+ extern const struct inode_operations ntfs_special_inode_operations;
+@@ -588,6 +589,7 @@ int ni_rename(struct ntfs_inode *dir_ni, struct ntfs_inode *new_dir_ni,
+ bool ni_is_dirty(struct inode *inode);
+ int ni_set_compress(struct inode *inode, bool compr);
++int ni_write_parents(struct ntfs_inode *ni, int sync);
+ /* Globals from fslog.c */
+ bool check_index_header(const struct INDEX_HDR *hdr, size_t bytes);
+-- 
+2.53.0
+
diff --git a/queue-6.12/fs-ntfs3-rename-ni_readpage_cmpr-into-ni_read_folio_.patch b/queue-6.12/fs-ntfs3-rename-ni_readpage_cmpr-into-ni_read_folio_.patch
new file mode 100644 (file)
index 0000000..72f09ec
--- /dev/null
@@ -0,0 +1,80 @@
+From 7ac4ccb0ec2cc433a62cbcf4fd27cc7551595bd3 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 8 Dec 2025 22:57:46 +0300
+Subject: fs/ntfs3: rename ni_readpage_cmpr into ni_read_folio_cmpr
+
+From: Konstantin Komarov <almaz.alexandrovich@paragon-software.com>
+
+[ Upstream commit 4248f563f0b76f3fb74b2a28ee068bf66fcbbedf ]
+
+The old "readpage" naming is still used in ni_readpage_cmpr(), even though
+the vfs has transitioned to the folio-based read_folio() API.
+
+This patch performs a straightforward renaming of the helper:
+ni_readpage_cmpr() -> ni_read_folio_cmpr().
+
+Signed-off-by: Konstantin Komarov <almaz.alexandrovich@paragon-software.com>
+Stable-dep-of: d7ea8495fd30 ("fs/ntfs3: fix missing run load for vcn0 in attr_data_get_block_locked()")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ fs/ntfs3/frecord.c | 8 ++++----
+ fs/ntfs3/inode.c   | 2 +-
+ fs/ntfs3/ntfs_fs.h | 2 +-
+ 3 files changed, 6 insertions(+), 6 deletions(-)
+
+diff --git a/fs/ntfs3/frecord.c b/fs/ntfs3/frecord.c
+index 6b04ad5d5f519c..bb1feb80d5cb79 100644
+--- a/fs/ntfs3/frecord.c
++++ b/fs/ntfs3/frecord.c
+@@ -2101,18 +2101,18 @@ static struct page *ntfs_lock_new_page(struct address_space *mapping,
+ }
+ /*
+- * ni_readpage_cmpr
++ * ni_read_folio_cmpr
+  *
+  * When decompressing, we typically obtain more than one page per reference.
+  * We inject the additional pages into the page cache.
+  */
+-int ni_readpage_cmpr(struct ntfs_inode *ni, struct folio *folio)
++int ni_read_folio_cmpr(struct ntfs_inode *ni, struct folio *folio)
+ {
+       int err;
+       struct ntfs_sb_info *sbi = ni->mi.sbi;
+       struct address_space *mapping = folio->mapping;
+-      pgoff_t index = folio->index;
+-      u64 frame_vbo, vbo = (u64)index << PAGE_SHIFT;
++      pgoff_t index;
++      u64 frame_vbo, vbo = folio_pos(folio);
+       struct page **pages = NULL; /* Array of at most 16 pages. stack? */
+       u8 frame_bits;
+       CLST frame;
+diff --git a/fs/ntfs3/inode.c b/fs/ntfs3/inode.c
+index b50c9dff327d28..5e240d7fb03709 100644
+--- a/fs/ntfs3/inode.c
++++ b/fs/ntfs3/inode.c
+@@ -736,7 +736,7 @@ static int ntfs_read_folio(struct file *file, struct folio *folio)
+       if (is_compressed(ni)) {
+               ni_lock(ni);
+-              err = ni_readpage_cmpr(ni, folio);
++              err = ni_read_folio_cmpr(ni, folio);
+               ni_unlock(ni);
+               return err;
+       }
+diff --git a/fs/ntfs3/ntfs_fs.h b/fs/ntfs3/ntfs_fs.h
+index a1040060b081f5..e860529e1ff996 100644
+--- a/fs/ntfs3/ntfs_fs.h
++++ b/fs/ntfs3/ntfs_fs.h
+@@ -567,7 +567,7 @@ int ni_write_inode(struct inode *inode, int sync, const char *hint);
+ #define _ni_write_inode(i, w) ni_write_inode(i, w, __func__)
+ int ni_fiemap(struct ntfs_inode *ni, struct fiemap_extent_info *fieinfo,
+             __u64 vbo, __u64 len);
+-int ni_readpage_cmpr(struct ntfs_inode *ni, struct folio *folio);
++int ni_read_folio_cmpr(struct ntfs_inode *ni, struct folio *folio);
+ int ni_decompress_file(struct ntfs_inode *ni);
+ int ni_read_frame(struct ntfs_inode *ni, u64 frame_vbo, struct page **pages,
+                 u32 pages_per_frame);
+-- 
+2.53.0
+
diff --git a/queue-6.12/fs-ntfs3-zero-fill-folios-beyond-i_valid-in-ntfs_rea.patch b/queue-6.12/fs-ntfs3-zero-fill-folios-beyond-i_valid-in-ntfs_rea.patch
new file mode 100644 (file)
index 0000000..f55aabf
--- /dev/null
@@ -0,0 +1,44 @@
+From 09042f1e9f6af3f676108988c50eb11058ffcf79 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 12 Dec 2025 14:38:10 +0300
+Subject: fs/ntfs3: zero-fill folios beyond i_valid in ntfs_read_folio()
+
+From: Konstantin Komarov <almaz.alexandrovich@paragon-software.com>
+
+[ Upstream commit 356fa248168be90109b66f32a61b8eaedc98424a ]
+
+Handle ntfs_read_folio() early when the folio offset is beyond i_valid
+by zero-filling the folio and marking it uptodate. This avoids needless
+I/O and locking, improves read performance.
+
+Signed-off-by: Konstantin Komarov <almaz.alexandrovich@paragon-software.com>
+Stable-dep-of: d7ea8495fd30 ("fs/ntfs3: fix missing run load for vcn0 in attr_data_get_block_locked()")
+[ sashal: dropped the is_bad_ni() bail-out; 6.12 lacks is_bad_ni()/ni_bad
+  (introduced upstream by 519b078998ce6e) ]
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ fs/ntfs3/inode.c | 8 ++++++++
+ 1 file changed, 8 insertions(+)
+
+diff --git a/fs/ntfs3/inode.c b/fs/ntfs3/inode.c
+index 5e240d7fb03709..93cf65db77a015 100644
+--- a/fs/ntfs3/inode.c
++++ b/fs/ntfs3/inode.c
+@@ -723,6 +723,14 @@ static int ntfs_read_folio(struct file *file, struct folio *folio)
+       struct address_space *mapping = folio->mapping;
+       struct inode *inode = mapping->host;
+       struct ntfs_inode *ni = ntfs_i(inode);
++      loff_t vbo = folio_pos(folio);
++
++      if (ni->i_valid <= vbo) {
++              folio_zero_range(folio, 0, folio_size(folio));
++              folio_mark_uptodate(folio);
++              folio_unlock(folio);
++              return 0;
++      }
+       if (is_resident(ni)) {
+               ni_lock(ni);
+-- 
+2.53.0
+
index 3406c53a7382fed917ded3767d084cdfacc09af9..292d8a735af74ba57b85df0a1b44e1ad0deb4b2d 100644 (file)
@@ -154,3 +154,7 @@ bluetooth-fix-uaf-in-bt_accept_dequeue.patch
 bluetooth-hci_uart-clear-hci_uart_sending-when-write_work-is-canceled.patch
 bluetooth-iso-avoid-null-deref-of-conn-in-iso_conn_big_sync.patch
 bluetooth-l2cap-validate-option-length-before-reading-conf-opt-value.patch
+fs-ntfs3-rename-ni_readpage_cmpr-into-ni_read_folio_.patch
+fs-ntfs3-fsync-files-by-syncing-parent-inodes.patch
+fs-ntfs3-zero-fill-folios-beyond-i_valid-in-ntfs_rea.patch
+fs-ntfs3-fix-missing-run-load-for-vcn0-in-attr_data_.patch
diff --git a/queue-6.18/fs-ntfs3-fix-missing-run-load-for-vcn0-in-attr_data_.patch b/queue-6.18/fs-ntfs3-fix-missing-run-load-for-vcn0-in-attr_data_.patch
new file mode 100644 (file)
index 0000000..bacb7dd
--- /dev/null
@@ -0,0 +1,72 @@
+From eb93aef94f1843be74bb99b8aee48e330fe391e7 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 19 Mar 2026 13:15:46 +0530
+Subject: fs/ntfs3: fix missing run load for vcn0 in
+ attr_data_get_block_locked()
+
+From: Deepanshu Kartikey <kartikey406@gmail.com>
+
+[ Upstream commit d7ea8495fd307b58f8867acd81a1b40075b1d3ba ]
+
+When a compressed or sparse attribute has its clusters frame-aligned,
+vcn is rounded down to the frame start using cmask, which can result
+in vcn != vcn0. In this case, vcn and vcn0 may reside in different
+attribute segments.
+
+The code already handles the case where vcn is in a different segment
+by loading its runs before allocation. However, it fails to load runs
+for vcn0 when vcn0 resides in a different segment than vcn. This causes
+run_lookup_entry() to return SPARSE_LCN for vcn0 since its segment was
+never loaded into the in-memory run list, triggering the WARN_ON(1).
+
+Fix this by adding a missing check for vcn0 after the existing vcn
+segment check. If vcn0 falls outside the current segment range
+[svcn, evcn1), find and load the attribute segment containing vcn0
+before performing the run lookup.
+
+The following scenario triggers the bug:
+  attr_data_get_block_locked()
+    vcn = vcn0 & cmask        <- vcn != vcn0 after frame alignment
+    load runs for vcn segment <- vcn0 segment not loaded!
+    attr_allocate_clusters()  <- allocation succeeds
+    run_lookup_entry(vcn0)    <- vcn0 not in run -> SPARSE_LCN
+    WARN_ON(1)                <- bug fires here!
+
+Reported-by: syzbot+c1e9aedbd913fadad617@syzkaller.appspotmail.com
+Closes: https://syzkaller.appspot.com/bug?extid=c1e9aedbd913fadad617
+Fixes: c380b52f6c57 ("fs/ntfs3: Change new sparse cluster processing")
+Signed-off-by: Deepanshu Kartikey <Kartikey406@gmail.com>
+Signed-off-by: Konstantin Komarov <almaz.alexandrovich@paragon-software.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ fs/ntfs3/attrib.c | 14 ++++++++++++++
+ 1 file changed, 14 insertions(+)
+
+diff --git a/fs/ntfs3/attrib.c b/fs/ntfs3/attrib.c
+index 5a7675112e7b8a..501f2ec153ab87 100644
+--- a/fs/ntfs3/attrib.c
++++ b/fs/ntfs3/attrib.c
+@@ -1045,6 +1045,20 @@ int attr_data_get_block(struct ntfs_inode *ni, CLST vcn, CLST clen, CLST *lcn,
+                       if (err)
+                               goto out;
+               }
++
++              if (vcn0 < svcn || evcn1 <= vcn0) {
++                      struct ATTRIB *attr2;
++
++                      attr2 = ni_find_attr(ni, attr_b, &le_b, ATTR_DATA, NULL,
++                                             0, &vcn0, &mi);
++                      if (!attr2) {
++                              err = -EINVAL;
++                              goto out;
++                      }
++                      err = attr_load_runs(attr2, ni, run, NULL);
++                      if (err)
++                              goto out;
++              }
+       }
+       if (vcn + to_alloc > asize)
+-- 
+2.53.0
+
diff --git a/queue-6.18/fs-ntfs3-fsync-files-by-syncing-parent-inodes.patch b/queue-6.18/fs-ntfs3-fsync-files-by-syncing-parent-inodes.patch
new file mode 100644 (file)
index 0000000..c19185b
--- /dev/null
@@ -0,0 +1,167 @@
+From 7e31a5cc9975b7f943d6517695a96829942e5102 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 12 Dec 2025 14:12:18 +0300
+Subject: fs/ntfs3: fsync files by syncing parent inodes
+
+From: Konstantin Komarov <almaz.alexandrovich@paragon-software.com>
+
+[ Upstream commit dcd9d6a47199565d83d61a11bbf91fa2ade4d676 ]
+
+Some xfstests expect fsync() on a file or directory to also persist
+directory metadata up the parent chain. Using generic_file_fsync() is not
+sufficient for ntfs, because parent directories are not explicitly
+written out.
+
+Signed-off-by: Konstantin Komarov <almaz.alexandrovich@paragon-software.com>
+Stable-dep-of: d7ea8495fd30 ("fs/ntfs3: fix missing run load for vcn0 in attr_data_get_block_locked()")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ fs/ntfs3/dir.c     |  2 +-
+ fs/ntfs3/file.c    | 30 ++++++++++++++++++++++++---
+ fs/ntfs3/frecord.c | 51 ++++++++++++++++++++++++++++++++++++++++++++++
+ fs/ntfs3/ntfs_fs.h |  2 ++
+ 4 files changed, 81 insertions(+), 4 deletions(-)
+
+diff --git a/fs/ntfs3/dir.c b/fs/ntfs3/dir.c
+index 460df046482c73..d41d02372c747b 100644
+--- a/fs/ntfs3/dir.c
++++ b/fs/ntfs3/dir.c
+@@ -624,7 +624,7 @@ const struct file_operations ntfs_dir_operations = {
+       .llseek         = generic_file_llseek,
+       .read           = generic_read_dir,
+       .iterate_shared = ntfs_readdir,
+-      .fsync          = generic_file_fsync,
++      .fsync          = ntfs_file_fsync,
+       .open           = ntfs_file_open,
+       .unlocked_ioctl = ntfs_ioctl,
+ #ifdef CONFIG_COMPAT
+diff --git a/fs/ntfs3/file.c b/fs/ntfs3/file.c
+index 5042eb321eb891..6a19bb04220809 100644
+--- a/fs/ntfs3/file.c
++++ b/fs/ntfs3/file.c
+@@ -1378,13 +1378,37 @@ static ssize_t ntfs_file_splice_write(struct pipe_inode_info *pipe,
+ /*
+  * ntfs_file_fsync - file_operations::fsync
+  */
+-static int ntfs_file_fsync(struct file *file, loff_t start, loff_t end, int datasync)
++int ntfs_file_fsync(struct file *file, loff_t start, loff_t end, int datasync)
+ {
+       struct inode *inode = file_inode(file);
+-      if (unlikely(ntfs3_forced_shutdown(inode->i_sb)))
++      struct super_block *sb = inode->i_sb;
++      struct ntfs_sb_info *sbi = sb->s_fs_info;
++      int err, ret;
++
++      if (unlikely(ntfs3_forced_shutdown(sb)))
+               return -EIO;
+-      return generic_file_fsync(file, start, end, datasync);
++      ret = file_write_and_wait_range(file, start, end);
++      if (ret)
++              return ret;
++
++      ret = write_inode_now(inode, !datasync);
++
++      if (!ret) {
++              ret = ni_write_parents(ntfs_i(inode), !datasync);
++      }
++
++      if (!ret) {
++              ntfs_set_state(sbi, NTFS_DIRTY_CLEAR);
++              ntfs_update_mftmirr(sbi, false);
++      }
++
++      err = sync_blockdev(sb->s_bdev);
++      if (unlikely(err && !ret))
++              ret = err;
++      if (!ret)
++              blkdev_issue_flush(sb->s_bdev);
++      return ret;
+ }
+ // clang-format off
+diff --git a/fs/ntfs3/frecord.c b/fs/ntfs3/frecord.c
+index 480f2ed518a783..e904c5da919222 100644
+--- a/fs/ntfs3/frecord.c
++++ b/fs/ntfs3/frecord.c
+@@ -3090,6 +3090,57 @@ bool ni_is_dirty(struct inode *inode)
+       return false;
+ }
++/*
++ * ni_write_parents
++ *
++ * Helper function for ntfs_file_fsync.
++ */
++int ni_write_parents(struct ntfs_inode *ni, int sync)
++{
++      int err = 0;
++      struct ATTRIB *attr = NULL;
++      struct ATTR_LIST_ENTRY *le = NULL;
++      struct ntfs_sb_info *sbi = ni->mi.sbi;
++      struct super_block *sb = sbi->sb;
++
++      while ((attr = ni_find_attr(ni, attr, &le, ATTR_NAME, NULL, 0, NULL,
++                                  NULL))) {
++              struct inode *dir;
++              struct ATTR_FILE_NAME *fname;
++
++              fname = resident_data_ex(attr, SIZEOF_ATTRIBUTE_FILENAME);
++              if (!fname)
++                      continue;
++
++              /* Check simple case when parent inode equals current inode. */
++              if (ino_get(&fname->home) == ni->vfs_inode.i_ino) {
++                      if (MFT_REC_ROOT != ni->vfs_inode.i_ino) {
++                              ntfs_set_state(sbi, NTFS_DIRTY_ERROR);
++                              err = -EINVAL;
++                      }
++                      continue;
++              }
++
++              dir = ntfs_iget5(sb, &fname->home, NULL);
++              if (IS_ERR(dir)) {
++                      ntfs_inode_warn(
++                              &ni->vfs_inode,
++                              "failed to open parent directory r=%lx to write",
++                              (long)ino_get(&fname->home));
++                      continue;
++              }
++
++              if (!is_bad_inode(dir)) {
++                      int err2 = write_inode_now(dir, sync);
++                      if (!err)
++                              err = err2;
++              }
++              iput(dir);
++      }
++
++      return err;
++}
++
+ /*
+  * ni_update_parent
+  *
+diff --git a/fs/ntfs3/ntfs_fs.h b/fs/ntfs3/ntfs_fs.h
+index a4466e9641f65d..7fb08bff025491 100644
+--- a/fs/ntfs3/ntfs_fs.h
++++ b/fs/ntfs3/ntfs_fs.h
+@@ -510,6 +510,7 @@ int ntfs_setattr(struct mnt_idmap *idmap, struct dentry *dentry,
+ int ntfs_file_open(struct inode *inode, struct file *file);
+ int ntfs_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo,
+               __u64 start, __u64 len);
++int ntfs_file_fsync(struct file *file, loff_t start, loff_t end, int datasync);
+ long ntfs_ioctl(struct file *filp, u32 cmd, unsigned long arg);
+ long ntfs_compat_ioctl(struct file *filp, u32 cmd, unsigned long arg);
+ extern const struct inode_operations ntfs_special_inode_operations;
+@@ -587,6 +588,7 @@ int ni_rename(struct ntfs_inode *dir_ni, struct ntfs_inode *new_dir_ni,
+             struct ntfs_inode *ni, struct NTFS_DE *de, struct NTFS_DE *new_de);
+ bool ni_is_dirty(struct inode *inode);
++int ni_write_parents(struct ntfs_inode *ni, int sync);
+ /* Globals from fslog.c */
+ bool check_index_header(const struct INDEX_HDR *hdr, size_t bytes);
+-- 
+2.53.0
+
diff --git a/queue-6.18/fs-ntfs3-rename-ni_readpage_cmpr-into-ni_read_folio_.patch b/queue-6.18/fs-ntfs3-rename-ni_readpage_cmpr-into-ni_read_folio_.patch
new file mode 100644 (file)
index 0000000..b5e1f44
--- /dev/null
@@ -0,0 +1,80 @@
+From 65ec61b5e713220ea5f4d6ad9ca93d672fbce2a4 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 8 Dec 2025 22:57:46 +0300
+Subject: fs/ntfs3: rename ni_readpage_cmpr into ni_read_folio_cmpr
+
+From: Konstantin Komarov <almaz.alexandrovich@paragon-software.com>
+
+[ Upstream commit 4248f563f0b76f3fb74b2a28ee068bf66fcbbedf ]
+
+The old "readpage" naming is still used in ni_readpage_cmpr(), even though
+the vfs has transitioned to the folio-based read_folio() API.
+
+This patch performs a straightforward renaming of the helper:
+ni_readpage_cmpr() -> ni_read_folio_cmpr().
+
+Signed-off-by: Konstantin Komarov <almaz.alexandrovich@paragon-software.com>
+Stable-dep-of: d7ea8495fd30 ("fs/ntfs3: fix missing run load for vcn0 in attr_data_get_block_locked()")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ fs/ntfs3/frecord.c | 8 ++++----
+ fs/ntfs3/inode.c   | 2 +-
+ fs/ntfs3/ntfs_fs.h | 2 +-
+ 3 files changed, 6 insertions(+), 6 deletions(-)
+
+diff --git a/fs/ntfs3/frecord.c b/fs/ntfs3/frecord.c
+index 295f6936a3e588..480f2ed518a783 100644
+--- a/fs/ntfs3/frecord.c
++++ b/fs/ntfs3/frecord.c
+@@ -2046,18 +2046,18 @@ static struct page *ntfs_lock_new_page(struct address_space *mapping,
+ }
+ /*
+- * ni_readpage_cmpr
++ * ni_read_folio_cmpr
+  *
+  * When decompressing, we typically obtain more than one page per reference.
+  * We inject the additional pages into the page cache.
+  */
+-int ni_readpage_cmpr(struct ntfs_inode *ni, struct folio *folio)
++int ni_read_folio_cmpr(struct ntfs_inode *ni, struct folio *folio)
+ {
+       int err;
+       struct ntfs_sb_info *sbi = ni->mi.sbi;
+       struct address_space *mapping = folio->mapping;
+-      pgoff_t index = folio->index;
+-      u64 frame_vbo, vbo = (u64)index << PAGE_SHIFT;
++      pgoff_t index;
++      u64 frame_vbo, vbo = folio_pos(folio);
+       struct page **pages = NULL; /* Array of at most 16 pages. stack? */
+       u8 frame_bits;
+       CLST frame;
+diff --git a/fs/ntfs3/inode.c b/fs/ntfs3/inode.c
+index 205baa791f998e..0cf2898334f33b 100644
+--- a/fs/ntfs3/inode.c
++++ b/fs/ntfs3/inode.c
+@@ -736,7 +736,7 @@ static int ntfs_read_folio(struct file *file, struct folio *folio)
+       if (is_compressed(ni)) {
+               ni_lock(ni);
+-              err = ni_readpage_cmpr(ni, folio);
++              err = ni_read_folio_cmpr(ni, folio);
+               ni_unlock(ni);
+               return err;
+       }
+diff --git a/fs/ntfs3/ntfs_fs.h b/fs/ntfs3/ntfs_fs.h
+index 2649fbe16669de..a4466e9641f65d 100644
+--- a/fs/ntfs3/ntfs_fs.h
++++ b/fs/ntfs3/ntfs_fs.h
+@@ -567,7 +567,7 @@ int ni_write_inode(struct inode *inode, int sync, const char *hint);
+ #define _ni_write_inode(i, w) ni_write_inode(i, w, __func__)
+ int ni_fiemap(struct ntfs_inode *ni, struct fiemap_extent_info *fieinfo,
+             __u64 vbo, __u64 len);
+-int ni_readpage_cmpr(struct ntfs_inode *ni, struct folio *folio);
++int ni_read_folio_cmpr(struct ntfs_inode *ni, struct folio *folio);
+ int ni_decompress_file(struct ntfs_inode *ni);
+ int ni_read_frame(struct ntfs_inode *ni, u64 frame_vbo, struct page **pages,
+                 u32 pages_per_frame);
+-- 
+2.53.0
+
diff --git a/queue-6.18/fs-ntfs3-zero-fill-folios-beyond-i_valid-in-ntfs_rea.patch b/queue-6.18/fs-ntfs3-zero-fill-folios-beyond-i_valid-in-ntfs_rea.patch
new file mode 100644 (file)
index 0000000..bf8188c
--- /dev/null
@@ -0,0 +1,47 @@
+From 8366ca0c493ea4eead2a95b9e3d7747a916368ed Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 12 Dec 2025 14:38:10 +0300
+Subject: fs/ntfs3: zero-fill folios beyond i_valid in ntfs_read_folio()
+
+From: Konstantin Komarov <almaz.alexandrovich@paragon-software.com>
+
+[ Upstream commit 356fa248168be90109b66f32a61b8eaedc98424a ]
+
+Handle ntfs_read_folio() early when the folio offset is beyond i_valid
+by zero-filling the folio and marking it uptodate. This avoids needless
+I/O and locking, improves read performance.
+
+Signed-off-by: Konstantin Komarov <almaz.alexandrovich@paragon-software.com>
+Stable-dep-of: d7ea8495fd30 ("fs/ntfs3: fix missing run load for vcn0 in attr_data_get_block_locked()")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ fs/ntfs3/inode.c | 13 +++++++++++++
+ 1 file changed, 13 insertions(+)
+
+diff --git a/fs/ntfs3/inode.c b/fs/ntfs3/inode.c
+index 0cf2898334f33b..1b7bb924fc0025 100644
+--- a/fs/ntfs3/inode.c
++++ b/fs/ntfs3/inode.c
+@@ -723,6 +723,19 @@ static int ntfs_read_folio(struct file *file, struct folio *folio)
+       struct address_space *mapping = folio->mapping;
+       struct inode *inode = mapping->host;
+       struct ntfs_inode *ni = ntfs_i(inode);
++      loff_t vbo = folio_pos(folio);
++
++      if (unlikely(is_bad_ni(ni))) {
++              folio_unlock(folio);
++              return -EIO;
++      }
++
++      if (ni->i_valid <= vbo) {
++              folio_zero_range(folio, 0, folio_size(folio));
++              folio_mark_uptodate(folio);
++              folio_unlock(folio);
++              return 0;
++      }
+       if (is_resident(ni)) {
+               ni_lock(ni);
+-- 
+2.53.0
+
index 65d7bf22c79382b12b18acbddab1015143ba96e0..7e3f333bf0e4dc0ad006aee2d20fbab1243f5224 100644 (file)
@@ -207,3 +207,7 @@ bluetooth-iso-avoid-null-deref-of-conn-in-iso_conn_big_sync.patch
 bluetooth-l2cap-cancel-pending_rx_work-before-taking-conn-lock.patch
 bluetooth-l2cap-validate-option-length-before-reading-conf-opt-value.patch
 iommu-vt-d-fix-race-condition-during-pasid-entry-replacement.patch
+fs-ntfs3-rename-ni_readpage_cmpr-into-ni_read_folio_.patch
+fs-ntfs3-fsync-files-by-syncing-parent-inodes.patch
+fs-ntfs3-zero-fill-folios-beyond-i_valid-in-ntfs_rea.patch
+fs-ntfs3-fix-missing-run-load-for-vcn0-in-attr_data_.patch
diff --git a/queue-6.6/fs-ntfs3-fix-missing-run-load-for-vcn0-in-attr_data_.patch b/queue-6.6/fs-ntfs3-fix-missing-run-load-for-vcn0-in-attr_data_.patch
new file mode 100644 (file)
index 0000000..abef7d0
--- /dev/null
@@ -0,0 +1,72 @@
+From 44554918e9b9e8f0a7f6fa08b1d459a88e6e9597 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 19 Mar 2026 13:15:46 +0530
+Subject: fs/ntfs3: fix missing run load for vcn0 in
+ attr_data_get_block_locked()
+
+From: Deepanshu Kartikey <kartikey406@gmail.com>
+
+[ Upstream commit d7ea8495fd307b58f8867acd81a1b40075b1d3ba ]
+
+When a compressed or sparse attribute has its clusters frame-aligned,
+vcn is rounded down to the frame start using cmask, which can result
+in vcn != vcn0. In this case, vcn and vcn0 may reside in different
+attribute segments.
+
+The code already handles the case where vcn is in a different segment
+by loading its runs before allocation. However, it fails to load runs
+for vcn0 when vcn0 resides in a different segment than vcn. This causes
+run_lookup_entry() to return SPARSE_LCN for vcn0 since its segment was
+never loaded into the in-memory run list, triggering the WARN_ON(1).
+
+Fix this by adding a missing check for vcn0 after the existing vcn
+segment check. If vcn0 falls outside the current segment range
+[svcn, evcn1), find and load the attribute segment containing vcn0
+before performing the run lookup.
+
+The following scenario triggers the bug:
+  attr_data_get_block_locked()
+    vcn = vcn0 & cmask        <- vcn != vcn0 after frame alignment
+    load runs for vcn segment <- vcn0 segment not loaded!
+    attr_allocate_clusters()  <- allocation succeeds
+    run_lookup_entry(vcn0)    <- vcn0 not in run -> SPARSE_LCN
+    WARN_ON(1)                <- bug fires here!
+
+Reported-by: syzbot+c1e9aedbd913fadad617@syzkaller.appspotmail.com
+Closes: https://syzkaller.appspot.com/bug?extid=c1e9aedbd913fadad617
+Fixes: c380b52f6c57 ("fs/ntfs3: Change new sparse cluster processing")
+Signed-off-by: Deepanshu Kartikey <Kartikey406@gmail.com>
+Signed-off-by: Konstantin Komarov <almaz.alexandrovich@paragon-software.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ fs/ntfs3/attrib.c | 14 ++++++++++++++
+ 1 file changed, 14 insertions(+)
+
+diff --git a/fs/ntfs3/attrib.c b/fs/ntfs3/attrib.c
+index 8f033e30e0d799..a590c541861332 100644
+--- a/fs/ntfs3/attrib.c
++++ b/fs/ntfs3/attrib.c
+@@ -1045,6 +1045,20 @@ int attr_data_get_block(struct ntfs_inode *ni, CLST vcn, CLST clen, CLST *lcn,
+                       if (err)
+                               goto out;
+               }
++
++              if (vcn0 < svcn || evcn1 <= vcn0) {
++                      struct ATTRIB *attr2;
++
++                      attr2 = ni_find_attr(ni, attr_b, &le_b, ATTR_DATA, NULL,
++                                             0, &vcn0, &mi);
++                      if (!attr2) {
++                              err = -EINVAL;
++                              goto out;
++                      }
++                      err = attr_load_runs(attr2, ni, run, NULL);
++                      if (err)
++                              goto out;
++              }
+       }
+       if (vcn + to_alloc > asize)
+-- 
+2.53.0
+
diff --git a/queue-6.6/fs-ntfs3-fsync-files-by-syncing-parent-inodes.patch b/queue-6.6/fs-ntfs3-fsync-files-by-syncing-parent-inodes.patch
new file mode 100644 (file)
index 0000000..f6dc393
--- /dev/null
@@ -0,0 +1,178 @@
+From 12ba1e2a40735c73a43330858e373fcf4c14cad2 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 12 Dec 2025 14:12:18 +0300
+Subject: fs/ntfs3: fsync files by syncing parent inodes
+
+From: Konstantin Komarov <almaz.alexandrovich@paragon-software.com>
+
+[ Upstream commit dcd9d6a47199565d83d61a11bbf91fa2ade4d676 ]
+
+Some xfstests expect fsync() on a file or directory to also persist
+directory metadata up the parent chain. Using generic_file_fsync() is not
+sufficient for ntfs, because parent directories are not explicitly
+written out.
+
+Signed-off-by: Konstantin Komarov <almaz.alexandrovich@paragon-software.com>
+Stable-dep-of: d7ea8495fd30 ("fs/ntfs3: fix missing run load for vcn0 in attr_data_get_block_locked()")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ fs/ntfs3/dir.c     |  2 +-
+ fs/ntfs3/file.c    | 38 +++++++++++++++++++++++++++++++++-
+ fs/ntfs3/frecord.c | 51 ++++++++++++++++++++++++++++++++++++++++++++++
+ fs/ntfs3/ntfs_fs.h |  2 ++
+ 4 files changed, 91 insertions(+), 2 deletions(-)
+
+diff --git a/fs/ntfs3/dir.c b/fs/ntfs3/dir.c
+index 894fd44164b48a..4bf851273e3324 100644
+--- a/fs/ntfs3/dir.c
++++ b/fs/ntfs3/dir.c
+@@ -623,7 +623,7 @@ const struct file_operations ntfs_dir_operations = {
+       .llseek         = generic_file_llseek,
+       .read           = generic_read_dir,
+       .iterate_shared = ntfs_readdir,
+-      .fsync          = generic_file_fsync,
++      .fsync          = ntfs_file_fsync,
+       .open           = ntfs_file_open,
+ };
+ // clang-format on
+diff --git a/fs/ntfs3/file.c b/fs/ntfs3/file.c
+index 212737a816d7af..89e35825c5c49d 100644
+--- a/fs/ntfs3/file.c
++++ b/fs/ntfs3/file.c
+@@ -1224,6 +1224,42 @@ int ntfs_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo,
+       return err;
+ }
++/*
++ * ntfs_file_fsync - file_operations::fsync
++ */
++int ntfs_file_fsync(struct file *file, loff_t start, loff_t end, int datasync)
++{
++      struct inode *inode = file_inode(file);
++      struct super_block *sb = inode->i_sb;
++      struct ntfs_sb_info *sbi = sb->s_fs_info;
++      int err, ret;
++
++      if (unlikely(ntfs3_forced_shutdown(sb)))
++              return -EIO;
++
++      ret = file_write_and_wait_range(file, start, end);
++      if (ret)
++              return ret;
++
++      ret = write_inode_now(inode, !datasync);
++
++      if (!ret) {
++              ret = ni_write_parents(ntfs_i(inode), !datasync);
++      }
++
++      if (!ret) {
++              ntfs_set_state(sbi, NTFS_DIRTY_CLEAR);
++              ntfs_update_mftmirr(sbi, false);
++      }
++
++      err = sync_blockdev(sb->s_bdev);
++      if (unlikely(err && !ret))
++              ret = err;
++      if (!ret)
++              ret = blkdev_issue_flush(sb->s_bdev);
++      return ret;
++}
++
+ // clang-format off
+ const struct inode_operations ntfs_file_inode_operations = {
+       .getattr        = ntfs_getattr,
+@@ -1245,7 +1281,7 @@ const struct file_operations ntfs_file_operations = {
+       .splice_read    = ntfs_file_splice_read,
+       .mmap           = ntfs_file_mmap,
+       .open           = ntfs_file_open,
+-      .fsync          = generic_file_fsync,
++      .fsync          = ntfs_file_fsync,
+       .splice_write   = iter_file_splice_write,
+       .fallocate      = ntfs_fallocate,
+       .release        = ntfs_file_release,
+diff --git a/fs/ntfs3/frecord.c b/fs/ntfs3/frecord.c
+index 06b1d82b599759..c265710d25b425 100644
+--- a/fs/ntfs3/frecord.c
++++ b/fs/ntfs3/frecord.c
+@@ -3145,6 +3145,57 @@ bool ni_is_dirty(struct inode *inode)
+       return false;
+ }
++/*
++ * ni_write_parents
++ *
++ * Helper function for ntfs_file_fsync.
++ */
++int ni_write_parents(struct ntfs_inode *ni, int sync)
++{
++      int err = 0;
++      struct ATTRIB *attr = NULL;
++      struct ATTR_LIST_ENTRY *le = NULL;
++      struct ntfs_sb_info *sbi = ni->mi.sbi;
++      struct super_block *sb = sbi->sb;
++
++      while ((attr = ni_find_attr(ni, attr, &le, ATTR_NAME, NULL, 0, NULL,
++                                  NULL))) {
++              struct inode *dir;
++              struct ATTR_FILE_NAME *fname;
++
++              fname = resident_data_ex(attr, SIZEOF_ATTRIBUTE_FILENAME);
++              if (!fname)
++                      continue;
++
++              /* Check simple case when parent inode equals current inode. */
++              if (ino_get(&fname->home) == ni->vfs_inode.i_ino) {
++                      if (MFT_REC_ROOT != ni->vfs_inode.i_ino) {
++                              ntfs_set_state(sbi, NTFS_DIRTY_ERROR);
++                              err = -EINVAL;
++                      }
++                      continue;
++              }
++
++              dir = ntfs_iget5(sb, &fname->home, NULL);
++              if (IS_ERR(dir)) {
++                      ntfs_inode_warn(
++                              &ni->vfs_inode,
++                              "failed to open parent directory r=%lx to write",
++                              (long)ino_get(&fname->home));
++                      continue;
++              }
++
++              if (!is_bad_inode(dir)) {
++                      int err2 = write_inode_now(dir, sync);
++                      if (!err)
++                              err = err2;
++              }
++              iput(dir);
++      }
++
++      return err;
++}
++
+ /*
+  * ni_update_parent
+  *
+diff --git a/fs/ntfs3/ntfs_fs.h b/fs/ntfs3/ntfs_fs.h
+index 31f57ee9a1a9b4..e534095078e89f 100644
+--- a/fs/ntfs3/ntfs_fs.h
++++ b/fs/ntfs3/ntfs_fs.h
+@@ -503,6 +503,7 @@ int ntfs3_setattr(struct mnt_idmap *idmap, struct dentry *dentry,
+ int ntfs_file_open(struct inode *inode, struct file *file);
+ int ntfs_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo,
+               __u64 start, __u64 len);
++int ntfs_file_fsync(struct file *file, loff_t start, loff_t end, int datasync);
+ extern const struct inode_operations ntfs_special_inode_operations;
+ extern const struct inode_operations ntfs_file_inode_operations;
+ extern const struct file_operations ntfs_file_operations;
+@@ -580,6 +581,7 @@ int ni_rename(struct ntfs_inode *dir_ni, struct ntfs_inode *new_dir_ni,
+             struct ntfs_inode *ni, struct NTFS_DE *de, struct NTFS_DE *new_de);
+ bool ni_is_dirty(struct inode *inode);
++int ni_write_parents(struct ntfs_inode *ni, int sync);
+ /* Globals from fslog.c */
+ bool check_index_header(const struct INDEX_HDR *hdr, size_t bytes);
+-- 
+2.53.0
+
diff --git a/queue-6.6/fs-ntfs3-handle-attr_set_size-errors-when-truncating.patch b/queue-6.6/fs-ntfs3-handle-attr_set_size-errors-when-truncating.patch
new file mode 100644 (file)
index 0000000..e8e9e65
--- /dev/null
@@ -0,0 +1,68 @@
+From 3ba2267f3e6d4e7a5ea3d8fd921e60505a764bd4 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 12 Dec 2025 14:33:19 +0300
+Subject: fs/ntfs3: handle attr_set_size() errors when truncating files
+
+From: Konstantin Komarov <almaz.alexandrovich@paragon-software.com>
+
+[ Upstream commit 576248a34b927e93b2fd3fff7df735ba73ad7d01 ]
+
+If attr_set_size() fails while truncating down, the error is silently
+ignored and the inode may be left in an inconsistent state.
+
+Signed-off-by: Konstantin Komarov <almaz.alexandrovich@paragon-software.com>
+Stable-dep-of: d7ea8495fd30 ("fs/ntfs3: fix missing run load for vcn0 in attr_data_get_block_locked()")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ fs/ntfs3/file.c | 10 ++++------
+ 1 file changed, 4 insertions(+), 6 deletions(-)
+
+diff --git a/fs/ntfs3/file.c b/fs/ntfs3/file.c
+index 89e35825c5c49d..3eee810c69e2f7 100644
+--- a/fs/ntfs3/file.c
++++ b/fs/ntfs3/file.c
+@@ -371,8 +371,8 @@ static int ntfs_truncate(struct inode *inode, loff_t new_size)
+ {
+       struct super_block *sb = inode->i_sb;
+       struct ntfs_inode *ni = ntfs_i(inode);
+-      int err, dirty = 0;
+       u64 new_valid;
++      int err;
+       if (!S_ISREG(inode->i_mode))
+               return 0;
+@@ -388,7 +388,6 @@ static int ntfs_truncate(struct inode *inode, loff_t new_size)
+       }
+       new_valid = ntfs_up_block(sb, min_t(u64, ni->i_valid, new_size));
+-
+       truncate_setsize(inode, new_size);
+       ni_lock(ni);
+@@ -402,20 +401,19 @@ static int ntfs_truncate(struct inode *inode, loff_t new_size)
+               ni->i_valid = new_valid;
+       ni_unlock(ni);
++      if (unlikely(err))
++              return err;
+       ni->std_fa |= FILE_ATTRIBUTE_ARCHIVE;
+       inode->i_mtime = inode_set_ctime_current(inode);
+       if (!IS_DIRSYNC(inode)) {
+-              dirty = 1;
++              mark_inode_dirty(inode);
+       } else {
+               err = ntfs_sync_inode(inode);
+               if (err)
+                       return err;
+       }
+-      if (dirty)
+-              mark_inode_dirty(inode);
+-
+       /*ntfs_flush_inodes(inode->i_sb, inode, NULL);*/
+       return 0;
+-- 
+2.53.0
+
diff --git a/queue-6.6/fs-ntfs3-zero-fill-folios-beyond-i_valid-in-ntfs_rea.patch b/queue-6.6/fs-ntfs3-zero-fill-folios-beyond-i_valid-in-ntfs_rea.patch
new file mode 100644 (file)
index 0000000..3a3f4dd
--- /dev/null
@@ -0,0 +1,45 @@
+From 2074da1280581eb43546d1b920dd62464de87c03 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 12 Dec 2025 14:38:10 +0300
+Subject: fs/ntfs3: zero-fill folios beyond i_valid in ntfs_read_folio()
+
+From: Konstantin Komarov <almaz.alexandrovich@paragon-software.com>
+
+[ Upstream commit 356fa248168be90109b66f32a61b8eaedc98424a ]
+
+Handle ntfs_read_folio() early when the folio offset is beyond i_valid
+by zero-filling the folio and marking it uptodate. This avoids needless
+I/O and locking, improves read performance.
+
+Signed-off-by: Konstantin Komarov <almaz.alexandrovich@paragon-software.com>
+Stable-dep-of: d7ea8495fd30 ("fs/ntfs3: fix missing run load for vcn0 in attr_data_get_block_locked()")
+[ sashal: dropped the is_bad_ni() guard, 6.6 lacks 519b078998ce6e
+  ("fs/ntfs3: Exclude call make_bad_inode for live nodes.") which
+  introduces that helper; pre-patch 6.6 had no such check either ]
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ fs/ntfs3/inode.c | 8 ++++++++
+ 1 file changed, 8 insertions(+)
+
+diff --git a/fs/ntfs3/inode.c b/fs/ntfs3/inode.c
+index 787e15ba0ca406..fff604cd952793 100644
+--- a/fs/ntfs3/inode.c
++++ b/fs/ntfs3/inode.c
+@@ -715,6 +715,14 @@ static int ntfs_read_folio(struct file *file, struct folio *folio)
+       struct address_space *mapping = page->mapping;
+       struct inode *inode = mapping->host;
+       struct ntfs_inode *ni = ntfs_i(inode);
++      loff_t vbo = folio_pos(folio);
++
++      if (ni->i_valid <= vbo) {
++              folio_zero_range(folio, 0, folio_size(folio));
++              folio_mark_uptodate(folio);
++              folio_unlock(folio);
++              return 0;
++      }
+       if (is_resident(ni)) {
+               ni_lock(ni);
+-- 
+2.53.0
+
index c6e1a5dacd291a460f84f2f79dd0a3a55df3120d..a5d0f09e5dea5f1ed564f559e71791a39f2f0089 100644 (file)
@@ -174,3 +174,7 @@ bluetooth-bnep-pin-l2cap-connection-during-netdev-registration.patch
 bluetooth-btnxpuart-fix-out-of-bounds-firmware-read-in-nxp_recv_fw_req_v3.patch
 bluetooth-fix-uaf-in-bt_accept_dequeue.patch
 bluetooth-l2cap-validate-option-length-before-reading-conf-opt-value.patch
+fs-ntfs3-fsync-files-by-syncing-parent-inodes.patch
+fs-ntfs3-handle-attr_set_size-errors-when-truncating.patch
+fs-ntfs3-zero-fill-folios-beyond-i_valid-in-ntfs_rea.patch
+fs-ntfs3-fix-missing-run-load-for-vcn0-in-attr_data_.patch