ronantakizawa/codereview-bench huggingface dataset:
Repository: opencontainers/runc
File: libcontainer/dmz/overlayfs_linux.go (Go)

Code before:
		return nil, fmt.Errorf("fsconfig set overlayfs lowerdir=%s: %w", lowerDirStr, err)
	}

	// Get an actual handle to the overlayfs.
	if err := unix.FsconfigCreate(int(overlayCtx.Fd())); err != nil {
		return nil, os.NewSyscallError("fsconfig create overlayfs", err)
	}
	overlayFd, err := fsmount(overlayCtx, unix.FSMOUNT_CLOEXEC, unix.MS_RDONLY|unix.MS_NODEV|unix.MS_NOSUID)
	if err != nil {
		return nil, err
	}
	defer overlayFd.Close()

	// Grab a handle to the binary through overlayfs.
	exeFile, err := utils.Openat(overlayFd, binName, unix.O_PATH|unix.O_NOFOLLOW|unix.O_CLOEXEC, 0)
	if err != nil {
		return nil, fmt.Errorf("open %s from overlayfs (lowerdir=%s): %w", binName, lowerDirStr, err)
	}
	defer func() {
		if Err != nil {
			_ = exeFile.Close()
		}
	}()

	// Check that the file is what we expect. Ideally we might check the hash
	// of the file against /proc/self/exe, but that would require us to copy
	// the binary (negating the benefit of using overlayfs over memfd-copying).
	// So instead we just check that the inode number is the same. In theory
	// this could result in us allowing an incorrect binary in some scenarios
	// but this would require an attacker to modify /usr/sbin in the host
	// filesystem -- at which point you've already lost.
	procSelfStat, err := fstatat(nil, "/proc/self/exe", 0)
	if err != nil {
		return nil, err
	}
	exeFileStat, err := fstatat(exeFile, "", 0)
	if err != nil {
		return nil, err
	}
	if procSelfStat.Ino != exeFileStat.Ino {
		return nil, fmt.Errorf("overlayfs cloned binary has different inode number %d != %d", procSelfStat.Ino, exeFileStat.Ino)
	}
	return exeFile, nil
}


Review comment (suggestion):
```suggestion
	// of the file against /proc/self/exe, but that would require us to fully read
```

Code after:
	}
	overlayFd, err := fsmount(overlayCtx, unix.FSMOUNT_CLOEXEC, unix.MS_RDONLY|unix.MS_NODEV|unix.MS_NOSUID)
	if err != nil {
		return nil, err
	}
	defer overlayFd.Close()

	// Grab a handle to the binary through overlayfs.
	exeFile, err := utils.Openat(overlayFd, binName, unix.O_PATH|unix.O_NOFOLLOW|unix.O_CLOEXEC, 0)
	if err != nil {
		return nil, fmt.Errorf("open %s from overlayfs (lowerdir=%s): %w", binName, lowerDirStr, err)
	}
	// NOTE: We would like to check that exeFile is the same as /proc/self/exe,
	// except this is a little difficult. Depending on what filesystems the
	// layers are on, overlayfs can remap the inode numbers (and it always
	// creates its own device numbers -- see ovl_map_dev_ino) so we can't do a
	// basic stat-based check. The only reasonable option would be to hash both
	// files and compare them, but this would require fully reading both files
	// which would produce a similar performance overhead to memfd cloning.
	//
	// Ultimately, there isn't a real attack to be worried about here. An
	// attacker would need to be able to modify files in /usr/sbin (or wherever
	// runc lives), at which point they could just replace the runc binary with
	// something malicious anyway.
	return exeFile, nil
}
Repository: opencontainers/runc
File: libcontainer/configs/validate/validator.go (Go)

