daemon/c8d: Unmount container fs after unclean shutdown

BaseFS is not serialized and is lost after an unclean shutdown. Unmount
method in the containerd image service implementation will not work
correctly in that case.
This patch will allow Unmount to restore the BaseFS if the target is
still mounted.

The reason it works with graphdrivers is that it doesn't directly
operate on BaseFS. It uses RWLayer, which is explicitly restored
immediately as soon as container is loaded.

Signed-off-by: Paweł Gronowski <pawel.gronowski@docker.com>
This commit is contained in:
Paweł Gronowski 2023-11-03 13:46:15 +01:00
parent 54fcd40aa4
commit 203bac0ec4
No known key found for this signature in database
GPG key ID: B85EFCFE26DEF92A
2 changed files with 44 additions and 3 deletions

View file

@ -2,6 +2,7 @@ package containerd
import (
"context"
"errors"
"fmt"
"github.com/containerd/log"
@ -30,11 +31,21 @@ func (i *ImageService) Mount(ctx context.Context, container *container.Container
// Unmount unmounts the container base filesystem
func (i *ImageService) Unmount(ctx context.Context, container *container.Container) error {
root := container.BaseFS
baseFS := container.BaseFS
if baseFS == "" {
target, err := i.refCountMounter.Mounted(container.ID)
if err != nil {
log.G(ctx).WithField("containerID", container.ID).Warn("failed to determine if container is already mounted")
}
if target == "" {
return errors.New("BaseFS is empty")
}
baseFS = target
}
if err := i.refCountMounter.Unmount(root); err != nil {
if err := i.refCountMounter.Unmount(baseFS); err != nil {
log.G(ctx).WithField("container", container.ID).WithError(err).Error("error unmounting container")
return fmt.Errorf("failed to unmount %s: %w", root, err)
return fmt.Errorf("failed to unmount %s: %w", baseFS, err)
}
return nil

View file

@ -10,6 +10,7 @@ import (
"github.com/docker/docker/daemon/graphdriver"
"github.com/docker/docker/pkg/idtools"
"github.com/moby/locker"
"github.com/moby/sys/mountinfo"
)
// List of known filesystems that can't be re-mounted or have shared layers
@ -22,6 +23,8 @@ type Mounter interface {
Mount(mounts []mount.Mount, containerID string) (string, error)
// Unmount unmounts the container rootfs
Unmount(target string) error
// Mounted returns a target mountpoint if it's already mounted
Mounted(containerID string) (string, error)
}
// inSlice tests whether a string is contained in a slice of strings or not.
@ -110,6 +113,23 @@ func (m *refCountMounter) Unmount(target string) error {
return nil
}
func (m *refCountMounter) Mounted(containerID string) (string, error) {
mounted, err := m.base.Mounted(containerID)
if err != nil || mounted == "" {
return mounted, err
}
target := m.base.target(containerID)
// Check if the refcount is non-zero.
m.rc.Increment(target)
if m.rc.Decrement(target) > 0 {
return mounted, nil
}
return "", nil
}
type mounter struct {
home string
snapshotter string
@ -137,6 +157,16 @@ func (m mounter) Unmount(target string) error {
return unmount(target)
}
func (m mounter) Mounted(containerID string) (string, error) {
target := m.target(containerID)
mounted, err := mountinfo.Mounted(target)
if err != nil || !mounted {
return "", err
}
return target, nil
}
func (m mounter) target(containerID string) string {
return filepath.Join(m.home, "rootfs", m.snapshotter, containerID)
}