Move SSD ZIP limit checks to typed Kotlin
ober
9a039a4c8265b7c09a0ab8368bc544d1c7f998cb
--- a/templates/ssd-review.ss +++ b/templates/ssd-review.ss @@ -1578,7 +1578,10 @@ pathIsSymbolicLink safeLocalRegularFile destinationNewFileDelta storageStatsExceedsQuota storageCapacityWithinQuota - storageEntryBytesAllowed zipExportQuotaExceeded + byteCountWithinLimit storageEntryBytesAllowed + positiveCount learnedExamplesShouldRebuild + zipEntryCountExceeded zipExpandedBytesExceeded + zipCompressionRatioExceeded zipExportQuotaExceeded treeExportQuotaExceeded byteArraySizeBytes byteArrayBytesWithinLimit textUtf8SizeBytes textUtf8BytesWithinLimit @@ -1674,8 +1677,26 @@ (maxFiles : Int32) (maxBytes : Int)) : Bool (and (<= files maxFiles) (<= bytes maxBytes))) - (def (storageEntryBytesAllowed (bytes : Int) (maxBytes : Int)) : Bool + (def (byteCountWithinLimit (bytes : Int) (maxBytes : Int)) : Bool (and (>= bytes (int 0)) (<= bytes maxBytes))) + (def (storageEntryBytesAllowed (bytes : Int) (maxBytes : Int)) : Bool + (byteCountWithinLimit bytes maxBytes)) + (def (positiveCount (count : Int32)) : Bool + (> count (int32 0))) + (def (learnedExamplesShouldRebuild (imported : Int32) + (learnedFile : File)) : Bool + (or (positiveCount imported) (not (fileExists learnedFile)))) + (def (zipEntryCountExceeded (entries : Int32) (maxEntries : Int32)) : Bool + (> entries maxEntries)) + (def (zipExpandedBytesExceeded (entryBytes : Int) + (expandedBytes : Int) + (maxEntryBytes : Int) + (maxTotalBytes : Int)) : Bool + (or (> entryBytes maxEntryBytes) + (> expandedBytes maxTotalBytes))) + (def (zipCompressionRatioExceeded (expandedBytes : Int) + (ratioBudget : Int)) : Bool + (> expandedBytes ratioBudget)) (def (zipExportQuotaExceeded (entries : Int32) (fileBytes : Int) (sourceBytes : Int) @@ -8246,7 +8267,7 @@ " if (acceptGzip) setRequestProperty(\"Accept-Encoding\", \"gzip\")" " if (contentType != null) setRequestProperty(\"Content-Type\", contentType)" " if (outputBytes != null) {" - " require(outputBytes in 0..MAX_ZIP_TOTAL_BYTES) { \"Upload exceeds byte limit\" }" + " require(byteCountWithinLimit(outputBytes, MAX_ZIP_TOTAL_BYTES)) { \"Upload exceeds byte limit\" }" " doOutput = true" " setFixedLengthStreamingMode(outputBytes)" " }" @@ -8413,7 +8434,7 @@ " while (true) {" " val entry = zip.nextEntry ?: break" " budget.entries += 1" - " if (budget.entries > MAX_ZIP_ENTRIES) throw IllegalStateException(zipEntryCountLimitExceededMessage(budget.entries, MAX_ZIP_ENTRIES))" + " if (zipEntryCountExceeded(budget.entries, MAX_ZIP_ENTRIES)) throw IllegalStateException(zipEntryCountLimitExceededMessage(budget.entries, MAX_ZIP_ENTRIES))" " val name = entry.name" " if (!safeZipEntryName(name)) {" " throw IllegalStateException(\"Unsafe ZIP entry\")" @@ -8431,14 +8452,13 @@ " }" " }" " val current = storageStats()" - " require(current.files + staged.size <= MAX_STORAGE_FILES) { \"Storage file-count quota exceeded\" }" - " require(current.bytes + budget.expandedBytes <= MAX_STORAGE_BYTES) { \"Storage byte quota exceeded\" }" + " require(storageCapacityWithinQuota(current.files + staged.size, current.bytes + budget.expandedBytes, MAX_STORAGE_FILES, MAX_STORAGE_BYTES)) { \"Storage quota exceeded\" }" " staged.forEach { if (installStagedZipEntry(it)) count += 1 }" - " if (count > 0) invalidateTruthIndex()" + " if (positiveCount(count)) invalidateTruthIndex()" " } finally {" " stage.deleteRecursively()" " }" - " if (count > 0 || !fileExists(File(learnedDir, learnedExamplesFileName()))) {" + " if (learnedExamplesShouldRebuild(count, File(learnedDir, learnedExamplesFileName()))) {" " rebuildLearnedExamples()" " }" " return count" @@ -8459,11 +8479,11 @@ " if (read == 0) continue" " entryBytes += read.toLong()" " budget.expandedBytes += read.toLong()" - " if (entryBytes > MAX_ZIP_ENTRY_BYTES || budget.expandedBytes > MAX_ZIP_TOTAL_BYTES) {" + " if (zipExpandedBytesExceeded(entryBytes, budget.expandedBytes, MAX_ZIP_ENTRY_BYTES, MAX_ZIP_TOTAL_BYTES)) {" " throw IllegalStateException(\"ZIP expanded-byte limit exceeded\")" " }" " val ratioBudget = compressionRatioBudget(RATIO_FLOOR_BYTES, compressed.count, MAX_COMPRESSION_RATIO)" - " if (budget.expandedBytes > ratioBudget) {" + " if (zipCompressionRatioExceeded(budget.expandedBytes, ratioBudget)) {" " throw IllegalStateException(\"ZIP compression-ratio limit exceeded\")" " }" " output.write(buffer, 0, read)" @@ -8537,11 +8557,11 @@ " val archive = File.createTempFile(\"ssd-upload-\", \".zip\", context.cacheDir)" " try {" " archive.outputStream().use { exportZip(it) }" - " require(archive.length() <= MAX_ZIP_TOTAL_BYTES) { \"Upload ZIP exceeds byte limit\" }" + " require(byteCountWithinLimit(fileSizeBytes(archive), MAX_ZIP_TOTAL_BYTES)) { \"Upload ZIP exceeds byte limit\" }" " val connection = openPinnedConnection(" " \"load.zip\"," " \"POST\"," - " outputBytes = archive.length()," + " outputBytes = fileSizeBytes(archive)," " contentType = \"application/zip\"" " )" " try {" @@ -8648,7 +8668,7 @@ " var count = 0" " sourceDir.listFiles().forEach { child ->" " budget.entries += 1" - " if (budget.entries > MAX_ZIP_ENTRIES) throw IllegalStateException(\"Import tree exceeds entry limit\")" + " if (zipEntryCountExceeded(budget.entries, MAX_ZIP_ENTRIES)) throw IllegalStateException(\"Import tree exceeds entry limit\")" " if (documentFileIsDirectory(child)) {" " val directoryName = child.name ?: return@forEach" " if (!safePathComponent(directoryName)) throw IllegalStateException(\"Unsafe import directory\")" @@ -8684,7 +8704,7 @@ "" " private fun writeTreeFile(parent: DocumentFile, relativeName: String, source: File) {" " require(safeLocalRegularFile(source))" - " require(fileSizeBytes(source) <= MAX_ZIP_ENTRY_BYTES) { \"Tree export file exceeds byte limit\" }" + " require(byteCountWithinLimit(fileSizeBytes(source), MAX_ZIP_ENTRY_BYTES)) { \"Tree export file exceeds byte limit\" }" " val parts = trimmedNonBlankPathParts(relativeName, File.separatorChar, '/')" " require(safeTreePathParts(parts, MAX_ZIP_DEPTH))" " val fileName = treePathLeaf(parts)"