ANDROID: e2fsck: Do not mutate encrypted names

We can't mutate a name without the key, as this will at best cause the
name to become gibberish, and at worst may introduce invalid characters
or even fail to be unique after decoding, so drop duplicates instead.
Files lost in this way will be reconnected to lost+found

Fixes: dbff534ec685 ("e2fsck: suppress bad name checks for encrypted directories")
Signed-off-by: Daniel Rosenberg <drosen@google.com>
Bug: 138322712
Test: f_dup_de_crypt
Change-Id: I8d6cc3984872868a845fafabc554abdd86351fcc
diff --git a/e2fsck/problem.c b/e2fsck/problem.c
index 5eb5973..ea9b296 100644
--- a/e2fsck/problem.c
+++ b/e2fsck/problem.c
@@ -1779,6 +1779,11 @@
 	  N_("Encrypted @E is too short.\n"),
 	  PROMPT_CLEAR, 0, 0, 0, 0 },
 
+	 /* Non-unique filename found, but can't rename */
+	 { PR_2_NON_UNIQUE_FILE_NO_RENAME,
+	   N_("Duplicate filename @E found.  "),
+	   PROMPT_CLEAR, 0, 0, 0, 0 },
+
 	/* Pass 3 errors */
 
 	/* Pass 3: Checking directory connectivity */
diff --git a/e2fsck/problem.h b/e2fsck/problem.h
index 5cc8924..7bcecfa 100644
--- a/e2fsck/problem.h
+++ b/e2fsck/problem.h
@@ -1017,6 +1017,9 @@
 /* Encrypted directory entry is too short */
 #define PR_2_BAD_ENCRYPTED_NAME		0x020050
 
+/* Non-unique filename found, but can't rename */
+#define PR_2_NON_UNIQUE_FILE_NO_RENAME	0x020053
+
 /*
  * Pass 3 errors
  */
diff --git a/e2fsck/rehash.c b/e2fsck/rehash.c
index a5fc1be..81d67c6 100644
--- a/e2fsck/rehash.c
+++ b/e2fsck/rehash.c
@@ -400,6 +400,15 @@
 			fixed++;
 			continue;
 		}
+		/* Can't alter encrypted name without key, so just drop it */
+		if (fd->inode->i_flags & EXT4_ENCRYPT_FL) {
+			if (fix_problem(ctx, PR_2_NON_UNIQUE_FILE_NO_RENAME, &pctx)) {
+				e2fsck_adjust_inode_count(ctx, ent->dir->inode, -1);
+				ent->dir->inode = 0;
+				fixed++;
+				continue;
+			}
+		}
 		new_len = ext2fs_dirent_name_len(ent->dir);
 		memcpy(new_name, ent->dir->name, new_len);
 		mutate_name(new_name, &new_len);
diff --git a/tests/f_dup_de_crypt/expect.1 b/tests/f_dup_de_crypt/expect.1
new file mode 100644
index 0000000..03e0ad6
--- /dev/null
+++ b/tests/f_dup_de_crypt/expect.1
@@ -0,0 +1,18 @@
+Pass 1: Checking inodes, blocks, and sizes
+Pass 2: Checking directory structure
+Duplicate entry '+M-^AT^EM-1M-^CM-/)*M-L^RM-^L^@M-WM-)M-+' found.
+	Marking /test (12) to be rebuilt.
+
+Pass 3: Checking directory connectivity
+Pass 3A: Optimizing directories
+Duplicate filename entry '+M-^AT^EM-1M-^CM-/)*M-L^RM-^L^@M-WM-)M-+' in /test (12) found.  Clear? yes
+
+Pass 4: Checking reference counts
+Unattached inode 13
+Connect to /lost+found? yes
+
+Pass 5: Checking group summary information
+
+test_filesys: ***** FILE SYSTEM WAS MODIFIED *****
+test_filesys: 14/16 files (0.0% non-contiguous), 26/60 blocks
+Exit status is 1
diff --git a/tests/f_dup_de_crypt/expect.2 b/tests/f_dup_de_crypt/expect.2
new file mode 100644
index 0000000..cfca772
--- /dev/null
+++ b/tests/f_dup_de_crypt/expect.2
@@ -0,0 +1,7 @@
+Pass 1: Checking inodes, blocks, and sizes
+Pass 2: Checking directory structure
+Pass 3: Checking directory connectivity
+Pass 4: Checking reference counts
+Pass 5: Checking group summary information
+test_filesys: 14/16 files (0.0% non-contiguous), 26/60 blocks
+Exit status is 0
diff --git a/tests/f_dup_de_crypt/image.gz b/tests/f_dup_de_crypt/image.gz
new file mode 100644
index 0000000..07a44d7
--- /dev/null
+++ b/tests/f_dup_de_crypt/image.gz
Binary files differ
diff --git a/tests/f_dup_de_crypt/name b/tests/f_dup_de_crypt/name
new file mode 100644
index 0000000..aff30a8
--- /dev/null
+++ b/tests/f_dup_de_crypt/name
@@ -0,0 +1 @@
+duplicate directory entries for encrypted dirs