blob: 8e24f90cff96de3bca3aedae17874d198aa75610 [file] [log] [blame]
/*
* Copyright (C) 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "tools/base/deploy/installer/root_push_install.h"
#include <ftw.h>
#include "tools/base/deploy/common/utils.h"
#include "tools/base/deploy/installer/executor/executor.h"
#include "tools/base/deploy/installer/patch_applier.h"
namespace deploy {
namespace {
const char* kTempSuffix = ".tmp";
}
void RootPushInstallCommand::ParseParameters(
const proto::InstallerRequest& request) {
if (!request.has_root_push_install_request()) {
ErrEvent("rootpushinstall: unable to get rootpushinstall request.");
return;
}
request_ = request.root_push_install_request();
ready_to_run_ = true;
}
void RootPushInstallCommand::Run(proto::InstallerResponse* response) {
Phase p("Command RootPushInstall");
auto install_response = response->mutable_root_push_install_response();
// Delete the native libs dir. We need to force the framework to read them
// directly from the APKs. This directory is only recreated when the app is
// installed via the package manager, so this deletion will not happen
// frequently.
auto lib_dir = request_.install_dir() + "/lib";
if (access(lib_dir.c_str(), F_OK) == 0 &&
nftw(lib_dir.c_str(),
[](const char* path, const struct stat* sbuf, int type,
struct FTW* ftwb) { return remove(path); },
10 /*max FD*/, FTW_DEPTH | FTW_MOUNT | FTW_PHYS) != 0) {
install_response->set_status(proto::RootPushInstallResponse::ERROR);
install_response->set_error_message(
"rootpushinstall: deleting lib dir failed: "_s + strerror(errno));
return;
}
auto install_info = request_.install_info();
for (const proto::PatchInstruction& patch :
install_info.patchinstructions()) {
// Skip if this apk did not change
if (patch.patches().size() == 0) {
LogEvent("rootpushinstall: skipping '"_s + patch.src_absolute_path() +
"' since apk did not change");
continue;
}
// If any of the following operations fail, the expectation is that the host
// will perform a correct install to properly complete the installation,
// which will naturally clean up any left behind temp files.
PatchApplier patchApplier;
std::string tmp_file = patch.src_absolute_path() + kTempSuffix;
int fd = IO::creat(tmp_file, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
if (fd < 0) {
install_response->set_status(proto::RootPushInstallResponse::ERROR);
install_response->set_error_message(
"rootpushinstall: creat() failed: "_s + strerror(errno));
return;
}
bool patch_result = patchApplier.ApplyPatchToFD(patch, fd);
if (!patch_result) {
install_response->set_status(proto::RootPushInstallResponse::ERROR);
install_response->set_error_message(
"rootpushinstall: unable to patch '"_s + patch.src_absolute_path() +
"'");
return;
}
if (close(fd) < 0) {
install_response->set_status(proto::RootPushInstallResponse::ERROR);
install_response->set_error_message(
"rootpushinstall:n close() failed: "_s + strerror(errno));
return;
}
if (IO::rename(tmp_file, patch.src_absolute_path()) < 0) {
install_response->set_status(proto::RootPushInstallResponse::ERROR);
install_response->set_error_message(
"rootpushinstall: rename() failed: "_s + strerror(errno));
return;
}
LogEvent("rootpushinstall: patching succeeded for '"_s +
patch.src_absolute_path() + "'");
install_response->set_status(proto::RootPushInstallResponse_Status_OK);
}
}
} // namespace deploy