* -------------------------------------------------------------------------
* This file is part of the MultimodalSDK project.
* Copyright (c) 2025 Huawei Technologies Co.,Ltd.
*
* MultimodalSDK is licensed under Mulan PSL v2.
* You can use this software according to the terms and conditions of the Mulan PSL v2.
* You may obtain a copy of Mulan PSL v2 at:
*
* http://license.coscl.org.cn/MulanPSL2
*
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
* See the Mulan PSL v2 for more details.
* -------------------------------------------------------------------------
* Description: File utils file.
* Author: ACC SDK
* Create: 2025
* History: NA
*/
#include "acc/utils/FileUtils.h"
#include <cstring>
#include <fstream>
#include <filesystem>
#include <sys/stat.h>
#include <unistd.h>
#include "acc/ErrorCode.h"
#include "acc/utils/LogImpl.h"
#include "acc/utils/ErrorCodeUtils.h"
namespace fs = std::filesystem;
namespace {
constexpr int FILE_PATH_MAX = 4096;
constexpr mode_t FILE_MODE = 0640;
}
namespace Acc {
bool CheckFileExtension(const char* path, const char* suffix)
{
if (!path || !suffix) {
LogError << "Invalid file path, input file path or target suffix is nullptr.";
return false;
}
const char* pos = path + strlen(path);
while (pos > path && *pos != '.') {
pos--;
}
if (pos == path) {
LogError << "Invalid file path, input file has no suffix.";
return false;
}
const char* a = pos + 1;
const char* b = suffix;
bool compareResult = true;
while (*a != '\0' && *b != '\0' && compareResult) {
compareResult = (tolower(static_cast<unsigned char>(*a)) == tolower(static_cast<unsigned char>(*b)));
if (compareResult) {
a++;
b++;
}
}
return (*a == '\0') && (*b == '\0');
}
bool CheckFileOpen(const char* path, std::ifstream& file)
{
fs::path normPath = fs::absolute(path).lexically_normal();
file.open(normPath, std::ios::binary | std::ios::ate);
if (!file.is_open()) {
LogError << "Check file open failed, file path is invalid";
return false;
}
return true;
}
bool CheckAndGetFileSize(std::ifstream& file, size_t maxFileSize, int64_t& fileSize)
{
std::streampos pos = file.tellg();
fileSize = (pos < 0) ? 0 : static_cast<int64_t>(pos);
file.seekg(0, std::ios::beg);
if (fileSize == 0) {
LogError << "File is empty or failed to get size.";
return false;
}
if (fileSize > static_cast<int64_t>(maxFileSize)) {
LogError << "File size exceeds maximum limit: " << fileSize << " bytes (max allowed: " << maxFileSize
<< " bytes).";
return false;
}
return true;
}
ErrorCode ReadFile(const char* path, std::vector<uint8_t>& data, size_t maxFileSize)
{
std::ifstream file;
if (!CheckFileOpen(path, file)) {
LogError << "The file is invalid, file open failed." << GetErrorInfo(ERR_OPEN_FILE_FAILURE);
return ERR_OPEN_FILE_FAILURE;
}
int64_t fileSize = 0;
if (!CheckAndGetFileSize(file, maxFileSize, fileSize)) {
LogError << "The file is invalid, file size out of range." << GetErrorInfo(ERR_INVALID_FILE_SIZE);
return ERR_INVALID_FILE_SIZE;
}
data.resize(fileSize);
if (!file.read(reinterpret_cast<char*>(data.data()), fileSize)) {
LogError << "Read file data failed." << GetErrorInfo(ERR_OPEN_FILE_FAILURE);
return ERR_OPEN_FILE_FAILURE;
}
return SUCCESS;
}
bool CheckFilePath(const std::string& path)
{
if (path.empty()) {
LogError << "Check file path failed. The path is empty.";
return false;
}
if (path.size() > FILE_PATH_MAX) {
LogError << "Check file path failed. The file path size: " << path.size()
<< " exceeds the maximum value: " << FILE_PATH_MAX << ".";
return false;
}
fs::path pathObj = fs::absolute(path);
if (!fs::exists(pathObj)) {
LogError << "Check file path failed. The file does not exist.";
return false;
}
if (fs::is_symlink(pathObj)) {
LogError << "Check file path failed. The file is a symlink.";
return false;
}
if (!fs::is_regular_file(pathObj)) {
LogError << "Check file path failed. The file is not a regular file.";
return false;
}
return true;
}
bool CheckFileOwner(const std::string& path)
{
struct stat fileStat;
if (stat(path.c_str(), &fileStat) != 0) {
LogError << "Check file owner failed, because get file stat failed.";
return false;
}
uid_t currentUid = getuid();
if (fileStat.st_uid != currentUid) {
LogError << "File owner mismatch. Process UID: " << currentUid << ", file UID: " << fileStat.st_uid << ".";
return false;
}
return true;
}
bool CheckFilePermission(const std::string& path, const mode_t mode)
{
struct stat buf;
int ret = stat(path.c_str(), &buf);
if (ret != 0) {
LogError << "Check File Permission failed, because get file stat failed.";
return false;
}
mode_t mask = 0700;
const int perPermWidth = 3;
std::vector<std::string> permMsg = {"Other group permission", "Owner group permission", "Owner permission"};
for (int i = perPermWidth; i > 0; i--) {
int curPerm = (buf.st_mode & mask) >> ((i - 1) * perPermWidth);
int maxPerm = (mode & mask) >> ((i - 1) * perPermWidth);
mask = mask >> perPermWidth;
if (curPerm > maxPerm) {
LogError << "Check " << permMsg[i - 1] << " failed: Current permission is " << curPerm
<< ", but required no greater than " << maxPerm << ".";
return false;
}
}
return true;
}
bool IsFileValid(const char* path)
{
if (!path) {
LogError << "The file is invalid, check file path failed.";
return false;
}
std::string pathStr(path);
if (!CheckFilePath(pathStr)) {
LogError << "The file is invalid, check file path failed.";
return false;
}
if (!CheckFileOwner(pathStr)) {
LogError << "The file is invalid, check file owner failed.";
return false;
}
if (!CheckFilePermission(pathStr, FILE_MODE)) {
LogError << "The file is invalid, check file permission failed.";
return false;
}
return true;
}
}