| // Copyright 2019 The Chromium Authors. All rights reserved. | |
| // Use of this source code is governed by a BSD-style license that can be | |
| // found in the LICENSE file. | |
| #include "chrome/updater/installer.h" | |
| #include <utility> | |
| #include "base/callback.h" | |
| #include "base/files/file_enumerator.h" | |
| #include "base/files/file_util.h" | |
| #include "base/logging.h" | |
| #include "chrome/updater/updater_constants.h" | |
| #include "chrome/updater/util.h" | |
| #include "components/crx_file/crx_verifier.h" | |
| #include "components/update_client/update_client_errors.h" | |
| #include "components/update_client/utils.h" | |
| namespace updater { | |
| namespace { | |
| // Version "0" corresponds to no installed version. | |
| const char kNullVersion[] = "0.0.0.0"; | |
| // Returns the full path to the installation directory for the application | |
| // identified by the |crx_id|. | |
| base::FilePath GetAppInstallDir(const std::string& crx_id) { | |
| base::FilePath app_install_dir; | |
| if (CreateProductDirectory(&app_install_dir)) { | |
| app_install_dir = app_install_dir.AppendASCII(kAppsDir); | |
| app_install_dir = app_install_dir.AppendASCII(crx_id); | |
| } | |
| return app_install_dir; | |
| } | |
| } // namespace | |
| Installer::InstallInfo::InstallInfo() : version(kNullVersion) {} | |
| Installer::InstallInfo::~InstallInfo() = default; | |
| Installer::Installer(const std::vector<uint8_t>& pk_hash) | |
| : pk_hash_(pk_hash), | |
| crx_id_(update_client::GetCrxIdFromPublicKeyHash(pk_hash)), | |
| install_info_(std::make_unique<InstallInfo>()) {} | |
| Installer::~Installer() = default; | |
| update_client::CrxComponent Installer::MakeCrxComponent() { | |
| update_client::CrxComponent component; | |
| component.installer = scoped_refptr<Installer>(this); | |
| component.requires_network_encryption = false; | |
| component.crx_format_requirement = | |
| crx_file::VerifierFormat::CRX3_WITH_PUBLISHER_PROOF; | |
| component.pk_hash = pk_hash_; | |
| component.name = crx_id_; | |
| component.version = install_info_->version; | |
| component.fingerprint = install_info_->fingerprint; | |
| return component; | |
| } | |
| void Installer::FindInstallOfApp() { | |
| VLOG(1) << __func__ << " for " << crx_id_; | |
| const base::FilePath app_install_dir = GetAppInstallDir(crx_id_); | |
| if (app_install_dir.empty() || !base::PathExists(app_install_dir)) { | |
| install_info_ = std::make_unique<InstallInfo>(); | |
| return; | |
| } | |
| base::Version latest_version(kNullVersion); | |
| base::FilePath latest_path; | |
| std::vector<base::FilePath> older_paths; | |
| base::FileEnumerator file_enumerator(app_install_dir, false, | |
| base::FileEnumerator::DIRECTORIES); | |
| for (auto path = file_enumerator.Next(); !path.value().empty(); | |
| path = file_enumerator.Next()) { | |
| const base::Version version(path.BaseName().MaybeAsASCII()); | |
| // Ignore folders that don't have valid version names. | |
| if (!version.IsValid()) | |
| continue; | |
| // The |version| not newer than the latest found version is marked for | |
| // removal. |kNullVersion| is also removed. | |
| if (version.CompareTo(latest_version) <= 0) { | |
| older_paths.push_back(path); | |
| continue; | |
| } | |
| // New valid |version| folder found. | |
| if (!latest_path.empty()) | |
| older_paths.push_back(latest_path); | |
| latest_version = version; | |
| latest_path = path; | |
| } | |
| install_info_->version = latest_version; | |
| install_info_->install_dir = latest_path; | |
| install_info_->manifest = update_client::ReadManifest(latest_path); | |
| base::ReadFileToString(latest_path.AppendASCII("manifest.fingerprint"), | |
| &install_info_->fingerprint); | |
| for (const auto& older_path : older_paths) | |
| base::DeleteFile(older_path, true); | |
| } | |
| Installer::Result Installer::InstallHelper(const base::FilePath& unpack_path) { | |
| auto local_manifest = update_client::ReadManifest(unpack_path); | |
| if (!local_manifest) | |
| return Result(update_client::InstallError::BAD_MANIFEST); | |
| std::string version_ascii; | |
| local_manifest->GetStringASCII("version", &version_ascii); | |
| const base::Version manifest_version(version_ascii); | |
| VLOG(1) << "Installed version=" << install_info_->version.GetString() | |
| << ", installing version=" << manifest_version.GetString(); | |
| if (!manifest_version.IsValid()) | |
| return Result(update_client::InstallError::INVALID_VERSION); | |
| if (install_info_->version.CompareTo(manifest_version) > 0) | |
| return Result(update_client::InstallError::VERSION_NOT_UPGRADED); | |
| const base::FilePath app_install_dir = GetAppInstallDir(crx_id_); | |
| if (app_install_dir.empty()) | |
| return Result(update_client::InstallError::NO_DIR_COMPONENT_USER); | |
| if (!base::CreateDirectory(app_install_dir)) { | |
| return Result( | |
| static_cast<int>(update_client::InstallError::CUSTOM_ERROR_BASE) + | |
| kCustomInstallErrorCreateAppInstallDirectory); | |
| } | |
| const auto versioned_install_dir = | |
| app_install_dir.AppendASCII(manifest_version.GetString()); | |
| if (base::PathExists(versioned_install_dir)) { | |
| if (!base::DeleteFile(versioned_install_dir, true)) | |
| return Result(update_client::InstallError::CLEAN_INSTALL_DIR_FAILED); | |
| } | |
| VLOG(1) << "Install_path=" << versioned_install_dir.AsUTF8Unsafe(); | |
| if (!base::Move(unpack_path, versioned_install_dir)) { | |
| PLOG(ERROR) << "Move failed."; | |
| base::DeleteFile(versioned_install_dir, true); | |
| return Result(update_client::InstallError::MOVE_FILES_ERROR); | |
| } | |
| DCHECK(!base::PathExists(unpack_path)); | |
| DCHECK(base::PathExists(versioned_install_dir)); | |
| install_info_->manifest = std::move(local_manifest); | |
| install_info_->version = manifest_version; | |
| install_info_->install_dir = versioned_install_dir; | |
| base::ReadFileToString( | |
| versioned_install_dir.AppendASCII("manifest.fingerprint"), | |
| &install_info_->fingerprint); | |
| return Result(update_client::InstallError::NONE); | |
| } | |
| void Installer::OnUpdateError(int error) { | |
| LOG(ERROR) << "updater error: " << error << " for " << crx_id_; | |
| } | |
| void Installer::Install(const base::FilePath& unpack_path, | |
| const std::string& public_key, | |
| Callback callback) { | |
| std::unique_ptr<base::DictionaryValue> manifest; | |
| base::Version version; | |
| base::FilePath install_path; | |
| const auto result = InstallHelper(unpack_path); | |
| base::DeleteFile(unpack_path, true); | |
| std::move(callback).Run(result); | |
| } | |
| bool Installer::GetInstalledFile(const std::string& file, | |
| base::FilePath* installed_file) { | |
| return false; | |
| } | |
| bool Installer::Uninstall() { | |
| return false; | |
| } | |
| } // namespace updater |