From 796e6d94032c408fb9a87bc11ace55b7ed6229ce Mon Sep 17 00:00:00 2001
From: Serge Bazanski <q3k@q3k.org>
Date: Sun, 30 Jul 2023 18:58:37 +0200
Subject: [PATCH] st3m: implement dumb VFS for /

This allows us to os.listdir("/").
---
 components/st3m/CMakeLists.txt |   1 +
 components/st3m/st3m_fs.c      | 127 +++++++++++++++++++++++++++++++++
 components/st3m/st3m_fs.h      |  10 +++
 main/fs.c                      |  21 +-----
 main/fs.h                      |   9 +--
 main/main.c                    |   2 +-
 6 files changed, 143 insertions(+), 27 deletions(-)
 create mode 100644 components/st3m/st3m_fs.c
 create mode 100644 components/st3m/st3m_fs.h

diff --git a/components/st3m/CMakeLists.txt b/components/st3m/CMakeLists.txt
index 2aac4cd768..d6cb7b5964 100644
--- a/components/st3m/CMakeLists.txt
+++ b/components/st3m/CMakeLists.txt
@@ -16,6 +16,7 @@ idf_component_register(
         st3m_captouch.c
         st3m_ringbuffer.c
         st3m_tar.c
+        st3m_fs.c
     INCLUDE_DIRS
         .
     REQUIRES
diff --git a/components/st3m/st3m_fs.c b/components/st3m/st3m_fs.c
new file mode 100644
index 0000000000..7738d1c93a
--- /dev/null
+++ b/components/st3m/st3m_fs.c
@@ -0,0 +1,127 @@
+#include "st3m_fs.h"
+
+#include "st3m_mode.h"
+#include "st3m_sys_data.h"
+#include "st3m_tar.h"
+
+#include "esp_system.h"
+#include "esp_vfs.h"
+#include "esp_vfs_fat.h"
+
+#include <errno.h>
+
+static const char *TAG = "st3m-fs";
+
+// Entries for root filesystem. Ideally this wouldn't be static but would
+// consult the ESP-IDF VFS registry.
+static struct dirent _root_dirents[4] = {
+	{ .d_ino = 1, .d_name = ".", .d_type = DT_DIR, },
+	{ .d_ino = 2, .d_name = "flash", .d_type = DT_DIR, },
+	{ .d_ino = 3, .d_name = "sd", .d_type = DT_DIR, },
+	{ .d_ino = 4, .d_name = "console", .d_type = DT_DIR, },
+};
+
+// Handle of the wear levelling library instance
+static wl_handle_t s_wl_handle = WL_INVALID_HANDLE;
+
+static int _root_vfs_close(int fd) {
+    return 0;
+} 
+
+static int _root_vfs_fcntl(int fd, int cmd, int arg) {
+    return 0;
+}       
+
+static int _root_vfs_fstat(int fd, struct stat *st) {
+    st->st_mode = S_IFDIR;
+    return 0;
+}
+
+static int _root_vfs_open(const char *path, int flags, int mode) {
+	if (strcmp(path, "/") == 0) {
+		if (flags == 0) {
+			return 0;
+		}
+		errno = EISDIR;
+	} else {
+		errno = ENOENT;
+	}
+	return -1;
+}
+
+static ssize_t _root_vfs_read(int fd, void *data, size_t size) {
+	errno = EISDIR;
+	return -1;
+}
+
+static ssize_t _root_vfs_write(int fd, const void *data, size_t size) {
+	errno = EISDIR;
+	return -1;
+}
+
+typedef struct {
+	DIR _inner;
+	size_t ix;
+} st3m_rootfs_dir_t;
+
+static DIR *_root_vfs_opendir(const char *name) {
+	if (strcmp(name, "/") != 0) {
+		errno = ENOENT;
+		return NULL;
+	}
+
+	st3m_rootfs_dir_t *dir = calloc(1, sizeof(st3m_rootfs_dir_t));
+	assert(dir != NULL);
+	return (DIR*)dir;
+}
+
+static struct dirent *_root_vfs_readdir(DIR *pdir) {
+	st3m_rootfs_dir_t *dir = pdir;
+	if (dir->ix >= (sizeof(_root_dirents)/sizeof(struct dirent))) {
+		return NULL;
+	}
+	return &_root_dirents[dir->ix++];
+}
+
+static int _root_vfs_closedir(DIR *pdir) {
+	free(pdir);
+	return 0;
+}
+
+// Root filesystem implementation, because otherwise os.listdir("/") fails...
+static esp_vfs_t _root_vfs = {
+	.flags = ESP_VFS_FLAG_DEFAULT,
+	.close = &_root_vfs_close,
+	.fcntl = &_root_vfs_fcntl,
+	.fstat = &_root_vfs_fstat,
+	.open = &_root_vfs_open,
+	.read = &_root_vfs_read,
+	.write = &_root_vfs_write,
+	.opendir = &_root_vfs_opendir,
+	.readdir = &_root_vfs_readdir,
+	.closedir = &_root_vfs_closedir,
+};
+
+void st3m_fs_init(void) {
+	esp_err_t err;
+
+	if ((err = esp_vfs_register("", &_root_vfs, NULL)) != ESP_OK) {
+		ESP_LOGE(TAG, "Failed to mount root VFS: %s", esp_err_to_name(err));
+		return;
+	}
+
+    const esp_vfs_fat_mount_config_t mount_config = {
+        .max_files = 4,
+        .format_if_mount_failed = true,
+        .allocation_unit_size = CONFIG_WL_SECTOR_SIZE
+    };
+
+    err = esp_vfs_fat_spiflash_mount("/flash", "vfs", &mount_config,
+                                               &s_wl_handle);
+    if (err != ESP_OK) {
+        ESP_LOGE(TAG, "Failed to mount FAT FS: %s", esp_err_to_name(err));
+        return;
+    }
+
+    ESP_LOGI(TAG, "Mounted Flash VFS Partition at /flash");
+}
diff --git a/components/st3m/st3m_fs.h b/components/st3m/st3m_fs.h
new file mode 100644
index 0000000000..0164c17a8a
--- /dev/null
+++ b/components/st3m/st3m_fs.h
@@ -0,0 +1,10 @@
+#pragma once
+
+// Mount filesystem. An error will be loged if the mount failed.
+//
+// Filesystem layout:
+//  /flash - FAT from flash ('vfat' partition, wear leveled)
+//  /sd    - FAT from SD card, if available
+//
+// This function must not be called more than once.
+void st3m_fs_init(void);
\ No newline at end of file
diff --git a/main/fs.c b/main/fs.c
index 1fcab2a06e..19be43aa6b 100644
--- a/main/fs.c
+++ b/main/fs.c
@@ -3,6 +3,7 @@
 #include "st3m_mode.h"
 #include "st3m_sys_data.h"
 #include "st3m_tar.h"
+#include "st3m_fs.h"
 
 #include "esp_system.h"
 #include "esp_vfs.h"
@@ -38,24 +39,8 @@ static void _extract_sys_data(void) {
     fclose(f);
 }
 
-// Handle of the wear levelling library instance
-static wl_handle_t s_wl_handle = WL_INVALID_HANDLE;
-
-void st3m_fs_init(void) {
-    const esp_vfs_fat_mount_config_t mount_config = {
-        .max_files = 4,
-        .format_if_mount_failed = true,
-        .allocation_unit_size = CONFIG_WL_SECTOR_SIZE
-    };
-
-    esp_err_t err = esp_vfs_fat_spiflash_mount("/flash", "vfs", &mount_config,
-                                               &s_wl_handle);
-    if (err != ESP_OK) {
-        ESP_LOGE(TAG, "Failed to mount FAT FS: %s", esp_err_to_name(err));
-        return;
-    }
-
-    ESP_LOGI(TAG, "Mounted Flash VFS Partition at /flash");
+void flow3r_fs_init(void) {
+    st3m_fs_init();
 
     bool have_mpy = false;
     struct stat st;
diff --git a/main/fs.h b/main/fs.h
index 0164c17a8a..9e17f44a4f 100644
--- a/main/fs.h
+++ b/main/fs.h
@@ -1,10 +1,3 @@
 #pragma once
 
-// Mount filesystem. An error will be loged if the mount failed.
-//
-// Filesystem layout:
-//  /flash - FAT from flash ('vfat' partition, wear leveled)
-//  /sd    - FAT from SD card, if available
-//
-// This function must not be called more than once.
-void st3m_fs_init(void);
\ No newline at end of file
+void flow3r_fs_init(void);
\ No newline at end of file
diff --git a/main/main.c b/main/main.c
index 42470a697d..d620b586d2 100644
--- a/main/main.c
+++ b/main/main.c
@@ -60,7 +60,7 @@ void flow3r_startup(void) {
     flow3r_bsp_i2c_init();
     st3m_mode_set(st3m_mode_kind_starting, "fs");
     st3m_mode_update_display(NULL);
-    st3m_fs_init();
+    flow3r_fs_init();
     st3m_mode_set(st3m_mode_kind_starting, "audio");
     st3m_mode_update_display(NULL);
     st3m_scope_init();
-- 
GitLab