1 // Copyright (C) 2019 The Android Open Source Project 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 #include <getopt.h> 16 #include <sysexits.h> 17 #include <unistd.h> 18 19 #include <iostream> 20 #include <optional> 21 22 #include <android-base/file.h> 23 #include <android-base/logging.h> 24 #include <android-base/unique_fd.h> 25 #include <liblp/builder.h> 26 #include <sparse/sparse.h> 27 28 using android::base::borrowed_fd; 29 using android::base::unique_fd; 30 using android::fs_mgr::LpMetadata; 31 using android::fs_mgr::MetadataBuilder; 32 using android::fs_mgr::ReadMetadata; 33 using android::fs_mgr::UpdatePartitionTable; 34 using SparsePtr = std::unique_ptr<sparse_file, decltype(&sparse_file_destroy)>; 35 36 std::optional<TemporaryDir> gTempDir; 37 38 static int usage(const char* program) { 39 std::cerr << program << " - command-line tool for adding partitions to a super.img\n"; 40 std::cerr << "\n"; 41 std::cerr << "Usage:\n"; 42 std::cerr << " " << program << " [options] SUPER PARTNAME PARTGROUP [IMAGE]\n"; 43 std::cerr << "\n"; 44 std::cerr << " SUPER Path to the super image. It can be sparsed or\n" 45 << " unsparsed. If sparsed, it will be unsparsed\n" 46 << " temporarily and re-sparsed over the original\n" 47 << " file. This will consume extra space during the\n" 48 << " execution of " << program << ".\n"; 49 std::cerr << " PARTNAME Name of the partition to add.\n"; 50 std::cerr << " PARTGROUP Name of the partition group to use. If the\n" 51 << " partition can be updated over OTA, the group\n" 52 << " should match its updatable group.\n"; 53 std::cerr << " IMAGE If specified, the contents of the given image\n" 54 << " will be added to the super image. If the image\n" 55 << " is sparsed, it will be temporarily unsparsed.\n" 56 << " If no image is specified, the partition will\n" 57 << " be zero-sized.\n"; 58 std::cerr << "\n"; 59 std::cerr << "Extra options:\n"; 60 std::cerr << " --readonly The partition should be mapped read-only.\n"; 61 std::cerr << "\n"; 62 return EX_USAGE; 63 } 64 65 enum class OptionCode : int { 66 kReadonly = 1, 67 68 // Special options. 69 kHelp = (int)'h', 70 }; 71 72 static std::string GetTemporaryDir() { 73 if (!gTempDir) { 74 gTempDir.emplace(); 75 int saved_errno = errno; 76 if (access(gTempDir->path, F_OK) != 0) { 77 std::cerr << "Could not create temporary dir: " << gTempDir->path << ": " 78 << strerror(saved_errno) << std::endl; 79 abort(); 80 } 81 } 82 return gTempDir->path; 83 } 84 85 class LocalSuperOpener final : public android::fs_mgr::PartitionOpener { 86 public: 87 LocalSuperOpener(const std::string& path, borrowed_fd fd) 88 : local_super_(path), local_super_fd_(fd) {} 89 90 unique_fd Open(const std::string& partition_name, int flags) const override { 91 if (partition_name == local_super_) { 92 return unique_fd{dup(local_super_fd_.get())}; 93 } 94 return PartitionOpener::Open(partition_name, flags); 95 } 96 97 private: 98 std::string local_super_; 99 borrowed_fd local_super_fd_; 100 }; 101 102 class SuperHelper final { 103 public: 104 explicit SuperHelper(const std::string& super_path) : super_path_(super_path) {} 105 106 bool Open(); 107 bool AddPartition(const std::string& partition_name, const std::string& group_name, 108 uint32_t attributes, const std::string& image_path); 109 bool Finalize(); 110 111 private: 112 bool OpenSuperFile(); 113 bool UpdateSuper(); 114 bool WritePartition(borrowed_fd fd, uint64_t file_size, const std::string& partition_name); 115 bool WriteExtent(borrowed_fd fd, uint64_t file_size, const LpMetadataExtent& extent); 116 117 // Returns true if |fd| does not contain a sparsed file. If |fd| does 118 // contain a sparsed file, |temp_file| will contain the unsparsed output. 119 // If |fd| cannot be read or failed to unsparse, false is returned. 120 bool MaybeUnsparse(const std::string& file, borrowed_fd fd, 121 std::optional<TemporaryFile>* temp_file, uint32_t* block_size = nullptr); 122 123 std::string super_path_; 124 std::string abs_super_path_; 125 bool was_empty_ = false; 126 // fd for the super file, sparsed or temporarily unsparsed. 127 int super_fd_; 128 // fd for the super file if unsparsed. 129 unique_fd output_fd_; 130 // If the super file is sparse, this holds the temp unsparsed file. 131 std::optional<TemporaryFile> temp_super_; 132 uint32_t sparse_block_size_ = 0; 133 std::unique_ptr<LpMetadata> metadata_; 134 std::unique_ptr<MetadataBuilder> builder_; 135 }; 136 137 bool SuperHelper::Open() { 138 if (!OpenSuperFile()) { 139 return false; 140 } 141 142 was_empty_ = android::fs_mgr::IsEmptySuperImage(abs_super_path_); 143 if (was_empty_) { 144 metadata_ = android::fs_mgr::ReadFromImageFile(abs_super_path_); 145 } else { 146 metadata_ = android::fs_mgr::ReadMetadata(abs_super_path_, 0); 147 } 148 if (!metadata_) { 149 std::cerr << "Could not read super partition metadata for " << super_path_ << "\n"; 150 return false; 151 } 152 builder_ = MetadataBuilder::New(*metadata_.get()); 153 if (!builder_) { 154 std::cerr << "Could not create MetadataBuilder for " << super_path_ << "\n"; 155 return false; 156 } 157 return true; 158 } 159 160 bool SuperHelper::AddPartition(const std::string& partition_name, const std::string& group_name, 161 uint32_t attributes, const std::string& image_path) { 162 if (!image_path.empty() && was_empty_) { 163 std::cerr << "Cannot add a partition image to an empty super file.\n"; 164 return false; 165 } 166 167 auto partition = builder_->AddPartition(partition_name, group_name, attributes); 168 if (!partition) { 169 std::cerr << "Could not add partition: " << partition_name << "\n"; 170 return false; 171 } 172 173 // Open the source image and get its file size so we can resize the 174 // partition. 175 int source_fd = -1; 176 uint64_t file_size; 177 unique_fd raw_image_fd; 178 std::optional<TemporaryFile> temp_image; 179 if (!image_path.empty()) { 180 raw_image_fd.reset(open(image_path.c_str(), O_RDONLY | O_CLOEXEC)); 181 if (raw_image_fd < 0) { 182 std::cerr << "open failed: " << image_path << ": " << strerror(errno) << "\n"; 183 return false; 184 } 185 if (!MaybeUnsparse(image_path, raw_image_fd, &temp_image)) { 186 return false; 187 } 188 source_fd = temp_image ? temp_image->fd : raw_image_fd.get(); 189 190 auto size = lseek(source_fd, 0, SEEK_END); 191 if (size < 0 || lseek(source_fd, 0, SEEK_SET) < 0) { 192 std::cerr << "lseek failed: " << image_path << ": " << strerror(errno) << "\n"; 193 return false; 194 } 195 if (!builder_->ResizePartition(partition, size)) { 196 std::cerr << "Failed to set partition " << partition_name << " size to " << size 197 << "bytes.\n"; 198 return false; 199 } 200 file_size = (uint64_t)size; 201 } 202 203 // Write the new metadata out. We do this by re-using the on-device flashing 204 // logic, and using the local file instead of a block device. 205 if (!UpdateSuper()) { 206 return false; 207 } 208 209 // If no partition contents were specified, early return. Otherwise, we 210 // require a full super image to continue writing. 211 if (source_fd >= 0 && !WritePartition(source_fd, file_size, partition_name)) { 212 return false; 213 } 214 return true; 215 } 216 217 bool SuperHelper::OpenSuperFile() { 218 auto actual_path = super_path_; 219 220 output_fd_.reset(open(actual_path.c_str(), O_RDWR | O_CLOEXEC)); 221 if (output_fd_ < 0) { 222 std::cerr << "open failed: " << actual_path << ": " << strerror(errno) << "\n"; 223 return false; 224 } 225 super_fd_ = output_fd_.get(); 226 227 if (!MaybeUnsparse(super_path_, super_fd_, &temp_super_, &sparse_block_size_)) { 228 return false; 229 } 230 if (temp_super_) { 231 actual_path = temp_super_->path; 232 super_fd_ = temp_super_->fd; 233 } 234 235 // PartitionOpener will decorate relative paths with /dev/block/by-name 236 // so get an absolute path here. 237 if (!android::base::Realpath(actual_path, &abs_super_path_)) { 238 std::cerr << "realpath failed: " << actual_path << ": " << strerror(errno) << "\n"; 239 return false; 240 } 241 return true; 242 } 243 244 bool SuperHelper::MaybeUnsparse(const std::string& file, borrowed_fd fd, 245 std::optional<TemporaryFile>* temp_file, 246 uint32_t* block_size) { 247 SparsePtr sf(sparse_file_import(fd.get(), false, false), sparse_file_destroy); 248 if (!sf) { 249 return true; 250 } 251 252 temp_file->emplace(GetTemporaryDir()); 253 if ((*temp_file)->fd < 0) { 254 std::cerr << "mkstemp failed: " << strerror(errno) << "\n"; 255 return false; 256 } 257 258 std::cout << "Unsparsing " << file << "... " << std::endl; 259 260 if (sparse_file_write(sf.get(), (*temp_file)->fd, false, false, false) != 0) { 261 std::cerr << "Could not write unsparsed file.\n"; 262 return false; 263 } 264 if (block_size) { 265 *block_size = sparse_file_block_size(sf.get()); 266 } 267 return true; 268 } 269 270 bool SuperHelper::UpdateSuper() { 271 metadata_ = builder_->Export(); 272 if (!metadata_) { 273 std::cerr << "Failed to export new metadata.\n"; 274 return false; 275 } 276 277 // Empty images get written at the very end. 278 if (was_empty_) { 279 return true; 280 } 281 282 // Note: A/B devices have an extra metadata slot that is unused, so we cap 283 // the writes to the first two slots. 284 LocalSuperOpener opener(abs_super_path_, super_fd_); 285 uint32_t slots = std::min(metadata_->geometry.metadata_slot_count, (uint32_t)2); 286 for (uint32_t i = 0; i < slots; i++) { 287 if (!UpdatePartitionTable(opener, abs_super_path_, *metadata_.get(), i)) { 288 std::cerr << "Could not write new super partition metadata.\n"; 289 return false; 290 } 291 } 292 return true; 293 } 294 295 bool SuperHelper::WritePartition(borrowed_fd fd, uint64_t file_size, 296 const std::string& partition_name) { 297 auto partition = android::fs_mgr::FindPartition(*metadata_.get(), partition_name); 298 if (!partition) { 299 std::cerr << "Could not find partition in metadata: " << partition_name << "\n"; 300 return false; 301 } 302 303 std::cout << "Writing data for partition " << partition_name << "..." << std::endl; 304 for (uint32_t i = 0; i < partition->num_extents; i++) { 305 auto extent_index = partition->first_extent_index + i; 306 const auto& extent = metadata_->extents[extent_index]; 307 if (!WriteExtent(fd, file_size, extent)) { 308 return false; 309 } 310 } 311 312 // Assert that the full file was written. 313 [[maybe_unused]] auto pos = lseek(fd.get(), 0, SEEK_CUR); 314 CHECK(pos >= 0 && (uint64_t)pos == file_size); 315 return true; 316 } 317 318 bool SuperHelper::WriteExtent(borrowed_fd fd, uint64_t file_size, const LpMetadataExtent& extent) { 319 // Must be a linear extent, and there must only be one block device. 320 CHECK(extent.target_type == LP_TARGET_TYPE_LINEAR); 321 CHECK(extent.target_source == 0); 322 323 auto pos = lseek(fd.get(), 0, SEEK_CUR); 324 if (pos < 0) { 325 std::cerr << "lseek failed: " << strerror(errno) << "\n"; 326 return false; 327 } 328 329 // Clamp the number of bytes to either remaining data in the file, or the 330 // size of this extent. 331 CHECK((uint64_t)pos <= file_size); 332 uint64_t bytes_remaining = 333 std::min(file_size - (uint64_t)pos, extent.num_sectors * LP_SECTOR_SIZE); 334 335 // Reposition to the appropriate offset in super. 336 if (lseek(super_fd_, extent.target_data * LP_SECTOR_SIZE, SEEK_SET) < 0) { 337 std::cerr << "lseek failed: " << strerror(errno) << "\n"; 338 return false; 339 } 340 341 uint8_t buffer[4096]; 342 while (bytes_remaining > 0) { 343 uint64_t bytes = std::min((uint64_t)sizeof(buffer), bytes_remaining); 344 if (!android::base::ReadFully(fd.get(), buffer, bytes)) { 345 std::cerr << "read failed: " << strerror(errno) << "\n"; 346 return false; 347 } 348 if (!android::base::WriteFully(super_fd_, buffer, bytes)) { 349 std::cerr << "write failed: " << strerror(errno) << "\n"; 350 return false; 351 } 352 bytes_remaining -= bytes; 353 } 354 return true; 355 } 356 357 static bool Truncate(borrowed_fd fd) { 358 if (ftruncate(fd.get(), 0) < 0) { 359 std::cerr << "truncate failed: " << strerror(errno) << "\n"; 360 return false; 361 } 362 if (lseek(fd.get(), 0, SEEK_SET) < 0) { 363 std::cerr << "lseek failed: " << strerror(errno) << "\n"; 364 return false; 365 } 366 return true; 367 } 368 369 bool SuperHelper::Finalize() { 370 if (was_empty_) { 371 if (!Truncate(super_fd_)) { 372 return false; 373 } 374 if (!android::fs_mgr::WriteToImageFile(super_fd_, *metadata_.get())) { 375 std::cerr << "Could not write image file.\n"; 376 return false; 377 } 378 } 379 380 // If the super image wasn't original sparsed, we don't have to do anything 381 // else. 382 if (!temp_super_) { 383 return true; 384 } 385 386 // Otherwise, we have to sparse the temporary file. Find its length. 387 auto len = lseek(super_fd_, 0, SEEK_END); 388 if (len < 0 || lseek(super_fd_, 0, SEEK_SET < 0)) { 389 std::cerr << "lseek failed: " << strerror(errno) << "\n"; 390 return false; 391 } 392 393 SparsePtr sf(sparse_file_new(sparse_block_size_, len), sparse_file_destroy); 394 if (!sf) { 395 std::cerr << "Could not allocate sparse file.\n"; 396 return false; 397 } 398 sparse_file_verbose(sf.get()); 399 400 std::cout << "Writing sparse super image... " << std::endl; 401 if (sparse_file_read(sf.get(), super_fd_, false, false) != 0) { 402 std::cerr << "Could not import super partition for sparsing.\n"; 403 return false; 404 } 405 if (!Truncate(output_fd_)) { 406 return false; 407 } 408 if (sparse_file_write(sf.get(), output_fd_, false, true, false)) { 409 return false; 410 } 411 return true; 412 } 413 414 static void ErrorLogger(android::base::LogId, android::base::LogSeverity severity, const char*, 415 const char*, unsigned int, const char* msg) { 416 if (severity < android::base::WARNING) { 417 return; 418 } 419 std::cerr << msg << std::endl; 420 } 421 422 int main(int argc, char* argv[]) { 423 struct option options[] = { 424 {"readonly", no_argument, nullptr, (int)OptionCode::kReadonly}, 425 {nullptr, 0, nullptr, 0}, 426 }; 427 428 bool readonly = false; 429 430 int rv, index; 431 while ((rv = getopt_long(argc, argv, "h", options, &index)) != -1) { 432 switch ((OptionCode)rv) { 433 case OptionCode::kHelp: 434 usage(argv[0]); 435 return EX_OK; 436 case OptionCode::kReadonly: 437 readonly = true; 438 break; 439 default: 440 return usage(argv[0]); 441 } 442 } 443 444 if (optind + 3 > argc) { 445 std::cerr << "Missing required arguments.\n\n"; 446 return usage(argv[0]); 447 } 448 449 std::string super_path = argv[optind++]; 450 std::string partition_name = argv[optind++]; 451 std::string group_name = argv[optind++]; 452 std::string image_path; 453 454 if (optind < argc) { 455 image_path = argv[optind++]; 456 } 457 if (optind != argc) { 458 std::cerr << "Unexpected arguments.\n\n"; 459 return usage(argv[0]); 460 } 461 462 // Suppress log spam from liblp. 463 android::base::SetLogger(ErrorLogger); 464 465 SuperHelper super(super_path); 466 if (!super.Open()) { 467 return EX_SOFTWARE; 468 } 469 470 uint32_t attributes = LP_PARTITION_ATTR_NONE; 471 if (readonly) { 472 attributes |= LP_PARTITION_ATTR_READONLY; 473 } 474 if (!super.AddPartition(partition_name, group_name, attributes, image_path)) { 475 return EX_SOFTWARE; 476 } 477 if (!super.Finalize()) { 478 return EX_SOFTWARE; 479 } 480 481 std::cout << "Done.\n"; 482 return EX_OK; 483 } 484