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

User avatar
kolban
Posts: 1683
Joined: Mon Nov 16, 2015 4:43 pm
Location: Texas, USA

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

Postby kolban » Sat Dec 03, 2016 4:07 pm

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.
Last edited by kolban on Sun Dec 04, 2016 3:30 pm, edited 1 time in total.
Free book on ESP32 available here: https://leanpub.com/kolban-ESP32

ESP_igrr
Posts: 2067
Joined: Tue Dec 01, 2015 8:37 am

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

Postby ESP_igrr » Sun Dec 04, 2016 12:59 pm

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.

Who is online

Users browsing this forum: Abisha and 295 guests