[PATCH RFC 02/28] ecryptfs: fall back to a read-only lower file

Christian Brauner brauner at kernel.org
Mon Oct 12 03:20:47 PDT 2026


ecryptfs opens the lower file read-write unless the lower inode is
IS_RDONLY(). But a filesystem whose superblock is read-write can still
refuse to open a file for writing. A read-only mount underneath does
that with EROFS, and so does ext4 after an error since commit
d3476f3dad4a ("ext4: don't set SB_RDONLY after filesystem errors"). The
retry through the kthread opens read-write as well and fails the same
way. Then every lookup of a regular file fails with EROFS because
ecryptfs can't read the header, and nothing can be read through
ecryptfs anymore.

The filesystems that still set SB_RDONLY after an error are about to
stop doing that as well. So far ecryptfs opened their files read-only
after an error. Fall back to that on EROFS. Opening an ecryptfs file for
writing then fails with EPERM like it does on top of a read-only
superblock.

Reproducer:

  sig=$(printf x | ecryptfs-add-passphrase - | sed -n 's/.*\[\(.*\)\].*/\1/p')
  mount --bind -o ro /lower /lower
  mount -i -t ecryptfs -o ecryptfs_sig=$sig,ecryptfs_cipher=aes,ecryptfs_key_bytes=16,ecryptfs_passthrough /lower /upper
  cat /upper/f      # before: EROFS, after: works

Signed-off-by: Christian Brauner (Amutable) <brauner at kernel.org>
---
 fs/ecryptfs/kthread.c | 19 +++++++++++++++++--
 1 file changed, 17 insertions(+), 2 deletions(-)

diff --git a/fs/ecryptfs/kthread.c b/fs/ecryptfs/kthread.c
index ae4cb4e2e134..ba94b304edf3 100644
--- a/fs/ecryptfs/kthread.c
+++ b/fs/ecryptfs/kthread.c
@@ -30,6 +30,21 @@ static struct ecryptfs_kthread_ctl {
 
 static struct task_struct *ecryptfs_kthread;
 
+/*
+ * A filesystem can refuse writes while its superblock stays read-write, e.g.
+ * after an error, and a read-only mount refuses them as well. Reading still
+ * works so fall back to that.
+ */
+static struct file *ecryptfs_open_lower(const struct path *path, int flags,
+					const struct cred *cred)
+{
+	struct file *file = dentry_open(path, flags, cred);
+
+	if (PTR_ERR_OR_ZERO(file) == -EROFS && (flags & O_ACCMODE) == O_RDWR)
+		file = dentry_open(path, (flags & ~O_ACCMODE) | O_RDONLY, cred);
+	return file;
+}
+
 /**
  * ecryptfs_threadfn
  * @ignored: ignored
@@ -59,7 +74,7 @@ static int ecryptfs_threadfn(void *ignored)
 					       struct ecryptfs_open_req,
 					       kthread_ctl_list);
 			list_del(&req->kthread_ctl_list);
-			*req->lower_file = dentry_open(&req->path,
+			*req->lower_file = ecryptfs_open_lower(&req->path,
 				(O_RDWR | O_LARGEFILE), current_cred());
 			complete(&req->done);
 		}
@@ -132,7 +147,7 @@ int ecryptfs_privileged_open(struct file **lower_file,
 	 * lower file is fput() when all eCryptfs files for the inode are
 	 * released. */
 	flags |= IS_RDONLY(d_inode(lower_dentry)) ? O_RDONLY : O_RDWR;
-	(*lower_file) = dentry_open(&req.path, flags, cred);
+	(*lower_file) = ecryptfs_open_lower(&req.path, flags, cred);
 	if (!IS_ERR(*lower_file))
 		goto out;
 	if ((flags & O_ACCMODE) == O_RDONLY) {

-- 
2.53.0




More information about the linux-mtd mailing list