mirror of
https://github.com/kata-containers/kata-containers.git
synced 2025-06-28 00:07:16 +00:00
logging: Replace nix::Error::EINVAL with more descriptive msgs
Replaces instances of anyhow!(nix::Error::EINVAL) with other messages to make it easier to debug. Fixes #954 Signed-off-by: Derek Lee <derlee@redhat.com>
This commit is contained in:
parent
873e75b915
commit
763ceeb7ba
@ -174,7 +174,7 @@ impl CgroupManager for Manager {
|
|||||||
freezer_controller.freeze()?;
|
freezer_controller.freeze()?;
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
return Err(anyhow!(nix::Error::EINVAL));
|
return Err(anyhow!("Invalid FreezerState"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -106,6 +106,11 @@ impl Default for ContainerStatus {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// We might want to change this to thiserror in the future
|
||||||
|
const MissingCGroupManager: &str = "failed to get container's cgroup Manager";
|
||||||
|
const MissingLinux: &str = "no linux config";
|
||||||
|
const InvalidNamespace: &str = "invalid namespace type";
|
||||||
|
|
||||||
pub type Config = CreateOpts;
|
pub type Config = CreateOpts;
|
||||||
type NamespaceType = String;
|
type NamespaceType = String;
|
||||||
|
|
||||||
@ -292,7 +297,7 @@ impl Container for LinuxContainer {
|
|||||||
self.status.transition(ContainerState::Paused);
|
self.status.transition(ContainerState::Paused);
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
Err(anyhow!("failed to get container's cgroup manager"))
|
Err(anyhow!(MissingCGroupManager))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn resume(&mut self) -> Result<()> {
|
fn resume(&mut self) -> Result<()> {
|
||||||
@ -310,7 +315,7 @@ impl Container for LinuxContainer {
|
|||||||
self.status.transition(ContainerState::Running);
|
self.status.transition(ContainerState::Running);
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
Err(anyhow!("failed to get container's cgroup manager"))
|
Err(anyhow!(MissingCGroupManager))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -397,7 +402,7 @@ fn do_init_child(cwfd: RawFd) -> Result<()> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
if spec.linux.is_none() {
|
if spec.linux.is_none() {
|
||||||
return Err(anyhow!("no linux config"));
|
return Err(anyhow!(MissingLinux));
|
||||||
}
|
}
|
||||||
let linux = spec.linux.as_ref().unwrap();
|
let linux = spec.linux.as_ref().unwrap();
|
||||||
|
|
||||||
@ -411,7 +416,7 @@ fn do_init_child(cwfd: RawFd) -> Result<()> {
|
|||||||
for ns in &nses {
|
for ns in &nses {
|
||||||
let s = NAMESPACES.get(&ns.r#type.as_str());
|
let s = NAMESPACES.get(&ns.r#type.as_str());
|
||||||
if s.is_none() {
|
if s.is_none() {
|
||||||
return Err(anyhow!("invalid ns type"));
|
return Err(anyhow!(InvalidNamespace));
|
||||||
}
|
}
|
||||||
let s = s.unwrap();
|
let s = s.unwrap();
|
||||||
|
|
||||||
@ -1437,18 +1442,10 @@ impl LinuxContainer {
|
|||||||
Some(unistd::getuid()),
|
Some(unistd::getuid()),
|
||||||
Some(unistd::getgid()),
|
Some(unistd::getgid()),
|
||||||
)
|
)
|
||||||
.context(format!("cannot change onwer of container {} root", id))?;
|
.context(format!("Cannot change owner of container {} root", id))?;
|
||||||
|
|
||||||
if config.spec.is_none() {
|
|
||||||
return Err(anyhow!(nix::Error::EINVAL));
|
|
||||||
}
|
|
||||||
|
|
||||||
let spec = config.spec.as_ref().unwrap();
|
let spec = config.spec.as_ref().unwrap();
|
||||||
|
|
||||||
if spec.linux.is_none() {
|
|
||||||
return Err(anyhow!(nix::Error::EINVAL));
|
|
||||||
}
|
|
||||||
|
|
||||||
let linux = spec.linux.as_ref().unwrap();
|
let linux = spec.linux.as_ref().unwrap();
|
||||||
|
|
||||||
let cpath = if linux.cgroups_path.is_empty() {
|
let cpath = if linux.cgroups_path.is_empty() {
|
||||||
@ -1525,7 +1522,7 @@ pub async fn execute_hook(logger: &Logger, h: &Hook, st: &OCIState) -> Result<()
|
|||||||
let binary = PathBuf::from(h.path.as_str());
|
let binary = PathBuf::from(h.path.as_str());
|
||||||
let path = binary.canonicalize()?;
|
let path = binary.canonicalize()?;
|
||||||
if !path.exists() {
|
if !path.exists() {
|
||||||
return Err(anyhow!(nix::Error::EINVAL));
|
return Err(anyhow!("Path {:?} does not exist", path));
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut args = h.args.clone();
|
let mut args = h.args.clone();
|
||||||
|
@ -1020,9 +1020,7 @@ pub fn finish_rootfs(cfd_log: RawFd, spec: &Spec, process: &Process) -> Result<(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn mask_path(path: &str) -> Result<()> {
|
fn mask_path(path: &str) -> Result<()> {
|
||||||
if !path.starts_with('/') || path.contains("..") {
|
check_paths(path)?;
|
||||||
return Err(anyhow!(nix::Error::EINVAL));
|
|
||||||
}
|
|
||||||
|
|
||||||
match mount(
|
match mount(
|
||||||
Some("/dev/null"),
|
Some("/dev/null"),
|
||||||
@ -1040,9 +1038,7 @@ fn mask_path(path: &str) -> Result<()> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn readonly_path(path: &str) -> Result<()> {
|
fn readonly_path(path: &str) -> Result<()> {
|
||||||
if !path.starts_with('/') || path.contains("..") {
|
check_paths(path)?;
|
||||||
return Err(anyhow!(nix::Error::EINVAL));
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Err(e) = mount(
|
if let Err(e) = mount(
|
||||||
Some(&path[1..]),
|
Some(&path[1..]),
|
||||||
@ -1068,6 +1064,16 @@ fn readonly_path(path: &str) -> Result<()> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn check_paths(path: &str) -> Result<()> {
|
||||||
|
if !path.starts_with('/') || path.contains("..") {
|
||||||
|
return Err(anyhow!(
|
||||||
|
"Cannot mount {} (path does not start with '/' or contains '..').",
|
||||||
|
path
|
||||||
|
));
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
@ -1420,6 +1426,55 @@ mod tests {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_check_paths() {
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct TestData<'a> {
|
||||||
|
name: &'a str,
|
||||||
|
path: &'a str,
|
||||||
|
result: Result<()>,
|
||||||
|
}
|
||||||
|
|
||||||
|
let tests = &[
|
||||||
|
TestData {
|
||||||
|
name: "valid path",
|
||||||
|
path: "/foo/bar",
|
||||||
|
result: Ok(()),
|
||||||
|
},
|
||||||
|
TestData {
|
||||||
|
name: "does not starts with /",
|
||||||
|
path: "foo/bar",
|
||||||
|
result: Err(anyhow!(
|
||||||
|
"Cannot mount foo/bar (path does not start with '/' or contains '..')."
|
||||||
|
)),
|
||||||
|
},
|
||||||
|
TestData {
|
||||||
|
name: "contains ..",
|
||||||
|
path: "../foo/bar",
|
||||||
|
result: Err(anyhow!(
|
||||||
|
"Cannot mount ../foo/bar (path does not start with '/' or contains '..')."
|
||||||
|
)),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
for (i, d) in tests.iter().enumerate() {
|
||||||
|
let msg = format!("test[{}]: {:?}", i, d.name);
|
||||||
|
|
||||||
|
let result = check_paths(d.path);
|
||||||
|
|
||||||
|
let msg = format!("{}: result: {:?}", msg, result);
|
||||||
|
|
||||||
|
if d.result.is_ok() {
|
||||||
|
assert!(result.is_ok());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let expected_error = format!("{}", d.result.as_ref().unwrap_err());
|
||||||
|
let actual_error = format!("{}", result.unwrap_err());
|
||||||
|
assert!(actual_error == expected_error, "{}", msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_check_proc_mount() {
|
fn test_check_proc_mount() {
|
||||||
let mount = oci::Mount {
|
let mount = oci::Mount {
|
||||||
|
@ -4,17 +4,15 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
use crate::container::Config;
|
use crate::container::Config;
|
||||||
use anyhow::{anyhow, Context, Error, Result};
|
use anyhow::{anyhow, Context, Result};
|
||||||
use oci::{Linux, LinuxIdMapping, LinuxNamespace, Spec};
|
use oci::{Linux, LinuxIdMapping, LinuxNamespace, Spec};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::path::{Component, PathBuf};
|
use std::path::{Component, PathBuf};
|
||||||
|
|
||||||
fn einval() -> Error {
|
|
||||||
anyhow!(nix::Error::EINVAL)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_linux(oci: &Spec) -> Result<&Linux> {
|
fn get_linux(oci: &Spec) -> Result<&Linux> {
|
||||||
oci.linux.as_ref().ok_or_else(einval)
|
oci.linux
|
||||||
|
.as_ref()
|
||||||
|
.ok_or_else(|| anyhow!("Unable to get Linux section from Spec"))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn contain_namespace(nses: &[LinuxNamespace], key: &str) -> bool {
|
fn contain_namespace(nses: &[LinuxNamespace], key: &str) -> bool {
|
||||||
@ -31,7 +29,10 @@ fn rootfs(root: &str) -> Result<()> {
|
|||||||
let path = PathBuf::from(root);
|
let path = PathBuf::from(root);
|
||||||
// not absolute path or not exists
|
// not absolute path or not exists
|
||||||
if !path.exists() || !path.is_absolute() {
|
if !path.exists() || !path.is_absolute() {
|
||||||
return Err(einval());
|
return Err(anyhow!(
|
||||||
|
"Path from {:?} does not exist or is not absolute",
|
||||||
|
root
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
// symbolic link? ..?
|
// symbolic link? ..?
|
||||||
@ -49,7 +50,7 @@ fn rootfs(root: &str) -> Result<()> {
|
|||||||
if let Some(v) = c.as_os_str().to_str() {
|
if let Some(v) = c.as_os_str().to_str() {
|
||||||
stack.push(v.to_string());
|
stack.push(v.to_string());
|
||||||
} else {
|
} else {
|
||||||
return Err(einval());
|
return Err(anyhow!("Invalid path component (unable to convert to str)"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -58,10 +59,13 @@ fn rootfs(root: &str) -> Result<()> {
|
|||||||
cleaned.push(e);
|
cleaned.push(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
let canon = path.canonicalize().context("canonicalize")?;
|
let canon = path.canonicalize().context("failed to canonicalize path")?;
|
||||||
if cleaned != canon {
|
if cleaned != canon {
|
||||||
// There is symbolic in path
|
// There is symbolic in path
|
||||||
return Err(einval());
|
return Err(anyhow!(
|
||||||
|
"There may be illegal symbols in the path name. Cleaned ({:?}) and canonicalized ({:?}) paths do not match",
|
||||||
|
cleaned,
|
||||||
|
canon));
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -74,7 +78,7 @@ fn hostname(oci: &Spec) -> Result<()> {
|
|||||||
|
|
||||||
let linux = get_linux(oci)?;
|
let linux = get_linux(oci)?;
|
||||||
if !contain_namespace(&linux.namespaces, "uts") {
|
if !contain_namespace(&linux.namespaces, "uts") {
|
||||||
return Err(einval());
|
return Err(anyhow!("Linux namespace does not contain uts"));
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -88,7 +92,7 @@ fn security(oci: &Spec) -> Result<()> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if !contain_namespace(&linux.namespaces, "mount") {
|
if !contain_namespace(&linux.namespaces, "mount") {
|
||||||
return Err(einval());
|
return Err(anyhow!("Linux namespace does not contain mount"));
|
||||||
}
|
}
|
||||||
|
|
||||||
// don't care about selinux at present
|
// don't care about selinux at present
|
||||||
@ -103,7 +107,7 @@ fn idmapping(maps: &[LinuxIdMapping]) -> Result<()> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Err(einval())
|
Err(anyhow!("No idmap has size > 0"))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn usernamespace(oci: &Spec) -> Result<()> {
|
fn usernamespace(oci: &Spec) -> Result<()> {
|
||||||
@ -121,7 +125,7 @@ fn usernamespace(oci: &Spec) -> Result<()> {
|
|||||||
} else {
|
} else {
|
||||||
// no user namespace but idmap
|
// no user namespace but idmap
|
||||||
if !linux.uid_mappings.is_empty() || !linux.gid_mappings.is_empty() {
|
if !linux.uid_mappings.is_empty() || !linux.gid_mappings.is_empty() {
|
||||||
return Err(einval());
|
return Err(anyhow!("No user namespace, but uid or gid mapping exists"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -163,7 +167,7 @@ fn sysctl(oci: &Spec) -> Result<()> {
|
|||||||
if contain_namespace(&linux.namespaces, "ipc") {
|
if contain_namespace(&linux.namespaces, "ipc") {
|
||||||
continue;
|
continue;
|
||||||
} else {
|
} else {
|
||||||
return Err(einval());
|
return Err(anyhow!("Linux namespace does not contain ipc"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -178,11 +182,11 @@ fn sysctl(oci: &Spec) -> Result<()> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if key == "kernel.hostname" {
|
if key == "kernel.hostname" {
|
||||||
return Err(einval());
|
return Err(anyhow!("Kernel hostname specfied in Spec"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return Err(einval());
|
return Err(anyhow!("Sysctl config contains invalid settings"));
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -191,12 +195,13 @@ fn rootless_euid_mapping(oci: &Spec) -> Result<()> {
|
|||||||
let linux = get_linux(oci)?;
|
let linux = get_linux(oci)?;
|
||||||
|
|
||||||
if !contain_namespace(&linux.namespaces, "user") {
|
if !contain_namespace(&linux.namespaces, "user") {
|
||||||
return Err(einval());
|
return Err(anyhow!("Linux namespace is missing user"));
|
||||||
}
|
}
|
||||||
|
|
||||||
if linux.uid_mappings.is_empty() || linux.gid_mappings.is_empty() {
|
if linux.uid_mappings.is_empty() || linux.gid_mappings.is_empty() {
|
||||||
// rootless containers requires at least one UID/GID mapping
|
return Err(anyhow!(
|
||||||
return Err(einval());
|
"Rootless containers require at least one UID/GID mapping"
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -220,7 +225,7 @@ fn rootless_euid_mount(oci: &Spec) -> Result<()> {
|
|||||||
let fields: Vec<&str> = opt.split('=').collect();
|
let fields: Vec<&str> = opt.split('=').collect();
|
||||||
|
|
||||||
if fields.len() != 2 {
|
if fields.len() != 2 {
|
||||||
return Err(einval());
|
return Err(anyhow!("Options has invalid field: {:?}", fields));
|
||||||
}
|
}
|
||||||
|
|
||||||
let id = fields[1]
|
let id = fields[1]
|
||||||
@ -229,11 +234,11 @@ fn rootless_euid_mount(oci: &Spec) -> Result<()> {
|
|||||||
.context(format!("parse field {}", &fields[1]))?;
|
.context(format!("parse field {}", &fields[1]))?;
|
||||||
|
|
||||||
if opt.starts_with("uid=") && !has_idmapping(&linux.uid_mappings, id) {
|
if opt.starts_with("uid=") && !has_idmapping(&linux.uid_mappings, id) {
|
||||||
return Err(einval());
|
return Err(anyhow!("uid of {} does not have a valid mapping", id));
|
||||||
}
|
}
|
||||||
|
|
||||||
if opt.starts_with("gid=") && !has_idmapping(&linux.gid_mappings, id) {
|
if opt.starts_with("gid=") && !has_idmapping(&linux.gid_mappings, id) {
|
||||||
return Err(einval());
|
return Err(anyhow!("gid of {} does not have a valid mapping", id));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -249,15 +254,18 @@ fn rootless_euid(oci: &Spec) -> Result<()> {
|
|||||||
|
|
||||||
pub fn validate(conf: &Config) -> Result<()> {
|
pub fn validate(conf: &Config) -> Result<()> {
|
||||||
lazy_static::initialize(&SYSCTLS);
|
lazy_static::initialize(&SYSCTLS);
|
||||||
let oci = conf.spec.as_ref().ok_or_else(einval)?;
|
let oci = conf
|
||||||
|
.spec
|
||||||
|
.as_ref()
|
||||||
|
.ok_or_else(|| anyhow!("Invalid config spec"))?;
|
||||||
|
|
||||||
if oci.linux.is_none() {
|
if oci.linux.is_none() {
|
||||||
return Err(einval());
|
return Err(anyhow!("oci Linux is none"));
|
||||||
}
|
}
|
||||||
|
|
||||||
let root = match oci.root.as_ref() {
|
let root = match oci.root.as_ref() {
|
||||||
Some(v) => v.path.as_str(),
|
Some(v) => v.path.as_str(),
|
||||||
None => return Err(einval()),
|
None => return Err(anyhow!("oci root is none")),
|
||||||
};
|
};
|
||||||
|
|
||||||
rootfs(root).context("rootfs")?;
|
rootfs(root).context("rootfs")?;
|
||||||
|
@ -523,7 +523,7 @@ impl Handle {
|
|||||||
.as_ref()
|
.as_ref()
|
||||||
.map(|to| to.address.as_str()) // Extract address field
|
.map(|to| to.address.as_str()) // Extract address field
|
||||||
.and_then(|addr| if addr.is_empty() { None } else { Some(addr) }) // Make sure it's not empty
|
.and_then(|addr| if addr.is_empty() { None } else { Some(addr) }) // Make sure it's not empty
|
||||||
.ok_or_else(|| anyhow!(nix::Error::EINVAL))?;
|
.ok_or_else(|| anyhow!("Unable to determine ip address of ARP neighbor"))?;
|
||||||
|
|
||||||
let ip = IpAddr::from_str(ip_address)
|
let ip = IpAddr::from_str(ip_address)
|
||||||
.map_err(|e| anyhow!("Failed to parse IP {}: {:?}", ip_address, e))?;
|
.map_err(|e| anyhow!("Failed to parse IP {}: {:?}", ip_address, e))?;
|
||||||
@ -612,7 +612,12 @@ fn parse_mac_address(addr: &str) -> Result<[u8; 6]> {
|
|||||||
|
|
||||||
// Parse single Mac address block
|
// Parse single Mac address block
|
||||||
let mut parse_next = || -> Result<u8> {
|
let mut parse_next = || -> Result<u8> {
|
||||||
let v = u8::from_str_radix(split.next().ok_or_else(|| anyhow!(nix::Error::EINVAL))?, 16)?;
|
let v = u8::from_str_radix(
|
||||||
|
split
|
||||||
|
.next()
|
||||||
|
.ok_or_else(|| anyhow!("Invalid MAC address {}", addr))?,
|
||||||
|
16,
|
||||||
|
)?;
|
||||||
Ok(v)
|
Ok(v)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -380,7 +380,7 @@ impl AgentService {
|
|||||||
let mut process = req
|
let mut process = req
|
||||||
.process
|
.process
|
||||||
.into_option()
|
.into_option()
|
||||||
.ok_or_else(|| anyhow!(nix::Error::EINVAL))?;
|
.ok_or_else(|| anyhow!("Unable to parse process from ExecProcessRequest"))?;
|
||||||
|
|
||||||
// Apply any necessary corrections for PCI addresses
|
// Apply any necessary corrections for PCI addresses
|
||||||
update_env_pci(&mut process.Env, &sandbox.pcimap)?;
|
update_env_pci(&mut process.Env, &sandbox.pcimap)?;
|
||||||
@ -629,7 +629,7 @@ impl AgentService {
|
|||||||
};
|
};
|
||||||
|
|
||||||
if reader.is_none() {
|
if reader.is_none() {
|
||||||
return Err(anyhow!(nix::Error::EINVAL));
|
return Err(anyhow!("Unable to determine stream reader, is None"));
|
||||||
}
|
}
|
||||||
|
|
||||||
let reader = reader.ok_or_else(|| anyhow!("cannot get stream reader"))?;
|
let reader = reader.ok_or_else(|| anyhow!("cannot get stream reader"))?;
|
||||||
@ -1843,7 +1843,11 @@ fn do_copy_file(req: &CopyFileRequest) -> Result<()> {
|
|||||||
let path = PathBuf::from(req.path.as_str());
|
let path = PathBuf::from(req.path.as_str());
|
||||||
|
|
||||||
if !path.starts_with(CONTAINER_BASE) {
|
if !path.starts_with(CONTAINER_BASE) {
|
||||||
return Err(anyhow!(nix::Error::EINVAL));
|
return Err(anyhow!(
|
||||||
|
"Path {:?} does not start with {}",
|
||||||
|
path,
|
||||||
|
CONTAINER_BASE
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
let parent = path.parent();
|
let parent = path.parent();
|
||||||
|
Loading…
Reference in New Issue
Block a user