Browse code

builder: fix layer lifecycle leak

Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>

Tonis Tiigi authored on 2018/02/17 06:50:57
Showing 8 changed files
... ...
@@ -53,7 +53,7 @@ type Backend interface {
53 53
 
54 54
 // ImageBackend are the interface methods required from an image component
55 55
 type ImageBackend interface {
56
-	GetImageAndReleasableLayer(ctx context.Context, refOrID string, opts backend.GetImageAndLayerOptions) (Image, ReleaseableLayer, error)
56
+	GetImageAndReleasableLayer(ctx context.Context, refOrID string, opts backend.GetImageAndLayerOptions) (Image, ROLayer, error)
57 57
 }
58 58
 
59 59
 // ExecBackend contains the interface methods required for executing containers
... ...
@@ -100,10 +100,16 @@ type Image interface {
100 100
 	OperatingSystem() string
101 101
 }
102 102
 
103
-// ReleaseableLayer is an image layer that can be mounted and released
104
-type ReleaseableLayer interface {
103
+// ROLayer is a reference to image rootfs layer
104
+type ROLayer interface {
105 105
 	Release() error
106
-	Mount() (containerfs.ContainerFS, error)
107
-	Commit() (ReleaseableLayer, error)
106
+	NewRWLayer() (RWLayer, error)
108 107
 	DiffID() layer.DiffID
109 108
 }
109
+
110
+// RWLayer is active layer that can be read/modified
111
+type RWLayer interface {
112
+	Release() error
113
+	Root() containerfs.ContainerFS
114
+	Commit() (ROLayer, error)
115
+}
... ...
@@ -72,8 +72,12 @@ type copier struct {
72 72
 	source      builder.Source
73 73
 	pathCache   pathCache
74 74
 	download    sourceDownloader
75
-	tmpPaths    []string
76 75
 	platform    string
76
+	// for cleanup. TODO: having copier.cleanup() is error prone and hard to
77
+	// follow. Code calling performCopy should manage the lifecycle of its params.
78
+	// Copier should take override source as input, not imageMount.
79
+	activeLayer builder.RWLayer
80
+	tmpPaths    []string
77 81
 }
78 82
 
79 83
 func copierFromDispatchRequest(req dispatchRequest, download sourceDownloader, imageSource *imageMount) copier {
... ...
@@ -155,6 +159,10 @@ func (o *copier) Cleanup() {
155 155
 		os.RemoveAll(path)
156 156
 	}
157 157
 	o.tmpPaths = []string{}
158
+	if o.activeLayer != nil {
159
+		o.activeLayer.Release()
160
+		o.activeLayer = nil
161
+	}
158 162
 }
159 163
 
160 164
 // TODO: allowWildcards can probably be removed by refactoring this function further.
... ...
@@ -166,9 +174,15 @@ func (o *copier) calcCopyInfo(origPath string, allowWildcards bool) ([]copyInfo,
166 166
 	// done on image Source?
167 167
 	if imageSource != nil {
168 168
 		var err error
169
-		o.source, err = imageSource.Source()
169
+		rwLayer, err := imageSource.NewRWLayer()
170
+		if err != nil {
171
+			return nil, err
172
+		}
173
+		o.activeLayer = rwLayer
174
+
175
+		o.source, err = remotecontext.NewLazySource(rwLayer.Root())
170 176
 		if err != nil {
171
-			return nil, errors.Wrapf(err, "failed to copy from %s", imageSource.ImageID())
177
+			return nil, errors.Wrapf(err, "failed to create context for copy from %s", rwLayer.Root().Path())
172 178
 		}
173 179
 	}
174 180
 
... ...
@@ -127,7 +127,7 @@ func TestFromScratch(t *testing.T) {
127 127
 func TestFromWithArg(t *testing.T) {
128 128
 	tag, expected := ":sometag", "expectedthisid"
129 129
 
130
-	getImage := func(name string) (builder.Image, builder.ReleaseableLayer, error) {
130
+	getImage := func(name string) (builder.Image, builder.ROLayer, error) {
131 131
 		assert.Equal(t, "alpine"+tag, name)
132 132
 		return &mockImage{id: "expectedthisid"}, nil, nil
133 133
 	}
... ...
@@ -159,7 +159,7 @@ func TestFromWithArg(t *testing.T) {
159 159
 func TestFromWithUndefinedArg(t *testing.T) {
160 160
 	tag, expected := "sometag", "expectedthisid"
161 161
 
162
-	getImage := func(name string) (builder.Image, builder.ReleaseableLayer, error) {
162
+	getImage := func(name string) (builder.Image, builder.ROLayer, error) {
163 163
 		assert.Equal(t, "alpine", name)
164 164
 		return &mockImage{id: "expectedthisid"}, nil, nil
165 165
 	}
... ...
@@ -433,7 +433,7 @@ func TestRunWithBuildArgs(t *testing.T) {
433 433
 		return imageCache
434 434
 	}
435 435
 	b.imageProber = newImageProber(mockBackend, nil, false)
436
-	mockBackend.getImageFunc = func(_ string) (builder.Image, builder.ReleaseableLayer, error) {
436
+	mockBackend.getImageFunc = func(_ string) (builder.Image, builder.ROLayer, error) {
437 437
 		return &mockImage{
438 438
 			id:     "abcdef",
439 439
 			config: &container.Config{Cmd: origCmd},
... ...
@@ -5,7 +5,6 @@ import (
5 5
 
6 6
 	"github.com/docker/docker/api/types/backend"
7 7
 	"github.com/docker/docker/builder"
8
-	"github.com/docker/docker/builder/remotecontext"
9 8
 	dockerimage "github.com/docker/docker/image"
10 9
 	"github.com/docker/docker/pkg/system"
11 10
 	"github.com/pkg/errors"
... ...
@@ -13,7 +12,7 @@ import (
13 13
 	"golang.org/x/net/context"
14 14
 )
15 15
 
16
-type getAndMountFunc func(string, bool) (builder.Image, builder.ReleaseableLayer, error)
16
+type getAndMountFunc func(string, bool) (builder.Image, builder.ROLayer, error)
17 17
 
18 18
 // imageSources mounts images and provides a cache for mounted images. It tracks
19 19
 // all images so they can be unmounted at the end of the build.
... ...
@@ -24,7 +23,7 @@ type imageSources struct {
24 24
 }
25 25
 
26 26
 func newImageSources(ctx context.Context, options builderOptions) *imageSources {
27
-	getAndMount := func(idOrRef string, localOnly bool) (builder.Image, builder.ReleaseableLayer, error) {
27
+	getAndMount := func(idOrRef string, localOnly bool) (builder.Image, builder.ROLayer, error) {
28 28
 		pullOption := backend.PullOptionNoPull
29 29
 		if !localOnly {
30 30
 			if options.Options.PullParent {
... ...
@@ -92,32 +91,14 @@ func (m *imageSources) Add(im *imageMount) {
92 92
 type imageMount struct {
93 93
 	image  builder.Image
94 94
 	source builder.Source
95
-	layer  builder.ReleaseableLayer
95
+	layer  builder.ROLayer
96 96
 }
97 97
 
98
-func newImageMount(image builder.Image, layer builder.ReleaseableLayer) *imageMount {
98
+func newImageMount(image builder.Image, layer builder.ROLayer) *imageMount {
99 99
 	im := &imageMount{image: image, layer: layer}
100 100
 	return im
101 101
 }
102 102
 
103
-func (im *imageMount) Source() (builder.Source, error) {
104
-	if im.source == nil {
105
-		if im.layer == nil {
106
-			return nil, errors.Errorf("empty context")
107
-		}
108
-		mountPath, err := im.layer.Mount()
109
-		if err != nil {
110
-			return nil, errors.Wrapf(err, "failed to mount %s", im.image.ImageID())
111
-		}
112
-		source, err := remotecontext.NewLazySource(mountPath)
113
-		if err != nil {
114
-			return nil, errors.Wrapf(err, "failed to create lazycontext for %s", mountPath)
115
-		}
116
-		im.source = source
117
-	}
118
-	return im.source, nil
119
-}
120
-
121 103
 func (im *imageMount) unmount() error {
122 104
 	if im.layer == nil {
123 105
 		return nil
... ...
@@ -133,8 +114,8 @@ func (im *imageMount) Image() builder.Image {
133 133
 	return im.image
134 134
 }
135 135
 
136
-func (im *imageMount) Layer() builder.ReleaseableLayer {
137
-	return im.layer
136
+func (im *imageMount) NewRWLayer() (builder.RWLayer, error) {
137
+	return im.layer.NewRWLayer()
138 138
 }
139 139
 
140 140
 func (im *imageMount) ImageID() string {
... ...
@@ -17,6 +17,7 @@ import (
17 17
 	"github.com/docker/docker/api/types"
18 18
 	"github.com/docker/docker/api/types/backend"
19 19
 	"github.com/docker/docker/api/types/container"
20
+	"github.com/docker/docker/builder"
20 21
 	"github.com/docker/docker/image"
21 22
 	"github.com/docker/docker/pkg/archive"
22 23
 	"github.com/docker/docker/pkg/chrootarchive"
... ...
@@ -114,8 +115,8 @@ func (b *Builder) commitContainer(dispatchState *dispatchState, id string, conta
114 114
 	return err
115 115
 }
116 116
 
117
-func (b *Builder) exportImage(state *dispatchState, imageMount *imageMount, runConfig *container.Config) error {
118
-	newLayer, err := imageMount.Layer().Commit()
117
+func (b *Builder) exportImage(state *dispatchState, layer builder.RWLayer, parent builder.Image, runConfig *container.Config) error {
118
+	newLayer, err := layer.Commit()
119 119
 	if err != nil {
120 120
 		return err
121 121
 	}
... ...
@@ -124,7 +125,7 @@ func (b *Builder) exportImage(state *dispatchState, imageMount *imageMount, runC
124 124
 	// if there is an error before we can add the full mount with image
125 125
 	b.imageSources.Add(newImageMount(nil, newLayer))
126 126
 
127
-	parentImage, ok := imageMount.Image().(*image.Image)
127
+	parentImage, ok := parent.(*image.Image)
128 128
 	if !ok {
129 129
 		return errors.Errorf("unexpected image type")
130 130
 	}
... ...
@@ -177,7 +178,13 @@ func (b *Builder) performCopy(state *dispatchState, inst copyInstruction) error
177 177
 		return errors.Wrapf(err, "failed to get destination image %q", state.imageID)
178 178
 	}
179 179
 
180
-	destInfo, err := createDestInfo(state.runConfig.WorkingDir, inst, imageMount, b.options.Platform)
180
+	rwLayer, err := imageMount.NewRWLayer()
181
+	if err != nil {
182
+		return err
183
+	}
184
+	defer rwLayer.Release()
185
+
186
+	destInfo, err := createDestInfo(state.runConfig.WorkingDir, inst, rwLayer, b.options.Platform)
181 187
 	if err != nil {
182 188
 		return err
183 189
 	}
... ...
@@ -203,10 +210,10 @@ func (b *Builder) performCopy(state *dispatchState, inst copyInstruction) error
203 203
 			return errors.Wrapf(err, "failed to copy files")
204 204
 		}
205 205
 	}
206
-	return b.exportImage(state, imageMount, runConfigWithCommentCmd)
206
+	return b.exportImage(state, rwLayer, imageMount.Image(), runConfigWithCommentCmd)
207 207
 }
208 208
 
209
-func createDestInfo(workingDir string, inst copyInstruction, imageMount *imageMount, platform string) (copyInfo, error) {
209
+func createDestInfo(workingDir string, inst copyInstruction, rwLayer builder.RWLayer, platform string) (copyInfo, error) {
210 210
 	// Twiddle the destination when it's a relative path - meaning, make it
211 211
 	// relative to the WORKINGDIR
212 212
 	dest, err := normalizeDest(workingDir, inst.dest, platform)
... ...
@@ -214,12 +221,7 @@ func createDestInfo(workingDir string, inst copyInstruction, imageMount *imageMo
214 214
 		return copyInfo{}, errors.Wrapf(err, "invalid %s", inst.cmdName)
215 215
 	}
216 216
 
217
-	destMount, err := imageMount.Source()
218
-	if err != nil {
219
-		return copyInfo{}, errors.Wrapf(err, "failed to mount copy source")
220
-	}
221
-
222
-	return newCopyInfoFromSource(destMount, dest, ""), nil
217
+	return copyInfo{root: rwLayer.Root(), path: dest}, nil
223 218
 }
224 219
 
225 220
 // normalizeDest normalises the destination of a COPY/ADD command in a
... ...
@@ -20,7 +20,7 @@ import (
20 20
 type MockBackend struct {
21 21
 	containerCreateFunc func(config types.ContainerCreateConfig) (container.ContainerCreateCreatedBody, error)
22 22
 	commitFunc          func(backend.CommitConfig) (image.ID, error)
23
-	getImageFunc        func(string) (builder.Image, builder.ReleaseableLayer, error)
23
+	getImageFunc        func(string) (builder.Image, builder.ROLayer, error)
24 24
 	makeImageCacheFunc  func(cacheFrom []string) builder.ImageCache
25 25
 }
26 26
 
... ...
@@ -66,7 +66,7 @@ func (m *MockBackend) CopyOnBuild(containerID string, destPath string, srcRoot s
66 66
 	return nil
67 67
 }
68 68
 
69
-func (m *MockBackend) GetImageAndReleasableLayer(ctx context.Context, refOrID string, opts backend.GetImageAndLayerOptions) (builder.Image, builder.ReleaseableLayer, error) {
69
+func (m *MockBackend) GetImageAndReleasableLayer(ctx context.Context, refOrID string, opts backend.GetImageAndLayerOptions) (builder.Image, builder.ROLayer, error) {
70 70
 	if m.getImageFunc != nil {
71 71
 		return m.getImageFunc(refOrID)
72 72
 	}
... ...
@@ -124,14 +124,25 @@ func (l *mockLayer) Release() error {
124 124
 	return nil
125 125
 }
126 126
 
127
-func (l *mockLayer) Mount() (containerfs.ContainerFS, error) {
128
-	return containerfs.NewLocalContainerFS("mountPath"), nil
127
+func (l *mockLayer) NewRWLayer() (builder.RWLayer, error) {
128
+	return &mockRWLayer{}, nil
129 129
 }
130 130
 
131
-func (l *mockLayer) Commit() (builder.ReleaseableLayer, error) {
131
+func (l *mockLayer) DiffID() layer.DiffID {
132
+	return layer.DiffID("abcdef")
133
+}
134
+
135
+type mockRWLayer struct {
136
+}
137
+
138
+func (l *mockRWLayer) Release() error {
139
+	return nil
140
+}
141
+
142
+func (l *mockRWLayer) Commit() (builder.ROLayer, error) {
132 143
 	return nil, nil
133 144
 }
134 145
 
135
-func (l *mockLayer) DiffID() layer.DiffID {
136
-	return layer.DiffID("abcdef")
146
+func (l *mockRWLayer) Root() containerfs.ContainerFS {
147
+	return nil
137 148
 }
... ...
@@ -15,130 +15,126 @@ import (
15 15
 	"github.com/docker/docker/pkg/system"
16 16
 	"github.com/docker/docker/registry"
17 17
 	"github.com/pkg/errors"
18
-	"github.com/sirupsen/logrus"
19 18
 	"golang.org/x/net/context"
20 19
 )
21 20
 
22
-type releaseableLayer struct {
21
+type roLayer struct {
23 22
 	released   bool
24 23
 	layerStore layer.Store
25 24
 	roLayer    layer.Layer
26
-	rwLayer    layer.RWLayer
27 25
 }
28 26
 
29
-func (rl *releaseableLayer) Mount() (containerfs.ContainerFS, error) {
30
-	var err error
31
-	var mountPath containerfs.ContainerFS
32
-	var chainID layer.ChainID
33
-	if rl.roLayer != nil {
34
-		chainID = rl.roLayer.ChainID()
27
+func (l *roLayer) DiffID() layer.DiffID {
28
+	if l.roLayer == nil {
29
+		return layer.DigestSHA256EmptyTar
35 30
 	}
31
+	return l.roLayer.DiffID()
32
+}
36 33
 
37
-	mountID := stringid.GenerateRandomID()
38
-	rl.rwLayer, err = rl.layerStore.CreateRWLayer(mountID, chainID, nil)
39
-	if err != nil {
40
-		return nil, errors.Wrap(err, "failed to create rwlayer")
34
+func (l *roLayer) Release() error {
35
+	if l.released {
36
+		return nil
41 37
 	}
42
-
43
-	mountPath, err = rl.rwLayer.Mount("")
44
-	if err != nil {
45
-		// Clean up the layer if we fail to mount it here.
46
-		metadata, err := rl.layerStore.ReleaseRWLayer(rl.rwLayer)
38
+	if l.roLayer != nil {
39
+		metadata, err := l.layerStore.Release(l.roLayer)
47 40
 		layer.LogReleaseMetadata(metadata)
48 41
 		if err != nil {
49
-			logrus.Errorf("Failed to release RWLayer: %s", err)
42
+			return errors.Wrap(err, "failed to release ROLayer")
50 43
 		}
51
-		rl.rwLayer = nil
52
-		return nil, err
53 44
 	}
54
-
55
-	return mountPath, nil
45
+	l.roLayer = nil
46
+	l.released = true
47
+	return nil
56 48
 }
57 49
 
58
-func (rl *releaseableLayer) Commit() (builder.ReleaseableLayer, error) {
50
+func (l *roLayer) NewRWLayer() (builder.RWLayer, error) {
59 51
 	var chainID layer.ChainID
60
-	if rl.roLayer != nil {
61
-		chainID = rl.roLayer.ChainID()
52
+	if l.roLayer != nil {
53
+		chainID = l.roLayer.ChainID()
62 54
 	}
63 55
 
64
-	stream, err := rl.rwLayer.TarStream()
56
+	mountID := stringid.GenerateRandomID()
57
+	newLayer, err := l.layerStore.CreateRWLayer(mountID, chainID, nil)
65 58
 	if err != nil {
66
-		return nil, err
59
+		return nil, errors.Wrap(err, "failed to create rwlayer")
67 60
 	}
68
-	defer stream.Close()
69 61
 
70
-	newLayer, err := rl.layerStore.Register(stream, chainID)
62
+	rwLayer := &rwLayer{layerStore: l.layerStore, rwLayer: newLayer}
63
+
64
+	fs, err := newLayer.Mount("")
71 65
 	if err != nil {
66
+		rwLayer.Release()
72 67
 		return nil, err
73 68
 	}
74
-	// TODO: An optimization would be to handle empty layers before returning
75
-	return &releaseableLayer{layerStore: rl.layerStore, roLayer: newLayer}, nil
69
+
70
+	rwLayer.fs = fs
71
+
72
+	return rwLayer, nil
76 73
 }
77 74
 
78
-func (rl *releaseableLayer) DiffID() layer.DiffID {
79
-	if rl.roLayer == nil {
80
-		return layer.DigestSHA256EmptyTar
81
-	}
82
-	return rl.roLayer.DiffID()
75
+type rwLayer struct {
76
+	released   bool
77
+	layerStore layer.Store
78
+	rwLayer    layer.RWLayer
79
+	fs         containerfs.ContainerFS
83 80
 }
84 81
 
85
-func (rl *releaseableLayer) Release() error {
86
-	if rl.released {
87
-		return nil
88
-	}
89
-	if err := rl.releaseRWLayer(); err != nil {
90
-		// Best effort attempt at releasing read-only layer before returning original error.
91
-		rl.releaseROLayer()
92
-		return err
93
-	}
94
-	if err := rl.releaseROLayer(); err != nil {
95
-		return err
96
-	}
97
-	rl.released = true
98
-	return nil
82
+func (l *rwLayer) Root() containerfs.ContainerFS {
83
+	return l.fs
99 84
 }
100 85
 
101
-func (rl *releaseableLayer) releaseRWLayer() error {
102
-	if rl.rwLayer == nil {
103
-		return nil
86
+func (l *rwLayer) Commit() (builder.ROLayer, error) {
87
+	stream, err := l.rwLayer.TarStream()
88
+	if err != nil {
89
+		return nil, err
104 90
 	}
105
-	if err := rl.rwLayer.Unmount(); err != nil {
106
-		logrus.Errorf("Failed to unmount RWLayer: %s", err)
107
-		return err
91
+	defer stream.Close()
92
+
93
+	var chainID layer.ChainID
94
+	if parent := l.rwLayer.Parent(); parent != nil {
95
+		chainID = parent.ChainID()
108 96
 	}
109
-	metadata, err := rl.layerStore.ReleaseRWLayer(rl.rwLayer)
110
-	layer.LogReleaseMetadata(metadata)
97
+
98
+	newLayer, err := l.layerStore.Register(stream, chainID)
111 99
 	if err != nil {
112
-		logrus.Errorf("Failed to release RWLayer: %s", err)
100
+		return nil, err
113 101
 	}
114
-	rl.rwLayer = nil
115
-	return err
102
+	// TODO: An optimization would be to handle empty layers before returning
103
+	return &roLayer{layerStore: l.layerStore, roLayer: newLayer}, nil
116 104
 }
117 105
 
118
-func (rl *releaseableLayer) releaseROLayer() error {
119
-	if rl.roLayer == nil {
106
+func (l *rwLayer) Release() error {
107
+	if l.released {
120 108
 		return nil
121 109
 	}
122
-	metadata, err := rl.layerStore.Release(rl.roLayer)
110
+
111
+	if l.fs != nil {
112
+		if err := l.rwLayer.Unmount(); err != nil {
113
+			return errors.Wrap(err, "failed to unmount RWLayer")
114
+		}
115
+		l.fs = nil
116
+	}
117
+
118
+	metadata, err := l.layerStore.ReleaseRWLayer(l.rwLayer)
123 119
 	layer.LogReleaseMetadata(metadata)
124 120
 	if err != nil {
125
-		logrus.Errorf("Failed to release ROLayer: %s", err)
121
+		return errors.Wrap(err, "failed to release RWLayer")
126 122
 	}
127
-	rl.roLayer = nil
128
-	return err
123
+	l.released = true
124
+	return nil
129 125
 }
130 126
 
131
-func newReleasableLayerForImage(img *image.Image, layerStore layer.Store) (builder.ReleaseableLayer, error) {
127
+func newROLayerForImage(img *image.Image, layerStore layer.Store) (builder.ROLayer, error) {
132 128
 	if img == nil || img.RootFS.ChainID() == "" {
133
-		return &releaseableLayer{layerStore: layerStore}, nil
129
+		return &roLayer{layerStore: layerStore}, nil
134 130
 	}
135 131
 	// Hold a reference to the image layer so that it can't be removed before
136 132
 	// it is released
137
-	roLayer, err := layerStore.Get(img.RootFS.ChainID())
133
+	layer, err := layerStore.Get(img.RootFS.ChainID())
138 134
 	if err != nil {
139 135
 		return nil, errors.Wrapf(err, "failed to get layer for image %s", img.ImageID())
140 136
 	}
141
-	return &releaseableLayer{layerStore: layerStore, roLayer: roLayer}, nil
137
+	return &roLayer{layerStore: layerStore, roLayer: layer}, nil
142 138
 }
143 139
 
144 140
 // TODO: could this use the regular daemon PullImage ?
... ...
@@ -170,12 +166,12 @@ func (daemon *Daemon) pullForBuilder(ctx context.Context, name string, authConfi
170 170
 // GetImageAndReleasableLayer returns an image and releaseable layer for a reference or ID.
171 171
 // Every call to GetImageAndReleasableLayer MUST call releasableLayer.Release() to prevent
172 172
 // leaking of layers.
173
-func (daemon *Daemon) GetImageAndReleasableLayer(ctx context.Context, refOrID string, opts backend.GetImageAndLayerOptions) (builder.Image, builder.ReleaseableLayer, error) {
173
+func (daemon *Daemon) GetImageAndReleasableLayer(ctx context.Context, refOrID string, opts backend.GetImageAndLayerOptions) (builder.Image, builder.ROLayer, error) {
174 174
 	if refOrID == "" {
175 175
 		if !system.IsOSSupported(opts.OS) {
176 176
 			return nil, nil, system.ErrNotSupportedOperatingSystem
177 177
 		}
178
-		layer, err := newReleasableLayerForImage(nil, daemon.layerStores[opts.OS])
178
+		layer, err := newROLayerForImage(nil, daemon.layerStores[opts.OS])
179 179
 		return nil, layer, err
180 180
 	}
181 181
 
... ...
@@ -189,7 +185,7 @@ func (daemon *Daemon) GetImageAndReleasableLayer(ctx context.Context, refOrID st
189 189
 			if !system.IsOSSupported(image.OperatingSystem()) {
190 190
 				return nil, nil, system.ErrNotSupportedOperatingSystem
191 191
 			}
192
-			layer, err := newReleasableLayerForImage(image, daemon.layerStores[image.OperatingSystem()])
192
+			layer, err := newROLayerForImage(image, daemon.layerStores[image.OperatingSystem()])
193 193
 			return image, layer, err
194 194
 		}
195 195
 	}
... ...
@@ -201,7 +197,7 @@ func (daemon *Daemon) GetImageAndReleasableLayer(ctx context.Context, refOrID st
201 201
 	if !system.IsOSSupported(image.OperatingSystem()) {
202 202
 		return nil, nil, system.ErrNotSupportedOperatingSystem
203 203
 	}
204
-	layer, err := newReleasableLayerForImage(image, daemon.layerStores[image.OperatingSystem()])
204
+	layer, err := newROLayerForImage(image, daemon.layerStores[image.OperatingSystem()])
205 205
 	return image, layer, err
206 206
 }
207 207
 
... ...
@@ -301,6 +301,46 @@ COPY bar /`
301 301
 	require.NotContains(t, out.String(), "Using cache")
302 302
 }
303 303
 
304
+// docker/for-linux#135
305
+// #35641
306
+func TestBuildMultiStageLayerLeak(t *testing.T) {
307
+	ctx := context.TODO()
308
+	defer setupTest(t)()
309
+
310
+	// all commands need to match until COPY
311
+	dockerfile := `FROM busybox
312
+WORKDIR /foo
313
+COPY foo .
314
+FROM busybox
315
+WORKDIR /foo
316
+COPY bar .
317
+RUN [ -f bar ]
318
+RUN [ ! -f foo ]
319
+`
320
+
321
+	source := fakecontext.New(t, "",
322
+		fakecontext.WithFile("foo", "0"),
323
+		fakecontext.WithFile("bar", "1"),
324
+		fakecontext.WithDockerfile(dockerfile))
325
+	defer source.Close()
326
+
327
+	apiclient := testEnv.APIClient()
328
+	resp, err := apiclient.ImageBuild(ctx,
329
+		source.AsTarReader(t),
330
+		types.ImageBuildOptions{
331
+			Remove:      true,
332
+			ForceRemove: true,
333
+		})
334
+
335
+	out := bytes.NewBuffer(nil)
336
+	require.NoError(t, err)
337
+	_, err = io.Copy(out, resp.Body)
338
+	resp.Body.Close()
339
+	require.NoError(t, err)
340
+
341
+	assert.Contains(t, out.String(), "Successfully built")
342
+}
343
+
304 344
 func writeTarRecord(t *testing.T, w *tar.Writer, fn, contents string) {
305 345
 	err := w.WriteHeader(&tar.Header{
306 346
 		Name:     fn,