]> granicus.if.org Git - esp-idf/commitdiff
VFS: Implement utime()
authorRoland Dobai <dobai.roland@gmail.com>
Thu, 25 Oct 2018 09:53:52 +0000 (11:53 +0200)
committerRoland Dobai <dobai.roland@gmail.com>
Mon, 5 Nov 2018 08:54:01 +0000 (09:54 +0100)
13 files changed:
components/fatfs/src/ffconf.h
components/fatfs/src/vfs_fat.c
components/fatfs/test/test_fatfs_common.c
components/fatfs/test/test_fatfs_common.h
components/fatfs/test/test_fatfs_sdmmc.c
components/fatfs/test/test_fatfs_spiflash.c
components/newlib/CMakeLists.txt
components/newlib/platform_include/sys/utime.h [new file with mode: 0644]
components/newlib/utime.c [new file with mode: 0644]
components/spiffs/esp_spiffs.c
components/spiffs/test/test_spiffs.c
components/vfs/include/esp_vfs.h
components/vfs/vfs.c

index 1b1cf8c85ca0a6bb462a14d3d9930a8d060f0ec5..bf0e1a8e944cbd680cc7f3ca4bc706888f5a4ad4 100644 (file)
@@ -52,7 +52,7 @@
 /* This option switches f_expand function. (0:Disable or 1:Enable) */
 
 
-#define FF_USE_CHMOD   0
+#define FF_USE_CHMOD   1
 /* This option switches attribute manipulation functions, f_chmod() and f_utime().
 /  (0:Disable or 1:Enable) Also FF_FS_READONLY needs to be 0 to enable this option. */
 
index 6d7019a1e25b3f6b83e17de7ffef9ec6e696067c..9f139f6eb4f332702cfd104008f10fc75385a26d 100644 (file)
@@ -86,6 +86,7 @@ static int vfs_fat_mkdir(void* ctx, const char* name, mode_t mode);
 static int vfs_fat_rmdir(void* ctx, const char* name);
 static int vfs_fat_access(void* ctx, const char *path, int amode);
 static int vfs_fat_truncate(void* ctx, const char *path, off_t length);
+static int vfs_fat_utime(void* ctx, const char *path, const struct utimbuf *times);
 
 static vfs_fat_ctx_t* s_fat_ctxs[FF_VOLUMES] = { NULL, NULL };
 //backwards-compatibility with esp_vfs_fat_unregister()
@@ -146,6 +147,7 @@ esp_err_t esp_vfs_fat_register(const char* base_path, const char* fat_drive, siz
         .rmdir_p = &vfs_fat_rmdir,
         .access_p = &vfs_fat_access,
         .truncate_p = &vfs_fat_truncate,
+        .utime_p = &vfs_fat_utime,
     };
     size_t ctx_size = sizeof(vfs_fat_ctx_t) + max_files * sizeof(FIL);
     vfs_fat_ctx_t* fat_ctx = (vfs_fat_ctx_t*) calloc(1, ctx_size);
@@ -824,3 +826,55 @@ out:
     _lock_release(&fat_ctx->lock);
     return ret;
 }
+
+static int vfs_fat_utime(void *ctx, const char *path, const struct utimbuf *times)
+{
+    FILINFO filinfo_time;
+
+    {
+        struct tm tm_time;
+
+        if (times) {
+            localtime_r(&times->modtime, &tm_time);
+        } else {
+            // use current time
+            struct timeval tv;
+            gettimeofday(&tv, NULL);
+            localtime_r(&tv.tv_sec, &tm_time);
+        }
+
+        if (tm_time.tm_year < 80) {
+            // FATFS cannot handle years before 1980
+            errno = EINVAL;
+            return -1;
+        }
+
+        fat_date_t fdate;
+        fat_time_t ftime;
+
+        // this time transformation is esentially the reverse of the one in vfs_fat_stat()
+        fdate.mday = tm_time.tm_mday;
+        fdate.mon = tm_time.tm_mon + 1;     // January in fdate.mon is 1, and 0 in tm_time.tm_mon
+        fdate.year = tm_time.tm_year - 80;  // tm_time.tm_year=0 is 1900, tm_time.tm_year=0 is 1980
+        ftime.sec = tm_time.tm_sec / 2,     // ftime.sec counts seconds by 2
+        ftime.min = tm_time.tm_min;
+        ftime.hour = tm_time.tm_hour;
+
+        filinfo_time.fdate = fdate.as_int;
+        filinfo_time.ftime = ftime.as_int;
+    }
+
+    vfs_fat_ctx_t *fat_ctx = (vfs_fat_ctx_t *) ctx;
+    _lock_acquire(&fat_ctx->lock);
+    prepend_drive_to_path(fat_ctx, &path, NULL);
+    FRESULT res = f_utime(path, &filinfo_time);
+    _lock_release(&fat_ctx->lock);
+
+    if (res != FR_OK) {
+        ESP_LOGD(TAG, "%s: fresult=%d", __func__, res);
+        errno = fresult_to_errno(res);
+        return -1;
+    }
+
+    return 0;
+}
index 0e84794157413d6a06cc127b46f141abde294137..4eeb4aad404be64ee4818ecf76bf455400ffb48f 100644 (file)
@@ -19,6 +19,7 @@
 #include <sys/time.h>
 #include <sys/unistd.h>
 #include <errno.h>
