| 1 | import 'dart:io'; |
| 2 | import 'dart:isolate'; |
| 3 | |
| 4 | import 'package:archive/archive.dart'; |
| 5 | import 'package:path/path.dart' as p; |
| 6 | |
| 7 | Future<int> directorySize(Directory dir) async { |
| 8 | var total = 0; |
| 9 | if (!await dir.exists()) { |
| 10 | return total; |
| 11 | } |
| 12 | |
| 13 | await for (final entity in dir.list(recursive: true, followLinks: false)) { |
| 14 | if (entity is File) { |
| 15 | total += await entity.length(); |
| 16 | } |
| 17 | } |
| 18 | return total; |
| 19 | } |
| 20 | |
| 21 | Future<void> extractTarBz2({ |
| 22 | required String archivePath, |
| 23 | required Directory targetDir, |
| 24 | }) async { |
| 25 | await Isolate.run(() => _extractTarBz2Sync(archivePath, targetDir.path)); |
| 26 | } |
| 27 | |
| 28 | void _extractTarBz2Sync(String archivePath, String targetDirPath) { |
| 29 | final archiveBytes = File(archivePath).readAsBytesSync(); |
| 30 | final tarBytes = BZip2Decoder().decodeBytes(archiveBytes); |
| 31 | final archive = TarDecoder().decodeBytes(tarBytes); |
| 32 | |
| 33 | final normalizedTargetDir = p.normalize(targetDirPath); |
| 34 | |
| 35 | for (final file in archive) { |
| 36 | final relativePath = file.name; |
| 37 | if (relativePath.isEmpty) continue; |
| 38 | |
| 39 | final outputPath = p.normalize(p.join(targetDirPath, relativePath)); |
| 40 | // Reject any entry whose resolved path would land outside the target |
| 41 | // directory (e.g. a `../` traversal sequence in the archive entry name) |
| 42 | // rather than trusting the archive's own file names. |
| 43 | if (outputPath != normalizedTargetDir && |
| 44 | !p.isWithin(normalizedTargetDir, outputPath)) { |
| 45 | continue; |
| 46 | } |
| 47 | |
| 48 | if (file.isFile) { |
| 49 | final outputFile = File(outputPath); |
| 50 | outputFile.createSync(recursive: true); |
| 51 | outputFile.writeAsBytesSync(file.content as List<int>, flush: true); |
| 52 | } else { |
| 53 | Directory(outputPath).createSync(recursive: true); |
| 54 | } |
| 55 | } |
| 56 | } |
| 57 | |
| 58 | Future<void> deleteDirectoryIfExists(Directory dir) async { |
| 59 | if (await dir.exists()) { |
| 60 | await dir.delete(recursive: true); |
| 61 | } |
| 62 | } |