* Copyright (c) 2021 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.h"
#include <algorithm>
#include <cerrno>
#include <cstdint>
#include <cstdlib>
#include <dirent.h>
#include <fcntl.h>
#include <limits>
#include <linux/reboot.h>
#include <string>
#include <sys/reboot.h>
#include <sys/stat.h>
#include <sys/sendfile.h>
#include <sys/syscall.h>
#include <sys/time.h>
#include <unistd.h>
#include <vector>
#include "fs_manager/mount.h"
#include "init_reboot.h"
#include "log/dump.h"
#include "log/log.h"
#include "misc_info/misc_info.h"
#ifdef WITH_SELINUX
#include <policycoreutils.h>
#include "selinux/selinux.h"
#endif
#include "package/pkg_manager.h"
#include "parameter.h"
#include "securec.h"
#include "updater/updater_const.h"
#include "scope_guard.h"
namespace Updater {
using namespace Hpackage;
namespace Utils {
constexpr uint8_t SHIFT_RIGHT_FOUR_BITS = 4;
constexpr int MAX_TIME_SIZE = 20;
constexpr const char *PREFIX_PARTITION_NODE = "/dev/block/by-name/";
constexpr mode_t DEFAULT_DIR_MODE = 0775;
namespace {
void UpdateInfoInMisc(const std::string headInfo, const std::optional<int64_t> message, bool isRemove)
{
if (headInfo.empty()) {
return;
}
std::vector<std::string> args = Utils::ParseParams(0, nullptr);
struct UpdateMessage msg {};
if (!ReadUpdaterMiscMsg(msg)) {
LOG(ERROR) << "SetMessageToMisc read misc failed";
return;
}
(void)memset_s(msg.update, sizeof(msg.update), 0, sizeof(msg.update));
for (const auto& arg : args) {
if (arg.find(headInfo) == std::string::npos) {
if (strncat_s(msg.update, sizeof(msg.update), arg.c_str(), strlen(arg.c_str()) + 1) != EOK) {
LOG(ERROR) << "SetMessageToMisc strncat_s failed";
return;
}
if (strncat_s(msg.update, sizeof(msg.update), "\n", strlen("\n") + 1) != EOK) {
LOG(ERROR) << "SetMessageToMisc strncat_s failed";
return;
}
}
}
char buffer[128] {};
if (isRemove) {
LOG(INFO) << "remove --" << headInfo << " from misc";
} else if (!message.has_value()) {
if (snprintf_s(buffer, sizeof(buffer), sizeof(buffer) - 1, "--%s", headInfo.c_str()) == -1) {
LOG(ERROR) << "SetMessageToMisc snprintf_s failed";
return;
}
} else {
if (snprintf_s(buffer, sizeof(buffer), sizeof(buffer) - 1, "--%s=%lld",
headInfo.c_str(), message.value()) == -1) {
LOG(ERROR) << "SetMessageToMisc snprintf_s failed";
return;
}
}
if (strncat_s(msg.update, sizeof(msg.update), buffer, strlen(buffer) + 1) != EOK) {
LOG(ERROR) << "SetMessageToMisc strncat_s failed";
return;
}
if (WriteUpdaterMiscMsg(msg) != true) {
LOG(ERROR) << "Write command to misc failed.";
}
}
}
void SaveLogs()
{
std::string updaterLogPath = std::string(UPDATER_LOG);
std::string stageLogPath = std::string(UPDATER_STAGE_LOG);
STAGE(UPDATE_STAGE_SUCCESS) << "PostUpdaterLog";
bool ret = CopyUpdaterLogs(TMP_LOG, updaterLogPath);
if (!ret) {
LOG(ERROR) << "Copy updater log failed!";
}
mode_t mode = 0660;
#ifndef __WIN32
SetFileAttributes(updaterLogPath, USER_UPDATE_AUTHORITY, GROUP_UPDATE_AUTHORITY, mode);
#endif
STAGE(UPDATE_STAGE_SUCCESS) << "PostUpdater";
ret = CopyUpdaterLogs(TMP_STAGE_LOG, stageLogPath);
chmod(stageLogPath.c_str(), mode);
if (!ret) {
LOG(ERROR) << "Copy stage log failed!";
}
}
int32_t DeleteFile(const std::string& filename)
{
if (filename.empty()) {
LOG(ERROR) << "Invalid filename";
return -1;
}
if (unlink(filename.c_str()) == -1 && errno != ENOENT) {
LOG(ERROR) << "unlink " << filename << " failed";
return -1;
}
return 0;
}
std::vector<std::string> SplitString(const std::string &str, const std::string del)
{
std::vector<std::string> result;
size_t found = std::string::npos;
size_t start = 0;
while (true) {
found = str.find_first_of(del, start);
result.push_back(str.substr(start, found - start));
if (found == std::string::npos) {
break;
}
start = found + 1;
}
return result;
}
std::string Trim(const std::string &str)
{
if (str.empty()) {
LOG(ERROR) << "str is empty";
return str;
}
size_t start = 0;
size_t end = str.size() - 1;
while (start < str.size()) {
if (!isspace(str[start])) {
break;
}
start++;
}
while (start < end) {
if (!isspace(str[end])) {
break;
}
end--;
}
if (end < start) {
return "";
}
return str.substr(start, end - start + 1);
}
std::string ConvertSha256Hex(const uint8_t* shaDigest, size_t length)
{
const std::string hexChars = "0123456789abcdef";
std::string haxSha256 = "";
unsigned int c;
for (size_t i = 0; i < length; ++i) {
auto d = shaDigest[i];
c = (d >> SHIFT_RIGHT_FOUR_BITS) & 0xf;
haxSha256.push_back(hexChars[c]);
haxSha256.push_back(hexChars[d & 0xf]);
}
return haxSha256;
}
bool SetRebootMisc(const std::string& rebootTarget, const std::string &extData, struct UpdateMessage &msg)
{
int result = 0;
if (rebootTarget == "updater" && strcmp(msg.command, "boot_updater") != 0) {
result = strcpy_s(msg.command, MAX_COMMAND_SIZE, "boot_updater");
} else if (rebootTarget == "flashd" && strcmp(msg.command, "flashd") != 0) {
result = strcpy_s(msg.command, MAX_COMMAND_SIZE, "boot_flash");
} else if (rebootTarget == "bootloader" && strcmp(msg.command, "boot_loader") != 0) {
result = strcpy_s(msg.command, MAX_COMMAND_SIZE, "boot_loader");
}
if (result != EOK) {
LOG(ERROR) << "reboot set misc strcpy failed";
return false;
}
msg.command[MAX_COMMAND_SIZE - 1] = 0;
if (extData.empty()) {
(void)memset_s(msg.update, sizeof(msg.update), 0, sizeof(msg.update));
return true;
}
if (strcpy_s(msg.update, sizeof(msg.update) - 1, extData.c_str()) != EOK) {
LOG(ERROR) << "failed to copy update";
return false;
}
msg.update[sizeof(msg.update) - 1] = 0;
return true;
}
void UmountUserdata()
{
#ifndef UPDATER_UT
LOG(INFO) << "Umount data start";
if (UmountForPath("/data") != 0) {
LOG(WARNING) << "Umount /data fail";
}
if (UmountForPath(INTERNAL_DATA_PATH) != 0) {
LOG(WARNING) << "Umount " << INTERNAL_DATA_PATH << " fail";
}
#endif
}
void UpdaterDoReboot(const std::string& rebootTarget, const std::string &rebootReason, const std::string &extData)
{
LOG(INFO) << ", rebootTarget: " << rebootTarget;
LOG(INFO) << ", rebootReason: " << rebootReason;
LoadFstab();
struct UpdateMessage msg = {};
if (rebootTarget.empty()) {
if (WriteUpdaterMiscMsg(msg) != true) {
LOG(INFO) << "UpdaterDoReboot: WriteUpdaterMessage empty error";
}
} else {
if (!ReadUpdaterMiscMsg(msg)) {
LOG(ERROR) << "UpdaterDoReboot read misc failed";
}
if (!SetRebootMisc(rebootTarget, extData, msg)) {
LOG(ERROR) << "UpdaterDoReboot set misc failed";
}
if (!WriteUpdaterMiscMsg(msg)) {
LOG(INFO) << "UpdaterDoReboot: WriteUpdaterMiscMsg error";
}
}
UmountUserdata();
sync();
#ifndef UPDATER_UT
DoRebootExt(rebootTarget.c_str(), rebootReason.c_str());
while (true) {
pause();
}
#else
return;
#endif
}
void DoShutdown(const std::string &shutdownReason)
{
UpdateMessage msg = {};
if (!WriteUpdaterMiscMsg(msg)) {
LOG(ERROR) << "DoShutdown: WriteUpdaterMessage empty error";
return;
}
UmountUserdata();
sync();
DoRebootExt("shutdown", shutdownReason.c_str());
}
std::string GetCertName()
{
#ifndef UPDATER_UT
static std::string signingCertName = "/etc/certificate/signing_cert.crt";
#ifdef SIGN_ON_SERVER
signingCertName = Updater::Utils::ON_SERVER;
#endif
#else
static std::string signingCertName = "/data/updater/src/signing_cert.crt";
#endif
return signingCertName;
}
bool WriteFully(int fd, const uint8_t *data, size_t size)
{
ssize_t written = 0;
size_t rest = size;
while (rest > 0) {
do {
written = write(fd, data, rest);
} while (written < 0 && errno == EINTR);
if (written < 0) {
return false;
}
data += written;
rest -= static_cast<size_t>(written);
if (rest != 0) {
LOG(INFO) << "totalSize = " << size << ", rest = " << rest;
}
}
return true;
}
bool ReadFully(int fd, void *data, size_t size)
{
UPDATER_INIT_RECORD;
auto p = reinterpret_cast<uint8_t *>(data);
size_t remaining = size;
while (remaining > 0) {
ssize_t sread = read(fd, p, remaining);
if (sread == -1) {
LOG(ERROR) << "read failed: " << strerror(errno);
UPDATER_LAST_WORD("read failed: ", strerror(errno));
return false;
}
if (sread == 0) {
LOG(ERROR) << "read reached unexpected EOF " << strerror(errno);
UPDATER_LAST_WORD("read reached unexpected EOF: ", strerror(errno));
return false;
}
p += sread;
remaining -= static_cast<size_t>(sread);
}
return true;
}
std::string ReadFile(const std::string &path)
{
char realPath[PATH_MAX] = {0};
if (realpath(path.c_str(), realPath) == nullptr) {
LOG(ERROR) << "realpath failed " << path;
return "";
}
std::string readFilePath(realPath);
std::ifstream file(readFilePath);
if (!file.is_open()) {
LOG(ERROR) << "Failed to open file: " << readFilePath;
return "";
}
std::string content((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>());
return content;
}
bool WriteStringToFile(int fd, const std::string& content)
{
const char *p = content.data();
size_t remaining = content.size();
while (remaining > 0) {
ssize_t n = write(fd, p, remaining);
if (n == -1) {
return false;
}
p += n;
remaining -= static_cast<size_t>(n);
}
return true;
}
void SyncFile(const std::string &dst)
{
int fd = open(dst.c_str(), O_RDWR);
if (fd < 0) {
LOG(ERROR) << "open " << dst << " failed! err " << strerror(errno);
return;
}
fdsan_exchange_owner_tag(fd, 0, FDSAN_UPDATER_TAG);
fsync(fd);
fdsan_close_with_tag(fd, FDSAN_UPDATER_TAG);
}
bool CopyFile(const std::string &src, const std::string &dest, bool isAppend)
{
char realPath[PATH_MAX + 1] = {0};
if (realpath(src.c_str(), realPath) == nullptr) {
LOG(ERROR) << src << " get realpath fail";
return false;
}
std::ios_base::openmode mode = isAppend ? std::ios::app | std::ios::out : std::ios_base::out;
std::ifstream fin(realPath);
std::ofstream fout(dest, mode);
if (!fin.is_open() || !fout.is_open()) {
return false;
}
fout << fin.rdbuf();
if (fout.fail()) {
fout.clear();
return false;
}
fout.flush();
fout.close();
SyncFile(dest);
return true;
}
bool CopyFileBySendFile(const std::string &srcFile, const std::string &destFile)
{
char realPath[PATH_MAX + 1] = {0};
if (realpath(srcFile.c_str(), realPath) == nullptr) {
LOG(ERROR) << srcFile << " get realpath fail";
return false;
}
int32_t source = open(realPath, O_RDONLY);
if (source == -1) {
LOG(ERROR) << srcFile << ", Open source file fail, errno: " << errno;
return false;
}
fdsan_exchange_owner_tag(source, 0, FDSAN_UPDATER_TAG);
(void)memset_s(realPath, PATH_MAX + 1, 0, PATH_MAX + 1);
std::string::size_type pos = destFile.find_last_of("/");
if (pos == std::string::npos || realpath(destFile.substr(0, pos).c_str(), realPath) == nullptr) {
LOG(ERROR) << destFile << " dest file dir realpath fail";
fdsan_close_with_tag(source, FDSAN_UPDATER_TAG);
return false;
}
std::string destRealPath = std::string(realPath) + "/" + destFile.substr(pos + 1);
int32_t dest = open(destRealPath.c_str(), O_WRONLY | O_CREAT, 0644);
if (dest == -1) {
LOG(ERROR) << destRealPath << ", Open dest file fail, errno: " << errno;
fdsan_close_with_tag(source, FDSAN_UPDATER_TAG);
return false;
}
fdsan_exchange_owner_tag(dest, 0, FDSAN_UPDATER_TAG);
struct stat fst{};
bool result = false;
if (fstat(source, &fst) == 0) {
if (sendfile(dest, source, nullptr, fst.st_size) != -1) {
result = true;
}
}
fdsan_close_with_tag(source, FDSAN_UPDATER_TAG);
fdsan_close_with_tag(dest, FDSAN_UPDATER_TAG);
close(dest);
return result;
}
bool CopyDir(const std::string &srcPath, const std::string &dstPath)
{
DIR *dir = opendir(srcPath.c_str());
if (dir == nullptr) {
LOG(ERROR) << "opendir failed, path: " << srcPath.c_str() << ", err: " << strerror(errno);
return false;
}
ON_SCOPE_EXIT(closedir) {
closedir(dir);
};
bool existFlag = (access(dstPath.c_str(), 0) == 0);
if ((!existFlag) && (mkdir(dstPath.c_str(), DEFAULT_DIR_MODE) != 0)) {
LOG(ERROR) << "mkdir failed, path: " << dstPath.c_str() << ", err: " << strerror(errno);
return false;
}
ON_SCOPE_EXIT(rmdir) {
if (!existFlag) {
remove(dstPath.c_str());
}
};
dirent *dirent = nullptr;
while ((dirent = readdir(dir)) != nullptr) {
if (strcmp(dirent->d_name, ".") == 0 || strcmp(dirent->d_name, "..") == 0) {
continue;
}
if (dirent->d_type == DT_DIR) {
std::string fullSourcePath = srcPath + dirent->d_name + "/";
std::string fullDestPath = dstPath + dirent->d_name + "/";
if (!CopyDir(fullSourcePath, fullDestPath)) {
LOG(ERROR) << "copydir failed, fullSourcePath: " << fullSourcePath.c_str()
<< ", fullDestPath: " << fullDestPath.c_str();
return false;
}
} else {
std::string fullSourcePath = srcPath + dirent->d_name;
std::string fullDestPath = dstPath + dirent->d_name;
if (!CopyFile(fullSourcePath, fullDestPath)) {
LOG(ERROR) << "copyfile failed, fullSourcePath: " << fullSourcePath.c_str()
<< ", fullDestPath: " << fullDestPath.c_str();
return false;
}
}
}
CANCEL_SCOPE_EXIT_GUARD(rmdir);
return true;
}
std::string GetLocalBoardId()
{
return "HI3516";
}
int32_t CreateCompressLogFile(const std::string &pkgName, std::vector<std::pair<std::string, ZipFileInfo>> &files)
{
PkgInfo pkgInfo;
pkgInfo.signMethod = PKG_SIGN_METHOD_NONE;
pkgInfo.digestMethod = PKG_SIGN_METHOD_NONE;
pkgInfo.pkgType = PKG_PACK_TYPE_ZIP;
PkgManager::PkgManagerPtr pkgManager = PkgManager::CreatePackageInstance();
if (pkgManager == nullptr) {
LOG(ERROR) << "pkgManager is nullptr";
return -1;
}
int32_t ret = pkgManager->CreatePackage(pkgName, GetCertName(), &pkgInfo, files);
PkgManager::ReleasePackageInstance(pkgManager);
return ret;
}
void CompressFiles(std::vector<std::string> &files, const std::string &zipFile)
{
(void)DeleteFile(zipFile);
std::vector<std::pair<std::string, ZipFileInfo>> zipFiles {};
for (auto path : files) {
ZipFileInfo file {};
file.fileInfo.identity = path.substr(path.find_last_of("/") + 1);
file.fileInfo.packMethod = PKG_COMPRESS_METHOD_ZIP;
file.fileInfo.digestMethod = PKG_DIGEST_TYPE_CRC;
zipFiles.push_back(std::pair<std::string, ZipFileInfo>(path, file));
}
int32_t ret = CreateCompressLogFile(zipFile, zipFiles);
if (ret != 0) {
LOG(WARNING) << "CompressFiles failed: " << zipFile;
return;
}
mode_t mode = 0660;
#ifndef __WIN32
SetFileAttributes(zipFile, USER_UPDATE_AUTHORITY, GROUP_SYS_AUTHORITY, mode);
#endif
}
void CompressLogs(const std::string &logName)
{
std::vector<std::pair<std::string, ZipFileInfo>> files;
std::vector<std::string> testFileNames;
std::string realName = logName.substr(logName.find_last_of("/") + 1);
std::string logPath = logName.substr(0, logName.find_last_of("/"));
testFileNames.push_back(realName);
for (auto name : testFileNames) {
ZipFileInfo file;
file.fileInfo.identity = name;
file.fileInfo.packMethod = PKG_COMPRESS_METHOD_ZIP;
file.fileInfo.digestMethod = PKG_DIGEST_TYPE_CRC;
std::string fileName = logName;
files.push_back(std::pair<std::string, ZipFileInfo>(fileName, file));
}
char realTime[MAX_TIME_SIZE] = {0};
auto sysTime = std::chrono::system_clock::now();
auto currentTime = std::chrono::system_clock::to_time_t(sysTime);
struct tm *localTime = std::localtime(¤tTime);
if (localTime != nullptr) {
std::strftime(realTime, sizeof(realTime), "%Y%m%d%H%M%S", localTime);
}
char pkgName[MAX_LOG_NAME_SIZE];
if (snprintf_s(pkgName, MAX_LOG_NAME_SIZE, MAX_LOG_NAME_SIZE - 1,
"%s/%s_%s.zip", logPath.c_str(), realName.c_str(), realTime) == -1) {
return;
}
int32_t ret = CreateCompressLogFile(pkgName, files);
if (ret != 0) {
LOG(WARNING) << "CompressLogs failed";
return;
}
mode_t mode = 0660;
#ifndef __WIN32
SetFileAttributes(pkgName, USER_UPDATE_AUTHORITY, GROUP_SYS_AUTHORITY, mode);
#endif
sync();
if (access(pkgName, 0) != 0) {
LOG(ERROR) << "Failed to create zipfile: " << pkgName;
} else {
(void)DeleteFile(logName);
}
}
size_t GetFileSize(const std::string &filePath)
{
size_t ret = 0;
std::ifstream ifs(filePath, std::ios::binary | std::ios::in);
if (ifs.is_open()) {
ifs.seekg(0, std::ios::end);
ret = static_cast<size_t>(ifs.tellg());
}
return ret;
}
bool RestoreconPath(const std::string &path)
{
if (MountForPath(path) != 0) {
LOG(ERROR) << "MountForPath " << path << " failed!";
return false;
}
#ifdef WITH_SELINUX
if (RestoreconRecurse(path.c_str()) == -1) {
LOG(WARNING) << "restore " << path << " failed";
}
#endif
if (UmountForPath(path) != 0) {
LOG(WARNING) << "UmountForPath " << path << " failed!";
}
return true;
}
bool CopyUpdaterLogs(const std::string &sLog, const std::string &dLog)
{
std::size_t found = dLog.find_last_of("/");
if (found == std::string::npos) {
LOG(ERROR) << "Dest filePath error";
return false;
}
std::string destPath = dLog.substr(0, found);
if (MountForPath(destPath) != 0) {
LOG(WARNING) << "MountForPath /data/log failed!";
}
if (access(destPath.c_str(), 0) != 0) {
if (MkdirRecursive(destPath.c_str(), S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) != 0) {
LOG(ERROR) << "MkdirRecursive error!";
return false;
}
#ifdef WITH_SELINUX
RestoreconRecurse(UPDATER_PATH);
#endif
}
if (Utils::GetFileSize(sLog) > MAX_LOG_SIZE) {
LOG(ERROR) << "Size bigger for" << sLog;
STAGE(UPDATE_STAGE_FAIL) << "Log file error, unable to copy";
return false;
}
while (Utils::GetFileSize(sLog) + GetDirSizeForFile(dLog) > MAX_LOG_DIR_SIZE) {
LOG(ERROR) << "Size bigger for" << sLog;
STAGE(UPDATE_STAGE_FAIL) << "sLog and dLog file error, unable to copy";
if (DeleteOldFile(destPath) != true) {
break;
}
}
if (!CopyFile(sLog, dLog, true)) {
LOG(ERROR) << "copy log file failed.";
return false;
}
if (GetFileSize(dLog) >= MAX_LOG_SIZE) {
LOG(INFO) << "log size greater than 5M!";
CompressLogs(dLog);
}
sync();
return true;
}
bool CheckResultFail()
{
std::ifstream ifs;
const std::string resultPath = std::string(UPDATER_PATH) + "/" + std::string(UPDATER_RESULT_FILE);
ifs.open(resultPath, std::ios::in);
std::string buff;
while (ifs.is_open() && getline(ifs, buff)) {
if (buff.find("fail|") != std::string::npos) {
ifs.close();
return true;
}
}
LOG(ERROR) << "open result file failed";
return false;
}
void WriteDumpResult(const std::string &result, const std::string &dirPath, const std::string &fileName)
{
if (access(dirPath.c_str(), 0) != 0) {
if (MkdirRecursive(dirPath.c_str(), 0755) != 0) {
LOG(ERROR) << "MkdirRecursive " << dirPath << " error:" << errno;
return;
}
}
const std::string file = dirPath + "/" + fileName;
FILE *fp = fopen(file.c_str(), "w+");
if (fp == nullptr) {
LOG(ERROR) << "open result file(" << file << ") failed, err:" << errno;
return;
}
char buf[MAX_RESULT_BUFF_SIZE] = "Pass\n";
if (sprintf_s(buf, MAX_RESULT_BUFF_SIZE - 1, "%s\n", result.c_str()) < 0) {
LOG(WARNING) << "sprintf status fialed";
}
if (fwrite(buf, 1, strlen(buf) + 1, fp) <= 0) {
LOG(WARNING) << "write result file(" << file << ") failed, err:" << errno;
}
if (fclose(fp) != 0) {
LOG(WARNING) << "close result file(" << file << ") failed, err:" << errno;
}
(void)chown(file.c_str(), USER_ROOT_AUTHORITY, GROUP_UPDATE_AUTHORITY);
(void)chmod(file.c_str(), 0660);
}
long long int GetDirSize(const std::string &folderPath)
{
DIR* dir = opendir(folderPath.c_str());
if (dir == nullptr) {
LOG(ERROR) << "Failed to open folder: " << folderPath << std::endl;
return 0;
}
struct dirent* entry;
long long int totalSize = 0;
while ((entry = readdir(dir)) != nullptr) {
std::string fileName = entry->d_name;
std::string filePath = folderPath + "/" + fileName;
struct stat fileStat;
if (stat(filePath.c_str(), &fileStat) != 0) {
LOG(ERROR) << "Failed to get file status: " << filePath << std::endl;
continue;
}
if (S_ISDIR(fileStat.st_mode)) {
if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) {
continue;
}
std::string subFolderPath = filePath;
totalSize += GetDirSize(subFolderPath);
} else {
totalSize += fileStat.st_size;
}
}
closedir(dir);
return totalSize;
}
long long int GetDirSizeForFile(const std::string &filePath)
{
std::size_t found = filePath.find_last_of("/");
if (found == std::string::npos) {
LOG(ERROR) << "filePath error";
return -1;
}
return GetDirSize(filePath.substr(0, found));
}
bool DeleteOldFile(const std::string folderPath)
{
DIR* dir = opendir(folderPath.c_str());
if (dir == nullptr) {
LOG(ERROR) << "Failed to open folder: " << folderPath << std::endl;
return false;
}
struct dirent* entry;
std::string oldestFilePath = "";
time_t oldestFileTime = std::numeric_limits<time_t>::max();
while ((entry = readdir(dir)) != nullptr) {
std::string fileName = entry->d_name;
std::string filePath = folderPath + "/" + fileName;
struct stat fileStat;
if (stat(filePath.c_str(), &fileStat) != 0) {
LOG(ERROR) << "Failed to get file status: " << filePath;
continue;
}
if (fileName == "." || fileName == "..") {
continue;
}
if (fileStat.st_mtime < oldestFileTime) {
oldestFileTime = fileStat.st_mtime;
oldestFilePath = filePath;
}
}
closedir(dir);
if (oldestFilePath.empty()) {
LOG(ERROR) << "Unable to delete file";
return false;
}
size_t size = GetFileSize(oldestFilePath);
if (remove(oldestFilePath.c_str()) != 0) {
LOG(ERROR) << "Failed to delete file: " << oldestFilePath;
return false;
}
LOG(INFO) << "Delete old file: " << oldestFilePath << " size: " << size;
return true;
}
std::vector<std::string> ParseParams(int argc, char **argv)
{
struct UpdateMessage boot {};
if (!ReadUpdaterMiscMsg(boot)) {
LOG(ERROR) << "ReadUpdaterMessage MISC_FILE failed!";
}
if (boot.update[0] == '\0' && !access(COMMAND_FILE, 0)) {
if (!ReadUpdaterMessage(COMMAND_FILE, boot)) {
LOG(ERROR) << "ReadUpdaterMessage COMMAND_FILE failed!";
}
}
boot.update[sizeof(boot.update) - 1] = '\0';
std::vector<std::string> parseParams = Utils::SplitString(boot.update, "\n");
if (argc != 0 && argv != nullptr) {
parseParams.insert(parseParams.begin(), argv, argv + argc);
}
return parseParams;
}
std::string TrimUpdateMode(const std::string &mode)
{
std::string optEqual = "=";
std::string modePrefix = "--";
size_t optPos = mode.size();
size_t prefixPos = 0;
if (mode.empty() || mode == "") {
return "";
}
if (mode.find(optEqual) != std::string::npos) {
optPos = mode.find(optEqual);
}
if (mode.find(modePrefix) != std::string::npos) {
prefixPos = mode.find(modePrefix) + modePrefix.size();
}
if (optPos < prefixPos) {
return mode;
}
return mode.substr(prefixPos, optPos - prefixPos);
}
bool CheckUpdateMode(const std::string &mode)
{
std::vector<std::string> args = ParseParams(0, nullptr);
for (const auto &arg : args) {
if (TrimUpdateMode(arg) == mode) {
return true;
}
}
return false;
}
std::string DurationToString(std::vector<std::chrono::duration<double>> &durations, std::size_t pkgPosition,
int precision)
{
if (pkgPosition >= durations.size()) {
LOG(ERROR) << "pkg position is " << pkgPosition << ", duration's size is " << durations.size();
return "0";
}
std::ostringstream oss;
oss << std::fixed << std::setprecision(precision) << durations[pkgPosition].count();
return oss.str();
}
std::string GetRealPath(const std::string &path)
{
char realPath[PATH_MAX + 1] = {0};
auto ret = realpath(path.c_str(), realPath);
return (ret == nullptr) ? "" : ret;
}
std::string GetPartitionRealPath(const std::string &name)
{
return GetRealPath(PREFIX_PARTITION_NODE + name);
}
void SetMessageToMisc(const std::string &miscCmd, const int message, const std::string headInfo)
{
if (headInfo.empty()) {
return;
}
std::vector<std::string> args = ParseParams(0, nullptr);
struct UpdateMessage msg {};
if (!ReadUpdaterMiscMsg(msg)) {
LOG(ERROR) << "SetMessageToMisc read misc failed";
return;
}
(void)memset_s(msg.command, sizeof(msg.command), 0, sizeof(msg.command));
if (strncpy_s(msg.command, sizeof(msg.command), miscCmd.c_str(), miscCmd.size() + 1) != EOK) {
LOG(ERROR) << "SetMessageToMisc strncpy_s failed";
return;
}
(void)memset_s(msg.update, sizeof(msg.update), 0, sizeof(msg.update));
for (const auto& arg : args) {
if (arg.find(headInfo) == std::string::npos) {
if (strncat_s(msg.update, sizeof(msg.update), arg.c_str(), strlen(arg.c_str()) + 1) != EOK) {
LOG(ERROR) << "SetMessageToMisc strncat_s failed";
return;
}
if (strncat_s(msg.update, sizeof(msg.update), "\n", strlen("\n") + 1) != EOK) {
LOG(ERROR) << "SetMessageToMisc strncat_s failed";
return;
}
}
}
char buffer[128] {};
if (headInfo == "sdcard_update") {
if (snprintf_s(buffer, sizeof(buffer), sizeof(buffer) - 1, "--%s", headInfo.c_str()) == -1) {
LOG(ERROR) << "SetMessageToMisc snprintf_s failed";
return;
}
} else {
if (snprintf_s(buffer, sizeof(buffer), sizeof(buffer) - 1, "--%s=%d", headInfo.c_str(), message) == -1) {
LOG(ERROR) << "SetMessageToMisc snprintf_s failed";
return;
}
}
if (strncat_s(msg.update, sizeof(msg.update), buffer, strlen(buffer) + 1) != EOK) {
LOG(ERROR) << "SetMessageToMisc strncat_s failed";
return;
}
if (WriteUpdaterMiscMsg(msg) != true) {
LOG(ERROR) << "Write command to misc failed.";
}
}
void SetCmdToMisc(const std::string &miscCmd)
{
struct UpdateMessage msg {};
if (!ReadUpdaterMiscMsg(msg)) {
LOG(ERROR) << "SetMessageToMisc read misc failed";
return;
}
(void)memset_s(msg.command, sizeof(msg.command), 0, sizeof(msg.command));
if (strncpy_s(msg.command, sizeof(msg.command), miscCmd.c_str(), miscCmd.size() + 1) != EOK) {
LOG(ERROR) << "SetMessageToMisc strncpy_s failed";
return;
}
if (WriteUpdaterMiscMsg(msg) != true) {
LOG(ERROR) << "Write command to misc failed.";
}
}
void AddUpdateInfoToMisc(const std::string headInfo, const std::optional<int64_t> message)
{
UpdateInfoInMisc(headInfo, message, false);
}
void RemoveUpdateInfoFromMisc(const std::string &headInfo)
{
UpdateInfoInMisc(headInfo, std::nullopt, true);
}
void SetFaultInfoToMisc(const std::string &faultInfo)
{
struct UpdateMessage msg {};
if (!ReadUpdaterMiscMsg(msg)) {
LOG(ERROR) << "SetMessageToMisc read misc failed";
return;
}
(void)memset_s(msg.faultinfo, sizeof(msg.faultinfo), 0, sizeof(msg.faultinfo));
if (strncpy_s(msg.faultinfo, sizeof(msg.faultinfo), faultInfo.c_str(), faultInfo.size() + 1) != EOK) {
LOG(ERROR) << "SetMessageToMisc strncpy_s failed";
return;
}
if (WriteUpdaterMiscMsg(msg) != true) {
LOG(ERROR) << "Write fault info to misc failed.";
}
}
std::string GetFaultInfo(void)
{
struct UpdateMessage msg = {};
if (!ReadUpdaterMiscMsg(msg)) {
LOG(ERROR) << "read misc data failed";
return "";
}
msg.faultinfo[MAX_FAULTINFO_SIZE - 1] = '\0';
return msg.faultinfo;
}
bool CheckFaultInfo(const std::string &faultInfo)
{
return strcmp(GetFaultInfo().c_str(), faultInfo.c_str()) == 0;
}
void GetTagValInStr(const std::string &str, const std::string &tag, std::string &val)
{
if (str.find(tag + "=") != std::string::npos) {
val = str.substr(str.find("=") + 1, str.size() - str.find("="));
}
}
bool IsValidHexStr(const std::string &str)
{
for (const auto &ch : str) {
if (isxdigit(ch) == 0) {
return false;
}
}
return true;
}
void TrimString(std::string &str)
{
auto pos = str.find_last_not_of("\r\n");
if (pos != std::string::npos) {
str.erase(pos + 1, str.size() - pos);
}
}
bool IsEsDevice()
{
char deviceType[PARAM_SIZE + 1] = {0};
if (GetParameter("ohos.boot.chiptype", "", deviceType, sizeof(deviceType) - 1) <= 0) {
LOG(ERROR) << "get device type failed";
return false;
}
LOG(INFO) << "device type is " << deviceType;
if (strstr(deviceType, "_es") == nullptr) {
return false;
}
return true;
}
bool ConvertToUnsignedLongLong(const std::string &str, uint64_t &value)
{
char *endPtr = nullptr;
errno = 0;
value = std::strtoull(str.c_str(), &endPtr, 0);
#ifndef UPDATER_UT
if (endPtr == str.c_str() || (endPtr != nullptr && *endPtr != '\0') || errno == ERANGE) {
LOG(ERROR) << "Convert string to uint64_t failed, str " << str << " converted to value " << value;
return false;
}
#endif
return true;
}
bool ConvertToLongLong(const std::string &str, int64_t &value)
{
char *endPtr = nullptr;
errno = 0;
value = std::strtoll(str.c_str(), &endPtr, 10);
#ifndef UPDATER_UT
if (endPtr == str.c_str() || (endPtr != nullptr && *endPtr != '\0') || errno == ERANGE) {
LOG(ERROR) << "Convert string to int64_t failed, str " << str << " converted to value " << value;
return false;
}
#endif
return true;
}
bool ConvertToLong(const std::string &str, int32_t &value)
{
char *endPtr = nullptr;
errno = 0;
value = std::strtol(str.c_str(), &endPtr, 10);
#ifndef UPDATER_UT
if (endPtr == str.c_str() || (endPtr != nullptr && *endPtr != '\0') || errno == ERANGE) {
LOG(ERROR) << "Convert string to int32_t failed, str " << str << " converted to value " << value;
return false;
}
#endif
return true;
}
bool ConvertToUnsignedLong(const std::string &str, uint32_t &value, int base)
{
char *endPtr = nullptr;
errno = 0;
value = std::strtoul(str.c_str(), &endPtr, base);
#ifndef UPDATER_UT
if (endPtr == str.c_str() || (endPtr != nullptr && *endPtr != '\0') || errno == ERANGE) {
LOG(ERROR) << "Convert string to uint32_t failed, str " << str << " converted to value " << value;
return false;
}
#endif
return true;
}
bool ConvertToDouble(const std::string &str, double &value)
{
char *endPtr = nullptr;
errno = 0;
value = std::strtod(str.c_str(), &endPtr);
#ifndef UPDATER_UT
if (endPtr == str.c_str() || (endPtr != nullptr && *endPtr != '\0') || errno == ERANGE) {
LOG(ERROR) << "Convert string to double failed, str " << str << " converted to value " << value;
return false;
}
#endif
return true;
}
bool ConvertToFloat(const std::string &str, float &value)
{
char *endPtr = nullptr;
errno = 0;
value = std::strtof(str.c_str(), &endPtr);
#ifndef UPDATER_UT
if (endPtr == str.c_str() || (endPtr != nullptr && *endPtr != '\0') || errno == ERANGE) {
LOG(ERROR) << "Convert string to float failed, str " << str << " converted to value " << value;
return false;
}
#endif
return true;
}
bool IsAbDevice()
{
char partType[PARAM_SIZE + 1] = {0};
if (GetParameter("ohos.boot.update.partition_type", "", partType, sizeof(partType) - 1) <= 0) {
LOG(ERROR) << "get part partition failed";
return false;
}
LOG(INFO) << "device type is " << partType;
return std::string(partType) == "ab";
}
bool IsVabDevice()
{
char partType[PARAM_SIZE + 1] = {0};
if (GetParameter("ohos.boot.update.partition_type", "", partType, sizeof(partType) - 1) <= 0) {
LOG(ERROR) << "get part partition failed";
return false;
}
LOG(INFO) << "device type is " << partType;
return std::string(partType) == "vab";
}
bool SetUpdateSlot(int setSlot)
{
if (setSlot == -1) {
LOG(ERROR) << "setSlot is invalid value";
return false;
}
int tryNum = 3;
std::string setSlotStr = std::to_string(setSlot);
while (tryNum-- > 0) {
if (SetParameter("update.part.slot", setSlotStr.c_str()) != 0) {
LOG(ERROR) << "set update.part.slot fail";
continue;
}
if (GetUpdateSlot() == setSlot) {
return true;
}
if (tryNum != 0) {
sleep(1);
}
}
return false;
}
bool SetUpdateSuffix(std::string stringsuffix)
{
int tryNum = 3;
while (tryNum-- > 0) {
if (SetParameter("update.part.suffix", stringsuffix.c_str()) != 0) {
LOG(ERROR) << "set update.part.suffix fail";
continue;
}
if (strcmp(GetUpdateSuffix().c_str(), stringsuffix.c_str()) == 0) {
LOG(INFO) << "set update.part.suffix is " << stringsuffix;
return true;
}
if (tryNum != 0) {
sleep(1);
}
}
return false;
}
int GetUpdateSlot()
{
if (!IsVabDevice() && !Utils::IsAbDevice()) {
return NOT_AB;
}
char paramValue[PARAM_SIZE + 1] = {0};
if (GetParameter("update.part.slot", "", paramValue, sizeof(paramValue) - 1) <= 0) {
LOG(ERROR) << "get update.part.slot failed";
return -1;
}
int updateSlot = -1;
if (!Utils::ConvertToLong(paramValue, updateSlot)) {
LOG(ERROR) << "ConvertToLong failed";
return -1;
}
return updateSlot;
}
std::string GetUpdateSuffix()
{
char paramValue[PARAM_SIZE + 1] = {0};
if (GetParameter("update.part.suffix", "", paramValue, sizeof(paramValue) - 1) <= 0) {
LOG(ERROR) << "get update.part.suffix failed";
return std::string(paramValue);
}
LOG(INFO) << "GetUpdateSuffix = " << paramValue;
return std::string(paramValue);
}
std::string GetUpdateActiveSuffix()
{
char paramValue[PARAM_SIZE + 1] = {0};
if (GetParameter("update.part.suffix", "", paramValue, sizeof(paramValue) - 1) <= 0) {
LOG(ERROR) << "get update.part.suffix failed";
return std::string(paramValue);
}
if (strcmp(paramValue, "_a") == 0) {
strncpy_s(paramValue, sizeof(paramValue), "_b", sizeof("_b"));
} else if (strcmp(paramValue, "_b") == 0) {
strncpy_s(paramValue, sizeof(paramValue), "_a", sizeof("_a"));
} else {
LOG(ERROR) << "Unexpected suffix value: " << paramValue;
return std::string(paramValue);
}
LOG(INFO) << "GetUpdateActiveSuffix = " << paramValue;
return std::string(paramValue);
}
std::vector<pid_t> GetAllTids(pid_t pid)
{
std::vector<pid_t> tids;
tids.push_back(pid);
std::string pathName = std::string("/proc/").append(std::to_string(pid)).append("/task");
char tmpPath[PATH_MAX + 1] = {0};
if (realpath(pathName.c_str(), tmpPath) == nullptr || tmpPath[0] == '\0') {
LOG(ERROR) << "realpath fail pathName:" << pathName;
return tids;
}
DIR *dir = opendir(tmpPath);
if (dir == nullptr) {
LOG(ERROR) << "opendir fail pathName:" << pathName;
return tids;
}
struct dirent *de = nullptr;
while ((de = readdir(dir)) != nullptr) {
if (!(de->d_type & DT_DIR) || !isdigit(de->d_name[0])) {
continue;
}
int32_t temp = -1;
if (!Utils::ConvertToLong(de->d_name, temp)) {
LOG(ERROR) << "ConvertToLong failed";
continue;
}
pid_t tid = static_cast<pid_t>(temp);
if (tid > 0) {
tids.push_back(tid);
}
}
closedir(dir);
return tids;
}
std::string VectorToString(const std::vector<pid_t> &pids)
{
if (pids.empty()) {
return "";
}
std::ostringstream oss;
auto it = pids.begin();
oss << *it++;
while (it != pids.end()) {
oss << "," << *it++;
}
return oss.str();
}
bool GetBatteryCapacity(int &capacity)
{
const static std::vector<const char *> vec = {
"/sys/class/power_supply/battery/capacity",
"/sys/class/power_supply/Battery/capacity"
};
for (auto &it : vec) {
std::ifstream ifs { it };
if (!ifs.is_open()) {
continue;
}
int tmpCapacity = 0;
ifs >> tmpCapacity;
if ((ifs.fail()) || (ifs.bad())) {
continue;
}
capacity = tmpCapacity;
return true;
}
return false;
}
void GetLocalTime(tm &tm)
{
#ifndef DIFF_PATCH_SDK
struct timeval tv {};
gettimeofday(&tv, nullptr);
localtime_r(&tv.tv_sec, &tm);
#endif
}
void RecordBatteryLevel()
{
int capacity = 0;
if (!GetBatteryCapacity(capacity)) {
LOG(WARNING) << "Maybe no battery or error value";
return;
}
LOG(ERROR) << "battery level is " << capacity;
}
void PrintHex(const uint8_t *buffer, size_t length)
{
if (buffer == nullptr || length == 0) {
LOG(WARNING) << "invalid buffer or length";
return;
}
LOG(INFO) << "PrintHex length: " << length;
constexpr size_t numPerLine = 16;
constexpr size_t hexWidth = 2;
std::stringstream ss;
ss << std::hex << std::setfill('0');
for (size_t i = 0; i < length; i++) {
if ((i % numPerLine) == 0) {
ss << std::endl;
}
ss << std::setw(hexWidth) << static_cast<int>(buffer[i]) << " ";
}
LOG(INFO) << ss.str();
}
bool IsAiUpdateBinary()
{
pid_t pid = getpid();
std::string content;
std::string cmdPath = "/proc/" + std::to_string(pid) + "/cmdline";
if (Updater::Utils::ReadStringFromProcFile(cmdPath, content) &&
content.find("ai_binary") != std::string::npos) {
return true;
}
return false;
}
std::string GetDiffTempPath()
{
if (IsAiUpdateBinary()) {
return SINGULAR_UPDATE_PATH;
}
return UPDATER_PATH;
}
#ifndef __WIN32
void SetFileAttributes(const std::string& file, uid_t owner, gid_t group, mode_t mode)
{
#ifdef WITH_SELINUX
RestoreconRecurse(file.c_str());
#endif
if (chown(file.c_str(), USER_ROOT_AUTHORITY, GROUP_ROOT_AUTHORITY) != 0) {
LOG(ERROR) << "Chown failed: " << file << " " << USER_ROOT_AUTHORITY << "," << GROUP_ROOT_AUTHORITY;
}
if (chmod(file.c_str(), mode) != EOK) {
LOG(ERROR) << "chmod failed: " << file << " " << mode;
}
if (chown(file.c_str(), owner, group) != 0) {
LOG(ERROR) << "Chown failed: " << file << " " << owner << "," << group;
}
}
#endif
}
void __attribute__((weak)) InitLogger(const std::string &tag, bool isCompress)
{
if (Utils::IsUpdaterMode()) {
InitUpdaterLogger(tag, TMP_LOG, TMP_STAGE_LOG, TMP_ERROR_CODE_PATH);
} else {
InitUpdaterLogger(tag, SYS_INSTALLER_LOG, UPDATER_STAGE_LOG, ERROR_CODE_PATH);
}
}
}