jehanne/sys/src/lib/posix/errors.c

195 lines
4.6 KiB
C

/*
* This file is part of Jehanne.
*
* Copyright (C) 2017 Giacomo Tesio <giacomo@tesio.it>
*
* This is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, version 3 of the License.
*
* Jehanne is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Jehanne. If not, see <http://www.gnu.org/licenses/>.
*/
#include <u.h>
#include <lib9.h>
#include <posix.h>
#include "internal.h"
int *__libposix_errors_codes;
typedef struct PosixErrorMap PosixErrorMap;
struct PosixErrorMap
{
PosixErrorTranslator translate;
PosixErrorMap *next;
};
typedef struct CustomErrorMap CustomErrorMap;
struct CustomErrorMap
{
uintptr_t caller;
PosixErrorMap *head;
CustomErrorMap *next;
};
static PosixErrorMap *generic_handlers;
static CustomErrorMap *custom_handlers;
void
__libposix_errors_check_conf(void)
{
int i;
/* check that all required configurations has been provided */
for(i = 0; i < ERRNO_LAST-ERRNO_FIRST; ++i){
if(__libposix_errors_codes[i] == 0)
sysfatal("libposix: PosixError %d is undefined", i + ERRNO_FIRST);
}
if(generic_handlers == nil)
sysfatal("libposix: no generic error handler");
}
int
libposix_define_errno(PosixError e, int errno)
{
if(e < ERRNO_FIRST || e > ERRNO_LAST)
return 0;
__libposix_errors_codes[e - ERRNO_FIRST] = errno;
return 1;
}
static int
register_translation(PosixErrorMap **map, PosixErrorTranslator translation)
{
PosixErrorMap *entry = malloc(sizeof(PosixErrorMap));
if(entry == nil)
return 0;
entry->translate = translation;
entry->next = *map;
*map = entry;
return 1;
}
int
libposix_translate_error(PosixErrorTranslator translation, uintptr_t caller)
{
CustomErrorMap **list = nil;
if(__libposix_initialized())
return 0;
if(translation == nil)
return 0;
if(caller == 0)
return register_translation(&generic_handlers, translation);
list = &custom_handlers;
while(*list != nil)
{
if((*list)->caller == caller)
break;
list = &(*list)->next;
}
if(*list == nil){
*list = malloc(sizeof(CustomErrorMap));
(*list)->next = nil;
(*list)->caller = caller;
(*list)->head = nil;
}
return register_translation(&(*list)->head, translation);
}
int
__libposix_get_errno(PosixError e)
{
if(e == 0)
return 0;
if(e < 0){
/* this way we allow a translator to return a
* non-posix errno as a negative number that will not
* collide with the indexes declared in PosixError enum
*/
return (int)-e;
}
if(e < ERRNO_FIRST || e > ERRNO_LAST)
return PosixEINVAL;
return __libposix_errors_codes[e - ERRNO_FIRST];
}
static PosixError
get_posix_error(PosixErrorMap *translations, char *err, uintptr_t caller)
{
PosixError e = 0;
while(translations != nil)
{
e = translations->translate(err, caller);
if(e != 0)
return e;
translations = translations->next;
}
return PosixEINVAL;
}
PosixError
libposix_translate_kernel_errors(const char *msg)
{
// TODO: autogenerate from /sys/src/sysconf.json
if(nil == msg)
return 0;
if(strncmp("interrupted", msg, 9) == 0)
return PosixEINTR;
if(strncmp("no living children", msg, 18) == 0)
return PosixECHILD;
if(strstr(msg, "file not found") != nil)
return PosixENOENT;
if(strstr(msg, "does not exist") != nil)
return PosixENOENT;
if(strstr(msg, "file already exists") != nil)
return PosixEEXIST;
if(strstr(msg, "file is a directory") != nil)
return PosixEISDIR;
if(strncmp("fd out of range or not open", msg, 27) == 0)
return PosixEBADF;
if(strstr(msg, "not a directory") != nil)
return PosixENOTDIR;
if(strstr(msg, "permission denied") != nil)
return PosixEPERM;
if(strstr(msg, "name too long") != nil)
return PosixENAMETOOLONG;
if(strcmp("i/o error", msg) == 0)
return PosixEIO;
if(strcmp("i/o on hungup channel", msg) == 0)
return PosixEIO;
return 0;
}
int
__libposix_translate_errstr(uintptr_t caller)
{
CustomErrorMap *handler;
PosixError perr = 0;
char err[ERRMAX];
int ret;
if(sys_errstr(err, ERRMAX) < 0)
return __libposix_get_errno(PosixEINVAL);
handler = custom_handlers;
while(handler != nil)
{
if(handler->caller == caller)
break;
}
if(handler != nil)
perr = get_posix_error(handler->head, err, caller);
if(perr == 0)
perr = get_posix_error(generic_handlers, err, caller);
if(perr == 0)
perr = libposix_translate_kernel_errors(err);
ret = __libposix_get_errno(perr);
sys_errstr(err, ERRMAX);
return ret;
}