+#include <utime.h>
 #include "unity.h"
 #include "esp_log.h"
 #include "esp_system.h"
@@ -246,6 +247,81 @@ void test_fatfs_stat(const char* filename, const char* root_dir)
     TEST_ASSERT_FALSE(st.st_mode & S_IFREG);
 }
 
+void test_fatfs_utime(const char* filename, const char* root_dir)
+{
+    struct stat achieved_stat;
+    struct tm desired_tm;
+    struct utimbuf desired_time = {
+        .actime = 0, // access time is not supported
+        .modtime = 0,
+    };
+    time_t false_now = 0;
+    memset(&desired_tm, 0, sizeof(struct tm));
+
+    {
+        // Setting up a false actual time - used when the file is created and for modification with the current time
+        desired_tm.tm_mon = 10 - 1;
+        desired_tm.tm_mday = 31;
+        desired_tm.tm_year = 2018 - 1900;
+        desired_tm.tm_hour = 10;
+        desired_tm.tm_min = 35;
+        desired_tm.tm_sec = 23;
+
+        false_now = mktime(&desired_tm);
+
+        struct timeval now = { .tv_sec = false_now };
+        settimeofday(&now, NULL);
+    }
+    test_fatfs_create_file_with_text(filename, "");
+
+    // 00:00:00. January 1st, 1980 - FATFS cannot handle earlier dates
+    desired_tm.tm_mon = 1 - 1;
+    desired_tm.tm_mday = 1;
+    desired_tm.tm_year = 1980 - 1900;
+    desired_tm.tm_hour = 0;
+    desired_tm.tm_min = 0;
+    desired_tm.tm_sec = 0;
+    printf("Testing mod. time: %s", asctime(&desired_tm));
+    desired_time.modtime = mktime(&desired_tm);
+    TEST_ASSERT_EQUAL(0, utime(filename, &desired_time));
+    TEST_ASSERT_EQUAL(0, stat(filename, &achieved_stat));
+    TEST_ASSERT_EQUAL_UINT32(desired_time.modtime, achieved_stat.st_mtime);
+
+    // current time
+    TEST_ASSERT_EQUAL(0, utime(filename, NULL));
+    TEST_ASSERT_EQUAL(0, stat(filename, &achieved_stat));
+    printf("Mod. time changed to (false actual time): %s", ctime(&achieved_stat.st_mtime));
+    TEST_ASSERT_NOT_EQUAL(desired_time.modtime, achieved_stat.st_mtime);
+    TEST_ASSERT(false_now - achieved_stat.st_mtime <= 2); // two seconds of tolerance are given
+
+    // 23:59:08. December 31st, 2037
+    desired_tm.tm_mon = 12 - 1;
+    desired_tm.tm_mday = 31;
+    desired_tm.tm_year = 2037 - 1900;
+    desired_tm.tm_hour = 23;
+    desired_tm.tm_min = 59;
+    desired_tm.tm_sec = 8;
+    printf("Testing mod. time: %s", asctime(&desired_tm));
+    desired_time.modtime = mktime(&desired_tm);
+    TEST_ASSERT_EQUAL(0, utime(filename, &desired_time));
+    TEST_ASSERT_EQUAL(0, stat(filename, &achieved_stat));
+    TEST_ASSERT_EQUAL_UINT32(desired_time.modtime, achieved_stat.st_mtime);
+
+    //WARNING: it has the Unix Millenium bug (Y2K38)
+
+    // 00:00:00. January 1st, 1970 - FATFS cannot handle years before 1980
+    desired_tm.tm_mon = 1 - 1;
+    desired_tm.tm_mday = 1;
+    desired_tm.tm_year = 1970 - 1900;
+    desired_tm.tm_hour = 0;
+    desired_tm.tm_min = 0;
+    desired_tm.tm_sec = 0;
+    printf("Testing mod. time: %s", asctime(&desired_tm));
+    desired_time.modtime = mktime(&desired_tm);
+    TEST_ASSERT_EQUAL(-1, utime(filename, &desired_time));
+    TEST_ASSERT_EQUAL(EINVAL, errno);
+}
+
 void test_fatfs_unlink(const char* filename)
 {
     test_fatfs_create_file_with_text(filename, "unlink\n");
index e5511ec43478248a5b4d74e103b49087194b0676..ba330b6b6b719f0235cf46b38c02665b1a2de52d 100644 (file)
@@ -49,6 +49,8 @@ void test_fatfs_truncate_file(const char* path);
 
 void test_fatfs_stat(const char* filename, const char* root_dir);
 
+void test_fatfs_utime(const char* filename, const char* root_dir);
+
 void test_fatfs_unlink(const char* filename);
 
 void test_fatfs_link_rename(const char* filename_prefix);
index 74ef2207b78d11998c7ea5e4bf9497e3417ae2cc..e1a6cfb3bd6c0333778a6ac157069104185427a0 100644 (file)
@@ -116,6 +116,13 @@ TEST_CASE("(SD) stat returns correct values", "[fatfs][test_env=UT_T1_SDMODE]")
     test_teardown();
 }
 
