Linux filesystem development
 help / color / mirror / Atom feed
* d_path() results in presence of detached mounts
@ 2025-04-07 16:00 Jan Kara
  2025-04-08  8:55 ` Christian Brauner
  0 siblings, 1 reply; 5+ messages in thread
From: Jan Kara @ 2025-04-07 16:00 UTC (permalink / raw)
  To: linux-fsdevel; +Cc: Christian Brauner, mkoutny

Hello!

Recently I've got a question from a user about the following:

# unshare --mount swapon /dev/sda3
# cat /proc/swaps
Filename                                Type            Size            Used            Priority
/sda3                                   partition       2098152         0               -2

Now everything works as expected here AFAICS. When namespace gets created
/dev mount is cloned into it. When swapon exits, the namespace is
destroyed and /dev mount clone is detached (no parent, namespace NULL).
Hence when d_path() crawls the path it stops at the mountpoint root and
exits. There's not much we can do about this but when discussing the
situation internally, Michal proposed that d_path() could append something
like "(detached)" to the path string - similarly to "(deleted)". That could
somewhat reduce the confusion about such paths? What do people think about
this?

								Honza
-- 
Jan Kara <jack@suse.com>
SUSE Labs, CR

^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: d_path() results in presence of detached mounts
  2025-04-07 16:00 d_path() results in presence of detached mounts Jan Kara
@ 2025-04-08  8:55 ` Christian Brauner
  2025-04-08 11:39   ` Christian Brauner
  0 siblings, 1 reply; 5+ messages in thread
From: Christian Brauner @ 2025-04-08  8:55 UTC (permalink / raw)
  To: Jan Kara; +Cc: linux-fsdevel, mkoutny

On Mon, Apr 07, 2025 at 06:00:07PM +0200, Jan Kara wrote:
> Hello!
> 
> Recently I've got a question from a user about the following:
> 
> # unshare --mount swapon /dev/sda3
> # cat /proc/swaps
> Filename                                Type            Size            Used            Priority
> /sda3                                   partition       2098152         0               -2
> 
> Now everything works as expected here AFAICS. When namespace gets created
> /dev mount is cloned into it. When swapon exits, the namespace is
> destroyed and /dev mount clone is detached (no parent, namespace NULL).
> Hence when d_path() crawls the path it stops at the mountpoint root and
> exits. There's not much we can do about this but when discussing the
> situation internally, Michal proposed that d_path() could append something
> like "(detached)" to the path string - similarly to "(deleted)". That could
> somewhat reduce the confusion about such paths? What do people think about
> this?

You can get into this situation in plenty of other ways. For example by
using detached mount via open_tree(OPEN_TREE_CLONE) as layers in
overlayfs. Or simply:

        int fd;
        char dpath[PATH_MAX];

        fd = open_tree(-EBADF, "/var/lib/fwupd", OPEN_TREE_CLONE);
        dup2(fd, 500);
        close(fd);
        readlink("/proc/self/fd/500", dpath, sizeof(dpath));
        printf("dpath = %s\n", dpath);

Showing "(detached)" will be ambiguous just like "(deleted)" is. If that
doesn't matter and it's clearly documented then it's probably fine. But
note that this will also affect /proc/<pid>/fd/ as can be seen from the
above example.

int main(int argc, char *argv[])
{
        int fd;
        char dpath[PATH_MAX];
        char *dirs[] = { "/ONE", "TWO", "THREE", "FOUR", NULL };

        for (char **dir = dirs; *dir; dir++) {
                mkdir(*dir, 0777);
                chdir(*dir);
        }

        chdir("/");
        fd = open_tree(-EBADF, "/ONE/TWO/THREE/FOUR", OPEN_TREE_CLONE);
        if (fd < 0) {
                perror("open_tree");
                _exit(1);
        }

        rmdir("/ONE/TWO/THREE/FOUR");

        if (dup2(fd, 500) < 0) {
                perror("dup2");
                _exit(1);
        }
        close(fd);

        readlink("/proc/self/fd/500", dpath, sizeof(dpath));
        if (strcmp("/ (detached) (deleted)", dpath) != 0) {
                printf("wrong dpath = %s\n", dpath);
                _exit(1);
        }
        printf("dpath = %s\n", dpath);
        _exit(0);
}

user1@localhost:~/data/scripts$ sudo ./open_tree_detached
dpath = / (detached) (deleted)

Seems good to me.

Should be as simple as:

diff --git a/fs/d_path.c b/fs/d_path.c
index 5f4da5c8d5db..58874a107634 100644
--- a/fs/d_path.c
+++ b/fs/d_path.c
@@ -246,6 +246,12 @@ static void get_fs_root_rcu(struct fs_struct *fs, struct path *root)
        } while (read_seqcount_retry(&fs->seq, seq));
 }

+static inline bool is_detached(struct mount *mnt)
+{
+       struct mnt_namespace *mnt_ns = READ_ONCE(mnt->mnt_ns);
+       return unlikely(!mnt_ns || is_anon_ns(mnt_ns));
+}
+
 /**
  * d_path - return the path of a dentry
  * @path: path to report
@@ -284,7 +290,11 @@ char *d_path(const struct path *path, char *buf, int buflen)

        rcu_read_lock();
        get_fs_root_rcu(current->fs, &root);
-       if (unlikely(d_unlinked(path->dentry)))
+       if (unlikely(is_detached(real_mount(path->mnt))) && d_unlinked(path->dentry))
+               prepend(&b, " (detached) (deleted)", 22);
+       else if (unlikely(is_detached(real_mount(path->mnt))))
+               prepend(&b, " (detached)", 11);
+       else if (unlikely(d_unlinked(path->dentry)))
                prepend(&b, " (deleted)", 11);
        else
                prepend_char(&b, 0);

This is racy. Iow, after the first check it's still possible that it's
both detached and deleted. But I don't think that matters. (deleted)
must stay at the end because there's userspace out there that expects
(deleted) to be at the end.


^ permalink raw reply related	[flat|nested] 5+ messages in thread

* Re: d_path() results in presence of detached mounts
  2025-04-08  8:55 ` Christian Brauner
