Verified Commit d0b7f792 authored by swym's avatar swym Committed by Rahix
Browse files

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()
parent ea085187
......@@ -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
* ===
......
......@@ -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];
}
......
......@@ -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
*
......
......@@ -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;
}
......@@ -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,
......
......@@ -89,3 +89,5 @@ Q(write)
Q(os)
Q(exit)
Q(exec)
Q(listdir)
Q(unlink)
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment