As the initial use case for the first boot feature request [0] was for running shell scripts, the auto installer retrieved the binary as a `String`. Unfortunately, this tries to interpret binary data as UTF-8 and will transform 'invalid' characters to replacement characters [1].
This causes the auto-installer to create an invalid binary when fetching from an URL, and error with a `stream did not contain valid UTF-8` error when fetching from the ISO image. To allow binary executables to be used as a first boot executable, this commit changes the fetching from being read as a `String` to being read as a `Vec<u8>` to not interfere with the content of the executable. [0] https://bugzilla.proxmox.com/show_bug.cgi?id=5579 [1] https://doc.rust-lang.org/src/alloc/string.rs.html#635 Signed-off-by: Daniel Kral <d.k...@proxmox.com> --- changes from v1: - remove explicit handling of `std::io::ErrorKind::UnexpectedEof` .../src/bin/proxmox-auto-installer.rs | 12 ++++++++---- proxmox-installer-common/src/http.rs | 18 ++++++++++++++---- 2 files changed, 22 insertions(+), 8 deletions(-) diff --git a/proxmox-auto-installer/src/bin/proxmox-auto-installer.rs b/proxmox-auto-installer/src/bin/proxmox-auto-installer.rs index ece9a94..408fc0e 100644 --- a/proxmox-auto-installer/src/bin/proxmox-auto-installer.rs +++ b/proxmox-auto-installer/src/bin/proxmox-auto-installer.rs @@ -38,14 +38,18 @@ fn setup_first_boot_executable(first_boot: &FirstBootHookInfo) -> Result<()> { FirstBootHookSourceMode::FromUrl => { if let Some(url) = &first_boot.url { info!("Fetching first-boot hook from {url} .."); - Some(http::get(url, first_boot.cert_fingerprint.as_deref())?) + Some(http::get_as_bytes( + url, + first_boot.cert_fingerprint.as_deref(), + FIRST_BOOT_EXEC_MAX_SIZE, + )?) } else { bail!("first-boot hook source set to URL, but none specified!"); } } - FirstBootHookSourceMode::FromIso => Some(fs::read_to_string(format!( - "/cdrom/{FIRST_BOOT_EXEC_NAME}" - ))?), + FirstBootHookSourceMode::FromIso => { + Some(fs::read(format!("/cdrom/{FIRST_BOOT_EXEC_NAME}"))?) + } }; if let Some(content) = content { diff --git a/proxmox-installer-common/src/http.rs b/proxmox-installer-common/src/http.rs index f4afe14..a520c40 100644 --- a/proxmox-installer-common/src/http.rs +++ b/proxmox-installer-common/src/http.rs @@ -1,6 +1,7 @@ use anyhow::Result; use rustls::ClientConfig; use sha2::{Digest, Sha256}; +use std::io::Read; use std::sync::Arc; use ureq::{Agent, AgentBuilder}; @@ -53,12 +54,21 @@ fn build_agent(fingerprint: Option<&str>) -> Result<Agent> { /// # Arguments /// * `url` - URL to fetch /// * `fingerprint` - SHA256 cert fingerprint if certificate pinning should be used. Optional. -pub fn get(url: &str, fingerprint: Option<&str>) -> Result<String> { - Ok(build_agent(fingerprint)? +/// * `max_size` - Maximum amount of bytes that will be read. +pub fn get_as_bytes(url: &str, fingerprint: Option<&str>, max_size: usize) -> Result<Vec<u8>> { + let mut result: Vec<u8> = Vec::new(); + + let response = build_agent(fingerprint)? .get(url) .timeout(std::time::Duration::from_secs(60)) - .call()? - .into_string()?) + .call()?; + + response + .into_reader() + .take(max_size as u64) + .read_to_end(&mut result)?; + + Ok(result) } /// Issues a POST request with the payload (JSON). Optionally a SHA256 fingerprint can be used to -- 2.39.5 _______________________________________________ pve-devel mailing list pve-devel@lists.proxmox.com https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel