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