daemon/daemon_unix.go
10d30c64
 // +build linux freebsd
8fb0ca2c
 
 package daemon
 
 import (
 	"fmt"
140a7434
 	"io/ioutil"
8fb0ca2c
 	"net"
 	"os"
 	"path/filepath"
557c7cb8
 	"runtime"
140a7434
 	"runtime/debug"
0f351ce3
 	"strconv"
8fb0ca2c
 	"strings"
b7f887a9
 	"syscall"
9c4570a9
 	"time"
8fb0ca2c
 
 	"github.com/Sirupsen/logrus"
6bb0d181
 	"github.com/docker/docker/container"
4352da78
 	"github.com/docker/docker/image"
 	"github.com/docker/docker/layer"
442b4562
 	"github.com/docker/docker/pkg/idtools"
5ce5a8e9
 	"github.com/docker/docker/pkg/parsers"
8fb0ca2c
 	"github.com/docker/docker/pkg/parsers/kernel"
b2d06b6f
 	"github.com/docker/docker/pkg/sysinfo"
2655954c
 	"github.com/docker/docker/reference"
8fb0ca2c
 	"github.com/docker/docker/runconfig"
2b7ad47b
 	runconfigopts "github.com/docker/docker/runconfig/opts"
9c4570a9
 	"github.com/docker/engine-api/types"
907407d0
 	pblkiodev "github.com/docker/engine-api/types/blkiodev"
 	containertypes "github.com/docker/engine-api/types/container"
8fb0ca2c
 	"github.com/docker/libnetwork"
da5a3e6d
 	nwconfig "github.com/docker/libnetwork/config"
0f351ce3
 	"github.com/docker/libnetwork/drivers/bridge"
 	"github.com/docker/libnetwork/ipamutils"
8fb0ca2c
 	"github.com/docker/libnetwork/netlabel"
 	"github.com/docker/libnetwork/options"
9c4570a9
 	lntypes "github.com/docker/libnetwork/types"
c86189d5
 	"github.com/opencontainers/runc/libcontainer/label"
557c7cb8
 	"github.com/opencontainers/runc/libcontainer/user"
9c4570a9
 	"github.com/opencontainers/specs/specs-go"
8fb0ca2c
 )
 
e0af23dc
 const (
 	// See https://git.kernel.org/cgit/linux/kernel/git/tip/tip.git/tree/kernel/sched/sched.h?id=8cd9234c64c584432f6992fe944ca9e46ca8ea76#n269
 	linuxMinCPUShares = 2
 	linuxMaxCPUShares = 262144
10d30c64
 	platformSupported = true
2347f980
 	// It's not kernel limit, we want this 4M limit to supply a reasonable functional container
 	linuxMinMemory = 4194304
557c7cb8
 	// constants for remapped root settings
 	defaultIDSpecifier string = "default"
 	defaultRemappedID  string = "dockremap"
8af4f89c
 
 	// constant for cgroup drivers
 	cgroupFsDriver      = "cgroupfs"
 	cgroupSystemdDriver = "systemd"
e0af23dc
 )
 
9c4570a9
 func getMemoryResources(config containertypes.Resources) *specs.Memory {
 	memory := specs.Memory{}
 
 	if config.Memory > 0 {
 		limit := uint64(config.Memory)
 		memory.Limit = &limit
 	}
 
 	if config.MemoryReservation > 0 {
 		reservation := uint64(config.MemoryReservation)
 		memory.Reservation = &reservation
 	}
 
 	if config.MemorySwap != 0 {
 		swap := uint64(config.MemorySwap)
 		memory.Swap = &swap
 	}
 
 	if config.MemorySwappiness != nil {
 		swappiness := uint64(*config.MemorySwappiness)
 		memory.Swappiness = &swappiness
 	}
 
 	if config.KernelMemory != 0 {
 		kernelMemory := uint64(config.KernelMemory)
 		memory.Kernel = &kernelMemory
 	}
 
 	return &memory
 }
 
 func getCPUResources(config containertypes.Resources) *specs.CPU {
 	cpu := specs.CPU{}
 
 	if config.CPUShares != 0 {
 		shares := uint64(config.CPUShares)
 		cpu.Shares = &shares
 	}
 
 	if config.CpusetCpus != "" {
 		cpuset := config.CpusetCpus
 		cpu.Cpus = &cpuset
 	}
 
 	if config.CpusetMems != "" {
 		cpuset := config.CpusetMems
 		cpu.Mems = &cpuset
 	}
 
 	if config.CPUPeriod != 0 {
 		period := uint64(config.CPUPeriod)
 		cpu.Period = &period
 	}
 
 	if config.CPUQuota != 0 {
 		quota := uint64(config.CPUQuota)
 		cpu.Quota = &quota
 	}
 
 	return &cpu
 }
 
 func getBlkioWeightDevices(config containertypes.Resources) ([]specs.WeightDevice, error) {
0fbfa144
 	var stat syscall.Stat_t
9c4570a9
 	var blkioWeightDevices []specs.WeightDevice
0fbfa144
 
 	for _, weightDevice := range config.BlkioWeightDevice {
 		if err := syscall.Stat(weightDevice.Path, &stat); err != nil {
 			return nil, err
 		}
9c4570a9
 		weight := weightDevice.Weight
 		d := specs.WeightDevice{Weight: &weight}
 		d.Major = int64(stat.Rdev / 256)
4b03e857
 		d.Minor = int64(stat.Rdev % 256)
9c4570a9
 		blkioWeightDevices = append(blkioWeightDevices, d)
0fbfa144
 	}
 
38797ca6
 	return blkioWeightDevices, nil
0fbfa144
 }
 
7ac4232e
 func parseSecurityOpt(container *container.Container, config *containertypes.HostConfig) error {
8fb0ca2c
 	var (
 		labelOpts []string
 		err       error
 	)
 
 	for _, opt := range config.SecurityOpt {
cb9aeb04
 		if opt == "no-new-privileges" {
 			container.NoNewPrivileges = true
 		} else {
 			var con []string
 			if strings.Contains(opt, "=") {
 				con = strings.SplitN(opt, "=", 2)
 			} else if strings.Contains(opt, ":") {
 				con = strings.SplitN(opt, ":", 2)
 				logrus.Warnf("Security options with `:` as a separator are deprecated and will be completely unsupported in 1.13, use `=` instead.")
 			}
 
 			if len(con) != 2 {
74bb1ce9
 				return fmt.Errorf("Invalid --security-opt 1: %q", opt)
 			}
cb9aeb04
 
74bb1ce9
 			switch con[0] {
 			case "label":
 				labelOpts = append(labelOpts, con[1])
 			case "apparmor":
 				container.AppArmorProfile = con[1]
 			case "seccomp":
 				container.SeccompProfile = con[1]
 			default:
 				return fmt.Errorf("Invalid --security-opt 2: %q", opt)
 			}
8fb0ca2c
 		}
 	}
 
 	container.ProcessLabel, container.MountLabel, err = label.InitLabels(labelOpts)
 	return err
 }
 
