]> git.ipfire.org Git - thirdparty/kernel/stable-queue.git/commitdiff
6.6-stable patches
authorGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Thu, 30 Jan 2025 08:59:27 +0000 (09:59 +0100)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Thu, 30 Jan 2025 08:59:27 +0000 (09:59 +0100)
added patches:
gfs2-truncate-address-space-when-flipping-gfs2_dif_jdata-flag.patch
libfs-add-simple_offset_empty.patch
libfs-add-simple_offset_rename-api.patch
libfs-define-a-minimum-directory-offset.patch
libfs-fix-simple_offset_rename_exchange.patch
libfs-re-arrange-locking-in-offset_iterate_dir.patch
libfs-replace-simple_offset-end-of-directory-detection.patch
libfs-return-enospc-when-the-directory-offset-range-is-exhausted.patch
libfs-use-d_children-list-to-iterate-simple_offset-directories.patch
revert-libfs-add-simple_offset_empty.patch
shmem-fix-shmem_rename2.patch
smb-client-handle-lack-of-ea-support-in-smb2_query_path_info.patch

13 files changed:
queue-6.6/gfs2-truncate-address-space-when-flipping-gfs2_dif_jdata-flag.patch [new file with mode: 0644]
queue-6.6/libfs-add-simple_offset_empty.patch [new file with mode: 0644]
queue-6.6/libfs-add-simple_offset_rename-api.patch [new file with mode: 0644]
queue-6.6/libfs-define-a-minimum-directory-offset.patch [new file with mode: 0644]
queue-6.6/libfs-fix-simple_offset_rename_exchange.patch [new file with mode: 0644]
queue-6.6/libfs-re-arrange-locking-in-offset_iterate_dir.patch [new file with mode: 0644]
queue-6.6/libfs-replace-simple_offset-end-of-directory-detection.patch [new file with mode: 0644]
queue-6.6/libfs-return-enospc-when-the-directory-offset-range-is-exhausted.patch [new file with mode: 0644]
queue-6.6/libfs-use-d_children-list-to-iterate-simple_offset-directories.patch [new file with mode: 0644]
queue-6.6/revert-libfs-add-simple_offset_empty.patch [new file with mode: 0644]
queue-6.6/series
queue-6.6/shmem-fix-shmem_rename2.patch [new file with mode: 0644]
queue-6.6/smb-client-handle-lack-of-ea-support-in-smb2_query_path_info.patch [new file with mode: 0644]