+TEST_CASE("(SD) utime sets modification time", "[fatfs][test_env=UT_T1_SDMODE]")
+{
+    test_setup();
+    test_fatfs_utime("/sdcard/utime.txt", "/sdcard");
+    test_teardown();
+}
+
 TEST_CASE("(SD) unlink removes a file", "[fatfs][test_env=UT_T1_SDMODE]")
 {
     test_setup();
index 9d07249480e128aaf399b4acb4a5ab0774e8dacb..6c33559395e9ecc7560654f00c56a1161b2fe330 100644 (file)
@@ -110,6 +110,13 @@ TEST_CASE("(WL) stat returns correct values", "[fatfs][wear_levelling]")
     test_teardown();
 }
 
+TEST_CASE("(WL) utime sets modification time", "[fatfs][wear_levelling]")
+{
+    test_setup();
+    test_fatfs_utime("/spiflash/utime.txt", "/spiflash");
+    test_teardown();
+}
+
 TEST_CASE("(WL) unlink removes a file", "[fatfs][wear_levelling]")
 {
     test_setup();
index a58cddeb7119c67dce8e707a0a81adcb80433283..a7b29c65484c5474d2ac34060b6dba453d292c4c 100644 (file)
@@ -6,6 +6,7 @@ set(COMPONENT_SRCS "locks.c"
                    "syscall_table.c"
                    "syscalls.c"
                    "termios.c"
+                   "utime.c"
                    "time.c")
 set(COMPONENT_ADD_INCLUDEDIRS platform_include include)
 
diff --git a/components/newlib/platform_include/sys/utime.h b/components/newlib/platform_include/sys/utime.h
new file mode 100644 (file)
index 0000000..3251d3c
--- /dev/null
@@ -0,0 +1,35 @@
+// Copyright 2018 Espressif Systems (Shanghai) PTE LTD
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef _UTIME_H_
+#define _UTIME_H_
+
+#include <sys/time.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct utimbuf {
+    time_t actime;       // access time
+    time_t modtime;      // modification time
+};
+
+int utime(const char *path, const struct utimbuf *times);
+
+#ifdef __cplusplus
+};
+#endif
+
+#endif /* _UTIME_H_ */
diff --git a/components/newlib/utime.c b/components/newlib/utime.c
new file mode 100644 (file)
index 0000000..c838fd4
--- /dev/null
@@ -0,0 +1,21 @@
+// Copyright 2018 Espressif Systems (Shanghai) PTE LTD
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include <utime.h>
+#include "esp_vfs.h"
+
+int utime(const char *path, const struct utimbuf *times)
+{
+    return esp_vfs_utime(path, times);
+}
index 853986e49956d1da2435585eef38a34bf7d76443..06cfe44c4ce9f87145f3359b3c001edd7263d8c5 100644 (file)
@@ -71,6 +71,7 @@ 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 int vfs_spiffs_utime(void *ctx, const char *path, const struct utimbuf *times);
 
 static esp_spiffs_t * _efs[CONFIG_SPIFFS_MAX_PARTITIONS];
 
