use super::builder::ContainerBuilder;
use super::sockaddr::build_sockaddrs;
use super::types::{RetainedBasicMetadata, RetainedNamedVolumes, RetainedVolumes};
use crate::error::WslcError;
use std::ffi::CString;
use windows_sys::Win32::Networking::WinSock::SOCKADDR_STORAGE;
use wslcsdk_sys::types::{
WslcContainerNamedVolume, WslcContainerPortMapping, WslcContainerSettings, WslcContainerVolume,
};
use wslcsdk_sys::*;
pub(super) fn apply_metadata(
builder: &ContainerBuilder,
settings: &mut WslcContainerSettings,
) -> Result<RetainedBasicMetadata, WslcError> {
let name = if let Some(ref n) = builder.name {
let c = CString::new(n.as_str())
.map_err(|e| WslcError::NulError(format!("容器名称含非法空字节: {e}")))?;
let hr = unsafe { WslcSetContainerSettingsName(settings, c.as_ptr()) };
WslcError::check_hr(hr, "设置容器名称失败")?;
Some(c)
} else {
None
};
if let Some(mode) = builder.networking_mode {
let hr = unsafe { WslcSetContainerSettingsNetworkingMode(settings, mode) };
WslcError::check_hr(hr, "设置容器网络模式失败")?;
}
let host = if let Some(ref h) = builder.host_name {
let c = CString::new(h.as_str())
.map_err(|e| WslcError::NulError(format!("主机名含非法空字节: {e}")))?;
let hr = unsafe { WslcSetContainerSettingsHostName(settings, c.as_ptr()) };
WslcError::check_hr(hr, "设置容器主机名失败")?;
Some(c)
} else {
None
};
let domain = if let Some(ref d) = builder.domain_name {
let c = CString::new(d.as_str())
.map_err(|e| WslcError::NulError(format!("域名含非法空字节: {e}")))?;
let hr = unsafe { WslcSetContainerSettingsDomainName(settings, c.as_ptr()) };
WslcError::check_hr(hr, "设置容器域名失败")?;
Some(c)
} else {
None
};
if builder.flags != 0 {
let hr = unsafe { WslcSetContainerSettingsFlags(settings, builder.flags) };
WslcError::check_hr(hr, "设置容器标志位失败")?;
}
Ok(RetainedBasicMetadata {
_name: name,
_host: host,
_domain: domain,
})
}
pub(super) fn apply_port_mappings(
builder: &ContainerBuilder,
settings: &mut WslcContainerSettings,
) -> Result<(Vec<WslcContainerPortMapping>, Vec<SOCKADDR_STORAGE>), WslcError> {
if builder.port_mappings.is_empty() {
return Ok((Vec::new(), Vec::new()));
}
let mut sockaddrs = build_sockaddrs(&builder.port_mappings);
let mut raw_ports: Vec<WslcContainerPortMapping> =
Vec::with_capacity(builder.port_mappings.len());
let mut storage_idx = 0;
for p in &builder.port_mappings {
let addr_ptr = if p.bind_ip.is_some() {
let ptr = &mut sockaddrs[storage_idx] as *mut SOCKADDR_STORAGE;
storage_idx += 1;
ptr
} else {
std::ptr::null_mut()
};
raw_ports.push(WslcContainerPortMapping {
windows_port: p.windows_port,
container_port: p.container_port,
protocol: p.protocol,
windows_address: addr_ptr,
});
}
let hr = unsafe {
WslcSetContainerSettingsPortMappings(settings, raw_ports.as_ptr(), raw_ports.len() as u32)
};
WslcError::check_hr(hr, "设置容器端口映射失败")?;
Ok((raw_ports, sockaddrs))
}
pub(super) fn apply_volumes(
builder: &ContainerBuilder,
settings: &mut WslcContainerSettings,
) -> Result<RetainedVolumes, WslcError> {
if builder.volumes.is_empty() {
return Ok(RetainedVolumes {
_strings: Vec::new(),
_wide_paths: Vec::new(),
_volumes: Vec::new(),
});
}
let mut volumes = Vec::with_capacity(builder.volumes.len());
let mut strings = Vec::with_capacity(builder.volumes.len());
let mut wide_paths = Vec::with_capacity(builder.volumes.len());
for (w, c, _ro) in &builder.volumes {
if w.as_os_str().is_empty() {
return Err(WslcError::InvalidConfiguration(
"宿主机挂载路径不能为空".to_string(),
));
}
if !c.starts_with('/') {
return Err(WslcError::InvalidConfiguration(format!(
"容器挂载路径必须是绝对路径(以 '/' 开头),实际为: {c}"
)));
}
let wide = crate::session::path_to_wide_null(w)?;
let c_str = CString::new(c.as_str())
.map_err(|e| WslcError::NulError(format!("挂载路径非法: {e}")))?;
wide_paths.push(wide);
strings.push(c_str);
}
for i in 0..builder.volumes.len() {
volumes.push(WslcContainerVolume {
windows_path: wide_paths[i].as_ptr(),
container_path: strings[i].as_ptr(),
read_only: if builder.volumes[i].2 { 1 } else { 0 },
});
}
let hr = unsafe {
WslcSetContainerSettingsVolumes(settings, volumes.as_ptr(), volumes.len() as u32)
};
WslcError::check_hr(hr, "设置容器目录卷挂载失败")?;
Ok(RetainedVolumes {
_strings: strings,
_wide_paths: wide_paths,
_volumes: volumes,
})
}
pub(super) fn apply_named_volumes(
builder: &ContainerBuilder,
settings: &mut WslcContainerSettings,
) -> Result<RetainedNamedVolumes, WslcError> {
if builder.named_volumes.is_empty() {
return Ok(RetainedNamedVolumes {
_strings: Vec::new(),
_volumes: Vec::new(),
});
}
let mut volumes = Vec::with_capacity(builder.named_volumes.len());
let mut strings = Vec::with_capacity(builder.named_volumes.len());
for (n, c, ro) in &builder.named_volumes {
if n.trim().is_empty() {
return Err(WslcError::InvalidConfiguration(
"具名卷名称不能为空".to_string(),
));
}
if !c.starts_with('/') {
return Err(WslcError::InvalidConfiguration(format!(
"容器挂载路径必须是绝对路径(以 '/' 开头),实际为: {c}"
)));
}
let c_name = CString::new(n.as_str())
.map_err(|e| WslcError::NulError(format!("卷名称非法: {e}")))?;
let c_cont = CString::new(c.as_str())
.map_err(|e| WslcError::NulError(format!("容器路径非法: {e}")))?;
volumes.push(WslcContainerNamedVolume {
name: c_name.as_ptr(),
container_path: c_cont.as_ptr(),
read_only: if *ro { 1 } else { 0 },
});
strings.push((c_name, c_cont));
}
let hr = unsafe {
WslcSetContainerSettingsNamedVolumes(settings, volumes.as_ptr(), volumes.len() as u32)
};
WslcError::check_hr(hr, "设置容器具名 VHD 卷挂载失败")?;
Ok(RetainedNamedVolumes {
_strings: strings,
_volumes: volumes,
})
}