return AVB_IO_RESULT_OK;
}
+AvbABFlowResult avb_ab_flow_fast(AvbABOps* ab_ops,
+ const char* const* requested_partitions,
+ bool allow_verification_error,
+ AvbSlotVerifyData** out_data) {
+ AvbOps* ops = ab_ops->ops;
+ AvbSlotVerifyData* slot_data[2] = {NULL, NULL};
+ AvbSlotVerifyData* data = NULL;
+ AvbABFlowResult ret;
+ AvbABData ab_data, ab_data_orig;
+ size_t slot_index_to_boot, n = 0;
+ AvbIOResult io_ret;
+ bool saw_and_allowed_verification_error = false;
+ size_t target_slot;
+ AvbSlotVerifyResult verify_result;
+ bool set_slot_unbootable = false;
+
+ io_ret = load_metadata(ab_ops, &ab_data, &ab_data_orig);
+ if (io_ret == AVB_IO_RESULT_ERROR_OOM) {
+ ret = AVB_AB_FLOW_RESULT_ERROR_OOM;
+ goto out;
+ } else if (io_ret != AVB_IO_RESULT_OK) {
+ ret = AVB_AB_FLOW_RESULT_ERROR_IO;
+ goto out;
+ }
+
+ slot_index_to_boot = 2; // Means not 0 or 1
+ target_slot = (ab_data.slots[1].priority > ab_data.slots[0].priority? 1 : 0);
+
+ for (n = 0; n < 2; n++) {
+ if (!slot_is_bootable(&ab_data.slots[target_slot])) {
+ target_slot = (target_slot == 1 ? 0 : 1);
+ continue;
+ }
+
+ verify_result = avb_slot_verify(ops, requested_partitions, slot_suffixes[target_slot],
+ allow_verification_error,
+ &slot_data[target_slot]);
+ switch (verify_result) {
+ case AVB_SLOT_VERIFY_RESULT_ERROR_OOM:
+ ret = AVB_AB_FLOW_RESULT_ERROR_OOM;
+ goto out;
+
+ case AVB_SLOT_VERIFY_RESULT_ERROR_IO:
+ ret = AVB_AB_FLOW_RESULT_ERROR_IO;
+ goto out;
+
+ case AVB_SLOT_VERIFY_RESULT_OK:
+ slot_index_to_boot = target_slot;
+ n = 2;
+ break;
+
+ case AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA:
+ case AVB_SLOT_VERIFY_RESULT_ERROR_UNSUPPORTED_VERSION:
+ /* Even with |allow_verification_error| these mean game over. */
+ set_slot_unbootable = true;
+ break;
+
+ /* explicit fallthrough. */
+ case AVB_SLOT_VERIFY_RESULT_ERROR_VERIFICATION:
+ case AVB_SLOT_VERIFY_RESULT_ERROR_ROLLBACK_INDEX:
+ case AVB_SLOT_VERIFY_RESULT_ERROR_PUBLIC_KEY_REJECTED:
+ if (allow_verification_error) {
+ /* Do nothing since we allow this. */
+ avb_debugv("Allowing slot ",
+ slot_suffixes[target_slot],
+ " which verified "
+ "with result ",
+ avb_slot_verify_result_to_string(verify_result),
+ " because |allow_verification_error| is true.\n",
+ NULL);
+ saw_and_allowed_verification_error = true;
+ slot_index_to_boot = target_slot;
+ n = 2;
+ } else {
+ set_slot_unbootable = true;
+ }
+ break;
+ default:
+ break;
+ }
+ if (set_slot_unbootable) {
+ avb_errorv("Error verifying slot ",
+ slot_suffixes[target_slot],
+ " with result ",
+ avb_slot_verify_result_to_string(verify_result),
+ " - setting unbootable.\n",
+ NULL);
+ slot_set_unbootable(&ab_data.slots[target_slot]);
+ set_slot_unbootable = false;
+ }
+ /* switch to another slot */
+ target_slot = (target_slot == 1 ? 0 : 1);
+ }
+
+ if (slot_index_to_boot == 2) {
+ /* No bootable slots! */
+ avb_error("No bootable slots found.\n");
+ ret = AVB_AB_FLOW_RESULT_ERROR_NO_BOOTABLE_SLOTS;
+ goto out;
+ }
+
+ /* Update stored rollback index such that the stored rollback index
+ * is the largest value supporting all currently bootable slots. Do
+ * this for every rollback index location.
+ */
+ for (n = 0; n < AVB_MAX_NUMBER_OF_ROLLBACK_INDEX_LOCATIONS; n++) {
+ uint64_t rollback_index_value = 0;
+
+ if (slot_data[0] != NULL && slot_data[1] != NULL) {
+ uint64_t a_rollback_index = slot_data[0]->rollback_indexes[n];
+ uint64_t b_rollback_index = slot_data[1]->rollback_indexes[n];
+ rollback_index_value =
+ (a_rollback_index < b_rollback_index ? a_rollback_index
+ : b_rollback_index);
+ } else if (slot_data[0] != NULL) {
+ rollback_index_value = slot_data[0]->rollback_indexes[n];
+ } else if (slot_data[1] != NULL) {
+ rollback_index_value = slot_data[1]->rollback_indexes[n];
+ }
+
+ if (rollback_index_value != 0) {
+ uint64_t current_rollback_index_value;
+ io_ret = ops->read_rollback_index(ops, n, ¤t_rollback_index_value);
+ if (io_ret == AVB_IO_RESULT_ERROR_OOM) {
+ ret = AVB_AB_FLOW_RESULT_ERROR_OOM;
+ goto out;
+ } else if (io_ret != AVB_IO_RESULT_OK) {
+ avb_error("Error getting rollback index for slot.\n");
+ ret = AVB_AB_FLOW_RESULT_ERROR_IO;
+ goto out;
+ }
+ if (current_rollback_index_value != rollback_index_value) {
+ io_ret = ops->write_rollback_index(ops, n, rollback_index_value);
+ if (io_ret == AVB_IO_RESULT_ERROR_OOM) {
+ ret = AVB_AB_FLOW_RESULT_ERROR_OOM;
+ goto out;
+ } else if (io_ret != AVB_IO_RESULT_OK) {
+ avb_error("Error setting stored rollback index.\n");
+ ret = AVB_AB_FLOW_RESULT_ERROR_IO;
+ goto out;
+ }
+ }
+ }
+ }
+
+ /* Finally, select this slot. */
+ avb_assert(slot_data[slot_index_to_boot] != NULL);
+ data = slot_data[slot_index_to_boot];
+ slot_data[slot_index_to_boot] = NULL;
+ if (saw_and_allowed_verification_error) {
+ avb_assert(allow_verification_error);
+ ret = AVB_AB_FLOW_RESULT_OK_WITH_VERIFICATION_ERROR;
+ } else {
+ ret = AVB_AB_FLOW_RESULT_OK;
+ }
+
+ /* ... and decrement tries remaining, if applicable. */
+ if (!ab_data.slots[slot_index_to_boot].successful_boot &&
+ ab_data.slots[slot_index_to_boot].tries_remaining > 0) {
+ ab_data.slots[slot_index_to_boot].tries_remaining -= 1;
+ }
+
+out:
+ io_ret = save_metadata_if_changed(ab_ops, &ab_data, &ab_data_orig);
+ if (io_ret != AVB_IO_RESULT_OK) {
+ if (io_ret == AVB_IO_RESULT_ERROR_OOM) {
+ ret = AVB_AB_FLOW_RESULT_ERROR_OOM;
+ } else {
+ ret = AVB_AB_FLOW_RESULT_ERROR_IO;
+ }
+ if (data != NULL) {
+ avb_slot_verify_data_free(data);
+ data = NULL;
+ }
+ }
+
+ for (n = 0; n < 2; n++) {
+ if (slot_data[n] != NULL) {
+ avb_slot_verify_data_free(slot_data[n]);
+ }
+ }
+
+ if (out_data != NULL) {
+ *out_data = data;
+ } else {
+ if (data != NULL) {
+ avb_slot_verify_data_free(data);
+ }
+ }
+
+ return ret;
+}
+
AvbABFlowResult avb_ab_flow(AvbABOps* ab_ops,
const char* const* requested_partitions,
bool allow_verification_error,