Code before:
	if s.Policy == specs.SchedOther || s.Policy == specs.SchedBatch {
		if s.Nice < -20 || s.Nice > 19 {
			return fmt.Errorf("invalid scheduler.nice: %d when scheduler.policy is %s", s.Nice, string(s.Policy))
		}
	}
	if s.Priority != 0 && (s.Policy != specs.SchedFIFO && s.Policy != specs.SchedRR) {
		return errors.New("scheduler.priority can only be specified for SchedFIFO or SchedRR policy")
	}
	if s.Policy != specs.SchedDeadline && (s.Runtime != 0 || s.Deadline != 0 || s.Period != 0) {
		return errors.New("scheduler runtime/deadline/period can only be specified for SchedDeadline policy")
	}
	return nil
}

func ioPriority(config *configs.Config) error {
	if config.IOPriority == nil {
		return nil
	}
	priority := config.IOPriority.Priority
	if priority < 0 || priority > 7 {
		return fmt.Errorf("invalid ioPriority.Priority: %d", priority)
	}

	switch config.IOPriority.Class {
	case specs.IOPRIO_CLASS_RT, specs.IOPRIO_CLASS_BE, specs.IOPRIO_CLASS_IDLE:
		// do nothing
	default:
		return fmt.Errorf("invalid ioPriority.Class: %q", config.IOPriority.Class)
	}

	return nil
}


Review comment (suggestion):
I don't like this comment, but this is my personal preference. Maybe should change to `// should return nil` or move to an new function:
```go
func checkIOPriorityClass(specs.IOPriorityClass) bool {
    switch config.IOPriority.Class {
	case specs.IOPRIO_CLASS_RT, specs.IOPRIO_CLASS_BE, specs.IOPRIO_CLASS_IDLE:
            return true
    }
    return false
}
```

Code after:
	if s.Policy == specs.SchedOther || s.Policy == specs.SchedBatch {
		if s.Nice < -20 || s.Nice > 19 {
			return fmt.Errorf("invalid scheduler.nice: %d when scheduler.policy is %s", s.Nice, string(s.Policy))
		}
	}
	if s.Priority != 0 && (s.Policy != specs.SchedFIFO && s.Policy != specs.SchedRR) {
		return errors.New("scheduler.priority can only be specified for SchedFIFO or SchedRR policy")
	}
	if s.Policy != specs.SchedDeadline && (s.Runtime != 0 || s.Deadline != 0 || s.Period != 0) {
		return errors.New("scheduler runtime/deadline/period can only be specified for SchedDeadline policy")
	}
	return nil
}

func ioPriority(config *configs.Config) error {
	if config.IOPriority == nil {
		return nil
	}
	priority := config.IOPriority.Priority
	if priority < 0 || priority > 7 {
		return fmt.Errorf("invalid ioPriority.Priority: %d", priority)
	}

	switch class := config.IOPriority.Class; class {
	case specs.IOPRIO_CLASS_RT, specs.IOPRIO_CLASS_BE, specs.IOPRIO_CLASS_IDLE:
		// Valid class, do nothing.
	default:
		return fmt.Errorf("invalid ioPriority.Class: %q", class)
	}

	return nil
}
Repository: opencontainers/runc
File: exec.go (Go)

Code before:
			return err
		}
		status, err := execProcess(context)
		if err == nil {
			os.Exit(status)
		}
		fatalWithCode(fmt.Errorf("exec failed: %w", err), 255)
		return nil // to satisfy the linter
	},
	SkipArgReorder: true,
}

func getSubCgroupPaths(args []string) (map[string]string, error) {
	if len(args) == 0 {
		return nil, nil
	}
	paths := make(map[string]string, len(args))
	for _, c := range args {
		// Split into controller:path.
		if ctr, path, ok := strings.Cut(c, ":"); ok {
			// There may be a few comma-separated controllers.
			for _, ctrl := range strings.Split(ctr, ",") {
				paths[ctrl] = path
			}
		} else {
			// No controller: prefix.
			if len(args) != 1 {
				return nil, fmt.Errorf("invalid --cgroup argument: %s (missing <controller>: prefix)", c)
			}
			paths[""] = c
		}
	}
	return paths, nil
}

