avbctl: Require --force option if in LOCKED mode.

Bail and print a nice little warning if the user tries to rewrite
vbmeta if the device is locked. This is to help protect the user from
potentially bricking his device.

 device:/ # avbctl disable-verity
 Manipulating vbmeta on a LOCKED device will likely cause the
 device to fail booting with little chance of recovery.

 If you really want to do this, use the --force option.

 ONLY DO THIS IF YOU KNOW WHAT YOU ARE DOING.

 77|device:/ # avbctl --force disable-verity
 Successfully disabled verity on slot with suffix _a. Reboot the device for changes to take effect.
 device:/ #

Bug: 64482211
Test: Manually tested in both LOCKED and UNLOCKED mode.
Change-Id: Ica7b7df947ccb5fe515a3dae8c81eedfd594ca38
diff --git a/tools/avbctl/avbctl.cc b/tools/avbctl/avbctl.cc
index d0922b3..480edc1 100644
--- a/tools/avbctl/avbctl.cc
+++ b/tools/avbctl/avbctl.cc
@@ -32,13 +32,15 @@
 
 namespace {
 
+static bool g_opt_force = false;
+
 /* Prints program usage to |where|. */
 void usage(FILE* where, int /* argc */, char* argv[]) {
   fprintf(where,
           "%s - command-line tool for AVB.\n"
           "\n"
           "Usage:\n"
-          "  %s COMMAND\n"
+          "  %s [--force] COMMAND\n"
           "\n"
           "Commands:\n"
           "  %s get-verity           - Prints whether verity is enabled in "
@@ -59,6 +61,29 @@
           argv[0]);
 }
 
+/* Returns true if device is in LOCKED mode and --force wasn't
+ * passed. In this case also prints diagnostic message to stderr as a
+ * side-effect.
+ */
+bool is_locked_and_not_forced() {
+  std::string device_state;
+
+  device_state = android::base::GetProperty("ro.boot.vbmeta.device_state", "");
+  if (device_state == "locked" && !g_opt_force) {
+    fprintf(stderr,
+            "Manipulating vbmeta on a LOCKED device will likely cause the\n"
+            "device to fail booting with little chance of recovery.\n"
+            "\n"
+            "If you really want to do this, use the --force option.\n"
+            "\n"
+            "ONLY DO THIS IF YOU KNOW WHAT YOU ARE DOING.\n"
+            "\n");
+    return false;
+  }
+
+  return true;
+}
+
 /* Function to enable and disable verification. The |ops| parameter
  * should be an |AvbOps| from libavb_user.
  */
@@ -85,6 +110,10 @@
     return EX_OK;
   }
 
+  if (!is_locked_and_not_forced()) {
+    return EX_NOPERM;
+  }
+
   if (!avb_user_verification_set(ops, ab_suffix.c_str(), enable_verification)) {
     fprintf(stderr, "Error setting verification.\n");
     return EX_SOFTWARE;
@@ -149,6 +178,10 @@
     return EX_OK;
   }
 
+  if (!is_locked_and_not_forced()) {
+    return EX_NOPERM;
+  }
+
   if (!avb_user_verity_set(ops, ab_suffix.c_str(), enable_verity)) {
     fprintf(stderr, "Error setting verity.\n");
     return EX_SOFTWARE;
@@ -206,10 +239,21 @@
 
 }  // namespace
 
+enum class Command {
+  kNone,
+  kDisableVerity,
+  kEnableVerity,
+  kGetVerity,
+  kDisableVerification,
+  kEnableVerification,
+  kGetVerification,
+};
+
 int main(int argc, char* argv[]) {
   int ret;
   AvbOps* ops = nullptr;
   std::string ab_suffix = get_ab_suffix();
+  Command cmd = Command::kNone;
 
   if (argc < 2) {
     usage(stderr, argc, argv);
@@ -224,24 +268,49 @@
     goto out;
   }
 
-  if (strcmp(argv[1], "disable-verity") == 0) {
-    ret = do_set_verity(ops, ab_suffix, false);
-  } else if (strcmp(argv[1], "enable-verity") == 0) {
-    ret = do_set_verity(ops, ab_suffix, true);
-  } else if (strcmp(argv[1], "get-verity") == 0) {
-    ret = do_get_verity(ops, ab_suffix);
-  } else if (strcmp(argv[1], "disable-verification") == 0) {
-    ret = do_set_verification(ops, ab_suffix, false);
-  } else if (strcmp(argv[1], "enable-verification") == 0) {
-    ret = do_set_verification(ops, ab_suffix, true);
-  } else if (strcmp(argv[1], "get-verification") == 0) {
-    ret = do_get_verification(ops, ab_suffix);
-  } else {
-    usage(stderr, argc, argv);
-    ret = EX_USAGE;
+  for (int n = 1; n < argc; n++) {
+    if (strcmp(argv[n], "--force") == 0) {
+      g_opt_force = true;
+    } else if (strcmp(argv[n], "disable-verity") == 0) {
+      cmd = Command::kDisableVerity;
+    } else if (strcmp(argv[n], "enable-verity") == 0) {
+      cmd = Command::kEnableVerity;
+    } else if (strcmp(argv[n], "get-verity") == 0) {
+      cmd = Command::kGetVerity;
+    } else if (strcmp(argv[n], "disable-verification") == 0) {
+      cmd = Command::kDisableVerification;
+    } else if (strcmp(argv[n], "enable-verification") == 0) {
+      cmd = Command::kEnableVerification;
+    } else if (strcmp(argv[n], "get-verification") == 0) {
+      cmd = Command::kGetVerification;
+    }
   }
 
-  ret = EX_OK;
+  switch (cmd) {
+    case Command::kNone:
+      usage(stderr, argc, argv);
+      ret = EX_USAGE;
+      break;
+    case Command::kDisableVerity:
+      ret = do_set_verity(ops, ab_suffix, false);
+      break;
+    case Command::kEnableVerity:
+      ret = do_set_verity(ops, ab_suffix, true);
+      break;
+    case Command::kGetVerity:
+      ret = do_get_verity(ops, ab_suffix);
+      break;
+    case Command::kDisableVerification:
+      ret = do_set_verification(ops, ab_suffix, false);
+      break;
+    case Command::kEnableVerification:
+      ret = do_set_verification(ops, ab_suffix, true);
+      break;
+    case Command::kGetVerification:
+      ret = do_get_verification(ops, ab_suffix);
+      break;
+  }
+
 out:
   if (ops != nullptr) {
     avb_ops_user_free(ops);