Skip to content

Commit 360f6d5

Browse files
committed
refactor(core): use upstream CDI name validation
Signed-off-by: Evan Lezar <elezar@nvidia.com>
1 parent 6e69360 commit 360f6d5

11 files changed

Lines changed: 538 additions & 33 deletions

File tree

‎.packit.yaml‎

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,9 @@ actions:
2727
# Uses git ls-files + tar instead of git archive so the tarball
2828
# reflects any patching that Packit may have done (e.g. version bumps).
2929
- 'bash -c "VERSION=${PACKIT_PROJECT_VERSION} && TMPDIR=$(mktemp -d) && DIR=openshell-${VERSION} && mkdir -p ${TMPDIR}/${DIR} && git ls-files -z | xargs -0 tar cf - | tar xf - -C ${TMPDIR}/${DIR}/ && tar -czf openshell-${VERSION}.tar.gz -C ${TMPDIR} ${DIR} && rm -rf ${TMPDIR}"'
30-
# Step 2: Create vendored Cargo dependencies tarball for offline RPM build.
31-
- 'bash -c "VERSION=${PACKIT_PROJECT_VERSION} && CARGO_HTTP_TIMEOUT=600 CARGO_NET_RETRY=5 cargo vendor --locked --quiet && tar -cJf openshell-${VERSION}-vendor.tar.xz vendor/ && rm -rf vendor/"'
30+
# Step 2: Preserve Cargo source mappings, including pinned Git dependencies,
31+
# with the vendored dependencies for the offline RPM build.
32+
- 'bash -c "VERSION=${PACKIT_PROJECT_VERSION} && CARGO_HTTP_TIMEOUT=600 CARGO_NET_RETRY=5 cargo vendor --locked > cargo-vendor-config.toml && tar -cJf openshell-${VERSION}-vendor.tar.xz vendor/ cargo-vendor-config.toml && rm -rf vendor/ cargo-vendor-config.toml"'
3233
# Step 3: Return the primary archive name. Packit expects create-archive
3334
# to print one path for Source0; Source1 is patched explicitly below.
3435
- 'bash -c "echo openshell-${PACKIT_PROJECT_VERSION}.tar.gz"'

‎Cargo.lock‎

Lines changed: 44 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎Cargo.toml‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@ rustix = { version = "1.1", features = ["process"] }
8484
socket2 = "0.6"
8585

8686
# Serialization
87+
container-device-interface = { git = "https://github.com/cncf-tags/container-device-interface-rs", rev = "b3fec145b213afc4dfd6900c578edb42dc99e5df", default-features = false }
8788
serde = { version = "1", features = ["derive"] }
8889
serde_json = "1"
8990
serde_path_to_error = "0.1"

‎crates/openshell-core/Cargo.toml‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,9 @@ openshell-otel = { path = "../openshell-otel", optional = true }
4646
nix = { workspace = true }
4747
rustix = { workspace = true }
4848

49+
[target.'cfg(target_os = "linux")'.dependencies]
50+
container-device-interface = { workspace = true }
51+
4952
[features]
5053
default = ["telemetry"]
5154
## Compile in anonymous telemetry emission support. On by default; disable with
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
//! Portable CDI name checking for non-Linux targets.
5+
6+
/// Check `<vendor>/<class>=<name>` syntax without resolving or authorizing devices.
7+
/// Input is neither trimmed nor normalized.
8+
#[must_use]
9+
pub fn is_qualified_name(device: &str) -> bool {
10+
let Some((kind, name)) = device.split_once('=') else {
11+
return false;
12+
};
13+
let Some((vendor, class)) = kind.split_once('/') else {
14+
return false;
15+
};
16+
let valid_component = |value: &str, device_name: bool| {
17+
let bytes = value.as_bytes();
18+
bytes.first().is_some_and(|byte| {
19+
if device_name {
20+
byte.is_ascii_alphanumeric()
21+
} else {
22+
byte.is_ascii_alphabetic()
23+
}
24+
}) && bytes.last().is_some_and(u8::is_ascii_alphanumeric)
25+
&& bytes.iter().all(|byte| {
26+
byte.is_ascii_alphanumeric()
27+
|| matches!(byte, b'_' | b'-' | b'.')
28+
|| (device_name && *byte == b':')
29+
})
30+
};
31+
valid_component(vendor, false) && valid_component(class, false) && valid_component(name, true)
32+
}

‎crates/openshell-core/src/gpu.rs‎

Lines changed: 4 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -302,39 +302,15 @@ fn cdi_nvidia_gpu_suffix(id: &str) -> Option<&str> {
302302
id.strip_prefix(CDI_NVIDIA_GPU_PREFIX)
303303
}
304304

305-
// Local equivalent of the upstream CDI parser until its public API is released:
306-
// https://github.com/cncf-tags/container-device-interface-rs/pull/177
307-
fn is_qualified_name(device: &str) -> bool {
308-
let Some((kind, name)) = device.split_once('=') else {
309-
return false;
310-
};
311-
let Some((vendor, class)) = kind.split_once('/') else {
312-
return false;
313-
};
314-
let valid_component = |value: &str, device_name: bool| {
315-
let bytes = value.as_bytes();
316-
bytes.first().is_some_and(|byte| {
317-
if device_name {
318-
byte.is_ascii_alphanumeric()
319-
} else {
320-
byte.is_ascii_alphabetic()
321-
}
322-
}) && bytes.last().is_some_and(u8::is_ascii_alphanumeric)
323-
&& bytes.iter().all(|byte| {
324-
byte.is_ascii_alphanumeric()
325-
|| matches!(byte, b'_' | b'-' | b'.')
326-
|| (device_name && *byte == b':')
327-
})
328-
};
329-
valid_component(vendor, false) && valid_component(class, false) && valid_component(name, true)
330-
}
331-
332305
/// Validate vendor-agnostic CDI qualified names without resolving or authorizing devices.
333306
///
334307
/// # Errors
335308
/// Returns an error for host paths or malformed `<vendor>/<class>=<name>` selectors.
336309
pub fn validate_cdi_device_names(devices: &[String], field: &str) -> Result<(), String> {
337-
if devices.iter().any(|device| !is_qualified_name(device)) {
310+
if devices
311+
.iter()
312+
.any(|device| !crate::cdi_name::is_qualified_name(device))
313+
{
338314
return Err(format!(
339315
"{field} must contain CDI qualified names (<vendor>/<class>=<name>)"
340316
));

‎crates/openshell-core/src/lib.rs‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,12 @@
1111
1212
pub mod activity;
1313
pub mod auth;
14+
#[cfg(target_os = "linux")]
15+
pub mod cdi_name {
16+
pub use container_device_interface::parser::is_qualified_name;
17+
}
18+
#[cfg(not(target_os = "linux"))]
19+
pub mod cdi_name;
1420
pub mod config;
1521
pub mod container_paths;
1622
pub mod denial;

‎deny.toml‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,4 +69,4 @@ external-default-features = "allow"
6969
unknown-registry = "deny"
7070
unknown-git = "deny"
7171
allow-registry = ["https://github.com/rust-lang/crates.io-index"]
72-
allow-git = []
72+
allow-git = ["https://github.com/cncf-tags/container-device-interface-rs"]

0 commit comments

Comments
 (0)