func execProcess(context *cli.Context) (int, error) {
	container, err := getContainer(context)
	if err != nil {
		return -1, err
	}
	status, err := container.Status()
	if err != nil {
		return -1, err
	}
	if status == libcontainer.Stopped {
		return -1, errors.New("cannot exec in a stopped container")
	}
	if status == libcontainer.Paused && !context.Bool("ignore-paused") {
		return -1, errors.New("cannot exec in a paused container (use --ignore-paused to override)")
	}
	path := context.String("process")

Review comment (suggestion):
I havn't checked that whether this will lead to some regressions or not, for example, if c == "a:b:c:d:e":
When using `SplitN`, `cs[1]` will be "b";
When using `Cut`, `path` will be "b:c:d:e".

Code after:
			return err
		}
		status, err := execProcess(context)
		if err == nil {
			os.Exit(status)
		}
		fatalWithCode(fmt.Errorf("exec failed: %w", err), 255)
		return nil // to satisfy the linter
	},
	SkipArgReorder: true,
}

// getSubCgroupPaths parses --cgroup arguments, which can either be
//   - a single "path" argument (for cgroup v2);
//   - one or more controller[,controller[,...]]:path arguments (for cgroup v1).
//
// Returns a controller to path map. For cgroup v2, it's a single entity map
// with empty controller value.
func getSubCgroupPaths(args []string) (map[string]string, error) {
	if len(args) == 0 {
		return nil, nil
	}
	paths := make(map[string]string, len(args))
	for _, c := range args {
		// Split into controller:path.
		if ctr, path, ok := strings.Cut(c, ":"); ok {
			// There may be a few comma-separated controllers.
			for _, ctrl := range strings.Split(ctr, ",") {
				if ctrl == "" {
					return nil, fmt.Errorf("invalid --cgroup argument: %s (empty <controller> prefix)", c)
				}
				if _, ok := paths[ctrl]; ok {
					return nil, fmt.Errorf("invalid --cgroup argument(s): controller %s specified multiple times", ctrl)
				}
				paths[ctrl] = path
			}
		} else {
			// No "controller:" prefix (cgroup v2, a single path).
			if len(args) != 1 {
				return nil, fmt.Errorf("invalid --cgroup argument: %s (missing <controller>: prefix)", c)
			}
			paths[""] = c
		}
	}
	return paths, nil
}

