paths.go 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186
  1. package fs
  2. import (
  3. "errors"
  4. "os"
  5. "path/filepath"
  6. "sync"
  7. "golang.org/x/sys/unix"
  8. "github.com/opencontainers/runc/libcontainer/cgroups"
  9. "github.com/opencontainers/runc/libcontainer/configs"
  10. "github.com/opencontainers/runc/libcontainer/utils"
  11. )
  12. // The absolute path to the root of the cgroup hierarchies.
  13. var (
  14. cgroupRootLock sync.Mutex
  15. cgroupRoot string
  16. )
  17. const defaultCgroupRoot = "/sys/fs/cgroup"
  18. func initPaths(cg *configs.Cgroup) (map[string]string, error) {
  19. root, err := rootPath()
  20. if err != nil {
  21. return nil, err
  22. }
  23. inner, err := innerPath(cg)
  24. if err != nil {
  25. return nil, err
  26. }
  27. paths := make(map[string]string)
  28. for _, sys := range subsystems {
  29. name := sys.Name()
  30. path, err := subsysPath(root, inner, name)
  31. if err != nil {
  32. // The non-presence of the devices subsystem
  33. // is considered fatal for security reasons.
  34. if cgroups.IsNotFound(err) && (cg.SkipDevices || name != "devices") {
  35. continue
  36. }
  37. return nil, err
  38. }
  39. paths[name] = path
  40. }
  41. return paths, nil
  42. }
  43. func tryDefaultCgroupRoot() string {
  44. var st, pst unix.Stat_t
  45. // (1) it should be a directory...
  46. err := unix.Lstat(defaultCgroupRoot, &st)
  47. if err != nil || st.Mode&unix.S_IFDIR == 0 {
  48. return ""
  49. }
  50. // (2) ... and a mount point ...
  51. err = unix.Lstat(filepath.Dir(defaultCgroupRoot), &pst)
  52. if err != nil {
  53. return ""
  54. }
  55. if st.Dev == pst.Dev {
  56. // parent dir has the same dev -- not a mount point
  57. return ""
  58. }
  59. // (3) ... of 'tmpfs' fs type.
  60. var fst unix.Statfs_t
  61. err = unix.Statfs(defaultCgroupRoot, &fst)
  62. if err != nil || fst.Type != unix.TMPFS_MAGIC {
  63. return ""
  64. }
  65. // (4) it should have at least 1 entry ...
  66. dir, err := os.Open(defaultCgroupRoot)
  67. if err != nil {
  68. return ""
  69. }
  70. names, err := dir.Readdirnames(1)
  71. if err != nil {
  72. return ""
  73. }
  74. if len(names) < 1 {
  75. return ""
  76. }
  77. // ... which is a cgroup mount point.
  78. err = unix.Statfs(filepath.Join(defaultCgroupRoot, names[0]), &fst)
  79. if err != nil || fst.Type != unix.CGROUP_SUPER_MAGIC {
  80. return ""
  81. }
  82. return defaultCgroupRoot
  83. }
  84. // rootPath finds and returns path to the root of the cgroup hierarchies.
  85. func rootPath() (string, error) {
  86. cgroupRootLock.Lock()
  87. defer cgroupRootLock.Unlock()
  88. if cgroupRoot != "" {
  89. return cgroupRoot, nil
  90. }
  91. // fast path
  92. cgroupRoot = tryDefaultCgroupRoot()
  93. if cgroupRoot != "" {
  94. return cgroupRoot, nil
  95. }
  96. // slow path: parse mountinfo
  97. mi, err := cgroups.GetCgroupMounts(false)
  98. if err != nil {
  99. return "", err
  100. }
  101. if len(mi) < 1 {
  102. return "", errors.New("no cgroup mount found in mountinfo")
  103. }
  104. // Get the first cgroup mount (e.g. "/sys/fs/cgroup/memory"),
  105. // use its parent directory.
  106. root := filepath.Dir(mi[0].Mountpoint)
  107. if _, err := os.Stat(root); err != nil {
  108. return "", err
  109. }
  110. cgroupRoot = root
  111. return cgroupRoot, nil
  112. }
  113. func innerPath(c *configs.Cgroup) (string, error) {
  114. if (c.Name != "" || c.Parent != "") && c.Path != "" {
  115. return "", errors.New("cgroup: either Path or Name and Parent should be used")
  116. }
  117. // XXX: Do not remove CleanPath. Path safety is important! -- cyphar
  118. innerPath := utils.CleanPath(c.Path)
  119. if innerPath == "" {
  120. cgParent := utils.CleanPath(c.Parent)
  121. cgName := utils.CleanPath(c.Name)
  122. innerPath = filepath.Join(cgParent, cgName)
  123. }
  124. return innerPath, nil
  125. }
  126. func subsysPath(root, inner, subsystem string) (string, error) {
  127. // If the cgroup name/path is absolute do not look relative to the cgroup of the init process.
  128. if filepath.IsAbs(inner) {
  129. mnt, err := cgroups.FindCgroupMountpoint(root, subsystem)
  130. // If we didn't mount the subsystem, there is no point we make the path.
  131. if err != nil {
  132. return "", err
  133. }
  134. // Sometimes subsystems can be mounted together as 'cpu,cpuacct'.
  135. return filepath.Join(root, filepath.Base(mnt), inner), nil
  136. }
  137. // Use GetOwnCgroupPath instead of GetInitCgroupPath, because the creating
  138. // process could in container and shared pid namespace with host, and
  139. // /proc/1/cgroup could point to whole other world of cgroups.
  140. parentPath, err := cgroups.GetOwnCgroupPath(subsystem)
  141. if err != nil {
  142. return "", err
  143. }
  144. return filepath.Join(parentPath, inner), nil
  145. }
  146. func apply(path string, pid int) error {
  147. if path == "" {
  148. return nil
  149. }
  150. if err := os.MkdirAll(path, 0o755); err != nil {
  151. return err
  152. }
  153. return cgroups.WriteCgroupProc(path, pid)
  154. }