diff --git a/components/fatfs/src/vfs_fat.c b/components/fatfs/src/vfs_fat.c index b1192b602c..556492e3ed 100644 --- a/components/fatfs/src/vfs_fat.c +++ b/components/fatfs/src/vfs_fat.c @@ -43,6 +43,25 @@ typedef struct { struct dirent cur_dirent; } vfs_fat_dir_t; +/* Date and time storage formats in FAT */ +typedef union { + struct { + uint16_t mday : 5; /* Day of month, 1 - 31 */ + uint16_t mon : 4; /* Month, 1 - 12 */ + uint16_t year : 7; /* Year, counting from 1980. E.g. 37 for 2017 */ + }; + uint16_t as_int; +} fat_date_t; + +typedef union { + struct { + uint16_t sec : 5; /* Seconds divided by 2. E.g. 21 for 42 seconds */ + uint16_t min : 6; /* Minutes, 0 - 59 */ + uint16_t hour : 5; /* Hour, 0 - 23 */ + }; + uint16_t as_int; +} fat_time_t; + static const char* TAG = "vfs_fat"; static ssize_t vfs_fat_write(void* p, int fd, const void * data, size_t size); @@ -395,8 +414,23 @@ static int vfs_fat_fstat(void* ctx, int fd, struct stat * st) return 0; } +static inline mode_t get_stat_mode(bool is_dir) +{ + return S_IRWXU | S_IRWXG | S_IRWXO | + ((is_dir) ? S_IFDIR : S_IFREG); +} + static int vfs_fat_stat(void* ctx, const char * path, struct stat * st) { + if (strcmp(path, "/") == 0) { + /* FatFS f_stat function does not work for the drive root. + * Just pretend that this is a directory. + */ + memset(st, 0, sizeof(*st)); + st->st_mode = get_stat_mode(true); + return 0; + } + vfs_fat_ctx_t* fat_ctx = (vfs_fat_ctx_t*) ctx; _lock_acquire(&fat_ctx->lock); prepend_drive_to_path(fat_ctx, &path, NULL); @@ -408,22 +442,20 @@ static int vfs_fat_stat(void* ctx, const char * path, struct stat * st) errno = fresult_to_errno(res); return -1; } + + memset(st, 0, sizeof(*st)); st->st_size = info.fsize; - st->st_mode = S_IRWXU | S_IRWXG | S_IRWXO | - ((info.fattrib & AM_DIR) ? S_IFDIR : S_IFREG); - struct tm tm; - uint16_t fdate = info.fdate; - tm.tm_mday = fdate & 0x1f; - fdate >>= 5; - tm.tm_mon = (fdate & 0xf) - 1; - fdate >>=4; - tm.tm_year = fdate + 80; - uint16_t ftime = info.ftime; - tm.tm_sec = (ftime & 0x1f) * 2; - ftime >>= 5; - tm.tm_min = (ftime & 0x3f); - ftime >>= 6; - tm.tm_hour = (ftime & 0x1f); + st->st_mode = get_stat_mode((info.fattrib & AM_DIR) != 0); + fat_date_t fdate = { .as_int = info.fdate }; + fat_time_t ftime = { .as_int = info.ftime }; + struct tm tm = { + .tm_mday = fdate.mday, + .tm_mon = fdate.mon - 1, /* unlike tm_mday, tm_mon is zero-based */ + .tm_year = fdate.year + 80, + .tm_sec = ftime.sec * 2, + .tm_min = ftime.min, + .tm_hour = ftime.hour + }; st->st_mtime = mktime(&tm); st->st_atime = 0; st->st_ctime = 0; diff --git a/components/fatfs/test/test_fatfs_common.c b/components/fatfs/test/test_fatfs_common.c index ca27cdd4ba..799d36f1b7 100644 --- a/components/fatfs/test/test_fatfs_common.c +++ b/components/fatfs/test/test_fatfs_common.c @@ -125,15 +125,15 @@ void test_fatfs_lseek(const char* filename) TEST_ASSERT_EQUAL(0, fclose(f)); } -void test_fatfs_stat(const char* filename) +void test_fatfs_stat(const char* filename, const char* root_dir) { struct tm tm; - tm.tm_year = 2016 - 1900; - tm.tm_mon = 0; - tm.tm_mday = 10; - tm.tm_hour = 16; - tm.tm_min = 30; - tm.tm_sec = 0; + tm.tm_year = 2017 - 1900; + tm.tm_mon = 11; + tm.tm_mday = 8; + tm.tm_hour = 19; + tm.tm_min = 51; + tm.tm_sec = 10; time_t t = mktime(&tm); printf("Setting time: %s", asctime(&tm)); struct timeval now = { .tv_sec = t }; @@ -151,6 +151,11 @@ void test_fatfs_stat(const char* filename) TEST_ASSERT(st.st_mode & S_IFREG); TEST_ASSERT_FALSE(st.st_mode & S_IFDIR); + + memset(&st, 0, sizeof(st)); + TEST_ASSERT_EQUAL(0, stat(root_dir, &st)); + TEST_ASSERT(st.st_mode & S_IFDIR); + TEST_ASSERT_FALSE(st.st_mode & S_IFREG); } void test_fatfs_unlink(const char* filename) diff --git a/components/fatfs/test/test_fatfs_common.h b/components/fatfs/test/test_fatfs_common.h index dcc31e37d4..36e7ca62b6 100644 --- a/components/fatfs/test/test_fatfs_common.h +++ b/components/fatfs/test/test_fatfs_common.h @@ -43,7 +43,7 @@ void test_fatfs_open_max_files(const char* filename_prefix, size_t files_count); void test_fatfs_lseek(const char* filename); -void test_fatfs_stat(const char* filename); +void test_fatfs_stat(const char* filename, const char* root_dir); void test_fatfs_unlink(const char* filename); diff --git a/components/fatfs/test/test_fatfs_sdmmc.c b/components/fatfs/test/test_fatfs_sdmmc.c index 6935fc7083..3610511a91 100644 --- a/components/fatfs/test/test_fatfs_sdmmc.c +++ b/components/fatfs/test/test_fatfs_sdmmc.c @@ -105,7 +105,7 @@ TEST_CASE("(SD) can lseek", "[fatfs][sdcard][ignore]") TEST_CASE("(SD) stat returns correct values", "[fatfs][ignore]") { test_setup(); - test_fatfs_stat("/sdcard/stat.txt"); + test_fatfs_stat("/sdcard/stat.txt", "/sdcard"); test_teardown(); } diff --git a/components/fatfs/test/test_fatfs_spiflash.c b/components/fatfs/test/test_fatfs_spiflash.c index bcc396b93c..47116cd8d5 100644 --- a/components/fatfs/test/test_fatfs_spiflash.c +++ b/components/fatfs/test/test_fatfs_spiflash.c @@ -100,7 +100,7 @@ TEST_CASE("(WL) can lseek", "[fatfs][wear_levelling]") TEST_CASE("(WL) stat returns correct values", "[fatfs][wear_levelling]") { test_setup(); - test_fatfs_stat("/spiflash/stat.txt"); + test_fatfs_stat("/spiflash/stat.txt", "/spiflash"); test_teardown(); }