func execProcess(context *cli.Context) (int, error) {
	container, err := getContainer(context)
	if err != nil {
		return -1, err
Repository: opencontainers/runc
File: libcontainer/cgroups/fscommon/utils.go (Go)

Code before:
	key, val, ok := strings.Cut(t, " ")
	if !ok {
		return "", 0, fmt.Errorf("line %q is not in key value format", t)
	}

	value, err := ParseUint(val, 10, 64)
	if err != nil {
		return "", 0, err
	}

	return key, value, nil
}

// GetValueByKey reads a key-value pairs from the specified cgroup file,
// and returns a value of the specified key. [ParseUint] is used for value
// conversion.
func GetValueByKey(path, file, key string) (uint64, error) {
	content, err := cgroups.ReadFile(path, file)
	if err != nil {
		return 0, err
	}

	lines := strings.Split(content, "\n")
	for _, line := range lines {
		k, v, ok := strings.Cut(line, " ")
		if ok && k == key {
			val, err := ParseUint(v, 10, 64)
			if err != nil {
				err = &ParseError{Path: path, File: file, Err: err}
			}
			return val, err
		}
	}

	return 0, nil
}

// GetCgroupParamUint reads a single uint64 value from the specified cgroup file.
// If the value read is "max", the math.MaxUint64 is returned.
func GetCgroupParamUint(path, file string) (uint64, error) {
	contents, err := GetCgroupParamString(path, file)
	if err != nil {
		return 0, err
	}
	contents = strings.TrimSpace(contents)
	if contents == "max" {
		return math.MaxUint64, nil
	}

	res, err := ParseUint(contents, 10, 64)
	if err != nil {

Review comment (suggestion):
Not sure if  relevant (and _probably_ new code is more correct?); previously, we would ignore cases with more than 2 fields. I.e., `key foo bar` would be ignored; in the new code, we'd try to parse `foo bar` as value.

That said; see my other comment; this would potentially be an issue if we try to parse something that may currently have 2 cols, and in future 3 cols; not sure if that should _error_ or silently _ignore_



Code after:
	key, val, ok := strings.Cut(t, " ")
	if !ok || key == "" || val == "" {
		return "", 0, fmt.Errorf(`line %q is not in "key value" format`, t)
	}

	value, err := ParseUint(val, 10, 64)
	if err != nil {
		return "", 0, err
	}

	return key, value, nil
}

// GetValueByKey reads space-separated "key value" pairs from the specified
// cgroup file, looking for a specified key, and returns its value as uint64,
// using [ParseUint] for conversion. If the value is not found, 0 is returned.
func GetValueByKey(path, file, key string) (uint64, error) {
	content, err := cgroups.ReadFile(path, file)
	if err != nil {
		return 0, err
	}

	key += " "
	lines := strings.Split(content, "\n")
	for _, line := range lines {
		v, ok := strings.CutPrefix(line, key)
		if ok {
			val, err := ParseUint(v, 10, 64)
			if err != nil {
				err = &ParseError{Path: path, File: file, Err: err}
			}
			return val, err
		}
	}

	return 0, nil
}

// GetCgroupParamUint reads a single uint64 value from the specified cgroup file.
// If the value read is "max", the math.MaxUint64 is returned.
func GetCgroupParamUint(path, file string) (uint64, error) {
	contents, err := GetCgroupParamString(path, file)
	if err != nil {
		return 0, err
	}
	if contents == "max" {
		return math.MaxUint64, nil
	}

	res, err := ParseUint(contents, 10, 64)
	if err != nil {
Repository: opencontainers/runc
File: libcontainer/configs/validate/rootless.go (Go)

Code before:
		}
	}
	return nil
}

// rootlessEUIDMount verifies that all mounts have valid uid=/gid= options,
// i.e. their arguments has proper ID mappings.
func rootlessEUIDMount(config *configs.Config) error {
	// XXX: We could whitelist allowed devices at this point, but I'm not
	//      convinced that's a good idea. The kernel is the best arbiter of
	//      access control.

	for _, mount := range config.Mounts {
		// Check that the options list doesn't contain any uid= or gid= entries
		// that don't resolve to root.
		for _, opt := range strings.Split(mount.Data, ",") {
			if str, ok := strings.CutPrefix(opt, "uid="); ok {
				uid, err := strconv.Atoi(str)
				if err != nil {
					// Ignore unknown mount options.
					continue
				}
				if _, err := config.HostUID(uid); err != nil {
					return fmt.Errorf("cannot specify uid=%d mount option for rootless container: %w", uid, err)
				}
				continue
			}
			if str, ok := strings.CutPrefix(opt, "gid="); ok {
				gid, err := strconv.Atoi(str)
				if err != nil {
					// Ignore unknown mount options.
					continue
				}
				if _, err := config.HostGID(gid); err != nil {
					return fmt.Errorf("cannot specify gid=%d mount option for rootless container: %w", gid, err)
				}
			}
		}
	}

	return nil
}


Review comment (suggestion):
This is added so when we've found `uid=...` in `opt`, we don't also try it for `gid=` but go to the next option.

Code after:
		}
	}
	return nil
}