9c4570a9
 func getBlkioReadIOpsDevices(config containertypes.Resources) ([]specs.ThrottleDevice, error) {
 	var blkioReadIOpsDevice []specs.ThrottleDevice
843084b0
 	var stat syscall.Stat_t
 
 	for _, iopsDevice := range config.BlkioDeviceReadIOps {
 		if err := syscall.Stat(iopsDevice.Path, &stat); err != nil {
 			return nil, err
 		}
9c4570a9
 		rate := iopsDevice.Rate
 		d := specs.ThrottleDevice{Rate: &rate}
 		d.Major = int64(stat.Rdev / 256)
4b03e857
 		d.Minor = int64(stat.Rdev % 256)
9c4570a9
 		blkioReadIOpsDevice = append(blkioReadIOpsDevice, d)
843084b0
 	}
 
 	return blkioReadIOpsDevice, nil
 }
 
9c4570a9
 func getBlkioWriteIOpsDevices(config containertypes.Resources) ([]specs.ThrottleDevice, error) {
 	var blkioWriteIOpsDevice []specs.ThrottleDevice
843084b0
 	var stat syscall.Stat_t
 
 	for _, iopsDevice := range config.BlkioDeviceWriteIOps {
 		if err := syscall.Stat(iopsDevice.Path, &stat); err != nil {
 			return nil, err
 		}
9c4570a9
 		rate := iopsDevice.Rate
 		d := specs.ThrottleDevice{Rate: &rate}
 		d.Major = int64(stat.Rdev / 256)
4b03e857
 		d.Minor = int64(stat.Rdev % 256)
9c4570a9
 		blkioWriteIOpsDevice = append(blkioWriteIOpsDevice, d)
843084b0
 	}
 
 	return blkioWriteIOpsDevice, nil
 }
 
9c4570a9
 func getBlkioReadBpsDevices(config containertypes.Resources) ([]specs.ThrottleDevice, error) {
 	var blkioReadBpsDevice []specs.ThrottleDevice
3f15a055
 	var stat syscall.Stat_t
 
 	for _, bpsDevice := range config.BlkioDeviceReadBps {
 		if err := syscall.Stat(bpsDevice.Path, &stat); err != nil {
 			return nil, err
 		}
9c4570a9
 		rate := bpsDevice.Rate
 		d := specs.ThrottleDevice{Rate: &rate}
 		d.Major = int64(stat.Rdev / 256)
4b03e857
 		d.Minor = int64(stat.Rdev % 256)
9c4570a9
 		blkioReadBpsDevice = append(blkioReadBpsDevice, d)
3f15a055
 	}
 
38797ca6
 	return blkioReadBpsDevice, nil
3f15a055
 }
 
9c4570a9
 func getBlkioWriteBpsDevices(config containertypes.Resources) ([]specs.ThrottleDevice, error) {
 	var blkioWriteBpsDevice []specs.ThrottleDevice
3f15a055
 	var stat syscall.Stat_t
 
 	for _, bpsDevice := range config.BlkioDeviceWriteBps {
 		if err := syscall.Stat(bpsDevice.Path, &stat); err != nil {
 			return nil, err
 		}
9c4570a9
 		rate := bpsDevice.Rate
 		d := specs.ThrottleDevice{Rate: &rate}
 		d.Major = int64(stat.Rdev / 256)
4b03e857
 		d.Minor = int64(stat.Rdev % 256)
9c4570a9
 		blkioWriteBpsDevice = append(blkioWriteBpsDevice, d)
3f15a055
 	}
 
38797ca6
 	return blkioWriteBpsDevice, nil
3f15a055
 }
 
abd72d40
 func checkKernelVersion(k, major, minor int) bool {
87959dbf
 	if v, err := kernel.GetKernelVersion(); err != nil {
 		logrus.Warnf("%s", err)
 	} else {
 		if kernel.CompareKernelVersion(*v, kernel.VersionInfo{Kernel: k, Major: major, Minor: minor}) < 0 {
 			return false
 		}
 	}
 	return true
 }
 
8fb0ca2c
 func checkKernel() error {
 	// Check for unsupported kernel versions
 	// FIXME: it would be cleaner to not test for specific versions, but rather
 	// test for specific functionalities.
 	// Unfortunately we can't test for the feature "does not cause a kernel panic"
 	// without actually causing a kernel panic, so we need this workaround until
 	// the circumstances of pre-3.10 crashes are clearer.
 	// For details see https://github.com/docker/docker/issues/407
abd72d40
 	if !checkKernelVersion(3, 10, 0) {
87959dbf
 		v, _ := kernel.GetKernelVersion()
 		if os.Getenv("DOCKER_NOWARN_KERNEL_VERSION") == "" {
 			logrus.Warnf("Your Linux kernel version %s can be unstable running docker. Please upgrade your kernel to 3.10.0.", v.String())
8fb0ca2c
 		}
 	}
 	return nil
 }
 
3fea79bf
 // adaptContainerSettings is called during container creation to modify any
 // settings necessary in the HostConfig structure.
7ac4232e
 func (daemon *Daemon) adaptContainerSettings(hostConfig *containertypes.HostConfig, adjustCPUShares bool) error {
e0af23dc
 	if adjustCPUShares && hostConfig.CPUShares > 0 {
 		// Handle unsupported CPUShares
 		if hostConfig.CPUShares < linuxMinCPUShares {
 			logrus.Warnf("Changing requested CPUShares of %d to minimum allowed of %d", hostConfig.CPUShares, linuxMinCPUShares)
 			hostConfig.CPUShares = linuxMinCPUShares
 		} else if hostConfig.CPUShares > linuxMaxCPUShares {
 			logrus.Warnf("Changing requested CPUShares of %d to maximum allowed of %d", hostConfig.CPUShares, linuxMaxCPUShares)
 			hostConfig.CPUShares = linuxMaxCPUShares
 		}
 	}
7e0dfbf4
 	if hostConfig.Memory > 0 && hostConfig.MemorySwap == 0 {
 		// By default, MemorySwap is set to twice the size of Memory.
 		hostConfig.MemorySwap = hostConfig.Memory * 2
 	}
5190794f
 	if hostConfig.ShmSize == 0 {
 		hostConfig.ShmSize = container.DefaultSHMSize
ef1d410b
 	}
1415f55c
 	var err error
 	if hostConfig.SecurityOpt == nil {
 		hostConfig.SecurityOpt, err = daemon.generateSecurityOpt(hostConfig.IpcMode, hostConfig.PidMode)
 		if err != nil {
 			return err
 		}
 	}
4089b4e4
 	if hostConfig.MemorySwappiness == nil {
 		defaultSwappiness := int64(-1)
 		hostConfig.MemorySwappiness = &defaultSwappiness
 	}
f4a68733
 	if hostConfig.OomKillDisable == nil {
 		defaultOomKillDisable := false
 		hostConfig.OomKillDisable = &defaultOomKillDisable
 	}
1415f55c
 
 	return nil
7e0dfbf4
 }
 