@ 2025-04-08 11:39   ` Christian Brauner
  2025-04-08 12:04     ` Jan Kara
  0 siblings, 1 reply; 5+ messages in thread
From: Christian Brauner @ 2025-04-08 11:39 UTC (permalink / raw)
  To: Jan Kara; +Cc: linux-fsdevel, mkoutny

On Tue, Apr 08, 2025 at 10:55:07AM +0200, Christian Brauner wrote:
> On Mon, Apr 07, 2025 at 06:00:07PM +0200, Jan Kara wrote:
> > Hello!
> > 
> > Recently I've got a question from a user about the following:
> > 
> > # unshare --mount swapon /dev/sda3
> > # cat /proc/swaps
> > Filename                                Type            Size            Used            Priority
> > /sda3                                   partition       2098152         0               -2
> > 
> > Now everything works as expected here AFAICS. When namespace gets created
> > /dev mount is cloned into it. When swapon exits, the namespace is
> > destroyed and /dev mount clone is detached (no parent, namespace NULL).

That's not the issue you're seeing here though

> > Hence when d_path() crawls the path it stops at the mountpoint root and
> > exits. There's not much we can do about this but when discussing the
> > situation internally, Michal proposed that d_path() could append something
> > like "(detached)" to the path string - similarly to "(deleted)". That could
> > somewhat reduce the confusion about such paths? What do people think about
> > this?
> 
> You can get into this situation in plenty of other ways. For example by
> using detached mount via open_tree(OPEN_TREE_CLONE) as layers in
> overlayfs. Or simply:
> 
>         int fd;
>         char dpath[PATH_MAX];
> 
>         fd = open_tree(-EBADF, "/var/lib/fwupd", OPEN_TREE_CLONE);
>         dup2(fd, 500);
>         close(fd);
>         readlink("/proc/self/fd/500", dpath, sizeof(dpath));
>         printf("dpath = %s\n", dpath);
> 
> Showing "(detached)" will be ambiguous just like "(deleted)" is. If that
> doesn't matter and it's clearly documented then it's probably fine. But
> note that this will also affect /proc/<pid>/fd/ as can be seen from the
> above example.

The other downside is that it will still be quite opaque because the
user will have to be aware of the concept of a detached mount. So it's
mostly useful for administrators tbh.

