Repository: firecracker-microvm/firecracker
File: src/jailer/src/cgroup.rs (Rust)

Code before:
        };

        // search PROC_MOUNTS for cgroup mount points
        let f = File::open(b.proc_mounts_path)
            .map_err(|err| JailerError::FileOpen(PathBuf::from(&b.proc_mounts_path), err))?;

        // Regex courtesy of Filippo.
        // This will match on each line from /proc/mounts for both v1 and v2 mount points.
        //
        // /proc/mounts cointains lines that look like this:
        // cgroup2 /sys/fs/cgroup/unified cgroup2 rw,nosuid,nodev,noexec,relatime,nsdelegate 0 0
        // cgroup /sys/fs/cgroup/cpu,cpuacct cgroup rw,nosuid,nodev,noexec,relatime,cpu,cpuacct 0 0
        //
        // This Regex will extract:
        //      * "/sys/fs/cgroup/unified" in the "dir" capture group.
        //      * "2" in the "ver" capture group as the cgroup version taken from "cgroup2"; for v1,
        //        the "ver" capture group will be empty (len = 0).
        //      * "[...],relatime,cpu,cpuacct" in the "options" capture group; this is used for
        //        cgroupv1 to determine what controllers are mounted at the location.
        let re = Regex::new(
            r"^([a-z2]*)[[:space:]](?P<dir>.*)[[:space:]]cgroup(?P<ver>2?)[[:space:]](?P<options>.*)[[:space:]]0[[:space:]]0$",
        ).map_err(JailerError::RegEx)?;

        for l in BufReader::new(f).lines() {
            let l =
                l.map_err(|err| JailerError::ReadLine(PathBuf::from(&b.proc_mounts_path), err))?;
            if let Some(capture) = re.captures(&l) {
                if ver == 2 && capture["ver"].len() == 1 {
                    // Found the cgroupv2 unified mountpoint; with cgroupsv2 there is only one
                    // hierarchy so we insert it in the hashmap to use it later when creating
                    // cgroups
                    b.hierarchies
                        .insert("unified".to_string(), PathBuf::from(&capture["dir"]));
                    break;
                } else if ver == 1 && capture["ver"].is_empty() {
                    // Found a cgroupv1 mountpoint; with cgroupsv1 we can have multiple hierarchies.
                    // Since we don't know which one will be used, we cache the mountpoints now,
                    // and will create the hierarchies on demand when a cgroup is built.
                    b.mount_points.push(CgroupMountPoint {
                        dir: String::from(&capture["dir"]),
                        options: String::from(&capture["options"]),
                    });
                }
            }
        }

        if b.hierarchies.is_empty() && b.mount_points.is_empty() {
            Err(JailerError::CgroupHierarchyMissing(
                "No hierarchy found for this cgroup version.".to_string(),
            ))
        } else {

Review comment (suggestion):
nitpicking: See https://github.com/firecracker-microvm/firecracker/pull/4438#discussion_r1523597582
```suggestion
                l.map_err(|err| JailerError::ReadLine(PathBuf::from(b.proc_mounts_path), err))?;
```

Code after:
        //
        // /proc/mounts cointains lines that look like this:
        // cgroup2 /sys/fs/cgroup/unified cgroup2 rw,nosuid,nodev,noexec,relatime,nsdelegate 0 0
        // cgroup /sys/fs/cgroup/cpu,cpuacct cgroup rw,nosuid,nodev,noexec,relatime,cpu,cpuacct 0 0
        //
        // This Regex will extract:
        //      * "/sys/fs/cgroup/unified" in the "dir" capture group.
        //      * "2" in the "ver" capture group as the cgroup version taken from "cgroup2"; for v1,
        //        the "ver" capture group will be empty (len = 0).
        //      * "[...],relatime,cpu,cpuacct" in the "options" capture group; this is used for
        //        cgroupv1 to determine what controllers are mounted at the location.
        let re = Regex::new(
            r"^([a-z2]*)[[:space:]](?P<dir>.*)[[:space:]]cgroup(?P<ver>2?)[[:space:]](?P<options>.*)[[:space:]]0[[:space:]]0$",
        ).map_err(JailerError::RegEx)?;

        for l in BufReader::new(f).lines() {
            let l = l.map_err(|err| JailerError::ReadLine(PathBuf::from(proc_mounts_path), err))?;
            if let Some(capture) = re.captures(&l) {
                if ver == 2 && capture["ver"].len() == 1 {
                    // Found the cgroupv2 unified mountpoint; with cgroupsv2 there is only one
                    // hierarchy so we insert it in the hashmap to use it later when creating
                    // cgroups
                    h.hierarchies
                        .insert("unified".to_string(), PathBuf::from(&capture["dir"]));
                    break;
                } else if ver == 1 && capture["ver"].is_empty() {
                    // Found a cgroupv1 mountpoint; with cgroupsv1 we can have multiple hierarchies.
                    // Since we don't know which one will be used, we cache the mountpoints now,
                    // and will create the hierarchies on demand when a cgroup is built.
                    h.mount_points.push(CgroupMountPoint {
                        dir: String::from(&capture["dir"]),
                        options: String::from(&capture["options"]),
                    });
                }
            }
        }

        if h.hierarchies.is_empty() && h.mount_points.is_empty() {
            Err(JailerError::CgroupHierarchyMissing(
                "No hierarchy found for this cgroup version.".to_string(),
            ))
        } else {
            Ok(h)
        }
    }

    // Returns the path to the root of the hierarchy for the controller specified
    // Cgroups for a controller are arranged in a hierarchy; multiple controllers
    // may share the same hierarchy
    fn get_v1_hierarchy_path(&mut self, controller: &str) -> Result<&PathBuf, JailerError> {
        // First try and see if the path is already discovered.