diff mbox series

[2/3] ecryptfs: use private mount in path

Message ID 20210409162422.1326565-3-brauner@kernel.org (mailing list archive)
State New, archived
Headers show
Series ecryptfs: fixes and port to private mounts | expand

Commit Message

Christian Brauner April 9, 2021, 4:24 p.m. UTC
From: Christian Brauner <christian.brauner@ubuntu.com>

Since [1] we support creating private mounts from a given path's
vfsmount. This makes them very suitable for any filesystem or
filesystem functionality that piggybacks on paths of another filesystem.
Overlayfs, cachefiles, and ecryptfs are three prime examples.

Since private mounts aren't attached in the filesystem they aren't
affected by mount property changes after ecryptfs makes use of them.
This seems a rather desirable property as the underlying path can't e.g.
suddenly go from read-write to read-only and in general it means that
ecryptfs is always in full control of the underlying mount after the
user has allowed it to be used (apart from operations that affect the
superblock of course).

Besides that it also makes things simpler for a variety of other vfs
features. One concrete example is fanotify. When the path->mnt of the
path that is used as a cache has been marked with FAN_MARK_MOUNT the
semantics get tricky as it isn't clear whether the watchers of path->mnt
should get notified about fsnotify events when files are created by
cachefilesd via path->mnt. Using a private mount let's us elegantly
handle this case too and aligns the behavior of stacks created by
overlayfs and cachefiles.

Reading through the codebase of ecryptfs it currently takes path->mnt
and then retrieves that path whenever it needs to perform operations in
the underlying filesystem. Simply drop the old path->mnt once we've
created a private mount and place the new private mnt into path->mnt.
This should be all that is needed to make this work since ecryptfs uses
the same lower path's vfsmount to construct the paths it uses to operate
on the underlying filesystem.

[1]: c771d683a62e ("vfs: introduce clone_private_mount()")
Cc: Amir Goldstein <amir73il@gmail.com>
Cc: Tyler Hicks <code@tyhicks.com>
Cc: ecryptfs@vger.kernel.org
Cc: linux-fsdevel@vger.kernel.org
Signed-off-by: Christian Brauner <christian.brauner@ubuntu.com>
---
 fs/ecryptfs/main.c | 17 +++++++++++++++++
 1 file changed, 17 insertions(+)

Comments

Al Viro April 10, 2021, 12:31 a.m. UTC | #1
On Fri, Apr 09, 2021 at 06:24:21PM +0200, Christian Brauner wrote:

> Reading through the codebase of ecryptfs it currently takes path->mnt
> and then retrieves that path whenever it needs to perform operations in
> the underlying filesystem. Simply drop the old path->mnt once we've
> created a private mount and place the new private mnt into path->mnt.
> This should be all that is needed to make this work since ecryptfs uses
> the same lower path's vfsmount to construct the paths it uses to operate
> on the underlying filesystem.

> +	mnt = clone_private_mount(&path);

Incidentally, why is that thing anything other than a trivial wrapper
for mnt_clone_internal() (if that - I'm not convinced that the check of
unbindable is the right thing to do here).  Miklos?
Christian Brauner April 10, 2021, 12:30 p.m. UTC | #2
On Sat, Apr 10, 2021 at 12:31:02AM +0000, Al Viro wrote:
> On Fri, Apr 09, 2021 at 06:24:21PM +0200, Christian Brauner wrote:
> 
> > Reading through the codebase of ecryptfs it currently takes path->mnt
> > and then retrieves that path whenever it needs to perform operations in
> > the underlying filesystem. Simply drop the old path->mnt once we've
> > created a private mount and place the new private mnt into path->mnt.
> > This should be all that is needed to make this work since ecryptfs uses
> > the same lower path's vfsmount to construct the paths it uses to operate
> > on the underlying filesystem.
> 
> > +	mnt = clone_private_mount(&path);
> 
> Incidentally, why is that thing anything other than a trivial wrapper
> for mnt_clone_internal() (if that - I'm not convinced that the check of
> unbindable is the right thing to do here).  Miklos?

The unbindable check is irrelevant at least for both ecryptfs and for
the corresponding cachefiles change I sent out since they don't care
about it.
In practice it doesn't matter to be honest. MS_UNBINDABLE is wildly
esoteric in userspace (We had a glaring bug with that some time ago that
went completely unnoticed for years.). Especially unlikely to be used
for a users home directory (ecryptfs) or /var/cache/fscache
(cachefiles). So even by leaving this check in it's very unlikely for
any regressions to appear.

I hadn't seen mnt_clone_internal() but it's different in so far as it
sets MNT_INTERNAL whereas clone_private_mount() uses MNT_NS_INTERNAL.
Which points me to another potential problem here:
clone_private_mount() seems to want kern_unmount() to be called instead
of just a simple mntput()?

