forked from brl/citadel-tools
citadel-update: copy image files to tmp directory before updating
This commit is contained in:
parent
12eed4d557
commit
668227af1e
@ -3,7 +3,9 @@ use std::path::{Path, PathBuf};
|
|||||||
use libcitadel::{Result, Partition, ResourceImage, ImageHeader, LogLevel, Logger, util};
|
use libcitadel::{Result, Partition, ResourceImage, ImageHeader, LogLevel, Logger, util};
|
||||||
use crate::update::kernel::{KernelInstaller, KernelVersion};
|
use crate::update::kernel::{KernelInstaller, KernelVersion};
|
||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
use std::fs::DirEntry;
|
use std::fs::{DirEntry, File};
|
||||||
|
use std::io;
|
||||||
|
use tempfile::Builder;
|
||||||
|
|
||||||
mod kernel;
|
mod kernel;
|
||||||
|
|
||||||
@ -11,6 +13,9 @@ const FLAG_SKIP_SHA: u32 = 0x01;
|
|||||||
const FLAG_NO_PREFER: u32 = 0x02;
|
const FLAG_NO_PREFER: u32 = 0x02;
|
||||||
const FLAG_QUIET: u32 = 0x04;
|
const FLAG_QUIET: u32 = 0x04;
|
||||||
|
|
||||||
|
const RESOURCES_DIRECTORY: &str = "/storage/resources";
|
||||||
|
const TEMP_DIRECTORY: &str = "/storage/resources/tmp";
|
||||||
|
|
||||||
pub fn main(args: Vec<String>) {
|
pub fn main(args: Vec<String>) {
|
||||||
let mut args = args.iter().skip(1);
|
let mut args = args.iter().skip(1);
|
||||||
let mut flags = 0;
|
let mut flags = 0;
|
||||||
@ -42,14 +47,14 @@ pub fn main(args: Vec<String>) {
|
|||||||
// Search directory containing installed image files for an
|
// Search directory containing installed image files for an
|
||||||
// image file that has an identical shasum and abort the installation
|
// image file that has an identical shasum and abort the installation
|
||||||
// if a duplicate is found.
|
// if a duplicate is found.
|
||||||
fn detect_duplicates(image: &ResourceImage) -> Result<()> {
|
fn detect_duplicates(header: &ImageHeader) -> Result<()> {
|
||||||
let metainfo = image.metainfo();
|
let metainfo = header.metainfo();
|
||||||
let channel = metainfo.channel();
|
let channel = metainfo.channel();
|
||||||
let shasum = metainfo.shasum();
|
let shasum = metainfo.shasum();
|
||||||
|
|
||||||
validate_channel_name(&channel)?;
|
validate_channel_name(&channel)?;
|
||||||
|
|
||||||
let resource_dir = Path::new("/storage/resources/")
|
let resource_dir = Path::new(RESOURCES_DIRECTORY)
|
||||||
.join(channel);
|
.join(channel);
|
||||||
|
|
||||||
if !resource_dir.exists() {
|
if !resource_dir.exists() {
|
||||||
@ -57,23 +62,52 @@ fn detect_duplicates(image: &ResourceImage) -> Result<()> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
util::read_directory(&resource_dir, |dent| {
|
util::read_directory(&resource_dir, |dent| {
|
||||||
match ResourceImage::from_path(dent.path()) {
|
if let Ok(hdr) = ImageHeader::from_file(dent.path()) {
|
||||||
Ok(img) => if img.metainfo().shasum() == shasum {
|
if hdr.metainfo().shasum() == shasum {
|
||||||
bail!("A duplicate image file with the same shasum already exists at {}", img.path().display());
|
bail!("A duplicate image file with the same shasum already exists at {}", dent.path().display());
|
||||||
},
|
}
|
||||||
Err(err) => warn!("{}", err),
|
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn install_image(path: &Path, flags: u32) -> Result<()> {
|
fn create_tmp_copy(path: &Path) -> Result<PathBuf> {
|
||||||
if !path.exists() {
|
if !Path::new(TEMP_DIRECTORY).exists() {
|
||||||
bail!("file path {} does not exist", path.display());
|
util::create_dir(TEMP_DIRECTORY)?;
|
||||||
|
}
|
||||||
|
let mut tmpfile = Builder::new()
|
||||||
|
.prefix("update-")
|
||||||
|
.suffix(".img")
|
||||||
|
.tempfile_in(TEMP_DIRECTORY)
|
||||||
|
.map_err(context!("Failed to open temporary file in {}", TEMP_DIRECTORY))?;
|
||||||
|
|
||||||
|
let mut src = File::open(path)
|
||||||
|
.map_err(context!("Failed to open image file {}", path.display()))?;
|
||||||
|
|
||||||
|
info!("Copying image to temporary file {}", tmpfile.path().display());
|
||||||
|
io::copy(&mut src, tmpfile.as_file_mut())
|
||||||
|
.map_err(context!("Failed copying image file to temporary file"))?;
|
||||||
|
|
||||||
|
let (_,path) = tmpfile
|
||||||
|
.keep().map_err(context!("Failed to persist temporary file"))?;
|
||||||
|
Ok(path)
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut image = ResourceImage::from_path(path)?;
|
fn install_image(path: &Path, flags: u32) -> Result<()> {
|
||||||
detect_duplicates(&image)?;
|
if !path.exists() || path.file_name().is_none() {
|
||||||
|
bail!("file path {} does not exist", path.display());
|
||||||
|
}
|
||||||
|
if !util::is_euid_root() {
|
||||||
|
bail!("Image updates must be installed by root user");
|
||||||
|
}
|
||||||
|
|
||||||
|
let header = ImageHeader::from_file(path)?;
|
||||||
|
detect_duplicates(&header)?;
|
||||||
|
|
||||||
|
let tmpfile = create_tmp_copy(path)?;
|
||||||
|
|
||||||
|
let mut image = ResourceImage::from_header(header, tmpfile)?;
|
||||||
|
|
||||||
prepare_image(&image, flags)?;
|
prepare_image(&image, flags)?;
|
||||||
|
|
||||||
match image.metainfo().image_type() {
|
match image.metainfo().image_type() {
|
||||||
@ -277,6 +311,7 @@ fn install_rootfs_image(image: &ResourceImage, flags: u32) -> Result<()> {
|
|||||||
|
|
||||||
image.write_to_partition(&partition)?;
|
image.write_to_partition(&partition)?;
|
||||||
info!("Image written to {:?}", partition.path());
|
info!("Image written to {:?}", partition.path());
|
||||||
|
util::remove_file(image.path())?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
use std::fs::{File,DirEntry};
|
use std::fs::{File,DirEntry};
|
||||||
use std::ffi::OsStr;
|
|
||||||
use std::io::{self, Seek, SeekFrom};
|
use std::io::{self, Seek, SeekFrom};
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
|
|
||||||
@ -73,14 +72,18 @@ impl ResourceImage {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn from_path<P: AsRef<Path>>(path: P) -> Result<Self> {
|
pub fn from_header<P: AsRef<Path>>(header: ImageHeader, path: P) -> Result<Self> {
|
||||||
let header = ImageHeader::from_file(path.as_ref())?;
|
|
||||||
if !header.is_magic_valid() {
|
if !header.is_magic_valid() {
|
||||||
bail!("image file {:?} does not have a valid header", path.as_ref());
|
bail!("image file {} does not have a valid header", path.as_ref().display());
|
||||||
}
|
}
|
||||||
Ok(Self::new(path.as_ref(), header))
|
Ok(Self::new(path.as_ref(), header))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn from_path<P: AsRef<Path>>(path: P) -> Result<Self> {
|
||||||
|
let header = ImageHeader::from_file(path.as_ref())?;
|
||||||
|
Self::from_header(header, path)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn is_valid_image(&self) -> bool {
|
pub fn is_valid_image(&self) -> bool {
|
||||||
self.header.is_magic_valid()
|
self.header.is_magic_valid()
|
||||||
}
|
}
|
||||||
@ -103,8 +106,6 @@ impl ResourceImage {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn new(path: &Path, header: ImageHeader) -> Self {
|
fn new(path: &Path, header: ImageHeader) -> Self {
|
||||||
assert_eq!(path.extension(), Some(OsStr::new("img")), "image filename must have .img extension");
|
|
||||||
|
|
||||||
ResourceImage {
|
ResourceImage {
|
||||||
path: path.to_owned(),
|
path: path.to_owned(),
|
||||||
header,
|
header,
|
||||||
@ -485,15 +486,19 @@ fn maybe_add_dir_entry(entry: &DirEntry,
|
|||||||
images: &mut Vec<ResourceImage>) -> Result<()> {
|
images: &mut Vec<ResourceImage>) -> Result<()> {
|
||||||
|
|
||||||
let path = entry.path();
|
let path = entry.path();
|
||||||
if Some(OsStr::new("img")) != path.extension() {
|
|
||||||
return Ok(())
|
|
||||||
}
|
|
||||||
let meta = entry.metadata()
|
let meta = entry.metadata()
|
||||||
.map_err(context!("failed to read metadata for {:?}", entry.path()))?;
|
.map_err(context!("failed to read metadata for {:?}", entry.path()))?;
|
||||||
if meta.len() < ImageHeader::HEADER_SIZE as u64 {
|
if !meta.is_file() || meta.len() < ImageHeader::HEADER_SIZE as u64 {
|
||||||
return Ok(())
|
return Ok(())
|
||||||
}
|
}
|
||||||
let header = ImageHeader::from_file(&path)?;
|
let header = match ImageHeader::from_file(&path) {
|
||||||
|
Ok(header) => header,
|
||||||
|
Err(err) => {
|
||||||
|
warn!("Unable to read image header from directory entry {}: {}", path.display(), err);
|
||||||
|
return Ok(())
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
if !header.is_magic_valid() {
|
if !header.is_magic_valid() {
|
||||||
return Ok(())
|
return Ok(())
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user