Installd: Add move_ab command
Add a command to move A/B artifacts into the right position. This
is used for apps that live in data.
Bug: 25612095
Change-Id: I40c3ca54bd0f3b2d646a14b145e733096dabbbb2
diff --git a/cmds/installd/commands.cpp b/cmds/installd/commands.cpp
index c9de3b9..44e2dd2 100644
--- a/cmds/installd/commands.cpp
+++ b/cmds/installd/commands.cpp
@@ -1786,5 +1786,83 @@
return 0;
}
+// Helper for move_ab, so that we can have common failure-case cleanup.
+static bool unlink_and_rename(const char* from, const char* to) {
+ // Check whether "from" exists, and if so whether it's regular. If it is, unlink. Otherwise,
+ // return a failure.
+ struct stat s;
+ if (stat(to, &s) == 0) {
+ if (!S_ISREG(s.st_mode)) {
+ LOG(ERROR) << from << " is not a regular file to replace for A/B.";
+ return false;
+ }
+ if (unlink(to) != 0) {
+ LOG(ERROR) << "Could not unlink " << to << " to move A/B.";
+ return false;
+ }
+ } else {
+ // This may be a permission problem. We could investigate the error code, but we'll just
+ // let the rename failure do the work for us.
+ }
+
+ // Try to rename "to" to "from."
+ if (rename(from, to) != 0) {
+ PLOG(ERROR) << "Could not rename " << from << " to " << to;
+ return false;
+ }
+
+ return true;
+}
+
+int move_ab(const char* apk_path, const char* instruction_set, const char* oat_dir) {
+ if (apk_path == nullptr || instruction_set == nullptr || oat_dir == nullptr) {
+ LOG(ERROR) << "Cannot move_ab with null input";
+ return -1;
+ }
+ if (validate_apk_path(apk_path) != 0) {
+ LOG(ERROR) << "invalid apk_path " << apk_path;
+ return -1;
+ }
+ if (validate_apk_path(oat_dir) != 0) {
+ LOG(ERROR) << "invalid oat_dir " << oat_dir;
+ return -1;
+ }
+
+ char a_path[PKG_PATH_MAX];
+ if (!calculate_oat_file_path(a_path, oat_dir, apk_path, instruction_set)) {
+ return -1;
+ }
+
+ // B path = A path + ".b"
+ std::string b_path = StringPrintf("%s.b", a_path);
+
+ // Check whether B exists.
+ {
+ struct stat s;
+ if (stat(b_path.c_str(), &s) != 0) {
+ LOG(ERROR) << "Can't find A/B artifact at " << b_path;
+ return -1;
+ }
+ if (!S_ISREG(s.st_mode)) {
+ LOG(ERROR) << "A/B artifact " << b_path << " is not a regular file.";
+ // Try to unlink, but swallow errors.
+ unlink(b_path.c_str());
+ return -1;
+ }
+ }
+
+ // Rename B to A.
+ if (!unlink_and_rename(b_path.c_str(), a_path)) {
+ // Delete the b_path so we don't try again (or fail earlier).
+ if (unlink(b_path.c_str()) != 0) {
+ PLOG(ERROR) << "Could not unlink " << b_path;
+ }
+
+ return -1;
+ }
+
+ return 0;
+}
+
} // namespace installd
} // namespace android
diff --git a/cmds/installd/commands.h b/cmds/installd/commands.h
index fe03397..8507eff 100644
--- a/cmds/installd/commands.h
+++ b/cmds/installd/commands.h
@@ -58,6 +58,9 @@
int rm_package_dir(const char* apk_path);
int link_file(const char *relative_path, const char *from_base, const char *to_base);
+// Move a B version over to the A location. Only works for oat_dir != nullptr.
+int move_ab(const char *apk_path, const char *instruction_set, const char* oat_dir);
+
} // namespace installd
} // namespace android
diff --git a/cmds/installd/installd.cpp b/cmds/installd/installd.cpp
index 63290a9..54d3970 100644
--- a/cmds/installd/installd.cpp
+++ b/cmds/installd/installd.cpp
@@ -336,6 +336,11 @@
return link_file(arg[0], arg[1], arg[2]);
}
+static int do_move_ab(char **arg, char reply[REPLY_MAX] ATTRIBUTE_UNUSED) {
+ // apk_path, instruction_set, oat_dir
+ return move_ab(arg[0], arg[1], arg[2]);
+}
+
struct cmdinfo {
const char *name;
unsigned numargs;
@@ -364,6 +369,7 @@
{ "createoatdir", 2, do_create_oat_dir },
{ "rmpackagedir", 1, do_rm_package_dir },
{ "linkfile", 3, do_link_file },
+ { "move_ab", 3, do_move_ab },
};
static int readx(int s, void *_buf, int count)