diff --git a/docs/README.md b/docs/README.md
index cdf67761..0bfa0d58 100644
--- a/docs/README.md
+++ b/docs/README.md
@@ -244,10 +244,12 @@ Here is a list of build constants and their default values:
|MG_ENABLE_DIRLIST | 0 | Enable directory listing |
|MG_ENABLE_CUSTOM_RANDOM | 0 | Provide custom RNG function `mg_random()` |
|MG_ENABLE_PACKED_FS | 0 | Enable embedded FS support |
+|MG_ENABLE_FATFS | 0 | Enable embedded FAT FS support |
|MG_IO_SIZE | 2048 | Granularity of the send/recv IO buffer growth |
|MG_MAX_RECV_BUF_SIZE | (3 * 1024 * 1024) | Maximum recv buffer size |
|MG_MAX_HTTP_HEADERS | 40 | Maximum number of HTTP headers |
|MG_HTTP_INDEX | "index.html" | Index file for HTML directory |
+|MG_FATFS_ROOT | "/" | FAT FS root directory |
|MG_ENABLE_LINES | undefined | If defined, show source file names in logs |
NOTE: the `MG_IO_SIZE` constant also sets
diff --git a/mongoose.c b/mongoose.c
index 4db842f2..86e9cf43 100644
--- a/mongoose.c
+++ b/mongoose.c
@@ -433,6 +433,87 @@ void mg_fs_close(struct mg_fd *fd) {
}
}
+#ifdef MG_ENABLE_LINES
+#line 1 "src/fs_fat.c"
+#endif
+#if MG_ENABLE_FATFS
+#include
+
+#if !defined(MG_FATFS_ROOT)
+#define MG_FATFS_ROOT "/"
+#endif
+
+static int ff_stat(const char *path, size_t *size, time_t *mtime) {
+ FILINFO fi;
+ if (path[0] == '\0' || strcmp(path, MG_FATFS_ROOT) == 0) {
+ if (size) *size = 0;
+ if (mtime) *mtime = 0;
+ return MG_FS_DIR;
+ } else if (f_stat(path, &fi) == 0) {
+ if (size) *size = (size_t) fi.fsize;
+ if (mtime) *mtime = (fi.fdate << 16) | fi.ftime;
+ return MG_FS_READ | MG_FS_WRITE | ((fi.fattrib & AM_DIR) ? MG_FS_DIR : 0);
+ } else {
+ return 0;
+ }
+}
+
+static void ff_list(const char *dir, void (*fn)(const char *, void *),
+ void *userdata) {
+ DIR d;
+ FILINFO fi;
+ if (f_opendir(&d, dir) == FR_OK) {
+ while (f_readdir(&d, &fi) == FR_OK && fi.fname[0] != '\0') {
+ if (!strcmp(fi.fname, ".") || !strcmp(fi.fname, "..")) continue;
+ fn(fi.fname, userdata);
+ }
+ f_closedir(&d);
+ }
+}
+
+static void *ff_open(const char *path, int flags) {
+ FIL f;
+ const char mode = flags == (MG_FS_READ | MG_FS_WRITE) ? FA_READ | FA_WRITE
+ : flags & MG_FS_READ ? FA_READ
+ : flags & MG_FS_WRITE ? FA_WRITE
+ : 0;
+ if (f_open(&f, path, mode) == 0) {
+ FIL *fp = calloc(1, sizeof(*fp));
+ *fp = f;
+ return fp;
+ } else {
+ return NULL;
+ }
+}
+
+static void ff_close(void *fp) {
+ if (fp != NULL) {
+ f_close((FIL *) fp);
+ free(fp);
+ }
+}
+
+static size_t ff_read(void *fp, void *buf, size_t len) {
+ unsigned n = 0;
+ f_read((FIL *) fp, buf, len, &n);
+ return n;
+}
+
+static size_t ff_write(void *fp, const void *buf, size_t len) {
+ unsigned n = 0;
+ f_write((FIL *) fp, buf, len, &n);
+ return n;
+}
+
+static size_t ff_seek(void *fp, size_t offset) {
+ f_lseek((FIL *) fp, offset);
+ return offset;
+}
+
+struct mg_fs mg_fs_fat = {ff_stat, ff_list, ff_open, ff_close,
+ ff_read, ff_write, ff_seek};
+#endif
+
#ifdef MG_ENABLE_LINES
#line 1 "src/fs_packed.c"
#endif
diff --git a/mongoose.h b/mongoose.h
index 60246657..9be79678 100644
--- a/mongoose.h
+++ b/mongoose.h
@@ -163,6 +163,11 @@ static __inline struct tm *localtime_r(const time_t *t, struct tm *tm) {
#endif
+#if defined(MG_ENABLE_FF) && MG_ENABLE_FF == 1
+#include
+#endif
+
+
#if MG_ARCH == MG_ARCH_FREERTOS_LWIP
#include
@@ -441,6 +446,10 @@ static __inline struct tm *localtime_r(const time_t *t, struct tm *tm) {
#endif
+#ifndef MG_ENABLE_FATFS
+#define MG_ENABLE_FATFS 0
+#endif
+
#ifndef MG_ENABLE_SOCKET
#define MG_ENABLE_SOCKET 1
#endif
@@ -699,6 +708,7 @@ void mg_fs_close(struct mg_fd *fd);
extern struct mg_fs mg_fs_posix; // POSIX open/close/read/write/seek
extern struct mg_fs mg_fs_packed; // Packed FS, see examples/complete
+extern struct mg_fs mg_fs_fat; // FAT FS
diff --git a/src/config.h b/src/config.h
index 8cad23eb..8fbeba60 100644
--- a/src/config.h
+++ b/src/config.h
@@ -1,5 +1,9 @@
#pragma once
+#ifndef MG_ENABLE_FATFS
+#define MG_ENABLE_FATFS 0
+#endif
+
#ifndef MG_ENABLE_SOCKET
#define MG_ENABLE_SOCKET 1
#endif
diff --git a/src/fs.h b/src/fs.h
index efbe5275..a05f9438 100644
--- a/src/fs.h
+++ b/src/fs.h
@@ -28,3 +28,4 @@ void mg_fs_close(struct mg_fd *fd);
extern struct mg_fs mg_fs_posix; // POSIX open/close/read/write/seek
extern struct mg_fs mg_fs_packed; // Packed FS, see examples/complete
+extern struct mg_fs mg_fs_fat; // FAT FS
diff --git a/src/fs_fat.c b/src/fs_fat.c
new file mode 100644
index 00000000..04079c55
--- /dev/null
+++ b/src/fs_fat.c
@@ -0,0 +1,77 @@
+#if MG_ENABLE_FATFS
+#include
+
+#if !defined(MG_FATFS_ROOT)
+#define MG_FATFS_ROOT "/"
+#endif
+
+static int ff_stat(const char *path, size_t *size, time_t *mtime) {
+ FILINFO fi;
+ if (path[0] == '\0' || strcmp(path, MG_FATFS_ROOT) == 0) {
+ if (size) *size = 0;
+ if (mtime) *mtime = 0;
+ return MG_FS_DIR;
+ } else if (f_stat(path, &fi) == 0) {
+ if (size) *size = (size_t) fi.fsize;
+ if (mtime) *mtime = (fi.fdate << 16) | fi.ftime;
+ return MG_FS_READ | MG_FS_WRITE | ((fi.fattrib & AM_DIR) ? MG_FS_DIR : 0);
+ } else {
+ return 0;
+ }
+}
+
+static void ff_list(const char *dir, void (*fn)(const char *, void *),
+ void *userdata) {
+ DIR d;
+ FILINFO fi;
+ if (f_opendir(&d, dir) == FR_OK) {
+ while (f_readdir(&d, &fi) == FR_OK && fi.fname[0] != '\0') {
+ if (!strcmp(fi.fname, ".") || !strcmp(fi.fname, "..")) continue;
+ fn(fi.fname, userdata);
+ }
+ f_closedir(&d);
+ }
+}
+
+static void *ff_open(const char *path, int flags) {
+ FIL f;
+ const char mode = flags == (MG_FS_READ | MG_FS_WRITE) ? FA_READ | FA_WRITE
+ : flags & MG_FS_READ ? FA_READ
+ : flags & MG_FS_WRITE ? FA_WRITE
+ : 0;
+ if (f_open(&f, path, mode) == 0) {
+ FIL *fp = calloc(1, sizeof(*fp));
+ *fp = f;
+ return fp;
+ } else {
+ return NULL;
+ }
+}
+
+static void ff_close(void *fp) {
+ if (fp != NULL) {
+ f_close((FIL *) fp);
+ free(fp);
+ }
+}
+
+static size_t ff_read(void *fp, void *buf, size_t len) {
+ unsigned n = 0;
+ f_read((FIL *) fp, buf, len, &n);
+ return n;
+}
+
+static size_t ff_write(void *fp, const void *buf, size_t len) {
+ unsigned n = 0;
+ f_write((FIL *) fp, buf, len, &n);
+ return n;
+}
+
+static size_t ff_seek(void *fp, size_t offset) {
+ f_lseek((FIL *) fp, offset);
+ return offset;
+}
+
+struct mg_fs mg_fs_fat = {ff_stat, ff_list, ff_open, ff_close,
+ ff_read, ff_write, ff_seek};
+#endif