Skip to content

Commit 35978ca

Browse files
authored
Merge pull request #13848 from obsidiansystems/factor-out-drv-check
Factor out `checkOutputs`
2 parents 6c8f5ef + d1bdaef commit 35978ca

File tree

4 files changed

+182
-151
lines changed

4 files changed

+182
-151
lines changed
Lines changed: 156 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,156 @@
1+
#include <queue>
2+
3+
#include "nix/store/store-api.hh"
4+
5+
#include "derivation-check.hh"
6+
7+
namespace nix {
8+
9+
void checkOutputs(
10+
Store & store,
11+
const StorePath & drvPath,
12+
const decltype(DerivationOptions::outputChecks) & outputChecks,
13+
const std::map<std::string, ValidPathInfo> & outputs)
14+
{
15+
std::map<Path, const ValidPathInfo &> outputsByPath;
16+
for (auto & output : outputs)
17+
outputsByPath.emplace(store.printStorePath(output.second.path), output.second);
18+
19+
for (auto & output : outputs) {
20+
auto & outputName = output.first;
21+
auto & info = output.second;
22+
23+
/* Compute the closure and closure size of some output. This
24+
is slightly tricky because some of its references (namely
25+
other outputs) may not be valid yet. */
26+
auto getClosure = [&](const StorePath & path) {
27+
uint64_t closureSize = 0;
28+
StorePathSet pathsDone;
29+
std::queue<StorePath> pathsLeft;
30+
pathsLeft.push(path);
31+
32+
while (!pathsLeft.empty()) {
33+
auto path = pathsLeft.front();
34+
pathsLeft.pop();
35+
if (!pathsDone.insert(path).second)
36+
continue;
37+
38+
auto i = outputsByPath.find(store.printStorePath(path));
39+
if (i != outputsByPath.end()) {
40+
closureSize += i->second.narSize;
41+
for (auto & ref : i->second.references)
42+
pathsLeft.push(ref);
43+
} else {
44+
auto info = store.queryPathInfo(path);
45+
closureSize += info->narSize;
46+
for (auto & ref : info->references)
47+
pathsLeft.push(ref);
48+
}
49+
}
50+
51+
return std::make_pair(std::move(pathsDone), closureSize);
52+
};
53+
54+
auto applyChecks = [&](const DerivationOptions::OutputChecks & checks) {
55+
if (checks.maxSize && info.narSize > *checks.maxSize)
56+
throw BuildError(
57+
"path '%s' is too large at %d bytes; limit is %d bytes",
58+
store.printStorePath(info.path),
59+
info.narSize,
60+
*checks.maxSize);
61+
62+
if (checks.maxClosureSize) {
63+
uint64_t closureSize = getClosure(info.path).second;
64+
if (closureSize > *checks.maxClosureSize)
65+
throw BuildError(
66+
"closure of path '%s' is too large at %d bytes; limit is %d bytes",
67+
store.printStorePath(info.path),
68+
closureSize,
69+
*checks.maxClosureSize);
70+
}
71+
72+
auto checkRefs = [&](const StringSet & value, bool allowed, bool recursive) {
73+
/* Parse a list of reference specifiers. Each element must
74+
either be a store path, or the symbolic name of the output
75+
of the derivation (such as `out'). */
76+
StorePathSet spec;
77+
for (auto & i : value) {
78+
if (store.isStorePath(i))
79+
spec.insert(store.parseStorePath(i));
80+
else if (auto output = get(outputs, i))
81+
spec.insert(output->path);
82+
else {
83+
std::string outputsListing =
84+
concatMapStringsSep(", ", outputs, [](auto & o) { return o.first; });
85+
throw BuildError(
86+
"derivation '%s' output check for '%s' contains an illegal reference specifier '%s',"
87+
" expected store path or output name (one of [%s])",
88+
store.printStorePath(drvPath),
89+
outputName,
90+
i,
91+
outputsListing);
92+
}
93+
}
94+
95+
auto used = recursive ? getClosure(info.path).first : info.references;
96+
97+
if (recursive && checks.ignoreSelfRefs)
98+
used.erase(info.path);
99+
100+
StorePathSet badPaths;
101+
102+
for (auto & i : used)
103+
if (allowed) {
104+
if (!spec.count(i))
105+
badPaths.insert(i);
106+
} else {
107+
if (spec.count(i))
108+
badPaths.insert(i);
109+
}
110+
111+
if (!badPaths.empty()) {
112+
std::string badPathsStr;
113+
for (auto & i : badPaths) {
114+
badPathsStr += "\n ";
115+
badPathsStr += store.printStorePath(i);
116+
}
117+
throw BuildError(
118+
"output '%s' is not allowed to refer to the following paths:%s",
119+
store.printStorePath(info.path),
120+
badPathsStr);
121+
}
122+
};
123+
124+
/* Mandatory check: absent whitelist, and present but empty
125+
whitelist mean very different things. */
126+
if (auto & refs = checks.allowedReferences) {
127+
checkRefs(*refs, true, false);
128+
}
129+
if (auto & refs = checks.allowedRequisites) {
130+
checkRefs(*refs, true, true);
131+
}
132+
133+
/* Optimization: don't need to do anything when
134+
disallowed and empty set. */
135+
if (!checks.disallowedReferences.empty()) {
136+
checkRefs(checks.disallowedReferences, false, false);
137+
}
138+
if (!checks.disallowedRequisites.empty()) {
139+
checkRefs(checks.disallowedRequisites, false, true);
140+
}
141+
};
142+
143+
std::visit(
144+
overloaded{
145+
[&](const DerivationOptions::OutputChecks & checks) { applyChecks(checks); },
146+
[&](const std::map<std::string, DerivationOptions::OutputChecks> & checksPerOutput) {
147+
if (auto outputChecks = get(checksPerOutput, outputName))
148+
149+
applyChecks(*outputChecks);
150+
},
151+
},
152+
outputChecks);
153+
}
154+
}
155+
156+
} // namespace nix
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
#include "nix/store/derivations.hh"
2+
#include "nix/store/derivation-options.hh"
3+
#include "nix/store/path-info.hh"
4+
5+
namespace nix {
6+
7+
/**
8+
* Check that outputs meets the requirements specified by the
9+
* 'outputChecks' attribute (or the legacy
10+
* '{allowed,disallowed}{References,Requisites}' attributes).
11+
*
12+
* The outputs may not be valid yet, hence outputs needs to contain all
13+
* needed info like the NAR size. However, the external (not other
14+
* output) references of the output must be valid, so we can compute the
15+
* closure size.
16+
*/
17+
void checkOutputs(
18+
Store & store,
19+
const StorePath & drvPath,
20+
const decltype(DerivationOptions::outputChecks) & drvOptions,
21+
const std::map<std::string, ValidPathInfo> & outputs);
22+
23+
} // namespace nix