@@ -347,7 +348,12 @@ esp_err_t esp_vfs_spiffs_register(const esp_vfs_spiffs_conf_t * conf)
         .seekdir_p = &vfs_spiffs_seekdir,
         .telldir_p = &vfs_spiffs_telldir,
         .mkdir_p = &vfs_spiffs_mkdir,
-        .rmdir_p = &vfs_spiffs_rmdir
+        .rmdir_p = &vfs_spiffs_rmdir,
+#ifdef CONFIG_SPIFFS_USE_MTIME
+        .utime_p = &vfs_spiffs_utime,
+#else
+        .utime_p = NULL,
+#endif // CONFIG_SPIFFS_USE_MTIME
     };
 
     esp_err_t err = esp_spiffs_init(conf);
@@ -744,3 +750,49 @@ static time_t vfs_spiffs_get_mtime(const spiffs_stat* s)
 #endif
     return t;
 }
+
+#ifdef CONFIG_SPIFFS_USE_MTIME
+static int vfs_spiffs_update_mtime_value(spiffs *fs, const char *path, time_t t)
+{
+    int ret = SPIFFS_OK;
+    spiffs_stat s;
+    if (CONFIG_SPIFFS_META_LENGTH > sizeof(t)) {
+        ret = SPIFFS_stat(fs, path, &s);
+    }
+    if (ret == SPIFFS_OK) {
+        memcpy(s.meta, &t, sizeof(t));
+        ret = SPIFFS_update_meta(fs, path, s.meta);
+    }
+    if (ret != SPIFFS_OK) {
+        ESP_LOGW(TAG, "Failed to update mtime (%d)", ret);
+    }
+    return ret;
+}
+#endif //CONFIG_SPIFFS_USE_MTIME
+
+#ifdef CONFIG_SPIFFS_USE_MTIME
+static int vfs_spiffs_utime(void *ctx, const char *path, const struct utimbuf *times)
+{
+    assert(path);
+
+    esp_spiffs_t *efs = (esp_spiffs_t *) ctx;
+    time_t t;
+
+    if (times) {
+        t = times->modtime;
+    } else {
+        // use current time
+        t = time(NULL);
+    }
+
+    int ret = vfs_spiffs_update_mtime_value(efs->fs, path, t);
+
+    if (ret != SPIFFS_OK) {
+        errno = spiffs_res_to_errno(SPIFFS_errno(efs->fs));
+        SPIFFS_clearerr(efs->fs);
+        return -1;
+    }
+
+    return 0;
+}
+#endif //CONFIG_SPIFFS_USE_MTIME
index cb2164ac1f8bb54f93aa9a462a9a01867ef7271b..0a70cbba7c23cbd1858aa5c40d38622813e61bd4 100644 (file)
@@ -649,4 +649,69 @@ TEST_CASE("mtime is updated when file is opened", "[spiffs]")
 
     test_teardown();
 }
