Commit e689baba authored by SamwiseFilmore's avatar SamwiseFilmore
Browse files

Add a header() function to PackageSrc; Standardize func names

I'm not super happy with this design because it makes for a reasonable
amount of ugly boilerplate for implementors of PackageSrc. Works though,
and it's faster.

TODO: Implement new type for buffers of u8s instead of impl PackageSrc
for AsRef<[u8]>
parent 48dac371
//! The packed structs represent the on-disk format of pkgar
use core::convert::TryFrom;
use core::fmt;
use core::mem;
use plain::Plain;
use sodiumoxide::crypto::sign::{self, PublicKey};
......@@ -51,6 +52,10 @@ impl Header {
Ok(plain::from_bytes(data)?)
}
pub fn count(&self) -> u64 {
self.count
}
/// Retrieve the size of the entries
pub fn entries_size(&self) -> Result<u64, Error> {
let entry_size = u64::try_from(mem::size_of::<Entry>())?;
......@@ -94,3 +99,14 @@ impl Header {
}
}
impl fmt::Debug for Header {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "Header {{\n\tsignature: {:?},\n\tpublic_key: {:?},\n\tblake3: {:?},count: {:?},\n}}",
&self.signature[..],
self.public_key,
self.blake3,
self.count(),
)
}
}
use alloc::vec;
use alloc::vec::Vec;
use core::convert::{AsRef, TryFrom};
......@@ -10,6 +11,31 @@ pub trait PackageSrc {
fn read_at(&mut self, offset: u64, buf: &mut [u8]) -> Result<usize, Self::Err>;
fn header(&self) -> Header;
/// Users of implementors of `PackageSrc` should use `header` instead of `read_header` for
/// cheap header access.
/// Implementors of `PackageSrc` should call this function during initialization and store
/// the result to pass out with `header`.
fn read_header(&mut self, public_key: &PublicKey) -> Result<Header, Self::Err> {
let mut header_data = [0; HEADER_SIZE];
self.read_at(0, &mut header_data)?;
let header = Header::new(&header_data, &public_key)?;
Ok(header.clone())
}
fn read_entries(&mut self) -> Result<Vec<Entry>, Self::Err> {
let header = self.header();
let entries_size = header.entries_size()
.and_then(|rslt| usize::try_from(rslt)
.map_err(Error::TryFromInt)
)?;
let mut entries_data = vec![0; entries_size];
self.read_at(HEADER_SIZE as u64, &mut entries_data)?;
let entries = header.entries(&entries_data)?;
Ok(entries.to_vec())
}
/// Read from this src at a given entry's data with a given offset within that entry
fn read_entry(&mut self, entry: Entry, offset: u64, buf: &mut [u8]) -> Result<usize, Self::Err> {
if offset > entry.size {
......@@ -23,46 +49,22 @@ pub trait PackageSrc {
end = buf.len();
}
let header = self.header_unchecked()?;
let offset =
HEADER_SIZE as u64 +
header.entries_size()? +
self.header().entries_size()? +
entry.offset + offset;
self.read_at(offset, &mut buf[..end])
}
fn header(&mut self, public_key: &PublicKey) -> Result<Header, Self::Err> {
let mut header_data = [0; HEADER_SIZE];
self.read_at(0, &mut header_data)?;
let header = Header::new(&header_data, &public_key)?;
Ok(header.clone())
}
fn header_unchecked(&mut self) -> Result<Header, Self::Err> {
let mut header = [0; HEADER_SIZE];
self.read_at(0, &mut header)?;
Ok(unsafe { Header::new_unchecked(&header) }?
.clone())
}
fn entries(&mut self, public_key: &PublicKey) -> Result<Vec<Entry>, Self::Err> {
let header = self.header(public_key)?;
let entries_size = header.entries_size()
.and_then(|rslt| usize::try_from(rslt)
.map_err(Error::TryFromInt)
)?;
let mut entries_data = vec![0; entries_size];
self.read_at(HEADER_SIZE as u64, &mut entries_data)?;
let entries = header.entries(&entries_data)?;
Ok(entries.to_vec())
}
}
impl<T: AsRef<[u8]>> PackageSrc for T {
type Err = Error;
fn header(&self) -> Header {
panic!("Temporary...");
}
fn read_at(&mut self, offset: u64, buf: &mut [u8]) -> Result<usize, Error> {
let start = usize::try_from(offset)
.map_err(Error::TryFromInt)?;
......
......@@ -221,8 +221,8 @@ pub fn create(secret_path: &str, archive_path: &str, folder: &str) -> Result<(),
pub fn extract(public_path: &str, archive_path: &str, folder: &str) -> Result<(), Error> {
let public_key = PublicKeyFile::open(&public_path.as_ref())?.pkey;
let mut package = PackageFile::new(archive_path)?;
let entries = package.entries(&public_key)?;
let mut package = PackageFile::new(archive_path, &public_key)?;
let entries = package.read_entries()?;
// TODO: Validate that all entries can be installed, before installing
......@@ -299,7 +299,7 @@ pub fn extract(public_path: &str, archive_path: &str, folder: &str) -> Result<()
if total != entry.size {
return Err(Error::Io(io::Error::new(
io::ErrorKind::InvalidInput,
format!("Copied {} instead of {}", total, entry.size)
format!("Copied {} instead of {}", total, entry.size())
)));
}
if entry_hash != hash {
......@@ -321,8 +321,8 @@ pub fn list(public_path: &str, archive_path: &str) -> Result<(), Error> {
let public_key = PublicKeyFile::open(&public_path.as_ref())?.pkey;
// Read header first
let mut package = PackageFile::new(archive_path)?;
let entries = package.entries(&public_key)?;
let mut package = PackageFile::new(archive_path, &public_key)?;
let entries = package.read_entries()?;
for entry in entries {
let relative = Path::new(OsStr::from_bytes(entry.path()));
println!("{}", relative.display());
......
......@@ -2,28 +2,39 @@ use std::fs::{File, OpenOptions};
use std::io::{Read, Seek, SeekFrom};
use std::path::Path;
use pkgar_core::PackageSrc;
use sodiumoxide::crypto::sign::PublicKey;
use pkgar_core::{Header, HEADER_SIZE, PackageSrc};
use crate::Error;
#[derive(Debug)]
pub struct PackageFile {
pub(crate) src: File,
header: Header,
}
impl PackageFile {
pub fn new(path: impl AsRef<Path>) -> Result<PackageFile, Error> {
Ok(PackageFile {
pub fn new(path: impl AsRef<Path>, public_key: &PublicKey) -> Result<PackageFile, Error> {
let zeroes = [0; HEADER_SIZE];
let mut new = PackageFile {
src: OpenOptions::new()
.read(true)
.open(path)?,
})
header: unsafe { *Header::new_unchecked(&zeroes)? },
};
new.header = new.read_header(public_key)?;
Ok(new)
}
}
impl PackageSrc for PackageFile {
type Err = Error;
fn header(&self) -> Header {
self.header
}
fn read_at(&mut self, offset: u64, buf: &mut [u8]) -> Result<usize, Self::Err> {
self.src.seek(SeekFrom::Start(offset))?;
Ok(self.src.read(buf)?)
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment