cmd/snap-update-ns: detach unused mount points (#6937)
* cmd/snap-update-ns: detach unused mount points
When snap-update-ns determines that a mount point is no longer needed
and should be unmounted it really tried to unmount things. This was
working because usually there were no nested mount structures present.
We were alerted that a particular layout would fail to refresh. The
layout in question is simply:
layout:
$SNAP/bar/mount:
bind: $SNAP/foo
$SNAP/baz:
bind: $SNAP/bar
That is:
- $SNAP/bar/mount is a view into $SNAP/foo
- $SNAP/baz is a view into $SNAP/bar
(which contains a view into SNAP/foo)
Since layout elements use $SNAP, which contains the revision number,
each time such layout is refreshed it will really be re-created as the
old paths will no longer be used. This will create a situation where
the mount point $SNAP/baz cannot be unmounted because there's
$SNAP/baz/mount is a mount point. This can be seen by looking at the
output of snap-update-ns debug log:
desired mount profile:
/snap/layout-content-test/x2/foo /snap/layout-content-test/x2/bar/mount none rbind,rw,x-snapd.origin=layout 0 0
/snap/layout-content-test/x2/bar /snap/layout-content-test/x2/baz none rbind,rw,x-snapd.origin=layout 0 0
current mount profile (before applying changes):
/snap/layout-content-test/x1/foo /snap/layout-content-test/x1/bar/mount none rbind,rw,x-snapd.origin=layout 0 0
/snap/layout-content-test/x1/bar /snap/layout-content-test/x1/baz none rbind,rw,x-snapd.origin=layout 0 0
desiredIDs: map[/snap/layout-content-test/x2/bar/mount:true /snap/layout-content-test/x2/baz:true]
reuse: map[]
mount changes needed:
unmount (/snap/layout-content-test/x1/bar /snap/layout-content-test/x1/baz none rbind,rw,x-snapd.origin=layout 0 0)
unmount (/snap/layout-content-test/x1/foo /snap/layout-content-test/x1/bar/mount none rbind,rw,x-snapd.origin=layout 0 0)
mount (/snap/layout-content-test/x2/foo /snap/layout-content-test/x2/bar/mount none rbind,rw,x-snapd.origin=layout 0 0)
mount (/snap/layout-content-test/x2/bar /snap/layout-content-test/x2/baz none rbind,rw,x-snapd.origin=layout 0 0)
performing mount changes:
* unmount (/snap/layout-content-test/x1/bar /snap/layout-content-test/x1/baz none rbind,rw,x-snapd.origin=layout 0 0)
umount "/snap/layout-content-test/x1/baz" (error: device or resource busy)
This is obviously something where we should have used MNT_DETACH, since
that will have the kernel handle the inner elements ahead of the outer
elements.
In fact, we should never really unmount anything as running applications
may hold open file descriptors and that would cause the mount namespace
update to fail miserably with partially-applied content changes. To
avoid this problem, whenever a mount entry, that doesn't represent a
symlink, is no longer needed it is simply detached, rather than
unmounted.
Fixes: https://bugs.launchpad.net/snapd/+bug/1831010
Signed-off-by: Zygmunt Krynicki <email address hidden>