Compare commits

..

6 Commits
v0.3.5 ... v0.3

Author SHA1 Message Date
Fabiano Fidêncio
6e273cd2fb Merge pull request #149 from kata-containers/bump-rust
Bump Rust version to 1.85.1
2025-07-09 09:37:32 +02:00
Xuewei Niu
bce4b0bb3b Fix the warning about config file
The cargo complaints that ".cargo/config" is deprecated in favor of
".cargo/config.toml". Therefore, the file is renamed to it to make cargo
happy.

Signed-off-by: Xuewei Niu <niuxuewei.nxw@antgroup.com>
2025-07-08 23:56:57 +08:00
Xuewei Niu
f5a67c632e Fix the issues that makes cargo clippy unhappy
Most of the issues are small, but `fold()`. The usage of `fold()` in these
cases confuses me a lot, so that I don't follow the sugguestions from
clippy to replace `fold()` with `try_fold()`. Instead, I replace them with
`map() + collect()`.

Signed-off-by: Xuewei Niu <niuxuewei.nxw@antgroup.com>
2025-07-08 23:56:55 +08:00
Xuewei Niu
c8febbc67d .github: Bump Rust version to 1.85.1
Rust 1.69.0, released in 2023, lags significantly behind the version used
by Kata Containers. Therefore, we bump it to 1.85.1.

Signed-off-by: Xuewei Niu <niuxuewei.nxw@antgroup.com>
2025-07-08 17:07:52 +08:00
Fupan Li
2536e33696 Merge pull request #143 from oguzbilgener/no-regex
refactor: Parse HugeTLB size without Regex
2025-06-12 10:23:34 +08:00
Oguz Bilgener
694627e2fc refactor: Parse HugeTLB size without Regex
This removes the only place the regex crate is used, which could lead to significant binary size savings in projects that don't use the regex craate.

Signed-off-by: Oguz Bilgener <oguz@bilgener.me>
2025-05-29 11:05:53 -04:00
10 changed files with 161 additions and 117 deletions

View File

@@ -1,7 +1,7 @@
name: BVT
on: [pull_request]
env:
RUST_VERSION: 1.69.0
RUST_VERSION: 1.85.1
jobs:
build:
name: Build

View File

@@ -13,7 +13,6 @@ readme = "README.md"
[dependencies]
log = "0.4"
regex = "1.1"
nix = { version = "0.25.0", default-features = false, features = ["event", "fs", "process"] }
libc = "0.2"
serde = { version = "1.0", features = ["derive"], optional = true }

View File

