update fds-flash tool
This commit is contained in:
@@ -0,0 +1,103 @@
|
||||
//! Guided and unattended installation of complete disk images on Linux.
|
||||
pub mod cli;
|
||||
pub mod device;
|
||||
mod image;
|
||||
|
||||
use fds_common::{Error, Result};
|
||||
use serde::Serialize;
|
||||
use std::{
|
||||
fs::File,
|
||||
os::{fd::AsRawFd, unix::fs::MetadataExt},
|
||||
path::{Path, PathBuf},
|
||||
};
|
||||
|
||||
#[derive(Serialize)]
|
||||
pub struct Plan {
|
||||
pub image_path: PathBuf,
|
||||
pub image: image::Geometry,
|
||||
pub sha256: String,
|
||||
pub target: device::Target,
|
||||
pub target_id: String,
|
||||
pub confirmation: String,
|
||||
}
|
||||
pub struct Prepared {
|
||||
pub plan: Plan,
|
||||
source: File,
|
||||
}
|
||||
|
||||
pub fn prepare(
|
||||
image_path: &Path,
|
||||
device_path: &Path,
|
||||
file_target: bool,
|
||||
expected_sha256: Option<&str>,
|
||||
) -> Result<Prepared> {
|
||||
let image_path = image_path.canonicalize()?;
|
||||
let source = crate::cartridge::open_image(&image_path)?;
|
||||
let geometry = image::inspect(&source, source.metadata()?.len())?;
|
||||
let sha256 = fds_burn::image::digest(&source, geometry.bytes, |_| Ok(()))?;
|
||||
if expected_sha256.is_some_and(|expected| expected != sha256) {
|
||||
return Err(Error(
|
||||
"Image SHA-256 does not match --sha256; target untouched".into(),
|
||||
));
|
||||
}
|
||||
if image::inspect(&source, source.metadata()?.len())? != geometry {
|
||||
return Err(Error("Image geometry changed during inspection".into()));
|
||||
}
|
||||
let target = device::inspect(device_path, file_target)?;
|
||||
let metadata = std::fs::metadata(target.path())?;
|
||||
if source.metadata()?.dev() == metadata.dev() && source.metadata()?.ino() == metadata.ino() {
|
||||
return Err(Error("Image and target refer to the same file".into()));
|
||||
}
|
||||
target.protect()?;
|
||||
if target.bytes() < geometry.bytes || target.bytes() % 512 != 0 {
|
||||
return Err(Error(
|
||||
"Destination is smaller than the image or is not sector aligned".into(),
|
||||
));
|
||||
}
|
||||
let target_id = target.id()?;
|
||||
let confirmation = format!("ERASE {}", target.path().display());
|
||||
Ok(Prepared {
|
||||
plan: Plan {
|
||||
image_path,
|
||||
image: geometry,
|
||||
sha256,
|
||||
target,
|
||||
target_id,
|
||||
confirmation,
|
||||
},
|
||||
source,
|
||||
})
|
||||
}
|
||||
|
||||
impl Prepared {
|
||||
pub fn write(&self) -> Result<()> {
|
||||
let target = self.plan.target.open()?;
|
||||
let source_meta = self.source.metadata()?;
|
||||
let target_meta = target.metadata()?;
|
||||
if source_meta.dev() == target_meta.dev() && source_meta.ino() == target_meta.ino() {
|
||||
return Err(Error("Image and destination are the same open file".into()));
|
||||
}
|
||||
image::transfer(
|
||||
&self.source,
|
||||
&target,
|
||||
&self.plan.image,
|
||||
&self.plan.sha256,
|
||||
self.plan.target.bytes(),
|
||||
|phase, bytes| {
|
||||
self.plan.target.present()?;
|
||||
eprintln!("{phase}: {bytes}/{} bytes", self.plan.image.bytes);
|
||||
Ok(())
|
||||
},
|
||||
)?;
|
||||
self.plan.target.present()?;
|
||||
if !self.plan.target.is_file()
|
||||
&& unsafe { libc::ioctl(target.as_raw_fd(), 0x125f as libc::Ioctl) } < 0
|
||||
{
|
||||
return Err(Error(format!(
|
||||
"Image verified, but the kernel could not reload the partition table: {}. Do not use the disk until it is reconnected and inspected",
|
||||
std::io::Error::last_os_error()
|
||||
)));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user