Installer::ProceedState RomInstaller::on_checked_device() { // /sbin is not going to be populated with anything useful in a normal boot // image. We can almost guarantee that a recovery image is going to be // installed though, so we'll open the recovery partition with libmbp and // extract its /sbin with libarchive into the chroot's /sbin. std::string block_dev(_recovery_block_dev); mbp::BootImage bi; mbp::CpioFile innerCpio; const unsigned char *ramdisk_data; std::size_t ramdisk_size; bool using_boot = false; // Check if the device has a combined boot/recovery partition. If the // FOTAKernel partition is listed, it will be used instead of the combined // ramdisk from the boot image bool combined = mb_device_flags(_device) & FLAG_HAS_COMBINED_BOOT_AND_RECOVERY; if (combined && block_dev.empty()) { block_dev = _boot_block_dev; using_boot = true; } if (block_dev.empty()) { display_msg("Could not determine the recovery block device"); return ProceedState::Fail; } if (!bi.loadFile(block_dev)) { display_msg("Failed to load recovery partition image"); return ProceedState::Fail; } // Load ramdisk bi.ramdiskImageC(&ramdisk_data, &ramdisk_size); if (using_boot) { if (!innerCpio.load(ramdisk_data, ramdisk_size)) { display_msg("Failed to load ramdisk from combined boot image"); return ProceedState::Fail; } if (!innerCpio.contentsC("sbin/ramdisk-recovery.cpio", &ramdisk_data, &ramdisk_size)) { display_msg("Could not find recovery ramdisk in combined boot image"); return ProceedState::Fail; } } autoclose::archive in(archive_read_new(), archive_read_free); autoclose::archive out(archive_write_disk_new(), archive_write_free); if (!in || !out) { LOGE("Out of memory"); return ProceedState::Fail; } archive_entry *entry; // Set up input archive_read_support_filter_gzip(in.get()); archive_read_support_filter_lzop(in.get()); archive_read_support_filter_lz4(in.get()); archive_read_support_filter_lzma(in.get()); archive_read_support_filter_xz(in.get()); archive_read_support_format_cpio(in.get()); int ret = archive_read_open_memory(in.get(), const_cast<unsigned char *>(ramdisk_data), ramdisk_size); if (ret != ARCHIVE_OK) { LOGW("Failed to open recovery ramdisk: %s", archive_error_string(in.get())); return ProceedState::Fail; } // Set up output archive_write_disk_set_options(out.get(), ARCHIVE_EXTRACT_ACL | ARCHIVE_EXTRACT_FFLAGS | ARCHIVE_EXTRACT_PERM | ARCHIVE_EXTRACT_SECURE_NODOTDOT | ARCHIVE_EXTRACT_SECURE_SYMLINKS | ARCHIVE_EXTRACT_TIME | ARCHIVE_EXTRACT_UNLINK | ARCHIVE_EXTRACT_XATTR); while ((ret = archive_read_next_header(in.get(), &entry)) == ARCHIVE_OK) { std::string path = archive_entry_pathname(entry); if (path == "default.prop") { path = "default.recovery.prop"; } else if (!util::starts_with(path, "sbin/")) { continue; } LOGE("Copying from recovery: %s", path.c_str()); archive_entry_set_pathname(entry, in_chroot(path).c_str()); if (util::libarchive_copy_header_and_data( in.get(), out.get(), entry) != ARCHIVE_OK) { return ProceedState::Fail; } archive_entry_set_pathname(entry, path.c_str()); } if (ret != ARCHIVE_EOF) { LOGE("Archive extraction ended without reaching EOF: %s", archive_error_string(in.get())); return ProceedState::Fail; } // Create fake /etc/fstab file to please installers that read the file std::string etc_fstab(in_chroot("/etc/fstab")); if (access(etc_fstab.c_str(), R_OK) < 0 && errno == ENOENT) { autoclose::file fp(autoclose::fopen(etc_fstab.c_str(), "w")); if (fp) { auto system_devs = mb_device_system_block_devs(_device); auto cache_devs = mb_device_cache_block_devs(_device); auto data_devs = mb_device_data_block_devs(_device); // Set block device if it's provided and non-empty const char *system_dev = system_devs && system_devs[0] && system_devs[0][0] ? system_devs[0] : "dummy"; const char *cache_dev = cache_devs && cache_devs[0] && cache_devs[0][0] ? cache_devs[0] : "dummy"; const char *data_dev = data_devs && data_devs[0] && data_devs[0][0] ? data_devs[0] : "dummy"; fprintf(fp.get(), "%s /system ext4 rw 0 0\n", system_dev); fprintf(fp.get(), "%s /cache ext4 rw 0 0\n", cache_dev); fprintf(fp.get(), "%s /data ext4 rw 0 0\n", data_dev); } } // Load recovery properties util::file_get_all_properties( in_chroot("/default.recovery.prop"), &_recovery_props); return ProceedState::Continue; }
TEST(JsonTest, LoadCompleteDefinition) { ScopedDevice sd(sample_complete); ASSERT_NE(sd.device, nullptr); ASSERT_STREQ(mb_device_id(sd.device), "test"); const char *codenames[] = { "test1", "test2", "test3", "test4", nullptr }; ASSERT_TRUE(string_array_eq(mb_device_codenames(sd.device), codenames)); ASSERT_STREQ(mb_device_name(sd.device), "Test Device"); ASSERT_STREQ(mb_device_architecture(sd.device), "arm64-v8a"); uint64_t device_flags = FLAG_HAS_COMBINED_BOOT_AND_RECOVERY; ASSERT_EQ(mb_device_flags(sd.device), device_flags); const char *base_dirs[] = { "/dev/block/bootdevice/by-name", nullptr }; ASSERT_TRUE(string_array_eq(mb_device_block_dev_base_dirs(sd.device), base_dirs)); const char *system_devs[] = { "/dev/block/bootdevice/by-name/system", "/dev/block/sda1", nullptr }; ASSERT_TRUE(string_array_eq(mb_device_system_block_devs(sd.device), system_devs)); const char *cache_devs[] = { "/dev/block/bootdevice/by-name/cache", "/dev/block/sda2", nullptr }; ASSERT_TRUE(string_array_eq(mb_device_cache_block_devs(sd.device), cache_devs)); const char *data_devs[] = { "/dev/block/bootdevice/by-name/userdata", "/dev/block/sda3", nullptr }; ASSERT_TRUE(string_array_eq(mb_device_data_block_devs(sd.device), data_devs)); const char *boot_devs[] = { "/dev/block/bootdevice/by-name/boot", "/dev/block/sda4", nullptr }; ASSERT_TRUE(string_array_eq(mb_device_boot_block_devs(sd.device), boot_devs)); const char *recovery_devs[] = { "/dev/block/bootdevice/by-name/recovery", "/dev/block/sda5", nullptr }; ASSERT_TRUE(string_array_eq(mb_device_recovery_block_devs(sd.device), recovery_devs)); const char *extra_devs[] = { "/dev/block/bootdevice/by-name/modem", "/dev/block/sda6", nullptr }; ASSERT_TRUE(string_array_eq(mb_device_extra_block_devs(sd.device), extra_devs)); /* Boot UI */ ASSERT_EQ(mb_device_tw_supported(sd.device), true); uint64_t flags = FLAG_TW_TOUCHSCREEN_SWAP_XY | FLAG_TW_TOUCHSCREEN_FLIP_X | FLAG_TW_TOUCHSCREEN_FLIP_Y | FLAG_TW_GRAPHICS_FORCE_USE_LINELENGTH | FLAG_TW_SCREEN_BLANK_ON_BOOT | FLAG_TW_BOARD_HAS_FLIPPED_SCREEN | FLAG_TW_IGNORE_MAJOR_AXIS_0 | FLAG_TW_IGNORE_MT_POSITION_0 | FLAG_TW_IGNORE_ABS_MT_TRACKING_ID | FLAG_TW_NEW_ION_HEAP | FLAG_TW_NO_SCREEN_BLANK | FLAG_TW_NO_SCREEN_TIMEOUT | FLAG_TW_ROUND_SCREEN | FLAG_TW_NO_CPU_TEMP | FLAG_TW_QCOM_RTC_FIX | FLAG_TW_HAS_DOWNLOAD_MODE | FLAG_TW_PREFER_LCD_BACKLIGHT; ASSERT_EQ(mb_device_tw_flags(sd.device), flags); ASSERT_EQ(mb_device_tw_pixel_format(sd.device), TW_PIXEL_FORMAT_RGBA_8888); ASSERT_EQ(mb_device_tw_force_pixel_format(sd.device), TW_FORCE_PIXEL_FORMAT_RGB_565); ASSERT_EQ(mb_device_tw_overscan_percent(sd.device), 10); ASSERT_EQ(mb_device_tw_default_x_offset(sd.device), 20); ASSERT_EQ(mb_device_tw_default_y_offset(sd.device), 30); ASSERT_STREQ(mb_device_tw_brightness_path(sd.device), "/sys/class/backlight"); ASSERT_STREQ(mb_device_tw_secondary_brightness_path(sd.device), "/sys/class/lcd-backlight"); ASSERT_EQ(mb_device_tw_max_brightness(sd.device), 255); ASSERT_EQ(mb_device_tw_default_brightness(sd.device), 100); ASSERT_STREQ(mb_device_tw_battery_path(sd.device), "/sys/class/battery"); ASSERT_STREQ(mb_device_tw_cpu_temp_path(sd.device), "/sys/class/cputemp"); ASSERT_STREQ(mb_device_tw_input_blacklist(sd.device), "foo"); ASSERT_STREQ(mb_device_tw_input_whitelist(sd.device), "bar"); const char *graphics_backends[] = { "overlay_msm_old", "fbdev", nullptr }; ASSERT_TRUE(string_array_eq(mb_device_tw_graphics_backends(sd.device), graphics_backends)); ASSERT_STREQ(mb_device_tw_theme(sd.device), "portrait_hdpi"); }