Merge ~mwhudson/curtin:even-more-unmounting-tweaks into curtin:master

Proposed by Michael Hudson-Doyle
Status: Merged
Approved by: Michael Hudson-Doyle
Approved revision: 916c3380bfe3818298a77d37403fcd47eb54ec87
Merge reported by: Server Team CI bot
Merged at revision: not available
Proposed branch: ~mwhudson/curtin:even-more-unmounting-tweaks
Merge into: curtin:master
Diff against target: 120 lines (+65/-21)
1 file modified
curtin/util.py (+65/-21)
Reviewer Review Type Date Requested Status
Dan Bungert Approve
Server Team CI bot continuous-integration Approve
Review via email: mp+406682@code.launchpad.net

Commit message

tweak making mountpoints private some more

My recent change to move the making of mountpoints private into
do_umount actually made it ineffective. I tried to change things
so that we always made mountpoints private before unmounting them
but that ran into problems of its own. So I changed do_umount to
have a "private" flag ChrootableTarget.__exit__ could use to
request that mountpoints be made private before unmounting, which
makes things work again but I still don't see how to make a
generally robust "curtin unmount" command, as explained at length
in the comment I added.

Description of the change

this time for sure etc

To post a comment you must log in.
Revision history for this message
Dan Bungert (dbungert) :
Revision history for this message
Server Team CI bot (server-team-bot) wrote :
review: Needs Fixing (continuous-integration)
d9e9119... by Michael Hudson-Doyle

do not read /proc/mounts twice

Revision history for this message
Michael Hudson-Doyle (mwhudson) :
Revision history for this message
Server Team CI bot (server-team-bot) wrote :
review: Needs Fixing (continuous-integration)
bed091e... by Michael Hudson-Doyle

updates

686ef9e... by Michael Hudson-Doyle

extend essay even more

Revision history for this message
Server Team CI bot (server-team-bot) wrote :
review: Approve (continuous-integration)
Revision history for this message
Server Team CI bot (server-team-bot) wrote :
review: Approve (continuous-integration)
fbe90ad... by Michael Hudson-Doyle

grammmmeeer

916c338... by Michael Hudson-Doyle

more wordsmithing

Revision history for this message
Server Team CI bot (server-team-bot) wrote :
review: Approve (continuous-integration)
Revision history for this message
Dan Bungert (dbungert) wrote :

