projects/guix·blob python-team · refs

364 lines · 10.1 KiB

1#include "store-api.hh"
2#include "globals.hh"
3#include "util.hh"
4
5#include <climits>
6#include <format>
7
8namespace nix {
9
10
11GCOptions::GCOptions()
12{
13 action = gcDeleteDead;
14 ignoreLiveness = false;
15 maxFreed = ULLONG_MAX;
16}
17
18
19bool isInStore(const Path & path)
20{
21 return isInDir(path, settings.nixStore);
22}
23
24
25bool isStorePath(const Path & path)
26{
27 return isInStore(path)
28 && path.find('/', settings.nixStore.size() + 1) == Path::npos;
29}
30
31
32bool isStoreName(const string & name, string & problemDescription)
33{
34 const string validChars = "+-._?=";
35 if (name.empty()) {
36 problemDescription = "empty string is not a valid name";
37 return false;
38 }
39 /* Disallow names starting with a dot for possible security
40 reasons (e.g., "." and ".."). */
41 if (name.starts_with(".")) {
42 problemDescription = std::format("invalid name: `{}' (can't begin with dot)",
43 name);
44 return false;
45 }
46 for (const auto& i : name)
47 if (!((i >= 'A' && i <= 'Z') ||
48 (i >= 'a' && i <= 'z') ||
49 (i >= '0' && i <= '9') ||
50 validChars.find(i) != string::npos))
51 {
52 problemDescription = std::format("invalid character `{}' in name `{}'",
53 i,
54 name);
55 return false;
56 }
57 return true;
58}
59
60
61bool isStoreName(const string & name)
62{
63 string problemDescription; /* Placeholder */
64 return isStoreName(name, problemDescription);
65}
66
67
68void checkStoreName(const string & name)
69{
70 string problemDescription;
71 if (!isStoreName(name, problemDescription))
72 throw Error(problemDescription);
73}
74
75
76bool isStoreBasenameStrict(const string & name)
77{
78 /* 1. At least 34 characters long
79 2. First 32 characters are all nix-base32 characters
80 3. 33rd character (index 32) is a dash
81 4. 34th character (index 33) and those following it form a valid store
82 name. */
83 return name.size() >= 34
84 && isHash32(string(name, 0, 32))
85 && name[32] == '-'
86 && isStoreName(string(name, 33));
87}
88
89
90bool isStorePathStrict(const Path & path)
91{
92 return isStorePath(path)
93 && isStoreBasenameStrict(string(path, settings.nixStore.size() + 1));
94}
95
96
97void assertStorePath(const Path & path)
98{
99 if (!isStorePath(path))
100 throw Error(std::format("path `{}' is not in the store", path));
101}
102
103
104void assertStorePathStrict(const Path & path)
105{
106 if (!isStorePathStrict(path))
107 throw Error(std::format("path `{}' is not a valid store path", path));
108}
109
110
111Path toStorePath(const Path & path)
112{
113 if (!isInStore(path))
114 throw Error(std::format("path `{}' is not in the store", path));
115 Path::size_type slash = path.find('/', settings.nixStore.size() + 1);
116 if (slash == Path::npos)
117 return path;
118 else
119 return Path(path, 0, slash);
120}
121
122
123string storePathToName(const Path & path)
124{
125 assertStorePath(path);
126 return string(path, settings.nixStore.size() + 34);
127}
128
129
130
131
132
133/* Store paths have the following form:
134
135 <store>/<h>-<name>
136
137 where
138
139 <store> = the location of the store, usually /gnu/store
140
141 <name> = a human readable name for the path, typically obtained
142 from the name attribute of the derivation, or the name of the
143 source file from which the store path is created. For derivation
144 outputs other than the default "out" output, the string "-<id>"
145 is suffixed to <name>.
146
147 <h> = base-32 representation of the first 160 bits of a SHA-256
148 hash of <s>; the hash part of the store name
149
150 <s> = the string "<type>:sha256:<h2>:<store>:<name>";
151 note that it includes the location of the store as well as the
152 name to make sure that changes to either of those are reflected
153 in the hash (e.g. you won't get /nix/store/<h>-name1 and
154 /nix/store/<h>-name2 with equal hash parts).
155
156 <type> = one of:
157 "text:<r1>:<r2>:...<rN>"
158 for plain text files written to the store using
159 addTextToStore(); <r1> ... <rN> are the references of the
160 path.
161 "source"
162 for paths copied to the store using addToStore() when recursive
163 = true and hashAlgo = "sha256"
164 "output:<id>"
165 for either the outputs created by derivations, OR paths copied
166 to the store using addToStore() with recursive != true or
167 hashAlgo != "sha256" (in that case "source" is used; it's
168 silly, but it's done that way for compatibility). <id> is the
169 name of the output (usually, "out").
170
171 <h2> = base-16 representation of a SHA-256 hash of:
172 if <type> = "text:...":
173 the string written to the resulting store path
174 if <type> = "source":
175 the serialisation of the path from which this store path is
176 copied, as returned by hashPath()
177 if <type> = "output:out":
178 for non-fixed derivation outputs:
179 the derivation (see hashDerivationModulo() in
180 primops.cc)
181 for paths copied by addToStore() or produced by fixed-output
182 derivations:
183 the string "fixed:out:<rec><algo>:<hash>:", where
184 <rec> = "r:" for recursive (path) hashes, or "" or flat
185 (file) hashes
186 <algo> = "md5", "sha1" or "sha256"
187 <hash> = base-16 representation of the path or flat hash of
188 the contents of the path (or expected contents of the
189 path for fixed-output derivations)
190
191 It would have been nicer to handle fixed-output derivations under
192 "source", e.g. have something like "source:<rec><algo>", but we're
193 stuck with this for now...
194
195 The main reason for this way of computing names is to prevent name
196 collisions (for security). For instance, it shouldn't be feasible
197 to come up with a derivation whose output path collides with the
198 path for a copied source. The former would have a <s> starting with
199 "output:out:", while the latter would have a <2> starting with
200 "source:".
201*/
202
203
204Path makeStorePath(const string & type,
205 const Hash & hash, const string & name)
206{
207 /* e.g., "source:sha256:1abc...:/nix/store:foo.tar.gz" */
208 string s = type + ":sha256:" + printHash(hash) + ":"
209 + settings.nixStore + ":" + name;
210
211 checkStoreName(name);
212
213 return settings.nixStore + "/"
214 + printHash32(compressHash(hashString(htSHA256, s), 20))
215 + "-" + name;
216}
217
218
219Path makeOutputPath(const string & id,
220 const Hash & hash, const string & name)
221{
222 return makeStorePath("output:" + id, hash,
223 name + (id == "out" ? "" : "-" + id));
224}
225
226
227Path makeFixedOutputPath(bool recursive,
228 HashType hashAlgo, Hash hash, string name)
229{
230 return hashAlgo == htSHA256 && recursive
231 ? makeStorePath("source", hash, name)
232 : makeStorePath("output:out", hashString(htSHA256,
233 "fixed:out:" + (recursive ? (string) "r:" : "") +
234 printHashType(hashAlgo) + ":" + printHash(hash) + ":"),
235 name);
236}
237
238
239/* Return the 'type' part of a text store item with the given REFERENCES. */
240static string textTypeWithReferences(const PathSet & references)
241{
242 /* Stuff the references (if any) into the type. This is a bit
243 hacky, but we can't put them in the file content since that would be
244 ambiguous. */
245 string type = "text";
246 for (const auto& i : references) {
247 type += ":";
248 type += i;
249 }
250 return type;
251}
252
253Path computeStorePathForText(const string & name, const string & s,
254 const PathSet & references)
255{
256 Hash hash = hashString(htSHA256, s);
257 string type = textTypeWithReferences(references);
258 return makeStorePath(type, hash, name);
259}
260
261bool isContentAddressedPath(const Path & path, const Hash & hash,
262 const PathSet & references, bool recursive)
263{
264 /* Check whether PATH corresponds to something introduced by 'addToStore'
265 or by 'addTextToStore'. For simplicity, anything with a hash other
266 than SHA256 is omitted: this returns false even though they are
267 content-addressed as well. */
268 string name = storePathToName(path);
269 if (recursive) {
270 /* HASH is interpreted as the nar hash. This can only come from
271 'addToStore'. */
272 return references.empty() &&
273 path == makeFixedOutputPath(true, htSHA256, hash, name);
274 } else {
275 /* HASH is interpreted as the content hash. This can come from
276 'addTextToStore' ("text" type, possibly with references) or from
277 'addToStore' ("output:out" type). */
278 string type = textTypeWithReferences(references);
279 return path == makeStorePath(type, hash, name)
280 || (references.empty() &&
281 path == makeFixedOutputPath(false, htSHA256, hash, name));
282 }
283}
284
285/* Return a string accepted by decodeValidPathInfo() that
286 registers the specified paths as valid. Note: it's the
287 responsibility of the caller to provide a closure. */
288string StoreAPI::makeValidityRegistration(const PathSet & paths,
289 bool showDerivers, bool showHash)
290{
291 string s = "";
292
293 for (auto& i : paths) {
294 s += i + "\n";
295
296 ValidPathInfo info = queryPathInfo(i);
297
298 if (showHash) {
299 s += printHash(info.hash) + "\n";
300 s += std::format("{}\n", info.narSize);
301 }
302
303 Path deriver = showDerivers ? info.deriver : "";
304 s += deriver + "\n";
305
306 s += std::format("{}\n", info.references.size());
307
308 for (auto& j : info.references)
309 s += j + "\n";
310 }
311
312 return s;
313}
314
315string showPaths(const PathSet & paths)
316{
317 string s;
318 for (const auto& i : paths) {
319 if (s.size() != 0) s += ", ";
320 s += "`" + i + "'";
321 }
322 return s;
323}
324
325Path readStorePath(Source & from)
326{
327 Path path = readString(from);
328 assertStorePath(path);
329 return path;
330}
331
332
333template<class T> T readStorePaths(Source & from)
334{
335 T paths = readStrings<T>(from);
336 for (auto& i : paths) assertStorePath(i);
337 return paths;
338}
339
340
341template PathSet readStorePaths(Source & from);
342
343
344Path readStorePathStrict(Source & from)
345{
346 Path path = readString(from);
347 assertStorePathStrict(path);
348 return path;
349}
350
351
352template<class T> T readStorePathsStrict(Source & from)
353{
354 T paths = readStrings<T>(from);
355 for (auto& i : paths) assertStorePathStrict(i);
356 return paths;
357}
358
359
360template PathSet readStorePathsStrict(Source & from);
361
362}
363
364