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