1 // Copyright 2015-2017 Espressif Systems (Shanghai) PTE LTD
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //     http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #include "esp_spiffs.h"
16 #include "spiffs.h"
17 #include "spiffs_nucleus.h"
18 #include "esp_log.h"
19 #include "esp_partition.h"
20 #include "esp_spi_flash.h"
21 #include "esp_image_format.h"
22 #include "freertos/FreeRTOS.h"
23 #include "freertos/task.h"
24 #include "freertos/semphr.h"
25 #include <unistd.h>
26 #include <dirent.h>
27 #include <sys/errno.h>
28 #include <sys/fcntl.h>
29 #include <sys/lock.h>
30 #include "esp_vfs.h"
31 #include "esp_err.h"
32 #if CONFIG_IDF_TARGET_ESP32
33 #include "esp32/rom/spi_flash.h"
34 #elif CONFIG_IDF_TARGET_ESP32S2
35 #include "esp32s2/rom/spi_flash.h"
36 #elif CONFIG_IDF_TARGET_ESP32S3
37 #include "esp32s3/rom/spi_flash.h"
38 #elif CONFIG_IDF_TARGET_ESP32C3
39 #include "esp32c3/rom/spi_flash.h"
40 #elif CONFIG_IDF_TARGET_ESP32H2
41 #include "esp32h2/rom/spi_flash.h"
42 #endif
43 
44 #include "spiffs_api.h"
45 
46 static const char* TAG = "SPIFFS";
47 
48 #ifdef CONFIG_SPIFFS_USE_MTIME
49 #ifdef CONFIG_SPIFFS_MTIME_WIDE_64_BITS
50 typedef time_t spiffs_time_t;
51 #else
52 typedef unsigned long spiffs_time_t;
53 #endif
54 _Static_assert(CONFIG_SPIFFS_META_LENGTH >= sizeof(spiffs_time_t),
55         "SPIFFS_META_LENGTH size should be >= sizeof(spiffs_time_t)");
56 #endif //CONFIG_SPIFFS_USE_MTIME
57 
58 /**
59  * @brief SPIFFS DIR structure
60  */
61 typedef struct {
62     DIR dir;            /*!< VFS DIR struct */
63     spiffs_DIR d;       /*!< SPIFFS DIR struct */
64     struct dirent e;    /*!< Last open dirent */
65     long offset;        /*!< Offset of the current dirent */
66     char path[SPIFFS_OBJ_NAME_LEN]; /*!< Requested directory name */
67 } vfs_spiffs_dir_t;
68 
69 static int vfs_spiffs_open(void* ctx, const char * path, int flags, int mode);
70 static ssize_t vfs_spiffs_write(void* ctx, int fd, const void * data, size_t size);
71 static ssize_t vfs_spiffs_read(void* ctx, int fd, void * dst, size_t size);
72 static int vfs_spiffs_close(void* ctx, int fd);
73 static off_t vfs_spiffs_lseek(void* ctx, int fd, off_t offset, int mode);
74 static int vfs_spiffs_fstat(void* ctx, int fd, struct stat * st);
75 #ifdef CONFIG_VFS_SUPPORT_DIR
76 static int vfs_spiffs_stat(void* ctx, const char * path, struct stat * st);
77 static int vfs_spiffs_unlink(void* ctx, const char *path);
78 static int vfs_spiffs_link(void* ctx, const char* n1, const char* n2);
79 static int vfs_spiffs_rename(void* ctx, const char *src, const char *dst);
80 static DIR* vfs_spiffs_opendir(void* ctx, const char* name);
81 static int vfs_spiffs_closedir(void* ctx, DIR* pdir);
82 static struct dirent* vfs_spiffs_readdir(void* ctx, DIR* pdir);
83 static int vfs_spiffs_readdir_r(void* ctx, DIR* pdir,
84                                 struct dirent* entry, struct dirent** out_dirent);
85 static long vfs_spiffs_telldir(void* ctx, DIR* pdir);
86 static void vfs_spiffs_seekdir(void* ctx, DIR* pdir, long offset);
87 static int vfs_spiffs_mkdir(void* ctx, const char* name, mode_t mode);
88 static int vfs_spiffs_rmdir(void* ctx, const char* name);
89 #ifdef CONFIG_SPIFFS_USE_MTIME
90 static int vfs_spiffs_utime(void *ctx, const char *path, const struct utimbuf *times);
91 #endif // CONFIG_SPIFFS_USE_MTIME
92 #endif // CONFIG_VFS_SUPPORT_DIR
93 static void vfs_spiffs_update_mtime(spiffs *fs, spiffs_file f);
94 static time_t vfs_spiffs_get_mtime(const spiffs_stat* s);
95 
96 static esp_spiffs_t * _efs[CONFIG_SPIFFS_MAX_PARTITIONS];
97 
esp_spiffs_free(esp_spiffs_t ** efs)98 static void esp_spiffs_free(esp_spiffs_t ** efs)
99 {
100     esp_spiffs_t * e = *efs;
101     if (*efs == NULL) {
102         return;
103     }
104     *efs = NULL;
105 
106     if (e->fs) {
107         SPIFFS_unmount(e->fs);
108         free(e->fs);
109     }
110     vSemaphoreDelete(e->lock);
111     free(e->fds);
112     free(e->cache);
113     free(e->work);
114     free(e);
115 }
116 
esp_spiffs_by_label(const char * label,int * index)117 static esp_err_t esp_spiffs_by_label(const char* label, int * index){
118     int i;
119     esp_spiffs_t * p;
120     for (i = 0; i < CONFIG_SPIFFS_MAX_PARTITIONS; i++) {
121         p = _efs[i];
122         if (p) {
123             if (!label && !p->by_label) {
124                 *index = i;
125                 return ESP_OK;
126             }
127             if (label && p->by_label && strncmp(label, p->partition->label, 17) == 0) {
128                 *index = i;
129                 return ESP_OK;
130             }
131         }
132     }
133     return ESP_ERR_NOT_FOUND;
134 }
135 
esp_spiffs_get_empty(int * index)136 static esp_err_t esp_spiffs_get_empty(int * index){
137     int i;
138     for (i = 0; i < CONFIG_SPIFFS_MAX_PARTITIONS; i++) {
139         if (_efs[i] == NULL) {
140             *index = i;
141             return ESP_OK;
142         }
143     }
144     return ESP_ERR_NOT_FOUND;
145 }
146 
esp_spiffs_init(const esp_vfs_spiffs_conf_t * conf)147 static esp_err_t esp_spiffs_init(const esp_vfs_spiffs_conf_t* conf)
148 {
149     int index;
150     //find if such partition is already mounted
151     if (esp_spiffs_by_label(conf->partition_label, &index) == ESP_OK) {
152         return ESP_ERR_INVALID_STATE;
153     }
154 
155     if (esp_spiffs_get_empty(&index) != ESP_OK) {
156         ESP_LOGE(TAG, "max mounted partitions reached");
157         return ESP_ERR_INVALID_STATE;
158     }
159 
160     uint32_t flash_page_size = g_rom_flashchip.page_size;
161     uint32_t log_page_size = CONFIG_SPIFFS_PAGE_SIZE;
162     if (log_page_size % flash_page_size != 0) {
163         ESP_LOGE(TAG, "SPIFFS_PAGE_SIZE is not multiple of flash chip page size (%d)",
164                 flash_page_size);
165         return ESP_ERR_INVALID_ARG;
166     }
167 
168     esp_partition_subtype_t subtype = conf->partition_label ?
169             ESP_PARTITION_SUBTYPE_ANY : ESP_PARTITION_SUBTYPE_DATA_SPIFFS;
170     const esp_partition_t* partition = esp_partition_find_first(ESP_PARTITION_TYPE_DATA,
171                                       subtype, conf->partition_label);
172     if (!partition) {
173         ESP_LOGE(TAG, "spiffs partition could not be found");
174         return ESP_ERR_NOT_FOUND;
175     }
176 
177     if (partition->encrypted) {
178         ESP_LOGE(TAG, "spiffs can not run on encrypted partition");
179         return ESP_ERR_INVALID_STATE;
180     }
181 
182     esp_spiffs_t * efs = malloc(sizeof(esp_spiffs_t));
183     if (efs == NULL) {
184         ESP_LOGE(TAG, "esp_spiffs could not be malloced");
185         return ESP_ERR_NO_MEM;
186     }
187     memset(efs, 0, sizeof(esp_spiffs_t));
188 
189     efs->cfg.hal_erase_f       = spiffs_api_erase;
190     efs->cfg.hal_read_f        = spiffs_api_read;
191     efs->cfg.hal_write_f       = spiffs_api_write;
192     efs->cfg.log_block_size    = g_rom_flashchip.sector_size;
193     efs->cfg.log_page_size     = log_page_size;
194     efs->cfg.phys_addr         = 0;
195     efs->cfg.phys_erase_block  = g_rom_flashchip.sector_size;
196     efs->cfg.phys_size         = partition->size;
197 
198     efs->by_label = conf->partition_label != NULL;
199 
200     efs->lock = xSemaphoreCreateMutex();
201     if (efs->lock == NULL) {
202         ESP_LOGE(TAG, "mutex lock could not be created");
203         esp_spiffs_free(&efs);
204         return ESP_ERR_NO_MEM;
205     }
206 
207     efs->fds_sz = conf->max_files * sizeof(spiffs_fd);
208     efs->fds = malloc(efs->fds_sz);
209     if (efs->fds == NULL) {
210         ESP_LOGE(TAG, "fd buffer could not be malloced");
211         esp_spiffs_free(&efs);
212         return ESP_ERR_NO_MEM;
213     }
214     memset(efs->fds, 0, efs->fds_sz);
215 
216 #if SPIFFS_CACHE
217     efs->cache_sz = sizeof(spiffs_cache) + conf->max_files * (sizeof(spiffs_cache_page)
218                           + efs->cfg.log_page_size);
219     efs->cache = malloc(efs->cache_sz);
220     if (efs->cache == NULL) {
221         ESP_LOGE(TAG, "cache buffer could not be malloced");
222         esp_spiffs_free(&efs);
223         return ESP_ERR_NO_MEM;
224     }
225     memset(efs->cache, 0, efs->cache_sz);
226 #endif
227 
228     const uint32_t work_sz = efs->cfg.log_page_size * 2;
229     efs->work = malloc(work_sz);
230     if (efs->work == NULL) {
231         ESP_LOGE(TAG, "work buffer could not be malloced");
232         esp_spiffs_free(&efs);
233         return ESP_ERR_NO_MEM;
234     }
235     memset(efs->work, 0, work_sz);
236 
237     efs->fs = malloc(sizeof(spiffs));
238     if (efs->fs == NULL) {
239         ESP_LOGE(TAG, "spiffs could not be malloced");
240         esp_spiffs_free(&efs);
241         return ESP_ERR_NO_MEM;
242     }
243     memset(efs->fs, 0, sizeof(spiffs));
244 
245     efs->fs->user_data = (void *)efs;
246     efs->partition = partition;
247 
248     s32_t res = SPIFFS_mount(efs->fs, &efs->cfg, efs->work, efs->fds, efs->fds_sz,
249                             efs->cache, efs->cache_sz, spiffs_api_check);
250 
251     if (conf->format_if_mount_failed && res != SPIFFS_OK) {
252         ESP_LOGW(TAG, "mount failed, %i. formatting...", SPIFFS_errno(efs->fs));
253         SPIFFS_clearerr(efs->fs);
254         res = SPIFFS_format(efs->fs);
255         if (res != SPIFFS_OK) {
256             ESP_LOGE(TAG, "format failed, %i", SPIFFS_errno(efs->fs));
257             SPIFFS_clearerr(efs->fs);
258             esp_spiffs_free(&efs);
259             return ESP_FAIL;
260         }
261         res = SPIFFS_mount(efs->fs, &efs->cfg, efs->work, efs->fds, efs->fds_sz,
262                             efs->cache, efs->cache_sz, spiffs_api_check);
263     }
264     if (res != SPIFFS_OK) {
265         ESP_LOGE(TAG, "mount failed, %i", SPIFFS_errno(efs->fs));
266         SPIFFS_clearerr(efs->fs);
267         esp_spiffs_free(&efs);
268         return ESP_FAIL;
269     }
270     _efs[index] = efs;
271     return ESP_OK;
272 }
273 
esp_spiffs_mounted(const char * partition_label)274 bool esp_spiffs_mounted(const char* partition_label)
275 {
276     int index;
277     if (esp_spiffs_by_label(partition_label, &index) != ESP_OK) {
278         return false;
279     }
280     return (SPIFFS_mounted(_efs[index]->fs));
281 }
282 
esp_spiffs_info(const char * partition_label,size_t * total_bytes,size_t * used_bytes)283 esp_err_t esp_spiffs_info(const char* partition_label, size_t *total_bytes, size_t *used_bytes)
284 {
285     int index;
286     if (esp_spiffs_by_label(partition_label, &index) != ESP_OK) {
287         return ESP_ERR_INVALID_STATE;
288     }
289     SPIFFS_info(_efs[index]->fs, (uint32_t *)total_bytes, (uint32_t *)used_bytes);
290     return ESP_OK;
291 }
292 
esp_spiffs_format(const char * partition_label)293 esp_err_t esp_spiffs_format(const char* partition_label)
294 {
295     bool partition_was_mounted = false;
296     int index;
297     /* If the partition is not mounted, need to create SPIFFS structures
298      * and mount the partition, unmount, format, delete SPIFFS structures.
299      * See SPIFFS wiki for the reason why.
300      */
301     esp_err_t err = esp_spiffs_by_label(partition_label, &index);
302     if (err != ESP_OK) {
303         esp_vfs_spiffs_conf_t conf = {
304                 .format_if_mount_failed = true,
305                 .partition_label = partition_label,
306                 .max_files = 1
307         };
308         err = esp_spiffs_init(&conf);
309         if (err != ESP_OK) {
310             return err;
311         }
312         err = esp_spiffs_by_label(partition_label, &index);
313         assert(err == ESP_OK && "failed to get index of the partition just mounted");
314     } else if (SPIFFS_mounted(_efs[index]->fs)) {
315         partition_was_mounted = true;
316     }
317 
318     SPIFFS_unmount(_efs[index]->fs);
319 
320     s32_t res = SPIFFS_format(_efs[index]->fs);
321     if (res != SPIFFS_OK) {
322         ESP_LOGE(TAG, "format failed, %i", SPIFFS_errno(_efs[index]->fs));
323         SPIFFS_clearerr(_efs[index]->fs);
324         /* If the partition was previously mounted, but format failed, don't
325          * try to mount the partition back (it will probably fail). On the
326          * other hand, if it was not mounted, need to clean up.
327          */
328         if (!partition_was_mounted) {
329             esp_spiffs_free(&_efs[index]);
330         }
331         return ESP_FAIL;
332     }
333 
334     if (partition_was_mounted) {
335         res = SPIFFS_mount(_efs[index]->fs, &_efs[index]->cfg, _efs[index]->work,
336                             _efs[index]->fds, _efs[index]->fds_sz, _efs[index]->cache,
337                             _efs[index]->cache_sz, spiffs_api_check);
338         if (res != SPIFFS_OK) {
339             ESP_LOGE(TAG, "mount failed, %i", SPIFFS_errno(_efs[index]->fs));
340             SPIFFS_clearerr(_efs[index]->fs);
341             return ESP_FAIL;
342         }
343     } else {
344         esp_spiffs_free(&_efs[index]);
345     }
346     return ESP_OK;
347 }
348 
esp_vfs_spiffs_register(const esp_vfs_spiffs_conf_t * conf)349 esp_err_t esp_vfs_spiffs_register(const esp_vfs_spiffs_conf_t * conf)
350 {
351     assert(conf->base_path);
352     const esp_vfs_t vfs = {
353         .flags = ESP_VFS_FLAG_CONTEXT_PTR,
354         .write_p = &vfs_spiffs_write,
355         .lseek_p = &vfs_spiffs_lseek,
356         .read_p = &vfs_spiffs_read,
357         .open_p = &vfs_spiffs_open,
358         .close_p = &vfs_spiffs_close,
359         .fstat_p = &vfs_spiffs_fstat,
360 #ifdef CONFIG_VFS_SUPPORT_DIR
361         .stat_p = &vfs_spiffs_stat,
362         .link_p = &vfs_spiffs_link,
363         .unlink_p = &vfs_spiffs_unlink,
364         .rename_p = &vfs_spiffs_rename,
365         .opendir_p = &vfs_spiffs_opendir,
366         .closedir_p = &vfs_spiffs_closedir,
367         .readdir_p = &vfs_spiffs_readdir,
368         .readdir_r_p = &vfs_spiffs_readdir_r,
369         .seekdir_p = &vfs_spiffs_seekdir,
370         .telldir_p = &vfs_spiffs_telldir,
371         .mkdir_p = &vfs_spiffs_mkdir,
372         .rmdir_p = &vfs_spiffs_rmdir,
373 #ifdef CONFIG_SPIFFS_USE_MTIME
374         .utime_p = &vfs_spiffs_utime,
375 #else
376         .utime_p = NULL,
377 #endif // CONFIG_SPIFFS_USE_MTIME
378 #endif // CONFIG_VFS_SUPPORT_DIR
379     };
380 
381     esp_err_t err = esp_spiffs_init(conf);
382     if (err != ESP_OK) {
383         return err;
384     }
385 
386     int index;
387     if (esp_spiffs_by_label(conf->partition_label, &index) != ESP_OK) {
388         return ESP_ERR_INVALID_STATE;
389     }
390 
391     strlcat(_efs[index]->base_path, conf->base_path, ESP_VFS_PATH_MAX + 1);
392     err = esp_vfs_register(conf->base_path, &vfs, _efs[index]);
393     if (err != ESP_OK) {
394         esp_spiffs_free(&_efs[index]);
395         return err;
396     }
397 
398     return ESP_OK;
399 }
400 
esp_vfs_spiffs_unregister(const char * partition_label)401 esp_err_t esp_vfs_spiffs_unregister(const char* partition_label)
402 {
403     int index;
404     if (esp_spiffs_by_label(partition_label, &index) != ESP_OK) {
405         return ESP_ERR_INVALID_STATE;
406     }
407     esp_err_t err = esp_vfs_unregister(_efs[index]->base_path);
408     if (err != ESP_OK) {
409         return err;
410     }
411     esp_spiffs_free(&_efs[index]);
412     return ESP_OK;
413 }
414 
spiffs_res_to_errno(s32_t fr)415 static int spiffs_res_to_errno(s32_t fr)
416 {
417     switch(fr) {
418     case SPIFFS_OK :
419         return 0;
420     case SPIFFS_ERR_NOT_MOUNTED :
421         return ENODEV;
422     case SPIFFS_ERR_NOT_A_FS :
423         return ENODEV;
424     case SPIFFS_ERR_FULL :
425         return ENOSPC;
426     case SPIFFS_ERR_BAD_DESCRIPTOR :
427         return EBADF;
428     case SPIFFS_ERR_MOUNTED :
429         return EEXIST;
430     case SPIFFS_ERR_FILE_EXISTS :
431         return EEXIST;
432     case SPIFFS_ERR_NOT_FOUND :
433         return ENOENT;
434     case SPIFFS_ERR_NOT_A_FILE :
435         return ENOENT;
436     case SPIFFS_ERR_DELETED :
437         return ENOENT;
438     case SPIFFS_ERR_FILE_DELETED :
439         return ENOENT;
440     case SPIFFS_ERR_NAME_TOO_LONG :
441         return ENAMETOOLONG;
442     case SPIFFS_ERR_RO_NOT_IMPL :
443         return EROFS;
444     case SPIFFS_ERR_RO_ABORTED_OPERATION :
445         return EROFS;
446     default :
447         return EIO;
448     }
449     return ENOTSUP;
450 }
451 
spiffs_mode_conv(int m)452 static int spiffs_mode_conv(int m)
453 {
454     int res = 0;
455     int acc_mode = m & O_ACCMODE;
456     if (acc_mode == O_RDONLY) {
457         res |= SPIFFS_O_RDONLY;
458     } else if (acc_mode == O_WRONLY) {
459         res |= SPIFFS_O_WRONLY;
460     } else if (acc_mode == O_RDWR) {
461         res |= SPIFFS_O_RDWR;
462     }
463     if ((m & O_CREAT) && (m & O_EXCL)) {
464         res |= SPIFFS_O_CREAT | SPIFFS_O_EXCL;
465     } else if ((m & O_CREAT) && (m & O_TRUNC)) {
466         res |= SPIFFS_O_CREAT | SPIFFS_O_TRUNC;
467     }
468     if (m & O_APPEND) {
469         res |= SPIFFS_O_CREAT | SPIFFS_O_APPEND;
470     }
471     return res;
472 }
473 
vfs_spiffs_open(void * ctx,const char * path,int flags,int mode)474 static int vfs_spiffs_open(void* ctx, const char * path, int flags, int mode)
475 {
476     assert(path);
477     esp_spiffs_t * efs = (esp_spiffs_t *)ctx;
478     int spiffs_flags = spiffs_mode_conv(flags);
479     int fd = SPIFFS_open(efs->fs, path, spiffs_flags, mode);
480     if (fd < 0) {
481         errno = spiffs_res_to_errno(SPIFFS_errno(efs->fs));
482         SPIFFS_clearerr(efs->fs);
483         return -1;
484     }
485     if (!(spiffs_flags & SPIFFS_RDONLY)) {
486         vfs_spiffs_update_mtime(efs->fs, fd);
487     }
488     return fd;
489 }
490 
vfs_spiffs_write(void * ctx,int fd,const void * data,size_t size)491 static ssize_t vfs_spiffs_write(void* ctx, int fd, const void * data, size_t size)
492 {
493     esp_spiffs_t * efs = (esp_spiffs_t *)ctx;
494     ssize_t res = SPIFFS_write(efs->fs, fd, (void *)data, size);
495     if (res < 0) {
496         errno = spiffs_res_to_errno(SPIFFS_errno(efs->fs));
497         SPIFFS_clearerr(efs->fs);
498         return -1;
499     }
500     return res;
501 }
502 
vfs_spiffs_read(void * ctx,int fd,void * dst,size_t size)503 static ssize_t vfs_spiffs_read(void* ctx, int fd, void * dst, size_t size)
504 {
505     esp_spiffs_t * efs = (esp_spiffs_t *)ctx;
506     ssize_t res = SPIFFS_read(efs->fs, fd, dst, size);
507     if (res < 0) {
508         errno = spiffs_res_to_errno(SPIFFS_errno(efs->fs));
509         SPIFFS_clearerr(efs->fs);
510         return -1;
511     }
512     return res;
513 }
514 
vfs_spiffs_close(void * ctx,int fd)515 static int vfs_spiffs_close(void* ctx, int fd)
516 {
517     esp_spiffs_t * efs = (esp_spiffs_t *)ctx;
518     int res = SPIFFS_close(efs->fs, fd);
519     if (res < 0) {
520         errno = spiffs_res_to_errno(SPIFFS_errno(efs->fs));
521         SPIFFS_clearerr(efs->fs);
522         return -1;
523     }
524     return res;
525 }
526 
vfs_spiffs_lseek(void * ctx,int fd,off_t offset,int mode)527 static off_t vfs_spiffs_lseek(void* ctx, int fd, off_t offset, int mode)
528 {
529     esp_spiffs_t * efs = (esp_spiffs_t *)ctx;
530     off_t res = SPIFFS_lseek(efs->fs, fd, offset, mode);
531     if (res < 0) {
532         errno = spiffs_res_to_errno(SPIFFS_errno(efs->fs));
533         SPIFFS_clearerr(efs->fs);
534         return -1;
535     }
536     return res;
537 }
538 
vfs_spiffs_fstat(void * ctx,int fd,struct stat * st)539 static int vfs_spiffs_fstat(void* ctx, int fd, struct stat * st)
540 {
541     assert(st);
542     spiffs_stat s;
543     esp_spiffs_t * efs = (esp_spiffs_t *)ctx;
544     off_t res = SPIFFS_fstat(efs->fs, fd, &s);
545     if (res < 0) {
546         errno = spiffs_res_to_errno(SPIFFS_errno(efs->fs));
547         SPIFFS_clearerr(efs->fs);
548         return -1;
549     }
550     memset(st, 0, sizeof(*st));
551     st->st_size = s.size;
552     st->st_mode = S_IRWXU | S_IRWXG | S_IRWXO | S_IFREG;
553     st->st_mtime = vfs_spiffs_get_mtime(&s);
554     st->st_atime = 0;
555     st->st_ctime = 0;
556     return res;
557 }
558 
559 #ifdef CONFIG_VFS_SUPPORT_DIR
560 
vfs_spiffs_stat(void * ctx,const char * path,struct stat * st)561 static int vfs_spiffs_stat(void* ctx, const char * path, struct stat * st)
562 {
563     assert(path);
564     assert(st);
565     spiffs_stat s;
566     esp_spiffs_t * efs = (esp_spiffs_t *)ctx;
567     off_t res = SPIFFS_stat(efs->fs, path, &s);
568     if (res < 0) {
569         errno = spiffs_res_to_errno(SPIFFS_errno(efs->fs));
570         SPIFFS_clearerr(efs->fs);
571         return -1;
572     }
573     memset(st, 0, sizeof(*st));
574     st->st_size = s.size;
575     st->st_mode = S_IRWXU | S_IRWXG | S_IRWXO;
576     st->st_mode |= (s.type == SPIFFS_TYPE_DIR)?S_IFDIR:S_IFREG;
577     st->st_mtime = vfs_spiffs_get_mtime(&s);
578     st->st_atime = 0;
579     st->st_ctime = 0;
580     return res;
581 }
582 
vfs_spiffs_rename(void * ctx,const char * src,const char * dst)583 static int vfs_spiffs_rename(void* ctx, const char *src, const char *dst)
584 {
585     assert(src);
586     assert(dst);
587     esp_spiffs_t * efs = (esp_spiffs_t *)ctx;
588     int res = SPIFFS_rename(efs->fs, src, dst);
589     if (res < 0) {
590         errno = spiffs_res_to_errno(SPIFFS_errno(efs->fs));
591         SPIFFS_clearerr(efs->fs);
592         return -1;
593     }
594     return res;
595 }
596 
vfs_spiffs_unlink(void * ctx,const char * path)597 static int vfs_spiffs_unlink(void* ctx, const char *path)
598 {
599     assert(path);
600     esp_spiffs_t * efs = (esp_spiffs_t *)ctx;
601     int res = SPIFFS_remove(efs->fs, path);
602     if (res < 0) {
603         errno = spiffs_res_to_errno(SPIFFS_errno(efs->fs));
604         SPIFFS_clearerr(efs->fs);
605         return -1;
606     }
607     return res;
608 }
609 
vfs_spiffs_opendir(void * ctx,const char * name)610 static DIR* vfs_spiffs_opendir(void* ctx, const char* name)
611 {
612     assert(name);
613     esp_spiffs_t * efs = (esp_spiffs_t *)ctx;
614     vfs_spiffs_dir_t * dir = calloc(1, sizeof(vfs_spiffs_dir_t));
615     if (!dir) {
616         errno = ENOMEM;
617         return NULL;
618     }
619     if (!SPIFFS_opendir(efs->fs, name, &dir->d)) {
620         free(dir);
621         errno = spiffs_res_to_errno(SPIFFS_errno(efs->fs));
622         SPIFFS_clearerr(efs->fs);
623         return NULL;
624     }
625     dir->offset = 0;
626     strlcpy(dir->path, name, SPIFFS_OBJ_NAME_LEN);
627     return (DIR*) dir;
628 }
629 
vfs_spiffs_closedir(void * ctx,DIR * pdir)630 static int vfs_spiffs_closedir(void* ctx, DIR* pdir)
631 {
632     assert(pdir);
633     esp_spiffs_t * efs = (esp_spiffs_t *)ctx;
634     vfs_spiffs_dir_t * dir = (vfs_spiffs_dir_t *)pdir;
635     int res = SPIFFS_closedir(&dir->d);
636     free(dir);
637     if (res < 0) {
638         errno = spiffs_res_to_errno(SPIFFS_errno(efs->fs));
639         SPIFFS_clearerr(efs->fs);
640         return -1;
641     }
642     return res;
643 }
644 
vfs_spiffs_readdir(void * ctx,DIR * pdir)645 static struct dirent* vfs_spiffs_readdir(void* ctx, DIR* pdir)
646 {
647     assert(pdir);
648     vfs_spiffs_dir_t * dir = (vfs_spiffs_dir_t *)pdir;
649     struct dirent* out_dirent;
650     int err = vfs_spiffs_readdir_r(ctx, pdir, &dir->e, &out_dirent);
651     if (err != 0) {
652         errno = err;
653         return NULL;
654     }
655     return out_dirent;
656 }
657 
vfs_spiffs_readdir_r(void * ctx,DIR * pdir,struct dirent * entry,struct dirent ** out_dirent)658 static int vfs_spiffs_readdir_r(void* ctx, DIR* pdir, struct dirent* entry,
659                                 struct dirent** out_dirent)
660 {
661     assert(pdir);
662     esp_spiffs_t * efs = (esp_spiffs_t *)ctx;
663     vfs_spiffs_dir_t * dir = (vfs_spiffs_dir_t *)pdir;
664     struct spiffs_dirent out;
665     size_t plen;
666     char * item_name;
667     do {
668         if (SPIFFS_readdir(&dir->d, &out) == 0) {
669             errno = spiffs_res_to_errno(SPIFFS_errno(efs->fs));
670             SPIFFS_clearerr(efs->fs);
671             if (!errno) {
672                 *out_dirent = NULL;
673             }
674             return errno;
675         }
676         item_name = (char *)out.name;
677         plen = strlen(dir->path);
678 
679     } while ((plen > 1) && (strncasecmp(dir->path, (const char*)out.name, plen) || out.name[plen] != '/' || !out.name[plen + 1]));
680 
681     if (plen > 1) {
682         item_name += plen + 1;
683     } else if (item_name[0] == '/') {
684         item_name++;
685     }
686     entry->d_ino = 0;
687     entry->d_type = out.type;
688     snprintf(entry->d_name, SPIFFS_OBJ_NAME_LEN, "%s", item_name);
689     dir->offset++;
690     *out_dirent = entry;
691     return 0;
692 }
693 
vfs_spiffs_telldir(void * ctx,DIR * pdir)694 static long vfs_spiffs_telldir(void* ctx, DIR* pdir)
695 {
696     assert(pdir);
697     vfs_spiffs_dir_t * dir = (vfs_spiffs_dir_t *)pdir;
698     return dir->offset;
699 }
700 
vfs_spiffs_seekdir(void * ctx,DIR * pdir,long offset)701 static void vfs_spiffs_seekdir(void* ctx, DIR* pdir, long offset)
702 {
703     assert(pdir);
704     esp_spiffs_t * efs = (esp_spiffs_t *)ctx;
705     vfs_spiffs_dir_t * dir = (vfs_spiffs_dir_t *)pdir;
706     struct spiffs_dirent tmp;
707     if (offset < dir->offset) {
708         //rewind dir
709         SPIFFS_closedir(&dir->d);
710         if (!SPIFFS_opendir(efs->fs, NULL, &dir->d)) {
711             errno = spiffs_res_to_errno(SPIFFS_errno(efs->fs));
712             SPIFFS_clearerr(efs->fs);
713             return;
714         }
715         dir->offset = 0;
716     }
717     while (dir->offset < offset) {
718         if (SPIFFS_readdir(&dir->d, &tmp) == 0) {
719             errno = spiffs_res_to_errno(SPIFFS_errno(efs->fs));
720             SPIFFS_clearerr(efs->fs);
721             return;
722         }
723         size_t plen = strlen(dir->path);
724         if (plen > 1) {
725             if (strncasecmp(dir->path, (const char *)tmp.name, plen) || tmp.name[plen] != '/' || !tmp.name[plen+1]) {
726                 continue;
727             }
728         }
729         dir->offset++;
730     }
731 }
732 
vfs_spiffs_mkdir(void * ctx,const char * name,mode_t mode)733 static int vfs_spiffs_mkdir(void* ctx, const char* name, mode_t mode)
734 {
735     errno = ENOTSUP;
736     return -1;
737 }
738 
vfs_spiffs_rmdir(void * ctx,const char * name)739 static int vfs_spiffs_rmdir(void* ctx, const char* name)
740 {
741     errno = ENOTSUP;
742     return -1;
743 }
744 
vfs_spiffs_link(void * ctx,const char * n1,const char * n2)745 static int vfs_spiffs_link(void* ctx, const char* n1, const char* n2)
746 {
747     errno = ENOTSUP;
748     return -1;
749 }
750 
751 #ifdef CONFIG_SPIFFS_USE_MTIME
vfs_spiffs_update_mtime_value(spiffs * fs,const char * path,spiffs_time_t t)752 static int vfs_spiffs_update_mtime_value(spiffs *fs, const char *path, spiffs_time_t t)
753 {
754     int ret = SPIFFS_OK;
755     spiffs_stat s;
756     if (CONFIG_SPIFFS_META_LENGTH > sizeof(t)) {
757         ret = SPIFFS_stat(fs, path, &s);
758     }
759     if (ret == SPIFFS_OK) {
760         memcpy(s.meta, &t, sizeof(t));
761         ret = SPIFFS_update_meta(fs, path, s.meta);
762     }
763     if (ret != SPIFFS_OK) {
764         ESP_LOGW(TAG, "Failed to update mtime (%d)", ret);
765     }
766     return ret;
767 }
768 #endif //CONFIG_SPIFFS_USE_MTIME
769 
770 #ifdef CONFIG_SPIFFS_USE_MTIME
vfs_spiffs_utime(void * ctx,const char * path,const struct utimbuf * times)771 static int vfs_spiffs_utime(void *ctx, const char *path, const struct utimbuf *times)
772 {
773     assert(path);
774 
775     esp_spiffs_t *efs = (esp_spiffs_t *) ctx;
776     spiffs_time_t t;
777 
778     if (times) {
779         t = (spiffs_time_t)times->modtime;
780     } else {
781         // use current time
782         t = (spiffs_time_t)time(NULL);
783     }
784 
785     int ret = vfs_spiffs_update_mtime_value(efs->fs, path, t);
786 
787     if (ret != SPIFFS_OK) {
788         errno = spiffs_res_to_errno(SPIFFS_errno(efs->fs));
789         SPIFFS_clearerr(efs->fs);
790         return -1;
791     }
792 
793     return 0;
794 }
795 #endif //CONFIG_SPIFFS_USE_MTIME
796 
797 #endif // CONFIG_VFS_SUPPORT_DIR
798 
vfs_spiffs_update_mtime(spiffs * fs,spiffs_file fd)799 static void vfs_spiffs_update_mtime(spiffs *fs, spiffs_file fd)
800 {
801 #ifdef CONFIG_SPIFFS_USE_MTIME
802     spiffs_time_t t = (spiffs_time_t)time(NULL);
803     spiffs_stat s;
804     int ret = SPIFFS_OK;
805     if (CONFIG_SPIFFS_META_LENGTH > sizeof(t)) {
806         ret = SPIFFS_fstat(fs, fd, &s);
807     }
808     if (ret == SPIFFS_OK) {
809         memcpy(s.meta, &t, sizeof(t));
810         ret = SPIFFS_fupdate_meta(fs, fd, s.meta);
811     }
812     if (ret != SPIFFS_OK) {
813         ESP_LOGW(TAG, "Failed to update mtime (%d)", ret);
814     }
815 #endif //CONFIG_SPIFFS_USE_MTIME
816 }
817 
vfs_spiffs_get_mtime(const spiffs_stat * s)818 static time_t vfs_spiffs_get_mtime(const spiffs_stat* s)
819 {
820 #ifdef CONFIG_SPIFFS_USE_MTIME
821     spiffs_time_t t = 0;
822     memcpy(&t, s->meta, sizeof(t));
823 #else
824     time_t t = 0;
825 #endif
826     return (time_t)t;
827 }
828