Skip to content

os.path.ismounts: further situations it doesn't detect correctly #96328

Open
@calestyo

Description

@calestyo

Bug report

I had a look at os.path.ismount():

cpython/Lib/posixpath.py

Lines 186 to 216 in 43a6dea

def ismount(path):
"""Test whether a path is a mount point"""
try:
s1 = os.lstat(path)
except (OSError, ValueError):
# It doesn't exist -- so not a mount point. :-)
return False
else:
# A symlink can never be a mount point
if stat.S_ISLNK(s1.st_mode):
return False
if isinstance(path, bytes):
parent = join(path, b'..')
else:
parent = join(path, '..')
parent = realpath(parent)
try:
s2 = os.lstat(parent)
except (OSError, ValueError):
return False
dev1 = s1.st_dev
dev2 = s2.st_dev
if dev1 != dev2:
return True # path/.. on a different device as path
ino1 = s1.st_ino
ino2 = s2.st_ino
if ino1 == ino2:
return True # path/.. is the same i-node as path
return False

And it may have some issues:

  1. The documentation already says, that it isn't capable of detecting bind-mounts of some filesystem on the very same filesystem. While it's good that it mentions this, the results of ismount() is IMO still simply wrong in these cases, cause it is a mountpoint.
  2. The same problem in the detection algorithm is not only limited to bind-mounts, but also to any other mount points, where the path’s st_dev equals that of its parent. For example, btrfs subvolumes. This is however not documented.

Looking a the code, it effectively does: lstat(), check whether it’s a symlink, lstat() the parent, check whether the device numbers are different (then it's a mountpoint), check whether the inodes are the same.

Now I thought about the latter, and what it is meant for.

The only case I could think of at first is /, where /.. is the same inode. [Is there any other?]

  1. This however already requires, that not only the inodes are the same, but also the device numbers. which is given because of the order of these checks, however, I'd suggest to comment that accordingly.
  2. If the above were the only case, wouldn't it be faster to simply check the pathname for being "/" or b"/". Would perhaps require some realpath() though, to also catch things like "/../.." and so on.
  3. However, I found that / is not the only case: Within a chroot, the chroot's / may (or may not) be a mountpoint - the problem is just, the current algorithm doesn't detect this either. Instead, it wrongly claims that a chroot’s "/" would be a mountpoint, even when it isn't.

Not sure what one could do about all that… /etc/mtab is dead... /proc/mounts may not be available on all POSIX systems and even on Linux it's nowadays rather superseded by /proc/self/mountinfo.

Your environment

  • CPython versions tested on: 3.10.6
  • Operating system and architecture: Debian sid

Cheers,
Chris.

Metadata

Metadata

Assignees

No one assigned

    Labels

    docsDocumentation in the Doc dirtype-bugAn unexpected behavior, bug, or error

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions