1 // Copyright 2015-2019 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 <stdlib.h>
16 #include <string.h>
17 #include <assert.h>
18 #include <sys/errno.h>
19 #include <sys/fcntl.h>
20 #include <sys/ioctl.h>
21 #include <sys/reent.h>
22 #include <sys/unistd.h>
23 #include <sys/lock.h>
24 #include <sys/param.h>
25 #include <dirent.h>
26 #include "freertos/FreeRTOS.h"
27 #include "freertos/semphr.h"
28 #include "esp_vfs.h"
29 #include "sdkconfig.h"
30 
31 #ifdef CONFIG_VFS_SUPPRESS_SELECT_DEBUG_OUTPUT
32 #define LOG_LOCAL_LEVEL ESP_LOG_NONE
33 #endif //CONFIG_VFS_SUPPRESS_SELECT_DEBUG_OUTPUT
34 #include "esp_log.h"
35 
36 static const char *TAG = "vfs";
37 
38 #define VFS_MAX_COUNT   8   /* max number of VFS entries (registered filesystems) */
39 #define LEN_PATH_PREFIX_IGNORED SIZE_MAX /* special length value for VFS which is never recognised by open() */
40 #define FD_TABLE_ENTRY_UNUSED   (fd_table_t) { .permanent = false, .vfs_index = -1, .local_fd = -1 }
41 
42 typedef uint8_t local_fd_t;
43 _Static_assert((1 << (sizeof(local_fd_t)*8)) >= MAX_FDS, "file descriptor type too small");
44 
45 typedef int8_t vfs_index_t;
46 _Static_assert((1 << (sizeof(vfs_index_t)*8)) >= VFS_MAX_COUNT, "VFS index type too small");
47 _Static_assert(((vfs_index_t) -1) < 0, "vfs_index_t must be a signed type");
48 
49 typedef struct {
50     bool permanent;
51     vfs_index_t vfs_index;
52     local_fd_t local_fd;
53 } fd_table_t;
54 
55 typedef struct vfs_entry_ {
56     esp_vfs_t vfs;          // contains pointers to VFS functions
57     char path_prefix[ESP_VFS_PATH_MAX]; // path prefix mapped to this VFS
58     size_t path_prefix_len; // micro-optimization to avoid doing extra strlen
59     void* ctx;              // optional pointer which can be passed to VFS
60     int offset;             // index of this structure in s_vfs array
61 } vfs_entry_t;
62 
63 typedef struct {
64     bool isset; // none or at least one bit is set in the following 3 fd sets
65     fd_set readfds;
66     fd_set writefds;
67     fd_set errorfds;
68 } fds_triple_t;
69 
70 static vfs_entry_t* s_vfs[VFS_MAX_COUNT] = { 0 };
71 static size_t s_vfs_count = 0;
72 
73 static fd_table_t s_fd_table[MAX_FDS] = { [0 ... MAX_FDS-1] = FD_TABLE_ENTRY_UNUSED };
74 static _lock_t s_fd_table_lock;
75 
esp_vfs_register_common(const char * base_path,size_t len,const esp_vfs_t * vfs,void * ctx,int * vfs_index)76 static esp_err_t esp_vfs_register_common(const char* base_path, size_t len, const esp_vfs_t* vfs, void* ctx, int *vfs_index)
77 {
78     if (len != LEN_PATH_PREFIX_IGNORED) {
79         /* empty prefix is allowed, "/" is not allowed */
80         if ((len == 1) || (len > ESP_VFS_PATH_MAX)) {
81             return ESP_ERR_INVALID_ARG;
82         }
83         /* prefix has to start with "/" and not end with "/" */
84         if (len >= 2 && ((base_path[0] != '/') || (base_path[len - 1] == '/'))) {
85             return ESP_ERR_INVALID_ARG;
86         }
87     }
88     vfs_entry_t *entry = (vfs_entry_t*) malloc(sizeof(vfs_entry_t));
89     if (entry == NULL) {
90         return ESP_ERR_NO_MEM;
91     }
92     size_t index;
93     for (index = 0; index < s_vfs_count; ++index) {
94         if (s_vfs[index] == NULL) {
95             break;
96         }
97     }
98     if (index == s_vfs_count) {
99         if (s_vfs_count >= VFS_MAX_COUNT) {
100             free(entry);
101             return ESP_ERR_NO_MEM;
102         }
103         ++s_vfs_count;
104     }
105     s_vfs[index] = entry;
106     if (len != LEN_PATH_PREFIX_IGNORED) {
107         strcpy(entry->path_prefix, base_path); // we have already verified argument length
108     } else {
109         bzero(entry->path_prefix, sizeof(entry->path_prefix));
110     }
111     memcpy(&entry->vfs, vfs, sizeof(esp_vfs_t));
112     entry->path_prefix_len = len;
113     entry->ctx = ctx;
114     entry->offset = index;
115 
116     if (vfs_index) {
117         *vfs_index = index;
118     }
119 
120     return ESP_OK;
121 }
122 
esp_vfs_register(const char * base_path,const esp_vfs_t * vfs,void * ctx)123 esp_err_t esp_vfs_register(const char* base_path, const esp_vfs_t* vfs, void* ctx)
124 {
125     return esp_vfs_register_common(base_path, strlen(base_path), vfs, ctx, NULL);
126 }
127 
esp_vfs_register_fd_range(const esp_vfs_t * vfs,void * ctx,int min_fd,int max_fd)128 esp_err_t esp_vfs_register_fd_range(const esp_vfs_t *vfs, void *ctx, int min_fd, int max_fd)
129 {
130     if (min_fd < 0 || max_fd < 0 || min_fd > MAX_FDS || max_fd > MAX_FDS || min_fd > max_fd) {
131         ESP_LOGD(TAG, "Invalid arguments: esp_vfs_register_fd_range(0x%x, 0x%x, %d, %d)", (int) vfs, (int) ctx, min_fd, max_fd);
132         return ESP_ERR_INVALID_ARG;
133     }
134 
135     int index = -1;
136     esp_err_t ret = esp_vfs_register_common("", LEN_PATH_PREFIX_IGNORED, vfs, ctx, &index);
137 
138     if (ret == ESP_OK) {
139         _lock_acquire(&s_fd_table_lock);
140         for (int i = min_fd; i < max_fd; ++i) {
141             if (s_fd_table[i].vfs_index != -1) {
142                 free(s_vfs[i]);
143                 s_vfs[i] = NULL;
144                 for (int j = min_fd; j < i; ++j) {
145                     if (s_fd_table[j].vfs_index == index) {
146                         s_fd_table[j] = FD_TABLE_ENTRY_UNUSED;
147                     }
148                 }
149                 _lock_release(&s_fd_table_lock);
150                 ESP_LOGD(TAG, "esp_vfs_register_fd_range cannot set fd %d (used by other VFS)", i);
151                 return ESP_ERR_INVALID_ARG;
152             }
153             s_fd_table[i].permanent = true;
154             s_fd_table[i].vfs_index = index;
155             s_fd_table[i].local_fd = i;
156         }
157         _lock_release(&s_fd_table_lock);
158     }
159 
160     ESP_LOGD(TAG, "esp_vfs_register_fd_range is successful for range <%d; %d) and VFS ID %d", min_fd, max_fd, index);
161 
162     return ret;
163 }
164 
esp_vfs_register_with_id(const esp_vfs_t * vfs,void * ctx,esp_vfs_id_t * vfs_id)165 esp_err_t esp_vfs_register_with_id(const esp_vfs_t *vfs, void *ctx, esp_vfs_id_t *vfs_id)
166 {
167     if (vfs_id == NULL) {
168         return ESP_ERR_INVALID_ARG;
169     }
170 
171     *vfs_id = -1;
172     return esp_vfs_register_common("", LEN_PATH_PREFIX_IGNORED, vfs, ctx, vfs_id);
173 }
174 
esp_vfs_unregister(const char * base_path)175 esp_err_t esp_vfs_unregister(const char* base_path)
176 {
177     const size_t base_path_len = strlen(base_path);
178     for (size_t i = 0; i < s_vfs_count; ++i) {
179         vfs_entry_t* vfs = s_vfs[i];
180         if (vfs == NULL) {
181             continue;
182         }
183         if (base_path_len == vfs->path_prefix_len &&
184                 memcmp(base_path, vfs->path_prefix, vfs->path_prefix_len) == 0) {
185             free(vfs);
186             s_vfs[i] = NULL;
187 
188             _lock_acquire(&s_fd_table_lock);
189             // Delete all references from the FD lookup-table
190             for (int j = 0; j < MAX_FDS; ++j) {
191                 if (s_fd_table[j].vfs_index == i) {
192                     s_fd_table[j] = FD_TABLE_ENTRY_UNUSED;
193                 }
194             }
195             _lock_release(&s_fd_table_lock);
196 
197             return ESP_OK;
198         }
199     }
200     return ESP_ERR_INVALID_STATE;
201 }
202 
esp_vfs_register_fd(esp_vfs_id_t vfs_id,int * fd)203 esp_err_t esp_vfs_register_fd(esp_vfs_id_t vfs_id, int *fd)
204 {
205     if (vfs_id < 0 || vfs_id >= s_vfs_count || fd == NULL) {
206         ESP_LOGD(TAG, "Invalid arguments for esp_vfs_register_fd(%d, 0x%x)", vfs_id, (int) fd);
207         return ESP_ERR_INVALID_ARG;
208     }
209 
210     esp_err_t ret = ESP_ERR_NO_MEM;
211     _lock_acquire(&s_fd_table_lock);
212     for (int i = 0; i < MAX_FDS; ++i) {
213         if (s_fd_table[i].vfs_index == -1) {
214             s_fd_table[i].permanent = true;
215             s_fd_table[i].vfs_index = vfs_id;
216             s_fd_table[i].local_fd = i;
217             *fd = i;
218             ret = ESP_OK;
219             break;
220         }
221     }
222     _lock_release(&s_fd_table_lock);
223 
224     ESP_LOGD(TAG, "esp_vfs_register_fd(%d, 0x%x) finished with %s", vfs_id, (int) fd, esp_err_to_name(ret));
225 
226     return ret;
227 }
228 
esp_vfs_unregister_fd(esp_vfs_id_t vfs_id,int fd)229 esp_err_t esp_vfs_unregister_fd(esp_vfs_id_t vfs_id, int fd)
230 {
231     esp_err_t ret = ESP_ERR_INVALID_ARG;
232 
233     if (vfs_id < 0 || vfs_id >= s_vfs_count || fd < 0 || fd >= MAX_FDS) {
234         ESP_LOGD(TAG, "Invalid arguments for esp_vfs_unregister_fd(%d, %d)", vfs_id, fd);
235         return ret;
236     }
237 
238     _lock_acquire(&s_fd_table_lock);
239     fd_table_t *item = s_fd_table + fd;
240     if (item->permanent == true && item->vfs_index == vfs_id && item->local_fd == fd) {
241         *item = FD_TABLE_ENTRY_UNUSED;
242         ret = ESP_OK;
243     }
244     _lock_release(&s_fd_table_lock);
245 
246     ESP_LOGD(TAG, "esp_vfs_unregister_fd(%d, %d) finished with %s", vfs_id, fd, esp_err_to_name(ret));
247 
248     return ret;
249 }
250 
get_vfs_for_index(int index)251 static inline const vfs_entry_t *get_vfs_for_index(int index)
252 {
253     if (index < 0 || index >= s_vfs_count) {
254         return NULL;
255     } else {
256         return s_vfs[index];
257     }
258 }
259 
fd_valid(int fd)260 static inline bool fd_valid(int fd)
261 {
262     return (fd < MAX_FDS) && (fd >= 0);
263 }
264 
get_vfs_for_fd(int fd)265 static const vfs_entry_t *get_vfs_for_fd(int fd)
266 {
267     const vfs_entry_t *vfs = NULL;
268     if (fd_valid(fd)) {
269         const int index = s_fd_table[fd].vfs_index; // single read -> no locking is required
270         vfs = get_vfs_for_index(index);
271     }
272     return vfs;
273 }
274 
get_local_fd(const vfs_entry_t * vfs,int fd)275 static inline int get_local_fd(const vfs_entry_t *vfs, int fd)
276 {
277     int local_fd = -1;
278 
279     if (vfs && fd_valid(fd)) {
280         local_fd = s_fd_table[fd].local_fd; // single read -> no locking is required
281     }
282 
283     return local_fd;
284 }
285 
translate_path(const vfs_entry_t * vfs,const char * src_path)286 static const char* translate_path(const vfs_entry_t* vfs, const char* src_path)
287 {
288     assert(strncmp(src_path, vfs->path_prefix, vfs->path_prefix_len) == 0);
289     if (strlen(src_path) == vfs->path_prefix_len) {
290         // special case when src_path matches the path prefix exactly
291         return "/";
292     }
293     return src_path + vfs->path_prefix_len;
294 }
295 
get_vfs_for_path(const char * path)296 static const vfs_entry_t* get_vfs_for_path(const char* path)
297 {
298     const vfs_entry_t* best_match = NULL;
299     ssize_t best_match_prefix_len = -1;
300     size_t len = strlen(path);
301     for (size_t i = 0; i < s_vfs_count; ++i) {
302         const vfs_entry_t* vfs = s_vfs[i];
303         if (!vfs || vfs->path_prefix_len == LEN_PATH_PREFIX_IGNORED) {
304             continue;
305         }
306         // match path prefix
307         if (len < vfs->path_prefix_len ||
308             memcmp(path, vfs->path_prefix, vfs->path_prefix_len) != 0) {
309             continue;
310         }
311         // this is the default VFS and we don't have a better match yet.
312         if (vfs->path_prefix_len == 0 && !best_match) {
313             best_match = vfs;
314             continue;
315         }
316         // if path is not equal to the prefix, expect to see a path separator
317         // i.e. don't match "/data" prefix for "/data1/foo.txt" path
318         if (len > vfs->path_prefix_len &&
319                 path[vfs->path_prefix_len] != '/') {
320             continue;
321         }
322         // Out of all matching path prefixes, select the longest one;
323         // i.e. if "/dev" and "/dev/uart" both match, for "/dev/uart/1" path,
324         // choose "/dev/uart",
325         // This causes all s_vfs_count VFS entries to be scanned when opening
326         // a file by name. This can be optimized by introducing a table for
327         // FS search order, sorted so that longer prefixes are checked first.
328         if (best_match_prefix_len < (ssize_t) vfs->path_prefix_len) {
329             best_match_prefix_len = (ssize_t) vfs->path_prefix_len;
330             best_match = vfs;
331         }
332     }
333     return best_match;
334 }
335 
336 /*
337  * Using huge multi-line macros is never nice, but in this case
338  * the only alternative is to repeat this chunk of code (with different function names)
339  * for each syscall being implemented. Given that this define is contained within a single
340  * file, this looks like a good tradeoff.
341  *
342  * First we check if syscall is implemented by VFS (corresponding member is not NULL),
343  * then call the right flavor of the method (e.g. open or open_p) depending on
344  * ESP_VFS_FLAG_CONTEXT_PTR flag. If ESP_VFS_FLAG_CONTEXT_PTR is set, context is passed
345  * in as first argument and _p variant is used for the call.
346  * It is enough to check just one of them for NULL, as both variants are part of a union.
347  */
348 #define CHECK_AND_CALL(ret, r, pvfs, func, ...) \
349     if (pvfs->vfs.func == NULL) { \
350         __errno_r(r) = ENOSYS; \
351         return -1; \
352     } \
353     if (pvfs->vfs.flags & ESP_VFS_FLAG_CONTEXT_PTR) { \
354         ret = (*pvfs->vfs.func ## _p)(pvfs->ctx, __VA_ARGS__); \
355     } else { \
356         ret = (*pvfs->vfs.func)(__VA_ARGS__);\
357     }
358 
359 
360 #define CHECK_AND_CALLV(r, pvfs, func, ...) \
361     if (pvfs->vfs.func == NULL) { \
362         __errno_r(r) = ENOSYS; \
363         return; \
364     } \
365     if (pvfs->vfs.flags & ESP_VFS_FLAG_CONTEXT_PTR) { \
366         (*pvfs->vfs.func ## _p)(pvfs->ctx, __VA_ARGS__); \
367     } else { \
368         (*pvfs->vfs.func)(__VA_ARGS__);\
369     }
370 
371 #define CHECK_AND_CALLP(ret, r, pvfs, func, ...) \
372     if (pvfs->vfs.func == NULL) { \
373         __errno_r(r) = ENOSYS; \
374         return NULL; \
375     } \
376     if (pvfs->vfs.flags & ESP_VFS_FLAG_CONTEXT_PTR) { \
377         ret = (*pvfs->vfs.func ## _p)(pvfs->ctx, __VA_ARGS__); \
378     } else { \
379         ret = (*pvfs->vfs.func)(__VA_ARGS__);\
380     }
381 
esp_vfs_open(struct _reent * r,const char * path,int flags,int mode)382 int esp_vfs_open(struct _reent *r, const char * path, int flags, int mode)
383 {
384     const vfs_entry_t *vfs = get_vfs_for_path(path);
385     if (vfs == NULL) {
386         __errno_r(r) = ENOENT;
387         return -1;
388     }
389     const char *path_within_vfs = translate_path(vfs, path);
390     int fd_within_vfs;
391     CHECK_AND_CALL(fd_within_vfs, r, vfs, open, path_within_vfs, flags, mode);
392     if (fd_within_vfs >= 0) {
393         _lock_acquire(&s_fd_table_lock);
394         for (int i = 0; i < MAX_FDS; ++i) {
395             if (s_fd_table[i].vfs_index == -1) {
396                 s_fd_table[i].permanent = false;
397                 s_fd_table[i].vfs_index = vfs->offset;
398                 s_fd_table[i].local_fd = fd_within_vfs;
399                 _lock_release(&s_fd_table_lock);
400                 return i;
401             }
402         }
403         _lock_release(&s_fd_table_lock);
404         int ret;
405         CHECK_AND_CALL(ret, r, vfs, close, fd_within_vfs);
406         (void) ret; // remove "set but not used" warning
407         __errno_r(r) = ENOMEM;
408         return -1;
409     }
410     __errno_r(r) = ENOENT;
411     return -1;
412 }
413 
esp_vfs_write(struct _reent * r,int fd,const void * data,size_t size)414 ssize_t esp_vfs_write(struct _reent *r, int fd, const void * data, size_t size)
415 {
416     const vfs_entry_t* vfs = get_vfs_for_fd(fd);
417     const int local_fd = get_local_fd(vfs, fd);
418     if (vfs == NULL || local_fd < 0) {
419         __errno_r(r) = EBADF;
420         return -1;
421     }
422     ssize_t ret;
423     CHECK_AND_CALL(ret, r, vfs, write, local_fd, data, size);
424     return ret;
425 }
426 
esp_vfs_lseek(struct _reent * r,int fd,off_t size,int mode)427 off_t esp_vfs_lseek(struct _reent *r, int fd, off_t size, int mode)
428 {
429     const vfs_entry_t* vfs = get_vfs_for_fd(fd);
430     const int local_fd = get_local_fd(vfs, fd);
431     if (vfs == NULL || local_fd < 0) {
432         __errno_r(r) = EBADF;
433         return -1;
434     }
435     off_t ret;
436     CHECK_AND_CALL(ret, r, vfs, lseek, local_fd, size, mode);
437     return ret;
438 }
439 
esp_vfs_read(struct _reent * r,int fd,void * dst,size_t size)440 ssize_t esp_vfs_read(struct _reent *r, int fd, void * dst, size_t size)
441 {
442     const vfs_entry_t* vfs = get_vfs_for_fd(fd);
443     const int local_fd = get_local_fd(vfs, fd);
444     if (vfs == NULL || local_fd < 0) {
445         __errno_r(r) = EBADF;
446         return -1;
447     }
448     ssize_t ret;
449     CHECK_AND_CALL(ret, r, vfs, read, local_fd, dst, size);
450     return ret;
451 }
452 
esp_vfs_pread(int fd,void * dst,size_t size,off_t offset)453 ssize_t esp_vfs_pread(int fd, void *dst, size_t size, off_t offset)
454 {
455     struct _reent *r = __getreent();
456     const vfs_entry_t* vfs = get_vfs_for_fd(fd);
457     const int local_fd = get_local_fd(vfs, fd);
458     if (vfs == NULL || local_fd < 0) {
459         __errno_r(r) = EBADF;
460         return -1;
461     }
462     ssize_t ret;
463     CHECK_AND_CALL(ret, r, vfs, pread, local_fd, dst, size, offset);
464     return ret;
465 }
466 
esp_vfs_pwrite(int fd,const void * src,size_t size,off_t offset)467 ssize_t esp_vfs_pwrite(int fd, const void *src, size_t size, off_t offset)
468 {
469     struct _reent *r = __getreent();
470     const vfs_entry_t* vfs = get_vfs_for_fd(fd);
471     const int local_fd = get_local_fd(vfs, fd);
472     if (vfs == NULL || local_fd < 0) {
473         __errno_r(r) = EBADF;
474         return -1;
475     }
476     ssize_t ret;
477     CHECK_AND_CALL(ret, r, vfs, pwrite, local_fd, src, size, offset);
478     return ret;
479 }
480 
esp_vfs_close(struct _reent * r,int fd)481 int esp_vfs_close(struct _reent *r, int fd)
482 {
483     const vfs_entry_t* vfs = get_vfs_for_fd(fd);
484     const int local_fd = get_local_fd(vfs, fd);
485     if (vfs == NULL || local_fd < 0) {
486         __errno_r(r) = EBADF;
487         return -1;
488     }
489     int ret;
490     CHECK_AND_CALL(ret, r, vfs, close, local_fd);
491 
492     _lock_acquire(&s_fd_table_lock);
493     if (!s_fd_table[fd].permanent) {
494         s_fd_table[fd] = FD_TABLE_ENTRY_UNUSED;
495     }
496     _lock_release(&s_fd_table_lock);
497     return ret;
498 }
499 
esp_vfs_fstat(struct _reent * r,int fd,struct stat * st)500 int esp_vfs_fstat(struct _reent *r, int fd, struct stat * st)
501 {
502     const vfs_entry_t* vfs = get_vfs_for_fd(fd);
503     const int local_fd = get_local_fd(vfs, fd);
504     if (vfs == NULL || local_fd < 0) {
505         __errno_r(r) = EBADF;
506         return -1;
507     }
508     int ret;
509     CHECK_AND_CALL(ret, r, vfs, fstat, local_fd, st);
510     return ret;
511 }
512 
esp_vfs_fcntl_r(struct _reent * r,int fd,int cmd,int arg)513 int esp_vfs_fcntl_r(struct _reent *r, int fd, int cmd, int arg)
514 {
515     const vfs_entry_t* vfs = get_vfs_for_fd(fd);
516     const int local_fd = get_local_fd(vfs, fd);
517     if (vfs == NULL || local_fd < 0) {
518         __errno_r(r) = EBADF;
519         return -1;
520     }
521     int ret;
522     CHECK_AND_CALL(ret, r, vfs, fcntl, local_fd, cmd, arg);
523     return ret;
524 }
525 
esp_vfs_ioctl(int fd,int cmd,...)526 int esp_vfs_ioctl(int fd, int cmd, ...)
527 {
528     const vfs_entry_t* vfs = get_vfs_for_fd(fd);
529     const int local_fd = get_local_fd(vfs, fd);
530     struct _reent* r = __getreent();
531     if (vfs == NULL || local_fd < 0) {
532         __errno_r(r) = EBADF;
533         return -1;
534     }
535     int ret;
536     va_list args;
537     va_start(args, cmd);
538     CHECK_AND_CALL(ret, r, vfs, ioctl, local_fd, cmd, args);
539     va_end(args);
540     return ret;
541 }
542 
esp_vfs_fsync(int fd)543 int esp_vfs_fsync(int fd)
544 {
545     const vfs_entry_t* vfs = get_vfs_for_fd(fd);
546     const int local_fd = get_local_fd(vfs, fd);
547     struct _reent* r = __getreent();
548     if (vfs == NULL || local_fd < 0) {
549         __errno_r(r) = EBADF;
550         return -1;
551     }
552     int ret;
553     CHECK_AND_CALL(ret, r, vfs, fsync, local_fd);
554     return ret;
555 }
556 
557 #ifdef CONFIG_VFS_SUPPORT_DIR
558 
esp_vfs_stat(struct _reent * r,const char * path,struct stat * st)559 int esp_vfs_stat(struct _reent *r, const char * path, struct stat * st)
560 {
561     const vfs_entry_t* vfs = get_vfs_for_path(path);
562     if (vfs == NULL) {
563         __errno_r(r) = ENOENT;
564         return -1;
565     }
566     const char* path_within_vfs = translate_path(vfs, path);
567     int ret;
568     CHECK_AND_CALL(ret, r, vfs, stat, path_within_vfs, st);
569     return ret;
570 }
571 
esp_vfs_utime(const char * path,const struct utimbuf * times)572 int esp_vfs_utime(const char *path, const struct utimbuf *times)
573 {
574     int ret;
575     const vfs_entry_t* vfs = get_vfs_for_path(path);
576     struct _reent* r = __getreent();
577     if (vfs == NULL) {
578         __errno_r(r) = ENOENT;
579         return -1;
580     }
581     const char* path_within_vfs = translate_path(vfs, path);
582     CHECK_AND_CALL(ret, r, vfs, utime, path_within_vfs, times);
583     return ret;
584 }
585 
esp_vfs_link(struct _reent * r,const char * n1,const char * n2)586 int esp_vfs_link(struct _reent *r, const char* n1, const char* n2)
587 {
588     const vfs_entry_t* vfs = get_vfs_for_path(n1);
589     if (vfs == NULL) {
590         __errno_r(r) = ENOENT;
591         return -1;
592     }
593     const vfs_entry_t* vfs2 = get_vfs_for_path(n2);
594     if (vfs != vfs2) {
595         __errno_r(r) = EXDEV;
596         return -1;
597     }
598     const char* path1_within_vfs = translate_path(vfs, n1);
599     const char* path2_within_vfs = translate_path(vfs, n2);
600     int ret;
601     CHECK_AND_CALL(ret, r, vfs, link, path1_within_vfs, path2_within_vfs);
602     return ret;
603 }
604 
esp_vfs_unlink(struct _reent * r,const char * path)605 int esp_vfs_unlink(struct _reent *r, const char *path)
606 {
607     const vfs_entry_t* vfs = get_vfs_for_path(path);
608     if (vfs == NULL) {
609         __errno_r(r) = ENOENT;
610         return -1;
611     }
612     const char* path_within_vfs = translate_path(vfs, path);
613     int ret;
614     CHECK_AND_CALL(ret, r, vfs, unlink, path_within_vfs);
615     return ret;
616 }
617 
esp_vfs_rename(struct _reent * r,const char * src,const char * dst)618 int esp_vfs_rename(struct _reent *r, const char *src, const char *dst)
619 {
620     const vfs_entry_t* vfs = get_vfs_for_path(src);
621     if (vfs == NULL) {
622         __errno_r(r) = ENOENT;
623         return -1;
624     }
625     const vfs_entry_t* vfs_dst = get_vfs_for_path(dst);
626     if (vfs != vfs_dst) {
627         __errno_r(r) = EXDEV;
628         return -1;
629     }
630     const char* src_within_vfs = translate_path(vfs, src);
631     const char* dst_within_vfs = translate_path(vfs, dst);
632     int ret;
633     CHECK_AND_CALL(ret, r, vfs, rename, src_within_vfs, dst_within_vfs);
634     return ret;
635 }
636 
esp_vfs_opendir(const char * name)637 DIR* esp_vfs_opendir(const char* name)
638 {
639     const vfs_entry_t* vfs = get_vfs_for_path(name);
640     struct _reent* r = __getreent();
641     if (vfs == NULL) {
642         __errno_r(r) = ENOENT;
643         return NULL;
644     }
645     const char* path_within_vfs = translate_path(vfs, name);
646     DIR* ret;
647     CHECK_AND_CALLP(ret, r, vfs, opendir, path_within_vfs);
648     if (ret != NULL) {
649         ret->dd_vfs_idx = vfs->offset;
650     }
651     return ret;
652 }
653 
esp_vfs_readdir(DIR * pdir)654 struct dirent* esp_vfs_readdir(DIR* pdir)
655 {
656     const vfs_entry_t* vfs = get_vfs_for_index(pdir->dd_vfs_idx);
657     struct _reent* r = __getreent();
658     if (vfs == NULL) {
659        __errno_r(r) = EBADF;
660         return NULL;
661     }
662     struct dirent* ret;
663     CHECK_AND_CALLP(ret, r, vfs, readdir, pdir);
664     return ret;
665 }
666 
esp_vfs_readdir_r(DIR * pdir,struct dirent * entry,struct dirent ** out_dirent)667 int esp_vfs_readdir_r(DIR* pdir, struct dirent* entry, struct dirent** out_dirent)
668 {
669     const vfs_entry_t* vfs = get_vfs_for_index(pdir->dd_vfs_idx);
670     struct _reent* r = __getreent();
671     if (vfs == NULL) {
672         errno = EBADF;
673         return -1;
674     }
675     int ret;
676     CHECK_AND_CALL(ret, r, vfs, readdir_r, pdir, entry, out_dirent);
677     return ret;
678 }
679 
esp_vfs_telldir(DIR * pdir)680 long esp_vfs_telldir(DIR* pdir)
681 {
682     const vfs_entry_t* vfs = get_vfs_for_index(pdir->dd_vfs_idx);
683     struct _reent* r = __getreent();
684     if (vfs == NULL) {
685         errno = EBADF;
686         return -1;
687     }
688     long ret;
689     CHECK_AND_CALL(ret, r, vfs, telldir, pdir);
690     return ret;
691 }
692 
esp_vfs_seekdir(DIR * pdir,long loc)693 void esp_vfs_seekdir(DIR* pdir, long loc)
694 {
695     const vfs_entry_t* vfs = get_vfs_for_index(pdir->dd_vfs_idx);
696     struct _reent* r = __getreent();
697     if (vfs == NULL) {
698         errno = EBADF;
699         return;
700     }
701     CHECK_AND_CALLV(r, vfs, seekdir, pdir, loc);
702 }
703 
esp_vfs_rewinddir(DIR * pdir)704 void esp_vfs_rewinddir(DIR* pdir)
705 {
706     seekdir(pdir, 0);
707 }
708 
esp_vfs_closedir(DIR * pdir)709 int esp_vfs_closedir(DIR* pdir)
710 {
711     const vfs_entry_t* vfs = get_vfs_for_index(pdir->dd_vfs_idx);
712     struct _reent* r = __getreent();
713     if (vfs == NULL) {
714         errno = EBADF;
715         return -1;
716     }
717     int ret;
718     CHECK_AND_CALL(ret, r, vfs, closedir, pdir);
719     return ret;
720 }
721 
esp_vfs_mkdir(const char * name,mode_t mode)722 int esp_vfs_mkdir(const char* name, mode_t mode)
723 {
724     const vfs_entry_t* vfs = get_vfs_for_path(name);
725     struct _reent* r = __getreent();
726     if (vfs == NULL) {
727         __errno_r(r) = ENOENT;
728         return -1;
729     }
730     const char* path_within_vfs = translate_path(vfs, name);
731     int ret;
732     CHECK_AND_CALL(ret, r, vfs, mkdir, path_within_vfs, mode);
733     return ret;
734 }
735 
esp_vfs_rmdir(const char * name)736 int esp_vfs_rmdir(const char* name)
737 {
738     const vfs_entry_t* vfs = get_vfs_for_path(name);
739     struct _reent* r = __getreent();
740     if (vfs == NULL) {
741         __errno_r(r) = ENOENT;
742         return -1;
743     }
744     const char* path_within_vfs = translate_path(vfs, name);
745     int ret;
746     CHECK_AND_CALL(ret, r, vfs, rmdir, path_within_vfs);
747     return ret;
748 }
749 
esp_vfs_access(const char * path,int amode)750 int esp_vfs_access(const char *path, int amode)
751 {
752     int ret;
753     const vfs_entry_t* vfs = get_vfs_for_path(path);
754     struct _reent* r = __getreent();
755     if (vfs == NULL) {
756         __errno_r(r) = ENOENT;
757         return -1;
758     }
759     const char* path_within_vfs = translate_path(vfs, path);
760     CHECK_AND_CALL(ret, r, vfs, access, path_within_vfs, amode);
761     return ret;
762 }
763 
esp_vfs_truncate(const char * path,off_t length)764 int esp_vfs_truncate(const char *path, off_t length)
765 {
766     int ret;
767     const vfs_entry_t* vfs = get_vfs_for_path(path);
768     struct _reent* r = __getreent();
769     if (vfs == NULL) {
770         __errno_r(r) = ENOENT;
771         return -1;
772     }
773     const char* path_within_vfs = translate_path(vfs, path);
774     CHECK_AND_CALL(ret, r, vfs, truncate, path_within_vfs, length);
775     return ret;
776 }
777 
778 #endif // CONFIG_VFS_SUPPORT_DIR
779 
780 #ifdef CONFIG_VFS_SUPPORT_SELECT
781 
call_end_selects(int end_index,const fds_triple_t * vfs_fds_triple,void ** driver_args)782 static void call_end_selects(int end_index, const fds_triple_t *vfs_fds_triple, void **driver_args)
783 {
784     for (int i = 0; i < end_index; ++i) {
785         const vfs_entry_t *vfs = get_vfs_for_index(i);
786         const fds_triple_t *item = &vfs_fds_triple[i];
787         if (vfs && vfs->vfs.end_select && item->isset) {
788             esp_err_t err = vfs->vfs.end_select(driver_args[i]);
789             if (err != ESP_OK) {
790                 ESP_LOGD(TAG, "end_select failed: %s", esp_err_to_name(err));
791             }
792         }
793     }
794 }
795 
esp_vfs_safe_fd_isset(int fd,const fd_set * fds)796 static inline bool esp_vfs_safe_fd_isset(int fd, const fd_set *fds)
797 {
798     return fds && FD_ISSET(fd, fds);
799 }
800 
set_global_fd_sets(const fds_triple_t * vfs_fds_triple,int size,fd_set * readfds,fd_set * writefds,fd_set * errorfds)801 static int set_global_fd_sets(const fds_triple_t *vfs_fds_triple, int size, fd_set *readfds, fd_set *writefds, fd_set *errorfds)
802 {
803     int ret = 0;
804 
805     for (int i = 0; i < size; ++i) {
806         const fds_triple_t *item = &vfs_fds_triple[i];
807         if (item->isset) {
808             for (int fd = 0; fd < MAX_FDS; ++fd) {
809                 const int local_fd = s_fd_table[fd].local_fd; // single read -> no locking is required
810                 if (readfds && esp_vfs_safe_fd_isset(local_fd, &item->readfds)) {
811                     ESP_LOGD(TAG, "FD %d in readfds was set from VFS ID %d", fd, i);
812                     FD_SET(fd, readfds);
813                     ++ret;
814                 }
815                 if (writefds && esp_vfs_safe_fd_isset(local_fd, &item->writefds)) {
816                     ESP_LOGD(TAG, "FD %d in writefds was set from VFS ID %d", fd, i);
817                     FD_SET(fd, writefds);
818                     ++ret;
819                 }
820                 if (errorfds && esp_vfs_safe_fd_isset(local_fd, &item->errorfds)) {
821                     ESP_LOGD(TAG, "FD %d in errorfds was set from VFS ID %d", fd, i);
822                     FD_SET(fd, errorfds);
823                     ++ret;
824                 }
825             }
826         }
827     }
828 
829     return ret;
830 }
831 
esp_vfs_log_fd_set(const char * fds_name,const fd_set * fds)832 static void esp_vfs_log_fd_set(const char *fds_name, const fd_set *fds)
833 {
834     if (fds_name && fds) {
835         ESP_LOGD(TAG, "FDs in %s =", fds_name);
836         for (int i = 0; i < MAX_FDS; ++i) {
837             if (esp_vfs_safe_fd_isset(i, fds)) {
838                 ESP_LOGD(TAG, "%d", i);
839             }
840         }
841     }
842 }
843 
esp_vfs_select(int nfds,fd_set * readfds,fd_set * writefds,fd_set * errorfds,struct timeval * timeout)844 int esp_vfs_select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *errorfds, struct timeval *timeout)
845 {
846     // NOTE: Please see the "Synchronous input/output multiplexing" section of the ESP-IDF Programming Guide
847     // (API Reference -> Storage -> Virtual Filesystem) for a general overview of the implementation of VFS select().
848     int ret = 0;
849     struct _reent* r = __getreent();
850 
851     ESP_LOGD(TAG, "esp_vfs_select starts with nfds = %d", nfds);
852     if (timeout) {
853         ESP_LOGD(TAG, "timeout is %lds + %ldus", (long)timeout->tv_sec, timeout->tv_usec);
854     }
855     esp_vfs_log_fd_set("readfds", readfds);
856     esp_vfs_log_fd_set("writefds", writefds);
857     esp_vfs_log_fd_set("errorfds", errorfds);
858 
859     if (nfds > MAX_FDS || nfds < 0) {
860         ESP_LOGD(TAG, "incorrect nfds");
861         __errno_r(r) = EINVAL;
862         return -1;
863     }
864 
865     // Capture s_vfs_count to a local variable in case a new driver is registered or removed during this actual select()
866     // call. s_vfs_count cannot be protected with a mutex during a select() call (which can be one without a timeout)
867     // because that could block the registration of new driver.
868     const size_t vfs_count = s_vfs_count;
869     fds_triple_t *vfs_fds_triple;
870     if ((vfs_fds_triple = calloc(vfs_count, sizeof(fds_triple_t))) == NULL) {
871         __errno_r(r) = ENOMEM;
872         ESP_LOGD(TAG, "calloc is unsuccessful");
873         return -1;
874     }
875 
876     esp_vfs_select_sem_t sel_sem = {
877         .is_sem_local = false,
878         .sem = NULL,
879     };
880 
881     int (*socket_select)(int, fd_set *, fd_set *, fd_set *, struct timeval *) = NULL;
882     for (int fd = 0; fd < nfds; ++fd) {
883         _lock_acquire(&s_fd_table_lock);
884         const bool is_socket_fd = s_fd_table[fd].permanent;
885         const int vfs_index = s_fd_table[fd].vfs_index;
886         const int local_fd = s_fd_table[fd].local_fd;
887         _lock_release(&s_fd_table_lock);
888 
889         if (vfs_index < 0) {
890             continue;
891         }
892 
893         if (is_socket_fd) {
894             if (!socket_select) {
895                 // no socket_select found yet so take a look
896                 if (esp_vfs_safe_fd_isset(fd, readfds) ||
897                         esp_vfs_safe_fd_isset(fd, writefds) ||
898                         esp_vfs_safe_fd_isset(fd, errorfds)) {
899                     const vfs_entry_t *vfs = s_vfs[vfs_index];
900                     socket_select = vfs->vfs.socket_select;
901                     sel_sem.sem = vfs->vfs.get_socket_select_semaphore();
902                 }
903             }
904             continue;
905         }
906 
907         fds_triple_t *item = &vfs_fds_triple[vfs_index]; // FD sets for VFS which belongs to fd
908         if (esp_vfs_safe_fd_isset(fd, readfds)) {
909             item->isset = true;
910             FD_SET(local_fd, &item->readfds);
911             FD_CLR(fd, readfds);
912             ESP_LOGD(TAG, "removing %d from readfds and adding as local FD %d to fd_set of VFS ID %d", fd, local_fd, vfs_index);
913         }
914         if (esp_vfs_safe_fd_isset(fd, writefds)) {
915             item->isset = true;
916             FD_SET(local_fd, &item->writefds);
917             FD_CLR(fd, writefds);
918             ESP_LOGD(TAG, "removing %d from writefds and adding as local FD %d to fd_set of VFS ID %d", fd, local_fd, vfs_index);
919         }
920         if (esp_vfs_safe_fd_isset(fd, errorfds)) {
921             item->isset = true;
922             FD_SET(local_fd, &item->errorfds);
923             FD_CLR(fd, errorfds);
924             ESP_LOGD(TAG, "removing %d from errorfds and adding as local FD %d to fd_set of VFS ID %d", fd, local_fd, vfs_index);
925         }
926     }
927 
928     // all non-socket VFSs have their FD sets in vfs_fds_triple
929     // the global readfds, writefds and errorfds contain only socket FDs (if
930     // there any)
931 
932     if (!socket_select) {
933         // There is no socket VFS registered or select() wasn't called for
934         // any socket. Therefore, we will use our own signalization.
935         sel_sem.is_sem_local = true;
936         if ((sel_sem.sem = xSemaphoreCreateBinary()) == NULL) {
937             free(vfs_fds_triple);
938             __errno_r(r) = ENOMEM;
939             ESP_LOGD(TAG, "cannot create select semaphore");
940             return -1;
941         }
942     }
943 
944     void **driver_args = calloc(vfs_count, sizeof(void *));
945 
946     if (driver_args == NULL) {
947         free(vfs_fds_triple);
948         __errno_r(r) = ENOMEM;
949         ESP_LOGD(TAG, "calloc is unsuccessful for driver args");
950         return -1;
951     }
952 
953     for (size_t i = 0; i < vfs_count; ++i) {
954         const vfs_entry_t *vfs = get_vfs_for_index(i);
955         fds_triple_t *item = &vfs_fds_triple[i];
956 
957         if (vfs && vfs->vfs.start_select && item->isset) {
958             // call start_select for all non-socket VFSs with has at least one FD set in readfds, writefds, or errorfds
959             // note: it can point to socket VFS but item->isset will be false for that
960             ESP_LOGD(TAG, "calling start_select for VFS ID %d with the following local FDs", i);
961             esp_vfs_log_fd_set("readfds", &item->readfds);
962             esp_vfs_log_fd_set("writefds", &item->writefds);
963             esp_vfs_log_fd_set("errorfds", &item->errorfds);
964             esp_err_t err = vfs->vfs.start_select(nfds, &item->readfds, &item->writefds, &item->errorfds, sel_sem,
965                     driver_args + i);
966 
967             if (err != ESP_OK) {
968                 call_end_selects(i, vfs_fds_triple, driver_args);
969                 (void) set_global_fd_sets(vfs_fds_triple, vfs_count, readfds, writefds, errorfds);
970                 if (sel_sem.is_sem_local && sel_sem.sem) {
971                     vSemaphoreDelete(sel_sem.sem);
972                     sel_sem.sem = NULL;
973                 }
974                 free(vfs_fds_triple);
975                 free(driver_args);
976                 __errno_r(r) = EINTR;
977                 ESP_LOGD(TAG, "start_select failed: %s", esp_err_to_name(err));
978                 return -1;
979             }
980         }
981     }
982 
983     if (socket_select) {
984         ESP_LOGD(TAG, "calling socket_select with the following FDs");
985         esp_vfs_log_fd_set("readfds", readfds);
986         esp_vfs_log_fd_set("writefds", writefds);
987         esp_vfs_log_fd_set("errorfds", errorfds);
988         ret = socket_select(nfds, readfds, writefds, errorfds, timeout);
989         ESP_LOGD(TAG, "socket_select returned %d and the FDs are the following", ret);
990         esp_vfs_log_fd_set("readfds", readfds);
991         esp_vfs_log_fd_set("writefds", writefds);
992         esp_vfs_log_fd_set("errorfds", errorfds);
993     } else {
994         if (readfds) {
995             FD_ZERO(readfds);
996         }
997         if (writefds) {
998             FD_ZERO(writefds);
999         }
1000         if (errorfds) {
1001             FD_ZERO(errorfds);
1002         }
1003 
1004         TickType_t ticks_to_wait = portMAX_DELAY;
1005         if (timeout) {
1006             uint32_t timeout_ms = timeout->tv_sec * 1000 + timeout->tv_usec / 1000;
1007             ticks_to_wait = timeout_ms / portTICK_PERIOD_MS;
1008             ESP_LOGD(TAG, "timeout is %dms", timeout_ms);
1009         }
1010         ESP_LOGD(TAG, "waiting without calling socket_select");
1011         xSemaphoreTake(sel_sem.sem, ticks_to_wait);
1012     }
1013 
1014     call_end_selects(vfs_count, vfs_fds_triple, driver_args); // for VFSs for start_select was called before
1015     if (ret >= 0) {
1016         ret += set_global_fd_sets(vfs_fds_triple, vfs_count, readfds, writefds, errorfds);
1017     }
1018     if (sel_sem.is_sem_local && sel_sem.sem) {
1019         vSemaphoreDelete(sel_sem.sem);
1020         sel_sem.sem = NULL;
1021     }
1022     free(vfs_fds_triple);
1023     free(driver_args);
1024 
1025     ESP_LOGD(TAG, "esp_vfs_select returns %d", ret);
1026     esp_vfs_log_fd_set("readfds", readfds);
1027     esp_vfs_log_fd_set("writefds", writefds);
1028     esp_vfs_log_fd_set("errorfds", errorfds);
1029     return ret;
1030 }
1031 
esp_vfs_select_triggered(esp_vfs_select_sem_t sem)1032 void esp_vfs_select_triggered(esp_vfs_select_sem_t sem)
1033 {
1034     if (sem.is_sem_local) {
1035         xSemaphoreGive(sem.sem);
1036     } else {
1037         // Another way would be to go through s_fd_table and find the VFS
1038         // which has a permanent FD. But in order to avoid to lock
1039         // s_fd_table_lock we go through the VFS table.
1040         for (int i = 0; i < s_vfs_count; ++i) {
1041             // Note: s_vfs_count could have changed since the start of vfs_select() call. However, that change doesn't
1042             // matter here stop_socket_select() will be called for only valid VFS drivers.
1043             const vfs_entry_t *vfs = s_vfs[i];
1044             if (vfs != NULL && vfs->vfs.stop_socket_select != NULL) {
1045                 vfs->vfs.stop_socket_select(sem.sem);
1046                 break;
1047             }
1048         }
1049     }
1050 }
1051 
esp_vfs_select_triggered_isr(esp_vfs_select_sem_t sem,BaseType_t * woken)1052 void esp_vfs_select_triggered_isr(esp_vfs_select_sem_t sem, BaseType_t *woken)
1053 {
1054     if (sem.is_sem_local) {
1055         xSemaphoreGiveFromISR(sem.sem, woken);
1056     } else {
1057         // Another way would be to go through s_fd_table and find the VFS
1058         // which has a permanent FD. But in order to avoid to lock
1059         // s_fd_table_lock we go through the VFS table.
1060         for (int i = 0; i < s_vfs_count; ++i) {
1061             // Note: s_vfs_count could have changed since the start of vfs_select() call. However, that change doesn't
1062             // matter here stop_socket_select() will be called for only valid VFS drivers.
1063             const vfs_entry_t *vfs = s_vfs[i];
1064             if (vfs != NULL && vfs->vfs.stop_socket_select_isr != NULL) {
1065                 vfs->vfs.stop_socket_select_isr(sem.sem, woken);
1066                 break;
1067             }
1068         }
1069     }
1070 }
1071 
1072 #endif // CONFIG_VFS_SUPPORT_SELECT
1073 
1074 #ifdef CONFIG_VFS_SUPPORT_TERMIOS
1075 
tcgetattr(int fd,struct termios * p)1076 int tcgetattr(int fd, struct termios *p)
1077 {
1078     const vfs_entry_t* vfs = get_vfs_for_fd(fd);
1079     const int local_fd = get_local_fd(vfs, fd);
1080     struct _reent* r = __getreent();
1081     if (vfs == NULL || local_fd < 0) {
1082         __errno_r(r) = EBADF;
1083         return -1;
1084     }
1085     int ret;
1086     CHECK_AND_CALL(ret, r, vfs, tcgetattr, local_fd, p);
1087     return ret;
1088 }
1089 
tcsetattr(int fd,int optional_actions,const struct termios * p)1090 int tcsetattr(int fd, int optional_actions, const struct termios *p)
1091 {
1092     const vfs_entry_t* vfs = get_vfs_for_fd(fd);
1093     const int local_fd = get_local_fd(vfs, fd);
1094     struct _reent* r = __getreent();
1095     if (vfs == NULL || local_fd < 0) {
1096         __errno_r(r) = EBADF;
1097         return -1;
1098     }
1099     int ret;
1100     CHECK_AND_CALL(ret, r, vfs, tcsetattr, local_fd, optional_actions, p);
1101     return ret;
1102 }
1103 
tcdrain(int fd)1104 int tcdrain(int fd)
1105 {
1106     const vfs_entry_t* vfs = get_vfs_for_fd(fd);
1107     const int local_fd = get_local_fd(vfs, fd);
1108     struct _reent* r = __getreent();
1109     if (vfs == NULL || local_fd < 0) {
1110         __errno_r(r) = EBADF;
1111         return -1;
1112     }
1113     int ret;
1114     CHECK_AND_CALL(ret, r, vfs, tcdrain, local_fd);
1115     return ret;
1116 }
1117 
tcflush(int fd,int select)1118 int tcflush(int fd, int select)
1119 {
1120     const vfs_entry_t* vfs = get_vfs_for_fd(fd);
1121     const int local_fd = get_local_fd(vfs, fd);
1122     struct _reent* r = __getreent();
1123     if (vfs == NULL || local_fd < 0) {
1124         __errno_r(r) = EBADF;
1125         return -1;
1126     }
1127     int ret;
1128     CHECK_AND_CALL(ret, r, vfs, tcflush, local_fd, select);
1129     return ret;
1130 }
1131 
tcflow(int fd,int action)1132 int tcflow(int fd, int action)
1133 {
1134     const vfs_entry_t* vfs = get_vfs_for_fd(fd);
1135     const int local_fd = get_local_fd(vfs, fd);
1136     struct _reent* r = __getreent();
1137     if (vfs == NULL || local_fd < 0) {
1138         __errno_r(r) = EBADF;
1139         return -1;
1140     }
1141     int ret;
1142     CHECK_AND_CALL(ret, r, vfs, tcflow, local_fd, action);
1143     return ret;
1144 }
1145 
tcgetsid(int fd)1146 pid_t tcgetsid(int fd)
1147 {
1148     const vfs_entry_t* vfs = get_vfs_for_fd(fd);
1149     const int local_fd = get_local_fd(vfs, fd);
1150     struct _reent* r = __getreent();
1151     if (vfs == NULL || local_fd < 0) {
1152         __errno_r(r) = EBADF;
1153         return -1;
1154     }
1155     int ret;
1156     CHECK_AND_CALL(ret, r, vfs, tcgetsid, local_fd);
1157     return ret;
1158 }
1159 
tcsendbreak(int fd,int duration)1160 int tcsendbreak(int fd, int duration)
1161 {
1162     const vfs_entry_t* vfs = get_vfs_for_fd(fd);
1163     const int local_fd = get_local_fd(vfs, fd);
1164     struct _reent* r = __getreent();
1165     if (vfs == NULL || local_fd < 0) {
1166         __errno_r(r) = EBADF;
1167         return -1;
1168     }
1169     int ret;
1170     CHECK_AND_CALL(ret, r, vfs, tcsendbreak, local_fd, duration);
1171     return ret;
1172 }
1173 #endif // CONFIG_VFS_SUPPORT_TERMIOS
1174 
1175 
1176 /* Create aliases for newlib syscalls
1177 
1178    These functions are also available in ROM as stubs which use the syscall table, but linking them
1179    directly here saves an additional function call when a software function is linked to one, and
1180    makes linking with -stdlib easier.
1181  */
1182 #ifdef CONFIG_VFS_SUPPORT_IO
1183 int _open_r(struct _reent *r, const char * path, int flags, int mode)
1184     __attribute__((alias("esp_vfs_open")));
1185 int _close_r(struct _reent *r, int fd)
1186     __attribute__((alias("esp_vfs_close")));
1187 ssize_t _read_r(struct _reent *r, int fd, void * dst, size_t size)
1188     __attribute__((alias("esp_vfs_read")));
1189 ssize_t _write_r(struct _reent *r, int fd, const void * data, size_t size)
1190     __attribute__((alias("esp_vfs_write")));
1191 ssize_t pread(int fd, void *dst, size_t size, off_t offset)
1192     __attribute__((alias("esp_vfs_pread")));
1193 ssize_t pwrite(int fd, const void *src, size_t size, off_t offset)
1194     __attribute__((alias("esp_vfs_pwrite")));
1195 off_t _lseek_r(struct _reent *r, int fd, off_t size, int mode)
1196     __attribute__((alias("esp_vfs_lseek")));
1197 int _fcntl_r(struct _reent *r, int fd, int cmd, int arg)
1198     __attribute__((alias("esp_vfs_fcntl_r")));
1199 int _fstat_r(struct _reent *r, int fd, struct stat * st)
1200     __attribute__((alias("esp_vfs_fstat")));
1201 int fsync(int fd)
1202     __attribute__((alias("esp_vfs_fsync")));
1203 int ioctl(int fd, int cmd, ...)
1204     __attribute__((alias("esp_vfs_ioctl")));
1205 #endif // CONFIG_VFS_SUPPORT_IO
1206 
1207 #ifdef CONFIG_VFS_SUPPORT_SELECT
1208 int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *errorfds, struct timeval *timeout)
1209     __attribute__((alias("esp_vfs_select")));
1210 #endif // CONFIG_VFS_SUPPORT_SELECT
1211 
1212 #ifdef CONFIG_VFS_SUPPORT_DIR
1213 int _stat_r(struct _reent *r, const char * path, struct stat * st)
1214     __attribute__((alias("esp_vfs_stat")));
1215 int _link_r(struct _reent *r, const char* n1, const char* n2)
1216     __attribute__((alias("esp_vfs_link")));
1217 int _unlink_r(struct _reent *r, const char *path)
1218     __attribute__((alias("esp_vfs_unlink")));
1219 int _rename_r(struct _reent *r, const char *src, const char *dst)
1220     __attribute__((alias("esp_vfs_rename")));
1221 int truncate(const char *path, off_t length)
1222     __attribute__((alias("esp_vfs_truncate")));
1223 int access(const char *path, int amode)
1224     __attribute__((alias("esp_vfs_access")));
1225 int utime(const char *path, const struct utimbuf *times)
1226     __attribute__((alias("esp_vfs_utime")));
1227 int rmdir(const char* name)
1228     __attribute__((alias("esp_vfs_rmdir")));
1229 int mkdir(const char* name, mode_t mode)
1230     __attribute__((alias("esp_vfs_mkdir")));
1231 DIR* opendir(const char* name)
1232     __attribute__((alias("esp_vfs_opendir")));
1233 int closedir(DIR* pdir)
1234     __attribute__((alias("esp_vfs_closedir")));
1235 int readdir_r(DIR* pdir, struct dirent* entry, struct dirent** out_dirent)
1236     __attribute__((alias("esp_vfs_readdir_r")));
1237 struct dirent* readdir(DIR* pdir)
1238     __attribute__((alias("esp_vfs_readdir")));
1239 long telldir(DIR* pdir)
1240     __attribute__((alias("esp_vfs_telldir")));
1241 void seekdir(DIR* pdir, long loc)
1242     __attribute__((alias("esp_vfs_seekdir")));
1243 void rewinddir(DIR* pdir)
1244     __attribute__((alias("esp_vfs_rewinddir")));
1245 #endif // CONFIG_VFS_SUPPORT_DIR
1246 
vfs_include_syscalls_impl(void)1247 void vfs_include_syscalls_impl(void)
1248 {
1249     // Linker hook function, exists to make the linker examine this fine
1250 }
1251