agent: Align agent OCI spec with oci-spec-rs

Fixes #9766

Signed-off-by: Alex Lyn <alex.lyn@antgroup.com>
This commit is contained in:
Alex Lyn 2024-07-16 16:41:23 +08:00
parent 882385858d
commit b56313472b
26 changed files with 1741 additions and 2401 deletions

217
src/agent/Cargo.lock generated
View File

@ -191,9 +191,9 @@ dependencies = [
[[package]] [[package]]
name = "async-executor" name = "async-executor"
version = "1.12.0" version = "1.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c8828ec6e544c02b0d6691d21ed9f9218d0384a82542855073c2a3f58304aaf0" checksum = "d7ebdfa2ebdab6b1760375fa7d6f382b9f486eac35fc994625a00e89280bdbb7"
dependencies = [ dependencies = [
"async-task", "async-task",
"concurrent-queue", "concurrent-queue",
@ -324,7 +324,7 @@ checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.70", "syn 2.0.71",
] ]
[[package]] [[package]]
@ -386,7 +386,7 @@ checksum = "6e0c28dcc82d7c8ead5cb13beb15405b57b8546e93215673ff8ca0349a028107"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.70", "syn 2.0.71",
] ]
[[package]] [[package]]
@ -560,7 +560,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "afb15541e888071f64592c0b4364fdff21b7cb0a247f984296699351963a8721" checksum = "afb15541e888071f64592c0b4364fdff21b7cb0a247f984296699351963a8721"
dependencies = [ dependencies = [
"quote", "quote",
"syn 2.0.70", "syn 2.0.71",
] ]
[[package]] [[package]]
@ -656,7 +656,7 @@ dependencies = [
"proc-macro-crate 3.1.0", "proc-macro-crate 3.1.0",
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.70", "syn 2.0.71",
"syn_derive", "syn_derive",
] ]
@ -729,9 +729,9 @@ dependencies = [
[[package]] [[package]]
name = "bytes" name = "bytes"
version = "1.6.0" version = "1.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "514de17de45fdb8dc022b1a7975556c53c86f9f0aa5f534b98977b171857c2c9" checksum = "a12916984aab3fa6e39d655a33e09c0071eb36d6ab3aea5c2d78551f1df6d952"
[[package]] [[package]]
name = "bzip2" name = "bzip2"
@ -805,13 +805,12 @@ dependencies = [
[[package]] [[package]]
name = "cc" name = "cc"
version = "1.0.106" version = "1.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "066fce287b1d4eafef758e89e09d724a24808a9196fe9756b8ca90e86d0719a2" checksum = "324c74f2155653c90b04f25b2a47a8a631360cb908f92a772695f430c7e31052"
dependencies = [ dependencies = [
"jobserver", "jobserver",
"libc", "libc",
"once_cell",
] ]
[[package]] [[package]]
@ -979,7 +978,7 @@ version = "4.6.7"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ba5a308b75df32fe02788e748662718f03fde005016435c444eea572398219fd" checksum = "ba5a308b75df32fe02788e748662718f03fde005016435c444eea572398219fd"
dependencies = [ dependencies = [
"bytes 1.6.0", "bytes 1.6.1",
"memchr", "memchr",
] ]
@ -1250,7 +1249,7 @@ checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.70", "syn 2.0.71",
] ]
[[package]] [[package]]
@ -1275,12 +1274,12 @@ dependencies = [
[[package]] [[package]]
name = "darling" name = "darling"
version = "0.20.9" version = "0.20.10"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "83b2eb4d90d12bdda5ed17de686c2acb4c57914f8f921b8da7e112b5a36f3fe1" checksum = "6f63b86c8a8826a49b8c21f08a2d07338eec8d900540f8630dc76284be802989"
dependencies = [ dependencies = [
"darling_core 0.20.9", "darling_core 0.20.10",
"darling_macro 0.20.9", "darling_macro 0.20.10",
] ]
[[package]] [[package]]
@ -1312,16 +1311,16 @@ dependencies = [
[[package]] [[package]]
name = "darling_core" name = "darling_core"
version = "0.20.9" version = "0.20.10"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "622687fe0bac72a04e5599029151f5796111b90f1baaa9b544d807a5e31cd120" checksum = "95133861a8032aaea082871032f5815eb9e98cef03fa916ab4500513994df9e5"
dependencies = [ dependencies = [
"fnv", "fnv",
"ident_case", "ident_case",
"proc-macro2", "proc-macro2",
"quote", "quote",
"strsim 0.11.1", "strsim 0.11.1",
"syn 2.0.70", "syn 2.0.71",
] ]
[[package]] [[package]]
@ -1348,13 +1347,13 @@ dependencies = [
[[package]] [[package]]
name = "darling_macro" name = "darling_macro"
version = "0.20.9" version = "0.20.10"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "733cabb43482b1a1b53eee8583c2b9e8684d592215ea83efd305dd31bc2f0178" checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806"
dependencies = [ dependencies = [
"darling_core 0.20.9", "darling_core 0.20.10",
"quote", "quote",
"syn 2.0.70", "syn 2.0.71",
] ]
[[package]] [[package]]
@ -1387,13 +1386,13 @@ dependencies = [
[[package]] [[package]]
name = "der_derive" name = "der_derive"
version = "0.7.2" version = "0.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5fe87ce4529967e0ba1dcf8450bab64d97dfd5010a6256187ffe2e43e6f0e049" checksum = "8034092389675178f570469e6c3b0465d3d30b4505c294a6550db47f3c17ad18"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.70", "syn 2.0.71",
] ]
[[package]] [[package]]
@ -1442,10 +1441,10 @@ version = "0.20.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d48cda787f839151732d396ac69e3473923d54312c070ee21e9effcaa8ca0b1d" checksum = "d48cda787f839151732d396ac69e3473923d54312c070ee21e9effcaa8ca0b1d"
dependencies = [ dependencies = [
"darling 0.20.9", "darling 0.20.10",
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.70", "syn 2.0.71",
] ]
[[package]] [[package]]
@ -1455,7 +1454,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "206868b8242f27cecce124c19fd88157fbd0dd334df2587f36417bafbc85097b" checksum = "206868b8242f27cecce124c19fd88157fbd0dd334df2587f36417bafbc85097b"
dependencies = [ dependencies = [
"derive_builder_core", "derive_builder_core",
"syn 2.0.70", "syn 2.0.71",
] ]
[[package]] [[package]]
@ -1508,7 +1507,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.70", "syn 2.0.71",
] ]
[[package]] [[package]]
@ -1658,7 +1657,7 @@ checksum = "de0d48a183585823424a4ce1aa132d174a6a81bd540895822eb4c8373a8e49e8"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.70", "syn 2.0.71",
] ]
[[package]] [[package]]
@ -1804,9 +1803,9 @@ checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80"
[[package]] [[package]]
name = "flagset" name = "flagset"
version = "0.4.5" version = "0.4.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cdeb3aa5e95cf9aabc17f060cfa0ced7b83f042390760ca53bf09df9968acaa1" checksum = "b3ea1ec5f8307826a5b71094dd91fc04d4ae75d5709b20ad351c7fb4815c86ec"
[[package]] [[package]]
name = "flate2" name = "flate2"
@ -1923,7 +1922,7 @@ checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.70", "syn 2.0.71",
] ]
[[package]] [[package]]
@ -2172,7 +2171,7 @@ version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "21b9ddb458710bc376481b842f5da65cdf31522de232c1ca8146abce2a358258" checksum = "21b9ddb458710bc376481b842f5da65cdf31522de232c1ca8146abce2a358258"
dependencies = [ dependencies = [
"bytes 1.6.0", "bytes 1.6.1",
"fnv", "fnv",
"itoa", "itoa",
] ]
@ -2188,11 +2187,11 @@ dependencies = [
[[package]] [[package]]
name = "http-body" name = "http-body"
version = "1.0.0" version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1cac85db508abc24a2e48553ba12a996e87244a0395ce011e62b37158745d643" checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184"
dependencies = [ dependencies = [
"bytes 1.6.0", "bytes 1.6.1",
"http", "http",
] ]
@ -2202,7 +2201,7 @@ version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "793429d76616a256bcb62c2a2ec2bed781c8307e797e2598c50010f2bee2544f" checksum = "793429d76616a256bcb62c2a2ec2bed781c8307e797e2598c50010f2bee2544f"
dependencies = [ dependencies = [
"bytes 1.6.0", "bytes 1.6.1",
"futures-util", "futures-util",
"http", "http",
"http-body", "http-body",
@ -2217,11 +2216,11 @@ checksum = "0fcc0b4a115bf80b728eb8ea024ad5bd707b615bfed49e0665b6e0f86fd082d9"
[[package]] [[package]]
name = "hyper" name = "hyper"
version = "1.4.0" version = "1.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c4fe55fb7a772d59a5ff1dfbff4fe0258d19b89fec4b233e75d35d5d2316badc" checksum = "50dfd22e0e76d0f662d429a5f80fcaf3855009297eab6a0a9f8543834744ba05"
dependencies = [ dependencies = [
"bytes 1.6.0", "bytes 1.6.1",
"futures-channel", "futures-channel",
"futures-util", "futures-util",
"http", "http",
@ -2258,7 +2257,7 @@ version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3ab92f4f49ee4fb4f997c784b7a2e0fa70050211e0b6a287f898c3c9785ca956" checksum = "3ab92f4f49ee4fb4f997c784b7a2e0fa70050211e0b6a287f898c3c9785ca956"
dependencies = [ dependencies = [
"bytes 1.6.0", "bytes 1.6.1",
"futures-channel", "futures-channel",
"futures-util", "futures-util",
"http", "http",
@ -2410,7 +2409,7 @@ checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.70", "syn 2.0.71",
] ]
[[package]] [[package]]
@ -2795,7 +2794,7 @@ dependencies = [
"netlink-packet-utils", "netlink-packet-utils",
"netlink-sys", "netlink-sys",
"nix 0.24.3", "nix 0.24.3",
"oci", "oci-spec",
"opentelemetry", "opentelemetry",
"procfs 0.12.0", "procfs 0.12.0",
"prometheus", "prometheus",
@ -2805,6 +2804,7 @@ dependencies = [
"regorus", "regorus",
"rstest", "rstest",
"rtnetlink", "rtnetlink",
"runtime-spec",
"rustjail", "rustjail",
"safe-path", "safe-path",
"scan_fmt", "scan_fmt",
@ -2848,9 +2848,10 @@ dependencies = [
"lazy_static", "lazy_static",
"libc", "libc",
"nix 0.24.3", "nix 0.24.3",
"oci", "oci-spec",
"once_cell", "once_cell",
"rand", "rand",
"runtime-spec",
"safe-path", "safe-path",
"serde", "serde",
"serde_json", "serde_json",
@ -2871,7 +2872,7 @@ dependencies = [
"glob", "glob",
"lazy_static", "lazy_static",
"num_cpus", "num_cpus",
"oci", "oci-spec",
"regex", "regex",
"safe-path", "safe-path",
"serde", "serde",
@ -3354,7 +3355,7 @@ version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ddd06e90449ae973fe3888c1ff85949604ef5189b4ac9a2ae39518da1e00762d" checksum = "ddd06e90449ae973fe3888c1ff85949604ef5189b4ac9a2ae39518da1e00762d"
dependencies = [ dependencies = [
"bytes 1.6.0", "bytes 1.6.1",
"futures", "futures",
"log", "log",
"netlink-packet-core", "netlink-packet-core",
@ -3625,23 +3626,13 @@ dependencies = [
"memchr", "memchr",
] ]
[[package]]
name = "oci"
version = "0.1.0"
dependencies = [
"libc",
"serde",
"serde_derive",
"serde_json",
]
[[package]] [[package]]
name = "oci-distribution" name = "oci-distribution"
version = "0.11.0" version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b95a2c51531af0cb93761f66094044ca6ea879320bccd35ab747ff3fcab3f422" checksum = "b95a2c51531af0cb93761f66094044ca6ea879320bccd35ab747ff3fcab3f422"
dependencies = [ dependencies = [
"bytes 1.6.0", "bytes 1.6.1",
"chrono", "chrono",
"futures-util", "futures-util",
"http", "http",
@ -3662,12 +3653,14 @@ dependencies = [
[[package]] [[package]]
name = "oci-spec" name = "oci-spec"
version = "0.6.7" version = "0.6.8"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bdf88ddc01cc6bccbe1044adb6a29057333f523deadcb4953c011a73158cfa5e" checksum = "3f5a3fe998d50101ae009351fec56d88a69f4ed182e11000e711068c2f5abf72"
dependencies = [ dependencies = [
"derive_builder", "derive_builder",
"getset", "getset",
"once_cell",
"regex",
"serde", "serde",
"serde_json", "serde_json",
"strum 0.26.3", "strum 0.26.3",
@ -3846,7 +3839,7 @@ checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8"
dependencies = [ dependencies = [
"cfg-if 1.0.0", "cfg-if 1.0.0",
"libc", "libc",
"redox_syscall 0.5.2", "redox_syscall 0.5.3",
"smallvec", "smallvec",
"windows-targets 0.52.6", "windows-targets 0.52.6",
] ]
@ -3974,7 +3967,7 @@ checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.70", "syn 2.0.71",
] ]
[[package]] [[package]]
@ -4238,7 +4231,7 @@ version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "de5e2533f59d08fcf364fd374ebda0692a70bd6d7e66ef97f306f45c6c5d8020" checksum = "de5e2533f59d08fcf364fd374ebda0692a70bd6d7e66ef97f306f45c6c5d8020"
dependencies = [ dependencies = [
"bytes 1.6.0", "bytes 1.6.1",
"prost-derive", "prost-derive",
] ]
@ -4248,7 +4241,7 @@ version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "355f634b43cdd80724ee7848f95770e7e70eefa6dcf14fea676216573b8fd603" checksum = "355f634b43cdd80724ee7848f95770e7e70eefa6dcf14fea676216573b8fd603"
dependencies = [ dependencies = [
"bytes 1.6.0", "bytes 1.6.1",
"heck 0.3.3", "heck 0.3.3",
"itertools 0.10.5", "itertools 0.10.5",
"log", "log",
@ -4279,7 +4272,7 @@ version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "603bbd6394701d13f3f25aada59c7de9d35a6a5887cfc156181234a44002771b" checksum = "603bbd6394701d13f3f25aada59c7de9d35a6a5887cfc156181234a44002771b"
dependencies = [ dependencies = [
"bytes 1.6.0", "bytes 1.6.1",
"prost", "prost",
] ]
@ -4354,7 +4347,8 @@ name = "protocols"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"async-trait", "async-trait",
"oci", "kata-sys-util",
"oci-spec",
"protobuf 3.5.0", "protobuf 3.5.0",
"serde", "serde",
"serde_json", "serde_json",
@ -4404,7 +4398,7 @@ version = "0.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e4ceeeeabace7857413798eb1ffa1e9c905a9946a57d81fb69b4b71c4d8eb3ad" checksum = "e4ceeeeabace7857413798eb1ffa1e9c905a9946a57d81fb69b4b71c4d8eb3ad"
dependencies = [ dependencies = [
"bytes 1.6.0", "bytes 1.6.1",
"pin-project-lite", "pin-project-lite",
"quinn-proto", "quinn-proto",
"quinn-udp", "quinn-udp",
@ -4421,7 +4415,7 @@ version = "0.11.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ddf517c03a109db8100448a4be38d498df8a210a99fe0e1b9eaf39e78c640efe" checksum = "ddf517c03a109db8100448a4be38d498df8a210a99fe0e1b9eaf39e78c640efe"
dependencies = [ dependencies = [
"bytes 1.6.0", "bytes 1.6.1",
"rand", "rand",
"ring", "ring",
"rustc-hash", "rustc-hash",
@ -4539,9 +4533,9 @@ dependencies = [
[[package]] [[package]]
name = "redox_syscall" name = "redox_syscall"
version = "0.5.2" version = "0.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c82cf8cff14456045f55ec4241383baeff27af886adb72ffb2162f99911de0fd" checksum = "2a908a6e00f1fdd0dfd9c0eb08ce85126f6d8bbda50017e74bc4a4b7d4a926a4"
dependencies = [ dependencies = [
"bitflags 2.6.0", "bitflags 2.6.0",
] ]
@ -4640,7 +4634,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c7d6d2a27d57148378eb5e111173f4276ad26340ecc5c49a4a2152167a2d6a37" checksum = "c7d6d2a27d57148378eb5e111173f4276ad26340ecc5c49a4a2152167a2d6a37"
dependencies = [ dependencies = [
"base64 0.22.1", "base64 0.22.1",
"bytes 1.6.0", "bytes 1.6.1",
"cookie", "cookie",
"cookie_store", "cookie_store",
"futures-core", "futures-core",
@ -4732,7 +4726,7 @@ checksum = "5cba464629b3394fc4dbc6f940ff8f5b4ff5c7aef40f29166fd4ad12acbc99c0"
dependencies = [ dependencies = [
"bitvec", "bitvec",
"bytecheck", "bytecheck",
"bytes 1.6.0", "bytes 1.6.1",
"hashbrown 0.12.3", "hashbrown 0.12.3",
"ptr_meta", "ptr_meta",
"rend", "rend",
@ -4808,7 +4802,7 @@ dependencies = [
"regex", "regex",
"relative-path", "relative-path",
"rustc_version", "rustc_version",
"syn 2.0.70", "syn 2.0.71",
"unicode-ident", "unicode-ident",
] ]
@ -4827,6 +4821,16 @@ dependencies = [
"tokio", "tokio",
] ]
[[package]]
name = "runtime-spec"
version = "0.1.0"
dependencies = [
"libc",
"serde",
"serde_derive",
"serde_json",
]
[[package]] [[package]]
name = "rust_decimal" name = "rust_decimal"
version = "1.35.0" version = "1.35.0"
@ -4835,7 +4839,7 @@ checksum = "1790d1c4c0ca81211399e0e0af16333276f375209e71a37b67698a373db5b47a"
dependencies = [ dependencies = [
"arrayvec", "arrayvec",
"borsh", "borsh",
"bytes 1.6.0", "bytes 1.6.1",
"num-traits", "num-traits",
"rand", "rand",
"rkyv", "rkyv",
@ -4910,12 +4914,13 @@ dependencies = [
"libc", "libc",
"libseccomp", "libseccomp",
"nix 0.24.3", "nix 0.24.3",
"oci", "oci-spec",
"path-absolutize", "path-absolutize",
"protobuf 3.5.0", "protobuf 3.5.0",
"protocols", "protocols",
"regex", "regex",
"rlimit", "rlimit",
"runtime-spec",
"scan_fmt", "scan_fmt",
"scopeguard", "scopeguard",
"serde", "serde",
@ -5042,7 +5047,7 @@ checksum = "d2ee4885492bb655bfa05d039cd9163eb8fe9f79ddebf00ca23a1637510c2fd2"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.70", "syn 2.0.71",
] ]
[[package]] [[package]]
@ -5091,9 +5096,9 @@ checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b"
[[package]] [[package]]
name = "sequoia-openpgp" name = "sequoia-openpgp"
version = "1.21.1" version = "1.21.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b870b0275eeae174058fcf0ce5affccaaafeb7eceeabce8d6c7f51fbe6a41e2a" checksum = "13261ee216b44d932ef93b2d4a75d45199bef77864bcc5b77ecfc7bc0ecb02d6"
dependencies = [ dependencies = [
"aes", "aes",
"aes-gcm", "aes-gcm",
@ -5194,7 +5199,7 @@ checksum = "e0cd7e117be63d3c3678776753929474f3b04a43a080c744d6b0ae2a8c28e222"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.70", "syn 2.0.71",
] ]
[[package]] [[package]]
@ -5216,7 +5221,7 @@ checksum = "6c64451ba24fc7a6a2d60fc75dd9c83c90903b19028d4eff35e88fc1e86564e9"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.70", "syn 2.0.71",
] ]
[[package]] [[package]]
@ -5611,7 +5616,7 @@ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"rustversion", "rustversion",
"syn 2.0.70", "syn 2.0.71",
] ]
[[package]] [[package]]
@ -5624,7 +5629,7 @@ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"rustversion", "rustversion",
"syn 2.0.70", "syn 2.0.71",
] ]
[[package]] [[package]]
@ -5669,9 +5674,9 @@ dependencies = [
[[package]] [[package]]
name = "syn" name = "syn"
version = "2.0.70" version = "2.0.71"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2f0209b68b3613b093e0ec905354eccaedcfe83b8cb37cbdeae64026c3064c16" checksum = "b146dcf730474b4bcd16c311627b31ede9ab149045db4d6088b3becaea046462"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@ -5687,7 +5692,7 @@ dependencies = [
"proc-macro-error", "proc-macro-error",
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.70", "syn 2.0.71",
] ]
[[package]] [[package]]
@ -5704,7 +5709,7 @@ checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.70", "syn 2.0.71",
] ]
[[package]] [[package]]
@ -5781,22 +5786,22 @@ checksum = "23d434d3f8967a09480fb04132ebe0a3e088c173e6d0ee7897abbdf4eab0f8b9"
[[package]] [[package]]
name = "thiserror" name = "thiserror"
version = "1.0.61" version = "1.0.62"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c546c80d6be4bc6a00c0f01730c08df82eaa7a7a61f11d656526506112cc1709" checksum = "f2675633b1499176c2dff06b0856a27976a8f9d436737b4cf4f312d4d91d8bbb"
dependencies = [ dependencies = [
"thiserror-impl", "thiserror-impl",
] ]
[[package]] [[package]]
name = "thiserror-impl" name = "thiserror-impl"
version = "1.0.61" version = "1.0.62"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "46c3384250002a6d5af4d114f2845d37b57521033f30d5c3f46c4d70e1197533" checksum = "d20468752b09f49e909e55a5d338caa8bedf615594e9d80bc4c565d30faf798c"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.70", "syn 2.0.71",
] ]
[[package]] [[package]]
@ -5892,7 +5897,7 @@ checksum = "8d9ef545650e79f30233c0003bcc2504d7efac6dad25fca40744de773fe2049c"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.70", "syn 2.0.71",
] ]
[[package]] [[package]]
@ -5902,7 +5907,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ba4f4a02a7a80d6f274636f0aa95c7e383b912d41fe721a31f29e29698585a4a" checksum = "ba4f4a02a7a80d6f274636f0aa95c7e383b912d41fe721a31f29e29698585a4a"
dependencies = [ dependencies = [
"backtrace", "backtrace",
"bytes 1.6.0", "bytes 1.6.1",
"libc", "libc",
"mio", "mio",
"num_cpus", "num_cpus",
@ -5922,7 +5927,7 @@ checksum = "5f5ae998a069d4b5aba8ee9dad856af7d520c3699e6159b185c2acd48155d39a"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.70", "syn 2.0.71",
] ]
[[package]] [[package]]
@ -5953,7 +5958,7 @@ version = "0.6.10"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "36943ee01a6d67977dd3f84a5a1d2efeb4ada3a1ae771cadfaa535d9d9fc6507" checksum = "36943ee01a6d67977dd3f84a5a1d2efeb4ada3a1ae771cadfaa535d9d9fc6507"
dependencies = [ dependencies = [
"bytes 1.6.0", "bytes 1.6.1",
"futures-core", "futures-core",
"futures-sink", "futures-sink",
"log", "log",
@ -5967,7 +5972,7 @@ version = "0.7.11"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9cf6b47b3771c49ac75ad09a6162f53ad4b8088b76ac60e8ec1455b31a189fe1" checksum = "9cf6b47b3771c49ac75ad09a6162f53ad4b8088b76ac60e8ec1455b31a189fe1"
dependencies = [ dependencies = [
"bytes 1.6.0", "bytes 1.6.1",
"futures-core", "futures-core",
"futures-sink", "futures-sink",
"pin-project-lite", "pin-project-lite",
@ -5993,7 +5998,7 @@ version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "52a15c15b1bc91f90902347eff163b5b682643aff0c8e972912cca79bd9208dd" checksum = "52a15c15b1bc91f90902347eff163b5b682643aff0c8e972912cca79bd9208dd"
dependencies = [ dependencies = [
"bytes 1.6.0", "bytes 1.6.1",
"futures", "futures",
"libc", "libc",
"tokio", "tokio",
@ -6084,7 +6089,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.70", "syn 2.0.71",
] ]
[[package]] [[package]]
@ -6454,7 +6459,7 @@ dependencies = [
"once_cell", "once_cell",
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.70", "syn 2.0.71",
"wasm-bindgen-shared", "wasm-bindgen-shared",
] ]
@ -6488,7 +6493,7 @@ checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.70", "syn 2.0.71",
"wasm-bindgen-backend", "wasm-bindgen-backend",
"wasm-bindgen-shared", "wasm-bindgen-shared",
] ]
@ -6937,7 +6942,7 @@ checksum = "28cc31741b18cb6f1d5ff12f5b7523e3d6eb0852bbbad19d73905511d9849b95"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.70", "syn 2.0.71",
"synstructure", "synstructure",
] ]
@ -7024,7 +7029,7 @@ checksum = "0ea7b4a3637ea8669cedf0f1fd5c286a17f3de97b8dd5a70a6c167a1730e63a5"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.70", "syn 2.0.71",
"synstructure", "synstructure",
] ]
@ -7045,7 +7050,7 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.70", "syn 2.0.71",
] ]
[[package]] [[package]]
@ -7067,7 +7072,7 @@ checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.70", "syn 2.0.71",
] ]
[[package]] [[package]]

View File

@ -6,7 +6,8 @@ edition = "2018"
license = "Apache-2.0" license = "Apache-2.0"
[dependencies] [dependencies]
oci = { path = "../libs/oci" } runtime-spec = { path = "../libs/runtime-spec" }
oci-spec = { version = "0.6.8", features = ["runtime"] }
rustjail = { path = "rustjail" } rustjail = { path = "rustjail" }
protocols = { path = "../libs/protocols", features = ["async", "with-serde"] } protocols = { path = "../libs/protocols", features = ["async", "with-serde"] }
lazy_static = "1.3.0" lazy_static = "1.3.0"
@ -19,7 +20,7 @@ serde_json = "1.0.39"
scan_fmt = "0.2.3" scan_fmt = "0.2.3"
scopeguard = "1.0.0" scopeguard = "1.0.0"
thiserror = "1.0.26" thiserror = "1.0.26"
regex = "1.10.4" regex = "1.10.5"
serial_test = "0.5.1" serial_test = "0.5.1"
url = "2.5.0" url = "2.5.0"
derivative = "2.2.0" derivative = "2.2.0"

View File

@ -10,7 +10,8 @@ awaitgroup = "0.6.0"
serde = "1.0.91" serde = "1.0.91"
serde_json = "1.0.39" serde_json = "1.0.39"
serde_derive = "1.0.91" serde_derive = "1.0.91"
oci = { path = "../../libs/oci" } runtime-spec = { path = "../../libs/runtime-spec" }
oci-spec = { version = "0.6.8", features = ["runtime"] }
protocols = { path ="../../libs/protocols" } protocols = { path ="../../libs/protocols" }
kata-sys-util = { path = "../../libs/kata-sys-util" } kata-sys-util = { path = "../../libs/kata-sys-util" }
caps = "0.5.0" caps = "0.5.0"
@ -44,6 +45,7 @@ xattr = "0.2.3"
serial_test = "0.5.0" serial_test = "0.5.0"
tempfile = "3.1.0" tempfile = "3.1.0"
test-utils = { path = "../../libs/test-utils" } test-utils = { path = "../../libs/test-utils" }
protocols = { path ="../../libs/protocols" }
[features] [features]
seccomp = ["libseccomp"] seccomp = ["libseccomp"]

View File

@ -10,17 +10,20 @@ use crate::log_child;
use crate::sync::write_count; use crate::sync::write_count;
use anyhow::{anyhow, Result}; use anyhow::{anyhow, Result};
use caps::{self, runtime, CapSet, Capability, CapsHashSet}; use caps::{self, runtime, CapSet, Capability, CapsHashSet};
use oci::LinuxCapabilities; use oci::{Capability as LinuxCapability, LinuxCapabilities};
use oci_spec::runtime as oci;
use std::collections::HashSet;
use std::os::unix::io::RawFd; use std::os::unix::io::RawFd;
use std::str::FromStr; use std::str::FromStr;
fn to_capshashset(cfd_log: RawFd, caps: &[String]) -> CapsHashSet { fn to_capshashset(cfd_log: RawFd, capabilities: &Option<HashSet<LinuxCapability>>) -> CapsHashSet {
let mut r = CapsHashSet::new(); let mut r = CapsHashSet::new();
let binding: HashSet<LinuxCapability> = HashSet::new();
let caps = capabilities.as_ref().unwrap_or(&binding);
for cap in caps.iter() { for cap in caps.iter() {
match Capability::from_str(cap) { match Capability::from_str(&format!("CAP_{}", cap)) {
Err(_) => { Err(_) => {
log_child!(cfd_log, "{} is not a cap", cap); log_child!(cfd_log, "{} is not a cap", &cap.to_string());
continue; continue;
} }
Ok(c) => r.insert(c), Ok(c) => r.insert(c),
@ -48,33 +51,33 @@ pub fn reset_effective() -> Result<()> {
pub fn drop_privileges(cfd_log: RawFd, caps: &LinuxCapabilities) -> Result<()> { pub fn drop_privileges(cfd_log: RawFd, caps: &LinuxCapabilities) -> Result<()> {
let all = get_all_caps(); let all = get_all_caps();
for c in all.difference(&to_capshashset(cfd_log, caps.bounding.as_ref())) { for c in all.difference(&to_capshashset(cfd_log, caps.bounding())) {
caps::drop(None, CapSet::Bounding, *c).map_err(|e| anyhow!(e.to_string()))?; caps::drop(None, CapSet::Bounding, *c).map_err(|e| anyhow!(e.to_string()))?;
} }
caps::set( caps::set(
None, None,
CapSet::Effective, CapSet::Effective,
&to_capshashset(cfd_log, caps.effective.as_ref()), &to_capshashset(cfd_log, caps.effective()),
) )
.map_err(|e| anyhow!(e.to_string()))?; .map_err(|e| anyhow!(e.to_string()))?;
caps::set( caps::set(
None, None,
CapSet::Permitted, CapSet::Permitted,
&to_capshashset(cfd_log, caps.permitted.as_ref()), &to_capshashset(cfd_log, caps.permitted()),
) )
.map_err(|e| anyhow!(e.to_string()))?; .map_err(|e| anyhow!(e.to_string()))?;
caps::set( caps::set(
None, None,
CapSet::Inheritable, CapSet::Inheritable,
&to_capshashset(cfd_log, caps.inheritable.as_ref()), &to_capshashset(cfd_log, caps.inheritable()),
) )
.map_err(|e| anyhow!(e.to_string()))?; .map_err(|e| anyhow!(e.to_string()))?;
let _ = caps::set( let _ = caps::set(
None, None,
CapSet::Ambient, CapSet::Ambient,
&to_capshashset(cfd_log, caps.ambient.as_ref()), &to_capshashset(cfd_log, caps.ambient()),
) )
.map_err(|_| log_child!(cfd_log, "failed to set ambient capability")); .map_err(|_| log_child!(cfd_log, "failed to set ambient capability"));

View File

@ -23,9 +23,10 @@ use crate::container::DEFAULT_DEVICES;
use anyhow::{anyhow, Context, Result}; use anyhow::{anyhow, Context, Result};
use libc::{self, pid_t}; use libc::{self, pid_t};
use oci::{ use oci::{
LinuxBlockIo, LinuxCpu, LinuxDevice, LinuxDeviceCgroup, LinuxHugepageLimit, LinuxMemory, LinuxBlockIo, LinuxCpu, LinuxDevice, LinuxDeviceCgroup, LinuxDeviceCgroupBuilder,
LinuxNetwork, LinuxPids, LinuxResources, Spec, LinuxHugepageLimit, LinuxMemory, LinuxNetwork, LinuxPids, LinuxResources, Spec,
}; };
use oci_spec::runtime as oci;
use protobuf::MessageField; use protobuf::MessageField;
use protocols::agent::{ use protocols::agent::{
@ -72,7 +73,7 @@ pub struct Manager {
// set_resource is used to set reources by cgroup controller. // set_resource is used to set reources by cgroup controller.
macro_rules! set_resource { macro_rules! set_resource {
($cont:ident, $func:ident, $res:ident, $field:ident) => { ($cont:ident, $func:ident, $res:ident, $field:ident) => {
let resource_value = $res.$field.unwrap_or(0); let resource_value = $res.$field().unwrap_or(0);
if resource_value != 0 { if resource_value != 0 {
$cont.$func(resource_value)?; $cont.$func(resource_value)?;
} }
@ -95,38 +96,40 @@ impl CgroupManager for Manager {
let pod_res = &mut cgroups::Resources::default(); let pod_res = &mut cgroups::Resources::default();
// set cpuset and cpu reources // set cpuset and cpu reources
if let Some(cpu) = &r.cpu { if let Some(cpu) = &r.cpu() {
set_cpu_resources(&self.cgroup, cpu)?; set_cpu_resources(&self.cgroup, cpu)?;
} }
// set memory resources // set memory resources
if let Some(memory) = &r.memory { if let Some(memory) = &r.memory() {
set_memory_resources(&self.cgroup, memory, update)?; set_memory_resources(&self.cgroup, memory, update)?;
} }
// set pids resources // set pids resources
if let Some(pids_resources) = &r.pids { if let Some(pids_resources) = &r.pids() {
set_pids_resources(&self.cgroup, pids_resources)?; set_pids_resources(&self.cgroup, pids_resources)?;
} }
// set block_io resources // set block_io resources
if let Some(blkio) = &r.block_io { if let Some(blkio) = &r.block_io() {
set_block_io_resources(&self.cgroup, blkio, res); set_block_io_resources(&self.cgroup, blkio, res);
} }
// set hugepages resources // set hugepages resources
if !r.hugepage_limits.is_empty() { if let Some(hugepage_limits) = r.hugepage_limits() {
set_hugepages_resources(&self.cgroup, &r.hugepage_limits, res); set_hugepages_resources(&self.cgroup, hugepage_limits, res);
} }
// set network resources // set network resources
if let Some(network) = &r.network { if let Some(network) = &r.network() {
set_network_resources(&self.cgroup, network, res); set_network_resources(&self.cgroup, network, res);
} }
// set devices resources // set devices resources
if !self.devcg_allowed_all { if !self.devcg_allowed_all {
set_devices_resources(&self.cgroup, &r.devices, res, pod_res); if let Some(devices) = r.devices() {
set_devices_resources(&self.cgroup, devices, res, pod_res);
}
} }
debug!( debug!(
sl(), sl(),
@ -301,7 +304,7 @@ fn set_network_resources(
// set classid // set classid
// description can be found at https://www.kernel.org/doc/html/latest/admin-guide/cgroup-v1/net_cls.html // description can be found at https://www.kernel.org/doc/html/latest/admin-guide/cgroup-v1/net_cls.html
let class_id = network.class_id.unwrap_or(0) as u64; let class_id = network.class_id().unwrap_or(0) as u64;
if class_id != 0 { if class_id != 0 {
res.network.class_id = Some(class_id); res.network.class_id = Some(class_id);
} }
@ -309,10 +312,11 @@ fn set_network_resources(
// set network priorities // set network priorities
// description can be found at https://www.kernel.org/doc/html/latest/admin-guide/cgroup-v1/net_prio.html // description can be found at https://www.kernel.org/doc/html/latest/admin-guide/cgroup-v1/net_prio.html
let mut priorities = vec![]; let mut priorities = vec![];
for p in network.priorities.iter() { let interface_priority = network.priorities().clone().unwrap_or_default();
for p in interface_priority.iter() {
priorities.push(NetworkPriority { priorities.push(NetworkPriority {
name: p.name.clone(), name: p.name().clone(),
priority: p.priority as u64, priority: p.priority() as u64,
}); });
} }
@ -351,17 +355,18 @@ fn set_hugepages_resources(
let hugetlb_controller = cg.controller_of::<HugeTlbController>(); let hugetlb_controller = cg.controller_of::<HugeTlbController>();
for l in hugepage_limits.iter() { for l in hugepage_limits.iter() {
if hugetlb_controller.is_some() && hugetlb_controller.unwrap().size_supported(&l.page_size) if hugetlb_controller.is_some() && hugetlb_controller.unwrap().size_supported(l.page_size())
{ {
let hr = HugePageResource { let hr = HugePageResource {
size: l.page_size.clone(), size: l.page_size().clone(),
limit: l.limit, limit: l.limit() as u64,
}; };
limits.push(hr); limits.push(hr);
} else { } else {
warn!( warn!(
sl(), sl(),
"{} page size support cannot be verified, dropping requested limit", l.page_size "{} page size support cannot be verified, dropping requested limit",
l.page_size()
); );
} }
} }
@ -375,29 +380,47 @@ fn set_block_io_resources(
) { ) {
info!(sl(), "cgroup manager set block io"); info!(sl(), "cgroup manager set block io");
res.blkio.weight = blkio.weight; res.blkio.weight = blkio.weight();
res.blkio.leaf_weight = blkio.leaf_weight; res.blkio.leaf_weight = blkio.leaf_weight();
let mut blk_device_resources = vec![]; let mut blk_device_resources = vec![];
for d in blkio.weight_device.iter() { let default_weight_device = vec![];
let weight_device = blkio
.weight_device()
.as_ref()
.unwrap_or(&default_weight_device);
for d in weight_device.iter() {
let dr = BlkIoDeviceResource { let dr = BlkIoDeviceResource {
major: d.blk.major as u64, major: d.major() as u64,
minor: d.blk.minor as u64, minor: d.minor() as u64,
weight: blkio.weight, weight: blkio.weight(),
leaf_weight: blkio.leaf_weight, leaf_weight: blkio.leaf_weight(),
}; };
blk_device_resources.push(dr); blk_device_resources.push(dr);
} }
res.blkio.weight_device = blk_device_resources; res.blkio.weight_device = blk_device_resources;
res.blkio.throttle_read_bps_device = res.blkio.throttle_read_bps_device = build_blk_io_device_throttle_resource(
build_blk_io_device_throttle_resource(&blkio.throttle_read_bps_device); blkio.throttle_read_bps_device().as_ref().unwrap_or(&vec![]),
res.blkio.throttle_write_bps_device = );
build_blk_io_device_throttle_resource(&blkio.throttle_write_bps_device); res.blkio.throttle_write_bps_device = build_blk_io_device_throttle_resource(
res.blkio.throttle_read_iops_device = blkio
build_blk_io_device_throttle_resource(&blkio.throttle_read_iops_device); .throttle_write_bps_device()
res.blkio.throttle_write_iops_device = .as_ref()
build_blk_io_device_throttle_resource(&blkio.throttle_write_iops_device); .unwrap_or(&vec![]),
);
res.blkio.throttle_read_iops_device = build_blk_io_device_throttle_resource(
blkio
.throttle_read_iops_device()
.as_ref()
.unwrap_or(&vec![]),
);
res.blkio.throttle_write_iops_device = build_blk_io_device_throttle_resource(
blkio
.throttle_write_iops_device()
.as_ref()
.unwrap_or(&vec![]),
);
} }
fn set_cpu_resources(cg: &cgroups::Cgroup, cpu: &LinuxCpu) -> Result<()> { fn set_cpu_resources(cg: &cgroups::Cgroup, cpu: &LinuxCpu) -> Result<()> {
@ -405,19 +428,19 @@ fn set_cpu_resources(cg: &cgroups::Cgroup, cpu: &LinuxCpu) -> Result<()> {
let cpuset_controller: &CpuSetController = cg.controller_of().unwrap(); let cpuset_controller: &CpuSetController = cg.controller_of().unwrap();
if !cpu.cpus.is_empty() { if let Some(cpus) = cpu.cpus() {
if let Err(e) = cpuset_controller.set_cpus(&cpu.cpus) { if let Err(e) = cpuset_controller.set_cpus(cpus) {
warn!(sl(), "write cpuset failed: {:?}", e); warn!(sl(), "write cpuset failed: {:?}", e);
} }
} }
if !cpu.mems.is_empty() { if let Some(mems) = cpu.mems() {
cpuset_controller.set_mems(&cpu.mems)?; cpuset_controller.set_mems(mems)?;
} }
let cpu_controller: &CpuController = cg.controller_of().unwrap(); let cpu_controller: &CpuController = cg.controller_of().unwrap();
if let Some(shares) = cpu.shares { if let Some(shares) = cpu.shares() {
let shares = if cg.v2() { let shares = if cg.v2() {
convert_shares_to_v2_value(shares) convert_shares_to_v2_value(shares)
} else { } else {
@ -449,12 +472,12 @@ fn set_memory_resources(cg: &cgroups::Cgroup, memory: &LinuxMemory, update: bool
// If the memory update is set to -1 we should also // If the memory update is set to -1 we should also
// set swap to -1, it means unlimited memory. // set swap to -1, it means unlimited memory.
let mut swap = memory.swap.unwrap_or(0); let mut swap = memory.swap().unwrap_or(0);
if memory.limit == Some(-1) { if memory.limit() == Some(-1) {
swap = -1; swap = -1;
} }
if memory.limit.is_some() && swap != 0 { if memory.limit().is_some() && swap != 0 {
let memstat = get_memory_stats(cg) let memstat = get_memory_stats(cg)
.into_option() .into_option()
.ok_or_else(|| anyhow!("failed to get the cgroup memory stats"))?; .ok_or_else(|| anyhow!("failed to get the cgroup memory stats"))?;
@ -475,7 +498,7 @@ fn set_memory_resources(cg: &cgroups::Cgroup, memory: &LinuxMemory, update: bool
} else { } else {
set_resource!(mem_controller, set_limit, memory, limit); set_resource!(mem_controller, set_limit, memory, limit);
swap = if cg.v2() { swap = if cg.v2() {
convert_memory_swap_to_v2_value(swap, memory.limit.unwrap_or(0))? convert_memory_swap_to_v2_value(swap, memory.limit().unwrap_or(0))?
} else { } else {
swap swap
}; };
@ -488,7 +511,7 @@ fn set_memory_resources(cg: &cgroups::Cgroup, memory: &LinuxMemory, update: bool
set_resource!(mem_controller, set_kmem_limit, memory, kernel); set_resource!(mem_controller, set_kmem_limit, memory, kernel);
set_resource!(mem_controller, set_tcp_limit, memory, kernel_tcp); set_resource!(mem_controller, set_tcp_limit, memory, kernel_tcp);
if let Some(swappiness) = memory.swappiness { if let Some(swappiness) = memory.swappiness() {
if (0..=100).contains(&swappiness) { if (0..=100).contains(&swappiness) {
mem_controller.set_swappiness(swappiness)?; mem_controller.set_swappiness(swappiness)?;
} else { } else {
@ -499,7 +522,7 @@ fn set_memory_resources(cg: &cgroups::Cgroup, memory: &LinuxMemory, update: bool
} }
} }
if memory.disable_oom_killer.unwrap_or(false) { if memory.disable_oom_killer().unwrap_or(false) {
mem_controller.disable_oom_killer()?; mem_controller.disable_oom_killer()?;
} }
@ -509,8 +532,8 @@ fn set_memory_resources(cg: &cgroups::Cgroup, memory: &LinuxMemory, update: bool
fn set_pids_resources(cg: &cgroups::Cgroup, pids: &LinuxPids) -> Result<()> { fn set_pids_resources(cg: &cgroups::Cgroup, pids: &LinuxPids) -> Result<()> {
info!(sl(), "cgroup manager set pids"); info!(sl(), "cgroup manager set pids");
let pid_controller: &PidController = cg.controller_of().unwrap(); let pid_controller: &PidController = cg.controller_of().unwrap();
let v = if pids.limit > 0 { let v = if pids.limit() > 0 {
MaxValue::Value(pids.limit) MaxValue::Value(pids.limit())
} else { } else {
MaxValue::Max MaxValue::Max
}; };
@ -525,9 +548,9 @@ fn build_blk_io_device_throttle_resource(
let mut blk_io_device_throttle_resources = vec![]; let mut blk_io_device_throttle_resources = vec![];
for d in input.iter() { for d in input.iter() {
let tr = BlkIoDeviceThrottleResource { let tr = BlkIoDeviceThrottleResource {
major: d.blk.major as u64, major: d.major() as u64,
minor: d.blk.minor as u64, minor: d.minor() as u64,
rate: d.rate, rate: d.rate(),
}; };
blk_io_device_throttle_resources.push(tr); blk_io_device_throttle_resources.push(tr);
} }
@ -536,13 +559,20 @@ fn build_blk_io_device_throttle_resource(
} }
fn linux_device_cgroup_to_device_resource(d: &LinuxDeviceCgroup) -> Option<DeviceResource> { fn linux_device_cgroup_to_device_resource(d: &LinuxDeviceCgroup) -> Option<DeviceResource> {
let dev_type = match DeviceType::from_char(d.r#type.chars().next()) { let dev_type = match DeviceType::from_char(d.typ().unwrap_or_default().as_str().chars().next())
{
Some(t) => t, Some(t) => t,
None => return None, None => return None,
}; };
let mut permissions: Vec<DevicePermissions> = vec![]; let mut permissions: Vec<DevicePermissions> = vec![];
for p in d.access.chars().collect::<Vec<char>>() { for p in d
.access()
.as_ref()
.unwrap_or(&"".to_owned())
.chars()
.collect::<Vec<char>>()
{
match p { match p {
'r' => permissions.push(DevicePermissions::Read), 'r' => permissions.push(DevicePermissions::Read),
'w' => permissions.push(DevicePermissions::Write), 'w' => permissions.push(DevicePermissions::Write),
@ -552,10 +582,10 @@ fn linux_device_cgroup_to_device_resource(d: &LinuxDeviceCgroup) -> Option<Devic
} }
Some(DeviceResource { Some(DeviceResource {
allow: d.allow, allow: d.allow(),
devtype: dev_type, devtype: dev_type,
major: d.major.unwrap_or(0), major: d.major().unwrap_or(0),
minor: d.minor.unwrap_or(0), minor: d.minor().unwrap_or(0),
access: permissions, access: permissions,
}) })
} }
@ -592,58 +622,64 @@ lazy_static! {
pub static ref DEFAULT_ALLOWED_DEVICES: Vec<LinuxDeviceCgroup> = { pub static ref DEFAULT_ALLOWED_DEVICES: Vec<LinuxDeviceCgroup> = {
vec![ vec![
// all mknod to all char devices // all mknod to all char devices
LinuxDeviceCgroup { LinuxDeviceCgroupBuilder::default()
allow: true, .allow(true)
r#type: "c".to_string(), .typ(oci::LinuxDeviceType::C)
major: Some(WILDCARD), .major(WILDCARD)
minor: Some(WILDCARD), .minor(WILDCARD)
access: "m".to_string(), .access("m")
}, .build()
.unwrap(),
// all mknod to all block devices // all mknod to all block devices
LinuxDeviceCgroup { LinuxDeviceCgroupBuilder::default()
allow: true, .allow(true)
r#type: "b".to_string(), .typ(oci::LinuxDeviceType::B)
major: Some(WILDCARD), .major(WILDCARD)
minor: Some(WILDCARD), .minor(WILDCARD)
access: "m".to_string(), .access("m")
}, .build()
.unwrap(),
// all read/write/mknod to char device /dev/console // all read/write/mknod to char device /dev/console
LinuxDeviceCgroup { LinuxDeviceCgroupBuilder::default()
allow: true, .allow(true)
r#type: "c".to_string(), .typ(oci::LinuxDeviceType::C)
major: Some(5), .major(5)
minor: Some(1), .minor(1)
access: "rwm".to_string(), .access("rwm")
}, .build()
.unwrap(),
// all read/write/mknod to char device /dev/pts/<N> // all read/write/mknod to char device /dev/pts/<N>
LinuxDeviceCgroup { LinuxDeviceCgroupBuilder::default()
allow: true, .allow(true)
r#type: "c".to_string(), .typ(oci::LinuxDeviceType::C)
major: Some(136), .major(136)
minor: Some(WILDCARD), .minor(WILDCARD)
access: "rwm".to_string(), .access("rwm")
}, .build()
.unwrap(),
// all read/write/mknod to char device /dev/ptmx // all read/write/mknod to char device /dev/ptmx
LinuxDeviceCgroup { LinuxDeviceCgroupBuilder::default()
allow: true, .allow(true)
r#type: "c".to_string(), .typ(oci::LinuxDeviceType::C)
major: Some(5), .major(5)
minor: Some(2), .minor(2)
access: "rwm".to_string(), .access("rwm")
}, .build()
.unwrap(),
// all read/write/mknod to char device /dev/net/tun // all read/write/mknod to char device /dev/net/tun
LinuxDeviceCgroup { LinuxDeviceCgroupBuilder::default()
allow: true, .allow(true)
r#type: "c".to_string(), .typ(oci::LinuxDeviceType::C)
major: Some(10), .major(10)
minor: Some(200), .minor(200)
access: "rwm".to_string(), .access("rwm")
}, .build()
.unwrap(),
] ]
}; };
} }
@ -1218,19 +1254,24 @@ impl Manager {
/// Check if OCI spec contains a rule of allowed all devices. /// Check if OCI spec contains a rule of allowed all devices.
fn has_allowed_all_devices_rule(spec: &Spec) -> bool { fn has_allowed_all_devices_rule(spec: &Spec) -> bool {
let linux = match spec.linux.as_ref() { let linux = match spec.linux().as_ref() {
Some(linux) => linux, Some(linux) => linux,
None => return false, None => return false,
}; };
let resources = match linux.resources.as_ref() { let resources = match linux.resources().as_ref() {
Some(resource) => resource, Some(resource) => resource,
None => return false, None => return false,
}; };
resources resources
.devices .devices()
.iter() .as_ref()
.find(|dev| rule_for_all_devices(dev)) .and_then(|devices| {
.map(|dev| dev.allow) devices
.iter()
.find(|dev| rule_for_all_devices(dev))
.map(|dev| dev.allow())
})
.unwrap_or_default() .unwrap_or_default()
} }
} }
@ -1254,7 +1295,7 @@ fn default_allowed_devices() -> Vec<DeviceResource> {
/// Convert LinuxDevice to DeviceResource. /// Convert LinuxDevice to DeviceResource.
fn linux_device_to_device_resource(d: &LinuxDevice) -> Option<DeviceResource> { fn linux_device_to_device_resource(d: &LinuxDevice) -> Option<DeviceResource> {
let dev_type = match DeviceType::from_char(d.r#type.chars().next()) { let dev_type = match DeviceType::from_char(d.typ().as_str().chars().next()) {
Some(t) => t, Some(t) => t,
None => return None, None => return None,
}; };
@ -1268,8 +1309,8 @@ fn linux_device_to_device_resource(d: &LinuxDevice) -> Option<DeviceResource> {
Some(DeviceResource { Some(DeviceResource {
allow: true, allow: true,
devtype: dev_type, devtype: dev_type,
major: d.major, major: d.major(),
minor: d.minor, minor: d.minor(),
access: permissions, access: permissions,
}) })
} }
@ -1328,7 +1369,11 @@ mod tests {
use std::time::{SystemTime, UNIX_EPOCH}; use std::time::{SystemTime, UNIX_EPOCH};
use cgroups::devices::{DevicePermissions, DeviceType}; use cgroups::devices::{DevicePermissions, DeviceType};
use oci::{Linux, LinuxDeviceCgroup, LinuxResources, Spec}; use oci::{
LinuxBuilder, LinuxDeviceCgroup, LinuxDeviceCgroupBuilder, LinuxDeviceType,
LinuxResourcesBuilder, SpecBuilder,
};
use oci_spec::runtime as oci;
use test_utils::skip_if_not_root; use test_utils::skip_if_not_root;
use super::default_allowed_devices; use super::default_allowed_devices;
@ -1423,21 +1468,22 @@ mod tests {
container_devices_list: Vec<String>, container_devices_list: Vec<String>,
} }
let allow_all = LinuxDeviceCgroup { let allow_all = LinuxDeviceCgroupBuilder::default()
allow: true, .allow(true)
r#type: String::new(), .typ(LinuxDeviceType::A)
major: Some(0), .major(0)
minor: Some(0), .minor(0)
access: String::from("rwm"), .access("rwm")
}; .build()
.unwrap();
let deny_all = LinuxDeviceCgroup { let deny_all = LinuxDeviceCgroupBuilder::default()
allow: false, .allow(false)
r#type: String::new(), .typ(LinuxDeviceType::A)
major: Some(0), .major(0)
minor: Some(0), .minor(0)
access: String::from("rwm"), .access("rwm")
}; .build()
.unwrap();
let now = SystemTime::now() let now = SystemTime::now()
.duration_since(UNIX_EPOCH) .duration_since(UNIX_EPOCH)
@ -1490,16 +1536,20 @@ mod tests {
let mut managers = Vec::with_capacity(tc.devices.len()); let mut managers = Vec::with_capacity(tc.devices.len());
for cid in 0..tc.devices.len() { for cid in 0..tc.devices.len() {
let spec = Spec { let spec = SpecBuilder::default()
linux: Some(Linux { .linux(
resources: Some(LinuxResources { LinuxBuilder::default()
devices: tc.devices[cid].clone(), .resources(
..Default::default() LinuxResourcesBuilder::default()
}), .devices(tc.devices[cid].clone())
..Default::default() .build()
}), .unwrap(),
..Default::default() )
}; .build()
.unwrap(),
)
.build()
.unwrap();
managers.push( managers.push(
Manager::new(&tc.cpath[cid], &spec, Some(sandbox.devcg_info.clone())).unwrap(), Manager::new(&tc.cpath[cid], &spec, Some(sandbox.devcg_info.clone())).unwrap(),
); );

View File

@ -11,6 +11,7 @@ use anyhow::Result;
use cgroups::freezer::FreezerState; use cgroups::freezer::FreezerState;
use libc::{self, pid_t}; use libc::{self, pid_t};
use oci::{LinuxResources, Spec}; use oci::{LinuxResources, Spec};
use oci_spec::runtime as oci;
use std::any::Any; use std::any::Any;
use std::collections::HashMap; use std::collections::HashMap;
use std::string::String; use std::string::String;

View File

@ -5,7 +5,7 @@
use anyhow::{anyhow, Result}; use anyhow::{anyhow, Result};
use core::fmt::Debug; use core::fmt::Debug;
use oci::{LinuxDeviceCgroup, LinuxResources}; use oci_spec::runtime::{LinuxDeviceCgroup, LinuxDeviceType, LinuxResources};
use protocols::agent::CgroupStats; use protocols::agent::CgroupStats;
use std::any::Any; use std::any::Any;
@ -75,15 +75,20 @@ impl Debug for dyn Manager + Send + Sync {
/// ///
/// The formats representing all devices between OCI spec and cgroups-rs /// The formats representing all devices between OCI spec and cgroups-rs
/// are different. /// are different.
/// - OCI spec: major: 0, minor: 0, type: "", access: "rwm"; /// - OCI spec: major: Some(0), minor: Some(0), type: Some(A), access: Some("rwm");
/// - Cgroups-rs: major: -1, minor: -1, type: "a", access: "rwm"; /// - Cgroups-rs: major: -1, minor: -1, type: "a", access: "rwm";
/// - Linux: a *:* rwm /// - Linux: a *:* rwm
#[inline] #[inline]
fn rule_for_all_devices(dev_cgroup: &LinuxDeviceCgroup) -> bool { fn rule_for_all_devices(dev_cgroup: &LinuxDeviceCgroup) -> bool {
dev_cgroup.major.unwrap_or(0) == 0 let cgrp_access = dev_cgroup.access().clone().unwrap_or_default();
&& dev_cgroup.minor.unwrap_or(0) == 0 let dev_type = dev_cgroup
&& (dev_cgroup.r#type.as_str() == "" || dev_cgroup.r#type.as_str() == "a") .typ()
&& dev_cgroup.access.contains('r') .as_ref()
&& dev_cgroup.access.contains('w') .map_or(LinuxDeviceType::default(), |x| *x);
&& dev_cgroup.access.contains('m') dev_cgroup.major().unwrap_or(0) == 0
&& dev_cgroup.minor().unwrap_or(0) == 0
&& dev_type == LinuxDeviceType::A
&& cgrp_access.contains('r')
&& cgrp_access.contains('w')
&& cgrp_access.contains('m')
} }

View File

@ -9,6 +9,7 @@ use anyhow::{anyhow, Result};
use cgroups::freezer::FreezerState; use cgroups::freezer::FreezerState;
use libc::{self, pid_t}; use libc::{self, pid_t};
use oci::LinuxResources; use oci::LinuxResources;
use oci_spec::runtime as oci;
use std::any::Any; use std::any::Any;
use std::collections::HashMap; use std::collections::HashMap;
use std::convert::TryInto; use std::convert::TryInto;

View File

@ -8,6 +8,7 @@ use super::transformer::Transformer;
use anyhow::{bail, Result}; use anyhow::{bail, Result};
use oci::{LinuxCpu, LinuxResources}; use oci::{LinuxCpu, LinuxResources};
use oci_spec::runtime as oci;
use zbus::zvariant::Value; use zbus::zvariant::Value;
const BASIC_SYSTEMD_VERSION: &str = "242"; const BASIC_SYSTEMD_VERSION: &str = "242";
@ -25,7 +26,7 @@ impl Transformer for Cpu {
cgroup_hierarchy: &CgroupHierarchy, cgroup_hierarchy: &CgroupHierarchy,
systemd_version: &str, systemd_version: &str,
) -> Result<()> { ) -> Result<()> {
if let Some(cpu_resources) = &r.cpu { if let Some(cpu_resources) = &r.cpu() {
match cgroup_hierarchy { match cgroup_hierarchy {
CgroupHierarchy::Legacy => { CgroupHierarchy::Legacy => {
Self::legacy_apply(cpu_resources, properties, systemd_version)? Self::legacy_apply(cpu_resources, properties, systemd_version)?
@ -50,7 +51,7 @@ impl Cpu {
properties: &mut Properties, properties: &mut Properties,
systemd_version: &str, systemd_version: &str,
) -> Result<()> { ) -> Result<()> {
if let Some(shares) = cpu_resources.shares { if let Some(shares) = cpu_resources.shares() {
// Minimum value of CPUShares should be 2, see https://github.com/systemd/systemd/blob/d19434fbf81db04d03c8cffa87821f754a86635b/src/basic/cgroup-util.h#L122 // Minimum value of CPUShares should be 2, see https://github.com/systemd/systemd/blob/d19434fbf81db04d03c8cffa87821f754a86635b/src/basic/cgroup-util.h#L122
let shares = match shares { let shares = match shares {
0 => 1024, 0 => 1024,
@ -60,14 +61,14 @@ impl Cpu {
properties.push(("CPUShares", Value::U64(shares))); properties.push(("CPUShares", Value::U64(shares)));
} }
if let Some(period) = cpu_resources.period { if let Some(period) = cpu_resources.period() {
if period != 0 && systemd_version >= BASIC_SYSTEMD_VERSION { if period != 0 && systemd_version >= BASIC_SYSTEMD_VERSION {
properties.push(("CPUQuotaPeriodUSec", Value::U64(period))); properties.push(("CPUQuotaPeriodUSec", Value::U64(period)));
} }
} }
if let Some(quota) = cpu_resources.quota { if let Some(quota) = cpu_resources.quota() {
let period = cpu_resources.period.unwrap_or(DEFAULT_CPUQUOTAPERIOD); let period = cpu_resources.period().unwrap_or(DEFAULT_CPUQUOTAPERIOD);
if period != 0 { if period != 0 {
let cpu_quota_per_sec_usec = resolve_cpuquota(quota, period); let cpu_quota_per_sec_usec = resolve_cpuquota(quota, period);
properties.push(("CPUQuotaPerSecUSec", Value::U64(cpu_quota_per_sec_usec))); properties.push(("CPUQuotaPerSecUSec", Value::U64(cpu_quota_per_sec_usec)));
@ -86,19 +87,19 @@ impl Cpu {
properties: &mut Properties, properties: &mut Properties,
systemd_version: &str, systemd_version: &str,
) -> Result<()> { ) -> Result<()> {
if let Some(shares) = cpu_resources.shares { if let Some(shares) = cpu_resources.shares() {
let weight = shares_to_weight(shares).unwrap(); let weight = shares_to_weight(shares).unwrap();
properties.push(("CPUWeight", Value::U64(weight))); properties.push(("CPUWeight", Value::U64(weight)));
} }
if let Some(period) = cpu_resources.period { if let Some(period) = cpu_resources.period() {
if period != 0 && systemd_version >= BASIC_SYSTEMD_VERSION { if period != 0 && systemd_version >= BASIC_SYSTEMD_VERSION {
properties.push(("CPUQuotaPeriodUSec", Value::U64(period))); properties.push(("CPUQuotaPeriodUSec", Value::U64(period)));
} }
} }
if let Some(quota) = cpu_resources.quota { if let Some(quota) = cpu_resources.quota() {
let period = cpu_resources.period.unwrap_or(DEFAULT_CPUQUOTAPERIOD); let period = cpu_resources.period().unwrap_or(DEFAULT_CPUQUOTAPERIOD);
if period != 0 { if period != 0 {
let cpu_quota_per_sec_usec = resolve_cpuquota(quota, period); let cpu_quota_per_sec_usec = resolve_cpuquota(quota, period);
properties.push(("CPUQuotaPerSecUSec", Value::U64(cpu_quota_per_sec_usec))); properties.push(("CPUQuotaPerSecUSec", Value::U64(cpu_quota_per_sec_usec)));

View File

@ -10,6 +10,7 @@ use super::transformer::Transformer;
use anyhow::{bail, Result}; use anyhow::{bail, Result};
use bit_vec::BitVec; use bit_vec::BitVec;
use oci::{LinuxCpu, LinuxResources}; use oci::{LinuxCpu, LinuxResources};
use oci_spec::runtime as oci;
use std::convert::{TryFrom, TryInto}; use std::convert::{TryFrom, TryInto};
use zbus::zvariant::Value; use zbus::zvariant::Value;
@ -24,7 +25,7 @@ impl Transformer for CpuSet {
_: &CgroupHierarchy, _: &CgroupHierarchy,
systemd_version: &str, systemd_version: &str,
) -> Result<()> { ) -> Result<()> {
if let Some(cpuset_resources) = &r.cpu { if let Some(cpuset_resources) = &r.cpu() {
Self::apply(cpuset_resources, properties, systemd_version)?; Self::apply(cpuset_resources, properties, systemd_version)?;
} }
@ -45,15 +46,13 @@ impl CpuSet {
return Ok(()); return Ok(());
} }
let cpus = cpuset_resources.cpus.as_str(); if let Some(cpus) = cpuset_resources.cpus().as_ref() {
if !cpus.is_empty() { let cpus_vec: BitMask = cpus.as_str().try_into()?;
let cpus_vec: BitMask = cpus.try_into()?;
properties.push(("AllowedCPUs", Value::Array(cpus_vec.0.into()))); properties.push(("AllowedCPUs", Value::Array(cpus_vec.0.into())));
} }
let mems = cpuset_resources.mems.as_str(); if let Some(mems) = cpuset_resources.mems().as_ref() {
if !mems.is_empty() { let mems_vec: BitMask = mems.as_str().try_into()?;
let mems_vec: BitMask = mems.try_into()?;
properties.push(("AllowedMemoryNodes", Value::Array(mems_vec.0.into()))); properties.push(("AllowedMemoryNodes", Value::Array(mems_vec.0.into())));
} }

View File

@ -9,6 +9,7 @@ use super::transformer::Transformer;
use anyhow::{bail, Result}; use anyhow::{bail, Result};
use oci::{LinuxMemory, LinuxResources}; use oci::{LinuxMemory, LinuxResources};
use oci_spec::runtime as oci;
use zbus::zvariant::Value; use zbus::zvariant::Value;
pub struct Memory {} pub struct Memory {}
@ -20,7 +21,7 @@ impl Transformer for Memory {
cgroup_hierarchy: &CgroupHierarchy, cgroup_hierarchy: &CgroupHierarchy,
_: &str, _: &str,
) -> Result<()> { ) -> Result<()> {
if let Some(memory_resources) = &r.memory { if let Some(memory_resources) = &r.memory() {
match cgroup_hierarchy { match cgroup_hierarchy {
CgroupHierarchy::Legacy => Self::legacy_apply(memory_resources, properties)?, CgroupHierarchy::Legacy => Self::legacy_apply(memory_resources, properties)?,
CgroupHierarchy::Unified => Self::unified_apply(memory_resources, properties)?, CgroupHierarchy::Unified => Self::unified_apply(memory_resources, properties)?,
@ -35,7 +36,7 @@ impl Memory {
// v1: // v1:
// memory.limit <-> MemoryLimit // memory.limit <-> MemoryLimit
fn legacy_apply(memory_resources: &LinuxMemory, properties: &mut Properties) -> Result<()> { fn legacy_apply(memory_resources: &LinuxMemory, properties: &mut Properties) -> Result<()> {
if let Some(limit) = memory_resources.limit { if let Some(limit) = memory_resources.limit() {
let limit = match limit { let limit = match limit {
1..=i64::MAX => limit as u64, 1..=i64::MAX => limit as u64,
0 => u64::MAX, 0 => u64::MAX,
@ -52,7 +53,7 @@ impl Memory {
// memory.max <-> MemoryMax // memory.max <-> MemoryMax
// memory.swap & memory.limit <-> MemorySwapMax // memory.swap & memory.limit <-> MemorySwapMax
fn unified_apply(memory_resources: &LinuxMemory, properties: &mut Properties) -> Result<()> { fn unified_apply(memory_resources: &LinuxMemory, properties: &mut Properties) -> Result<()> {
if let Some(limit) = memory_resources.limit { if let Some(limit) = memory_resources.limit() {
let limit = match limit { let limit = match limit {
1..=i64::MAX => limit as u64, 1..=i64::MAX => limit as u64,
0 => u64::MAX, 0 => u64::MAX,
@ -61,7 +62,7 @@ impl Memory {
properties.push(("MemoryMax", Value::U64(limit))); properties.push(("MemoryMax", Value::U64(limit)));
} }
if let Some(reservation) = memory_resources.reservation { if let Some(reservation) = memory_resources.reservation() {
let reservation = match reservation { let reservation = match reservation {
1..=i64::MAX => reservation as u64, 1..=i64::MAX => reservation as u64,
0 => u64::MAX, 0 => u64::MAX,
@ -70,11 +71,11 @@ impl Memory {
properties.push(("MemoryLow", Value::U64(reservation))); properties.push(("MemoryLow", Value::U64(reservation)));
} }
let swap = match memory_resources.swap { let swap = match memory_resources.swap() {
Some(0) => u64::MAX, Some(0) => u64::MAX,
Some(1..=i64::MAX) => match memory_resources.limit { Some(1..=i64::MAX) => match memory_resources.limit() {
Some(1..=i64::MAX) => { Some(1..=i64::MAX) => {
(memory_resources.limit.unwrap() - memory_resources.swap.unwrap()) as u64 (memory_resources.limit().unwrap() - memory_resources.swap().unwrap()) as u64
} }
_ => bail!("invalid memory.limit when memory.swap specified"), _ => bail!("invalid memory.limit when memory.swap specified"),
}, },
@ -93,18 +94,21 @@ mod tests {
use super::Memory; use super::Memory;
use super::Properties; use super::Properties;
use super::Value; use super::Value;
use oci_spec::runtime as oci;
#[test] #[test]
fn test_unified_memory() { fn test_unified_memory() {
let memory_resources = oci::LinuxMemory { let memory_resources = oci::LinuxMemoryBuilder::default()
limit: Some(736870912), .limit(736870912)
reservation: Some(536870912), .reservation(536870912)
swap: Some(536870912), .swap(536870912)
kernel: Some(0), .kernel(0)
kernel_tcp: Some(0), .kernel_tcp(0)
swappiness: Some(0), .swappiness(0u64)
disable_oom_killer: Some(false), .disable_oom_killer(false)
}; .build()
.unwrap();
let mut properties: Properties = vec![]; let mut properties: Properties = vec![];
assert_eq!( assert_eq!(

View File

@ -9,6 +9,7 @@ use super::transformer::Transformer;
use anyhow::Result; use anyhow::Result;
use oci::{LinuxPids, LinuxResources}; use oci::{LinuxPids, LinuxResources};
use oci_spec::runtime as oci;
use zbus::zvariant::Value; use zbus::zvariant::Value;
pub struct Pids {} pub struct Pids {}
@ -20,7 +21,7 @@ impl Transformer for Pids {
_: &CgroupHierarchy, _: &CgroupHierarchy,
_: &str, _: &str,
) -> Result<()> { ) -> Result<()> {
if let Some(pids_resources) = &r.pids { if let Some(pids_resources) = &r.pids() {
Self::apply(pids_resources, properties)?; Self::apply(pids_resources, properties)?;
} }
@ -31,8 +32,8 @@ impl Transformer for Pids {
// pids.limit <-> TasksMax // pids.limit <-> TasksMax
impl Pids { impl Pids {
fn apply(pids_resources: &LinuxPids, properties: &mut Properties) -> Result<()> { fn apply(pids_resources: &LinuxPids, properties: &mut Properties) -> Result<()> {
let limit = if pids_resources.limit > 0 { let limit = if pids_resources.limit() > 0 {
pids_resources.limit as u64 pids_resources.limit() as u64
} else { } else {
u64::MAX u64::MAX
}; };
@ -47,10 +48,13 @@ mod tests {
use super::Pids; use super::Pids;
use super::Properties; use super::Properties;
use super::Value; use super::Value;
use oci_spec::runtime as oci;
#[test] #[test]
fn test_subsystem_workflow() { fn test_subsystem_workflow() {
let pids_resources = oci::LinuxPids { limit: 0 }; let mut pids_resources = oci::LinuxPids::default();
pids_resources.set_limit(0 as i64);
let mut properties: Properties = vec![]; let mut properties: Properties = vec![];
assert_eq!(true, Pids::apply(&pids_resources, &mut properties).is_ok()); assert_eq!(true, Pids::apply(&pids_resources, &mut properties).is_ok());

View File

@ -6,6 +6,7 @@
use super::super::common::{CgroupHierarchy, Properties}; use super::super::common::{CgroupHierarchy, Properties};
use anyhow::Result; use anyhow::Result;
use oci::LinuxResources; use oci::LinuxResources;
use oci_spec::runtime as oci;
pub trait Transformer { pub trait Transformer {
fn apply( fn apply(

View File

@ -5,8 +5,10 @@
use anyhow::{anyhow, Context, Result}; use anyhow::{anyhow, Context, Result};
use libc::pid_t; use libc::pid_t;
use oci::{ContainerState, LinuxDevice, LinuxIdMapping}; use oci::{Linux, LinuxDevice, LinuxIdMapping, LinuxNamespace, LinuxResources, Spec};
use oci::{Linux, LinuxNamespace, LinuxResources, Spec}; use oci_spec::runtime as oci;
use runtime_spec as spec;
use spec::{ContainerState, State as OCIState};
use std::clone::Clone; use std::clone::Clone;
use std::ffi::CString; use std::ffi::CString;
use std::fmt::Display; use std::fmt::Display;
@ -51,7 +53,6 @@ use std::os::unix::io::AsRawFd;
use protobuf::MessageField; use protobuf::MessageField;
use oci::State as OCIState;
use regex::Regex; use regex::Regex;
use std::collections::HashMap; use std::collections::HashMap;
use std::os::unix::io::FromRawFd; use std::os::unix::io::FromRawFd;
@ -130,82 +131,88 @@ lazy_static! {
m.insert("user", CloneFlags::CLONE_NEWUSER); m.insert("user", CloneFlags::CLONE_NEWUSER);
m.insert("ipc", CloneFlags::CLONE_NEWIPC); m.insert("ipc", CloneFlags::CLONE_NEWIPC);
m.insert("pid", CloneFlags::CLONE_NEWPID); m.insert("pid", CloneFlags::CLONE_NEWPID);
m.insert("network", CloneFlags::CLONE_NEWNET); m.insert("net", CloneFlags::CLONE_NEWNET);
m.insert("mount", CloneFlags::CLONE_NEWNS); m.insert("mnt", CloneFlags::CLONE_NEWNS);
m.insert("uts", CloneFlags::CLONE_NEWUTS); m.insert("uts", CloneFlags::CLONE_NEWUTS);
m.insert("cgroup", CloneFlags::CLONE_NEWCGROUP); m.insert("cgroup", CloneFlags::CLONE_NEWCGROUP);
m m
}; };
// type to name hashmap, better to be in NAMESPACES // type to name hashmap, better to be in NAMESPACES
pub static ref TYPETONAME: HashMap<&'static str, &'static str> = { pub static ref TYPETONAME: HashMap<oci::LinuxNamespaceType, &'static str> = {
let mut m = HashMap::new(); let mut m = HashMap::new();
m.insert("ipc", "ipc"); m.insert(oci::LinuxNamespaceType::Ipc, "ipc");
m.insert("user", "user"); m.insert(oci::LinuxNamespaceType::User, "user");
m.insert("pid", "pid"); m.insert(oci::LinuxNamespaceType::Pid, "pid");
m.insert("network", "net"); m.insert(oci::LinuxNamespaceType::Network, "net");
m.insert("mount", "mnt"); m.insert(oci::LinuxNamespaceType::Mount, "mnt");
m.insert("cgroup", "cgroup"); m.insert(oci::LinuxNamespaceType::Cgroup, "cgroup");
m.insert("uts", "uts"); m.insert(oci::LinuxNamespaceType::Uts, "uts");
m m
}; };
pub static ref DEFAULT_DEVICES: Vec<LinuxDevice> = { pub static ref DEFAULT_DEVICES: Vec<LinuxDevice> = {
vec![ vec![
LinuxDevice { oci::LinuxDeviceBuilder::default()
path: "/dev/null".to_string(), .path(PathBuf::from("/dev/null"))
r#type: "c".to_string(), .typ(oci::LinuxDeviceType::C)
major: 1, .major(1)
minor: 3, .minor(3)
file_mode: Some(0o666), .file_mode(0o066_u32)
uid: Some(0xffffffff), .uid(0xffffffff_u32)
gid: Some(0xffffffff), .gid(0xffffffff_u32)
}, .build()
LinuxDevice { .unwrap(),
path: "/dev/zero".to_string(), oci::LinuxDeviceBuilder::default()
r#type: "c".to_string(), .path(PathBuf::from("/dev/zero"))
major: 1, .typ(oci::LinuxDeviceType::C)
minor: 5, .major(1)
file_mode: Some(0o666), .minor(5)
uid: Some(0xffffffff), .file_mode(0o066_u32)
gid: Some(0xffffffff), .uid(0xffffffff_u32)
}, .gid(0xffffffff_u32)
LinuxDevice { .build()
path: "/dev/full".to_string(), .unwrap(),
r#type: "c".to_string(), oci::LinuxDeviceBuilder::default()
major: 1, .path(PathBuf::from("/dev/full"))
minor: 7, .typ(oci::LinuxDeviceType::C)
file_mode: Some(0o666), .major(1)
uid: Some(0xffffffff), .minor(7)
gid: Some(0xffffffff), .file_mode(0o066_u32)
}, .uid(0xffffffff_u32)
LinuxDevice { .gid(0xffffffff_u32)
path: "/dev/tty".to_string(), .build()
r#type: "c".to_string(), .unwrap(),
major: 5, oci::LinuxDeviceBuilder::default()
minor: 0, .path(PathBuf::from("/dev/tty"))
file_mode: Some(0o666), .typ(oci::LinuxDeviceType::C)
uid: Some(0xffffffff), .major(5)
gid: Some(0xffffffff), .minor(0)
}, .file_mode(0o066_u32)
LinuxDevice { .uid(0xffffffff_u32)
path: "/dev/urandom".to_string(), .gid(0xffffffff_u32)
r#type: "c".to_string(), .build()
major: 1, .unwrap(),
minor: 9, oci::LinuxDeviceBuilder::default()
file_mode: Some(0o666), .path(PathBuf::from("/dev/urandom"))
uid: Some(0xffffffff), .typ(oci::LinuxDeviceType::C)
gid: Some(0xffffffff), .major(1)
}, .minor(9)
LinuxDevice { .file_mode(0o066_u32)
path: "/dev/random".to_string(), .uid(0xffffffff_u32)
r#type: "c".to_string(), .gid(0xffffffff_u32)
major: 1, .build()
minor: 8, .unwrap(),
file_mode: Some(0o666), oci::LinuxDeviceBuilder::default()
uid: Some(0xffffffff), .path(PathBuf::from("/dev/random"))
gid: Some(0xffffffff), .typ(oci::LinuxDeviceType::C)
}, .major(1)
.minor(8)
.file_mode(0o066_u32)
.uid(0xffffffff_u32)
.gid(0xffffffff_u32)
.build()
.unwrap(),
] ]
}; };
@ -402,7 +409,7 @@ fn do_init_child(cwfd: RawFd) -> Result<()> {
let buf = read_sync(crfd)?; let buf = read_sync(crfd)?;
let state_str = std::str::from_utf8(&buf)?; let state_str = std::str::from_utf8(&buf)?;
let mut state: oci::State = serde_json::from_str(state_str)?; let mut state: OCIState = serde_json::from_str(state_str)?;
log_child!(cfd_log, "notify parent to send cgroup manager"); log_child!(cfd_log, "notify parent to send cgroup manager");
write_sync(cwfd, SYNC_SUCCESS, "")?; write_sync(cwfd, SYNC_SUCCESS, "")?;
@ -416,16 +423,16 @@ fn do_init_child(cwfd: RawFd) -> Result<()> {
#[cfg(feature = "standard-oci-runtime")] #[cfg(feature = "standard-oci-runtime")]
let csocket_fd = console::setup_console_socket(&std::env::var(CONSOLE_SOCKET_FD)?)?; let csocket_fd = console::setup_console_socket(&std::env::var(CONSOLE_SOCKET_FD)?)?;
let p = if spec.process.is_some() { let p = if spec.process().is_some() {
spec.process.as_ref().unwrap() spec.process().as_ref().unwrap()
} else { } else {
return Err(anyhow!("didn't find process in Spec")); return Err(anyhow!("didn't find process in Spec"));
}; };
if spec.linux.is_none() { if spec.linux().is_none() {
return Err(anyhow!(MissingLinux)); return Err(anyhow!(MissingLinux));
} }
let linux = spec.linux.as_ref().unwrap(); let linux = spec.linux().as_ref().unwrap();
// get namespace vector to join/new // get namespace vector to join/new
let nses = get_namespaces(linux); let nses = get_namespaces(linux);
@ -435,25 +442,30 @@ fn do_init_child(cwfd: RawFd) -> Result<()> {
let mut to_join = Vec::new(); let mut to_join = Vec::new();
for ns in &nses { for ns in &nses {
let s = NAMESPACES.get(&ns.r#type.as_str()); let ns_type = ns.typ().to_string();
let s = NAMESPACES.get(&ns_type.as_str());
if s.is_none() { if s.is_none() {
return Err(anyhow!(InvalidNamespace)); return Err(anyhow!(InvalidNamespace));
} }
let s = s.unwrap(); let s = s.unwrap();
if ns.path.is_empty() { if ns
.path()
.as_ref()
.map_or(true, |p| p.as_os_str().is_empty())
{
// skip the pidns since it has been done in parent process. // skip the pidns since it has been done in parent process.
if *s != CloneFlags::CLONE_NEWPID { if *s != CloneFlags::CLONE_NEWPID {
to_new.set(*s, true); to_new.set(*s, true);
} }
} else { } else {
let fd = let fd = fcntl::open(ns.path().as_ref().unwrap(), OFlag::O_CLOEXEC, Mode::empty())
fcntl::open(ns.path.as_str(), OFlag::O_CLOEXEC, Mode::empty()).map_err(|e| { .map_err(|e| {
log_child!( log_child!(
cfd_log, cfd_log,
"cannot open type: {} path: {}", "cannot open type: {} path: {}",
ns.r#type.clone(), &ns.typ().to_string(),
ns.path.clone() ns.path().as_ref().unwrap().display()
); );
log_child!(cfd_log, "error is : {:?}", e); log_child!(cfd_log, "error is : {:?}", e);
e e
@ -469,21 +481,23 @@ fn do_init_child(cwfd: RawFd) -> Result<()> {
userns = true; userns = true;
} }
if p.oom_score_adj.is_some() { if p.oom_score_adj().is_some() {
log_child!(cfd_log, "write oom score {}", p.oom_score_adj.unwrap()); log_child!(cfd_log, "write oom score {}", p.oom_score_adj().unwrap());
fs::write( fs::write(
"/proc/self/oom_score_adj", "/proc/self/oom_score_adj",
p.oom_score_adj.unwrap().to_string().as_bytes(), p.oom_score_adj().unwrap().to_string().as_bytes(),
)?; )?;
} }
// set rlimit // set rlimit
for rl in p.rlimits.iter() { let default_rlimits = Vec::new();
let process_rlimits = p.rlimits().as_ref().unwrap_or(&default_rlimits);
for rl in process_rlimits.iter() {
log_child!(cfd_log, "set resource limit: {:?}", rl); log_child!(cfd_log, "set resource limit: {:?}", rl);
setrlimit( setrlimit(
Resource::from_str(&rl.r#type)?, Resource::from_str(&rl.typ().to_string())?,
Rlim::from_raw(rl.soft), Rlim::from_raw(rl.soft()),
Rlim::from_raw(rl.hard), Rlim::from_raw(rl.hard()),
)?; )?;
} }
@ -565,12 +579,17 @@ fn do_init_child(cwfd: RawFd) -> Result<()> {
} }
if to_new.contains(CloneFlags::CLONE_NEWUTS) { if to_new.contains(CloneFlags::CLONE_NEWUTS) {
unistd::sethostname(&spec.hostname)?; unistd::sethostname(
spec.hostname()
.as_ref()
.map_or("".to_string(), |x| x.clone()),
)?;
} }
let rootfs = spec.root.as_ref().unwrap().path.as_str(); let rootfs = spec.root().as_ref().unwrap().path().display().to_string();
log_child!(cfd_log, "setup rootfs {}", rootfs);
let root = fs::canonicalize(rootfs)?; log_child!(cfd_log, "setup rootfs {}", &rootfs);
let root = fs::canonicalize(&rootfs)?;
let rootfs = root.to_str().unwrap(); let rootfs = root.to_str().unwrap();
if to_new.contains(CloneFlags::CLONE_NEWNS) { if to_new.contains(CloneFlags::CLONE_NEWNS) {
@ -605,15 +624,22 @@ fn do_init_child(cwfd: RawFd) -> Result<()> {
// CreateContainer Hooks: // CreateContainer Hooks:
// before pivot_root after prestart, createruntime // before pivot_root after prestart, createruntime
state.pid = std::process::id() as i32; state.pid = std::process::id() as i32;
state.status = oci::ContainerState::Created; state.status = spec::ContainerState::Created;
if let Some(hooks) = spec.hooks.as_ref() { if let Some(hooks) = spec.hooks().as_ref() {
log_child!( log_child!(
cfd_log, cfd_log,
"create_container hooks {:?}", "create_container hooks {:?}",
hooks.create_container hooks.create_container()
); );
let mut create_container_states = HookStates::new(); let mut create_container_states = HookStates::new();
create_container_states.execute_hooks(&hooks.create_container, Some(state.clone()))?; create_container_states.execute_hooks(
hooks
.create_container()
.clone()
.unwrap_or_default()
.as_slice(),
Some(state.clone()),
)?;
} }
} }
@ -627,7 +653,7 @@ fn do_init_child(cwfd: RawFd) -> Result<()> {
} }
// setup sysctl // setup sysctl
set_sysctls(&linux.sysctl)?; set_sysctls(&linux.sysctl().clone().unwrap_or_default())?;
unistd::chdir("/")?; unistd::chdir("/")?;
} }
@ -635,14 +661,14 @@ fn do_init_child(cwfd: RawFd) -> Result<()> {
mount::finish_rootfs(cfd_log, &spec, &oci_process)?; mount::finish_rootfs(cfd_log, &spec, &oci_process)?;
} }
if !oci_process.cwd.is_empty() { if !oci_process.cwd().as_os_str().is_empty() {
unistd::chdir(oci_process.cwd.as_str())?; unistd::chdir(oci_process.cwd().display().to_string().as_str())?;
} }
let guser = &oci_process.user; let guser = &oci_process.user();
let uid = Uid::from_raw(guser.uid); let uid = Uid::from_raw(guser.uid());
let gid = Gid::from_raw(guser.gid); let gid = Gid::from_raw(guser.gid());
// only change stdio devices owner when user // only change stdio devices owner when user
// isn't root. // isn't root.
@ -652,9 +678,8 @@ fn do_init_child(cwfd: RawFd) -> Result<()> {
setid(uid, gid)?; setid(uid, gid)?;
if !guser.additional_gids.is_empty() { if let Some(additional_gids) = guser.additional_gids() {
let gids: Vec<Gid> = guser let gids: Vec<Gid> = additional_gids
.additional_gids
.iter() .iter()
.map(|gid| Gid::from_raw(*gid)) .map(|gid| Gid::from_raw(*gid))
.collect(); .collect();
@ -671,12 +696,17 @@ fn do_init_child(cwfd: RawFd) -> Result<()> {
} }
// NoNewPrivileges // NoNewPrivileges
if oci_process.no_new_privileges { if oci_process.no_new_privileges().unwrap_or_default() {
capctl::prctl::set_no_new_privs().map_err(|_| anyhow!("cannot set no new privileges"))?; capctl::prctl::set_no_new_privs().map_err(|_| anyhow!("cannot set no new privileges"))?;
} }
// Set SELinux label // Set SELinux label
if !oci_process.selinux_label.is_empty() { if !oci_process
.selinux_label()
.clone()
.unwrap_or_default()
.is_empty()
{
if !selinux_enabled { if !selinux_enabled {
return Err(anyhow!( return Err(anyhow!(
"SELinux label for the process is provided but SELinux is not enabled on the running kernel" "SELinux label for the process is provided but SELinux is not enabled on the running kernel"
@ -684,12 +714,18 @@ fn do_init_child(cwfd: RawFd) -> Result<()> {
} }
log_child!(cfd_log, "Set SELinux label to the container process"); log_child!(cfd_log, "Set SELinux label to the container process");
selinux::set_exec_label(&oci_process.selinux_label)?; let default_label = String::new();
selinux::set_exec_label(
oci_process
.selinux_label()
.as_ref()
.unwrap_or(&default_label),
)?;
} }
// Log unknown seccomp system calls in advance before the log file descriptor closes. // Log unknown seccomp system calls in advance before the log file descriptor closes.
#[cfg(feature = "seccomp")] #[cfg(feature = "seccomp")]
if let Some(ref scmp) = linux.seccomp { if let Some(ref scmp) = linux.seccomp() {
if let Some(syscalls) = seccomp::get_unknown_syscalls(scmp) { if let Some(syscalls) = seccomp::get_unknown_syscalls(scmp) {
log_child!(cfd_log, "unknown seccomp system calls: {:?}", syscalls); log_child!(cfd_log, "unknown seccomp system calls: {:?}", syscalls);
} }
@ -699,20 +735,21 @@ fn do_init_child(cwfd: RawFd) -> Result<()> {
// before dropping capabilities because the calling thread // before dropping capabilities because the calling thread
// must have the CAP_SYS_ADMIN. // must have the CAP_SYS_ADMIN.
#[cfg(feature = "seccomp")] #[cfg(feature = "seccomp")]
if !oci_process.no_new_privileges { if !oci_process.no_new_privileges().unwrap_or_default() {
if let Some(ref scmp) = linux.seccomp { if let Some(ref scmp) = linux.seccomp() {
seccomp::init_seccomp(scmp)?; seccomp::init_seccomp(scmp)?;
} }
} }
// Drop capabilities // Drop capabilities
if oci_process.capabilities.is_some() { if oci_process.capabilities().is_some() {
let c = oci_process.capabilities.as_ref().unwrap(); let c = oci_process.capabilities().as_ref().unwrap();
capabilities::drop_privileges(cfd_log, c)?; capabilities::drop_privileges(cfd_log, c)?;
} }
let args = oci_process.args.to_vec(); let default_vec = Vec::new();
let env = oci_process.env.to_vec(); let args = oci_process.args().as_ref().unwrap_or(&default_vec).to_vec();
let env = oci_process.env().as_ref().unwrap_or(&default_vec).to_vec();
let mut fifofd = -1; let mut fifofd = -1;
if init { if init {
@ -734,7 +771,7 @@ fn do_init_child(cwfd: RawFd) -> Result<()> {
if env::var_os(HOME_ENV_KEY).is_none() { if env::var_os(HOME_ENV_KEY).is_none() {
// try to set "HOME" env by uid // try to set "HOME" env by uid
if let Ok(Some(user)) = User::from_uid(Uid::from_raw(guser.uid)) { if let Ok(Some(user)) = User::from_uid(Uid::from_raw(guser.uid())) {
if let Ok(user_home_dir) = user.dir.into_os_string().into_string() { if let Ok(user_home_dir) = user.dir.into_os_string().into_string() {
env::set_var(HOME_ENV_KEY, user_home_dir); env::set_var(HOME_ENV_KEY, user_home_dir);
} }
@ -758,7 +795,7 @@ fn do_init_child(cwfd: RawFd) -> Result<()> {
let _ = unistd::close(crfd); let _ = unistd::close(crfd);
let _ = unistd::close(cwfd); let _ = unistd::close(cwfd);
if oci_process.terminal { if oci_process.terminal().unwrap_or_default() {
cfg_if::cfg_if! { cfg_if::cfg_if! {
if #[cfg(feature = "standard-oci-runtime")] { if #[cfg(feature = "standard-oci-runtime")] {
if let Some(csocket_fd) = csocket_fd { if let Some(csocket_fd) = csocket_fd {
@ -791,10 +828,17 @@ fn do_init_child(cwfd: RawFd) -> Result<()> {
// * should be run after container is created and before container is started (before user-specific command is executed) // * should be run after container is created and before container is started (before user-specific command is executed)
// * spec details: https://github.com/opencontainers/runtime-spec/blob/c1662686cff159595277b79322d0272f5182941b/config.md#startcontainer-hooks // * spec details: https://github.com/opencontainers/runtime-spec/blob/c1662686cff159595277b79322d0272f5182941b/config.md#startcontainer-hooks
state.pid = std::process::id() as i32; state.pid = std::process::id() as i32;
state.status = oci::ContainerState::Created; state.status = spec::ContainerState::Created;
if let Some(hooks) = spec.hooks.as_ref() { if let Some(hooks) = spec.hooks().as_ref() {
let mut start_container_states = HookStates::new(); let mut start_container_states = HookStates::new();
start_container_states.execute_hooks(&hooks.start_container, Some(state))?; start_container_states.execute_hooks(
hooks
.start_container()
.clone()
.unwrap_or_default()
.as_slice(),
Some(state),
)?;
} }
} }
@ -802,8 +846,8 @@ fn do_init_child(cwfd: RawFd) -> Result<()> {
// do_exec as possible in order to reduce the amount of // do_exec as possible in order to reduce the amount of
// system calls in the seccomp profiles. // system calls in the seccomp profiles.
#[cfg(feature = "seccomp")] #[cfg(feature = "seccomp")]
if oci_process.no_new_privileges { if oci_process.no_new_privileges().unwrap_or_default() {
if let Some(ref scmp) = linux.seccomp { if let Some(ref scmp) = linux.seccomp() {
seccomp::init_seccomp(scmp)?; seccomp::init_seccomp(scmp)?;
} }
} }
@ -869,8 +913,8 @@ impl BaseContainer for LinuxContainer {
0 0
}; };
let root = match oci.root.as_ref() { let root = match oci.root().as_ref() {
Some(s) => s.path.as_str(), Some(s) => s.path().display().to_string(),
None => return Err(anyhow!("Unable to get root path: oci.root is none")), None => return Err(anyhow!("Unable to get root path: oci.root is none")),
}; };
@ -881,12 +925,12 @@ impl BaseContainer for LinuxContainer {
}; };
Ok(OCIState { Ok(OCIState {
version: oci.version.clone(), version: oci.version().clone(),
id: self.id(), id: self.id(),
status, status,
pid, pid,
bundle, bundle,
annotations: oci.annotations.clone(), annotations: oci.annotations().clone().unwrap_or_default(),
}) })
} }
@ -920,14 +964,10 @@ impl BaseContainer for LinuxContainer {
fn set(&mut self, r: LinuxResources) -> Result<()> { fn set(&mut self, r: LinuxResources) -> Result<()> {
self.cgroup_manager.as_ref().set(&r, true)?; self.cgroup_manager.as_ref().set(&r, true)?;
self.config if let Some(linux) = self.config.spec.as_mut().unwrap().linux_mut() {
.spec linux.set_resources(Some(r));
.as_mut() }
.unwrap()
.linux
.as_mut()
.unwrap()
.resources = Some(r);
Ok(()) Ok(())
} }
@ -956,23 +996,23 @@ impl BaseContainer for LinuxContainer {
} }
let spec = self.config.spec.as_ref().unwrap(); let spec = self.config.spec.as_ref().unwrap();
if spec.linux.is_none() { if spec.linux().is_none() {
return Err(anyhow!("no linux config")); return Err(anyhow!("no linux config"));
} }
let linux = spec.linux.as_ref().unwrap(); let linux = spec.linux().as_ref().unwrap();
if p.oci.capabilities.is_none() { if p.oci.capabilities().is_none() {
// No capabilities, inherit from container process // No capabilities, inherit from container process
let process = spec let process = spec
.process .process()
.as_ref() .as_ref()
.ok_or_else(|| anyhow!("no process config"))?; .ok_or_else(|| anyhow!("no process config"))?;
p.oci.capabilities = Some( p.oci.set_capabilities(Some(
process process
.capabilities .capabilities()
.clone() .clone()
.ok_or_else(|| anyhow!("missing process capabilities"))?, .ok_or_else(|| anyhow!("missing process capabilities"))?,
); ));
} }
let (pfd_log, cfd_log) = unistd::pipe().context("failed to create pipe")?; let (pfd_log, cfd_log) = unistd::pipe().context("failed to create pipe")?;
@ -1247,15 +1287,24 @@ impl BaseContainer for LinuxContainer {
// * should be executed after the container is deleted but before the delete operation returns // * should be executed after the container is deleted but before the delete operation returns
// * the executable file is in agent namespace // * the executable file is in agent namespace
// * should also be executed in agent namespace. // * should also be executed in agent namespace.
if let Some(hooks) = spec.hooks.as_ref() { if let Some(hooks) = spec.hooks().as_ref() {
info!(self.logger, "guest Poststop hook"); info!(self.logger, "guest Poststop hook");
let mut hook_states = HookStates::new(); let mut hook_states = HookStates::new();
hook_states.execute_hooks(&hooks.poststop, Some(st))?; hook_states.execute_hooks(
hooks.poststop().clone().unwrap_or_default().as_slice(),
Some(st),
)?;
} }
self.status.transition(ContainerState::Stopped); self.status.transition(ContainerState::Stopped);
mount::umount2( mount::umount2(
spec.root.as_ref().unwrap().path.as_str(), spec.root()
.as_ref()
.unwrap()
.path()
.display()
.to_string()
.as_str(),
MntFlags::MNT_DETACH, MntFlags::MNT_DETACH,
)?; )?;
fs::remove_dir_all(&self.root)?; fs::remove_dir_all(&self.root)?;
@ -1300,10 +1349,13 @@ impl BaseContainer for LinuxContainer {
// * should be executed after the container is started but before the delete operation returns // * should be executed after the container is started but before the delete operation returns
// * the executable file is in agent namespace // * the executable file is in agent namespace
// * should also be executed in agent namespace. // * should also be executed in agent namespace.
if let Some(hooks) = spec.hooks.as_ref() { if let Some(hooks) = spec.hooks().as_ref() {
info!(self.logger, "guest Poststart hook"); info!(self.logger, "guest Poststart hook");
let mut hook_states = HookStates::new(); let mut hook_states = HookStates::new();
hook_states.execute_hooks(&hooks.poststart, Some(st))?; hook_states.execute_hooks(
hooks.poststart().clone().unwrap_or_default().as_slice(),
Some(st),
)?;
} }
unistd::close(fd)?; unistd::close(fd)?;
@ -1351,21 +1403,26 @@ fn do_exec(args: &[String]) -> ! {
pub fn update_namespaces(logger: &Logger, spec: &mut Spec, init_pid: RawFd) -> Result<()> { pub fn update_namespaces(logger: &Logger, spec: &mut Spec, init_pid: RawFd) -> Result<()> {
info!(logger, "updating namespaces"); info!(logger, "updating namespaces");
let linux = spec let linux = spec
.linux .linux_mut()
.as_mut() .as_mut()
.ok_or_else(|| anyhow!("Spec didn't contain linux field"))?; .ok_or_else(|| anyhow!("Spec didn't contain linux field"))?;
let namespaces = linux.namespaces.as_mut_slice(); if let Some(namespaces) = linux.namespaces_mut().as_mut() {
for namespace in namespaces.iter_mut() { for namespace in namespaces.iter_mut() {
if TYPETONAME.contains_key(namespace.r#type.as_str()) { if TYPETONAME.contains_key(&namespace.typ()) {
let ns_path = format!( let ns_path = format!(
"/proc/{}/ns/{}", "/proc/{}/ns/{}",
init_pid, init_pid,
TYPETONAME.get(namespace.r#type.as_str()).unwrap() TYPETONAME.get(&namespace.typ()).unwrap()
); );
if namespace.path.is_empty() { if namespace
namespace.path = ns_path; .path()
.as_ref()
.map_or(true, |p| p.as_os_str().is_empty())
{
namespace.set_path(Some(PathBuf::from(&ns_path)));
}
} }
} }
} }
@ -1374,24 +1431,28 @@ pub fn update_namespaces(logger: &Logger, spec: &mut Spec, init_pid: RawFd) -> R
} }
fn get_pid_namespace(logger: &Logger, linux: &Linux) -> Result<PidNs> { fn get_pid_namespace(logger: &Logger, linux: &Linux) -> Result<PidNs> {
for ns in &linux.namespaces { let linux_namespaces = linux.namespaces().clone().unwrap_or_default();
if ns.r#type == "pid" { for ns in &linux_namespaces {
if ns.path.is_empty() { if &ns.typ().to_string() == "pid" {
return Ok(PidNs::new(true, None)); let fd = match ns.path() {
} None => return Ok(PidNs::new(true, None)),
Some(ns_path) => fcntl::open(
let fd = ns_path.display().to_string().as_str(),
fcntl::open(ns.path.as_str(), OFlag::O_RDONLY, Mode::empty()).map_err(|e| { OFlag::O_RDONLY,
Mode::empty(),
)
.map_err(|e| {
error!( error!(
logger, logger,
"cannot open type: {} path: {}", "cannot open type: {} path: {}",
ns.r#type.clone(), &ns.typ().to_string(),
ns.path.clone() ns_path.display()
); );
error!(logger, "error is : {:?}", e); error!(logger, "error is : {:?}", e);
e e
})?; })?,
};
return Ok(PidNs::new(true, Some(fd))); return Ok(PidNs::new(true, Some(fd)));
} }
@ -1402,18 +1463,25 @@ fn get_pid_namespace(logger: &Logger, linux: &Linux) -> Result<PidNs> {
fn is_userns_enabled(linux: &Linux) -> bool { fn is_userns_enabled(linux: &Linux) -> bool {
linux linux
.namespaces .namespaces()
.clone()
.unwrap_or_default()
.iter() .iter()
.any(|ns| ns.r#type == "user" && ns.path.is_empty()) .any(|ns| &ns.typ().to_string() == "user" && ns.path().is_none())
} }
fn get_namespaces(linux: &Linux) -> Vec<LinuxNamespace> { fn get_namespaces(linux: &Linux) -> Vec<LinuxNamespace> {
linux linux
.namespaces .namespaces()
.clone()
.unwrap_or_default()
.iter() .iter()
.map(|ns| LinuxNamespace { .map(|ns| {
r#type: ns.r#type.clone(), let mut namespace = LinuxNamespace::default();
path: ns.path.clone(), namespace.set_typ(ns.typ());
namespace.set_path(ns.path().clone());
namespace
}) })
.collect() .collect()
} }
@ -1455,8 +1523,11 @@ async fn join_namespaces(
) -> Result<()> { ) -> Result<()> {
let logger = logger.new(o!("action" => "join-namespaces")); let logger = logger.new(o!("action" => "join-namespaces"));
let linux = spec.linux.as_ref().unwrap(); let linux = spec
let res = linux.resources.as_ref(); .linux()
.as_ref()
.ok_or_else(|| anyhow!("Spec didn't contain linux field"))?;
let res = linux.resources().as_ref();
let userns = is_userns_enabled(linux); let userns = is_userns_enabled(linux);
@ -1494,17 +1565,11 @@ async fn join_namespaces(
if userns { if userns {
info!(logger, "setup uid/gid mappings"); info!(logger, "setup uid/gid mappings");
let uid_mappings = linux.uid_mappings().clone().unwrap_or_default();
let gid_mappings = linux.gid_mappings().clone().unwrap_or_default();
// setup uid/gid mappings // setup uid/gid mappings
write_mappings( write_mappings(&logger, &format!("/proc/{}/uid_map", p.pid), &uid_mappings)?;
&logger, write_mappings(&logger, &format!("/proc/{}/gid_map", p.pid), &gid_mappings)?;
&format!("/proc/{}/uid_map", p.pid),
&linux.uid_mappings,
)?;
write_mappings(
&logger,
&format!("/proc/{}/gid_map", p.pid),
&linux.gid_mappings,
)?;
} }
// apply cgroups // apply cgroups
@ -1534,10 +1599,13 @@ async fn join_namespaces(
// * should be executed during the start operation, and before the container command is executed // * should be executed during the start operation, and before the container command is executed
// * the executable file is in agent namespace // * the executable file is in agent namespace
// * should also be executed in agent namespace. // * should also be executed in agent namespace.
if let Some(hooks) = spec.hooks.as_ref() { if let Some(hooks) = spec.hooks().as_ref() {
info!(logger, "guest Prestart hook"); info!(logger, "guest Prestart hook");
let mut hook_states = HookStates::new(); let mut hook_states = HookStates::new();
hook_states.execute_hooks(&hooks.prestart, Some(st.clone()))?; hook_states.execute_hooks(
hooks.prestart().clone().unwrap_or_default().as_slice(),
Some(st.clone()),
)?;
} }
// notify child run prestart hooks completed // notify child run prestart hooks completed
@ -1554,8 +1622,8 @@ async fn join_namespaces(
fn write_mappings(logger: &Logger, path: &str, maps: &[LinuxIdMapping]) -> Result<()> { fn write_mappings(logger: &Logger, path: &str, maps: &[LinuxIdMapping]) -> Result<()> {
let data = maps let data = maps
.iter() .iter()
.filter(|m| m.size != 0) .filter(|m| m.size() != 0)
.map(|m| format!("{} {} {}\n", m.container_id, m.host_id, m.size)) .map(|m| format!("{} {} {}\n", m.container_id(), m.host_id(), m.size()))
.collect::<Vec<_>>() .collect::<Vec<_>>()
.join(""); .join("");
@ -1624,18 +1692,24 @@ impl LinuxContainer {
.context(format!("Cannot change owner of container {} root", id))?; .context(format!("Cannot change owner of container {} root", id))?;
let spec = config.spec.as_ref().unwrap(); let spec = config.spec.as_ref().unwrap();
let linux = spec.linux.as_ref().unwrap(); let linux_cgroups_path = spec
.linux()
.as_ref()
.unwrap()
.cgroups_path()
.as_ref()
.map_or(String::new(), |cgrp| cgrp.display().to_string());
let cpath = if config.use_systemd_cgroup { let cpath = if config.use_systemd_cgroup {
if linux.cgroups_path.len() == 2 { if linux_cgroups_path.len() == 2 {
format!("system.slice:kata_agent:{}", id.as_str()) format!("system.slice:kata_agent:{}", id.as_str())
} else { } else {
linux.cgroups_path.clone() linux_cgroups_path.clone()
} }
} else if linux.cgroups_path.is_empty() { } else if linux_cgroups_path.is_empty() {
format!("/{}", id.as_str()) format!("/{}", id.as_str())
} else { } else {
// if we have a systemd cgroup path we need to convert it to a fs cgroup path // if we have a systemd cgroup path we need to convert it to a fs cgroup path
linux.cgroups_path.replace(':', "/") linux_cgroups_path.replace(':', "/")
}; };
let cgroup_manager: Box<dyn Manager + Send + Sync> = if config.use_systemd_cgroup { let cgroup_manager: Box<dyn Manager + Send + Sync> = if config.use_systemd_cgroup {
@ -1708,7 +1782,8 @@ mod tests {
use super::*; use super::*;
use crate::process::Process; use crate::process::Process;
use nix::unistd::Uid; use nix::unistd::Uid;
use oci::{LinuxDeviceCgroup, Root}; use oci::{LinuxBuilder, LinuxDeviceCgroupBuilder, LinuxResourcesBuilder, Root, SpecBuilder};
use oci_spec::runtime as oci;
use std::fs; use std::fs;
use std::os::unix::fs::MetadataExt; use std::os::unix::fs::MetadataExt;
use std::os::unix::io::AsRawFd; use std::os::unix::io::AsRawFd;
@ -1777,10 +1852,10 @@ mod tests {
let ns = NAMESPACES.get("pid"); let ns = NAMESPACES.get("pid");
assert!(ns.is_some()); assert!(ns.is_some());
let ns = NAMESPACES.get("network"); let ns = NAMESPACES.get("net");
assert!(ns.is_some()); assert!(ns.is_some());
let ns = NAMESPACES.get("mount"); let ns = NAMESPACES.get("mnt");
assert!(ns.is_some()); assert!(ns.is_some());
let ns = NAMESPACES.get("uts"); let ns = NAMESPACES.get("uts");
@ -1795,25 +1870,25 @@ mod tests {
lazy_static::initialize(&TYPETONAME); lazy_static::initialize(&TYPETONAME);
assert_eq!(TYPETONAME.len(), 7); assert_eq!(TYPETONAME.len(), 7);
let ns = TYPETONAME.get("user"); let ns = TYPETONAME.get(&oci::LinuxNamespaceType::User);
assert!(ns.is_some()); assert!(ns.is_some());
let ns = TYPETONAME.get("ipc"); let ns = TYPETONAME.get(&oci::LinuxNamespaceType::Ipc);
assert!(ns.is_some()); assert!(ns.is_some());
let ns = TYPETONAME.get("pid"); let ns = TYPETONAME.get(&oci::LinuxNamespaceType::Pid);
assert!(ns.is_some()); assert!(ns.is_some());
let ns = TYPETONAME.get("network"); let ns = TYPETONAME.get(&oci::LinuxNamespaceType::Network);
assert!(ns.is_some()); assert!(ns.is_some());
let ns = TYPETONAME.get("mount"); let ns = TYPETONAME.get(&oci::LinuxNamespaceType::Mount);
assert!(ns.is_some()); assert!(ns.is_some());
let ns = TYPETONAME.get("uts"); let ns = TYPETONAME.get(&oci::LinuxNamespaceType::Uts);
assert!(ns.is_some()); assert!(ns.is_some());
let ns = TYPETONAME.get("cgroup"); let ns = TYPETONAME.get(&oci::LinuxNamespaceType::Cgroup);
assert!(ns.is_some()); assert!(ns.is_some());
} }
@ -1823,21 +1898,18 @@ mod tests {
.duration_since(UNIX_EPOCH) .duration_since(UNIX_EPOCH)
.expect("Time went backwards"); .expect("Time went backwards");
let root = Root { let mut root = Root::default();
path: String::from("/tmp"), root.set_path(String::from("/tmp").into());
..Default::default()
};
let linux_resources = LinuxResources { let linux_resources = LinuxResourcesBuilder::default()
devices: vec![LinuxDeviceCgroup { .devices(vec![LinuxDeviceCgroupBuilder::default()
allow: true, .allow(true)
r#type: String::new(), .typ(oci::LinuxDeviceType::C)
major: None, .access("rwm")
minor: None, .build()
access: String::from("rwm"), .unwrap()])
}], .build()
..Default::default() .unwrap();
};
let cgroups_path = format!( let cgroups_path = format!(
"/{}/dummycontainer{}", "/{}/dummycontainer{}",
@ -1845,15 +1917,18 @@ mod tests {
since_the_epoch.as_millis() since_the_epoch.as_millis()
); );
let spec = Spec { let mut spec = SpecBuilder::default()
linux: Some(Linux { .linux(
cgroups_path, LinuxBuilder::default()
resources: Some(linux_resources), .cgroups_path(cgroups_path)
..Default::default() .resources(linux_resources)
}), .build()
root: Some(root), .unwrap(),
..Default::default() )
}; .root(root)
.build()
.unwrap();
spec.set_process(None);
CreateOpts { CreateOpts {
cgroup_name: "".to_string(), cgroup_name: "".to_string(),
@ -1959,7 +2034,14 @@ mod tests {
#[test] #[test]
fn test_linuxcontainer_oci_state_no_root_parent() { fn test_linuxcontainer_oci_state_no_root_parent() {
let ret = new_linux_container_and_then(|mut c: LinuxContainer| { let ret = new_linux_container_and_then(|mut c: LinuxContainer| {
c.config.spec.as_mut().unwrap().root.as_mut().unwrap().path = "/".to_string(); c.config
.spec
.as_mut()
.unwrap()
.root_mut()
.as_mut()
.unwrap()
.set_path("/".to_string().into());
c.oci_state() c.oci_state()
}); });
assert!(ret.is_err(), "Expecting Err, Got {:?}", ret); assert!(ret.is_err(), "Expecting Err, Got {:?}", ret);
@ -2032,21 +2114,25 @@ mod tests {
#[tokio::test] #[tokio::test]
async fn test_linuxcontainer_start() { async fn test_linuxcontainer_start() {
let (c, _dir) = new_linux_container(); let (c, _dir) = new_linux_container();
let mut oci_process = oci::Process::default();
oci_process.set_capabilities(None);
let ret = c let ret = c
.unwrap() .unwrap()
.start(Process::new(&sl(), &oci::Process::default(), "123", true, 1, None).unwrap()) .start(Process::new(&sl(), &oci_process, "123", true, 1, None).unwrap())
.await; .await;
assert!(ret.is_err(), "Expecting Err, Got {:?}", ret); assert!(format!("{:?}", ret).contains("no process config"));
} }
#[tokio::test] #[tokio::test]
async fn test_linuxcontainer_run() { async fn test_linuxcontainer_run() {
let (c, _dir) = new_linux_container(); let (c, _dir) = new_linux_container();
let mut oci_process = oci::Process::default();
oci_process.set_capabilities(None);
let ret = c let ret = c
.unwrap() .unwrap()
.run(Process::new(&sl(), &oci::Process::default(), "123", true, 1, None).unwrap()) .run(Process::new(&sl(), &oci_process, "123", true, 1, None).unwrap())
.await; .await;
assert!(ret.is_err(), "Expecting Err, Got {:?}", ret); assert!(format!("{:?}", ret).contains("no process config"));
} }
#[tokio::test] #[tokio::test]

File diff suppressed because it is too large Load Diff

View File

@ -13,6 +13,7 @@ use nix::sys::stat::{self, Mode, SFlag};
use nix::unistd::{self, Gid, Uid}; use nix::unistd::{self, Gid, Uid};
use nix::NixPath; use nix::NixPath;
use oci::{LinuxDevice, Mount, Process, Spec}; use oci::{LinuxDevice, Mount, Process, Spec};
use oci_spec::runtime as oci;
use std::collections::{HashMap, HashSet}; use std::collections::{HashMap, HashSet};
use std::fs::{self, OpenOptions}; use std::fs::{self, OpenOptions};
use std::mem::MaybeUninit; use std::mem::MaybeUninit;
@ -172,24 +173,33 @@ pub fn init_rootfs(
lazy_static::initialize(&LINUXDEVICETYPE); lazy_static::initialize(&LINUXDEVICETYPE);
let linux = &spec let linux = &spec
.linux .linux()
.as_ref() .as_ref()
.ok_or_else(|| anyhow!("Could not get linux configuration from spec"))?; .ok_or_else(|| anyhow!("Could not get linux configuration from spec"))?;
let mut flags = MsFlags::MS_REC; let mut flags = MsFlags::MS_REC;
match PROPAGATION.get(&linux.rootfs_propagation.as_str()) { let default_propagation = String::new();
match PROPAGATION.get(
&linux
.rootfs_propagation()
.as_ref()
.unwrap_or(&default_propagation)
.as_str(),
) {
Some(fl) => flags |= *fl, Some(fl) => flags |= *fl,
None => flags |= MsFlags::MS_SLAVE, None => flags |= MsFlags::MS_SLAVE,
} }
let label = &linux.mount_label; let default_mntlabel = String::new();
let label = linux.mount_label().as_ref().unwrap_or(&default_mntlabel);
let root = spec let root = spec
.root .root()
.as_ref() .as_ref()
.ok_or_else(|| anyhow!("Could not get rootfs path from spec")) .ok_or_else(|| anyhow!("Could not get rootfs path from spec"))
.and_then(|r| { .and_then(|r| {
fs::canonicalize(r.path.as_str()).context("Could not canonicalize rootfs path") fs::canonicalize(r.path().display().to_string().as_str())
.context("Could not canonicalize rootfs path")
})?; })?;
let rootfs = (*root) let rootfs = (*root)
@ -209,13 +219,13 @@ pub fn init_rootfs(
)?; )?;
let mut bind_mount_dev = false; let mut bind_mount_dev = false;
for m in &spec.mounts { let default_mnts = vec![];
for m in spec.mounts().as_ref().unwrap_or(&default_mnts) {
let (mut flags, pgflags, data) = parse_mount(m); let (mut flags, pgflags, data) = parse_mount(m);
if !m.destination.starts_with('/') || m.destination.contains("..") {
return Err(anyhow!( let mount_dest = &m.destination().display().to_string();
"the mount destination {} is invalid", if !mount_dest.starts_with('/') || mount_dest.contains("..") {
m.destination return Err(anyhow!("the mount destination {} is invalid", mount_dest));
));
} }
// From https://github.com/opencontainers/runtime-spec/blob/main/config.md#mounts // From https://github.com/opencontainers/runtime-spec/blob/main/config.md#mounts
@ -223,35 +233,37 @@ pub fn init_rootfs(
// bind may be only specified in the oci spec options -> flags update r#type // bind may be only specified in the oci spec options -> flags update r#type
let m = &{ let m = &{
let mut mbind = m.clone(); let mut mbind = m.clone();
if mbind.r#type.is_empty() && flags & MsFlags::MS_BIND == MsFlags::MS_BIND { if mbind.typ().is_none() && flags & MsFlags::MS_BIND == MsFlags::MS_BIND {
mbind.r#type = "bind".to_string(); mbind.set_typ(Some("bind".to_string()));
} }
mbind mbind
}; };
if m.r#type == "cgroup" { let default_typ = String::new();
let mount_typ = m.typ().as_ref().unwrap_or(&default_typ);
if mount_typ == "cgroup" {
mount_cgroups(cfd_log, m, rootfs, flags, &data, cpath, mounts)?; mount_cgroups(cfd_log, m, rootfs, flags, &data, cpath, mounts)?;
} else { } else {
if m.destination == "/dev" { if mount_dest.clone().as_str() == "/dev" {
if m.r#type == "bind" { if mount_typ == "bind" {
bind_mount_dev = true; bind_mount_dev = true;
} }
flags &= !MsFlags::MS_RDONLY; flags &= !MsFlags::MS_RDONLY;
} }
if m.r#type == "bind" { if mount_typ == "bind" {
check_proc_mount(m)?; check_proc_mount(m)?;
} }
// If the destination already exists and is not a directory, we bail // If the destination already exists and is not a directory, we bail
// out This is to avoid mounting through a symlink or similar -- which // out This is to avoid mounting through a symlink or similar -- which
// has been a "fun" attack scenario in the past. // has been a "fun" attack scenario in the past.
if m.r#type == "proc" || m.r#type == "sysfs" { if mount_typ == "proc" || mount_typ == "sysfs" {
if let Ok(meta) = fs::symlink_metadata(&m.destination) { if let Ok(meta) = fs::symlink_metadata(mount_dest) {
if !meta.is_dir() { if !meta.is_dir() {
return Err(anyhow!( return Err(anyhow!(
"Mount point {} must be ordinary directory: got {:?}", "Mount point {} must be ordinary directory: got {:?}",
m.destination, &mount_dest,
meta.file_type() meta.file_type()
)); ));
} }
@ -263,8 +275,8 @@ pub fn init_rootfs(
// effective. // effective.
// first check that we have non-default options required before attempting a // first check that we have non-default options required before attempting a
// remount // remount
if m.r#type == "bind" && !pgflags.is_empty() { if mount_typ == "bind" && !pgflags.is_empty() {
let dest = secure_join(rootfs, &m.destination); let dest = secure_join(rootfs, mount_dest);
mount( mount(
None::<&str>, None::<&str>,
dest.as_str(), dest.as_str(),
@ -282,9 +294,11 @@ pub fn init_rootfs(
// in case the /dev directory was binded mount from guest, // in case the /dev directory was binded mount from guest,
// then there's no need to create devices nodes and symlinks // then there's no need to create devices nodes and symlinks
// in /dev. // in /dev.
let default_devs = Vec::new();
let linux_devices = linux.devices().as_ref().unwrap_or(&default_devs);
if !bind_mount_dev { if !bind_mount_dev {
default_symlinks()?; default_symlinks()?;
create_devices(&linux.devices, bind_device)?; create_devices(linux_devices, bind_device)?;
ensure_ptmx()?; ensure_ptmx()?;
} }
@ -308,17 +322,19 @@ fn check_proc_mount(m: &Mount) -> Result<()> {
"/proc/net/dev", "/proc/net/dev",
]; ];
let mount_dest = m.destination().display().to_string();
for i in valid_destinations.iter() { for i in valid_destinations.iter() {
if m.destination.as_str() == *i { if mount_dest == *i {
return Ok(()); return Ok(());
} }
} }
if m.destination == PROC_PATH { if mount_dest == PROC_PATH {
// only allow a mount on-top of proc if it's source is "proc" // only allow a mount on-top of proc if it's source is "proc"
unsafe { unsafe {
let mut stats = MaybeUninit::<libc::statfs>::uninit(); let mut stats = MaybeUninit::<libc::statfs>::uninit();
if m.source let mount_source = m.source().as_ref().unwrap().display().to_string();
if mount_source
.with_nix_path(|path| libc::statfs(path.as_ptr(), stats.as_mut_ptr())) .with_nix_path(|path| libc::statfs(path.as_ptr(), stats.as_mut_ptr()))
.is_ok() .is_ok()
{ {
@ -331,15 +347,15 @@ fn check_proc_mount(m: &Mount) -> Result<()> {
return Err(anyhow!(format!( return Err(anyhow!(format!(
"{} cannot be mounted to {} because it is not of type proc", "{} cannot be mounted to {} because it is not of type proc",
m.source, m.destination &mount_source, &mount_dest
))); )));
} }
} }
if m.destination.starts_with(PROC_PATH) { if mount_dest.starts_with(PROC_PATH) {
return Err(anyhow!(format!( return Err(anyhow!(format!(
"{} cannot be mounted because it is inside /proc", "{} cannot be mounted because it is inside /proc",
m.destination &mount_dest
))); )));
} }
@ -351,12 +367,11 @@ fn mount_cgroups_v2(cfd_log: RawFd, m: &Mount, rootfs: &str, flags: MsFlags) ->
unistd::chdir(rootfs)?; unistd::chdir(rootfs)?;
// https://github.com/opencontainers/runc/blob/09ddc63afdde16d5fb859a1d3ab010bd45f08497/libcontainer/rootfs_linux.go#L287 // https://github.com/opencontainers/runc/blob/09ddc63afdde16d5fb859a1d3ab010bd45f08497/libcontainer/rootfs_linux.go#L287
let bm = Mount {
source: "cgroup".to_string(), let mut bm = oci::Mount::default();
r#type: "cgroup2".to_string(), bm.set_source(Some(PathBuf::from("cgroup")));
destination: m.destination.clone(), bm.set_typ(Some("cgroup2".to_string()));
options: Vec::new(), bm.set_destination(m.destination().clone());
};
let mount_flags: MsFlags = flags; let mount_flags: MsFlags = flags;
@ -365,7 +380,11 @@ fn mount_cgroups_v2(cfd_log: RawFd, m: &Mount, rootfs: &str, flags: MsFlags) ->
unistd::chdir(&olddir)?; unistd::chdir(&olddir)?;
if flags.contains(MsFlags::MS_RDONLY) { if flags.contains(MsFlags::MS_RDONLY) {
let dest = format!("{}{}", rootfs, m.destination.as_str()); let dest = format!(
"{}{}",
rootfs,
m.destination().display().to_string().as_str()
);
mount( mount(
Some(dest.as_str()), Some(dest.as_str()),
dest.as_str(), dest.as_str(),
@ -390,13 +409,13 @@ fn mount_cgroups(
if cgroups::hierarchies::is_cgroup2_unified_mode() { if cgroups::hierarchies::is_cgroup2_unified_mode() {
return mount_cgroups_v2(cfd_log, m, rootfs, flags); return mount_cgroups_v2(cfd_log, m, rootfs, flags);
} }
let mount_dest = m.destination().display().to_string();
// mount tmpfs // mount tmpfs
let ctm = Mount { let mut ctm = oci::Mount::default();
source: "tmpfs".to_string(), ctm.set_source(Some(PathBuf::from("tmpfs")));
r#type: "tmpfs".to_string(), ctm.set_typ(Some("tmpfs".to_string()));
destination: m.destination.clone(), ctm.set_destination(m.destination().clone());
options: Vec::new(),
};
let cflags = MsFlags::MS_NOEXEC | MsFlags::MS_NOSUID | MsFlags::MS_NODEV; let cflags = MsFlags::MS_NOEXEC | MsFlags::MS_NOSUID | MsFlags::MS_NODEV;
mount_from(cfd_log, &ctm, rootfs, cflags, "", "")?; mount_from(cfd_log, &ctm, rootfs, cflags, "", "")?;
@ -421,12 +440,12 @@ fn mount_cgroups(
&mount[..] &mount[..]
}; };
let destination = format!("{}/{}", m.destination.as_str(), base); let destination = format!("{}/{}", &mount_dest, base);
if srcs.contains(source) { if srcs.contains(source) {
// already mounted, xxx,yyy style cgroup // already mounted, xxx,yyy style cgroup
if key != base { if key != base {
let src = format!("{}/{}", m.destination.as_str(), key); let src = format!("{}/{}", &mount_dest, key);
unix::fs::symlink(destination.as_str(), &src[1..])?; unix::fs::symlink(destination.as_str(), &src[1..])?;
} }
@ -437,12 +456,10 @@ fn mount_cgroups(
log_child!(cfd_log, "mount destination: {}", destination.as_str()); log_child!(cfd_log, "mount destination: {}", destination.as_str());
let bm = Mount { let mut bm = oci::Mount::default();
source: source.to_string(), bm.set_source(Some(PathBuf::from(source)));
r#type: "bind".to_string(), bm.set_typ(Some("bind".to_string()));
destination: destination.clone(), bm.set_destination(PathBuf::from(destination.clone()));
options: Vec::new(),
};
let mut mount_flags: MsFlags = flags | MsFlags::MS_REC | MsFlags::MS_BIND; let mut mount_flags: MsFlags = flags | MsFlags::MS_REC | MsFlags::MS_BIND;
if key.contains("systemd") { if key.contains("systemd") {
@ -451,7 +468,7 @@ fn mount_cgroups(
mount_from(cfd_log, &bm, rootfs, mount_flags, "", "")?; mount_from(cfd_log, &bm, rootfs, mount_flags, "", "")?;
if key != base { if key != base {
let src = format!("{}/{}", m.destination.as_str(), key); let src = format!("{}/{}", &mount_dest, key);
unix::fs::symlink(destination.as_str(), &src[1..]).map_err(|e| { unix::fs::symlink(destination.as_str(), &src[1..]).map_err(|e| {
log_child!( log_child!(
cfd_log, cfd_log,
@ -469,7 +486,7 @@ fn mount_cgroups(
unistd::chdir(&olddir)?; unistd::chdir(&olddir)?;
if flags.contains(MsFlags::MS_RDONLY) { if flags.contains(MsFlags::MS_RDONLY) {
let dest = format!("{}{}", rootfs, m.destination.as_str()); let dest = format!("{}{}", rootfs, &mount_dest);
mount( mount(
Some(dest.as_str()), Some(dest.as_str()),
dest.as_str(), dest.as_str(),
@ -710,7 +727,9 @@ fn parse_mount(m: &Mount) -> (MsFlags, MsFlags, String) {
let mut pgflags = MsFlags::empty(); let mut pgflags = MsFlags::empty();
let mut data = Vec::new(); let mut data = Vec::new();
for o in &m.options { let default_options = Vec::new();
let mount_options = m.options().as_ref().unwrap_or(&default_options);
for o in mount_options {
if let Some(v) = OPTIONS.get(o.as_str()) { if let Some(v) = OPTIONS.get(o.as_str()) {
let (clear, fl) = *v; let (clear, fl) = *v;
if clear { if clear {
@ -783,10 +802,13 @@ fn mount_from(
label: &str, label: &str,
) -> Result<()> { ) -> Result<()> {
let mut d = String::from(data); let mut d = String::from(data);
let dest = secure_join(rootfs, &m.destination); let mount_dest = m.destination().display().to_string();
let mount_typ = m.typ().as_ref().unwrap();
let dest = secure_join(rootfs, &mount_dest);
let src = if m.r#type.as_str() == "bind" { let mount_source = m.source().as_ref().unwrap().display().to_string();
let src = fs::canonicalize(m.source.as_str())?; let src = if mount_typ == "bind" {
let src = fs::canonicalize(&mount_source)?;
let dir = if src.is_dir() { let dir = if src.is_dir() {
Path::new(&dest) Path::new(&dest)
} else { } else {
@ -822,11 +844,10 @@ fn mount_from(
src.to_str().unwrap().to_string() src.to_str().unwrap().to_string()
} else { } else {
let _ = fs::create_dir_all(&dest); let _ = fs::create_dir_all(&dest);
if m.r#type.as_str() == "cgroup2" { if mount_typ == "cgroup2" {
"cgroup2".to_string() "cgroup2".to_string()
} else { } else {
let tmp = PathBuf::from(&m.source); mount_source.to_string()
tmp.to_str().unwrap().to_string()
} }
}; };
@ -839,11 +860,16 @@ fn mount_from(
let mut use_xattr = false; let mut use_xattr = false;
if !label.is_empty() { if !label.is_empty() {
if selinux::is_enabled()? { if selinux::is_enabled()? {
let device = Path::new(&m.source) let device = m
.source()
.as_ref()
.unwrap()
.file_name() .file_name()
.ok_or_else(|| anyhow!("invalid device source path: {}", &m.source))? .ok_or_else(|| anyhow!("invalid device source path: {}", &mount_source))?
.to_str() .to_str()
.ok_or_else(|| anyhow!("failed to convert device source path: {}", &m.source))?; .ok_or_else(|| {
anyhow!("failed to convert device source path: {}", &mount_source)
})?;
match device { match device {
// SELinux does not support labeling of /proc or /sys // SELinux does not support labeling of /proc or /sys
@ -869,7 +895,7 @@ fn mount_from(
mount( mount(
Some(src.as_str()), Some(src.as_str()),
dest.as_str(), dest.as_str(),
Some(m.r#type.as_str()), Some(mount_typ.as_str()),
flags, flags,
Some(d.as_str()), Some(d.as_str()),
) )
@ -924,9 +950,7 @@ fn default_symlinks() -> Result<()> {
Ok(()) Ok(())
} }
fn dev_rel_path(path: &str) -> Option<&Path> { fn dev_rel_path(path: &PathBuf) -> Option<&Path> {
let path = Path::new(path);
if !path.starts_with("/dev") if !path.starts_with("/dev")
|| path == Path::new("/dev") || path == Path::new("/dev")
|| path.components().any(|c| c == Component::ParentDir) || path.components().any(|c| c == Component::ParentDir)
@ -940,12 +964,17 @@ fn create_devices(devices: &[LinuxDevice], bind: bool) -> Result<()> {
let op: fn(&LinuxDevice, &Path) -> Result<()> = if bind { bind_dev } else { mknod_dev }; let op: fn(&LinuxDevice, &Path) -> Result<()> = if bind { bind_dev } else { mknod_dev };
let old = stat::umask(Mode::from_bits_truncate(0o000)); let old = stat::umask(Mode::from_bits_truncate(0o000));
for dev in DEFAULT_DEVICES.iter() { for dev in DEFAULT_DEVICES.iter() {
let path = Path::new(&dev.path[1..]); let dev_path = dev.path().display().to_string();
let path = Path::new(&dev_path[1..]);
op(dev, path).context(format!("Creating container device {:?}", dev))?; op(dev, path).context(format!("Creating container device {:?}", dev))?;
} }
for dev in devices { for dev in devices {
let path = dev_rel_path(&dev.path).ok_or_else(|| { let dev_path = &dev.path();
let msg = format!("{} is not a valid device path", dev.path); let path = dev_rel_path(dev_path).ok_or_else(|| {
let msg = format!(
"{} is not a valid device path",
&dev.path().display().to_string().as_str()
);
anyhow!(msg) anyhow!(msg)
})?; })?;
if let Some(dir) = path.parent() { if let Some(dir) = path.parent() {
@ -974,7 +1003,7 @@ lazy_static! {
} }
fn mknod_dev(dev: &LinuxDevice, relpath: &Path) -> Result<()> { fn mknod_dev(dev: &LinuxDevice, relpath: &Path) -> Result<()> {
let f = match LINUXDEVICETYPE.get(dev.r#type.as_str()) { let f = match LINUXDEVICETYPE.get(dev.typ().as_str()) {
Some(v) => v, Some(v) => v,
None => return Err(anyhow!("invalid spec".to_string())), None => return Err(anyhow!("invalid spec".to_string())),
}; };
@ -982,14 +1011,14 @@ fn mknod_dev(dev: &LinuxDevice, relpath: &Path) -> Result<()> {
stat::mknod( stat::mknod(
relpath, relpath,
*f, *f,
Mode::from_bits_truncate(dev.file_mode.unwrap_or(0)), Mode::from_bits_truncate(dev.file_mode().unwrap_or(0)),
nix::sys::stat::makedev(dev.major as u64, dev.minor as u64), nix::sys::stat::makedev(dev.major() as u64, dev.minor() as u64),
)?; )?;
unistd::chown( unistd::chown(
relpath, relpath,
Some(Uid::from_raw(dev.uid.unwrap_or(0) as uid_t)), Some(Uid::from_raw(dev.uid().unwrap_or(0) as uid_t)),
Some(Gid::from_raw(dev.gid.unwrap_or(0) as uid_t)), Some(Gid::from_raw(dev.gid().unwrap_or(0) as uid_t)),
)?; )?;
Ok(()) Ok(())
@ -1005,7 +1034,7 @@ fn bind_dev(dev: &LinuxDevice, relpath: &Path) -> Result<()> {
unistd::close(fd)?; unistd::close(fd)?;
mount( mount(
Some(&*dev.path), Some(dev.path()),
relpath, relpath,
None::<&str>, None::<&str>,
MsFlags::MS_BIND, MsFlags::MS_BIND,
@ -1019,30 +1048,34 @@ pub fn finish_rootfs(cfd_log: RawFd, spec: &Spec, process: &Process) -> Result<(
log_child!(cfd_log, "old cwd: {}", olddir.to_str().unwrap()); log_child!(cfd_log, "old cwd: {}", olddir.to_str().unwrap());
unistd::chdir("/")?; unistd::chdir("/")?;
if !process.cwd.is_empty() { let process_cwd = process.cwd().display().to_string();
if process_cwd.is_empty() {
// Although the process.cwd string can be unclean/malicious (../../dev, etc), // Although the process.cwd string can be unclean/malicious (../../dev, etc),
// we are running on our own mount namespace and we just chrooted into the // we are running on our own mount namespace and we just chrooted into the
// container's root. It's safe to create CWD from there. // container's root. It's safe to create CWD from there.
log_child!(cfd_log, "Creating CWD {}", process.cwd.as_str()); log_child!(cfd_log, "Creating CWD {}", process_cwd.as_str());
// Unconditionally try to create CWD, create_dir_all will not fail if // Unconditionally try to create CWD, create_dir_all will not fail if
// it already exists. // it already exists.
fs::create_dir_all(process.cwd.as_str())?; fs::create_dir_all(process_cwd.as_str())?;
} }
if spec.linux.is_some() { if spec.linux().is_some() {
let linux = spec.linux.as_ref().unwrap(); let linux = spec.linux().as_ref().unwrap();
let linux_masked_paths = linux.masked_paths().clone().unwrap_or_default();
for path in linux.masked_paths.iter() { for path in linux_masked_paths.iter() {
mask_path(path)?; mask_path(path)?;
} }
let ro_paths = vec![];
for path in linux.readonly_paths.iter() { let linux_readonly_paths = linux.readonly_paths().as_ref().unwrap_or(&ro_paths);
for path in linux_readonly_paths.iter() {
readonly_path(path)?; readonly_path(path)?;
} }
} }
let default_mnts = vec![];
for m in spec.mounts.iter() { let spec_mounts = spec.mounts().as_ref().unwrap_or(&default_mnts);
if m.destination == "/dev" { for m in spec_mounts.iter() {
let mount_dest = m.destination().display().to_string();
if &mount_dest == "/dev" {
let (flags, _, _) = parse_mount(m); let (flags, _, _) = parse_mount(m);
if flags.contains(MsFlags::MS_RDONLY) { if flags.contains(MsFlags::MS_RDONLY) {
mount( mount(
@ -1056,7 +1089,7 @@ pub fn finish_rootfs(cfd_log: RawFd, spec: &Spec, process: &Process) -> Result<(
} }
} }
if spec.root.as_ref().unwrap().readonly { if spec.root().as_ref().unwrap().readonly().unwrap_or_default() {
let flags = MsFlags::MS_BIND | MsFlags::MS_RDONLY | MsFlags::MS_NODEV | MsFlags::MS_REMOUNT; let flags = MsFlags::MS_BIND | MsFlags::MS_RDONLY | MsFlags::MS_NODEV | MsFlags::MS_REMOUNT;
mount(Some("/"), "/", None::<&str>, flags, None::<&str>)?; mount(Some("/"), "/", None::<&str>, flags, None::<&str>)?;
@ -1125,7 +1158,6 @@ fn check_paths(path: &str) -> Result<()> {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use crate::assert_result;
use std::fs::create_dir; use std::fs::create_dir;
use std::fs::create_dir_all; use std::fs::create_dir_all;
use std::fs::remove_dir_all; use std::fs::remove_dir_all;
@ -1134,6 +1166,7 @@ mod tests {
use std::os::unix::fs; use std::os::unix::fs;
use std::os::unix::io::AsRawFd; use std::os::unix::io::AsRawFd;
use tempfile::tempdir; use tempfile::tempdir;
use test_utils::assert_result;
use test_utils::skip_if_not_root; use test_utils::skip_if_not_root;
#[test] #[test]
@ -1153,7 +1186,7 @@ mod tests {
); );
// there is no spec.Root, should fail // there is no spec.Root, should fail
spec.linux = Some(oci::Linux::default()); spec.set_linux(Some(oci::Linux::default()));
let ret = init_rootfs(stdout_fd, &spec, &cpath, &mounts, true); let ret = init_rootfs(stdout_fd, &spec, &cpath, &mounts, true);
assert!( assert!(
ret.is_err(), ret.is_err(),
@ -1165,10 +1198,10 @@ mod tests {
let ret = create_dir(rootfs.path().join("dev")); let ret = create_dir(rootfs.path().join("dev"));
assert!(ret.is_ok(), "Got: {:?}", ret); assert!(ret.is_ok(), "Got: {:?}", ret);
spec.root = Some(oci::Root { let mut oci_root = oci::Root::default();
path: rootfs.path().to_str().unwrap().to_string(), oci_root.set_path(rootfs.path().to_path_buf());
readonly: false, oci_root.set_readonly(Some(false));
}); spec.set_root(Some(oci_root));
// there is no spec.mounts, but should pass // there is no spec.mounts, but should pass
let ret = init_rootfs(stdout_fd, &spec, &cpath, &mounts, true); let ret = init_rootfs(stdout_fd, &spec, &cpath, &mounts, true);
@ -1176,13 +1209,16 @@ mod tests {
let _ = remove_dir_all(rootfs.path().join("dev")); let _ = remove_dir_all(rootfs.path().join("dev"));
let _ = create_dir(rootfs.path().join("dev")); let _ = create_dir(rootfs.path().join("dev"));
if spec.mounts().is_none() {
spec.set_mounts(Some(Vec::new()));
}
// Adding bad mount point to spec.mounts // Adding bad mount point to spec.mounts
spec.mounts.push(oci::Mount { let mut oci_mount = oci::Mount::default();
destination: "error".into(), oci_mount.set_destination("error".into());
r#type: "bind".into(), oci_mount.set_typ(Some("bind".to_string()));
source: "error".into(), oci_mount.set_source(Some("error".into()));
options: vec!["shared".into(), "rw".into(), "dev".into()], oci_mount.set_options(Some(vec!["shared".into(), "rw".into(), "dev".into()]));
}); spec.mounts_mut().as_mut().unwrap().push(oci_mount);
// destination doesn't start with /, should fail // destination doesn't start with /, should fail
let ret = init_rootfs(stdout_fd, &spec, &cpath, &mounts, true); let ret = init_rootfs(stdout_fd, &spec, &cpath, &mounts, true);
@ -1191,31 +1227,31 @@ mod tests {
"Should fail: destination doesn't start with '/'. Got: {:?}", "Should fail: destination doesn't start with '/'. Got: {:?}",
ret ret
); );
spec.mounts.pop(); spec.mounts_mut().as_mut().unwrap().pop();
let _ = remove_dir_all(rootfs.path().join("dev")); let _ = remove_dir_all(rootfs.path().join("dev"));
let _ = create_dir(rootfs.path().join("dev")); let _ = create_dir(rootfs.path().join("dev"));
// mounting a cgroup // mounting a cgroup
spec.mounts.push(oci::Mount { let mut oci_mount = oci::Mount::default();
destination: "/cgroup".into(), oci_mount.set_destination("/cgroup".into());
r#type: "cgroup".into(), oci_mount.set_typ(Some("cgroup".into()));
source: "/cgroup".into(), oci_mount.set_source(Some("/cgroup".into()));
options: vec!["shared".into()], oci_mount.set_options(Some(vec!["shared".into()]));
}); spec.mounts_mut().as_mut().unwrap().push(oci_mount);
let ret = init_rootfs(stdout_fd, &spec, &cpath, &mounts, true); let ret = init_rootfs(stdout_fd, &spec, &cpath, &mounts, true);
assert!(ret.is_ok(), "Should pass. Got: {:?}", ret); assert!(ret.is_ok(), "Should pass. Got: {:?}", ret);
spec.mounts.pop(); spec.mounts_mut().as_mut().unwrap().pop();
let _ = remove_dir_all(rootfs.path().join("dev")); let _ = remove_dir_all(rootfs.path().join("dev"));
let _ = create_dir(rootfs.path().join("dev")); let _ = create_dir(rootfs.path().join("dev"));
// mounting /dev // mounting /dev
spec.mounts.push(oci::Mount { let mut oci_mount = oci::Mount::default();
destination: "/dev".into(), oci_mount.set_destination("/dev".into());
r#type: "bind".into(), oci_mount.set_typ(Some("bind".into()));
source: "/dev".into(), oci_mount.set_source(Some("/dev".into()));
options: vec!["shared".into()], oci_mount.set_options(Some(vec!["shared".into()]));
}); spec.mounts_mut().as_mut().unwrap().push(oci_mount);
let ret = init_rootfs(stdout_fd, &spec, &cpath, &mounts, true); let ret = init_rootfs(stdout_fd, &spec, &cpath, &mounts, true);
assert!(ret.is_ok(), "Should pass. Got: {:?}", ret); assert!(ret.is_ok(), "Should pass. Got: {:?}", ret);
@ -1225,12 +1261,13 @@ mod tests {
#[serial(chdir)] #[serial(chdir)]
fn test_mount_cgroups() { fn test_mount_cgroups() {
let stdout_fd = std::io::stdout().as_raw_fd(); let stdout_fd = std::io::stdout().as_raw_fd();
let mount = oci::Mount {
destination: "/cgroups".to_string(), let mut mount = oci::Mount::default();
r#type: "cgroup".to_string(), mount.set_destination("/cgroup".into());
source: "/cgroups".to_string(), mount.set_typ(Some("cgroup".into()));
options: vec!["shared".to_string()], mount.set_source(Some("/cgroup".into()));
}; mount.set_options(Some(vec!["shared".into()]));
let tempdir = tempdir().unwrap(); let tempdir = tempdir().unwrap();
let rootfs = tempdir.path().to_str().unwrap().to_string(); let rootfs = tempdir.path().to_str().unwrap().to_string();
let flags = MsFlags::MS_RDONLY; let flags = MsFlags::MS_RDONLY;
@ -1310,19 +1347,27 @@ mod tests {
let stdout_fd = std::io::stdout().as_raw_fd(); let stdout_fd = std::io::stdout().as_raw_fd();
let mut spec = oci::Spec::default(); let mut spec = oci::Spec::default();
spec.linux = Some(oci::Linux::default()); spec.set_linux(Some(oci::Linux::default()));
spec.linux.as_mut().unwrap().masked_paths = vec!["/tmp".to_string()]; spec.linux_mut()
spec.linux.as_mut().unwrap().readonly_paths = vec!["/tmp".to_string()]; .as_mut()
spec.root = Some(oci::Root { .unwrap()
path: "/tmp".to_string(), .set_masked_paths(Some(vec!["/tmp".to_string()]));
readonly: true, spec.linux_mut()
}); .as_mut()
spec.mounts = vec![oci::Mount { .unwrap()
destination: "/dev".to_string(), .set_readonly_paths(Some(vec!["/tmp".to_string()]));
r#type: "bind".to_string(),
source: "/dev".to_string(), let mut oci_root = oci::Root::default();
options: vec!["ro".to_string(), "shared".to_string()], oci_root.set_path(PathBuf::from("/tmp"));
}]; oci_root.set_readonly(Some(true));
spec.set_root(Some(oci_root));
let mut oci_mount = oci::Mount::default();
oci_mount.set_destination("/dev".into());
oci_mount.set_typ(Some("bind".into()));
oci_mount.set_source(Some("/dev".into()));
oci_mount.set_options(Some(vec!["shared".into()]));
spec.set_mounts(Some(vec![oci_mount]));
let ret = finish_rootfs(stdout_fd, &spec, &oci::Process::default()); let ret = finish_rootfs(stdout_fd, &spec, &oci::Process::default());
assert!(ret.is_ok(), "Should pass. Got: {:?}", ret); assert!(ret.is_ok(), "Should pass. Got: {:?}", ret);
@ -1346,15 +1391,16 @@ mod tests {
skip_if_not_root!(); skip_if_not_root!();
let path = "/dev/fifo-test"; let path = "/dev/fifo-test";
let dev = oci::LinuxDevice { let dev = oci::LinuxDeviceBuilder::default()
path: path.to_string(), .path(PathBuf::from(path))
r#type: "c".to_string(), .typ(oci::LinuxDeviceType::C)
major: 0, .major(0)
minor: 0, .minor(0)
file_mode: Some(0660), .file_mode(0660 as u32)
uid: Some(unistd::getuid().as_raw()), .uid(unistd::getuid().as_raw())
gid: Some(unistd::getgid().as_raw()), .gid(unistd::getgid().as_raw())
}; .build()
.unwrap();
let ret = mknod_dev(&dev, Path::new(path)); let ret = mknod_dev(&dev, Path::new(path));
assert!(ret.is_ok(), "Should pass. Got: {:?}", ret); assert!(ret.is_ok(), "Should pass. Got: {:?}", ret);
@ -1370,6 +1416,7 @@ mod tests {
#[test] #[test]
fn test_mount_from() { fn test_mount_from() {
#[derive(Debug)] #[derive(Debug)]
#[allow(dead_code)]
struct TestData<'a> { struct TestData<'a> {
source: &'a str, source: &'a str,
destination: &'a str, destination: &'a str,
@ -1444,12 +1491,11 @@ mod tests {
std::fs::write(&source_path, []).unwrap(); std::fs::write(&source_path, []).unwrap();
} }
let mount = Mount { let mut mount = oci::Mount::default();
source: source_path, mount.set_destination(d.destination.into());
destination: d.destination.to_string(), mount.set_typ(Some("bind".into()));
r#type: d.r#type.to_string(), mount.set_source(Some(source_path.into()));
options: vec![], mount.set_options(Some(vec![]));
};
let result = mount_from( let result = mount_from(
wfd, wfd,
@ -1524,30 +1570,27 @@ mod tests {
#[test] #[test]
fn test_check_proc_mount() { fn test_check_proc_mount() {
let mount = oci::Mount { let mut mount = oci::Mount::default();
destination: "/proc".to_string(), mount.set_destination("/proc".into());
r#type: "bind".to_string(), mount.set_typ(Some("bind".into()));
source: "/test".to_string(), mount.set_source(Some("/test".into()));
options: vec!["shared".to_string()], mount.set_options(Some(vec!["shared".to_string()]));
};
assert!(check_proc_mount(&mount).is_err()); assert!(check_proc_mount(&mount).is_err());
let mount = oci::Mount { let mut mount = oci::Mount::default();
destination: "/proc/cpuinfo".to_string(), mount.set_destination("/proc/cpuinfo".into());
r#type: "bind".to_string(), mount.set_typ(Some("bind".into()));
source: "/test".to_string(), mount.set_source(Some("/test".into()));
options: vec!["shared".to_string()], mount.set_options(Some(vec!["shared".to_string()]));
};
assert!(check_proc_mount(&mount).is_ok()); assert!(check_proc_mount(&mount).is_ok());
let mount = oci::Mount { let mut mount = oci::Mount::default();
destination: "/proc/test".to_string(), mount.set_destination("/proc/test".into());
r#type: "bind".to_string(), mount.set_typ(Some("bind".into()));
source: "/test".to_string(), mount.set_source(Some("/test".into()));
options: vec!["shared".to_string()], mount.set_options(Some(vec!["shared".to_string()]));
};
assert!(check_proc_mount(&mount).is_err()); assert!(check_proc_mount(&mount).is_err());
} }
@ -1755,22 +1798,37 @@ mod tests {
#[test] #[test]
fn test_dev_rel_path() { fn test_dev_rel_path() {
// Valid device paths // Valid device paths
assert_eq!(dev_rel_path("/dev/sda").unwrap(), Path::new("dev/sda"));
assert_eq!(dev_rel_path("//dev/sda").unwrap(), Path::new("dev/sda"));
assert_eq!( assert_eq!(
dev_rel_path("/dev/vfio/99").unwrap(), dev_rel_path(&PathBuf::from("/dev/sda")).unwrap(),
Path::new("dev/sda")
);
assert_eq!(
dev_rel_path(&PathBuf::from("//dev/sda")).unwrap(),
Path::new("dev/sda")
);
assert_eq!(
dev_rel_path(&PathBuf::from("/dev/vfio/99")).unwrap(),
Path::new("dev/vfio/99") Path::new("dev/vfio/99")
); );
assert_eq!(dev_rel_path("/dev/...").unwrap(), Path::new("dev/...")); assert_eq!(
assert_eq!(dev_rel_path("/dev/a..b").unwrap(), Path::new("dev/a..b")); dev_rel_path(&PathBuf::from("/dev/...")).unwrap(),
assert_eq!(dev_rel_path("/dev//foo").unwrap(), Path::new("dev/foo")); Path::new("dev/...")
);
assert_eq!(
dev_rel_path(&PathBuf::from("/dev/a..b")).unwrap(),
Path::new("dev/a..b")
);
assert_eq!(
dev_rel_path(&PathBuf::from("/dev//foo")).unwrap(),
Path::new("dev/foo")
);
// Bad device paths // Bad device paths
assert!(dev_rel_path("/devfoo").is_none()); assert!(dev_rel_path(&PathBuf::from("/devfoo")).is_none());
assert!(dev_rel_path("/etc/passwd").is_none()); assert!(dev_rel_path(&PathBuf::from("/etc/passwd")).is_none());
assert!(dev_rel_path("/dev/../etc/passwd").is_none()); assert!(dev_rel_path(&PathBuf::from("/dev/../etc/passwd")).is_none());
assert!(dev_rel_path("dev/foo").is_none()); assert!(dev_rel_path(&PathBuf::from("dev/foo")).is_none());
assert!(dev_rel_path("").is_none()); assert!(dev_rel_path(&PathBuf::from("")).is_none());
assert!(dev_rel_path("/dev").is_none()); assert!(dev_rel_path(&PathBuf::from("/dev")).is_none());
} }
} }

View File

@ -16,6 +16,7 @@ use nix::unistd::{self, Pid};
use nix::Result; use nix::Result;
use oci::Process as OCIProcess; use oci::Process as OCIProcess;
use oci_spec::runtime as oci;
use slog::Logger; use slog::Logger;
use crate::pipestream::PipeStream; use crate::pipestream::PipeStream;
@ -147,7 +148,7 @@ impl Process {
exit_tx: Some(exit_tx), exit_tx: Some(exit_tx),
exit_rx: Some(exit_rx), exit_rx: Some(exit_rx),
extra_files: Vec::new(), extra_files: Vec::new(),
tty: ocip.terminal, tty: ocip.terminal().unwrap_or_default(),
term_master: None, term_master: None,
parent_stdin: None, parent_stdin: None,
parent_stdout: None, parent_stdout: None,

View File

@ -5,9 +5,11 @@
use anyhow::{anyhow, Result}; use anyhow::{anyhow, Result};
use libseccomp::*; use libseccomp::*;
use oci::{LinuxSeccomp, LinuxSeccompArg};
use std::str::FromStr; use std::str::FromStr;
use oci::{LinuxSeccomp, LinuxSeccompArg};
use oci_spec::runtime as oci;
fn get_filter_attr_from_flag(flag: &str) -> Result<ScmpFilterAttr> { fn get_filter_attr_from_flag(flag: &str) -> Result<ScmpFilterAttr> {
match flag { match flag {
"SECCOMP_FILTER_FLAG_TSYNC" => Ok(ScmpFilterAttr::CtlTsync), "SECCOMP_FILTER_FLAG_TSYNC" => Ok(ScmpFilterAttr::CtlTsync),
@ -22,19 +24,15 @@ fn get_rule_conditions(args: &[LinuxSeccompArg]) -> Result<Vec<ScmpArgCompare>>
let mut conditions: Vec<ScmpArgCompare> = Vec::new(); let mut conditions: Vec<ScmpArgCompare> = Vec::new();
for arg in args { for arg in args {
if arg.op.is_empty() { let mut op = ScmpCompareOp::from_str(&arg.op().to_string())?;
return Err(anyhow!("seccomp opreator is required")); let mut value = arg.value();
}
let mut op = ScmpCompareOp::from_str(&arg.op)?;
let mut value = arg.value;
// For SCMP_CMP_MASKED_EQ, arg.value is the mask and arg.value_two is the value // For SCMP_CMP_MASKED_EQ, arg.value is the mask and arg.value_two is the value
if op == ScmpCompareOp::MaskedEqual(u64::default()) { if op == ScmpCompareOp::MaskedEqual(u64::default()) {
op = ScmpCompareOp::MaskedEqual(arg.value); op = ScmpCompareOp::MaskedEqual(arg.value());
value = arg.value_two; value = arg.value_two().unwrap_or(0);
} }
let cond = ScmpArgCompare::new(arg.index, op, value); let cond = ScmpArgCompare::new(arg.index() as u32, op, value);
conditions.push(cond); conditions.push(cond);
} }
@ -44,9 +42,9 @@ fn get_rule_conditions(args: &[LinuxSeccompArg]) -> Result<Vec<ScmpArgCompare>>
pub fn get_unknown_syscalls(scmp: &LinuxSeccomp) -> Option<Vec<String>> { pub fn get_unknown_syscalls(scmp: &LinuxSeccomp) -> Option<Vec<String>> {
let mut unknown_syscalls: Vec<String> = Vec::new(); let mut unknown_syscalls: Vec<String> = Vec::new();
let scmp_syscalls = scmp.syscalls().clone().unwrap_or_default();
for syscall in &scmp.syscalls { for syscall in scmp_syscalls.iter() {
for name in &syscall.names { for name in syscall.names().iter() {
if ScmpSyscall::from_name(name).is_err() { if ScmpSyscall::from_name(name).is_err() {
unknown_syscalls.push(name.to_string()); unknown_syscalls.push(name.to_string());
} }
@ -63,14 +61,15 @@ pub fn get_unknown_syscalls(scmp: &LinuxSeccomp) -> Option<Vec<String>> {
// init_seccomp creates a seccomp filter and loads it for the current process // init_seccomp creates a seccomp filter and loads it for the current process
// including all the child processes. // including all the child processes.
pub fn init_seccomp(scmp: &LinuxSeccomp) -> Result<()> { pub fn init_seccomp(scmp: &LinuxSeccomp) -> Result<()> {
let def_action = ScmpAction::from_str(scmp.default_action.as_str(), Some(libc::EPERM))?; let def_action = ScmpAction::from_str(&scmp.default_action().to_string(), Some(libc::EPERM))?;
// Create a new filter context // Create a new filter context
let mut filter = ScmpFilterContext::new_filter(def_action)?; let mut filter = ScmpFilterContext::new_filter(def_action)?;
// Add extra architectures // Add extra architectures
for arch in &scmp.architectures { let architectures = scmp.architectures().clone().unwrap_or_default();
let scmp_arch = ScmpArch::from_str(arch)?; for arch in architectures {
let scmp_arch = ScmpArch::from_str(&arch.to_string())?;
filter.add_arch(scmp_arch)?; filter.add_arch(scmp_arch)?;
} }
@ -78,17 +77,23 @@ pub fn init_seccomp(scmp: &LinuxSeccomp) -> Result<()> {
filter.set_ctl_nnp(false)?; filter.set_ctl_nnp(false)?;
// Add a rule for each system call // Add a rule for each system call
for syscall in &scmp.syscalls { let scmp_syscalls = scmp.syscalls().clone().unwrap_or_default();
if syscall.names.is_empty() { for syscall in scmp_syscalls {
if syscall.names().is_empty() {
return Err(anyhow!("syscall name is required")); return Err(anyhow!("syscall name is required"));
} }
let action = ScmpAction::from_str(&syscall.action, Some(syscall.errno_ret as i32))?; let action = ScmpAction::from_str(
&syscall.action().to_string(),
syscall
.errno_ret()
.map_or(Some(libc::EPERM), |x| Some(x as i32)),
)?;
if action == def_action { if action == def_action {
continue; continue;
} }
for name in &syscall.names { for name in syscall.names() {
let syscall_num = match ScmpSyscall::from_name(name) { let syscall_num = match ScmpSyscall::from_name(name) {
Ok(num) => num, Ok(num) => num,
Err(_) => { Err(_) => {
@ -98,18 +103,20 @@ pub fn init_seccomp(scmp: &LinuxSeccomp) -> Result<()> {
} }
}; };
if syscall.args.is_empty() { if syscall.args().is_none() {
filter.add_rule(action, syscall_num)?; filter.add_rule(action, syscall_num)?;
} else { } else {
let conditions = get_rule_conditions(&syscall.args)?; let syscall_args = syscall.args().clone().unwrap_or_default();
let conditions = get_rule_conditions(&syscall_args)?;
filter.add_rule_conditional(action, syscall_num, &conditions)?; filter.add_rule_conditional(action, syscall_num, &conditions)?;
} }
} }
} }
// Set filter attributes for each seccomp flag // Set filter attributes for each seccomp flag
for flag in &scmp.flags { let flags = scmp.flags().clone().unwrap_or_default();
let scmp_attr = get_filter_attr_from_flag(flag)?; for flag in flags {
let scmp_attr = get_filter_attr_from_flag(&flag.to_string())?;
filter.set_filter_attr(scmp_attr, 1)?; filter.set_filter_attr(scmp_attr, 1)?;
} }
@ -123,6 +130,7 @@ pub fn init_seccomp(scmp: &LinuxSeccomp) -> Result<()> {
mod tests { mod tests {
use super::*; use super::*;
use libc::{dup3, process_vm_readv, EPERM, O_CLOEXEC}; use libc::{dup3, process_vm_readv, EPERM, O_CLOEXEC};
use oci_spec::runtime as oci;
use std::io::Error; use std::io::Error;
use std::ptr::null; use std::ptr::null;
use test_utils::skip_if_not_root; use test_utils::skip_if_not_root;
@ -233,21 +241,23 @@ mod tests {
if cfg!(target_endian = "little") { if cfg!(target_endian = "little") {
// For little-endian architectures // For little-endian architectures
arch = vec![ arch = vec![
"SCMP_ARCH_X86".to_string(), "SCMP_ARCH_X86".parse::<oci::Arch>().unwrap(),
"SCMP_ARCH_X32".to_string(), "SCMP_ARCH_X32".parse::<oci::Arch>().unwrap(),
"SCMP_ARCH_X86_64".to_string(), "SCMP_ARCH_X86_64".parse::<oci::Arch>().unwrap(),
"SCMP_ARCH_AARCH64".to_string(), "SCMP_ARCH_AARCH64".parse::<oci::Arch>().unwrap(),
"SCMP_ARCH_ARM".to_string(), "SCMP_ARCH_ARM".parse::<oci::Arch>().unwrap(),
"SCMP_ARCH_PPC64LE".to_string(), "SCMP_ARCH_PPC64LE".parse::<oci::Arch>().unwrap(),
]; ];
} else { } else {
// For big-endian architectures // For big-endian architectures
arch = vec!["SCMP_ARCH_S390X".to_string()]; arch = vec!["SCMP_ARCH_S390X".parse::<oci::Arch>().unwrap()];
} }
scmp.architectures.append(&mut arch); let mut archs = scmp.architectures().clone().unwrap();
archs.append(&mut arch);
scmp.set_architectures(Some(archs));
init_seccomp(&scmp).unwrap(); assert!(init_seccomp(&scmp).is_ok());
// Basic syscall with simple rule // Basic syscall with simple rule
syscall_assert!(unsafe { dup3(0, 1, O_CLOEXEC) }, -EPERM); syscall_assert!(unsafe { dup3(0, 1, O_CLOEXEC) }, -EPERM);

View File

@ -3,7 +3,7 @@
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
// //
use oci::Spec; use oci_spec::runtime::Spec;
#[derive(Serialize, Deserialize, Debug, Default, Clone)] #[derive(Serialize, Deserialize, Debug, Default, Clone)]
pub struct CreateOpts { pub struct CreateOpts {

View File

@ -6,19 +6,26 @@
use crate::container::Config; use crate::container::Config;
use anyhow::{anyhow, Context, Result}; use anyhow::{anyhow, Context, Result};
use oci::{Linux, LinuxIdMapping, LinuxNamespace, Spec}; use oci::{Linux, LinuxIdMapping, LinuxNamespace, Spec};
use oci_spec::runtime as oci;
use regex::Regex; use regex::Regex;
use std::collections::HashMap; use std::collections::HashMap;
use std::convert::TryFrom;
use std::path::{Component, PathBuf}; use std::path::{Component, PathBuf};
fn get_linux(oci: &Spec) -> Result<&Linux> { fn get_linux(oci: &Spec) -> Result<&Linux> {
oci.linux oci.linux()
.as_ref() .as_ref()
.ok_or_else(|| anyhow!("Unable to get Linux section from Spec")) .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 {
let nstype = match oci::LinuxNamespaceType::try_from(key) {
Ok(ns_type) => ns_type,
Err(_e) => return false,
};
for ns in nses { for ns in nses {
if ns.r#type.as_str() == key { if ns.typ() == nstype {
return true; return true;
} }
} }
@ -73,12 +80,13 @@ fn rootfs(root: &str) -> Result<()> {
} }
fn hostname(oci: &Spec) -> Result<()> { fn hostname(oci: &Spec) -> Result<()> {
if oci.hostname.is_empty() { if oci.hostname().is_none() {
return Ok(()); return Ok(());
} }
let linux = get_linux(oci)?; let linux = get_linux(oci)?;
if !contain_namespace(&linux.namespaces, "uts") { let default_vec = vec![];
if !contain_namespace(linux.namespaces().as_ref().unwrap_or(&default_vec), "uts") {
return Err(anyhow!("Linux namespace does not contain uts")); return Err(anyhow!("Linux namespace does not contain uts"));
} }
@ -90,26 +98,30 @@ fn security(oci: &Spec) -> Result<()> {
let label_pattern = r".*_u:.*_r:.*_t:s[0-9]|1[0-5].*"; let label_pattern = r".*_u:.*_r:.*_t:s[0-9]|1[0-5].*";
let label_regex = Regex::new(label_pattern)?; let label_regex = Regex::new(label_pattern)?;
if let Some(ref process) = oci.process { let default_vec = vec![];
if !process.selinux_label.is_empty() && !label_regex.is_match(&process.selinux_label) { if let Some(process) = oci.process().as_ref() {
if process.selinux_label().is_some()
&& !label_regex.is_match(process.selinux_label().as_ref().unwrap())
{
return Err(anyhow!( return Err(anyhow!(
"SELinux label for the process is invalid format: {}", "SELinux label for the process is invalid format: {:?}",
&process.selinux_label &process.selinux_label()
)); ));
} }
} }
if !linux.mount_label.is_empty() && !label_regex.is_match(&linux.mount_label) { if linux.mount_label().is_some() && !label_regex.is_match(linux.mount_label().as_ref().unwrap())
{
return Err(anyhow!( return Err(anyhow!(
"SELinux label for the mount is invalid format: {}", "SELinux label for the mount is invalid format: {}",
&linux.mount_label linux.mount_label().as_ref().unwrap()
)); ));
} }
if linux.masked_paths.is_empty() && linux.readonly_paths.is_empty() { if linux.masked_paths().is_none() && linux.readonly_paths().is_none() {
return Ok(()); return Ok(());
} }
if !contain_namespace(&linux.namespaces, "mount") { if !contain_namespace(linux.namespaces().as_ref().unwrap_or(&default_vec), "mnt") {
return Err(anyhow!("Linux namespace does not contain mount")); return Err(anyhow!("Linux namespace does not contain mount"));
} }
@ -118,7 +130,7 @@ fn security(oci: &Spec) -> Result<()> {
fn idmapping(maps: &[LinuxIdMapping]) -> Result<()> { fn idmapping(maps: &[LinuxIdMapping]) -> Result<()> {
for map in maps { for map in maps {
if map.size > 0 { if map.size() > 0 {
return Ok(()); return Ok(());
} }
} }
@ -129,18 +141,22 @@ fn idmapping(maps: &[LinuxIdMapping]) -> Result<()> {
fn usernamespace(oci: &Spec) -> Result<()> { fn usernamespace(oci: &Spec) -> Result<()> {
let linux = get_linux(oci)?; let linux = get_linux(oci)?;
if contain_namespace(&linux.namespaces, "user") { let default_vec = vec![];
if contain_namespace(linux.namespaces().as_ref().unwrap_or(&default_vec), "user") {
let user_ns = PathBuf::from("/proc/self/ns/user"); let user_ns = PathBuf::from("/proc/self/ns/user");
if !user_ns.exists() { if !user_ns.exists() {
return Err(anyhow!("user namespace not supported!")); return Err(anyhow!("user namespace not supported!"));
} }
// check if idmappings is correct, at least I saw idmaps // check if idmappings is correct, at least I saw idmaps
// with zero size was passed to agent // with zero size was passed to agent
idmapping(&linux.uid_mappings).context("idmapping uid")?; let default_vec2 = vec![];
idmapping(&linux.gid_mappings).context("idmapping gid")?; idmapping(linux.uid_mappings().as_ref().unwrap_or(&default_vec2))
.context("idmapping uid")?;
idmapping(linux.gid_mappings().as_ref().unwrap_or(&default_vec2))
.context("idmapping gid")?;
} 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_none() || !linux.gid_mappings().is_none() {
return Err(anyhow!("No user namespace, but uid or gid mapping exists")); return Err(anyhow!("No user namespace, but uid or gid mapping exists"));
} }
} }
@ -151,7 +167,11 @@ fn usernamespace(oci: &Spec) -> Result<()> {
fn cgroupnamespace(oci: &Spec) -> Result<()> { fn cgroupnamespace(oci: &Spec) -> Result<()> {
let linux = get_linux(oci)?; let linux = get_linux(oci)?;
if contain_namespace(&linux.namespaces, "cgroup") { let default_vec = vec![];
if contain_namespace(
linux.namespaces().as_ref().unwrap_or(&default_vec),
"cgroup",
) {
let path = PathBuf::from("/proc/self/ns/cgroup"); let path = PathBuf::from("/proc/self/ns/cgroup");
if !path.exists() { if !path.exists() {
return Err(anyhow!("cgroup unsupported!")); return Err(anyhow!("cgroup unsupported!"));
@ -178,9 +198,13 @@ lazy_static! {
fn sysctl(oci: &Spec) -> Result<()> { fn sysctl(oci: &Spec) -> Result<()> {
let linux = get_linux(oci)?; let linux = get_linux(oci)?;
for (key, _) in linux.sysctl.iter() { let default_hash = HashMap::new();
let sysctl_hash = linux.sysctl().as_ref().unwrap_or(&default_hash);
let default_vec = vec![];
let linux_namespaces = linux.namespaces().as_ref().unwrap_or(&default_vec);
for (key, _) in sysctl_hash.iter() {
if SYSCTLS.contains_key(key.as_str()) || key.starts_with("fs.mqueue.") { if SYSCTLS.contains_key(key.as_str()) || key.starts_with("fs.mqueue.") {
if contain_namespace(&linux.namespaces, "ipc") { if contain_namespace(linux_namespaces, "ipc") {
continue; continue;
} else { } else {
return Err(anyhow!("Linux namespace does not contain ipc")); return Err(anyhow!("Linux namespace does not contain ipc"));
@ -192,7 +216,7 @@ fn sysctl(oci: &Spec) -> Result<()> {
continue; continue;
} }
if contain_namespace(&linux.namespaces, "uts") { if contain_namespace(linux_namespaces, "uts") {
if key == "kernel.domainname" { if key == "kernel.domainname" {
continue; continue;
} }
@ -210,11 +234,12 @@ fn sysctl(oci: &Spec) -> Result<()> {
fn rootless_euid_mapping(oci: &Spec) -> Result<()> { fn rootless_euid_mapping(oci: &Spec) -> Result<()> {
let linux = get_linux(oci)?; let linux = get_linux(oci)?;
if !contain_namespace(&linux.namespaces, "user") { let default_ns = vec![];
if !contain_namespace(linux.namespaces().as_ref().unwrap_or(&default_ns), "user") {
return Err(anyhow!("Linux namespace is missing user")); return Err(anyhow!("Linux namespace is missing user"));
} }
if linux.uid_mappings.is_empty() || linux.gid_mappings.is_empty() { if linux.uid_mappings().is_none() || linux.gid_mappings().is_none() {
return Err(anyhow!( return Err(anyhow!(
"Rootless containers require at least one UID/GID mapping" "Rootless containers require at least one UID/GID mapping"
)); ));
@ -225,7 +250,7 @@ fn rootless_euid_mapping(oci: &Spec) -> Result<()> {
fn has_idmapping(maps: &[LinuxIdMapping], id: u32) -> bool { fn has_idmapping(maps: &[LinuxIdMapping], id: u32) -> bool {
for map in maps { for map in maps {
if id >= map.container_id && id < map.container_id + map.size { if id >= map.container_id() && id < map.container_id() + map.size() {
return true; return true;
} }
} }
@ -235,8 +260,12 @@ fn has_idmapping(maps: &[LinuxIdMapping], id: u32) -> bool {
fn rootless_euid_mount(oci: &Spec) -> Result<()> { fn rootless_euid_mount(oci: &Spec) -> Result<()> {
let linux = get_linux(oci)?; let linux = get_linux(oci)?;
for mnt in oci.mounts.iter() { let default_mounts = vec![];
for opt in mnt.options.iter() { let oci_mounts = oci.mounts().as_ref().unwrap_or(&default_mounts);
for mnt in oci_mounts.iter() {
let default_options = vec![];
let mnt_options = mnt.options().as_ref().unwrap_or(&default_options);
for opt in mnt_options.iter() {
if opt.starts_with("uid=") || opt.starts_with("gid=") { if opt.starts_with("uid=") || opt.starts_with("gid=") {
let fields: Vec<&str> = opt.split('=').collect(); let fields: Vec<&str> = opt.split('=').collect();
@ -249,11 +278,15 @@ fn rootless_euid_mount(oci: &Spec) -> Result<()> {
.parse::<u32>() .parse::<u32>()
.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().as_ref().unwrap_or(&vec![]), id)
{
return Err(anyhow!("uid of {} does not have a valid mapping", id)); 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().as_ref().unwrap_or(&vec![]), id)
{
return Err(anyhow!("gid of {} does not have a valid mapping", id)); return Err(anyhow!("gid of {} does not have a valid mapping", id));
} }
} }
@ -275,16 +308,16 @@ pub fn validate(conf: &Config) -> Result<()> {
.as_ref() .as_ref()
.ok_or_else(|| anyhow!("Invalid config spec"))?; .ok_or_else(|| anyhow!("Invalid config spec"))?;
if oci.linux.is_none() { if oci.linux().is_none() {
return Err(anyhow!("oci Linux is none")); 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().display().to_string(),
None => return Err(anyhow!("oci root is none")), None => return Err(anyhow!("oci root is none")),
}; };
rootfs(root).context("rootfs")?; rootfs(&root).context("rootfs")?;
hostname(oci).context("hostname")?; hostname(oci).context("hostname")?;
security(oci).context("security")?; security(oci).context("security")?;
usernamespace(oci).context("usernamespace")?; usernamespace(oci).context("usernamespace")?;
@ -301,19 +334,22 @@ pub fn validate(conf: &Config) -> Result<()> {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use oci::{Mount, Process}; use oci::{LinuxIdMappingBuilder, LinuxNamespaceBuilder, LinuxNamespaceType, Process, Spec};
use oci_spec::runtime as oci;
#[test] #[test]
fn test_namespace() { fn test_namespace() {
let namespaces = [ let namespaces = [
LinuxNamespace { LinuxNamespaceBuilder::default()
r#type: "net".to_owned(), .typ(LinuxNamespaceType::Network)
path: "/sys/cgroups/net".to_owned(), .path("/sys/cgroups/net")
}, .build()
LinuxNamespace { .unwrap(),
r#type: "uts".to_owned(), LinuxNamespaceBuilder::default()
path: "/sys/cgroups/uts".to_owned(), .typ(LinuxNamespaceType::Uts)
}, .path("/sys/cgroups/uts")
.build()
.unwrap(),
]; ];
assert_eq!(contain_namespace(&namespaces, "net"), true); assert_eq!(contain_namespace(&namespaces, "net"), true);
@ -347,24 +383,27 @@ mod tests {
fn test_hostname() { fn test_hostname() {
let mut spec = Spec::default(); let mut spec = Spec::default();
hostname(&spec).unwrap(); assert!(hostname(&spec).is_ok());
spec.hostname = "a.test.com".to_owned(); spec.set_hostname(Some("a.test.com".to_owned()));
hostname(&spec).unwrap_err(); assert!(hostname(&spec).is_ok());
let mut linux = Linux::default(); let mut linux = Linux::default();
linux.namespaces = vec![ let namespaces = vec![
LinuxNamespace { LinuxNamespaceBuilder::default()
r#type: "net".to_owned(), .typ(LinuxNamespaceType::Network)
path: "/sys/cgroups/net".to_owned(), .path("/sys/cgroups/net")
}, .build()
LinuxNamespace { .unwrap(),
r#type: "uts".to_owned(), LinuxNamespaceBuilder::default()
path: "/sys/cgroups/uts".to_owned(), .typ(LinuxNamespaceType::Uts)
}, .path("/sys/cgroups/uts")
.build()
.unwrap(),
]; ];
spec.linux = Some(linux); linux.set_namespaces(Some(namespaces));
hostname(&spec).unwrap(); spec.set_linux(Some(linux));
assert!(hostname(&spec).is_ok());
} }
#[test] #[test]
@ -372,88 +411,89 @@ mod tests {
let mut spec = Spec::default(); let mut spec = Spec::default();
let linux = Linux::default(); let linux = Linux::default();
spec.linux = Some(linux); spec.set_linux(Some(linux));
security(&spec).unwrap(); security(&spec).unwrap();
let mut linux = Linux::default(); let mut linux = Linux::default();
linux.masked_paths.push("/test".to_owned()); linux.set_masked_paths(Some(vec!["/test".to_owned()]));
linux.namespaces = vec![ let namespaces = vec![
LinuxNamespace { LinuxNamespaceBuilder::default()
r#type: "net".to_owned(), .typ(LinuxNamespaceType::Network)
path: "/sys/cgroups/net".to_owned(), .path("/sys/cgroups/net")
}, .build()
LinuxNamespace { .unwrap(),
r#type: "uts".to_owned(), LinuxNamespaceBuilder::default()
path: "/sys/cgroups/uts".to_owned(), .typ(LinuxNamespaceType::Uts)
}, .path("/sys/cgroups/uts")
.build()
.unwrap(),
]; ];
spec.linux = Some(linux); linux.set_namespaces(Some(namespaces));
spec.set_linux(Some(linux));
security(&spec).unwrap_err(); security(&spec).unwrap_err();
let mut linux = Linux::default(); let mut linux = Linux::default();
linux.masked_paths.push("/test".to_owned()); linux.set_masked_paths(Some(vec!["/test".to_owned()]));
linux.namespaces = vec![ let namespaces = vec![
LinuxNamespace { LinuxNamespaceBuilder::default()
r#type: "net".to_owned(), .typ(LinuxNamespaceType::Network)
path: "/sys/cgroups/net".to_owned(), .path("/sys/cgroups/net")
}, .build()
LinuxNamespace { .unwrap(),
r#type: "mount".to_owned(), LinuxNamespaceBuilder::default()
path: "/sys/cgroups/mount".to_owned(), .typ(LinuxNamespaceType::Mount)
}, .path("/sys/cgroups/mount")
.build()
.unwrap(),
]; ];
spec.linux = Some(linux); linux.set_namespaces(Some(namespaces));
security(&spec).unwrap(); spec.set_linux(Some(linux));
assert!(security(&spec).is_ok());
// SELinux // SELinux
let valid_label = "system_u:system_r:container_t:s0:c123,c456"; let valid_label = "system_u:system_r:container_t:s0:c123,c456";
let mut process = Process::default(); let mut process = Process::default();
process.selinux_label = valid_label.to_string(); process.set_selinux_label(Some(valid_label.to_string()));
spec.process = Some(process); spec.set_process(Some(process));
security(&spec).unwrap(); security(&spec).unwrap();
let mut linux = Linux::default(); let mut linux = Linux::default();
linux.mount_label = valid_label.to_string(); linux.set_mount_label(Some(valid_label.to_string()));
spec.linux = Some(linux); spec.set_linux(Some(linux));
security(&spec).unwrap(); security(&spec).unwrap();
let invalid_label = "system_u:system_r:container_t"; let invalid_label = "system_u:system_r:container_t";
let mut process = Process::default(); let mut process = Process::default();
process.selinux_label = invalid_label.to_string(); process.set_selinux_label(Some(invalid_label.to_string()));
spec.process = Some(process); spec.set_process(Some(process));
security(&spec).unwrap_err(); security(&spec).unwrap_err();
let mut linux = Linux::default(); let mut linux = Linux::default();
linux.mount_label = invalid_label.to_string(); linux.set_mount_label(Some(valid_label.to_string()));
spec.linux = Some(linux); spec.set_linux(Some(linux));
security(&spec).unwrap_err(); security(&spec).unwrap_err();
} }
#[test] #[test]
fn test_usernamespace() { fn test_usernamespace() {
let mut spec = Spec::default(); let mut spec = Spec::default();
usernamespace(&spec).unwrap_err(); assert!(usernamespace(&spec).is_ok());
let linux = Linux::default(); let linux = Linux::default();
spec.linux = Some(linux); spec.set_linux(Some(linux));
usernamespace(&spec).unwrap(); usernamespace(&spec).unwrap();
let mut linux = Linux::default(); let mut linux = Linux::default();
linux.uid_mappings = vec![LinuxIdMapping {
container_id: 0,
host_id: 1000,
size: 0,
}];
spec.linux = Some(linux);
usernamespace(&spec).unwrap_err();
let mut linux = Linux::default(); let uidmap = LinuxIdMappingBuilder::default()
linux.uid_mappings = vec![LinuxIdMapping { .container_id(0u32)
container_id: 0, .host_id(1000u32)
host_id: 1000, .size(0u32)
size: 100, .build()
}]; .unwrap();
spec.linux = Some(linux);
linux.set_uid_mappings(Some(vec![uidmap]));
spec.set_linux(Some(linux));
usernamespace(&spec).unwrap_err(); usernamespace(&spec).unwrap_err();
} }
@ -467,62 +507,73 @@ mod tests {
// Test case: without user namespace // Test case: without user namespace
let linux = Linux::default(); let linux = Linux::default();
spec.linux = Some(linux); spec.set_linux(Some(linux));
rootless_euid_mapping(&spec).unwrap_err(); rootless_euid_mapping(&spec).unwrap_err();
// Test case: without user namespace // Test case: without user namespace
let linux = spec.linux.as_mut().unwrap(); let linux = spec.linux_mut().as_mut().unwrap();
linux.namespaces = vec![ let namespaces = vec![
LinuxNamespace { LinuxNamespaceBuilder::default()
r#type: "net".to_owned(), .typ(LinuxNamespaceType::Network)
path: "/sys/cgroups/net".to_owned(), .path("/sys/cgroups/net")
}, .build()
LinuxNamespace { .unwrap(),
r#type: "uts".to_owned(), LinuxNamespaceBuilder::default()
path: "/sys/cgroups/uts".to_owned(), .typ(LinuxNamespaceType::Uts)
}, .path("/sys/cgroups/uts")
.build()
.unwrap(),
]; ];
linux.set_namespaces(Some(namespaces));
rootless_euid_mapping(&spec).unwrap_err(); rootless_euid_mapping(&spec).unwrap_err();
let linux = spec.linux.as_mut().unwrap(); let linux = spec.linux_mut().as_mut().unwrap();
linux.namespaces = vec![ let namespaces = vec![
LinuxNamespace { LinuxNamespaceBuilder::default()
r#type: "net".to_owned(), .typ(LinuxNamespaceType::Network)
path: "/sys/cgroups/net".to_owned(), .path("/sys/cgroups/net")
}, .build()
LinuxNamespace { .unwrap(),
r#type: "user".to_owned(), LinuxNamespaceBuilder::default()
path: "/sys/cgroups/user".to_owned(), .typ(LinuxNamespaceType::User)
}, .path("/sys/cgroups/user")
.build()
.unwrap(),
]; ];
linux.uid_mappings = vec![LinuxIdMapping { linux.set_namespaces(Some(namespaces));
container_id: 0,
host_id: 1000, let uidmap = LinuxIdMappingBuilder::default()
size: 1000, .container_id(0u32)
}]; .host_id(1000u32)
linux.gid_mappings = vec![LinuxIdMapping { .size(1000u32)
container_id: 0, .build()
host_id: 1000, .unwrap();
size: 1000, let gidmap = LinuxIdMappingBuilder::default()
}]; .container_id(0u32)
.host_id(1000u32)
.size(1000u32)
.build()
.unwrap();
linux.set_uid_mappings(Some(vec![uidmap]));
linux.set_gid_mappings(Some(vec![gidmap]));
rootless_euid_mapping(&spec).unwrap(); rootless_euid_mapping(&spec).unwrap();
spec.mounts.push(Mount { let mut oci_mount = oci::Mount::default();
destination: "/app".to_owned(), oci_mount.set_destination("/app".into());
r#type: "tmpfs".to_owned(), oci_mount.set_typ(Some("tmpfs".to_owned()));
source: "".to_owned(), oci_mount.set_source(Some("".into()));
options: vec!["uid=10000".to_owned()], oci_mount.set_options(Some(vec!["uid=10000".to_owned()]));
}); spec.mounts_mut().as_mut().unwrap().push(oci_mount);
rootless_euid_mount(&spec).unwrap_err(); rootless_euid_mount(&spec).unwrap_err();
spec.mounts = vec![ let mut oci_mount = oci::Mount::default();
(Mount { oci_mount.set_destination("/app".into());
destination: "/app".to_owned(), oci_mount.set_typ(Some("tmpfs".to_owned()));
r#type: "tmpfs".to_owned(), oci_mount.set_source(Some("".into()));
source: "".to_owned(), oci_mount.set_options(Some(vec!["uid=500".to_owned(), "gid=500".to_owned()]));
options: vec!["uid=500".to_owned(), "gid=500".to_owned()], spec.set_mounts(Some(vec![oci_mount]));
}),
];
rootless_euid(&spec).unwrap(); rootless_euid(&spec).unwrap();
} }
@ -531,25 +582,34 @@ mod tests {
let mut spec = Spec::default(); let mut spec = Spec::default();
let mut linux = Linux::default(); let mut linux = Linux::default();
linux.namespaces = vec![LinuxNamespace { let namespaces = vec![LinuxNamespaceBuilder::default()
r#type: "net".to_owned(), .typ(LinuxNamespaceType::Network)
path: "/sys/cgroups/net".to_owned(), .path("/sys/cgroups/net")
}]; .build()
linux .unwrap()];
.sysctl linux.set_namespaces(Some(namespaces));
.insert("kernel.domainname".to_owned(), "test.com".to_owned());
spec.linux = Some(linux); let mut sysctl_hash = HashMap::new();
sysctl_hash.insert("kernel.domainname".to_owned(), "test.com".to_owned());
linux.set_sysctl(Some(sysctl_hash));
spec.set_linux(Some(linux));
sysctl(&spec).unwrap_err(); sysctl(&spec).unwrap_err();
spec.linux spec.linux_mut()
.as_mut() .as_mut()
.unwrap() .unwrap()
.namespaces .namespaces_mut()
.push(LinuxNamespace { .as_mut()
r#type: "uts".to_owned(), .unwrap()
path: "/sys/cgroups/uts".to_owned(), .push(
}); LinuxNamespaceBuilder::default()
sysctl(&spec).unwrap(); .typ(LinuxNamespaceType::User)
.path("/sys/cgroups/user")
.build()
.unwrap(),
);
assert!(sysctl(&spec).is_err());
} }
#[test] #[test]
@ -569,7 +629,7 @@ mod tests {
validate(&config).unwrap_err(); validate(&config).unwrap_err();
let linux = Linux::default(); let linux = Linux::default();
config.spec.as_mut().unwrap().linux = Some(linux); config.spec.as_mut().unwrap().set_linux(Some(linux));
validate(&config).unwrap_err(); validate(&config).unwrap_err();
} }
} }

View File

@ -23,7 +23,8 @@ use crate::sandbox::Sandbox;
use crate::uevent::{wait_for_uevent, Uevent, UeventMatcher}; use crate::uevent::{wait_for_uevent, Uevent, UeventMatcher};
use anyhow::{anyhow, Context, Result}; use anyhow::{anyhow, Context, Result};
use cfg_if::cfg_if; use cfg_if::cfg_if;
use oci::{LinuxDeviceCgroup, LinuxResources, Spec}; use oci::{LinuxDeviceCgroup, Spec};
use oci_spec::runtime as oci;
use protocols::agent::Device; use protocols::agent::Device;
use tracing::instrument; use tracing::instrument;
@ -621,21 +622,25 @@ impl<T: Into<DevUpdate>> From<T> for SpecUpdate {
#[instrument] #[instrument]
fn update_spec_devices(spec: &mut Spec, mut updates: HashMap<&str, DevUpdate>) -> Result<()> { fn update_spec_devices(spec: &mut Spec, mut updates: HashMap<&str, DevUpdate>) -> Result<()> {
let linux = spec let linux = spec
.linux .linux_mut()
.as_mut() .as_mut()
.ok_or_else(|| anyhow!("Spec didn't contain linux field"))?; .ok_or_else(|| anyhow!("Spec didn't contain linux field"))?;
let mut res_updates = HashMap::<(&str, i64, i64), DeviceInfo>::with_capacity(updates.len()); let mut res_updates = HashMap::<(String, i64, i64), DeviceInfo>::with_capacity(updates.len());
for specdev in &mut linux.devices { let mut default_devices = Vec::new();
if let Some(update) = updates.remove(specdev.path.as_str()) { let linux_devices = linux.devices_mut().as_mut().unwrap_or(&mut default_devices);
let host_major = specdev.major; for specdev in linux_devices.iter_mut() {
let host_minor = specdev.minor; let devtype = specdev.typ().as_str().to_string();
if let Some(update) = updates.remove(specdev.path().clone().display().to_string().as_str())
{
let host_major = specdev.major();
let host_minor = specdev.minor();
info!( info!(
sl(), sl(),
"update_spec_devices() updating device"; "update_spec_devices() updating device";
"container_path" => &specdev.path, "container_path" => &specdev.path().display().to_string(),
"type" => &specdev.r#type, "type" => &devtype,
"host_major" => host_major, "host_major" => host_major,
"host_minor" => host_minor, "host_minor" => host_minor,
"guest_major" => update.info.guest_major, "guest_major" => update.info.guest_major,
@ -643,17 +648,14 @@ fn update_spec_devices(spec: &mut Spec, mut updates: HashMap<&str, DevUpdate>) -
"final_path" => update.final_path.as_ref(), "final_path" => update.final_path.as_ref(),
); );
specdev.major = update.info.guest_major; specdev.set_major(update.info.guest_major);
specdev.minor = update.info.guest_minor; specdev.set_minor(update.info.guest_minor);
if let Some(final_path) = update.final_path { if let Some(final_path) = update.final_path {
specdev.path = final_path; specdev.set_path(PathBuf::from(&final_path));
} }
if res_updates if res_updates
.insert( .insert((devtype, host_major, host_minor), update.info)
(specdev.r#type.as_str(), host_major, host_minor),
update.info,
)
.is_some() .is_some()
{ {
return Err(anyhow!( return Err(anyhow!(
@ -677,23 +679,27 @@ fn update_spec_devices(spec: &mut Spec, mut updates: HashMap<&str, DevUpdate>) -
)); ));
} }
if let Some(resources) = linux.resources.as_mut() { if let Some(resources) = linux.resources_mut().as_mut() {
for r in &mut resources.devices { if let Some(resources_devices) = resources.devices_mut().as_mut() {
if let (Some(host_major), Some(host_minor)) = (r.major, r.minor) { for d in resources_devices.iter_mut() {
if let Some(update) = res_updates.get(&(r.r#type.as_str(), host_major, host_minor)) let dev_type = d.typ().unwrap_or_default().as_str().to_string();
{ if let (Some(host_major), Some(host_minor)) = (d.major(), d.minor()) {
info!( if let Some(update) =
sl(), res_updates.get(&(dev_type.clone(), host_major, host_minor))
"update_spec_devices() updating resource"; {
"type" => &r.r#type, info!(
"host_major" => host_major, sl(),
"host_minor" => host_minor, "update_spec_devices() updating resource";
"guest_major" => update.guest_major, "type" => &dev_type,
"guest_minor" => update.guest_minor, "host_major" => host_major,
); "host_minor" => host_minor,
"guest_major" => update.guest_major,
"guest_minor" => update.guest_minor,
);
r.major = Some(update.guest_major); d.set_major(Some(update.guest_major));
r.minor = Some(update.guest_minor); d.set_minor(Some(update.guest_minor));
}
} }
} }
} }
@ -984,8 +990,10 @@ pub async fn add_devices(
} }
} }
if let Some(process) = spec.process.as_mut() { if let Some(process) = spec.process_mut() {
update_env_pci(&mut process.env, &sandbox.lock().await.pcimap)? let env_vec: &mut Vec<String> =
&mut process.env_mut().get_or_insert_with(Vec::new).to_vec();
update_env_pci(env_vec, &sandbox.lock().await.pcimap)?
} }
update_spec_devices(spec, dev_updates) update_spec_devices(spec, dev_updates)
} }
@ -1031,31 +1039,40 @@ pub fn insert_devices_cgroup_rule(
access: &str, access: &str,
) -> Result<()> { ) -> Result<()> {
let linux = spec let linux = spec
.linux .linux_mut()
.as_mut() .as_mut()
.ok_or_else(|| anyhow!("Spec didn't container linux field"))?; .ok_or_else(|| anyhow!("Spec didn't container linux field"))?;
let devcgrp_type = dev_info
let resources = linux.resources.get_or_insert(LinuxResources::default()); .cgroup_type
.parse::<oci::LinuxDeviceType>()
let cgroup = LinuxDeviceCgroup { .context(format!(
allow, "Failed to parse {:?} to Enum LinuxDeviceType",
major: Some(dev_info.guest_major), dev_info.cgroup_type
minor: Some(dev_info.guest_minor), ))?;
r#type: dev_info.cgroup_type.clone(), let linux_resource = &mut oci::LinuxResources::default();
access: access.to_owned(), let resource = linux.resources_mut().as_mut().unwrap_or(linux_resource);
}; let mut device_cgrp = LinuxDeviceCgroup::default();
device_cgrp.set_allow(allow);
device_cgrp.set_major(Some(dev_info.guest_major));
device_cgrp.set_minor(Some(dev_info.guest_minor));
device_cgrp.set_typ(Some(devcgrp_type));
device_cgrp.set_access(Some(access.to_owned()));
debug!( debug!(
sl(), sl(),
"Insert a devices cgroup rule"; "Insert a devices cgroup rule";
"linux_device_cgroup" => cgroup.allow, "linux_device_cgroup" => device_cgrp.allow(),
"guest_major" => cgroup.major, "guest_major" => device_cgrp.major(),
"guest_minor" => cgroup.minor, "guest_minor" => device_cgrp.minor(),
"type" => cgroup.r#type.as_str(), "type" => device_cgrp.typ().unwrap().as_str(),
"access" => cgroup.access.as_str(), "access" => device_cgrp.access().as_ref().unwrap().as_str(),
); );
resources.devices.push(cgroup); if let Some(devices) = resource.devices_mut() {
devices.push(device_cgrp);
} else {
resource.set_devices(Some(vec![device_cgrp]));
}
Ok(()) Ok(())
} }
@ -1064,7 +1081,11 @@ pub fn insert_devices_cgroup_rule(
mod tests { mod tests {
use super::*; use super::*;
use crate::uevent::spawn_test_watcher; use crate::uevent::spawn_test_watcher;
use oci::Linux; use oci::{
Linux, LinuxBuilder, LinuxDeviceBuilder, LinuxDeviceCgroupBuilder, LinuxDeviceType,
LinuxResources, LinuxResourcesBuilder, SpecBuilder,
};
use oci_spec::runtime as oci;
use std::iter::FromIterator; use std::iter::FromIterator;
use tempfile::tempdir; use tempfile::tempdir;
@ -1072,15 +1093,23 @@ mod tests {
#[test] #[test]
fn test_update_device_cgroup() { fn test_update_device_cgroup() {
let mut spec = Spec { let mut linux = Linux::default();
linux: Some(Linux::default()), linux.set_resources(Some(LinuxResources::default()));
..Default::default() let mut spec = SpecBuilder::default().linux(linux).build().unwrap();
};
let dev_info = DeviceInfo::new(VM_ROOTFS, false).unwrap(); let dev_info = DeviceInfo::new(VM_ROOTFS, false).unwrap();
insert_devices_cgroup_rule(&mut spec, &dev_info, false, "rw").unwrap(); insert_devices_cgroup_rule(&mut spec, &dev_info, false, "rw").unwrap();
let devices = spec.linux.unwrap().resources.unwrap().devices; let devices = spec
.linux()
.as_ref()
.unwrap()
.resources()
.as_ref()
.unwrap()
.devices()
.clone()
.unwrap();
assert_eq!(devices.len(), 1); assert_eq!(devices.len(), 1);
let meta = fs::metadata(VM_ROOTFS).unwrap(); let meta = fs::metadata(VM_ROOTFS).unwrap();
@ -1088,8 +1117,8 @@ mod tests {
let major = stat::major(rdev) as i64; let major = stat::major(rdev) as i64;
let minor = stat::minor(rdev) as i64; let minor = stat::minor(rdev) as i64;
assert_eq!(devices[0].major, Some(major)); assert_eq!(devices[0].major(), Some(major));
assert_eq!(devices[0].minor, Some(minor)); assert_eq!(devices[0].minor(), Some(minor));
} }
#[test] #[test]
@ -1113,7 +1142,7 @@ mod tests {
); );
assert!(res.is_err()); assert!(res.is_err());
spec.linux = Some(Linux::default()); spec.set_linux(Some(Linux::default()));
// linux.devices doesn't contain the updated device // linux.devices doesn't contain the updated device
let res = update_spec_devices( let res = update_spec_devices(
@ -1125,12 +1154,15 @@ mod tests {
); );
assert!(res.is_err()); assert!(res.is_err());
spec.linux.as_mut().unwrap().devices = vec![oci::LinuxDevice { spec.linux_mut()
path: "/dev/null2".to_string(), .as_mut()
major, .unwrap()
minor, .set_devices(Some(vec![LinuxDeviceBuilder::default()
..oci::LinuxDevice::default() .path(PathBuf::from("/dev/null2"))
}]; .major(major)
.minor(minor)
.build()
.unwrap()]));
// guest and host path are not the same // guest and host path are not the same
let res = update_spec_devices( let res = update_spec_devices(
@ -1148,7 +1180,13 @@ mod tests {
spec spec
); );
spec.linux.as_mut().unwrap().devices[0].path = container_path.to_string(); spec.linux_mut()
.as_mut()
.unwrap()
.devices_mut()
.as_mut()
.unwrap()[0]
.set_path(PathBuf::from(container_path));
// spec.linux.resources is empty // spec.linux.resources is empty
let res = update_spec_devices( let res = update_spec_devices(
@ -1161,21 +1199,26 @@ mod tests {
assert!(res.is_ok()); assert!(res.is_ok());
// update both devices and cgroup lists // update both devices and cgroup lists
spec.linux.as_mut().unwrap().devices = vec![oci::LinuxDevice { spec.linux_mut()
path: container_path.to_string(), .as_mut()
major, .unwrap()
minor, .set_devices(Some(vec![LinuxDeviceBuilder::default()
..oci::LinuxDevice::default() .path(PathBuf::from(container_path))
}]; .major(major)
.minor(minor)
.build()
.unwrap()]));
spec.linux.as_mut().unwrap().resources = Some(oci::LinuxResources { spec.linux_mut().as_mut().unwrap().set_resources(Some(
devices: vec![oci::LinuxDeviceCgroup { oci::LinuxResourcesBuilder::default()
major: Some(major), .devices(vec![LinuxDeviceCgroupBuilder::default()
minor: Some(minor), .major(major)
..oci::LinuxDeviceCgroup::default() .minor(minor)
}], .build()
..oci::LinuxResources::default() .unwrap()])
}); .build()
.unwrap(),
));
let res = update_spec_devices( let res = update_spec_devices(
&mut spec, &mut spec,
@ -1198,45 +1241,49 @@ mod tests {
let host_major_b = stat::major(zero_rdev) as i64; let host_major_b = stat::major(zero_rdev) as i64;
let host_minor_b = stat::minor(zero_rdev) as i64; let host_minor_b = stat::minor(zero_rdev) as i64;
let mut spec = Spec { let mut spec = SpecBuilder::default()
linux: Some(Linux { .linux(
devices: vec![ LinuxBuilder::default()
oci::LinuxDevice { .devices(vec![
path: "/dev/a".to_string(), LinuxDeviceBuilder::default()
r#type: "c".to_string(), .path(PathBuf::from("/dev/a"))
major: host_major_a, .typ(LinuxDeviceType::C)
minor: host_minor_a, .major(host_major_a)
..oci::LinuxDevice::default() .minor(host_minor_a)
}, .build()
oci::LinuxDevice { .unwrap(),
path: "/dev/b".to_string(), LinuxDeviceBuilder::default()
r#type: "c".to_string(), .path(PathBuf::from("/dev/b"))
major: host_major_b, .typ(LinuxDeviceType::C)
minor: host_minor_b, .major(host_major_b)
..oci::LinuxDevice::default() .minor(host_minor_b)
}, .build()
], .unwrap(),
resources: Some(LinuxResources { ])
devices: vec![ .resources(
oci::LinuxDeviceCgroup { LinuxResourcesBuilder::default()
r#type: "c".to_string(), .devices(vec![
major: Some(host_major_a), LinuxDeviceCgroupBuilder::default()
minor: Some(host_minor_a), .typ(LinuxDeviceType::C)
..oci::LinuxDeviceCgroup::default() .major(host_major_a)
}, .minor(host_minor_a)
oci::LinuxDeviceCgroup { .build()
r#type: "c".to_string(), .unwrap(),
major: Some(host_major_b), LinuxDeviceCgroupBuilder::default()
minor: Some(host_minor_b), .typ(LinuxDeviceType::C)
..oci::LinuxDeviceCgroup::default() .major(host_major_b)
}, .minor(host_minor_b)
], .build()
..LinuxResources::default() .unwrap(),
}), ])
..Linux::default() .build()
}), .unwrap(),
..Spec::default() )
}; .build()
.unwrap(),
)
.build()
.unwrap();
let container_path_a = "/dev/a"; let container_path_a = "/dev/a";
let vm_path_a = "/dev/zero"; let vm_path_a = "/dev/zero";
@ -1250,17 +1297,26 @@ mod tests {
let guest_major_b = stat::major(full_rdev) as i64; let guest_major_b = stat::major(full_rdev) as i64;
let guest_minor_b = stat::minor(full_rdev) as i64; let guest_minor_b = stat::minor(full_rdev) as i64;
let specdevices = &spec.linux.as_ref().unwrap().devices; let specdevices = &spec.linux().as_ref().unwrap().devices().clone().unwrap();
assert_eq!(host_major_a, specdevices[0].major); assert_eq!(host_major_a, specdevices[0].major());
assert_eq!(host_minor_a, specdevices[0].minor); assert_eq!(host_minor_a, specdevices[0].minor());
assert_eq!(host_major_b, specdevices[1].major); assert_eq!(host_major_b, specdevices[1].major());
assert_eq!(host_minor_b, specdevices[1].minor); assert_eq!(host_minor_b, specdevices[1].minor());
let specresources = spec.linux.as_ref().unwrap().resources.as_ref().unwrap(); let specresources_devices = spec
assert_eq!(Some(host_major_a), specresources.devices[0].major); .linux()
assert_eq!(Some(host_minor_a), specresources.devices[0].minor); .as_ref()
assert_eq!(Some(host_major_b), specresources.devices[1].major); .unwrap()
assert_eq!(Some(host_minor_b), specresources.devices[1].minor); .resources()
.as_ref()
.unwrap()
.devices()
.clone()
.unwrap();
assert_eq!(Some(host_major_a), specresources_devices[0].major());
assert_eq!(Some(host_minor_a), specresources_devices[0].minor());
assert_eq!(Some(host_major_b), specresources_devices[1].major());
assert_eq!(Some(host_minor_b), specresources_devices[1].minor());
let updates = HashMap::from_iter(vec![ let updates = HashMap::from_iter(vec![
( (
@ -1275,17 +1331,26 @@ mod tests {
let res = update_spec_devices(&mut spec, updates); let res = update_spec_devices(&mut spec, updates);
assert!(res.is_ok()); assert!(res.is_ok());
let specdevices = &spec.linux.as_ref().unwrap().devices; let specdevices = &spec.linux().as_ref().unwrap().devices().clone().unwrap();
assert_eq!(guest_major_a, specdevices[0].major); assert_eq!(guest_major_a, specdevices[0].major());
assert_eq!(guest_minor_a, specdevices[0].minor); assert_eq!(guest_minor_a, specdevices[0].minor());
assert_eq!(guest_major_b, specdevices[1].major); assert_eq!(guest_major_b, specdevices[1].major());
assert_eq!(guest_minor_b, specdevices[1].minor); assert_eq!(guest_minor_b, specdevices[1].minor());
let specresources = spec.linux.as_ref().unwrap().resources.as_ref().unwrap(); let specresources_devices = spec
assert_eq!(Some(guest_major_a), specresources.devices[0].major); .linux()
assert_eq!(Some(guest_minor_a), specresources.devices[0].minor); .as_ref()
assert_eq!(Some(guest_major_b), specresources.devices[1].major); .unwrap()
assert_eq!(Some(guest_minor_b), specresources.devices[1].minor); .resources()
.as_ref()
.unwrap()
.devices()
.clone()
.unwrap();
assert_eq!(Some(guest_major_a), specresources_devices[0].major());
assert_eq!(Some(guest_minor_a), specresources_devices[0].minor());
assert_eq!(Some(guest_major_b), specresources_devices[1].major());
assert_eq!(Some(guest_minor_b), specresources_devices[1].minor());
} }
#[test] #[test]
@ -1297,54 +1362,67 @@ mod tests {
let host_major: i64 = 99; let host_major: i64 = 99;
let host_minor: i64 = 99; let host_minor: i64 = 99;
let mut spec = Spec { let mut spec = SpecBuilder::default()
linux: Some(Linux { .linux(
devices: vec![ LinuxBuilder::default()
oci::LinuxDevice { .devices(vec![
path: "/dev/char".to_string(), LinuxDeviceBuilder::default()
r#type: "c".to_string(), .path(PathBuf::from("/dev/char"))
major: host_major, .typ(LinuxDeviceType::C)
minor: host_minor, .major(host_major)
..oci::LinuxDevice::default() .minor(host_minor)
}, .build()
oci::LinuxDevice { .unwrap(),
path: "/dev/block".to_string(), LinuxDeviceBuilder::default()
r#type: "b".to_string(), .path(PathBuf::from("/dev/block"))
major: host_major, .typ(LinuxDeviceType::B)
minor: host_minor, .major(host_major)
..oci::LinuxDevice::default() .minor(host_minor)
}, .build()
], .unwrap(),
resources: Some(LinuxResources { ])
devices: vec![ .resources(
LinuxDeviceCgroup { LinuxResourcesBuilder::default()
r#type: "c".to_string(), .devices(vec![
major: Some(host_major), LinuxDeviceCgroupBuilder::default()
minor: Some(host_minor), .typ(LinuxDeviceType::C)
..LinuxDeviceCgroup::default() .major(host_major)
}, .minor(host_minor)
LinuxDeviceCgroup { .build()
r#type: "b".to_string(), .unwrap(),
major: Some(host_major), LinuxDeviceCgroupBuilder::default()
minor: Some(host_minor), .typ(LinuxDeviceType::B)
..LinuxDeviceCgroup::default() .major(host_major)
}, .minor(host_minor)
], .build()
..LinuxResources::default() .unwrap(),
}), ])
..Linux::default() .build()
}), .unwrap(),
..Spec::default() )
}; .build()
.unwrap(),
)
.build()
.unwrap();
let container_path = "/dev/char"; let container_path = "/dev/char";
let vm_path = "/dev/null"; let vm_path = "/dev/null";
let specresources = spec.linux.as_ref().unwrap().resources.as_ref().unwrap(); let specresources_devices = spec
assert_eq!(Some(host_major), specresources.devices[0].major); .linux()
assert_eq!(Some(host_minor), specresources.devices[0].minor); .as_ref()
assert_eq!(Some(host_major), specresources.devices[1].major); .unwrap()
assert_eq!(Some(host_minor), specresources.devices[1].minor); .resources()
.as_ref()
.unwrap()
.devices()
.clone()
.unwrap();
assert_eq!(Some(host_major), specresources_devices[0].major());
assert_eq!(Some(host_minor), specresources_devices[0].minor());
assert_eq!(Some(host_major), specresources_devices[1].major());
assert_eq!(Some(host_minor), specresources_devices[1].minor());
let res = update_spec_devices( let res = update_spec_devices(
&mut spec, &mut spec,
@ -1356,11 +1434,20 @@ mod tests {
assert!(res.is_ok()); assert!(res.is_ok());
// Only the char device, not the block device should be updated // Only the char device, not the block device should be updated
let specresources = spec.linux.as_ref().unwrap().resources.as_ref().unwrap(); let specresources_devices = spec
assert_eq!(Some(guest_major), specresources.devices[0].major); .linux()
assert_eq!(Some(guest_minor), specresources.devices[0].minor); .as_ref()
assert_eq!(Some(host_major), specresources.devices[1].major); .unwrap()
assert_eq!(Some(host_minor), specresources.devices[1].minor); .resources()
.as_ref()
.unwrap()
.devices()
.clone()
.unwrap();
assert_eq!(Some(guest_major), specresources_devices[0].major());
assert_eq!(Some(guest_minor), specresources_devices[0].minor());
assert_eq!(Some(host_major), specresources_devices[1].major());
assert_eq!(Some(host_minor), specresources_devices[1].minor());
} }
#[test] #[test]
@ -1373,19 +1460,21 @@ mod tests {
let host_major: i64 = 99; let host_major: i64 = 99;
let host_minor: i64 = 99; let host_minor: i64 = 99;
let mut spec = Spec { let mut spec = SpecBuilder::default()
linux: Some(Linux { .linux(
devices: vec![oci::LinuxDevice { LinuxBuilder::default()
path: container_path.to_string(), .devices(vec![LinuxDeviceBuilder::default()
r#type: "c".to_string(), .path(PathBuf::from(container_path))
major: host_major, .typ(LinuxDeviceType::C)
minor: host_minor, .major(host_major)
..oci::LinuxDevice::default() .minor(host_minor)
}], .build()
..Linux::default() .unwrap()])
}), .build()
..Spec::default() .unwrap(),
}; )
.build()
.unwrap();
let vm_path = "/dev/null"; let vm_path = "/dev/null";
let final_path = "/dev/new"; let final_path = "/dev/new";
@ -1399,10 +1488,10 @@ mod tests {
); );
assert!(res.is_ok()); assert!(res.is_ok());
let specdevices = &spec.linux.as_ref().unwrap().devices; let specdevices = &spec.linux().as_ref().unwrap().devices().clone().unwrap();
assert_eq!(guest_major, specdevices[0].major); assert_eq!(guest_major, specdevices[0].major());
assert_eq!(guest_minor, specdevices[0].minor); assert_eq!(guest_minor, specdevices[0].minor());
assert_eq!(final_path, specdevices[0].path); assert_eq!(&PathBuf::from(final_path), specdevices[0].path());
} }
#[test] #[test]

View File

@ -15,6 +15,7 @@ use std::sync::Arc;
use anyhow::{anyhow, bail, Context, Result}; use anyhow::{anyhow, bail, Context, Result};
use image_rs::image::ImageClient; use image_rs::image::ImageClient;
use kata_sys_util::validate::verify_id; use kata_sys_util::validate::verify_id;
use oci_spec::runtime as oci;
use tokio::sync::Mutex; use tokio::sync::Mutex;
use crate::rpc::CONTAINER_BASE; use crate::rpc::CONTAINER_BASE;
@ -78,7 +79,7 @@ impl ImageService {
})?) })?)
.context("load image config file")?; .context("load image config file")?;
let image_oci_process = image_oci.process.ok_or_else(|| { let image_oci_process = image_oci.process().as_ref().ok_or_else(|| {
anyhow!("The guest pause image config does not contain a process specification. Please check the pause image.") anyhow!("The guest pause image config does not contain a process specification. Please check the pause image.")
})?; })?;
info!( info!(
@ -88,11 +89,12 @@ impl ImageService {
); );
// Ensure that the args vector is not empty before accessing its elements. // Ensure that the args vector is not empty before accessing its elements.
let args = image_oci_process.args;
// Check the number of arguments. // Check the number of arguments.
if args.is_empty() { let args = if let Some(args_vec) = image_oci_process.args() {
args_vec
} else {
bail!("The number of args should be greater than or equal to one! Please check the pause image."); bail!("The number of args should be greater than or equal to one! Please check the pause image.");
} };
let pause_bundle = scoped_join(CONTAINER_BASE, cid)?; let pause_bundle = scoped_join(CONTAINER_BASE, cid)?;
fs::create_dir_all(&pause_bundle)?; fs::create_dir_all(&pause_bundle)?;

View File

@ -6,7 +6,6 @@
#[macro_use] #[macro_use]
extern crate lazy_static; extern crate lazy_static;
extern crate capctl; extern crate capctl;
extern crate oci;
extern crate prometheus; extern crate prometheus;
extern crate protocols; extern crate protocols;
extern crate regex; extern crate regex;

View File

@ -8,6 +8,7 @@ use rustjail::{pipestream::PipeStream, process::StreamType};
use tokio::io::{AsyncReadExt, AsyncWriteExt, ReadHalf}; use tokio::io::{AsyncReadExt, AsyncWriteExt, ReadHalf};
use tokio::sync::Mutex; use tokio::sync::Mutex;
use std::convert::TryFrom;
use std::ffi::{CString, OsStr}; use std::ffi::{CString, OsStr};
use std::fmt::Debug; use std::fmt::Debug;
use std::io; use std::io;
@ -22,7 +23,8 @@ use ttrpc::{
use anyhow::{anyhow, Context, Result}; use anyhow::{anyhow, Context, Result};
use cgroups::freezer::FreezerState; use cgroups::freezer::FreezerState;
use oci::{LinuxNamespace, Root, Spec}; use oci::{Hooks, LinuxNamespace, Spec};
use oci_spec::runtime as oci;
use protobuf::MessageField; use protobuf::MessageField;
use protocols::agent::{ use protocols::agent::{
AddSwapRequest, AgentDetails, CopyFileRequest, GetIPTablesRequest, GetIPTablesResponse, AddSwapRequest, AgentDetails, CopyFileRequest, GetIPTablesRequest, GetIPTablesResponse,
@ -64,6 +66,7 @@ use crate::pci;
use crate::random; use crate::random;
use crate::sandbox::Sandbox; use crate::sandbox::Sandbox;
use crate::storage::{add_storages, update_ephemeral_mounts, STORAGE_HANDLERS}; use crate::storage::{add_storages, update_ephemeral_mounts, STORAGE_HANDLERS};
use crate::util;
use crate::version::{AGENT_VERSION, API_VERSION}; use crate::version::{AGENT_VERSION, API_VERSION};
use crate::AGENT_CONFIG; use crate::AGENT_CONFIG;
@ -194,11 +197,10 @@ impl AgentService {
kata_sys_util::validate::verify_id(&cid)?; kata_sys_util::validate::verify_id(&cid)?;
let mut oci_spec = req.OCI.clone();
let use_sandbox_pidns = req.sandbox_pidns(); let use_sandbox_pidns = req.sandbox_pidns();
let mut oci = match oci_spec.as_mut() { let mut oci = match req.OCI.into_option() {
Some(spec) => rustjail::grpc_to_oci(spec), Some(spec) => spec.into(),
None => { None => {
error!(sl(), "no oci spec in the create container request!"); error!(sl(), "no oci spec in the create container request!");
return Err(anyhow!(nix::Error::EINVAL)); return Err(anyhow!(nix::Error::EINVAL));
@ -222,15 +224,17 @@ impl AgentService {
if let Some(cdh) = self.cdh_client.as_ref() { if let Some(cdh) = self.cdh_client.as_ref() {
let process = oci let process = oci
.process .process_mut()
.as_mut() .as_mut()
.ok_or_else(|| anyhow!("Spec didn't contain process field"))?; .ok_or_else(|| anyhow!("Spec didn't contain process field"))?;
for env in process.env.iter_mut() { if let Some(envs) = process.env_mut().as_mut() {
match cdh.unseal_env(env).await { for env in envs.iter_mut() {
Ok(unsealed_env) => *env = unsealed_env.to_string(), match cdh.unseal_env(env).await {
Err(e) => { Ok(unsealed_env) => *env = unsealed_env.to_string(),
warn!(sl(), "Failed to unseal secret: {}", e) Err(e) => {
warn!(sl(), "Failed to unseal secret: {}", e)
}
} }
} }
} }
@ -263,7 +267,13 @@ impl AgentService {
// systemd: "[slice]:[prefix]:[name]" // systemd: "[slice]:[prefix]:[name]"
// fs: "/path_a/path_b" // fs: "/path_a/path_b"
// If agent is init we can't use systemd cgroup mode, no matter what the host tells us // If agent is init we can't use systemd cgroup mode, no matter what the host tells us
let cgroups_path = oci.linux.as_ref().map_or("", |linux| &linux.cgroups_path); let cgroups_path = &oci
.linux()
.as_ref()
.and_then(|linux| linux.cgroups_path().as_ref())
.map(|cgrps_path| cgrps_path.display().to_string())
.unwrap_or_default();
let use_systemd_cgroup = if self.init_mode { let use_systemd_cgroup = if self.init_mode {
false false
} else { } else {
@ -291,8 +301,8 @@ impl AgentService {
let pipe_size = AGENT_CONFIG.container_pipe_size; let pipe_size = AGENT_CONFIG.container_pipe_size;
let p = if let Some(p) = oci.process { let p = if let Some(p) = oci.process() {
Process::new(&sl(), &p, cid.as_str(), true, pipe_size, proc_io)? Process::new(&sl(), p, cid.as_str(), true, pipe_size, proc_io)?
} else { } else {
info!(sl(), "no process configurations!"); info!(sl(), "no process configurations!");
return Err(anyhow!(nix::Error::EINVAL)); return Err(anyhow!(nix::Error::EINVAL));
@ -408,8 +418,7 @@ impl AgentService {
update_env_pci(&mut process.Env, &sandbox.pcimap)?; update_env_pci(&mut process.Env, &sandbox.pcimap)?;
let pipe_size = AGENT_CONFIG.container_pipe_size; let pipe_size = AGENT_CONFIG.container_pipe_size;
let ocip = rustjail::process_grpc_to_oci(&process); let ocip = process.into();
let p = Process::new(&sl(), &ocip, exec_id.as_str(), false, pipe_size, proc_io)?; let p = Process::new(&sl(), &ocip, exec_id.as_str(), false, pipe_size, proc_io)?;
let ctr = sandbox let ctr = sandbox
@ -759,7 +768,7 @@ impl agent_ttrpc::AgentService for AgentService {
.get_container(&req.container_id) .get_container(&req.container_id)
.map_ttrpc_err(ttrpc::Code::INVALID_ARGUMENT, "invalid container id")?; .map_ttrpc_err(ttrpc::Code::INVALID_ARGUMENT, "invalid container id")?;
if let Some(res) = req.resources.as_ref() { if let Some(res) = req.resources.as_ref() {
let oci_res = rustjail::resources_grpc_to_oci(res); let oci_res = res.clone().into();
ctr.set(oci_res).map_ttrpc_err(same)?; ctr.set(oci_res).map_ttrpc_err(same)?;
} }
@ -1668,41 +1677,45 @@ fn update_container_namespaces(
sandbox_pidns: bool, sandbox_pidns: bool,
) -> Result<()> { ) -> Result<()> {
let linux = spec let linux = spec
.linux .linux_mut()
.as_mut() .as_mut()
.ok_or_else(|| anyhow!(ERR_NO_LINUX_FIELD))?; .ok_or_else(|| anyhow!(ERR_NO_LINUX_FIELD))?;
let namespaces = linux.namespaces.as_mut_slice(); if let Some(namespaces) = linux.namespaces_mut() {
for namespace in namespaces.iter_mut() { for namespace in namespaces.iter_mut() {
if namespace.r#type == NSTYPEIPC { if namespace.typ().to_string() == NSTYPEIPC {
namespace.path = sandbox.shared_ipcns.path.clone(); namespace.set_path(Some(PathBuf::from(&sandbox.shared_ipcns.path.clone())));
continue; namespace.set_path(None);
continue;
}
if namespace.typ().to_string() == NSTYPEUTS {
namespace.set_path(Some(PathBuf::from(&sandbox.shared_utsns.path.clone())));
namespace.set_path(None);
continue;
}
} }
if namespace.r#type == NSTYPEUTS {
namespace.path = sandbox.shared_utsns.path.clone(); // update pid namespace
continue; let mut pid_ns = LinuxNamespace::default();
pid_ns.set_typ(oci::LinuxNamespaceType::try_from(NSTYPEPID).unwrap());
// Use shared pid ns if useSandboxPidns has been set in either
// the create_sandbox request or create_container request.
// Else set this to empty string so that a new pid namespace is
// created for the container.
if sandbox_pidns {
if let Some(ref pidns) = &sandbox.sandbox_pidns {
if !pidns.path.is_empty() {
pid_ns.set_path(Some(PathBuf::from(&pidns.path)));
}
} else {
return Err(anyhow!(ERR_NO_SANDBOX_PIDNS));
}
} }
namespaces.push(pid_ns);
} }
// update pid namespace
let mut pid_ns = LinuxNamespace {
r#type: NSTYPEPID.to_string(),
..Default::default()
};
// Use shared pid ns if useSandboxPidns has been set in either
// the create_sandbox request or create_container request.
// Else set this to empty string so that a new pid namespace is
// created for the container.
if sandbox_pidns {
if let Some(ref pidns) = &sandbox.sandbox_pidns {
pid_ns.path = String::from(pidns.path.as_str());
} else {
return Err(anyhow!(ERR_NO_SANDBOX_PIDNS));
}
}
linux.namespaces.push(pid_ns);
Ok(()) Ok(())
} }
@ -1737,11 +1750,18 @@ async fn remove_container_resources(sandbox: &mut Sandbox, cid: &str) -> Result<
fn append_guest_hooks(s: &Sandbox, oci: &mut Spec) -> Result<()> { fn append_guest_hooks(s: &Sandbox, oci: &mut Spec) -> Result<()> {
if let Some(ref guest_hooks) = s.hooks { if let Some(ref guest_hooks) = s.hooks {
let mut hooks = oci.hooks.take().unwrap_or_default(); if let Some(hooks) = oci.hooks_mut() {
hooks.prestart.append(&mut guest_hooks.prestart.clone()); util::merge(hooks.poststart_mut(), guest_hooks.prestart());
hooks.poststart.append(&mut guest_hooks.poststart.clone()); util::merge(hooks.poststart_mut(), guest_hooks.poststart());
hooks.poststop.append(&mut guest_hooks.poststop.clone()); util::merge(hooks.poststop_mut(), guest_hooks.poststop());
oci.hooks = Some(hooks); } else {
let _oci_hooks = oci.set_hooks(Some(Hooks::default()));
if let Some(hooks) = oci.hooks_mut() {
hooks.set_prestart(guest_hooks.prestart().clone());
hooks.set_poststart(guest_hooks.poststart().clone());
hooks.set_poststop(guest_hooks.poststop().clone());
}
}
} }
Ok(()) Ok(())
@ -1941,7 +1961,7 @@ async fn do_add_swap(sandbox: &Arc<Mutex<Sandbox>>, req: &AddSwapRequest) -> Res
// - container rootfs bind mounted at /<CONTAINER_BASE>/<cid>/rootfs // - container rootfs bind mounted at /<CONTAINER_BASE>/<cid>/rootfs
// - modify container spec root to point to /<CONTAINER_BASE>/<cid>/rootfs // - modify container spec root to point to /<CONTAINER_BASE>/<cid>/rootfs
pub fn setup_bundle(cid: &str, spec: &mut Spec) -> Result<PathBuf> { pub fn setup_bundle(cid: &str, spec: &mut Spec) -> Result<PathBuf> {
let spec_root = if let Some(sr) = &spec.root { let spec_root = if let Some(sr) = &spec.root() {
sr sr
} else { } else {
return Err(anyhow!(nix::Error::EINVAL)); return Err(anyhow!(nix::Error::EINVAL));
@ -1950,7 +1970,7 @@ pub fn setup_bundle(cid: &str, spec: &mut Spec) -> Result<PathBuf> {
let bundle_path = Path::new(CONTAINER_BASE).join(cid); let bundle_path = Path::new(CONTAINER_BASE).join(cid);
let config_path = bundle_path.join("config.json"); let config_path = bundle_path.join("config.json");
let rootfs_path = bundle_path.join("rootfs"); let rootfs_path = bundle_path.join("rootfs");
let spec_root_path = Path::new(&spec_root.path); let spec_root_path = spec_root.path();
let rootfs_exists = Path::new(&rootfs_path).exists(); let rootfs_exists = Path::new(&rootfs_path).exists();
info!( info!(
@ -1970,15 +1990,10 @@ pub fn setup_bundle(cid: &str, spec: &mut Spec) -> Result<PathBuf> {
)?; )?;
} }
let rootfs_path_name = rootfs_path let mut oci_root = oci::Root::default();
.to_str() oci_root.set_path(rootfs_path);
.ok_or_else(|| anyhow!("failed to convert rootfs to unicode"))? oci_root.set_readonly(spec_root.readonly());
.to_string(); spec.set_root(Some(oci_root));
spec.root = Some(Root {
path: rootfs_path_name,
readonly: spec_root.readonly,
});
let _ = spec.save( let _ = spec.save(
config_path config_path
@ -2045,7 +2060,11 @@ mod tests {
use crate::{namespace::Namespace, protocols::agent_ttrpc_async::AgentService as _}; use crate::{namespace::Namespace, protocols::agent_ttrpc_async::AgentService as _};
use nix::mount; use nix::mount;
use nix::sched::{unshare, CloneFlags}; use nix::sched::{unshare, CloneFlags};
use oci::{Hook, Hooks, Linux, LinuxDeviceCgroup, LinuxNamespace, LinuxResources}; use oci::{
HookBuilder, HooksBuilder, Linux, LinuxBuilder, LinuxDeviceCgroupBuilder, LinuxNamespace,
LinuxNamespaceBuilder, LinuxResourcesBuilder, SpecBuilder,
};
use oci_spec::runtime::{LinuxNamespaceType, Root};
use tempfile::{tempdir, TempDir}; use tempfile::{tempdir, TempDir};
use test_utils::{assert_result, skip_if_not_root}; use test_utils::{assert_result, skip_if_not_root};
use ttrpc::{r#async::TtrpcContext, MessageHeader}; use ttrpc::{r#async::TtrpcContext, MessageHeader};
@ -2072,21 +2091,17 @@ mod tests {
.duration_since(UNIX_EPOCH) .duration_since(UNIX_EPOCH)
.expect("Time went backwards"); .expect("Time went backwards");
let root = Root { let mut root = Root::default();
path: String::from("/"), root.set_path(PathBuf::from("/"));
..Default::default()
};
let linux_resources = LinuxResources { let linux_resources = LinuxResourcesBuilder::default()
devices: vec![LinuxDeviceCgroup { .devices(vec![LinuxDeviceCgroupBuilder::default()
allow: true, .allow(true)
r#type: String::new(), .access("rwm")
major: None, .build()
minor: None, .unwrap()])
access: String::from("rwm"), .build()
}], .unwrap();
..Default::default()
};
let cgroups_path = format!( let cgroups_path = format!(
"/{}/dummycontainer{}", "/{}/dummycontainer{}",
@ -2094,15 +2109,17 @@ mod tests {
since_the_epoch.as_millis() since_the_epoch.as_millis()
); );
let spec = Spec { let spec = SpecBuilder::default()
linux: Some(Linux { .linux(
cgroups_path, LinuxBuilder::default()
resources: Some(linux_resources), .cgroups_path(cgroups_path)
..Default::default() .resources(linux_resources)
}), .build()
root: Some(root), .unwrap(),
..Default::default() )
}; .root(root)
.build()
.unwrap();
CreateOpts { CreateOpts {
cgroup_name: "".to_string(), cgroup_name: "".to_string(),
@ -2160,18 +2177,18 @@ mod tests {
async fn test_append_guest_hooks() { async fn test_append_guest_hooks() {
let logger = slog::Logger::root(slog::Discard, o!()); let logger = slog::Logger::root(slog::Discard, o!());
let mut s = Sandbox::new(&logger).unwrap(); let mut s = Sandbox::new(&logger).unwrap();
s.hooks = Some(Hooks { let hooks = HooksBuilder::default()
prestart: vec![Hook { .prestart(vec![HookBuilder::default()
path: "foo".to_string(), .path(PathBuf::from("foo"))
..Default::default() .build()
}], .unwrap()])
..Default::default() .build()
}); .unwrap();
let mut oci = Spec { s.hooks = Some(hooks);
..Default::default()
}; let mut oci = Spec::default();
append_guest_hooks(&s, &mut oci).unwrap(); append_guest_hooks(&s, &mut oci).unwrap();
assert_eq!(s.hooks, oci.hooks); assert_eq!(s.hooks, oci.hooks().clone());
} }
#[tokio::test] #[tokio::test]
@ -2399,30 +2416,32 @@ mod tests {
has_linux_in_spec: true, has_linux_in_spec: true,
sandbox_pidns_path: Some("sharedpidns"), sandbox_pidns_path: Some("sharedpidns"),
namespaces: vec![ namespaces: vec![
LinuxNamespace { LinuxNamespaceBuilder::default()
r#type: NSTYPEIPC.to_string(), .typ(LinuxNamespaceType::Ipc)
path: "ipcpath".to_string(), .path("ipcpath")
}, .build()
LinuxNamespace { .unwrap(),
r#type: NSTYPEUTS.to_string(), LinuxNamespaceBuilder::default()
path: "utspath".to_string(), .typ(LinuxNamespaceType::Uts)
}, .path("utspath")
.build()
.unwrap(),
], ],
use_sandbox_pidns: false, use_sandbox_pidns: false,
result: Ok(()), result: Ok(()),
expected_namespaces: vec![ expected_namespaces: vec![
LinuxNamespace { LinuxNamespaceBuilder::default()
r#type: NSTYPEIPC.to_string(), .typ(LinuxNamespaceType::Ipc)
path: "".to_string(), .build()
}, .unwrap(),
LinuxNamespace { LinuxNamespaceBuilder::default()
r#type: NSTYPEUTS.to_string(), .typ(LinuxNamespaceType::Uts)
path: "".to_string(), .build()
}, .unwrap(),
LinuxNamespace { LinuxNamespaceBuilder::default()
r#type: NSTYPEPID.to_string(), .typ(LinuxNamespaceType::Pid)
path: "".to_string(), .build()
}, .unwrap(),
], ],
} }
} }
@ -2435,37 +2454,39 @@ mod tests {
TestData { TestData {
use_sandbox_pidns: true, use_sandbox_pidns: true,
expected_namespaces: vec![ expected_namespaces: vec![
LinuxNamespace { LinuxNamespaceBuilder::default()
r#type: NSTYPEIPC.to_string(), .typ(LinuxNamespaceType::Ipc)
path: "".to_string(), .build()
}, .unwrap(),
LinuxNamespace { LinuxNamespaceBuilder::default()
r#type: NSTYPEUTS.to_string(), .typ(LinuxNamespaceType::Uts)
path: "".to_string(), .build()
}, .unwrap(),
LinuxNamespace { LinuxNamespaceBuilder::default()
r#type: NSTYPEPID.to_string(), .typ(LinuxNamespaceType::Pid)
path: "sharedpidns".to_string(), .path("sharedpidns")
}, .build()
.unwrap(),
], ],
..Default::default() ..Default::default()
}, },
TestData { TestData {
namespaces: vec![], namespaces: vec![],
use_sandbox_pidns: true, use_sandbox_pidns: true,
expected_namespaces: vec![LinuxNamespace { expected_namespaces: vec![LinuxNamespaceBuilder::default()
r#type: NSTYPEPID.to_string(), .typ(LinuxNamespaceType::Pid)
path: "sharedpidns".to_string(), .path("sharedpidns")
}], .build()
.unwrap()],
..Default::default() ..Default::default()
}, },
TestData { TestData {
namespaces: vec![], namespaces: vec![],
use_sandbox_pidns: false, use_sandbox_pidns: false,
expected_namespaces: vec![LinuxNamespace { expected_namespaces: vec![LinuxNamespaceBuilder::default()
r#type: NSTYPEPID.to_string(), .typ(LinuxNamespaceType::Pid)
path: "".to_string(), .build()
}], .unwrap()],
..Default::default() ..Default::default()
}, },
TestData { TestData {
@ -2495,11 +2516,11 @@ mod tests {
} }
let mut oci = Spec::default(); let mut oci = Spec::default();
oci.set_linux(None);
if d.has_linux_in_spec { if d.has_linux_in_spec {
oci.linux = Some(Linux { let mut linux = Linux::default();
namespaces: d.namespaces.clone(), linux.set_namespaces(Some(d.namespaces.clone()));
..Default::default() oci.set_linux(Some(linux));
});
} }
let result = update_container_namespaces(&sandbox, &mut oci, d.use_sandbox_pidns); let result = update_container_namespaces(&sandbox, &mut oci, d.use_sandbox_pidns);
@ -2507,8 +2528,13 @@ mod tests {
let msg = format!("{}, result: {:?}", msg, result); let msg = format!("{}, result: {:?}", msg, result);
assert_result!(d.result, result, msg); assert_result!(d.result, result, msg);
if let Some(linux) = oci.linux { if let Some(linux) = oci.linux() {
assert_eq!(d.expected_namespaces, linux.namespaces, "{}", msg); assert_eq!(
d.expected_namespaces,
linux.namespaces().clone().unwrap(),
"{}",
msg
);
} }
} }
} }

View File

@ -9,7 +9,7 @@ use std::fmt::{Debug, Formatter};
use std::fs; use std::fs;
use std::os::fd::FromRawFd; use std::os::fd::FromRawFd;
use std::os::unix::fs::PermissionsExt; use std::os::unix::fs::PermissionsExt;
use std::path::Path; use std::path::{Path, PathBuf};
use std::str::FromStr; use std::str::FromStr;
use std::sync::atomic::{AtomicU32, Ordering}; use std::sync::atomic::{AtomicU32, Ordering};
use std::sync::{Arc, RwLock}; use std::sync::{Arc, RwLock};
@ -24,6 +24,7 @@ use nix::fcntl::{self, OFlag};
use nix::sched::{setns, unshare, CloneFlags}; use nix::sched::{setns, unshare, CloneFlags};
use nix::sys::stat::Mode; use nix::sys::stat::Mode;
use oci::{Hook, Hooks}; use oci::{Hook, Hooks};
use oci_spec::runtime as oci;
use protocols::agent::{OnlineCPUMemRequest, SharedMount}; use protocols::agent::{OnlineCPUMemRequest, SharedMount};
use regex::Regex; use regex::Regex;
use rustjail::cgroups::{self as rustjail_cgroups, DevicesCgroupInfo}; use rustjail::cgroups::{self as rustjail_cgroups, DevicesCgroupInfo};
@ -319,16 +320,21 @@ impl Sandbox {
let guest_cpuset = rustjail_cgroups::fs::get_guest_cpuset()?; let guest_cpuset = rustjail_cgroups::fs::get_guest_cpuset()?;
for (_, ctr) in self.containers.iter() { for (_, ctr) in self.containers.iter() {
if let Some(spec) = ctr.config.spec.as_ref() { match ctr
if let Some(linux) = spec.linux.as_ref() { .config
if let Some(resources) = linux.resources.as_ref() { .spec
if let Some(cpus) = resources.cpu.as_ref() { .as_ref()
info!(self.logger, "updating {}", ctr.id.as_str()); .and_then(|spec| spec.linux().as_ref())
ctr.cgroup_manager .and_then(|linux| linux.resources().as_ref())
.update_cpuset_path(guest_cpuset.as_str(), &cpus.cpus)?; .and_then(|resources| resources.cpu().as_ref())
} .and_then(|cpus| cpus.cpus().as_ref())
} {
Some(cpu_set) => {
info!(self.logger, "updating {}", ctr.id.as_str());
ctr.cgroup_manager
.update_cpuset_path(guest_cpuset.as_str(), cpu_set)?;
} }
None => continue,
} }
} }
@ -339,15 +345,16 @@ impl Sandbox {
pub fn add_hooks(&mut self, dir: &str) -> Result<()> { pub fn add_hooks(&mut self, dir: &str) -> Result<()> {
let mut hooks = Hooks::default(); let mut hooks = Hooks::default();
if let Ok(hook) = self.find_hooks(dir, "prestart") { if let Ok(hook) = self.find_hooks(dir, "prestart") {
hooks.prestart = hook; hooks.set_prestart(Some(hook));
} }
if let Ok(hook) = self.find_hooks(dir, "poststart") { if let Ok(hook) = self.find_hooks(dir, "poststart") {
hooks.poststart = hook; hooks.set_poststart(Some(hook));
} }
if let Ok(hook) = self.find_hooks(dir, "poststop") { if let Ok(hook) = self.find_hooks(dir, "poststop") {
hooks.poststop = hook; hooks.set_poststop(Some(hook));
} }
self.hooks = Some(hooks); self.hooks = Some(hooks);
Ok(()) Ok(())
} }
@ -365,16 +372,13 @@ impl Sandbox {
} }
let name = entry.file_name(); let name = entry.file_name();
let hook = Hook { let mut hook = oci::Hook::default();
path: Path::new(hook_path) hook.set_path(PathBuf::from(hook_path).join(hook_type).join(&name));
.join(hook_type) hook.set_args(Some(vec![
.join(&name) name.to_str().unwrap().to_owned(),
.to_str() hook_type.to_owned(),
.unwrap() ]));
.to_owned(),
args: vec![name.to_str().unwrap().to_owned(), hook_type.to_owned()],
..Default::default()
};
info!( info!(
self.logger, self.logger,
"found {} hook {:?} mode {:o}", "found {} hook {:?} mode {:o}",
@ -382,6 +386,7 @@ impl Sandbox {
hook, hook,
entry.metadata()?.permissions().mode() entry.metadata()?.permissions().mode()
); );
hooks.push(hook); hooks.push(hook);
} }
@ -662,7 +667,8 @@ mod tests {
use crate::mount::baremount; use crate::mount::baremount;
use anyhow::{anyhow, Error}; use anyhow::{anyhow, Error};
use nix::mount::MsFlags; use nix::mount::MsFlags;
use oci::{Linux, LinuxDeviceCgroup, LinuxResources, Root, Spec}; use oci::{Linux, LinuxBuilder, LinuxDeviceCgroup, LinuxResources, Root, Spec, SpecBuilder};
use oci_spec::runtime as oci;
use rustjail::container::LinuxContainer; use rustjail::container::LinuxContainer;
use rustjail::process::Process; use rustjail::process::Process;
use rustjail::specconv::CreateOpts; use rustjail::specconv::CreateOpts;
@ -836,21 +842,15 @@ mod tests {
.duration_since(UNIX_EPOCH) .duration_since(UNIX_EPOCH)
.expect("Time went backwards"); .expect("Time went backwards");
let root = Root { let mut root = Root::default();
path: String::from("/"), root.set_path(PathBuf::from("/"));
..Default::default()
};
let linux_resources = LinuxResources { let mut cgroup = LinuxDeviceCgroup::default();
devices: vec![LinuxDeviceCgroup { cgroup.set_allow(true);
allow: true, cgroup.set_access(Some(String::from("rwm")));
r#type: String::new(),
major: None, let mut linux_resources = LinuxResources::default();
minor: None, linux_resources.set_devices(Some(vec![cgroup]));
access: String::from("rwm"),
}],
..Default::default()
};
let cgroups_path = format!( let cgroups_path = format!(
"/{}/dummycontainer{}", "/{}/dummycontainer{}",
@ -858,15 +858,17 @@ mod tests {
since_the_epoch.as_millis() since_the_epoch.as_millis()
); );
let spec = Spec { let spec = SpecBuilder::default()
linux: Some(Linux { .linux(
cgroups_path, LinuxBuilder::default()
resources: Some(linux_resources), .cgroups_path(cgroups_path)
..Default::default() .resources(linux_resources)
}), .build()
root: Some(root), .unwrap(),
..Default::default() )
}; .root(root)
.build()
.unwrap();
CreateOpts { CreateOpts {
cgroup_name: "".to_string(), cgroup_name: "".to_string(),
@ -977,9 +979,18 @@ mod tests {
assert!(s.add_hooks(tmpdir_path).is_ok()); assert!(s.add_hooks(tmpdir_path).is_ok());
assert!(s.hooks.is_some()); assert!(s.hooks.is_some());
assert!(s.hooks.as_ref().unwrap().prestart.len() == 1); assert!(s.hooks.as_ref().unwrap().prestart().clone().unwrap().len() == 1);
assert!(s.hooks.as_ref().unwrap().poststart.is_empty()); // As we don't create poststart/xxx, the poststart will be none
assert!(s.hooks.as_ref().unwrap().poststop.is_empty()); assert!(s.hooks.as_ref().unwrap().poststart().clone().is_none());
// poststop path is created but as the problem of file perm is rejected.
assert!(s
.hooks
.as_ref()
.unwrap()
.poststop()
.clone()
.unwrap()
.is_empty());
} }
#[tokio::test] #[tokio::test]

View File

@ -72,6 +72,24 @@ pub async fn get_vsock_stream(fd: RawFd) -> Result<VsockStream> {
Ok(stream?) Ok(stream?)
} }
pub fn merge<T>(v1: &mut Option<Vec<T>>, v2: &Option<Vec<T>>) -> Option<Vec<T>>
where
T: Clone,
{
let mut result = v1.clone().map(|mut vec| {
if let Some(ref other) = v2 {
vec.extend(other.iter().cloned());
}
vec
});
if result.is_none() {
result.clone_from(v2);
}
result
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;