#![allow(clippy::default_trait_access)]
use crate::schema::RoleType;
use crate::{schema, TargetName, TransportError};
use chrono::{DateTime, Utc};
use snafu::{Backtrace, Snafu};
use std::io;
use std::path::PathBuf;
use url::Url;
pub type Result<T> = std::result::Result<T, Error>;
#[derive(Debug, Snafu)]
#[snafu(visibility(pub(crate)))]
#[non_exhaustive]
#[allow(missing_docs)]
pub enum Error {
#[snafu(display("Unable to canonicalize path '{}': {}", path.display(), source))]
AbsolutePath {
path: PathBuf,
source: std::io::Error,
backtrace: Backtrace,
},
#[snafu(display(
"Failed to create temp directory for the repository datastore: {}",
source
))]
DatastoreInit {
source: std::io::Error,
backtrace: Backtrace,
},
#[snafu(display("Failed to create file at datastore path {}: {}", path.display(), source))]
DatastoreCreate {
path: PathBuf,
source: std::io::Error,
backtrace: Backtrace,
},
#[snafu(display("Failed to open file from datastore path {}: {}", path.display(), source))]
DatastoreOpen {
path: PathBuf,
source: std::io::Error,
backtrace: Backtrace,
},
#[snafu(display("Failed to remove file at datastore path {}: {}", path.display(), source))]
DatastoreRemove {
path: PathBuf,
source: std::io::Error,
backtrace: Backtrace,
},
#[snafu(display("Failed to serialize {} to JSON at datastore path {}: {}", what, path.display(), source))]
DatastoreSerialize {
what: String,
path: PathBuf,
source: serde_json::Error,
backtrace: Backtrace,
},
#[snafu(display("Failed to create directory '{}': {}", path.display(), source))]
DirCreate {
path: PathBuf,
source: std::io::Error,
backtrace: Backtrace,
},
#[snafu(display("{} metadata is expired", role))]
ExpiredMetadata {
role: RoleType,
backtrace: Backtrace,
},
#[snafu(display("Failed to stat '{}': {}", path.display(), source))]
FileMetadata {
path: PathBuf,
source: std::io::Error,
backtrace: Backtrace,
},
#[snafu(display("Failed to open {}: {}", path.display(), source))]
FileOpen {
path: PathBuf,
source: std::io::Error,
backtrace: Backtrace,
},
#[snafu(display("Failed to read {}: {}", path.display(), source))]
FileRead {
path: PathBuf,
source: std::io::Error,
backtrace: Backtrace,
},
#[snafu(display("Failed to parse {}: {}", path.display(), source))]
FileParseJson {
path: PathBuf,
source: serde_json::Error,
backtrace: Backtrace,
},
#[snafu(display("Can't build URL from relative path '{}'", path.display()))]
FileUrl { path: PathBuf, backtrace: Backtrace },
#[snafu(display("Failed to write to {}: {}", path.display(), source))]
FileWrite {
path: PathBuf,
source: std::io::Error,
backtrace: Backtrace,
},
#[snafu(display(
"Hash mismatch for {}: calculated {}, expected {}",
context,
calculated,
expected,
))]
HashMismatch {
context: String,
calculated: String,
expected: String,
backtrace: Backtrace,
},
#[snafu(display("Source path for target must be file or symlink - '{}'", path.display()))]
InvalidFileType { path: PathBuf, backtrace: Backtrace },
#[snafu(display("Encountered an invalid target name: {}", inner))]
InvalidTargetName { inner: String, backtrace: Backtrace },
#[snafu(display("Failed to join \"{}\" to URL \"{}\": {}", path, url, source))]
JoinUrl {
path: String,
url: url::Url,
source: url::ParseError,
backtrace: Backtrace,
},
#[snafu(display(
"After encoding the name '{}' to '{}', failed to join '{}' to URL '{}': {}",
original,
encoded,
filename,
url,
source
))]
JoinUrlEncoded {
original: String,
encoded: String,
filename: String,
url: url::Url,
source: url::ParseError,
backtrace: Backtrace,
},
#[snafu(display("Unable to parse keypair: {}", source))]
KeyPairFromKeySource {
source: Box<dyn std::error::Error + Send + Sync + 'static>,
backtrace: Backtrace,
},
#[snafu(display("Private key rejected: {}", source))]
KeyRejected {
source: aws_lc_rs::error::KeyRejected,
backtrace: Backtrace,
},
#[snafu(display("Unable to match any of the provided keys with root.json"))]
KeysNotFoundInRoot { backtrace: Backtrace },
#[snafu(display("Unrecognized private key format"))]
KeyUnrecognized { backtrace: Backtrace },
#[snafu(display("Failed to create symlink at '{}': {}", path.display(), source))]
LinkCreate {
path: PathBuf,
source: io::Error,
backtrace: Backtrace,
},
#[snafu(display("Maximum size {} (specified by {}) exceeded", max_size, specifier))]
MaxSizeExceeded {
max_size: u64,
specifier: &'static str,
backtrace: Backtrace,
},
#[snafu(display("Maximum root updates {} exceeded", max_root_updates))]
MaxUpdatesExceeded {
max_root_updates: u64,
backtrace: Backtrace,
},
#[snafu(display("Meta for {:?} missing from {} metadata", file, role))]
MetaMissing {
file: &'static str,
role: RoleType,
backtrace: Backtrace,
},
#[snafu(display("Missing '{}' when building repo from RepositoryEditor", field))]
Missing { field: String, backtrace: Backtrace },
#[snafu(display("Unable to create NamedTempFile in directory '{}': {}", path.display(), source))]
NamedTempFileCreate {
path: PathBuf,
source: std::io::Error,
backtrace: Backtrace,
},
#[snafu(display("Unable to persist NamedTempFile to '{}': {}", path.display(), source))]
NamedTempFilePersist {
path: PathBuf,
source: tempfile::PersistError,
backtrace: Backtrace,
},
#[snafu(display("Unable to determine file name from path: '{}'", path.display()))]
NoFileName { path: PathBuf, backtrace: Backtrace },
#[snafu(display("Key for role '{}' doesn't exist in root.json", role))]
NoRoleKeysinRoot { role: String },
#[snafu(display(
"Found version {} of {} metadata when we had previously fetched version {}",
new_version,
role,
current_version
))]
OlderMetadata {
role: RoleType,
current_version: u64,
new_version: u64,
backtrace: Backtrace,
},
#[snafu(display(
"Timestamp version {} meta length {} is not exactly one",
version,
meta_length
))]
TimestampMetaLength { version: u64, meta_length: usize },
#[snafu(display("No snapshot meta in timestamp.json version {}", version))]
MissingSnapshotMeta { version: u64 },
#[snafu(display(
"Snapshot version {} in timestamp {} is less than {} in timestamp {}",
snapshot_new,
timestamp_new,
snapshot_old,
timestamp_old
))]
OlderSnapshotInTimestamp {
snapshot_new: u64,
timestamp_new: u64,
snapshot_old: u64,
timestamp_old: u64,
},
#[snafu(display("Snapshot version {} does not contain targets.json", version))]
SnapshotTargetsMetaMissing { version: u64 },
#[snafu(display(
"Role {} appears in snapshot version {} but not version {}",
role,
old_version,
new_version
))]
SnapshotRoleMissing {
role: String,
old_version: u64,
new_version: u64,
},
#[snafu(display(
"Role {} version {} in snapshot {} is greater than version {} in snapshot {}",
role,
old_role_version,
old_snapshot_version,
new_role_version,
new_snapshot_version
))]
SnapshotRoleRollback {
role: String,
old_role_version: u64,
old_snapshot_version: u64,
new_role_version: u64,
new_snapshot_version: u64,
},
#[snafu(display("Failed to parse {} metadata: {}", role, source))]
ParseMetadata {
role: RoleType,
source: serde_json::Error,
backtrace: Backtrace,
},
#[snafu(display("Failed to parse trusted root metadata: {}", source))]
ParseTrustedMetadata {
source: serde_json::Error,
backtrace: Backtrace,
},
#[snafu(display("Failed to parse URL {:?}: {}", url, source))]
ParseUrl {
url: String,
source: url::ParseError,
backtrace: Backtrace,
},
#[snafu(display("Target path exists, caller requested we fail - '{}'", path.display()))]
PathExistsFail { path: PathBuf, backtrace: Backtrace },
#[snafu(display("Requested copy/link of '{}' which is not a file", path.display()))]
PathIsNotFile { path: PathBuf, backtrace: Backtrace },
#[snafu(display("Requested copy/link of '{}' which is not a repo target", path.display()))]
PathIsNotTarget { path: PathBuf, backtrace: Backtrace },
#[snafu(display("Path {} is not valid UTF-8", path.display()))]
PathUtf8 { path: PathBuf, backtrace: Backtrace },
#[snafu(display("Path {} is not valid UTF-8", path.display()))]
UnixPathUtf8 {
path: typed_path::UnixPathBuf,
backtrace: Backtrace,
},
#[snafu(display("Failed to remove existing target path '{}': {}", path.display(), source))]
RemoveTarget {
path: PathBuf,
source: std::io::Error,
backtrace: Backtrace,
},
#[snafu(display("Unable to get info about the outdir '{}': {}", path.display(), source))]
SaveTargetDirInfo {
path: PathBuf,
source: std::io::Error,
backtrace: Backtrace,
},
#[snafu(display("The outdir '{}' either does not exist or is not a directory", path.display()))]
SaveTargetOutdir { path: PathBuf, backtrace: Backtrace },
#[snafu(display("Unable to canonicalize the outdir '{}': {}", path.display(), source))]
SaveTargetOutdirCanonicalize {
path: PathBuf,
source: std::io::Error,
backtrace: Backtrace,
},
#[snafu(display(
"The path '{}' to which we would save target '{}' has no parent",
path.display(),
name.raw(),
))]
SaveTargetNoParent {
path: PathBuf,
name: TargetName,
backtrace: Backtrace,
},
#[snafu(display("The target '{}' was not found", name.raw()))]
SaveTargetNotFound {
name: TargetName,
backtrace: Backtrace,
},
#[snafu(display(
"The target '{}' had an unsafe name. Not writing to '{}' because it is not in the outdir '{}'",
name.raw(),
filepath.display(),
outdir.display()
))]
SaveTargetUnsafePath {
name: TargetName,
outdir: PathBuf,
filepath: PathBuf,
},
#[snafu(display("Failed to serialize role '{}' for signing: {}", role, source))]
SerializeRole {
role: String,
source: serde_json::Error,
backtrace: Backtrace,
},
#[snafu(display("Failed to serialize signed role '{}': {}", role, source))]
SerializeSignedRole {
role: String,
source: serde_json::Error,
backtrace: Backtrace,
},
#[snafu(display("Failed to sign message"))]
Sign {
source: aws_lc_rs::error::Unspecified,
backtrace: Backtrace,
},
#[snafu(display("Failed to sign message: {}", source))]
SignMessage {
source: Box<dyn std::error::Error + Send + Sync + 'static>,
backtrace: Backtrace,
},
#[snafu(display("Unable to find signing keys for role '{}'", role))]
SigningKeysNotFound { role: String },
#[snafu(display(
"Tried to use role metadata with spec version '{}', version '{}' is supported",
given,
supported
))]
SpecVersion {
given: String,
supported: String,
backtrace: Backtrace,
},
#[snafu(display(
"System time stepped backward: system time '{}', last known time '{}'",
sys_time,
latest_known_time,
))]
SystemTimeSteppedBackward {
sys_time: DateTime<Utc>,
latest_known_time: DateTime<Utc>,
},
#[snafu(display("Refusing to replace {} with requested {} for target {}", found, expected, path.display()))]
TargetFileTypeMismatch {
expected: String,
found: String,
path: PathBuf,
backtrace: Backtrace,
},
#[snafu(display("Unable to create Target from path '{}': {}", path.display(), source))]
TargetFromPath {
path: PathBuf,
source: crate::schema::Error,
backtrace: Backtrace,
},
#[snafu(display("Unable to resolve the target name '{}': {}", name, source))]
TargetNameResolve {
name: String,
source: std::io::Error,
},
#[snafu(display(
"Unable to resolve target name '{}', a path with no components was produced",
name
))]
TargetNameComponentsEmpty { name: String },
#[snafu(display("Unable to resolve target name '{}', expected a rooted path", name))]
TargetNameRootMissing { name: String },
#[snafu(display("Failed to fetch {}: {}", url, source))]
Transport {
url: url::Url,
source: TransportError,
backtrace: Backtrace,
},
#[snafu(display(
"The target name '..' is unsafe. Interpreting it as a path could escape from the intended \
directory",
))]
UnsafeTargetNameDotDot {},
#[snafu(display(
"The target name '{}' is unsafe. Interpreting it as a path would lead to an empty filename",
name
))]
UnsafeTargetNameEmpty { name: String },
#[snafu(display(
"The target name '{}' is unsafe. Interpreting it as a path would lead to a filename of '/'",
name
))]
UnsafeTargetNameSlash { name: String },
#[snafu(display("Failed to verify {} metadata: {}", role, source))]
VerifyMetadata {
role: RoleType,
source: crate::schema::Error,
backtrace: Backtrace,
},
#[snafu(display("Failed to verify {} metadata: {}", role, source))]
VerifyRoleMetadata {
role: String,
source: crate::schema::Error,
backtrace: Backtrace,
},
#[snafu(display("Failed to verify trusted root metadata: {}", source))]
VerifyTrustedMetadata {
source: crate::schema::Error,
backtrace: Backtrace,
},
#[snafu(display(
"{} metadata version mismatch: fetched {}, expected {}",
role,
fetched,
expected
))]
VersionMismatch {
role: RoleType,
fetched: u64,
expected: u64,
backtrace: Backtrace,
},
#[snafu(display("Error reading data from '{}': {}", url, source))]
CacheFileRead {
url: Url,
source: std::io::Error,
backtrace: Backtrace,
},
#[snafu(display("Error writing data to '{}': {}", path.display(), source))]
CacheFileWrite {
path: PathBuf,
source: std::io::Error,
backtrace: Backtrace,
},
#[snafu(display("Error creating the directory '{}': {}", path.display(), source))]
CacheDirectoryCreate {
path: PathBuf,
source: std::io::Error,
backtrace: Backtrace,
},
#[snafu(display("Error writing target file to '{}': {}", path.display(), source))]
CacheTargetWrite {
path: PathBuf,
source: std::io::Error,
backtrace: Backtrace,
},
#[snafu(display("The target '{}' was not found", target_name.raw()))]
CacheTargetMissing {
target_name: TargetName,
source: crate::schema::Error,
backtrace: Backtrace,
},
#[snafu(display("Failed to walk directory tree '{}': {}", directory.display(), source))]
WalkDir {
directory: PathBuf,
source: walkdir::Error,
backtrace: Backtrace,
},
#[snafu(display("Delegated role not found: {}", name))]
DelegateNotFound { name: String },
#[snafu(display("Targets role '{}' not found: {}", name, source))]
TargetsNotFound {
name: String,
source: crate::schema::Error,
},
#[snafu(display("Delegated role not found: {}", name))]
DelegateMissing {
name: String,
source: crate::schema::Error,
},
#[snafu(display("Delegation doesn't contain targets field"))]
NoTargets,
#[snafu(display("Targets doesn't contain delegations field"))]
NoDelegations,
#[snafu(display("Delegated roles are not consistent for {}", name))]
DelegatedRolesNotConsistent { name: String },
#[snafu(display("Invalid file permissions"))]
InvalidPath { source: crate::schema::Error },
#[snafu(display("Role missing from snapshot meta: {} ({})", name, parent))]
RoleNotInMeta { name: String, parent: String },
#[snafu(display("The key for {} was not included", role))]
KeyNotFound {
role: String,
source: schema::Error,
backtrace: Backtrace,
},
#[snafu(display("No keys were found for role '{}'", role))]
NoKeys { role: String },
#[snafu(display("Invalid number"))]
InvalidInto {
source: std::num::TryFromIntError,
backtrace: Backtrace,
},
#[snafu(display("Invalid threshold number"))]
InvalidThreshold { backtrace: Backtrace },
#[snafu(display("Failed to serialize to JSON: {}", source))]
JsonSerialization {
source: schema::Error,
backtrace: Backtrace,
},
#[snafu(display("Invalid path permission of {} : {:?}", name, paths))]
InvalidPathPermission {
name: String,
paths: Vec<String>,
source: schema::Error,
},
#[snafu(display("Duplicate keyid {} in signatures", keyid))]
DuplicateKeyid { keyid: String },
#[snafu(display("Exactly 1 role was required, but {} were created", count))]
InvalidRoleCount { count: usize },
#[snafu(display("Could not create a targets map: {}", source))]
TargetsMap { source: schema::Error },
#[snafu(display("A key holder must be set"))]
NoKeyHolder,
#[snafu(display("No limits in editor"))]
MissingLimits,
#[snafu(display("The transport is not in editor"))]
MissingTransport,
#[snafu(display(
"Unstable root; found {} keys for role {}, threshold is {}",
role,
actual,
threshold
))]
UnstableRoot {
role: RoleType,
actual: usize,
threshold: u64,
},
#[snafu(display("The targets editor was not cleared"))]
TargetsEditorSome,
#[snafu(display("Failed to join spawn_blocking task: {source}"))]
JoinSpawnBlockingTask {
source: tokio::task::JoinError,
backtrace: Backtrace,
},
}