Project

General

Profile

Defect #663 » glue_imp.c

Rochus Keller, 02 July 2024 19:10

 
#include <pdclib/_PDCLIB_glue.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "pdclib/_PDCLIB_tzcode.h"
#include <signal.h>
#include <limits.h>
#include <errno.h>

struct timeval
{
long tv_sec; /* Seconds. */
};

struct tms
{
clock_t tms_utime; /* User CPU time. */
clock_t tms_stime; /* System CPU time. */

clock_t tms_cutime; /* User CPU time of dead children. */
clock_t tms_cstime; /* System CPU time of dead children. */
};

// defined in ECS runtime
extern int sys_close(int fd);
extern int sys_open(const char *pathname, int flags, ... /* mode_t mode */ );
extern int sys_renameat(int olddirfd, const char *oldpath,
int newdirfd, const char *newpath);
extern int sys_gettimeofday(struct timeval *restrict tv, void * restrict tz);
extern int sys_unlink(const char *pathname);
extern char *sys_getcwd(char* buf, size_t size);
extern size_t sys_read(int fd, void* buf, size_t count);
extern size_t sys_write(int fd, const void* buf, size_t count);
extern clock_t sys_times(struct tms *buf);
extern long sys_lseek(int fd, long offset, int whence);
extern void sys_exit(int status);

_PDCLIB_LOCAL _PDCLIB_Noreturn void _PDCLIB_Exit( int status ) _PDCLIB_NORETURN
{
sys_exit( status );
}

#define O_RDONLY 00
#define O_WRONLY 01
#define O_RDWR 02
# define O_CREAT 0100 /* Not fcntl. */
# define O_TRUNC 01000 /* Not fcntl. */
# define O_APPEND 02000
# define S_IRUSR 0400 /* Read by owner. */
# define S_IWUSR 0200 /* Write by owner. */
# define S_IRGRP (S_IRUSR >> 3) /* Read by group. */
# define S_IWGRP (S_IWUSR >> 3) /* Write by group. */
# define S_IROTH (S_IRGRP >> 3) /* Read by others. */
# define S_IWOTH (S_IWGRP >> 3) /* Write by others. */

_PDCLIB_LOCAL _PDCLIB_fd_t _PDCLIB_open( const char * const filename, unsigned int mode )
{
int osmode;
_PDCLIB_fd_t rc;

switch ( mode & ( _PDCLIB_FREAD | _PDCLIB_FWRITE | _PDCLIB_FAPPEND | _PDCLIB_FRW ) )
{
case _PDCLIB_FREAD: /* "r" */
osmode = O_RDONLY;
break;

case _PDCLIB_FWRITE: /* "w" */
osmode = O_WRONLY | O_CREAT | O_TRUNC;
break;

case _PDCLIB_FAPPEND: /* "a" */
osmode = O_WRONLY | O_APPEND | O_CREAT;
break;

case _PDCLIB_FREAD | _PDCLIB_FRW: /* "r+" */
osmode = O_RDWR;
break;

case _PDCLIB_FWRITE | _PDCLIB_FRW: /* "w+" */
osmode = O_RDWR | O_CREAT | O_TRUNC;
break;

case _PDCLIB_FAPPEND | _PDCLIB_FRW: /* "a+" */
osmode = O_RDWR | O_APPEND | O_CREAT;
break;

default: /* Invalid mode */
return _PDCLIB_NOHANDLE;
}

if ( osmode & O_CREAT )
{
rc = sys_open( filename, osmode, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH );
}
else
{
rc = sys_open( filename, osmode );
}

if ( rc == _PDCLIB_NOHANDLE )
{
/* The 1:1 mapping in _PDCLIB_config.h ensures this works. */
*_PDCLIB_errno_func() = _PDCLIB_ENOTSUP; // TODO errno;
}

return rc;
}

#define _PDCLIB_IO_RETRIES 1

_PDCLIB_LOCAL int _PDCLIB_flushbuffer( struct _PDCLIB_file_t * stream )
{
/* No need to handle buffers > INT_MAX, as PDCLib doesn't allow them */
_PDCLIB_size_t written = 0;
int rc;
unsigned int retries;

if ( !( stream->status & _PDCLIB_FBIN ) )
{
/* TODO: Text stream conversion here */
}

/* Keep trying to write data until everything is written, an error
occurs, or the configured number of retries is exceeded.
*/
for ( retries = _PDCLIB_IO_RETRIES; retries > 0; --retries )
{
rc = ( int )sys_write( stream->handle, stream->buffer + written, stream->bufidx - written );

if ( rc < 0 )
{
/* The 1:1 mapping done in _PDCLIB_config.h ensures
this works.
*/
*_PDCLIB_errno_func() = -1; // TODO errno;
/* Flag the stream */
stream->status |= _PDCLIB_ERRORFLAG;
/* Move unwritten remains to begin of buffer. */
stream->bufidx -= written;
memmove( stream->buffer, stream->buffer + written, stream->bufidx );
return EOF;
}

written += ( _PDCLIB_size_t )rc;
stream->pos.offset += rc;

if ( written == stream->bufidx )
{
/* Buffer written completely. */
stream->bufidx = 0;
return 0;
}
}

/* Number of retries exceeded. */
*_PDCLIB_errno_func() = _PDCLIB_EAGAIN;
stream->status |= _PDCLIB_ERRORFLAG;
/* Move unwritten remains to begin of buffer. */
stream->bufidx -= written;
memmove( stream->buffer, stream->buffer + written, stream->bufidx );
return EOF;
}

_PDCLIB_LOCAL int _PDCLIB_fillbuffer( struct _PDCLIB_file_t * stream )
{
/* No need to handle buffers > INT_MAX, as PDCLib doesn't allow them */
size_t rc = sys_read( stream->handle, stream->buffer, stream->bufsize );

if ( rc > 0 )
{
/* Reading successful. */
if ( !( stream->status & _PDCLIB_FBIN ) )
{
/* TODO: Text stream conversion here */
}

stream->pos.offset += rc;
stream->bufend = rc;
stream->bufidx = 0;
return 0;
}

if ( rc < 0 )
{
/* The 1:1 mapping done in _PDCLIB_config.h ensures
this works.
*/
*_PDCLIB_errno_func() = errno;
/* Flag the stream */
stream->status |= _PDCLIB_ERRORFLAG;
return EOF;
}

/* End-of-File */
stream->status |= _PDCLIB_EOFFLAG;
return EOF;
}

_PDCLIB_LOCAL _PDCLIB_int_least64_t _PDCLIB_seek( struct _PDCLIB_file_t * stream, _PDCLIB_int_least64_t offset, int whence )
{
_PDCLIB_int_least64_t rc;

switch ( whence )
{
case SEEK_SET:
case SEEK_CUR:
case SEEK_END:
/* EMPTY - OK */
break;

default:
*_PDCLIB_errno_func() = _PDCLIB_EINVAL;
return EOF;
break;
}

rc = sys_lseek( stream->handle, offset, whence );

if ( rc != EOF )
{
stream->ungetidx = 0;
stream->bufidx = 0;
stream->bufend = 0;
stream->pos.offset = rc;
return rc;
}

/* The 1:1 mapping in _PDCLIB_config.h ensures that this works. */
*_PDCLIB_errno_func() = -1; // TODO errno;
return EOF;
}

_PDCLIB_LOCAL int _PDCLIB_close( _PDCLIB_fd_t fd )
{
return sys_close( fd );
}

_PDCLIB_LOCAL int _PDCLIB_changemode( struct _PDCLIB_file_t * stream, unsigned int mode )
{
if ( mode == 0 )
{
return INT_MIN;
}

/* Attempt mode change without closing the stream */

if ( stream->filename == NULL )
{
/* Standard stream, no filename for reopen */
return INT_MIN;
}
else
{
/* Stream with file associated, attempt reopen */
return 0;
}
}

_PDCLIB_LOCAL int _PDCLIB_remove( const char * pathname )
{
return sys_unlink( pathname );
}

# define AT_FDCWD -100

_PDCLIB_LOCAL int _PDCLIB_rename( const char * oldpath, const char * newpath )
{
/* Whether existing newpath is overwritten is implementation-
defined. This system call *does* overwrite.
*/
if ( sys_renameat( AT_FDCWD, oldpath, AT_FDCWD, newpath ) != 0 )
{
/* The 1:1 mapping in _PDCLIB_config.h ensures this works. */
*_PDCLIB_errno_func() = errno;
return -1;
}
else
{
return 0;
}
}

#define PATH_MAX 1024

_PDCLIB_LOCAL char * _PDCLIB_realpath( const char * path )
{
// this is adopted from uClibc-ng 1.0.49
char copy_path[PATH_MAX];
char *max_path, *new_path, *allocated_path, *got_path;
size_t path_len;

if (path == NULL) {
*_PDCLIB_errno_func() = EINVAL;
return NULL;
}
if (*path == '\0') {
*_PDCLIB_errno_func() = ENOENT;
return NULL;
}
/* Make a copy of the source path since we may need to modify it. */
path_len = strlen(path);
if (path_len >= PATH_MAX - 2) {
*_PDCLIB_errno_func() = ENAMETOOLONG;
return NULL;
}
/* Copy so that path is at the end of copy_path[] */
strcpy(copy_path + (PATH_MAX-1) - path_len, path);
path = copy_path + (PATH_MAX-1) - path_len;
allocated_path = got_path = malloc(PATH_MAX);
max_path = got_path + PATH_MAX - 2; /* points to last non-NUL char */
new_path = got_path;
if (*path != '/') {
/* If it's a relative pathname use getcwd for starters. */
if (!sys_getcwd(new_path, PATH_MAX - 1))
goto err;
new_path += strlen(new_path);
if (new_path[-1] != '/')
*new_path++ = '/';
} else {
*new_path++ = '/';
path++;
}
/* Expand each slash-separated pathname component. */
while (*path != '\0') {
/* Ignore stray "/". */
if (*path == '/') {
path++;
continue;
}
if (*path == '.') {
/* Ignore ".". */
if (path[1] == '\0' || path[1] == '/') {
path++;
continue;
}
if (path[1] == '.') {
if (path[2] == '\0' || path[2] == '/') {
path += 2;
/* Ignore ".." at root. */
if (new_path == got_path + 1)
continue;
/* Handle ".." by backing up. */
while ((--new_path)[-1] != '/');
continue;
}
}
}
/* Safely copy the next pathname component. */
while (*path != '\0' && *path != '/') {
if (new_path > max_path) {
*_PDCLIB_errno_func() = ENAMETOOLONG;
err:
free(allocated_path);
return NULL;
}
*new_path++ = *path++;
}
*new_path++ = '/';
}
/* Delete trailing slash but don't whomp a lone slash. */
if (new_path != got_path + 1 && new_path[-1] == '/')
new_path--;
/* Make sure it's null terminated. */
*new_path = '\0';
return got_path;
}

extern struct _PDCLIB_file_t * _PDCLIB_filelist;

FILE * tmpfile( void )
{
FILE * rc;

char * filename = ( char * )malloc( L_tmpnam );
_PDCLIB_fd_t fd;

for ( ;; )
{
/* Get a filename candidate. What constitutes a valid filename and
where temporary files are usually located is platform-dependent,
which is one reason why this function is located in the platform
overlay. The other reason is that a *good* implementation should
use high-quality randomness instead of a pseudo-random sequence to
generate the filename candidate, which is *also* platform-dependent.
*/
unsigned int random = rand();
sprintf( filename, "/tmp/%u.tmp", random );
/* Check if file of this name exists. Note that fopen() is a very weak
check, which does not take e.g. access permissions into account
(file might exist but not readable). Replace with something more
appropriate.
*/
fd = sys_open( filename, O_CREAT | O_RDWR, S_IRUSR | S_IWUSR );

if ( fd != -1 )
{
/* Found a file that does not exist yet */
break;
}

sys_close( fd );
}

/* See fopen(), which does much of the same. */

if ( ( rc = _PDCLIB_init_file_t( NULL ) ) == NULL )
{
/* initializing FILE structure failed */
sys_close( fd );
return NULL;
}

rc->status |= _PDCLIB_filemode( "wb+" ) | _IOLBF | _PDCLIB_DELONCLOSE;
rc->handle = fd;

/* Filename (for potential freopen()) */
rc->filename = filename;

/* Adding to list of open files */
_PDCLIB_LOCK( _PDCLIB_filelist_mtx );
rc->next = _PDCLIB_filelist;
_PDCLIB_filelist = rc;
_PDCLIB_UNLOCK( _PDCLIB_filelist_mtx );
return rc;
}

