Skip to content

Commit 44dbc0d

Browse files
darstahlruncom
authored andcommitted
Block Windows images on Linux
Signed-off-by: Darren Stahl <darst@microsoft.com>
1 parent 5366019 commit 44dbc0d

3 files changed

Lines changed: 55 additions & 30 deletions

File tree

distribution/config.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -148,9 +148,12 @@ func (s *imageConfigStore) RootFSFromConfig(c []byte) (*image.RootFS, error) {
148148
return nil, err
149149
}
150150

151-
// fail immediately on windows
151+
// fail immediately on Windows when downloading a non-Windows image
152+
// and vice versa
152153
if runtime.GOOS == "windows" && unmarshalledConfig.OS == "linux" {
153154
return nil, fmt.Errorf("image operating system %q cannot be used on this platform", unmarshalledConfig.OS)
155+
} else if runtime.GOOS != "windows" && unmarshalledConfig.OS == "windows" {
156+
return nil, fmt.Errorf("image operating system %q cannot be used on this platform", unmarshalledConfig.OS)
154157
}
155158

156159
return unmarshalledConfig.RootFS, nil

distribution/pull_v2.go

Lines changed: 44 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -601,15 +601,18 @@ func (p *v2Puller) pullSchema2(ctx context.Context, ref reference.Named, mfst *s
601601
}
602602

603603
configChan := make(chan []byte, 1)
604-
errChan := make(chan error, 1)
604+
configErrChan := make(chan error, 1)
605+
layerErrChan := make(chan error, 1)
606+
downloadsDone := make(chan struct{})
605607
var cancel func()
606608
ctx, cancel = context.WithCancel(ctx)
609+
defer cancel()
607610

608611
// Pull the image config
609612
go func() {
610613
configJSON, err := p.pullSchema2Config(ctx, target.Digest)
611614
if err != nil {
612-
errChan <- ImageConfigPullError{Err: err}
615+
configErrChan <- ImageConfigPullError{Err: err}
613616
cancel()
614617
return
615618
}
@@ -620,6 +623,7 @@ func (p *v2Puller) pullSchema2(ctx context.Context, ref reference.Named, mfst *s
620623
configJSON []byte // raw serialized image config
621624
downloadedRootFS *image.RootFS // rootFS from registered layers
622625
configRootFS *image.RootFS // rootFS from configuration
626+
release func() // release resources from rootFS download
623627
)
624628

625629
// https://github.com/docker/docker/issues/24766 - Err on the side of caution,
@@ -631,7 +635,7 @@ func (p *v2Puller) pullSchema2(ctx context.Context, ref reference.Named, mfst *s
631635
// check to block Windows images being pulled on Linux is implemented, it
632636
// may be necessary to perform the same type of serialisation.
633637
if runtime.GOOS == "windows" {
634-
configJSON, configRootFS, err = receiveConfig(p.config.ImageStore, configChan, errChan)
638+
configJSON, configRootFS, err = receiveConfig(p.config.ImageStore, configChan, configErrChan)
635639
if err != nil {
636640
return "", "", err
637641
}
@@ -642,41 +646,52 @@ func (p *v2Puller) pullSchema2(ctx context.Context, ref reference.Named, mfst *s
642646
}
643647

644648
if p.config.DownloadManager != nil {
645-
downloadRootFS := *image.NewRootFS()
646-
rootFS, release, err := p.config.DownloadManager.Download(ctx, downloadRootFS, descriptors, p.config.ProgressOutput)
647-
if err != nil {
648-
if configJSON != nil {
649-
// Already received the config
650-
return "", "", err
651-
}
652-
select {
653-
case err = <-errChan:
654-
return "", "", err
655-
default:
656-
cancel()
657-
select {
658-
case <-configChan:
659-
case <-errChan:
660-
}
661-
return "", "", err
649+
go func() {
650+
var (
651+
err error
652+
rootFS image.RootFS
653+
)
654+
downloadRootFS := *image.NewRootFS()
655+
rootFS, release, err = p.config.DownloadManager.Download(ctx, downloadRootFS, descriptors, p.config.ProgressOutput)
656+
if err != nil {
657+
// Intentionally do not cancel the config download here
658+
// as the error from config download (if there is one)
659+
// is more interesting than the layer download error
660+
layerErrChan <- err
661+
return
662662
}
663-
}
664-
if release != nil {
665-
defer release()
666-
}
667663

668-
downloadedRootFS = &rootFS
664+
downloadedRootFS = &rootFS
665+
close(downloadsDone)
666+
}()
667+
} else {
668+
// We have nothing to download
669+
close(downloadsDone)
669670
}
670671

671672
if configJSON == nil {
672-
configJSON, configRootFS, err = receiveConfig(p.config.ImageStore, configChan, errChan)
673+
configJSON, configRootFS, err = receiveConfig(p.config.ImageStore, configChan, configErrChan)
674+
if err == nil && configRootFS == nil {
675+
err = errRootFSInvalid
676+
}
673677
if err != nil {
678+
cancel()
679+
select {
680+
case <-downloadsDone:
681+
case <-layerErrChan:
682+
}
674683
return "", "", err
675684
}
685+
}
676686

677-
if configRootFS == nil {
678-
return "", "", errRootFSInvalid
679-
}
687+
select {
688+
case <-downloadsDone:
689+
case err = <-layerErrChan:
690+
return "", "", err
691+
}
692+
693+
if release != nil {
694+
defer release()
680695
}
681696

682697
if downloadedRootFS != nil {

integration-cli/docker_cli_pull_test.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -524,3 +524,10 @@ func (s *DockerRegistriesSuite) TestPullNeedsAuth(c *check.C) {
524524
c.Fatalf("Wanted %s in output, got %s", repo, out)
525525
}
526526
}
527+
528+
// Regression test for https://github.com/docker/docker/issues/28892
529+
func (s *DockerSuite) TestPullWindowsImageFailsOnLinux(c *check.C) {
530+
testRequires(c, DaemonIsLinux, Network)
531+
_, _, err := dockerCmdWithError("pull", "microsoft/nanoserver")
532+
c.Assert(err.Error(), checker.Contains, "cannot be used on this platform")
533+
}

0 commit comments

Comments
 (0)