In general, I think it needs to be made clear to userspace that paths
shown in such tables are open()-able in the best case and decorative or
even misleading in the worst case.

The swap case is particularly ugly because it's a kernel-internal open
so in essence you might be located in a namespace where you can't even
even possibly reach that path.

In the

	unshare --mount swapon /dev/sda3
	exit

example you're lucky. If you're on the host then you can reasonably
guess and go "Oh, this is probably a fscked path we could look at /dev"
and parse through /dev/ to find "sda3" but that's a risky game even in
that case.

If you're in a container however that /proc/swaps output is completely
useless as there surely (unless you have a very broken container) won't
be a devtmpfs mount in there at all so that device isn't reachable at
all. Same for files.

In short, that output is purely diagnostic at best.

^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: d_path() results in presence of detached mounts
  2025-04-08 11:39   ` Christian Brauner
@ 2025-04-08 12:04     ` Jan Kara
  2025-04-08 12:44       ` Christian Brauner
  0 siblings, 1 reply; 5+ messages in thread
From: Jan Kara @ 2025-04-08 12:04 UTC (permalink / raw)
  To: Christian Brauner; +Cc: Jan Kara, linux-fsdevel, mkoutny

On Tue 08-04-25 13:39:28, Christian Brauner wrote:
> On Tue, Apr 08, 2025 at 10:55:07AM +0200, Christian Brauner wrote:
> > On Mon, Apr 07, 2025 at 06:00:07PM +0200, Jan Kara wrote:
> > > Hello!
> > > 
> > > Recently I've got a question from a user about the following:
> > > 
> > > # unshare --mount swapon /dev/sda3
> > > # cat /proc/swaps
> > > Filename                                Type            Size            Used            Priority
> > > /sda3                                   partition       2098152         0               -2
> > > 
> > > Now everything works as expected here AFAICS. When namespace gets created
> > > /dev mount is cloned into it. When swapon exits, the namespace is
> > > destroyed and /dev mount clone is detached (no parent, namespace NULL).
> 
> That's not the issue you're seeing here though
> 
> > > Hence when d_path() crawls the path it stops at the mountpoint root and
> > > exits. There's not much we can do about this but when discussing the
> > > situation internally, Michal proposed that d_path() could append something
> > > like "(detached)" to the path string - similarly to "(deleted)". That could
> > > somewhat reduce the confusion about such paths? What do people think about
> > > this?
> > 
> > You can get into this situation in plenty of other ways. For example by
> > using detached mount via open_tree(OPEN_TREE_CLONE) as layers in
> > overlayfs. Or simply:
> > 
> >         int fd;
> >         char dpath[PATH_MAX];
> > 
> >         fd = open_tree(-EBADF, "/var/lib/fwupd", OPEN_TREE_CLONE);
> >         dup2(fd, 500);
> >         close(fd);
> >         readlink("/proc/self/fd/500", dpath, sizeof(dpath));
> >         printf("dpath = %s\n", dpath);
> > 
> > Showing "(detached)" will be ambiguous just like "(deleted)" is. If that
> > doesn't matter and it's clearly documented then it's probably fine. But
> > note that this will also affect /proc/<pid>/fd/ as can be seen from the
> > above example.
> 
> The other downside is that it will still be quite opaque because the
> user will have to be aware of the concept of a detached mount. So it's
> mostly useful for administrators tbh.

Thanks for the insights!

> In general, I think it needs to be made clear to userspace that paths
> shown in such tables are open()-able in the best case and decorative or
> even misleading in the worst case.

Yes, I know this and I was just wondering if we can at least somehow
visibly indicate the path shown is likely unusable. If you think it would
do more harm than good, I'm fine with that answer, I just thought I'll
ask...

								Honza
-- 
Jan Kara <jack@suse.com>
SUSE Labs, CR

^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: d_path() results in presence of detached mounts
  2025-04-08 12:04     ` Jan Kara
