From: Jeff Mahoney <jeffm@suse.com>
Subject: reiserfs: don't panic on bad directory entries
Patch-mainline: Submitted, reiserfs-devel@vger.kernel.org, 18 Oct 2018
References: bsc#1109818

If a directory entry has been corrupted such that the flags reflect
a different visibility state than expected, we will panic the node.

This patch instead returns -EIO and sets the file system read-only.

Signed-off-by: Jeff Mahoney <jeffm@suse.com>
---
 fs/reiserfs/namei.c | 108 ++++++++++++++++++++++++++++++++--------------------
 1 file changed, 67 insertions(+), 41 deletions(-)

diff --git a/fs/reiserfs/namei.c b/fs/reiserfs/namei.c
index 5be509a9204f..01e788f91a1d 100644
--- a/fs/reiserfs/namei.c
+++ b/fs/reiserfs/namei.c
@@ -1254,7 +1254,7 @@ static int reiserfs_link(struct dentry *old_dentry, struct inode *dir,
 
 /* de contains information pointing to an entry which */
 static int de_still_valid(const char *name, int len,
-			  struct reiserfs_dir_entry *de)
+			  const struct reiserfs_dir_entry *de)
 {
 	struct reiserfs_dir_entry tmp = *de;
 
@@ -1266,23 +1266,31 @@ static int de_still_valid(const char *name, int len,
 	return 1;
 }
 
-static int entry_points_to_object(const char *name, int len,
-				  struct reiserfs_dir_entry *de,
-				  struct inode *inode)
+static int entry_points_to_object(const struct inode *dir,
+				  const char *name, int len,
+				  const struct reiserfs_dir_entry *de,
+				  const struct inode *inode)
 {
 	if (!de_still_valid(name, len, de))
 		return 0;
 
 	if (inode) {
-		if (!de_visible(de->de_deh + de->de_entry_num))
-			reiserfs_panic(inode->i_sb, "vs-7042",
-				       "entry must be visible");
+		if (!de_visible(de->de_deh + de->de_entry_num)) {
+			reiserfs_error(dir->i_sb, "vs-7042",
+				       "entry must be visible (%.*s in dir %k",
+				       len, name, INODE_PKEY(dir));
+			return -EIO;
+		}
 		return (de->de_objectid == inode->i_ino) ? 1 : 0;
 	}
 
 	/* this must be added hidden entry */
-	if (de_visible(de->de_deh + de->de_entry_num))
-		reiserfs_panic(NULL, "vs-7043", "entry must be invisible");
+	if (de_visible(de->de_deh + de->de_entry_num)) {
+		reiserfs_error(dir->i_sb, "vs-7043",
+			       "entry must be invisible (%.*s in dir %k",
+			       len, name, INODE_PKEY(dir));
+		return -EIO;
+	}
 
 	return 1;
 }
@@ -1541,47 +1549,65 @@ static int reiserfs_rename(struct inode *old_dir, struct dentry *old_dentry,
 		 * of the above checks could have scheduled.  We have to be
 		 * sure our items haven't been shifted by another process.
 		 */
-		if (item_moved(&new_entry_ih, &new_entry_path) ||
-		    !entry_points_to_object(new_dentry->d_name.name,
-					    new_dentry->d_name.len,
-					    &new_de, new_dentry_inode) ||
-		    item_moved(&old_entry_ih, &old_entry_path) ||
-		    !entry_points_to_object(old_dentry->d_name.name,
-					    old_dentry->d_name.len,
-					    &old_de, old_inode)) {
-			reiserfs_restore_prepared_buffer(old_inode->i_sb,
-							 new_de.de_bh);
-			reiserfs_restore_prepared_buffer(old_inode->i_sb,
-							 old_de.de_bh);
-			if (S_ISDIR(old_inode_mode))
-				reiserfs_restore_prepared_buffer(old_inode->
-								 i_sb,
-								 dot_dot_de.
-								 de_bh);
-			continue;
+		if (item_moved(&new_entry_ih, &new_entry_path)) {
+			retval = 0;
+			goto restore;
 		}
+
+		retval = entry_points_to_object(new_dir,
+						new_dentry->d_name.name,
+						new_dentry->d_name.len,
+						&new_de, new_dentry_inode);
+		if (retval != 1)
+			goto restore;
+
+		if (item_moved(&old_entry_ih, &old_entry_path)) {
+			retval = 0;
+			goto restore;
+		}
+
+		retval = entry_points_to_object(old_dir,
+						old_dentry->d_name.name,
+						old_dentry->d_name.len,
+						&old_de, old_inode);
+		if (retval != 1)
+			goto restore;
+
 		if (S_ISDIR(old_inode_mode)) {
-			if (item_moved(&dot_dot_ih, &dot_dot_entry_path) ||
-			    !entry_points_to_object("..", 2, &dot_dot_de,
-						    old_dir)) {
-				reiserfs_restore_prepared_buffer(old_inode->
-								 i_sb,
-								 old_de.de_bh);
-				reiserfs_restore_prepared_buffer(old_inode->
-								 i_sb,
-								 new_de.de_bh);
-				reiserfs_restore_prepared_buffer(old_inode->
-								 i_sb,
-								 dot_dot_de.
-								 de_bh);
-				continue;
+			if (item_moved(&dot_dot_ih, &dot_dot_entry_path)) {
+				retval = 0;
+				goto restore;
 			}
+
+			retval = entry_points_to_object(old_dir, "..", 2,
+							&dot_dot_de, old_dir);
+			if (retval != 1)
+				goto restore;
 		}
 
+		retval = 0;
+
 		RFALSE(S_ISDIR(old_inode_mode) &&
 		       !buffer_journal_prepared(dot_dot_de.de_bh), "");
 
 		break;
+
+restore:
+		reiserfs_restore_prepared_buffer(old_inode->i_sb,
+						 new_de.de_bh);
+		reiserfs_restore_prepared_buffer(old_inode->i_sb,
+						 old_de.de_bh);
+		if (S_ISDIR(old_inode_mode))
+			reiserfs_restore_prepared_buffer(old_inode->i_sb,
+							 dot_dot_de.de_bh);
+		if (retval) {
+			pathrelse(&dot_dot_entry_path);
+			pathrelse(&new_entry_path);
+			pathrelse(&old_entry_path);
+			journal_end(&th);
+			reiserfs_write_unlock(old_dir->i_sb);
+			return retval;
+		}
 	}
 
 	/*