LGTM

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1diff --git a/curtin/util.py b/curtin/util.py
2index ec1d4f9..5b66b55 100644
3--- a/curtin/util.py
4+++ b/curtin/util.py
5@@ -518,31 +518,69 @@ def do_mount(src, target, opts=None):
6 return True
7
8
9-def do_umount(mountpoint, recursive=False):
10+def do_umount(mountpoint, recursive=False, private=False):
11 mp = os.path.abspath(mountpoint)
12- # unmount mountpoint. if recursive, unmount all mounts under it.
13+ # unmount mountpoint.
14 #
15- # note that recursive unmounting is different to umount -R's
16- # behaviour. Consider the following sequence:
17+ # if recursive, unmount all mounts under it. if private (which
18+ # implies recursive), mark all mountpoints private before
19+ # unmounting.
20+ #
21+ # To explain the 'private' parameter, consider the following sequence:
22 #
23 # mkdir a a/b c
24 # mount --bind a c
25 # mount -t sysfs sysfs a/b
26 #
27- # "umount c" will fail, with "mountpoint is busy", because of the
28- # mountpoint at c/b. But "umount -R c" will unmount a/b (and as in
29- # pratice "a/b" is often actually "/sys" itself, this would be
30- # Bad(tm)). So what we do is iterate over the mountpoints under
31- # `mountpoint` and mark each one as "private" before unmounting
32- # it, which means the unmount does not propagate to any mount tree
33- # it was cloned from (See "Shared subtree operations" in mount(8)
34- # for more on this).
35+ # "umount c" will fail with "mountpoint is busy" because the mount
36+ # of a/b "propagates" into the subtree at c, i.e. creates a mount
37+ # at "c/b". But if you run "umount -R c" the unmount of c/b will
38+ # propagate to back to a and unmount a/b (and as in pratice "a/b"
39+ # can be something like driver specific mounts in /sys, this would
40+ # be Bad(tm)). So what we do here is iterate over the mountpoints
41+ # under `mountpoint` and mark them as "private" doing any
42+ # unmounting, which means the unmounts do not propagate to any
43+ # mount tree they were cloned from.
44+ #
45+ # So why not do this always? Well! Several systemd services (like
46+ # udevd) run in private mount namespaces, set up so that mount
47+ # operations on the default namespace propagate into the service's
48+ # namespace (but not the other way). This means if you do this:
49+ #
50+ # mount /dev/sda2 /tmp/my-mount
51+ # mount --make-private /tmp/my-mount
52+ # umount /tmp/my-mount
53+ #
54+ # then the mount operation propagates into udevd's mount namespace
55+ # but the unmount operation does not and so /dev/sda2 remains
56+ # mounted, which causes problems down the road.
57+ #
58+ # It would be nice to not have to have the caller care about all
59+ # this detail. In particular imagine the target system is set up
60+ # at /target and has host directories bind-mounted into it, so the
61+ # mount tree looks something like this:
62+ #
63+ # /dev/vda2 is mounted at /target
64+ # /dev/vda1 is mounted at /target/boot
65+ # /sys is bind-mounted at /target/sys
66+ #
67+ # And for whatever reason, a mount has appeared at /sys/foo since
68+ # this was setup, so there is now an additional mount at
69+ # /target/sys/foo.
70+ #
71+ # What I would like is to be able to run something like "curtin
72+ # unmount /target" and have curtin figure out that the mountpoint
73+ # at /target/sys/foo should be made private before unmounting and
74+ # the others should not. But I don't know how to do that.
75+ #
76+ # See "Shared subtree operations" in mount(8) for more on all
77+ # this.
78 #
79- # You might think we could replace all this with a call to "mount
80- # --make-rprivate" followed by a call to "umount --recursive" but
81- # that would fail in the case where `mountpoint` is not actually a
82- # mount point, and not doing that is actually relied on by other
83- # parts of curtin.
84+ # You might also think we could replace all this with a call to
85+ # "mount --make-rprivate" followed by a call to "umount
86+ # --recursive" but that would fail in the case where `mountpoint`
87+ # is not actually a mount point, and not doing that is actually
88+ # relied on by other parts of curtin.
89 #
90 # Related bug reports:
91 # https://bugs.launchpad.net/maas/+bug/1928839
92@@ -550,10 +588,16 @@ def do_umount(mountpoint, recursive=False):
93 #
94 # return boolean indicating if mountpoint was previously mounted.
95 ret = False
96- for line in reversed(load_file("/proc/mounts", decode=True).splitlines()):
97- curmp = line.split()[1]
98+ mountpoints = [
99+ line.split()[1]
100+ for line in load_file("/proc/mounts", decode=True).splitlines()]
101+ if private:
102+ recursive = True
103+ for curmp in mountpoints:
104+ if curmp == mp or curmp.startswith(mp + os.path.sep):
105+ subp(['mount', '--make-private', curmp])
106+ for curmp in reversed(mountpoints):
107 if curmp == mp or (recursive and curmp.startswith(mp + os.path.sep)):
108- subp(['mount', '--make-private', curmp])
109 subp(['umount', curmp])
110 if curmp == mp:
111 ret = True
112@@ -723,7 +767,7 @@ class ChrootableTarget(object):
113 log_call(subp, ['udevadm', 'settle'])
114
115 for p in reversed(self.umounts):
116- do_umount(p, recursive=True)
117+ do_umount(p, private=True)
118
119 rconf = paths.target_path(self.target, "/etc/resolv.conf")
120 if self.sys_resolvconf and self.rconf_d:

Subscribers

People subscribed via source and target branches