272 lines
6.3 KiB
C
272 lines
6.3 KiB
C
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <ctype.h>
|
|
|
|
#include "FreeRTOS.h"
|
|
|
|
#include "ff.h"
|
|
#include "private/lftpd_io.h"
|
|
#include "lftpd.h"
|
|
#include "vol_lock.h"
|
|
|
|
#define DBG_TAG "[LFT_IO]"
|
|
#include "dbg_log.h"
|
|
|
|
/*
|
|
* FatFS 在 FF_USE_LFN=0 时不接受 "/" 前缀路径。
|
|
* 剥离前导 "/" 后用 FatFS 兼容路径,根目录用 "0:"(驱动号格式)替代。
|
|
*/
|
|
static const char *to_fatfs_path(const char *path)
|
|
{
|
|
if (path == NULL || *path == '\0') {
|
|
return "0:";
|
|
}
|
|
if (*path == '/') {
|
|
path++;
|
|
if (*path == '\0') {
|
|
return "0:";
|
|
}
|
|
}
|
|
return path;
|
|
}
|
|
|
|
static int s_file_open = 0;
|
|
static FIL s_ftp_file;
|
|
|
|
/*
|
|
* 卷锁获取(分片阻塞 + 心跳):FatFs/FTL 卷状态非线程安全,文件操作序列
|
|
* 必须与 defaultTask 的 syslog/配置盘写互斥(2026-10-02 台架实证:并发
|
|
* FTL 写 → dhara journal 竞争 → 长耗时恢复旋转饥饿 defaultTask 主循环
|
|
* (看门狗喂狗点)→ IWDG 误复位)。等待按 100ms 分片、每片置 FTP 心跳位
|
|
* (与就绪屏障等待同模式),防长等待造成 FTP 心跳位缺席误复位。
|
|
* 持有约定:open...close / opendir...closedir 全程持锁;lftpd_io_read/
|
|
* write 假定锁已由 open 持有;stat/unlink 自行短持。
|
|
*/
|
|
static void lftpd_io_vol_lock_take(void) {
|
|
for (;;) {
|
|
if (vol_lock_wait(100u) != 0) {
|
|
return;
|
|
}
|
|
lftpd_task_wd_beat(); /* 看门狗:分片心跳(卷锁等待中) */
|
|
}
|
|
}
|
|
|
|
char *lftpd_io_canonicalize_path(const char *base, const char *name)
|
|
{
|
|
if (base == NULL) {
|
|
base = "";
|
|
}
|
|
if (name == NULL) {
|
|
name = "";
|
|
}
|
|
|
|
char *path;
|
|
if (name[0] == '/') {
|
|
path = (char *)pvPortMalloc(strlen(name) + 1);
|
|
if (path == NULL) return NULL;
|
|
strcpy(path, name);
|
|
} else {
|
|
size_t len = strlen(base) + 1 + strlen(name) + 1;
|
|
path = (char *)pvPortMalloc(len);
|
|
if (path == NULL) return NULL;
|
|
snprintf(path, len, "%s/%s", base, name);
|
|
}
|
|
|
|
char *abs_path = (char *)pvPortMalloc(strlen(path) + 2);
|
|
if (abs_path == NULL) {
|
|
vPortFree(path);
|
|
return NULL;
|
|
}
|
|
abs_path[0] = '\0';
|
|
|
|
char *p = path;
|
|
while (*p != '\0') {
|
|
while (*p == '/') p++;
|
|
if (*p == '\0') break;
|
|
|
|
char *seg_start = p;
|
|
while (*p != '\0' && *p != '/') p++;
|
|
|
|
char saved = *p;
|
|
*p = '\0';
|
|
|
|
if (strcmp(seg_start, ".") == 0) {
|
|
/* ignore */
|
|
} else if (strcmp(seg_start, "..") == 0) {
|
|
char *slash = strrchr(abs_path, '/');
|
|
if (slash != NULL) {
|
|
*slash = '\0';
|
|
}
|
|
} else {
|
|
strcat(abs_path, "/");
|
|
strcat(abs_path, seg_start);
|
|
}
|
|
|
|
*p = saved;
|
|
}
|
|
vPortFree(path);
|
|
|
|
if (strlen(abs_path) == 0) {
|
|
strcpy(abs_path, "/");
|
|
}
|
|
|
|
return abs_path;
|
|
}
|
|
|
|
int lftpd_io_open_read(const char *path)
|
|
{
|
|
if (s_file_open) {
|
|
DBG_ERROR("open_read '%s': already open", path);
|
|
return -1;
|
|
}
|
|
lftpd_io_vol_lock_take();
|
|
const char *fpath = to_fatfs_path(path);
|
|
FRESULT fr = f_open(&s_ftp_file, fpath, FA_READ);
|
|
if (fr != FR_OK) {
|
|
DBG_ERROR("open_read '%s': f_open err=%d", fpath, (int)fr);
|
|
vol_lock_release();
|
|
return -1;
|
|
}
|
|
s_file_open = 1;
|
|
return 0;
|
|
}
|
|
|
|
int lftpd_io_open_write(const char *path)
|
|
{
|
|
if (s_file_open) {
|
|
DBG_ERROR("open_write '%s': already open", path);
|
|
return -1;
|
|
}
|
|
lftpd_io_vol_lock_take();
|
|
const char *fpath = to_fatfs_path(path);
|
|
FRESULT fr = f_open(&s_ftp_file, fpath, FA_WRITE | FA_CREATE_ALWAYS);
|
|
if (fr != FR_OK) {
|
|
DBG_ERROR("open_write '%s': f_open err=%d", fpath, (int)fr);
|
|
vol_lock_release();
|
|
return -1;
|
|
}
|
|
s_file_open = 1;
|
|
return 0;
|
|
}
|
|
|
|
int lftpd_io_read(int handle, void *buf, int len)
|
|
{
|
|
(void)handle;
|
|
UINT br;
|
|
if (f_read(&s_ftp_file, buf, (UINT)len, &br) != FR_OK) {
|
|
return -1;
|
|
}
|
|
return (int)br;
|
|
}
|
|
|
|
int lftpd_io_write(int handle, const void *buf, int len)
|
|
{
|
|
(void)handle;
|
|
UINT bw;
|
|
if (f_write(&s_ftp_file, buf, (UINT)len, &bw) != FR_OK) {
|
|
return -1;
|
|
}
|
|
if ((int)bw != len) {
|
|
return -1;
|
|
}
|
|
return (int)bw;
|
|
}
|
|
|
|
void lftpd_io_close(int handle)
|
|
{
|
|
(void)handle;
|
|
if (s_file_open) {
|
|
f_close(&s_ftp_file);
|
|
s_file_open = 0;
|
|
vol_lock_release(); /* 卷锁:open...close 序列结束 */
|
|
}
|
|
}
|
|
|
|
int lftpd_io_stat(const char *path, uint32_t *p_size, int *p_is_dir)
|
|
{
|
|
FILINFO fno;
|
|
const char *fpath = to_fatfs_path(path);
|
|
|
|
lftpd_io_vol_lock_take();
|
|
if (f_stat(fpath, &fno) != FR_OK) {
|
|
vol_lock_release();
|
|
return -1;
|
|
}
|
|
|
|
if (p_size != NULL) {
|
|
*p_size = (uint32_t)fno.fsize;
|
|
}
|
|
if (p_is_dir != NULL) {
|
|
*p_is_dir = (fno.fattrib & AM_DIR) ? 1 : 0;
|
|
}
|
|
|
|
vol_lock_release();
|
|
return 0;
|
|
}
|
|
|
|
int lftpd_io_unlink(const char *path)
|
|
{
|
|
const char *fpath = to_fatfs_path(path);
|
|
lftpd_io_vol_lock_take();
|
|
if (f_unlink(fpath) == FR_OK) {
|
|
vol_lock_release();
|
|
return 0;
|
|
}
|
|
vol_lock_release();
|
|
return -1;
|
|
}
|
|
|
|
void *lftpd_io_opendir(const char *path)
|
|
{
|
|
DIR *dp = (DIR *)pvPortMalloc(sizeof(DIR));
|
|
if (dp == NULL) {
|
|
return NULL;
|
|
}
|
|
const char *fpath = to_fatfs_path(path);
|
|
lftpd_io_vol_lock_take();
|
|
if (f_opendir(dp, fpath) != FR_OK) {
|
|
vPortFree(dp);
|
|
vol_lock_release();
|
|
return NULL;
|
|
}
|
|
return (void *)dp; /* 卷锁持有至 lftpd_io_closedir */
|
|
}
|
|
|
|
int lftpd_io_readdir(void *dp, char *name, int name_max, uint32_t *p_size, int *p_is_dir)
|
|
{
|
|
FILINFO fno;
|
|
|
|
while (1) {
|
|
FRESULT fr = f_readdir((DIR *)dp, &fno);
|
|
if (fr != FR_OK || fno.fname[0] == 0) {
|
|
return -1;
|
|
}
|
|
if (fno.fname[0] == '.') {
|
|
continue;
|
|
}
|
|
break;
|
|
}
|
|
|
|
strncpy(name, fno.fname, (size_t)(name_max - 1));
|
|
name[name_max - 1] = '\0';
|
|
|
|
if (p_size != NULL) {
|
|
*p_size = (uint32_t)fno.fsize;
|
|
}
|
|
if (p_is_dir != NULL) {
|
|
*p_is_dir = (fno.fattrib & AM_DIR) ? 1 : 0;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
void lftpd_io_closedir(void *dp)
|
|
{
|
|
if (dp != NULL) {
|
|
f_closedir((DIR *)dp);
|
|
vPortFree(dp);
|
|
vol_lock_release(); /* 卷锁:opendir...closedir 序列结束 */
|
|
}
|
|
}
|