From 75a90154267381f1ef2546a639d8f96ebbdb3634 Mon Sep 17 00:00:00 2001 From: Kir Kolyshkin Date: Wed, 23 Sep 2020 14:26:19 -0700 Subject: [PATCH] libct/cg/fs.getMount: don't parse mountinfo In cgroup v1, the parent of a cgroup mount is on tmpfs (or maybe any other fs but definitely not cgroupfs). Use this to traverse up the tree until we reach non-cgroupfs. This should work in any setups (nested container, non-standard cgroup mounts) so issue [1] won't happen. Theoretically, the only problematic case is when someone mounts cpuset cgroupfs into a directory on another cgroupfs. Let's assume people don't do that -- if they do, they will get other error (e.g. inability to read cpuset.cpus file). [1] https://github.com/opencontainers/runc/issues/1367 Signed-off-by: Kir Kolyshkin --- libcontainer/cgroups/fs/cpuset.go | 61 ++++++++++--------------------- 1 file changed, 19 insertions(+), 42 deletions(-) diff --git a/libcontainer/cgroups/fs/cpuset.go b/libcontainer/cgroups/fs/cpuset.go index e7b5cf6726b..95c4a7d7d86 100644 --- a/libcontainer/cgroups/fs/cpuset.go +++ b/libcontainer/cgroups/fs/cpuset.go @@ -6,12 +6,10 @@ import ( "os" "path/filepath" - "github.com/moby/sys/mountinfo" "github.com/opencontainers/runc/libcontainer/cgroups" "github.com/opencontainers/runc/libcontainer/cgroups/fscommon" "github.com/opencontainers/runc/libcontainer/configs" - libcontainerUtils "github.com/opencontainers/runc/libcontainer/utils" - "github.com/pkg/errors" + "golang.org/x/sys/unix" ) type CpusetGroup struct { @@ -43,46 +41,19 @@ func (s *CpusetGroup) GetStats(path string, stats *cgroups.Stats) error { return nil } -// Get the source mount point of directory passed in as argument. -func getMount(dir string) (string, error) { - mi, err := mountinfo.GetMounts(mountinfo.ParentsFilter(dir)) - if err != nil { - return "", err - } - if len(mi) < 1 { - return "", errors.Errorf("Can't find mount point of %s", dir) - } - - // find the longest mount point - var idx, maxlen int - for i := range mi { - if len(mi[i].Mountpoint) > maxlen { - maxlen = len(mi[i].Mountpoint) - idx = i - } - } - - return mi[idx].Mountpoint, nil -} - func (s *CpusetGroup) ApplyDir(dir string, cgroup *configs.Cgroup, pid int) error { // This might happen if we have no cpuset cgroup mounted. // Just do nothing and don't fail. if dir == "" { return nil } - root, err := getMount(dir) - if err != nil { - return err - } - root = filepath.Dir(root) // 'ensureParent' start with parent because we don't want to // explicitly inherit from parent, it could conflict with // 'cpuset.cpu_exclusive'. - if err := cpusetEnsureParent(filepath.Dir(dir), root); err != nil { + if err := cpusetEnsureParent(filepath.Dir(dir)); err != nil { return err } - if err := os.MkdirAll(dir, 0755); err != nil { + if err := os.Mkdir(dir, 0755); err != nil { return err } // We didn't inherit cpuset configs from parent, but we have @@ -111,22 +82,28 @@ func getCpusetSubsystemSettings(parent string) (cpus, mems string, err error) { return cpus, mems, nil } -// cpusetEnsureParent makes sure that the parent directory of current is created -// and populated with the proper cpus and mems files copied from -// its parent. -func cpusetEnsureParent(current, root string) error { +// cpusetEnsureParent makes sure that the parent directories of current +// are created and populated with the proper cpus and mems files copied +// from their respective parent. It does that recursively, starting from +// the top of the cpuset hierarchy (i.e. cpuset cgroup mount point). +func cpusetEnsureParent(current string) error { + var st unix.Statfs_t + parent := filepath.Dir(current) - if libcontainerUtils.CleanPath(parent) == root { + err := unix.Statfs(parent, &st) + if err == nil && st.Type != unix.CGROUP_SUPER_MAGIC { return nil } - // Avoid infinite recursion. - if parent == current { - return errors.New("cpuset: cgroup parent path outside cgroup root") + // Treat non-existing directory as cgroupfs as it will be created, + // and the root cpuset directory obviously exists. + if err != unix.ENOENT { + return &os.PathError{Op: "statfs", Path: parent, Err: err} } - if err := cpusetEnsureParent(parent, root); err != nil { + + if err := cpusetEnsureParent(parent); err != nil { return err } - if err := os.MkdirAll(current, 0755); err != nil { + if err := os.Mkdir(current, 0755); err != nil { return err } return cpusetCopyIfNeeded(current, parent)