[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