void ( *_PDCLIB_sigabrt )( int ) = SIG_DFL;
void ( *_PDCLIB_sigfpe )( int ) = SIG_DFL;
void ( *_PDCLIB_sigill )( int ) = SIG_DFL;
void ( *_PDCLIB_sigint )( int ) = SIG_DFL;
void ( *_PDCLIB_sigsegv )( int ) = SIG_DFL;
void ( *_PDCLIB_sigterm )( int ) = SIG_DFL;

void ( *signal( int sig, void ( *func )( int ) ) )( int )
{
void ( *oldhandler )( int );

if ( sig <= 0 || func == SIG_ERR )
{
return SIG_ERR;
}

switch ( sig )
{
case SIGABRT:
oldhandler = _PDCLIB_sigabrt;
_PDCLIB_sigabrt = func;
break;

case SIGFPE:
oldhandler = _PDCLIB_sigfpe;
_PDCLIB_sigfpe = func;
break;

case SIGILL:
oldhandler = _PDCLIB_sigill;
_PDCLIB_sigill = func;
break;

case SIGINT:
oldhandler = _PDCLIB_sigint;
_PDCLIB_sigint = func;
break;

case SIGSEGV:
oldhandler = _PDCLIB_sigsegv;
_PDCLIB_sigsegv = func;
break;

case SIGTERM:
oldhandler = _PDCLIB_sigterm;
_PDCLIB_sigterm = func;
break;

default:
/* The standard calls for an unspecified "positive value".
Copying Linux' behaviour of setting EINVAL.
*/
*_PDCLIB_errno_func() = _PDCLIB_EINVAL;
return SIG_ERR;
}

return oldhandler;
}

int system( const char * string )
{
return -1; // TODO
}

double strtod( const char * _PDCLIB_restrict nptr, char ** _PDCLIB_restrict endptr )
{
return 0.0; // TODO
}

float strtof( const char * _PDCLIB_restrict nptr, char ** _PDCLIB_restrict endptr )
{
return 0.0; // TODO
}


//extern char ** environ; // TODO

char * getenv( const char * name )
{
size_t len = strlen( name );
size_t index = 0;

#if 0 // TODO
while ( environ[ index ] != NULL )
{
if ( strncmp( environ[ index ], name, len ) == 0 )
{
return environ[ index ] + len + 1;
}

index++;
}
#endif

return NULL;
}

clock_t clock( void )
{
struct tms buf;

if ( sys_times( &buf ) != ( clock_t )-1 )
{
return buf.tms_utime + buf.tms_stime;
}

return -1;
}

time_t time( time_t * timer )
{
struct timeval tv;

if ( sys_gettimeofday( &tv, NULL ) == 0 )
{
if ( timer != NULL )
{
*timer = tv.tv_sec;
}

return tv.tv_sec;
}

return -1;
}

static char _PDCLIB_sin_buffer[BUFSIZ];
static char _PDCLIB_sout_buffer[BUFSIZ];
static char _PDCLIB_serr_buffer[BUFSIZ];

static struct _PDCLIB_file_t _PDCLIB_serr = { 2, _PDCLIB_serr_buffer, BUFSIZ, 0, 0, { 0, 0 }, 0, { 0 }, _IONBF | _PDCLIB_FWRITE,
NULL, NULL
};
static struct _PDCLIB_file_t _PDCLIB_sout = { 1, _PDCLIB_sout_buffer, BUFSIZ, 0, 0, { 0, 0 }, 0, { 0 }, _IOLBF | _PDCLIB_FWRITE,
NULL, &_PDCLIB_serr
};
static struct _PDCLIB_file_t _PDCLIB_sin = { 0, _PDCLIB_sin_buffer, BUFSIZ, 0, 0, { 0, 0 }, 0, { 0 }, _IOLBF | _PDCLIB_FREAD,
NULL, &_PDCLIB_sout
};

struct _PDCLIB_file_t * stdin = &_PDCLIB_sin;
struct _PDCLIB_file_t * stdout = &_PDCLIB_sout;
struct _PDCLIB_file_t * stderr = &_PDCLIB_serr;

/* FIXME: This approach is a possible attack vector. */
struct _PDCLIB_file_t * _PDCLIB_filelist = &_PDCLIB_sin;

struct state _PDCLIB_lclmem;
struct state _PDCLIB_gmtmem;
struct tm _PDCLIB_tm;
(1-1/2)