Discussion:
Detecting bind-mounts
Michael Tokarev
2010-11-04 20:45:30 UTC
Permalink
Hello.

There are quite some talks on the 'net - questions, not
answers - about detecting bind mounts - be it a directory
or a file.

There are 2 (mostly) different kinds of applications. One
is cp/tar/find with --same-filesystem option (or equivalent),
that should not cross mountpoints. And one more, apps like
mountpoint(1) from sysvinit - a utility to determine if a
given path is a mountpoint.

Neither of the two work when two directores on the same
filesystem are bind-mounted.

The usual idiom is to compare st_dev of current directory and
the parent - if they're different that's a mount point. But
in this case, two st_devs will be the same, so such a mount
point will not be detected.

It is even worse for bind-mounted files (as opposed to dirs):
there's no path/file/.. entry to stat(2), and cutting the
last component from the pathname does not work reliable due
to symlinks (it may be a symlink from different filesystem).

So far I know only one way to detect a bind mount like this,
and it is unreliable anyway. It is to parse /proc/mounts
and try to find the object(s) in question. Unreliable because
of, again, symlinks, and possible complex mounts and bind-
mounts. And this is also very slow - imagine using this way
for find/tar/cp --one-file-system.

Is there some simpler and more reliable way? Maybe use mount
syscall, like we use kill($pid, 0) to check existance of a
process?

And as far as I understand, the same applies to multiple
mounts of the same filesystem.

Thanks!

/mjt
Pádraig Brady
2010-11-05 10:24:29 UTC
Permalink
Post by Michael Tokarev
Hello.
=20
There are quite some talks on the 'net - questions, not
answers - about detecting bind mounts - be it a directory
or a file.
=20
There are 2 (mostly) different kinds of applications. One
is cp/tar/find with --same-filesystem option (or equivalent),
that should not cross mountpoints. And one more, apps like
mountpoint(1) from sysvinit - a utility to determine if a
given path is a mountpoint.
=20
Neither of the two work when two directores on the same
filesystem are bind-mounted.
=20
The usual idiom is to compare st_dev of current directory and
the parent - if they're different that's a mount point. But
in this case, two st_devs will be the same, so such a mount
point will not be detected.
=20
there's no path/file/.. entry to stat(2), and cutting the
last component from the pathname does not work reliable due
to symlinks (it may be a symlink from different filesystem).
=20
So far I know only one way to detect a bind mount like this,
and it is unreliable anyway. It is to parse /proc/mounts
and try to find the object(s) in question. Unreliable because
of, again, symlinks, and possible complex mounts and bind-
mounts. And this is also very slow - imagine using this way
for find/tar/cp --one-file-system.
=20
Is there some simpler and more reliable way? Maybe use mount
syscall, like we use kill($pid, 0) to check existance of a
process?
=20
And as far as I understand, the same applies to multiple
mounts of the same filesystem.
The `stat` command recently got support for
printing the mount point for a file:
http://git.savannah.gnu.org/gitweb/?p=3Dcoreutils.git;a=3Dcommit;h=3Ddd=
f6fb86

`stat` will output the alias for a bind mounted file
while `df` will output the initial mount point of its backing device
So you could do something like:

file=3D.
df_mnt=3D$(df -P "$file" | sed -n '2s/.* \([^ ]*$\)/\1/p')
stat_mnt=3D$(stat -c%m "$file")
test "$df_mnt" =3D "$stat_mnt" || echo "bind mount"

cheers,
P=E1draig.
Michael Tokarev
2010-11-05 18:30:56 UTC
Permalink
[]
Post by Pádraig Brady
Post by Michael Tokarev
There are 2 (mostly) different kinds of applications. One
is cp/tar/find with --same-filesystem option (or equivalent),
that should not cross mountpoints. And one more, apps like
mountpoint(1) from sysvinit - a utility to determine if a
given path is a mountpoint.
Neither of the two work when two directores on the same
filesystem are bind-mounted.
[]
Post by Pádraig Brady
The `stat` command recently got support for
http://git.savannah.gnu.org/gitweb/?p=3Dcoreutils.git;a=3Dcommit;h=3D=
ddf6fb86
Post by Pádraig Brady
`stat` will output the alias for a bind mounted file
while `df` will output the initial mount point of its backing device
=20
file=3D.
df_mnt=3D$(df -P "$file" | sed -n '2s/.* \([^ ]*$\)/\1/p')
stat_mnt=3D$(stat -c%m "$file")
test "$df_mnt" =3D "$stat_mnt" || echo "bind mount"
This is incorrect in two ways.

