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