From d0b7f7926725da51328be5b125fbeab51fa77c2f Mon Sep 17 00:00:00 2001 From: swym <0xfd000000@gmail.com> Date: Wed, 14 Aug 2019 22:32:34 +0200 Subject: [PATCH] feat(fatfs): Implement opendir, readdir & unlink This commit adds 3 new API calls - epic_file_opendir() - epic_file_readdir() - epic_file_unlink() and 2 new functions in Pycardium - os.listdir() - os.unlink() --- epicardium/epicardium.h | 70 +++++++++++--- epicardium/fs/filesystem_fat.c | 171 ++++++++++++++++++++++++++------- epicardium/fs/internal.h | 3 + epicardium/modules/fileops.c | 43 +++++++++ pycardium/modules/os.c | 49 +++++++++- pycardium/modules/qstrdefs.h | 2 + 6 files changed, 285 insertions(+), 53 deletions(-) diff --git a/epicardium/epicardium.h b/epicardium/epicardium.h index c680d559..2dd6b63f 100644 --- a/epicardium/epicardium.h +++ b/epicardium/epicardium.h @@ -60,6 +60,9 @@ typedef _Bool bool; #define API_FILE_SEEK 0x46 #define API_FILE_TELL 0x47 #define API_FILE_STAT 0x48 +#define API_FILE_OPENDIR 0x49 +#define API_FILE_READDIR 0x4a +#define API_FILE_UNLINK 0x4b #define API_RTC_GET_SECONDS 0x50 #define API_RTC_SCHEDULE_ALARM 0x51 @@ -805,13 +808,28 @@ API( int epic_file_open(const char* filename, const char* modeString) ); -/** */ +/** + * epic_file_close + * + * :param int fd: descriptor returned by epic_file_opendir + * + * :return: ``0`` on success, negative on error + */ API(API_FILE_CLOSE, int epic_file_close(int fd)); /** */ API(API_FILE_READ, int epic_file_read(int fd, void* buf, size_t nbytes)); -/** */ +/** + * epic_file_write + * + * :param int fd: descriptor returned by epic_file_open + * :param const void* buf: data to write + * :param size_t nbytes: no of bytes to write + * + * :return: ``< 0`` on error, ``nbytes`` on success. (Partial writes don't occur on success!) + * +*/ API( API_FILE_WRITE, int epic_file_write(int fd, const void* buf, size_t nbytes) @@ -842,6 +860,9 @@ enum epic_stat_type { EPICSTAT_DIR, }; +#define EPICSTAT_MAX_PATH 255 //conveniently the same as FF_MAX_LFN + + /** */ struct epic_stat { /** Entity Type: file, directory or none */ @@ -860,21 +881,10 @@ struct epic_stat { /** Size in bytes. */ uint32_t size; - /** - * Which FAT volume this entity resides on. - * - * (will be needed later once we distinguish between system and user volume) - */ - uint8_t volume; - uint8_t _reserved[9]; + char name[EPICSTAT_MAX_PATH + 1]; + uint8_t _reserved[12]; }; -#ifndef __cplusplus -#if defined(__GNUC__) && ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)) -_Static_assert(sizeof(struct epic_stat) == 20, ""); -#endif -#endif - /** * stat path * @@ -887,6 +897,36 @@ API(API_FILE_STAT, int epic_file_stat( const char* path, struct epic_stat* stat )); +/** + * open directory + * + * :param char* path: directory to open + * + * :return: ``> 0`` on success, negative on error + */ +API(API_FILE_OPENDIR, int epic_file_opendir(const char* path)); + +/** + * readdir + * + * :param int fd: descriptor returned by epic_file_opendir + * :param epic_stat* stat: pointer to result - pass NULL to reset iteration offset of fd + * + * :return: ``0`` on success, negative on error + */ +API(API_FILE_READDIR, int epic_file_readdir(int fd, struct epic_stat* stat)); + +/** + * epic_file_unlink + * + * :param char* path: file to delete + * + * :return: ``0`` on success, negative on error + */ +API(API_FILE_UNLINK, int epic_file_unlink(const char* path)); + + + /** * RTC * === diff --git a/epicardium/fs/filesystem_fat.c b/epicardium/fs/filesystem_fat.c index 2ff00a67..05b7f4a0 100644 --- a/epicardium/fs/filesystem_fat.c +++ b/epicardium/fs/filesystem_fat.c @@ -65,6 +65,9 @@ static bool globalLockAccquire(); static void globalLockRelease(); static void efs_close_all(EpicFileSystem *fs); +/** + * if EPICSTAT_NONE is passed to `expected`, the type is not checked. + */ static bool efs_get_opened( EpicFileSystem *fs, int i, @@ -73,6 +76,19 @@ static bool efs_get_opened( int *rc ); +static bool +efs_get_new(EpicFileSystem *fs, uint32_t *idx, struct FatObject **obj, int *rc); + +static int efs_obj_init( + EpicFileSystem *fs, + struct FatObject *obj, + uint32_t index, + enum epic_stat_type type +); + +static void efs_obj_deinit(EpicFileSystem *fs, struct FatObject *obj); +static void efs_init_stat(struct epic_stat *stat, FILINFO *finfo); + static EpicFileSystem s_globalFileSystem; #if (EPIC_FAT_STATIC_SEMAPHORE == 1) @@ -225,8 +241,10 @@ static bool efs_get_opened( generation >= EPIC_FAT_FD_MAX_GENERATION) { return false; } - if (fs->pool[index].type != expected || - fs->pool[index].generation != generation) { + if (fs->pool[index].generation != generation) { + return false; + } + if (expected != EPICSTAT_NONE && fs->pool[index].type != expected) { return false; } @@ -235,6 +253,54 @@ static bool efs_get_opened( return true; } +static bool +efs_get_new(EpicFileSystem *fs, uint32_t *idx, struct FatObject **obj, int *rc) +{ + uint32_t index; + + *obj = NULL; + *rc = 0; + *idx = 0; + + //find free object to use + for (index = 0; index < EPIC_FAT_MAX_OPENED; ++index) { + if (fs->pool[index].type == EPICSTAT_NONE) { + break; + } + } + if (index == EPIC_FAT_MAX_OPENED) { + *rc = -s_libffToErrno[FR_TOO_MANY_OPEN_FILES]; + return false; + } + + *obj = &fs->pool[index]; + return true; +} + +static int efs_obj_init( + EpicFileSystem *fs, + struct FatObject *obj, + uint32_t index, + enum epic_stat_type type +) { + uint32_t generation; + + generation = fs->generationCount++; + if (generation == EPIC_FAT_FD_MAX_GENERATION) { + fs->generationCount = 1; + } + obj->type = type; + obj->generation = generation; + + return EPIC_FAT_FD(index, generation); +} + +static void efs_obj_deinit(EpicFileSystem *fs, struct FatObject *obj) +{ + obj->type = EPICSTAT_NONE; + obj->generation = 0; +} + /* here we're trying to mirror glibc's behaviour: * any combination of rwax parses but only the first of those flags wins: * - rw, ra, rr all open read-only @@ -282,58 +348,45 @@ static inline bool parse_mode(const char *mstring, int *mode) int efs_open(EpicFileSystem *fs, const char *filename, const char *modeString) { struct FatObject *o = NULL; - uint32_t index, generation; + uint32_t index; int mode = 0; int res; - //find free object to use - for (index = 0; index < EPIC_FAT_MAX_OPENED; ++index) { - if (fs->pool[index].type == EPICSTAT_NONE) { - break; - } - } - if (index == EPIC_FAT_MAX_OPENED) { - return -s_libffToErrno[FR_TOO_MANY_OPEN_FILES]; - } - - generation = fs->generationCount++; - if (generation == EPIC_FAT_FD_MAX_GENERATION) { - fs->generationCount = 1; - } - o = &fs->pool[index]; - if (!parse_mode(modeString, &mode)) { return -EINVAL; } + if (!efs_get_new(fs, &index, &o, &res)) { + return res; + } + res = f_open(&o->file, filename, mode); if (res != FR_OK) { return -s_libffToErrno[res]; } - o->type = EPICSTAT_FILE; - o->generation = generation; - // for 'a' mode, we must begin at the end of the file if ((mode & FA_OPEN_APPEND) != 0) { f_lseek(&o->file, f_size(&o->file)); } - return EPIC_FAT_FD(index, generation); + return efs_obj_init(fs, o, index, EPICSTAT_FILE); } int efs_close(EpicFileSystem *fs, int fd) { int res; struct FatObject *o; - if (efs_get_opened(fs, fd, EPICSTAT_FILE, &o, &res)) { - res = f_close(&o->file); + if (efs_get_opened(fs, fd, EPICSTAT_NONE, &o, &res)) { + if (o->type == EPICSTAT_FILE) { + res = f_close(&o->file); + } else { + res = f_closedir(&o->dir); + } if (res != FR_OK) { return -s_libffToErrno[res]; } - - o->type = EPICSTAT_NONE; - o->generation = 0; + efs_obj_deinit(fs, o); } return res; } @@ -346,13 +399,12 @@ void efs_close_all(EpicFileSystem *fs) f_close(&fs->pool[i].file); break; case EPICSTAT_DIR: - //NYI + f_closedir(&fs->pool[i].dir); break; case EPICSTAT_NONE: break; } - fs->pool[i].type = EPICSTAT_NONE; - fs->pool[i].generation = 0; + efs_obj_deinit(fs, &fs->pool[i]); } } @@ -440,18 +492,65 @@ int efs_tell(EpicFileSystem *fs, int fd) return res; } +static void efs_init_stat(struct epic_stat *stat, FILINFO *finfo) +{ + if (finfo->fname[0] != 0) { + if (finfo->fattrib & AM_DIR) { + stat->type = EPICSTAT_DIR; + } else { + stat->type = EPICSTAT_FILE; + } + strncpy(stat->name, finfo->fname, EPICSTAT_MAX_PATH); + } else { + stat->name[0] = 0; + stat->type = EPICSTAT_NONE; + } +} + int efs_stat(EpicFileSystem *fs, const char *filename, struct epic_stat *stat) { int res = 0; - FILINFO finfo; + static FILINFO finfo; res = f_stat(filename, &finfo); if (res == 0) { - if (finfo.fattrib & AM_DIR) { - stat->type = EPICSTAT_DIR; - } else { - stat->type = EPICSTAT_FILE; + efs_init_stat(stat, &finfo); + } + return -s_libffToErrno[res]; +} + +int efs_opendir(EpicFileSystem *fs, const char *path) +{ + int res; + struct FatObject *o; + uint32_t index; + + if (efs_get_new(fs, &index, &o, &res)) { + res = f_opendir(&o->dir, path); + if (res != FR_OK) { + return -s_libffToErrno[res]; } + return efs_obj_init(fs, o, index, EPICSTAT_DIR); } + return res; +} + +int efs_readdir(EpicFileSystem *fs, int fd, struct epic_stat *stat) +{ + int res; + struct FatObject *o; + if (efs_get_opened(fs, fd, EPICSTAT_DIR, &o, &res)) { + FILINFO finfo; + res = f_readdir(&o->dir, stat ? &finfo : NULL); + if (res == FR_OK && stat) { + efs_init_stat(stat, &finfo); + } + } + return res; +} + +int efs_unlink(EpicFileSystem *fs, const char *path) +{ + int res = f_unlink(path); return -s_libffToErrno[res]; } diff --git a/epicardium/fs/internal.h b/epicardium/fs/internal.h index b3f3acb3..97b878f0 100644 --- a/epicardium/fs/internal.h +++ b/epicardium/fs/internal.h @@ -26,6 +26,9 @@ int efs_flush(EpicFileSystem *fs, int fd); int efs_seek(EpicFileSystem *fs, int fd, long offset, int whence); int efs_tell(EpicFileSystem *fs, int fd); int efs_stat(EpicFileSystem *fs, const char *filename, struct epic_stat *stat); +int efs_opendir(EpicFileSystem *fs, const char *path); +int efs_readdir(EpicFileSystem *fs, int fd, struct epic_stat *stat); +int efs_unlink(EpicFileSystem *fs, const char *path); /** * lock global filesystem * diff --git a/epicardium/modules/fileops.c b/epicardium/modules/fileops.c index fc13417b..53dc7fd4 100644 --- a/epicardium/modules/fileops.c +++ b/epicardium/modules/fileops.c @@ -8,6 +8,16 @@ #include "fs/internal.h" +#if defined(__GNUC__) && \ + ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)) +#define HAVE_STATIC_ASSERT 1 +#elif defined(__clang__) +#define HAVE_STATIC_ASSERT 1 +#endif +#if HAVE_STATIC_ASSERT +_Static_assert(sizeof(struct epic_stat) == 276, ""); +#endif + int epic_file_open(const char *filename, const char *mode) { EpicFileSystem *fs; @@ -95,3 +105,36 @@ int epic_file_stat(const char *filename, struct epic_stat *stat) } return res; } + +int epic_file_opendir(const char *path) +{ + EpicFileSystem *fs; + int res = efs_lock_global(&fs); + if (res == 0) { + res = efs_opendir(fs, path); + efs_unlock_global(fs); + } + return res; +} + +int epic_file_readdir(int fd, struct epic_stat *stat) +{ + EpicFileSystem *fs; + int res = efs_lock_global(&fs); + if (res == 0) { + res = efs_readdir(fs, fd, stat); + efs_unlock_global(fs); + } + return res; +} + +int epic_file_unlink(const char *path) +{ + EpicFileSystem *fs; + int res = efs_lock_global(&fs); + if (res == 0) { + res = efs_unlink(fs, path); + efs_unlock_global(fs); + } + return res; +} diff --git a/pycardium/modules/os.c b/pycardium/modules/os.c index 3f84c26e..e5c625ef 100644 --- a/pycardium/modules/os.c +++ b/pycardium/modules/os.c @@ -48,13 +48,58 @@ static mp_obj_t mp_os_exec(mp_obj_t name_in) } static MP_DEFINE_CONST_FUN_OBJ_1(exec_obj, mp_os_exec); -static const mp_rom_map_elem_t os_module_gobals_table[] = { +static mp_obj_t mp_os_listdir(mp_obj_t py_path) +{ + const char *path = mp_obj_str_get_str(py_path); + int fd = epic_file_opendir(path); + + if (fd < 0) { + mp_raise_OSError(-fd); + } + struct epic_stat entry; + mp_obj_list_t *list = mp_obj_new_list(0, NULL); + for (;;) { + int res = epic_file_readdir(fd, &entry); + if (res < 0) { + m_del_obj(mp_obj_list_t, list); + epic_file_close(fd); + mp_raise_OSError(-res); + } + if (entry.type == EPICSTAT_NONE) { + break; + } + mp_obj_list_append( + list, mp_obj_new_str(entry.name, strlen(entry.name)) + ); + } + epic_file_close(fd); + return MP_OBJ_FROM_PTR(list); +} +static MP_DEFINE_CONST_FUN_OBJ_1(listdir_obj, mp_os_listdir); + +static mp_obj_t mp_os_unlink(mp_obj_t py_path) +{ + const char *path = mp_obj_str_get_str(py_path); + int rc = epic_file_unlink(path); + + if (rc < 0) { + mp_raise_OSError(-rc); + } + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_1(unlink_obj, mp_os_unlink); + +static const mp_rom_map_elem_t os_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_os) }, { MP_ROM_QSTR(MP_QSTR_exit), MP_ROM_PTR(&exit_obj) }, { MP_ROM_QSTR(MP_QSTR_exec), MP_ROM_PTR(&exec_obj) }, + { MP_ROM_QSTR(MP_QSTR_listdir), MP_ROM_PTR(&listdir_obj) }, + { MP_ROM_QSTR(MP_QSTR_unlink), MP_ROM_PTR(&unlink_obj) }, }; -static MP_DEFINE_CONST_DICT(os_module_globals, os_module_gobals_table); +static MP_DEFINE_CONST_DICT(os_module_globals, os_module_globals_table); + +// Define module object. const mp_obj_module_t os_module = { .base = { &mp_type_module }, .globals = (mp_obj_dict_t *)&os_module_globals, diff --git a/pycardium/modules/qstrdefs.h b/pycardium/modules/qstrdefs.h index f25307d5..2da8ea8e 100644 --- a/pycardium/modules/qstrdefs.h +++ b/pycardium/modules/qstrdefs.h @@ -89,3 +89,5 @@ Q(write) Q(os) Q(exit) Q(exec) +Q(listdir) +Q(unlink) -- GitLab