Loop umount'ing rootfs until there are no more mounts

This is simpler than trying to count how many successful mounts we made.

Signed-off-by: Ian Campbell <ian.campbell@docker.com>
This commit is contained in:
Ian Campbell 2017-07-13 09:59:19 +01:00
parent d63d2ecf6c
commit d42cb88ba2
5 changed files with 42 additions and 35 deletions

View File

@ -39,19 +39,18 @@ type initProcess struct {
// the reaper interface. // the reaper interface.
mu sync.Mutex mu sync.Mutex
id string id string
bundle string bundle string
console console.Console console console.Console
io runc.IO io runc.IO
runtime *runc.Runc runtime *runc.Runc
status int status int
exited time.Time exited time.Time
pid int pid int
closers []io.Closer closers []io.Closer
stdin io.Closer stdin io.Closer
stdio stdio stdio stdio
rootfs string rootfs string
nrRootMounts int // Number of rootfs overmounts
} }
func newInitProcess(context context.Context, path, namespace string, r *shimapi.CreateTaskRequest) (*initProcess, error) { func newInitProcess(context context.Context, path, namespace string, r *shimapi.CreateTaskRequest) (*initProcess, error) {
@ -73,12 +72,11 @@ func newInitProcess(context context.Context, path, namespace string, r *shimapi.
// count the number of successful mounts so we can undo // count the number of successful mounts so we can undo
// what was actually done rather than what should have been // what was actually done rather than what should have been
// done. // done.
nrRootMounts := 0
defer func() { defer func() {
if success { if success {
return return
} }
if err2 := mount.UnmountN(rootfs, 0, nrRootMounts); err2 != nil { if err2 := mount.UnmountAll(rootfs, 0); err2 != nil {
log.G(context).WithError(err2).Warn("Failed to cleanup rootfs mount") log.G(context).WithError(err2).Warn("Failed to cleanup rootfs mount")
} }
}() }()
@ -91,7 +89,6 @@ func newInitProcess(context context.Context, path, namespace string, r *shimapi.
if err := m.Mount(rootfs); err != nil { if err := m.Mount(rootfs); err != nil {
return nil, errors.Wrapf(err, "failed to mount rootfs component %v", m) return nil, errors.Wrapf(err, "failed to mount rootfs component %v", m)
} }
nrRootMounts++
} }
runtime := &runc.Runc{ runtime := &runc.Runc{
Command: r.Runtime, Command: r.Runtime,
@ -110,8 +107,7 @@ func newInitProcess(context context.Context, path, namespace string, r *shimapi.
stderr: r.Stderr, stderr: r.Stderr,
terminal: r.Terminal, terminal: r.Terminal,
}, },
rootfs: rootfs, rootfs: rootfs,
nrRootMounts: nrRootMounts,
} }
var ( var (
err error err error
@ -253,7 +249,7 @@ func (p *initProcess) Delete(context context.Context) error {
} }
err = p.runtimeError(err, "OCI runtime delete failed") err = p.runtimeError(err, "OCI runtime delete failed")
if err2 := mount.UnmountN(p.rootfs, 0, p.nrRootMounts); err2 != nil { if err2 := mount.UnmountAll(p.rootfs, 0); err2 != nil {
log.G(context).WithError(err2).Warn("Failed to cleanup rootfs mount") log.G(context).WithError(err2).Warn("Failed to cleanup rootfs mount")
if err == nil { if err == nil {
err = errors.Wrap(err2, "Failed rootfs umount") err = errors.Wrap(err2, "Failed rootfs umount")

View File

@ -22,19 +22,3 @@ func MountAll(mounts []Mount, target string) error {
} }
return nil return nil
} }
// UnmountN tries to unmount the given mount point nr times, which is
// useful for undoing a stack of mounts on the same mount
// point. Returns the first error encountered, but always attempts the
// full nr umounts.
func UnmountN(mount string, flags, nr int) error {
var err error
for i := 0; i < nr; i++ {
if err2 := Unmount(mount, flags); err2 != nil {
if err == nil {
err = err2
}
}
}
return err
}

View File

@ -15,6 +15,25 @@ func Unmount(mount string, flags int) error {
return unix.Unmount(mount, flags) return unix.Unmount(mount, flags)
} }
// UnmountAll repeatedly unmounts the given mount point until there
// are no mounts remaining (EINVAL is returned by mount), which is
// useful for undoing a stack of mounts on the same mount point.
func UnmountAll(mount string, flags int) error {
for {
if err := Unmount(mount, flags); err != nil {
// EINVAL is returned if the target is not a
// mount point, indicating that we are
// done. It can also indicate a few other
// things (such as invalid flags) which we
// unfortunately end up squelching here too.
if err == unix.EINVAL {
return nil
}
return err
}
}
}
// parseMountOptions takes fstab style mount options and parses them for // parseMountOptions takes fstab style mount options and parses them for
// use with a standard mount() syscall // use with a standard mount() syscall
func parseMountOptions(options []string) (int, string) { func parseMountOptions(options []string) (int, string) {

View File

@ -15,3 +15,7 @@ func (m *Mount) Mount(target string) error {
func Unmount(mount string, flags int) error { func Unmount(mount string, flags int) error {
return ErrNotImplementOnUnix return ErrNotImplementOnUnix
} }
func UnmountAll(mount string, flags int) error {
return ErrNotImplementOnUnix
}

View File

@ -13,3 +13,7 @@ func (m *Mount) Mount(target string) error {
func Unmount(mount string, flags int) error { func Unmount(mount string, flags int) error {
return ErrNotImplementOnWindows return ErrNotImplementOnWindows
} }
func UnmountAll(mount string, flags int) error {
return ErrNotImplementOnWindows
}