// rootlessEUIDMount verifies that all mounts have valid uid=/gid= options,
// i.e. their arguments has proper ID mappings.
func rootlessEUIDMount(config *configs.Config) error {
	// XXX: We could whitelist allowed devices at this point, but I'm not
	//      convinced that's a good idea. The kernel is the best arbiter of
	//      access control.

	// Check that the options list doesn't contain any uid= or gid= entries
	// that don't resolve to root.
	for _, mount := range config.Mounts {
		// Look for a common substring; skip further processing
		// if there can't be any uid= or gid= options.
		if !strings.Contains(mount.Data, "id=") {
			continue
		}
		for _, opt := range strings.Split(mount.Data, ",") {
			if str, ok := strings.CutPrefix(opt, "uid="); ok {
				uid, err := strconv.Atoi(str)
				if err != nil {
					// Ignore unknown mount options.
					continue
				}
				if _, err := config.HostUID(uid); err != nil {
					return fmt.Errorf("cannot specify %s mount option for rootless container: %w", opt, err)
				}
			} else if str, ok := strings.CutPrefix(opt, "gid="); ok {
				gid, err := strconv.Atoi(str)
				if err != nil {
					// Ignore unknown mount options.
					continue
				}
				if _, err := config.HostGID(gid); err != nil {
					return fmt.Errorf("cannot specify %s mount option for rootless container: %w", opt, err)
				}
			}
		}
	}

	return nil
}
Repository: opencontainers/runc
File: libcontainer/nsenter/nsexec.c (C)

Code before:

static char *nsset_to_str(nsset_t nsset)
{
	char *str = NULL;
	for (struct nstype_t * ns = all_ns_types; ns->name != NULL; ns++) {
		if (ns->type & nsset) {
			if (str)
				str = strappend(str, ", ");
			str = strappend(str, ns->name);
		}
	}
	return str ? : strdup("");
}

static void __close_namespaces(nsset_t to_join, nsset_t joined, struct namespace_t *ns_list, size_t ns_len)
{
	/* We expect to have joined every namespace. */
	nsset_t failed_to_join = to_join & ~joined;

	/* Double-check that we used up (and thus joined) all of the nsfds. */
	for (size_t i = 0; i < ns_len; i++) {
		struct namespace_t *ns = &ns_list[i];
		int type = nstype(ns->type);

		if (ns->fd < 0)
			continue;

		failed_to_join |= type;
		write_log(FATAL, "failed to setns(%#x) into %s namespace (with path %s): %s",
			  type, ns->type, ns->path, strerror(EPERM));
		close(ns->fd);
		ns->fd = -1;
	}

	/* Make sure we joined the namespaces we planned to. */
	if (failed_to_join)
		bail("failed to join {%s} namespaces: %s", nsset_to_str(failed_to_join), strerror(EPERM));

	free(ns_list);
}

void join_namespaces(char *nsspec)
{
	nsset_t to_join = 0, joined = 0;
	struct namespace_t *ns_list;
	size_t ns_len;

	/*
	 * We have to open the file descriptors first, since after we join the
	 * mnt or user namespaces we might no longer be able to access the
	 * paths.

Review comment (bug):
Because in the next we will call bail to print the error msg when there were some ns failed to join, so I think this FATAL log could be removed.

Code after:
	char *str = NULL;
	for (struct nstype_t * ns = all_ns_types; ns->name != NULL; ns++) {
		if (ns->type & nsset) {
			if (str)
				str = strappend(str, ", ");
			str = strappend(str, ns->name);
		}
	}
	return str ? : strdup("");
}

static void __close_namespaces(nsset_t to_join, nsset_t joined, struct namespace_t *ns_list, size_t ns_len)
{
	/* We expect to have joined every namespace. */
	nsset_t failed_to_join = to_join & ~joined;

	/* Double-check that we used up (and thus joined) all of the nsfds. */
	for (size_t i = 0; i < ns_len; i++) {
		struct namespace_t *ns = &ns_list[i];
		int type = nstype(ns->type);

		if (ns->fd < 0)
			continue;

		failed_to_join |= type;
		write_log(FATAL, "failed to setns(%#x) into %s namespace (with path %s): %s",
			  type, ns->type, ns->path, strerror(EPERM));
		close(ns->fd);
		ns->fd = -1;
	}

	/* Make sure we joined the namespaces we planned to. */
	if (failed_to_join)
		bail("failed to join {%s} namespaces: %s", nsset_to_str(failed_to_join), strerror(EPERM));

	free(ns_list);
}

void join_namespaces(char *nsspec)
{
	nsset_t to_join = 0, joined = 0;
	struct namespace_t *ns_list;
	size_t ns_len;

	/*
	 * We have to open the file descriptors first, since after we join the
	 * mnt or user namespaces we might no longer be able to access the
	 * paths.
	 */
	to_join = __open_namespaces(nsspec, &ns_list, &ns_len);
Repository: opencontainers/runc
File: libcontainer/cgroups/fs/cpuset.go (Go)

Code before:
	cpusetPrefix string
	cpusetInit   bool
)

func getCpusetPrefix() string {
	cpusetLock.Lock()
	defer cpusetLock.Unlock()

	if cpusetInit {
		return cpusetPrefix
	}

	cpusetInit = true
	cpusetPrefix = "cpuset."

	file, err := os.Open("/proc/self/mountinfo")
	if err != nil {
		return cpusetPrefix
	}
	defer file.Close()

	scanner := bufio.NewScanner(file)

	for scanner.Scan() {
		line := scanner.Text()
		if strings.Contains(line, "cpuset") && strings.Contains(line, "cgroup") && strings.Contains(line, "noprefix") {
			cpusetPrefix = ""
			break
		}
	}
	return cpusetPrefix
}

type CpusetGroup struct{}

func (s *CpusetGroup) Name() string {
	return "cpuset"
}

func (s *CpusetGroup) Apply(path string, r *configs.Resources, pid int) error {
	return s.ApplyDir(path, r, pid)
}

