github.com/portworx/docker@v1.12.1/daemon/oci_linux.go (about)

     1  package daemon
     2  
     3  import (
     4  	"fmt"
     5  	"io"
     6  	"os"
     7  	"path/filepath"
     8  	"sort"
     9  	"strconv"
    10  	"strings"
    11  
    12  	"github.com/Sirupsen/logrus"
    13  	"github.com/docker/docker/container"
    14  	"github.com/docker/docker/daemon/caps"
    15  	"github.com/docker/docker/libcontainerd"
    16  	"github.com/docker/docker/oci"
    17  	"github.com/docker/docker/pkg/idtools"
    18  	"github.com/docker/docker/pkg/mount"
    19  	"github.com/docker/docker/pkg/stringutils"
    20  	"github.com/docker/docker/pkg/symlink"
    21  	"github.com/docker/docker/volume"
    22  	containertypes "github.com/docker/engine-api/types/container"
    23  	"github.com/opencontainers/runc/libcontainer/apparmor"
    24  	"github.com/opencontainers/runc/libcontainer/devices"
    25  	"github.com/opencontainers/runc/libcontainer/user"
    26  	"github.com/opencontainers/specs/specs-go"
    27  )
    28  
    29  func setResources(s *specs.Spec, r containertypes.Resources) error {
    30  	weightDevices, err := getBlkioWeightDevices(r)
    31  	if err != nil {
    32  		return err
    33  	}
    34  	readBpsDevice, err := getBlkioThrottleDevices(r.BlkioDeviceReadBps)
    35  	if err != nil {
    36  		return err
    37  	}
    38  	writeBpsDevice, err := getBlkioThrottleDevices(r.BlkioDeviceWriteBps)
    39  	if err != nil {
    40  		return err
    41  	}
    42  	readIOpsDevice, err := getBlkioThrottleDevices(r.BlkioDeviceReadIOps)
    43  	if err != nil {
    44  		return err
    45  	}
    46  	writeIOpsDevice, err := getBlkioThrottleDevices(r.BlkioDeviceWriteIOps)
    47  	if err != nil {
    48  		return err
    49  	}
    50  
    51  	memoryRes := getMemoryResources(r)
    52  	cpuRes := getCPUResources(r)
    53  	blkioWeight := r.BlkioWeight
    54  
    55  	specResources := &specs.Resources{
    56  		Memory: memoryRes,
    57  		CPU:    cpuRes,
    58  		BlockIO: &specs.BlockIO{
    59  			Weight:                  &blkioWeight,
    60  			WeightDevice:            weightDevices,
    61  			ThrottleReadBpsDevice:   readBpsDevice,
    62  			ThrottleWriteBpsDevice:  writeBpsDevice,
    63  			ThrottleReadIOPSDevice:  readIOpsDevice,
    64  			ThrottleWriteIOPSDevice: writeIOpsDevice,
    65  		},
    66  		DisableOOMKiller: r.OomKillDisable,
    67  		Pids: &specs.Pids{
    68  			Limit: &r.PidsLimit,
    69  		},
    70  	}
    71  
    72  	if s.Linux.Resources != nil && len(s.Linux.Resources.Devices) > 0 {
    73  		specResources.Devices = s.Linux.Resources.Devices
    74  	}
    75  
    76  	s.Linux.Resources = specResources
    77  	return nil
    78  }
    79  
    80  func setDevices(s *specs.Spec, c *container.Container) error {
    81  	// Build lists of devices allowed and created within the container.
    82  	var devs []specs.Device
    83  	devPermissions := s.Linux.Resources.Devices
    84  	if c.HostConfig.Privileged {
    85  		hostDevices, err := devices.HostDevices()
    86  		if err != nil {
    87  			return err
    88  		}
    89  		for _, d := range hostDevices {
    90  			devs = append(devs, specDevice(d))
    91  		}
    92  		rwm := "rwm"
    93  		devPermissions = []specs.DeviceCgroup{
    94  			{
    95  				Allow:  true,
    96  				Access: &rwm,
    97  			},
    98  		}
    99  	} else {
   100  		for _, deviceMapping := range c.HostConfig.Devices {
   101  			d, dPermissions, err := getDevicesFromPath(deviceMapping)
   102  			if err != nil {
   103  				return err
   104  			}
   105  			devs = append(devs, d...)
   106  			devPermissions = append(devPermissions, dPermissions...)
   107  		}
   108  	}
   109  
   110  	s.Linux.Devices = append(s.Linux.Devices, devs...)
   111  	s.Linux.Resources.Devices = devPermissions
   112  	return nil
   113  }
   114  
   115  func setRlimits(daemon *Daemon, s *specs.Spec, c *container.Container) error {
   116  	var rlimits []specs.Rlimit
   117  
   118  	ulimits := c.HostConfig.Ulimits
   119  	// Merge ulimits with daemon defaults
   120  	ulIdx := make(map[string]struct{})
   121  	for _, ul := range ulimits {
   122  		ulIdx[ul.Name] = struct{}{}
   123  	}
   124  	for name, ul := range daemon.configStore.Ulimits {
   125  		if _, exists := ulIdx[name]; !exists {
   126  			ulimits = append(ulimits, ul)
   127  		}
   128  	}
   129  
   130  	for _, ul := range ulimits {
   131  		rlimits = append(rlimits, specs.Rlimit{
   132  			Type: "RLIMIT_" + strings.ToUpper(ul.Name),
   133  			Soft: uint64(ul.Soft),
   134  			Hard: uint64(ul.Hard),
   135  		})
   136  	}
   137  
   138  	s.Process.Rlimits = rlimits
   139  	return nil
   140  }
   141  
   142  func setUser(s *specs.Spec, c *container.Container) error {
   143  	uid, gid, additionalGids, err := getUser(c, c.Config.User)
   144  	if err != nil {
   145  		return err
   146  	}
   147  	s.Process.User.UID = uid
   148  	s.Process.User.GID = gid
   149  	s.Process.User.AdditionalGids = additionalGids
   150  	return nil
   151  }
   152  
   153  func readUserFile(c *container.Container, p string) (io.ReadCloser, error) {
   154  	fp, err := symlink.FollowSymlinkInScope(filepath.Join(c.BaseFS, p), c.BaseFS)
   155  	if err != nil {
   156  		return nil, err
   157  	}
   158  	return os.Open(fp)
   159  }
   160  
   161  func getUser(c *container.Container, username string) (uint32, uint32, []uint32, error) {
   162  	passwdPath, err := user.GetPasswdPath()
   163  	if err != nil {
   164  		return 0, 0, nil, err
   165  	}
   166  	groupPath, err := user.GetGroupPath()
   167  	if err != nil {
   168  		return 0, 0, nil, err
   169  	}
   170  	passwdFile, err := readUserFile(c, passwdPath)
   171  	if err == nil {
   172  		defer passwdFile.Close()
   173  	}
   174  	groupFile, err := readUserFile(c, groupPath)
   175  	if err == nil {
   176  		defer groupFile.Close()
   177  	}
   178  
   179  	execUser, err := user.GetExecUser(username, nil, passwdFile, groupFile)
   180  	if err != nil {
   181  		return 0, 0, nil, err
   182  	}
   183  
   184  	// todo: fix this double read by a change to libcontainer/user pkg
   185  	groupFile, err = readUserFile(c, groupPath)
   186  	if err == nil {
   187  		defer groupFile.Close()
   188  	}
   189  	var addGroups []int
   190  	if len(c.HostConfig.GroupAdd) > 0 {
   191  		addGroups, err = user.GetAdditionalGroups(c.HostConfig.GroupAdd, groupFile)
   192  		if err != nil {
   193  			return 0, 0, nil, err
   194  		}
   195  	}
   196  	uid := uint32(execUser.Uid)
   197  	gid := uint32(execUser.Gid)
   198  	sgids := append(execUser.Sgids, addGroups...)
   199  	var additionalGids []uint32
   200  	for _, g := range sgids {
   201  		additionalGids = append(additionalGids, uint32(g))
   202  	}
   203  	return uid, gid, additionalGids, nil
   204  }
   205  
   206  func setNamespace(s *specs.Spec, ns specs.Namespace) {
   207  	for i, n := range s.Linux.Namespaces {
   208  		if n.Type == ns.Type {
   209  			s.Linux.Namespaces[i] = ns
   210  			return
   211  		}
   212  	}
   213  	s.Linux.Namespaces = append(s.Linux.Namespaces, ns)
   214  }
   215  
   216  func setCapabilities(s *specs.Spec, c *container.Container) error {
   217  	var caplist []string
   218  	var err error
   219  	if c.HostConfig.Privileged {
   220  		caplist = caps.GetAllCapabilities()
   221  	} else {
   222  		caplist, err = caps.TweakCapabilities(s.Process.Capabilities, c.HostConfig.CapAdd, c.HostConfig.CapDrop)
   223  		if err != nil {
   224  			return err
   225  		}
   226  	}
   227  	s.Process.Capabilities = caplist
   228  	return nil
   229  }
   230  
   231  func delNamespace(s *specs.Spec, nsType specs.NamespaceType) {
   232  	idx := -1
   233  	for i, n := range s.Linux.Namespaces {
   234  		if n.Type == nsType {
   235  			idx = i
   236  		}
   237  	}
   238  	if idx >= 0 {
   239  		s.Linux.Namespaces = append(s.Linux.Namespaces[:idx], s.Linux.Namespaces[idx+1:]...)
   240  	}
   241  }
   242  
   243  func setNamespaces(daemon *Daemon, s *specs.Spec, c *container.Container) error {
   244  	userNS := false
   245  	// user
   246  	if c.HostConfig.UsernsMode.IsPrivate() {
   247  		uidMap, gidMap := daemon.GetUIDGIDMaps()
   248  		if uidMap != nil {
   249  			userNS = true
   250  			ns := specs.Namespace{Type: "user"}
   251  			setNamespace(s, ns)
   252  			s.Linux.UIDMappings = specMapping(uidMap)
   253  			s.Linux.GIDMappings = specMapping(gidMap)
   254  		}
   255  	}
   256  	// network
   257  	if !c.Config.NetworkDisabled {
   258  		ns := specs.Namespace{Type: "network"}
   259  		parts := strings.SplitN(string(c.HostConfig.NetworkMode), ":", 2)
   260  		if parts[0] == "container" {
   261  			nc, err := daemon.getNetworkedContainer(c.ID, c.HostConfig.NetworkMode.ConnectedContainer())
   262  			if err != nil {
   263  				return err
   264  			}
   265  			ns.Path = fmt.Sprintf("/proc/%d/ns/net", nc.State.GetPID())
   266  			if userNS {
   267  				// to share a net namespace, they must also share a user namespace
   268  				nsUser := specs.Namespace{Type: "user"}
   269  				nsUser.Path = fmt.Sprintf("/proc/%d/ns/user", nc.State.GetPID())
   270  				setNamespace(s, nsUser)
   271  			}
   272  		} else if c.HostConfig.NetworkMode.IsHost() {
   273  			ns.Path = c.NetworkSettings.SandboxKey
   274  		}
   275  		setNamespace(s, ns)
   276  	}
   277  	// ipc
   278  	if c.HostConfig.IpcMode.IsContainer() {
   279  		ns := specs.Namespace{Type: "ipc"}
   280  		ic, err := daemon.getIpcContainer(c)
   281  		if err != nil {
   282  			return err
   283  		}
   284  		ns.Path = fmt.Sprintf("/proc/%d/ns/ipc", ic.State.GetPID())
   285  		setNamespace(s, ns)
   286  		if userNS {
   287  			// to share an IPC namespace, they must also share a user namespace
   288  			nsUser := specs.Namespace{Type: "user"}
   289  			nsUser.Path = fmt.Sprintf("/proc/%d/ns/user", ic.State.GetPID())
   290  			setNamespace(s, nsUser)
   291  		}
   292  	} else if c.HostConfig.IpcMode.IsHost() {
   293  		delNamespace(s, specs.NamespaceType("ipc"))
   294  	} else {
   295  		ns := specs.Namespace{Type: "ipc"}
   296  		setNamespace(s, ns)
   297  	}
   298  	// pid
   299  	if c.HostConfig.PidMode.IsContainer() {
   300  		ns := specs.Namespace{Type: "pid"}
   301  		pc, err := daemon.getPidContainer(c)
   302  		if err != nil {
   303  			return err
   304  		}
   305  		ns.Path = fmt.Sprintf("/proc/%d/ns/pid", pc.State.GetPID())
   306  		setNamespace(s, ns)
   307  		if userNS {
   308  			// to share a PID namespace, they must also share a user namespace
   309  			nsUser := specs.Namespace{Type: "user"}
   310  			nsUser.Path = fmt.Sprintf("/proc/%d/ns/user", pc.State.GetPID())
   311  			setNamespace(s, nsUser)
   312  		}
   313  	} else if c.HostConfig.PidMode.IsHost() {
   314  		delNamespace(s, specs.NamespaceType("pid"))
   315  	} else {
   316  		ns := specs.Namespace{Type: "pid"}
   317  		setNamespace(s, ns)
   318  	}
   319  	// uts
   320  	if c.HostConfig.UTSMode.IsHost() {
   321  		delNamespace(s, specs.NamespaceType("uts"))
   322  		s.Hostname = ""
   323  	}
   324  
   325  	return nil
   326  }
   327  
   328  func specMapping(s []idtools.IDMap) []specs.IDMapping {
   329  	var ids []specs.IDMapping
   330  	for _, item := range s {
   331  		ids = append(ids, specs.IDMapping{
   332  			HostID:      uint32(item.HostID),
   333  			ContainerID: uint32(item.ContainerID),
   334  			Size:        uint32(item.Size),
   335  		})
   336  	}
   337  	return ids
   338  }
   339  
   340  func getMountInfo(mountinfo []*mount.Info, dir string) *mount.Info {
   341  	for _, m := range mountinfo {
   342  		if m.Mountpoint == dir {
   343  			return m
   344  		}
   345  	}
   346  	return nil
   347  }
   348  
   349  // Get the source mount point of directory passed in as argument. Also return
   350  // optional fields.
   351  func getSourceMount(source string) (string, string, error) {
   352  	// Ensure any symlinks are resolved.
   353  	sourcePath, err := filepath.EvalSymlinks(source)
   354  	if err != nil {
   355  		return "", "", err
   356  	}
   357  
   358  	mountinfos, err := mount.GetMounts()
   359  	if err != nil {
   360  		return "", "", err
   361  	}
   362  
   363  	mountinfo := getMountInfo(mountinfos, sourcePath)
   364  	if mountinfo != nil {
   365  		return sourcePath, mountinfo.Optional, nil
   366  	}
   367  
   368  	path := sourcePath
   369  	for {
   370  		path = filepath.Dir(path)
   371  
   372  		mountinfo = getMountInfo(mountinfos, path)
   373  		if mountinfo != nil {
   374  			return path, mountinfo.Optional, nil
   375  		}
   376  
   377  		if path == "/" {
   378  			break
   379  		}
   380  	}
   381  
   382  	// If we are here, we did not find parent mount. Something is wrong.
   383  	return "", "", fmt.Errorf("Could not find source mount of %s", source)
   384  }
   385  
   386  // Ensure mount point on which path is mounted, is shared.
   387  func ensureShared(path string) error {
   388  	sharedMount := false
   389  
   390  	sourceMount, optionalOpts, err := getSourceMount(path)
   391  	if err != nil {
   392  		return err
   393  	}
   394  	// Make sure source mount point is shared.
   395  	optsSplit := strings.Split(optionalOpts, " ")
   396  	for _, opt := range optsSplit {
   397  		if strings.HasPrefix(opt, "shared:") {
   398  			sharedMount = true
   399  			break
   400  		}
   401  	}
   402  
   403  	if !sharedMount {
   404  		return fmt.Errorf("Path %s is mounted on %s but it is not a shared mount.", path, sourceMount)
   405  	}
   406  	return nil
   407  }
   408  
   409  // Ensure mount point on which path is mounted, is either shared or slave.
   410  func ensureSharedOrSlave(path string) error {
   411  	sharedMount := false
   412  	slaveMount := false
   413  
   414  	sourceMount, optionalOpts, err := getSourceMount(path)
   415  	if err != nil {
   416  		return err
   417  	}
   418  	// Make sure source mount point is shared.
   419  	optsSplit := strings.Split(optionalOpts, " ")
   420  	for _, opt := range optsSplit {
   421  		if strings.HasPrefix(opt, "shared:") {
   422  			sharedMount = true
   423  			break
   424  		} else if strings.HasPrefix(opt, "master:") {
   425  			slaveMount = true
   426  			break
   427  		}
   428  	}
   429  
   430  	if !sharedMount && !slaveMount {
   431  		return fmt.Errorf("Path %s is mounted on %s but it is not a shared or slave mount.", path, sourceMount)
   432  	}
   433  	return nil
   434  }
   435  
   436  var (
   437  	mountPropagationMap = map[string]int{
   438  		"private":  mount.PRIVATE,
   439  		"rprivate": mount.RPRIVATE,
   440  		"shared":   mount.SHARED,
   441  		"rshared":  mount.RSHARED,
   442  		"slave":    mount.SLAVE,
   443  		"rslave":   mount.RSLAVE,
   444  	}
   445  
   446  	mountPropagationReverseMap = map[int]string{
   447  		mount.PRIVATE:  "private",
   448  		mount.RPRIVATE: "rprivate",
   449  		mount.SHARED:   "shared",
   450  		mount.RSHARED:  "rshared",
   451  		mount.SLAVE:    "slave",
   452  		mount.RSLAVE:   "rslave",
   453  	}
   454  )
   455  
   456  func setMounts(daemon *Daemon, s *specs.Spec, c *container.Container, mounts []container.Mount) error {
   457  	userMounts := make(map[string]struct{})
   458  	for _, m := range mounts {
   459  		userMounts[m.Destination] = struct{}{}
   460  	}
   461  
   462  	// Filter out mounts that are overridden by user supplied mounts
   463  	var defaultMounts []specs.Mount
   464  	_, mountDev := userMounts["/dev"]
   465  	for _, m := range s.Mounts {
   466  		if _, ok := userMounts[m.Destination]; !ok {
   467  			if mountDev && strings.HasPrefix(m.Destination, "/dev/") {
   468  				continue
   469  			}
   470  			defaultMounts = append(defaultMounts, m)
   471  		}
   472  	}
   473  
   474  	s.Mounts = defaultMounts
   475  	for _, m := range mounts {
   476  		for _, cm := range s.Mounts {
   477  			if cm.Destination == m.Destination {
   478  				return fmt.Errorf("Duplicate mount point '%s'", m.Destination)
   479  			}
   480  		}
   481  
   482  		if m.Source == "tmpfs" {
   483  			data := c.HostConfig.Tmpfs[m.Destination]
   484  			options := []string{"noexec", "nosuid", "nodev", volume.DefaultPropagationMode}
   485  			if data != "" {
   486  				options = append(options, strings.Split(data, ",")...)
   487  			}
   488  
   489  			merged, err := mount.MergeTmpfsOptions(options)
   490  			if err != nil {
   491  				return err
   492  			}
   493  
   494  			s.Mounts = append(s.Mounts, specs.Mount{Destination: m.Destination, Source: m.Source, Type: "tmpfs", Options: merged})
   495  			continue
   496  		}
   497  
   498  		mt := specs.Mount{Destination: m.Destination, Source: m.Source, Type: "bind"}
   499  
   500  		// Determine property of RootPropagation based on volume
   501  		// properties. If a volume is shared, then keep root propagation
   502  		// shared. This should work for slave and private volumes too.
   503  		//
   504  		// For slave volumes, it can be either [r]shared/[r]slave.
   505  		//
   506  		// For private volumes any root propagation value should work.
   507  		pFlag := mountPropagationMap[m.Propagation]
   508  		if pFlag == mount.SHARED || pFlag == mount.RSHARED {
   509  			if err := ensureShared(m.Source); err != nil {
   510  				return err
   511  			}
   512  			rootpg := mountPropagationMap[s.Linux.RootfsPropagation]
   513  			if rootpg != mount.SHARED && rootpg != mount.RSHARED {
   514  				s.Linux.RootfsPropagation = mountPropagationReverseMap[mount.SHARED]
   515  			}
   516  		} else if pFlag == mount.SLAVE || pFlag == mount.RSLAVE {
   517  			if err := ensureSharedOrSlave(m.Source); err != nil {
   518  				return err
   519  			}
   520  			rootpg := mountPropagationMap[s.Linux.RootfsPropagation]
   521  			if rootpg != mount.SHARED && rootpg != mount.RSHARED && rootpg != mount.SLAVE && rootpg != mount.RSLAVE {
   522  				s.Linux.RootfsPropagation = mountPropagationReverseMap[mount.RSLAVE]
   523  			}
   524  		}
   525  
   526  		opts := []string{"rbind"}
   527  		if !m.Writable {
   528  			opts = append(opts, "ro")
   529  		}
   530  		if pFlag != 0 {
   531  			opts = append(opts, mountPropagationReverseMap[pFlag])
   532  		}
   533  
   534  		mt.Options = opts
   535  		s.Mounts = append(s.Mounts, mt)
   536  	}
   537  
   538  	if s.Root.Readonly {
   539  		for i, m := range s.Mounts {
   540  			switch m.Destination {
   541  			case "/proc", "/dev/pts", "/dev/mqueue": // /dev is remounted by runc
   542  				continue
   543  			}
   544  			if _, ok := userMounts[m.Destination]; !ok {
   545  				if !stringutils.InSlice(m.Options, "ro") {
   546  					s.Mounts[i].Options = append(s.Mounts[i].Options, "ro")
   547  				}
   548  			}
   549  		}
   550  	}
   551  
   552  	if c.HostConfig.Privileged {
   553  		if !s.Root.Readonly {
   554  			// clear readonly for /sys
   555  			for i := range s.Mounts {
   556  				if s.Mounts[i].Destination == "/sys" {
   557  					clearReadOnly(&s.Mounts[i])
   558  				}
   559  			}
   560  		}
   561  		s.Linux.ReadonlyPaths = nil
   562  		s.Linux.MaskedPaths = nil
   563  	}
   564  
   565  	// TODO: until a kernel/mount solution exists for handling remount in a user namespace,
   566  	// we must clear the readonly flag for the cgroups mount (@mrunalp concurs)
   567  	if uidMap, _ := daemon.GetUIDGIDMaps(); uidMap != nil || c.HostConfig.Privileged {
   568  		for i, m := range s.Mounts {
   569  			if m.Type == "cgroup" {
   570  				clearReadOnly(&s.Mounts[i])
   571  			}
   572  		}
   573  	}
   574  
   575  	return nil
   576  }
   577  
   578  func (daemon *Daemon) populateCommonSpec(s *specs.Spec, c *container.Container) error {
   579  	linkedEnv, err := daemon.setupLinkedContainers(c)
   580  	if err != nil {
   581  		return err
   582  	}
   583  	s.Root = specs.Root{
   584  		Path:     c.BaseFS,
   585  		Readonly: c.HostConfig.ReadonlyRootfs,
   586  	}
   587  	rootUID, rootGID := daemon.GetRemappedUIDGID()
   588  	if err := c.SetupWorkingDirectory(rootUID, rootGID); err != nil {
   589  		return err
   590  	}
   591  	cwd := c.Config.WorkingDir
   592  	if len(cwd) == 0 {
   593  		cwd = "/"
   594  	}
   595  	s.Process.Args = append([]string{c.Path}, c.Args...)
   596  	s.Process.Cwd = cwd
   597  	s.Process.Env = c.CreateDaemonEnvironment(linkedEnv)
   598  	s.Process.Terminal = c.Config.Tty
   599  	s.Hostname = c.FullHostname()
   600  
   601  	return nil
   602  }
   603  
   604  func (daemon *Daemon) createSpec(c *container.Container) (*libcontainerd.Spec, error) {
   605  	s := oci.DefaultSpec()
   606  	if err := daemon.populateCommonSpec(&s, c); err != nil {
   607  		return nil, err
   608  	}
   609  
   610  	var cgroupsPath string
   611  	scopePrefix := "docker"
   612  	parent := "/docker"
   613  	useSystemd := UsingSystemd(daemon.configStore)
   614  	if useSystemd {
   615  		parent = "system.slice"
   616  	}
   617  
   618  	if c.HostConfig.CgroupParent != "" {
   619  		parent = c.HostConfig.CgroupParent
   620  	} else if daemon.configStore.CgroupParent != "" {
   621  		parent = daemon.configStore.CgroupParent
   622  	}
   623  
   624  	if useSystemd {
   625  		cgroupsPath = parent + ":" + scopePrefix + ":" + c.ID
   626  		logrus.Debugf("createSpec: cgroupsPath: %s", cgroupsPath)
   627  	} else {
   628  		cgroupsPath = filepath.Join(parent, c.ID)
   629  	}
   630  	s.Linux.CgroupsPath = &cgroupsPath
   631  
   632  	if err := setResources(&s, c.HostConfig.Resources); err != nil {
   633  		return nil, fmt.Errorf("linux runtime spec resources: %v", err)
   634  	}
   635  	s.Linux.Resources.OOMScoreAdj = &c.HostConfig.OomScoreAdj
   636  	s.Linux.Sysctl = c.HostConfig.Sysctls
   637  	if err := setDevices(&s, c); err != nil {
   638  		return nil, fmt.Errorf("linux runtime spec devices: %v", err)
   639  	}
   640  	if err := setRlimits(daemon, &s, c); err != nil {
   641  		return nil, fmt.Errorf("linux runtime spec rlimits: %v", err)
   642  	}
   643  	if err := setUser(&s, c); err != nil {
   644  		return nil, fmt.Errorf("linux spec user: %v", err)
   645  	}
   646  	if err := setNamespaces(daemon, &s, c); err != nil {
   647  		return nil, fmt.Errorf("linux spec namespaces: %v", err)
   648  	}
   649  	if err := setCapabilities(&s, c); err != nil {
   650  		return nil, fmt.Errorf("linux spec capabilities: %v", err)
   651  	}
   652  	if err := setSeccomp(daemon, &s, c); err != nil {
   653  		return nil, fmt.Errorf("linux seccomp: %v", err)
   654  	}
   655  
   656  	if err := daemon.setupIpcDirs(c); err != nil {
   657  		return nil, err
   658  	}
   659  
   660  	ms, err := daemon.setupMounts(c)
   661  	if err != nil {
   662  		return nil, err
   663  	}
   664  	ms = append(ms, c.IpcMounts()...)
   665  	ms = append(ms, c.TmpfsMounts()...)
   666  	sort.Sort(mounts(ms))
   667  	if err := setMounts(daemon, &s, c, ms); err != nil {
   668  		return nil, fmt.Errorf("linux mounts: %v", err)
   669  	}
   670  
   671  	for _, ns := range s.Linux.Namespaces {
   672  		if ns.Type == "network" && ns.Path == "" && !c.Config.NetworkDisabled {
   673  			target, err := os.Readlink(filepath.Join("/proc", strconv.Itoa(os.Getpid()), "exe"))
   674  			if err != nil {
   675  				return nil, err
   676  			}
   677  
   678  			s.Hooks = specs.Hooks{
   679  				Prestart: []specs.Hook{{
   680  					Path: target, // FIXME: cross-platform
   681  					Args: []string{"libnetwork-setkey", c.ID, daemon.netController.ID()},
   682  				}},
   683  			}
   684  		}
   685  	}
   686  
   687  	if apparmor.IsEnabled() {
   688  		appArmorProfile := "docker-default"
   689  		if len(c.AppArmorProfile) > 0 {
   690  			appArmorProfile = c.AppArmorProfile
   691  		} else if c.HostConfig.Privileged {
   692  			appArmorProfile = "unconfined"
   693  		}
   694  		s.Process.ApparmorProfile = appArmorProfile
   695  	}
   696  	s.Process.SelinuxLabel = c.GetProcessLabel()
   697  	s.Process.NoNewPrivileges = c.NoNewPrivileges
   698  	s.Linux.MountLabel = c.MountLabel
   699  
   700  	return (*libcontainerd.Spec)(&s), nil
   701  }
   702  
   703  func clearReadOnly(m *specs.Mount) {
   704  	var opt []string
   705  	for _, o := range m.Options {
   706  		if o != "ro" {
   707  			opt = append(opt, o)
   708  		}
   709  	}
   710  	m.Options = opt
   711  }