mk_payload is a host tool
Because virtualization service makes a microdroid payload disk image,
we don't need mk_payload any longer. But having a host tool might be
helpful for other uses. For example, ARCVM can use this tool for its own
use.
example_config.json:
{
"apexes": [
{ "name": "com.android.foo",
"path": "path/to/com.android.foo.apex" },
...
}
Bug: 189242583
Test: mk_payload example_config.json payload.img
Change-Id: I8d04701ed27782ddebb28449ba75e089309ec4c8
diff --git a/microdroid/payload/Android.bp b/microdroid/payload/Android.bp
index c7bc415..72711c3 100644
--- a/microdroid/payload/Android.bp
+++ b/microdroid/payload/Android.bp
@@ -49,30 +49,26 @@
],
}
-cc_binary {
+cc_binary_host {
name: "mk_payload",
srcs: [
"mk_payload.cc",
],
- shared_libs: [
+ static_libs: [
+ "lib_microdroid_metadata_proto",
"libbase",
+ "libcdisk_spec",
"libcuttlefish_fs",
"libcuttlefish_utils",
- "liblog",
- "libz",
- ],
- static_libs: [
- "lib_microdroid_metadata_proto_lite",
- "libcdisk_spec",
"libext2_uuid",
"libimage_aggregator",
"libjsoncpp",
+ "liblog",
+ "libprotobuf-cpp-full",
"libprotobuf-cpp-lite",
"libsparse",
"libxml2",
+ "libz",
],
- generated_sources: ["apex-info-list"],
- apex_available: [
- "com.android.virt",
- ],
+ static_executable: true,
}
diff --git a/microdroid/payload/README.md b/microdroid/payload/README.md
index 35502c1..bf05c49 100644
--- a/microdroid/payload/README.md
+++ b/microdroid/payload/README.md
@@ -3,6 +3,9 @@
Payload disk is a composite disk image referencing host APEXes and an APK so that microdroid
mounts/activates APK/APEXes and executes a binary within the APK.
+Payload disk is created by [VirtualizationService](../../virtualizationservice) Service when
+starting a VM.
+
## Partitions
Payload disk has 1 + N(number of APEX/APK payloads) partitions.
@@ -14,7 +17,7 @@
* partition 1: Metadata partition
* partition 2 ~ n: APEX payloads
-* partition n + 1: APK payload
+* partition n+1, n+2: APK payload and its idsig
It's subject to change in the future, though.
@@ -34,52 +37,37 @@
Each payload partition presents APEX or APK passed from the host.
-At the end of each payload partition the size of the original payload file (APEX or APK) is stored
-in 4-byte big endian.
+Note that each payload passed to the Guest is read by a block device. If a payload is not sized to a
+multiples of 4k, reading it would fail. To prevent that, "zero fillers" are added for those files.
+For example, if an APK is 8000 byte big, the APK partition would be padded with 192 bytes of zeros.
-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,
+# `mk_payload`
- 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_payload`
-
-`mk_payload` creates a payload composite disk image as described in a JSON which is intentionlly
-similar to the schema of VM payload config.
+`mk_payload` is a small utility to create a payload disk image.
```
$ cat payload_config.json
{
- "system_apexes": [
- "com.android.adbd",
- ],
"apexes": [
{
"name": "com.my.hello",
- "path": "hello.apex"
+ "path": "hello.apex",
}
],
"apk": {
"name": "com.my.world",
- "path": "/path/to/world.apk"
+ "path": "/path/to/world.apk",
+ "idsigPath": "/path/to/world.apk.idsig",
}
}
-$ 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
+$ m mk_payload
+$ mk_payload payload_config.json payload.img
+$ ls
payload.img
payload-footer.img
payload-header.img
payload-metadata.img
-payload.img.0 # fillers
-payload.img.1
+payload-filler-0.img
+payload-filler-1.img
...
```
-
-In the future, [VirtualizationService](../../virtualizationservice) will handle this.
diff --git a/microdroid/payload/mk_payload.cc b/microdroid/payload/mk_payload.cc
index b27683c..33e91b9 100644
--- a/microdroid/payload/mk_payload.cc
+++ b/microdroid/payload/mk_payload.cc
@@ -26,7 +26,6 @@
#include <android-base/file.h>
#include <android-base/result.h>
-#include <com_android_apex.h>
#include <image_aggregator.h>
#include <json/json.h>
@@ -42,9 +41,6 @@
using android::microdroid::Metadata;
using android::microdroid::WriteMetadata;
-using com::android::apex::ApexInfoList;
-using com::android::apex::readApexInfoList;
-
using cuttlefish::AlignToPartitionSize;
using cuttlefish::CreateCompositeDisk;
using cuttlefish::kLinuxFilesystem;
@@ -58,9 +54,9 @@
return static_cast<uint32_t>(st.st_size);
}
-std::string ToAbsolute(const std::string& path, const std::string& dirname) {
+std::string RelativeTo(const std::string& path, const std::string& dirname) {
bool is_absolute = !path.empty() && path[0] == '/';
- if (is_absolute) {
+ if (is_absolute || dirname == ".") {
return path;
} else {
return dirname + "/" + path;
@@ -81,25 +77,20 @@
std::string name; // the apex name
std::string path; // the path to the apex file
// absolute or relative to the config file
- std::optional<std::string> public_key;
- std::optional<std::string> root_digest;
};
struct ApkConfig {
std::string name;
std::string path;
- // TODO(jooyung) make this required?
- std::optional<std::string> idsig_path;
+ std::string idsig_path;
};
struct Config {
std::string dirname; // config file's direname to resolve relative paths in the config
- // TODO(b/185956069) remove this when VirtualizationService can provide apex paths
- std::vector<std::string> system_apexes;
-
std::vector<ApexConfig> apexes;
std::optional<ApkConfig> apk;
+ // This is a path in the guest side
std::optional<std::string> payload_config_path;
};
@@ -137,8 +128,6 @@
Result<void> ParseJson(const Json::Value& value, ApexConfig& apex_config) {
DO(ParseJson(value["name"], apex_config.name));
DO(ParseJson(value["path"], apex_config.path));
- DO(ParseJson(value["publicKey"], apex_config.public_key));
- DO(ParseJson(value["rootDigest"], apex_config.root_digest));
return {};
}
@@ -150,7 +139,6 @@
}
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));
DO(ParseJson(value["payload_config_path"], config.payload_config_path));
@@ -163,7 +151,7 @@
Json::Value root;
Json::String errs;
if (!parseFromStream(builder, in, &root, &errs)) {
- return Error() << "bad config: " << errs;
+ return Error() << errs;
}
Config config;
@@ -174,63 +162,22 @@
#undef DO
-Result<void> LoadSystemApexes(Config& config) {
- static const char* kApexInfoListFile = "/apex/apex-info-list.xml";
- std::optional<ApexInfoList> apex_info_list = readApexInfoList(kApexInfoListFile);
- if (!apex_info_list.has_value()) {
- return Error() << "Failed to read " << kApexInfoListFile;
- }
- auto get_apex_path = [&](const std::string& apex_name) -> std::optional<std::string> {
- for (const auto& apex_info : apex_info_list->getApexInfo()) {
- if (apex_info.getIsActive() && apex_info.getModuleName() == apex_name) {
- return apex_info.getModulePath();
- }
- }
- return std::nullopt;
- };
- for (const auto& apex_name : config.system_apexes) {
- const auto& apex_path = get_apex_path(apex_name);
- if (!apex_path.has_value()) {
- return Error() << "Can't find the system apex: " << apex_name;
- }
- config.apexes.push_back(ApexConfig{
- .name = apex_name,
- .path = *apex_path,
- .public_key = std::nullopt,
- .root_digest = std::nullopt,
- });
- }
- return {};
-}
-
Result<void> MakeMetadata(const Config& config, const std::string& filename) {
Metadata metadata;
metadata.set_version(1);
+ int apex_index = 0;
for (const auto& apex_config : config.apexes) {
auto* apex = metadata.add_apexes();
-
- // name
apex->set_name(apex_config.name);
-
- // publicKey
- if (apex_config.public_key.has_value()) {
- apex->set_publickey(apex_config.public_key.value());
- }
-
- // rootDigest
- if (apex_config.root_digest.has_value()) {
- apex->set_rootdigest(apex_config.root_digest.value());
- }
+ apex->set_partition_name("microdroid-apex-" + std::to_string(apex_index++));
}
if (config.apk.has_value()) {
auto* apk = metadata.mutable_apk();
apk->set_name(config.apk->name);
apk->set_payload_partition_name("microdroid-apk");
- if (config.apk->idsig_path.has_value()) {
- apk->set_idsig_partition_name("microdroid-apk-idsig");
- }
+ apk->set_idsig_partition_name("microdroid-apk-idsig");
}
if (config.payload_config_path.has_value()) {
@@ -241,34 +188,8 @@
return WriteMetadata(metadata, out);
}
-// fill (zeros + original file's size) with aligning BLOCK_SIZE(4096) boundary
-// return true when the filler is generated.
-Result<bool> SizeFiller(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 true;
-}
-
// fill zeros to align |file_path|'s size to BLOCK_SIZE(4096) boundary.
-// return true when the filler is generated.
+// return true when the filler is needed.
Result<bool> ZeroFiller(const std::string& file_path, const std::string& filler_path) {
auto file_size = GetFileSize(file_path);
if (!file_size.ok()) {
@@ -288,32 +209,17 @@
return true;
}
-// Do not generate any fillers
-// Note that CreateCompositeDisk() handles gaps between partitions.
-Result<bool> NoFiller(const std::string& file_path, const std::string& filler_path) {
- (void)file_path;
- (void)filler_path;
- return false;
-}
-
Result<void> MakePayload(const Config& config, const std::string& metadata_file,
const std::string& output_file) {
std::vector<MultipleImagePartition> partitions;
- // put metadata at the first partition
- partitions.push_back(MultipleImagePartition{
- .label = "payload-metadata",
- .image_file_paths = {metadata_file},
- .type = kLinuxFilesystem,
- .read_only = true,
- });
-
int filler_count = 0;
- auto add_partition = [&](auto partition_name, auto file_path, auto filler) -> Result<void> {
+ auto add_partition = [&](auto partition_name, auto file_path) -> Result<void> {
std::vector<std::string> image_files{file_path};
- std::string filler_path = output_file + "." + std::to_string(filler_count++);
- if (auto ret = filler(file_path, filler_path); !ret.ok()) {
+ std::string filler_path =
+ AppendFileName(output_file, "-filler-" + std::to_string(filler_count++));
+ if (auto ret = ZeroFiller(file_path, filler_path); !ret.ok()) {
return ret.error();
} else if (*ret) {
image_files.push_back(filler_path);
@@ -327,27 +233,31 @@
return {};
};
- // put apexes at the subsequent partitions with "size" filler
+ // put metadata at the first partition
+ partitions.push_back(MultipleImagePartition{
+ .label = "payload-metadata",
+ .image_file_paths = {metadata_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];
- std::string apex_path = ToAbsolute(apex_config.path, config.dirname);
- if (auto ret = add_partition("microdroid-apex-" + std::to_string(i), apex_path, SizeFiller);
+ std::string apex_path = RelativeTo(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 "zero" filler.
- // TODO(jooyung): partition name("microdroid-apk") is TBD
+ // put apk and its idsig
if (config.apk.has_value()) {
- std::string apk_path = ToAbsolute(config.apk->path, config.dirname);
- if (auto ret = add_partition("microdroid-apk", apk_path, ZeroFiller); !ret.ok()) {
+ std::string apk_path = RelativeTo(config.apk->path, config.dirname);
+ if (auto ret = add_partition("microdroid-apk", apk_path); !ret.ok()) {
return ret.error();
}
- if (config.apk->idsig_path.has_value()) {
- std::string idsig_path = ToAbsolute(config.apk->idsig_path.value(), config.dirname);
- if (auto ret = add_partition("microdroid-apk-idsig", idsig_path, NoFiller); !ret.ok()) {
- return ret.error();
- }
+ std::string idsig_path = RelativeTo(config.apk->idsig_path, config.dirname);
+ if (auto ret = add_partition("microdroid-apk-idsig", idsig_path); !ret.ok()) {
+ return ret.error();
}
}
@@ -365,12 +275,7 @@
auto config = LoadConfig(argv[1]);
if (!config.ok()) {
- std::cerr << config.error() << '\n';
- return 1;
- }
-
- if (const auto res = LoadSystemApexes(*config); !res.ok()) {
- std::cerr << res.error() << '\n';
+ std::cerr << "bad config: " << config.error() << '\n';
return 1;
}