8ae6f6ac
 func verifyContainerResources(resources *containertypes.Resources, sysInfo *sysinfo.SysInfo, update bool) ([]string, error) {
b2d06b6f
 	warnings := []string{}
8fb0ca2c
 
4177b0ba
 	// memory subsystem checks and adjustments
c6bfb54a
 	if resources.Memory != 0 && resources.Memory < linuxMinMemory {
8fb0ca2c
 		return warnings, fmt.Errorf("Minimum memory limit allowed is 4MB")
 	}
c6bfb54a
 	if resources.Memory > 0 && !sysInfo.MemoryLimit {
8fb0ca2c
 		warnings = append(warnings, "Your kernel does not support memory limit capabilities. Limitation discarded.")
 		logrus.Warnf("Your kernel does not support memory limit capabilities. Limitation discarded.")
c6bfb54a
 		resources.Memory = 0
 		resources.MemorySwap = -1
8fb0ca2c
 	}
c6bfb54a
 	if resources.Memory > 0 && resources.MemorySwap != -1 && !sysInfo.SwapLimit {
8fb0ca2c
 		warnings = append(warnings, "Your kernel does not support swap limit capabilities, memory limited without swap.")
 		logrus.Warnf("Your kernel does not support swap limit capabilities, memory limited without swap.")
c6bfb54a
 		resources.MemorySwap = -1
8fb0ca2c
 	}
c6bfb54a
 	if resources.Memory > 0 && resources.MemorySwap > 0 && resources.MemorySwap < resources.Memory {
8fb0ca2c
 		return warnings, fmt.Errorf("Minimum memoryswap limit should be larger than memory limit, see usage.")
 	}
8ae6f6ac
 	if resources.Memory == 0 && resources.MemorySwap > 0 && !update {
8fb0ca2c
 		return warnings, fmt.Errorf("You should always set the Memory limit when using Memoryswap limit, see usage.")
 	}
c6bfb54a
 	if resources.MemorySwappiness != nil && *resources.MemorySwappiness != -1 && !sysInfo.MemorySwappiness {
19c7b65e
 		warnings = append(warnings, "Your kernel does not support memory swappiness capabilities, memory swappiness discarded.")
 		logrus.Warnf("Your kernel does not support memory swappiness capabilities, memory swappiness discarded.")
c6bfb54a
 		resources.MemorySwappiness = nil
19c7b65e
 	}
c6bfb54a
 	if resources.MemorySwappiness != nil {
 		swappiness := *resources.MemorySwappiness
4e25d298
 		if swappiness < -1 || swappiness > 100 {
 			return warnings, fmt.Errorf("Invalid value: %v, valid memory swappiness range is 0-100.", swappiness)
 		}
19c7b65e
 	}
c6bfb54a
 	if resources.MemoryReservation > 0 && !sysInfo.MemoryReservation {
aa178099
 		warnings = append(warnings, "Your kernel does not support memory soft limit capabilities. Limitation discarded.")
 		logrus.Warnf("Your kernel does not support memory soft limit capabilities. Limitation discarded.")
c6bfb54a
 		resources.MemoryReservation = 0
aa178099
 	}
c6bfb54a
 	if resources.Memory > 0 && resources.MemoryReservation > 0 && resources.Memory < resources.MemoryReservation {
aa178099
 		return warnings, fmt.Errorf("Minimum memory limit should be larger than memory reservation limit, see usage.")
 	}
c6bfb54a
 	if resources.KernelMemory > 0 && !sysInfo.KernelMemory {
b6f1b4ad
 		warnings = append(warnings, "Your kernel does not support kernel memory limit capabilities. Limitation discarded.")
 		logrus.Warnf("Your kernel does not support kernel memory limit capabilities. Limitation discarded.")
c6bfb54a
 		resources.KernelMemory = 0
b6f1b4ad
 	}
c6bfb54a
 	if resources.KernelMemory > 0 && resources.KernelMemory < linuxMinMemory {
2347f980
 		return warnings, fmt.Errorf("Minimum kernel memory limit allowed is 4MB")
 	}
c6bfb54a
 	if resources.KernelMemory > 0 && !checkKernelVersion(4, 0, 0) {
b6f1b4ad
 		warnings = append(warnings, "You specified a kernel memory limit on a kernel older than 4.0. Kernel memory limits are experimental on older kernels, it won't work as expected and can cause your system to be unstable.")
 		logrus.Warnf("You specified a kernel memory limit on a kernel older than 4.0. Kernel memory limits are experimental on older kernels, it won't work as expected and can cause your system to be unstable.")
 	}
f4a68733
 	if resources.OomKillDisable != nil && !sysInfo.OomKillDisable {
5a707d94
 		// only produce warnings if the setting wasn't to *disable* the OOM Kill; no point
 		// warning the caller if they already wanted the feature to be off
 		if *resources.OomKillDisable {
 			warnings = append(warnings, "Your kernel does not support OomKillDisable, OomKillDisable discarded.")
 			logrus.Warnf("Your kernel does not support OomKillDisable, OomKillDisable discarded.")
 		}
f4a68733
 		resources.OomKillDisable = nil
8498ed73
 	}
c6bfb54a
 
69cf0370
 	if resources.PidsLimit != 0 && !sysInfo.PidsLimit {
 		warnings = append(warnings, "Your kernel does not support pids limit capabilities, pids limit discarded.")
 		logrus.Warnf("Your kernel does not support pids limit capabilities, pids limit discarded.")
 		resources.PidsLimit = 0
 	}
 
c6bfb54a
 	// cpu subsystem checks and adjustments
 	if resources.CPUShares > 0 && !sysInfo.CPUShares {
b7599d58
 		warnings = append(warnings, "Your kernel does not support CPU shares. Shares discarded.")
 		logrus.Warnf("Your kernel does not support CPU shares. Shares discarded.")
c6bfb54a
 		resources.CPUShares = 0
b7599d58
 	}
c6bfb54a
 	if resources.CPUPeriod > 0 && !sysInfo.CPUCfsPeriod {
8fb0ca2c
 		warnings = append(warnings, "Your kernel does not support CPU cfs period. Period discarded.")
 		logrus.Warnf("Your kernel does not support CPU cfs period. Period discarded.")
c6bfb54a
 		resources.CPUPeriod = 0
8fb0ca2c
 	}
c6bfb54a
 	if resources.CPUQuota > 0 && !sysInfo.CPUCfsQuota {
8fb0ca2c
 		warnings = append(warnings, "Your kernel does not support CPU cfs quota. Quota discarded.")
 		logrus.Warnf("Your kernel does not support CPU cfs quota. Quota discarded.")
c6bfb54a
 		resources.CPUQuota = 0
8fb0ca2c
 	}
c6bfb54a
 
 	// cpuset subsystem checks and adjustments
 	if (resources.CpusetCpus != "" || resources.CpusetMems != "") && !sysInfo.Cpuset {
b7599d58
 		warnings = append(warnings, "Your kernel does not support cpuset. Cpuset discarded.")
 		logrus.Warnf("Your kernel does not support cpuset. Cpuset discarded.")
c6bfb54a
 		resources.CpusetCpus = ""
 		resources.CpusetMems = ""
b7599d58
 	}
c6bfb54a
 	cpusAvailable, err := sysInfo.IsCpusetCpusAvailable(resources.CpusetCpus)
94464e3a
 	if err != nil {
a793564b
 		return warnings, fmt.Errorf("Invalid value %s for cpuset cpus.", resources.CpusetCpus)
94464e3a
 	}
 	if !cpusAvailable {
a793564b
 		return warnings, fmt.Errorf("Requested CPUs are not available - requested %s, available: %s.", resources.CpusetCpus, sysInfo.Cpus)
94464e3a
 	}
c6bfb54a
 	memsAvailable, err := sysInfo.IsCpusetMemsAvailable(resources.CpusetMems)
94464e3a
 	if err != nil {
a793564b
 		return warnings, fmt.Errorf("Invalid value %s for cpuset mems.", resources.CpusetMems)
94464e3a
 	}
 	if !memsAvailable {
a793564b
 		return warnings, fmt.Errorf("Requested memory nodes are not available - requested %s, available: %s.", resources.CpusetMems, sysInfo.Mems)
94464e3a
 	}
c6bfb54a
 
 	// blkio subsystem checks and adjustments
 	if resources.BlkioWeight > 0 && !sysInfo.BlkioWeight {
b7599d58
 		warnings = append(warnings, "Your kernel does not support Block I/O weight. Weight discarded.")
 		logrus.Warnf("Your kernel does not support Block I/O weight. Weight discarded.")
c6bfb54a
 		resources.BlkioWeight = 0
b7599d58
 	}
c6bfb54a
 	if resources.BlkioWeight > 0 && (resources.BlkioWeight < 10 || resources.BlkioWeight > 1000) {
8fb0ca2c
 		return warnings, fmt.Errorf("Range of blkio weight is from 10 to 1000.")
 	}
c6bfb54a
 	if len(resources.BlkioWeightDevice) > 0 && !sysInfo.BlkioWeightDevice {
0fbfa144
 		warnings = append(warnings, "Your kernel does not support Block I/O weight_device.")
 		logrus.Warnf("Your kernel does not support Block I/O weight_device. Weight-device discarded.")
c6bfb54a
 		resources.BlkioWeightDevice = []*pblkiodev.WeightDevice{}
0fbfa144
 	}
c6bfb54a
 	if len(resources.BlkioDeviceReadBps) > 0 && !sysInfo.BlkioReadBpsDevice {
3f15a055
 		warnings = append(warnings, "Your kernel does not support Block read limit in bytes per second.")
 		logrus.Warnf("Your kernel does not support Block I/O read limit in bytes per second. --device-read-bps discarded.")
c6bfb54a
 		resources.BlkioDeviceReadBps = []*pblkiodev.ThrottleDevice{}
3f15a055
 	}
c6bfb54a
 	if len(resources.BlkioDeviceWriteBps) > 0 && !sysInfo.BlkioWriteBpsDevice {
3f15a055
 		warnings = append(warnings, "Your kernel does not support Block write limit in bytes per second.")
 		logrus.Warnf("Your kernel does not support Block I/O write limit in bytes per second. --device-write-bps discarded.")
c6bfb54a
 		resources.BlkioDeviceWriteBps = []*pblkiodev.ThrottleDevice{}
3f15a055
 	}
843084b0
 	if len(resources.BlkioDeviceReadIOps) > 0 && !sysInfo.BlkioReadIOpsDevice {
 		warnings = append(warnings, "Your kernel does not support Block read limit in IO per second.")
 		logrus.Warnf("Your kernel does not support Block I/O read limit in IO per second. -device-read-iops discarded.")
 		resources.BlkioDeviceReadIOps = []*pblkiodev.ThrottleDevice{}
 	}
 	if len(resources.BlkioDeviceWriteIOps) > 0 && !sysInfo.BlkioWriteIOpsDevice {
 		warnings = append(warnings, "Your kernel does not support Block write limit in IO per second.")
 		logrus.Warnf("Your kernel does not support Block I/O write limit in IO per second. --device-write-iops discarded.")
 		resources.BlkioDeviceWriteIOps = []*pblkiodev.ThrottleDevice{}
 	}
c6bfb54a
 
 	return warnings, nil
 }
 
ca89c329
 func (daemon *Daemon) getCgroupDriver() string {
8af4f89c
 	cgroupDriver := cgroupFsDriver
ca89c329
 
f97f3e98
 	if UsingSystemd(daemon.configStore) {
 		cgroupDriver = cgroupSystemdDriver
 	}
 	return cgroupDriver
 }
 
 // getCD gets the raw value of the native.cgroupdriver option, if set.
 func getCD(config *Config) string {
 	for _, option := range config.ExecOptions {
5ce5a8e9
 		key, val, err := parsers.ParseKeyValueOpt(option)
 		if err != nil || !strings.EqualFold(key, "native.cgroupdriver") {
 			continue
 		}
f97f3e98
 		return val
5ce5a8e9
 	}
f97f3e98
 	return ""
8af4f89c
 }
 
f97f3e98
 // VerifyCgroupDriver validates native.cgroupdriver
 func VerifyCgroupDriver(config *Config) error {
 	cd := getCD(config)
 	if cd == "" || cd == cgroupFsDriver || cd == cgroupSystemdDriver {
 		return nil
 	}
 	return fmt.Errorf("native.cgroupdriver option %s not supported", cd)
5ce5a8e9
 }
 
f97f3e98
 // UsingSystemd returns true if cli option includes native.cgroupdriver=systemd
 func UsingSystemd(config *Config) bool {
 	return getCD(config) == cgroupSystemdDriver
5ce5a8e9
 }
 
c6bfb54a
 // verifyPlatformContainerSettings performs platform-specific validation of the
 // hostconfig and config structures.
8ae6f6ac
 func verifyPlatformContainerSettings(daemon *Daemon, hostConfig *containertypes.HostConfig, config *containertypes.Config, update bool) ([]string, error) {
c6bfb54a
 	warnings := []string{}
 	sysInfo := sysinfo.New(true)
 
 	warnings, err := daemon.verifyExperimentalContainerSettings(hostConfig, config)
 	if err != nil {
 		return warnings, err
 	}
 
8ae6f6ac
 	w, err := verifyContainerResources(&hostConfig.Resources, sysInfo, update)
c6bfb54a
 	if err != nil {
 		return warnings, err
 	}
 	warnings = append(warnings, w...)
 
5190794f
 	if hostConfig.ShmSize < 0 {
c6bfb54a
 		return warnings, fmt.Errorf("SHM size must be greater then 0")
 	}
 
d3af7f28
 	if hostConfig.OomScoreAdj < -1000 || hostConfig.OomScoreAdj > 1000 {
 		return warnings, fmt.Errorf("Invalid value %d, range for oom score adj is [-1000, 1000].", hostConfig.OomScoreAdj)
 	}
b2d06b6f
 	if sysInfo.IPv4ForwardingDisabled {
8fb0ca2c
 		warnings = append(warnings, "IPv4 forwarding is disabled. Networking will not work.")
 		logrus.Warnf("IPv4 forwarding is disabled. Networking will not work")
 	}
d5743a3a
 	// check for various conflicting options with user namespaces
6993e891
 	if daemon.configStore.RemappedRoot != "" && hostConfig.UsernsMode.IsPrivate() {
d5743a3a
 		if hostConfig.Privileged {
858f852d
 			return warnings, fmt.Errorf("Privileged mode is incompatible with user namespaces")
d5743a3a
 		}
2b278f48
 		if hostConfig.NetworkMode.IsHost() {
 			return warnings, fmt.Errorf("Cannot share the host's network namespace when user namespaces are enabled")
d5743a3a
 		}
 		if hostConfig.PidMode.IsHost() {
858f852d
 			return warnings, fmt.Errorf("Cannot share the host PID namespace when user namespaces are enabled")
d5743a3a
 		}
 		if hostConfig.ReadonlyRootfs {
858f852d
 			return warnings, fmt.Errorf("Cannot use the --read-only option when user namespaces are enabled")
d5743a3a
 		}
557c7cb8
 	}
f97f3e98
 	if hostConfig.CgroupParent != "" && UsingSystemd(daemon.configStore) {
5ce5a8e9
 		// CgroupParent for systemd cgroup should be named as "xxx.slice"
 		if len(hostConfig.CgroupParent) <= 6 || !strings.HasSuffix(hostConfig.CgroupParent, ".slice") {
 			return warnings, fmt.Errorf("cgroup-parent for systemd cgroup should be a valid slice named as \"xxx.slice\"")
 		}
 	}
8fb0ca2c
 	return warnings, nil
 }
 
5ce5a8e9
 // verifyDaemonSettings performs validation of daemon config struct
 func verifyDaemonSettings(config *Config) error {
8fb0ca2c
 	// Check for mutually incompatible config options
c539be88
 	if config.bridgeConfig.Iface != "" && config.bridgeConfig.IP != "" {
858f852d
 		return fmt.Errorf("You specified -b & --bip, mutually exclusive options. Please specify only one")
8fb0ca2c
 	}
c539be88
 	if !config.bridgeConfig.EnableIPTables && !config.bridgeConfig.InterContainerCommunication {
858f852d
 		return fmt.Errorf("You specified --iptables=false with --icc=false. ICC=false uses iptables to function. Please set --icc or --iptables to true")
8fb0ca2c
 	}
c539be88
 	if !config.bridgeConfig.EnableIPTables && config.bridgeConfig.EnableIPMasq {
 		config.bridgeConfig.EnableIPMasq = false
8fb0ca2c
 	}
f97f3e98
 	if err := VerifyCgroupDriver(config); err != nil {
 		return err
 	}
 	if config.CgroupParent != "" && UsingSystemd(config) {
5ce5a8e9
 		if len(config.CgroupParent) <= 6 || !strings.HasSuffix(config.CgroupParent, ".slice") {
 			return fmt.Errorf("cgroup-parent for systemd cgroup should be a valid slice named as \"xxx.slice\"")
 		}
 	}
8fb0ca2c
 	return nil
 }
 
62a75fca
 // checkSystem validates platform-specific requirements
8fb0ca2c
 func checkSystem() error {
 	if os.Geteuid() != 0 {
 		return fmt.Errorf("The Docker daemon needs to be run as root")
 	}
94464e3a
 	return checkKernel()
8fb0ca2c
 }
 
140a7434
 // configureMaxThreads sets the Go runtime max threads threshold
 // which is 90% of the kernel setting from /proc/sys/kernel/threads-max
 func configureMaxThreads(config *Config) error {
 	mt, err := ioutil.ReadFile("/proc/sys/kernel/threads-max")
 	if err != nil {
 		return err
 	}
 	mtint, err := strconv.Atoi(strings.TrimSpace(string(mt)))
 	if err != nil {
 		return err
 	}
 	maxThreads := (mtint / 100) * 90
 	debug.SetMaxThreads(maxThreads)
 	logrus.Debugf("Golang's threads limit set to %d", maxThreads)
 	return nil
 }
 
8fb0ca2c
 // configureKernelSecuritySupport configures and validate security support for the kernel
 func configureKernelSecuritySupport(config *Config, driverName string) error {
 	if config.EnableSelinuxSupport {
 		if selinuxEnabled() {
1716d497
 			// As Docker on overlayFS and SELinux are incompatible at present, error on overlayfs being enabled
 			if driverName == "overlay" {
04329e0b
 				return fmt.Errorf("SELinux is not supported with the %s graph driver", driverName)
8fb0ca2c
 			}
 			logrus.Debug("SELinux enabled successfully")
 		} else {
 			logrus.Warn("Docker could not enable SELinux on the host system")
 		}
 	} else {
 		selinuxSetDisabled()
 	}
 	return nil
 }
 
139ea5b7
 func (daemon *Daemon) initNetworkController(config *Config) (libnetwork.NetworkController, error) {
 	netOptions, err := daemon.networkOptions(config)
da5a3e6d
 	if err != nil {
 		return nil, err
 	}
 
 	controller, err := libnetwork.New(netOptions...)
8fb0ca2c
 	if err != nil {
 		return nil, fmt.Errorf("error obtaining controller instance: %v", err)
 	}
 
 	// Initialize default network on "null"
6db15920
 	if _, err := controller.NewNetwork("null", "none", libnetwork.NetworkOptionPersist(false)); err != nil {
8fb0ca2c
 		return nil, fmt.Errorf("Error creating default \"null\" network: %v", err)
 	}
 
 	// Initialize default network on "host"
6db15920
 	if _, err := controller.NewNetwork("host", "host", libnetwork.NetworkOptionPersist(false)); err != nil {
8fb0ca2c
 		return nil, fmt.Errorf("Error creating default \"host\" network: %v", err)
 	}
 
c9328c6c
 	if !config.DisableBridge {
 		// Initialize default driver "bridge"
 		if err := initBridgeDriver(controller, config); err != nil {
 			return nil, err
 		}
 	}
 
 	return controller, nil
 }
 
6db15920
 func driverOptions(config *Config) []nwconfig.Option {
 	bridgeConfig := options.Generic{
c539be88
 		"EnableIPForwarding":  config.bridgeConfig.EnableIPForward,
 		"EnableIPTables":      config.bridgeConfig.EnableIPTables,
 		"EnableUserlandProxy": config.bridgeConfig.EnableUserlandProxy}
6db15920
 	bridgeOption := options.Generic{netlabel.GenericData: bridgeConfig}
8fb0ca2c
 
6db15920
 	dOptions := []nwconfig.Option{}
 	dOptions = append(dOptions, nwconfig.OptionDriverConfig("bridge", bridgeOption))
 	return dOptions
 }
8fb0ca2c
 
6db15920
 func initBridgeDriver(controller libnetwork.NetworkController, config *Config) error {
0f351ce3
 	if n, err := controller.NetworkByName("bridge"); err == nil {
 		if err = n.Delete(); err != nil {
 			return fmt.Errorf("could not delete the default bridge network: %v", err)
 		}
 	}
 
 	bridgeName := bridge.DefaultBridgeName
c539be88
 	if config.bridgeConfig.Iface != "" {
 		bridgeName = config.bridgeConfig.Iface
0f351ce3
 	}
 	netOption := map[string]string{
 		bridge.BridgeName:         bridgeName,
 		bridge.DefaultBridge:      strconv.FormatBool(true),
 		netlabel.DriverMTU:        strconv.Itoa(config.Mtu),
c539be88
 		bridge.EnableIPMasquerade: strconv.FormatBool(config.bridgeConfig.EnableIPMasq),
 		bridge.EnableICC:          strconv.FormatBool(config.bridgeConfig.InterContainerCommunication),
0f351ce3
 	}
 
 	// --ip processing
c539be88
 	if config.bridgeConfig.DefaultIP != nil {
 		netOption[bridge.DefaultBindingIP] = config.bridgeConfig.DefaultIP.String()
0f351ce3
 	}
 
cfa3682c
 	var (
 		ipamV4Conf *libnetwork.IpamConf
 		ipamV6Conf *libnetwork.IpamConf
 	)
0f351ce3
 
cfa3682c
 	ipamV4Conf = &libnetwork.IpamConf{AuxAddresses: make(map[string]string)}
0f351ce3
 
cfa3682c
 	nw, nw6List, err := ipamutils.ElectInterfaceAddresses(bridgeName)
 	if err == nil {
9c4570a9
 		ipamV4Conf.PreferredPool = lntypes.GetIPNetCanonical(nw).String()
 		hip, _ := lntypes.GetHostPartIP(nw.IP, nw.Mask)
0f351ce3
 		if hip.IsGlobalUnicast() {
 			ipamV4Conf.Gateway = nw.IP.String()
 		}
8fb0ca2c
 	}
 
c539be88
 	if config.bridgeConfig.IP != "" {
 		ipamV4Conf.PreferredPool = config.bridgeConfig.IP
 		ip, _, err := net.ParseCIDR(config.bridgeConfig.IP)
8fb0ca2c
 		if err != nil {
c9328c6c
 			return err
8fb0ca2c
 		}
0f351ce3
 		ipamV4Conf.Gateway = ip.String()
126d1b6c
 	} else if bridgeName == bridge.DefaultBridgeName && ipamV4Conf.PreferredPool != "" {
 		logrus.Infof("Default bridge (%s) is assigned with an IP address %s. Daemon option --bip can be used to set a preferred IP address", bridgeName, ipamV4Conf.PreferredPool)
8fb0ca2c
 	}
 
c539be88
 	if config.bridgeConfig.FixedCIDR != "" {
 		_, fCIDR, err := net.ParseCIDR(config.bridgeConfig.FixedCIDR)
8fb0ca2c
 		if err != nil {
c9328c6c
 			return err
8fb0ca2c
 		}
 
0f351ce3
 		ipamV4Conf.SubPool = fCIDR.String()
8fb0ca2c
 	}
 
c539be88
 	if config.bridgeConfig.DefaultGatewayIPv4 != nil {
 		ipamV4Conf.AuxAddresses["DefaultGatewayIPv4"] = config.bridgeConfig.DefaultGatewayIPv4.String()
0f351ce3
 	}
 
cfa3682c
 	var deferIPv6Alloc bool
c539be88
 	if config.bridgeConfig.FixedCIDRv6 != "" {
 		_, fCIDRv6, err := net.ParseCIDR(config.bridgeConfig.FixedCIDRv6)
8fb0ca2c
 		if err != nil {
c9328c6c
 			return err
8fb0ca2c
 		}
095a8ac5
 
 		// In case user has specified the daemon flag --fixed-cidr-v6 and the passed network has
 		// at least 48 host bits, we need to guarantee the current behavior where the containers'
 		// IPv6 addresses will be constructed based on the containers' interface MAC address.
 		// We do so by telling libnetwork to defer the IPv6 address allocation for the endpoints
 		// on this network until after the driver has created the endpoint and returned the
 		// constructed address. Libnetwork will then reserve this address with the ipam driver.
 		ones, _ := fCIDRv6.Mask.Size()
 		deferIPv6Alloc = ones <= 80
 
0f351ce3
 		if ipamV6Conf == nil {
aa97eee1
 			ipamV6Conf = &libnetwork.IpamConf{AuxAddresses: make(map[string]string)}
0f351ce3
 		}
 		ipamV6Conf.PreferredPool = fCIDRv6.String()
cfa3682c
 
 		// In case the --fixed-cidr-v6 is specified and the current docker0 bridge IPv6
 		// address belongs to the same network, we need to inform libnetwork about it, so
 		// that it can be reserved with IPAM and it will not be given away to somebody else
 		for _, nw6 := range nw6List {
 			if fCIDRv6.Contains(nw6.IP) {
 				ipamV6Conf.Gateway = nw6.IP.String()
 				break
 			}
 		}
8fb0ca2c
 	}
 
c539be88
 	if config.bridgeConfig.DefaultGatewayIPv6 != nil {
0f351ce3
 		if ipamV6Conf == nil {
aa97eee1
 			ipamV6Conf = &libnetwork.IpamConf{AuxAddresses: make(map[string]string)}
0f351ce3
 		}
c539be88
 		ipamV6Conf.AuxAddresses["DefaultGatewayIPv6"] = config.bridgeConfig.DefaultGatewayIPv6.String()
8fb0ca2c
 	}
 
cfa3682c
 	v4Conf := []*libnetwork.IpamConf{ipamV4Conf}
0f351ce3
 	v6Conf := []*libnetwork.IpamConf{}
 	if ipamV6Conf != nil {
 		v6Conf = append(v6Conf, ipamV6Conf)
8fb0ca2c
 	}
 	// Initialize default network on "bridge" with the same name
cfa3682c
 	_, err = controller.NewNetwork("bridge", "bridge",
dfb00652
 		libnetwork.NetworkOptionEnableIPv6(config.bridgeConfig.EnableIPv6),
 		libnetwork.NetworkOptionDriverOpts(netOption),
64a6dc35
 		libnetwork.NetworkOptionIpam("default", "", v4Conf, v6Conf, nil),
095a8ac5
 		libnetwork.NetworkOptionDeferIPv6Alloc(deferIPv6Alloc))
8fb0ca2c
 	if err != nil {
c9328c6c
 		return fmt.Errorf("Error creating default \"bridge\" network: %v", err)
8fb0ca2c
 	}
c9328c6c
 	return nil
8fb0ca2c
 }
b7f887a9
 
 // setupInitLayer populates a directory with mountpoints suitable
4357ed4a
 // for bind-mounting things into the container.
b7f887a9
 //
 // This extra layer is used by all containers as the top-most ro layer. It protects
 // the container from unwanted side-effects on the rw layer.
442b4562
 func setupInitLayer(initLayer string, rootUID, rootGID int) error {
b7f887a9
 	for pth, typ := range map[string]string{
 		"/dev/pts":         "dir",
 		"/dev/shm":         "dir",
 		"/proc":            "dir",
 		"/sys":             "dir",
 		"/.dockerenv":      "file",
 		"/etc/resolv.conf": "file",
 		"/etc/hosts":       "file",
 		"/etc/hostname":    "file",
 		"/dev/console":     "file",
 		"/etc/mtab":        "/proc/mounts",
 	} {
 		parts := strings.Split(pth, "/")
 		prev := "/"
 		for _, p := range parts[1:] {
 			prev = filepath.Join(prev, p)
 			syscall.Unlink(filepath.Join(initLayer, prev))
 		}
 
 		if _, err := os.Stat(filepath.Join(initLayer, pth)); err != nil {
 			if os.IsNotExist(err) {
23b77178
 				if err := idtools.MkdirAllNewAs(filepath.Join(initLayer, filepath.Dir(pth)), 0755, rootUID, rootGID); err != nil {
b7f887a9
 					return err
 				}
 				switch typ {
 				case "dir":
23b77178
 					if err := idtools.MkdirAllNewAs(filepath.Join(initLayer, pth), 0755, rootUID, rootGID); err != nil {
b7f887a9
 						return err
 					}
 				case "file":
 					f, err := os.OpenFile(filepath.Join(initLayer, pth), os.O_CREATE, 0755)
 					if err != nil {
 						return err
 					}
442b4562
 					f.Chown(rootUID, rootGID)
23b77178
 					f.Close()
b7f887a9
 				default:
 					if err := os.Symlink(typ, filepath.Join(initLayer, pth)); err != nil {
 						return err
 					}
 				}
 			} else {
 				return err
 			}
 		}
 	}
 
 	// Layer is ready to use, if it wasn't before.
 	return nil
 }
da5a3e6d
 
557c7cb8
 // Parse the remapped root (user namespace) option, which can be one of:
 //   username            - valid username from /etc/passwd
 //   username:groupname  - valid username; valid groupname from /etc/group
 //   uid                 - 32-bit unsigned int valid Linux UID value
 //   uid:gid             - uid value; 32-bit unsigned int Linux GID value
 //
 //  If no groupname is specified, and a username is specified, an attempt
 //  will be made to lookup a gid for that username as a groupname
 //
 //  If names are used, they are verified to exist in passwd/group
 func parseRemappedRoot(usergrp string) (string, string, error) {
 
 	var (
 		userID, groupID     int
 		username, groupname string
 	)
 
 	idparts := strings.Split(usergrp, ":")
 	if len(idparts) > 2 {
 		return "", "", fmt.Errorf("Invalid user/group specification in --userns-remap: %q", usergrp)
 	}
 
 	if uid, err := strconv.ParseInt(idparts[0], 10, 32); err == nil {
 		// must be a uid; take it as valid
 		userID = int(uid)
 		luser, err := user.LookupUid(userID)
 		if err != nil {
 			return "", "", fmt.Errorf("Uid %d has no entry in /etc/passwd: %v", userID, err)
 		}
 		username = luser.Name
 		if len(idparts) == 1 {
 			// if the uid was numeric and no gid was specified, take the uid as the gid
 			groupID = userID
 			lgrp, err := user.LookupGid(groupID)
 			if err != nil {
 				return "", "", fmt.Errorf("Gid %d has no entry in /etc/group: %v", groupID, err)
 			}
 			groupname = lgrp.Name
 		}
 	} else {
 		lookupName := idparts[0]
 		// special case: if the user specified "default", they want Docker to create or
 		// use (after creation) the "dockremap" user/group for root remapping
 		if lookupName == defaultIDSpecifier {
 			lookupName = defaultRemappedID
 		}
 		luser, err := user.LookupUser(lookupName)
 		if err != nil && idparts[0] != defaultIDSpecifier {
 			// error if the name requested isn't the special "dockremap" ID
 			return "", "", fmt.Errorf("Error during uid lookup for %q: %v", lookupName, err)
 		} else if err != nil {
 			// special case-- if the username == "default", then we have been asked
 			// to create a new entry pair in /etc/{passwd,group} for which the /etc/sub{uid,gid}
 			// ranges will be used for the user and group mappings in user namespaced containers
 			_, _, err := idtools.AddNamespaceRangesUser(defaultRemappedID)
 			if err == nil {
 				return defaultRemappedID, defaultRemappedID, nil
 			}
 			return "", "", fmt.Errorf("Error during %q user creation: %v", defaultRemappedID, err)
 		}
 		username = luser.Name
 		if len(idparts) == 1 {
 			// we only have a string username, and no group specified; look up gid from username as group
 			group, err := user.LookupGroup(lookupName)
 			if err != nil {
 				return "", "", fmt.Errorf("Error during gid lookup for %q: %v", lookupName, err)
 			}
 			groupID = group.Gid
 			groupname = group.Name
 		}
 	}
 
 	if len(idparts) == 2 {
 		// groupname or gid is separately specified and must be resolved
 		// to a unsigned 32-bit gid
 		if gid, err := strconv.ParseInt(idparts[1], 10, 32); err == nil {
 			// must be a gid, take it as valid
 			groupID = int(gid)
 			lgrp, err := user.LookupGid(groupID)
 			if err != nil {
 				return "", "", fmt.Errorf("Gid %d has no entry in /etc/passwd: %v", groupID, err)
 			}
 			groupname = lgrp.Name
 		} else {
 			// not a number; attempt a lookup
0e025b4b
 			if _, err := user.LookupGroup(idparts[1]); err != nil {
 				return "", "", fmt.Errorf("Error during groupname lookup for %q: %v", idparts[1], err)
557c7cb8
 			}
 			groupname = idparts[1]
 		}
 	}
 	return username, groupname, nil
 }
 
 func setupRemappedRoot(config *Config) ([]idtools.IDMap, []idtools.IDMap, error) {
 	if runtime.GOOS != "linux" && config.RemappedRoot != "" {
 		return nil, nil, fmt.Errorf("User namespaces are only supported on Linux")
 	}
 
 	// if the daemon was started with remapped root option, parse
 	// the config option to the int uid,gid values
 	var (
 		uidMaps, gidMaps []idtools.IDMap
 	)
 	if config.RemappedRoot != "" {
 		username, groupname, err := parseRemappedRoot(config.RemappedRoot)
 		if err != nil {
 			return nil, nil, err
 		}
 		if username == "root" {
 			// Cannot setup user namespaces with a 1-to-1 mapping; "--root=0:0" is a no-op
 			// effectively
 			logrus.Warnf("User namespaces: root cannot be remapped with itself; user namespaces are OFF")
 			return uidMaps, gidMaps, nil
 		}
 		logrus.Infof("User namespaces: ID ranges will be mapped to subuid/subgid ranges of: %s:%s", username, groupname)
 		// update remapped root setting now that we have resolved them to actual names
 		config.RemappedRoot = fmt.Sprintf("%s:%s", username, groupname)
 
 		uidMaps, gidMaps, err = idtools.CreateIDMappings(username, groupname)
 		if err != nil {
 			return nil, nil, fmt.Errorf("Can't create ID mappings: %v", err)
 		}
 	}
 	return uidMaps, gidMaps, nil
 }
 
 func setupDaemonRoot(config *Config, rootDir string, rootUID, rootGID int) error {
 	config.Root = rootDir
e91ca0e2
 	// the docker root metadata directory needs to have execute permissions for all users (g+x,o+x)
557c7cb8
 	// so that syscalls executing as non-root, operating on subdirectories of the graph root
 	// (e.g. mounted layers of a container) can traverse this path.
 	// The user namespace support will create subdirectories for the remapped root host uid:gid
 	// pair owned by that same uid:gid pair for proper write access to those needed metadata and
 	// layer content subtrees.
 	if _, err := os.Stat(rootDir); err == nil {
 		// root current exists; verify the access bits are correct by setting them
e91ca0e2
 		if err = os.Chmod(rootDir, 0711); err != nil {
557c7cb8
 			return err
 		}
 	} else if os.IsNotExist(err) {
e91ca0e2
 		// no root exists yet, create it 0711 with root:root ownership
 		if err := os.MkdirAll(rootDir, 0711); err != nil {
557c7cb8
 			return err
 		}
 	}
 
 	// if user namespaces are enabled we will create a subtree underneath the specified root
 	// with any/all specified remapped root uid/gid options on the daemon creating
 	// a new subdirectory with ownership set to the remapped uid/gid (so as to allow
 	// `chdir()` to work for containers namespaced to that uid/gid)
 	if config.RemappedRoot != "" {
 		config.Root = filepath.Join(rootDir, fmt.Sprintf("%d.%d", rootUID, rootGID))
 		logrus.Debugf("Creating user namespaced daemon root: %s", config.Root)
 		// Create the root directory if it doesn't exists
 		if err := idtools.MkdirAllAs(config.Root, 0700, rootUID, rootGID); err != nil {
 			return fmt.Errorf("Cannot create daemon root: %s: %v", config.Root, err)
 		}
 	}
 	return nil
 }
 
abd72d40
 // registerLinks writes the links to a file.
7ac4232e
 func (daemon *Daemon) registerLinks(container *container.Container, hostConfig *containertypes.HostConfig) error {
e221b8a3
 	if hostConfig == nil || hostConfig.NetworkMode.IsUserDefined() {
c5e6a4b3
 		return nil
 	}
 
 	for _, l := range hostConfig.Links {
2b7ad47b
 		name, alias, err := runconfigopts.ParseLink(l)
c5e6a4b3
 		if err != nil {
 			return err
 		}
d7d512bb
 		child, err := daemon.GetContainer(name)
c5e6a4b3
 		if err != nil {
d7d512bb
 			//An error from daemon.GetContainer() means this name could not be found
c5e6a4b3
 			return fmt.Errorf("Could not get container for %s", name)
 		}
6bb0d181
 		for child.HostConfig.NetworkMode.IsContainer() {
 			parts := strings.SplitN(string(child.HostConfig.NetworkMode), ":", 2)
d7d512bb
 			child, err = daemon.GetContainer(parts[1])
c5e6a4b3
 			if err != nil {
 				return fmt.Errorf("Could not get container for %s", parts[1])
 			}
 		}
6bb0d181
 		if child.HostConfig.NetworkMode.IsHost() {
c5e6a4b3
 			return runconfig.ErrConflictHostNetworkAndLinks
 		}
abd72d40
 		if err := daemon.registerLink(container, child, alias); err != nil {
c5e6a4b3
 			return err
 		}
 	}
 
 	// After we load all the links into the daemon
 	// set them to nil on the hostconfig
0f9f9950
 	return container.WriteHostConfig()
c5e6a4b3
 }
47c56e43
 
3a497650
 // conditionalMountOnStart is a platform specific helper function during the
 // container start to call mount.
6bb0d181
 func (daemon *Daemon) conditionalMountOnStart(container *container.Container) error {
3a497650
 	return daemon.Mount(container)
 }
 
 // conditionalUnmountOnCleanup is a platform specific helper function called
 // during the cleanup of a container to unmount.
9c4570a9
 func (daemon *Daemon) conditionalUnmountOnCleanup(container *container.Container) error {
 	return daemon.Unmount(container)
3a497650
 }
 
f5916b10
 func restoreCustomImage(is image.Store, ls layer.Store, rs reference.Store) error {
4352da78
 	// Unix has no custom images to register
 	return nil
 }
9c4570a9
 
 func (daemon *Daemon) stats(c *container.Container) (*types.StatsJSON, error) {
 	if !c.IsRunning() {
 		return nil, errNotRunning{c.ID}
 	}
 	stats, err := daemon.containerd.Stats(c.ID)
 	if err != nil {
 		return nil, err
 	}
 	s := &types.StatsJSON{}
 	cgs := stats.CgroupStats
 	if cgs != nil {
 		s.BlkioStats = types.BlkioStats{
 			IoServiceBytesRecursive: copyBlkioEntry(cgs.BlkioStats.IoServiceBytesRecursive),
 			IoServicedRecursive:     copyBlkioEntry(cgs.BlkioStats.IoServicedRecursive),
 			IoQueuedRecursive:       copyBlkioEntry(cgs.BlkioStats.IoQueuedRecursive),
 			IoServiceTimeRecursive:  copyBlkioEntry(cgs.BlkioStats.IoServiceTimeRecursive),
 			IoWaitTimeRecursive:     copyBlkioEntry(cgs.BlkioStats.IoWaitTimeRecursive),
 			IoMergedRecursive:       copyBlkioEntry(cgs.BlkioStats.IoMergedRecursive),
 			IoTimeRecursive:         copyBlkioEntry(cgs.BlkioStats.IoTimeRecursive),
 			SectorsRecursive:        copyBlkioEntry(cgs.BlkioStats.SectorsRecursive),
 		}
 		cpu := cgs.CpuStats
 		s.CPUStats = types.CPUStats{
 			CPUUsage: types.CPUUsage{
 				TotalUsage:        cpu.CpuUsage.TotalUsage,
 				PercpuUsage:       cpu.CpuUsage.PercpuUsage,
 				UsageInKernelmode: cpu.CpuUsage.UsageInKernelmode,
 				UsageInUsermode:   cpu.CpuUsage.UsageInUsermode,
 			},
 			ThrottlingData: types.ThrottlingData{
 				Periods:          cpu.ThrottlingData.Periods,
 				ThrottledPeriods: cpu.ThrottlingData.ThrottledPeriods,
 				ThrottledTime:    cpu.ThrottlingData.ThrottledTime,
 			},
 		}
 		mem := cgs.MemoryStats.Usage
 		s.MemoryStats = types.MemoryStats{
 			Usage:    mem.Usage,
 			MaxUsage: mem.MaxUsage,
 			Stats:    cgs.MemoryStats.Stats,
 			Failcnt:  mem.Failcnt,
eb405d2b
 			Limit:    mem.Limit,
 		}
 		// if the container does not set memory limit, use the machineMemory
 		if mem.Limit > daemon.statsCollector.machineMemory && daemon.statsCollector.machineMemory > 0 {
 			s.MemoryStats.Limit = daemon.statsCollector.machineMemory
9c4570a9
 		}
 		if cgs.PidsStats != nil {
 			s.PidsStats = types.PidsStats{
 				Current: cgs.PidsStats.Current,
 			}
 		}
 	}
 	s.Read = time.Unix(int64(stats.Timestamp), 0)
 	return s, nil
 }
 
 // setDefaultIsolation determine the default isolation mode for the
 // daemon to run in. This is only applicable on Windows
 func (daemon *Daemon) setDefaultIsolation() error {
 	return nil
 }
14dc4a71
 
 func rootFSToAPIType(rootfs *image.RootFS) types.RootFS {
 	var layers []string
 	for _, l := range rootfs.DiffIDs {
 		layers = append(layers, l.String())
 	}
 	return types.RootFS{
 		Type:   rootfs.Type,
 		Layers: layers,
 	}
 }