@ 2025-04-08 12:44       ` Christian Brauner
  0 siblings, 0 replies; 5+ messages in thread
From: Christian Brauner @ 2025-04-08 12:44 UTC (permalink / raw)
  To: Jan Kara; +Cc: linux-fsdevel, mkoutny

On Tue, Apr 08, 2025 at 02:04:48PM +0200, Jan Kara wrote:
> On Tue 08-04-25 13:39:28, Christian Brauner wrote:
> > On Tue, Apr 08, 2025 at 10:55:07AM +0200, Christian Brauner wrote:
> > > On Mon, Apr 07, 2025 at 06:00:07PM +0200, Jan Kara wrote:
> > > > Hello!
> > > > 
> > > > Recently I've got a question from a user about the following:
> > > > 
> > > > # unshare --mount swapon /dev/sda3
> > > > # cat /proc/swaps
> > > > Filename                                Type            Size            Used            Priority
> > > > /sda3                                   partition       2098152         0               -2
> > > > 
> > > > Now everything works as expected here AFAICS. When namespace gets created
> > > > /dev mount is cloned into it. When swapon exits, the namespace is
> > > > destroyed and /dev mount clone is detached (no parent, namespace NULL).
> > 
> > That's not the issue you're seeing here though

Uh, sorry about this weird stray sentence. Not sure how that ended up in
here. I think that was supposed to be in another mail.

> > 
> > > > Hence when d_path() crawls the path it stops at the mountpoint root and
> > > > exits. There's not much we can do about this but when discussing the
> > > > situation internally, Michal proposed that d_path() could append something
> > > > like "(detached)" to the path string - similarly to "(deleted)". That could
> > > > somewhat reduce the confusion about such paths? What do people think about
> > > > this?
> > > 
> > > You can get into this situation in plenty of other ways. For example by
> > > using detached mount via open_tree(OPEN_TREE_CLONE) as layers in
> > > overlayfs. Or simply:
> > > 
> > >         int fd;
> > >         char dpath[PATH_MAX];
> > > 
> > >         fd = open_tree(-EBADF, "/var/lib/fwupd", OPEN_TREE_CLONE);
> > >         dup2(fd, 500);
> > >         close(fd);
> > >         readlink("/proc/self/fd/500", dpath, sizeof(dpath));
> > >         printf("dpath = %s\n", dpath);
> > > 
> > > Showing "(detached)" will be ambiguous just like "(deleted)" is. If that
> > > doesn't matter and it's clearly documented then it's probably fine. But
> > > note that this will also affect /proc/<pid>/fd/ as can be seen from the
> > > above example.
> > 
> > The other downside is that it will still be quite opaque because the
> > user will have to be aware of the concept of a detached mount. So it's
> > mostly useful for administrators tbh.
> 
> Thanks for the insights!
> 
> > In general, I think it needs to be made clear to userspace that paths
> > shown in such tables are open()-able in the best case and decorative or
> > even misleading in the worst case.
> 
> Yes, I know this and I was just wondering if we can at least somehow
> visibly indicate the path shown is likely unusable. If you think it would
> do more harm than good, I'm fine with that answer, I just thought I'll
> ask...

Oh yes, absolutely. It's good to bring this up. I just wonder whether
we could do better. Karel suggested using a stx_attribute and I had
pondered a statmount() extension but both have some issues though let me
pitch them nonetheless somewhat independent of the issue.

We could add a statmount() extension to mark detached mounts as being
detached. That however will only work for anonymous detached mounts,
i.e., mounts that have been created using OPEN_TREE_CLONE or fsmount().
Since statmount() uses the mount namespace rbtree to lookup mounts any
unmounted mount with mnt->mnt_ns == NULL cannot be found anymore. So
it's - currently at least - not useful to handle the /proc/swaps case.

A statx() extension would be more useful because it would work
independent of whether this is a detached mount or not. So we would
start reporting STATX_MNT_DETACHED in stx_attributes which allows
userspace to figure out that the thing was unmounted. That might be
genuinely useful.

But both solution wouldn't help with the /proc/swaps scenario because
it's not guaranteed that the file can be opened because it might be
outside the caller's mount namespace (or even deleted).

^ permalink raw reply	[flat|nested] 5+ messages in thread

end of thread, other threads:[~2025-04-08 12:44 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-04-07 16:00 d_path() results in presence of detached mounts Jan Kara
2025-04-08  8:55 ` Christian Brauner
2025-04-08 11:39   ` Christian Brauner
2025-04-08 12:04     ` Jan Kara
2025-04-08 12:44       ` Christian Brauner

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox