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;
     }