+
+TEST_CASE("utime() works well", "[spiffs]")
+{
+    const char filename[] = "/spiffs/utime.txt";
+    struct stat achieved_stat;
+    struct tm desired_tm;
+    struct utimbuf desired_time = {
+        .actime = 0, // access time is not supported
+        .modtime = 0,
+    };
+    time_t false_now = 0;
+    memset(&desired_tm, 0, sizeof(struct tm));
+
+    test_setup();
+    {
+        // Setting up a false actual time - used when the file is created and for modification with the current time
+        desired_tm.tm_mon = 10 - 1;
+        desired_tm.tm_mday = 31;
+        desired_tm.tm_year = 2018 - 1900;
+        desired_tm.tm_hour = 10;
+        desired_tm.tm_min = 35;
+        desired_tm.tm_sec = 23;
+
+        false_now = mktime(&desired_tm);
+
+        struct timeval now = { .tv_sec = false_now };
+        settimeofday(&now, NULL);
+    }
+    test_spiffs_create_file_with_text(filename, "");
+
+    // 00:00:00. January 1st, 1900
+    desired_tm.tm_mon = 1 - 1;
+    desired_tm.tm_mday = 1;
+    desired_tm.tm_year = 0;
+    desired_tm.tm_hour = 0;
+    desired_tm.tm_min = 0;
+    desired_tm.tm_sec = 0;
+    printf("Testing mod. time: %s", asctime(&desired_tm));
+    desired_time.modtime = mktime(&desired_tm);
+    TEST_ASSERT_EQUAL(0, utime(filename, &desired_time));
+    TEST_ASSERT_EQUAL(0, stat(filename, &achieved_stat));
+    TEST_ASSERT_EQUAL_UINT32(desired_time.modtime, achieved_stat.st_mtime);
+
+    // 23:59:08. December 31st, 2145
+    desired_tm.tm_mon = 12 - 1;
+    desired_tm.tm_mday = 31;
+    desired_tm.tm_year = 2145 - 1900;
+    desired_tm.tm_hour = 23;
+    desired_tm.tm_min = 59;
+    desired_tm.tm_sec = 8;
+    printf("Testing mod. time: %s", asctime(&desired_tm));
+    desired_time.modtime = mktime(&desired_tm);
+    TEST_ASSERT_EQUAL(0, utime(filename, &desired_time));
+    TEST_ASSERT_EQUAL(0, stat(filename, &achieved_stat));
+    TEST_ASSERT_EQUAL_UINT32(desired_time.modtime, achieved_stat.st_mtime);
+
+    // Current time
+    TEST_ASSERT_EQUAL(0, utime(filename, NULL));
+    TEST_ASSERT_EQUAL(0, stat(filename, &achieved_stat));
+    printf("Mod. time changed to (false actual time): %s", ctime(&achieved_stat.st_mtime));
+    TEST_ASSERT_NOT_EQUAL(desired_time.modtime, achieved_stat.st_mtime);
+    TEST_ASSERT(false_now - achieved_stat.st_mtime <= 2); // two seconds of tolerance are given
+
+    test_teardown();
+}
 #endif // CONFIG_SPIFFS_USE_MTIME
index d7467d227f54b922ec66f610028c2970258f1a18..e54a3e9835c20c608e1b793ca15476eb25a070aa 100644 (file)
@@ -19,6 +19,7 @@
 #include <stddef.h>
 #include <stdarg.h>
 #include <unistd.h>
+#include <utime.h>
 #include "freertos/FreeRTOS.h"
 #include "freertos/semphr.h"
 #include "esp_err.h"
@@ -180,6 +181,10 @@ typedef struct
         int (*truncate_p)(void* ctx, const char *path, off_t length);
         int (*truncate)(const char *path, off_t length);
     };
+    union {
+        int (*utime_p)(void* ctx, const char *path, const struct utimbuf *times);
+        int (*utime)(const char *path, const struct utimbuf *times);
+    };
 #ifdef CONFIG_SUPPORT_TERMIOS
     union {
         int (*tcsetattr_p)(void *ctx, int fd, int optional_actions, const struct termios *p);
@@ -330,6 +335,7 @@ int esp_vfs_stat(struct _reent *r, const char * path, struct stat * st);
 int esp_vfs_link(struct _reent *r, const char* n1, const char* n2);
 int esp_vfs_unlink(struct _reent *r, const char *path);
 int esp_vfs_rename(struct _reent *r, const char *src, const char *dst);
+int esp_vfs_utime(const char *path, const struct utimbuf *times);
 /**@}*/
 
 /**
index e8b99eff2ccbccac421cd2551717cb1d2f454bb1..65718ca90faae5c2cdf0a61e30df104dd99f82d4 100644 (file)
@@ -1094,3 +1094,17 @@ int tcsendbreak(int fd, int duration)
     return ret;
 }
 #endif // CONFIG_SUPPORT_TERMIOS
+
+int esp_vfs_utime(const char *path, const struct utimbuf *times)
+{
+    int ret;
+    const vfs_entry_t* vfs = get_vfs_for_path(path);
+    struct _reent* r = __getreent();
+    if (vfs == NULL) {
+        __errno_r(r) = ENOENT;
+        return -1;
+    }
+    const char* path_within_vfs = translate_path(vfs, path);
+    CHECK_AND_CALL(ret, r, vfs, utime, path_within_vfs, times);
+    return ret;
+}