Short answer: yes, you should be able to set it like this:
errno = ENOENT;.
Long answer (this is something I should put into ESP-IDF docs, I suppose):
Newlib library has different ways to deal with global state of the C library. Global state is the state of non-reentrant functions (such as strtok, rand, and so on), and among other things it also includes the value of errno. This state is collected in one structure,
struct _reent (
source).
The option we have enabled when building newlib for the ESP-IDF is called
__DYNAMIC_REENT__ in newlib source code. This option defines `_REENT` to a function called __getreent (
declaration,
definition). This function returns a pointer to struct _reent allocated for the calling task. In other words, all this "global" state is global only within a task. Different tasks have different copies of struct _reent and therefore different errno variables. There is also a global copy of struct _reent, which is used at startup (before the scheduler has started).
Now, how is errno defined? If you look at sys/errno.h, you will find
the following comment:
errno is not a global variable, because that would make using it non-reentrant. Instead, its address is returned by the function __errno.
So the chain of declarations is:
Declaration of errno and __errno
Definition of __errno
_REENT defined to __getreent for the case of __DYNAMIC_REENT__.
So unwinding all this mess, you can come to a conclusion that
errno = ENOENT expands (roughly) into
*(&(__getreent()->_errno)), i.e. to
__getreent()->_errno. So you can use errno as a value, and you can also assign to it. The __DYNAMIC_REENT__ magic will make sure that the variable is not shared between different tasks.