=46irst of all, stat(1), even after that commit you quote,
still compares st_dev fields, which are the same for this
and parent directory in case of bind mount. So this version
of stat(1) does _not_ detect a bind mount, unfortunately.

Second, I asked for a low-level way to detect such a mount.
I know how to do it not as efficient as stat(2) and not as
reliable but much simpler than you propose above, in shell
or in C, and I already provided that way in my original
email: we just parse /proc/mounts file, this is faster and
more reliable than the above shell fragment which calls a
few external commands.

In the above example, both stat(1) (even the one with the
commit you refers to) and df(1) reports the same for the
case I'm referring to, the both fails to detect a bind-
mount.

Thanks!

/mjt
Michael Tokarev
2010-11-05 20:30:11 UTC
Permalink
Post by Michael Tokarev
[]
Post by Pádraig Brady
Post by Michael Tokarev
There are 2 (mostly) different kinds of applications. One
is cp/tar/find with --same-filesystem option (or equivalent),
that should not cross mountpoints. And one more, apps like
mountpoint(1) from sysvinit - a utility to determine if a
given path is a mountpoint.
Neither of the two work when two directores on the same
filesystem are bind-mounted.
[]
Post by Pádraig Brady
The `stat` command recently got support for
http://git.savannah.gnu.org/gitweb/?p=3Dcoreutils.git;a=3Dcommit;h=3D=
ddf6fb86
Post by Michael Tokarev
Post by Pádraig Brady
`stat` will output the alias for a bind mounted file
while `df` will output the initial mount point of its backing device
file=3D.
df_mnt=3D$(df -P "$file" | sed -n '2s/.* \([^ ]*$\)/\1/p')
stat_mnt=3D$(stat -c%m "$file")
test "$df_mnt" =3D "$stat_mnt" || echo "bind mount"
=20
This is incorrect in two ways.
=20
First of all, stat(1), even after that commit you quote,
still compares st_dev fields, which are the same for this
and parent directory in case of bind mount. So this version
of stat(1) does _not_ detect a bind mount, unfortunately.
And this statement, in turn, is untrue. I apologize for the
misinformation, it wasn't intentional. The mentioned commit
adds the ability to detect bind mounts indeed. but...
Post by Michael Tokarev
Second, I asked for a low-level way to detect such a mount.
I know how to do it not as efficient as stat(2) and not as
reliable but much simpler than you propose above, in shell
or in C, and I already provided that way in my original
email: we just parse /proc/mounts file, this is faster and
more reliable than the above shell fragment which calls a
few external commands.
=2E. the way used by stat(1) is to enumerate /proc/mounts --
which is what I were able to come with initially. It is slow
and unreliable. Hence I asked if a faster way exist.

/mjt
Pádraig Brady
2010-11-06 00:32:49 UTC
Permalink
Post by Michael Tokarev
Post by Michael Tokarev
[]
Post by Pádraig Brady
Post by Michael Tokarev
There are 2 (mostly) different kinds of applications. One
is cp/tar/find with --same-filesystem option (or equivalent),
that should not cross mountpoints. And one more, apps like
mountpoint(1) from sysvinit - a utility to determine if a
given path is a mountpoint.
Neither of the two work when two directores on the same
filesystem are bind-mounted.
[]
Post by Pádraig Brady
The `stat` command recently got support for
http://git.savannah.gnu.org/gitweb/?p=3Dcoreutils.git;a=3Dcommit;h=3D=
ddf6fb86
Post by Michael Tokarev
Post by Michael Tokarev
Post by Pádraig Brady
`stat` will output the alias for a bind mounted file
while `df` will output the initial mount point of its backing devic=
e
Post by Michael Tokarev
Post by Michael Tokarev
Post by Pádraig Brady
file=3D.
df_mnt=3D$(df -P "$file" | sed -n '2s/.* \([^ ]*$\)/\1/p')
stat_mnt=3D$(stat -c%m "$file")
test "$df_mnt" =3D "$stat_mnt" || echo "bind mount"
This is incorrect in two ways.
First of all, stat(1), even after that commit you quote,
still compares st_dev fields, which are the same for this
and parent directory in case of bind mount. So this version
of stat(1) does _not_ detect a bind mount, unfortunately.
=20
And this statement, in turn, is untrue. I apologize for the
misinformation, it wasn't intentional. The mentioned commit
adds the ability to detect bind mounts indeed. but...
=20
Post by Michael Tokarev
Second, I asked for a low-level way to detect such a mount.
I know how to do it not as efficient as stat(2) and not as
reliable but much simpler than you propose above, in shell
or in C, and I already provided that way in my original
email: we just parse /proc/mounts file, this is faster and
more reliable than the above shell fragment which calls a
few external commands.
=20
.. the way used by stat(1) is to enumerate /proc/mounts --
which is what I were able to come with initially. It is slow
and unreliable. Hence I asked if a faster way exist.
Sorry I was unclear.