func (s *CpusetGroup) Set(path string, r *configs.Resources) error {
	if r.CpusetCpus != "" {
		if err := cgroups.WriteFile(path, getCpusetPrefix()+"cpus", r.CpusetCpus); err != nil {
			return err
		}
	}
	if r.CpusetMems != "" {
		if err := cgroups.WriteFile(path, getCpusetPrefix()+"mems", r.CpusetMems); err != nil {

Review comment (suggestion):
Better to use `sync.OnceValue` for this kind of stuff. 

E.g.
```go
var getCpusetPrefix = sync.OnceValue(func() string{
....
})


Code after:
)

func cpusetFile(path string, name string) string {
	cpusetLock.Lock()
	defer cpusetLock.Unlock()

	// Only the v1 cpuset cgroup is allowed to mount with noprefix.
	// See kernel source: https://github.com/torvalds/linux/blob/2e1b3cc9d7f790145a80cb705b168f05dab65df2/kernel/cgroup/cgroup-v1.c#L1070
	// Cpuset cannot be mounted with and without prefix simultaneously.
	// Commonly used in Android environments.

	if cpusetFastPath {
		return cpusetPrefix + name
	}

	err := unix.Access(filepath.Join(path, cpusetPrefix+name), unix.F_OK)
	if err == nil {
		// Use the fast path only if we can access one type of mount for cpuset already
		cpusetFastPath = true
	} else {
		err = unix.Access(filepath.Join(path, name), unix.F_OK)
		if err == nil {
			cpusetPrefix = ""
			cpusetFastPath = true
		}
	}

	return cpusetPrefix + name
}

type CpusetGroup struct{}

func (s *CpusetGroup) Name() string {
	return "cpuset"
}

func (s *CpusetGroup) Apply(path string, r *cgroups.Resources, pid int) error {
	return s.ApplyDir(path, r, pid)
}

func (s *CpusetGroup) Set(path string, r *cgroups.Resources) error {
	if r.CpusetCpus != "" {
		if err := cgroups.WriteFile(path, cpusetFile(path, "cpus"), r.CpusetCpus); err != nil {
			return err
		}
	}
	if r.CpusetMems != "" {
		if err := cgroups.WriteFile(path, cpusetFile(path, "mems"), r.CpusetMems); err != nil {
			return err
		}
	}
Repository: opencontainers/runc
File: libcontainer/cgroups/fs/cpuset.go (Go)

Code before:
	cpusetPrefix string
	cpusetInit   bool
)

func getCpusetPrefix() string {
	cpusetLock.Lock()
	defer cpusetLock.Unlock()

	if cpusetInit {
		return cpusetPrefix
	}

	cpusetInit = true
	cpusetPrefix = "cpuset."

	file, err := os.Open("/proc/self/mountinfo")
	if err != nil {
		return cpusetPrefix
	}
	defer file.Close()

	scanner := bufio.NewScanner(file)

	for scanner.Scan() {
		line := scanner.Text()
		if strings.Contains(line, "cpuset") && strings.Contains(line, "cgroup") && strings.Contains(line, "noprefix") {
			cpusetPrefix = ""
			break
		}
	}
	return cpusetPrefix
}

type CpusetGroup struct{}

func (s *CpusetGroup) Name() string {
	return "cpuset"
}

func (s *CpusetGroup) Apply(path string, r *configs.Resources, pid int) error {
	return s.ApplyDir(path, r, pid)
}

func (s *CpusetGroup) Set(path string, r *configs.Resources) error {
	if r.CpusetCpus != "" {
		if err := cgroups.WriteFile(path, getCpusetPrefix()+"cpus", r.CpusetCpus); err != nil {
			return err
		}
	}
	if r.CpusetMems != "" {
		if err := cgroups.WriteFile(path, getCpusetPrefix()+"mems", r.CpusetMems); err != nil {

Review comment (performance):
Please find a way to do this without parsing `/proc/self/mountinfo`, as this is expensive and error-prone (on older kernels -- and we're using cgroup v1 on older kernels).

Code after:
)

func cpusetFile(path string, name string) string {
	cpusetLock.Lock()
	defer cpusetLock.Unlock()

	// Only the v1 cpuset cgroup is allowed to mount with noprefix.
	// See kernel source: https://github.com/torvalds/linux/blob/2e1b3cc9d7f790145a80cb705b168f05dab65df2/kernel/cgroup/cgroup-v1.c#L1070
	// Cpuset cannot be mounted with and without prefix simultaneously.
	// Commonly used in Android environments.

	if cpusetFastPath {
		return cpusetPrefix + name
	}

	err := unix.Access(filepath.Join(path, cpusetPrefix+name), unix.F_OK)
	if err == nil {
		// Use the fast path only if we can access one type of mount for cpuset already
		cpusetFastPath = true
	} else {
		err = unix.Access(filepath.Join(path, name), unix.F_OK)
		if err == nil {
			cpusetPrefix = ""
			cpusetFastPath = true
		}
	}

	return cpusetPrefix + name
}

type CpusetGroup struct{}

func (s *CpusetGroup) Name() string {
	return "cpuset"
}

func (s *CpusetGroup) Apply(path string, r *cgroups.Resources, pid int) error {
	return s.ApplyDir(path, r, pid)
}

func (s *CpusetGroup) Set(path string, r *cgroups.Resources) error {
	if r.CpusetCpus != "" {
		if err := cgroups.WriteFile(path, cpusetFile(path, "cpus"), r.CpusetCpus); err != nil {
			return err
		}
	}
	if r.CpusetMems != "" {
		if err := cgroups.WriteFile(path, cpusetFile(path, "mems"), r.CpusetMems); err != nil {
			return err
		}
	}