dvExtractFiles function

String dvExtractFiles(
  1. Uint8List packed,
  2. String parent
)

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;
}