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