Merge "v4 digest tree streaming" into rvc-dev am: 437fd07a1f am: debbc7a829

Change-Id: Iec27d519f88a2b8667428fa48b0c77d54d8b9226
This commit is contained in:
Alex Buynytskyy
2020-03-27 22:48:40 +00:00
committed by Automerger Merge Worker
3 changed files with 208 additions and 75 deletions

View File

@@ -105,6 +105,7 @@ import com.android.internal.util.ArrayUtils;
import com.android.internal.util.IndentingPrintWriter; import com.android.internal.util.IndentingPrintWriter;
import com.android.server.LocalServices; import com.android.server.LocalServices;
import com.android.server.SystemConfig; import com.android.server.SystemConfig;
import com.android.server.pm.PackageManagerShellCommandDataLoader.Metadata;
import dalvik.system.DexFile; import dalvik.system.DexFile;
@@ -118,7 +119,6 @@ import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
import java.io.PrintWriter; import java.io.PrintWriter;
import java.net.URISyntaxException; import java.net.URISyntaxException;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Base64; import java.util.Base64;
import java.util.Collection; import java.util.Collection;
@@ -3025,9 +3025,9 @@ class PackageManagerShellCommand extends ShellCommand {
// 1. Single file from stdin. // 1. Single file from stdin.
if (args.isEmpty() || STDIN_PATH.equals(args.get(0))) { if (args.isEmpty() || STDIN_PATH.equals(args.get(0))) {
final String name = "base." + (isApex ? "apex" : "apk"); final String name = "base." + (isApex ? "apex" : "apk");
final String metadata = "-" + name; final Metadata metadata = Metadata.forStdIn(name);
session.addFile(LOCATION_DATA_APP, name, sessionSizeBytes, session.addFile(LOCATION_DATA_APP, name, sessionSizeBytes,
metadata.getBytes(StandardCharsets.UTF_8), null); metadata.toByteArray(), null);
return 0; return 0;
} }
@@ -3056,9 +3056,10 @@ class PackageManagerShellCommand extends ShellCommand {
private int processArgForStdin(String arg, PackageInstaller.Session session) { private int processArgForStdin(String arg, PackageInstaller.Session session) {
final String[] fileDesc = arg.split(":"); final String[] fileDesc = arg.split(":");
String name, metadata; String name, fileId;
long sizeBytes; long sizeBytes;
byte[] signature = null; byte[] signature = null;
int streamingVersion = 0;
try { try {
if (fileDesc.length < 2) { if (fileDesc.length < 2) {
@@ -3067,14 +3068,22 @@ class PackageManagerShellCommand extends ShellCommand {
} }
name = fileDesc[0]; name = fileDesc[0];
sizeBytes = Long.parseUnsignedLong(fileDesc[1]); sizeBytes = Long.parseUnsignedLong(fileDesc[1]);
metadata = name; fileId = name;
if (fileDesc.length > 2 && !TextUtils.isEmpty(fileDesc[2])) { if (fileDesc.length > 2 && !TextUtils.isEmpty(fileDesc[2])) {
metadata = fileDesc[2]; fileId = fileDesc[2];
} }
if (fileDesc.length > 3) { if (fileDesc.length > 3) {
signature = Base64.getDecoder().decode(fileDesc[3]); signature = Base64.getDecoder().decode(fileDesc[3]);
} }
if (fileDesc.length > 4) {
streamingVersion = Integer.parseUnsignedInt(fileDesc[4]);
if (streamingVersion < 0 || streamingVersion > 1) {
getErrPrintWriter().println(
"Unsupported streaming version: " + streamingVersion);
return 1;
}
}
} catch (IllegalArgumentException e) { } catch (IllegalArgumentException e) {
getErrPrintWriter().println( getErrPrintWriter().println(
"Unable to parse file parameters: " + arg + ", reason: " + e); "Unable to parse file parameters: " + arg + ", reason: " + e);
@@ -3086,9 +3095,14 @@ class PackageManagerShellCommand extends ShellCommand {
return 1; return 1;
} }
final Metadata metadata;
if (signature != null) { if (signature != null) {
// Streaming/adb mode. // Streaming/adb mode. Versions:
metadata = "+" + metadata; // 0: data only streaming, tree has to be fully available,
// 1: tree and data streaming.
metadata = (streamingVersion == 0) ? Metadata.forDataOnlyStreaming(fileId)
: Metadata.forStreaming(fileId);
try { try {
if (V4Signature.readFrom(signature) == null) { if (V4Signature.readFrom(signature) == null) {
getErrPrintWriter().println("V4 signature is invalid in: " + arg); getErrPrintWriter().println("V4 signature is invalid in: " + arg);
@@ -3101,11 +3115,10 @@ class PackageManagerShellCommand extends ShellCommand {
} }
} else { } else {
// Single-shot read from stdin. // Single-shot read from stdin.
metadata = "-" + metadata; metadata = Metadata.forStdIn(fileId);
} }
session.addFile(LOCATION_DATA_APP, name, sizeBytes, session.addFile(LOCATION_DATA_APP, name, sizeBytes, metadata.toByteArray(), signature);
metadata.getBytes(StandardCharsets.UTF_8), signature);
return 0; return 0;
} }
@@ -3115,7 +3128,7 @@ class PackageManagerShellCommand extends ShellCommand {
final File file = new File(inPath); final File file = new File(inPath);
final String name = file.getName(); final String name = file.getName();
final long size = file.length(); final long size = file.length();
final byte[] metadata = inPath.getBytes(StandardCharsets.UTF_8); final Metadata metadata = Metadata.forLocalFile(inPath);
byte[] v4signatureBytes = null; byte[] v4signatureBytes = null;
// Try to load the v4 signature file for the APK; it might not exist. // Try to load the v4 signature file for the APK; it might not exist.
@@ -3132,7 +3145,7 @@ class PackageManagerShellCommand extends ShellCommand {
} }
} }
session.addFile(LOCATION_DATA_APP, name, size, metadata, v4signatureBytes); session.addFile(LOCATION_DATA_APP, name, size, metadata.toByteArray(), v4signatureBytes);
} }
private int doWriteSplits(int sessionId, ArrayList<String> splitPaths, long sessionSizeBytes, private int doWriteSplits(int sessionId, ArrayList<String> splitPaths, long sessionSizeBytes,

View File

@@ -24,7 +24,6 @@ import android.content.pm.PackageInstaller;
import android.os.ParcelFileDescriptor; import android.os.ParcelFileDescriptor;
import android.os.ShellCommand; import android.os.ShellCommand;
import android.service.dataloader.DataLoaderService; import android.service.dataloader.DataLoaderService;
import android.text.TextUtils;
import android.util.Slog; import android.util.Slog;
import android.util.SparseArray; import android.util.SparseArray;
@@ -114,6 +113,74 @@ public class PackageManagerShellCommandDataLoader extends DataLoaderService {
} }
} }
static class Metadata {
/**
* Full files read from stdin.
*/
static final byte STDIN = 0;
/**
* Full files read from local file.
*/
static final byte LOCAL_FILE = 1;
/**
* Signature tree read from stdin, data streamed.
*/
static final byte DATA_ONLY_STREAMING = 2;
/**
* Everything streamed.
*/
static final byte STREAMING = 3;
private final byte mMode;
private final String mData;
static Metadata forStdIn(String fileId) {
return new Metadata(STDIN, fileId);
}
static Metadata forLocalFile(String filePath) {
return new Metadata(LOCAL_FILE, filePath);
}
static Metadata forDataOnlyStreaming(String fileId) {
return new Metadata(DATA_ONLY_STREAMING, fileId);
}
static Metadata forStreaming(String fileId) {
return new Metadata(STREAMING, fileId);
}
private Metadata(byte mode, String data) {
this.mMode = mode;
this.mData = (data == null) ? "" : data;
}
static Metadata fromByteArray(byte[] bytes) throws IOException {
if (bytes == null || bytes.length == 0) {
return null;
}
byte mode = bytes[0];
String data = new String(bytes, 1, bytes.length - 1, StandardCharsets.UTF_8);
return new Metadata(mode, data);
}
byte[] toByteArray() {
byte[] dataBytes = this.mData.getBytes(StandardCharsets.UTF_8);
byte[] result = new byte[1 + dataBytes.length];
result[0] = this.mMode;
System.arraycopy(dataBytes, 0, result, 1, dataBytes.length);
return result;
}
byte getMode() {
return this.mMode;
}
String getData() {
return this.mData;
}
}
private static class DataLoader implements DataLoaderService.DataLoader { private static class DataLoader implements DataLoaderService.DataLoader {
private DataLoaderParams mParams = null; private DataLoaderParams mParams = null;
private FileSystemConnector mConnector = null; private FileSystemConnector mConnector = null;
@@ -136,19 +203,31 @@ public class PackageManagerShellCommandDataLoader extends DataLoaderService {
} }
try { try {
for (InstallationFile file : addedFiles) { for (InstallationFile file : addedFiles) {
String filePath = new String(file.getMetadata(), StandardCharsets.UTF_8); Metadata metadata = Metadata.fromByteArray(file.getMetadata());
if (TextUtils.isEmpty(filePath) || filePath.startsWith(STDIN_PATH)) { if (metadata == null) {
final ParcelFileDescriptor inFd = getStdInPFD(shellCommand); Slog.e(TAG, "Invalid metadata for file: " + file.getName());
mConnector.writeData(file.getName(), 0, file.getLengthBytes(), inFd); return false;
} else { }
ParcelFileDescriptor incomingFd = null; switch (metadata.getMode()) {
try { case Metadata.STDIN: {
incomingFd = getLocalFile(shellCommand, filePath); final ParcelFileDescriptor inFd = getStdInPFD(shellCommand);
mConnector.writeData(file.getName(), 0, incomingFd.getStatSize(), mConnector.writeData(file.getName(), 0, file.getLengthBytes(), inFd);
incomingFd); break;
} finally {
IoUtils.closeQuietly(incomingFd);
} }
case Metadata.LOCAL_FILE: {
ParcelFileDescriptor incomingFd = null;
try {
incomingFd = getLocalFile(shellCommand, metadata.getData());
mConnector.writeData(file.getName(), 0, incomingFd.getStatSize(),
incomingFd);
} finally {
IoUtils.closeQuietly(incomingFd);
}
break;
}
default:
Slog.e(TAG, "Unsupported metadata mode: " + metadata.getMode());
return false;
} }
} }
return true; return true;

View File

@@ -227,56 +227,40 @@ static inline unique_fd convertPfdToFdAndDup(JNIEnv* env, const JniIds& jni, job
return result; return result;
} }
enum MetadataMode : int8_t {
STDIN = 0,
LOCAL_FILE = 1,
DATA_ONLY_STREAMING = 2,
STREAMING = 3,
};
struct InputDesc { struct InputDesc {
unique_fd fd; unique_fd fd;
IncFsSize size; IncFsSize size;
IncFsBlockKind kind = INCFS_BLOCK_KIND_DATA; IncFsBlockKind kind = INCFS_BLOCK_KIND_DATA;
bool waitOnEof = false; bool waitOnEof = false;
bool streaming = false; bool streaming = false;
MetadataMode mode = STDIN;
}; };
using InputDescs = std::vector<InputDesc>; using InputDescs = std::vector<InputDesc>;
static inline InputDescs openInputs(JNIEnv* env, const JniIds& jni, jobject shellCommand, template <class T>
IncFsSize size, IncFsSpan metadata) { std::optional<T> read(IncFsSpan& data) {
if (data.size < (int32_t)sizeof(T)) {
return {};
}
T res;
memcpy(&res, data.data, sizeof(res));
data.data += sizeof(res);
data.size -= sizeof(res);
return res;
}
static inline InputDescs openLocalFile(JNIEnv* env, const JniIds& jni, jobject shellCommand,
IncFsSize size, const std::string& filePath) {
InputDescs result; InputDescs result;
result.reserve(2); result.reserve(2);
if (metadata.size == 0 || *metadata.data == '-') {
// stdin
auto fd = convertPfdToFdAndDup(
env, jni,
env->CallStaticObjectMethod(jni.packageManagerShellCommandDataLoader,
jni.pmscdGetStdInPFD, shellCommand));
if (fd.ok()) {
result.push_back(InputDesc{
.fd = std::move(fd),
.size = size,
.waitOnEof = true,
});
}
return result;
}
if (*metadata.data == '+') {
// verity tree from stdin, rest is streaming
auto fd = convertPfdToFdAndDup(
env, jni,
env->CallStaticObjectMethod(jni.packageManagerShellCommandDataLoader,
jni.pmscdGetStdInPFD, shellCommand));
if (fd.ok()) {
auto treeSize = verityTreeSizeForFile(size);
result.push_back(InputDesc{
.fd = std::move(fd),
.size = treeSize,
.kind = INCFS_BLOCK_KIND_HASH,
.waitOnEof = true,
.streaming = true,
});
}
return result;
}
// local file and possibly signature
const std::string filePath(metadata.data, metadata.size);
const std::string idsigPath = filePath + ".idsig"; const std::string idsigPath = filePath + ".idsig";
auto idsigFd = convertPfdToFdAndDup( auto idsigFd = convertPfdToFdAndDup(
@@ -314,6 +298,59 @@ static inline InputDescs openInputs(JNIEnv* env, const JniIds& jni, jobject shel
return result; return result;
} }
static inline InputDescs openInputs(JNIEnv* env, const JniIds& jni, jobject shellCommand,
IncFsSize size, IncFsSpan metadata) {
auto mode = read<int8_t>(metadata).value_or(STDIN);
if (mode == LOCAL_FILE) {
// local file and possibly signature
return openLocalFile(env, jni, shellCommand, size,
std::string(metadata.data, metadata.size));
}
auto fd = convertPfdToFdAndDup(
env, jni,
env->CallStaticObjectMethod(jni.packageManagerShellCommandDataLoader,
jni.pmscdGetStdInPFD, shellCommand));
if (!fd.ok()) {
return {};
}
InputDescs result;
switch (mode) {
case STDIN: {
result.push_back(InputDesc{
.fd = std::move(fd),
.size = size,
.waitOnEof = true,
});
break;
}
case DATA_ONLY_STREAMING: {
// verity tree from stdin, rest is streaming
auto treeSize = verityTreeSizeForFile(size);
result.push_back(InputDesc{
.fd = std::move(fd),
.size = treeSize,
.kind = INCFS_BLOCK_KIND_HASH,
.waitOnEof = true,
.streaming = true,
.mode = DATA_ONLY_STREAMING,
});
break;
}
case STREAMING: {
result.push_back(InputDesc{
.fd = std::move(fd),
.size = 0,
.streaming = true,
.mode = STREAMING,
});
break;
}
}
return result;
}
static inline JNIEnv* GetJNIEnvironment(JavaVM* vm) { static inline JNIEnv* GetJNIEnvironment(JavaVM* vm) {
JNIEnv* env; JNIEnv* env;
if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) { if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) {
@@ -390,6 +427,7 @@ private:
blocks.reserve(BLOCKS_COUNT); blocks.reserve(BLOCKS_COUNT);
unique_fd streamingFd; unique_fd streamingFd;
MetadataMode streamingMode;
for (auto&& file : addedFiles) { for (auto&& file : addedFiles) {
auto inputs = openInputs(env, jni, shellCommand, file.size, file.metadata); auto inputs = openInputs(env, jni, shellCommand, file.size, file.metadata);
if (inputs.empty()) { if (inputs.empty()) {
@@ -411,6 +449,7 @@ private:
for (auto&& input : inputs) { for (auto&& input : inputs) {
if (input.streaming && !streamingFd.ok()) { if (input.streaming && !streamingFd.ok()) {
streamingFd.reset(dup(input.fd)); streamingFd.reset(dup(input.fd));
streamingMode = input.mode;
} }
if (!copyToIncFs(incfsFd, input.size, input.kind, input.fd, input.waitOnEof, if (!copyToIncFs(incfsFd, input.size, input.kind, input.fd, input.waitOnEof,
&buffer, &blocks)) { &buffer, &blocks)) {
@@ -425,7 +464,7 @@ private:
if (streamingFd.ok()) { if (streamingFd.ok()) {
ALOGE("onPrepareImage: done, proceeding to streaming."); ALOGE("onPrepareImage: done, proceeding to streaming.");
return initStreaming(std::move(streamingFd)); return initStreaming(std::move(streamingFd), streamingMode);
} }
ALOGE("onPrepareImage: done."); ALOGE("onPrepareImage: done.");
@@ -564,7 +603,7 @@ private:
} }
// Streaming. // Streaming.
bool initStreaming(unique_fd inout) { bool initStreaming(unique_fd inout, MetadataMode mode) {
mEventFd.reset(eventfd(0, EFD_CLOEXEC)); mEventFd.reset(eventfd(0, EFD_CLOEXEC));
if (mEventFd < 0) { if (mEventFd < 0) {
ALOGE("Failed to create eventfd."); ALOGE("Failed to create eventfd.");
@@ -591,8 +630,8 @@ private:
} }
} }
mReceiverThread = mReceiverThread = std::thread(
std::thread([this, io = std::move(inout)]() mutable { receiver(std::move(io)); }); [this, io = std::move(inout), mode]() mutable { receiver(std::move(io), mode); });
ALOGI("Started streaming..."); ALOGI("Started streaming...");
return true; return true;
} }
@@ -624,7 +663,7 @@ private:
} }
} }
void receiver(unique_fd inout) { void receiver(unique_fd inout, MetadataMode mode) {
std::vector<uint8_t> data; std::vector<uint8_t> data;
std::vector<IncFsDataBlock> instructions; std::vector<IncFsDataBlock> instructions;
std::unordered_map<FileIdx, unique_fd> writeFds; std::unordered_map<FileIdx, unique_fd> writeFds;
@@ -667,7 +706,7 @@ private:
break; break;
} }
const FileIdx fileIdx = header.fileIdx; const FileIdx fileIdx = header.fileIdx;
const android::dataloader::FileId fileId = convertFileIndexToFileId(fileIdx); const android::dataloader::FileId fileId = convertFileIndexToFileId(mode, fileIdx);
if (!android::incfs::isValidFileId(fileId)) { if (!android::incfs::isValidFileId(fileId)) {
ALOGE("Unknown data destination for file ID %d. " ALOGE("Unknown data destination for file ID %d. "
"Ignore.", "Ignore.",
@@ -679,7 +718,7 @@ private:
if (writeFd < 0) { if (writeFd < 0) {
writeFd.reset(this->mIfs->openWrite(fileId)); writeFd.reset(this->mIfs->openWrite(fileId));
if (writeFd < 0) { if (writeFd < 0) {
ALOGE("Failed to open file %d for writing (%d). Aboring.", header.fileIdx, ALOGE("Failed to open file %d for writing (%d). Aborting.", header.fileIdx,
-writeFd); -writeFd);
break; break;
} }
@@ -716,9 +755,11 @@ private:
} }
FileIdx convertFileIdToFileIndex(android::dataloader::FileId fileId) { FileIdx convertFileIdToFileIndex(android::dataloader::FileId fileId) {
// FileId is a string in format '+FileIdx\0'. // FileId has format '\2FileIdx'.
const char* meta = (const char*)&fileId; const char* meta = (const char*)&fileId;
if (*meta != '+') {
int8_t mode = *meta;
if (mode != DATA_ONLY_STREAMING && mode != STREAMING) {
return -1; return -1;
} }
@@ -732,10 +773,10 @@ private:
return FileIdx(fileIdx); return FileIdx(fileIdx);
} }
android::dataloader::FileId convertFileIndexToFileId(FileIdx fileIdx) { android::dataloader::FileId convertFileIndexToFileId(MetadataMode mode, FileIdx fileIdx) {
IncFsFileId fileId = {}; IncFsFileId fileId = {};
char* meta = (char*)&fileId; char* meta = (char*)&fileId;
*meta = '+'; *meta = mode;
if (auto [p, ec] = std::to_chars(meta + 1, meta + sizeof(fileId), fileIdx); if (auto [p, ec] = std::to_chars(meta + 1, meta + sizeof(fileId), fileIdx);
ec != std::errc()) { ec != std::errc()) {
return {}; return {};