* 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 "bbox_tool_fs.h"
#include <stdbool.h>
#include "bbox_int.h"
#include "bbox_print.h"
#include "bbox_file_list.h"
#include "bbox_log_common.h"
#include "bbox_system_api.h"
#define DONE_FILE "DONE"
static const char *g_done_file_str[] = {
"STARTING\n",
"COMPLETE\n",
"FILEDONE\n",
"PROCFAIL\n",
NULL,
};
static inline bool bbox_file_is_writable(const char *file)
{
bbox_stat64_t stat = {0};
bbox_status ret = bbox_stat64(file, &stat);
return (ret != BBOX_SUCCESS && ERRNO_IS_NOT_FOUND) || (ret == BBOX_SUCCESS && (stat.st_mode & M_IWUSR) != 0);
}
STATIC int bbox_check_path_perm(const char *path)
{
bbox_stat64_t stat = {0};
BBOX_CHK_NULL_PTR(path, return BBOX_FAILURE);
BBOX_CHK_EXPR_ACTION(strnlen(path, DIR_MAXLEN) == 0, return BBOX_FAILURE, "Empty path.");
if (bbox_stat64(path, &stat) != BBOX_SUCCESS) {
BBOX_ERR("Stat failed. (path=%s, errno=%s)", path, strerror(errno));
return BBOX_FAILURE;
}
if ((stat.st_mode & MMPA_DEFAULT_PIPE_PERMISSION) > DIR_CHECK_MODE) {
BBOX_ERR("Directory permissions exceed maximum allowed. (path=%s, current_mode=%04o, max_mode=%04o)",
path, (stat.st_mode & MMPA_DEFAULT_PIPE_PERMISSION), DIR_CHECK_MODE);
return BBOX_FAILURE;
}
if (stat.st_uid != 0) {
BBOX_ERR("Directory owner must be root. (path=%s, current_uid=%u)", path, (unsigned int)stat.st_uid);
return BBOX_FAILURE;
}
return BBOX_SUCCESS;
}
STATIC bbox_status bbox_check_path(const char *dir_path)
{
bbox_status ret = BBOX_SUCCESS;
u32 index = 0;
char cur_path[DIR_MAXLEN] = {0};
BBOX_CHK_NULL_PTR(dir_path, return BBOX_FAILURE);
BBOX_CHK_EXPR_ACTION(strnlen(dir_path, DIR_MAXLEN) == 0, return BBOX_FAILURE, "Empty path.");
char *abs_path = realpath(dir_path, NULL);
if (abs_path == NULL) {
BBOX_ERR("Realpath failed (path=%s, error=%s)", dir_path, strerror(errno));
return BBOX_FAILURE;
}
ret = bbox_check_path_perm(OS_ROOT_DIR);
if (ret != BBOX_SUCCESS) {
bbox_free(abs_path);
return ret;
}
const char *path = abs_path;
while ((*path != '\0') && (ret == BBOX_SUCCESS)) {
if (*path == OS_PATH_SPLIT) {
cur_path[index] = '\0';
if (index > 0) {
ret = bbox_check_path_perm(cur_path);
}
while (*(path + 1) == OS_PATH_SPLIT) path++;
}
if (index >= (DIR_MAXLEN - 1)) {
BBOX_ERR("Path too long. (max_len=%d)", DIR_MAXLEN);
ret = BBOX_FAILURE;
break;
}
cur_path[index++] = *path++;
}
if ((ret == BBOX_SUCCESS) && (index > 0) && (cur_path[index - 1] != OS_PATH_SPLIT)) {
cur_path[index] = '\0';
ret = bbox_check_path_perm(cur_path);
}
bbox_free(abs_path);
return ret;
}
* @brief : append(save) data to path.
* @param [in] : const char *log_path path of save file.
* @param [in] : const char *file_name filename
* @param [in] : const buff *buf save data.
* @param [in] : u32 len data length.
* @param [in] : u32 is_append determine whether write with append
* @return : <0 failure; >=0 success
*/
s32 bbox_save_buf_to_fs(const char *log_path, const char *file_name,
const buff *buf, u32 len, u32 is_append)
{
BBOX_CHK_NULL_PTR(log_path, return BBOX_FAILURE);
BBOX_CHK_NULL_PTR(file_name, return BBOX_FAILURE);
BBOX_CHK_NULL_PTR(buf, return BBOX_FAILURE);
BBOX_CHK_INVALID_PARAM(len == 0, return BBOX_FAILURE, "%u", len);
char path[DIR_MAXLEN] = {0};
u32 len_temp = len;
u32 offset = 0;
if ((strcmp(file_name, "vmcore") == 0) && (bbox_check_path(log_path) != BBOX_SUCCESS)) {
return BBOX_FAILURE;
}
s32 ret = bbox_format_path(path, DIR_MAXLEN, log_path, file_name);
if (ret == -1) {
BBOX_ERR("format path with dir(%s) file(%s) failed.", log_path, file_name);
return BBOX_FAILURE;
}
if (!bbox_file_is_writable(path)) {
bbox_chmod(path, FILE_RW_MODE);
}
s32 flags = O_CREAT | O_RDWR | ((is_append != 0) ? O_APPEND : O_TRUNC);
s32 fd = bbox_open(path, flags, FILE_RW_MODE);
if (fd < 0) {
BBOX_PERROR("open", path);
return BBOX_FAILURE;
}
ret = (s32)bbox_lseek(fd, 0L, SEEK_END);
if (ret < 0) {
BBOX_PERROR("lseek", path);
bbox_close(fd);
return BBOX_FAILURE;
}
while (len_temp) {
ret = bbox_write(fd, (const void *)((const char *)(buf) + offset), len_temp);
if (ret <= 0) {
bbox_close(fd);
return ret;
}
len_temp -= (u32)ret;
offset += (u32)ret;
}
if (offset != len) {
BBOX_ERR("write file %s exception. (offset=%u, len=%u)", path, offset, len);
}
bbox_close(fd);
return ret;
}
* @brief : write done file
* @param [in] : char *path file path
* @param [in] : s32 stat file stat
* @return : <0 failure; ==0 success
*/
s32 bbox_write_done_file(const char *path, s32 stat)
{
BBOX_CHK_INVALID_PARAM(stat < STORE_STARTING || stat >= STORE_MAX, return BBOX_FAILURE, "%d", stat);
const char *str = g_done_file_str[stat];
BBOX_DBG("write done file stat: %s", str);
return bbox_save_buf_to_fs(path, DONE_FILE, str, (u32)strlen(str), BBOX_FALSE);
}
* @brief : chmod file mode
* @param [in] : const char *file_path file path
* @param [in] : MODE mode file mode
* @return : < 0 fail == 0 success
*/
STATIC_INLINE bbox_status bbox_file_chmod(const char *file_path, s32 mode)
{
if (bbox_chmod(file_path, mode) != BBOX_SUCCESS) {
BBOX_ERR_CTRL(BBOX_PERROR, return BBOX_FAILURE, "chmod", file_path);
}
return BBOX_SUCCESS;
}
* @brief : chmod dir mode by recursive function
* @param [in] : const char *log_path dir path
* @param [in] : MODE mode file mode
* @param [in] : u32 rec_num number of recursions
* @return : < 0 fail == 0 success
*/
bbox_status bbox_dir_chmod(const char *log_path, s32 mode, u32 rec_num)
{
BBOX_CHK_NULL_PTR(log_path, return BBOX_FAILURE);
if (rec_num == 0) {
BBOX_WAR("Dir chmod reached the number of recursions.");
return BBOX_SUCCESS;
}
bbox_dirent **dir = NULL;
s32 num = bbox_scan_dir(log_path, &dir, NULL, NULL);
if (num < 0) {
BBOX_PERROR("scandir", log_path);
return BBOX_FAILURE;
}
s32 i;
for (i = 0; i < num && dir != NULL && dir[i] != NULL; i++) {
if ((strcmp(dir[i]->d_name, CURRENT_DIRNAME) == 0) || (strcmp(dir[i]->d_name, PARENT_DIRNAME) == 0)) {
continue;
}
char path[DIR_MAXLEN] = {0};
s32 err = bbox_format_path(path, DIR_MAXLEN, log_path, dir[i]->d_name);
if (err == -1) {
bbox_scan_dir_free(dir, num);
BBOX_ERR_CTRL(BBOX_ERR, return BBOX_FAILURE, "bbox_format_path error");
}
if (dir[i]->d_type == DT_DIR) {
bbox_status ret = bbox_dir_chmod(path, mode, rec_num - 1U);
if (ret != BBOX_SUCCESS) {
bbox_scan_dir_free(dir, num);
BBOX_ERR_CTRL(BBOX_ERR, return ret, "dir[%s] files chmod failed", path);
}
ret = bbox_file_chmod(path, DIR_MODE);
if (ret != BBOX_SUCCESS) {
bbox_scan_dir_free(dir, num);
BBOX_ERR_CTRL(BBOX_ERR, return ret, "dir[%s] chmod failed", path);
}
} else {
bbox_status ret = bbox_file_chmod((const char *)path, mode);
if (ret != BBOX_SUCCESS) {
bbox_scan_dir_free(dir, num);
BBOX_ERR_CTRL(BBOX_ERR, return ret, "file[%s] chmod failed", path);
}
}
}
bbox_scan_dir_free(dir, num);
return BBOX_SUCCESS;
}
* @brief create dir under log_path
* @param [out] sub_path sub path
* @param [in] len length of log_path
* @param [in] log_path log path
* @param [in] dir dir name
* @return 0 on success otherwise -1
*/
STATIC bbox_status bbox_create_sub_path(char *sub_path, u32 len, const char *log_path, const char *dir)
{
return bbox_age_add_folder(log_path, dir, sub_path, len);
}
* @brief create sub path under log_path to store ddr maintenance data
* @param [out] sub_path log sub path
* @param [in] len length of log_path
* @param [in] log_path log path
* @return 0 on success otherwise -1
*/
bbox_status bbox_create_bbox_sub_path(char *sub_path, u32 len, const char *log_path)
{
return bbox_create_sub_path(sub_path, len, log_path, DIR_BBOXDUMP);
}
* @brief create sub path under log_path to store register & sram logs
* @param [out] sub_path log sub path
* @param [in] len length of log_path
* @param [in] log_path log path
* @return 0 on success otherwise -1
*/
bbox_status bbox_create_mntn_sub_path(char *sub_path, u32 len, const char *log_path)
{
return bbox_create_sub_path(sub_path, len, log_path, DIR_MNTN);
}
* @brief create sub path under log_path to store module log data
* @param [out] sub_path mntn sub path
* @param [in] len length of log_path
* @param [in] log_path log path
* @return 0 on success otherwise -1
*/
bbox_status bbox_create_log_sub_path(char *sub_path, u32 len, const char *log_path)
{
return bbox_create_sub_path(sub_path, len, log_path, DIR_MODULELOG);
}
* @brief : append(save) data to log path.
* @param [in] : const char *log_path path of save file.
* @param [in] : const char *file_name filename
* @param [in] : const buff *buf save data.
* @param [in] : u32 len data length.
* @param [in] : u32 is_append determine whether write with append
* @return : <0 failure; >=0 success
*/
s32 bbox_save_log_buf_to_fs(const char *log_path, const char *file_name,
const buff *buf, u32 len, u32 is_append)
{
char sub_path[DIR_MAXLEN];
bbox_status ret = bbox_create_log_sub_path(sub_path, DIR_MAXLEN, log_path);
if (ret != BBOX_SUCCESS) {
BBOX_ERR_CTRL(BBOX_ERR, return ret, "bbox create log sub path failed.");
}
return bbox_save_buf_to_fs(sub_path, file_name, buf, len, is_append);
}
* @brief : create dir if not present
* @param [in] : const char *path path.
* @return : <0 failure; ==0 success
*/
STATIC bbox_status bbox_mkdir_not_present(const char *path)
{
bbox_stat64_t stat = {0};
BBOX_CHK_NULL_PTR(path, return BBOX_FAILURE);
BBOX_CHK_INVALID_PARAM(strlen(path) == 0, return BBOX_FAILURE, "%s", path);
if (bbox_stat64(path, &stat) != BBOX_SUCCESS) {
BBOX_DBG("create dir: %s", path);
bbox_status ret = bbox_mkdir(path, DIR_MODE);
BBOX_CHK_EXPR_CTRL(BBOX_PERROR, ret != BBOX_SUCCESS, return BBOX_FAILURE, "mkdir", path);
}
return BBOX_SUCCESS;
}
* @brief : recursively create dir
* @param [in] : const char *dir_path dir path.
* @return : <0 failure; ==0 success
*/
bbox_status bbox_mkdir_recur(const char *dir_path)
{
bbox_status ret = BBOX_SUCCESS;
u32 index;
u32 rt_len = strlen(OS_ROOT_DIR);
char cur_path[DIR_MAXLEN] = {0};
const char *path = dir_path;
BBOX_CHK_NULL_PTR(path, return BBOX_FAILURE);
for (index = 0; index < rt_len && index < DIR_MAXLEN && (*path != '\0');) {
cur_path[index++] = *path++;
}
while (*path != '\0' && ret == BBOX_SUCCESS) {
if (*path == OS_PATH_SPLIT) {
ret = bbox_mkdir_not_present(cur_path);
}
cur_path[index] = *path;
if (++index == DIR_MAXLEN) {
BBOX_ERR("path name(%s) too long.", path);
return BBOX_FAILURE;
}
path++;
}
if (ret == BBOX_SUCCESS) {
ret = bbox_mkdir_not_present(cur_path);
}
return ret;
}
* @brief : append(save) data to path.
* @param [in] : const char *old_path the old path
* @param [in] : const char *folder add the folder
* @param [out] : char *new_path the new path to return
* @param [in] : u32 len the new path length
* @return : <0 failure; ==0 success
*/
bbox_status bbox_age_add_folder(const char *old_path, const char *folder, char *new_path, u32 len)
{
BBOX_CHK_NULL_PTR(old_path, return BBOX_FAILURE);
BBOX_CHK_NULL_PTR(new_path, return BBOX_FAILURE);
BBOX_CHK_NULL_PTR(folder, return BBOX_FAILURE);
BBOX_CHK_INVALID_PARAM(len == 0, return BBOX_FAILURE, "%u", len);
s32 ret = bbox_format_path(new_path, len, old_path, folder);
if (ret == -1 || ret == 0) {
BBOX_ERR_CTRL(BBOX_ERR, return BBOX_FAILURE, "bbox_format_path [%s][%s] failed.", old_path, folder);
}
return bbox_mkdir_recur(new_path);
}
* @brief : format the child full path from parent path
* @param [out] : char *buf path buffer
* @param [in] : u32 len path buffer len
* @param [in] : const char *parent parent path
* @param [in] : const char *child child dir name
* @return : < 0 fail >= 0 success
*/
s32 bbox_format_path(char *buf, u32 len, const char *parent, const char *child)
{
return sprintf_s(buf, len, OS_PATH_FORMAT, parent, child);
}
* @brief : format the exception's device path
* @param [out] : char *buf path buffer
* @param [in] : u32 len path buffer len
* @param [in] : const char *parent parent path
* @param [in] : u32 dev device id
* @return : < 0 fail >= 0 success
*/
s32 bbox_format_device_path(char *buf, u32 len, const char *parent, u32 dev)
{
return sprintf_s(buf, len, "%s" OS_DIR_SLASH DEVDIR_FORMAT, parent, dev);
}