src/libstore/meson.build

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -265,6 +265,7 @@ sources = files(
265265
'binary-cache-store.cc',
266266
'build-result.cc',
267267
'build/derivation-building-goal.cc',
268+
'build/derivation-check.cc',
268269
'build/derivation-goal.cc',
269270
'build/derivation-trampoline-goal.cc',
270271
'build/drv-output-substitution-goal.cc',

src/libstore/unix/build/derivation-builder.cc

Lines changed: 2 additions & 151 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
#include "nix/util/signals.hh"
4343

4444
#include "store-config-private.hh"
45+
#include "build/derivation-check.hh"
4546

4647
namespace nix {
4748

@@ -335,13 +336,6 @@ class DerivationBuilderImpl : public DerivationBuilder, public DerivationBuilder
335336
*/
336337
SingleDrvOutputs registerOutputs();
337338

338-
/**
339-
* Check that an output meets the requirements specified by the
340-
* 'outputChecks' attribute (or the legacy
341-
* '{allowed,disallowed}{References,Requisites}' attributes).
342-
*/
343-
void checkOutputs(const std::map<std::string, ValidPathInfo> & outputs);
344-
345339
public:
346340

347341
void deleteTmpDir(bool force) override;
@@ -1810,7 +1804,7 @@ SingleDrvOutputs DerivationBuilderImpl::registerOutputs()
18101804
}
18111805

18121806
/* Apply output checks. */
1813-
checkOutputs(infos);
1807+
checkOutputs(store, drvPath, drvOptions.outputChecks, infos);
18141808

18151809
/* Register each output path as valid, and register the sets of
18161810
paths referenced by each of them. If there are cycles in the
@@ -1849,149 +1843,6 @@ SingleDrvOutputs DerivationBuilderImpl::registerOutputs()
18491843
return builtOutputs;
18501844
}
18511845

1852-
void DerivationBuilderImpl::checkOutputs(const std::map<std::string, ValidPathInfo> & outputs)
1853-
{
1854-
std::map<Path, const ValidPathInfo &> outputsByPath;
1855-
for (auto & output : outputs)
1856-
outputsByPath.emplace(store.printStorePath(output.second.path), output.second);
1857-
1858-
for (auto & output : outputs) {
1859-
auto & outputName = output.first;
1860-
auto & info = output.second;
1861-
1862-
/* Compute the closure and closure size of some output. This
1863-
is slightly tricky because some of its references (namely
1864-
other outputs) may not be valid yet. */
1865-
auto getClosure = [&](const StorePath & path) {
1866-
uint64_t closureSize = 0;
1867-
StorePathSet pathsDone;
1868-
std::queue<StorePath> pathsLeft;
1869-
pathsLeft.push(path);
1870-
1871-
while (!pathsLeft.empty()) {
1872-
auto path = pathsLeft.front();
1873-
pathsLeft.pop();
1874-
if (!pathsDone.insert(path).second)
1875-
continue;
1876-
1877-
auto i = outputsByPath.find(store.printStorePath(path));
1878-
if (i != outputsByPath.end()) {
1879-
closureSize += i->second.narSize;
1880-
for (auto & ref : i->second.references)
1881-
pathsLeft.push(ref);
1882-
} else {
1883-
auto info = store.queryPathInfo(path);
1884-
closureSize += info->narSize;
1885-
for (auto & ref : info->references)
1886-
pathsLeft.push(ref);
1887-
}
1888-
}
1889-
1890-
return std::make_pair(std::move(pathsDone), closureSize);
1891-
};
1892-
1893-
auto applyChecks = [&](const DerivationOptions::OutputChecks & checks) {
1894-
if (checks.maxSize && info.narSize > *checks.maxSize)
1895-
throw BuildError(
1896-
"path '%s' is too large at %d bytes; limit is %d bytes",
1897-
store.printStorePath(info.path),
1898-
info.narSize,
1899-
*checks.maxSize);
1900-
1901-
if (checks.maxClosureSize) {
1902-
uint64_t closureSize = getClosure(info.path).second;
1903-
if (closureSize > *checks.maxClosureSize)
1904-
throw BuildError(
1905-
"closure of path '%s' is too large at %d bytes; limit is %d bytes",
1906-
store.printStorePath(info.path),
1907-
closureSize,
1908-
*checks.maxClosureSize);
1909-
}
1910-
1911-
auto checkRefs = [&](const StringSet & value, bool allowed, bool recursive) {
1912-
/* Parse a list of reference specifiers. Each element must
1913-
either be a store path, or the symbolic name of the output
1914-
of the derivation (such as `out'). */
1915-
StorePathSet spec;
1916-
for (auto & i : value) {
1917-
if (store.isStorePath(i))
1918-
spec.insert(store.parseStorePath(i));
1919-
else if (auto output = get(outputs, i))
1920-
spec.insert(output->path);
1921-
else {
1922-
std::string outputsListing =
1923-
concatMapStringsSep(", ", outputs, [](auto & o) { return o.first; });
1924-
throw BuildError(
1925-
"derivation '%s' output check for '%s' contains an illegal reference specifier '%s',"
1926-
" expected store path or output name (one of [%s])",
1927-
store.printStorePath(drvPath),
1928-
outputName,
1929-
i,
1930-
outputsListing);
1931-
}
1932-
}
1933-
1934-
auto used = recursive ? getClosure(info.path).first : info.references;
1935-
1936-
if (recursive && checks.ignoreSelfRefs)
1937-
used.erase(info.path);
1938-
1939-
StorePathSet badPaths;
1940-
1941-
for (auto & i : used)
1942-
if (allowed) {
1943-
if (!spec.count(i))
1944-
badPaths.insert(i);
1945-
} else {
1946-
if (spec.count(i))
1947-
badPaths.insert(i);
1948-
}
1949-
1950-
if (!badPaths.empty()) {
1951-
std::string badPathsStr;
1952-
for (auto & i : badPaths) {
1953-
badPathsStr += "\n ";
1954-
badPathsStr += store.printStorePath(i);
1955-
}
1956-
throw BuildError(
1957-
"output '%s' is not allowed to refer to the following paths:%s",
1958-
store.printStorePath(info.path),
1959-
badPathsStr);
1960-
}
1961-
};
1962-
1963-
/* Mandatory check: absent whitelist, and present but empty
1964-
whitelist mean very different things. */
1965-
if (auto & refs = checks.allowedReferences) {
1966-
checkRefs(*refs, true, false);
1967-
}
1968-
if (auto & refs = checks.allowedRequisites) {
1969-
checkRefs(*refs, true, true);
1970-
}
1971-
1972-
/* Optimization: don't need to do anything when
1973-
disallowed and empty set. */
1974-
if (!checks.disallowedReferences.empty()) {
1975-
checkRefs(checks.disallowedReferences, false, false);
1976-
}
1977-
if (!checks.disallowedRequisites.empty()) {
1978-
checkRefs(checks.disallowedRequisites, false, true);
1979-
}
1980-
};
1981-
1982-
std::visit(
1983-
overloaded{
1984-
[&](const DerivationOptions::OutputChecks & checks) { applyChecks(checks); },
1985-
[&](const std::map<std::string, DerivationOptions::OutputChecks> & checksPerOutput) {
1986-
if (auto outputChecks = get(checksPerOutput, outputName))
1987-
1988-
applyChecks(*outputChecks);
1989-
},
1990-
},
1991-
drvOptions.outputChecks);
1992-
}
1993-
}
1994-
19951846
void DerivationBuilderImpl::deleteTmpDir(bool force)
19961847
{
19971848
if (topTmpDir != "") {

0 commit comments

Comments
 (0)