diff --git a/queue-6.6/gfs2-truncate-address-space-when-flipping-gfs2_dif_jdata-flag.patch b/queue-6.6/gfs2-truncate-address-space-when-flipping-gfs2_dif_jdata-flag.patch
new file mode 100644 (file)
index 0000000..4e0fcd6
--- /dev/null
@@ -0,0 +1,30 @@
+From 7c9d9223802fbed4dee1ae301661bf346964c9d2 Mon Sep 17 00:00:00 2001
+From: Andreas Gruenbacher <agruenba@redhat.com>
+Date: Mon, 13 Jan 2025 19:31:28 +0100
+Subject: gfs2: Truncate address space when flipping GFS2_DIF_JDATA flag
+
+From: Andreas Gruenbacher <agruenba@redhat.com>
+
+commit 7c9d9223802fbed4dee1ae301661bf346964c9d2 upstream.
+
+Truncate an inode's address space when flipping the GFS2_DIF_JDATA flag:
+depending on that flag, the pages in the address space will either use
+buffer heads or iomap_folio_state structs, and we cannot mix the two.
+
+Reported-by: Kun Hu <huk23@m.fudan.edu.cn>, Jiaji Qin <jjtan24@m.fudan.edu.cn>
+Signed-off-by: Andreas Gruenbacher <agruenba@redhat.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/gfs2/file.c |    1 +
+ 1 file changed, 1 insertion(+)
+
+--- a/fs/gfs2/file.c
++++ b/fs/gfs2/file.c
+@@ -251,6 +251,7 @@ static int do_gfs2_set_flags(struct inod
+               error = filemap_fdatawait(inode->i_mapping);
+               if (error)
+                       goto out;
++              truncate_inode_pages(inode->i_mapping, 0);
+               if (new_flags & GFS2_DIF_JDATA)
+                       gfs2_ordered_del_inode(ip);
+       }
diff --git a/queue-6.6/libfs-add-simple_offset_empty.patch b/queue-6.6/libfs-add-simple_offset_empty.patch
new file mode 100644 (file)
index 0000000..c77c405
--- /dev/null
@@ -0,0 +1,106 @@
+From stable+bounces-110403-greg=kroah.com@vger.kernel.org Fri Jan 24 20:20:42 2025
+From: cel@kernel.org
+Date: Fri, 24 Jan 2025 14:19:38 -0500
+Subject: libfs: Add simple_offset_empty()
+To: Hugh Dickins <hughd@google.com>, Andrew Morten <akpm@linux-foundation.org>, Christian Brauner <brauner@kernel.org>, Al Viro <viro@zeniv.linux.org.uk>, Greg Kroah-Hartman <gregkh@linuxfoundation.org>, Sasha Levin <sashal@kernel.org>
+Cc: <linux-fsdevel@vger.kernel.org>, <stable@vger.kernel.org>, <linux-mm@kvack.org>, yukuai3@huawei.com, yangerkun@huawei.com, Chuck Lever <chuck.lever@oracle.com>, Jan Kara <jack@suse.cz>
+Message-ID: <20250124191946.22308-4-cel@kernel.org>
+
+From: Chuck Lever <chuck.lever@oracle.com>
+
+[ Upstream commit ecba88a3b32d733d41e27973e25b2bc580f64281 ]
+
+For simple filesystems that use directory offset mapping, rely
+strictly on the directory offset map to tell when a directory has
+no children.
+
+After this patch is applied, the emptiness test holds only the RCU
+read lock when the directory being tested has no children.
+
+In addition, this adds another layer of confirmation that
+simple_offset_add/remove() are working as expected.
+
+Reviewed-by: Jan Kara <jack@suse.cz>
+Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
+Link: https://lore.kernel.org/r/170820143463.6328.7872919188371286951.stgit@91.116.238.104.host.secureserver.net
+Signed-off-by: Christian Brauner <brauner@kernel.org>
+Stable-dep-of: 5a1a25be995e ("libfs: Add simple_offset_rename() API")
+Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/libfs.c         |   32 ++++++++++++++++++++++++++++++++
+ include/linux/fs.h |    1 +
+ mm/shmem.c         |    4 ++--
+ 3 files changed, 35 insertions(+), 2 deletions(-)
+
+--- a/fs/libfs.c
++++ b/fs/libfs.c
+@@ -313,6 +313,38 @@ void simple_offset_remove(struct offset_
+ }
+ /**
++ * simple_offset_empty - Check if a dentry can be unlinked
++ * @dentry: dentry to be tested
++ *
++ * Returns 0 if @dentry is a non-empty directory; otherwise returns 1.
++ */
++int simple_offset_empty(struct dentry *dentry)
++{
++      struct inode *inode = d_inode(dentry);
++      struct offset_ctx *octx;
++      struct dentry *child;
++      unsigned long index;
++      int ret = 1;
++
++      if (!inode || !S_ISDIR(inode->i_mode))
++              return ret;
++
++      index = DIR_OFFSET_MIN;
++      octx = inode->i_op->get_offset_ctx(inode);
++      xa_for_each(&octx->xa, index, child) {
++              spin_lock(&child->d_lock);
++              if (simple_positive(child)) {
++                      spin_unlock(&child->d_lock);
++                      ret = 0;
++                      break;
++              }
++              spin_unlock(&child->d_lock);
++      }
++
++      return ret;
++}
++
++/**
+  * simple_offset_rename_exchange - exchange rename with directory offsets
+  * @old_dir: parent of dentry being moved
+  * @old_dentry: dentry being moved
+--- a/include/linux/fs.h
++++ b/include/linux/fs.h
+@@ -3197,6 +3197,7 @@ struct offset_ctx {
+ void simple_offset_init(struct offset_ctx *octx);
+ int simple_offset_add(struct offset_ctx *octx, struct dentry *dentry);
+ void simple_offset_remove(struct offset_ctx *octx, struct dentry *dentry);
++int simple_offset_empty(struct dentry *dentry);
+ int simple_offset_rename_exchange(struct inode *old_dir,
+                                 struct dentry *old_dentry,
+                                 struct inode *new_dir,
+--- a/mm/shmem.c
++++ b/mm/shmem.c
+@@ -3368,7 +3368,7 @@ static int shmem_unlink(struct inode *di
+ static int shmem_rmdir(struct inode *dir, struct dentry *dentry)
+ {
+-      if (!simple_empty(dentry))
++      if (!simple_offset_empty(dentry))
+               return -ENOTEMPTY;
+       drop_nlink(d_inode(dentry));
+@@ -3425,7 +3425,7 @@ static int shmem_rename2(struct mnt_idma
+               return simple_offset_rename_exchange(old_dir, old_dentry,
+                                                    new_dir, new_dentry);
+-      if (!simple_empty(new_dentry))
++      if (!simple_offset_empty(new_dentry))
+               return -ENOTEMPTY;
+       if (flags & RENAME_WHITEOUT) {
diff --git a/queue-6.6/libfs-add-simple_offset_rename-api.patch b/queue-6.6/libfs-add-simple_offset_rename-api.patch
new file mode 100644 (file)
index 0000000..58aa1bc
--- /dev/null
@@ -0,0 +1,79 @@
+From stable+bounces-110405-greg=kroah.com@vger.kernel.org Fri Jan 24 20:21:49 2025
+From: cel@kernel.org
+Date: Fri, 24 Jan 2025 14:19:40 -0500
+Subject: libfs: Add simple_offset_rename() API
+To: Hugh Dickins <hughd@google.com>, Andrew Morten <akpm@linux-foundation.org>, Christian Brauner <brauner@kernel.org>, Al Viro <viro@zeniv.linux.org.uk>, Greg Kroah-Hartman <gregkh@linuxfoundation.org>, Sasha Levin <sashal@kernel.org>
+Cc: <linux-fsdevel@vger.kernel.org>, <stable@vger.kernel.org>, <linux-mm@kvack.org>, yukuai3@huawei.com, yangerkun@huawei.com, Chuck Lever <chuck.lever@oracle.com>
+Message-ID: <20250124191946.22308-6-cel@kernel.org>
+
+From: Chuck Lever <chuck.lever@oracle.com>
+
+[ Upstream commit 5a1a25be995e1014abd01600479915683e356f5c ]
+
+I'm about to fix a tmpfs rename bug that requires the use of
+internal simple_offset helpers that are not available in mm/shmem.c
+
+Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
+Link: https://lore.kernel.org/r/20240415152057.4605-3-cel@kernel.org
+Signed-off-by: Christian Brauner <brauner@kernel.org>
+Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/libfs.c         |   21 +++++++++++++++++++++
+ include/linux/fs.h |    2 ++
+ mm/shmem.c         |    3 +--
+ 3 files changed, 24 insertions(+), 2 deletions(-)
+
+--- a/fs/libfs.c
++++ b/fs/libfs.c
+@@ -357,6 +357,27 @@ int simple_offset_empty(struct dentry *d
+ }
+ /**
++ * simple_offset_rename - handle directory offsets for rename
++ * @old_dir: parent directory of source entry
++ * @old_dentry: dentry of source entry
++ * @new_dir: parent_directory of destination entry
++ * @new_dentry: dentry of destination
++ *
++ * Caller provides appropriate serialization.
++ *
++ * Returns zero on success, a negative errno value on failure.
++ */
++int simple_offset_rename(struct inode *old_dir, struct dentry *old_dentry,
++                       struct inode *new_dir, struct dentry *new_dentry)
++{
++      struct offset_ctx *old_ctx = old_dir->i_op->get_offset_ctx(old_dir);
++      struct offset_ctx *new_ctx = new_dir->i_op->get_offset_ctx(new_dir);
++
++      simple_offset_remove(old_ctx, old_dentry);
++      return simple_offset_add(new_ctx, old_dentry);
++}
++
++/**
+  * simple_offset_rename_exchange - exchange rename with directory offsets
+  * @old_dir: parent of dentry being moved
+  * @old_dentry: dentry being moved
+--- a/include/linux/fs.h
++++ b/include/linux/fs.h
+@@ -3198,6 +3198,8 @@ void simple_offset_init(struct offset_ct
+ int simple_offset_add(struct offset_ctx *octx, struct dentry *dentry);
+ void simple_offset_remove(struct offset_ctx *octx, struct dentry *dentry);
+ int simple_offset_empty(struct dentry *dentry);
++int simple_offset_rename(struct inode *old_dir, struct dentry *old_dentry,
++                       struct inode *new_dir, struct dentry *new_dentry);
+ int simple_offset_rename_exchange(struct inode *old_dir,
+                                 struct dentry *old_dentry,
+                                 struct inode *new_dir,
+--- a/mm/shmem.c
++++ b/mm/shmem.c
+@@ -3434,8 +3434,7 @@ static int shmem_rename2(struct mnt_idma
+                       return error;
+       }
+-      simple_offset_remove(shmem_get_offset_ctx(old_dir), old_dentry);
+-      error = simple_offset_add(shmem_get_offset_ctx(new_dir), old_dentry);
++      error = simple_offset_rename(old_dir, old_dentry, new_dir, new_dentry);
+       if (error)
+               return error;
diff --git a/queue-6.6/libfs-define-a-minimum-directory-offset.patch b/queue-6.6/libfs-define-a-minimum-directory-offset.patch
new file mode 100644 (file)
index 0000000..023ccc1
--- /dev/null
@@ -0,0 +1,69 @@
+From stable+bounces-110402-greg=kroah.com@vger.kernel.org Fri Jan 24 20:21:35 2025
+From: cel@kernel.org
+Date: Fri, 24 Jan 2025 14:19:37 -0500
+Subject: libfs: Define a minimum directory offset
+To: Hugh Dickins <hughd@google.com>, Andrew Morten <akpm@linux-foundation.org>, Christian Brauner <brauner@kernel.org>, Al Viro <viro@zeniv.linux.org.uk>, Greg Kroah-Hartman <gregkh@linuxfoundation.org>, Sasha Levin <sashal@kernel.org>
+Cc: <linux-fsdevel@vger.kernel.org>, <stable@vger.kernel.org>, <linux-mm@kvack.org>, yukuai3@huawei.com, yangerkun@huawei.com, Chuck Lever <chuck.lever@oracle.com>, Jan Kara <jack@suse.cz>
+Message-ID: <20250124191946.22308-3-cel@kernel.org>
+
+From: Chuck Lever <chuck.lever@oracle.com>
+
+[ Upstream commit 7beea725a8ca412c6190090ce7c3a13b169592a1 ]
+
+This value is used in several places, so make it a symbolic
+constant.
+
+Reviewed-by: Jan Kara <jack@suse.cz>
+Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
+Link: https://lore.kernel.org/r/170820142741.6328.12428356024575347885.stgit@91.116.238.104.host.secureserver.net
+Signed-off-by: Christian Brauner <brauner@kernel.org>
+Stable-dep-of: ecba88a3b32d ("libfs: Add simple_offset_empty()")
+Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/libfs.c |   13 ++++++++-----
+ 1 file changed, 8 insertions(+), 5 deletions(-)
+
+--- a/fs/libfs.c
++++ b/fs/libfs.c
+@@ -239,6 +239,11 @@ const struct inode_operations simple_dir
+ };
+ EXPORT_SYMBOL(simple_dir_inode_operations);
++/* 0 is '.', 1 is '..', so always start with offset 2 or more */
++enum {
++      DIR_OFFSET_MIN  = 2,
++};
++
+ static void offset_set(struct dentry *dentry, u32 offset)
+ {
+       dentry->d_fsdata = (void *)((uintptr_t)(offset));
+@@ -260,9 +265,7 @@ void simple_offset_init(struct offset_ct
+ {
+       xa_init_flags(&octx->xa, XA_FLAGS_ALLOC1);
+       lockdep_set_class(&octx->xa.xa_lock, &simple_offset_xa_lock);
+-
+-      /* 0 is '.', 1 is '..', so always start with offset 2 */
+-      octx->next_offset = 2;
++      octx->next_offset = DIR_OFFSET_MIN;
+ }
+ /**
+@@ -275,7 +278,7 @@ void simple_offset_init(struct offset_ct
+  */
+ int simple_offset_add(struct offset_ctx *octx, struct dentry *dentry)
+ {
+-      static const struct xa_limit limit = XA_LIMIT(2, U32_MAX);
++      static const struct xa_limit limit = XA_LIMIT(DIR_OFFSET_MIN, U32_MAX);
+       u32 offset;
+       int ret;
+@@ -480,7 +483,7 @@ static int offset_readdir(struct file *f
+               return 0;
+       /* In this case, ->private_data is protected by f_pos_lock */
+-      if (ctx->pos == 2)
++      if (ctx->pos == DIR_OFFSET_MIN)
+               file->private_data = NULL;
+       else if (file->private_data == ERR_PTR(-ENOENT))
+               return 0;
diff --git a/queue-6.6/libfs-fix-simple_offset_rename_exchange.patch b/queue-6.6/libfs-fix-simple_offset_rename_exchange.patch
new file mode 100644 (file)
index 0000000..c3831a4
--- /dev/null
@@ -0,0 +1,85 @@
+From stable+bounces-110404-greg=kroah.com@vger.kernel.org Fri Jan 24 20:21:48 2025
+From: cel@kernel.org
+Date: Fri, 24 Jan 2025 14:19:39 -0500
+Subject: libfs: Fix simple_offset_rename_exchange()
+To: Hugh Dickins <hughd@google.com>, Andrew Morten <akpm@linux-foundation.org>, Christian Brauner <brauner@kernel.org>, Al Viro <viro@zeniv.linux.org.uk>, Greg Kroah-Hartman <gregkh@linuxfoundation.org>, Sasha Levin <sashal@kernel.org>
+Cc: <linux-fsdevel@vger.kernel.org>, <stable@vger.kernel.org>, <linux-mm@kvack.org>, yukuai3@huawei.com, yangerkun@huawei.com, Chuck Lever <chuck.lever@oracle.com>
+Message-ID: <20250124191946.22308-5-cel@kernel.org>
+
+From: Chuck Lever <chuck.lever@oracle.com>
+
+[ Upstream commit 23cdd0eed3f1fff3af323092b0b88945a7950d8e ]
+
+User space expects the replacement (old) directory entry to have
+the same directory offset after the rename.
+
+Suggested-by: Christian Brauner <brauner@kernel.org>
+Fixes: a2e459555c5f ("shmem: stable directory offsets")
+Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
+Link: https://lore.kernel.org/r/20240415152057.4605-2-cel@kernel.org
+Signed-off-by: Christian Brauner <brauner@kernel.org>
+[ cel: adjusted to apply to origin/linux-6.6.y ]
+Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/libfs.c |   25 +++++++++++++++++++------
+ 1 file changed, 19 insertions(+), 6 deletions(-)
+
+--- a/fs/libfs.c
++++ b/fs/libfs.c
+@@ -294,6 +294,18 @@ int simple_offset_add(struct offset_ctx
+       return 0;
+ }
++static int simple_offset_replace(struct offset_ctx *octx, struct dentry *dentry,
++                               long offset)
++{
++      void *ret;
++
++      ret = xa_store(&octx->xa, offset, dentry, GFP_KERNEL);
++      if (xa_is_err(ret))
++              return xa_err(ret);
++      offset_set(dentry, offset);
++      return 0;
++}
++
+ /**
+  * simple_offset_remove - Remove an entry to a directory's offset map
+  * @octx: directory offset ctx to be updated
+@@ -351,6 +363,9 @@ int simple_offset_empty(struct dentry *d
+  * @new_dir: destination parent
+  * @new_dentry: destination dentry
+  *
++ * This API preserves the directory offset values. Caller provides
++ * appropriate serialization.
++ *
+  * Returns zero on success. Otherwise a negative errno is returned and the
+  * rename is rolled back.
+  */
+@@ -368,11 +383,11 @@ int simple_offset_rename_exchange(struct
+       simple_offset_remove(old_ctx, old_dentry);
+       simple_offset_remove(new_ctx, new_dentry);
+-      ret = simple_offset_add(new_ctx, old_dentry);
++      ret = simple_offset_replace(new_ctx, old_dentry, new_index);
+       if (ret)
+               goto out_restore;
+-      ret = simple_offset_add(old_ctx, new_dentry);
++      ret = simple_offset_replace(old_ctx, new_dentry, old_index);
+       if (ret) {
+               simple_offset_remove(new_ctx, old_dentry);
+               goto out_restore;
+@@ -387,10 +402,8 @@ int simple_offset_rename_exchange(struct
+       return 0;
+ out_restore:
+-      offset_set(old_dentry, old_index);
+-      xa_store(&old_ctx->xa, old_index, old_dentry, GFP_KERNEL);
+-      offset_set(new_dentry, new_index);
+-      xa_store(&new_ctx->xa, new_index, new_dentry, GFP_KERNEL);
++      (void)simple_offset_replace(old_ctx, old_dentry, old_index);
++      (void)simple_offset_replace(new_ctx, new_dentry, new_index);
+       return ret;
+ }
diff --git a/queue-6.6/libfs-re-arrange-locking-in-offset_iterate_dir.patch b/queue-6.6/libfs-re-arrange-locking-in-offset_iterate_dir.patch
new file mode 100644 (file)
index 0000000..0151d1a
--- /dev/null
@@ -0,0 +1,85 @@
+From stable+bounces-110401-greg=kroah.com@vger.kernel.org Fri Jan 24 20:20:29 2025
+From: cel@kernel.org
+Date: Fri, 24 Jan 2025 14:19:36 -0500
+Subject: libfs: Re-arrange locking in offset_iterate_dir()
+To: Hugh Dickins <hughd@google.com>, Andrew Morten <akpm@linux-foundation.org>, Christian Brauner <brauner@kernel.org>, Al Viro <viro@zeniv.linux.org.uk>, Greg Kroah-Hartman <gregkh@linuxfoundation.org>, Sasha Levin <sashal@kernel.org>
+Cc: <linux-fsdevel@vger.kernel.org>, <stable@vger.kernel.org>, <linux-mm@kvack.org>, yukuai3@huawei.com, yangerkun@huawei.com, Chuck Lever <chuck.lever@oracle.com>, "Liam R. Howlett" <Liam.Howlett@Oracle.com>, Jan Kara <jack@suse.cz>
+Message-ID: <20250124191946.22308-2-cel@kernel.org>
+
+From: Chuck Lever <chuck.lever@oracle.com>
+
+[ Upstream commit 3f6d810665dfde0d33785420618ceb03fba0619d ]
+
+Liam and Matthew say that once the RCU read lock is released,
+xa_state is not safe to re-use for the next xas_find() call. But the
+RCU read lock must be released on each loop iteration so that
+dput(), which might_sleep(), can be called safely.
+
+Thus we are forced to walk the offset tree with fresh state for each
+directory entry. xa_find() can do this for us, though it might be a
+little less efficient than maintaining xa_state locally.
+
+We believe that in the current code base, inode->i_rwsem provides
+protection for the xa_state maintained in
+offset_iterate_dir(). However, there is no guarantee that will
+continue to be the case in the future.
+
+Since offset_iterate_dir() doesn't build xa_state locally any more,
+there's no longer a strong need for offset_find_next(). Clean up by
+rolling these two helpers together.
+
+Suggested-by: Liam R. Howlett <Liam.Howlett@Oracle.com>
+Message-ID: <170785993027.11135.8830043889278631735.stgit@91.116.238.104.host.secureserver.net>
+Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
+Link: https://lore.kernel.org/r/170820142021.6328.15047865406275957018.stgit@91.116.238.104.host.secureserver.net
+Reviewed-by: Jan Kara <jack@suse.cz>
+Signed-off-by: Christian Brauner <brauner@kernel.org>
+Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/libfs.c |   12 ++++++------
+ 1 file changed, 6 insertions(+), 6 deletions(-)
+
+--- a/fs/libfs.c
++++ b/fs/libfs.c
+@@ -401,12 +401,13 @@ static loff_t offset_dir_llseek(struct f
+       return vfs_setpos(file, offset, U32_MAX);
+ }
+-static struct dentry *offset_find_next(struct xa_state *xas)
++static struct dentry *offset_find_next(struct offset_ctx *octx, loff_t offset)
+ {
+       struct dentry *child, *found = NULL;
++      XA_STATE(xas, &octx->xa, offset);
+       rcu_read_lock();
+-      child = xas_next_entry(xas, U32_MAX);
++      child = xas_next_entry(&xas, U32_MAX);
+       if (!child)
+               goto out;
+       spin_lock(&child->d_lock);
+@@ -429,12 +430,11 @@ static bool offset_dir_emit(struct dir_c
+ static void *offset_iterate_dir(struct inode *inode, struct dir_context *ctx)
+ {
+-      struct offset_ctx *so_ctx = inode->i_op->get_offset_ctx(inode);
+-      XA_STATE(xas, &so_ctx->xa, ctx->pos);
++      struct offset_ctx *octx = inode->i_op->get_offset_ctx(inode);
+       struct dentry *dentry;
+       while (true) {
+-              dentry = offset_find_next(&xas);
++              dentry = offset_find_next(octx, ctx->pos);
+               if (!dentry)
+                       return ERR_PTR(-ENOENT);
+@@ -443,8 +443,8 @@ static void *offset_iterate_dir(struct i
+                       break;
+               }
++              ctx->pos = dentry2offset(dentry) + 1;
+               dput(dentry);
+-              ctx->pos = xas.xa_index + 1;
+       }
+       return NULL;
+ }
diff --git a/queue-6.6/libfs-replace-simple_offset-end-of-directory-detection.patch b/queue-6.6/libfs-replace-simple_offset-end-of-directory-detection.patch
new file mode 100644 (file)
index 0000000..3df89d9
--- /dev/null
@@ -0,0 +1,154 @@
+From stable+bounces-110409-greg=kroah.com@vger.kernel.org Fri Jan 24 20:22:03 2025
+From: cel@kernel.org
+Date: Fri, 24 Jan 2025 14:19:44 -0500
+Subject: libfs: Replace simple_offset end-of-directory detection
+To: Hugh Dickins <hughd@google.com>, Andrew Morten <akpm@linux-foundation.org>, Christian Brauner <brauner@kernel.org>, Al Viro <viro@zeniv.linux.org.uk>, Greg Kroah-Hartman <gregkh@linuxfoundation.org>, Sasha Levin <sashal@kernel.org>
+Cc: <linux-fsdevel@vger.kernel.org>, <stable@vger.kernel.org>, <linux-mm@kvack.org>, yukuai3@huawei.com, yangerkun@huawei.com, Chuck Lever <chuck.lever@oracle.com>
+Message-ID: <20250124191946.22308-10-cel@kernel.org>
+
+From: Chuck Lever <chuck.lever@oracle.com>
+
+[ Upstream commit 68a3a65003145644efcbb651e91db249ccd96281 ]
+
+According to getdents(3), the d_off field in each returned directory
+entry points to the next entry in the directory. The d_off field in
+the last returned entry in the readdir buffer must contain a valid
+offset value, but if it points to an actual directory entry, then
+readdir/getdents can loop.
+
+This patch introduces a specific fixed offset value that is placed
+in the d_off field of the last entry in a directory. Some user space
+applications assume that the EOD offset value is larger than the
+offsets of real directory entries, so the largest valid offset value
+is reserved for this purpose. This new value is never allocated by
+simple_offset_add().
+
+When ->iterate_dir() returns, getdents{64} inserts the ctx->pos
+value into the d_off field of the last valid entry in the readdir
+buffer. When it hits EOD, offset_readdir() sets ctx->pos to the EOD
+offset value so the last entry is updated to point to the EOD marker.
+
+When trying to read the entry at the EOD offset, offset_readdir()
+terminates immediately.
+
+It is worth noting that using a Maple tree for directory offset
+value allocation does not guarantee a 63-bit range of values --
+on platforms where "long" is a 32-bit type, the directory offset
+value range is still 0..(2^31 - 1). For broad compatibility with
+32-bit user space, the largest tmpfs directory cookie value is now
+S32_MAX.
+
+Fixes: 796432efab1e ("libfs: getdents() should return 0 after reaching EOD")
+Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
+Link: https://lore.kernel.org/r/20241228175522.1854234-5-cel@kernel.org
+Signed-off-by: Christian Brauner <brauner@kernel.org>
+[ cel: adjusted to apply to origin/linux-6.6.y ]
+Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/libfs.c |   37 +++++++++++++++++++++----------------
+ 1 file changed, 21 insertions(+), 16 deletions(-)
+
+--- a/fs/libfs.c
++++ b/fs/libfs.c
+@@ -239,9 +239,15 @@ const struct inode_operations simple_dir
+ };
+ EXPORT_SYMBOL(simple_dir_inode_operations);
+-/* 0 is '.', 1 is '..', so always start with offset 2 or more */
++/* simple_offset_add() never assigns these to a dentry */
+ enum {
+-      DIR_OFFSET_MIN  = 2,
++      DIR_OFFSET_EOD          = S32_MAX,
++};
++
++/* simple_offset_add() allocation range */
++enum {
++      DIR_OFFSET_MIN          = 2,
++      DIR_OFFSET_MAX          = DIR_OFFSET_EOD - 1,
+ };
+ static void offset_set(struct dentry *dentry, u32 offset)
+@@ -278,7 +284,8 @@ void simple_offset_init(struct offset_ct
+  */
+ int simple_offset_add(struct offset_ctx *octx, struct dentry *dentry)
+ {
+-      static const struct xa_limit limit = XA_LIMIT(DIR_OFFSET_MIN, U32_MAX);
++      static const struct xa_limit limit = XA_LIMIT(DIR_OFFSET_MIN,
++                                                    DIR_OFFSET_MAX);
+       u32 offset;
+       int ret;
+@@ -442,8 +449,6 @@ static loff_t offset_dir_llseek(struct f
+               return -EINVAL;
+       }
+-      /* In this case, ->private_data is protected by f_pos_lock */
+-      file->private_data = NULL;
+       return vfs_setpos(file, offset, U32_MAX);
+ }
+@@ -453,7 +458,7 @@ static struct dentry *offset_find_next(s
+       XA_STATE(xas, &octx->xa, offset);
+       rcu_read_lock();
+-      child = xas_next_entry(&xas, U32_MAX);
++      child = xas_next_entry(&xas, DIR_OFFSET_MAX);
+       if (!child)
+               goto out;
+       spin_lock(&child->d_lock);
+@@ -474,7 +479,7 @@ static bool offset_dir_emit(struct dir_c
+                         inode->i_ino, fs_umode_to_dtype(inode->i_mode));
+ }
+-static void *offset_iterate_dir(struct inode *inode, struct dir_context *ctx)
++static void offset_iterate_dir(struct inode *inode, struct dir_context *ctx)
+ {
+       struct offset_ctx *octx = inode->i_op->get_offset_ctx(inode);
+       struct dentry *dentry;
+@@ -482,7 +487,7 @@ static void *offset_iterate_dir(struct i
+       while (true) {
+               dentry = offset_find_next(octx, ctx->pos);
+               if (!dentry)
+-                      return ERR_PTR(-ENOENT);
++                      goto out_eod;
+               if (!offset_dir_emit(ctx, dentry)) {
+                       dput(dentry);
+@@ -492,7 +497,10 @@ static void *offset_iterate_dir(struct i
+               ctx->pos = dentry2offset(dentry) + 1;
+               dput(dentry);
+       }
+-      return NULL;
++      return;
++
++out_eod:
++      ctx->pos = DIR_OFFSET_EOD;
+ }
+ /**
+@@ -512,6 +520,8 @@ static void *offset_iterate_dir(struct i
+  *
+  * On return, @ctx->pos contains an offset that will read the next entry
+  * in this directory when offset_readdir() is called again with @ctx.
++ * Caller places this value in the d_off field of the last entry in the
++ * user's buffer.
+  *
+  * Return values:
+  *   %0 - Complete
+@@ -524,13 +534,8 @@ static int offset_readdir(struct file *f
+       if (!dir_emit_dots(file, ctx))
+               return 0;
+-
+-      /* In this case, ->private_data is protected by f_pos_lock */
+-      if (ctx->pos == DIR_OFFSET_MIN)
+-              file->private_data = NULL;
+-      else if (file->private_data == ERR_PTR(-ENOENT))
+-              return 0;
+-      file->private_data = offset_iterate_dir(d_inode(dir), ctx);
++      if (ctx->pos != DIR_OFFSET_EOD)
++              offset_iterate_dir(d_inode(dir), ctx);
+       return 0;
+ }
diff --git a/queue-6.6/libfs-return-enospc-when-the-directory-offset-range-is-exhausted.patch b/queue-6.6/libfs-return-enospc-when-the-directory-offset-range-is-exhausted.patch
new file mode 100644 (file)
index 0000000..0edf62f
--- /dev/null
@@ -0,0 +1,51 @@
+From stable+bounces-110407-greg=kroah.com@vger.kernel.org Fri Jan 24 20:20:27 2025
+From: cel@kernel.org
+Date: Fri, 24 Jan 2025 14:19:42 -0500
+Subject: libfs: Return ENOSPC when the directory offset range is exhausted
+To: Hugh Dickins <hughd@google.com>, Andrew Morten <akpm@linux-foundation.org>, Christian Brauner <brauner@kernel.org>, Al Viro <viro@zeniv.linux.org.uk>, Greg Kroah-Hartman <gregkh@linuxfoundation.org>, Sasha Levin <sashal@kernel.org>
+Cc: <linux-fsdevel@vger.kernel.org>, <stable@vger.kernel.org>, <linux-mm@kvack.org>, yukuai3@huawei.com, yangerkun@huawei.com, Chuck Lever <chuck.lever@oracle.com>, Jeff Layton <jlayton@kernel.org>
+Message-ID: <20250124191946.22308-8-cel@kernel.org>
+
+From: Chuck Lever <chuck.lever@oracle.com>
+
+[ Upstream commit 903dc9c43a155e0893280c7472d4a9a3a83d75a6 ]
+
+Testing shows that the EBUSY error return from mtree_alloc_cyclic()
+leaks into user space. The ERRORS section of "man creat(2)" says:
+
+>      EBUSY   O_EXCL was specified in flags and pathname refers
+>              to a block device that is in use by the system
+>              (e.g., it is mounted).
+
+ENOSPC is closer to what applications expect in this situation.
+
+Note that the normal range of simple directory offset values is
+2..2^63, so hitting this error is going to be rare to impossible.
+
+Fixes: 6faddda69f62 ("libfs: Add directory operations for stable offsets")
+Cc: stable@vger.kernel.org # v6.9+
+Reviewed-by: Jeff Layton <jlayton@kernel.org>
+Reviewed-by: Yang Erkun <yangerkun@huawei.com>
+Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
+Link: https://lore.kernel.org/r/20241228175522.1854234-2-cel@kernel.org
+Signed-off-by: Christian Brauner <brauner@kernel.org>
+[ cel: adjusted to apply to origin/linux-6.6.y ]
+Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/libfs.c |    4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+--- a/fs/libfs.c
++++ b/fs/libfs.c
+@@ -287,8 +287,8 @@ int simple_offset_add(struct offset_ctx
+       ret = xa_alloc_cyclic(&octx->xa, &offset, dentry, limit,
+                             &octx->next_offset, GFP_KERNEL);
+-      if (ret < 0)
+-              return ret;
++      if (unlikely(ret < 0))
++              return ret == -EBUSY ? -ENOSPC : ret;
+       offset_set(dentry, offset);
+       return 0;
diff --git a/queue-6.6/libfs-use-d_children-list-to-iterate-simple_offset-directories.patch b/queue-6.6/libfs-use-d_children-list-to-iterate-simple_offset-directories.patch
new file mode 100644 (file)
index 0000000..2526ae5
--- /dev/null
@@ -0,0 +1,177 @@
+From stable+bounces-110410-greg=kroah.com@vger.kernel.org Fri Jan 24 20:22:00 2025
+From: cel@kernel.org
+Date: Fri, 24 Jan 2025 14:19:45 -0500
+Subject: libfs: Use d_children list to iterate simple_offset directories
+To: Hugh Dickins <hughd@google.com>, Andrew Morten <akpm@linux-foundation.org>, Christian Brauner <brauner@kernel.org>, Al Viro <viro@zeniv.linux.org.uk>, Greg Kroah-Hartman <gregkh@linuxfoundation.org>, Sasha Levin <sashal@kernel.org>
+Cc: <linux-fsdevel@vger.kernel.org>, <stable@vger.kernel.org>, <linux-mm@kvack.org>, yukuai3@huawei.com, yangerkun@huawei.com, Chuck Lever <chuck.lever@oracle.com>
+Message-ID: <20250124191946.22308-11-cel@kernel.org>
+
+From: Chuck Lever <chuck.lever@oracle.com>
+
+[ Upstream commit b9b588f22a0c049a14885399e27625635ae6ef91 ]
+
+The mtree mechanism has been effective at creating directory offsets
+that are stable over multiple opendir instances. However, it has not
+been able to handle the subtleties of renames that are concurrent
+with readdir.
+
+Instead of using the mtree to emit entries in the order of their
+offset values, use it only to map incoming ctx->pos to a starting
+entry. Then use the directory's d_children list, which is already
+maintained properly by the dcache, to find the next child to emit.
+
+One of the sneaky things about this is that when the mtree-allocated
+offset value wraps (which is very rare), looking up ctx->pos++ is
+not going to find the next entry; it will return NULL. Instead, by
+following the d_children list, the offset values can appear in any
+order but all of the entries in the directory will be visited
+eventually.
+
+Note also that the readdir() is guaranteed to reach the tail of this
+list. Entries are added only at the head of d_children, and readdir
+walks from its current position in that list towards its tail.
+
+Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
+Link: https://lore.kernel.org/r/20241228175522.1854234-6-cel@kernel.org
+Signed-off-by: Christian Brauner <brauner@kernel.org>
+[ cel: adjusted to apply to origin/linux-6.6.y ]
+Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/libfs.c |   84 ++++++++++++++++++++++++++++++++++++++++++-------------------
+ 1 file changed, 59 insertions(+), 25 deletions(-)
+
+--- a/fs/libfs.c
++++ b/fs/libfs.c
+@@ -241,12 +241,13 @@ EXPORT_SYMBOL(simple_dir_inode_operation
+ /* simple_offset_add() never assigns these to a dentry */
+ enum {
++      DIR_OFFSET_FIRST        = 2,            /* Find first real entry */
+       DIR_OFFSET_EOD          = S32_MAX,
+ };
+ /* simple_offset_add() allocation range */
+ enum {
+-      DIR_OFFSET_MIN          = 2,
++      DIR_OFFSET_MIN          = DIR_OFFSET_FIRST + 1,
+       DIR_OFFSET_MAX          = DIR_OFFSET_EOD - 1,
+ };
+@@ -452,51 +453,84 @@ static loff_t offset_dir_llseek(struct f
+       return vfs_setpos(file, offset, U32_MAX);
+ }
+-static struct dentry *offset_find_next(struct offset_ctx *octx, loff_t offset)
++static struct dentry *find_positive_dentry(struct dentry *parent,
++                                         struct dentry *dentry,
++                                         bool next)
++{
++      struct dentry *found = NULL;
++
++      spin_lock(&parent->d_lock);
++      if (next)
++              dentry = list_next_entry(dentry, d_child);
++      else if (!dentry)
++              dentry = list_first_entry_or_null(&parent->d_subdirs,
++                                                struct dentry, d_child);
++      for (; dentry && !list_entry_is_head(dentry, &parent->d_subdirs, d_child);
++           dentry = list_next_entry(dentry, d_child)) {
++              if (!simple_positive(dentry))
++                      continue;
++              spin_lock_nested(&dentry->d_lock, DENTRY_D_LOCK_NESTED);
++              if (simple_positive(dentry))
++                      found = dget_dlock(dentry);
++              spin_unlock(&dentry->d_lock);
++              if (likely(found))
++                      break;
++      }
++      spin_unlock(&parent->d_lock);
++      return found;
++}
++
++static noinline_for_stack struct dentry *
++offset_dir_lookup(struct dentry *parent, loff_t offset)
+ {
++      struct inode *inode = d_inode(parent);
++      struct offset_ctx *octx = inode->i_op->get_offset_ctx(inode);
+       struct dentry *child, *found = NULL;
++
+       XA_STATE(xas, &octx->xa, offset);
+-      rcu_read_lock();
+-      child = xas_next_entry(&xas, DIR_OFFSET_MAX);
+-      if (!child)
+-              goto out;
+-      spin_lock(&child->d_lock);
+-      if (simple_positive(child))
+-              found = dget_dlock(child);
+-      spin_unlock(&child->d_lock);
+-out:
+-      rcu_read_unlock();
++      if (offset == DIR_OFFSET_FIRST)
++              found = find_positive_dentry(parent, NULL, false);
++      else {
++              rcu_read_lock();
++              child = xas_next_entry(&xas, DIR_OFFSET_MAX);
++              found = find_positive_dentry(parent, child, false);
++              rcu_read_unlock();
++      }
+       return found;
+ }
+ static bool offset_dir_emit(struct dir_context *ctx, struct dentry *dentry)
+ {
+-      u32 offset = dentry2offset(dentry);
+       struct inode *inode = d_inode(dentry);
+-      return ctx->actor(ctx, dentry->d_name.name, dentry->d_name.len, offset,
+-                        inode->i_ino, fs_umode_to_dtype(inode->i_mode));
++      return dir_emit(ctx, dentry->d_name.name, dentry->d_name.len,
++                      inode->i_ino, fs_umode_to_dtype(inode->i_mode));
+ }
+-static void offset_iterate_dir(struct inode *inode, struct dir_context *ctx)
++static void offset_iterate_dir(struct file *file, struct dir_context *ctx)
+ {
+-      struct offset_ctx *octx = inode->i_op->get_offset_ctx(inode);
++      struct dentry *dir = file->f_path.dentry;
+       struct dentry *dentry;
++      dentry = offset_dir_lookup(dir, ctx->pos);
++      if (!dentry)
++              goto out_eod;
+       while (true) {
+-              dentry = offset_find_next(octx, ctx->pos);
+-              if (!dentry)
+-                      goto out_eod;
++              struct dentry *next;
+-              if (!offset_dir_emit(ctx, dentry)) {
+-                      dput(dentry);
++              ctx->pos = dentry2offset(dentry);
++              if (!offset_dir_emit(ctx, dentry))
+                       break;
+-              }
+-              ctx->pos = dentry2offset(dentry) + 1;
++              next = find_positive_dentry(dir, dentry, true);
+               dput(dentry);
++
++              if (!next)
++                      goto out_eod;
++              dentry = next;
+       }
++      dput(dentry);
+       return;
+ out_eod:
+@@ -535,7 +569,7 @@ static int offset_readdir(struct file *f
+       if (!dir_emit_dots(file, ctx))
+               return 0;
+       if (ctx->pos != DIR_OFFSET_EOD)
+-              offset_iterate_dir(d_inode(dir), ctx);
++              offset_iterate_dir(file, ctx);
+       return 0;
+ }
diff --git a/queue-6.6/revert-libfs-add-simple_offset_empty.patch b/queue-6.6/revert-libfs-add-simple_offset_empty.patch
new file mode 100644 (file)
index 0000000..d086a07
--- /dev/null
@@ -0,0 +1,104 @@
+From stable+bounces-110408-greg=kroah.com@vger.kernel.org Fri Jan 24 20:21:08 2025
+From: cel@kernel.org
+Date: Fri, 24 Jan 2025 14:19:43 -0500
+Subject: Revert "libfs: Add simple_offset_empty()"
+To: Hugh Dickins <hughd@google.com>, Andrew Morten <akpm@linux-foundation.org>, Christian Brauner <brauner@kernel.org>, Al Viro <viro@zeniv.linux.org.uk>, Greg Kroah-Hartman <gregkh@linuxfoundation.org>, Sasha Levin <sashal@kernel.org>
+Cc: <linux-fsdevel@vger.kernel.org>, <stable@vger.kernel.org>, <linux-mm@kvack.org>, yukuai3@huawei.com, yangerkun@huawei.com, Chuck Lever <chuck.lever@oracle.com>
+Message-ID: <20250124191946.22308-9-cel@kernel.org>
+
+From: Chuck Lever <chuck.lever@oracle.com>
+
+[ Upstream commit d7bde4f27ceef3dc6d72010a20d4da23db835a32 ]
+
+simple_empty() and simple_offset_empty() perform the same task.
+The latter's use as a canary to find bugs has not found any new
+issues. A subsequent patch will remove the use of the mtree for
+iterating directory contents, so revert back to using a similar
+mechanism for determining whether a directory is indeed empty.
+
+Only one such mechanism is ever needed.
+
+Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
+Link: https://lore.kernel.org/r/20241228175522.1854234-3-cel@kernel.org
+Reviewed-by: Yang Erkun <yangerkun@huawei.com>
+Signed-off-by: Christian Brauner <brauner@kernel.org>
+[ cel: adjusted to apply to origin/linux-6.6.y ]
+Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/libfs.c         |   32 --------------------------------
+ include/linux/fs.h |    1 -
+ mm/shmem.c         |    4 ++--
+ 3 files changed, 2 insertions(+), 35 deletions(-)
+
+--- a/fs/libfs.c
++++ b/fs/libfs.c
+@@ -325,38 +325,6 @@ void simple_offset_remove(struct offset_
+ }
+ /**
+- * simple_offset_empty - Check if a dentry can be unlinked
+- * @dentry: dentry to be tested
+- *
+- * Returns 0 if @dentry is a non-empty directory; otherwise returns 1.
+- */
+-int simple_offset_empty(struct dentry *dentry)
+-{
+-      struct inode *inode = d_inode(dentry);
+-      struct offset_ctx *octx;
+-      struct dentry *child;
+-      unsigned long index;
+-      int ret = 1;
+-
+-      if (!inode || !S_ISDIR(inode->i_mode))
+-              return ret;
+-
+-      index = DIR_OFFSET_MIN;
+-      octx = inode->i_op->get_offset_ctx(inode);
+-      xa_for_each(&octx->xa, index, child) {
+-              spin_lock(&child->d_lock);
+-              if (simple_positive(child)) {
+-                      spin_unlock(&child->d_lock);
+-                      ret = 0;
+-                      break;
+-              }
+-              spin_unlock(&child->d_lock);
+-      }
+-
+-      return ret;
+-}
+-
+-/**
+  * simple_offset_rename - handle directory offsets for rename
+  * @old_dir: parent directory of source entry
+  * @old_dentry: dentry of source entry
+--- a/include/linux/fs.h
++++ b/include/linux/fs.h
+@@ -3197,7 +3197,6 @@ struct offset_ctx {
+ void simple_offset_init(struct offset_ctx *octx);
+ int simple_offset_add(struct offset_ctx *octx, struct dentry *dentry);
+ void simple_offset_remove(struct offset_ctx *octx, struct dentry *dentry);
+-int simple_offset_empty(struct dentry *dentry);
+ int simple_offset_rename(struct inode *old_dir, struct dentry *old_dentry,
+                        struct inode *new_dir, struct dentry *new_dentry);
+ int simple_offset_rename_exchange(struct inode *old_dir,
+--- a/mm/shmem.c
++++ b/mm/shmem.c
+@@ -3368,7 +3368,7 @@ static int shmem_unlink(struct inode *di
+ static int shmem_rmdir(struct inode *dir, struct dentry *dentry)
+ {
+-      if (!simple_offset_empty(dentry))
++      if (!simple_empty(dentry))
+               return -ENOTEMPTY;
+       drop_nlink(d_inode(dentry));
+@@ -3425,7 +3425,7 @@ static int shmem_rename2(struct mnt_idma
+               return simple_offset_rename_exchange(old_dir, old_dentry,
+                                                    new_dir, new_dentry);
+-      if (!simple_offset_empty(new_dentry))
++      if (!simple_empty(new_dentry))
+               return -ENOTEMPTY;
+       if (flags & RENAME_WHITEOUT) {
index d9c1c34871749d0e7669faac9f227166f2c308ae..94ca9666d566afcbe05ae3223ea4edd3f7dc91ec 100644 (file)
@@ -11,3 +11,15 @@ ata-libata-core-set-ata_qcflag_rtf_filled-in-fill_result_tf.patch
 cpufreq-amd-pstate-add-check-for-cpufreq_cpu_get-s-return-value.patch
 ipv6-fix-soft-lockups-in-fib6_select_path-under-high-next-hop-churn.patch
 rdma-bnxt_re-avoid-cpu-lockups-due-fifo-occupancy-check-loop.patch
+gfs2-truncate-address-space-when-flipping-gfs2_dif_jdata-flag.patch
+libfs-re-arrange-locking-in-offset_iterate_dir.patch
+libfs-define-a-minimum-directory-offset.patch
+libfs-add-simple_offset_empty.patch
+libfs-fix-simple_offset_rename_exchange.patch
+libfs-add-simple_offset_rename-api.patch
+shmem-fix-shmem_rename2.patch
+libfs-return-enospc-when-the-directory-offset-range-is-exhausted.patch
+revert-libfs-add-simple_offset_empty.patch
+libfs-replace-simple_offset-end-of-directory-detection.patch
+libfs-use-d_children-list-to-iterate-simple_offset-directories.patch
+smb-client-handle-lack-of-ea-support-in-smb2_query_path_info.patch
diff --git a/queue-6.6/shmem-fix-shmem_rename2.patch b/queue-6.6/shmem-fix-shmem_rename2.patch
new file mode 100644 (file)
index 0000000..287112e
--- /dev/null
@@ -0,0 +1,54 @@
+From stable+bounces-110406-greg=kroah.com@vger.kernel.org Fri Jan 24 20:22:02 2025
+From: cel@kernel.org
+Date: Fri, 24 Jan 2025 14:19:41 -0500
+Subject: shmem: Fix shmem_rename2()
+To: Hugh Dickins <hughd@google.com>, Andrew Morten <akpm@linux-foundation.org>, Christian Brauner <brauner@kernel.org>, Al Viro <viro@zeniv.linux.org.uk>, Greg Kroah-Hartman <gregkh@linuxfoundation.org>, Sasha Levin <sashal@kernel.org>
+Cc: <linux-fsdevel@vger.kernel.org>, <stable@vger.kernel.org>, <linux-mm@kvack.org>, yukuai3@huawei.com, yangerkun@huawei.com, Chuck Lever <chuck.lever@oracle.com>
+Message-ID: <20250124191946.22308-7-cel@kernel.org>
+
+From: Chuck Lever <chuck.lever@oracle.com>
+
+[ Upstream commit ad191eb6d6942bb835a0b20b647f7c53c1d99ca4 ]
+
+When renaming onto an existing directory entry, user space expects
+the replacement entry to have the same directory offset as the
+original one.
+
+Link: https://gitlab.alpinelinux.org/alpine/aports/-/issues/15966
+Fixes: a2e459555c5f ("shmem: stable directory offsets")
+Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
+Link: https://lore.kernel.org/r/20240415152057.4605-4-cel@kernel.org
+Signed-off-by: Christian Brauner <brauner@kernel.org>
+Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/libfs.c |    9 +++++++++
+ 1 file changed, 9 insertions(+)
+
+--- a/fs/libfs.c
++++ b/fs/libfs.c
+@@ -365,6 +365,9 @@ int simple_offset_empty(struct dentry *d
+  *
+  * Caller provides appropriate serialization.
+  *
++ * User space expects the directory offset value of the replaced
++ * (new) directory entry to be unchanged after a rename.
++ *
+  * Returns zero on success, a negative errno value on failure.
+  */
+ int simple_offset_rename(struct inode *old_dir, struct dentry *old_dentry,
+@@ -372,8 +375,14 @@ int simple_offset_rename(struct inode *o
+ {
+       struct offset_ctx *old_ctx = old_dir->i_op->get_offset_ctx(old_dir);
+       struct offset_ctx *new_ctx = new_dir->i_op->get_offset_ctx(new_dir);
++      long new_offset = dentry2offset(new_dentry);
+       simple_offset_remove(old_ctx, old_dentry);
++
++      if (new_offset) {
++              offset_set(new_dentry, 0);
++              return simple_offset_replace(new_ctx, old_dentry, new_offset);
++      }
+       return simple_offset_add(new_ctx, old_dentry);
+ }
diff --git a/queue-6.6/smb-client-handle-lack-of-ea-support-in-smb2_query_path_info.patch b/queue-6.6/smb-client-handle-lack-of-ea-support-in-smb2_query_path_info.patch
new file mode 100644 (file)
index 0000000..d48e75f
--- /dev/null
@@ -0,0 +1,198 @@
+From 3681c74d342db75b0d641ba60de27bf73e16e66b Mon Sep 17 00:00:00 2001
+From: Paulo Alcantara <pc@manguebit.com>
+Date: Tue, 21 Jan 2025 15:25:36 -0300
+Subject: smb: client: handle lack of EA support in smb2_query_path_info()
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+From: Paulo Alcantara <pc@manguebit.com>
+
+commit 3681c74d342db75b0d641ba60de27bf73e16e66b upstream.
+
+If the server doesn't support both EAs and reparse point in a file,
+the SMB2_QUERY_INFO request will fail with either
+STATUS_NO_EAS_ON_FILE or STATUS_EAS_NOT_SUPPORT in the compound chain,
+so ignore it as long as reparse point isn't
+IO_REPARSE_TAG_LX_(CHR|BLK), which would require the EAs to know about
+major/minor numbers.
+
+Reported-by: Pali Rohár <pali@kernel.org>
+Signed-off-by: Paulo Alcantara (Red Hat) <pc@manguebit.com>
+Signed-off-by: Steve French <stfrench@microsoft.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/smb/client/smb2inode.c |   92 ++++++++++++++++++++++++++++++++++------------
+ 1 file changed, 69 insertions(+), 23 deletions(-)
+
+--- a/fs/smb/client/smb2inode.c
++++ b/fs/smb/client/smb2inode.c
+@@ -176,27 +176,27 @@ static int smb2_compound_op(const unsign
+                           struct kvec *out_iov, int *out_buftype, struct dentry *dentry)
+ {
+-      struct reparse_data_buffer *rbuf;
++      struct smb2_query_info_rsp *qi_rsp = NULL;
+       struct smb2_compound_vars *vars = NULL;
+-      struct kvec *rsp_iov, *iov;
+-      struct smb_rqst *rqst;
+-      int rc;
+-      __le16 *utf16_path = NULL;
+       __u8 oplock = SMB2_OPLOCK_LEVEL_NONE;
+-      struct cifs_fid fid;
++      struct cifs_open_info_data *idata;
+       struct cifs_ses *ses = tcon->ses;
++      struct reparse_data_buffer *rbuf;
+       struct TCP_Server_Info *server;
+-      int num_rqst = 0, i;
+       int resp_buftype[MAX_COMPOUND];
+-      struct smb2_query_info_rsp *qi_rsp = NULL;
+-      struct cifs_open_info_data *idata;
++      int retries = 0, cur_sleep = 1;
++      __u8 delete_pending[8] = {1,};
++      struct kvec *rsp_iov, *iov;
+       struct inode *inode = NULL;
+-      int flags = 0;
+-      __u8 delete_pending[8] = {1, 0, 0, 0, 0, 0, 0, 0};
++      __le16 *utf16_path = NULL;
++      struct smb_rqst *rqst;
+       unsigned int size[2];
+-      void *data[2];
++      struct cifs_fid fid;
++      int num_rqst = 0, i;
+       unsigned int len;
+-      int retries = 0, cur_sleep = 1;
++      int tmp_rc, rc;
++      int flags = 0;
++      void *data[2];
+ replay_again:
+       /* reinitialize for possible replay */
+@@ -637,7 +637,14 @@ finished:
+               tcon->need_reconnect = true;
+       }
++      tmp_rc = rc;
+       for (i = 0; i < num_cmds; i++) {
++              char *buf = rsp_iov[i + i].iov_base;
++
++              if (buf && resp_buftype[i + 1] != CIFS_NO_BUFFER)
++                      rc = server->ops->map_error(buf, false);
++              else
++                      rc = tmp_rc;
+               switch (cmds[i]) {
+               case SMB2_OP_QUERY_INFO:
+                       idata = in_iov[i].iov_base;
+@@ -803,6 +810,7 @@ finished:
+               }
+       }
+       SMB2_close_free(&rqst[num_rqst]);
++      rc = tmp_rc;
+       num_cmds += 2;
+       if (out_iov && out_buftype) {
+@@ -858,22 +866,52 @@ static int parse_create_response(struct
+       return rc;
+ }
++/* Check only if SMB2_OP_QUERY_WSL_EA command failed in the compound chain */
++static bool ea_unsupported(int *cmds, int num_cmds,
++                         struct kvec *out_iov, int *out_buftype)
++{
++      int i;
++
++      if (cmds[num_cmds - 1] != SMB2_OP_QUERY_WSL_EA)
++              return false;
++
++      for (i = 1; i < num_cmds - 1; i++) {
++              struct smb2_hdr *hdr = out_iov[i].iov_base;
++
++              if (out_buftype[i] == CIFS_NO_BUFFER || !hdr ||
++                  hdr->Status != STATUS_SUCCESS)
++                      return false;
++      }
++      return true;
++}
++
++static inline void free_rsp_iov(struct kvec *iovs, int *buftype, int count)
++{
++      int i;
++
++      for (i = 0; i < count; i++) {
++              free_rsp_buf(buftype[i], iovs[i].iov_base);
++              memset(&iovs[i], 0, sizeof(*iovs));
++              buftype[i] = CIFS_NO_BUFFER;
++      }
++}
++
+ int smb2_query_path_info(const unsigned int xid,
+                        struct cifs_tcon *tcon,
+                        struct cifs_sb_info *cifs_sb,
+                        const char *full_path,
+                        struct cifs_open_info_data *data)
+ {
++      struct kvec in_iov[3], out_iov[5] = {};
++      struct cached_fid *cfid = NULL;
+       struct cifs_open_parms oparms;
+-      __u32 create_options = 0;
+       struct cifsFileInfo *cfile;
+-      struct cached_fid *cfid = NULL;
++      __u32 create_options = 0;
++      int out_buftype[5] = {};
+       struct smb2_hdr *hdr;
+-      struct kvec in_iov[3], out_iov[3] = {};
+-      int out_buftype[3] = {};
++      int num_cmds = 0;
+       int cmds[3];
+       bool islink;
+-      int i, num_cmds = 0;
+       int rc, rc2;
+       data->adjust_tz = false;
+@@ -943,14 +981,14 @@ int smb2_query_path_info(const unsigned
+               if (rc || !data->reparse_point)
+                       goto out;
+-              if (!tcon->posix_extensions)
+-                      cmds[num_cmds++] = SMB2_OP_QUERY_WSL_EA;
+               /*
+                * Skip SMB2_OP_GET_REPARSE if symlink already parsed in create
+                * response.
+                */
+               if (data->reparse.tag != IO_REPARSE_TAG_SYMLINK)
+                       cmds[num_cmds++] = SMB2_OP_GET_REPARSE;
++              if (!tcon->posix_extensions)
++                      cmds[num_cmds++] = SMB2_OP_QUERY_WSL_EA;
+               oparms = CIFS_OPARMS(cifs_sb, tcon, full_path,
+                                    FILE_READ_ATTRIBUTES |
+@@ -958,9 +996,18 @@ int smb2_query_path_info(const unsigned
+                                    FILE_OPEN, create_options |
+                                    OPEN_REPARSE_POINT, ACL_NO_MODE);
+               cifs_get_readable_path(tcon, full_path, &cfile);
++              free_rsp_iov(out_iov, out_buftype, ARRAY_SIZE(out_iov));
+               rc = smb2_compound_op(xid, tcon, cifs_sb, full_path,
+                                     &oparms, in_iov, cmds, num_cmds,
+-                                    cfile, NULL, NULL, NULL);
++                                    cfile, out_iov, out_buftype, NULL);
++              if (rc && ea_unsupported(cmds, num_cmds,
++                                       out_iov, out_buftype)) {
++                      if (data->reparse.tag != IO_REPARSE_TAG_LX_BLK &&
++                          data->reparse.tag != IO_REPARSE_TAG_LX_CHR)
++                              rc = 0;
++                      else
++                              rc = -EOPNOTSUPP;
++              }
+               break;
+       case -EREMOTE:
+               break;
+@@ -978,8 +1025,7 @@ int smb2_query_path_info(const unsigned
+       }
+ out:
+-      for (i = 0; i < ARRAY_SIZE(out_buftype); i++)
+-              free_rsp_buf(out_buftype[i], out_iov[i].iov_base);
++      free_rsp_iov(out_iov, out_buftype, ARRAY_SIZE(out_iov));
+       return rc;
+ }