1#![allow(clippy::default_trait_access)]
7
8use crate::schema::RoleType;
9use crate::{schema, TargetName, TransportError};
10use chrono::{DateTime, Utc};
11use snafu::{Backtrace, Snafu};
12use std::io;
13use std::path::PathBuf;
14use url::Url;
15
16pub type Result<T> = std::result::Result<T, Error>;
18
19#[derive(Debug, Snafu)]
21#[snafu(visibility(pub(crate)))]
22#[non_exhaustive]
23#[allow(missing_docs)]
24pub enum Error {
25 #[snafu(display("Unable to canonicalize path '{}': {}", path.display(), source))]
26 AbsolutePath {
27 path: PathBuf,
28 source: std::io::Error,
29 backtrace: Backtrace,
30 },
31
32 #[snafu(display(
33 "Failed to create temp directory for the repository datastore: {}",
34 source
35 ))]
36 DatastoreInit {
37 source: std::io::Error,
38 backtrace: Backtrace,
39 },
40
41 #[snafu(display("Failed to create file at datastore path {}: {}", path.display(), source))]
43 DatastoreCreate {
44 path: PathBuf,
45 source: std::io::Error,
46 backtrace: Backtrace,
47 },
48
49 #[snafu(display("Failed to open file from datastore path {}: {}", path.display(), source))]
51 DatastoreOpen {
52 path: PathBuf,
53 source: std::io::Error,
54 backtrace: Backtrace,
55 },
56
57 #[snafu(display("Failed to remove file at datastore path {}: {}", path.display(), source))]
59 DatastoreRemove {
60 path: PathBuf,
61 source: std::io::Error,
62 backtrace: Backtrace,
63 },
64
65 #[snafu(display("Failed to serialize {} to JSON at datastore path {}: {}", what, path.display(), source))]
67 DatastoreSerialize {
68 what: String,
69 path: PathBuf,
70 source: serde_json::Error,
71 backtrace: Backtrace,
72 },
73
74 #[snafu(display("Failed to create directory '{}': {}", path.display(), source))]
75 DirCreate {
76 path: PathBuf,
77 source: std::io::Error,
78 backtrace: Backtrace,
79 },
80
81 #[snafu(display("{} metadata is expired", role))]
83 ExpiredMetadata {
84 role: RoleType,
85 backtrace: Backtrace,
86 },
87
88 #[snafu(display("Failed to stat '{}': {}", path.display(), source))]
89 FileMetadata {
90 path: PathBuf,
91 source: std::io::Error,
92 backtrace: Backtrace,
93 },
94
95 #[snafu(display("Failed to open {}: {}", path.display(), source))]
96 FileOpen {
97 path: PathBuf,
98 source: std::io::Error,
99 backtrace: Backtrace,
100 },
101
102 #[snafu(display("Failed to read {}: {}", path.display(), source))]
103 FileRead {
104 path: PathBuf,
105 source: std::io::Error,
106 backtrace: Backtrace,
107 },
108
109 #[snafu(display("Failed to parse {}: {}", path.display(), source))]
110 FileParseJson {
111 path: PathBuf,
112 source: serde_json::Error,
113 backtrace: Backtrace,
114 },
115
116 #[snafu(display("Can't build URL from relative path '{}'", path.display()))]
117 FileUrl { path: PathBuf, backtrace: Backtrace },
118
119 #[snafu(display("Failed to write to {}: {}", path.display(), source))]
120 FileWrite {
121 path: PathBuf,
122 source: std::io::Error,
123 backtrace: Backtrace,
124 },
125
126 #[snafu(display(
129 "Hash mismatch for {}: calculated {}, expected {}",
130 context,
131 calculated,
132 expected,
133 ))]
134 HashMismatch {
135 context: String,
136 calculated: String,
137 expected: String,
138 backtrace: Backtrace,
139 },
140
141 #[snafu(display("Source path for target must be file or symlink - '{}'", path.display()))]
142 InvalidFileType { path: PathBuf, backtrace: Backtrace },
143
144 #[snafu(display("Encountered an invalid target name: {}", inner))]
145 InvalidTargetName { inner: String, backtrace: Backtrace },
146
147 #[snafu(display("Failed to join \"{}\" to URL \"{}\": {}", path, url, source))]
149 JoinUrl {
150 path: String,
151 url: url::Url,
152 source: url::ParseError,
153 backtrace: Backtrace,
154 },
155
156 #[snafu(display(
157 "After encoding the name '{}' to '{}', failed to join '{}' to URL '{}': {}",
158 original,
159 encoded,
160 filename,
161 url,
162 source
163 ))]
164 JoinUrlEncoded {
165 original: String,
166 encoded: String,
167 filename: String,
168 url: url::Url,
169 source: url::ParseError,
170 backtrace: Backtrace,
171 },
172
173 #[snafu(display("Unable to parse keypair: {}", source))]
174 KeyPairFromKeySource {
175 source: Box<dyn std::error::Error + Send + Sync + 'static>,
176 backtrace: Backtrace,
177 },
178
179 #[snafu(display("Private key rejected: {}", source))]
180 KeyRejected {
181 source: aws_lc_rs::error::KeyRejected,
182 backtrace: Backtrace,
183 },
184
185 #[snafu(display("Unable to match any of the provided keys with root.json"))]
186 KeysNotFoundInRoot { backtrace: Backtrace },
187
188 #[snafu(display("Unrecognized private key format"))]
189 KeyUnrecognized { backtrace: Backtrace },
190
191 #[snafu(display("Failed to create symlink at '{}': {}", path.display(), source))]
192 LinkCreate {
193 path: PathBuf,
194 source: io::Error,
195 backtrace: Backtrace,
196 },
197
198 #[snafu(display("Maximum size {} (specified by {}) exceeded", max_size, specifier))]
200 MaxSizeExceeded {
201 max_size: u64,
202 specifier: &'static str,
203 backtrace: Backtrace,
204 },
205
206 #[snafu(display("Maximum root updates {} exceeded", max_root_updates))]
208 MaxUpdatesExceeded {
209 max_root_updates: u64,
210 backtrace: Backtrace,
211 },
212
213 #[snafu(display("Meta for {:?} missing from {} metadata", file, role))]
215 MetaMissing {
216 file: &'static str,
217 role: RoleType,
218 backtrace: Backtrace,
219 },
220
221 #[snafu(display("Missing '{}' when building repo from RepositoryEditor", field))]
222 Missing { field: String, backtrace: Backtrace },
223
224 #[snafu(display("Unable to create NamedTempFile in directory '{}': {}", path.display(), source))]
225 NamedTempFileCreate {
226 path: PathBuf,
227 source: std::io::Error,
228 backtrace: Backtrace,
229 },
230
231 #[snafu(display("Unable to persist NamedTempFile to '{}': {}", path.display(), source))]
232 NamedTempFilePersist {
233 path: PathBuf,
234 source: tempfile::PersistError,
235 backtrace: Backtrace,
236 },
237
238 #[snafu(display("Unable to determine file name from path: '{}'", path.display()))]
240 NoFileName { path: PathBuf, backtrace: Backtrace },
241
242 #[snafu(display("Key for role '{}' doesn't exist in root.json", role))]
243 NoRoleKeysinRoot { role: String },
244
245 #[snafu(display(
247 "Found version {} of {} metadata when we had previously fetched version {}",
248 new_version,
249 role,
250 current_version
251 ))]
252 OlderMetadata {
253 role: RoleType,
254 current_version: u64,
255 new_version: u64,
256 backtrace: Backtrace,
257 },
258
259 #[snafu(display(
261 "Timestamp version {} meta length {} is not exactly one",
262 version,
263 meta_length
264 ))]
265 TimestampMetaLength { version: u64, meta_length: usize },
266
267 #[snafu(display("No snapshot meta in timestamp.json version {}", version))]
269 MissingSnapshotMeta { version: u64 },
270
271 #[snafu(display(
274 "Snapshot version {} in timestamp {} is less than {} in timestamp {}",
275 snapshot_new,
276 timestamp_new,
277 snapshot_old,
278 timestamp_old
279 ))]
280 OlderSnapshotInTimestamp {
281 snapshot_new: u64,
282 timestamp_new: u64,
283 snapshot_old: u64,
284 timestamp_old: u64,
285 },
286
287 #[snafu(display("Snapshot version {} does not contain targets.json", version))]
289 SnapshotTargetsMetaMissing { version: u64 },
290
291 #[snafu(display(
293 "Role {} appears in snapshot version {} but not version {}",
294 role,
295 old_version,
296 new_version
297 ))]
298 SnapshotRoleMissing {
299 role: String,
300 old_version: u64,
301 new_version: u64,
302 },
303
304 #[snafu(display(
306 "Role {} version {} in snapshot {} is greater than version {} in snapshot {}",
307 role,
308 old_role_version,
309 old_snapshot_version,
310 new_role_version,
311 new_snapshot_version
312 ))]
313 SnapshotRoleRollback {
314 role: String,
315 old_role_version: u64,
316 old_snapshot_version: u64,
317 new_role_version: u64,
318 new_snapshot_version: u64,
319 },
320
321 #[snafu(display("Failed to parse {} metadata: {}", role, source))]
331 ParseMetadata {
332 role: RoleType,
333 source: serde_json::Error,
334 backtrace: Backtrace,
335 },
336
337 #[snafu(display("Failed to parse trusted root metadata: {}", source))]
342 ParseTrustedMetadata {
343 source: serde_json::Error,
344 backtrace: Backtrace,
345 },
346
347 #[snafu(display("Failed to parse URL {:?}: {}", url, source))]
349 ParseUrl {
350 url: String,
351 source: url::ParseError,
352 backtrace: Backtrace,
353 },
354
355 #[snafu(display("Target path exists, caller requested we fail - '{}'", path.display()))]
356 PathExistsFail { path: PathBuf, backtrace: Backtrace },
357
358 #[snafu(display("Requested copy/link of '{}' which is not a file", path.display()))]
359 PathIsNotFile { path: PathBuf, backtrace: Backtrace },
360
361 #[snafu(display("Requested copy/link of '{}' which is not a repo target", path.display()))]
362 PathIsNotTarget { path: PathBuf, backtrace: Backtrace },
363
364 #[snafu(display("Path {} is not valid UTF-8", path.display()))]
366 PathUtf8 { path: PathBuf, backtrace: Backtrace },
367
368 #[snafu(display("Path {} is not valid UTF-8", path.display()))]
369 UnixPathUtf8 {
370 path: typed_path::UnixPathBuf,
371 backtrace: Backtrace,
372 },
373
374 #[snafu(display("Failed to remove existing target path '{}': {}", path.display(), source))]
375 RemoveTarget {
376 path: PathBuf,
377 source: std::io::Error,
378 backtrace: Backtrace,
379 },
380
381 #[snafu(display("Unable to get info about the outdir '{}': {}", path.display(), source))]
382 SaveTargetDirInfo {
383 path: PathBuf,
384 source: std::io::Error,
385 backtrace: Backtrace,
386 },
387
388 #[snafu(display("The outdir '{}' either does not exist or is not a directory", path.display()))]
389 SaveTargetOutdir { path: PathBuf, backtrace: Backtrace },
390
391 #[snafu(display("Unable to canonicalize the outdir '{}': {}", path.display(), source))]
392 SaveTargetOutdirCanonicalize {
393 path: PathBuf,
394 source: std::io::Error,
395 backtrace: Backtrace,
396 },
397
398 #[snafu(display(
399 "The path '{}' to which we would save target '{}' has no parent",
400 path.display(),
401 name.raw(),
402 ))]
403 SaveTargetNoParent {
404 path: PathBuf,
405 name: TargetName,
406 backtrace: Backtrace,
407 },
408
409 #[snafu(display("The target '{}' was not found", name.raw()))]
410 SaveTargetNotFound {
411 name: TargetName,
412 backtrace: Backtrace,
413 },
414
415 #[snafu(display(
416 "The target '{}' had an unsafe name. Not writing to '{}' because it is not in the outdir '{}'",
417 name.raw(),
418 filepath.display(),
419 outdir.display()
420 ))]
421 SaveTargetUnsafePath {
422 name: TargetName,
423 outdir: PathBuf,
424 filepath: PathBuf,
425 },
426
427 #[snafu(display("Failed to serialize role '{}' for signing: {}", role, source))]
428 SerializeRole {
429 role: String,
430 source: serde_json::Error,
431 backtrace: Backtrace,
432 },
433
434 #[snafu(display("Failed to serialize signed role '{}': {}", role, source))]
435 SerializeSignedRole {
436 role: String,
437 source: serde_json::Error,
438 backtrace: Backtrace,
439 },
440
441 #[snafu(display("Failed to sign message"))]
442 Sign {
443 source: aws_lc_rs::error::Unspecified,
444 backtrace: Backtrace,
445 },
446
447 #[snafu(display("Failed to sign message: {}", source))]
448 SignMessage {
449 source: Box<dyn std::error::Error + Send + Sync + 'static>,
450 backtrace: Backtrace,
451 },
452
453 #[snafu(display("Unable to find signing keys for role '{}'", role))]
454 SigningKeysNotFound { role: String },
455
456 #[snafu(display(
457 "Tried to use role metadata with spec version '{}', version '{}' is supported",
458 given,
459 supported
460 ))]
461 SpecVersion {
462 given: String,
463 supported: String,
464 backtrace: Backtrace,
465 },
466
467 #[snafu(display(
469 "System time stepped backward: system time '{}', last known time '{}'",
470 sys_time,
471 latest_known_time,
472 ))]
473 SystemTimeSteppedBackward {
474 sys_time: DateTime<Utc>,
475 latest_known_time: DateTime<Utc>,
476 },
477
478 #[snafu(display("Refusing to replace {} with requested {} for target {}", found, expected, path.display()))]
479 TargetFileTypeMismatch {
480 expected: String,
481 found: String,
482 path: PathBuf,
483 backtrace: Backtrace,
484 },
485
486 #[snafu(display("Unable to create Target from path '{}': {}", path.display(), source))]
487 TargetFromPath {
488 path: PathBuf,
489 source: crate::schema::Error,
490 backtrace: Backtrace,
491 },
492
493 #[snafu(display("Unable to resolve the target name '{}': {}", name, source))]
494 TargetNameResolve {
495 name: String,
496 source: std::io::Error,
497 },
498
499 #[snafu(display(
500 "Unable to resolve target name '{}', a path with no components was produced",
501 name
502 ))]
503 TargetNameComponentsEmpty { name: String },
504
505 #[snafu(display("Unable to resolve target name '{}', expected a rooted path", name))]
506 TargetNameRootMissing { name: String },
507
508 #[snafu(display("Failed to fetch {}: {}", url, source))]
510 Transport {
511 url: url::Url,
512 source: TransportError,
513 backtrace: Backtrace,
514 },
515
516 #[snafu(display(
517 "The target name '..' is unsafe. Interpreting it as a path could escape from the intended \
518 directory",
519 ))]
520 UnsafeTargetNameDotDot {},
521
522 #[snafu(display(
523 "The target name '{}' is unsafe. Interpreting it as a path would lead to an empty filename",
524 name
525 ))]
526 UnsafeTargetNameEmpty { name: String },
527
528 #[snafu(display(
529 "The target name '{}' is unsafe. Interpreting it as a path would lead to a filename of '/'",
530 name
531 ))]
532 UnsafeTargetNameSlash { name: String },
533
534 #[snafu(display("Failed to verify {} metadata: {}", role, source))]
536 VerifyMetadata {
537 role: RoleType,
538 source: crate::schema::Error,
539 backtrace: Backtrace,
540 },
541
542 #[snafu(display("Failed to verify {} metadata: {}", role, source))]
543 VerifyRoleMetadata {
544 role: String,
545 source: crate::schema::Error,
546 backtrace: Backtrace,
547 },
548
549 #[snafu(display("Failed to verify trusted root metadata: {}", source))]
551 VerifyTrustedMetadata {
552 source: crate::schema::Error,
553 backtrace: Backtrace,
554 },
555
556 #[snafu(display(
558 "{} metadata version mismatch: fetched {}, expected {}",
559 role,
560 fetched,
561 expected
562 ))]
563 VersionMismatch {
564 role: RoleType,
565 fetched: u64,
566 expected: u64,
567 backtrace: Backtrace,
568 },
569
570 #[snafu(display("Error reading data from '{}': {}", url, source))]
571 CacheFileRead {
572 url: Url,
573 source: std::io::Error,
574 backtrace: Backtrace,
575 },
576
577 #[snafu(display("Error writing data to '{}': {}", path.display(), source))]
578 CacheFileWrite {
579 path: PathBuf,
580 source: std::io::Error,
581 backtrace: Backtrace,
582 },
583
584 #[snafu(display("Error creating the directory '{}': {}", path.display(), source))]
585 CacheDirectoryCreate {
586 path: PathBuf,
587 source: std::io::Error,
588 backtrace: Backtrace,
589 },
590
591 #[snafu(display("Error writing target file to '{}': {}", path.display(), source))]
592 CacheTargetWrite {
593 path: PathBuf,
594 source: std::io::Error,
595 backtrace: Backtrace,
596 },
597
598 #[snafu(display("The target '{}' was not found", target_name.raw()))]
599 CacheTargetMissing {
600 target_name: TargetName,
601 source: crate::schema::Error,
602 backtrace: Backtrace,
603 },
604
605 #[snafu(display("Failed to walk directory tree '{}': {}", directory.display(), source))]
606 WalkDir {
607 directory: PathBuf,
608 source: walkdir::Error,
609 backtrace: Backtrace,
610 },
611
612 #[snafu(display("Delegated role not found: {}", name))]
613 DelegateNotFound { name: String },
614
615 #[snafu(display("Targets role '{}' not found: {}", name, source))]
616 TargetsNotFound {
617 name: String,
618 source: crate::schema::Error,
619 },
620
621 #[snafu(display("Delegated role not found: {}", name))]
622 DelegateMissing {
623 name: String,
624 source: crate::schema::Error,
625 },
626
627 #[snafu(display("Delegation doesn't contain targets field"))]
628 NoTargets,
629
630 #[snafu(display("Targets doesn't contain delegations field"))]
631 NoDelegations,
632
633 #[snafu(display("Delegated roles are not consistent for {}", name))]
634 DelegatedRolesNotConsistent { name: String },
635
636 #[snafu(display("Invalid file permissions"))]
638 InvalidPath { source: crate::schema::Error },
639
640 #[snafu(display("Role missing from snapshot meta: {} ({})", name, parent))]
641 RoleNotInMeta { name: String, parent: String },
642
643 #[snafu(display("The key for {} was not included", role))]
644 KeyNotFound {
645 role: String,
646 source: schema::Error,
647 backtrace: Backtrace,
648 },
649
650 #[snafu(display("No keys were found for role '{}'", role))]
651 NoKeys { role: String },
652
653 #[snafu(display("Invalid number"))]
654 InvalidInto {
655 source: std::num::TryFromIntError,
656 backtrace: Backtrace,
657 },
658
659 #[snafu(display("Invalid threshold number"))]
660 InvalidThreshold { backtrace: Backtrace },
661
662 #[snafu(display("Failed to serialize to JSON: {}", source))]
664 JsonSerialization {
665 source: schema::Error,
666 backtrace: Backtrace,
667 },
668
669 #[snafu(display("Invalid path permission of {} : {:?}", name, paths))]
671 InvalidPathPermission {
672 name: String,
673 paths: Vec<String>,
674 source: schema::Error,
675 },
676
677 #[snafu(display("Duplicate keyid {} in signatures", keyid))]
679 DuplicateKeyid { keyid: String },
680
681 #[snafu(display("Exactly 1 role was required, but {} were created", count))]
683 InvalidRoleCount { count: usize },
684
685 #[snafu(display("Could not create a targets map: {}", source))]
687 TargetsMap { source: schema::Error },
688
689 #[snafu(display("A key holder must be set"))]
691 NoKeyHolder,
692
693 #[snafu(display("No limits in editor"))]
694 MissingLimits,
695
696 #[snafu(display("The transport is not in editor"))]
697 MissingTransport,
698
699 #[snafu(display(
701 "Unstable root; found {} keys for role {}, threshold is {}",
702 role,
703 actual,
704 threshold
705 ))]
706 UnstableRoot {
707 role: RoleType,
708 actual: usize,
709 threshold: u64,
710 },
711
712 #[snafu(display("The targets editor was not cleared"))]
713 TargetsEditorSome,
714}