diff options
Diffstat (limited to 'nix/libutil/archive.cc')
| -rw-r--r-- | nix/libutil/archive.cc | 192 |
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 | ||
| 159 | static void parse(ParseSink & sink, Source & source, const Path & path) | 161 | static 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 */ |
| 238 | static void parse_unbounded(ParseSink & sink, Source & source, Path & path) | ||
| 239 | { | ||
| 240 | string prevName; | ||
| 241 | string s; | ||
| 242 | start: | ||
| 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 | ||
