文件系统使用总结
📎 原始文档:https://www.kdocs.cn/l/cvRgZsXlnCaN 文件系统使用总结
隐藏文件
/**
* @brief 隐藏属性文件是否过滤
*
* @param flag 置1 为过滤
*/
void hidden_file(u8 flag);
隐藏文件扫描开关,置1不扫描隐藏文件
当需要fopen打开一个隐藏文件之前,需要置0
长\短名
15bytes以内长度的文件名为短名,格式是u8
15bytes以上的为长名,格式是unicode
unicode转码工具:
在线Unicode编码转换-Unicode和ASCII在线互转-中文转Unicode工具 (jsons.cn)
文件打开
FILE *fopen(const char *path, const char *mode);
- 写入模式下,如果是往中间插入写文件,fclose关闭文件指针之前,需要fseek到文件结尾,但是不可用 fseek(file, 0, SEEK_END),需要fseek(file, flen, SEEK_SET),其中flen是文件长度
- 一次写入操作,不允许没有fclose之前又fseek到中间位置重新写入,数据会丢失,如果想要修改中间数据,需要如下操作:fwrite->fclose->fopen->fseek->fwrite
- 长名文件的fopen实现demo如下
#define PATCH_LONG_NAME_LEN_MAX 260
#define PATCH_ROOT "storage/sd0/C/"
/**
* @brief 文件名传入后转换成unicode用于获取长名文件路径
* @param patch_name:路径名字
* @param name_len:传入名字长度
* @param unicode_patch:获取unicode路径名buffer
* @param unicode_len:unicode路径名buffer长度
*/
void file_bs_patch_unicode(u8 *patch_name, int name_len, u8 *unicode_patch, int unicode_len)
{
file_bs_printf_lite("%s->patch[%d]:%s\n", __func__, name_len, patch_name);
u8 root_patch[] = {PATCH_ROOT};
u8 *root_patch_unicode = unicode_patch;
u16 *name_unicode = malloc(PATCH_LONG_NAME_LEN_MAX/2);
int offset = 0;
memset(name_unicode, 0, PATCH_LONG_NAME_LEN_MAX/2);
memset(root_patch_unicode, 0, unicode_len);
int patch_len = sizeof(root_patch)-1;
int len = UTF82Unicode(patch_name, name_unicode + 1, name_len);
file_bs_printf("%s->UTF82Unicode len:%d\n", __func__, len);
memcpy(name_unicode, "\\U", 2);
file_bs_put_buf(name_unicode, (len + 1) * 2);
memcpy(root_patch_unicode + offset, root_patch, sizeof(root_patch)-1);
offset += sizeof(root_patch)-1;
memcpy(root_patch_unicode + offset, name_unicode, (len + 1) * 2);
offset += (len + 1) * 2;
file_bs_put_buf(root_patch_unicode, offset);
free(name_unicode);
name_unicode = NULL;
}
文件写入
int fwrite(FILE *file, void *buf, u32 len);
512bytes对齐写入速度最快,因此如果流程允许,最好采用缓存buffer的形式去写入
demo如下
memcpy(__this.allsong_data_buffer+__this.allsong_data_buffer_write_len, play_tail_buffer, sizeof(play_tail_buffer)-1);
__this.allsong_data_buffer_write_len += sizeof(play_tail_buffer)-1;
file_bs_printf("%s->__this.allsong_data_buffer_write_len = %d\n", __func__, __this.allsong_data_buffer_write_len);
if(__this.allsong_data_buffer_write_len >= 512) {
file_bs_printf("%s->full write: 0x%x, file: 0x%x\n", __func__, __this.allsong_data_buffer, file);
write_len = fwrite(file, __this.allsong_data_buffer, 512);
file_bs_printf("%s->write_len:%d\n", __func__, write_len);
memset(__this.allsong_data_buffer, 0, write_len);
__this.allsong_data_buffer_write_len -= write_len;
file_bs_printf("%s->after write: %d\n", __func__, __this.allsong_data_buffer_write_len);
memcpy(__this.allsong_data_buffer, __this.allsong_data_buffer+write_len, __this.allsong_data_buffer_write_len);
}
其中变量为:
play_tail_buffer:当前要写入的数据
__this.allsong_data_buffer:缓存用buffer,长度>512
__this.allsong_data_buffer_write_len:缓存用buffer的已写入长度
write_len:当前写入长度
file:写入的文件指针
文件偏移
int fseek(FILE *file, int offset, int orig);
fseek函数不允许往前偏移,即offset >= 0
文件重命名
int frename(FILE *file, const char *path);
用法:patch直接传要改的文件名,不需要传绝对路径
文件名获取
int fget_name(FILE *file, u8 *name, int len);
传入的len>15,则获取长名,unicode编码格式,否则为短名,为u8类型
unicode转u8函数如下
int Unicode2UTF8(char *utf8_buf, u16 *pUniBuf, int uni_len);//utf8_buf开头3bytes无效数据,需要去掉
反之u8转unicode函数如下
int UTF82Unicode(const char *utf8_buf, u16 *pUniBuf, int utf8_len);
断点加速逻辑使用
music_info.bp_flag = 0;//需要是全局变量
set_bp_info(breakpoint->sclust, breakpoint->fsize, &music_info.bp_flag);
dev_manager_scan_disk(...);//扫盘结束后,如果断点有效,music_info.bp_flag置1
if (!music_info.bp_flag) { //断点无效
printf("%s MUSIC_PLAYER_ERR_PARM\n",__func__);
put_bp_info();
}
//若断点有效,则文件扫描fopen后,调用put_bp_info()释放资源
该加速逻辑在双扫盘fsn的使用下,不可连续扫描不同fsn的簇路径去找文件,会导致两个不同的fsn因为断点扫描加速逻辑导致fsn句柄异常
例如:
music_info.bp_flag = 0;//需要是全局变量
set_bp_info(breakpoint->sclust, breakpoint->fsize, &music_info.bp_flag);
fsn_A = dev_manager_scan_disk(...)//路径A的fsn
fsn_B = dev_manager_scan_disk(...)//路径B的fsn
fselect(fsn_A, FSEL_BY_SCLUST, arg);
fselect(fsn_B, FSEL_BY_SCLUST, arg);
//此时fsn_A的记录信息会被异常修改为fsn_B
正确用法为:
music_info.bp_flag = 0;//需要是全局变量
bp_A_flag = 0, bp_B_flag = 0;
FILE *file = NULL;
set_bp_info(breakpoint->sclust, breakpoint->fsize, &music_info.bp_flag);
fsn_A = dev_manager_scan_disk(...)//路径A的fsn
if (!music_info.bp_flag) { //断点无效
printf("%s MUSIC_PLAYER_ERR_PARM\n",__func__);
put_bp_info();
set_bp_info(breakpoint->sclust, breakpoint->fsize, &music_info.bp_flag);
fsn_B = dev_manager_scan_disk(...)//路径B的fsn
if (!music_info.bp_flag) {
bp_B_flag = 1;
}
} else {
bp_A_flag = 1;
}
if(bp_A_flag && (bp_B_flag == 0)) {
file = fselect(fsn_A, FSEL_BY_SCLUST, arg);
put_bp_info();
}
if(file == NULL) {
file = fselect(fsn_B, FSEL_BY_SCLUST, arg);
}
if(bp_B_flag && (bp_A_flag == 0)) {
file = fselect(fsn_B, FSEL_BY_SCLUST, arg);
put_bp_info();
}
if(file == NULL) {
file = fselect(fsn_A, FSEL_BY_SCLUST, arg);
}
//即哪个扫盘句柄存在断点信息,簇号查找优先去用那个扫盘句柄进行查找文件,扫盘结束后,put_bp_info释放掉断点加速
隐藏文件夹的创建
#define F_ATTR_RW 0x00
#define F_ATTR_RO 0x01
#define F_ATTR_HID 0x02
#define F_ATTR_SYS 0x04
#define F_ATTR_VOL 0x08
#define F_ATTR_DIR 0x10
#define F_ATTR_ARC 0x20
int fmk_dir(const char *path, char *folder, u8 mode); //创建目录
使用demo
//创建隐藏SYSTEM文件夹
dir_f = fopen("storage/sd0/C/system/", "r");
printf("%s->check_dir: %d\n", __func__, dir_f);
if(dir_f) {
fclose(dir_f);
dir_f = NULL;
} else {
printf("%s->fmk_dir\n", __func__);
fmk_dir("storage/sd0/C/", "/system", F_ATTR_HID);
}
扫盘扫描所有文件,不区分格式
扫盘函数如下
struct vfscan *dev_manager_scan_disk(struct __dev *dev, const char *path, const char *parm, u8 cycle_mode, struct __scan_callback *callback);
传参如下
if(bs_info.system_fsn == NULL) {
struct __dev *dev = dev_manager_check_by_logo("sd0");
bs_info.system_fsn = dev_manager_scan_disk(dev, "system/", "-t ALL -sn -r", FCYCLE_ALL, NULL);
printf("%s->system_fsn: 0x%x\n", __func__, bs_info.system_fsn);
}
扫盘速度参考数据
| 文件数量(首) | 时间(s) |
|---|---|
| 200 | 1 |
| 1600 | 4 |
| 4000 | 8 |
| 50000 | 15 |