On 04/09/2026 23:34, Collin Funk wrote:
Collin Funk <[email protected]> writes:

stat -c%m does not seem to work correctly with bind mounts.
I have tested the following on Debian Jessie:

# mkdir /home/root ; touch /home/zero
# mount -o bind /root /home/root
# mount -o bind /dev/zero /home/zero
# mount -o bind /tmp /tmp
# ./src/df -P /home/root /home/zero /tmp
Filesystem     1024-blocks    Used Available Capacity Mounted on
/dev/sda1          7736784 2431152   4889584      34% /home/root
udev                 10240       0     10240       0% /home/zero
/dev/sda1          7736784 2431152   4889584      34% /tmp
# ./src/stat -c%m /home/root /home/zero /tmp
/
/
/
# ./src/stat --version
stat (GNU coreutils) 8.26.4-ca52f


The documentation explains:

stat outputs the alias for a bind mounted file, rather than the
initial mount point of its backing device. One can recursively call
stat until there is no change in output, to get the current base mount
point
(https://www.gnu.org/software/coreutils/manual/html_node/stat-invocation.html#index-bind-mount-1)

At least the documentation does not match the result. Note that the
documented behaviour would not work for /tmp bind mounted to itself.

It looks like back when this feature implemented mountlist in Gnulib
used /etc/mtab. Back then, it was very easy to find the alias for a
mounted file. Using the following setup:

     $ mkdir -p /tmp/test
     $ mount -t tmpfs tmpfs /tmp/test
     $ mkdir -p /tmp/test/source /tmp/test/dest
     $ mount --bind /tmp/test/source /tmp/test/dest

Here is what I see on a Debian 6 install:

     $ uname -sr
     Linux 2.6.32-5-amd64
     $ src/stat --version | head -n 1
     stat (GNU coreutils) 8.6
     $ src/stat -c %m /tmp/test/dest
     /tmp/test/source
     $ strace src/stat -c %m /tmp/test/dest 2>&1 >/dev/null \
         | grep '^open' | tail -n 1
     open("/etc/mtab", O_RDONLY)             = 3
     $ tail -n 1 /etc/mtab
     /tmp/test/source /tmp/test/dest none rw,bind 0 0
     $ tail -n 1 /proc/self/mountinfo
     23 22 0:17 /source /tmp/test/dest rw,relatime - tmpfs tmpfs rw

That made it very simple to get the alias. However, the format of
/etc/mtab changed at some point, such that it is no longer easy to get
the alias. Here is an example from my Fedora 44 machine, where you can
also see that we use /proc/self/mountinfo nowadays:

      $ uname -sr
      Linux 7.1.10-200.fc44.x86_64
      $ stat --version | head -n 1
      stat (GNU coreutils) 9.11.252-aea70
      $ src/stat -c %m /tmp/test/dest
      /tmp/test
      $ strace src/stat -c %m /tmp/test/dest 2>&1 >/dev/null | grep '^open' | 
sed -n 'x;$p'
      openat(AT_FDCWD, "/proc/self/mountinfo", O_RDONLY|O_CLOEXEC) = 3
      $ tail -n 1 /etc/mtab
      tmpfs /tmp/test/dest tmpfs rw,seclabel,relatime,inode64 0 0
      $ tail -n 1 /proc/self/mountinfo
      1570 1446 0:143 /source /tmp/test/dest rw,relatime shared:1267 - tmpfs 
tmpfs rw,seclabel,inode64

So, I guess we would have to get the parent mount point and then stat
the root (i.e., "source" in the above example) relative to it.

Thanks for the detailed report.

I believe these two patches should restore the documented behavior. We
have to use the mount ID and the parent mount ID from
/proc/self/mountinfo to determine if it is a bind mount.

The first patch is for Gnulib and the second is for coreutils. The
version in NEWS is roughly correct. I'm not too sure when the /etc/mtab
format changed, but I know when we started using /proc/self/mountinfo
started being used, so I have used that version:

     $ git tag --contains 974c355c595598a219785b6661bb6b030aaef4df \
         | sort --version-sort | head -n 1
     v8.24

So I have used that. On Debian 6 with coreutils-8.6:

     $ mkdir -p /tmp/test
     $ mount -t tmpfs tmpfs /tmp/test
     $ mkdir -p /tmp/test/source /tmp/test/dest
     $ mount --bind /tmp/test/source /tmp/test/dest
     $ touch /tmp/test/dest/a
     $ mkdir -p /tmp/test/dest/b
     $ src/stat -c %m /tmp/test/dest /tmp/test/source \
         /tmp/test /tmp/test/dest/a /tmp/test/source/a \
         /tmp/test/source/a /tmp/test/source/b
     /tmp/test/source
     /tmp/test
     /tmp/test
     /tmp/test
     /tmp/test
     /tmp/test
     /tmp/test

The result is the same on Fedora 44 after applying this patch.

I don't think me_parent_id always identifies the bind source?
This wouldn't worko for cross file system binds,
or if the parent exposes a non-root subtree I think?
E.g. does this work for the /dev/zero to /home/zero case in the original report?

Also I'm worried about that the file name concatenation
would need to consider parent->me_mntroot.

bool malloced_bp should be initialized to false,
as otherwise the if (malloced_bp) free(bp); could be UMR.

this stuff is hard to think about,
so no need to rush it in.

cheers,
Padraig

p.s. the gnulib patch commit message references /proc/self/mountlist,
which should be /proc/self/mountinfo

Reply via email to