* 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 <securec.h>
#include <cstdlib>
#include <fstream>
#include <vector>
#include <iostream>
#include <array>
#include "common/llm_file_saver.h"
#include "def_types.h"
#include "common/llm_checker.h"
#include "common/llm_utils.h"
#include "common/llm_log.h"
#include "common/def_types.h"
#include "graph_metadef/common/ge_common/util.h"
namespace llm {
constexpr int32_t kFileOpSuccess = 0;
constexpr size_t kMaxErrStrLen = 128U;
inline int32_t CheckAndMkdir(const char_t *tmp_dir_path, mmMode_t mode) {
if (mmAccess2(tmp_dir_path, M_F_OK) != EN_OK) {
const int32_t ret = mmMkdir(tmp_dir_path, mode);
if (ret != 0) {
REPORT_INNER_ERR_MSG("E18888",
"Cannot create directory %s. Make sure the directory "
"exists and writable. errmsg:%s",
tmp_dir_path, strerror(errno));
LLMLOGW(
"[Util][mkdir] Create directory %s failed, reason:%s. Make sure the "
"directory exists and writable.",
tmp_dir_path, strerror(errno));
return ret;
}
}
return 0;
}
namespace {
* @ingroup domi_common
* @brief Create directory, support to create multi-level directory
* @param [in] directory_path Path, can be multi-level directory
* @return -1 fail
* @return 0 success
*/
int32_t CreateDir(const std::string &directory_path, uint32_t mode) {
LLM_CHK_BOOL_EXEC(!directory_path.empty(),
REPORT_INNER_ERR_MSG("E18888", "directory path is empty, check invalid");
return -1, "[Check][Param] directory path is empty.");
const auto dir_path_len = directory_path.length();
if (dir_path_len >= static_cast<size_t>(MMPA_MAX_PATH)) {
LLMLOGW("[Util][mkdir] Path %s len is too long, it must be less than %d", directory_path.c_str(), MMPA_MAX_PATH);
return -1;
}
char_t tmp_dir_path[MMPA_MAX_PATH] = {};
const auto mkdir_mode = static_cast<mmMode_t>(mode);
for (size_t i = 0U; i < dir_path_len; i++) {
tmp_dir_path[i] = directory_path[i];
if ((tmp_dir_path[i] == '\\') || (tmp_dir_path[i] == '/')) {
const int32_t ret = CheckAndMkdir(&(tmp_dir_path[0U]), mkdir_mode);
if (ret != 0) {
return ret;
}
}
}
return CheckAndMkdir(directory_path.c_str(), mkdir_mode);
}
* @ingroup domi_common
* @brief Create directory, support to create multi-level directory
* @param [in] directory_path Path, can be multi-level directory
* @return -1 fail
* @return 0 success
*/
int32_t CreateDir(const std::string &directory_path) {
constexpr auto mkdir_mode = static_cast<uint32_t>(M_IRUSR | M_IWUSR | M_IXUSR);
return CreateDir(directory_path, mkdir_mode);
}
}
* @ingroup domi_common
* @brief Create directory, support to create multi-level directory
* @param [in] directory_path Path, can be multi-level directory
* @return -1 fail
* @return 0 success
*/
int32_t CreateDirectory(const std::string &directory_path) {
return CreateDir(directory_path);
}
ge::Status LLMFileSaver::CheckPathValid(const std::string &file_path) {
if (file_path.size() >= static_cast<size_t>(MMPA_MAX_PATH)) {
LLMLOGE(ge::FAILED, "[Check][FilePath]Failed, file path's length:%zu >= mmpa_max_path:%d",
file_path.size(), MMPA_MAX_PATH);
REPORT_INNER_ERR_MSG("E19999", "Check file path failed, file path's length:%zu >= "
"mmpa_max_path:%d", file_path.size(), MMPA_MAX_PATH);
return ge::FAILED;
}
int32_t path_split_pos = static_cast<int32_t>(file_path.size() - 1U);
for (; path_split_pos >= 0; path_split_pos--) {
if ((file_path[static_cast<size_t>(path_split_pos)] == '\\') ||
(file_path[static_cast<size_t>(path_split_pos)] == '/')) {
break;
}
}
if (path_split_pos == 0) {
return ge::SUCCESS;
}
if (path_split_pos != -1) {
if (CreateDirectory(std::string(file_path).substr(0U, static_cast<size_t>(path_split_pos))) != kFileOpSuccess) {
LLMLOGE(ge::FAILED, "[Create][Directory]Failed, file path:%s.", file_path.c_str());
return ge::FAILED;
}
}
return ge::SUCCESS;
}
ge::Status LLMFileSaver::OpenFile(int32_t &fd, const std::string &file_path, const bool append) {
if (CheckPathValid(file_path) != ge::SUCCESS) {
LLMLOGE(ge::FAILED, "[Check][FilePath]Check output file failed, file_path:%s.",
file_path.c_str());
REPORT_INNER_ERR_MSG("E19999", "Check output file failed, file_path:%s.",
file_path.c_str());
return ge::FAILED;
}
std::array<char_t, MMPA_MAX_PATH> real_path = {};
LLM_IF_BOOL_EXEC(mmRealPath(file_path.c_str(), &real_path[0], MMPA_MAX_PATH) != EN_OK,
LLMLOGI("File %s does not exist, it will be created.", file_path.c_str()));
constexpr mmMode_t mode = static_cast<mmMode_t>(static_cast<uint32_t>(M_IRUSR) | static_cast<uint32_t>(M_IWUSR));
uint32_t open_flag = static_cast<uint32_t>(M_RDWR) | static_cast<uint32_t>(M_CREAT);
if (append) {
open_flag |= static_cast<uint32_t>(O_APPEND);
} else {
open_flag |= static_cast<uint32_t>(O_TRUNC);
}
fd = mmOpen2(&real_path[0], static_cast<int32_t>(open_flag), mode);
if ((fd == EN_INVALID_PARAM) || (fd == EN_ERROR)) {
std::array<char_t, kMaxErrStrLen + 1U> err_buf = {};
const auto err_msg = mmGetErrorFormatMessage(mmGetErrorCode(), &err_buf[0], kMaxErrStrLen);
std::string reason = "[Errno " + std::to_string(mmGetErrorCode()) + "] " + err_msg + ".";
LLMLOGE(ge::FAILED, "[Open][File]Failed. errno:%d, errmsg:%s", fd, err_msg);
REPORT_PREDEFINED_ERR_MSG("E10001", std::vector<const char *>({"value", "parameter", "reason"}),
std::vector<const char *>({file_path.c_str(), "file path", reason.c_str()}));
return ge::FAILED;
}
return ge::SUCCESS;
}
ge::Status LLMFileSaver::WriteData(const void * const data, uint64_t size, const int32_t fd) {
if ((size == 0U) || (data == nullptr)) {
return ge::LLM_PARAM_INVALID;
}
int64_t write_count;
constexpr uint64_t kMaxWriteSize = 1 * 1024 * 1024 * 1024UL;
auto seek = PtrToPtr<void, uint8_t>(const_cast<void *>(data));
while (size > 0U) {
const uint64_t expect_write_size = std::min(size, kMaxWriteSize);
write_count = mmWrite(fd, reinterpret_cast<void *>(seek), static_cast<uint32_t>(expect_write_size));
LLM_ASSERT_TRUE(((write_count != EN_INVALID_PARAM) && (write_count != EN_ERROR)),
"Write data failed, errno: %lld", write_count);
seek = PtrAdd<uint8_t>(seek, static_cast<size_t>(size), write_count);
LLM_ASSERT_TRUE(size >= static_cast<uint64_t>(write_count),
"Write data failed, errno: %lld, size: %u", write_count, size);
size -= write_count;
}
return ge::SUCCESS;
}
ge::Status LLMFileSaver::SaveToFile(const std::string &file_path, const void *const data, const uint64_t len,
const bool append) {
if ((data == nullptr) || (len <= 0)) {
LLMLOGE(ge::FAILED, "[Check][Param]Failed, model_data is null or the "
"length[%lu] is less than 1.", len);
REPORT_INNER_ERR_MSG("E19999", "Save file failed, the model_data is null or "
"its length:%" PRIu64 " is less than 1.", len);
return ge::FAILED;
}
int32_t fd = 0;
if (OpenFile(fd, file_path, append) != ge::SUCCESS) {
LLMLOGE(ge::FAILED, "OpenFile ge::FAILED");
return ge::FAILED;
}
ge::Status ret = ge::SUCCESS;
LLM_CHK_BOOL_EXEC(WriteData(data, len, fd) == ge::SUCCESS, ret = ge::FAILED, "WriteData ge::FAILED");
if (mmClose(fd) != 0) {
std::array<char_t, kMaxErrStrLen + 1U> err_buf = {};
const auto err_msg = mmGetErrorFormatMessage(mmGetErrorCode(), &err_buf[0], kMaxErrStrLen);
const std::string reason = ge::FormatErrnoReason(mmGetErrorCode(), err_msg);
LLMLOGE(ge::FAILED, "[Close][File]Failed, error_code:%u errmsg:%s", ret, err_msg);
REPORT_INNER_ERR_MSG("E19999", "Close file failed, error_code:%u reason:%s", ret, reason.c_str());
ret = ge::FAILED;
}
return ret;
}
}