Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add from_path() to Target #122

Merged
merged 1 commit into from
May 19, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions tough/src/schema/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
use crate::schema::RoleType;
use snafu::{Backtrace, Snafu};
use std::fmt::{self, Debug, Display};
use std::path::PathBuf;

/// Alias for `Result<T, Error>`.
pub type Result<T> = std::result::Result<T, Error>;
Expand All @@ -17,6 +18,22 @@ pub enum Error {
#[snafu(display("Duplicate key ID: {}", keyid))]
DuplicateKeyId { keyid: String },

/// Unable to open a file
#[snafu(display("Failed to open '{}': {}", path.display(), source))]
FileOpen {
path: PathBuf,
source: std::io::Error,
backtrace: Backtrace,
},

/// Unable to read the file
#[snafu(display("Failed to read '{}': {}", path.display(), source))]
FileRead {
path: PathBuf,
source: std::io::Error,
backtrace: Backtrace,
},

/// A downloaded target's checksum does not match the checksum listed in the repository
/// metadata.
#[snafu(display("Invalid key ID {}: calculated {}", keyid, calculated))]
Expand Down Expand Up @@ -72,6 +89,10 @@ pub enum Error {
/// Failed to extract a bit string from a `SubjectPublicKeyInfo` document.
#[snafu(display("Invalid SubjectPublicKeyInfo document"))]
SpkiDecode { backtrace: Backtrace },

/// Unable to create a TUF target from anything but a file
#[snafu(display("TUF targets must be files, given: '{}'", path.display()))]
TargetNotAFile { path: PathBuf, backtrace: Backtrace },
}

/// Wrapper for error types that don't impl [`std::error::Error`].
Expand Down
43 changes: 43 additions & 0 deletions tough/src/schema/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,16 @@ use crate::schema::iter::KeysIter;
use crate::schema::key::Key;
use chrono::{DateTime, Utc};
use olpc_cjson::CanonicalFormatter;
use ring::digest::{Context, SHA256};
use serde::{Deserialize, Serialize};
use serde_json::Value;
use serde_plain::{forward_display_to_serde, forward_from_str_to_serde};
use snafu::ResultExt;
use std::collections::HashMap;
use std::fs::File;
use std::io::Read;
use std::num::NonZeroU64;
use std::path::Path;

/// A role type.
#[derive(Debug, Clone, Copy, Deserialize, Serialize, PartialEq, Eq, Hash)]
Expand Down Expand Up @@ -224,6 +228,45 @@ pub struct Target {
pub _extra: HashMap<String, Value>,
}

impl Target {
/// Given a path, returns a Target struct
pub fn from_path<P>(path: P) -> Result<Target>
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I should mention that I initially tried to implement TryFrom<AsRef<Path>> and ran into this Rust issue. After that, I didn't think this was worth it to implement TryFrom with String, Path, PathBuf, etc.

where
P: AsRef<Path>,
{
// Ensure the given path is a file
let path = path.as_ref();
if !path.is_file() {
return error::TargetNotAFile { path }.fail();
}

// Get the sha256 and length of the target
let mut file = File::open(path).context(error::FileOpen { path })?;
let mut digest = Context::new(&SHA256);
let mut buf = [0; 8 * 1024];
let mut length = 0;
loop {
match file.read(&mut buf).context(error::FileRead { path })? {
0 => break,
n => {
digest.update(&buf[..n]);
length += n as u64;
}
}
}

Ok(Target {
length,
hashes: Hashes {
sha256: Decoded::from(digest.finish().as_ref().to_vec()),
_extra: HashMap::new(),
},
custom: HashMap::new(),
_extra: HashMap::new(),
})
}
}

impl Role for Targets {
const TYPE: RoleType = RoleType::Targets;

Expand Down