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

Code before:
    version: u8,
    hierarchies: HashMap<String, PathBuf>,
    mount_points: Vec<CgroupMountPoint>,
    proc_mounts_path: &'a str,
}

impl<'a> CgroupBuilder<'a> {
    // Creates the builder object
    // It will discover cgroup mount points and hierarchies configured
    // on the system and cache the info required to create cgroups later
    // within this hierarchies
    pub fn new(ver: u8, proc_mounts_path: &'a str) -> Result<Self, JailerError> {
        if ver != 1 && ver != 2 {
            return Err(JailerError::CgroupInvalidVersion(ver.to_string()));
        }

        let mut b = CgroupBuilder {
            version: ver,
            hierarchies: HashMap::new(),
            mount_points: Vec::new(),
            proc_mounts_path,
        };

        // 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

Review comment (suggestion):
nitpicking, but `&` can be removed.

```suggestion
            .map_err(|err| JailerError::FileOpen(PathBuf::from(b.proc_mounts_path), err))?;
```

Code after:
    hierarchies: HashMap<String, PathBuf>,
    mount_points: Vec<CgroupMountPoint>,
}

impl CgroupHierarchies {
    // Constructs a new cache of hierarchies and mount points
    // It will discover cgroup mount points and hierarchies configured
    // on the system and cache the info required to create cgroups later
    // within this hierarchies
    fn new(ver: u8, proc_mounts_path: &str) -> Result<Self, JailerError> {
        let mut h = CgroupHierarchies {
            hierarchies: HashMap::new(),
            mount_points: Vec::new(),
        };

        // search PROC_MOUNTS for cgroup mount points
        let f = File::open(proc_mounts_path)
            .map_err(|err| JailerError::FileOpen(PathBuf::from(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(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 {