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