I was just providing reference info about the changes in
the new `stat -c %m` that both showed tools do now exist
to determine if a file/dir is a mount point, and to
validate your "slow way" of detecting (bind) mounts.

As for a fast way, I don't think one exists.
BTW I'm not sure your examples are actually valid.
If a file/dir is bind mounted to the same file system, then
`find -xdev` should be listing it (as it has the same dev).

You want a separate option --same-mount or something,
though I don't know what it would be useful for.

cheers,
P=E1draig.
Michael Tokarev
2010-11-06 00:47:48 UTC
Permalink
06.11.2010 03:32, P=E1draig Brady wrote:
[]
Post by Pádraig Brady
As for a fast way, I don't think one exists.
BTW I'm not sure your examples are actually valid.
If a file/dir is bind mounted to the same file system, then
`find -xdev` should be listing it (as it has the same dev).
Think what, say, cp or tar with --one-file-system option are
used for. It is usually to copy a system to another place.
There, we only want single copy of everything, and with current
situation it will be two, with additional mess with hardlinks
for files wich were hardlinks already (due to optimizations
done by the utils based on link counts).

find -xdev is a bit different since it explicitly mentions
"dev", and in my examples the device is actually the same.
But usage case can be the same as for cp/tar above too, or
may be different.
Post by Pádraig Brady
You want a separate option --same-mount or something,
though I don't know what it would be useful for.
Stopping at the bind-mount dir definitely is useful, see
above for the "main" (and very important) usage case (this
can be solved differently on linux too - by cloning a new
namespace and removing the bind mounts before doing that
copy. but this is, again, ugly at best).

Note that this "main" usage case requires fast way to
determine mount points...

/mjt
Pádraig Brady
2010-11-06 10:57:29 UTC
Permalink
Post by Michael Tokarev
[]
Post by Pádraig Brady
As for a fast way, I don't think one exists.
BTW I'm not sure your examples are actually valid.
If a file/dir is bind mounted to the same file system, then
`find -xdev` should be listing it (as it has the same dev).
=20
Think what, say, cp or tar with --one-file-system option are
used for. It is usually to copy a system to another place.
There, we only want single copy of everything, and with current
situation it will be two, with additional mess with hardlinks
for files wich were hardlinks already (due to optimizations
done by the utils based on link counts).
=46air enough, but that starts to overlap with how hardlinks
are handled. If you're following symlinks then you can't
use the hardlink count. Note the du utility has recently
handled this by using a "di-set" to efficiently exclude
duplicate dev,inode pairs.
Post by Michael Tokarev
=20
find -xdev is a bit different since it explicitly mentions
"dev", and in my examples the device is actually the same.
But usage case can be the same as for cp/tar above too, or
may be different.
=20
Post by Pádraig Brady
You want a separate option --same-mount or something,
though I don't know what it would be useful for.
=20
Stopping at the bind-mount dir definitely is useful, see
above for the "main" (and very important) usage case (this
can be solved differently on linux too - by cloning a new
namespace and removing the bind mounts before doing that
copy. but this is, again, ugly at best).
=20
Note that this "main" usage case requires fast way to
determine mount points...
I can see how it would be useful, yes.

cheers,
P=E1draig.

Loading...