After implementing sourcejedi's algorithm I hit a major roadblock as it gave me false positives, that is, declaring a visible mountpoint to be hidden. Let's take this set of mount points with their paths, IDs and parent IDs:
- ID:2, "/"
- ID:3, "/a/b", ParentID: 2
- ID:4, "/a/b/c", ParentID: 3
- ID:5, "/a/b/c/e/f", ParentID: 4
- ID:40, "/a/b/c", ParentID: 4
- ID:5, "/a", ParentID: 2
- ID:50, "/a/b/c/e/f", ParentID: 5
Only mount points with ID:2 "/", ID:5, "/a" and ID:50 "/a/b/c/e/f" should be visible. In my implementation of sourcejedi's algorithm step 3 gives me a false positive for mountpoint ID:50. I pondered for a long while how to correctly get PRE and PAR in this case, assuming that PRE is to be calculated on the "VFS view", that is, PRE is a mount path that may relate to multiple mount points. Moreover, there is a PRE that might reference only mount points from a different hierarchy and this combination might make step 3 trip.
Considering that the Linux kernel's VFS resolves paths into inodes by marching along the VFS nodes starting from the root "downwards" it is thus probably a safer bet to do the same for determining if a mount point is hidden or not (as opposed to figuring it out by going up the hierarchy, starting at the mount point in question). The downside is that we (more or less) need to determine overmounting for the complete tree of mount points before we can safely determine whether a specific mount point really is overmounted/hidden.
My implementation (in Golang) is as follows and successfully passes the test case above; it basically works as follows:
- Starting with the mount point at mount path "/":
- check for an "in-place" overmount, where the path P(child(MP)) of one of the children of the current mount point is the same as the path P(MP) of the current mount point.
- yes: mark all child mount points (recursively with their children) of the current mount point hidden, except for the child mount point that is overmounting the current mount point. Then recursively check the overmounting child mount point and be done afterwards.
- no: proceed
- check for children overmounting other children by comparing their paths: ISPREFIX(P(ith-child(MP)),P(jth-child(MP))):
- yes: recursively mark jth-child(MP) as hidden (overmounted), as well as all its (grand)children.
- no: proceed.
- for all child(MP) which aren't hidden (overmounted): recursively check them using this algorithm.
The implementation linked to has some mild optimizations, such as building a linked tree of mount points based on mount point IDs and sorting child mount points by their length(!) of mount paths. For instance, this allows to simplify the check for a child mount point overmounting its parent mount point to just looking at the first child mount point, if there is any. It also halves the O² prefix overmount search in the set of child mount points, which admittedly doesn't gain much in view of O²...