github.com/kimh/docker@v1.10.0/distribution/pull_v2.go (about)

     1  package distribution
     2  
     3  import (
     4  	"encoding/json"
     5  	"errors"
     6  	"fmt"
     7  	"io"
     8  	"io/ioutil"
     9  	"os"
    10  	"runtime"
    11  
    12  	"github.com/Sirupsen/logrus"
    13  	"github.com/docker/distribution"
    14  	"github.com/docker/distribution/digest"
    15  	"github.com/docker/distribution/manifest/manifestlist"
    16  	"github.com/docker/distribution/manifest/schema1"
    17  	"github.com/docker/distribution/manifest/schema2"
    18  	"github.com/docker/distribution/registry/api/errcode"
    19  	"github.com/docker/distribution/registry/client"
    20  	"github.com/docker/docker/distribution/metadata"
    21  	"github.com/docker/docker/distribution/xfer"
    22  	"github.com/docker/docker/image"
    23  	"github.com/docker/docker/image/v1"
    24  	"github.com/docker/docker/layer"
    25  	"github.com/docker/docker/pkg/ioutils"
    26  	"github.com/docker/docker/pkg/progress"
    27  	"github.com/docker/docker/pkg/stringid"
    28  	"github.com/docker/docker/reference"
    29  	"github.com/docker/docker/registry"
    30  	"golang.org/x/net/context"
    31  )
    32  
    33  var errRootFSMismatch = errors.New("layers from manifest don't match image configuration")
    34  
    35  type v2Puller struct {
    36  	V2MetadataService *metadata.V2MetadataService
    37  	endpoint          registry.APIEndpoint
    38  	config            *ImagePullConfig
    39  	repoInfo          *registry.RepositoryInfo
    40  	repo              distribution.Repository
    41  	// confirmedV2 is set to true if we confirm we're talking to a v2
    42  	// registry. This is used to limit fallbacks to the v1 protocol.
    43  	confirmedV2 bool
    44  }
    45  
    46  func (p *v2Puller) Pull(ctx context.Context, ref reference.Named) (err error) {
    47  	// TODO(tiborvass): was ReceiveTimeout
    48  	p.repo, p.confirmedV2, err = NewV2Repository(ctx, p.repoInfo, p.endpoint, p.config.MetaHeaders, p.config.AuthConfig, "pull")
    49  	if err != nil {
    50  		logrus.Warnf("Error getting v2 registry: %v", err)
    51  		return fallbackError{err: err, confirmedV2: p.confirmedV2}
    52  	}
    53  
    54  	if err = p.pullV2Repository(ctx, ref); err != nil {
    55  		if _, ok := err.(fallbackError); ok {
    56  			return err
    57  		}
    58  		if registry.ContinueOnError(err) {
    59  			logrus.Debugf("Error trying v2 registry: %v", err)
    60  			return fallbackError{err: err, confirmedV2: p.confirmedV2}
    61  		}
    62  	}
    63  	return err
    64  }
    65  
    66  func (p *v2Puller) pullV2Repository(ctx context.Context, ref reference.Named) (err error) {
    67  	var layersDownloaded bool
    68  	if !reference.IsNameOnly(ref) {
    69  		layersDownloaded, err = p.pullV2Tag(ctx, ref)
    70  		if err != nil {
    71  			return err
    72  		}
    73  	} else {
    74  		tags, err := p.repo.Tags(ctx).All(ctx)
    75  		if err != nil {
    76  			// If this repository doesn't exist on V2, we should
    77  			// permit a fallback to V1.
    78  			return allowV1Fallback(err)
    79  		}
    80  
    81  		// The v2 registry knows about this repository, so we will not
    82  		// allow fallback to the v1 protocol even if we encounter an
    83  		// error later on.
    84  		p.confirmedV2 = true
    85  
    86  		for _, tag := range tags {
    87  			tagRef, err := reference.WithTag(ref, tag)
    88  			if err != nil {
    89  				return err
    90  			}
    91  			pulledNew, err := p.pullV2Tag(ctx, tagRef)
    92  			if err != nil {
    93  				// Since this is the pull-all-tags case, don't
    94  				// allow an error pulling a particular tag to
    95  				// make the whole pull fall back to v1.
    96  				if fallbackErr, ok := err.(fallbackError); ok {
    97  					return fallbackErr.err
    98  				}
    99  				return err
   100  			}
   101  			// pulledNew is true if either new layers were downloaded OR if existing images were newly tagged
   102  			// TODO(tiborvass): should we change the name of `layersDownload`? What about message in WriteStatus?
   103  			layersDownloaded = layersDownloaded || pulledNew
   104  		}
   105  	}
   106  
   107  	writeStatus(ref.String(), p.config.ProgressOutput, layersDownloaded)
   108  
   109  	return nil
   110  }
   111  
   112  type v2LayerDescriptor struct {
   113  	digest            digest.Digest
   114  	repoInfo          *registry.RepositoryInfo
   115  	repo              distribution.Repository
   116  	V2MetadataService *metadata.V2MetadataService
   117  }
   118  
   119  func (ld *v2LayerDescriptor) Key() string {
   120  	return "v2:" + ld.digest.String()
   121  }
   122  
   123  func (ld *v2LayerDescriptor) ID() string {
   124  	return stringid.TruncateID(ld.digest.String())
   125  }
   126  
   127  func (ld *v2LayerDescriptor) DiffID() (layer.DiffID, error) {
   128  	return ld.V2MetadataService.GetDiffID(ld.digest)
   129  }
   130  
   131  func (ld *v2LayerDescriptor) Download(ctx context.Context, progressOutput progress.Output) (io.ReadCloser, int64, error) {
   132  	logrus.Debugf("pulling blob %q", ld.digest)
   133  
   134  	blobs := ld.repo.Blobs(ctx)
   135  
   136  	layerDownload, err := blobs.Open(ctx, ld.digest)
   137  	if err != nil {
   138  		logrus.Debugf("Error statting layer: %v", err)
   139  		if err == distribution.ErrBlobUnknown {
   140  			return nil, 0, xfer.DoNotRetry{Err: err}
   141  		}
   142  		return nil, 0, retryOnError(err)
   143  	}
   144  
   145  	size, err := layerDownload.Seek(0, os.SEEK_END)
   146  	if err != nil {
   147  		// Seek failed, perhaps because there was no Content-Length
   148  		// header. This shouldn't fail the download, because we can
   149  		// still continue without a progress bar.
   150  		size = 0
   151  	} else {
   152  		// Restore the seek offset at the beginning of the stream.
   153  		_, err = layerDownload.Seek(0, os.SEEK_SET)
   154  		if err != nil {
   155  			return nil, 0, err
   156  		}
   157  	}
   158  
   159  	reader := progress.NewProgressReader(ioutils.NewCancelReadCloser(ctx, layerDownload), progressOutput, size, ld.ID(), "Downloading")
   160  	defer reader.Close()
   161  
   162  	verifier, err := digest.NewDigestVerifier(ld.digest)
   163  	if err != nil {
   164  		return nil, 0, xfer.DoNotRetry{Err: err}
   165  	}
   166  
   167  	tmpFile, err := ioutil.TempFile("", "GetImageBlob")
   168  	if err != nil {
   169  		return nil, 0, xfer.DoNotRetry{Err: err}
   170  	}
   171  
   172  	_, err = io.Copy(tmpFile, io.TeeReader(reader, verifier))
   173  	if err != nil {
   174  		tmpFile.Close()
   175  		if err := os.Remove(tmpFile.Name()); err != nil {
   176  			logrus.Errorf("Failed to remove temp file: %s", tmpFile.Name())
   177  		}
   178  		return nil, 0, retryOnError(err)
   179  	}
   180  
   181  	progress.Update(progressOutput, ld.ID(), "Verifying Checksum")
   182  
   183  	if !verifier.Verified() {
   184  		err = fmt.Errorf("filesystem layer verification failed for digest %s", ld.digest)
   185  		logrus.Error(err)
   186  
   187  		tmpFile.Close()
   188  		if err := os.Remove(tmpFile.Name()); err != nil {
   189  			logrus.Errorf("Failed to remove temp file: %s", tmpFile.Name())
   190  		}
   191  
   192  		return nil, 0, xfer.DoNotRetry{Err: err}
   193  	}
   194  
   195  	progress.Update(progressOutput, ld.ID(), "Download complete")
   196  
   197  	logrus.Debugf("Downloaded %s to tempfile %s", ld.ID(), tmpFile.Name())
   198  
   199  	_, err = tmpFile.Seek(0, os.SEEK_SET)
   200  	if err != nil {
   201  		tmpFile.Close()
   202  		if err := os.Remove(tmpFile.Name()); err != nil {
   203  			logrus.Errorf("Failed to remove temp file: %s", tmpFile.Name())
   204  		}
   205  		return nil, 0, xfer.DoNotRetry{Err: err}
   206  	}
   207  	return ioutils.NewReadCloserWrapper(tmpFile, tmpFileCloser(tmpFile)), size, nil
   208  }
   209  
   210  func (ld *v2LayerDescriptor) Registered(diffID layer.DiffID) {
   211  	// Cache mapping from this layer's DiffID to the blobsum
   212  	ld.V2MetadataService.Add(diffID, metadata.V2Metadata{Digest: ld.digest, SourceRepository: ld.repoInfo.FullName()})
   213  }
   214  
   215  func (p *v2Puller) pullV2Tag(ctx context.Context, ref reference.Named) (tagUpdated bool, err error) {
   216  	manSvc, err := p.repo.Manifests(ctx)
   217  	if err != nil {
   218  		return false, err
   219  	}
   220  
   221  	var (
   222  		manifest    distribution.Manifest
   223  		tagOrDigest string // Used for logging/progress only
   224  	)
   225  	if tagged, isTagged := ref.(reference.NamedTagged); isTagged {
   226  		// NOTE: not using TagService.Get, since it uses HEAD requests
   227  		// against the manifests endpoint, which are not supported by
   228  		// all registry versions.
   229  		manifest, err = manSvc.Get(ctx, "", client.WithTag(tagged.Tag()))
   230  		if err != nil {
   231  			return false, allowV1Fallback(err)
   232  		}
   233  		tagOrDigest = tagged.Tag()
   234  	} else if digested, isDigested := ref.(reference.Canonical); isDigested {
   235  		manifest, err = manSvc.Get(ctx, digested.Digest())
   236  		if err != nil {
   237  			return false, err
   238  		}
   239  		tagOrDigest = digested.Digest().String()
   240  	} else {
   241  		return false, fmt.Errorf("internal error: reference has neither a tag nor a digest: %s", ref.String())
   242  	}
   243  
   244  	if manifest == nil {
   245  		return false, fmt.Errorf("image manifest does not exist for tag or digest %q", tagOrDigest)
   246  	}
   247  
   248  	// If manSvc.Get succeeded, we can be confident that the registry on
   249  	// the other side speaks the v2 protocol.
   250  	p.confirmedV2 = true
   251  
   252  	logrus.Debugf("Pulling ref from V2 registry: %s", ref.String())
   253  	progress.Message(p.config.ProgressOutput, tagOrDigest, "Pulling from "+p.repo.Name())
   254  
   255  	var (
   256  		imageID        image.ID
   257  		manifestDigest digest.Digest
   258  	)
   259  
   260  	switch v := manifest.(type) {
   261  	case *schema1.SignedManifest:
   262  		imageID, manifestDigest, err = p.pullSchema1(ctx, ref, v)
   263  		if err != nil {
   264  			return false, err
   265  		}
   266  	case *schema2.DeserializedManifest:
   267  		imageID, manifestDigest, err = p.pullSchema2(ctx, ref, v)
   268  		if err != nil {
   269  			return false, err
   270  		}
   271  	case *manifestlist.DeserializedManifestList:
   272  		imageID, manifestDigest, err = p.pullManifestList(ctx, ref, v)
   273  		if err != nil {
   274  			return false, err
   275  		}
   276  	default:
   277  		return false, errors.New("unsupported manifest format")
   278  	}
   279  
   280  	progress.Message(p.config.ProgressOutput, "", "Digest: "+manifestDigest.String())
   281  
   282  	oldTagImageID, err := p.config.ReferenceStore.Get(ref)
   283  	if err == nil {
   284  		if oldTagImageID == imageID {
   285  			return false, nil
   286  		}
   287  	} else if err != reference.ErrDoesNotExist {
   288  		return false, err
   289  	}
   290  
   291  	if canonical, ok := ref.(reference.Canonical); ok {
   292  		if err = p.config.ReferenceStore.AddDigest(canonical, imageID, true); err != nil {
   293  			return false, err
   294  		}
   295  	} else if err = p.config.ReferenceStore.AddTag(ref, imageID, true); err != nil {
   296  		return false, err
   297  	}
   298  
   299  	return true, nil
   300  }
   301  
   302  func (p *v2Puller) pullSchema1(ctx context.Context, ref reference.Named, unverifiedManifest *schema1.SignedManifest) (imageID image.ID, manifestDigest digest.Digest, err error) {
   303  	var verifiedManifest *schema1.Manifest
   304  	verifiedManifest, err = verifySchema1Manifest(unverifiedManifest, ref)
   305  	if err != nil {
   306  		return "", "", err
   307  	}
   308  
   309  	rootFS := image.NewRootFS()
   310  
   311  	if err := detectBaseLayer(p.config.ImageStore, verifiedManifest, rootFS); err != nil {
   312  		return "", "", err
   313  	}
   314  
   315  	// remove duplicate layers and check parent chain validity
   316  	err = fixManifestLayers(verifiedManifest)
   317  	if err != nil {
   318  		return "", "", err
   319  	}
   320  
   321  	var descriptors []xfer.DownloadDescriptor
   322  
   323  	// Image history converted to the new format
   324  	var history []image.History
   325  
   326  	// Note that the order of this loop is in the direction of bottom-most
   327  	// to top-most, so that the downloads slice gets ordered correctly.
   328  	for i := len(verifiedManifest.FSLayers) - 1; i >= 0; i-- {
   329  		blobSum := verifiedManifest.FSLayers[i].BlobSum
   330  
   331  		var throwAway struct {
   332  			ThrowAway bool `json:"throwaway,omitempty"`
   333  		}
   334  		if err := json.Unmarshal([]byte(verifiedManifest.History[i].V1Compatibility), &throwAway); err != nil {
   335  			return "", "", err
   336  		}
   337  
   338  		h, err := v1.HistoryFromConfig([]byte(verifiedManifest.History[i].V1Compatibility), throwAway.ThrowAway)
   339  		if err != nil {
   340  			return "", "", err
   341  		}
   342  		history = append(history, h)
   343  
   344  		if throwAway.ThrowAway {
   345  			continue
   346  		}
   347  
   348  		layerDescriptor := &v2LayerDescriptor{
   349  			digest:            blobSum,
   350  			repoInfo:          p.repoInfo,
   351  			repo:              p.repo,
   352  			V2MetadataService: p.V2MetadataService,
   353  		}
   354  
   355  		descriptors = append(descriptors, layerDescriptor)
   356  	}
   357  
   358  	resultRootFS, release, err := p.config.DownloadManager.Download(ctx, *rootFS, descriptors, p.config.ProgressOutput)
   359  	if err != nil {
   360  		return "", "", err
   361  	}
   362  	defer release()
   363  
   364  	config, err := v1.MakeConfigFromV1Config([]byte(verifiedManifest.History[0].V1Compatibility), &resultRootFS, history)
   365  	if err != nil {
   366  		return "", "", err
   367  	}
   368  
   369  	imageID, err = p.config.ImageStore.Create(config)
   370  	if err != nil {
   371  		return "", "", err
   372  	}
   373  
   374  	manifestDigest = digest.FromBytes(unverifiedManifest.Canonical)
   375  
   376  	return imageID, manifestDigest, nil
   377  }
   378  
   379  func (p *v2Puller) pullSchema2(ctx context.Context, ref reference.Named, mfst *schema2.DeserializedManifest) (imageID image.ID, manifestDigest digest.Digest, err error) {
   380  	manifestDigest, err = schema2ManifestDigest(ref, mfst)
   381  	if err != nil {
   382  		return "", "", err
   383  	}
   384  
   385  	target := mfst.Target()
   386  	imageID = image.ID(target.Digest)
   387  	if _, err := p.config.ImageStore.Get(imageID); err == nil {
   388  		// If the image already exists locally, no need to pull
   389  		// anything.
   390  		return imageID, manifestDigest, nil
   391  	}
   392  
   393  	configChan := make(chan []byte, 1)
   394  	errChan := make(chan error, 1)
   395  	var cancel func()
   396  	ctx, cancel = context.WithCancel(ctx)
   397  
   398  	// Pull the image config
   399  	go func() {
   400  		configJSON, err := p.pullSchema2ImageConfig(ctx, target.Digest)
   401  		if err != nil {
   402  			errChan <- err
   403  			cancel()
   404  			return
   405  		}
   406  		configChan <- configJSON
   407  	}()
   408  
   409  	var descriptors []xfer.DownloadDescriptor
   410  
   411  	// Note that the order of this loop is in the direction of bottom-most
   412  	// to top-most, so that the downloads slice gets ordered correctly.
   413  	for _, d := range mfst.References() {
   414  		layerDescriptor := &v2LayerDescriptor{
   415  			digest:            d.Digest,
   416  			repo:              p.repo,
   417  			repoInfo:          p.repoInfo,
   418  			V2MetadataService: p.V2MetadataService,
   419  		}
   420  
   421  		descriptors = append(descriptors, layerDescriptor)
   422  	}
   423  
   424  	var (
   425  		configJSON         []byte       // raw serialized image config
   426  		unmarshalledConfig image.Image  // deserialized image config
   427  		downloadRootFS     image.RootFS // rootFS to use for registering layers.
   428  	)
   429  	if runtime.GOOS == "windows" {
   430  		configJSON, unmarshalledConfig, err = receiveConfig(configChan, errChan)
   431  		if err != nil {
   432  			return "", "", err
   433  		}
   434  		if unmarshalledConfig.RootFS == nil {
   435  			return "", "", errors.New("image config has no rootfs section")
   436  		}
   437  		downloadRootFS = *unmarshalledConfig.RootFS
   438  		downloadRootFS.DiffIDs = []layer.DiffID{}
   439  	} else {
   440  		downloadRootFS = *image.NewRootFS()
   441  	}
   442  
   443  	rootFS, release, err := p.config.DownloadManager.Download(ctx, downloadRootFS, descriptors, p.config.ProgressOutput)
   444  	if err != nil {
   445  		if configJSON != nil {
   446  			// Already received the config
   447  			return "", "", err
   448  		}
   449  		select {
   450  		case err = <-errChan:
   451  			return "", "", err
   452  		default:
   453  			cancel()
   454  			select {
   455  			case <-configChan:
   456  			case <-errChan:
   457  			}
   458  			return "", "", err
   459  		}
   460  	}
   461  	defer release()
   462  
   463  	if configJSON == nil {
   464  		configJSON, unmarshalledConfig, err = receiveConfig(configChan, errChan)
   465  		if err != nil {
   466  			return "", "", err
   467  		}
   468  	}
   469  
   470  	// The DiffIDs returned in rootFS MUST match those in the config.
   471  	// Otherwise the image config could be referencing layers that aren't
   472  	// included in the manifest.
   473  	if len(rootFS.DiffIDs) != len(unmarshalledConfig.RootFS.DiffIDs) {
   474  		return "", "", errRootFSMismatch
   475  	}
   476  
   477  	for i := range rootFS.DiffIDs {
   478  		if rootFS.DiffIDs[i] != unmarshalledConfig.RootFS.DiffIDs[i] {
   479  			return "", "", errRootFSMismatch
   480  		}
   481  	}
   482  
   483  	imageID, err = p.config.ImageStore.Create(configJSON)
   484  	if err != nil {
   485  		return "", "", err
   486  	}
   487  
   488  	return imageID, manifestDigest, nil
   489  }
   490  
   491  func receiveConfig(configChan <-chan []byte, errChan <-chan error) ([]byte, image.Image, error) {
   492  	select {
   493  	case configJSON := <-configChan:
   494  		var unmarshalledConfig image.Image
   495  		if err := json.Unmarshal(configJSON, &unmarshalledConfig); err != nil {
   496  			return nil, image.Image{}, err
   497  		}
   498  		return configJSON, unmarshalledConfig, nil
   499  	case err := <-errChan:
   500  		return nil, image.Image{}, err
   501  		// Don't need a case for ctx.Done in the select because cancellation
   502  		// will trigger an error in p.pullSchema2ImageConfig.
   503  	}
   504  }
   505  
   506  // pullManifestList handles "manifest lists" which point to various
   507  // platform-specifc manifests.
   508  func (p *v2Puller) pullManifestList(ctx context.Context, ref reference.Named, mfstList *manifestlist.DeserializedManifestList) (imageID image.ID, manifestListDigest digest.Digest, err error) {
   509  	manifestListDigest, err = schema2ManifestDigest(ref, mfstList)
   510  	if err != nil {
   511  		return "", "", err
   512  	}
   513  
   514  	var manifestDigest digest.Digest
   515  	for _, manifestDescriptor := range mfstList.Manifests {
   516  		// TODO(aaronl): The manifest list spec supports optional
   517  		// "features" and "variant" fields. These are not yet used.
   518  		// Once they are, their values should be interpreted here.
   519  		if manifestDescriptor.Platform.Architecture == runtime.GOARCH && manifestDescriptor.Platform.OS == runtime.GOOS {
   520  			manifestDigest = manifestDescriptor.Digest
   521  			break
   522  		}
   523  	}
   524  
   525  	if manifestDigest == "" {
   526  		return "", "", errors.New("no supported platform found in manifest list")
   527  	}
   528  
   529  	manSvc, err := p.repo.Manifests(ctx)
   530  	if err != nil {
   531  		return "", "", err
   532  	}
   533  
   534  	manifest, err := manSvc.Get(ctx, manifestDigest)
   535  	if err != nil {
   536  		return "", "", err
   537  	}
   538  
   539  	manifestRef, err := reference.WithDigest(ref, manifestDigest)
   540  	if err != nil {
   541  		return "", "", err
   542  	}
   543  
   544  	switch v := manifest.(type) {
   545  	case *schema1.SignedManifest:
   546  		imageID, _, err = p.pullSchema1(ctx, manifestRef, v)
   547  		if err != nil {
   548  			return "", "", err
   549  		}
   550  	case *schema2.DeserializedManifest:
   551  		imageID, _, err = p.pullSchema2(ctx, manifestRef, v)
   552  		if err != nil {
   553  			return "", "", err
   554  		}
   555  	default:
   556  		return "", "", errors.New("unsupported manifest format")
   557  	}
   558  
   559  	return imageID, manifestListDigest, err
   560  }
   561  
   562  func (p *v2Puller) pullSchema2ImageConfig(ctx context.Context, dgst digest.Digest) (configJSON []byte, err error) {
   563  	blobs := p.repo.Blobs(ctx)
   564  	configJSON, err = blobs.Get(ctx, dgst)
   565  	if err != nil {
   566  		return nil, err
   567  	}
   568  
   569  	// Verify image config digest
   570  	verifier, err := digest.NewDigestVerifier(dgst)
   571  	if err != nil {
   572  		return nil, err
   573  	}
   574  	if _, err := verifier.Write(configJSON); err != nil {
   575  		return nil, err
   576  	}
   577  	if !verifier.Verified() {
   578  		err := fmt.Errorf("image config verification failed for digest %s", dgst)
   579  		logrus.Error(err)
   580  		return nil, err
   581  	}
   582  
   583  	return configJSON, nil
   584  }
   585  
   586  // schema2ManifestDigest computes the manifest digest, and, if pulling by
   587  // digest, ensures that it matches the requested digest.
   588  func schema2ManifestDigest(ref reference.Named, mfst distribution.Manifest) (digest.Digest, error) {
   589  	_, canonical, err := mfst.Payload()
   590  	if err != nil {
   591  		return "", err
   592  	}
   593  
   594  	// If pull by digest, then verify the manifest digest.
   595  	if digested, isDigested := ref.(reference.Canonical); isDigested {
   596  		verifier, err := digest.NewDigestVerifier(digested.Digest())
   597  		if err != nil {
   598  			return "", err
   599  		}
   600  		if _, err := verifier.Write(canonical); err != nil {
   601  			return "", err
   602  		}
   603  		if !verifier.Verified() {
   604  			err := fmt.Errorf("manifest verification failed for digest %s", digested.Digest())
   605  			logrus.Error(err)
   606  			return "", err
   607  		}
   608  		return digested.Digest(), nil
   609  	}
   610  
   611  	return digest.FromBytes(canonical), nil
   612  }
   613  
   614  // allowV1Fallback checks if the error is a possible reason to fallback to v1
   615  // (even if confirmedV2 has been set already), and if so, wraps the error in
   616  // a fallbackError with confirmedV2 set to false. Otherwise, it returns the
   617  // error unmodified.
   618  func allowV1Fallback(err error) error {
   619  	switch v := err.(type) {
   620  	case errcode.Errors:
   621  		if len(v) != 0 {
   622  			if v0, ok := v[0].(errcode.Error); ok && registry.ShouldV2Fallback(v0) {
   623  				return fallbackError{err: err, confirmedV2: false}
   624  			}
   625  		}
   626  	case errcode.Error:
   627  		if registry.ShouldV2Fallback(v) {
   628  			return fallbackError{err: err, confirmedV2: false}
   629  		}
   630  	}
   631  
   632  	return err
   633  }
   634  
   635  func verifySchema1Manifest(signedManifest *schema1.SignedManifest, ref reference.Named) (m *schema1.Manifest, err error) {
   636  	// If pull by digest, then verify the manifest digest. NOTE: It is
   637  	// important to do this first, before any other content validation. If the
   638  	// digest cannot be verified, don't even bother with those other things.
   639  	if digested, isCanonical := ref.(reference.Canonical); isCanonical {
   640  		verifier, err := digest.NewDigestVerifier(digested.Digest())
   641  		if err != nil {
   642  			return nil, err
   643  		}
   644  		if _, err := verifier.Write(signedManifest.Canonical); err != nil {
   645  			return nil, err
   646  		}
   647  		if !verifier.Verified() {
   648  			err := fmt.Errorf("image verification failed for digest %s", digested.Digest())
   649  			logrus.Error(err)
   650  			return nil, err
   651  		}
   652  	}
   653  	m = &signedManifest.Manifest
   654  
   655  	if m.SchemaVersion != 1 {
   656  		return nil, fmt.Errorf("unsupported schema version %d for %q", m.SchemaVersion, ref.String())
   657  	}
   658  	if len(m.FSLayers) != len(m.History) {
   659  		return nil, fmt.Errorf("length of history not equal to number of layers for %q", ref.String())
   660  	}
   661  	if len(m.FSLayers) == 0 {
   662  		return nil, fmt.Errorf("no FSLayers in manifest for %q", ref.String())
   663  	}
   664  	return m, nil
   665  }
   666  
   667  // fixManifestLayers removes repeated layers from the manifest and checks the
   668  // correctness of the parent chain.
   669  func fixManifestLayers(m *schema1.Manifest) error {
   670  	imgs := make([]*image.V1Image, len(m.FSLayers))
   671  	for i := range m.FSLayers {
   672  		img := &image.V1Image{}
   673  
   674  		if err := json.Unmarshal([]byte(m.History[i].V1Compatibility), img); err != nil {
   675  			return err
   676  		}
   677  
   678  		imgs[i] = img
   679  		if err := v1.ValidateID(img.ID); err != nil {
   680  			return err
   681  		}
   682  	}
   683  
   684  	if imgs[len(imgs)-1].Parent != "" && runtime.GOOS != "windows" {
   685  		// Windows base layer can point to a base layer parent that is not in manifest.
   686  		return errors.New("Invalid parent ID in the base layer of the image.")
   687  	}
   688  
   689  	// check general duplicates to error instead of a deadlock
   690  	idmap := make(map[string]struct{})
   691  
   692  	var lastID string
   693  	for _, img := range imgs {
   694  		// skip IDs that appear after each other, we handle those later
   695  		if _, exists := idmap[img.ID]; img.ID != lastID && exists {
   696  			return fmt.Errorf("ID %+v appears multiple times in manifest", img.ID)
   697  		}
   698  		lastID = img.ID
   699  		idmap[lastID] = struct{}{}
   700  	}
   701  
   702  	// backwards loop so that we keep the remaining indexes after removing items
   703  	for i := len(imgs) - 2; i >= 0; i-- {
   704  		if imgs[i].ID == imgs[i+1].ID { // repeated ID. remove and continue
   705  			m.FSLayers = append(m.FSLayers[:i], m.FSLayers[i+1:]...)
   706  			m.History = append(m.History[:i], m.History[i+1:]...)
   707  		} else if imgs[i].Parent != imgs[i+1].ID {
   708  			return fmt.Errorf("Invalid parent ID. Expected %v, got %v.", imgs[i+1].ID, imgs[i].Parent)
   709  		}
   710  	}
   711  
   712  	return nil
   713  }