483 lines
13 KiB
C
483 lines
13 KiB
C
/*
|
|
* This file is part of Jehanne.
|
|
*
|
|
* Copyright (C) 2017 Giacomo Tesio <giacomo@tesio.it>
|
|
*
|
|
* Jehanne is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, version 2 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 General Public License
|
|
* along with Jehanne. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
/* we cannot include u.h and libc.h due to name collisions */
|
|
|
|
#define nil ((void*)0)
|
|
typedef long off_t;
|
|
typedef long ptrdiff_t;
|
|
typedef unsigned long uint64_t;
|
|
typedef unsigned long uintptr_t;
|
|
typedef unsigned int uint32_t;
|
|
typedef long int64_t;
|
|
typedef unsigned char uint8_t;
|
|
typedef unsigned short uint16_t;
|
|
typedef __SIZE_TYPE__ size_t;
|
|
|
|
typedef
|
|
struct Tm
|
|
{
|
|
int sec;
|
|
int min;
|
|
int hour;
|
|
int mday;
|
|
int mon;
|
|
int year;
|
|
int wday;
|
|
int yday;
|
|
char zone[4];
|
|
int tzoff;
|
|
} Tm;
|
|
|
|
typedef
|
|
struct Qid
|
|
{
|
|
uint64_t path;
|
|
uint32_t vers;
|
|
uint8_t type;
|
|
} Qid;
|
|
|
|
typedef
|
|
struct Dir {
|
|
/* jehanne_system-modified data */
|
|
uint16_t type; /* server type */
|
|
uint32_t dev; /* server subtype */
|
|
/* file data */
|
|
Qid qid; /* unique id from server */
|
|
uint32_t mode; /* permissions */
|
|
uint32_t atime; /* last jehanne_read jehanne_time */
|
|
uint32_t mtime; /* last jehanne_write jehanne_time */
|
|
int64_t length; /* file length */
|
|
char *name; /* last element of path */
|
|
char *uid; /* owner name */
|
|
char *gid; /* group name */
|
|
char *muid; /* last modifier name */
|
|
} Dir;
|
|
|
|
#define OSTAT 0x00 /* open for stat/wstat */
|
|
#define OREAD 0x01 /* open for read */
|
|
#define OWRITE 0x02 /* write */
|
|
#define ORDWR (OREAD|OWRITE) /* read and write */
|
|
#define OEXEC 0x04 /* execute, == read but check execute permission */
|
|
#define OCEXEC 0x08 /* or'ed in, close on exec */
|
|
#define ORCLOSE 0x10 /* or'ed in, remove on close */
|
|
#define OTRUNC 0x0100 /* or'ed in (except for exec), truncate file first */
|
|
|
|
#define DMDIR 0x80000000 /* mode bit for directories */
|
|
#define DMAPPEND 0x40000000 /* mode bit for append only files */
|
|
#define DMEXCL 0x20000000 /* mode bit for exclusive use files */
|
|
#define DMMOUNT 0x10000000 /* mode bit for mounted channel */
|
|
#define DMAUTH 0x08000000 /* mode bit for authentication file */
|
|
#define DMTMP 0x04000000 /* mode bit for non-backed-up files */
|
|
#define DMREAD 0x4 /* mode bit for read permission */
|
|
#define DMWRITE 0x2 /* mode bit for write permission */
|
|
#define DMEXEC 0x1 /* mode bit for execute permission */
|
|
|
|
extern void jehanne_sysfatal(const char*, ...);
|
|
extern int jehanne_strcmp(const char*, const char*);
|
|
extern char* jehanne_strstr(const char*, const char*);
|
|
extern int jehanne_tm2sec(Tm*);
|
|
|
|
#include <posix.h>
|
|
#include <errno.h>
|
|
#include <unistd.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/wait.h>
|
|
#include <sys/signal.h>
|
|
#include <sys/time.h>
|
|
#define __CYGWIN__ /* needed for O_ACCMODE */
|
|
#include <fcntl.h>
|
|
#undef __CYGWIN__
|
|
#include <reent.h>
|
|
|
|
extern void __application_newlib_init(int argc, char *argv[]) __attribute__((weak));
|
|
|
|
static PosixError
|
|
__stat_reader(struct stat *s, const Dir *d)
|
|
{
|
|
s->st_dev = d->dev;
|
|
s->st_ino = d->qid.path;
|
|
s->st_mode = d->mode;
|
|
/* we have to map types to UNIX */
|
|
if(d->mode & DMDIR)
|
|
s->st_mode |= _IFDIR;
|
|
else if(jehanne_strcmp("cons", d->name) == 0 || jehanne_strstr(d->name, "tty") != nil){
|
|
/* newlib expect consoles to be character devices */
|
|
s->st_mode |= _IFCHR;
|
|
} else
|
|
s->st_mode |= _IFREG; /* UNIX lack fantasy :-) */
|
|
s->st_nlink = 1;
|
|
s->st_uid = 1;
|
|
s->st_gid = 1;
|
|
s->st_atime = d->atime;
|
|
s->st_mtime = d->mtime;
|
|
s->st_ctime = d->mtime;
|
|
s->st_size = d->length;
|
|
return 0;
|
|
}
|
|
|
|
static PosixError
|
|
stat_reader(void *statp, const Dir *dir)
|
|
{
|
|
return __stat_reader((struct stat *)statp, dir);
|
|
}
|
|
|
|
static PosixError
|
|
open_translator(int flag, int mode, long *omode, long *cperm)
|
|
{
|
|
*cperm = 0;
|
|
|
|
if(omode != NULL){
|
|
*omode = 0;
|
|
switch(flag & O_ACCMODE){
|
|
case O_RDONLY:
|
|
*omode |= OREAD;
|
|
break;
|
|
case O_WRONLY:
|
|
*omode |= OWRITE;
|
|
break;
|
|
case O_RDWR:
|
|
*omode |= ORDWR;
|
|
break;
|
|
}
|
|
|
|
if(flag & O_EXEC)
|
|
*omode = OEXEC;
|
|
else if(flag & O_SEARCH){
|
|
/* POSIX_open will fail if the file is
|
|
* not a directory
|
|
*/
|
|
*omode |= (OEXEC|DMDIR);
|
|
}
|
|
|
|
if(flag & O_TRUNC)
|
|
*omode |= OTRUNC;
|
|
if(flag & O_CLOEXEC)
|
|
*omode |= OCEXEC;
|
|
}
|
|
|
|
if(omode == nil || (flag & O_CREAT) != 0){
|
|
/* translate permissions */
|
|
if(mode & S_IRUSR)
|
|
*cperm |= DMREAD << 6;
|
|
if(mode & S_IWUSR)
|
|
*cperm |= DMWRITE << 6;
|
|
if(mode & S_IXUSR)
|
|
*cperm |= DMEXEC << 6;
|
|
if(mode & S_IRGRP)
|
|
*cperm |= DMREAD << 3;
|
|
if(mode & S_IWGRP)
|
|
*cperm |= DMWRITE << 3;
|
|
if(mode & S_IXGRP)
|
|
*cperm |= DMEXEC << 3;
|
|
if(mode & S_IROTH)
|
|
*cperm |= DMREAD;
|
|
if(mode & S_IWOTH)
|
|
*cperm |= DMWRITE;
|
|
if(mode & S_IXOTH)
|
|
*cperm |= DMEXEC;
|
|
}
|
|
|
|
if(flag & O_CREAT){
|
|
if(flag & O_DIRECTORY){
|
|
/* let's copy NetBSD's behaviour
|
|
* see https://stackoverflow.com/questions/45818628/
|
|
*/
|
|
return PosixEINVAL;
|
|
}
|
|
if(flag & O_EXCL)
|
|
*cperm |= DMEXCL;
|
|
} else {
|
|
if(flag & O_APPEND)
|
|
return PosixENOTSUP;
|
|
if(flag & O_EXCL)
|
|
return PosixENOTSUP;
|
|
if(omode != NULL){
|
|
if(flag & O_DIRECTORY){
|
|
/* POSIX_open will fail if the file is
|
|
* not a directory
|
|
*/
|
|
*omode |= DMDIR;
|
|
}
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
#define ADDRESS(func) ((long)func)
|
|
static PosixError
|
|
default_error_translator(char* error, uintptr_t caller)
|
|
{
|
|
//jehanne_fprint(2, "newlib: %s\n", error);
|
|
PosixError e = libposix_translate_kernel_errors(error);
|
|
if(e != 0)
|
|
return e;
|
|
if(caller == ADDRESS(POSIX_open))
|
|
return PosixEIO;
|
|
if(caller == ADDRESS(POSIX_execve))
|
|
return PosixEPERM;
|
|
if(caller == ADDRESS(POSIX_chdir))
|
|
return PosixEACCES;
|
|
if(caller == ADDRESS(POSIX_chmod))
|
|
return PosixEPERM;
|
|
if(caller == ADDRESS(POSIX_fchmodat))
|
|
return PosixEPERM;
|
|
if(caller == ADDRESS(POSIX_fchdir))
|
|
return PosixEACCES;
|
|
return PosixEINVAL;
|
|
}
|
|
|
|
static PosixError
|
|
default_timeval_reader(void *tv, const Tm *time)
|
|
{
|
|
struct timeval *t = tv;
|
|
|
|
t->tv_sec = jehanne_tm2sec((Tm*)time);
|
|
t->tv_usec = 0;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static PosixError
|
|
default_timezone_reader(void *tz, const Tm *time)
|
|
{
|
|
struct timezone *t = tz;
|
|
|
|
t->tz_minuteswest = time->tzoff;
|
|
t->tz_dsttime = 0;
|
|
|
|
return 0;
|
|
}
|
|
|
|
void
|
|
on_process_disposition(int status)
|
|
{
|
|
extern void __libc_fini_array(void);
|
|
extern void __call_exitprocs (int, void*);
|
|
|
|
__libc_fini_array();
|
|
__call_exitprocs(status, NULL);
|
|
if (_REENT->__cleanup)
|
|
(*_REENT->__cleanup) (_REENT);
|
|
}
|
|
|
|
void
|
|
initialize_newlib(int argc, char *argv[])
|
|
{
|
|
extern void __libc_init_array(void);
|
|
|
|
__libc_init_array();
|
|
|
|
/* */
|
|
libposix_define_at_fdcwd(AT_FDCWD);
|
|
libposix_define_ononblock(O_NONBLOCK);
|
|
libposix_set_stat_reader(stat_reader);
|
|
libposix_set_timeval_reader(default_timeval_reader);
|
|
libposix_set_timezone_reader(default_timezone_reader);
|
|
libposix_translate_seek_whence(SEEK_SET, SEEK_CUR, SEEK_END);
|
|
libposix_translate_access_mode(F_OK, R_OK, W_OK, X_OK);
|
|
libposix_translate_open(open_translator);
|
|
libposix_translate_error(default_error_translator, 0);
|
|
libposix_set_wait_options(0, WNOHANG, 0);
|
|
libposix_on_process_disposition(on_process_disposition);
|
|
|
|
/* error numbers */
|
|
libposix_define_errno(PosixE2BIG, E2BIG);
|
|
libposix_define_errno(PosixEACCES, EACCES);
|
|
libposix_define_errno(PosixEADDRINUSE, EADDRINUSE);
|
|
libposix_define_errno(PosixEADDRNOTAVAIL, EADDRNOTAVAIL);
|
|
libposix_define_errno(PosixEAFNOSUPPORT, EAFNOSUPPORT);
|
|
libposix_define_errno(PosixEAGAIN, EAGAIN);
|
|
libposix_define_errno(PosixEALREADY, EALREADY);
|
|
libposix_define_errno(PosixEBADF, EBADF);
|
|
libposix_define_errno(PosixEBADMSG, EBADMSG);
|
|
libposix_define_errno(PosixEBUSY, EBUSY);
|
|
libposix_define_errno(PosixECANCELED, ECANCELED);
|
|
libposix_define_errno(PosixECHILD, ECHILD);
|
|
libposix_define_errno(PosixECONNABORTED, ECONNABORTED);
|
|
libposix_define_errno(PosixECONNREFUSED, ECONNREFUSED);
|
|
libposix_define_errno(PosixECONNRESET, ECONNRESET);
|
|
libposix_define_errno(PosixEDEADLK, EDEADLK);
|
|
libposix_define_errno(PosixEDESTADDRREQ, EDESTADDRREQ);
|
|
libposix_define_errno(PosixEDOM, EDOM);
|
|
libposix_define_errno(PosixEDQUOT, EDQUOT);
|
|
libposix_define_errno(PosixEEXIST, EEXIST);
|
|
libposix_define_errno(PosixEFAULT, EFAULT);
|
|
libposix_define_errno(PosixEFBIG, EFBIG);
|
|
libposix_define_errno(PosixEHOSTUNREACH, EHOSTUNREACH);
|
|
libposix_define_errno(PosixEIDRM, EIDRM);
|
|
libposix_define_errno(PosixEILSEQ, EILSEQ);
|
|
libposix_define_errno(PosixEINPROGRESS, EINPROGRESS);
|
|
libposix_define_errno(PosixEINTR, EINTR);
|
|
libposix_define_errno(PosixEINVAL, EINVAL);
|
|
libposix_define_errno(PosixEIO, EIO);
|
|
libposix_define_errno(PosixEISCONN, EISCONN);
|
|
libposix_define_errno(PosixEISDIR, EISDIR);
|
|
libposix_define_errno(PosixELOOP, ELOOP);
|
|
libposix_define_errno(PosixEMFILE, EMFILE);
|
|
libposix_define_errno(PosixEMLINK, EMLINK);
|
|
libposix_define_errno(PosixEMSGSIZE, EMSGSIZE);
|
|
libposix_define_errno(PosixEMULTIHOP, EMULTIHOP);
|
|
libposix_define_errno(PosixENAMETOOLONG, ENAMETOOLONG);
|
|
libposix_define_errno(PosixENETDOWN, ENETDOWN);
|
|
libposix_define_errno(PosixENETRESET, ENETRESET);
|
|
libposix_define_errno(PosixENETUNREACH, ENETUNREACH);
|
|
libposix_define_errno(PosixENFILE, ENFILE);
|
|
libposix_define_errno(PosixENOBUFS, ENOBUFS);
|
|
libposix_define_errno(PosixENODATA, ENODATA);
|
|
libposix_define_errno(PosixENODEV, ENODEV);
|
|
libposix_define_errno(PosixENOENT, ENOENT);
|
|
libposix_define_errno(PosixENOEXEC, ENOEXEC);
|
|
libposix_define_errno(PosixENOLCK, ENOLCK);
|
|
libposix_define_errno(PosixENOLINK, ENOLINK);
|
|
libposix_define_errno(PosixENOMEM, ENOMEM);
|
|
libposix_define_errno(PosixENOMSG, ENOMSG);
|
|
libposix_define_errno(PosixENOPROTOOPT, ENOPROTOOPT);
|
|
libposix_define_errno(PosixENOSPC, ENOSPC);
|
|
libposix_define_errno(PosixENOSR, ENOSR);
|
|
libposix_define_errno(PosixENOSTR, ENOSTR);
|
|
libposix_define_errno(PosixENOSYS, ENOSYS);
|
|
libposix_define_errno(PosixENOTCONN, ENOTCONN);
|
|
libposix_define_errno(PosixENOTDIR, ENOTDIR);
|
|
libposix_define_errno(PosixENOTEMPTY, ENOTEMPTY);
|
|
libposix_define_errno(PosixENOTRECOVERABLE, ENOTRECOVERABLE);
|
|
libposix_define_errno(PosixENOTSOCK, ENOTSOCK);
|
|
libposix_define_errno(PosixENOTSUP, ENOTSUP);
|
|
libposix_define_errno(PosixENOTTY, ENOTTY);
|
|
libposix_define_errno(PosixENXIO, ENXIO);
|
|
libposix_define_errno(PosixEOPNOTSUPP, EOPNOTSUPP);
|
|
libposix_define_errno(PosixEOVERFLOW, EOVERFLOW);
|
|
libposix_define_errno(PosixEOWNERDEAD, EOWNERDEAD);
|
|
libposix_define_errno(PosixEPERM, EPERM);
|
|
libposix_define_errno(PosixEPIPE, EPIPE);
|
|
libposix_define_errno(PosixEPROTO, EPROTO);
|
|
libposix_define_errno(PosixEPROTONOSUPPORT, EPROTONOSUPPORT);
|
|
libposix_define_errno(PosixEPROTOTYPE, EPROTOTYPE);
|
|
libposix_define_errno(PosixERANGE, ERANGE);
|
|
libposix_define_errno(PosixEROFS, EROFS);
|
|
libposix_define_errno(PosixESPIPE, ESPIPE);
|
|
libposix_define_errno(PosixESRCH, ESRCH);
|
|
libposix_define_errno(PosixESTALE, ESTALE);
|
|
libposix_define_errno(PosixETIME, ETIME);
|
|
libposix_define_errno(PosixETIMEDOUT, ETIMEDOUT);
|
|
libposix_define_errno(PosixETXTBSY, ETXTBSY);
|
|
libposix_define_errno(PosixEWOULDBLOCK, EWOULDBLOCK);
|
|
libposix_define_errno(PosixEXDEV, EXDEV);
|
|
|
|
/* let the application override defaults */
|
|
if(__application_newlib_init != 0)
|
|
__application_newlib_init(argc, argv);
|
|
}
|
|
|
|
/* _fcntl_r is here to access <fcntl.h> */
|
|
int
|
|
_fcntl_r(struct _reent *r, int fd, int cmd, int arg)
|
|
{
|
|
/*
|
|
extern int jehanne_print(const char*, ...);
|
|
extern uintptr_t jehanne_getcallerpc(void);
|
|
jehanne_fprint(2, "_fcntl_r(%d, %d, %d) from %#p\n", fd, cmd, arg, jehanne_getcallerpc());
|
|
*/
|
|
int *errnop = &r->_errno;
|
|
PosixFDCmds pcmd;
|
|
int tmp;
|
|
switch(cmd){
|
|
case F_DUPFD:
|
|
pcmd = PosixFDCDupFD;
|
|
break;
|
|
case F_DUPFD_CLOEXEC:
|
|
pcmd = PosixFDCDupFDCloseOnExec;
|
|
break;
|
|
case F_GETFD:
|
|
tmp = POSIX_fcntl(errnop, fd, PosixFDCGetFD, arg);
|
|
if(tmp < 0)
|
|
return -1;
|
|
if(tmp)
|
|
return O_CLOEXEC;
|
|
return 0;
|
|
case F_SETFD:
|
|
pcmd = PosixFDCSetFD;
|
|
break;
|
|
case F_GETFL:
|
|
tmp = POSIX_fcntl(errnop, fd, PosixFDCGetFL, arg);
|
|
if(tmp < 0)
|
|
return -1;
|
|
if((tmp & ORDWR) == ORDWR)
|
|
return O_RDWR;
|
|
if(tmp & OREAD)
|
|
return O_RDONLY;
|
|
if(tmp & OWRITE)
|
|
return O_WRONLY;
|
|
return 0;
|
|
case F_SETFL:
|
|
pcmd = PosixFDCSetFL;
|
|
break;
|
|
default:
|
|
*errnop = EINVAL;
|
|
return -1;
|
|
}
|
|
return POSIX_fcntl(errnop, fd, pcmd, arg);
|
|
}
|
|
|
|
/* sysconf is here to access <unistd.h> */
|
|
long sysconf(int name)
|
|
{
|
|
int *errnop = &_REENT->_errno;
|
|
PosixSysConfNames request = 0;
|
|
switch(name){
|
|
case _SC_ARG_MAX:
|
|
request = PosixSCNArgMax;
|
|
break;
|
|
case _SC_CHILD_MAX:
|
|
request = PosixSCNChildMax;
|
|
break;
|
|
case _SC_HOST_NAME_MAX:
|
|
request = PosixSCNHostNameMax;
|
|
break;
|
|
case _SC_LOGIN_NAME_MAX:
|
|
request = PosixSCNLoginNameMax;
|
|
break;
|
|
case _SC_CLK_TCK:
|
|
request = PosixSCNClockTicks;
|
|
break;
|
|
case _SC_OPEN_MAX:
|
|
request = PosixSCNOpenMax;
|
|
break;
|
|
case _SC_PAGESIZE:
|
|
request = PosixSCNPageSize;
|
|
break;
|
|
case _SC_VERSION:
|
|
request = PosixSCNPosixVersion;
|
|
break;
|
|
case _SC_LINE_MAX:
|
|
request = PosixSCNLineMax;
|
|
break;
|
|
}
|
|
|
|
return POSIX_sysconf(errnop, request);
|
|
}
|
|
|
|
int
|
|
__fail_with_einval(void)
|
|
{
|
|
int *errnop = &_REENT->_errno;
|
|
*errnop = EINVAL;
|
|
return -1;
|
|
}
|