* Copyright (c) 2024 Huawei Device Co., Ltd.
* 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.
*/
#define LOG_TAG "RdConnection"
#include "rd_connection.h"
#include <securec.h>
#include <string>
#include "grd_api_manager.h"
#include "logger.h"
#include "rd_statement.h"
#include "rdb_errno.h"
#include "rdb_security_manager.h"
#include "sqlite_global_config.h"
#include "sqlite_utils.h"
namespace OHOS {
namespace NativeRdb {
using namespace OHOS::Rdb;
__attribute__((used))
const int32_t RdConnection::regCreator_ = Connection::RegisterCreator(DB_VECTOR, RdConnection::Create);
__attribute__((used))
const int32_t RdConnection::regRepairer_ = Connection::RegisterRepairer(DB_VECTOR, RdConnection::Repair);
__attribute__((used))
const int32_t RdConnection::regDeleter_ = Connection::RegisterDeleter(DB_VECTOR, RdConnection::Delete);
constexpr int CONFIG_SIZE_EXCEPT_ENCRYPT = 167;
std::pair<int32_t, std::shared_ptr<Connection>> RdConnection::Create(const RdbStoreConfig &config, bool isWrite)
{
std::pair<int32_t, std::shared_ptr<Connection>> result = { E_ERROR, nullptr };
if (!IsUsingArkData() || config.GetStorageMode() == StorageMode::MODE_MEMORY) {
result.first = E_NOT_SUPPORT;
return result;
}
auto &[errCode, conn] = result;
for (size_t i = 0; i < ITERS_COUNT; i++) {
std::shared_ptr<RdConnection> connection = std::make_shared<RdConnection>(config, isWrite);
if (connection == nullptr) {
LOG_ERROR("SqliteConnection::Open new failed, connection is nullptr.");
return result;
}
errCode = connection->InnerOpen(config);
if (errCode == E_OK) {
conn = connection;
break;
}
}
return result;
}
int32_t RdConnection::Repair(const RdbStoreConfig &config)
{
std::string dbPath = "";
auto errCode = SqliteGlobalConfig::GetDbPath(config, dbPath);
if (errCode != E_OK) {
LOG_ERROR("Can not get db path.");
return errCode;
}
std::vector<uint8_t> key = config.GetEncryptKey();
errCode = RdUtils::RdDbRepair(dbPath.c_str(), GetConfigStr(key, config.IsEncrypt()).c_str());
key.assign(key.size(), 0);
if (errCode != E_OK) {
LOG_ERROR("Fail to repair db.");
}
return errCode;
}
static constexpr const char *RD_POST_FIXES[] = {
"",
".redo",
".undo",
".ctrl",
".ctrl.dwr",
".safe",
".map",
".corruptedflg",
};
int32_t RdConnection::Delete(const RdbStoreConfig &config)
{
auto path = config.GetPath();
for (auto postFix : RD_POST_FIXES) {
std::string shmFilePath = path + postFix;
if (access(shmFilePath.c_str(), F_OK) == 0) {
remove(shmFilePath.c_str());
}
}
return E_OK;
}
RdConnection::RdConnection(const RdbStoreConfig &config, bool isWriter) : isWriter_(isWriter), config_(config)
{
}
RdConnection::~RdConnection()
{
if (dbHandle_ != nullptr) {
int errCode = RdUtils::RdDbClose(dbHandle_, 0);
if (errCode != E_OK) {
LOG_ERROR("~RdConnection ~RdConnection: could not close database err = %{public}d", errCode);
}
dbHandle_ = nullptr;
}
}
std::string RdConnection::GetConfigStr(const std::vector<uint8_t> &keys, bool isEncrypt)
{
std::string config = "{";
if (isEncrypt) {
const size_t keyBuffSize = keys.size() * 2 + 1;
config.reserve(CONFIG_SIZE_EXCEPT_ENCRYPT + keyBuffSize);
std::vector<char> keyBuff(keyBuffSize);
config += "\"isEncrypted\":1,";
config += "\"hexPassword\":\"";
config += RdUtils::GetEncryptKey(keys, keyBuff.data(), keyBuff.size());
config += "\",";
std::fill(keyBuff.begin(), keyBuff.end(), 0);
}
config += RdConnection::GRD_OPEN_CONFIG_STR;
config += "}";
return config;
}
int RdConnection::InnerOpen(const RdbStoreConfig &config)
{
std::string dbPath = "";
auto errCode = SqliteGlobalConfig::GetDbPath(config, dbPath);
if (errCode != E_OK) {
LOG_ERROR("Can not get db path.");
return errCode;
}
std::vector<uint8_t> newKey = config.GetNewEncryptKey();
if (!newKey.empty()) {
newKey.assign(newKey.size(), 0);
errCode = ResetKey(config);
if (errCode != E_OK) {
LOG_ERROR("Can not reset key %{public}d.", errCode);
return errCode;
}
}
std::vector<uint8_t> key = config.GetEncryptKey();
std::string configStr = GetConfigStr(key, config.IsEncrypt());
errCode = RdUtils::RdDbOpen(dbPath.c_str(), configStr.c_str(),
GRD_DB_OPEN_CREATE | GRD_DB_OPEN_IGNORE_DATA_CORRPUPTION, &dbHandle_);
if (errCode == E_CHANGE_UNENCRYPTED_TO_ENCRYPTED) {
errCode = RdUtils::RdDbRekey(dbPath.c_str(), GetConfigStr({}, false).c_str(), key);
if (errCode != E_OK) {
key.assign(key.size(), 0);
RdUtils::ClearAndZeroString(configStr);
LOG_ERROR("Can not rekey caylay db %{public}d.", errCode);
return errCode;
}
errCode = RdUtils::RdDbOpen(dbPath.c_str(), configStr.c_str(),
GRD_DB_OPEN_CREATE | GRD_DB_OPEN_IGNORE_DATA_CORRPUPTION, &dbHandle_);
}
key.assign(key.size(), 0);
RdUtils::ClearAndZeroString(configStr);
if (errCode != E_OK) {
LOG_ERROR("Can not open rd db %{public}d.", errCode);
return errCode;
}
errCode = RdUtils::RdSqlRegistryThreadPool(dbHandle_);
if (errCode != E_OK) {
LOG_ERROR("Can not registry ThreadPool rd db %{public}d.", errCode);
return errCode;
}
return errCode;
}
int32_t RdConnection::VerifyAndRegisterHook(const RdbStoreConfig &config)
{
return E_NOT_SUPPORT;
}
void RdConnection::Interrupt()
{
return;
}
std::pair<int32_t, RdConnection::Stmt> RdConnection::CreateStatement(
const std::string &sql, Connection::SConn conn, const std::string &returningSql)
{
auto stmt = std::make_shared<RdStatement>();
stmt->conn_ = conn;
stmt->config_ = &config_;
stmt->setPragmas_["user_version"] = ([this](const int &value) -> int32_t {
return RdUtils::RdDbSetVersion(dbHandle_, GRD_CONFIG_USER_VERSION, value);
});
stmt->getPragmas_["user_version"] = ([this](int &version) -> int32_t {
return RdUtils::RdDbGetVersion(dbHandle_, GRD_CONFIG_USER_VERSION, version);
});
int32_t ret = stmt->Prepare(dbHandle_, sql);
if (ret != E_OK) {
return { ret, nullptr };
}
return { ret, stmt };
}
std::pair<int32_t, RdConnection::Stmt> RdConnection::CreateReplicaStatement([[gnu::unused]]
const std::string &sql, [[gnu::unused]] Connection::SConn conn, const std::string &returningSql)
{
return { E_NOT_SUPPORT, nullptr };
}
int RdConnection::CheckReplicaForRestore(const bool isForceRestore)
{
return E_NOT_SUPPORT;
}
int32_t RdConnection::GetDBType() const
{
return DB_VECTOR;
}
bool RdConnection::IsWriter() const
{
return isWriter_;
}
int32_t RdConnection::ResetKey(const RdbStoreConfig &config)
{
if (!IsWriter()) {
return E_OK;
}
std::string dbPath = "";
int errCode = SqliteGlobalConfig::GetDbPath(config, dbPath);
if (errCode != E_OK) {
LOG_ERROR("Can not get db path.");
return errCode;
}
std::vector<uint8_t> key = config.GetEncryptKey();
std::vector<uint8_t> newKey = config.GetNewEncryptKey();
std::string configStr = GetConfigStr(key, config.IsEncrypt());
errCode = RdUtils::RdDbRekey(dbPath.c_str(), configStr.c_str(), newKey);
RdUtils::ClearAndZeroString(configStr);
key.assign(key.size(), 0);
newKey.assign(newKey.size(), 0);
if (errCode != E_OK) {
LOG_ERROR("ReKey failed, err = %{public}d, errno = %{public}d", errCode, errno);
RdbSecurityManager::GetInstance().DelKeyFile(
config.GetPath(), RdbSecurityManager::KeyFileType::PUB_KEY_FILE_NEW_KEY);
return E_OK;
}
config.ChangeEncryptKey();
return E_OK;
}
std::pair<int32_t, std::vector<uint8_t>> RdConnection::GetRekeyNewKey(const RdbStoreConfig::CryptoParam &cryptoParam)
{
std::vector<uint8_t> key;
RdbPassword rdbPwd;
if (cryptoParam.encryptKey_.empty()) {
rdbPwd = RdbSecurityManager::GetInstance().GetRdbPassword(
config_.GetPath(), RdbSecurityManager::PUB_KEY_FILE_NEW_KEY);
if (!rdbPwd.IsValid()) {
LOG_ERROR("key is invalid");
return { E_ERROR, {} };
}
key = std::vector<uint8_t>(rdbPwd.GetData(), rdbPwd.GetData() + rdbPwd.GetSize());
rdbPwd.Clear();
} else {
key = cryptoParam.encryptKey_;
}
if (key.empty()) {
LOG_ERROR("key is empty");
return { E_ERROR, {} };
}
return { E_OK, key };
}
int32_t RdConnection::Rekey(const RdbStoreConfig::CryptoParam &cryptoParam)
{
std::string dbPath = "";
int errCode = SqliteGlobalConfig::GetDbPath(config_, dbPath);
if (errCode != E_OK) {
LOG_ERROR("Can not get db path, errCode = %{public}d, errno = %{public}d", errCode, errno);
return errCode;
}
auto [ret, key] = GetRekeyNewKey(cryptoParam);
if (ret != E_OK) {
LOG_ERROR("Can not get encryption key, ret = %{public}d, errno = %{public}d", ret, errno);
key.assign(key.size(), 0);
return ret;
}
errCode = RdUtils::RdDbClose(dbHandle_, 0);
if (errCode != E_OK) {
LOG_ERROR("Close db failed, errCode = %{public}d, errno = %{public}d", errCode, errno);
key.assign(key.size(), 0);
return errCode;
}
dbHandle_ = nullptr;
std::string configStr = GetConfigStr(config_.GetEncryptKey(), config_.IsEncrypt());
errCode = RdUtils::RdDbRekey(dbPath.c_str(), configStr.c_str(), key);
RdUtils::ClearAndZeroString(configStr);
if (errCode != E_OK) {
LOG_ERROR("Rekey failed, errCode = %{public}d, errno = %{public}d", errCode, errno);
key.assign(key.size(), 0);
return errCode;
}
if (cryptoParam.encryptKey_.empty()) {
RdbSecurityManager::GetInstance().ChangeKeyFile(config_.GetPath());
}
config_.ResetEncryptKey(key);
LOG_INFO("Rekey successful, bundleName is %{public}s, store is %{public}s.",
config_.GetBundleName().c_str(), SqliteUtils::Anonymous(config_.GetName()).c_str());
key.assign(key.size(), 0);
return E_OK;
}
int32_t RdConnection::RekeyEx(const RdbStoreConfig &config, const RdbStoreConfig::CryptoParam &cryptoParam)
{
return E_NOT_SUPPORT;
}
int32_t RdConnection::TryCheckPoint(bool timeout)
{
return E_NOT_SUPPORT;
}
int32_t RdConnection::LimitWalSize()
{
return E_NOT_SUPPORT;
}
int32_t RdConnection::ConfigLocale(const std::string &localeStr)
{
return E_NOT_SUPPORT;
}
int32_t RdConnection::SetTokenizer(Tokenizer tokenizer)
{
return E_NOT_SUPPORT;
}
int32_t RdConnection::CleanDirtyData(const std::string &table, uint64_t cursor)
{
return E_NOT_SUPPORT;
}
int32_t RdConnection::SubscribeTableChanges(const Connection::Notifier ¬ifier)
{
return E_NOT_SUPPORT;
}
int32_t RdConnection::GetMaxVariable() const
{
return MAX_VARIABLE_NUM;
}
int32_t RdConnection::GetJournalMode()
{
return E_NOT_SUPPORT;
}
int32_t RdConnection::ClearCache(bool isForceClear)
{
return E_NOT_SUPPORT;
}
int32_t RdConnection::Subscribe(const std::shared_ptr<DistributedDB::StoreObserver> &observer)
{
return E_NOT_SUPPORT;
}
int32_t RdConnection::Unsubscribe(const std::shared_ptr<DistributedDB::StoreObserver> &observer)
{
return E_NOT_SUPPORT;
}
int32_t RdConnection::Backup(const std::string &databasePath, const std::vector<uint8_t> &destEncryptKey, bool isAsync,
std::shared_ptr<SlaveStatus> slaveStatus, bool verifyDb)
{
if (!destEncryptKey.empty() && !config_.IsEncrypt()) {
return RdUtils::RdDbBackup(dbHandle_, databasePath.c_str(), destEncryptKey);
}
if (config_.IsEncrypt()) {
std::vector<uint8_t> key = config_.GetEncryptKey();
int32_t ret = RdUtils::RdDbBackup(dbHandle_, databasePath.c_str(), key);
key.assign(key.size(), 0);
return ret;
}
return RdUtils::RdDbBackup(dbHandle_, databasePath.c_str(), {});
}
int32_t RdConnection::Restore(
const std::string &databasePath, const std::vector<uint8_t> &destEncryptKey,
std::shared_ptr<SlaveStatus> slaveStatus, const bool isForceRestore)
{
auto ret = RdUtils::RdDbClose(dbHandle_, 0);
if (ret != E_OK) {
LOG_ERROR("Close db failed");
return ret;
}
if (destEncryptKey.empty()) {
std::vector<uint8_t> key = config_.GetEncryptKey();
ret = RdUtils::RdDbRestore(config_.GetPath().c_str(), databasePath.c_str(), key);
key.assign(key.size(), 0);
} else {
ret = RdUtils::RdDbRestore(config_.GetPath().c_str(), databasePath.c_str(), destEncryptKey);
}
if (ret != E_OK) {
LOG_ERROR("Restore failed, original datapath:%{public}s, restorepath:%{public}s, errcode:%{public}d",
SqliteUtils::Anonymous(config_.GetPath()).c_str(),
SqliteUtils::Anonymous(databasePath).c_str(), ret);
return ret;
}
ret = InnerOpen(config_);
if (ret != E_OK) {
LOG_ERROR("Reopen db failed:%{public}d", ret);
return ret;
}
return ret;
}
ExchangeStrategy RdConnection::GenerateExchangeStrategy(std::shared_ptr<SlaveStatus> status, bool isRelpay)
{
return ExchangeStrategy::NOT_HANDLE;
}
int RdConnection::SetKnowledgeSchema([[gnu::unused]] const DistributedRdb::RdbKnowledgeSchema &schema)
{
return E_NOT_SUPPORT;
}
int32_t RdConnection::RegisterReplayCallback(const RdbStoreConfig &config,
const ReplayCallBack &replayCallback)
{
return E_NOT_SUPPORT;
}
void RdConnection::ReplayBinlog([[gnu::unused]] const RdbStoreConfig &config, [[gnu::unused]] bool chkBinlogCount)
{
}
int RdConnection::CleanDirtyLog([[gnu::unused]] const std::string &table, [[gnu::unused]] uint64_t cursor)
{
return E_NOT_SUPPORT;
}
int RdConnection::ArchiveSyncedData([[gnu::unused]] const std::string &table, [[gnu::unused]] uint64_t cursor)
{
return E_NOT_SUPPORT;
}
int RdConnection::DeleteSyncedData([[gnu::unused]] const std::string &table,
[[gnu::unused]] const std::vector<std::vector<PRIKey>> &keys)
{
return E_NOT_SUPPORT;
}
int RdConnection::RegisterAlgo(const std::string &clstAlgoName, ClusterAlgoFunc func)
{
int errCode =
RdUtils::RdSqlRegistryClusterAlgo(dbHandle_, clstAlgoName.c_str(), reinterpret_cast<GRD_ClusterAlgoFunc>(func));
if (errCode != E_OK) {
LOG_ERROR("Can not registry cluster func in rd db %{public}d.", errCode);
return errCode;
}
return E_OK;
}
int RdConnection::SetMatrixFileInfo([[gnu::unused]] const DistributedRdb::MatrixFileInfo &fileInfo)
{
return E_NOT_SUPPORT;
}
int RdConnection::UpdateTrackerMatrix([[gnu::unused]] const DistributedRdb::RdbChangedData &rdbChangedData,
[[gnu::unused]] bool isFull)
{
return E_NOT_SUPPORT;
}
}
}