Page 1 of 1

[Answered] Is there a legal way to set the value of "errno"?

Posted: Sat Dec 03, 2016 4:07 pm
by kolban
In my application, I find I have the need to actually set the value of "errno". This is the code that is returned from some system calls when an error is detected. Is there a legal way to actually set the value of this global variable from user code?

The back story: I am mapping a virtual file system to the SPIFFS API and when I perform a SPIFFS call it can generate an error. Looking at the VFS APIs, they map to the posix I/O APIs which set errno to indicate the underlying error.

Re: Is there a legal way to set the value of "errno"?

Posted: Sun Dec 04, 2016 12:59 pm
by ESP_igrr
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.