@onekeyfe/react-native-range-downloader 3.0.81 → 3.0.82
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/ReactNativeRangeDownloader.podspec +7 -0
- package/android/src/main/java/com/margelo/nitro/reactnativerangedownloader/FirmwareArchiveRules.kt +291 -0
- package/android/src/main/java/com/margelo/nitro/reactnativerangedownloader/FirmwareArtifactOrphanSweep.kt +111 -0
- package/android/src/main/java/com/margelo/nitro/reactnativerangedownloader/FirmwareArtifactStore.kt +822 -0
- package/android/src/main/java/com/margelo/nitro/reactnativerangedownloader/ReactNativeRangeDownloader.kt +148 -0
- package/android/src/test/java/com/margelo/nitro/reactnativerangedownloader/FirmwareArchiveRulesTest.kt +114 -0
- package/android/src/test/java/com/margelo/nitro/reactnativerangedownloader/FirmwareArtifactDeadlineTest.kt +26 -0
- package/android/src/test/java/com/margelo/nitro/reactnativerangedownloader/FirmwareArtifactOrphanSweepTest.kt +107 -0
- package/ios/FirmwareArchiveMinizipBridge.h +33 -0
- package/ios/FirmwareArchiveMinizipBridge.mm +213 -0
- package/ios/FirmwareArtifactStore.swift +1508 -0
- package/ios/RangeDownloadLogic.swift +317 -0
- package/ios/ReactNativeRangeDownloader.swift +214 -6
- package/lib/module/index.js +2 -1
- package/lib/module/index.js.map +1 -1
- package/lib/typescript/src/ReactNativeRangeDownloader.nitro.d.ts +92 -1
- package/lib/typescript/src/ReactNativeRangeDownloader.nitro.d.ts.map +1 -1
- package/lib/typescript/src/index.d.ts +1 -0
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/nitrogen/generated/android/c++/JDownloadChannel.hpp +8 -6
- package/nitrogen/generated/android/c++/JFirmwareArchiveExpectedEntry.hpp +69 -0
- package/nitrogen/generated/android/c++/JFirmwareArchiveMaterializeParams.hpp +87 -0
- package/nitrogen/generated/android/c++/JFirmwareArchiveMaterializeResult.hpp +80 -0
- package/nitrogen/generated/android/c++/JFirmwareArchiveMaterializedArtifact.hpp +63 -0
- package/nitrogen/generated/android/c++/JFirmwareArtifactCancelParams.hpp +57 -0
- package/nitrogen/generated/android/c++/JFirmwareArtifactCapabilities.hpp +88 -0
- package/nitrogen/generated/android/c++/JFirmwareArtifactDownloadParams.hpp +98 -0
- package/nitrogen/generated/android/c++/JFirmwareArtifactLease.hpp +57 -0
- package/nitrogen/generated/android/c++/JFirmwareArtifactLeaseCreateParams.hpp +57 -0
- package/nitrogen/generated/android/c++/JFirmwareArtifactLeaseReleaseParams.hpp +61 -0
- package/nitrogen/generated/android/c++/JFirmwareArtifactLeaseRetainParams.hpp +61 -0
- package/nitrogen/generated/android/c++/JFirmwareArtifactReaderCloseParams.hpp +57 -0
- package/nitrogen/generated/android/c++/JFirmwareArtifactReaderInfo.hpp +61 -0
- package/nitrogen/generated/android/c++/JFirmwareArtifactReaderReadParams.hpp +65 -0
- package/nitrogen/generated/android/c++/JFirmwareArtifactReceipt.hpp +69 -0
- package/nitrogen/generated/android/c++/JFirmwareArtifactRefParams.hpp +57 -0
- package/nitrogen/generated/android/c++/JFirmwareArtifactSweepResult.hpp +61 -0
- package/nitrogen/generated/android/c++/JFunc_void_RangeDownloadEvent.hpp +3 -3
- package/nitrogen/generated/android/c++/JHybridReactNativeRangeDownloaderSpec.cpp +274 -25
- package/nitrogen/generated/android/c++/JHybridReactNativeRangeDownloaderSpec.hpp +32 -22
- package/nitrogen/generated/android/c++/JRangeDownloadEvent.hpp +2 -2
- package/nitrogen/generated/android/c++/JRangeDownloadOutcome.hpp +6 -7
- package/nitrogen/generated/android/c++/JRangeDownloadParams.hpp +2 -2
- package/nitrogen/generated/android/c++/JRangeDownloadResult.hpp +2 -2
- package/nitrogen/generated/android/c++/JRangeFallbackKind.hpp +12 -13
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/reactnativerangedownloader/DownloadChannel.kt +5 -2
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/reactnativerangedownloader/FirmwareArchiveExpectedEntry.kt +47 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/reactnativerangedownloader/FirmwareArchiveMaterializeParams.kt +44 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/reactnativerangedownloader/FirmwareArchiveMaterializeResult.kt +38 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/reactnativerangedownloader/FirmwareArchiveMaterializedArtifact.kt +41 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/reactnativerangedownloader/FirmwareArtifactCancelParams.kt +38 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/reactnativerangedownloader/FirmwareArtifactCapabilities.kt +47 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/reactnativerangedownloader/FirmwareArtifactDownloadParams.kt +68 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/reactnativerangedownloader/FirmwareArtifactLease.kt +38 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/reactnativerangedownloader/FirmwareArtifactLeaseCreateParams.kt +38 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/reactnativerangedownloader/FirmwareArtifactLeaseReleaseParams.kt +41 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/reactnativerangedownloader/FirmwareArtifactLeaseRetainParams.kt +41 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/reactnativerangedownloader/FirmwareArtifactReaderCloseParams.kt +38 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/reactnativerangedownloader/FirmwareArtifactReaderInfo.kt +41 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/reactnativerangedownloader/FirmwareArtifactReaderReadParams.kt +44 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/reactnativerangedownloader/FirmwareArtifactReceipt.kt +47 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/reactnativerangedownloader/FirmwareArtifactRefParams.kt +38 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/reactnativerangedownloader/FirmwareArtifactSweepResult.kt +41 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/reactnativerangedownloader/Func_void_RangeDownloadEvent.kt +1 -1
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/reactnativerangedownloader/HybridReactNativeRangeDownloaderSpec.kt +65 -19
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/reactnativerangedownloader/RangeDownloadEvent.kt +2 -2
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/reactnativerangedownloader/RangeDownloadOutcome.kt +3 -1
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/reactnativerangedownloader/RangeDownloadParams.kt +2 -2
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/reactnativerangedownloader/RangeDownloadResult.kt +2 -2
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/reactnativerangedownloader/RangeFallbackKind.kt +3 -1
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/reactnativerangedownloader/reactnativerangedownloaderOnLoad.kt +1 -1
- package/nitrogen/generated/android/reactnativerangedownloader+autolinking.cmake +1 -1
- package/nitrogen/generated/android/reactnativerangedownloader+autolinking.gradle +1 -1
- package/nitrogen/generated/android/reactnativerangedownloaderOnLoad.cpp +28 -18
- package/nitrogen/generated/android/reactnativerangedownloaderOnLoad.hpp +14 -5
- package/nitrogen/generated/ios/ReactNativeRangeDownloader+autolinking.rb +2 -2
- package/nitrogen/generated/ios/ReactNativeRangeDownloader-Swift-Cxx-Bridge.cpp +49 -1
- package/nitrogen/generated/ios/ReactNativeRangeDownloader-Swift-Cxx-Bridge.hpp +348 -4
- package/nitrogen/generated/ios/ReactNativeRangeDownloader-Swift-Cxx-Umbrella.hpp +54 -1
- package/nitrogen/generated/ios/ReactNativeRangeDownloaderAutolinking.mm +1 -1
- package/nitrogen/generated/ios/ReactNativeRangeDownloaderAutolinking.swift +9 -8
- package/nitrogen/generated/ios/c++/HybridReactNativeRangeDownloaderSpecSwift.cpp +1 -1
- package/nitrogen/generated/ios/c++/HybridReactNativeRangeDownloaderSpecSwift.hpp +159 -1
- package/nitrogen/generated/ios/swift/DownloadChannel.swift +5 -1
- package/nitrogen/generated/ios/swift/FirmwareArchiveExpectedEntry.swift +44 -0
- package/nitrogen/generated/ios/swift/FirmwareArchiveMaterializeParams.swift +58 -0
- package/nitrogen/generated/ios/swift/FirmwareArchiveMaterializeResult.swift +35 -0
- package/nitrogen/generated/ios/swift/FirmwareArchiveMaterializedArtifact.swift +34 -0
- package/nitrogen/generated/ios/swift/FirmwareArtifactCancelParams.swift +29 -0
- package/nitrogen/generated/ios/swift/FirmwareArtifactCapabilities.swift +50 -0
- package/nitrogen/generated/ios/swift/FirmwareArtifactDownloadParams.swift +131 -0
- package/nitrogen/generated/ios/swift/FirmwareArtifactLease.swift +29 -0
- package/nitrogen/generated/ios/swift/FirmwareArtifactLeaseCreateParams.swift +29 -0
- package/nitrogen/generated/ios/swift/FirmwareArtifactLeaseReleaseParams.swift +34 -0
- package/nitrogen/generated/ios/swift/FirmwareArtifactLeaseRetainParams.swift +34 -0
- package/nitrogen/generated/ios/swift/FirmwareArtifactReaderCloseParams.swift +29 -0
- package/nitrogen/generated/ios/swift/FirmwareArtifactReaderInfo.swift +34 -0
- package/nitrogen/generated/ios/swift/FirmwareArtifactReaderReadParams.swift +39 -0
- package/nitrogen/generated/ios/swift/FirmwareArtifactReceipt.swift +44 -0
- package/nitrogen/generated/ios/swift/FirmwareArtifactRefParams.swift +29 -0
- package/nitrogen/generated/ios/swift/FirmwareArtifactSweepResult.swift +34 -0
- package/nitrogen/generated/ios/swift/Func_void.swift +1 -2
- package/nitrogen/generated/ios/swift/Func_void_FirmwareArchiveMaterializeResult.swift +46 -0
- package/nitrogen/generated/ios/swift/Func_void_FirmwareArtifactLease.swift +46 -0
- package/nitrogen/generated/ios/swift/Func_void_FirmwareArtifactReaderInfo.swift +46 -0
- package/nitrogen/generated/ios/swift/Func_void_FirmwareArtifactReceipt.swift +46 -0
- package/nitrogen/generated/ios/swift/Func_void_FirmwareArtifactSweepResult.swift +46 -0
- package/nitrogen/generated/ios/swift/Func_void_RangeDownloadEvent.swift +1 -2
- package/nitrogen/generated/ios/swift/Func_void_RangeDownloadResult.swift +1 -2
- package/nitrogen/generated/ios/swift/Func_void_std__exception_ptr.swift +1 -2
- package/nitrogen/generated/ios/swift/Func_void_std__shared_ptr_ArrayBuffer_.swift +46 -0
- package/nitrogen/generated/ios/swift/HybridReactNativeRangeDownloaderSpec.swift +15 -4
- package/nitrogen/generated/ios/swift/HybridReactNativeRangeDownloaderSpec_cxx.swift +230 -2
- package/nitrogen/generated/ios/swift/RangeDownloadEvent.swift +11 -42
- package/nitrogen/generated/ios/swift/RangeDownloadOutcome.swift +1 -1
- package/nitrogen/generated/ios/swift/RangeDownloadParams.swift +72 -139
- package/nitrogen/generated/ios/swift/RangeDownloadResult.swift +16 -53
- package/nitrogen/generated/ios/swift/RangeFallbackKind.swift +1 -1
- package/nitrogen/generated/shared/c++/DownloadChannel.hpp +5 -1
- package/nitrogen/generated/shared/c++/FirmwareArchiveExpectedEntry.hpp +95 -0
- package/nitrogen/generated/shared/c++/FirmwareArchiveMaterializeParams.hpp +95 -0
- package/nitrogen/generated/shared/c++/FirmwareArchiveMaterializeResult.hpp +85 -0
- package/nitrogen/generated/shared/c++/FirmwareArchiveMaterializedArtifact.hpp +89 -0
- package/nitrogen/generated/shared/c++/FirmwareArtifactCancelParams.hpp +83 -0
- package/nitrogen/generated/shared/c++/FirmwareArtifactCapabilities.hpp +96 -0
- package/nitrogen/generated/shared/c++/FirmwareArtifactDownloadParams.hpp +124 -0
- package/nitrogen/generated/shared/c++/FirmwareArtifactLease.hpp +83 -0
- package/nitrogen/generated/shared/c++/FirmwareArtifactLeaseCreateParams.hpp +83 -0
- package/nitrogen/generated/shared/c++/FirmwareArtifactLeaseReleaseParams.hpp +87 -0
- package/nitrogen/generated/shared/c++/FirmwareArtifactLeaseRetainParams.hpp +87 -0
- package/nitrogen/generated/shared/c++/FirmwareArtifactReaderCloseParams.hpp +83 -0
- package/nitrogen/generated/shared/c++/FirmwareArtifactReaderInfo.hpp +87 -0
- package/nitrogen/generated/shared/c++/FirmwareArtifactReaderReadParams.hpp +91 -0
- package/nitrogen/generated/shared/c++/FirmwareArtifactReceipt.hpp +95 -0
- package/nitrogen/generated/shared/c++/FirmwareArtifactRefParams.hpp +83 -0
- package/nitrogen/generated/shared/c++/FirmwareArtifactSweepResult.hpp +87 -0
- package/nitrogen/generated/shared/c++/HybridReactNativeRangeDownloaderSpec.cpp +13 -1
- package/nitrogen/generated/shared/c++/HybridReactNativeRangeDownloaderSpec.hpp +59 -1
- package/nitrogen/generated/shared/c++/RangeDownloadEvent.hpp +25 -17
- package/nitrogen/generated/shared/c++/RangeDownloadOutcome.hpp +1 -1
- package/nitrogen/generated/shared/c++/RangeDownloadParams.hpp +43 -35
- package/nitrogen/generated/shared/c++/RangeDownloadResult.hpp +22 -14
- package/nitrogen/generated/shared/c++/RangeFallbackKind.hpp +1 -1
- package/package.json +4 -4
- package/src/ReactNativeRangeDownloader.nitro.ts +130 -1
- package/src/index.tsx +3 -1
|
@@ -2,12 +2,16 @@ package com.margelo.nitro.reactnativerangedownloader
|
|
|
2
2
|
|
|
3
3
|
import com.facebook.proguard.annotations.DoNotStrip
|
|
4
4
|
import com.margelo.nitro.NitroModules
|
|
5
|
+
import com.margelo.nitro.core.ArrayBuffer
|
|
5
6
|
import com.margelo.nitro.core.Promise
|
|
6
7
|
import com.margelo.nitro.nativelogger.OneKeyLog
|
|
7
8
|
import java.io.File
|
|
8
9
|
import java.security.MessageDigest
|
|
9
10
|
import java.util.concurrent.CopyOnWriteArrayList
|
|
10
11
|
import java.util.concurrent.atomic.AtomicLong
|
|
12
|
+
import kotlinx.coroutines.CoroutineScope
|
|
13
|
+
import kotlinx.coroutines.Dispatchers
|
|
14
|
+
import kotlinx.coroutines.SupervisorJob
|
|
11
15
|
|
|
12
16
|
// P1: Nitro adapter for the Android concurrent multi-range downloader.
|
|
13
17
|
//
|
|
@@ -33,6 +37,7 @@ class ReactNativeRangeDownloader : HybridReactNativeRangeDownloaderSpec() {
|
|
|
33
37
|
|
|
34
38
|
private val listeners = CopyOnWriteArrayList<Listener>()
|
|
35
39
|
private val nextListenerId = AtomicLong(1)
|
|
40
|
+
private val firmwareArtifactScope = CoroutineScope(SupervisorJob() + Dispatchers.IO)
|
|
36
41
|
|
|
37
42
|
// Active downloads keyed by "channel|taskId" so cancel/discardArtifacts can
|
|
38
43
|
// flip the abort flag + stop the worker pool BEFORE deleting files, instead of
|
|
@@ -265,6 +270,149 @@ class ReactNativeRangeDownloader : HybridReactNativeRangeDownloaderSpec() {
|
|
|
265
270
|
return ctx.cacheDir.absolutePath
|
|
266
271
|
}
|
|
267
272
|
|
|
273
|
+
override fun getFirmwareArtifactCapabilities(): FirmwareArtifactCapabilities {
|
|
274
|
+
return FirmwareArtifactCapabilities(
|
|
275
|
+
firmwareArtifactProtocolVersion = 4.0,
|
|
276
|
+
supportedRouteTypes = arrayOf("domain"),
|
|
277
|
+
supportsArchiveMaterialization = true,
|
|
278
|
+
maxReadBytes = FirmwareArtifactStore.MAX_READ_BYTES.toDouble(),
|
|
279
|
+
)
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
override fun downloadFirmwareArtifact(
|
|
283
|
+
params: FirmwareArtifactDownloadParams,
|
|
284
|
+
): Promise<FirmwareArtifactReceipt> {
|
|
285
|
+
return Promise.async(firmwareArtifactScope) {
|
|
286
|
+
val artifact = FirmwareArtifactStore.download(params)
|
|
287
|
+
FirmwareArtifactReceipt(
|
|
288
|
+
artifactRef = artifact.artifactRef,
|
|
289
|
+
size = artifact.size.toDouble(),
|
|
290
|
+
sha256 = artifact.sha256,
|
|
291
|
+
expectedSha256Verified = params.expectedSha256 != null,
|
|
292
|
+
)
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
override fun cancelFirmwareArtifactDownloads(
|
|
297
|
+
params: FirmwareArtifactCancelParams,
|
|
298
|
+
): Promise<Unit> {
|
|
299
|
+
return Promise.async(firmwareArtifactScope) {
|
|
300
|
+
FirmwareArtifactStore.cancelDownloads(params.transactionId)
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
override fun discardFirmwareArtifact(
|
|
305
|
+
params: FirmwareArtifactRefParams,
|
|
306
|
+
): Promise<Unit> {
|
|
307
|
+
return Promise.async(firmwareArtifactScope) {
|
|
308
|
+
FirmwareArtifactStore.discard(params.artifactRef)
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
override fun openFirmwareArtifact(
|
|
313
|
+
params: FirmwareArtifactRefParams,
|
|
314
|
+
): Promise<FirmwareArtifactReaderInfo> {
|
|
315
|
+
return Promise.async(firmwareArtifactScope) {
|
|
316
|
+
val (readerId, size) = FirmwareArtifactStore.open(params.artifactRef)
|
|
317
|
+
FirmwareArtifactReaderInfo(readerId = readerId, size = size.toDouble())
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
override fun readFirmwareArtifact(
|
|
322
|
+
params: FirmwareArtifactReaderReadParams,
|
|
323
|
+
): Promise<ArrayBuffer> {
|
|
324
|
+
return Promise.async(firmwareArtifactScope) {
|
|
325
|
+
require(
|
|
326
|
+
params.offset.isFinite() &&
|
|
327
|
+
params.offset >= 0 &&
|
|
328
|
+
params.offset <= FirmwareArtifactStore.MAX_ARTIFACT_BYTES.toDouble() &&
|
|
329
|
+
params.offset.toLong().toDouble() == params.offset &&
|
|
330
|
+
params.length.isFinite() &&
|
|
331
|
+
params.length > 0 &&
|
|
332
|
+
params.length <= FirmwareArtifactStore.MAX_READ_BYTES.toDouble() &&
|
|
333
|
+
params.length.toInt().toDouble() == params.length
|
|
334
|
+
) {
|
|
335
|
+
"Invalid firmware artifact read"
|
|
336
|
+
}
|
|
337
|
+
ArrayBuffer.copy(
|
|
338
|
+
FirmwareArtifactStore.read(
|
|
339
|
+
readerId = params.readerId,
|
|
340
|
+
offset = params.offset.toLong(),
|
|
341
|
+
length = params.length.toInt(),
|
|
342
|
+
),
|
|
343
|
+
)
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
override fun closeFirmwareArtifact(
|
|
348
|
+
params: FirmwareArtifactReaderCloseParams,
|
|
349
|
+
): Promise<Unit> {
|
|
350
|
+
return Promise.async(firmwareArtifactScope) {
|
|
351
|
+
FirmwareArtifactStore.close(params.readerId)
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
override fun materializeFirmwareArchive(
|
|
356
|
+
params: FirmwareArchiveMaterializeParams,
|
|
357
|
+
): Promise<FirmwareArchiveMaterializeResult> {
|
|
358
|
+
return Promise.async(firmwareArtifactScope) {
|
|
359
|
+
val artifacts = FirmwareArtifactStore.materializeArchive(
|
|
360
|
+
params.leaseRef,
|
|
361
|
+
params.archiveArtifactRef,
|
|
362
|
+
params.expectedEntries,
|
|
363
|
+
)
|
|
364
|
+
FirmwareArchiveMaterializeResult(
|
|
365
|
+
artifacts = artifacts.map { entry ->
|
|
366
|
+
FirmwareArchiveMaterializedArtifact(
|
|
367
|
+
entryName = entry.entryName,
|
|
368
|
+
receipt = FirmwareArtifactReceipt(
|
|
369
|
+
artifactRef = entry.artifact.artifactRef,
|
|
370
|
+
size = entry.artifact.size.toDouble(),
|
|
371
|
+
sha256 = entry.artifact.sha256,
|
|
372
|
+
expectedSha256Verified = params.expectedEntries != null,
|
|
373
|
+
),
|
|
374
|
+
)
|
|
375
|
+
}.toTypedArray(),
|
|
376
|
+
)
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
override fun createFirmwareArtifactLease(
|
|
381
|
+
params: FirmwareArtifactLeaseCreateParams,
|
|
382
|
+
): Promise<FirmwareArtifactLease> {
|
|
383
|
+
return Promise.async(firmwareArtifactScope) {
|
|
384
|
+
FirmwareArtifactLease(
|
|
385
|
+
leaseRef = FirmwareArtifactStore.createLease(params.transactionId),
|
|
386
|
+
)
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
override fun retainFirmwareArtifact(
|
|
391
|
+
params: FirmwareArtifactLeaseRetainParams,
|
|
392
|
+
): Promise<Unit> {
|
|
393
|
+
return Promise.async(firmwareArtifactScope) {
|
|
394
|
+
FirmwareArtifactStore.retain(params.leaseRef, params.artifactRef)
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
override fun releaseFirmwareArtifactLease(
|
|
399
|
+
params: FirmwareArtifactLeaseReleaseParams,
|
|
400
|
+
): Promise<Unit> {
|
|
401
|
+
return Promise.async(firmwareArtifactScope) {
|
|
402
|
+
FirmwareArtifactStore.releaseLease(params.leaseRef, params.disposition)
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
override fun sweepFirmwareArtifactOrphans(): Promise<FirmwareArtifactSweepResult> {
|
|
407
|
+
return Promise.async(firmwareArtifactScope) {
|
|
408
|
+
val (deletedFiles, deletedBytes) = FirmwareArtifactStore.sweepOrphans()
|
|
409
|
+
FirmwareArtifactSweepResult(
|
|
410
|
+
deletedFiles = deletedFiles.toDouble(),
|
|
411
|
+
deletedBytes = deletedBytes.toDouble(),
|
|
412
|
+
)
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
|
|
268
416
|
// Broadcast one event to every registered listener. Listeners filter by
|
|
269
417
|
// channel/taskId on their side (shared registry, per the design).
|
|
270
418
|
private fun sendEvent(
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
package com.margelo.nitro.reactnativerangedownloader
|
|
2
|
+
|
|
3
|
+
import java.io.File
|
|
4
|
+
import java.io.FileOutputStream
|
|
5
|
+
import java.util.zip.ZipEntry
|
|
6
|
+
import java.util.zip.ZipOutputStream
|
|
7
|
+
import org.junit.Assert.assertEquals
|
|
8
|
+
import org.junit.Assert.assertThrows
|
|
9
|
+
import org.junit.Rule
|
|
10
|
+
import org.junit.Test
|
|
11
|
+
import org.junit.rules.TemporaryFolder
|
|
12
|
+
|
|
13
|
+
class FirmwareArchiveRulesTest {
|
|
14
|
+
@get:Rule
|
|
15
|
+
val temporaryFolder = TemporaryFolder()
|
|
16
|
+
|
|
17
|
+
private fun entry(
|
|
18
|
+
artifactId: String = "resource-entry",
|
|
19
|
+
entryName: String = "assets/icon.png",
|
|
20
|
+
expectedSize: Double = 128.0,
|
|
21
|
+
expectedSha256: String = "a".repeat(64),
|
|
22
|
+
) = FirmwareArchiveExpectedEntry(
|
|
23
|
+
artifactId = artifactId,
|
|
24
|
+
entryName = entryName,
|
|
25
|
+
expectedSize = expectedSize,
|
|
26
|
+
expectedSha256 = expectedSha256,
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
@Test
|
|
30
|
+
fun acceptsAnExactPortableAllowlist() {
|
|
31
|
+
val requirements = FirmwareArchiveRules.validateRequirements(
|
|
32
|
+
arrayOf(
|
|
33
|
+
entry(),
|
|
34
|
+
entry(
|
|
35
|
+
artifactId = "resource-entry-2",
|
|
36
|
+
entryName = "assets/sub/icon-2.png",
|
|
37
|
+
expectedSha256 = "b".repeat(64),
|
|
38
|
+
),
|
|
39
|
+
),
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
assertEquals(2, requirements.size)
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
@Test
|
|
46
|
+
fun rejectsTraversalAndNestedArchives() {
|
|
47
|
+
for (entryName in listOf("../icon.png", "assets/../icon.png", "assets.zip")) {
|
|
48
|
+
assertThrows(IllegalArgumentException::class.java) {
|
|
49
|
+
FirmwareArchiveRules.validateRequirements(
|
|
50
|
+
arrayOf(entry(entryName = entryName)),
|
|
51
|
+
)
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
@Test
|
|
57
|
+
fun rejectsCaseFoldedDuplicateNames() {
|
|
58
|
+
assertThrows(IllegalArgumentException::class.java) {
|
|
59
|
+
FirmwareArchiveRules.validateRequirements(
|
|
60
|
+
arrayOf(
|
|
61
|
+
entry(entryName = "assets/Icon.png"),
|
|
62
|
+
entry(
|
|
63
|
+
artifactId = "resource-entry-2",
|
|
64
|
+
entryName = "assets/icon.png",
|
|
65
|
+
),
|
|
66
|
+
),
|
|
67
|
+
)
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
@Test
|
|
72
|
+
fun rejectsNonIntegralSizesAndInvalidDigests() {
|
|
73
|
+
assertThrows(IllegalArgumentException::class.java) {
|
|
74
|
+
FirmwareArchiveRules.validateRequirements(
|
|
75
|
+
arrayOf(entry(expectedSize = 1.5)),
|
|
76
|
+
)
|
|
77
|
+
}
|
|
78
|
+
assertThrows(IllegalArgumentException::class.java) {
|
|
79
|
+
FirmwareArchiveRules.validateRequirements(
|
|
80
|
+
arrayOf(entry(expectedSha256 = "not-a-digest")),
|
|
81
|
+
)
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
@Test
|
|
86
|
+
fun acceptsPortableEntriesWithoutExpectedIntegrityMetadata() {
|
|
87
|
+
val archive = createArchive("assets/icon.png", byteArrayOf(1, 2, 3))
|
|
88
|
+
|
|
89
|
+
val entries = FirmwareArchiveRules.validateCentralDirectory(archive, null)
|
|
90
|
+
|
|
91
|
+
assertEquals(1, entries.size)
|
|
92
|
+
assertEquals("assets/icon.png", entries.single().name)
|
|
93
|
+
assertEquals(3, entries.single().uncompressedSize)
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
@Test
|
|
97
|
+
fun rejectsTraversalWithoutExpectedIntegrityMetadata() {
|
|
98
|
+
val archive = createArchive("../icon.png", byteArrayOf(1))
|
|
99
|
+
|
|
100
|
+
assertThrows(IllegalArgumentException::class.java) {
|
|
101
|
+
FirmwareArchiveRules.validateCentralDirectory(archive, null)
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
private fun createArchive(entryName: String, content: ByteArray): File {
|
|
106
|
+
val archive = temporaryFolder.newFile("firmware.zip")
|
|
107
|
+
ZipOutputStream(FileOutputStream(archive)).use { zip ->
|
|
108
|
+
zip.putNextEntry(ZipEntry(entryName))
|
|
109
|
+
zip.write(content)
|
|
110
|
+
zip.closeEntry()
|
|
111
|
+
}
|
|
112
|
+
return archive
|
|
113
|
+
}
|
|
114
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
package com.margelo.nitro.reactnativerangedownloader
|
|
2
|
+
|
|
3
|
+
import org.junit.Assert.assertEquals
|
|
4
|
+
import org.junit.Assert.assertThrows
|
|
5
|
+
import org.junit.Test
|
|
6
|
+
|
|
7
|
+
class FirmwareArtifactDeadlineTest {
|
|
8
|
+
@Test
|
|
9
|
+
fun defaultsToABoundedThreeMinuteDeadline() {
|
|
10
|
+
assertEquals(180.0, validateFirmwareDownloadDeadlineSeconds(null), 0.0)
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
@Test
|
|
14
|
+
fun preservesFractionalDeadlines() {
|
|
15
|
+
assertEquals(2.75, validateFirmwareDownloadDeadlineSeconds(2.75), 0.0)
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
@Test
|
|
19
|
+
fun rejectsUnboundedOrInvalidDeadlines() {
|
|
20
|
+
listOf(0.0, -1.0, Double.NaN, Double.POSITIVE_INFINITY, 86_400.1).forEach {
|
|
21
|
+
assertThrows(IllegalArgumentException::class.java) {
|
|
22
|
+
validateFirmwareDownloadDeadlineSeconds(it)
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
package com.margelo.nitro.reactnativerangedownloader
|
|
2
|
+
|
|
3
|
+
import java.io.File
|
|
4
|
+
import org.junit.Assert.assertEquals
|
|
5
|
+
import org.junit.Assert.assertFalse
|
|
6
|
+
import org.junit.Assert.assertTrue
|
|
7
|
+
import org.junit.Rule
|
|
8
|
+
import org.junit.Test
|
|
9
|
+
import org.junit.rules.TemporaryFolder
|
|
10
|
+
|
|
11
|
+
class FirmwareArtifactOrphanSweepTest {
|
|
12
|
+
@get:Rule
|
|
13
|
+
val temporaryFolder = TemporaryFolder()
|
|
14
|
+
|
|
15
|
+
@Test
|
|
16
|
+
fun removesOnlyStaleRootScratchEntriesWithExactNamesAndTypes() {
|
|
17
|
+
val root = temporaryFolder.newFolder("firmware-artifact-sweep")
|
|
18
|
+
val nowMs = 2_000_000_000_000L
|
|
19
|
+
val staleAt = nowMs - FIRMWARE_ARTIFACT_SCRATCH_GRACE_MS - 1
|
|
20
|
+
val freshAt = nowMs - FIRMWARE_ARTIFACT_SCRATCH_GRACE_MS + 1
|
|
21
|
+
val staleArchive = File(
|
|
22
|
+
root,
|
|
23
|
+
"archive-00000000-0000-4000-8000-000000000001",
|
|
24
|
+
).apply {
|
|
25
|
+
assertTrue(mkdirs())
|
|
26
|
+
File(this, "0.entry").writeText("archive")
|
|
27
|
+
}
|
|
28
|
+
val stalePromote = File(
|
|
29
|
+
root,
|
|
30
|
+
".promote-00000000-0000-4000-8000-000000000002",
|
|
31
|
+
).apply { writeText("promote") }
|
|
32
|
+
val freshArchive = File(
|
|
33
|
+
root,
|
|
34
|
+
"archive-00000000-0000-4000-8000-000000000003",
|
|
35
|
+
).apply { assertTrue(mkdirs()) }
|
|
36
|
+
val freshPromote = File(
|
|
37
|
+
root,
|
|
38
|
+
".promote-00000000-0000-4000-8000-000000000004",
|
|
39
|
+
).apply { writeText("promote") }
|
|
40
|
+
val malformedArchive = File(
|
|
41
|
+
root,
|
|
42
|
+
"archive-00000000-0000-4000-8000-000000000005.extra",
|
|
43
|
+
).apply { assertTrue(mkdirs()) }
|
|
44
|
+
val malformedPromote = File(
|
|
45
|
+
root,
|
|
46
|
+
".promote-00000000-0000-4000-8000-000000000006.tmp",
|
|
47
|
+
).apply { writeText("promote") }
|
|
48
|
+
val archiveNamedFile = File(
|
|
49
|
+
root,
|
|
50
|
+
"archive-00000000-0000-4000-8000-000000000007",
|
|
51
|
+
).apply { writeText("promote") }
|
|
52
|
+
val promoteNamedDirectory = File(
|
|
53
|
+
root,
|
|
54
|
+
".promote-00000000-0000-4000-8000-000000000008",
|
|
55
|
+
).apply { assertTrue(mkdirs()) }
|
|
56
|
+
val nestedArchive = File(
|
|
57
|
+
File(root, "nested"),
|
|
58
|
+
"archive-00000000-0000-4000-8000-000000000009",
|
|
59
|
+
).apply { assertTrue(mkdirs()) }
|
|
60
|
+
|
|
61
|
+
for (
|
|
62
|
+
entry in listOf(
|
|
63
|
+
staleArchive,
|
|
64
|
+
stalePromote,
|
|
65
|
+
malformedArchive,
|
|
66
|
+
malformedPromote,
|
|
67
|
+
archiveNamedFile,
|
|
68
|
+
promoteNamedDirectory,
|
|
69
|
+
nestedArchive,
|
|
70
|
+
)
|
|
71
|
+
) {
|
|
72
|
+
assertTrue(entry.setLastModified(staleAt))
|
|
73
|
+
}
|
|
74
|
+
for (entry in listOf(freshArchive, freshPromote)) {
|
|
75
|
+
assertTrue(entry.setLastModified(freshAt))
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
val result = sweepFirmwareArtifactOrphansAtRoot(
|
|
79
|
+
root = root,
|
|
80
|
+
retainedSha256 = emptySet(),
|
|
81
|
+
activeSha256 = emptySet(),
|
|
82
|
+
openPaths = emptySet(),
|
|
83
|
+
nowMs = nowMs,
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
assertEquals(2, result.first)
|
|
87
|
+
assertEquals(14L, result.second)
|
|
88
|
+
assertFalse(staleArchive.exists())
|
|
89
|
+
assertFalse(stalePromote.exists())
|
|
90
|
+
for (
|
|
91
|
+
retainedEntry in listOf(
|
|
92
|
+
freshArchive,
|
|
93
|
+
freshPromote,
|
|
94
|
+
malformedArchive,
|
|
95
|
+
malformedPromote,
|
|
96
|
+
archiveNamedFile,
|
|
97
|
+
promoteNamedDirectory,
|
|
98
|
+
nestedArchive,
|
|
99
|
+
)
|
|
100
|
+
) {
|
|
101
|
+
assertTrue(
|
|
102
|
+
"Unexpectedly removed ${retainedEntry.name}",
|
|
103
|
+
retainedEntry.exists(),
|
|
104
|
+
)
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
#import <Foundation/Foundation.h>
|
|
2
|
+
|
|
3
|
+
NS_ASSUME_NONNULL_BEGIN
|
|
4
|
+
|
|
5
|
+
@interface FirmwareArchiveEntryInfo : NSObject
|
|
6
|
+
|
|
7
|
+
@property(nonatomic, readonly) NSString *name;
|
|
8
|
+
@property(nonatomic, readonly) int64_t compressedSize;
|
|
9
|
+
@property(nonatomic, readonly) int64_t uncompressedSize;
|
|
10
|
+
@property(nonatomic, readonly) uint32_t crc32;
|
|
11
|
+
@property(nonatomic, readonly) uint16_t flags;
|
|
12
|
+
@property(nonatomic, readonly) uint16_t compressionMethod;
|
|
13
|
+
@property(nonatomic, readonly) uint16_t versionMadeBy;
|
|
14
|
+
@property(nonatomic, readonly) uint32_t externalAttributes;
|
|
15
|
+
@property(nonatomic, readonly) uint32_t diskNumber;
|
|
16
|
+
@property(nonatomic, readonly, nullable) NSString *linkName;
|
|
17
|
+
|
|
18
|
+
@end
|
|
19
|
+
|
|
20
|
+
@interface FirmwareArchiveMinizipBridge : NSObject
|
|
21
|
+
|
|
22
|
+
+ (nullable NSArray<FirmwareArchiveEntryInfo *> *)scanArchiveAtPath:
|
|
23
|
+
(NSString *)path
|
|
24
|
+
error:(NSError * _Nullable * _Nullable)error;
|
|
25
|
+
|
|
26
|
+
+ (BOOL)extractEntryNamed:(NSString *)entryName
|
|
27
|
+
archivePath:(NSString *)archivePath
|
|
28
|
+
consumer:(BOOL (^)(NSData *chunk))consumer
|
|
29
|
+
error:(NSError * _Nullable * _Nullable)error;
|
|
30
|
+
|
|
31
|
+
@end
|
|
32
|
+
|
|
33
|
+
NS_ASSUME_NONNULL_END
|
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
#import "FirmwareArchiveMinizipBridge.h"
|
|
2
|
+
|
|
3
|
+
#import "mz.h"
|
|
4
|
+
#import "mz_strm.h"
|
|
5
|
+
#import "mz_zip.h"
|
|
6
|
+
#import "mz_zip_rw.h"
|
|
7
|
+
|
|
8
|
+
#include <cstring>
|
|
9
|
+
|
|
10
|
+
static NSString *const FirmwareArchiveBridgeErrorDomain =
|
|
11
|
+
@"so.onekey.firmware.archive.minizip";
|
|
12
|
+
|
|
13
|
+
static NSError *FirmwareArchiveBridgeError(NSString *message, int32_t code) {
|
|
14
|
+
return [NSError errorWithDomain:FirmwareArchiveBridgeErrorDomain
|
|
15
|
+
code:code
|
|
16
|
+
userInfo:@{NSLocalizedDescriptionKey : message}];
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
@interface FirmwareArchiveEntryInfo ()
|
|
20
|
+
|
|
21
|
+
@property(nonatomic, readwrite) NSString *name;
|
|
22
|
+
@property(nonatomic, readwrite) int64_t compressedSize;
|
|
23
|
+
@property(nonatomic, readwrite) int64_t uncompressedSize;
|
|
24
|
+
@property(nonatomic, readwrite) uint32_t crc32;
|
|
25
|
+
@property(nonatomic, readwrite) uint16_t flags;
|
|
26
|
+
@property(nonatomic, readwrite) uint16_t compressionMethod;
|
|
27
|
+
@property(nonatomic, readwrite) uint16_t versionMadeBy;
|
|
28
|
+
@property(nonatomic, readwrite) uint32_t externalAttributes;
|
|
29
|
+
@property(nonatomic, readwrite) uint32_t diskNumber;
|
|
30
|
+
@property(nonatomic, readwrite, nullable) NSString *linkName;
|
|
31
|
+
|
|
32
|
+
@end
|
|
33
|
+
|
|
34
|
+
@implementation FirmwareArchiveEntryInfo
|
|
35
|
+
@end
|
|
36
|
+
|
|
37
|
+
@implementation FirmwareArchiveMinizipBridge
|
|
38
|
+
|
|
39
|
+
+ (nullable NSArray<FirmwareArchiveEntryInfo *> *)scanArchiveAtPath:
|
|
40
|
+
(NSString *)path
|
|
41
|
+
error:(NSError * _Nullable * _Nullable)error {
|
|
42
|
+
void *reader = nullptr;
|
|
43
|
+
mz_zip_reader_create(&reader);
|
|
44
|
+
if (reader == nullptr) {
|
|
45
|
+
if (error != nullptr) {
|
|
46
|
+
*error = FirmwareArchiveBridgeError(@"Archive reader allocation failed",
|
|
47
|
+
MZ_MEM_ERROR);
|
|
48
|
+
}
|
|
49
|
+
return nil;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
int32_t result = mz_zip_reader_open_file(reader, path.fileSystemRepresentation);
|
|
53
|
+
if (result != MZ_OK) {
|
|
54
|
+
mz_zip_reader_delete(&reader);
|
|
55
|
+
if (error != nullptr) {
|
|
56
|
+
*error = FirmwareArchiveBridgeError(@"Archive open failed", result);
|
|
57
|
+
}
|
|
58
|
+
return nil;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
NSMutableArray<FirmwareArchiveEntryInfo *> *entries =
|
|
62
|
+
[NSMutableArray array];
|
|
63
|
+
result = mz_zip_reader_goto_first_entry(reader);
|
|
64
|
+
while (result == MZ_OK) {
|
|
65
|
+
mz_zip_file *fileInfo = nullptr;
|
|
66
|
+
result = mz_zip_reader_entry_get_info(reader, &fileInfo);
|
|
67
|
+
if (result != MZ_OK || fileInfo == nullptr ||
|
|
68
|
+
fileInfo->filename == nullptr) {
|
|
69
|
+
result = result == MZ_OK ? MZ_FORMAT_ERROR : result;
|
|
70
|
+
break;
|
|
71
|
+
}
|
|
72
|
+
const size_t filenameLength = std::strlen(fileInfo->filename);
|
|
73
|
+
if (filenameLength == 0 || filenameLength > 4096) {
|
|
74
|
+
result = MZ_FORMAT_ERROR;
|
|
75
|
+
break;
|
|
76
|
+
}
|
|
77
|
+
if ((fileInfo->flag & (1 << 11)) == 0) {
|
|
78
|
+
const unsigned char *filenameBytes =
|
|
79
|
+
reinterpret_cast<const unsigned char *>(fileInfo->filename);
|
|
80
|
+
bool isAscii = true;
|
|
81
|
+
for (size_t index = 0; index < filenameLength; index += 1) {
|
|
82
|
+
if (filenameBytes[index] >= 0x80) {
|
|
83
|
+
isAscii = false;
|
|
84
|
+
break;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
if (!isAscii) {
|
|
88
|
+
result = MZ_FORMAT_ERROR;
|
|
89
|
+
break;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
NSString *name = [NSString stringWithUTF8String:fileInfo->filename];
|
|
93
|
+
if (name == nil) {
|
|
94
|
+
result = MZ_FORMAT_ERROR;
|
|
95
|
+
break;
|
|
96
|
+
}
|
|
97
|
+
NSString *linkName = nil;
|
|
98
|
+
if (fileInfo->linkname != nullptr) {
|
|
99
|
+
linkName = [NSString stringWithUTF8String:fileInfo->linkname];
|
|
100
|
+
if (linkName == nil) {
|
|
101
|
+
result = MZ_FORMAT_ERROR;
|
|
102
|
+
break;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
FirmwareArchiveEntryInfo *entry =
|
|
107
|
+
[[FirmwareArchiveEntryInfo alloc] init];
|
|
108
|
+
entry.name = name;
|
|
109
|
+
entry.compressedSize = fileInfo->compressed_size;
|
|
110
|
+
entry.uncompressedSize = fileInfo->uncompressed_size;
|
|
111
|
+
entry.crc32 = fileInfo->crc;
|
|
112
|
+
entry.flags = fileInfo->flag;
|
|
113
|
+
entry.compressionMethod = fileInfo->compression_method;
|
|
114
|
+
entry.versionMadeBy = fileInfo->version_madeby;
|
|
115
|
+
entry.externalAttributes = fileInfo->external_fa;
|
|
116
|
+
entry.diskNumber = fileInfo->disk_number;
|
|
117
|
+
entry.linkName = linkName;
|
|
118
|
+
[entries addObject:entry];
|
|
119
|
+
if (entries.count > 4096) {
|
|
120
|
+
result = MZ_FORMAT_ERROR;
|
|
121
|
+
break;
|
|
122
|
+
}
|
|
123
|
+
result = mz_zip_reader_goto_next_entry(reader);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
if (result == MZ_END_OF_LIST) {
|
|
127
|
+
result = MZ_OK;
|
|
128
|
+
}
|
|
129
|
+
int32_t closeResult = mz_zip_reader_close(reader);
|
|
130
|
+
mz_zip_reader_delete(&reader);
|
|
131
|
+
if (result != MZ_OK || closeResult != MZ_OK) {
|
|
132
|
+
if (error != nullptr) {
|
|
133
|
+
*error = FirmwareArchiveBridgeError(@"Archive scan failed",
|
|
134
|
+
result != MZ_OK ? result
|
|
135
|
+
: closeResult);
|
|
136
|
+
}
|
|
137
|
+
return nil;
|
|
138
|
+
}
|
|
139
|
+
return entries;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
+ (BOOL)extractEntryNamed:(NSString *)entryName
|
|
143
|
+
archivePath:(NSString *)archivePath
|
|
144
|
+
consumer:(BOOL (^)(NSData *chunk))consumer
|
|
145
|
+
error:(NSError * _Nullable * _Nullable)error {
|
|
146
|
+
void *reader = nullptr;
|
|
147
|
+
bool archiveOpened = false;
|
|
148
|
+
bool entryOpened = false;
|
|
149
|
+
mz_zip_reader_create(&reader);
|
|
150
|
+
if (reader == nullptr) {
|
|
151
|
+
if (error != nullptr) {
|
|
152
|
+
*error = FirmwareArchiveBridgeError(@"Archive reader allocation failed",
|
|
153
|
+
MZ_MEM_ERROR);
|
|
154
|
+
}
|
|
155
|
+
return NO;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
int32_t result =
|
|
159
|
+
mz_zip_reader_open_file(reader, archivePath.fileSystemRepresentation);
|
|
160
|
+
if (result == MZ_OK) {
|
|
161
|
+
archiveOpened = true;
|
|
162
|
+
}
|
|
163
|
+
if (result == MZ_OK) {
|
|
164
|
+
result = mz_zip_reader_locate_entry(
|
|
165
|
+
reader, entryName.UTF8String, 0);
|
|
166
|
+
}
|
|
167
|
+
if (result == MZ_OK) {
|
|
168
|
+
result = mz_zip_reader_entry_open(reader);
|
|
169
|
+
if (result == MZ_OK) {
|
|
170
|
+
entryOpened = true;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
uint8_t buffer[64 * 1024];
|
|
175
|
+
while (result == MZ_OK) {
|
|
176
|
+
int32_t count =
|
|
177
|
+
mz_zip_reader_entry_read(reader, buffer, (int32_t)sizeof(buffer));
|
|
178
|
+
if (count < 0) {
|
|
179
|
+
result = count;
|
|
180
|
+
break;
|
|
181
|
+
}
|
|
182
|
+
if (count == 0) {
|
|
183
|
+
break;
|
|
184
|
+
}
|
|
185
|
+
NSData *chunk = [NSData dataWithBytes:buffer length:(NSUInteger)count];
|
|
186
|
+
if (!consumer(chunk)) {
|
|
187
|
+
result = MZ_WRITE_ERROR;
|
|
188
|
+
break;
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
int32_t entryCloseResult =
|
|
193
|
+
entryOpened ? mz_zip_reader_entry_close(reader) : MZ_OK;
|
|
194
|
+
int32_t archiveCloseResult =
|
|
195
|
+
archiveOpened ? mz_zip_reader_close(reader) : MZ_OK;
|
|
196
|
+
mz_zip_reader_delete(&reader);
|
|
197
|
+
if (result != MZ_OK || entryCloseResult != MZ_OK ||
|
|
198
|
+
archiveCloseResult != MZ_OK) {
|
|
199
|
+
if (error != nullptr) {
|
|
200
|
+
int32_t failure = result != MZ_OK
|
|
201
|
+
? result
|
|
202
|
+
: (entryCloseResult != MZ_OK
|
|
203
|
+
? entryCloseResult
|
|
204
|
+
: archiveCloseResult);
|
|
205
|
+
*error = FirmwareArchiveBridgeError(
|
|
206
|
+
@"Archive entry extraction or CRC validation failed", failure);
|
|
207
|
+
}
|
|
208
|
+
return NO;
|
|
209
|
+
}
|
|
210
|
+
return YES;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
@end
|