dvExtractFiles function
Writes packed into a directory under parent named by its content, and
returns that directory.
A second start of the same binary finds the directory complete and writes nothing. A new build names a different directory, and the old one is removed once the new one is complete. Written beside its final name and renamed into place, so a start that is killed half way leaves nothing a later start would take as complete.
Throws FormatException for a path that would land outside the directory.
Implementation
String dvExtractFiles(Uint8List packed, String parent) {
final String id =
sha256.convert(packed).toString().substring(0, 16);
final Directory root = Directory(parent)..createSync(recursive: true);
final Directory target = Directory('${root.path}${Platform.pathSeparator}$id');
if (!target.existsSync()) {
final Map<String, List<int>> files = dvUnpackFiles(packed);
for (final String path in files.keys) {
final List<String> parts = path.split('/');
if (path.startsWith('/') ||
parts.any((String part) => part == '..' || part.contains(r'\'))) {
throw FormatException('a packed path leaves its directory', path);
}
}
final Directory partial = Directory('${target.path}.partial-$pid');
if (partial.existsSync()) partial.deleteSync(recursive: true);
final String base = partial.absolute.path;
for (final MapEntry<String, List<int>> entry in files.entries) {
File(<String>[base, ...entry.key.split('/')]
.join(Platform.pathSeparator))
..createSync(recursive: true)
..writeAsBytesSync(entry.value);
}
partial.renameSync(target.path);
}
for (final FileSystemEntity other in root.listSync()) {
// Another process's extraction in progress is left to finish.
if (other is Directory &&
other.path != target.path &&
!other.path.contains('.partial-')) {
other.deleteSync(recursive: true);
}
}
return target.path;
}