mirror of
https://github.com/corda/corda.git
synced 2024-12-21 13:57:54 +00:00
Support auto-expansion of attachment jars on disk.
This commit is contained in:
parent
7fd9b43e50
commit
8d906c703d
@ -8,10 +8,12 @@
|
||||
|
||||
package core
|
||||
|
||||
import com.google.common.io.ByteStreams
|
||||
import com.google.common.util.concurrent.ListenableFuture
|
||||
import com.google.common.util.concurrent.MoreExecutors
|
||||
import com.google.common.util.concurrent.SettableFuture
|
||||
import org.slf4j.Logger
|
||||
import java.io.BufferedInputStream
|
||||
import java.io.InputStream
|
||||
import java.nio.file.Files
|
||||
import java.nio.file.Path
|
||||
@ -21,6 +23,7 @@ import java.time.temporal.Temporal
|
||||
import java.util.concurrent.Executor
|
||||
import java.util.concurrent.locks.Lock
|
||||
import java.util.concurrent.locks.ReentrantLock
|
||||
import java.util.zip.ZipInputStream
|
||||
import kotlin.concurrent.withLock
|
||||
import kotlin.reflect.KProperty
|
||||
|
||||
@ -122,3 +125,31 @@ class TransientProperty<T>(private val initializer: () -> T) {
|
||||
return v!!
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a path to a zip file, extracts it to the given directory.
|
||||
*/
|
||||
fun extractZipFile(zipPath: Path, toPath: Path) {
|
||||
if (!Files.exists(toPath))
|
||||
Files.createDirectories(toPath)
|
||||
|
||||
ZipInputStream(BufferedInputStream(Files.newInputStream(zipPath))).use { zip ->
|
||||
while (true) {
|
||||
val e = zip.nextEntry ?: break
|
||||
val outPath = toPath.resolve(e.name)
|
||||
|
||||
// Security checks: we should reject a zip that contains tricksy paths that try to escape toPath.
|
||||
if (!outPath.normalize().startsWith(toPath))
|
||||
throw IllegalStateException("ZIP contained a path that resolved incorrectly: ${e.name}")
|
||||
|
||||
if (e.isDirectory) {
|
||||
Files.createDirectories(outPath)
|
||||
continue
|
||||
}
|
||||
Files.newOutputStream(outPath).use { out ->
|
||||
ByteStreams.copy(zip, out)
|
||||
}
|
||||
zip.closeEntry()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -15,11 +15,13 @@ import com.google.common.io.CountingInputStream
|
||||
import core.Attachment
|
||||
import core.AttachmentStorage
|
||||
import core.crypto.SecureHash
|
||||
import core.extractZipFile
|
||||
import core.utilities.loggerFor
|
||||
import java.io.FilterInputStream
|
||||
import java.io.InputStream
|
||||
import java.nio.file.Files
|
||||
import java.nio.file.Path
|
||||
import java.nio.file.Paths
|
||||
import java.nio.file.StandardCopyOption
|
||||
import java.util.*
|
||||
import java.util.jar.JarInputStream
|
||||
@ -35,6 +37,13 @@ class NodeAttachmentStorage(val storePath: Path) : AttachmentStorage {
|
||||
@VisibleForTesting
|
||||
var checkAttachmentsOnLoad = true
|
||||
|
||||
/**
|
||||
* If true, newly inserted attachments will be unzipped to a subdirectory of the [storePath]. This is intended for
|
||||
* human browsing convenience: the attachment itself will still be the file (that is, edits to the extracted directory
|
||||
* will not have any effect).
|
||||
*/
|
||||
@Volatile var automaticallyExtractAttachments = false
|
||||
|
||||
init {
|
||||
require(Files.isDirectory(storePath)) { "$storePath must be a directory" }
|
||||
}
|
||||
@ -102,14 +111,29 @@ class NodeAttachmentStorage(val storePath: Path) : AttachmentStorage {
|
||||
Files.deleteIfExists(tmp)
|
||||
}
|
||||
log.info("Stored new attachment $id")
|
||||
if (automaticallyExtractAttachments) {
|
||||
val extractTo = storePath.resolve("${id}.jar")
|
||||
try {
|
||||
Files.createDirectory(extractTo)
|
||||
extractZipFile(finalPath, extractTo)
|
||||
} catch(e: Exception) {
|
||||
log.error("Failed to extract attachment jar $id, ", e)
|
||||
// TODO: Delete the extractTo directory here.
|
||||
}
|
||||
}
|
||||
return id
|
||||
}
|
||||
|
||||
private fun checkIsAValidJAR(path: Path) {
|
||||
// Just iterate over the entries with verification enabled: should be good enough to catch mistakes.
|
||||
JarInputStream(Files.newInputStream(path), true).use { stream ->
|
||||
var cursor = stream.nextJarEntry
|
||||
while (cursor != null) cursor = stream.nextJarEntry
|
||||
while (true) {
|
||||
val cursor = stream.nextJarEntry ?: break
|
||||
val entryPath = Paths.get(cursor.name)
|
||||
// Security check to stop zips trying to escape their rightful place.
|
||||
if (entryPath.isAbsolute || entryPath.normalize() != entryPath)
|
||||
throw IllegalArgumentException("Path is either absolute or non-normalised: $entryPath")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user