/*---------------------------------------------------------------------------- * Copyright (c) Fenda Technologies Co., Ltd. 2021. All rights reserved. * * Description: fs_user_common.c * * Author: saimen * * Create: 2022-06-08 *--------------------------------------------------------------------------*/ #include "sys_config.h" #include "sys_typedef.h" #include "sql_setting.h" #include "time.h" #include "fs_user_common.h" /********************************************************************************************************************** * DEFINE */ #define ENABLE_STATIC_PRINT false #define static_print_error(...) sys_fs_log_e(__VA_ARGS__) //错误信息打印一般常开 #define static_print_warn(...) sys_fs_log_w(__VA_ARGS__) //警告信息打印一般常开 #if ENABLE_STATIC_PRINT #define static_print_debug(...) sys_fs_log_d(__VA_ARGS__) #else #define static_print_debug(...) #endif #define PATH_TEMP_MAX_LENGTH 64 /********************************************************************************************************************** * VARIABLES */ struct DelOldestObjectDef { uint8_t cnt; char path[PATH_TEMP_MAX_LENGTH]; }; //用来给删除最老的文件或目录的回调函数传递参数 /********************************************************************************************************************** * LOCAL FUNCTIONS */ /// 通过参数arg回传扫描到的文件个数. static int DirGetFileNumberCb(bool isDir, const char* path, int32_t ret, void* arg) { uint32_t *cnt = (void*)arg; if(ret >= 0 && isDir == false) { (*cnt)++; } return RET_SUCCESS; } /// 通过参数arg回传扫描到的子目录个数. static int DirGetDirNumberCb(bool isDir, const char* path, int32_t ret, void* arg) { uint32_t *cnt = (void*)arg; if(ret == 0 && isDir == true) { (*cnt)++; } return RET_SUCCESS; } /// 比较arg传入的文件名字符串与扫描到的文件名字符串path的大小关系,通过arg回传较小的那一个. static int DirGetOldestFileCb(bool isDir, const char *path, int32_t ret, void *arg) { char *pFile = (char*)arg; if(ret >= 0 && isDir == false) { if(strlen(pFile) == 0) { strcpy(pFile, path); } else { if(strcmp(path, pFile) < 0) { strcpy(pFile, path); } } } return RET_SUCCESS; } /// 比较arg传入的文件名字符串与扫描到的文件名字符串path的大小关系,通过arg回传较大的那一个. static int DirGetNewestFileCb(bool isDir, const char *path, int32_t ret, void *arg) { char *pFile = (char*)arg; if(ret >= 0 && isDir == false) { if(strlen(pFile) == 0) { strcpy(pFile, path); } else { if(strcmp(path, pFile) > 0) { strcpy(pFile, path); } } } return RET_SUCCESS; } /// 比较arg传入的子目录名字符串与扫描到的子目录名字符串path的大小关系,通过arg回传较小的那一个. static int DirGetOldestDirCb(bool isDir, const char *path, int32_t ret, void *arg) { char *pChildDir = (char*)arg; if(ret == 0 && isDir == true) { if(strlen(pChildDir) == 0) { strcpy(pChildDir, path); } else { if(strcmp(path, pChildDir) < 0) { strcpy(pChildDir, path); } } } return RET_SUCCESS; } /// 比较arg传入的子目录名字符串与扫描到的子目录名字符串path的大小关系,通过arg回传较大的那一个. static int DirGetNewestDirCb(bool isDir, const char *path, int32_t ret, void *arg) { char *pChildDir = (char*)arg; if(ret == 0 && isDir == true) { if(strlen(pChildDir) == 0) { strcpy(pChildDir, path); } else { if(strcmp(path, pChildDir) > 0) { strcpy(pChildDir, path); } } } return RET_SUCCESS; } /// 比较arg域传入的文件名字符串与扫描到的文件名字符串path的大小关系,通过arg域回传较大的那一个, /// 同时通过arg域统计文件数量并回传 static int DirDeleteOldestFileCb(bool isDir, const char *path, int32_t ret, void *arg) { struct DelOldestObjectDef *param = (struct DelOldestObjectDef*)arg; if(ret >= 0 && isDir == false) { if(param->cnt == 0) { strcpy(param->path, path); } else { if(strcmp(path, param->path) < 0) { strcpy(param->path, path); } } param->cnt++; } return RET_SUCCESS; } /// 比较arg域传入的子目录名字符串与扫描到的子目录名字符串path的大小关系,通过arg域回传较大的那一个, /// 同时通过arg域统计子目录数量并回传 static int DirDeleteOldestDirCb(bool isDir, const char *path, int32_t ret, void *arg) { struct DelOldestObjectDef *param = (struct DelOldestObjectDef*)arg; if(ret == 0 && isDir == true) { if(param->cnt == 0) { strcpy(param->path, path); } else { if(strcmp(path, param->path) < 0) { strcpy(param->path, path); } } param->cnt++; } return RET_SUCCESS; } /********************************************************************************************************************** * PUBLIC FUNCTIONS */ /** * @brief 统计目录下的文件数量,包括子目录的文件. * @param path:char*|目录的绝对路径. * @retval int|返回统计结果,负数表示错误. */ int tjd_fs_user_get_file_number(const char *path) { int err = RET_SUCCESS; uint32_t cnt = 0; err = tjd_fs_api_dir_scan(path, DirGetFileNumberCb, &cnt); return (err < 0) ? err : cnt; } /** * @brief 统计目录下的文件数量,不包括子目录的文件. * @param path:char*|目录的绝对路径. * @retval int|返回统计结果,负数表示错误. */ int tjd_fs_user_get_file_number_exclude_child_dir(const char *path) { int err = RET_SUCCESS; uint32_t cnt = 0; err = tjd_fs_api_dir_scan_exclude_child_dir(path, DirGetFileNumberCb, &cnt); return (err < 0) ? err : cnt; } /** * @brief 统计目录下的子目录数量,不包括子目录的子目录. * @param path:char*|目录的绝对路径. * @retval int|返回统计结果,负数表示错误. */ int tjd_fs_user_get_dir_number_exclude_child_dir(const char *path) { int err = RET_SUCCESS; uint32_t cnt = 0; err = tjd_fs_api_dir_scan_exclude_child_dir(path, DirGetDirNumberCb, &cnt); return (err < 0) ? err : cnt; } /** * @brief 获取目录下最老文件的绝对路径,不包括子目录. * @param path:char*|目录的绝对路径. * @param buf:char*|最老文件绝对路径的缓存指针. * @retval int|返回结果,0=目录为空,1=成功,负数=错误码. */ int tjd_fs_user_get_oldest_file_exclude_child_dir(const char *path, char *buf) { int err = RET_SUCCESS; char pathCache[PATH_TEMP_MAX_LENGTH]; memset(pathCache, 0, sizeof(pathCache)); err = tjd_fs_api_dir_scan_exclude_child_dir(path, DirGetOldestFileCb, pathCache); if(err < 0) { return err; } if(strlen(pathCache) == 0) { return 0; } else { strcpy(buf, pathCache); return 1; } } /** * @brief 获取目录下最新文件的绝对路径,不包括子目录. * @param path:char*|目录的绝对路径. * @param buf:char*|最新文件绝对路径的缓存指针. * @retval int|返回结果,0=目录为空,1=成功,负数=错误码. */ int tjd_fs_user_get_newest_file_exclude_child_dir(const char *path, char *buf) { int err = RET_SUCCESS; char pathCache[PATH_TEMP_MAX_LENGTH]; memset(pathCache, 0, sizeof(pathCache)); err = tjd_fs_api_dir_scan_exclude_child_dir(path, DirGetNewestFileCb, pathCache); if(err < 0) { return err; } if(strlen(pathCache) == 0) { return 0; } else { strcpy(buf, pathCache); return 1; } } /** * @brief 获取目录下最老目录的绝对路径,不包括子目录. * @param path:char*|目录的绝对路径. * @param buf:char*|最老目录绝对路径的缓存指针. * @retval int|返回结果,0=目录为空,1=成功,负数=错误码. */ int tjd_fs_user_get_oldest_dir_exclude_child_dir(const char *path, char *buf) { int err = RET_SUCCESS; char pathCache[PATH_TEMP_MAX_LENGTH]; memset(pathCache, 0, sizeof(pathCache)); err = tjd_fs_api_dir_scan_exclude_child_dir(path, DirGetOldestDirCb, pathCache); if(err < 0) { return err; } if(strlen(pathCache) == 0) { return 0; } else { strcpy(buf, pathCache); return 1; } } /** * @brief 获取目录下最新目录的绝对路径,不包括子目录. * @param path:char*|目录的绝对路径. * @param buf:char*|最新目录绝对路径的缓存指针. * @retval int|返回结果,0=目录为空,1=成功,负数=错误码. */ int tjd_fs_user_get_newest_dir_exclude_child_dir(const char *path, char *buf) { int err = RET_SUCCESS; char pathCache[PATH_TEMP_MAX_LENGTH]; memset(pathCache, 0, sizeof(pathCache)); err = tjd_fs_api_dir_scan_exclude_child_dir(path, DirGetNewestDirCb, pathCache); if(err < 0) { return err; } if(strlen(pathCache) == 0) { return 0; } else { strcpy(buf, pathCache); return 1; } } /** * @brief 当目录下的文件数量超过保留值时,删除最老的一个文件 * @param path:char*|目录的绝对路径. * @param retain:UINT8|要保留的文件数量. * @retval int|返回结果,非负数=剩余的文件数量,负数=错误码. * @note 每次最多只能删除一个文件.不递归进入下一级子目录. */ int tjd_fs_user_delete_oldest_file_exclude_child_dir(const char *path, uint8_t retain) { int err = 0; struct DelOldestObjectDef obj; memset(&obj, 0, sizeof(struct DelOldestObjectDef)); err = tjd_fs_api_dir_scan_exclude_child_dir(path, DirDeleteOldestFileCb, &obj); if(err < 0) { return err; } static_print_debug("current file number %d\r\n", obj.cnt); if(obj.cnt > retain) { err = tjd_fs_api_unlink(obj.path); if(err < 0) { return err; } else { return obj.cnt - 1; } } return obj.cnt; } /** * @brief 当目录下的子目录数量超过保留值时,删除最老的一个文件 * @param path:char*|目录的绝对路径. * @param retain:UINT8|要保留的子目录数量. * @retval int|返回结果,非负数=剩余的子目录数量,负数=错误码. * @note 每次最多只能删除一个子目录.不递归进入下一级子目录. */ int tjd_fs_user_delete_oldest_dir_exclude_child_dir(const char *path, uint8_t retain) { int err = 0; struct DelOldestObjectDef obj; memset(&obj, 0, sizeof(struct DelOldestObjectDef)); err = tjd_fs_api_dir_scan_exclude_child_dir(path, DirDeleteOldestDirCb, &obj); if(err < 0) { return err; } static_print_debug("current dir number %d\r\n", obj.cnt); if(obj.cnt > retain) { err = tjd_fs_api_path_remove(obj.path); if(err < 0) { return err; } else { return obj.cnt - 1; } } return obj.cnt; } static int tjd_fs_user_del_file_periodically_cb(bool isDir, const char* path, int32_t ret, void* arg) { int err=RET_SUCCESS; char dest[10] = {0}; uint8_t path_len=0; uint32_t time_dest=0; uint32_t time_file=0; struct tm *pDat = arg; time_dest =10000*pDat->tm_year+100*pDat->tm_mon+pDat->tm_mday; static_print_debug("tjd_fs_user_del_file_periodically_cb time_dest=%d\r\n",time_dest); if(ret >= 0) { if(isDir) { static_print_debug("dir:%s\r\n", path); } else { static_print_debug("file | %s | %d Bytes\r\n", path, ret); path_len = strlen(path); static_print_debug("file len is %d\r\n",path_len); strncpy(dest, path+path_len-12, 8); time_file=strtol(dest,NULL,10); if(time_file<=time_dest) { //delete file err = tjd_fs_api_unlink(path); static_print_debug("tjd_fs_user_del_file_periodically_cb del file name is:%s,err=%d\r\n",path,err); } else { //do not need to del file static_print_debug("tjd_fs_user_del_file_periodically_cb do not del file name is:%s\r\n",path); } } } else { static_print_debug("scan path %s err %d\r\n", path, ret); } return RET_SUCCESS; } /* 功能:删除指定路径下,指定日期及该日期前所有如20211103.txt的文件 参数1:path 指定路径 参数2:time 指定日期 */ int tjd_fs_user_remove_file_periodically(const char *path, void *time) { tjd_fs_api_dir_scan(path, tjd_fs_user_del_file_periodically_cb, time); return 0; } static int tjd_fs_user_del_dir_periodically_cb(bool isDir, const char* path, int32_t ret, void* arg) { int err=RET_SUCCESS; char dest[10] = {0}; uint8_t path_len=0; uint32_t time_dest=0; uint32_t time_file=0; struct tm *pDat = arg; time_dest =10000*pDat->tm_year+100*pDat->tm_mon+pDat->tm_mday; static_print_debug("tjd_fs_user_del_file_periodically_cb time_dest=%d\r\n",time_dest); if(ret >= 0) { if(isDir) { static_print_debug("dir:%s\r\n", path); path_len = strlen(path); //static_print_debug("file len is %d\r\n",path_len); strncpy(dest, path+path_len-8, 8); time_file=strtol(dest,NULL,10); static_print_debug("tjd_fs_user_del_file_periodically_cb time_file=%d\r\n",time_file); if(time_file<=time_dest) { //delete file //err = tjd_fs_api_unlink(path); err = tjd_fs_api_path_remove(path); static_print_debug("tjd_fs_user_del_file_periodically_cb del file name is:%s,err=%d\r\n",path,err); } else { //do not need to del file static_print_debug("tjd_fs_user_del_file_periodically_cb do not del file name is:%s\r\n",path); } } else { static_print_debug("file | %s | %d Bytes\r\n", path, ret); } } else { static_print_debug("scan path %s err %d\r\n", path, ret); } return 0; } /* 功能:删除指定路径下,指定日期及该日期前所有如20211103的目录 参数1:path 指定路径 const char DIR_SPORT_OUTDOR_RUN[] "/FD/data_sport/outdor_run"; 参数2:time 指定日期 如20211104 则删除20211104及该日期以前的所有目录 */ int tjd_fs_user_remove_dir_periodically(const char *path,void *time) { tjd_fs_api_dir_scan_exclude_child_dir(path, tjd_fs_user_del_dir_periodically_cb, time); return 0; } /** * @brief 打印文本文件的内容. * @param path:const char *|文件的绝对路径. * @retval int|返回结果,0=成功,负数表示错误码. */ int tjd_fs_user_print_text_file(const char *path) { #define TEXT_PRINT_BUF_MAX_SIZE 256 int err = 0; uint16_t i = 0; uint32_t total = 0; uint8_t buf[TEXT_PRINT_BUF_MAX_SIZE + 1]; if(tjd_fs_api_file_exist(path) != RET_SUCCESS) { return 0; } static_print_debug("\r\n**** Start Print %s Content ****\r\n", path); do { err = tjd_fs_api_file_load(path, i * TEXT_PRINT_BUF_MAX_SIZE, buf, TEXT_PRINT_BUF_MAX_SIZE); if(err <= 0) { break; } else if(err == TEXT_PRINT_BUF_MAX_SIZE) { buf[TEXT_PRINT_BUF_MAX_SIZE] = 0; static_print_debug("%s", buf); total += TEXT_PRINT_BUF_MAX_SIZE; } else { buf[err] = 0; total += err; static_print_debug("%s", buf); break; } i++; } while(1); static_print_debug("\r\n**** End Print Total Bytes %d ****\r\n", total); return 0; } /* 文件系统通过字符串创建路径 ************************************************************ */ /** * @brief 创建目录 * @param dir,上层目录,path 要建立的路径 * @retval int | 返回错误代码, 0=原来没有此目录情况下创建,1=原来有此目录的情况下不创建 */ int tjd_fs_user_create_dir_by_str(const char *dir,const char *path) { int err = 0; char fullPath[PATH_TEMP_MAX_LENGTH]; uint32_t temp_size=0; bool temp_isdir=0; snprintf(fullPath, PATH_TEMP_MAX_LENGTH, "%s%s%s", dir, "/", path); err = tjd_fs_api_path_exist(fullPath, &temp_size, &temp_isdir); if(err == 0) { static_print_debug("dir already exists\r\n"); } else { err = tjd_fs_user_create_dir(fullPath); if(err != 0) { static_print_warn("mkdir %s failed, err code:%d\r\n", fullPath, err); } else { static_print_debug("mkdir %s success.\r\n", fullPath); } } return err; } int tjd_fs_user_dir_init(void) { //创建一级目录 return 0; } int tjd_fs_user_mount(bool format) { int err = 0; return err; } void tjd_fs_user_init(bool format) { static uint8_t count=0; uint32_t temp_size=0; bool temp_isdir=0; repetition: tjd_fs_user_mount(format); tjd_fs_user_dir_init(); tjd_fs_user_print_dir_list("/"); if(tjd_fs_api_path_exist(TJD_FS_DIR_SYS, &temp_size, &temp_isdir) != RET_SUCCESS && count < 3) { count++; tjd_fs_api_format(); goto repetition; } } //待保留的文件及目录 static const char *retain_file_dir[] = { TJD_FS_DIR_UPDATE, }; int tjd_fs_user_factory_reset(void) { tjd_fs_api_path_remove(TJD_FS_DIR_USER); // tjd_fs_api_path_remove(TJD_FS_DIR_SYS);// log_api_save_at_power_off(); return 0; }