FAILED: patch "[PATCH] nilfs2: propagate directory read errors from" failed to apply to 5.4-stable tree
Subsystems (2)
[email protected]
maintainerThe patch below does not apply to the 5.4-stable tree.
If someone wants it applied there, or to any other stable or longterm tree, then please email the backport, including the original git commit id to <[email protected]>.
To reproduce the conflict and resubmit, you may use the following commands:
git fetch https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/ linux-5.4.y
git checkout FETCH_HEAD
git cherry-pick -x 08cfa12adf888db98879dbd735bc741360a34168
# <resolve conflicts, build, test, etc.>
git commit -s
git send-email --to '<[email protected]>' --in-reply-to '2024101854-exterior-cufflink-ccf4@gregkh' --subject-prefix 'PATCH 5.4.y' HEAD^..
Possible dependencies:
thanks,
greg k-h
------------------ original commit in Linus's tree ------------------
From 08cfa12adf888db98879dbd735bc741360a34168 Mon Sep 17 00:00:00 2001
From: Ryusuke Konishi <[email protected]>
Date: Fri, 4 Oct 2024 12:35:31 +0900
Subject: [PATCH] nilfs2: propagate directory read errors from
nilfs_find_entry()
Syzbot reported that a task hang occurs in vcs_open() during a fuzzing test for nilfs2.
The root cause of this problem is that in nilfs_find_entry(), which searches for directory entries, ignores errors when loading a directory page/folio via nilfs_get_folio() fails.
If the filesystem images is corrupted, and the i_size of the directory inode is large, and the directory page/folio is successfully read but fails the sanity check, for example when it is zero-filled, nilfs_check_folio() may continue to spit out error messages in bursts.
Fix this issue by propagating the error to the callers when loading a page/folio fails in nilfs_find_entry().
The current interface of nilfs_find_entry() and its callers is outdated and cannot propagate error codes such as -EIO and -ENOMEM returned via nilfs_find_entry(), so fix it together.
fs/nilfs2/dir.c +25 -23
@@ -289,7 +289,7 @@ static int nilfs_readdir(struct file *file, struct dir_context *ctx)* The folio is mapped and unlocked. When the caller is finished with * the entry, it should call folio_release_kmap(). * -* On failure, returns NULL and the caller should ignore foliop. +* On failure, returns an error pointer and the caller should ignore foliop. */ struct nilfs_dir_entry *nilfs_find_entry (struct inode *dir,const struct qstr *qstr,struct folio **foliop)
@@ -312,22 +312,24 @@ struct nilfs_dir_entry *nilfs_find_entry(struct inode *dir, do { char *kaddr = nilfs_get_folio(dir, n, foliop); - if (!IS_ERR(kaddr)) { - de = (struct nilfs_dir_entry *)kaddr; - kaddr += nilfs_last_byte(dir, n) - reclen; - while ((char *) de <= kaddr) { - if (de->rec_len == 0) { - nilfs_error(dir->i_sb, - "zero-length directory entry"); - folio_release_kmap(*foliop, kaddr); - goto out; - } - if (nilfs_match(namelen, name, de)) - goto found; - de = nilfs_next_entry(de); + if (IS_ERR(kaddr)) + return ERR_CAST(kaddr); + + de = (struct nilfs_dir_entry *)kaddr; + kaddr += nilfs_last_byte(dir, n) - reclen; + while ((char *)de <= kaddr) { + if (de->rec_len == 0) { + nilfs_error(dir->i_sb, + "zero-length directory entry"); + folio_release_kmap(*foliop, kaddr); + goto out; } - folio_release_kmap(*foliop, kaddr); + if (nilfs_match(namelen, name, de)) + goto found; + de = nilfs_next_entry(de); } + folio_release_kmap(*foliop, kaddr); + if (++n >= npages) n = 0; /* next folio is past the blocks we've got */
@@ -340,7 +342,7 @@ struct nilfs_dir_entry *nilfs_find_entry(struct inode *dir, } }while (n != start); out: -return NULL ; +return ERR_PTR (-ENOENT ); found: ei->i_dir_start_lookup = n;
@@ -384,18 +386,18 @@ fail:return NULL ; } -ino_t nilfs_inode_by_name (struct inode *dir,const struct qstr *qstr) +int nilfs_inode_by_name (struct inode *dir,const struct qstr *qstr,ino_t *ino) { -ino_t res =0 ;struct nilfs_dir_entry *de;struct folio *folio; de =nilfs_find_entry (dir, qstr, &folio); -if (de) { - res =le64_to_cpu (de->inode); -folio_release_kmap (folio, de); - } -return res; +if (IS_ERR (de)) +return PTR_ERR (de); + + *ino =le64_to_cpu (de->inode); +folio_release_kmap (folio, de); +return 0 ; } void nilfs_set_link(struct inode *dir, struct nilfs_dir_entry *de,
fs/nilfs2/namei.c +26 -13
@@ -55,12 +55,20 @@ nilfs_lookup(struct inode *dir, struct dentry *dentry, unsigned int flags) {struct inode *inode;ino_t ino; +int res;if (dentry->d_name.len >NILFS_NAME_LEN )return ERR_PTR (-ENAMETOOLONG ); - ino =nilfs_inode_by_name (dir, &dentry->d_name); - inode = ino ?nilfs_iget (dir->i_sb,NILFS_I (dir)->i_root, ino) :NULL ; + res =nilfs_inode_by_name (dir, &dentry->d_name, &ino); +if (res) { +if (res != -ENOENT ) +return ERR_PTR (res); + inode =NULL ; + }else { + inode =nilfs_iget (dir->i_sb,NILFS_I (dir)->i_root, ino); + } +return d_splice_alias (inode, dentry); }
@@ -263,10 +271,11 @@ static int nilfs_do_unlink(struct inode *dir, struct dentry *dentry)struct folio *folio;int err; - err = -ENOENT ; de =nilfs_find_entry (dir, &dentry->d_name, &folio); -if (!de) +if (IS_ERR (de)) { + err =PTR_ERR (de); goto out; + } inode =d_inode (dentry); err = -EIO ;
@@ -362,10 +371,11 @@ static int nilfs_rename(struct mnt_idmap *idmap,if (unlikely (err))return err; - err = -ENOENT ; old_de =nilfs_find_entry (old_dir, &old_dentry->d_name, &old_folio); -if (!old_de) +if (IS_ERR (old_de)) { + err =PTR_ERR (old_de); goto out; + }if (S_ISDIR (old_inode->i_mode)) { err = -EIO ;
@@ -382,10 +392,12 @@ static int nilfs_rename(struct mnt_idmap *idmap,if (dir_de && !nilfs_empty_dir (new_inode)) goto out_dir; - err = -ENOENT ; - new_de =nilfs_find_entry (new_dir, &new_dentry->d_name, &new_folio); -if (!new_de) + new_de =nilfs_find_entry (new_dir, &new_dentry->d_name, + &new_folio); +if (IS_ERR (new_de)) { + err =PTR_ERR (new_de); goto out_dir; + }nilfs_set_link (new_dir, new_de, new_folio, old_inode);folio_release_kmap (new_folio, new_de);nilfs_mark_inode_dirty (new_dir);
@@ -440,12 +452,13 @@ out:*/ static struct dentry *nilfs_get_parent (struct dentry *child) { -unsigned long ino; +ino_t ino; +int res;struct nilfs_root *root; - ino =nilfs_inode_by_name (d_inode (child), &dotdot_name); -if (!ino) -return ERR_PTR (-ENOENT ); + res =nilfs_inode_by_name (d_inode (child), &dotdot_name, &ino); +if (res) +return ERR_PTR (res); root =NILFS_I (d_inode (child))->i_root;
fs/nilfs2/nilfs.h +1 -1
@@ -254,7 +254,7 @@ static inline __u32 nilfs_mask_flags(umode_t mode, __u32 flags)/* dir.c */ int nilfs_add_link (struct dentry *,struct inode *); -ino_t nilfs_inode_by_name (struct inode *,const struct qstr *); +int nilfs_inode_by_name (struct inode *dir,const struct qstr *qstr,ino_t *ino);int nilfs_make_empty (struct inode *,struct inode *);struct nilfs_dir_entry *nilfs_find_entry (struct inode *,const struct qstr *,struct folio **);