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