* This file is part of the MindStudio project.
* Copyright (c) 2025 Huawei Technologies Co.,Ltd.
*
* MindStudio 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.
* ------------------------------------------------------------------------- */
#include "file.h"
#include <dirent.h>
#include <sys/stat.h>
#include <unistd.h>
#include <cstring>
#include <iostream>
#include <map>
#include <regex>
#include <unordered_map>
#include "const.h"
using MsConst::SUFFIX;
using MsConst::SUFFIX_TYPE_TABLE;
namespace File
{
std::string GetFullPath(const std::string& originPath)
{
if (originPath.empty())
{
return "";
}
if (originPath[0] == MsConst::PATH_SEPARATOR)
{
return originPath;
}
char* cwd = nullptr;
char* cwdBuf = nullptr;
try
{
cwdBuf = new char[PATH_MAX];
}
catch (const std::bad_alloc& e)
{
AIT_LOG_ERROR("create buffer failed: " + std::string(e.what()));
throw std::runtime_error("No memory.");
}
cwd = getcwd(cwdBuf, PATH_MAX);
if (cwd == nullptr)
{
delete[] cwdBuf;
return "";
}
std::string fullPath = std::move(std::string(cwd) + MsConst::PATH_SEPARATOR + originPath);
delete[] cwdBuf;
cwdBuf = nullptr;
return fullPath;
}
static std::vector<std::string> SplitPath(const std::string& path)
{
std::vector<std::string> tokens;
size_t len = path.length();
size_t start = 0;
while (start < len)
{
size_t end = path.find(MsConst::PATH_SEPARATOR, start);
if (end == std::string::npos)
{
end = len;
}
if (start != end)
{
tokens.push_back(path.substr(start, end - start));
}
start = end + 1;
}
return tokens;
}
std::string GetAbsPath(const std::string& originPath)
{
std::string fullPath = GetFullPath(originPath);
if (fullPath.empty())
{
return "";
}
std::vector<std::string> tokens = SplitPath(fullPath);
std::vector<std::string> tokensRefined;
for (std::string& token : tokens)
{
if (token.empty() || token == ".")
{
continue;
}
else if (token == "..")
{
if (tokensRefined.empty())
{
return "";
}
tokensRefined.pop_back();
}
else
{
tokensRefined.emplace_back(token);
}
}
if (tokensRefined.empty())
{
return "/";
}
std::string resolvedPath("");
for (std::string& token : tokensRefined)
{
resolvedPath.append("/").append(token);
}
return resolvedPath;
}
bool IsFileReadable(const std::string& path) { return access(path.c_str(), R_OK) == 0; }
bool IsFileWritable(const std::string& path) { return access(path.c_str(), W_OK) == 0; }
bool IsPathExist(const std::string& path)
{
struct stat buffer;
return (stat(path.c_str(), &buffer) == 0);
}
bool IsPathLengthLegal(const std::string& path)
{
if (path.length() > MsConst::FULL_PATH_LENGTH_MAX || path.length() == 0)
{
return false;
}
std::vector<std::string> tokens = SplitPath(path);
for (std::string& token : tokens)
{
if (token.length() > MsConst::FILE_NAME_LENGTH_MAX)
{
return false;
}
}
return true;
}
bool IsPathCharactersValid(const std::string& path)
{
return std::regex_match(path, std::regex(MsConst::FILE_VALID_PATTERN));
}
bool IsPathDepthValid(const std::string& path)
{
return static_cast<size_t>(std::count(path.begin(), path.end(), MsConst::PATH_SEPARATOR)) <=
MsConst::PATH_DEPTH_MAX;
}
bool IsRegularFile(const std::string& path)
{
struct stat pathStat;
if (stat(path.c_str(), &pathStat) == 0)
{
return S_ISREG(pathStat.st_mode);
}
return false;
}
bool IsDir(const std::string& path)
{
struct stat buffer;
if (stat(path.c_str(), &buffer) == 0)
{
return (buffer.st_mode & S_IFDIR) != 0;
}
return false;
}
size_t GetFileSize(const std::string& path)
{
struct stat pathStat;
if (stat(path.c_str(), &pathStat) != 0)
{
AIT_LOG_ERROR("file not exists");
return 0;
}
return static_cast<size_t>(pathStat.st_size);
}
std::string GetParentDir(const std::string& path)
{
size_t found = path.find_last_of('/');
if (found != std::string::npos)
{
return path.substr(0, found);
}
return ".";
}
std::string GetFileName(const std::string& path)
{
size_t found = path.find_last_of('/');
if (found != std::string::npos)
{
return path.substr(found + 1);
}
return path;
}
mode_t GetPathPermissions(const std::string& path)
{
struct stat pathStat;
if (stat(path.c_str(), &pathStat) != 0)
{
AIT_LOG_ERROR("path is not exists");
return MsConst::MAX_PERMISSION;
}
mode_t permissions = pathStat.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO);
return permissions;
}
std::string GetFileSuffix(const std::string& path)
{
std::string fileName = GetFileName(path);
size_t dotPos = fileName.find_last_of('.');
if (dotPos != std::string::npos && dotPos + 1 < fileName.size())
{
return fileName.substr(dotPos + 1);
}
return "";
}
bool CheckFileSuffixAndSize(const std::string& path, SUFFIX type, const size_t maxSize)
{
struct stat pathStat;
if (stat(path.c_str(), &pathStat) != 0)
{
AIT_LOG_ERROR("file not exists");
return false;
}
size_t size = GetFileSize(path);
if (type == SUFFIX::NONE)
{
if (size > maxSize)
{
AIT_LOG_ERROR("file size invalid");
return false;
}
return true;
}
auto iter = SUFFIX_TYPE_TABLE.find(type);
if (iter == SUFFIX_TYPE_TABLE.end())
{
AIT_LOG_ERROR("unknown file suffix");
return false;
}
std::string suffix = GetFileSuffix(path);
if (suffix != iter->second.first)
{
AIT_LOG_ERROR("unknown file suffix");
return false;
}
if (size > iter->second.second && size > maxSize)
{
AIT_LOG_ERROR("file size is invalid");
return false;
}
return true;
}
static bool CreateDirAux(const std::string& path, bool recursion, mode_t mode)
{
std::string parent = GetParentDir(path);
if (!IsPathExist(parent))
{
if (!recursion)
{
AIT_LOG_ERROR("dir path not exist");
return false;
}
if (!CreateDirAux(parent, recursion, mode))
{
AIT_LOG_ERROR("recursive creation of parent directory failed");
return false;
}
}
if (!CheckDir(parent))
{
AIT_LOG_ERROR("parent directory is illegal");
return false;
}
if (mkdir(path.c_str(), mode) != 0)
{
if (errno == EACCES || errno == EROFS)
{
AIT_LOG_ERROR("mkdir permission denined");
return false;
}
else if (errno == EEXIST)
{
std::string msg = "path is exists: " + path;
AIT_LOG_DEBUG(msg);
return true;
}
else
{
AIT_LOG_ERROR("syscall failed");
return false;
}
}
return true;
}
bool CreateDir(const std::string& path, bool recursion, mode_t mode)
{
if (IsPathExist(path))
{
AIT_LOG_INFO("dir already exist, no need to create");
return true;
}
std::string absPath = GetAbsPath(path);
if (absPath.empty())
{
AIT_LOG_ERROR("path is empty");
return false;
}
if (!IsPathLengthLegal(absPath))
{
AIT_LOG_ERROR("path length illegal");
return false;
}
if (!IsPathCharactersValid(absPath))
{
AIT_LOG_ERROR("path characters invalid");
return false;
}
if (!IsPathDepthValid(absPath))
{
AIT_LOG_ERROR("path depth invalid");
return false;
}
return CreateDirAux(absPath, recursion, mode);
}
bool CheckDir(const std::string& path)
{
std::string absPath = GetAbsPath(path);
if (absPath.empty())
{
AIT_LOG_ERROR("path is empty");
return false;
}
if (!IsPathLengthLegal(absPath))
{
AIT_LOG_ERROR("path length illegal");
return false;
}
if (!IsPathCharactersValid(absPath))
{
AIT_LOG_ERROR("path characters invalid");
return false;
}
if (!IsPathDepthValid(absPath))
{
AIT_LOG_ERROR("path depth invalid");
return false;
}
if (!IsPathExist(absPath))
{
AIT_LOG_ERROR("path is not exist");
return false;
}
if (!IsDir(absPath))
{
AIT_LOG_ERROR("path is not a dir");
return false;
}
return true;
}
bool CheckFileBeforeRead(const std::string& path, SUFFIX type, const size_t maxSize)
{
std::string absPath = GetAbsPath(path);
if (absPath.empty())
{
AIT_LOG_ERROR("path is empty");
return false;
}
if (!IsPathLengthLegal(absPath))
{
AIT_LOG_ERROR("path length illegal");
return false;
}
if (!IsPathCharactersValid(absPath))
{
AIT_LOG_ERROR("path characters invalid");
return false;
}
if (!IsPathDepthValid(absPath))
{
AIT_LOG_ERROR("path depth invalid");
return false;
}
if (!IsRegularFile(absPath))
{
AIT_LOG_ERROR("path is not regular file");
return false;
}
if (!IsFileReadable(absPath) || (GetPathPermissions(absPath) & S_IRUSR) == 0)
{
AIT_LOG_ERROR("file permission should be at least 0o400(r--------)");
return false;
}
if (!CheckFileSuffixAndSize(path, type, maxSize))
{
AIT_LOG_ERROR("file suffix and size invalid");
return false;
}
return CheckDir(GetParentDir(absPath));
}
bool CheckFileBeforeCreateOrWrite(const std::string& path, bool overwrite)
{
std::string absPath = GetAbsPath(path);
if (absPath.empty())
{
AIT_LOG_ERROR("path is empty");
return false;
}
if (!IsPathLengthLegal(absPath))
{
AIT_LOG_ERROR("path length illegal");
return false;
}
if (!IsPathCharactersValid(absPath))
{
AIT_LOG_ERROR("path characters invalid");
return false;
}
if (!IsPathDepthValid(absPath))
{
AIT_LOG_ERROR("path depth invalid");
return false;
}
if (IsPathExist(absPath))
{
if (!overwrite)
{
AIT_LOG_ERROR("path already exist and not allow to overwrite");
return false;
}
if (!IsRegularFile(absPath))
{
AIT_LOG_ERROR("path is not regular file");
return false;
}
if ((GetPathPermissions(absPath) & MsConst::WRITE_FILE_NOT_PERMITTED) > 0)
{
AIT_LOG_ERROR("path permission should not be over 0o750(rwxr-x---)");
return false;
}
if (!IsFileWritable(absPath))
{
AIT_LOG_ERROR("path is not writable");
return false;
}
}
return CheckDir(GetParentDir(absPath));
}
bool CheckConfigFile(const std::string& absPath, const size_t maxSize)
{
struct stat fileStat;
if (lstat(absPath.c_str(), &fileStat) != 0)
{
return false;
}
if (!S_ISREG(fileStat.st_mode))
{
AIT_LOG_DEBUG("path is not regular file");
return false;
}
if (getuid() != 0 && fileStat.st_uid != getuid())
{
AIT_LOG_DEBUG("file owner is not process user");
return false;
}
if (access(absPath.c_str(), R_OK) != 0)
{
AIT_LOG_DEBUG("file is not Readable");
return false;
}
mode_t permissions = fileStat.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO);
if ((permissions & MsConst::READ_FILE_NOT_PERMITTED) > 0)
{
AIT_LOG_DEBUG("file permission should not be over 0o755(rwxr-xr-x)");
return false;
}
size_t fileSize = static_cast<size_t>(fileStat.st_size);
if (fileSize == 0 || fileSize > maxSize)
{
AIT_LOG_DEBUG("file size is invalid");
return false;
}
return true;
}
bool WriteTextToFile(const std::string& filePath, const std::string& textContent)
{
std::ofstream file(filePath, std::ios::out | std::ios::binary);
if (!file.is_open())
{
AIT_LOG_WARNING("Unable to open file! File name: " + filePath);
return false;
}
file << textContent << std::endl;
if (!file.good())
{
AIT_LOG_WARNING("Failed to write text to " + filePath);
file.close();
return false;
}
file.close();
return true;
}
}