If that's relevant then I think the unbindable check should probably
move out of clone_private_mount() and into overlayfs itself but the rest
be kept.
Miklos Szeredi April 12, 2021, 8:53 a.m. UTC | #3
On Sat, Apr 10, 2021 at 2:30 PM Christian Brauner
<christian.brauner@ubuntu.com> wrote:
>
> On Sat, Apr 10, 2021 at 12:31:02AM +0000, Al Viro wrote:
> > On Fri, Apr 09, 2021 at 06:24:21PM +0200, Christian Brauner wrote:
> >
> > > Reading through the codebase of ecryptfs it currently takes path->mnt
> > > and then retrieves that path whenever it needs to perform operations in
> > > the underlying filesystem. Simply drop the old path->mnt once we've
> > > created a private mount and place the new private mnt into path->mnt.
> > > This should be all that is needed to make this work since ecryptfs uses
> > > the same lower path's vfsmount to construct the paths it uses to operate
> > > on the underlying filesystem.
> >
> > > +   mnt = clone_private_mount(&path);
> >
> > Incidentally, why is that thing anything other than a trivial wrapper
> > for mnt_clone_internal() (if that - I'm not convinced that the check of
> > unbindable is the right thing to do here).  Miklos?
>
> The unbindable check is irrelevant at least for both ecryptfs and for
> the corresponding cachefiles change I sent out since they don't care
> about it.
> In practice it doesn't matter to be honest. MS_UNBINDABLE is wildly
> esoteric in userspace (We had a glaring bug with that some time ago that
> went completely unnoticed for years.). Especially unlikely to be used
> for a users home directory (ecryptfs) or /var/cache/fscache
> (cachefiles). So even by leaving this check in it's very unlikely for
> any regressions to appear.
>
> I hadn't seen mnt_clone_internal() but it's different in so far as it
> sets MNT_INTERNAL whereas clone_private_mount() uses MNT_NS_INTERNAL.
> Which points me to another potential problem here:
> clone_private_mount() seems to want kern_unmount() to be called instead
> of just a simple mntput()?

Yes, that's stated in a comment in the clone_private_mount() helper.

The difference is that short term mounts take a small penalty on each
mntput(), while longterm mounts take a fairly large penalty on
kern_unmount().  It's just a performance thing, AFAIK.

As for MS_UNBINDABLE, my recollection is that it was copy-pasted from
regular bind mount.  I agree that it can be moved to overlayfs (or
removed altogether, with some thought into what MS_UNBINDABLE actually
is used for).

Thanks,
Miklos
diff mbox series

Patch

diff --git a/fs/ecryptfs/main.c b/fs/ecryptfs/main.c
index cdf40a54a35d..9dcf9a0dd37b 100644
--- a/fs/ecryptfs/main.c
+++ b/fs/ecryptfs/main.c
@@ -476,6 +476,7 @@  static struct file_system_type ecryptfs_fs_type;
 static struct dentry *ecryptfs_mount(struct file_system_type *fs_type, int flags,
 			const char *dev_name, void *raw_data)
 {
+	struct vfsmount *mnt = NULL;
 	struct super_block *s;
 	struct ecryptfs_sb_info *sbi;
 	struct ecryptfs_mount_crypt_stat *mount_crypt_stat;
@@ -537,6 +538,14 @@  static struct dentry *ecryptfs_mount(struct file_system_type *fs_type, int flags
 		goto out_free;
 	}
 
+	mnt = clone_private_mount(&path);
+	if (IS_ERR(mnt)) {
+		rc = PTR_ERR(mnt);
+		mnt = NULL;
+		pr_warn("Failed to create private mount for ecryptfs\n");
+		goto out_free;
+	}
+
 	if (check_ruid && !uid_eq(d_inode(path.dentry)->i_uid, current_uid())) {
 		rc = -EPERM;
 		printk(KERN_ERR "Mount of device (uid: %d) not owned by "
@@ -592,6 +601,13 @@  static struct dentry *ecryptfs_mount(struct file_system_type *fs_type, int flags
 
 	/* ->kill_sb() will take care of root_info */
 	ecryptfs_set_dentry_private(s->s_root, root_info);
+
+	/* We've created a private clone of this mount above so drop it now. */
+	mntput(path.mnt);
+
+	/* Use our private mount from now on. */
+	path.mnt = mnt;
+
 	root_info->lower_path = path;
 
 	s->s_flags |= SB_ACTIVE;
@@ -599,6 +615,7 @@  static struct dentry *ecryptfs_mount(struct file_system_type *fs_type, int flags
 
 out_free:
 	path_put(&path);
+	mntput(mnt);
 out1:
 	deactivate_locked_super(s);
 out: