c8d: Fix re-pull of an image when the snapshotter is changed
**- What I did**

Changing the snapshotter would result in docker re-pulling the image because the `PrepareSnapshot` method would return an error. We make sure that the image we want to prepare a snapshot for is indeed unpacked for the current snapshotter before preparing it.

Relates to #45273

**- How I did it**

**- How to verify it**

* start dockerd with the `overlayfs` snapshotter
* run an image `docker run --rm hello-world`
* restart dockerd and change the snapshotter (use `native` for example)
* run `docker images`, you should see the `hello-world` image in the list
* run the image: `docker run --rm hello-world`, docker should just run the image without re-pulling it

**- 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/image_snapshot.go
package containerd

import (
	"context"

	containerdimages "github.com/containerd/containerd/images"
	"github.com/containerd/containerd/leases"
	"github.com/containerd/containerd/platforms"
	"github.com/opencontainers/image-spec/identity"
	ocispec "github.com/opencontainers/image-spec/specs-go/v1"
)

// PrepareSnapshot prepares a snapshot from a parent image for a container
func (i *ImageService) PrepareSnapshot(ctx context.Context, id string, parentImage string, platform *ocispec.Platform) error {
	desc, err := i.resolveDescriptor(ctx, parentImage)
	if err != nil {
		return err
	}

	cs := i.client.ContentStore()

	matcher := platforms.Default()
	if platform != nil {
		matcher = platforms.Only(*platform)
	}

	desc, err = containerdimages.Config(ctx, cs, desc, matcher)
	if err != nil {
		return err
	}

	diffIDs, err := containerdimages.RootFS(ctx, cs, desc)
	if err != nil {
		return err
	}

	parent := identity.ChainID(diffIDs).String()

	// Add a lease so that containerd doesn't garbage collect our snapshot
	ls := i.client.LeasesService()
	lease, err := ls.Create(ctx, leases.WithID(id))
	if err != nil {
		return err
	}

	if err := ls.AddResource(ctx, lease, leases.Resource{
		ID:   id,
		Type: "snapshots/" + i.StorageDriver(),
	}); err != nil {
		return err
	}

	s := i.client.SnapshotService(i.StorageDriver())
	_, err = s.Prepare(ctx, id, parent)
	return err
}

package containerd

import (
	"context"

	"github.com/containerd/containerd"
	containerdimages "github.com/containerd/containerd/images"
	"github.com/containerd/containerd/leases"
	"github.com/containerd/containerd/platforms"
	"github.com/opencontainers/image-spec/identity"
	ocispec "github.com/opencontainers/image-spec/specs-go/v1"
)

// PrepareSnapshot prepares a snapshot from a parent image for a container
func (i *ImageService) PrepareSnapshot(ctx context.Context, id string, parentImage string, platform *ocispec.Platform) error {
	img, err := i.resolveImage(ctx, parentImage)
	if err != nil {
		return err
	}

	cs := i.client.ContentStore()

	matcher := platforms.Default()
	if platform != nil {
		matcher = platforms.Only(*platform)
	}

	platformImg := containerd.NewImageWithPlatform(i.client, img, matcher)
	unpacked, err := platformImg.IsUnpacked(ctx, i.snapshotter)
	if err != nil {
		return err
	}

	if !unpacked {
		if err := platformImg.Unpack(ctx, i.snapshotter); err != nil {
			return err
		}
	}

	desc, err := containerdimages.Config(ctx, cs, img.Target, matcher)
	if err != nil {
		return err
	}

	diffIDs, err := containerdimages.RootFS(ctx, cs, desc)
	if err != nil {
		return err
	}

	parent := identity.ChainID(diffIDs).String()

	// Add a lease so that containerd doesn't garbage collect our snapshot
	ls := i.client.LeasesService()
	lease, err := ls.Create(ctx, leases.WithID(id))
	if err != nil {
		return err
	}

	if err := ls.AddResource(ctx, lease, leases.Resource{
		ID:   id,
		Type: "snapshots/" + i.StorageDriver(),
	}); err != nil {
		return err
	}

	s := i.client.SnapshotService(i.StorageDriver())
	_, err = s.Prepare(ctx, id, parent)
	return err
}

At a quick glance, `PrepareShapshot()` is only called on container _create_, correct? So in this case we must always (at least currently) use the snapshotter that's used as a default, and never take existing containers (which were potentially created with "previous default") into account?