@hot-updater/react-native 1.0.0-rc.4 → 1.0.0-rc.41
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/android/build.gradle +13 -2
- package/android/proguard-rules.pro +8 -2
- package/android/src/bridge/BridgeReactInstance.kt +38 -0
- package/android/src/bridgeless/BridgeReactInstance.kt +24 -0
- package/android/src/main/java/com/hotupdater/ActivityStartGate.kt +94 -0
- package/android/src/main/java/com/hotupdater/BuiltInAssetResolver.kt +286 -0
- package/android/src/main/java/com/hotupdater/BundleFileStorageService.kt +1064 -844
- package/android/src/main/java/com/hotupdater/BundleMetadata.kt +50 -11
- package/android/src/main/java/com/hotupdater/FileManagerService.kt +45 -0
- package/android/src/main/java/com/hotupdater/HotUpdaterException.kt +287 -22
- package/android/src/main/java/com/hotupdater/HotUpdaterImpl.kt +46 -30
- package/android/src/main/java/com/hotupdater/HotUpdaterRecoveryManager.kt +75 -192
- package/android/src/main/java/com/hotupdater/HotUpdaterRecoveryState.kt +64 -0
- package/android/src/main/java/com/hotupdater/InstallIdentityService.kt +37 -0
- package/android/src/main/java/com/hotupdater/KeyValueStorageService.kt +86 -0
- package/android/src/main/java/com/hotupdater/NetworkState.kt +31 -0
- package/android/src/main/java/com/hotupdater/NoBackupStorage.kt +17 -0
- package/android/src/main/java/com/hotupdater/OkHttpDownloadService.kt +127 -50
- package/android/src/main/java/com/hotupdater/ReactNativeValueConverters.kt +34 -0
- package/android/src/main/java/com/hotupdater/TarArchiveInputStream.kt +214 -375
- package/android/src/main/java/com/hotupdater/TarBrArchiveExtractor.kt +119 -0
- package/android/src/newarch/HotUpdater.kt +7 -9
- package/android/src/newarch/HotUpdaterModule.kt +109 -84
- package/android/src/newarch/JavaScriptExceptionHooks.kt +48 -0
- package/android/src/newarch/ReactIntegrationManager.kt +6 -32
- package/android/src/oldarch/HotUpdater.kt +7 -9
- package/android/src/oldarch/HotUpdaterModule.kt +98 -74
- package/android/src/oldarch/HotUpdaterSpec.kt +4 -6
- package/android/src/oldarch/JavaScriptExceptionHooks.kt +71 -0
- package/ios/HotUpdater/Internal/BrotliFileDecompressor.swift +167 -0
- package/ios/HotUpdater/Internal/BuiltInAssetResolver.swift +196 -0
- package/ios/HotUpdater/Internal/BundleFileStorageService.swift +1032 -1116
- package/ios/HotUpdater/Internal/BundleMetadata.swift +61 -54
- package/ios/HotUpdater/Internal/FileManagerService.swift +48 -0
- package/ios/HotUpdater/Internal/FileUtilities.swift +64 -0
- package/ios/HotUpdater/Internal/HotUpdater-Bridging-Header.h +0 -1
- package/ios/HotUpdater/Internal/HotUpdater.mm +32 -67
- package/ios/HotUpdater/Internal/HotUpdaterImpl.swift +229 -73
- package/ios/HotUpdater/Internal/KeyValueStorageService.swift +71 -0
- package/ios/HotUpdater/Internal/NotificationExtension.swift +0 -2
- package/ios/HotUpdater/Internal/TarArchiveExtractor.swift +182 -311
- package/ios/HotUpdater/Internal/URLSessionDownloadService.swift +119 -71
- package/ios/HotUpdater/Internal/UpdateFailure.swift +390 -0
- package/ios/HotUpdater/Recovery/HotUpdaterRecovery.h +16 -0
- package/ios/HotUpdater/Recovery/HotUpdaterRecovery.mm +83 -0
- package/ios/HotUpdater/Recovery/HotUpdaterRecoveryHooks.mm +49 -0
- package/lib/commonjs/appReady.js +96 -0
- package/lib/commonjs/appReady.js.map +1 -0
- package/lib/commonjs/checkForUpdate.js +121 -69
- package/lib/commonjs/checkForUpdate.js.map +1 -1
- package/lib/commonjs/clientPlugin.js +13 -0
- package/lib/commonjs/clientPlugin.js.map +1 -0
- package/lib/commonjs/error.js +5 -14
- package/lib/commonjs/error.js.map +1 -1
- package/lib/commonjs/fetchJSON.js +16 -27
- package/lib/commonjs/fetchJSON.js.map +1 -1
- package/lib/commonjs/fetchUpdateResponse.js +60 -0
- package/lib/commonjs/fetchUpdateResponse.js.map +1 -0
- package/lib/commonjs/httpClient.js +64 -95
- package/lib/commonjs/httpClient.js.map +1 -1
- package/lib/commonjs/index.js +355 -324
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/init.types.js +6 -0
- package/lib/commonjs/init.types.js.map +1 -0
- package/lib/commonjs/native.js +77 -63
- package/lib/commonjs/native.js.map +1 -1
- package/lib/commonjs/pluginHost.js +41 -0
- package/lib/commonjs/pluginHost.js.map +1 -0
- package/lib/commonjs/releaseCatalogCache.js +36 -87
- package/lib/commonjs/releaseCatalogCache.js.map +1 -1
- package/lib/commonjs/sdkVersion.js +1 -1
- package/lib/commonjs/sdkVersion.js.map +1 -1
- package/lib/commonjs/specs/NativeHotUpdater.js.map +1 -1
- package/lib/commonjs/store.js +3 -10
- package/lib/commonjs/store.js.map +1 -1
- package/lib/commonjs/updateError.js +145 -0
- package/lib/commonjs/updateError.js.map +1 -0
- package/lib/commonjs/wrap.js +46 -38
- package/lib/commonjs/wrap.js.map +1 -1
- package/lib/module/appReady.js +90 -0
- package/lib/module/appReady.js.map +1 -0
- package/lib/module/checkForUpdate.js +114 -64
- package/lib/module/checkForUpdate.js.map +1 -1
- package/lib/module/clientPlugin.js +4 -0
- package/lib/module/clientPlugin.js.map +1 -0
- package/lib/module/error.js +5 -14
- package/lib/module/error.js.map +1 -1
- package/lib/module/fetchJSON.js +14 -26
- package/lib/module/fetchJSON.js.map +1 -1
- package/lib/module/fetchUpdateResponse.js +55 -0
- package/lib/module/fetchUpdateResponse.js.map +1 -0
- package/lib/module/httpClient.js +63 -94
- package/lib/module/httpClient.js.map +1 -1
- package/lib/module/index.js +335 -327
- package/lib/module/index.js.map +1 -1
- package/lib/module/init.types.js +4 -0
- package/lib/module/init.types.js.map +1 -0
- package/lib/module/native.js +70 -56
- package/lib/module/native.js.map +1 -1
- package/lib/module/pluginHost.js +36 -0
- package/lib/module/pluginHost.js.map +1 -0
- package/lib/module/releaseCatalogCache.js +32 -82
- package/lib/module/releaseCatalogCache.js.map +1 -1
- package/lib/module/sdkVersion.js +1 -1
- package/lib/module/sdkVersion.js.map +1 -1
- package/lib/module/specs/NativeHotUpdater.js.map +1 -1
- package/lib/module/store.js +3 -10
- package/lib/module/store.js.map +1 -1
- package/lib/module/updateError.js +139 -0
- package/lib/module/updateError.js.map +1 -0
- package/lib/module/wrap.js +47 -38
- package/lib/module/wrap.js.map +1 -1
- package/lib/typescript/commonjs/appReady.d.ts +30 -0
- package/lib/typescript/commonjs/appReady.d.ts.map +1 -0
- package/lib/typescript/commonjs/checkForUpdate.d.ts +20 -6
- package/lib/typescript/commonjs/checkForUpdate.d.ts.map +1 -1
- package/lib/typescript/commonjs/clientPlugin.d.ts +2 -0
- package/lib/typescript/commonjs/clientPlugin.d.ts.map +1 -0
- package/lib/typescript/commonjs/error.d.ts +5 -14
- package/lib/typescript/commonjs/error.d.ts.map +1 -1
- package/lib/typescript/commonjs/fetchJSON.d.ts +5 -5
- package/lib/typescript/commonjs/fetchJSON.d.ts.map +1 -1
- package/lib/typescript/commonjs/fetchUpdateResponse.d.ts +14 -0
- package/lib/typescript/commonjs/fetchUpdateResponse.d.ts.map +1 -0
- package/lib/typescript/commonjs/httpClient.d.ts +5 -34
- package/lib/typescript/commonjs/httpClient.d.ts.map +1 -1
- package/lib/typescript/commonjs/index.d.ts +124 -116
- package/lib/typescript/commonjs/index.d.ts.map +1 -1
- package/lib/typescript/commonjs/init.types.d.ts +21 -0
- package/lib/typescript/commonjs/init.types.d.ts.map +1 -0
- package/lib/typescript/commonjs/native.d.ts +32 -25
- package/lib/typescript/commonjs/native.d.ts.map +1 -1
- package/lib/typescript/commonjs/pluginHost.d.ts +8 -0
- package/lib/typescript/commonjs/pluginHost.d.ts.map +1 -0
- package/lib/typescript/commonjs/releaseCatalogCache.d.ts +8 -14
- package/lib/typescript/commonjs/releaseCatalogCache.d.ts.map +1 -1
- package/lib/typescript/commonjs/sdkVersion.d.ts +1 -1
- package/lib/typescript/commonjs/sdkVersion.d.ts.map +1 -1
- package/lib/typescript/commonjs/specs/NativeHotUpdater.d.ts +35 -47
- package/lib/typescript/commonjs/specs/NativeHotUpdater.d.ts.map +1 -1
- package/lib/typescript/commonjs/store.d.ts +1 -3
- package/lib/typescript/commonjs/store.d.ts.map +1 -1
- package/lib/typescript/commonjs/types.d.ts +1 -1
- package/lib/typescript/commonjs/types.d.ts.map +1 -1
- package/lib/typescript/commonjs/updateError.d.ts +26 -0
- package/lib/typescript/commonjs/updateError.d.ts.map +1 -0
- package/lib/typescript/commonjs/wrap.d.ts +2 -3
- package/lib/typescript/commonjs/wrap.d.ts.map +1 -1
- package/lib/typescript/commonjs/wrap.types.d.ts +13 -41
- package/lib/typescript/commonjs/wrap.types.d.ts.map +1 -1
- package/lib/typescript/module/appReady.d.ts +30 -0
- package/lib/typescript/module/appReady.d.ts.map +1 -0
- package/lib/typescript/module/checkForUpdate.d.ts +20 -6
- package/lib/typescript/module/checkForUpdate.d.ts.map +1 -1
- package/lib/typescript/module/clientPlugin.d.ts +2 -0
- package/lib/typescript/module/clientPlugin.d.ts.map +1 -0
- package/lib/typescript/module/error.d.ts +5 -14
- package/lib/typescript/module/error.d.ts.map +1 -1
- package/lib/typescript/module/fetchJSON.d.ts +5 -5
- package/lib/typescript/module/fetchJSON.d.ts.map +1 -1
- package/lib/typescript/module/fetchUpdateResponse.d.ts +14 -0
- package/lib/typescript/module/fetchUpdateResponse.d.ts.map +1 -0
- package/lib/typescript/module/httpClient.d.ts +5 -34
- package/lib/typescript/module/httpClient.d.ts.map +1 -1
- package/lib/typescript/module/index.d.ts +124 -116
- package/lib/typescript/module/index.d.ts.map +1 -1
- package/lib/typescript/module/init.types.d.ts +21 -0
- package/lib/typescript/module/init.types.d.ts.map +1 -0
- package/lib/typescript/module/native.d.ts +32 -25
- package/lib/typescript/module/native.d.ts.map +1 -1
- package/lib/typescript/module/pluginHost.d.ts +8 -0
- package/lib/typescript/module/pluginHost.d.ts.map +1 -0
- package/lib/typescript/module/releaseCatalogCache.d.ts +8 -14
- package/lib/typescript/module/releaseCatalogCache.d.ts.map +1 -1
- package/lib/typescript/module/sdkVersion.d.ts +1 -1
- package/lib/typescript/module/sdkVersion.d.ts.map +1 -1
- package/lib/typescript/module/specs/NativeHotUpdater.d.ts +35 -47
- package/lib/typescript/module/specs/NativeHotUpdater.d.ts.map +1 -1
- package/lib/typescript/module/store.d.ts +1 -3
- package/lib/typescript/module/store.d.ts.map +1 -1
- package/lib/typescript/module/types.d.ts +1 -1
- package/lib/typescript/module/types.d.ts.map +1 -1
- package/lib/typescript/module/updateError.d.ts +26 -0
- package/lib/typescript/module/updateError.d.ts.map +1 -0
- package/lib/typescript/module/wrap.d.ts +2 -3
- package/lib/typescript/module/wrap.d.ts.map +1 -1
- package/lib/typescript/module/wrap.types.d.ts +13 -41
- package/lib/typescript/module/wrap.types.d.ts.map +1 -1
- package/package.json +8 -4
- package/src/appReady.ts +147 -0
- package/src/checkForUpdate.ts +156 -89
- package/src/clientPlugin.ts +21 -0
- package/src/error.ts +5 -15
- package/src/fetchJSON.ts +14 -30
- package/src/fetchUpdateResponse.ts +60 -0
- package/src/httpClient.ts +89 -160
- package/src/index.ts +413 -425
- package/src/init.types.ts +24 -0
- package/src/native.ts +101 -105
- package/src/pluginHost.ts +52 -0
- package/src/releaseCatalogCache.ts +43 -172
- package/src/sdkVersion.ts +1 -1
- package/src/specs/NativeHotUpdater.ts +34 -48
- package/src/store.ts +2 -23
- package/src/types.ts +1 -1
- package/src/updateError.ts +167 -0
- package/src/wrap.tsx +60 -43
- package/src/wrap.types.ts +18 -46
- package/android/src/main/java/com/hotupdater/DecompressService.kt +0 -89
- package/android/src/main/java/com/hotupdater/DecompressionStrategy.kt +0 -26
- package/android/src/main/java/com/hotupdater/TarBrDecompressionStrategy.kt +0 -117
- package/android/src/main/java/com/hotupdater/TarGzDecompressionStrategy.kt +0 -117
- package/android/src/main/java/com/hotupdater/ZipDecompressionStrategy.kt +0 -192
- package/android/src/test/java/com/hotupdater/BsdiffPatchTest.kt +0 -46
- package/android/src/test/java/com/hotupdater/BundleFileStorageServiceTest.kt +0 -799
- package/android/src/test/java/com/hotupdater/HotUpdaterImplTest.kt +0 -273
- package/android/src/test/java/com/hotupdater/NativeConfigUtilsTest.kt +0 -20
- package/android/src/test/java/com/hotupdater/OkHttpDownloadServiceTest.kt +0 -111
- package/android/src/test/java/com/hotupdater/RelativePathResolverTest.kt +0 -41
- package/android/src/test/java/com/hotupdater/ReleaseCatalogCacheServiceTest.kt +0 -118
- package/android/src/test/java/com/hotupdater/TarArchiveInputStreamTest.kt +0 -68
- package/ios/HotUpdater/Internal/ArchiveExtractionUtilities.swift +0 -178
- package/ios/HotUpdater/Internal/DecompressService.swift +0 -112
- package/ios/HotUpdater/Internal/DecompressionStrategy.swift +0 -22
- package/ios/HotUpdater/Internal/StreamingTarArchiveExtractor.swift +0 -370
- package/ios/HotUpdater/Internal/TarBrDecompressionStrategy.swift +0 -39
- package/ios/HotUpdater/Internal/TarGzDecompressionStrategy.swift +0 -59
- package/ios/HotUpdater/Internal/ZipArchiveExtractor.swift +0 -482
- package/ios/HotUpdater/Internal/ZipDecompressionStrategy.swift +0 -56
- package/lib/commonjs/catalogCacheNative.spec.js +0 -31
- package/lib/commonjs/catalogCacheNative.spec.js.map +0 -1
- package/lib/commonjs/checkForUpdate.releaseCatalog.spec.js +0 -616
- package/lib/commonjs/checkForUpdate.releaseCatalog.spec.js.map +0 -1
- package/lib/commonjs/httpClient.spec.js +0 -102
- package/lib/commonjs/httpClient.spec.js.map +0 -1
- package/lib/commonjs/index.spec.js +0 -302
- package/lib/commonjs/index.spec.js.map +0 -1
- package/lib/commonjs/iosInterop.spec.js +0 -18
- package/lib/commonjs/iosInterop.spec.js.map +0 -1
- package/lib/commonjs/native.spec.js +0 -883
- package/lib/commonjs/native.spec.js.map +0 -1
- package/lib/commonjs/notifyAppReadyInsights.boundary.spec.js +0 -218
- package/lib/commonjs/notifyAppReadyInsights.boundary.spec.js.map +0 -1
- package/lib/commonjs/notifyAppReadyInsights.js +0 -133
- package/lib/commonjs/notifyAppReadyInsights.js.map +0 -1
- package/lib/commonjs/notifyAppReadyInsights.spec.js +0 -246
- package/lib/commonjs/notifyAppReadyInsights.spec.js.map +0 -1
- package/lib/commonjs/notifyAppReadyInsights.test-utils.js +0 -24
- package/lib/commonjs/notifyAppReadyInsights.test-utils.js.map +0 -1
- package/lib/commonjs/notifyAppReadyInsights.transport.spec.js +0 -117
- package/lib/commonjs/notifyAppReadyInsights.transport.spec.js.map +0 -1
- package/lib/commonjs/releaseCatalogCache.spec.js +0 -195
- package/lib/commonjs/releaseCatalogCache.spec.js.map +0 -1
- package/lib/commonjs/releaseCatalogCacheNativeContract.spec.js +0 -64
- package/lib/commonjs/releaseCatalogCacheNativeContract.spec.js.map +0 -1
- package/lib/commonjs/store.notification.spec.js +0 -76
- package/lib/commonjs/store.notification.spec.js.map +0 -1
- package/lib/commonjs/store.spec.js +0 -316
- package/lib/commonjs/store.spec.js.map +0 -1
- package/lib/commonjs/wrap.spec.js +0 -329
- package/lib/commonjs/wrap.spec.js.map +0 -1
- package/lib/module/catalogCacheNative.spec.js +0 -31
- package/lib/module/catalogCacheNative.spec.js.map +0 -1
- package/lib/module/checkForUpdate.releaseCatalog.spec.js +0 -615
- package/lib/module/checkForUpdate.releaseCatalog.spec.js.map +0 -1
- package/lib/module/httpClient.spec.js +0 -102
- package/lib/module/httpClient.spec.js.map +0 -1
- package/lib/module/index.spec.js +0 -301
- package/lib/module/index.spec.js.map +0 -1
- package/lib/module/iosInterop.spec.js +0 -18
- package/lib/module/iosInterop.spec.js.map +0 -1
- package/lib/module/native.spec.js +0 -882
- package/lib/module/native.spec.js.map +0 -1
- package/lib/module/notifyAppReadyInsights.boundary.spec.js +0 -217
- package/lib/module/notifyAppReadyInsights.boundary.spec.js.map +0 -1
- package/lib/module/notifyAppReadyInsights.js +0 -128
- package/lib/module/notifyAppReadyInsights.js.map +0 -1
- package/lib/module/notifyAppReadyInsights.spec.js +0 -245
- package/lib/module/notifyAppReadyInsights.spec.js.map +0 -1
- package/lib/module/notifyAppReadyInsights.test-utils.js +0 -18
- package/lib/module/notifyAppReadyInsights.test-utils.js.map +0 -1
- package/lib/module/notifyAppReadyInsights.transport.spec.js +0 -116
- package/lib/module/notifyAppReadyInsights.transport.spec.js.map +0 -1
- package/lib/module/releaseCatalogCache.spec.js +0 -195
- package/lib/module/releaseCatalogCache.spec.js.map +0 -1
- package/lib/module/releaseCatalogCacheNativeContract.spec.js +0 -64
- package/lib/module/releaseCatalogCacheNativeContract.spec.js.map +0 -1
- package/lib/module/store.notification.spec.js +0 -77
- package/lib/module/store.notification.spec.js.map +0 -1
- package/lib/module/store.spec.js +0 -315
- package/lib/module/store.spec.js.map +0 -1
- package/lib/module/wrap.spec.js +0 -330
- package/lib/module/wrap.spec.js.map +0 -1
- package/lib/typescript/commonjs/notifyAppReadyInsights.d.ts +0 -13
- package/lib/typescript/commonjs/notifyAppReadyInsights.d.ts.map +0 -1
- package/lib/typescript/commonjs/notifyAppReadyInsights.test-utils.d.ts +0 -8
- package/lib/typescript/commonjs/notifyAppReadyInsights.test-utils.d.ts.map +0 -1
- package/lib/typescript/module/notifyAppReadyInsights.d.ts +0 -13
- package/lib/typescript/module/notifyAppReadyInsights.d.ts.map +0 -1
- package/lib/typescript/module/notifyAppReadyInsights.test-utils.d.ts +0 -8
- package/lib/typescript/module/notifyAppReadyInsights.test-utils.d.ts.map +0 -1
- package/src/catalogCacheNative.spec.ts +0 -63
- package/src/checkForUpdate.releaseCatalog.spec.ts +0 -612
- package/src/httpClient.spec.ts +0 -132
- package/src/index.spec.ts +0 -337
- package/src/iosInterop.spec.ts +0 -30
- package/src/native.spec.ts +0 -945
- package/src/notifyAppReadyInsights.boundary.spec.ts +0 -234
- package/src/notifyAppReadyInsights.spec.ts +0 -243
- package/src/notifyAppReadyInsights.test-utils.ts +0 -31
- package/src/notifyAppReadyInsights.transport.spec.ts +0 -130
- package/src/notifyAppReadyInsights.ts +0 -194
- package/src/releaseCatalogCache.spec.ts +0 -282
- package/src/releaseCatalogCacheNativeContract.spec.ts +0 -80
- package/src/store.notification.spec.ts +0 -92
- package/src/store.spec.ts +0 -372
- package/src/wrap.spec.ts +0 -406
|
@@ -2,385 +2,256 @@ import Foundation
|
|
|
2
2
|
|
|
3
3
|
enum TarArchiveExtractor {
|
|
4
4
|
private static let blockSize = 512
|
|
5
|
-
|
|
6
5
|
private static let regularFileType: UInt8 = 48
|
|
7
6
|
private static let alternateRegularFileType: UInt8 = 0
|
|
8
|
-
private static let hardLinkType: UInt8 = 49
|
|
9
|
-
private static let symbolicLinkType: UInt8 = 50
|
|
10
|
-
private static let directoryType: UInt8 = 53
|
|
11
|
-
private static let contiguousFileType: UInt8 = 55
|
|
12
|
-
private static let globalPaxHeaderType: UInt8 = 103
|
|
13
7
|
private static let paxHeaderType: UInt8 = 120
|
|
14
|
-
private static let
|
|
15
|
-
private static let gnuLongLinkType: UInt8 = 75
|
|
8
|
+
private static let maximumPaxByteSize = 1024 * 1024
|
|
16
9
|
|
|
17
10
|
private struct Header {
|
|
18
11
|
let path: String
|
|
19
12
|
let size: UInt64
|
|
20
13
|
let typeFlag: UInt8
|
|
21
|
-
let linkName: String
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
static func containsEntries(at tarPath: String) throws -> Bool {
|
|
25
|
-
let handle = try FileHandle(forReadingFrom: URL(fileURLWithPath: tarPath))
|
|
26
|
-
|
|
27
|
-
defer {
|
|
28
|
-
try? handle.close()
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
var globalPaxHeaders: [String: String] = [:]
|
|
32
|
-
var pendingPaxHeaders: [String: String] = [:]
|
|
33
|
-
var pendingLongPath: String?
|
|
34
|
-
var pendingLongLink: String?
|
|
35
|
-
|
|
36
|
-
while true {
|
|
37
|
-
let headerBlock = try ArchiveExtractionUtilities.readExactly(from: handle, count: blockSize)
|
|
38
|
-
guard !isZeroBlock(headerBlock) else {
|
|
39
|
-
return false
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
let header = try parseHeader(from: headerBlock)
|
|
43
|
-
|
|
44
|
-
switch header.typeFlag {
|
|
45
|
-
case globalPaxHeaderType:
|
|
46
|
-
let paxData = try readEntryPayloadData(from: handle, size: header.size)
|
|
47
|
-
globalPaxHeaders.merge(parsePaxHeaders(from: paxData)) { _, newValue in
|
|
48
|
-
newValue
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
case paxHeaderType:
|
|
52
|
-
let paxData = try readEntryPayloadData(from: handle, size: header.size)
|
|
53
|
-
pendingPaxHeaders.merge(parsePaxHeaders(from: paxData)) { _, newValue in
|
|
54
|
-
newValue
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
case gnuLongNameType:
|
|
58
|
-
pendingLongPath = decodeLongPath(from: try readEntryPayloadData(from: handle, size: header.size))
|
|
59
|
-
|
|
60
|
-
case gnuLongLinkType:
|
|
61
|
-
pendingLongLink = decodeLongPath(from: try readEntryPayloadData(from: handle, size: header.size))
|
|
62
|
-
|
|
63
|
-
default:
|
|
64
|
-
let effectiveHeaders = globalPaxHeaders.merging(pendingPaxHeaders) { _, newValue in
|
|
65
|
-
newValue
|
|
66
|
-
}
|
|
67
|
-
let resolvedPath = pendingLongPath ?? effectiveHeaders["path"] ?? header.path
|
|
68
|
-
_ = pendingLongLink ?? effectiveHeaders["linkpath"] ?? header.linkName
|
|
69
|
-
|
|
70
|
-
defer {
|
|
71
|
-
pendingPaxHeaders.removeAll()
|
|
72
|
-
pendingLongPath = nil
|
|
73
|
-
pendingLongLink = nil
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
if let normalizedPath = ArchiveExtractionUtilities.normalizedRelativePath(from: resolvedPath),
|
|
77
|
-
!normalizedPath.isEmpty {
|
|
78
|
-
return true
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
try skipEntryPayload(in: handle, size: header.size)
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
14
|
}
|
|
85
15
|
|
|
86
16
|
static func extract(
|
|
87
17
|
from tarPath: String,
|
|
88
18
|
to destination: String,
|
|
89
|
-
|
|
19
|
+
expectedFiles: [String: Int64]
|
|
90
20
|
) throws {
|
|
91
|
-
let
|
|
92
|
-
|
|
93
|
-
|
|
21
|
+
let destinationURL = URL(fileURLWithPath: destination, isDirectory: true)
|
|
22
|
+
try FileManager.default.createDirectory(
|
|
23
|
+
at: destinationURL,
|
|
24
|
+
withIntermediateDirectories: true
|
|
25
|
+
)
|
|
94
26
|
|
|
95
|
-
let tarSize = try archiveFileSize(at: tarPath)
|
|
96
27
|
let handle = try FileHandle(forReadingFrom: URL(fileURLWithPath: tarPath))
|
|
28
|
+
defer { try? handle.close() }
|
|
97
29
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
var globalPaxHeaders: [String: String] = [:]
|
|
103
|
-
var pendingPaxHeaders: [String: String] = [:]
|
|
104
|
-
var pendingLongPath: String?
|
|
105
|
-
var pendingLongLink: String?
|
|
30
|
+
var extractedPaths = Set<String>()
|
|
31
|
+
var pendingPaxHeaders: [String: String]?
|
|
106
32
|
|
|
107
33
|
while true {
|
|
108
|
-
let headerBlock = try
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
let header = try parseHeader(from: headerBlock)
|
|
114
|
-
|
|
115
|
-
switch header.typeFlag {
|
|
116
|
-
case globalPaxHeaderType:
|
|
117
|
-
let paxData = try readEntryPayloadData(from: handle, size: header.size)
|
|
118
|
-
globalPaxHeaders.merge(parsePaxHeaders(from: paxData)) { _, newValue in
|
|
119
|
-
newValue
|
|
34
|
+
let headerBlock = try readExactly(from: handle, count: blockSize)
|
|
35
|
+
if isZeroBlock(headerBlock) {
|
|
36
|
+
let secondEndBlock = try readExactly(from: handle, count: blockSize)
|
|
37
|
+
guard isZeroBlock(secondEndBlock), pendingPaxHeaders == nil else {
|
|
38
|
+
throw archiveError(1, "Invalid TAR termination")
|
|
120
39
|
}
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
pendingPaxHeaders.merge(parsePaxHeaders(from: paxData)) { _, newValue in
|
|
125
|
-
newValue
|
|
40
|
+
try requireZeroPaddingToEnd(handle)
|
|
41
|
+
guard extractedPaths == Set(expectedFiles.keys) else {
|
|
42
|
+
throw archiveError(2, "TAR file set does not match the manifest")
|
|
126
43
|
}
|
|
44
|
+
return
|
|
45
|
+
}
|
|
127
46
|
|
|
128
|
-
|
|
129
|
-
|
|
47
|
+
let header = try parseHeader(headerBlock)
|
|
48
|
+
if header.typeFlag == paxHeaderType {
|
|
49
|
+
guard pendingPaxHeaders == nil,
|
|
50
|
+
header.size <= UInt64(maximumPaxByteSize) else {
|
|
51
|
+
throw archiveError(3, "Invalid PAX header")
|
|
52
|
+
}
|
|
53
|
+
let payload = try readPayload(from: handle, size: header.size)
|
|
54
|
+
pendingPaxHeaders = try parsePaxHeaders(payload)
|
|
55
|
+
continue
|
|
56
|
+
}
|
|
130
57
|
|
|
131
|
-
|
|
132
|
-
|
|
58
|
+
guard header.typeFlag == regularFileType ||
|
|
59
|
+
header.typeFlag == alternateRegularFileType else {
|
|
60
|
+
throw archiveError(4, "Unsupported TAR entry type")
|
|
61
|
+
}
|
|
133
62
|
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
63
|
+
let paxHeaders = pendingPaxHeaders ?? [:]
|
|
64
|
+
pendingPaxHeaders = nil
|
|
65
|
+
guard paxHeaders["linkpath"] == nil else {
|
|
66
|
+
throw archiveError(5, "TAR links are not allowed")
|
|
67
|
+
}
|
|
68
|
+
let rawPath = paxHeaders["path"] ?? header.path
|
|
69
|
+
guard FileUtilities.normalizedRelativePath(from: rawPath) == rawPath,
|
|
70
|
+
rawPath != "manifest.json",
|
|
71
|
+
!extractedPaths.contains(rawPath),
|
|
72
|
+
let expectedByteSize = expectedFiles[rawPath],
|
|
73
|
+
expectedByteSize >= 0 else {
|
|
74
|
+
throw archiveError(6, "TAR contains an unsafe, duplicate, or unknown path")
|
|
75
|
+
}
|
|
140
76
|
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
77
|
+
let entrySize: UInt64
|
|
78
|
+
if let paxSize = paxHeaders["size"] {
|
|
79
|
+
guard let parsedSize = UInt64(paxSize) else {
|
|
80
|
+
throw archiveError(7, "Invalid PAX entry size")
|
|
145
81
|
}
|
|
82
|
+
entrySize = parsedSize
|
|
83
|
+
} else {
|
|
84
|
+
entrySize = header.size
|
|
85
|
+
}
|
|
86
|
+
guard entrySize == UInt64(expectedByteSize) else {
|
|
87
|
+
throw archiveError(8, "TAR entry size does not match the manifest")
|
|
88
|
+
}
|
|
146
89
|
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
90
|
+
let outputURL = try FileUtilities.fileURL(
|
|
91
|
+
for: rawPath,
|
|
92
|
+
destinationRoot: destination
|
|
93
|
+
)
|
|
94
|
+
try FileManager.default.createDirectory(
|
|
95
|
+
at: outputURL.deletingLastPathComponent(),
|
|
96
|
+
withIntermediateDirectories: true
|
|
97
|
+
)
|
|
98
|
+
guard FileManager.default.createFile(atPath: outputURL.path, contents: nil) else {
|
|
99
|
+
throw archiveError(9, "Failed to create TAR output file")
|
|
100
|
+
}
|
|
101
|
+
let output = try FileHandle(forWritingTo: outputURL)
|
|
102
|
+
do {
|
|
103
|
+
try copyPayload(
|
|
152
104
|
from: handle,
|
|
153
|
-
|
|
105
|
+
size: entrySize,
|
|
106
|
+
to: output
|
|
154
107
|
)
|
|
108
|
+
try output.close()
|
|
109
|
+
} catch {
|
|
110
|
+
try? output.close()
|
|
111
|
+
try? FileManager.default.removeItem(at: outputURL)
|
|
112
|
+
throw error
|
|
155
113
|
}
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
let offset = ArchiveExtractionUtilities.currentOffset(for: handle)
|
|
159
|
-
let progress = min(Double(offset) / Double(tarSize), 1.0)
|
|
160
|
-
progressHandler(progress)
|
|
161
|
-
}
|
|
114
|
+
try readPadding(from: handle, size: entrySize)
|
|
115
|
+
extractedPaths.insert(rawPath)
|
|
162
116
|
}
|
|
163
|
-
|
|
164
|
-
progressHandler(1.0)
|
|
165
117
|
}
|
|
166
118
|
|
|
167
|
-
private static func
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
from handle: FileHandle,
|
|
173
|
-
to destinationRoot: String
|
|
174
|
-
) throws {
|
|
175
|
-
guard let relativePath = ArchiveExtractionUtilities.normalizedRelativePath(from: rawPath) else {
|
|
176
|
-
try skipEntryPayload(in: handle, size: size)
|
|
177
|
-
return
|
|
119
|
+
private static func parseHeader(_ block: Data) throws -> Header {
|
|
120
|
+
guard block.count == blockSize,
|
|
121
|
+
Data(block[257..<262]) == Data("ustar".utf8),
|
|
122
|
+
try verifyChecksum(block) else {
|
|
123
|
+
throw archiveError(10, "Invalid USTAR header")
|
|
178
124
|
}
|
|
179
|
-
|
|
180
|
-
let
|
|
181
|
-
|
|
182
|
-
|
|
125
|
+
let name = try parseString(block[0..<100])
|
|
126
|
+
let prefix = try parseString(block[345..<500])
|
|
127
|
+
let path = prefix.isEmpty ? name : (name.isEmpty ? prefix : "\(prefix)/\(name)")
|
|
128
|
+
return Header(
|
|
129
|
+
path: path,
|
|
130
|
+
size: try parseOctal(block[124..<136]),
|
|
131
|
+
typeFlag: block[156]
|
|
183
132
|
)
|
|
184
|
-
|
|
185
|
-
switch typeFlag {
|
|
186
|
-
case directoryType:
|
|
187
|
-
try ArchiveExtractionUtilities.ensureDirectory(at: targetURL)
|
|
188
|
-
try skipEntryPayload(in: handle, size: size)
|
|
189
|
-
|
|
190
|
-
case regularFileType, alternateRegularFileType, contiguousFileType:
|
|
191
|
-
let outputHandle = try ArchiveExtractionUtilities.createOutputFile(at: targetURL)
|
|
192
|
-
|
|
193
|
-
defer {
|
|
194
|
-
try? outputHandle.close()
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
try copyEntryPayload(from: handle, size: size, to: outputHandle)
|
|
198
|
-
try skipPadding(in: handle, size: size)
|
|
199
|
-
|
|
200
|
-
case hardLinkType, symbolicLinkType:
|
|
201
|
-
NSLog("[TarArchiveExtractor] Skipping link entry: \(rawPath) -> \(linkPath)")
|
|
202
|
-
try skipEntryPayload(in: handle, size: size)
|
|
203
|
-
|
|
204
|
-
default:
|
|
205
|
-
NSLog("[TarArchiveExtractor] Skipping unsupported TAR entry type: \(typeFlag) (\(rawPath))")
|
|
206
|
-
try skipEntryPayload(in: handle, size: size)
|
|
207
|
-
}
|
|
208
133
|
}
|
|
209
134
|
|
|
210
|
-
private static func
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
var remainingBytes = size
|
|
216
|
-
|
|
217
|
-
while remainingBytes > 0 {
|
|
218
|
-
let chunkSize = Int(min(remainingBytes, UInt64(ArchiveExtractionUtilities.bufferSize)))
|
|
219
|
-
let chunk = try ArchiveExtractionUtilities.readExactly(from: handle, count: chunkSize)
|
|
220
|
-
outputHandle.write(chunk)
|
|
221
|
-
remainingBytes -= UInt64(chunk.count)
|
|
135
|
+
private static func verifyChecksum(_ block: Data) throws -> Bool {
|
|
136
|
+
let expected = try parseOctal(block[148..<156])
|
|
137
|
+
var actual: UInt64 = 0
|
|
138
|
+
for index in block.indices {
|
|
139
|
+
actual += UInt64((148..<156).contains(index) ? 32 : block[index])
|
|
222
140
|
}
|
|
141
|
+
return actual == expected
|
|
223
142
|
}
|
|
224
143
|
|
|
225
|
-
private static func
|
|
226
|
-
|
|
227
|
-
|
|
144
|
+
private static func parseOctal(_ bytes: Data.SubSequence) throws -> UInt64 {
|
|
145
|
+
let value = try parseString(bytes)
|
|
146
|
+
.trimmingCharacters(in: CharacterSet(charactersIn: " \0"))
|
|
147
|
+
if value.isEmpty { return 0 }
|
|
148
|
+
guard value.allSatisfy({ ("0"..."7").contains($0) }),
|
|
149
|
+
let parsed = UInt64(value, radix: 8) else {
|
|
150
|
+
throw archiveError(11, "Invalid TAR numeric field")
|
|
228
151
|
}
|
|
229
|
-
|
|
230
|
-
guard size <= UInt64(Int.max) else {
|
|
231
|
-
throw NSError(
|
|
232
|
-
domain: "TarArchiveExtractor",
|
|
233
|
-
code: 3,
|
|
234
|
-
userInfo: [NSLocalizedDescriptionKey: "TAR payload exceeds supported in-memory size: \(size) bytes"]
|
|
235
|
-
)
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
let payload = try ArchiveExtractionUtilities.readExactly(from: handle, count: Int(size))
|
|
239
|
-
try skipPadding(in: handle, size: size)
|
|
240
|
-
return payload
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
private static func skipEntryPayload(in handle: FileHandle, size: UInt64) throws {
|
|
244
|
-
try ArchiveExtractionUtilities.skipBytes(size, in: handle)
|
|
245
|
-
try skipPadding(in: handle, size: size)
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
private static func skipPadding(in handle: FileHandle, size: UInt64) throws {
|
|
249
|
-
let padding = (UInt64(blockSize) - (size % UInt64(blockSize))) % UInt64(blockSize)
|
|
250
|
-
try ArchiveExtractionUtilities.skipBytes(padding, in: handle)
|
|
152
|
+
return parsed
|
|
251
153
|
}
|
|
252
154
|
|
|
253
|
-
private static func
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
code: 1,
|
|
258
|
-
userInfo: [NSLocalizedDescriptionKey: "Invalid TAR block size: \(block.count)"]
|
|
259
|
-
)
|
|
155
|
+
private static func parseString(_ bytes: Data.SubSequence) throws -> String {
|
|
156
|
+
let value = bytes.prefix { $0 != 0 }
|
|
157
|
+
guard let decoded = String(data: Data(value), encoding: .utf8) else {
|
|
158
|
+
throw archiveError(12, "Invalid UTF-8 in TAR header")
|
|
260
159
|
}
|
|
261
|
-
|
|
262
|
-
return Header(
|
|
263
|
-
path: parseTarPath(from: block),
|
|
264
|
-
size: try parseTarNumber(block[124..<136]),
|
|
265
|
-
typeFlag: block[156],
|
|
266
|
-
linkName: parseCString(block[157..<257])
|
|
267
|
-
)
|
|
160
|
+
return decoded
|
|
268
161
|
}
|
|
269
162
|
|
|
270
|
-
private static func
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
163
|
+
private static func parsePaxHeaders(_ data: Data) throws -> [String: String] {
|
|
164
|
+
var headers: [String: String] = [:]
|
|
165
|
+
var offset = 0
|
|
166
|
+
while offset < data.count {
|
|
167
|
+
guard let space = data[offset...].firstIndex(of: 0x20),
|
|
168
|
+
let lengthText = String(data: data[offset..<space], encoding: .ascii),
|
|
169
|
+
let recordLength = Int(lengthText),
|
|
170
|
+
recordLength > space - offset + 2 else {
|
|
171
|
+
throw archiveError(13, "Malformed PAX record")
|
|
172
|
+
}
|
|
173
|
+
let (end, overflowed) = offset.addingReportingOverflow(recordLength)
|
|
174
|
+
guard !overflowed, end <= data.count, data[end - 1] == 0x0A else {
|
|
175
|
+
throw archiveError(13, "Malformed PAX record")
|
|
176
|
+
}
|
|
177
|
+
let bodyStart = space + 1
|
|
178
|
+
guard let equals = data[bodyStart..<(end - 1)].firstIndex(of: 0x3D),
|
|
179
|
+
let key = String(data: data[bodyStart..<equals], encoding: .utf8),
|
|
180
|
+
let value = String(data: data[(equals + 1)..<(end - 1)], encoding: .utf8),
|
|
181
|
+
!key.isEmpty,
|
|
182
|
+
headers[key] == nil else {
|
|
183
|
+
throw archiveError(13, "Malformed PAX record")
|
|
184
|
+
}
|
|
185
|
+
headers[key] = value
|
|
186
|
+
offset = end
|
|
280
187
|
}
|
|
281
|
-
|
|
282
|
-
return "\(prefix)/\(name)"
|
|
188
|
+
return headers
|
|
283
189
|
}
|
|
284
190
|
|
|
285
|
-
private static func
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
191
|
+
private static func copyPayload(
|
|
192
|
+
from input: FileHandle,
|
|
193
|
+
size: UInt64,
|
|
194
|
+
to output: FileHandle
|
|
195
|
+
) throws {
|
|
196
|
+
var remaining = size
|
|
197
|
+
while remaining > 0 {
|
|
198
|
+
let count = Int(min(remaining, 64 * 1024))
|
|
199
|
+
let chunk = try readExactly(from: input, count: count)
|
|
200
|
+
output.write(chunk)
|
|
201
|
+
remaining -= UInt64(chunk.count)
|
|
293
202
|
}
|
|
294
|
-
|
|
295
|
-
return String(decoding: bytes, as: UTF8.self)
|
|
296
203
|
}
|
|
297
204
|
|
|
298
|
-
private static func
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
return 0
|
|
205
|
+
private static func readPayload(from handle: FileHandle, size: UInt64) throws -> Data {
|
|
206
|
+
guard size <= UInt64(Int.max) else {
|
|
207
|
+
throw archiveError(14, "TAR payload is too large")
|
|
302
208
|
}
|
|
209
|
+
let payload = try readExactly(from: handle, count: Int(size))
|
|
210
|
+
try readPadding(from: handle, size: size)
|
|
211
|
+
return payload
|
|
212
|
+
}
|
|
303
213
|
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
214
|
+
private static func readPadding(from handle: FileHandle, size: UInt64) throws {
|
|
215
|
+
let padding = (UInt64(blockSize) - size % UInt64(blockSize)) % UInt64(blockSize)
|
|
216
|
+
if padding > 0 {
|
|
217
|
+
let bytes = try readExactly(from: handle, count: Int(padding))
|
|
218
|
+
guard bytes.allSatisfy({ $0 == 0 }) else {
|
|
219
|
+
throw archiveError(15, "Invalid TAR entry padding")
|
|
308
220
|
}
|
|
309
|
-
return value
|
|
310
221
|
}
|
|
311
|
-
|
|
312
|
-
let stringValue = String(bytes: bytes, encoding: .ascii)?
|
|
313
|
-
.trimmingCharacters(in: CharacterSet(charactersIn: "\0 "))
|
|
314
|
-
|
|
315
|
-
guard let stringValue, !stringValue.isEmpty,
|
|
316
|
-
let parsedValue = UInt64(stringValue, radix: 8) else {
|
|
317
|
-
throw NSError(
|
|
318
|
-
domain: "TarArchiveExtractor",
|
|
319
|
-
code: 2,
|
|
320
|
-
userInfo: [NSLocalizedDescriptionKey: "Invalid TAR numeric field"]
|
|
321
|
-
)
|
|
322
|
-
}
|
|
323
|
-
|
|
324
|
-
return parsedValue
|
|
325
222
|
}
|
|
326
223
|
|
|
327
|
-
private static func
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
}
|
|
338
|
-
|
|
339
|
-
let recordEnd = index + recordLength
|
|
340
|
-
guard recordEnd <= data.endIndex else {
|
|
341
|
-
break
|
|
342
|
-
}
|
|
343
|
-
|
|
344
|
-
let recordBodyStart = data.index(after: spaceIndex)
|
|
345
|
-
let recordBody = data[recordBodyStart..<recordEnd]
|
|
346
|
-
|
|
347
|
-
if let newlineIndex = recordBody.lastIndex(of: 0x0A),
|
|
348
|
-
let separatorIndex = recordBody[..<newlineIndex].firstIndex(of: 0x3D),
|
|
349
|
-
let key = String(data: recordBody[..<separatorIndex], encoding: .utf8),
|
|
350
|
-
let value = String(data: recordBody[recordBody.index(after: separatorIndex)..<newlineIndex], encoding: .utf8) {
|
|
351
|
-
headers[key] = value
|
|
224
|
+
private static func requireZeroPaddingToEnd(_ handle: FileHandle) throws {
|
|
225
|
+
while true {
|
|
226
|
+
let bytes = try FileUtilities.readUpToCount(
|
|
227
|
+
from: handle,
|
|
228
|
+
count: blockSize
|
|
229
|
+
) ?? Data()
|
|
230
|
+
if bytes.isEmpty { return }
|
|
231
|
+
guard bytes.count == blockSize,
|
|
232
|
+
bytes.allSatisfy({ $0 == 0 }) else {
|
|
233
|
+
throw archiveError(16, "TAR contains trailing data")
|
|
352
234
|
}
|
|
353
|
-
|
|
354
|
-
index = recordEnd
|
|
355
235
|
}
|
|
356
|
-
|
|
357
|
-
return headers
|
|
358
236
|
}
|
|
359
237
|
|
|
360
|
-
private static func
|
|
361
|
-
let
|
|
362
|
-
|
|
363
|
-
|
|
238
|
+
private static func readExactly(from handle: FileHandle, count: Int) throws -> Data {
|
|
239
|
+
guard let data = try FileUtilities.readUpToCount(from: handle, count: count),
|
|
240
|
+
data.count == count else {
|
|
241
|
+
throw archiveError(17, "Truncated TAR archive")
|
|
364
242
|
}
|
|
365
|
-
|
|
366
|
-
return String(decoding: trimmedData, as: UTF8.self)
|
|
367
|
-
.trimmingCharacters(in: .newlines)
|
|
243
|
+
return data
|
|
368
244
|
}
|
|
369
245
|
|
|
370
246
|
private static func isZeroBlock(_ data: Data) -> Bool {
|
|
371
247
|
data.allSatisfy { $0 == 0 }
|
|
372
248
|
}
|
|
373
249
|
|
|
374
|
-
private static func
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
if let value = attributes[.size] as? UInt64 {
|
|
381
|
-
return value
|
|
382
|
-
}
|
|
383
|
-
|
|
384
|
-
return 0
|
|
250
|
+
private static func archiveError(_ code: Int, _ message: String) -> NSError {
|
|
251
|
+
NSError(
|
|
252
|
+
domain: "TarArchiveExtractor",
|
|
253
|
+
code: code,
|
|
254
|
+
userInfo: [NSLocalizedDescriptionKey: message]
|
|
255
|
+
)
|
|
385
256
|
}
|
|
386
257
|
}
|