From 988554ab5c12971383bc717cda615ca672953cd5 Mon Sep 17 00:00:00 2001
From: yangshukui <yangshukui@huawei.com>
Date: Fri, 18 May 2018 11:03:48 +0800
Subject: [PATCH 36/94] runc-17: add compatibility for docker-1.11.2

[Changelog]: add compatibility for docker-1.11.2
[Author]: Shukui Yang

Change-Id: I188db47db8f4bcd744ac8218bfe966de48e97c22
Signed-off-by: yangshukui <yangshukui@huawei.com>
---
 libcontainer/configs/cgroup_unix.go |   6 +++
 libcontainer/configs/config.go      |  11 ++++
 libcontainer/container_linux.go     |   6 +++
 libcontainer/factory_linux.go       | 102 +++++++++++++++++++++++++++++++-----
 4 files changed, 113 insertions(+), 12 deletions(-)

diff --git a/libcontainer/configs/cgroup_unix.go b/libcontainer/configs/cgroup_unix.go
index e654960..75a3db0 100644
--- a/libcontainer/configs/cgroup_unix.go
+++ b/libcontainer/configs/cgroup_unix.go
@@ -33,6 +33,12 @@ type Cgroup struct {
 	*Resources
 }
 
+// CompatCgroup
+type CompatCgroup struct {
+   Cgroup
+   MemorySwappiness interface{} `json:"memory_swappiness"`
+}
+
 type Resources struct {
 	// If this is true allow access to any kind of device within the container.  If false, allow access only to devices explicitly listed in the allowed_devices list.
 	// Deprecated
diff --git a/libcontainer/configs/config.go b/libcontainer/configs/config.go
index af25972..3a2e824 100644
--- a/libcontainer/configs/config.go
+++ b/libcontainer/configs/config.go
@@ -188,6 +188,17 @@ type Config struct {
 	Rootless bool `json:"rootless"`
 }
 
+// CompatConfig is a structure inheriting from spec.Process defined
+// in runtime-spec/specs-go package. The goal is to be compatible with
+// both v1.0.0-rc4 and v1.0.0-rc5 since the latter introduced a change
+// about the type of the Capabilities field.
+// Refer to: https://github.com/opencontainers/runtime-spec/commit/37391fb
+type CompatConfig struct {
+   Config
+   Cgroups *CompatCgroup `json:"cgroups"`
+   Capabilities interface{} `json:"capabilities,omitempty" platform:"linux"`
+}
+
 type Hooks struct {
 	// Prestart commands are executed after the container namespaces are created,
 	// but before the user supplied command is executed from init.
diff --git a/libcontainer/container_linux.go b/libcontainer/container_linux.go
index ea6ef4c..f4eec7e 100644
--- a/libcontainer/container_linux.go
+++ b/libcontainer/container_linux.go
@@ -66,6 +66,12 @@ type State struct {
 	ExternalDescriptors []string `json:"external_descriptors,omitempty"`
 }
 
+// CompatState
+type CompatState struct{
+   State
+   Config configs.CompatConfig `json:"config"`
+}
+
 // Container is a libcontainer container object.
 //
 // Each container is thread-safe within the same process. Since a container can
diff --git a/libcontainer/factory_linux.go b/libcontainer/factory_linux.go
index 8bf448a..b533346 100644
--- a/libcontainer/factory_linux.go
+++ b/libcontainer/factory_linux.go
@@ -10,8 +10,9 @@ import (
 	"regexp"
 	"runtime/debug"
 	"strconv"
-	"strings"
 	"syscall"
+	"io/ioutil"
+	"errors"
 
 	"github.com/docker/docker/pkg/mount"
 	"github.com/opencontainers/runc/libcontainer/cgroups"
@@ -311,28 +312,105 @@ func (l *LinuxFactory) StartInitialization() (err error) {
 	return i.Init()
 }
 
-func (l *LinuxFactory) loadState(root, id string) (*State, error) {
-	f, err := os.Open(filepath.Join(root, stateFilename))
+func (l *LinuxFactory) updateStateCapabilites(compatState *CompatState, configPath string) error {
+	needUpdate := false
+
+	// In spec v1.0.0-rc4, capabilities was a list of strings. This was changed
+	// to an object with v1.0.0-rc5.
+	// Check for the interface type to support both the versions.
+	capabilities := compatState.Config.Capabilities
+	switch caps := capabilities.(type) {
+	case []interface{}:
+		var list []string
+		for _, str := range caps {
+			list = append(list, str.(string))
+		}
+
+		c := configs.Capabilities{
+			Bounding:    list,
+			Effective:   list,
+			Inheritable: list,
+			Ambient:     list,
+			Permitted:   list,
+		}
+		compatState.Config.Capabilities = c
+		needUpdate = true
+	}
+
+	//In spec v1.0.0-rc4, MemorySwappiness  was a *int64. This was changed
+	// to an *uint64 with v1.0.0-rc5.
+	if compatState.Config.Cgroups != nil &&
+		compatState.Config.Cgroups.MemorySwappiness != nil {
+		memorySwappiness, ok := compatState.Config.Cgroups.MemorySwappiness.(float64)
+		if ok {
+			var memSize int64 = int64(memorySwappiness)
+			if memSize < 0 {
+				memSize = 0
+				var memUSize uint64 = uint64(memSize-1)
+				compatState.Config.Cgroups.MemorySwappiness = &memUSize
+				needUpdate = true
+			}
+		}
+	}
+
+	if needUpdate {
+		f, err := os.Create(configPath)
+		if err != nil {
+			return err
+		}
+		defer f.Close()
+		if err := json.NewEncoder(f).Encode(&compatState); err != nil {
+			return err
+		}
+		return nil
+	}
+
+	return errors.New("updateStateCapabilites unexpected format for capabilities")
+}
+
+func (l *LinuxFactory) loadOriginState(configPath string) (*State, error) {
+	f, err := os.Open(configPath)
 	if err != nil {
 		if os.IsNotExist(err) {
-			return nil, newGenericError(fmt.Errorf("container %q does not exist", id), ContainerNotExists)
+			return nil, newGenericError(err, ContainerNotExists)
 		}
 		return nil, newGenericError(err, SystemError)
 	}
 	defer f.Close()
 	var state *State
 	if err := json.NewDecoder(f).Decode(&state); err != nil {
-		if !strings.Contains(err.Error(), "memory_swappiness") {
-			return nil, newGenericError(err, SystemError)
-		}
+		return nil, newGenericError(err, SystemError)
+	}
+	return state, nil
+}
 
-		if state.BaseState.Config.Cgroups != nil &&
-			state.BaseState.Config.Cgroups.Resources != nil &&
-			state.BaseState.Config.Cgroups.Resources.MemorySwappiness != nil {
-			memorySwappiness := int64(-1)
-			*state.BaseState.Config.Cgroups.Resources.MemorySwappiness = uint64(memorySwappiness)
+func (l *LinuxFactory) loadCompatState(configPath string) (*State, error) {
+	dt, err := ioutil.ReadFile(configPath)
+	if err != nil {
+		if os.IsNotExist(err) {
+			return nil, newGenericError(err, ContainerNotExists)
 		}
+		return nil, newGenericError(err, SystemError)
+	}
 
+	var state *CompatState
+	if err := json.Unmarshal(dt, &state); err != nil {
+		return nil, newGenericError(err, SystemError)
+	}
+
+	err = l.updateStateCapabilites(state, configPath)
+	if err != nil {
+		return nil, newGenericError(err, SystemError)
+	}
+
+	return l.loadOriginState(configPath)
+}
+
+func (l *LinuxFactory) loadState(root, id string) (*State, error) {
+	configPath := filepath.Join(root, stateFilename)
+	state, err := l.loadOriginState(configPath)
+	if err != nil {
+		return l.loadCompatState(configPath)
 	}
 	return state, nil
 }
-- 
2.7.4.3

