daemon/c8d: Unmount container fs after unclean shutdown
- depends on: https://github.com/moby/moby/pull/46769

`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.


**- What I did**

**- How I did it**

**- How to verify it**
```diff
$ make TEST_FILTER='TestCleanupMountsAfterDaemonAndContainerKill'   TEST_IGNORE_CGROUP_CHECK=1    DOCKER_GRAPHDRIVER=overlayfs TEST_INTEGRATION_USE_SNAPSHOTTER=1    test-integration
-=== FAIL: amd64.integration-cli TestDockerDaemonSuite/TestCleanupMountsAfterDaemonAndContainerKill (1.57s)
-    docker_cli_daemon_test.go:1326: [de493fadc3745] daemon is not started
-    docker_cli_daemon_test.go:1331: assertion failed: strings.Contains(string(mountOut), id) is true: f86f0c70e20064dc73d47b3561f1946ca210aa45baafbed69ce4c4c7f8920924 is still mounted from older daemon start:
-(...)
---- FAIL: TestDockerDaemonSuite/TestCleanupMountsAfterDaemonAndContainerKill (1.57s)
-
-=== FAIL: amd64.integration-cli TestDockerDaemonSuite (292.98s)
+=== RUN   TestDockerDaemonSuite/TestCleanupMountsAfterDaemonAndContainerKill
+    docker_cli_daemon_test.go:1326: [d2a267ea1e12e] daemon is not started
+--- PASS: TestDockerDaemonSuite (1.82s)
+    --- PASS: TestDockerDaemonSuite/TestCleanupMountsAfterDaemonAndContainerKill (1.82s)
```

**- Description for the changelog**
<!--
Write a short (one line) summary that describes the changes in this
pull request for inclusion in the changelog:
-->


**- A picture of a cute animal (not mandatory but encouraged)**


daemon/containerd/mount.go
package containerd

import (
	"context"
	"fmt"

	"github.com/containerd/log"
	"github.com/docker/docker/container"
)

// Mount mounts the container filesystem in a temporary location, use defer imageService.Unmount
// to unmount the filesystem when calling this
func (i *ImageService) Mount(ctx context.Context, container *container.Container) error {
	snapshotter := i.client.SnapshotService(container.Driver)
	mounts, err := snapshotter.Mounts(ctx, container.ID)
	if err != nil {
		return err
	}

	var root string
	if root, err = i.refCountMounter.Mount(mounts, container.ID); err != nil {
		return fmt.Errorf("failed to mount %s: %w", root, err)
	}

	log.G(ctx).WithField("container", container.ID).Debugf("container mounted via snapshotter: %v", root)

	container.BaseFS = root
	return nil
}

// Unmount unmounts the container base filesystem
func (i *ImageService) Unmount(ctx context.Context, container *container.Container) error {
	root := container.BaseFS

	if err := i.refCountMounter.Unmount(root); 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 nil
}

package containerd

import (
	"context"
	"errors"
	"fmt"

	"github.com/containerd/log"
	"github.com/docker/docker/container"
)

// Mount mounts the container filesystem in a temporary location, use defer imageService.Unmount
// to unmount the filesystem when calling this
func (i *ImageService) Mount(ctx context.Context, container *container.Container) error {
	snapshotter := i.client.SnapshotService(container.Driver)
	mounts, err := snapshotter.Mounts(ctx, container.ID)
	if err != nil {
		return err
	}

	var root string
	if root, err = i.refCountMounter.Mount(mounts, container.ID); err != nil {
		return fmt.Errorf("failed to mount %s: %w", root, err)
	}

	log.G(ctx).WithField("container", container.ID).Debugf("container mounted via snapshotter: %v", root)

	container.BaseFS = root
	return nil
}

// Unmount unmounts the container base filesystem
func (i *ImageService) Unmount(ctx context.Context, container *container.Container) error {
	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(baseFS); err != nil {
		log.G(ctx).WithField("container", container.ID).WithError(err).Error("error unmounting container")
		return fmt.Errorf("failed to unmount %s: %w", baseFS, err)
	}

	return nil
}

I'm trying and failing to understand why we re-populate the `BaseFS` on unmount, could you please explain? Seems very wrong 