diff --git a/components/spiffs/Kconfig b/components/spiffs/Kconfig index d82ceec73e..1f7a196d72 100644 --- a/components/spiffs/Kconfig +++ b/components/spiffs/Kconfig @@ -88,6 +88,25 @@ config SPIFFS_USE_MAGIC_LENGTH configured and formatted for 4 megabytes will not be accepted for mounting with a configuration defining the filesystem as 2 megabytes. +config SPIFFS_META_LENGTH + int "Size of per-file metadata field" + default 4 + help + This option sets the number of extra bytes stored in the file header. + These bytes can be used in an application-specific manner. + Set this to at least 4 bytes to enable support for saving file + modification time. + +config SPIFFS_USE_MTIME + bool "Save file modification time" + default "y" + depends on SPIFFS_META_LENGTH >= 4 + help + If enabled, then the first 4 bytes of per-file metadata will be used + to store file modification time (mtime), accessible through + stat/fstat functions. + Modification time is updated when the file is opened. + menu "Debug Configuration" config SPIFFS_DBG diff --git a/components/spiffs/esp_spiffs.c b/components/spiffs/esp_spiffs.c index 4520c81fa6..2c454e9dd3 100644 --- a/components/spiffs/esp_spiffs.c +++ b/components/spiffs/esp_spiffs.c @@ -33,6 +33,10 @@ static const char * TAG = "SPIFFS"; +#ifdef CONFIG_SPIFFS_USE_MTIME +_Static_assert(CONFIG_SPIFFS_META_LENGTH >= sizeof(time_t), + "SPIFFS_META_LENGTH size should be >= sizeof(time_t)"); +#endif //CONFIG_SPIFFS_USE_MTIME /** * @brief SPIFFS definition structure */ @@ -80,6 +84,8 @@ static long vfs_spiffs_telldir(void* ctx, DIR* pdir); static void vfs_spiffs_seekdir(void* ctx, DIR* pdir, long offset); static int vfs_spiffs_mkdir(void* ctx, const char* name, mode_t mode); static int vfs_spiffs_rmdir(void* ctx, const char* name); +static void vfs_spiffs_update_mtime(spiffs *fs, spiffs_file f); +static time_t vfs_spiffs_get_mtime(const spiffs_stat* s); static esp_spiffs_t * _efs[CONFIG_SPIFFS_MAX_PARTITIONS]; @@ -507,22 +513,16 @@ static int vfs_spiffs_open(void* ctx, const char * path, int flags, int mode) { assert(path); esp_spiffs_t * efs = (esp_spiffs_t *)ctx; - int fd = SPIFFS_open(efs->fs, path, spiffs_mode_conv(flags), mode); + int spiffs_flags = spiffs_mode_conv(flags); + int fd = SPIFFS_open(efs->fs, path, spiffs_flags, mode); if (fd < 0) { errno = spiffs_res_to_errno(SPIFFS_errno(efs->fs)); SPIFFS_clearerr(efs->fs); return -1; } -#if SPIFFS_OBJ_META_LEN > 0 - if (!(spiffs_mode_conv(flags) & SPIFFS_O_RDONLY)) - { - time_t t = time(NULL); - struct tm tmr; - localtime_r(&t, &tmr); - time_t meta = mktime(&tmr); - SPIFFS_fupdate_meta(efs->fs, fd, &meta); - } -#endif + if (!(spiffs_flags & SPIFFS_RDONLY)) { + vfs_spiffs_update_mtime(efs->fs, fd); + } return fd; } @@ -572,7 +572,6 @@ static off_t vfs_spiffs_lseek(void* ctx, int fd, off_t offset, int mode) return -1; } return res; - } static int vfs_spiffs_fstat(void* ctx, int fd, struct stat * st) @@ -588,7 +587,7 @@ static int vfs_spiffs_fstat(void* ctx, int fd, struct stat * st) } st->st_size = s.size; st->st_mode = S_IRWXU | S_IRWXG | S_IRWXO | S_IFREG; - st->st_mtime = 0; + st->st_mtime = vfs_spiffs_get_mtime(&s); st->st_atime = 0; st->st_ctime = 0; return res; @@ -610,13 +609,7 @@ static int vfs_spiffs_stat(void* ctx, const char * path, struct stat * st) st->st_size = s.size; st->st_mode = S_IRWXU | S_IRWXG | S_IRWXO; st->st_mode |= (s.type == SPIFFS_TYPE_DIR)?S_IFDIR:S_IFREG; -#if SPIFFS_OBJ_META_LEN > 0 - time_t t =0; - memcpy(&t, s.meta, sizeof(time_t)); - st->st_mtime = t; -#else - st->st_mtime = 0; -#endif + st->st_mtime = vfs_spiffs_get_mtime(&s); st->st_atime = 0; st->st_ctime = 0; return res; @@ -786,3 +779,31 @@ static int vfs_spiffs_link(void* ctx, const char* n1, const char* n2) errno = ENOTSUP; return -1; } + +static void vfs_spiffs_update_mtime(spiffs *fs, spiffs_file fd) +{ +#ifdef CONFIG_SPIFFS_USE_MTIME + time_t t = time(NULL); + spiffs_stat s; + int ret = SPIFFS_OK; + if (CONFIG_SPIFFS_META_LENGTH > sizeof(t)) { + ret = SPIFFS_fstat(fs, fd, &s); + } + if (ret == SPIFFS_OK) { + memcpy(s.meta, &t, sizeof(t)); + ret = SPIFFS_fupdate_meta(fs, fd, s.meta); + } + if (ret != SPIFFS_OK) { + ESP_LOGW(TAG, "Failed to update mtime (%d)", ret); + } +#endif //CONFIG_SPIFFS_USE_MTIME +} + +static time_t vfs_spiffs_get_mtime(const spiffs_stat* s) +{ + time_t t = 0; +#ifdef CONFIG_SPIFFS_USE_MTIME + memcpy(&t, s->meta, sizeof(t)); +#endif + return t; +} diff --git a/components/spiffs/include/spiffs_config.h b/components/spiffs/include/spiffs_config.h index c96a3412d3..28414facf9 100755 --- a/components/spiffs/include/spiffs_config.h +++ b/components/spiffs/include/spiffs_config.h @@ -158,7 +158,7 @@ extern void spiffs_api_unlock(struct spiffs_t *fs); // This is derived from following: // logical_page_size - (SPIFFS_OBJ_NAME_LEN + sizeof(spiffs_page_header) + // spiffs_object_ix_header fields + at least some LUT entries) -#define SPIFFS_OBJ_META_LEN (4) +#define SPIFFS_OBJ_META_LEN (CONFIG_SPIFFS_META_LENGTH) // Size of buffer allocated on stack used when copying data. // Lower value generates more read/writes. No meaning having it bigger diff --git a/components/spiffs/test/test_spiffs.c b/components/spiffs/test/test_spiffs.c index daf11ddc94..b428de4ceb 100644 --- a/components/spiffs/test/test_spiffs.c +++ b/components/spiffs/test/test_spiffs.c @@ -505,3 +505,45 @@ TEST_CASE("multiple tasks can use same volume", "[spiffs]") test_spiffs_concurrent("/spiffs/f"); test_teardown(); } + +#ifdef CONFIG_SPIFFS_USE_MTIME +TEST_CASE("mtime is updated when file is opened", "[spiffs]") +{ + /* Open a file, check that mtime is set correctly */ + const char* filename = "/spiffs/time"; + test_setup(); + time_t t_before_create = time(NULL); + test_spiffs_create_file_with_text(filename, "\n"); + time_t t_after_create = time(NULL); + + struct stat st; + TEST_ASSERT_EQUAL(0, stat(filename, &st)); + printf("mtime=%d\n", (int) st.st_mtime); + TEST_ASSERT(st.st_mtime >= t_before_create + && st.st_mtime <= t_after_create); + + /* Wait a bit, open again, check that mtime is updated */ + vTaskDelay(2000 / portTICK_PERIOD_MS); + time_t t_before_open = time(NULL); + FILE *f = fopen(filename, "a"); + time_t t_after_open = time(NULL); + TEST_ASSERT_EQUAL(0, fstat(fileno(f), &st)); + printf("mtime=%d\n", (int) st.st_mtime); + TEST_ASSERT(st.st_mtime >= t_before_open + && st.st_mtime <= t_after_open); + fclose(f); + + /* Wait a bit, open for reading, check that mtime is not updated */ + vTaskDelay(2000 / portTICK_PERIOD_MS); + time_t t_before_open_ro = time(NULL); + f = fopen(filename, "r"); + TEST_ASSERT_EQUAL(0, fstat(fileno(f), &st)); + printf("mtime=%d\n", (int) st.st_mtime); + TEST_ASSERT(t_before_open_ro > t_after_open + && st.st_mtime >= t_before_open + && st.st_mtime <= t_after_open); + fclose(f); + + test_teardown(); +} +#endif // CONFIG_SPIFFS_USE_MTIME