summaryrefslogtreecommitdiff
path: root/nix/libutil
diff options
context:
space:
mode:
Diffstat (limited to 'nix/libutil')
-rw-r--r--nix/libutil/archive.cc192
1 files changed, 136 insertions, 56 deletions
diff --git a/nix/libutil/archive.cc b/nix/libutil/archive.cc
index 938fe8d4492..81c03f44d37 100644
--- a/nix/libutil/archive.cc
+++ b/nix/libutil/archive.cc
@@ -155,94 +155,169 @@ struct CaseInsensitiveCompare
155 } 155 }
156}; 156};
157 157
158/* Used to place a consistent upper bound on recursion depth */
159#define DIRECTORY_NESTING_LIMIT 256
158 160
159static void parse(ParseSink & sink, Source & source, const Path & path) 161static void parse(ParseSink & sink, Source & source, const Path & path,
162 int nestLimit)
160{ 163{
164 if (nestLimit <= 0) throw Error("nar directory nesting limit reached");
161 string s; 165 string s;
162 166
163 s = readString(source); 167 s = readString(source);
164 if (s != "(") throw badArchive("expected open tag"); 168 if (s != "(") throw badArchive("expected open tag");
165 169
166 enum { tpUnknown, tpRegular, tpDirectory, tpSymlink } type = tpUnknown; 170 s = readString(source);
167 171 if (s != "type") throw badArchive("expected type tag");
168 std::map<Path, int, CaseInsensitiveCompare> names;
169 172
170 while (1) { 173 s = readString(source);
171 checkInterrupt();
172 174
175 if (s == "regular") {
176 bool executable = false;
173 s = readString(source); 177 s = readString(source);
174 178 if (s == "executable") {
175 if (s == ")") { 179 executable = true;
176 break; 180 readString(source);
181 s = readString(source);
177 } 182 }
178 183
179 else if (s == "type") { 184 if (s != "contents") throw badArchive("expected contents tag");
180 if (type != tpUnknown)
181 throw badArchive("multiple type fields");
182 string t = readString(source);
183 185
184 if (t == "regular") { 186 sink.createRegularFile(path);
185 type = tpRegular; 187 if (executable) sink.isExecutable();
186 sink.createRegularFile(path); 188 parseContents(sink, source, path);
187 } 189 s = readString(source);
190 if (s != ")") throw badArchive("expected close tag");
191 }
192 else if (s == "symlink") {
193 s = readString(source);
194 if (s != "target") throw badArchive("expected target tag");
195 s = readString(source);
196 sink.createSymlink(path, s);
197 s = readString(source);
198 if (s != ")") throw badArchive("expected close tag");
199 }
200 else if (s == "directory") {
201 string prevName;
202 sink.createDirectory(path);
203 while (true) {
204 checkInterrupt();
205 s = readString(source);
206 if (s == ")") break;
207 if (s != "entry") throw badArchive("expected entry tag");
188 208
189 else if (t == "directory") { 209 s = readString(source);
190 sink.createDirectory(path); 210 if (s != "(") throw badArchive("expected entry open tag");
191 type = tpDirectory;
192 }
193 211
194 else if (t == "symlink") { 212 s = readString(source);
195 type = tpSymlink; 213 if (s != "name") throw badArchive("expected name tag");
196 }
197 214
198 else throw badArchive("unknown file type " + t); 215 s = readString(source);
216 string name = s;
217 if (name.empty() || name == "." || name == ".."
218 || name.find('/') != string::npos
219 || name.find((char) 0) != string::npos)
220 throw Error(std::format("NAR contains invalid file name `{}'", name));
221 if (!prevName.empty() && name <= prevName)
222 throw Error("NAR directory is not sorted");
199 223
200 } 224 s = readString(source);
225 if (s != "node") throw badArchive("expected node tag");
226
227 parse(sink, source, path + "/" + name, nestLimit - 1);
201 228
202 else if (s == "contents" && type == tpRegular) { 229 s = readString(source);
203 parseContents(sink, source, path); 230 if (s != ")") throw badArchive("expected entry close tag");
231 prevName = name;
204 } 232 }
233 }
234 else throw badArchive("unknown type");
235}
205 236
206 else if (s == "executable" && type == tpRegular) { 237/* Unbounded variant that doesn't recurse, uses path as its stack */
238static void parse_unbounded(ParseSink & sink, Source & source, Path & path)
239{
240 string prevName;
241 string s;
242start:
243 s = readString(source);
244 if (s != "(") throw badArchive("expected open tag");
245
246 s = readString(source);
247 if (s != "type") throw badArchive("expected type tag");
248
249 s = readString(source);
250
251 if (s == "regular") {
252 bool executable = false;
253 s = readString(source);
254 if (s == "executable") {
255 executable = true;
207 readString(source); 256 readString(source);
208 sink.isExecutable(); 257 s = readString(source);
209 } 258 }
210 259
211 else if (s == "entry" && type == tpDirectory) { 260 if (s != "contents") throw badArchive("expected contents tag");
212 string name, prevName; 261
262 sink.createRegularFile(path);
263 if (executable) sink.isExecutable();
264 parseContents(sink, source, path);
265 s = readString(source);
266 if (s != ")") throw badArchive("expected close tag");
267 }
268 else if (s == "symlink") {
269 s = readString(source);
270 if (s != "target") throw badArchive("expected target tag");
271 s = readString(source);
272 sink.createSymlink(path, s);
273 s = readString(source);
274 if (s != ")") throw badArchive("expected close tag");
275 }
276 else if (s == "directory") {
277 sink.createDirectory(path);
278 while (true) {
279 checkInterrupt();
280 s = readString(source);
281 if (s == ")") break;
282 if (s != "entry") throw badArchive("expected entry tag");
283
284 s = readString(source);
285 if (s != "(") throw badArchive("expected entry open tag");
213 286
214 s = readString(source); 287 s = readString(source);
215 if (s != "(") throw badArchive("expected open tag"); 288 if (s != "name") throw badArchive("expected name tag");
216 289
217 while (1) { 290 s = readString(source);
218 checkInterrupt(); 291 {
292 string name = s;
293 if (name.empty() || name == "." || name == ".."
294 || name.find('/') != string::npos
295 || name.find((char) 0) != string::npos)
296 throw Error(std::format("NAR contains invalid file name `{}'", name));
297 if (!prevName.empty() && name <= prevName)
298 throw Error("NAR directory is not sorted");
219 299
220 s = readString(source); 300 s = readString(source);
301 if (s != "node") throw badArchive("expected node tag");
221 302
222 if (s == ")") { 303 /* parse(sink, source, path + "/" + name); */
223 break; 304 path.append("/" + name);
224 } else if (s == "name") { 305 prevName = "";
225 name = readString(source); 306 goto start;
226 if (name.empty() || name == "." || name == ".." || name.find('/') != string::npos || name.find((char) 0) != string::npos)
227 throw Error(std::format("NAR contains invalid file name `{}'", name));
228 if (name <= prevName)
229 throw Error("NAR directory is not sorted");
230 prevName = name;
231 } else if (s == "node") {
232 if (s.empty()) throw badArchive("entry name missing");
233 parse(sink, source, path + "/" + name);
234 } else
235 throw badArchive("unknown field " + s);
236 } 307 }
237 }
238 308
239 else if (s == "target" && type == tpSymlink) { 309 continue_directory:
240 string target = readString(source); 310 s = readString(source);
241 sink.createSymlink(path, target); 311 if (s != ")") throw badArchive("expected entry close tag");
242 } 312 }
313 }
314 else throw badArchive("unknown type");
243 315
244 else 316 string::size_type slash_pos = path.rfind("/");
245 throw badArchive("unknown field " + s); 317 if (slash_pos != string::npos) {
318 prevName = string(path, slash_pos+1);
319 path.erase(slash_pos);
320 goto continue_directory;
246 } 321 }
247} 322}
248 323
@@ -258,7 +333,12 @@ void parseDump(ParseSink & sink, Source & source)
258 } 333 }
259 if (version != archiveVersion1) 334 if (version != archiveVersion1)
260 throw badArchive("input doesn't look like a normalized archive"); 335 throw badArchive("input doesn't look like a normalized archive");
261 parse(sink, source, ""); 336
337 parse(sink, source, "", DIRECTORY_NESTING_LIMIT);
338 /*
339 string file = "";
340 parse_unbounded(sink, source, file);
341 */
262} 342}
263 343
264 344