Name update
This commit is contained in:
+80
-17
@@ -25,11 +25,11 @@
|
||||
//! Samba), we return `SmbState::SmbdMissing` and the UI surfaces the
|
||||
//! gap. No panics, no retries, no silent failure.
|
||||
|
||||
use parking_lot::Mutex;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::process::{Child, Command, Stdio};
|
||||
use std::sync::Arc;
|
||||
use parking_lot::Mutex;
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
#[serde(rename_all = "snake_case", tag = "state")]
|
||||
@@ -72,7 +72,9 @@ impl SmbManager {
|
||||
/// ISO under `smb_dir/<slug>/` becomes a share named `<slug>`. Returns
|
||||
/// the sorted list.
|
||||
pub fn discover_shares(&self) -> Vec<String> {
|
||||
let Ok(rd) = std::fs::read_dir(&self.smb_dir) else { return vec![]; };
|
||||
let Ok(rd) = std::fs::read_dir(&self.smb_dir) else {
|
||||
return vec![];
|
||||
};
|
||||
let mut out: Vec<String> = rd
|
||||
.flatten()
|
||||
.filter(|e| e.path().is_dir())
|
||||
@@ -130,7 +132,9 @@ impl SmbManager {
|
||||
let shares = match self.write_conf() {
|
||||
Ok(v) => v,
|
||||
Err(e) => {
|
||||
let s = SmbState::Failed { reason: format!("write smb.conf: {e}") };
|
||||
let s = SmbState::Failed {
|
||||
reason: format!("write smb.conf: {e}"),
|
||||
};
|
||||
*self.state.lock() = s.clone();
|
||||
return s;
|
||||
}
|
||||
@@ -139,7 +143,8 @@ impl SmbManager {
|
||||
.args([
|
||||
"--foreground",
|
||||
"--no-process-group",
|
||||
"--configfile", self.conf_path.to_str().unwrap_or(""),
|
||||
"--configfile",
|
||||
self.conf_path.to_str().unwrap_or(""),
|
||||
"--log-stdout",
|
||||
])
|
||||
.stdin(Stdio::null())
|
||||
@@ -156,7 +161,9 @@ impl SmbManager {
|
||||
s
|
||||
}
|
||||
Err(e) => {
|
||||
let s = SmbState::Failed { reason: format!("spawn smbd: {e}") };
|
||||
let s = SmbState::Failed {
|
||||
reason: format!("spawn smbd: {e}"),
|
||||
};
|
||||
*self.state.lock() = s.clone();
|
||||
s
|
||||
}
|
||||
@@ -168,11 +175,15 @@ impl SmbManager {
|
||||
#[allow(unsafe_code)]
|
||||
pub fn reconcile(&self) -> SmbState {
|
||||
let mut g = self.child.lock();
|
||||
if g.is_none() { return self.state.lock().clone(); }
|
||||
if g.is_none() {
|
||||
return self.state.lock().clone();
|
||||
}
|
||||
let shares = match self.write_conf() {
|
||||
Ok(v) => v,
|
||||
Err(e) => {
|
||||
let s = SmbState::Failed { reason: format!("write smb.conf: {e}") };
|
||||
let s = SmbState::Failed {
|
||||
reason: format!("write smb.conf: {e}"),
|
||||
};
|
||||
*self.state.lock() = s.clone();
|
||||
return s;
|
||||
}
|
||||
@@ -191,8 +202,13 @@ impl SmbManager {
|
||||
// covers this is `nix`, which pulls ~40 transitive deps for a
|
||||
// single signal send. One documented unsafe call is the better
|
||||
// tradeoff for a container-first project.
|
||||
unsafe { libc::kill(pid, libc::SIGHUP); }
|
||||
let s = SmbState::Running { pid: pid as u32, shares };
|
||||
unsafe {
|
||||
libc::kill(pid, libc::SIGHUP);
|
||||
}
|
||||
let s = SmbState::Running {
|
||||
pid: pid as u32,
|
||||
shares,
|
||||
};
|
||||
*self.state.lock() = s.clone();
|
||||
s
|
||||
} else {
|
||||
@@ -212,15 +228,19 @@ impl SmbManager {
|
||||
}
|
||||
|
||||
fn smbd_present() -> bool {
|
||||
let Ok(paths) = std::env::var("PATH") else { return false; };
|
||||
let Ok(paths) = std::env::var("PATH") else {
|
||||
return false;
|
||||
};
|
||||
for dir in std::env::split_paths(&paths) {
|
||||
if dir.join("smbd").is_file() { return true; }
|
||||
if dir.join("smbd").is_file() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
const SMB_CONF_GLOBAL: &str = r"[global]
|
||||
workgroup = PXEFORGE
|
||||
workgroup = OPENPXE
|
||||
server min protocol = SMB2
|
||||
smb ports = 445
|
||||
log level = 1
|
||||
@@ -235,6 +255,15 @@ lock directory = /tmp
|
||||
state directory = /tmp
|
||||
cache directory = /tmp
|
||||
pid directory = /tmp
|
||||
# WinPE reconnect hardening. Windows Setup can reboot mid-install and
|
||||
# reconnect from the same IP; stale sessions/oplocks otherwise cause
|
||||
# intermittent `net use` failures on the second stage.
|
||||
reset on zero vc = yes
|
||||
oplocks = no
|
||||
kernel oplocks = no
|
||||
level2 oplocks = no
|
||||
strict locking = no
|
||||
deadtime = 1
|
||||
";
|
||||
|
||||
/// Extract a Windows ISO at `iso_path` into `smb_dir/<slug>/`. Uses
|
||||
@@ -245,7 +274,11 @@ pid directory = /tmp
|
||||
/// Idempotent: if the target dir already contains `sources/boot.wim`, we
|
||||
/// skip extraction. Callers who want a forced re-extract should remove the
|
||||
/// dir first.
|
||||
pub fn extract_windows_iso(iso_path: &Path, smb_dir: &Path, slug: &str) -> std::io::Result<PathBuf> {
|
||||
pub fn extract_windows_iso(
|
||||
iso_path: &Path,
|
||||
smb_dir: &Path,
|
||||
slug: &str,
|
||||
) -> std::io::Result<PathBuf> {
|
||||
let target = smb_dir.join(slug);
|
||||
if target.join("sources").join("boot.wim").is_file() {
|
||||
tracing::debug!(target: "openpxe::smb", slug, "ISO already extracted, skipping");
|
||||
@@ -263,7 +296,9 @@ pub fn extract_windows_iso(iso_path: &Path, smb_dir: &Path, slug: &str) -> std::
|
||||
.stdout(Stdio::null())
|
||||
.stderr(Stdio::piped())
|
||||
.output()?;
|
||||
if out.status.success() { return Ok(target); }
|
||||
if out.status.success() {
|
||||
return Ok(target);
|
||||
}
|
||||
tracing::warn!(
|
||||
target: "openpxe::smb",
|
||||
stderr=%String::from_utf8_lossy(&out.stderr),
|
||||
@@ -278,7 +313,9 @@ pub fn extract_windows_iso(iso_path: &Path, smb_dir: &Path, slug: &str) -> std::
|
||||
.args(["-C"])
|
||||
.arg(&target)
|
||||
.output()?;
|
||||
if out.status.success() { return Ok(target); }
|
||||
if out.status.success() {
|
||||
return Ok(target);
|
||||
}
|
||||
return Err(std::io::Error::other(format!(
|
||||
"bsdtar failed: {}",
|
||||
String::from_utf8_lossy(&out.stderr)
|
||||
@@ -294,7 +331,9 @@ fn which(cmd: &str) -> Option<PathBuf> {
|
||||
let paths = std::env::var_os("PATH")?;
|
||||
for dir in std::env::split_paths(&paths) {
|
||||
let p = dir.join(cmd);
|
||||
if p.is_file() { return Some(p); }
|
||||
if p.is_file() {
|
||||
return Some(p);
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
@@ -320,7 +359,9 @@ mod tests {
|
||||
let m = SmbManager::new(dir.path().into());
|
||||
let st = m.start();
|
||||
// Restore PATH before asserting so any subsequent failure is legible.
|
||||
if let Some(p) = saved { std::env::set_var("PATH", p); }
|
||||
if let Some(p) = saved {
|
||||
std::env::set_var("PATH", p);
|
||||
}
|
||||
assert_eq!(st, SmbState::SmbdMissing);
|
||||
}
|
||||
|
||||
@@ -347,5 +388,27 @@ mod tests {
|
||||
assert!(conf.contains("guest ok = yes"));
|
||||
assert!(conf.contains("read only = yes"));
|
||||
assert!(conf.contains("server min protocol = SMB2"));
|
||||
assert!(conf.contains("workgroup = OPENPXE"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn write_conf_includes_winpe_reconnect_tuning() {
|
||||
let dir = tempdir().unwrap();
|
||||
let m = SmbManager::new(dir.path().into());
|
||||
m.write_conf().unwrap();
|
||||
let conf = std::fs::read_to_string(dir.path().join("smb.conf")).unwrap();
|
||||
for expected in [
|
||||
"reset on zero vc = yes",
|
||||
"oplocks = no",
|
||||
"kernel oplocks = no",
|
||||
"level2 oplocks = no",
|
||||
"strict locking = no",
|
||||
"deadtime = 1",
|
||||
] {
|
||||
assert!(
|
||||
conf.contains(expected),
|
||||
"missing Windows reconnect Samba option {expected} in:\n{conf}"
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user