diff --git a/components/fatfs/src/vfs_fat.c b/components/fatfs/src/vfs_fat.c index a5a12d5b65..b1192b602c 100644 --- a/components/fatfs/src/vfs_fat.c +++ b/components/fatfs/src/vfs_fat.c @@ -389,6 +389,9 @@ static int vfs_fat_fstat(void* ctx, int fd, struct stat * st) FIL* file = &fat_ctx->files[fd]; st->st_size = f_size(file); st->st_mode = S_IRWXU | S_IRWXG | S_IRWXO | S_IFREG; + st->st_mtime = 0; + st->st_atime = 0; + st->st_ctime = 0; return 0; } @@ -422,6 +425,8 @@ static int vfs_fat_stat(void* ctx, const char * path, struct stat * st) ftime >>= 6; tm.tm_hour = (ftime & 0x1f); st->st_mtime = mktime(&tm); + st->st_atime = 0; + st->st_ctime = 0; return 0; } 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 3f81b151d5..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,12 +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_flags & SPIFFS_RDONLY)) { + vfs_spiffs_update_mtime(efs->fs, fd); + } return fd; } @@ -562,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) @@ -578,6 +587,9 @@ 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 = vfs_spiffs_get_mtime(&s); + st->st_atime = 0; + st->st_ctime = 0; return res; } @@ -597,6 +609,9 @@ 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; + st->st_mtime = vfs_spiffs_get_mtime(&s); + st->st_atime = 0; + st->st_ctime = 0; return res; } @@ -764,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 e412bfd0cf..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 (0) +#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