Please apply this series to these stable trees.
This series makes it possible to backport the fix for the BUG_ON check failure on rename operations reported by syzbot.
The first two patches are for dependency resolution. Patch 3/3 is the target patch, and it has been tailored to avoid extensive page/folio conversion.
This patch set has been tested against the latest stable kernels listed in the subject prefix.
Thanks, Ryusuke Konishi
Ryusuke Konishi (3): nilfs2: move page release outside of nilfs_delete_entry and nilfs_set_link nilfs2: eliminate staggered calls to kunmap in nilfs_rename nilfs2: handle errors that nilfs_prepare_chunk() may return
fs/nilfs2/dir.c | 24 +++++++++++------------- fs/nilfs2/namei.c | 37 ++++++++++++++++++++----------------- fs/nilfs2/nilfs.h | 10 ++++++++-- 3 files changed, 39 insertions(+), 32 deletions(-)
commit 584db20c181f5e28c0386d7987406ace7fbd3e49 upstream.
Patch series "nilfs2: Folio conversions for directory paths".
This series applies page->folio conversions to nilfs2 directory operations. This reduces hidden compound_head() calls and also converts deprecated kmap calls to kmap_local in the directory code.
Although nilfs2 does not yet support large folios, Matthew has done his best here to include support for large folios, which will be needed for devices with large block sizes.
This series corresponds to the second half of the original post [1], but with two complementary patches inserted at the beginning and some adjustments, to prevent a kmap_local constraint violation found during testing with highmem mapping.
[1] https://lkml.kernel.org/r/20231106173903.1734114-1-willy@infradead.org
I have reviewed all changes and tested this for regular and small block sizes, both on machines with and without highmem mapping. No issues found.
This patch (of 17):
In a few directory operations, the call to nilfs_put_page() for a page obtained using nilfs_find_entry() or nilfs_dotdot() is hidden in nilfs_set_link() and nilfs_delete_entry(), making it difficult to track page release and preventing change of its call position.
By moving nilfs_put_page() out of these functions, this makes the page get/put correspondence clearer and makes it easier to swap nilfs_put_page() calls (and kunmap calls within them) when modifying multiple directory entries simultaneously in nilfs_rename().
Also, update comments for nilfs_set_link() and nilfs_delete_entry() to reflect changes in their behavior.
To make nilfs_put_page() visible from namei.c, this moves its definition to nilfs.h and replaces existing equivalents to use it, but the exposure of that definition is temporary and will be removed on a later kmap -> kmap_local conversion.
Link: https://lkml.kernel.org/r/20231127143036.2425-1-konishi.ryusuke@gmail.com Link: https://lkml.kernel.org/r/20231127143036.2425-2-konishi.ryusuke@gmail.com Signed-off-by: Ryusuke Konishi konishi.ryusuke@gmail.com Reviewed-by: Matthew Wilcox (Oracle) willy@infradead.org Signed-off-by: Andrew Morton akpm@linux-foundation.org Stable-dep-of: ee70999a988b ("nilfs2: handle errors that nilfs_prepare_chunk() may return") --- fs/nilfs2/dir.c | 11 +---------- fs/nilfs2/namei.c | 13 +++++++------ fs/nilfs2/nilfs.h | 6 ++++++ 3 files changed, 14 insertions(+), 16 deletions(-)
diff --git a/fs/nilfs2/dir.c b/fs/nilfs2/dir.c index 889e3e570213..de040ab05d37 100644 --- a/fs/nilfs2/dir.c +++ b/fs/nilfs2/dir.c @@ -64,12 +64,6 @@ static inline unsigned int nilfs_chunk_size(struct inode *inode) return inode->i_sb->s_blocksize; }
-static inline void nilfs_put_page(struct page *page) -{ - kunmap(page); - put_page(page); -} - /* * Return the offset into page `page_nr' of the last valid * byte in that page, plus one. @@ -450,7 +444,6 @@ int nilfs_inode_by_name(struct inode *dir, const struct qstr *qstr, ino_t *ino) return 0; }
-/* Releases the page */ void nilfs_set_link(struct inode *dir, struct nilfs_dir_entry *de, struct page *page, struct inode *inode) { @@ -465,7 +458,6 @@ void nilfs_set_link(struct inode *dir, struct nilfs_dir_entry *de, de->inode = cpu_to_le64(inode->i_ino); nilfs_set_de_type(de, inode); nilfs_commit_chunk(page, mapping, from, to); - nilfs_put_page(page); dir->i_mtime = dir->i_ctime = current_time(dir); }
@@ -569,7 +561,7 @@ int nilfs_add_link(struct dentry *dentry, struct inode *inode)
/* * nilfs_delete_entry deletes a directory entry by merging it with the - * previous entry. Page is up-to-date. Releases the page. + * previous entry. Page is up-to-date. */ int nilfs_delete_entry(struct nilfs_dir_entry *dir, struct page *page) { @@ -605,7 +597,6 @@ int nilfs_delete_entry(struct nilfs_dir_entry *dir, struct page *page) nilfs_commit_chunk(page, mapping, from, to); inode->i_ctime = inode->i_mtime = current_time(inode); out: - nilfs_put_page(page); return err; }
diff --git a/fs/nilfs2/namei.c b/fs/nilfs2/namei.c index a14f6342a025..9283a707c013 100644 --- a/fs/nilfs2/namei.c +++ b/fs/nilfs2/namei.c @@ -297,6 +297,7 @@ static int nilfs_do_unlink(struct inode *dir, struct dentry *dentry) set_nlink(inode, 1); } err = nilfs_delete_entry(de, page); + nilfs_put_page(page); if (err) goto out;
@@ -406,6 +407,7 @@ static int nilfs_rename(struct user_namespace *mnt_userns, goto out_dir; } nilfs_set_link(new_dir, new_de, new_page, old_inode); + nilfs_put_page(new_page); nilfs_mark_inode_dirty(new_dir); new_inode->i_ctime = current_time(new_inode); if (dir_de) @@ -429,9 +431,11 @@ static int nilfs_rename(struct user_namespace *mnt_userns, old_inode->i_ctime = current_time(old_inode);
nilfs_delete_entry(old_de, old_page); + nilfs_put_page(old_page);
if (dir_de) { nilfs_set_link(old_inode, dir_de, dir_page, new_dir); + nilfs_put_page(dir_page); drop_nlink(old_dir); } nilfs_mark_inode_dirty(old_dir); @@ -441,13 +445,10 @@ static int nilfs_rename(struct user_namespace *mnt_userns, return err;
out_dir: - if (dir_de) { - kunmap(dir_page); - put_page(dir_page); - } + if (dir_de) + nilfs_put_page(dir_page); out_old: - kunmap(old_page); - put_page(old_page); + nilfs_put_page(old_page); out: nilfs_transaction_abort(old_dir->i_sb); return err; diff --git a/fs/nilfs2/nilfs.h b/fs/nilfs2/nilfs.h index 5a880b4edf3d..b577ca0575d7 100644 --- a/fs/nilfs2/nilfs.h +++ b/fs/nilfs2/nilfs.h @@ -243,6 +243,12 @@ extern struct nilfs_dir_entry *nilfs_dotdot(struct inode *, struct page **); extern void nilfs_set_link(struct inode *, struct nilfs_dir_entry *, struct page *, struct inode *);
+static inline void nilfs_put_page(struct page *page) +{ + kunmap(page); + put_page(page); +} + /* file.c */ extern int nilfs_sync_file(struct file *, loff_t, loff_t, int);
[ Sasha's backport helper bot ]
Hi,
Summary of potential issues: ℹ️ Patch is missing in 6.13.y (ignore if backport was sent) ⚠️ Commit missing in all newer stable branches
The upstream commit SHA1 provided is correct: 584db20c181f5e28c0386d7987406ace7fbd3e49
Status in newer kernel trees: 6.13.y | Present (exact SHA1) 6.12.y | Present (exact SHA1) 6.6.y | Not found 6.1.y | Not found
Note: The patch differs from the upstream commit: --- 1: 584db20c181f5 ! 1: da857438e7bd2 nilfs2: move page release outside of nilfs_delete_entry and nilfs_set_link @@ Metadata ## Commit message ## nilfs2: move page release outside of nilfs_delete_entry and nilfs_set_link
+ commit 584db20c181f5e28c0386d7987406ace7fbd3e49 upstream. + Patch series "nilfs2: Folio conversions for directory paths".
This series applies page->folio conversions to nilfs2 directory @@ Commit message sizes, both on machines with and without highmem mapping. No issues found.
- This patch (of 17):
In a few directory operations, the call to nilfs_put_page() for a page @@ Commit message Signed-off-by: Ryusuke Konishi konishi.ryusuke@gmail.com Reviewed-by: Matthew Wilcox (Oracle) willy@infradead.org Signed-off-by: Andrew Morton akpm@linux-foundation.org + Stable-dep-of: ee70999a988b ("nilfs2: handle errors that nilfs_prepare_chunk() may return")
## fs/nilfs2/dir.c ## @@ fs/nilfs2/dir.c: static inline unsigned int nilfs_chunk_size(struct inode *inode) @@ fs/nilfs2/dir.c: static inline unsigned int nilfs_chunk_size(struct inode *inode /* * Return the offset into page `page_nr' of the last valid * byte in that page, plus one. -@@ fs/nilfs2/dir.c: ino_t nilfs_inode_by_name(struct inode *dir, const struct qstr *qstr) - return res; +@@ fs/nilfs2/dir.c: int nilfs_inode_by_name(struct inode *dir, const struct qstr *qstr, ino_t *ino) + return 0; }
-/* Releases the page */ @@ fs/nilfs2/dir.c: void nilfs_set_link(struct inode *dir, struct nilfs_dir_entry * nilfs_set_de_type(de, inode); nilfs_commit_chunk(page, mapping, from, to); - nilfs_put_page(page); - inode_set_mtime_to_ts(dir, inode_set_ctime_current(dir)); + dir->i_mtime = dir->i_ctime = current_time(dir); }
@@ fs/nilfs2/dir.c: int nilfs_add_link(struct dentry *dentry, struct inode *inode) @@ fs/nilfs2/dir.c: int nilfs_add_link(struct dentry *dentry, struct inode *inode) { @@ fs/nilfs2/dir.c: int nilfs_delete_entry(struct nilfs_dir_entry *dir, struct page *page) nilfs_commit_chunk(page, mapping, from, to); - inode_set_mtime_to_ts(inode, inode_set_ctime_current(inode)); + inode->i_ctime = inode->i_mtime = current_time(inode); out: - nilfs_put_page(page); return err; @@ fs/nilfs2/namei.c: static int nilfs_do_unlink(struct inode *dir, struct dentry * if (err) goto out;
-@@ fs/nilfs2/namei.c: static int nilfs_rename(struct mnt_idmap *idmap, - if (!new_de) +@@ fs/nilfs2/namei.c: static int nilfs_rename(struct inode *old_dir, struct dentry *old_dentry, goto out_dir; + } nilfs_set_link(new_dir, new_de, new_page, old_inode); + nilfs_put_page(new_page); nilfs_mark_inode_dirty(new_dir); - inode_set_ctime_current(new_inode); + new_inode->i_ctime = current_time(new_inode); if (dir_de) -@@ fs/nilfs2/namei.c: static int nilfs_rename(struct mnt_idmap *idmap, - inode_set_ctime_current(old_inode); +@@ fs/nilfs2/namei.c: static int nilfs_rename(struct inode *old_dir, struct dentry *old_dentry, + old_inode->i_ctime = current_time(old_inode);
nilfs_delete_entry(old_de, old_page); + nilfs_put_page(old_page); @@ fs/nilfs2/namei.c: static int nilfs_rename(struct mnt_idmap *idmap, drop_nlink(old_dir); } nilfs_mark_inode_dirty(old_dir); -@@ fs/nilfs2/namei.c: static int nilfs_rename(struct mnt_idmap *idmap, +@@ fs/nilfs2/namei.c: static int nilfs_rename(struct inode *old_dir, struct dentry *old_dentry, return err;
out_dir: ---
Results of testing on various branches:
| Branch | Patch Apply | Build Test | |---------------------------|-------------|------------| | stable/linux-5.4.y | Success | Success | | stable/linux-5.10.y | Success | Success | | stable/linux-5.15.y | Success | Success | | stable/linux-6.1.y | Success | Success |
[ Sasha's backport helper bot ]
Hi,
Summary of potential issues: ℹ️ Patch is missing in 6.13.y (ignore if backport was sent) ⚠️ Commit missing in all newer stable branches
The upstream commit SHA1 provided is correct: 584db20c181f5e28c0386d7987406ace7fbd3e49
Status in newer kernel trees: 6.13.y | Present (exact SHA1) 6.12.y | Present (exact SHA1) 6.6.y | Present (different SHA1: 944a4f8f0b07) 6.1.y | Not found
Note: The patch differs from the upstream commit: --- 1: 584db20c181f5 ! 1: e7803a5bf15f3 nilfs2: move page release outside of nilfs_delete_entry and nilfs_set_link @@ Metadata ## Commit message ## nilfs2: move page release outside of nilfs_delete_entry and nilfs_set_link
+ commit 584db20c181f5e28c0386d7987406ace7fbd3e49 upstream. + Patch series "nilfs2: Folio conversions for directory paths".
This series applies page->folio conversions to nilfs2 directory @@ Commit message sizes, both on machines with and without highmem mapping. No issues found.
- This patch (of 17):
In a few directory operations, the call to nilfs_put_page() for a page @@ Commit message Signed-off-by: Ryusuke Konishi konishi.ryusuke@gmail.com Reviewed-by: Matthew Wilcox (Oracle) willy@infradead.org Signed-off-by: Andrew Morton akpm@linux-foundation.org + Stable-dep-of: ee70999a988b ("nilfs2: handle errors that nilfs_prepare_chunk() may return")
## fs/nilfs2/dir.c ## @@ fs/nilfs2/dir.c: static inline unsigned int nilfs_chunk_size(struct inode *inode) @@ fs/nilfs2/dir.c: static inline unsigned int nilfs_chunk_size(struct inode *inode /* * Return the offset into page `page_nr' of the last valid * byte in that page, plus one. -@@ fs/nilfs2/dir.c: ino_t nilfs_inode_by_name(struct inode *dir, const struct qstr *qstr) - return res; +@@ fs/nilfs2/dir.c: int nilfs_inode_by_name(struct inode *dir, const struct qstr *qstr, ino_t *ino) + return 0; }
-/* Releases the page */ @@ fs/nilfs2/dir.c: void nilfs_set_link(struct inode *dir, struct nilfs_dir_entry * nilfs_set_de_type(de, inode); nilfs_commit_chunk(page, mapping, from, to); - nilfs_put_page(page); - inode_set_mtime_to_ts(dir, inode_set_ctime_current(dir)); + dir->i_mtime = dir->i_ctime = current_time(dir); }
@@ fs/nilfs2/dir.c: int nilfs_add_link(struct dentry *dentry, struct inode *inode) @@ fs/nilfs2/dir.c: int nilfs_add_link(struct dentry *dentry, struct inode *inode) { @@ fs/nilfs2/dir.c: int nilfs_delete_entry(struct nilfs_dir_entry *dir, struct page *page) nilfs_commit_chunk(page, mapping, from, to); - inode_set_mtime_to_ts(inode, inode_set_ctime_current(inode)); + inode->i_ctime = inode->i_mtime = current_time(inode); out: - nilfs_put_page(page); return err; @@ fs/nilfs2/namei.c: static int nilfs_do_unlink(struct inode *dir, struct dentry * if (err) goto out;
-@@ fs/nilfs2/namei.c: static int nilfs_rename(struct mnt_idmap *idmap, - if (!new_de) +@@ fs/nilfs2/namei.c: static int nilfs_rename(struct inode *old_dir, struct dentry *old_dentry, goto out_dir; + } nilfs_set_link(new_dir, new_de, new_page, old_inode); + nilfs_put_page(new_page); nilfs_mark_inode_dirty(new_dir); - inode_set_ctime_current(new_inode); + new_inode->i_ctime = current_time(new_inode); if (dir_de) -@@ fs/nilfs2/namei.c: static int nilfs_rename(struct mnt_idmap *idmap, - inode_set_ctime_current(old_inode); +@@ fs/nilfs2/namei.c: static int nilfs_rename(struct inode *old_dir, struct dentry *old_dentry, + old_inode->i_ctime = current_time(old_inode);
nilfs_delete_entry(old_de, old_page); + nilfs_put_page(old_page); @@ fs/nilfs2/namei.c: static int nilfs_rename(struct mnt_idmap *idmap, drop_nlink(old_dir); } nilfs_mark_inode_dirty(old_dir); -@@ fs/nilfs2/namei.c: static int nilfs_rename(struct mnt_idmap *idmap, +@@ fs/nilfs2/namei.c: static int nilfs_rename(struct inode *old_dir, struct dentry *old_dentry, return err;
out_dir: ---
Results of testing on various branches:
| Branch | Patch Apply | Build Test | |---------------------------|-------------|------------| | stable/linux-5.4.y | Success | Success | | stable/linux-5.10.y | Success | Success | | stable/linux-5.15.y | Success | Success | | stable/linux-6.1.y | Success | Success |
commit 8cf57c6df818f58fdad16a909506be213623a88e upstream.
In nilfs_rename(), calls to nilfs_put_page() to release pages obtained with nilfs_find_entry() or nilfs_dotdot() are alternated in the normal path.
When replacing the kernel memory mapping method from kmap to kmap_local_{page,folio}, this violates the constraint on the calling order of kunmap_local().
Swap the order of nilfs_put_page calls where the kmap sections of multiple pages overlap so that they are nested, allowing direct replacement of nilfs_put_page() -> unmap_and_put_page().
Without this reordering, that replacement will cause a kernel WARNING in kunmap_local_indexed() on architectures with high memory mapping.
Link: https://lkml.kernel.org/r/20231127143036.2425-3-konishi.ryusuke@gmail.com Signed-off-by: Ryusuke Konishi konishi.ryusuke@gmail.com Reviewed-by: Matthew Wilcox (Oracle) willy@infradead.org Signed-off-by: Andrew Morton akpm@linux-foundation.org Stable-dep-of: ee70999a988b ("nilfs2: handle errors that nilfs_prepare_chunk() may return") --- fs/nilfs2/namei.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/fs/nilfs2/namei.c b/fs/nilfs2/namei.c index 9283a707c013..bbd27238b0e6 100644 --- a/fs/nilfs2/namei.c +++ b/fs/nilfs2/namei.c @@ -431,13 +431,14 @@ static int nilfs_rename(struct user_namespace *mnt_userns, old_inode->i_ctime = current_time(old_inode);
nilfs_delete_entry(old_de, old_page); - nilfs_put_page(old_page);
if (dir_de) { nilfs_set_link(old_inode, dir_de, dir_page, new_dir); nilfs_put_page(dir_page); drop_nlink(old_dir); } + nilfs_put_page(old_page); + nilfs_mark_inode_dirty(old_dir); nilfs_mark_inode_dirty(old_inode);
[ Sasha's backport helper bot ]
Hi,
Summary of potential issues: ℹ️ This is part 2/3 of a series ℹ️ Patch is missing in 6.13.y (ignore if backport was sent) ⚠️ Commit missing in all newer stable branches
The upstream commit SHA1 provided is correct: 8cf57c6df818f58fdad16a909506be213623a88e
Status in newer kernel trees: 6.13.y | Present (exact SHA1) 6.12.y | Present (exact SHA1) 6.6.y | Not found 6.1.y | Not found
Note: The patch differs from the upstream commit: --- 1: 8cf57c6df818f ! 1: 13fc7f2e9d896 nilfs2: eliminate staggered calls to kunmap in nilfs_rename @@ Metadata ## Commit message ## nilfs2: eliminate staggered calls to kunmap in nilfs_rename
+ commit 8cf57c6df818f58fdad16a909506be213623a88e upstream. + In nilfs_rename(), calls to nilfs_put_page() to release pages obtained with nilfs_find_entry() or nilfs_dotdot() are alternated in the normal path. @@ Commit message Signed-off-by: Ryusuke Konishi konishi.ryusuke@gmail.com Reviewed-by: Matthew Wilcox (Oracle) willy@infradead.org Signed-off-by: Andrew Morton akpm@linux-foundation.org + Stable-dep-of: ee70999a988b ("nilfs2: handle errors that nilfs_prepare_chunk() may return")
## fs/nilfs2/namei.c ## -@@ fs/nilfs2/namei.c: static int nilfs_rename(struct mnt_idmap *idmap, - inode_set_ctime_current(old_inode); +@@ fs/nilfs2/namei.c: static int nilfs_rename(struct inode *old_dir, struct dentry *old_dentry, + old_inode->i_ctime = current_time(old_inode);
nilfs_delete_entry(old_de, old_page); - nilfs_put_page(old_page); ---
NOTE: These results are for this patch alone. Full series testing will be performed when all parts are received.
Results of testing on various branches:
| Branch | Patch Apply | Build Test | |---------------------------|-------------|------------| | stable/linux-5.4.y | Success | Success | | stable/linux-5.10.y | Success | Success | | stable/linux-5.15.y | Success | Success | | stable/linux-6.1.y | Success | Success |
[ Sasha's backport helper bot ]
Hi,
Summary of potential issues: ℹ️ This is part 2/3 of a series ℹ️ Patch is missing in 6.13.y (ignore if backport was sent) ⚠️ Commit missing in all newer stable branches
The upstream commit SHA1 provided is correct: 8cf57c6df818f58fdad16a909506be213623a88e
Status in newer kernel trees: 6.13.y | Present (exact SHA1) 6.12.y | Present (exact SHA1) 6.6.y | Present (different SHA1: 35dcb8a3a70e) 6.1.y | Not found
Note: The patch differs from the upstream commit: --- 1: 8cf57c6df818f ! 1: 125b899dd0a8e nilfs2: eliminate staggered calls to kunmap in nilfs_rename @@ Metadata ## Commit message ## nilfs2: eliminate staggered calls to kunmap in nilfs_rename
+ commit 8cf57c6df818f58fdad16a909506be213623a88e upstream. + In nilfs_rename(), calls to nilfs_put_page() to release pages obtained with nilfs_find_entry() or nilfs_dotdot() are alternated in the normal path. @@ Commit message Signed-off-by: Ryusuke Konishi konishi.ryusuke@gmail.com Reviewed-by: Matthew Wilcox (Oracle) willy@infradead.org Signed-off-by: Andrew Morton akpm@linux-foundation.org + Stable-dep-of: ee70999a988b ("nilfs2: handle errors that nilfs_prepare_chunk() may return")
## fs/nilfs2/namei.c ## -@@ fs/nilfs2/namei.c: static int nilfs_rename(struct mnt_idmap *idmap, - inode_set_ctime_current(old_inode); +@@ fs/nilfs2/namei.c: static int nilfs_rename(struct inode *old_dir, struct dentry *old_dentry, + old_inode->i_ctime = current_time(old_inode);
nilfs_delete_entry(old_de, old_page); - nilfs_put_page(old_page); ---
NOTE: These results are for this patch alone. Full series testing will be performed when all parts are received.
Results of testing on various branches:
| Branch | Patch Apply | Build Test | |---------------------------|-------------|------------| | stable/linux-5.4.y | Success | Success | | stable/linux-5.10.y | Success | Success | | stable/linux-5.15.y | Success | Success | | stable/linux-6.1.y | Success | Success |
commit ee70999a988b8abc3490609142f50ebaa8344432 upstream.
Patch series "nilfs2: fix issues with rename operations".
This series fixes BUG_ON check failures reported by syzbot around rename operations, and a minor behavioral issue where the mtime of a child directory changes when it is renamed instead of moved.
This patch (of 2):
The directory manipulation routines nilfs_set_link() and nilfs_delete_entry() rewrite the directory entry in the folio/page previously read by nilfs_find_entry(), so error handling is omitted on the assumption that nilfs_prepare_chunk(), which prepares the buffer for rewriting, will always succeed for these. And if an error is returned, it triggers the legacy BUG_ON() checks in each routine.
This assumption is wrong, as proven by syzbot: the buffer layer called by nilfs_prepare_chunk() may call nilfs_get_block() if necessary, which may fail due to metadata corruption or other reasons. This has been there all along, but improved sanity checks and error handling may have made it more reproducible in fuzzing tests.
Fix this issue by adding missing error paths in nilfs_set_link(), nilfs_delete_entry(), and their caller nilfs_rename().
[konishi.ryusuke@gmail.com: adjusted for page/folio conversion] Link: https://lkml.kernel.org/r/20250111143518.7901-1-konishi.ryusuke@gmail.com Link: https://lkml.kernel.org/r/20250111143518.7901-2-konishi.ryusuke@gmail.com Signed-off-by: Ryusuke Konishi konishi.ryusuke@gmail.com Reported-by: syzbot+32c3706ebf5d95046ea1@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=32c3706ebf5d95046ea1 Reported-by: syzbot+1097e95f134f37d9395c@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=1097e95f134f37d9395c Fixes: 2ba466d74ed7 ("nilfs2: directory entry operations") Signed-off-by: Andrew Morton akpm@linux-foundation.org --- fs/nilfs2/dir.c | 13 ++++++++++--- fs/nilfs2/namei.c | 29 +++++++++++++++-------------- fs/nilfs2/nilfs.h | 4 ++-- 3 files changed, 27 insertions(+), 19 deletions(-)
diff --git a/fs/nilfs2/dir.c b/fs/nilfs2/dir.c index de040ab05d37..0f3753af1674 100644 --- a/fs/nilfs2/dir.c +++ b/fs/nilfs2/dir.c @@ -444,7 +444,7 @@ int nilfs_inode_by_name(struct inode *dir, const struct qstr *qstr, ino_t *ino) return 0; }
-void nilfs_set_link(struct inode *dir, struct nilfs_dir_entry *de, +int nilfs_set_link(struct inode *dir, struct nilfs_dir_entry *de, struct page *page, struct inode *inode) { unsigned int from = (char *)de - (char *)page_address(page); @@ -454,11 +454,15 @@ void nilfs_set_link(struct inode *dir, struct nilfs_dir_entry *de,
lock_page(page); err = nilfs_prepare_chunk(page, from, to); - BUG_ON(err); + if (unlikely(err)) { + unlock_page(page); + return err; + } de->inode = cpu_to_le64(inode->i_ino); nilfs_set_de_type(de, inode); nilfs_commit_chunk(page, mapping, from, to); dir->i_mtime = dir->i_ctime = current_time(dir); + return 0; }
/* @@ -590,7 +594,10 @@ int nilfs_delete_entry(struct nilfs_dir_entry *dir, struct page *page) from = (char *)pde - (char *)page_address(page); lock_page(page); err = nilfs_prepare_chunk(page, from, to); - BUG_ON(err); + if (unlikely(err)) { + unlock_page(page); + goto out; + } if (pde) pde->rec_len = nilfs_rec_len_to_disk(to - from); dir->inode = 0; diff --git a/fs/nilfs2/namei.c b/fs/nilfs2/namei.c index bbd27238b0e6..67d66207fae1 100644 --- a/fs/nilfs2/namei.c +++ b/fs/nilfs2/namei.c @@ -406,8 +406,10 @@ static int nilfs_rename(struct user_namespace *mnt_userns, err = PTR_ERR(new_de); goto out_dir; } - nilfs_set_link(new_dir, new_de, new_page, old_inode); + err = nilfs_set_link(new_dir, new_de, new_page, old_inode); nilfs_put_page(new_page); + if (unlikely(err)) + goto out_dir; nilfs_mark_inode_dirty(new_dir); new_inode->i_ctime = current_time(new_inode); if (dir_de) @@ -430,28 +432,27 @@ static int nilfs_rename(struct user_namespace *mnt_userns, */ old_inode->i_ctime = current_time(old_inode);
- nilfs_delete_entry(old_de, old_page); - - if (dir_de) { - nilfs_set_link(old_inode, dir_de, dir_page, new_dir); - nilfs_put_page(dir_page); - drop_nlink(old_dir); + err = nilfs_delete_entry(old_de, old_page); + if (likely(!err)) { + if (dir_de) { + err = nilfs_set_link(old_inode, dir_de, dir_page, + new_dir); + drop_nlink(old_dir); + } + nilfs_mark_inode_dirty(old_dir); } - nilfs_put_page(old_page); - - nilfs_mark_inode_dirty(old_dir); nilfs_mark_inode_dirty(old_inode);
- err = nilfs_transaction_commit(old_dir->i_sb); - return err; - out_dir: if (dir_de) nilfs_put_page(dir_page); out_old: nilfs_put_page(old_page); out: - nilfs_transaction_abort(old_dir->i_sb); + if (likely(!err)) + err = nilfs_transaction_commit(old_dir->i_sb); + else + nilfs_transaction_abort(old_dir->i_sb); return err; }
diff --git a/fs/nilfs2/nilfs.h b/fs/nilfs2/nilfs.h index b577ca0575d7..dadafad2fae7 100644 --- a/fs/nilfs2/nilfs.h +++ b/fs/nilfs2/nilfs.h @@ -240,8 +240,8 @@ nilfs_find_entry(struct inode *, const struct qstr *, struct page **); extern int nilfs_delete_entry(struct nilfs_dir_entry *, struct page *); extern int nilfs_empty_dir(struct inode *); extern struct nilfs_dir_entry *nilfs_dotdot(struct inode *, struct page **); -extern void nilfs_set_link(struct inode *, struct nilfs_dir_entry *, - struct page *, struct inode *); +int nilfs_set_link(struct inode *dir, struct nilfs_dir_entry *de, + struct page *page, struct inode *inode);
static inline void nilfs_put_page(struct page *page) {
[ Sasha's backport helper bot ]
Hi,
Summary of potential issues: ℹ️ This is part 3/3 of a series ℹ️ Patch is missing in 6.13.y (ignore if backport was sent) ⚠️ Commit missing in all newer stable branches
The upstream commit SHA1 provided is correct: ee70999a988b8abc3490609142f50ebaa8344432
Status in newer kernel trees: 6.13.y | Present (different SHA1: 481136234dfe) 6.12.y | Present (different SHA1: eddd3176b8c4) 6.6.y | Not found 6.1.y | Not found
Note: The patch differs from the upstream commit: --- 1: ee70999a988b8 < -: ------------- nilfs2: handle errors that nilfs_prepare_chunk() may return -: ------------- > 1: 4477ef0e62101 nilfs2: handle errors that nilfs_prepare_chunk() may return ---
NOTE: These results are for this patch alone. Full series testing will be performed when all parts are received.
Results of testing on various branches:
| Branch | Patch Apply | Build Test | |---------------------------|-------------|------------| | stable/linux-5.4.y | Success | Success | | stable/linux-5.10.y | Success | Success | | stable/linux-5.15.y | Success | Success | | stable/linux-6.1.y | Success | Success |
[ Sasha's backport helper bot ]
Hi,
Summary of potential issues: ℹ️ This is part 3/3 of a series ℹ️ Patch is missing in 6.13.y (ignore if backport was sent) ⚠️ Commit missing in all newer stable branches
The upstream commit SHA1 provided is correct: ee70999a988b8abc3490609142f50ebaa8344432
Status in newer kernel trees: 6.13.y | Present (different SHA1: 481136234dfe) 6.12.y | Present (different SHA1: eddd3176b8c4) 6.6.y | Present (different SHA1: 7891ac3b0a5c) 6.1.y | Not found
Note: The patch differs from the upstream commit: --- 1: ee70999a988b8 < -: ------------- nilfs2: handle errors that nilfs_prepare_chunk() may return -: ------------- > 1: 6030ff62ea011 nilfs2: handle errors that nilfs_prepare_chunk() may return ---
NOTE: These results are for this patch alone. Full series testing will be performed when all parts are received.
Results of testing on various branches:
| Branch | Patch Apply | Build Test | |---------------------------|-------------|------------| | stable/linux-5.4.y | Success | Success | | stable/linux-5.10.y | Success | Success | | stable/linux-5.15.y | Success | Success | | stable/linux-6.1.y | Success | Success |
linux-stable-mirror@lists.linaro.org