#include #include #include #include #include "ff.h" #include "private/lftpd_io.h" #define DBG_TAG "[LFT_IO]" #include "dbg_log.h" /* * FatFS 在 FF_USE_LFN=0 时不接受 "/" 前缀路径。 * 剥离前导 "/" 后用 FatFS 兼容路径,根目录用 "." 替代空字符串。 */ static const char *to_fatfs_path(const char *path) { if (path == NULL || *path == '\0') { return "."; } if (*path == '/') { path++; if (*path == '\0') { return "."; } } return path; } static int s_file_open = 0; static FIL s_ftp_file; 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 *)malloc(strlen(name) + 1); if (path == NULL) return NULL; strcpy(path, name); } else { size_t len = strlen(base) + 1 + strlen(name) + 1; path = (char *)malloc(len); if (path == NULL) return NULL; snprintf(path, len, "%s/%s", base, name); } char *abs_path = (char *)malloc(strlen(path) + 2); if (abs_path == NULL) { free(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; } free(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; } 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); 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; } 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); 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; } } int lftpd_io_stat(const char *path, uint32_t *p_size, int *p_is_dir) { FILINFO fno; const char *fpath = to_fatfs_path(path); if (f_stat(fpath, &fno) != FR_OK) { 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; } return 0; } int lftpd_io_unlink(const char *path) { const char *fpath = to_fatfs_path(path); if (f_unlink(fpath) == FR_OK) { return 0; } return -1; } void *lftpd_io_opendir(const char *path) { DIR *dp = (DIR *)malloc(sizeof(DIR)); if (dp == NULL) { return NULL; } const char *fpath = to_fatfs_path(path); if (f_opendir(dp, fpath) != FR_OK) { free(dp); return NULL; } return (void *)dp; } int lftpd_io_readdir(void *dp, char *name, int name_max, uint32_t *p_size, int *p_is_dir) { FILINFO fno; FRESULT fr; while (1) { 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); free(dp); } }