* Copyright (c) 2025 Huawei Technologies Co., Ltd.
* This program is free software, you can redistribute it and/or modify it under the terms and conditions of
* CANN Open Software License Agreement Version 2.0 (the "License").
* Please refer to the License for details. You may not use this file except in compliance with the License.
* 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 FITNESS FOR A PARTICULAR PURPOSE.
* See LICENSE in the root of the software repository for the full text of the License.
*/
#include "file_utils.h"
#include <cctype>
#include "log/adx_log.h"
#include "ide_os_type.h"
namespace Adx {
static const std::string MAPPING_FILE_NAME = "mapping.csv";
* @brief Write data to file
* @param [in] fileName: File path
* @param [in] data : Data to be written
* @param [in] len : The length of data that needs to be written
* @param [in] offset : File offset
* @return
* IDE_DAEMON_NONE_ERROR: Write data to file success
* Other: failed, check for IdeErrorCode
*/
IdeErrorT FileUtils::WriteFile(const std::string &fileName, IdeSendBuffT data, uint32_t len, int64_t offset)
{
IDE_CTRL_VALUE_FAILED(!fileName.empty(), return IDE_DAEMON_INVALID_PARAM_ERROR, "fileName is nullptr");
IDE_CTRL_VALUE_FAILED(data != nullptr, return IDE_DAEMON_INVALID_PARAM_ERROR, "data is nullptr");
std::string wrFile = fileName;
int32_t fd = mmOpen2(wrFile.c_str(), O_APPEND | M_RDWR | M_CREAT, M_IREAD | M_IWRITE);
if (fd < 0 && mmGetErrorCode() != ENAMETOOLONG) {
char errBuf[MAX_ERRSTR_LEN + 1] = {0};
IDE_LOGE("Open file %s failed , exception %s", fileName.c_str(),
mmGetErrorFormatMessage(mmGetErrorCode(), errBuf, MAX_ERRSTR_LEN));
return IDE_DAEMON_INVALID_PATH_ERROR;
} else if (fd < 0) {
std::string wrPath = GetFileDir(wrFile);
IDE_CTRL_VALUE_FAILED(!wrPath.empty(), return IDE_DAEMON_INVALID_PATH_ERROR, "file path error");
std::string hashValue = std::to_string(std::hash<std::string>{}(fileName));
if (AddMappingFileItem(fileName, hashValue) != IDE_DAEMON_NONE_ERROR) {
IDE_LOGE("add mapping item [ %s ] failed", hashValue.c_str());
}
wrFile = wrPath + OS_SPLIT_STR + hashValue;
IDE_LOGW("file name [ %s ] too long rename as [ %s ] and the mapping record in mapping.csv",
fileName.c_str(), wrFile.c_str());
fd = mmOpen2(wrFile.c_str(), O_APPEND | M_RDWR | M_CREAT, M_IREAD | M_IWRITE);
IDE_CTRL_VALUE_FAILED(fd >= 0, return IDE_DAEMON_INVALID_PATH_ERROR, "file path error");
}
if (offset != -1) {
long err = mmLseek(fd, offset, SEEK_SET);
if (err < 0) {
IDE_LOGE("invalid param, fd %d, offset %ld", fd, offset);
FILE_MMCLOSE_AND_SET_INVALID(fd);
return IDE_DAEMON_UNKNOW_ERROR;
}
}
IdeU8Pt wrData = static_cast<IdeU8Pt>(const_cast<IdeBuffT>(data));
uint32_t reserve = len;
do {
mmSsize_t writeLen = mmWrite(fd, wrData + (len - reserve), reserve);
if (writeLen < 0) {
char errBuf[MAX_ERRSTR_LEN + 1] = {0};
IDE_LOGE("Write failed, info: %s, write ratio: %u/%u",
mmGetErrorFormatMessage(mmGetErrorCode(), errBuf, MAX_ERRSTR_LEN), len - reserve, len);
FILE_MMCLOSE_AND_SET_INVALID(fd);
return IDE_DAEMON_NO_SPACE_ERROR;
}
reserve -= writeLen;
} while (reserve != 0);
FILE_MMCLOSE_AND_SET_INVALID(fd);
return IDE_DAEMON_NONE_ERROR;
}
* @brief Add map item into mapping.csv
* @param [in] filePath: file path
* @param [in] hashValue: hash value of filePath
* @return
* IDE_DAEMON_NONE_ERROR: add mapping item success
* Other: failed, check for IdeErrorCode
*/
IdeErrorT FileUtils::AddMappingFileItem(const std::string &filePath, const std::string &hashValue)
{
IDE_CTRL_VALUE_FAILED(!filePath.empty(), return IDE_DAEMON_INVALID_PARAM_ERROR, "filePath is nullptr");
IDE_CTRL_VALUE_FAILED(!hashValue.empty(), return IDE_DAEMON_INVALID_PARAM_ERROR, "hashValue is null");
std::string dir = GetFileDir(filePath);
IDE_CTRL_VALUE_FAILED(!dir.empty(), return IDE_DAEMON_INVALID_PATH_ERROR, "file path error");
std::string mappingFile = dir + OS_SPLIT_STR + MAPPING_FILE_NAME;
int32_t fd = mmOpen2(mappingFile.c_str(), O_APPEND | M_RDWR | M_CREAT, M_IRUSR | M_IWRITE);
IDE_CTRL_VALUE_FAILED(fd >= 0, return IDE_DAEMON_INVALID_PATH_ERROR, "the path of mapping.csv error");
long err = mmLseek(fd, 0, SEEK_END);
if (err < 0) {
IDE_LOGE("fd %d, set the current pos at the end of mapping.csv failed", fd);
FILE_MMCLOSE_AND_SET_INVALID(fd);
return IDE_DAEMON_UNKNOW_ERROR;
}
std::string filename;
if (GetFileName(filePath, filename) != IDE_DAEMON_NONE_ERROR) {
IDE_LOGE("invalid filepath [ %s ]", filePath.c_str());
FILE_MMCLOSE_AND_SET_INVALID(fd);
return IDE_DAEMON_INVALID_PATH_ERROR;
}
std::string mapItem = hashValue + ',' + filename + '\n';
uint32_t mapItemDataLen = mapItem.length();
uint32_t residLen = mapItemDataLen;
do {
mmSsize_t writeLen = mmWrite(fd, const_cast<IdeStringBuffer>(mapItem.c_str()) +
(mapItemDataLen - residLen), residLen);
if (writeLen < 0) {
char errBuf[MAX_ERRSTR_LEN + 1] = {0};
IDE_LOGE("Write failed, info: %s, write ratio: %u/%u",
mmGetErrorFormatMessage(mmGetErrorCode(), errBuf, MAX_ERRSTR_LEN),
mapItemDataLen - residLen, mapItemDataLen);
FILE_MMCLOSE_AND_SET_INVALID(fd);
return IDE_DAEMON_NO_SPACE_ERROR;
}
if (residLen >= static_cast<uint32_t>(writeLen)) {
residLen -= writeLen;
} else {
char errBuf[MAX_ERRSTR_LEN + 1] = {0};
IDE_LOGE("Write failed, info : %s, writeLen larger than residLen",
mmGetErrorFormatMessage(mmGetErrorCode(), errBuf, MAX_ERRSTR_LEN));
FILE_MMCLOSE_AND_SET_INVALID(fd);
return IDE_DAEMON_UNKNOW_ERROR;
}
} while (residLen != 0);
FILE_MMCLOSE_AND_SET_INVALID(fd);
return IDE_DAEMON_NONE_ERROR;
}
* @brief Check file is exist
* @param [in] path: file path
* @return
* true: file exists
* false: file not exist
*/
bool FileUtils::IsFileExist(const std::string &path)
{
if (path.empty()) {
return false;
}
if (::mmAccess(path.c_str()) == EN_OK) {
return true;
}
return false;
}
* @brief Create dir
* @param [in] path: file path
* @return
* IDE_DAEMON_NONE_ERROR: Exist or Mkdir success
* IDE_DAEMON_INVALID_PATH_ERROR: Invalid path
* IDE_DAEMON_MKDIR_ERROR: Mkdir failed
*/
IdeErrorT FileUtils::CreateDir(const std::string &path)
{
std::string curr = path;
IdeErrorT ret = IDE_DAEMON_UNKNOW_ERROR;
if (curr.empty()) {
IDE_LOGE("create dir input empty");
return IDE_DAEMON_INVALID_PATH_ERROR;
}
if (IsFileExist(curr)) {
return IDE_DAEMON_NONE_ERROR;
} else {
std::string dir = GetFileDir(curr);
IDE_CTRL_VALUE_FAILED(!dir.empty(), return ret, "Get file dir failed, ret: %d", ret);
ret = CreateDir(dir);
IDE_CTRL_VALUE_FAILED(ret == IDE_DAEMON_NONE_ERROR, return ret, "Create dir failed, ret: %d", ret);
}
if (!IsFileExist(path)) {
if (mmMkdir(path.c_str(), (mmMode_t)DEFAULT_PATH_MODE) != EN_OK && errno != EEXIST) {
char errBuf[MAX_ERRSTR_LEN + 1] = {0};
IDE_LOGE("mkdir %s failed, errorstr: %s", path.c_str(),
mmGetErrorFormatMessage(mmGetErrorCode(), errBuf, MAX_ERRSTR_LEN));
return IDE_DAEMON_MKDIR_ERROR;
}
}
return IDE_DAEMON_NONE_ERROR;
}
* @brief Get the path part of the path
* @param [in] path: file path
* @param [out] dir: the path part of path
* @return
* IDE_DAEMON_NONE_ERROR: Dump start Success
* IDE_DAEMON_INVALID_PATH_ERROR: Invalid path
*/
std::string FileUtils::GetFileDir(const std::string &path)
{
std::string dir;
size_t pos = path.find_last_of(OS_SPLIT_CHAR);
if (pos != std::string::npos) {
dir = path.substr(0, pos);
if (dir.empty()) {
dir += OS_SPLIT_STR;
}
return dir;
}
return dir;
}
* @brief Check if the path has sufficient disk space
* @param [in] path: file path
* @param [in] size: The amount of disk space required
* @return
* true: the path has sufficient disk space
* false: invalid path or the path does not have sufficient disk space
*/
bool FileUtils::IsDiskFull(const std::string &path, uint64_t size)
{
IDE_CTRL_VALUE_FAILED(!path.empty(), return false, "path is empty");
mmDiskSize diskSize;
(void)memset_s(&diskSize, sizeof(diskSize), 0, sizeof(diskSize));
int32_t ret = mmGetDiskFreeSpace(path.c_str(), &diskSize);
IDE_CTRL_VALUE_FAILED(ret == EN_OK, return true, "get disk free space fail");
IDE_CTRL_VALUE_FAILED(diskSize.freeSize > DISK_RESERVED_SPACE, return true,
"the %s more than disk reserved space(1Mb)", path.c_str());
IDE_CTRL_VALUE_FAILED(size < diskSize.freeSize, return true, "the %s is full", path.c_str());
return false;
}
* @brief Get the name part of the path
* @param [in] path: file path
* @param [out] dir: the name part of path
* @return
* IDE_DAEMON_NONE_ERROR: Dump start Success
* IDE_DAEMON_INVALID_PATH_ERROR: Invalid path
*/
IdeErrorT FileUtils::GetFileName(const std::string &path, std::string &name)
{
size_t pos = path.find_last_of(OS_SPLIT_CHAR);
if (pos != std::string::npos) {
name = path.substr(pos + 1);
if (name.empty()) {
return IDE_DAEMON_INVALID_PATH_ERROR;
}
return IDE_DAEMON_NONE_ERROR;
}
name = path;
return IDE_DAEMON_NONE_ERROR;
}
* @brief Check file path exit cross path
* @param [in] path: file path
* @return
* true : file not exists cross path
* false : file exists cross path
*/
bool FileUtils::CheckNonCrossPath(const std::string &path)
{
if (path.empty() || path.length() > MMPA_MAX_PATH) {
return false;
}
size_t pos = path.find("..");
if (pos == 0) {
return false;
}
if (path.find("/..") != std::string::npos ||
path.find("/\\.\\.") != std::string::npos) {
return false;
}
return true;
}
* @brief jugde the file_path is realpath
* @param [in] file_path: file path
* @param [out] resolved_path: return path
*
* @return
* IDE_DAEMON_OK: succ
* IDE_DAEMON_ERROR: failed
*/
int32_t FileUtils::FilePathIsReal(const std::string &filePath, std::string &resultPath)
{
if (filePath.empty()) {
return IDE_DAEMON_ERROR;
}
char resolvedPath[MMPA_MAX_PATH] = {0};
int32_t ret = mmRealPath(filePath.c_str(), resolvedPath, MMPA_MAX_PATH);
if (ret != EN_OK) {
return IDE_DAEMON_ERROR;
}
resultPath = resolvedPath;
return IDE_DAEMON_OK;
}
* @brief jugde the file path with filename is realpath
* @param file_path: file path
* @param resolved_path: return path
* @param path_len: the max length of resolved_path buffer
*
* @return
* IDE_DAEMON_OK: succ
* IDE_DAEMON_ERROR: failed
*/
int32_t FileUtils::FileNameIsReal(const std::string &file, std::string &resultPath)
{
int32_t ret = 0;
if (file.empty()) {
return IDE_DAEMON_ERROR;
}
size_t pos = file.find_last_of(OS_SPLIT_CHAR);
if (pos != std::string::npos) {
std::string path = file.substr(0, pos + 1);
ret = FilePathIsReal(path, resultPath);
if (ret != IDE_DAEMON_OK) {
IDE_LOGE("path %s does not exist", file.c_str());
return IDE_DAEMON_ERROR;
}
}
resultPath = file;
return IDE_DAEMON_OK;
}
* @brief Check if characters of the dir are all valid
* @param [in] path: file path
* @return
* true: characters of the dir are all valid
* false: characters of the dir are not all valid
*/
bool FileUtils::IsValidDirChar(const std::string &path)
{
if (path.empty()) {
IDE_LOGE("invalid parameter");
return false;
}
const std::string pathWhiteList = "-=[];\\,./!@#$%^&*()_+{}:?";
size_t len = path.length();
for (size_t i = 0; i < len; i++) {
if (!islower(path[i]) && !isupper(path[i]) && !isdigit(path[i]) &&
pathWhiteList.find(path[i]) == std::string::npos) {
IDE_LOGW("invalid path [%s] in char : [%c] at location %zu", path.c_str(), path[i], i);
return false;
}
}
return true;
}
std::string FileUtils::ReplaceAll(std::string &base, const std::string &src, const std::string &dst)
{
size_t pos = 0;
std::string targetStr = dst;
while ((pos = base.find(src, pos)) != std::string::npos) {
base.replace(pos, src.size(), targetStr);
pos += targetStr.size();
}
return base;
}
bool FileUtils::IsAbsolutePath(const std::string &path)
{
#if (OS_TYPE == LINUX)
return path.front() == OS_SPLIT_CHAR;
#else
if (path.length() < WIN_PATH_MIN_LENGTH) {
return false;
}
return (path[1] == COLON) &&
((path[0] >= 'a' && path[0] <= 'z') ||
(path[0] >= 'A' && path[0] <= 'Z'));
#endif
}
bool FileUtils::IsPathHasPermission(const std::string &path, std::string &errorMsg) {
std::string trustedPath;
int32_t ret = FilePathIsReal(path, trustedPath);
if (ret != IDE_DAEMON_OK) {
errorMsg = "The dump_path " + path + " is not a real path.";
IDE_LOGE("%s", errorMsg.c_str());
return false;
}
constexpr uint32_t accessMode = static_cast<uint32_t>(M_R_OK) | static_cast<uint32_t>(M_W_OK);
if (mmAccess2(trustedPath.c_str(), static_cast<INT32>(accessMode)) != EN_OK) {
errorMsg = "The path " + trustedPath + " does not have read and write permission";
IDE_LOGE("%s", errorMsg.c_str());
return false;
}
errorMsg.clear();
return true;
}
}