@@ -181,13 +181,14 @@ fn parse_io_stat(s: String) -> Vec<IoStat> {
fn parse_io_service_total(s: String) -> Result<u64> {
s.lines()
.filter(|x| x.split_whitespace().count() == 2)
.fold(Err(Error::new(ParseError)), |_, x| {
match x.split_whitespace().collect::<Vec<_>>().as_slice() {
["Total", val] => val.parse::<u64>().map_err(|_| Error::new(ParseError)),
_ => Err(Error::new(ParseError)),
.find_map(|line| {
let mut parts = line.split_whitespace();
match (parts.next(), parts.next(), parts.next()) {
(Some("Total"), Some(val), None) => val.parse::<u64>().ok(),
_ => None,
}
})
.ok_or_else(|| Error::new(ParseError))
}
fn parse_blkio_data(s: String) -> Result<Vec<BlkIoData>> {

View File

@@ -602,7 +602,7 @@ mod tests {
"1,2,3,4".to_string(),
"1-5,6-7,8-9".to_string(),
];
let expecteds = vec![
let expecteds = [
vec![(1, 1), (2, 2), (4, 6), (9, 9)],
vec![],
vec![(1, 1)],

View File

@@ -295,43 +295,48 @@ impl DevicesController {
let mut s = String::new();
let res = file.read_to_string(&mut s);
match res {
Ok(_) => {
s.lines().fold(Ok(Vec::new()), |acc, line| {
let ls = line.split(|c| c == ' ' || c == ':').map(|x| x.to_string()).collect::<Vec<String>>();
if acc.is_err() || ls.len() != 4 {
error!("allowed_devices: acc: {:?}, ls: {:?}", acc, ls);
Err(Error::new(ParseError))
} else {
let devtype = DeviceType::from_char(ls[0].chars().next());
let mut major = ls[1].parse::<i64>();
let mut minor = ls[2].parse::<i64>();
if major.is_err() && ls[1] == "*" {
major = Ok(-1);
}
if minor.is_err() && ls[2] == "*" {
minor = Ok(-1);
}
if devtype.is_none() || major.is_err() || minor.is_err() || !DevicePermissions::is_valid(&ls[3]) {
error!("allowed_devices: acc: {:?}, ls: {:?}, devtype: {:?}, major {:?} minor {:?} ls3 {:?}",
acc, ls, devtype, major, minor, &ls[3]);
Err(Error::new(ParseError))
} else {
let access = DevicePermissions::from_str(&ls[3])?;
let mut acc = acc.unwrap();
acc.push(DeviceResource {
allow: true,
devtype: devtype.unwrap(),
major: major.unwrap(),
minor: minor.unwrap(),
access,
});
Ok(acc)
}
}
})
},
Ok(_) => s
.lines()
.map(|line| parse_device_line(line, true))
.collect(),
Err(e) => Err(Error::with_cause(ReadFailed("devices.list".to_string()), e)),
}
})
}
}
fn parse_device_number(s: &str) -> Result<i64> {
if s == "*" {
Ok(-1)
} else {
s.parse::<i64>().map_err(|_| Error::new(ParseError))
}
}
fn parse_device_line(line: &str, allow: bool) -> Result<DeviceResource> {
let parts: Vec<&str> = line.split([' ', ':']).collect();
if parts.len() != 4 {
error!("allowed_devices: invalid line format: {:?}", line);
return Err(Error::new(ParseError));
}
let devtype = DeviceType::from_char(parts[0].chars().next()).ok_or_else(|| {
error!("allowed_devices: invalid device type: {:?}", parts[0]);
Error::new(ParseError)
})?;
let major = parse_device_number(parts[1]).inspect_err(|_| {
error!("allowed_devices: invalid major number: {:?}", parts[1]);
})?;
let minor = parse_device_number(parts[2]).inspect_err(|_| {
error!("allowed_devices: invalid minor number: {:?}", parts[2]);
})?;
let access = DevicePermissions::from_str(parts[3])?;
Ok(DeviceResource {
allow,
devtype,
major,
minor,
access,
})
}

View File

@@ -181,7 +181,6 @@ impl HugeTlbController {
}
pub const HUGEPAGESIZE_DIR: &str = "/sys/kernel/mm/hugepages";
use regex::Regex;
use std::collections::HashMap;
use std::fs;
@@ -263,37 +262,46 @@ pub fn get_decimal_abbrs() -> Vec<String> {
}
fn parse_size(s: &str, m: &HashMap<String, u128>) -> Result<u128> {
let re = Regex::new(r"(?P<num>\d+)(?P<mul>[kKmMgGtTpP]?)[bB]?$");
// Remove leading/trailing whitespace.
let s = s.trim();
if re.is_err() {
// Remove an optional trailing 'b' or 'B'
let s = if let Some(stripped) = s.strip_suffix('b').or_else(|| s.strip_suffix('B')) {
stripped
} else {
s
};
// Ensure that the string is not empty after stripping.
if s.is_empty() {
return Err(Error::new(InvalidBytesSize));
}
let caps = re.unwrap().captures(s).unwrap();
let num = caps.name("num");
let size: u128 = if let Some(num) = num {
let n = num.as_str().trim().parse::<u128>();
if n.is_err() {
return Err(Error::new(InvalidBytesSize));
}
n.unwrap()
} else {
// The last character should be the multiplier letter.
let last_char = s.chars().last().unwrap();
if !"kKmMgGtTpP".contains(last_char) {
return Err(Error::new(InvalidBytesSize));
};
}
let q = caps.name("mul");
let mul: u128 = if let Some(q) = q {
let t = m.get(q.as_str());
if let Some(t) = t {
*t
} else {
return Err(Error::new(InvalidBytesSize));
}
} else {
// The numeric part is everything before the multiplier letter.
let num_part = &s[..s.len() - last_char.len_utf8()];
if num_part.trim().is_empty() {
return Err(Error::new(InvalidBytesSize));
};
}
Ok(size * mul)
// Parse the numeric part into a u128.
let number: u128 = num_part
.trim()
.parse()
.map_err(|_| Error::new(InvalidBytesSize))?;
// Look up the multiplier in the provided HashMap.
let multiplier_key = last_char.to_string();
let multiplier = m
.get(&multiplier_key)
.ok_or_else(|| Error::new(InvalidBytesSize))?;
Ok(number * multiplier)
}
fn custom_size(mut size: f64, base: f64, m: &[String]) -> String {
@@ -305,3 +313,60 @@ fn custom_size(mut size: f64, base: f64, m: &[String]) -> String {
format!("{}{}", size, m[i].as_str())
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_binary_size_valid() {
let m = get_binary_size_map();
// Valid inputs must include a multiplier letter.
assert_eq!(parse_size("1k", &m).unwrap(), KiB);
assert_eq!(parse_size("2m", &m).unwrap(), 2 * MiB);
assert_eq!(parse_size("3g", &m).unwrap(), 3 * GiB);
assert_eq!(parse_size("4t", &m).unwrap(), 4 * TiB);
assert_eq!(parse_size("5p", &m).unwrap(), 5 * PiB);
}
#[test]
fn test_decimal_size_valid() {
let m = get_decimal_size_map();
assert_eq!(parse_size("1k", &m).unwrap(), KB);
assert_eq!(parse_size("2m", &m).unwrap(), 2 * MB);
assert_eq!(parse_size("3g", &m).unwrap(), 3 * GB);
assert_eq!(parse_size("4t", &m).unwrap(), 4 * TB);
assert_eq!(parse_size("5p", &m).unwrap(), 5 * PB);
}
#[test]
fn test_trailing_b_suffix() {
let m = get_binary_size_map();
// Trailing 'b' or 'B' should be accepted.
assert_eq!(parse_size("1kb", &m).unwrap(), KiB);
assert_eq!(parse_size("2mB", &m).unwrap(), 2 * MiB);
}
#[test]
fn test_invalid_inputs() {
let m = get_binary_size_map();
// Missing multiplier letter results in error.
assert!(parse_size("1", &m).is_err());
// Invalid multiplier letter.
assert!(parse_size("10x", &m).is_err());
// Non-numeric input.
assert!(parse_size("abc", &m).is_err());
// Only multiplier letter with no number.
assert!(parse_size("k", &m).is_err());
// Number with an invalid trailing character.
assert!(parse_size("123z", &m).is_err());
}
#[test]
fn test_uppercase_multiplier_fails() {
let m = get_binary_size_map();
// Although the regex matches uppercase letters, the provided map only contains lowercase keys.
// Therefore, "1K" does not match any key and should produce an error.
assert!(parse_size("1K", &m).is_err());
}
}

View File

@@ -785,7 +785,7 @@ impl From<u64> for CgroupPid {
}
}
impl<'a> From<&'a std::process::Child> for CgroupPid {
impl From<&std::process::Child> for CgroupPid {
fn from(u: &std::process::Child) -> CgroupPid {
CgroupPid { pid: u.id() as u64 }
}

View File

@@ -142,9 +142,7 @@ fn parse_numa_stat(s: String) -> Result<NumaStat> {
let hier_unevict_line = ls.next().unwrap_or_default();
Ok(NumaStat {
total_pages: total_line
.split(|x| x == ' ' || x == '=')
.collect::<Vec<_>>()[1]
total_pages: total_line.split([' ', '=']).collect::<Vec<_>>()[1]
.parse::<u64>()
.unwrap_or(0),
total_pages_per_node: {
@@ -157,9 +155,7 @@ fn parse_numa_stat(s: String) -> Result<NumaStat> {
})
.collect()
},
file_pages: file_line
.split(|x| x == ' ' || x == '=')
.collect::<Vec<_>>()[1]
file_pages: file_line.split([' ', '=']).collect::<Vec<_>>()[1]
.parse::<u64>()
.unwrap_or(0),
file_pages_per_node: {
@@ -172,9 +168,7 @@ fn parse_numa_stat(s: String) -> Result<NumaStat> {
})
.collect()
},
anon_pages: anon_line
.split(|x| x == ' ' || x == '=')
.collect::<Vec<_>>()[1]
anon_pages: anon_line.split([' ', '=']).collect::<Vec<_>>()[1]
.parse::<u64>()
.unwrap_or(0),
anon_pages_per_node: {
@@ -187,9 +181,7 @@ fn parse_numa_stat(s: String) -> Result<NumaStat> {
})
.collect()
},
unevictable_pages: unevict_line
.split(|x| x == ' ' || x == '=')
.collect::<Vec<_>>()[1]
unevictable_pages: unevict_line.split([' ', '=']).collect::<Vec<_>>()[1]
.parse::<u64>()
.unwrap_or(0),
unevictable_pages_per_node: {
@@ -204,9 +196,7 @@ fn parse_numa_stat(s: String) -> Result<NumaStat> {
},
hierarchical_total_pages: {
if !hier_total_line.is_empty() {
hier_total_line
.split(|x| x == ' ' || x == '=')
.collect::<Vec<_>>()[1]
hier_total_line.split([' ', '=']).collect::<Vec<_>>()[1]
.parse::<u64>()
.unwrap_or(0)
} else {
@@ -229,9 +219,7 @@ fn parse_numa_stat(s: String) -> Result<NumaStat> {
},
hierarchical_file_pages: {
if !hier_file_line.is_empty() {
hier_file_line
.split(|x| x == ' ' || x == '=')
.collect::<Vec<_>>()[1]
hier_file_line.split([' ', '=']).collect::<Vec<_>>()[1]
.parse::<u64>()
.unwrap_or(0)
} else {
@@ -254,9 +242,7 @@ fn parse_numa_stat(s: String) -> Result<NumaStat> {
},
hierarchical_anon_pages: {
if !hier_anon_line.is_empty() {
hier_anon_line
.split(|x| x == ' ' || x == '=')
.collect::<Vec<_>>()[1]
hier_anon_line.split([' ', '=']).collect::<Vec<_>>()[1]
.parse::<u64>()
.unwrap_or(0)
} else {
@@ -279,9 +265,7 @@ fn parse_numa_stat(s: String) -> Result<NumaStat> {
},
hierarchical_unevictable_pages: {
if !hier_unevict_line.is_empty() {
hier_unevict_line
.split(|x| x == ' ' || x == '=')
.collect::<Vec<_>>()[1]
hier_unevict_line.split([' ', '=']).collect::<Vec<_>>()[1]
.parse::<u64>()
.unwrap_or(0)
} else {

View File

@@ -94,36 +94,26 @@ impl NetPrioController {
}
/// A map of priorities for each network interface.
#[allow(clippy::iter_nth_zero, clippy::unnecessary_unwrap)]
pub fn ifpriomap(&self) -> Result<HashMap<String, u64>> {
self.open_path("net_prio.ifpriomap", false)
.and_then(|file| {
let bf = BufReader::new(file);
bf.lines().fold(Ok(HashMap::new()), |acc, line| {
if acc.is_err() {
acc
} else {
let mut acc = acc.unwrap();
let l = line.unwrap();
let mut sp = l.split_whitespace();
bf.lines()
.map(|line| {
let line = line.map_err(|_| Error::new(ParseError))?;
let mut parts = line.split_whitespace();
let ifname = sp.nth(0);
let ifprio = sp.nth(1);
if ifname.is_none() || ifprio.is_none() {
Err(Error::new(ParseError))
} else {
let ifname = ifname.unwrap();
let ifprio = ifprio.unwrap().trim().parse();
match ifprio {
Err(e) => Err(Error::with_cause(ParseError, e)),
Ok(_) => {
acc.insert(ifname.to_string(), ifprio.unwrap());
Ok(acc)
}
}
}
}
})
let ifname = parts.next().ok_or(Error::new(ParseError))?;
let ifprio_str = parts.next().ok_or(Error::new(ParseError))?;
let ifprio = ifprio_str
.trim()
.parse()
.map_err(|e| Error::with_cause(ParseError, e))?;
Ok((ifname.to_string(), ifprio))
})
.collect::<Result<HashMap<String, _>>>()
})
}