Browse code

Fixes #29654: take reference to RWLayer while committing/exporting

Take an extra reference to rwlayer while the container is being
committed or exported to avoid the removal of that layer.

Also add some checks before commit/export.

Signed-off-by: Yuanhong Peng <pengyuanhong@huawei.com>

Yuanhong Peng authored on 2017/05/20 11:38:45
Showing 3 changed files
... ...
@@ -357,6 +357,15 @@ func (s *State) ResetRemovalInProgress() {
357 357
 	s.Unlock()
358 358
 }
359 359
 
360
+// IsRemovalInProgress returns whether the RemovalInProgress flag is set.
361
+// Used by Container to check whether a container is being removed.
362
+func (s *State) IsRemovalInProgress() bool {
363
+	s.Lock()
364
+	res := s.RemovalInProgress
365
+	s.Unlock()
366
+	return res
367
+}
368
+
360 369
 // SetDead sets the container state to "dead"
361 370
 func (s *State) SetDead() {
362 371
 	s.Lock()
... ...
@@ -364,6 +373,14 @@ func (s *State) SetDead() {
364 364
 	s.Unlock()
365 365
 }
366 366
 
367
+// IsDead returns whether the Dead flag is set. Used by Container to check whether a container is dead.
368
+func (s *State) IsDead() bool {
369
+	s.Lock()
370
+	res := s.Dead
371
+	s.Unlock()
372
+	return res
373
+}
374
+
367 375
 // SetRemoved assumes this container is already in the "dead" state and
368 376
 // closes the internal waitRemove channel to unblock callers waiting for a
369 377
 // container to be removed.
... ...
@@ -2,6 +2,7 @@ package daemon
2 2
 
3 3
 import (
4 4
 	"encoding/json"
5
+	"fmt"
5 6
 	"io"
6 7
 	"runtime"
7 8
 	"strings"
... ...
@@ -133,6 +134,16 @@ func (daemon *Daemon) Commit(name string, c *backend.ContainerCommitConfig) (str
133 133
 		return "", errors.Errorf("%+v does not support commit of a running container", runtime.GOOS)
134 134
 	}
135 135
 
136
+	if container.IsDead() {
137
+		err := fmt.Errorf("You cannot commit container %s which is Dead", container.ID)
138
+		return "", stateConflictError{err}
139
+	}
140
+
141
+	if container.IsRemovalInProgress() {
142
+		err := fmt.Errorf("You cannot commit container %s which is being removed", container.ID)
143
+		return "", stateConflictError{err}
144
+	}
145
+
136 146
 	if c.Pause && !container.IsPaused() {
137 147
 		daemon.containerPause(container)
138 148
 		defer daemon.containerUnpause(container)
... ...
@@ -234,19 +245,36 @@ func (daemon *Daemon) Commit(name string, c *backend.ContainerCommitConfig) (str
234 234
 	return id.String(), nil
235 235
 }
236 236
 
237
-func (daemon *Daemon) exportContainerRw(container *container.Container) (io.ReadCloser, error) {
238
-	if err := daemon.Mount(container); err != nil {
237
+func (daemon *Daemon) exportContainerRw(container *container.Container) (arch io.ReadCloser, err error) {
238
+	rwlayer, err := daemon.stores[container.Platform].layerStore.GetRWLayer(container.ID)
239
+	if err != nil {
240
+		return nil, err
241
+	}
242
+	defer func() {
243
+		if err != nil {
244
+			daemon.stores[container.Platform].layerStore.ReleaseRWLayer(rwlayer)
245
+		}
246
+	}()
247
+
248
+	// TODO: this mount call is not necessary as we assume that TarStream() should
249
+	// mount the layer if needed. But the Diff() function for windows requests that
250
+	// the layer should be mounted when calling it. So we reserve this mount call
251
+	// until windows driver can implement Diff() interface correctly.
252
+	_, err = rwlayer.Mount(container.GetMountLabel())
253
+	if err != nil {
239 254
 		return nil, err
240 255
 	}
241 256
 
242
-	archive, err := container.RWLayer.TarStream()
257
+	archive, err := rwlayer.TarStream()
243 258
 	if err != nil {
244
-		daemon.Unmount(container) // logging is already handled in the `Unmount` function
259
+		rwlayer.Unmount()
245 260
 		return nil, err
246 261
 	}
247 262
 	return ioutils.NewReadCloserWrapper(archive, func() error {
248 263
 			archive.Close()
249
-			return container.RWLayer.Unmount()
264
+			err = rwlayer.Unmount()
265
+			daemon.stores[container.Platform].layerStore.ReleaseRWLayer(rwlayer)
266
+			return err
250 267
 		}),
251 268
 		nil
252 269
 }
... ...
@@ -22,6 +22,16 @@ func (daemon *Daemon) ContainerExport(name string, out io.Writer) error {
22 22
 		return fmt.Errorf("the daemon on this platform does not support exporting Windows containers")
23 23
 	}
24 24
 
25
+	if container.IsDead() {
26
+		err := fmt.Errorf("You cannot export container %s which is Dead", container.ID)
27
+		return stateConflictError{err}
28
+	}
29
+
30
+	if container.IsRemovalInProgress() {
31
+		err := fmt.Errorf("You cannot export container %s which is being removed", container.ID)
32
+		return stateConflictError{err}
33
+	}
34
+
25 35
 	data, err := daemon.containerExport(container)
26 36
 	if err != nil {
27 37
 		return fmt.Errorf("Error exporting container %s: %v", name, err)
... ...
@@ -35,8 +45,19 @@ func (daemon *Daemon) ContainerExport(name string, out io.Writer) error {
35 35
 	return nil
36 36
 }
37 37
 
38
-func (daemon *Daemon) containerExport(container *container.Container) (io.ReadCloser, error) {
39
-	if err := daemon.Mount(container); err != nil {
38
+func (daemon *Daemon) containerExport(container *container.Container) (arch io.ReadCloser, err error) {
39
+	rwlayer, err := daemon.stores[container.Platform].layerStore.GetRWLayer(container.ID)
40
+	if err != nil {
41
+		return nil, err
42
+	}
43
+	defer func() {
44
+		if err != nil {
45
+			daemon.stores[container.Platform].layerStore.ReleaseRWLayer(rwlayer)
46
+		}
47
+	}()
48
+
49
+	_, err = rwlayer.Mount(container.GetMountLabel())
50
+	if err != nil {
40 51
 		return nil, err
41 52
 	}
42 53
 
... ...
@@ -46,12 +67,13 @@ func (daemon *Daemon) containerExport(container *container.Container) (io.ReadCl
46 46
 		GIDMaps:     daemon.idMappings.GIDs(),
47 47
 	})
48 48
 	if err != nil {
49
-		daemon.Unmount(container)
49
+		rwlayer.Unmount()
50 50
 		return nil, err
51 51
 	}
52
-	arch := ioutils.NewReadCloserWrapper(archive, func() error {
52
+	arch = ioutils.NewReadCloserWrapper(archive, func() error {
53 53
 		err := archive.Close()
54
-		daemon.Unmount(container)
54
+		rwlayer.Unmount()
55
+		daemon.stores[container.Platform].layerStore.ReleaseRWLayer(rwlayer)
55 56
 		return err
56 57
 	})
57 58
 	daemon.LogContainerEvent(container, "export")