losetup displays wrong path after underlying mount namespace is removed · Issue #4166 · util-linux/util-linux · GitHub
Skip to content

losetup displays wrong path after underlying mount namespace is removed #4166

Open

Description

@msanft

losetup displays a wrong path for the backing file after the mount
namespace the backing file lived in is removed.

This can be reproduced fairly simple.

  1. Start a container with:

    sudo podman run --rm -it -v$PWD:/bar:ro --privileged ubuntu
  2. Within that container, run:

    $ losetup -f /bar/foo/image
    $ losetup
    NAME       SIZELIMIT OFFSET AUTOCLEAR RO BACK-FILE      DIO LOG-SEC
    /dev/loop0         0      0         0  1 /bar/foo/image   0     512
    

    As expected, we see the path /bar/foo/image for the backing file.

  3. When we run losetup on the host, we see the same path:

    $ losetup
    NAME       SIZELIMIT OFFSET AUTOCLEAR RO BACK-FILE      DIO LOG-SEC
    /dev/loop0         0      0         0  1 /bar/foo/image   0     512
    
  4. Now, exit from / remove the container (mount namespace).

  5. Now, re-run losetup on the host

    $ losetup
    NAME       SIZELIMIT OFFSET AUTOCLEAR RO BACK-FILE  DIO LOG-SEC
    /dev/loop0         0      0         0  1 /foo/image   0     512
    

    This yields the path minus the previous mount point /bar:
    /foo/image.

For most applications, this should be non-critical. But it is a
scenario that you're somewhat likely to run into when setting up loop
devices in containers. It's especially misleading if the mount doesn't
happen at the toy /bar path but / of the container, where losetup
just suddenly points to a non-existent path that can't really be traced
back to the container easily.

Skimming loopdev.c, it appears that this is the value reported by
the sysfs, so one could argue if this should be addressed in losetup
at all. I'm not a kernel expert, so I'm not sure if other options
exist for this, but I still wanted to report this as a potential
papercut. Feel free to let me know if this is a wontfix for you.

Activity

  1. cgoesche commented on Apr 1, 2026

    @cgoesche
    Collaborator

    Reproducible on 6.17.12-400.asahi.fc42.aarch64+16k with the same path issue. Seems like a problem originating in the kernel indeed.

    After browsing a bit through the kernel code, looks like the magic happens here: linux/drivers/block/loop.c:loop_attr_backing_file_show()->file_path()->d_path()->prepend_path()

    It searches for the mount struct the path's vfsmount object belongs to here and pass that to __prepend_path(), which looks for the mountpoint, if I got it correctly. However, since the struct mount created in the container namespace is gone it will not find anything and return to simply prepend a '/' here.

    If you make the mount namespace persistent (unshare --mount=/foo/bar/mnt_ns) you will not have this issue.

    Anyways, I'm not very familiar with the VFS internals so maybe @karelzak or @brauner have a better idea of what's going on here.

    Would it make sense to use fs/d_path.c:__d_path() to test whether the path is reachable from the root found with fs/d_path.c:get_fs_root_rcu() and then annotate the filename string with (unreachable) if not ?

  2. cgoesche commented on Apr 1, 2026

    @cgoesche
    Collaborator

    After debugging a bit with bpftrace, it seems like __prepend_path() returns 2 (here), and if I understand correctly in this case it might indicate that it is an anonymous/detached mount object that is kept alive due to the backing file's reference to it via the loop device. The return value may be useful in dpath() for annotations (e.g. unreachable/detached/not-mounted), no ?

  3. msanft commented on Apr 2, 2026

    @msanft
    Author

    Thanks for taking this up @cgoesche!

    if I understand correctly in this case it might indicate that it is an anonymous/detached mount object that is kept alive due to the backing file's reference to it via the loop device.

    I'm no kernel expert, but I understand it like that too. I think annotating it as you explained above will be a great improvement. Potentially also for other tools in util-linux.

  4. karelzak commented on Apr 2, 2026

    @karelzak
    Collaborator

    This is a common issue; the kernel can provide paths that may not be valid for userspace. It's well known for deleted files; in this case, the kernel is so nice that it appends "(deleted)" to the path.

    # rm /root/a
    # losetup
    NAME       SIZELIMIT OFFSET AUTOCLEAR RO BACK-FILE         DIO LOG-SEC
    /dev/loop0         0      0         0  0 /root/a (deleted)   0     512

    Userspace tools that just print kernel status (like losetup) should not try to be smart in this case. All we can do is show it to users. I agree that, in the case of an unreachable namespace, it would be nice to add something like "(unreachable)", especially since it already supports "(deleted)".

  5. cgoesche commented on May 3, 2026

    @cgoesche
    Collaborator

    With this patch the unreachable mount path component is replaced with (unreachable).

    diff --git a/fs/d_path.c b/fs/d_path.c
    index a48957c0971e..07e4397b1ceb 100644
    --- a/fs/d_path.c
    +++ b/fs/d_path.c
    @@ -288,7 +288,10 @@ char *d_path(const struct path *path, char *buf, int buflen)
                    prepend(&b, " (deleted)", 11);
            else
                    prepend_char(&b, 0);
    -       prepend_path(path, &root, &b);
    +
    +       if(unlikely(prepend_path(path, &root, &b) == 2))
    +               prepend(&b, "(unreachable)", 13);
    +
            rcu_read_unlock();
    
            return extract_string(&b);

    The output then looks like this

    $ losetup
    NAME       SIZELIMIT OFFSET AUTOCLEAR RO BACK-FILE  DIO LOG-SEC
    /dev/loop0         0      0         0  1 (unreachable)/foo/image   0     512
    

    If this is a reasonable semantic I'd bring this to the VFS maintainers, what do you think ?

  6. msanft commented on May 3, 2026

    @msanft
    Author

    Thanks @cgoesche! The patch LGTM! You'd probably want to align the leading space to the " (deleted)" one, I guess

  7. cgoesche commented on May 3, 2026

    @cgoesche
    Collaborator

    Since the (unreachable) is prepended to the path it should be fine without the whitespace. Unlike the (deleted) label, which is appended.

  8. msanft commented on May 4, 2026

    @msanft
    Author

    Since the (unreachable) is prepended to the path it should be fine without the whitespace. Unlike the (deleted) label, which is appended.

    Oops. Sorry! Thanks :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions