github.com/zhouyu0/docker-note@v0.0.0-20190722021225-b8d3825084db/distribution/pull_v2.go (about)

     1  package distribution // import "github.com/docker/docker/distribution"
     2  
     3  import (
     4  	"context"
     5  	"encoding/json"
     6  	"fmt"
     7  	"io"
     8  	"io/ioutil"
     9  	"net/url"
    10  	"os"
    11  	"runtime"
    12  	"strings"
    13  
    14  	"github.com/containerd/containerd/platforms"
    15  	"github.com/docker/distribution"
    16  	"github.com/docker/distribution/manifest/manifestlist"
    17  	"github.com/docker/distribution/manifest/schema1"
    18  	"github.com/docker/distribution/manifest/schema2"
    19  	"github.com/docker/distribution/reference"
    20  	"github.com/docker/distribution/registry/api/errcode"
    21  	"github.com/docker/distribution/registry/client/auth"
    22  	"github.com/docker/distribution/registry/client/transport"
    23  	"github.com/docker/docker/distribution/metadata"
    24  	"github.com/docker/docker/distribution/xfer"
    25  	"github.com/docker/docker/image"
    26  	"github.com/docker/docker/image/v1"
    27  	"github.com/docker/docker/layer"
    28  	"github.com/docker/docker/pkg/ioutils"
    29  	"github.com/docker/docker/pkg/progress"
    30  	"github.com/docker/docker/pkg/stringid"
    31  	"github.com/docker/docker/pkg/system"
    32  	refstore "github.com/docker/docker/reference"
    33  	"github.com/docker/docker/registry"
    34  	"github.com/opencontainers/go-digest"
    35  	specs "github.com/opencontainers/image-spec/specs-go/v1"
    36  	"github.com/pkg/errors"
    37  	"github.com/sirupsen/logrus"
    38  )
    39  
    40  var (
    41  	errRootFSMismatch = errors.New("layers from manifest don't match image configuration")
    42  	errRootFSInvalid  = errors.New("invalid rootfs in image configuration")
    43  )
    44  
    45  // ImageConfigPullError is an error pulling the image config blob
    46  // (only applies to schema2).
    47  type ImageConfigPullError struct {
    48  	Err error
    49  }
    50  
    51  // Error returns the error string for ImageConfigPullError.
    52  func (e ImageConfigPullError) Error() string {
    53  	return "error pulling image configuration: " + e.Err.Error()
    54  }
    55  
    56  type v2Puller struct {
    57  	V2MetadataService metadata.V2MetadataService
    58  	endpoint          registry.APIEndpoint
    59  	config            *ImagePullConfig
    60  	repoInfo          *registry.RepositoryInfo
    61  	repo              distribution.Repository
    62  	// confirmedV2 is set to true if we confirm we're talking to a v2
    63  	// registry. This is used to limit fallbacks to the v1 protocol.
    64  	confirmedV2 bool
    65  }
    66  
    67  func (p *v2Puller) Pull(ctx context.Context, ref reference.Named, platform *specs.Platform) (err error) {
    68  	// TODO(tiborvass): was ReceiveTimeout
    69  	p.repo, p.confirmedV2, err = NewV2Repository(ctx, p.repoInfo, p.endpoint, p.config.MetaHeaders, p.config.AuthConfig, "pull")
    70  	if err != nil {
    71  		logrus.Warnf("Error getting v2 registry: %v", err)
    72  		return err
    73  	}
    74  
    75  	if err = p.pullV2Repository(ctx, ref, platform); err != nil {
    76  		if _, ok := err.(fallbackError); ok {
    77  			return err
    78  		}
    79  		if continueOnError(err, p.endpoint.Mirror) {
    80  			return fallbackError{
    81  				err:         err,
    82  				confirmedV2: p.confirmedV2,
    83  				transportOK: true,
    84  			}
    85  		}
    86  	}
    87  	return err
    88  }
    89  
    90  func (p *v2Puller) pullV2Repository(ctx context.Context, ref reference.Named, platform *specs.Platform) (err error) {
    91  	var layersDownloaded bool
    92  	if !reference.IsNameOnly(ref) {
    93  		layersDownloaded, err = p.pullV2Tag(ctx, ref, platform)
    94  		if err != nil {
    95  			return err
    96  		}
    97  	} else {
    98  		tags, err := p.repo.Tags(ctx).All(ctx)
    99  		if err != nil {
   100  			// If this repository doesn't exist on V2, we should
   101  			// permit a fallback to V1.
   102  			return allowV1Fallback(err)
   103  		}
   104  
   105  		// The v2 registry knows about this repository, so we will not
   106  		// allow fallback to the v1 protocol even if we encounter an
   107  		// error later on.
   108  		p.confirmedV2 = true
   109  
   110  		for _, tag := range tags {
   111  			tagRef, err := reference.WithTag(ref, tag)
   112  			if err != nil {
   113  				return err
   114  			}
   115  			pulledNew, err := p.pullV2Tag(ctx, tagRef, platform)
   116  			if err != nil {
   117  				// Since this is the pull-all-tags case, don't
   118  				// allow an error pulling a particular tag to
   119  				// make the whole pull fall back to v1.
   120  				if fallbackErr, ok := err.(fallbackError); ok {
   121  					return fallbackErr.err
   122  				}
   123  				return err
   124  			}
   125  			// pulledNew is true if either new layers were downloaded OR if existing images were newly tagged
   126  			// TODO(tiborvass): should we change the name of `layersDownload`? What about message in WriteStatus?
   127  			layersDownloaded = layersDownloaded || pulledNew
   128  		}
   129  	}
   130  
   131  	writeStatus(reference.FamiliarString(ref), p.config.ProgressOutput, layersDownloaded)
   132  
   133  	return nil
   134  }
   135  
   136  type v2LayerDescriptor struct {
   137  	digest            digest.Digest
   138  	diffID            layer.DiffID
   139  	repoInfo          *registry.RepositoryInfo
   140  	repo              distribution.Repository
   141  	V2MetadataService metadata.V2MetadataService
   142  	tmpFile           *os.File
   143  	verifier          digest.Verifier
   144  	src               distribution.Descriptor
   145  }
   146  
   147  func (ld *v2LayerDescriptor) Key() string {
   148  	return "v2:" + ld.digest.String()
   149  }
   150  
   151  func (ld *v2LayerDescriptor) ID() string {
   152  	return stringid.TruncateID(ld.digest.String())
   153  }
   154  
   155  func (ld *v2LayerDescriptor) DiffID() (layer.DiffID, error) {
   156  	if ld.diffID != "" {
   157  		return ld.diffID, nil
   158  	}
   159  	return ld.V2MetadataService.GetDiffID(ld.digest)
   160  }
   161  
   162  func (ld *v2LayerDescriptor) Download(ctx context.Context, progressOutput progress.Output) (io.ReadCloser, int64, error) {
   163  	logrus.Debugf("pulling blob %q", ld.digest)
   164  
   165  	var (
   166  		err    error
   167  		offset int64
   168  	)
   169  
   170  	if ld.tmpFile == nil {
   171  		ld.tmpFile, err = createDownloadFile()
   172  		if err != nil {
   173  			return nil, 0, xfer.DoNotRetry{Err: err}
   174  		}
   175  	} else {
   176  		offset, err = ld.tmpFile.Seek(0, os.SEEK_END)
   177  		if err != nil {
   178  			logrus.Debugf("error seeking to end of download file: %v", err)
   179  			offset = 0
   180  
   181  			ld.tmpFile.Close()
   182  			if err := os.Remove(ld.tmpFile.Name()); err != nil {
   183  				logrus.Errorf("Failed to remove temp file: %s", ld.tmpFile.Name())
   184  			}
   185  			ld.tmpFile, err = createDownloadFile()
   186  			if err != nil {
   187  				return nil, 0, xfer.DoNotRetry{Err: err}
   188  			}
   189  		} else if offset != 0 {
   190  			logrus.Debugf("attempting to resume download of %q from %d bytes", ld.digest, offset)
   191  		}
   192  	}
   193  
   194  	tmpFile := ld.tmpFile
   195  
   196  	layerDownload, err := ld.open(ctx)
   197  	if err != nil {
   198  		logrus.Errorf("Error initiating layer download: %v", err)
   199  		return nil, 0, retryOnError(err)
   200  	}
   201  
   202  	if offset != 0 {
   203  		_, err := layerDownload.Seek(offset, os.SEEK_SET)
   204  		if err != nil {
   205  			if err := ld.truncateDownloadFile(); err != nil {
   206  				return nil, 0, xfer.DoNotRetry{Err: err}
   207  			}
   208  			return nil, 0, err
   209  		}
   210  	}
   211  	size, err := layerDownload.Seek(0, os.SEEK_END)
   212  	if err != nil {
   213  		// Seek failed, perhaps because there was no Content-Length
   214  		// header. This shouldn't fail the download, because we can
   215  		// still continue without a progress bar.
   216  		size = 0
   217  	} else {
   218  		if size != 0 && offset > size {
   219  			logrus.Debug("Partial download is larger than full blob. Starting over")
   220  			offset = 0
   221  			if err := ld.truncateDownloadFile(); err != nil {
   222  				return nil, 0, xfer.DoNotRetry{Err: err}
   223  			}
   224  		}
   225  
   226  		// Restore the seek offset either at the beginning of the
   227  		// stream, or just after the last byte we have from previous
   228  		// attempts.
   229  		_, err = layerDownload.Seek(offset, os.SEEK_SET)
   230  		if err != nil {
   231  			return nil, 0, err
   232  		}
   233  	}
   234  
   235  	reader := progress.NewProgressReader(ioutils.NewCancelReadCloser(ctx, layerDownload), progressOutput, size-offset, ld.ID(), "Downloading")
   236  	defer reader.Close()
   237  
   238  	if ld.verifier == nil {
   239  		ld.verifier = ld.digest.Verifier()
   240  	}
   241  
   242  	_, err = io.Copy(tmpFile, io.TeeReader(reader, ld.verifier))
   243  	if err != nil {
   244  		if err == transport.ErrWrongCodeForByteRange {
   245  			if err := ld.truncateDownloadFile(); err != nil {
   246  				return nil, 0, xfer.DoNotRetry{Err: err}
   247  			}
   248  			return nil, 0, err
   249  		}
   250  		return nil, 0, retryOnError(err)
   251  	}
   252  
   253  	progress.Update(progressOutput, ld.ID(), "Verifying Checksum")
   254  
   255  	if !ld.verifier.Verified() {
   256  		err = fmt.Errorf("filesystem layer verification failed for digest %s", ld.digest)
   257  		logrus.Error(err)
   258  
   259  		// Allow a retry if this digest verification error happened
   260  		// after a resumed download.
   261  		if offset != 0 {
   262  			if err := ld.truncateDownloadFile(); err != nil {
   263  				return nil, 0, xfer.DoNotRetry{Err: err}
   264  			}
   265  
   266  			return nil, 0, err
   267  		}
   268  		return nil, 0, xfer.DoNotRetry{Err: err}
   269  	}
   270  
   271  	progress.Update(progressOutput, ld.ID(), "Download complete")
   272  
   273  	logrus.Debugf("Downloaded %s to tempfile %s", ld.ID(), tmpFile.Name())
   274  
   275  	_, err = tmpFile.Seek(0, os.SEEK_SET)
   276  	if err != nil {
   277  		tmpFile.Close()
   278  		if err := os.Remove(tmpFile.Name()); err != nil {
   279  			logrus.Errorf("Failed to remove temp file: %s", tmpFile.Name())
   280  		}
   281  		ld.tmpFile = nil
   282  		ld.verifier = nil
   283  		return nil, 0, xfer.DoNotRetry{Err: err}
   284  	}
   285  
   286  	// hand off the temporary file to the download manager, so it will only
   287  	// be closed once
   288  	ld.tmpFile = nil
   289  
   290  	return ioutils.NewReadCloserWrapper(tmpFile, func() error {
   291  		tmpFile.Close()
   292  		err := os.RemoveAll(tmpFile.Name())
   293  		if err != nil {
   294  			logrus.Errorf("Failed to remove temp file: %s", tmpFile.Name())
   295  		}
   296  		return err
   297  	}), size, nil
   298  }
   299  
   300  func (ld *v2LayerDescriptor) Close() {
   301  	if ld.tmpFile != nil {
   302  		ld.tmpFile.Close()
   303  		if err := os.RemoveAll(ld.tmpFile.Name()); err != nil {
   304  			logrus.Errorf("Failed to remove temp file: %s", ld.tmpFile.Name())
   305  		}
   306  	}
   307  }
   308  
   309  func (ld *v2LayerDescriptor) truncateDownloadFile() error {
   310  	// Need a new hash context since we will be redoing the download
   311  	ld.verifier = nil
   312  
   313  	if _, err := ld.tmpFile.Seek(0, os.SEEK_SET); err != nil {
   314  		logrus.Errorf("error seeking to beginning of download file: %v", err)
   315  		return err
   316  	}
   317  
   318  	if err := ld.tmpFile.Truncate(0); err != nil {
   319  		logrus.Errorf("error truncating download file: %v", err)
   320  		return err
   321  	}
   322  
   323  	return nil
   324  }
   325  
   326  func (ld *v2LayerDescriptor) Registered(diffID layer.DiffID) {
   327  	// Cache mapping from this layer's DiffID to the blobsum
   328  	ld.V2MetadataService.Add(diffID, metadata.V2Metadata{Digest: ld.digest, SourceRepository: ld.repoInfo.Name.Name()})
   329  }
   330  
   331  func (p *v2Puller) pullV2Tag(ctx context.Context, ref reference.Named, platform *specs.Platform) (tagUpdated bool, err error) {
   332  	manSvc, err := p.repo.Manifests(ctx)
   333  	if err != nil {
   334  		return false, err
   335  	}
   336  
   337  	var (
   338  		manifest    distribution.Manifest
   339  		tagOrDigest string // Used for logging/progress only
   340  	)
   341  	if digested, isDigested := ref.(reference.Canonical); isDigested {
   342  		manifest, err = manSvc.Get(ctx, digested.Digest())
   343  		if err != nil {
   344  			return false, err
   345  		}
   346  		tagOrDigest = digested.Digest().String()
   347  	} else if tagged, isTagged := ref.(reference.NamedTagged); isTagged {
   348  		manifest, err = manSvc.Get(ctx, "", distribution.WithTag(tagged.Tag()))
   349  		if err != nil {
   350  			return false, allowV1Fallback(err)
   351  		}
   352  		tagOrDigest = tagged.Tag()
   353  	} else {
   354  		return false, fmt.Errorf("internal error: reference has neither a tag nor a digest: %s", reference.FamiliarString(ref))
   355  	}
   356  
   357  	if manifest == nil {
   358  		return false, fmt.Errorf("image manifest does not exist for tag or digest %q", tagOrDigest)
   359  	}
   360  
   361  	if m, ok := manifest.(*schema2.DeserializedManifest); ok {
   362  		var allowedMediatype bool
   363  		for _, t := range p.config.Schema2Types {
   364  			if m.Manifest.Config.MediaType == t {
   365  				allowedMediatype = true
   366  				break
   367  			}
   368  		}
   369  		if !allowedMediatype {
   370  			configClass := mediaTypeClasses[m.Manifest.Config.MediaType]
   371  			if configClass == "" {
   372  				configClass = "unknown"
   373  			}
   374  			return false, invalidManifestClassError{m.Manifest.Config.MediaType, configClass}
   375  		}
   376  	}
   377  
   378  	// If manSvc.Get succeeded, we can be confident that the registry on
   379  	// the other side speaks the v2 protocol.
   380  	p.confirmedV2 = true
   381  
   382  	logrus.Debugf("Pulling ref from V2 registry: %s", reference.FamiliarString(ref))
   383  	progress.Message(p.config.ProgressOutput, tagOrDigest, "Pulling from "+reference.FamiliarName(p.repo.Named()))
   384  
   385  	var (
   386  		id             digest.Digest
   387  		manifestDigest digest.Digest
   388  	)
   389  
   390  	switch v := manifest.(type) {
   391  	case *schema1.SignedManifest:
   392  		if p.config.RequireSchema2 {
   393  			return false, fmt.Errorf("invalid manifest: not schema2")
   394  		}
   395  		id, manifestDigest, err = p.pullSchema1(ctx, ref, v, platform)
   396  		if err != nil {
   397  			return false, err
   398  		}
   399  	case *schema2.DeserializedManifest:
   400  		id, manifestDigest, err = p.pullSchema2(ctx, ref, v, platform)
   401  		if err != nil {
   402  			return false, err
   403  		}
   404  	case *manifestlist.DeserializedManifestList:
   405  		id, manifestDigest, err = p.pullManifestList(ctx, ref, v, platform)
   406  		if err != nil {
   407  			return false, err
   408  		}
   409  	default:
   410  		return false, invalidManifestFormatError{}
   411  	}
   412  
   413  	progress.Message(p.config.ProgressOutput, "", "Digest: "+manifestDigest.String())
   414  
   415  	if p.config.ReferenceStore != nil {
   416  		oldTagID, err := p.config.ReferenceStore.Get(ref)
   417  		if err == nil {
   418  			if oldTagID == id {
   419  				return false, addDigestReference(p.config.ReferenceStore, ref, manifestDigest, id)
   420  			}
   421  		} else if err != refstore.ErrDoesNotExist {
   422  			return false, err
   423  		}
   424  
   425  		if canonical, ok := ref.(reference.Canonical); ok {
   426  			if err = p.config.ReferenceStore.AddDigest(canonical, id, true); err != nil {
   427  				return false, err
   428  			}
   429  		} else {
   430  			if err = addDigestReference(p.config.ReferenceStore, ref, manifestDigest, id); err != nil {
   431  				return false, err
   432  			}
   433  			if err = p.config.ReferenceStore.AddTag(ref, id, true); err != nil {
   434  				return false, err
   435  			}
   436  		}
   437  	}
   438  	return true, nil
   439  }
   440  
   441  func (p *v2Puller) pullSchema1(ctx context.Context, ref reference.Reference, unverifiedManifest *schema1.SignedManifest, platform *specs.Platform) (id digest.Digest, manifestDigest digest.Digest, err error) {
   442  	var verifiedManifest *schema1.Manifest
   443  	verifiedManifest, err = verifySchema1Manifest(unverifiedManifest, ref)
   444  	if err != nil {
   445  		return "", "", err
   446  	}
   447  
   448  	rootFS := image.NewRootFS()
   449  
   450  	// remove duplicate layers and check parent chain validity
   451  	err = fixManifestLayers(verifiedManifest)
   452  	if err != nil {
   453  		return "", "", err
   454  	}
   455  
   456  	var descriptors []xfer.DownloadDescriptor
   457  
   458  	// Image history converted to the new format
   459  	var history []image.History
   460  
   461  	// Note that the order of this loop is in the direction of bottom-most
   462  	// to top-most, so that the downloads slice gets ordered correctly.
   463  	for i := len(verifiedManifest.FSLayers) - 1; i >= 0; i-- {
   464  		blobSum := verifiedManifest.FSLayers[i].BlobSum
   465  
   466  		var throwAway struct {
   467  			ThrowAway bool `json:"throwaway,omitempty"`
   468  		}
   469  		if err := json.Unmarshal([]byte(verifiedManifest.History[i].V1Compatibility), &throwAway); err != nil {
   470  			return "", "", err
   471  		}
   472  
   473  		h, err := v1.HistoryFromConfig([]byte(verifiedManifest.History[i].V1Compatibility), throwAway.ThrowAway)
   474  		if err != nil {
   475  			return "", "", err
   476  		}
   477  		history = append(history, h)
   478  
   479  		if throwAway.ThrowAway {
   480  			continue
   481  		}
   482  
   483  		layerDescriptor := &v2LayerDescriptor{
   484  			digest:            blobSum,
   485  			repoInfo:          p.repoInfo,
   486  			repo:              p.repo,
   487  			V2MetadataService: p.V2MetadataService,
   488  		}
   489  
   490  		descriptors = append(descriptors, layerDescriptor)
   491  	}
   492  
   493  	// The v1 manifest itself doesn't directly contain an OS. However,
   494  	// the history does, but unfortunately that's a string, so search through
   495  	// all the history until hopefully we find one which indicates the OS.
   496  	// supertest2014/nyan is an example of a registry image with schemav1.
   497  	configOS := runtime.GOOS
   498  	if system.LCOWSupported() {
   499  		type config struct {
   500  			Os string `json:"os,omitempty"`
   501  		}
   502  		for _, v := range verifiedManifest.History {
   503  			var c config
   504  			if err := json.Unmarshal([]byte(v.V1Compatibility), &c); err == nil {
   505  				if c.Os != "" {
   506  					configOS = c.Os
   507  					break
   508  				}
   509  			}
   510  		}
   511  	}
   512  
   513  	// In the situation that the API call didn't specify an OS explicitly, but
   514  	// we support the operating system, switch to that operating system.
   515  	// eg FROM supertest2014/nyan with no platform specifier, and docker build
   516  	// with no --platform= flag under LCOW.
   517  	requestedOS := ""
   518  	if platform != nil {
   519  		requestedOS = platform.OS
   520  	} else if system.IsOSSupported(configOS) {
   521  		requestedOS = configOS
   522  	}
   523  
   524  	// Early bath if the requested OS doesn't match that of the configuration.
   525  	// This avoids doing the download, only to potentially fail later.
   526  	if !strings.EqualFold(configOS, requestedOS) {
   527  		return "", "", fmt.Errorf("cannot download image with operating system %q when requesting %q", configOS, requestedOS)
   528  	}
   529  
   530  	resultRootFS, release, err := p.config.DownloadManager.Download(ctx, *rootFS, configOS, descriptors, p.config.ProgressOutput)
   531  	if err != nil {
   532  		return "", "", err
   533  	}
   534  	defer release()
   535  
   536  	config, err := v1.MakeConfigFromV1Config([]byte(verifiedManifest.History[0].V1Compatibility), &resultRootFS, history)
   537  	if err != nil {
   538  		return "", "", err
   539  	}
   540  
   541  	imageID, err := p.config.ImageStore.Put(config)
   542  	if err != nil {
   543  		return "", "", err
   544  	}
   545  
   546  	manifestDigest = digest.FromBytes(unverifiedManifest.Canonical)
   547  
   548  	return imageID, manifestDigest, nil
   549  }
   550  
   551  func (p *v2Puller) pullSchema2(ctx context.Context, ref reference.Named, mfst *schema2.DeserializedManifest, platform *specs.Platform) (id digest.Digest, manifestDigest digest.Digest, err error) {
   552  	manifestDigest, err = schema2ManifestDigest(ref, mfst)
   553  	if err != nil {
   554  		return "", "", err
   555  	}
   556  
   557  	target := mfst.Target()
   558  	if _, err := p.config.ImageStore.Get(target.Digest); err == nil {
   559  		// If the image already exists locally, no need to pull
   560  		// anything.
   561  		return target.Digest, manifestDigest, nil
   562  	}
   563  
   564  	var descriptors []xfer.DownloadDescriptor
   565  
   566  	// Note that the order of this loop is in the direction of bottom-most
   567  	// to top-most, so that the downloads slice gets ordered correctly.
   568  	for _, d := range mfst.Layers {
   569  		layerDescriptor := &v2LayerDescriptor{
   570  			digest:            d.Digest,
   571  			repo:              p.repo,
   572  			repoInfo:          p.repoInfo,
   573  			V2MetadataService: p.V2MetadataService,
   574  			src:               d,
   575  		}
   576  
   577  		descriptors = append(descriptors, layerDescriptor)
   578  	}
   579  
   580  	configChan := make(chan []byte, 1)
   581  	configErrChan := make(chan error, 1)
   582  	layerErrChan := make(chan error, 1)
   583  	downloadsDone := make(chan struct{})
   584  	var cancel func()
   585  	ctx, cancel = context.WithCancel(ctx)
   586  	defer cancel()
   587  
   588  	// Pull the image config
   589  	go func() {
   590  		configJSON, err := p.pullSchema2Config(ctx, target.Digest)
   591  		if err != nil {
   592  			configErrChan <- ImageConfigPullError{Err: err}
   593  			cancel()
   594  			return
   595  		}
   596  		configChan <- configJSON
   597  	}()
   598  
   599  	var (
   600  		configJSON       []byte          // raw serialized image config
   601  		downloadedRootFS *image.RootFS   // rootFS from registered layers
   602  		configRootFS     *image.RootFS   // rootFS from configuration
   603  		release          func()          // release resources from rootFS download
   604  		configPlatform   *specs.Platform // for LCOW when registering downloaded layers
   605  	)
   606  
   607  	layerStoreOS := runtime.GOOS
   608  	if platform != nil {
   609  		layerStoreOS = platform.OS
   610  	}
   611  
   612  	// https://github.com/docker/docker/issues/24766 - Err on the side of caution,
   613  	// explicitly blocking images intended for linux from the Windows daemon. On
   614  	// Windows, we do this before the attempt to download, effectively serialising
   615  	// the download slightly slowing it down. We have to do it this way, as
   616  	// chances are the download of layers itself would fail due to file names
   617  	// which aren't suitable for NTFS. At some point in the future, if a similar
   618  	// check to block Windows images being pulled on Linux is implemented, it
   619  	// may be necessary to perform the same type of serialisation.
   620  	if runtime.GOOS == "windows" {
   621  		configJSON, configRootFS, configPlatform, err = receiveConfig(p.config.ImageStore, configChan, configErrChan)
   622  		if err != nil {
   623  			return "", "", err
   624  		}
   625  		if configRootFS == nil {
   626  			return "", "", errRootFSInvalid
   627  		}
   628  		if err := checkImageCompatibility(configPlatform.OS, configPlatform.OSVersion); err != nil {
   629  			return "", "", err
   630  		}
   631  
   632  		if len(descriptors) != len(configRootFS.DiffIDs) {
   633  			return "", "", errRootFSMismatch
   634  		}
   635  		if platform == nil {
   636  			// Early bath if the requested OS doesn't match that of the configuration.
   637  			// This avoids doing the download, only to potentially fail later.
   638  			if !system.IsOSSupported(configPlatform.OS) {
   639  				return "", "", fmt.Errorf("cannot download image with operating system %q when requesting %q", configPlatform.OS, layerStoreOS)
   640  			}
   641  			layerStoreOS = configPlatform.OS
   642  		}
   643  
   644  		// Populate diff ids in descriptors to avoid downloading foreign layers
   645  		// which have been side loaded
   646  		for i := range descriptors {
   647  			descriptors[i].(*v2LayerDescriptor).diffID = configRootFS.DiffIDs[i]
   648  		}
   649  	}
   650  
   651  	if p.config.DownloadManager != nil {
   652  		go func() {
   653  			var (
   654  				err    error
   655  				rootFS image.RootFS
   656  			)
   657  			downloadRootFS := *image.NewRootFS()
   658  			rootFS, release, err = p.config.DownloadManager.Download(ctx, downloadRootFS, layerStoreOS, descriptors, p.config.ProgressOutput)
   659  			if err != nil {
   660  				// Intentionally do not cancel the config download here
   661  				// as the error from config download (if there is one)
   662  				// is more interesting than the layer download error
   663  				layerErrChan <- err
   664  				return
   665  			}
   666  
   667  			downloadedRootFS = &rootFS
   668  			close(downloadsDone)
   669  		}()
   670  	} else {
   671  		// We have nothing to download
   672  		close(downloadsDone)
   673  	}
   674  
   675  	if configJSON == nil {
   676  		configJSON, configRootFS, _, err = receiveConfig(p.config.ImageStore, configChan, configErrChan)
   677  		if err == nil && configRootFS == nil {
   678  			err = errRootFSInvalid
   679  		}
   680  		if err != nil {
   681  			cancel()
   682  			select {
   683  			case <-downloadsDone:
   684  			case <-layerErrChan:
   685  			}
   686  			return "", "", err
   687  		}
   688  	}
   689  
   690  	select {
   691  	case <-downloadsDone:
   692  	case err = <-layerErrChan:
   693  		return "", "", err
   694  	}
   695  
   696  	if release != nil {
   697  		defer release()
   698  	}
   699  
   700  	if downloadedRootFS != nil {
   701  		// The DiffIDs returned in rootFS MUST match those in the config.
   702  		// Otherwise the image config could be referencing layers that aren't
   703  		// included in the manifest.
   704  		if len(downloadedRootFS.DiffIDs) != len(configRootFS.DiffIDs) {
   705  			return "", "", errRootFSMismatch
   706  		}
   707  
   708  		for i := range downloadedRootFS.DiffIDs {
   709  			if downloadedRootFS.DiffIDs[i] != configRootFS.DiffIDs[i] {
   710  				return "", "", errRootFSMismatch
   711  			}
   712  		}
   713  	}
   714  
   715  	imageID, err := p.config.ImageStore.Put(configJSON)
   716  	if err != nil {
   717  		return "", "", err
   718  	}
   719  
   720  	return imageID, manifestDigest, nil
   721  }
   722  
   723  func receiveConfig(s ImageConfigStore, configChan <-chan []byte, errChan <-chan error) ([]byte, *image.RootFS, *specs.Platform, error) {
   724  	select {
   725  	case configJSON := <-configChan:
   726  		rootfs, err := s.RootFSFromConfig(configJSON)
   727  		if err != nil {
   728  			return nil, nil, nil, err
   729  		}
   730  		platform, err := s.PlatformFromConfig(configJSON)
   731  		if err != nil {
   732  			return nil, nil, nil, err
   733  		}
   734  		return configJSON, rootfs, platform, nil
   735  	case err := <-errChan:
   736  		return nil, nil, nil, err
   737  		// Don't need a case for ctx.Done in the select because cancellation
   738  		// will trigger an error in p.pullSchema2ImageConfig.
   739  	}
   740  }
   741  
   742  // pullManifestList handles "manifest lists" which point to various
   743  // platform-specific manifests.
   744  func (p *v2Puller) pullManifestList(ctx context.Context, ref reference.Named, mfstList *manifestlist.DeserializedManifestList, pp *specs.Platform) (id digest.Digest, manifestListDigest digest.Digest, err error) {
   745  	manifestListDigest, err = schema2ManifestDigest(ref, mfstList)
   746  	if err != nil {
   747  		return "", "", err
   748  	}
   749  
   750  	var platform specs.Platform
   751  	if pp != nil {
   752  		platform = *pp
   753  	}
   754  	logrus.Debugf("%s resolved to a manifestList object with %d entries; looking for a %s/%s match", ref, len(mfstList.Manifests), platforms.Format(platform), runtime.GOARCH)
   755  
   756  	manifestMatches := filterManifests(mfstList.Manifests, platform)
   757  
   758  	if len(manifestMatches) == 0 {
   759  		errMsg := fmt.Sprintf("no matching manifest for %s in the manifest list entries", formatPlatform(platform))
   760  		logrus.Debugf(errMsg)
   761  		return "", "", errors.New(errMsg)
   762  	}
   763  
   764  	if len(manifestMatches) > 1 {
   765  		logrus.Debugf("found multiple matches in manifest list, choosing best match %s", manifestMatches[0].Digest.String())
   766  	}
   767  	manifestDigest := manifestMatches[0].Digest
   768  
   769  	if err := checkImageCompatibility(manifestMatches[0].Platform.OS, manifestMatches[0].Platform.OSVersion); err != nil {
   770  		return "", "", err
   771  	}
   772  
   773  	manSvc, err := p.repo.Manifests(ctx)
   774  	if err != nil {
   775  		return "", "", err
   776  	}
   777  
   778  	manifest, err := manSvc.Get(ctx, manifestDigest)
   779  	if err != nil {
   780  		return "", "", err
   781  	}
   782  
   783  	manifestRef, err := reference.WithDigest(reference.TrimNamed(ref), manifestDigest)
   784  	if err != nil {
   785  		return "", "", err
   786  	}
   787  
   788  	switch v := manifest.(type) {
   789  	case *schema1.SignedManifest:
   790  		platform := toOCIPlatform(manifestMatches[0].Platform)
   791  		id, _, err = p.pullSchema1(ctx, manifestRef, v, &platform)
   792  		if err != nil {
   793  			return "", "", err
   794  		}
   795  	case *schema2.DeserializedManifest:
   796  		platform := toOCIPlatform(manifestMatches[0].Platform)
   797  		id, _, err = p.pullSchema2(ctx, manifestRef, v, &platform)
   798  		if err != nil {
   799  			return "", "", err
   800  		}
   801  	default:
   802  		return "", "", errors.New("unsupported manifest format")
   803  	}
   804  
   805  	return id, manifestListDigest, err
   806  }
   807  
   808  func (p *v2Puller) pullSchema2Config(ctx context.Context, dgst digest.Digest) (configJSON []byte, err error) {
   809  	blobs := p.repo.Blobs(ctx)
   810  	configJSON, err = blobs.Get(ctx, dgst)
   811  	if err != nil {
   812  		return nil, err
   813  	}
   814  
   815  	// Verify image config digest
   816  	verifier := dgst.Verifier()
   817  	if _, err := verifier.Write(configJSON); err != nil {
   818  		return nil, err
   819  	}
   820  	if !verifier.Verified() {
   821  		err := fmt.Errorf("image config verification failed for digest %s", dgst)
   822  		logrus.Error(err)
   823  		return nil, err
   824  	}
   825  
   826  	return configJSON, nil
   827  }
   828  
   829  // schema2ManifestDigest computes the manifest digest, and, if pulling by
   830  // digest, ensures that it matches the requested digest.
   831  func schema2ManifestDigest(ref reference.Named, mfst distribution.Manifest) (digest.Digest, error) {
   832  	_, canonical, err := mfst.Payload()
   833  	if err != nil {
   834  		return "", err
   835  	}
   836  
   837  	// If pull by digest, then verify the manifest digest.
   838  	if digested, isDigested := ref.(reference.Canonical); isDigested {
   839  		verifier := digested.Digest().Verifier()
   840  		if _, err := verifier.Write(canonical); err != nil {
   841  			return "", err
   842  		}
   843  		if !verifier.Verified() {
   844  			err := fmt.Errorf("manifest verification failed for digest %s", digested.Digest())
   845  			logrus.Error(err)
   846  			return "", err
   847  		}
   848  		return digested.Digest(), nil
   849  	}
   850  
   851  	return digest.FromBytes(canonical), nil
   852  }
   853  
   854  // allowV1Fallback checks if the error is a possible reason to fallback to v1
   855  // (even if confirmedV2 has been set already), and if so, wraps the error in
   856  // a fallbackError with confirmedV2 set to false. Otherwise, it returns the
   857  // error unmodified.
   858  func allowV1Fallback(err error) error {
   859  	switch v := err.(type) {
   860  	case errcode.Errors:
   861  		if len(v) != 0 {
   862  			if v0, ok := v[0].(errcode.Error); ok && shouldV2Fallback(v0) {
   863  				return fallbackError{
   864  					err:         err,
   865  					confirmedV2: false,
   866  					transportOK: true,
   867  				}
   868  			}
   869  		}
   870  	case errcode.Error:
   871  		if shouldV2Fallback(v) {
   872  			return fallbackError{
   873  				err:         err,
   874  				confirmedV2: false,
   875  				transportOK: true,
   876  			}
   877  		}
   878  	case *url.Error:
   879  		if v.Err == auth.ErrNoBasicAuthCredentials {
   880  			return fallbackError{err: err, confirmedV2: false}
   881  		}
   882  	}
   883  
   884  	return err
   885  }
   886  
   887  func verifySchema1Manifest(signedManifest *schema1.SignedManifest, ref reference.Reference) (m *schema1.Manifest, err error) {
   888  	// If pull by digest, then verify the manifest digest. NOTE: It is
   889  	// important to do this first, before any other content validation. If the
   890  	// digest cannot be verified, don't even bother with those other things.
   891  	if digested, isCanonical := ref.(reference.Canonical); isCanonical {
   892  		verifier := digested.Digest().Verifier()
   893  		if _, err := verifier.Write(signedManifest.Canonical); err != nil {
   894  			return nil, err
   895  		}
   896  		if !verifier.Verified() {
   897  			err := fmt.Errorf("image verification failed for digest %s", digested.Digest())
   898  			logrus.Error(err)
   899  			return nil, err
   900  		}
   901  	}
   902  	m = &signedManifest.Manifest
   903  
   904  	if m.SchemaVersion != 1 {
   905  		return nil, fmt.Errorf("unsupported schema version %d for %q", m.SchemaVersion, reference.FamiliarString(ref))
   906  	}
   907  	if len(m.FSLayers) != len(m.History) {
   908  		return nil, fmt.Errorf("length of history not equal to number of layers for %q", reference.FamiliarString(ref))
   909  	}
   910  	if len(m.FSLayers) == 0 {
   911  		return nil, fmt.Errorf("no FSLayers in manifest for %q", reference.FamiliarString(ref))
   912  	}
   913  	return m, nil
   914  }
   915  
   916  // fixManifestLayers removes repeated layers from the manifest and checks the
   917  // correctness of the parent chain.
   918  func fixManifestLayers(m *schema1.Manifest) error {
   919  	imgs := make([]*image.V1Image, len(m.FSLayers))
   920  	for i := range m.FSLayers {
   921  		img := &image.V1Image{}
   922  
   923  		if err := json.Unmarshal([]byte(m.History[i].V1Compatibility), img); err != nil {
   924  			return err
   925  		}
   926  
   927  		imgs[i] = img
   928  		if err := v1.ValidateID(img.ID); err != nil {
   929  			return err
   930  		}
   931  	}
   932  
   933  	if imgs[len(imgs)-1].Parent != "" && runtime.GOOS != "windows" {
   934  		// Windows base layer can point to a base layer parent that is not in manifest.
   935  		return errors.New("invalid parent ID in the base layer of the image")
   936  	}
   937  
   938  	// check general duplicates to error instead of a deadlock
   939  	idmap := make(map[string]struct{})
   940  
   941  	var lastID string
   942  	for _, img := range imgs {
   943  		// skip IDs that appear after each other, we handle those later
   944  		if _, exists := idmap[img.ID]; img.ID != lastID && exists {
   945  			return fmt.Errorf("ID %+v appears multiple times in manifest", img.ID)
   946  		}
   947  		lastID = img.ID
   948  		idmap[lastID] = struct{}{}
   949  	}
   950  
   951  	// backwards loop so that we keep the remaining indexes after removing items
   952  	for i := len(imgs) - 2; i >= 0; i-- {
   953  		if imgs[i].ID == imgs[i+1].ID { // repeated ID. remove and continue
   954  			m.FSLayers = append(m.FSLayers[:i], m.FSLayers[i+1:]...)
   955  			m.History = append(m.History[:i], m.History[i+1:]...)
   956  		} else if imgs[i].Parent != imgs[i+1].ID {
   957  			return fmt.Errorf("invalid parent ID. Expected %v, got %v", imgs[i+1].ID, imgs[i].Parent)
   958  		}
   959  	}
   960  
   961  	return nil
   962  }
   963  
   964  func createDownloadFile() (*os.File, error) {
   965  	return ioutil.TempFile("", "GetImageBlob")
   966  }
   967  
   968  func toOCIPlatform(p manifestlist.PlatformSpec) specs.Platform {
   969  	return specs.Platform{
   970  		OS:           p.OS,
   971  		Architecture: p.Architecture,
   972  		Variant:      p.Variant,
   973  		OSFeatures:   p.OSFeatures,
   974  		OSVersion:    p.OSVersion,
   975  	}
   976  }