nix_compat/derivation/validate.rs
1use crate::derivation::{Derivation, DerivationError};
2use crate::store_path;
3
4impl Derivation {
5 /// validate ensures a Derivation struct is properly populated,
6 /// and returns a [DerivationError] if not.
7 ///
8 /// if `validate_output_paths` is set to false, the output paths are
9 /// excluded from validation.
10 ///
11 /// This is helpful to validate struct population before invoking
12 /// [Derivation::calculate_output_paths].
13 pub fn validate(&self, validate_output_paths: bool) -> Result<(), DerivationError> {
14 // Ensure the number of outputs is > 1
15 if self.outputs.is_empty() {
16 return Err(DerivationError::NoOutputs());
17 }
18
19 // Validate all outputs
20 for (output_name, output) in &self.outputs {
21 // empty output names are invalid.
22 //
23 // `drv` is an invalid output name too, as this would cause
24 // a `builtins.derivation` call to return an attrset with a
25 // `drvPath` key (which already exists) and has a different
26 // meaning.
27 //
28 // Other output names that don't match the name restrictions from
29 // [StorePathRef] will fail the [StorePathRef::validate_name] check.
30 if output_name.is_empty()
31 || output_name == "drv"
32 || store_path::validate_name(output_name.as_bytes()).is_err()
33 {
34 return Err(DerivationError::InvalidOutputName(output_name.to_string()));
35 }
36
37 if output.is_fixed() {
38 if self.outputs.len() != 1 {
39 return Err(DerivationError::MoreThanOneOutputButFixed());
40 }
41 if output_name != "out" {
42 return Err(DerivationError::InvalidOutputNameForFixed(
43 output_name.to_string(),
44 ));
45 }
46 }
47
48 if let Err(e) = output.validate(validate_output_paths) {
49 return Err(DerivationError::InvalidOutput(output_name.to_string(), e));
50 }
51 }
52
53 // Validate all input_derivations
54 for (input_derivation_path, output_names) in &self.input_derivations {
55 // Validate input_derivation_path
56 if !input_derivation_path.name().ends_with(".drv") {
57 return Err(DerivationError::InvalidInputDerivationPrefix(
58 input_derivation_path.to_string(),
59 ));
60 }
61
62 if output_names.is_empty() {
63 return Err(DerivationError::EmptyInputDerivationOutputNames(
64 input_derivation_path.to_string(),
65 ));
66 }
67
68 for output_name in output_names.iter() {
69 // empty output names are invalid.
70 //
71 // `drv` is an invalid output name too, as this would cause
72 // a `builtins.derivation` call to return an attrset with a
73 // `drvPath` key (which already exists) and has a different
74 // meaning.
75 //
76 // Other output names that don't match the name restrictions from
77 // [StorePath] will fail the [StorePathRef::validate_name] check.
78 if output_name.is_empty()
79 || output_name == "drv"
80 || store_path::validate_name(output_name.as_bytes()).is_err()
81 {
82 return Err(DerivationError::InvalidInputDerivationOutputName(
83 input_derivation_path.to_string(),
84 output_name.to_string(),
85 ));
86 }
87 }
88 }
89
90 // validate platform
91 if self.system.is_empty() {
92 return Err(DerivationError::InvalidPlatform(self.system.to_string()));
93 }
94
95 // validate builder
96 if self.builder.is_empty() {
97 return Err(DerivationError::InvalidBuilder(self.builder.to_string()));
98 }
99
100 // validate env, none of the keys may be empty.
101 // We skip the `name` validation seen in go-nix.
102 for k in self.environment.keys() {
103 if k.is_empty() {
104 return Err(DerivationError::InvalidEnvironmentKey(k.to_string()));
105 }
106 }
107
108 Ok(())
109 }
110}
111
112#[cfg(test)]
113mod test {
114 use std::collections::BTreeMap;
115
116 use crate::derivation::{CAHash, Derivation, Output};
117
118 /// Regression test: produce a Derivation that's almost valid, except its
119 /// fixed-output output has the wrong hash specified.
120 #[test]
121 fn output_validate() {
122 let mut outputs = BTreeMap::new();
123 outputs.insert(
124 "out".to_string(),
125 Output {
126 path: None,
127 ca_hash: Some(CAHash::Text([0; 32])), // This is disallowed
128 },
129 );
130
131 let drv = Derivation {
132 arguments: vec![],
133 builder: "/bin/sh".to_string(),
134 outputs,
135 system: "x86_64-linux".to_string(),
136 ..Default::default()
137 };
138
139 drv.validate(false).expect_err("must fail");
140 }
141}