* Copyright (c) 2021-2026 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.
*/
#include "utils/string_utils.h"
#include "utils/file_utils.h"
#include "utils/hi_audit.h"
#include <dirent.h>
#include <fcntl.h>
#include <regex>
#include <unistd.h>
#include <memory>
#include <sstream>
#include <cinttypes>
#include "res_type.h"
#include "res_sched_client.h"
#include "securec.h"
#include "storage_service_log.h"
#include "storage_service_constant.h"
using namespace std;
namespace OHOS {
namespace StorageDaemon {
static constexpr int32_t BUFF_SIZE = 1024;
static constexpr int32_t THREAD_QOS_HIGH_LEVEL = 7;
static constexpr int32_t THREAD_QOS_LOW_LEVEL = -1;
static constexpr const char *APP_EL1_PATH = "/data/app/el1";
static constexpr int32_t TWO_CHARACTER = 2;
static constexpr int32_t FOUR_CHARACTER = 4;
static constexpr int32_t FIVE_CHARACTER = 5;
std::string StringPrintf(const char *format, ...)
{
va_list ap;
va_list apBackup;
va_start(ap, format);
va_copy(apBackup, ap);
char buf[BUFF_SIZE] = {0};
std::string result;
int count = vsnprintf_s(buf, sizeof(buf), sizeof(buf), format, apBackup);
if (count < 0) {
LOGE("[L8:StringUtils] StringPrintf: <<< EXIT FAILED <<< vsnprintf_s error, errno=%{public}d", errno);
} else if (count < BUFF_SIZE) {
result.append(buf, count);
} else {
LOGI("[L8:StringUtils] StringPrintf: allocate larger buffer, len=%{public}d", count + 1);
char *newBuf = new (std::nothrow) char[count + 1];
if (newBuf != nullptr) {
count = vsnprintf_s(newBuf, count + 1, count + 1, format, ap);
if (count >= 0) {
result.append(newBuf, count);
}
}
delete[] newBuf;
}
va_end(apBackup);
va_end(ap);
return result;
}
std::vector<std::string> SplitLine(std::string &line, std::string &token)
{
std::vector<std::string> result;
std::string::size_type start;
std::string::size_type end;
start = 0;
end = line.find(token);
while (std::string::npos != end) {
result.push_back(line.substr(start, end - start));
start = end + token.size();
end = line.find(token, start);
}
if (start != line.length()) {
result.push_back(line.substr(start));
}
return result;
}
bool WriteFileSync(const char *path, const uint8_t *data, size_t size, std::string &errMsg)
{
LOGD("[L8:StringUtils] WriteFileSync: >>> ENTER <<< path=%{public}s, size=%{public}zu", path, size);
FILE *f = fopen(path, "w");
if (f == nullptr) {
errMsg = "f == nullptr, errno" + std::to_string(errno);
LOGE("[L8:StringUtils] WriteFileSync: <<< EXIT FAILED <<< open failed, errno=%{public}d", errno);
return false;
}
ChMod(path, S_IRUSR | S_IWUSR);
int fd = fileno(f);
if (fd == -1) {
errMsg = "fd == -1, errno" + std::to_string(errno);
LOGE("[L8:StringUtils] WriteFileSync: <<< EXIT FAILED <<< fd=-1, errno=%{public}d", errno);
(void)fclose(f);
return false;
}
long len = write(fd, data, size);
if (len < 0) {
errMsg = "fd == -1, errno" + std::to_string(errno);
LOGE("[L8:StringUtils] WriteFileSync: <<< EXIT FAILED <<< write failed, errno=%{public}d", errno);
(void)fclose(f);
return false;
}
if (static_cast<size_t>(len) != size) {
errMsg = "len != size, errno" + std::to_string(errno);
LOGE("[L8:StringUtils] WriteFileSync: <<< EXIT FAILED <<< len mismatch, len=%{public}ld, size=%{public}zu",
len, size);
(void)fclose(f);
return false;
}
if (fsync(fd) != 0) {
errMsg = "fsync(fd), errno" + std::to_string(errno);
LOGE("[L8:StringUtils] WriteFileSync: <<< EXIT FAILED <<< fsync failed, errno=%{public}d", errno);
(void)fclose(f);
return false;
}
(void)fclose(f);
LOGD("[L8:StringUtils] WriteFileSync: <<< EXIT SUCCESS <<<");
return true;
}
bool SaveStringToFileSync(const std::string &path, const std::string &data, std::string &errMsg)
{
if (path.empty() || data.empty()) {
LOGE("[L8:StringUtils] SaveStringToFileSync: <<< EXIT FAILED <<< empty input");
return false;
}
LOGI("[L8:StringUtils] SaveStringToFileSync: >>> ENTER <<< path=%{public}s, size=%{public}zu",
path.c_str(), data.length());
bool ret = WriteFileSync(path.c_str(), reinterpret_cast<const uint8_t *>(data.c_str()), data.size(), errMsg);
if (ret) {
LOGI("[L8:StringUtils] SaveStringToFileSync: <<< EXIT SUCCESS <<<");
} else {
LOGE("[L8:StringUtils] SaveStringToFileSync: <<< EXIT FAILED <<<");
}
return ret;
}
bool StringIsNumber(const std::string &content)
{
if (content.empty()) {
return false;
}
bool isNum = true;
for (char c : content) {
if (!isdigit(c)) {
isNum = false;
break;
}
}
return isNum;
}
bool IsStringExist(const std::list<std::string> &strList, const std::string &content)
{
auto it = std::find(strList.begin(), strList.end(), content);
return it != strList.end();
}
std::string ListToString(const std::list<std::string> &strList)
{
if (strList.empty()) {
return "";
}
std::string result;
for (auto &iter : strList) {
result += iter + ",";
}
return result.empty() ? "" : result.substr(0, result.length() -1);
}
void GetAllUserIds(std::vector<int32_t> &userIds)
{
LOGD("[L8:StringUtils] GetAllUserIds: >>> ENTER <<<");
auto procDir = std::unique_ptr<DIR, int (*)(DIR*)>(opendir(APP_EL1_PATH), closedir);
if (!procDir) {
LOGE("[L8:StringUtils] GetAllUserIds: <<< EXIT FAILED <<< open dir failed, errno=%{public}d", errno);
return;
}
std::regex pattern("^[1-9]\\d*$");
struct dirent *entry;
while ((entry = readdir(procDir.get())) != nullptr) {
if (entry->d_type != DT_DIR) {
continue;
}
std::string name = entry->d_name;
if (!std::regex_match(name, pattern)) {
continue;
}
char *endptr = nullptr;
errno = 0;
int64_t tollRes = strtoll(name.c_str(), &endptr, BASE_DECIMAL);
if (errno != 0 || endptr != name.c_str() + name.size()) {
continue;
}
if (tollRes < INT32_MIN || tollRes > INT32_MAX) {
continue;
}
int32_t userId = static_cast<int32_t>(tollRes);
if (userId < StorageService::DEFAULT_USER_ID) {
continue;
}
userIds.push_back(userId);
}
LOGD("[L8:StringUtils] GetAllUserIds: <<< EXIT SUCCESS <<< userIdCount=%{public}zu", userIds.size());
}
bool ConvertStringToInt(const std::string &str, int64_t &value, int32_t base)
{
if (str.empty()) {
return false;
}
errno = 0;
char* endptr = nullptr;
int64_t result = std::strtoll(str.c_str(), &endptr, base);
if (endptr == str.c_str()) {
return false;
}
if (errno == ERANGE && (result == LLONG_MAX || result == LLONG_MIN)) {
return false;
}
if (*endptr != '\0') {
return false;
}
value = result;
LOGD("[L8:StringUtils] ConvertStringToInt: <<< EXIT SUCCESS <<< value=%{public}" PRId64, value);
return true;
}
std::unordered_map<std::string, std::string> ParseKeyValuePairs(const std::string &input, char delimiter)
{
std::unordered_map<std::string, std::string> result;
std::istringstream ss(input);
std::string token;
while (std::getline(ss, token, delimiter)) {
size_t equalPos = token.find('=');
if (equalPos != std::string::npos) {
std::string key = token.substr(0, equalPos);
std::string value = token.substr(equalPos + 1);
if (!key.empty()) {
result[key] = value;
}
} else {
std::string key = token;
if (!key.empty()) {
result[key] = "";
}
}
}
return result;
}
int32_t ReplaceAndCount(std::string &str, const std::string &target, const std::string &replacement)
{
if (str.empty() || target.empty()) {
return 0;
}
int32_t count = 0;
size_t pos = 0;
while ((pos = str.find(target, pos)) != std::string::npos) {
str.replace(pos, target.length(), replacement);
pos += replacement.length();
count++;
}
return count;
}
bool ConvertStringToInt32(const std::string &context, int32_t &value)
{
if (context.empty()) {
return false;
}
std::regex pattern(R"(^([1-9]\d{0,9})$)");
if (!std::regex_match(context, pattern)) {
return false;
}
char *endptr;
errno = 0;
int64_t tollRes = strtoll(context.c_str(), &endptr, BASE_DECIMAL);
if (errno != 0 || endptr != context.c_str() + context.size()) {
return false;
}
if (tollRes <= 0 || tollRes >= INT32_MAX) {
return false;
}
value = static_cast<int32_t>(tollRes);
LOGD("[L8:StringUtils] ConvertStringToInt32: <<< EXIT SUCCESS <<< value=%{public}d", value);
return true;
}
void IncreaseThreadPriority(const std::string &processName)
{
HiAudit::GetInstance().WriteStart("IncreaseThreadPriority", "processName=" + processName);
LOGD("IncreaseThreadPriority start");
std::unordered_map<std::string, std::string> mapPayLoad;
mapPayLoad["pid"] = std::to_string(getpid());
mapPayLoad[std::to_string(gettid())] = std::to_string(THREAD_QOS_HIGH_LEVEL);
mapPayLoad["bundleName"] = processName;
uint32_t type = OHOS::ResourceSchedule::ResType::RES_TYPE_THREAD_QOS_CHANGE;
OHOS::ResourceSchedule::ResSchedClient::GetInstance().ReportData(type, 0, mapPayLoad);
LOGD("IncreaseThreadPriority end");
HiAudit::GetInstance().WriteEnd("IncreaseThreadPriority", 0);
}
void DecreaseThreadPriority(const std::string &processName)
{
HiAudit::GetInstance().WriteStart("DecreaseThreadPriority", "processName=" + processName);
LOGD("DecreaseThreadPriority start");
std::unordered_map<std::string, std::string> mapPayLoad;
mapPayLoad["pid"] = std::to_string(getpid());
mapPayLoad[std::to_string(gettid())] = std::to_string(THREAD_QOS_LOW_LEVEL);
mapPayLoad["bundleName"] = processName;
uint32_t type = OHOS::ResourceSchedule::ResType::RES_TYPE_THREAD_QOS_CHANGE;
OHOS::ResourceSchedule::ResSchedClient::GetInstance().ReportData(type, 0, mapPayLoad);
LOGD("DecreaseThreadPriority end");
HiAudit::GetInstance().WriteEnd("DecreaseThreadPriority", 0);
}
std::string AnonymizePath(const std::string &path)
{
if (path.empty()) {
return path;
}
size_t lastSlash = path.find_last_of("/\\");
std::string dirPart;
std::string fileName;
if (lastSlash == std::string::npos) {
fileName = path;
} else {
dirPart = path.substr(0, lastSlash + 1);
fileName = path.substr(lastSlash + 1);
}
if (fileName.length() <= FIVE_CHARACTER) {
if (fileName.length() >= TWO_CHARACTER) {
size_t replaceCount = fileName.length() - TWO_CHARACTER;
fileName = fileName[0] + std::string(replaceCount, '*') + fileName[fileName.length() - 1];
} else {
}
} else {
size_t startPos = (fileName.length() - FOUR_CHARACTER) / 2;
fileName = fileName.substr(0, startPos) + "****" + fileName.substr(startPos + FOUR_CHARACTER);
}
return dirPart + fileName;
}
bool IsDlpPathValid(const std::string &dstPath)
{
std::string realPath;
if (!GetRealPath(dstPath, realPath)) {
LOGE("IsDlpPathValid: realpath failed for %{public}s", dstPath.c_str());
return false;
}
if (realPath != dstPath) {
LOGE("IsDlpPathValid: realPath %{public}s differs from dstPath %{public}s", realPath.c_str(),
dstPath.c_str());
return false;
}
const std::string prefix = "/data/service/el1/public/dlp_credential_service/";
if (realPath.size() <= prefix.size()) {
return false;
}
if (realPath.compare(0, prefix.length(), prefix) != 0) {
LOGE("IsDlpPathValid: path %{public}s does not start with dlp prefix", realPath.c_str());
return false;
}
return true;
}
}
}