Merge changes from topic "add-file-size"
* changes:
microdroid: remove redundant mk_microdroid_signature
microdroid: add "apk" to payload
microdroid: each partition in payload has size in it
diff --git a/apex/Android.bp b/apex/Android.bp
index 91df00c..459545c 100644
--- a/apex/Android.bp
+++ b/apex/Android.bp
@@ -49,7 +49,6 @@
// tools to create composite images
"mk_cdisk",
- "mk_microdroid_signature",
"mk_payload",
],
prebuilts: [
diff --git a/microdroid/signature/Android.bp b/microdroid/signature/Android.bp
index b993e4e..35c4e9e 100644
--- a/microdroid/signature/Android.bp
+++ b/microdroid/signature/Android.bp
@@ -39,25 +39,6 @@
}
cc_binary {
- name: "mk_microdroid_signature",
- srcs: [
- "mk_microdroid_signature.cc",
- ],
- shared_libs: [
- "libbase",
- "liblog",
- ],
- static_libs: [
- "lib_microdroid_signature_proto_lite",
- "libjsoncpp",
- "libprotobuf-cpp-lite",
- ],
- apex_available: [
- "com.android.virt",
- ],
-}
-
-cc_binary {
name: "mk_payload",
srcs: [
"mk_payload.cc",
diff --git a/microdroid/signature/README.md b/microdroid/signature/README.md
index 0a39c31..b47c303 100644
--- a/microdroid/signature/README.md
+++ b/microdroid/signature/README.md
@@ -1,12 +1,28 @@
-# Microdroid Signature
+# Microdroid Payload
+
+Payload disk is a composite disk referencing host APEXes and an APK so that microdroid
+reads activates APEXes and executes a binary within the APK.
+
+## Format
+
+Payload disk has 1 + N(number of APEX/APK payloads) partitions.
+
+The first partition is a Microdroid Signature partition which describes other partitions.
+And APEXes and an APK are following as separate partitions.
+
+For now, the order of partitions are important.
+
+* partition 1: Microdroid Signature
+* partition 2 ~ n: APEX payloads
+* partition n + 1: APK payload
+
+It's subject to change in the future, though.
+
+### Microdroid Signature
Microdroid Signature contains the signatures of the payloads so that the payloads are
verified inside the Guest OS.
-* APEX packages that are passed to microdroid should be listed in the Microroid Signature.
-
-## Format
-
Microdroid Signature is composed of header and body.
| offset | size | description |
@@ -14,52 +30,25 @@
| 0 | 4 | Header. unsigned int32: body length(L) in big endian |
| 4 | L | Body. A protobuf message. [schema](microdroid_signature.proto) |
+### Payload Partitions
+
+At the end of each payload partition the size of the original payload file (APEX or APK) is stored
+in 4-byte big endian.
+
+For example, the following code shows how to get the original size of host apex file
+when the apex is read in microdroid as /dev/block/vdc2,
+
+ int fd = open("/dev/block/vdc2", O_RDONLY | O_BINARY | O_CLOEXEC);
+ uint32_t size;
+ lseek(fd, -sizeof(size), SEEK_END);
+ read(fd, &size, sizeof(size));
+ size = betoh32(size);
+
## How to Create
-### `mk_microdroid_signature` and `mk_cdisk`
-
-For testing purpose, use `mk_microdroid_signature` to create a Microdroid Signature.
-
-```
-$ cat signature_config.json
-{
- "apexes": [
- {
- "name": "com.my.hello",
- "path": "hello.apex"
- }
- ]
-}
-$ adb push signature_config.json hello.apex /data/local/tmp/
-$ adb shell 'cd /data/local/tmp; /apex/com.android.virt/bin/mk_microdroid_signature signature_config.json signature
-```
-
-Then, pass the signature as the first partition of the payload disk image.
-
-```
-$ cat payload_cdisk.json
-{
- "partitions": [
- {
- "label": "signature",
- "path": "signature"
- },
- {
- "label": "com.my.hello",
- "path": "hello.apex"
- }
- ]
-}
-$ adb push payload_cdisk.json /data/local/tmp/
-$ adb shell 'cd /data/local/tmp; /apex/com.android.virt/bin/mk_cdisk payload_cdisk.json payload.img
-$ adb shell 'cd /data/local/tmp; /apex/com.android.virt/bin/crosvm .... --disk=payload.img'
-```
-
### `mk_payload`
-`mk_payload` combines these two steps into a single step. Additionally, `mk_payload` can search system APEXes as well.
-This will generate the output composite image as well as three more component images. (See below)
-
+`mk_payload` creates a payload image.
```
$ cat payload_config.json
{
@@ -71,15 +60,22 @@
"name": "com.my.hello",
"path": "hello.apex"
}
- ]
+ ],
+ "apk": {
+ "name": "com.my.world",
+ "path": "/path/to/world.apk"
+ }
}
$ adb push payload_config.json hello.apex /data/local/tmp/
$ adb shell 'cd /data/local/tmp; /apex/com.android.virt/bin/mk_payload payload_config.json payload.img
$ adb shell ls /data/local/tmp/*.img
+payload.img
payload-footer.img
payload-header.img
payload-signature.img
-payload.img
+payload.img.0 # fillers
+payload.img.1
+...
```
-In the future, [VirtManager](../../virtmanager) will handle these stuffs.
\ No newline at end of file
+In the future, [VirtManager](../../virtmanager) will handle this.
\ No newline at end of file
diff --git a/microdroid/signature/microdroid_signature.proto b/microdroid/signature/microdroid_signature.proto
index 8335ff5..8816aa8 100644
--- a/microdroid/signature/microdroid_signature.proto
+++ b/microdroid/signature/microdroid_signature.proto
@@ -25,6 +25,8 @@
// Lists the signature information of the payload apexes.
// The payload apexes are mapped to the partitions following the signature partition.
repeated ApexSignature apexes = 2;
+
+ ApkSignature apk = 3;
}
message ApexSignature {
@@ -44,3 +46,12 @@
// When specified, the root digest of the apex should match with it.
string rootDigest = 4;
}
+
+message ApkSignature {
+ // Required.
+ // The name of APK.
+ string name = 1;
+
+ string payload_partition_name = 2;
+ string idsig_partition_name = 3;
+}
\ No newline at end of file
diff --git a/microdroid/signature/mk_microdroid_signature.cc b/microdroid/signature/mk_microdroid_signature.cc
deleted file mode 100644
index d5cb1a5..0000000
--- a/microdroid/signature/mk_microdroid_signature.cc
+++ /dev/null
@@ -1,141 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <unistd.h>
-
-#include <fstream>
-#include <iostream>
-#include <string>
-
-#include <android-base/file.h>
-#include <android-base/result.h>
-#include <json/json.h>
-
-#include "microdroid/signature.h"
-
-using android::base::Dirname;
-using android::base::ErrnoError;
-using android::base::Error;
-using android::base::Result;
-using android::microdroid::MicrodroidSignature;
-using android::microdroid::WriteMicrodroidSignature;
-
-Result<uint32_t> GetFileSize(const std::string& path) {
- struct stat st;
- if (lstat(path.c_str(), &st) == -1) {
- return ErrnoError() << "Can't lstat " << path;
- }
- return static_cast<uint32_t>(st.st_size);
-}
-
-// config JSON schema:
-// {
-// "apexes": [
-// {
-// "name": string, // the apex name
-// "path": string, // the path to the apex file
-// // absolute or relative to the config file
-// "publicKey": string, // optional
-// "rootDigest": string, // optional
-// }
-// ]
-// }
-
-Result<MicrodroidSignature> LoadConfig(const std::string& config_file) {
- MicrodroidSignature signature;
- signature.set_version(1);
-
- const std::string dirname = Dirname(config_file);
- std::ifstream in(config_file);
- Json::CharReaderBuilder builder;
- Json::Value root;
- Json::String errs;
- if (!parseFromStream(builder, in, &root, &errs)) {
- return Error() << "bad config: " << errs;
- }
-
- for (const Json::Value& apex : root["apexes"]) {
- auto apex_signature = signature.add_apexes();
-
- Json::Value name = apex["name"];
- Json::Value path = apex["path"];
- Json::Value publicKey = apex["publicKey"];
- Json::Value rootDigest = apex["rootDigest"];
-
- if (name.isString()) {
- apex_signature->set_name(name.asString());
- } else {
- return Error() << "bad config: apexes.name should be a string: " << path;
- }
-
- if (path.isString()) {
- std::string apex_path = path.asString();
-
- // resolve path with the config_file's dirname if not absolute
- bool is_absolute = !apex_path.empty() && apex_path[0] == '/';
- if (!is_absolute) {
- apex_path = dirname + "/" + apex_path;
- }
-
- auto file_size = GetFileSize(apex_path);
- if (!file_size.ok()) {
- return Error() << "I/O error: " << file_size.error();
- }
- apex_signature->set_size(file_size.value());
- } else {
- return Error() << "bad config: apexes.path should be a string: " << path;
- }
-
- if (publicKey.isString()) {
- apex_signature->set_publickey(publicKey.asString());
- } else if (!publicKey.isNull()) {
- return Error() << "bad config: apexes.publicKey should be a string or null: "
- << publicKey;
- }
-
- if (rootDigest.isString()) {
- apex_signature->set_rootdigest(rootDigest.asString());
- } else if (!rootDigest.isNull()) {
- return Error() << "bad config: apexes.rootDigest should be a string or null: "
- << rootDigest;
- }
- }
-
- return signature;
-}
-
-int main(int argc, char** argv) {
- if (argc != 3) {
- std::cerr << "Usage: " << argv[0] << " <config> <output>\n";
- return 1;
- }
-
- auto config = LoadConfig(argv[1]);
- if (!config.ok()) {
- std::cerr << config.error() << '\n';
- return 1;
- }
-
- std::ofstream out(argv[2]);
- auto result = WriteMicrodroidSignature(*config, out);
- if (!result.ok()) {
- std::cerr << result.error() << '\n';
- return 1;
- }
- return 0;
-}
\ No newline at end of file
diff --git a/microdroid/signature/mk_payload.cc b/microdroid/signature/mk_payload.cc
index 8046b5e..a3501d4 100644
--- a/microdroid/signature/mk_payload.cc
+++ b/microdroid/signature/mk_payload.cc
@@ -36,16 +36,19 @@
using android::base::ErrnoError;
using android::base::Error;
using android::base::Result;
+using android::base::unique_fd;
using android::microdroid::ApexSignature;
+using android::microdroid::ApkSignature;
using android::microdroid::MicrodroidSignature;
using android::microdroid::WriteMicrodroidSignature;
using com::android::apex::ApexInfoList;
using com::android::apex::readApexInfoList;
+using cuttlefish::AlignToPartitionSize;
using cuttlefish::CreateCompositeDisk;
-using cuttlefish::ImagePartition;
using cuttlefish::kLinuxFilesystem;
+using cuttlefish::MultipleImagePartition;
Result<uint32_t> GetFileSize(const std::string& path) {
struct stat st;
@@ -82,11 +85,18 @@
std::optional<std::string> root_digest;
};
+struct ApkConfig {
+ std::string name;
+ // TODO(jooyung): find path/idsig with name
+ std::string path;
+};
+
struct Config {
std::string dirname; // config file's direname to resolve relative paths in the config
std::vector<std::string> system_apexes;
std::vector<ApexConfig> apexes;
+ std::optional<ApkConfig> apk;
};
#define DO(expr) \
@@ -100,7 +110,8 @@
return {};
}
-Result<void> ParseJson(const Json::Value& value, std::optional<std::string>& s) {
+template <typename T>
+Result<void> ParseJson(const Json::Value& value, std::optional<T>& s) {
if (value.isNull()) {
s.reset();
return {};
@@ -117,6 +128,12 @@
return {};
}
+Result<void> ParseJson(const Json::Value& value, ApkConfig& apk_config) {
+ DO(ParseJson(value["name"], apk_config.name));
+ DO(ParseJson(value["path"], apk_config.path));
+ return {};
+}
+
template <typename T>
Result<void> ParseJson(const Json::Value& values, std::vector<T>& parsed) {
for (const Json::Value& value : values) {
@@ -130,6 +147,7 @@
Result<void> ParseJson(const Json::Value& value, Config& config) {
DO(ParseJson(value["system_apexes"], config.system_apexes));
DO(ParseJson(value["apexes"], config.apexes));
+ DO(ParseJson(value["apk"], config.apk));
return {};
}
@@ -207,31 +225,84 @@
}
}
+ if (config.apk.has_value()) {
+ ApkSignature* apk_signature = signature.mutable_apk();
+ apk_signature->set_name(config.apk->name);
+ apk_signature->set_payload_partition_name("microdroid-apk");
+ // TODO(jooyung): set idsig partition as well
+ }
+
std::ofstream out(filename);
return WriteMicrodroidSignature(signature, out);
}
+Result<void> GenerateFiller(const std::string& file_path, const std::string& filler_path) {
+ auto file_size = GetFileSize(file_path);
+ if (!file_size.ok()) {
+ return file_size.error();
+ }
+ auto disk_size = AlignToPartitionSize(*file_size + sizeof(uint32_t));
+
+ unique_fd fd(TEMP_FAILURE_RETRY(open(filler_path.c_str(), O_CREAT | O_WRONLY | O_TRUNC, 0600)));
+ if (fd.get() == -1) {
+ return ErrnoError() << "open(" << filler_path << ") failed.";
+ }
+ uint32_t size = htobe32(static_cast<uint32_t>(*file_size));
+ if (ftruncate(fd.get(), disk_size - *file_size) == -1) {
+ return ErrnoError() << "ftruncate(" << filler_path << ") failed.";
+ }
+ if (lseek(fd.get(), -sizeof(size), SEEK_END) == -1) {
+ return ErrnoError() << "lseek(" << filler_path << ") failed.";
+ }
+ if (write(fd.get(), &size, sizeof(size)) <= 0) {
+ return ErrnoError() << "write(" << filler_path << ") failed.";
+ }
+ return {};
+}
+
Result<void> MakePayload(const Config& config, const std::string& signature_file,
const std::string& output_file) {
- std::vector<ImagePartition> partitions;
+ std::vector<MultipleImagePartition> partitions;
// put signature at the first partition
- partitions.push_back(ImagePartition{
+ partitions.push_back(MultipleImagePartition{
.label = "signature",
- .image_file_path = signature_file,
+ .image_file_paths = {signature_file},
.type = kLinuxFilesystem,
.read_only = true,
});
- // put apexes at the subsequent partitions
- for (size_t i = 0; i < config.apexes.size(); i++) {
- const auto& apex_config = config.apexes[i];
- partitions.push_back(ImagePartition{
- .label = "payload_apex_" + std::to_string(i),
- .image_file_path = apex_config.path,
+ int filler_count = 0;
+ auto add_partition = [&](auto partition_name, auto file_path) -> Result<void> {
+ std::string filler_path = output_file + "." + std::to_string(filler_count++);
+ if (auto ret = GenerateFiller(file_path, filler_path); !ret.ok()) {
+ return ret.error();
+ }
+ partitions.push_back(MultipleImagePartition{
+ .label = partition_name,
+ .image_file_paths = {file_path, filler_path},
.type = kLinuxFilesystem,
.read_only = true,
});
+ return {};
+ };
+
+ // put apexes at the subsequent partitions with "size" filler
+ for (size_t i = 0; i < config.apexes.size(); i++) {
+ const auto& apex_config = config.apexes[i];
+ std::string apex_path = ToAbsolute(apex_config.path, config.dirname);
+ if (auto ret = add_partition("microdroid-apex-" + std::to_string(i), apex_path);
+ !ret.ok()) {
+ return ret.error();
+ }
+ }
+ // put apk with "size" filler if necessary.
+ // TODO(jooyung): partition name("microdroid-apk") is TBD
+ if (config.apk.has_value()) {
+ std::string apk_path = ToAbsolute(config.apk->path, config.dirname);
+ if (auto ret = add_partition("microdroid-apk", apk_path); !ret.ok()) {
+ return ret.error();
+ }
}
const std::string gpt_header = AppendFileName(output_file, "-header");