From b549b19ea0295aa15b5884fe7c7a9944dfb17b64 Mon Sep 17 00:00:00 2001 From: "jongjin1.park" Date: Fri, 24 Apr 2026 09:56:26 +0900 Subject: [PATCH 1/2] assemble_cvd: Support named/A-B partitions in custom_partition_path flag 1. Extended custom_partition_path flag format (disk_flags.cc) Previously, the --custom_partition_path flag only accepted a list of image file paths separated by semicolons, and partition names were hard-coded as "custom", "custom_1", "custom_2", etc. This made it impossible to assign OEM-specific partition names required by the bootloader (e.g. "oem_ext", "confptstatic", etc.). The entry format is now extended to 'name:path[:ab]': - name : partition label visible to the bootloader/kernel - path : image file path - :ab : optional suffix; if present, both _a and _b A/B slot partitions are created from the same image file Legacy format (path only) is still accepted for backward compatibility, in which case partition names fall back to the original auto-generated "custom" / "custom_N" naming. A LOG(INFO) message is emitted for each partition that is added to aid debugging of partition configuration issues. Example: --custom_partition_path="oem_ext:./oem_ext.img:ab;confcal:./confcal.img" 2. Include partition label in TextConfig() (disk_builder.cpp) TextConfig() is used to detect whether the composite disk needs to be rebuilt on the next run. Previously it stored only the image file path for each partition. As a result, changing only a partition label (while keeping the same image file) would not trigger a rebuild, leaving the stale composite disk in use. Now the format is 'label:path' per line, so any change to either the partition name or the image path correctly invalidates the cached composite disk and forces a rebuild. Change-Id: Ic953c84ce2c07bea2a1f12111726b366a43e33c1 Signed-off-by: Jongjin Park --- .../assemble_cvd/assemble_cvd_flags.cpp | 12 ++++-- .../disk/android_composite_disk_config.cc | 38 +++++++++++++++++-- .../commands/assemble_cvd/disk_builder.cpp | 2 +- 3 files changed, 43 insertions(+), 9 deletions(-) diff --git a/base/cvd/cuttlefish/host/commands/assemble_cvd/assemble_cvd_flags.cpp b/base/cvd/cuttlefish/host/commands/assemble_cvd/assemble_cvd_flags.cpp index e62f828b4d0..ecb5c7d6ec4 100644 --- a/base/cvd/cuttlefish/host/commands/assemble_cvd/assemble_cvd_flags.cpp +++ b/base/cvd/cuttlefish/host/commands/assemble_cvd/assemble_cvd_flags.cpp @@ -497,10 +497,14 @@ DEFINE_string(fuchsia_root_image, CF_DEFAULTS_FUCHSIA_ROOT_IMAGE, DEFINE_string( custom_partition_path, CF_DEFAULTS_CUSTOM_PARTITION_PATH, - "Location of custom image that will be passed as a \"custom\" partition" - "to rootfs and can be used by /dev/block/by-name/custom. Multiple images " - "can be passed, separated by semicolons and can be used as " - "/dev/block/by-name/custom_1, /dev/block/by-name/custom_2, etc. Example: " + "Location of custom image that will be passed as a custom partition " + "to rootfs. Multiple entries can be passed, separated by semicolons. " + "Each entry format is 'name:path[:ab]'. If ':ab' suffix is present, " + "A/B partition slots (name_a, name_b) are created. " + "If no name is provided (legacy), defaults to 'custom', 'custom_1', etc. " + "Example with A/B: " + "--custom_partition_path=\"oem:./oem.img:ab;config:./config.img\" " + "Example without names (legacy): " "--custom_partition_path=\"/path/to/custom.img;/path/to/other.img\""); DEFINE_string( diff --git a/base/cvd/cuttlefish/host/commands/assemble_cvd/disk/android_composite_disk_config.cc b/base/cvd/cuttlefish/host/commands/assemble_cvd/disk/android_composite_disk_config.cc index 2c31ea21708..d07eeebd840 100644 --- a/base/cvd/cuttlefish/host/commands/assemble_cvd/disk/android_composite_disk_config.cc +++ b/base/cvd/cuttlefish/host/commands/assemble_cvd/disk/android_composite_disk_config.cc @@ -26,6 +26,7 @@ #include #include +#include "absl/log/log.h" #include "absl/strings/str_cat.h" #include "absl/strings/str_split.h" @@ -214,10 +215,39 @@ Result> AndroidCompositeDiskConfig( std::vector custom_partition_paths = absl::StrSplit(custom_partition_path, ';'); for (int i = 0; i < custom_partition_paths.size(); i++) { - partitions.push_back(ImagePartition{ - .label = i > 0 ? "custom_" + std::to_string(i) : "custom", - .image_file_path = AbsolutePath(custom_partition_paths[i]), - }); + std::string label; + std::string image_path; + bool ab_enabled = false; + std::vector parts = + absl::StrSplit(custom_partition_paths[i], ':'); + if (parts.size() >= 2) { + label = std::string(parts[0]); + image_path = std::string(parts[1]); + if (parts.size() >= 3 && parts[2] == "ab") { + ab_enabled = true; + } + } else { + label = i > 0 ? "custom_" + std::to_string(i) : "custom"; + image_path = std::string(custom_partition_paths[i]); + } + if (ab_enabled) { + LOG(INFO) << "Adding custom partition: " << label + "_a"; + partitions.push_back(ImagePartition{ + .label = label + "_a", + .image_file_path = AbsolutePath(image_path), + }); + LOG(INFO) << "Adding custom partition: " << label + "_b"; + partitions.push_back(ImagePartition{ + .label = label + "_b", + .image_file_path = AbsolutePath(image_path), + }); + } else { + LOG(INFO) << "Adding custom partition: " << label; + partitions.push_back(ImagePartition{ + .label = label, + .image_file_path = AbsolutePath(image_path), + }); + } } } diff --git a/base/cvd/cuttlefish/host/commands/assemble_cvd/disk_builder.cpp b/base/cvd/cuttlefish/host/commands/assemble_cvd/disk_builder.cpp index 6fdb6390210..1637fb7c850 100644 --- a/base/cvd/cuttlefish/host/commands/assemble_cvd/disk_builder.cpp +++ b/base/cvd/cuttlefish/host/commands/assemble_cvd/disk_builder.cpp @@ -155,7 +155,7 @@ Result DiskBuilder::TextConfig() { CF_EXPECT(!partitions_.empty() ^ !entire_disk_.empty(), "Specify either partitions or a whole disk"); for (auto& partition : partitions_) { - disk_conf << partition.image_file_path << "\n"; + disk_conf << partition.label << ":" << partition.image_file_path << "\n"; } if (!entire_disk_.empty()) { disk_conf << entire_disk_; From a999770e1f4d39a1abefd41f69aa5bc1d7f22f29 Mon Sep 17 00:00:00 2001 From: "jongjin1.park" Date: Fri, 24 Apr 2026 09:56:26 +0900 Subject: [PATCH 2/2] assemble_cvd: Support named/A-B partitions in custom_partition_path flag 1. Adopt key=value syntax for custom_partition_path (assemble_cvd_flags.cpp) The --custom_partition_path flag accepts comma-separated key=value entries per partition, with semicolons separating multiple partitions. Supported keys: - name : partition label (defaults to "custom", "custom_1", ...) - path : image file path (required) - ab : "true" or "false" for A/B slot creation (defaults to false) Legacy format (plain path only, no '=' sign) is still accepted for backward compatibility. Multi-instance values are separated by '|' instead of ',' to avoid conflict with the comma delimiter inside key=value entries. Example: --custom_partition_path="name=oem1,path=./oem1.img,ab=true;name=oem2,path=./oem2.img" 2. Refactor custom partition parsing with validation (android_composite_disk_config.cc, disk_builder.cpp) ParseCustomPartitionSpec() parses each partition entry and returns Result. It validates key=value syntax, rejects unknown keys, checks ab values ("true"/"false"), and requires the 'path' key to be present. When ab is enabled, both _a and _b slot partitions are created from the same image file. TextConfig() now outputs "label:path" per line so that partition label changes also invalidate the cached composite disk and trigger a rebuild. Change-Id: Ic953c84ce2c07bea2a1f12111726b366a43e33c1 Signed-off-by: Jongjin Park --- .../assemble_cvd/assemble_cvd_flags.cpp | 13 ++- .../disk/android_composite_disk_config.cc | 94 ++++++++++++++----- .../disk_image_flags_vectorization.cc | 4 +- 3 files changed, 80 insertions(+), 31 deletions(-) diff --git a/base/cvd/cuttlefish/host/commands/assemble_cvd/assemble_cvd_flags.cpp b/base/cvd/cuttlefish/host/commands/assemble_cvd/assemble_cvd_flags.cpp index ecb5c7d6ec4..862cd29b0ce 100644 --- a/base/cvd/cuttlefish/host/commands/assemble_cvd/assemble_cvd_flags.cpp +++ b/base/cvd/cuttlefish/host/commands/assemble_cvd/assemble_cvd_flags.cpp @@ -499,13 +499,16 @@ DEFINE_string( custom_partition_path, CF_DEFAULTS_CUSTOM_PARTITION_PATH, "Location of custom image that will be passed as a custom partition " "to rootfs. Multiple entries can be passed, separated by semicolons. " - "Each entry format is 'name:path[:ab]'. If ':ab' suffix is present, " - "A/B partition slots (name_a, name_b) are created. " - "If no name is provided (legacy), defaults to 'custom', 'custom_1', etc. " + "Each entry uses key=value pairs separated by commas. " + "Supported keys: name (partition label), path (image file path), " + "ab (true/false for A/B slots). 'path' is required; 'name' defaults to " + "'custom', 'custom_1', etc.; 'ab' defaults to false. " "Example with A/B: " - "--custom_partition_path=\"oem:./oem.img:ab;config:./config.img\" " + "--custom_partition_path=\"name=oem1,path=./oem1.img,ab=true;" + "name=oem2,path=./oem2.img\" " "Example without names (legacy): " - "--custom_partition_path=\"/path/to/custom.img;/path/to/other.img\""); + "--custom_partition_path=\"/path/to/custom.img;/path/to/other.img\" " + "For multi-instance, separate per-instance values with '|'."); DEFINE_string( blank_sdcard_image_mb, CF_DEFAULTS_BLANK_SDCARD_IMAGE_MB, diff --git a/base/cvd/cuttlefish/host/commands/assemble_cvd/disk/android_composite_disk_config.cc b/base/cvd/cuttlefish/host/commands/assemble_cvd/disk/android_composite_disk_config.cc index d07eeebd840..f78f25a8ad0 100644 --- a/base/cvd/cuttlefish/host/commands/assemble_cvd/disk/android_composite_disk_config.cc +++ b/base/cvd/cuttlefish/host/commands/assemble_cvd/disk/android_composite_disk_config.cc @@ -73,6 +73,63 @@ std::optional HibernationImage( return FileExists(path) ? std::optional(image) : std::nullopt; } +struct CustomPartitionSpec { + std::string label; + std::string path; + bool ab_enabled; +}; + +Result ParseCustomPartitionSpec(std::string_view spec, + int index) { + std::string label; + std::string path; + bool ab_enabled = false; + + // Key=value format: "name=oem,path=./oem.img,ab=true" + if (spec.find('=') != std::string_view::npos) { + std::vector kvpairs = absl::StrSplit(spec, ','); + for (const auto& kvpair : kvpairs) { + std::vector kv = absl::StrSplit(kvpair, '='); + CF_EXPECTF(kv.size() == 2, + "Invalid key=value pair '{}' in custom partition spec '{}'", + kvpair, spec); + std::string_view key = kv[0]; + std::string_view value = kv[1]; + if (key == "name") { + label = std::string(value); + } else if (key == "path") { + path = std::string(value); + } else if (key == "ab") { + CF_EXPECTF(value == "true" || value == "false", + "Invalid value '{}' for key 'ab' in custom partition spec " + "'{}'. Expected 'true' or 'false'", + value, spec); + ab_enabled = (value == "true"); + } else { + return CF_ERRF("Unknown key '{}' in custom partition spec '{}'. " + "Valid keys are: name, path, ab", + key, spec); + } + } + CF_EXPECTF(!path.empty(), + "Missing required 'path' key in custom partition spec '{}'", + spec); + if (label.empty()) { + label = index > 0 ? "custom_" + std::to_string(index) : "custom"; + } + } else { + // Legacy format: plain path only + label = index > 0 ? "custom_" + std::to_string(index) : "custom"; + path = std::string(spec); + } + + return CustomPartitionSpec{ + .label = std::move(label), + .path = std::move(path), + .ab_enabled = ab_enabled, + }; +} + } // namespace Result> AndroidCompositeDiskConfig( @@ -215,37 +272,24 @@ Result> AndroidCompositeDiskConfig( std::vector custom_partition_paths = absl::StrSplit(custom_partition_path, ';'); for (int i = 0; i < custom_partition_paths.size(); i++) { - std::string label; - std::string image_path; - bool ab_enabled = false; - std::vector parts = - absl::StrSplit(custom_partition_paths[i], ':'); - if (parts.size() >= 2) { - label = std::string(parts[0]); - image_path = std::string(parts[1]); - if (parts.size() >= 3 && parts[2] == "ab") { - ab_enabled = true; - } - } else { - label = i > 0 ? "custom_" + std::to_string(i) : "custom"; - image_path = std::string(custom_partition_paths[i]); - } - if (ab_enabled) { - LOG(INFO) << "Adding custom partition: " << label + "_a"; + CustomPartitionSpec spec = + CF_EXPECT(ParseCustomPartitionSpec(custom_partition_paths[i], i)); + if (spec.ab_enabled) { + VLOG(1) << "Adding custom partition: " << spec.label + "_a"; partitions.push_back(ImagePartition{ - .label = label + "_a", - .image_file_path = AbsolutePath(image_path), + .label = spec.label + "_a", + .image_file_path = AbsolutePath(spec.path), }); - LOG(INFO) << "Adding custom partition: " << label + "_b"; + VLOG(1) << "Adding custom partition: " << spec.label + "_b"; partitions.push_back(ImagePartition{ - .label = label + "_b", - .image_file_path = AbsolutePath(image_path), + .label = spec.label + "_b", + .image_file_path = AbsolutePath(spec.path), }); } else { - LOG(INFO) << "Adding custom partition: " << label; + VLOG(1) << "Adding custom partition: " << spec.label; partitions.push_back(ImagePartition{ - .label = label, - .image_file_path = AbsolutePath(image_path), + .label = spec.label, + .image_file_path = AbsolutePath(spec.path), }); } } diff --git a/base/cvd/cuttlefish/host/commands/assemble_cvd/disk_image_flags_vectorization.cc b/base/cvd/cuttlefish/host/commands/assemble_cvd/disk_image_flags_vectorization.cc index 8bc5ee5c2ea..2981373cc19 100644 --- a/base/cvd/cuttlefish/host/commands/assemble_cvd/disk_image_flags_vectorization.cc +++ b/base/cvd/cuttlefish/host/commands/assemble_cvd/disk_image_flags_vectorization.cc @@ -84,8 +84,10 @@ Result DiskImageFlagsVectorization( std::vector fuchsia_root_image = absl::StrSplit(FLAGS_fuchsia_root_image, ','); + // custom_partition_path uses ',' inside key=value entries, so split + // on '|' for multi-instance separation instead of ','. std::vector custom_partition_path = - absl::StrSplit(FLAGS_custom_partition_path, ','); + absl::StrSplit(FLAGS_custom_partition_path, '|'); std::vector blank_sdcard_image_mb = absl::StrSplit(FLAGS_blank_sdcard_image_mb, ',');