// -*- C++ -*- (c) 2015 Jiří Weiser

#include <cerrno>
#include <cstdarg>
#include <cstdlib>
#include <cstring>
#include <string>

#include <bits/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <dios.h>

#include "fs-manager.h"

#ifdef __divine__

# define FS_MALLOC( x ) __vm_obj_make( x )
# define FS_PROBLEM( msg ) __dios_fault( _VM_Fault::_VM_F_Assert, msg )
# define FS_FREE( x ) __dios::delete_object( x )

#else
# define FS_MALLOC( x ) std::malloc( x )
# define FS_PROBLEM( msg )
# define FS_FREE( x ) std::free( x )

#endif

struct DirWrapper {
    int fd;
};

using __dios::fs::Error;
// __dios::fs::VFS vfs{};

namespace conversion {

    using namespace __dios::fs::flags;

    __dios::fs::Flags <Open> open( int fls ) {
        __dios::fs::Flags <Open> f = Open::NoFlags;
        // special behaviour - check for access rights but do not grant them
        if (( fls & 3 ) == 3 )
            f |= Open::NoAccess;
        if ( fls & O_RDWR ) {
            f |= Open::Read;
            f |= Open::Write;
        }
        else if ( fls & O_WRONLY )
            f |= Open::Write;
        else
            f |= Open::Read;

        if ( fls & O_CREAT ) f |= Open::Create;
        if ( fls & O_EXCL ) f |= Open::Excl;
        if ( fls & O_TRUNC ) f |= Open::Truncate;
        if ( fls & O_APPEND ) f |= Open::Append;
        if ( fls & O_NOFOLLOW ) f |= Open::SymNofollow;
        if ( fls & O_NONBLOCK ) f |= Open::NonBlock;
        return f;
    }

    int open( __dios::fs::Flags <Open> fls ) {
        int f;
        if ( fls.has( Open::NoAccess ))
            f = 3;
        else if ( fls.has( Open::Read ) && fls.has( Open::Write ))
            f = O_RDWR;
        else if ( fls.has( Open::Write ))
            f = O_WRONLY;
        else
            f = O_RDONLY;

        if ( fls.has( Open::Create )) f |= O_CREAT;
        if ( fls.has( Open::Excl )) f |= O_EXCL;
        if ( fls.has( Open::Truncate )) f |= O_TRUNC;
        if ( fls.has( Open::Append )) f |= O_APPEND;
        if ( fls.has( Open::SymNofollow )) f |= O_NOFOLLOW;
        if ( fls.has( Open::NonBlock )) f |= O_NONBLOCK;
        return f;
    }

    __dios::fs::Flags <Message> message( int fls ) {
        __dios::fs::Flags <Message> f = Message::NoFlags;

        if ( fls & MSG_DONTWAIT ) f |= Message::DontWait;
        if ( fls & MSG_PEEK ) f |= Message::Peek;
        if ( fls & MSG_WAITALL ) f |= Message::WaitAll;
        return f;
    }

    static_assert( AT_FDCWD == __dios::fs::CURRENT_DIRECTORY,
                   "mismatch value of AT_FDCWD and __dios::fs::CURRENT_DIRECTORY" );
}

namespace __sc {
    static void _initStat( struct stat *buf )
    {
        buf->st_dev = 0;
        buf->st_rdev = 0;
        buf->st_atime = 0;
        buf->st_mtime = 0;
        buf->st_ctime = 0;
    }

    static int _fillStat( const __dios::fs::Node item, struct stat *buf )
    {
        if ( !item )
            return -1;
        _initStat( buf );
        buf->st_ino = item->ino( );
        buf->st_mode = item->mode( );
        buf->st_nlink = item.use_count( );
        buf->st_size = item->size( );
        buf->st_uid = item->uid( );
        buf->st_gid = item->gid( );
        buf->st_blksize = 512;
        buf->st_blocks = ( buf->st_size + 1 ) / buf->st_blksize;
        return 0;
    }

     int _mknodat( int dirfd, const char *path, mode_t mode, dev_t dev, __dios::fs::VFS *vfs )
    {
            if ( dev != 0 )
                throw Error( EINVAL );
            if ( !S_ISCHR( mode ) && !S_ISBLK( mode ) && !S_ISREG( mode ) && !S_ISFIFO( mode ) && !S_ISSOCK( mode ))
                throw Error( EINVAL );
            vfs->instance( ).createNodeAt( dirfd, path, mode );
            return  0;
    }

    void creat( __dios::Context& ctx, int* err, void* retval, va_list vl  )
    {
        auto path = va_arg( vl, const char * );
        auto mode = va_arg( vl, mode_t );
        auto ret = static_cast< int * >( retval );
        auto vfs = ctx.vfs;
        va_end( vl );

        try {
            *ret =  _mknodat( AT_FDCWD, path, mode | S_IFREG, 0, vfs );
        }catch( Error &e ) {
            *ret = -1;
            *err = e.code();
        }
    }

    void open( __dios::Context& ctx, int* err, void* retval, va_list vl  )
    {
        auto path = va_arg( vl, const char * );
        auto flags = va_arg(  vl, int );
        auto ret = static_cast< int* >( retval );
        auto vfs = ctx.vfs;
        int mode = 0;
        using namespace __dios::fs::flags;
        __dios::fs::Flags <Open> f = Open::NoFlags;
        if (( flags & 3 ) == 3 )
            f |= Open::NoAccess;
        if ( flags & O_RDWR ) {
            f |= Open::Read;
            f |= Open::Write;
        }
        else if ( flags & O_WRONLY )
            f |= Open::Write;
        else
            f |= Open::Read;

        if ( flags & O_CREAT ) {
            f |= __dios::fs::flags::Open::Create;
            if ( !vl )
                    FS_PROBLEM( "flag O_CREAT has been specified but mode was not set" );
            mode = va_arg(  vl, int );
        }
        va_end( vl );

        if ( flags & O_EXCL )
            f |= __dios::fs::flags::Open::Excl;
        if ( flags & O_TRUNC )
            f |= __dios::fs::flags::Open::Truncate;
        if ( flags & O_APPEND )
            f |= __dios::fs::flags::Open::Append;
        if ( flags & O_NOFOLLOW )
            f |= __dios::fs::flags::Open::SymNofollow;
        if ( flags & O_NONBLOCK )
            f |= __dios::fs::flags::Open::NonBlock;
        if ( flags & O_DIRECTORY )
            f |= __dios::fs::flags::Open::Directory;


        try {
            *ret = vfs->instance( ).openFileAt( AT_FDCWD, path, f, mode );
        } catch ( Error & e ) {
            *err = e.code();
            *ret = -1;
        }

    }

    void openat( __dios::Context& ctx, int* err, void* retval, va_list vl )
    {
        using namespace __dios::fs::flags;
        __dios::fs::Flags <Open> f = Open::NoFlags;
        mode_t mode = 0;
        auto dirfd = va_arg(  vl, int );
        auto path = va_arg( vl, const char * );
        auto flags = va_arg(  vl, int );
        auto ret = static_cast< int *>( retval );
        auto vfs = ctx.vfs;
        // special behaviour - check for access rights but do not grant them
        if (( flags & 3 ) == 3 )
            f |= Open::NoAccess;
        if ( flags & O_RDWR ) {
            f |= Open::Read;
            f |= Open::Write;
        }
        else if ( flags & O_WRONLY )
            f |= Open::Write;
        else
            f |= Open::Read;

        if ( flags & O_CREAT ) {
            f |= __dios::fs::flags::Open::Create;
            if ( !vl )
                    FS_PROBLEM( "flag O_CREAT has been specified but mode was not set" );
            mode = va_arg(  vl, mode_t );
            va_end(  vl );
        }

        if ( flags & O_EXCL )
            f |= __dios::fs::flags::Open::Excl;
        if ( flags & O_TRUNC )
            f |= __dios::fs::flags::Open::Truncate;
        if ( flags & O_APPEND )
            f |= __dios::fs::flags::Open::Append;
        if ( flags & O_NOFOLLOW )
            f |= __dios::fs::flags::Open::SymNofollow;
        if ( flags & O_NONBLOCK )
            f |= __dios::fs::flags::Open::NonBlock;

        try {
            *ret = vfs->instance( ).openFileAt( dirfd, path, f, mode );
        } catch ( Error & e ) {
            *err = e.code();
            *ret = -1;
        }
    }

    void fcntl( __dios::Context& ctx, int* err, void* retval, va_list vl )
    {
        auto fd = va_arg( vl, int );
        auto cmd = va_arg( vl, int );
        auto ret = static_cast< int* >( retval );
        auto vfs = ctx.vfs;

        try {
            auto f = vfs->instance( ).getFile( fd );

            switch ( cmd ) {
                case F_SETFD: {
                    if ( !vl )
                            FS_PROBLEM( "command F_SETFD requires additional argument" );
                    va_end( vl );
                }
                case F_GETFD:
                    *ret = 0;
                    return;
                case F_DUPFD_CLOEXEC: // for now let assume we cannot handle O_CLOEXEC
                case F_DUPFD: {
                    if ( !vl )
                            FS_PROBLEM( "command F_DUPFD requires additional argument" );
                    int lowEdge = va_arg(  vl, int );
                    va_end( vl );
                    *ret = vfs->instance( ).duplicate( fd, lowEdge );
                    return;
                }
                case F_GETFL:
                    va_end( vl );
                    *ret = conversion::open( f->flags( ));
                    return;
                case F_SETFL: {
                    if ( !vl )
                            FS_PROBLEM( "command F_SETFL requires additional argument" );
                    int mode = va_arg(  vl, int );

                    if ( mode & O_APPEND )
                        f->flags( ) |= __dios::fs::flags::Open::Append;
                    else if ( f->flags( ).has( __dios::fs::flags::Open::Append ))
                        throw Error( EPERM );

                    if ( mode & O_NONBLOCK )
                        f->flags( ) |= __dios::fs::flags::Open::NonBlock;
                    else
                        f->flags( ).clear( __dios::fs::flags::Open::NonBlock );

                    va_end( vl );
                    *ret = 0;
                    return;
                }
                default:
                    FS_PROBLEM( "the requested command is not implemented" );
                    va_end( vl );
                    *ret = 0;
                    return;
            }

        } catch ( Error & e ) {
            va_end( vl );
            *err = e.code();
            *ret = -1;
        }
    }

    void close( __dios::Context& ctx, int* err, void* retval, va_list vl )
    {
        auto fd = va_arg( vl, int );
        auto ret = static_cast< int* >( retval );
        auto vfs = ctx.vfs;

        try {
            vfs->instance( ).closeFile( fd );
            *ret = 0;
        } catch ( Error & e ) {
            *err = e.code();
            *ret = -1;
        }
    }

    void write( __dios::Context& ctx, int* err, void* retval, va_list vl )
    {
        auto fd = va_arg( vl, int );
        auto buf = va_arg( vl, const void * );
        auto count = va_arg( vl, size_t );
        auto ret = static_cast< ssize_t* >( retval );
        auto vfs = ctx.vfs;

        try {
            auto f = vfs->instance( ).getFile( fd );
            *ret = f->write( buf, count );
        } catch ( Error & e ) {
            *err = e.code();
            *ret = -1;
        }
    }

    void read( __dios::Context& ctx, int* err, void* retval, va_list vl )
    {
        auto fd = va_arg( vl, int );
        auto buf = va_arg( vl, void * );
        auto count = va_arg( vl, size_t );
        auto ret = static_cast< ssize_t* >( retval );
        auto vfs = ctx.vfs;

        try {
            auto f = vfs->instance( ).getFile( fd );
            *ret = f->read( buf, count );
        } catch ( Error & e ) {
            *err = e.code();
            *ret = -1;
        }
    }

    void pipe( __dios::Context& ctx, int* err, void* retval, va_list vl )
    {
        auto ret = static_cast< int* >( retval );
        auto pipefd = va_arg( vl, int* );
        auto vfs = ctx.vfs;

        try {
            std::tie( pipefd[ 0 ], pipefd[ 1 ] ) = vfs->instance( ).pipe( );
            *ret = 0;
        } catch ( Error & e ) {
            *err = e.code();
            *ret = -1;
        }
    }

    void lseek( __dios::Context& ctx, int* err, void* retval, va_list vl )
    {
        auto ret = static_cast< off_t* >( retval );
        auto fd = va_arg( vl, int );
        auto offset = va_arg( vl, off_t );
        auto whence = va_arg( vl, int );
        auto vfs = ctx.vfs;

        try {
            __dios::fs::Seek w = __dios::fs::Seek::Undefined;
            switch ( whence ) {
                case SEEK_SET:
                    w = __dios::fs::Seek::Set;
                    break;
                case SEEK_CUR:
                    w = __dios::fs::Seek::Current;
                    break;
                case SEEK_END:
                    w = __dios::fs::Seek::End;
                    break;
            }
            *ret = vfs->instance( ).lseek( fd, offset, w );
        } catch ( Error & e ) {
            *err = e.code();
            *ret = -1;
        }
    }

    void dup( __dios::Context& ctx, int* err, void* retval, va_list vl )
    {
        auto ret = static_cast< int* >( retval );
        auto fd = va_arg( vl, int );
        auto vfs = ctx.vfs;

        try {
            *ret = vfs->instance( ).duplicate( fd );
        } catch ( Error & e ) {
            *err = e.code();
            *ret = -1;
        }
    }

    void dup2( __dios::Context& ctx, int* err, void* retval, va_list vl )
    {
        auto ret = static_cast< int* >( retval );
        auto oldfd = va_arg( vl, int );
        auto newfd = va_arg( vl, int );
        auto vfs = ctx.vfs;

        try {
            *ret = vfs->instance( ).duplicate2( oldfd, newfd );
        } catch ( Error & e ) {
            *err = e.code();
            *ret = -1;
        }
    }

    void ftruncate( __dios::Context& ctx, int* err, void* retval, va_list vl )
    {
        auto ret = static_cast< int* >( retval );
        auto fd = va_arg( vl, int );
        auto length = va_arg( vl, off_t );
        auto vfs = ctx.vfs;

        try {
            auto item = vfs->instance( ).getFile( fd );
            if ( !item->flags( ).has( __dios::fs::flags::Open::Write ))
                throw Error( EINVAL );
            vfs->instance( ).truncate( item->inode( ), length );
            *ret = 0;
        } catch ( Error & e ) {
            *err = e.code();
            *ret = -1;
        }
    }

    void truncate( __dios::Context& ctx, int* err, void* retval, va_list vl )
    {
        auto ret = static_cast< int* >( retval );
        auto path = va_arg( vl, const char* );
        auto length = va_arg( vl, off_t );
        auto vfs = ctx.vfs;

        try {
            auto item = vfs->instance( ).findDirectoryItem( path );
            vfs->instance( ).truncate( item, length );
            *ret =  0;
        } catch ( Error & e ) {
            *err = e.code();
            *ret = -1;
        }
    }

    void unlink( __dios::Context& ctx, int* err, void* retval, va_list vl )
    {
        auto ret = static_cast< int* >( retval );
        auto path = va_arg( vl, const char* );
        auto vfs = ctx.vfs;

        try {
            vfs->instance( ).removeFile( path );
            *ret = 0;
        } catch ( Error & e ) {
            *err = e.code();
            *ret = -1;
        }
    }

    void rmdir( __dios::Context& ctx, int* err, void* retval, va_list vl )
    {
        auto ret = static_cast< int* >( retval );
        auto path = va_arg( vl, const char* );
        auto vfs = ctx.vfs;

        try {
            vfs->instance( ).removeDirectory( path );
            *ret = 0;
        } catch ( Error & e ) {
            *err = e.code();
            *ret = -1;
        }
    }

    void unlinkat( __dios::Context& ctx, int* err, void* retval, va_list vl )
    {
        auto ret = static_cast< int* >( retval );
        auto dirfd = va_arg( vl, int );
        auto path = va_arg( vl, const char* );
        auto flags = va_arg( vl, int );
        auto vfs = ctx.vfs;

        __dios::fs::flags::At f;
        switch ( flags ) {
            case 0:
                f = __dios::fs::flags::At::NoFlags;
                break;
            case AT_REMOVEDIR:
                f = __dios::fs::flags::At::RemoveDir;
                break;
            default:
                f = __dios::fs::flags::At::Invalid;
                break;
        }
        try {
            vfs->instance( ).removeAt( dirfd, path, f );
            *ret = 0;
        } catch ( Error & e ) {
            *err = e.code();
            *ret = -1;
        }
    }

    int _linkat(int olddirfd, const char *target, int newdirfd, const char *linkpath, int flags,__dios::fs::VFS* vfs)
    {

        __dios::fs::Flags <__dios::fs::flags::At> fl = __dios::fs::flags::At::NoFlags;
        if ( flags & AT_SYMLINK_FOLLOW ) fl |= __dios::fs::flags::At::SymFollow;
        if (( flags | AT_SYMLINK_FOLLOW ) != AT_SYMLINK_FOLLOW )
            fl |= __dios::fs::flags::At::Invalid;
            vfs->instance( ).createHardLinkAt( newdirfd, linkpath, olddirfd, target, fl );
            return 0;
    }

    void linkat( __dios::Context& ctx, int* err, void* retval, va_list vl )
    {
        auto ret = static_cast< int* >( retval );
        auto olddirfd = va_arg( vl, int );
        auto target = va_arg( vl, const char* );
        auto newdirfd = va_arg( vl, int );
        auto linkpath = va_arg( vl, const char* );
        auto flags = va_arg( vl, int );
        auto vfs = ctx.vfs;

        try {
            *ret = _linkat( olddirfd, target, newdirfd, linkpath, flags, vfs);
        } catch ( Error & e ) {
            *err = e.code();
            *ret = -1;
        }


    }

    void link( __dios::Context& ctx, int* err, void* retval, va_list vl )
    {
        auto ret = static_cast< int* >( retval );
        auto target = va_arg( vl, const char* );
        auto linkpath = va_arg( vl, const char* );
        auto vfs = ctx.vfs;
         try {
            *ret = _linkat(AT_FDCWD, target, AT_FDCWD, linkpath, 0 , vfs);
        } catch ( Error & e ) {
            *err = e.code();
            *ret = -1;
        }
    }

    void symlinkat( __dios::Context& ctx, int* err, void* retval, va_list vl )
    {
        auto ret = static_cast< int* >( retval );
        auto target = va_arg( vl, const char* );
        auto dirfd = va_arg( vl, int );
        auto linkpath = va_arg( vl, const char* );
        auto vfs = ctx.vfs;

        try {
            vfs->instance( ).createSymLinkAt( dirfd, linkpath, target );
            *ret = 0;
        } catch ( Error & e ) {
            *err = e.code();
            *ret = -1;
        }
    }

    void symlink( __dios::Context& ctx, int* err, void* retval, va_list vl )
    {
        auto ret = static_cast< int* >( retval );
        auto target = va_arg( vl, const char* );
        auto linkpath = va_arg( vl, const char* );
        auto vfs = ctx.vfs;

        try {
            vfs->instance( ).createSymLinkAt( AT_FDCWD, linkpath, target );
            *ret = 0;
        } catch ( Error & e ) {
            *err = e.code();
            *ret = -1;
        }
    }

    void readlinkat( __dios::Context& ctx, int* err, void* retval, va_list vl )
    {
        auto ret = static_cast< ssize_t* >( retval );
        auto dirfd = va_arg( vl, int );
        auto path = va_arg( vl, const char* );
        auto buf = va_arg( vl, char* );
        auto count = va_arg( vl, size_t );
        auto vfs = ctx.vfs;

        try {
            *ret = vfs->instance( ).readLinkAt( dirfd, path, buf, count );
        } catch ( Error & e ) {
            *err = e.code();
            *ret = -1;
        }
    }

    void readlink( __dios::Context& ctx, int* err, void* retval, va_list vl )
    {
        auto ret = static_cast< ssize_t* >( retval );
        auto path = va_arg( vl, const char* );
        auto buf = va_arg( vl, char* );
        auto count = va_arg( vl, size_t );
        auto vfs = ctx.vfs;

        try {
            *ret = vfs->instance( ).readLinkAt( AT_FDCWD, path, buf, count );
        } catch ( Error & e ) {
            *err = e.code();
            *ret = -1;
        }
    }

    void faccessat( __dios::Context& ctx, int* err, void* retval, va_list vl )
    {
        auto ret = static_cast< int* >( retval );
        auto dirfd = va_arg( vl, int );
        auto path = va_arg( vl, const char* );
        auto mode = va_arg( vl, int );
        auto flags = va_arg( vl, int );
        auto vfs = ctx.vfs;

        __dios::fs::Flags <__dios::fs::flags::Access> m = __dios::fs::flags::Access::OK;
        if ( mode & R_OK ) m |= __dios::fs::flags::Access::Read;
        if ( mode & W_OK ) m |= __dios::fs::flags::Access::Write;
        if ( mode & X_OK ) m |= __dios::fs::flags::Access::Execute;
        if (( mode | R_OK | W_OK | X_OK ) != ( R_OK | W_OK | X_OK ))
            m |= __dios::fs::flags::Access::Invalid;

        __dios::fs::Flags <__dios::fs::flags::At> fl = __dios::fs::flags::At::NoFlags;
        if ( flags & AT_EACCESS ) fl |= __dios::fs::flags::At::EffectiveID;
        if ( flags & AT_SYMLINK_NOFOLLOW ) fl |= __dios::fs::flags::At::SymNofollow;
        if (( flags | AT_EACCESS | AT_SYMLINK_NOFOLLOW ) != ( AT_EACCESS | AT_SYMLINK_NOFOLLOW ))
            fl |= __dios::fs::flags::At::Invalid;

        try {
            vfs->instance( ).accessAt( dirfd, path, m, fl );
            *ret = 0;
        } catch ( Error & e ) {
            *err = e.code();
            *ret = -1;
        }
    }

    void access( __dios::Context& ctx, int* err, void* retval, va_list vl )
    {
        auto ret = static_cast< int* >( retval );
        auto path = va_arg( vl, const char* );
        auto mode = va_arg( vl, int );
        auto vfs = ctx.vfs;

        __dios::fs::Flags <__dios::fs::flags::Access> m = __dios::fs::flags::Access::OK;
        if ( mode & R_OK ) m |= __dios::fs::flags::Access::Read;
        if ( mode & W_OK ) m |= __dios::fs::flags::Access::Write;
        if ( mode & X_OK ) m |= __dios::fs::flags::Access::Execute;
        if (( mode | R_OK | W_OK | X_OK ) != ( R_OK | W_OK | X_OK ))
            m |= __dios::fs::flags::Access::Invalid;

        __dios::fs::Flags <__dios::fs::flags::At> fl = __dios::fs::flags::At::NoFlags;

        try {
            vfs->instance( ).accessAt( AT_FDCWD, path, m, fl );
            *ret = 0;
        } catch ( Error & e ) {
            *err = e.code();
            *ret = -1;
        }
    }

    void chdir( __dios::Context& ctx, int* err, void* retval, va_list vl )
    {
        auto ret = static_cast< int* >( retval );
        auto path = va_arg( vl, const char* );
        auto vfs = ctx.vfs;

        try {
            vfs->instance( ).changeDirectory( path );
            *ret = 0;
        } catch ( Error & e ) {
            *err = e.code();
            *ret = -1;
        }
    }

    void fchdir( __dios::Context& ctx, int* err, void* retval, va_list vl )
    {
        auto ret = static_cast< int* >( retval );
        auto dirfd = va_arg( vl, int );
        auto vfs = ctx.vfs;

        try {
            vfs->instance( ).changeDirectory( dirfd );
            *ret = 0;
        } catch ( Error & e ) {
            *err = e.code();
            *ret = -1;
        }
    }

    void fdatasync( __dios::Context& ctx, int* err, void* retval, va_list vl )
    {
        auto ret = static_cast< int* >( retval );
        auto fd = va_arg( vl, int );
        auto vfs = ctx.vfs;

        try {
            vfs->instance( ).getFile( fd );
            *ret = 0;
        } catch ( Error & e ) {
            *err = e.code();
            *ret = -1;
        }
    }

    void fsync( __dios::Context& ctx, int* err, void* retval, va_list vl )
    {
        auto ret = static_cast< int* >( retval );
        auto fd = va_arg( vl, int );
        auto vfs = ctx.vfs;

        try {
            vfs->instance( ).getFile( fd );
            *ret = 0;
        } catch ( Error & e ) {
            *err = e.code();
            *ret = -1;
        }
    }

    void syncfs( __dios::Context& ctx, int* err, void* retval, va_list vl )
    {
        auto ret = static_cast< int* >( retval );
        auto fd = va_arg( vl, int );
        auto vfs = ctx.vfs;
        va_end( vl );

        try {
            vfs->instance( ).getFile( fd );
            *ret = 0;
        } catch ( Error & e ) {
            *err = e.code();
            *ret = -1;
        }
    }

    void sync( __dios::Context&, int* /*err*/, void* retval, va_list vl )
    {
        auto ret = static_cast< int * >( retval );
        va_end( vl );

        *ret = 0;
    }

    void stat(  __dios::Context& ctx, int* err, void* retval, va_list vl )
    {
        auto ret = static_cast< int* >( retval );
        auto path = va_arg( vl, const char* );
        auto buf = va_arg( vl, struct stat* );
        auto vfs = ctx.vfs;

        try {
            auto item = vfs->instance( ).findDirectoryItem( path );
            if ( !item )
                throw Error( ENOENT );
            *ret = _fillStat( item, buf );
        } catch ( Error & e ) {
            *err = e.code();
            *ret = -1;
        }
    }

    void lstat(  __dios::Context& ctx, int* err, void* retval, va_list vl )
    {
        auto ret = static_cast< int* >( retval );
        auto path = va_arg( vl, const char* );
        auto buf = va_arg( vl, struct stat* );
        auto vfs = ctx.vfs;

        try {
            auto item = vfs->instance( ).findDirectoryItem( path, false );
            if ( !item )
                throw Error( ENOENT );
            *ret = _fillStat( item, buf );
        } catch ( Error & e ) {
            *err = e.code();
            *ret = -1;
        }
    }

    void fstat(  __dios::Context& ctx, int* err, void* retval, va_list vl )
    {
        auto ret = static_cast< int* >( retval );
        auto fd = va_arg( vl, int );
        auto buf = va_arg( vl, struct stat* );
        auto vfs = ctx.vfs;

        try {
            auto item = vfs->instance( ).getFile( fd );
            *ret = _fillStat( item->inode( ), buf );
        } catch ( Error & e ) {
            *err = e.code();
            *ret = -1;
        }
    }

    void fstatfs(  __dios::Context &, int* /*err*/, void* retval, va_list vl )
    {
        auto ret = static_cast< int* >( retval );
        /*auto fd = */va_arg( vl, int );
        /*auto buf = */va_arg( vl, struct statfs* );

        *ret = -1;
        FS_PROBLEM("Fstatfs not implemented");
    }

    void statfs(  __dios::Context &, int* /*err*/, void* retval, va_list vl )
    {
        auto ret = static_cast< int* >( retval );
        /*auto path = */va_arg( vl, const char* );
        /*auto buf = */va_arg( vl, struct statfs* );

        *ret = -1;
        FS_PROBLEM("statfs not implemented");
    }

    void fchmodat(  __dios::Context& ctx, int* err, void* retval, va_list vl )
    {
        auto ret = static_cast< int* >( retval );
        auto dirfd = va_arg( vl, int );
        auto path = va_arg( vl, const char* );
        auto mode = va_arg( vl, mode_t );
        auto flags = va_arg( vl, int );
        auto vfs = ctx.vfs;

        __dios::fs::Flags <__dios::fs::flags::At> fl = __dios::fs::flags::At::NoFlags;
        if ( flags & AT_SYMLINK_NOFOLLOW ) fl |= __dios::fs::flags::At::SymNofollow;
        if (( flags | AT_SYMLINK_NOFOLLOW ) != AT_SYMLINK_NOFOLLOW )
            fl |= __dios::fs::flags::At::Invalid;

        try {
            vfs->instance( ).chmodAt( dirfd, path, mode, fl );
            *ret = 0;
        } catch ( Error & e ) {
            *err = e.code();
            *ret = -1;
        }
    }

    void chmod(  __dios::Context& ctx, int* err, void* retval, va_list vl )
    {
        auto ret = static_cast< int* >( retval );
        auto path = va_arg( vl, const char* );
        auto mode = va_arg( vl, mode_t );
        auto vfs = ctx.vfs;

        __dios::fs::Flags <__dios::fs::flags::At> fl = __dios::fs::flags::At::NoFlags;

        try {
            vfs->instance( ).chmodAt( AT_FDCWD, path, mode, fl );
            *ret = 0;
        } catch ( Error & e ) {
            *err = e.code();
            *ret = -1;
        }
    }

    void fchmod(  __dios::Context& ctx, int* err, void* retval, va_list vl )
    {
        auto ret = static_cast< int* >( retval );
        auto fd = va_arg( vl, int );
        auto mode = va_arg( vl, mode_t );
        auto vfs = ctx.vfs;

        try {
            vfs->instance( ).chmod( fd, mode );
            *ret = 0;
        } catch ( Error & e ) {
            *err = e.code();
            *ret = -1;
        }
    }

    void mkdirat( __dios::Context& ctx, int* err, void* retval, va_list vl )
    {
        auto ret = static_cast< int* >( retval );
        auto dirfd = va_arg( vl, int );
        auto path = va_arg( vl, const char* );
        auto mode = va_arg( vl, mode_t );
        auto vfs = ctx.vfs;

        try {
            vfs->instance( ).createNodeAt( dirfd, path, ( ACCESSPERMS & mode ) | S_IFDIR );
            *ret =  0;
        } catch ( Error & e ) {
            *err = e.code();
            *ret =  -1;
        }
    }

    void mkdir( __dios::Context& ctx, int* err, void* retval, va_list vl )
    {
        auto ret = static_cast< int* >( retval );
        auto path = va_arg( vl, const char* );
        auto mode = va_arg( vl, mode_t );
        auto vfs = ctx.vfs;

        try {
            vfs->instance( ).createNodeAt( AT_FDCWD, path, ( ACCESSPERMS & mode ) | S_IFDIR );
            *ret =  0;
        } catch ( Error & e ) {
            *err = e.code();
            *ret =  -1;
        }
    }

    void mknodat( __dios::Context& ctx, int* err, void* retval, va_list vl )
    {
        auto ret = static_cast< int* >( retval );
        auto dirfd = va_arg( vl, int );
        auto path = va_arg( vl, const char* );
        auto mode = va_arg( vl, mode_t );
        auto dev = va_arg( vl, dev_t );
        auto vfs = ctx.vfs;

        try {
            *ret = _mknodat( dirfd, path, mode, dev, vfs);
        } catch ( Error & e ) {
            *err = e.code();
            *ret =  -1;
        }
    }

    void mknod( __dios::Context& ctx, int* err, void* retval, va_list vl )
    {
        auto ret = static_cast< int* >( retval );
        auto path = va_arg( vl, const char* );
        auto mode = va_arg( vl, mode_t );
        auto dev = va_arg( vl, dev_t );
        auto vfs = ctx.vfs;

         try {
            if ( dev != 0 )
                throw Error( EINVAL );
            if ( !S_ISCHR( mode ) && !S_ISBLK( mode ) && !S_ISREG( mode ) && !S_ISFIFO( mode ) && !S_ISSOCK( mode ))
                throw Error( EINVAL );
            vfs->instance( ).createNodeAt( AT_FDCWD, path, mode );
            *ret =  0;
        } catch ( Error & e ) {
            *err = e.code();
            *ret =  -1;
        }
    }

    void umask( __dios::Context& ctx, int* /*err*/, void* retval, va_list vl )
    {
        auto ret = static_cast< mode_t* >( retval );
        auto mask = va_arg( vl, mode_t );
        auto vfs = ctx.vfs;


        mode_t result = vfs->instance( ).umask( );
        vfs->instance( ).umask( mask & 0777 );
        *ret = result;
    }


    void socket( __dios::Context& ctx, int* err, void* retval, va_list vl )
    {
        using SocketType = __dios::fs::SocketType;
        using namespace __dios::fs::flags;
        auto domain = va_arg( vl, int );
        auto t = va_arg( vl, int );
        auto protocol = va_arg( vl, int );
        auto ret = static_cast< int* >( retval );
        auto vfs = ctx.vfs;

        try {
            if ( domain != AF_UNIX )
                throw Error( EAFNOSUPPORT );

            SocketType type;
            switch ( t & __SOCK_TYPE_MASK ) {
                case SOCK_STREAM:
                    type = SocketType::Stream;
                    break;
                case SOCK_DGRAM:
                    type = SocketType::Datagram;
                    break;
                default:
                    throw Error( EPROTONOSUPPORT );
            }
            if ( protocol )
                throw Error( EPROTONOSUPPORT );

            *ret = vfs->instance( ).socket( type, t & SOCK_NONBLOCK ? Open::NonBlock : Open::NoFlags );

        } catch ( Error & e ) {
            *err = e.code();
            *ret = -1;
        }
    }

    void socketpair( __dios::Context& ctx, int* err, void* retval, va_list vl )
    {
        using SocketType = __dios::fs::SocketType;
        using Open = __dios::fs::flags::Open;
        auto domain = va_arg( vl, int );
        auto t = va_arg( vl, int );
        auto protocol = va_arg( vl, int );
        auto fds = va_arg( vl, int* );
        auto ret = static_cast< int* >( retval );
        auto vfs = ctx.vfs;

        try {

            if ( domain != AF_UNIX )
                throw Error( EAFNOSUPPORT );

            SocketType type;
            switch ( t & __SOCK_TYPE_MASK ) {
                case SOCK_STREAM:
                    type = SocketType::Stream;
                    break;
                case SOCK_DGRAM:
                    type = SocketType::Datagram;
                    break;
                default:
                    throw Error( EPROTONOSUPPORT );
            }
            if ( protocol )
                throw Error( EPROTONOSUPPORT );

            std::tie( fds[ 0 ], fds[ 1 ] ) = vfs->instance( ).socketpair( type, t & SOCK_NONBLOCK ? Open::NonBlock
                                                                                                 : Open::NoFlags );
            *ret = 0;
        } catch ( Error & e ) {
            *err = e.code();
            *ret = -1;
        }
    }

    void getsockname( __dios::Context& ctx, int* err, void* retval, va_list vl )
    {
        auto sockfd = va_arg( vl, int );
        auto addr = va_arg( vl,  struct sockaddr* );
        auto len = va_arg( vl, socklen_t* );
        auto ret = static_cast< int* >( retval );
        auto vfs = ctx.vfs;

        try {
            if ( !len )
                throw Error( EFAULT );

            auto s = vfs->instance( ).getSocket( sockfd );
            struct sockaddr_un *target = reinterpret_cast< struct sockaddr_un * >( addr );

            auto &address = s->address( );

            if ( address.size( ) >= *len )
                throw Error( ENOBUFS );

            if ( target ) {
                target->sun_family = AF_UNIX;
                char *end = std::copy( address.value( ).begin( ), address.value( ).end( ), target->sun_path );
                *end = '\0';
            }
            *len = address.size( ) + 1 + sizeof( target->sun_family );
            *ret = 0;
        } catch ( Error & e ) {
            *err = e.code();
            *ret = -1;
        }
    }

    void bind( __dios::Context& ctx, int* err, void* retval, va_list vl )
    {
        auto sockfd = va_arg( vl, int );
        auto addr = va_arg( vl,  const struct sockaddr* );
        /*auto len = */va_arg( vl, socklen_t );
        auto ret = static_cast< int* >( retval );
        auto vfs = ctx.vfs;
        using Address = __dios::fs::Socket::Address;

        try {

            if ( !addr )
                throw Error( EFAULT );
            if ( addr->sa_family != AF_UNIX )
                throw Error( EINVAL );

            const struct sockaddr_un *target = reinterpret_cast< const struct sockaddr_un * >( addr );
            Address address( target->sun_path );

            vfs->instance( ).bind( sockfd, std::move( address ));
            *ret = 0;
        } catch ( Error & e ) {
            *err = e.code();
            *ret = -1;
        }
    }

    void connect( __dios::Context& ctx, int* err, void* retval, va_list vl )
    {
        auto sockfd = va_arg( vl, int );
        auto addr = va_arg( vl,  const struct sockaddr* );
        /*auto len = */va_arg( vl, socklen_t );
        auto ret = static_cast< int* >( retval );
        auto vfs = ctx.vfs;
        using Address = __dios::fs::Socket::Address;

        try {

            if ( !addr )
                throw Error( EFAULT );
            if ( addr->sa_family != AF_UNIX )
                throw Error( EAFNOSUPPORT );

            const struct sockaddr_un *target = reinterpret_cast< const struct sockaddr_un * >( addr );
            Address address( target->sun_path );

            vfs->instance( ).connect( sockfd, address );
            *ret = 0;
        } catch ( Error & e ) {
            *err = e.code();
            *ret = -1;
        }
    }

    void getpeername( __dios::Context& ctx, int* err, void* retval, va_list vl )
    {
        auto sockfd = va_arg( vl, int );
        auto addr = va_arg( vl, struct sockaddr* );
        auto len = va_arg( vl, socklen_t* );
        auto ret = static_cast< int* >( retval );
        auto vfs = ctx.vfs;

        try {
            if ( !len )
                throw Error( EFAULT );

            auto s = vfs->instance( ).getSocket( sockfd );
            struct sockaddr_un *target = reinterpret_cast< struct sockaddr_un * >( addr );

            auto &address = s->peer( ).address( );

            if ( address.size( ) >= *len )
                throw Error( ENOBUFS );

            if ( target ) {
                target->sun_family = AF_UNIX;
                char *end = std::copy( address.value( ).begin( ), address.value( ).end( ), target->sun_path );
                *end = '\0';
            }
            *len = address.size( ) + 1 + sizeof( target->sun_family );
            *ret = 0;
        } catch ( Error & e ) {
            *err = e.code();
            *ret = -1;
        }
    }

    void sendto( __dios::Context& ctx, int* err, void* retval, va_list vl )
    {
        auto sockfd = va_arg( vl, int );
        auto buf = va_arg( vl,  const void* );
        auto n = va_arg( vl, size_t );
        auto flags = va_arg( vl, int );
        auto addr = va_arg( vl,  const struct sockaddr* );
        /*auto len = */va_arg( vl, socklen_t );
        auto ret = static_cast< ssize_t* >( retval );
        auto vfs = ctx.vfs;
        using Address = __dios::fs::Socket::Address;

        if ( !addr ) {
            try {
                auto s = vfs->instance( ).getSocket( sockfd );
                *ret = s->send( static_cast< const char * >( buf ), n, conversion::message( flags ));
            } catch ( Error & e ) {
                *err = e.code();
                *ret = -1;
            }
            return;
        }

        try {
            if ( addr->sa_family != AF_UNIX )
                throw Error( EAFNOSUPPORT );

            auto s = vfs->instance( ).getSocket( sockfd );
            const struct sockaddr_un *target = reinterpret_cast< const struct sockaddr_un * >( addr );
            Address address( target ? target->sun_path : __dios::String( ));

            *ret = s->sendTo( static_cast< const char * >( buf ), n, conversion::message( flags ),
                              vfs->instance( ).resolveAddress( address ));
        } catch ( Error & e ) {
            *err = e.code();
            *ret = -1;
        }
    }

    ssize_t _recvfrom(int sockfd, void *buf, size_t n, int flags, struct sockaddr *addr, socklen_t *len, __dios::fs::VFS* vfs )
    {
        using Address = __dios::fs::Socket::Address;
        Address address;
        struct sockaddr_un *target = reinterpret_cast< struct sockaddr_un * >( addr );
        if ( target && !len )
            throw Error( EFAULT );

        auto s = vfs->instance( ).getSocket( sockfd );
        n = s->receive( static_cast< char * >( buf ), n, conversion::message( flags ), address );

        if ( target ) {
            target->sun_family = AF_UNIX;
            char *end = std::copy( address.value( ).begin( ), address.value( ).end( ), target->sun_path );
            *end = '\0';
            *len = address.size( ) + 1 + sizeof( target->sun_family );
        }
        return n;
    }

    void recv( __dios::Context& ctx, int* err, void* retval, va_list vl )
    {
        auto sockfd = va_arg( vl, int );
        auto buf = va_arg( vl,  void* );
        auto n = va_arg( vl, size_t );
        auto flags = va_arg( vl, int );
        auto ret = static_cast< ssize_t* >( retval );
        auto vfs = ctx.vfs;

        try {
             *ret = _recvfrom( sockfd, buf, n, flags, nullptr, nullptr, vfs );
        }catch( Error & e ){
             *err = e.code();
            *ret = -1;
        }
    }

    void recvfrom( __dios::Context& ctx, int* err, void* retval, va_list vl )
    {
        auto sockfd = va_arg( vl, int );
        auto buf = va_arg( vl,  void* );
        auto n = va_arg( vl, size_t );
        auto flags = va_arg( vl, int );
        auto addr = va_arg( vl,  struct sockaddr* );
        auto len = va_arg( vl, socklen_t* );
        auto ret = static_cast< ssize_t* >( retval );
        auto vfs = ctx.vfs;

        try {
             *ret = _recvfrom( sockfd, buf, n, flags, addr, len, vfs );
        }catch( Error & e ){
             *err = e.code();
            *ret = -1;
        }
    }

    void listen( __dios::Context& ctx, int* err, void* retval, va_list vl )
    {
        auto sockfd = va_arg( vl, int );
        auto n = va_arg( vl, int );
        auto ret = static_cast< int* >( retval );
        auto vfs = ctx.vfs;

        try {
            auto s = vfs->instance( ).getSocket( sockfd );
            s->listen( n );
            *ret = 0;
        } catch ( Error & e ) {
            *err = e.code();
            *ret = -1;
        }
    }

    int _accept4( int sockfd, struct sockaddr *addr, socklen_t *len, int flags, __dios::fs::VFS* vfs )
    {
        using Address = __dios::fs::Socket::Address;

        if ( addr && !len )
            throw Error( EFAULT );

        if (( flags | SOCK_NONBLOCK | SOCK_CLOEXEC ) != ( SOCK_NONBLOCK | SOCK_CLOEXEC ))
            throw Error( EINVAL );

        Address address;
        int newSocket = vfs->instance( ).accept( sockfd, address );

        if ( addr ) {
            struct sockaddr_un *target = reinterpret_cast< struct sockaddr_un * >( addr );
            target->sun_family = AF_UNIX;
            char *end = std::copy( address.value( ).begin( ), address.value( ).end( ), target->sun_path );
            *end = '\0';
            *len = address.size( ) + 1 + sizeof( target->sun_family );
        }
        if ( flags & SOCK_NONBLOCK )
            vfs->instance( ).getSocket( newSocket )->flags( ) |= __dios::fs::flags::Open::NonBlock;

        return newSocket;
    }

    void accept( __dios::Context& ctx, int* err, void* retval, va_list vl )
    {
        auto sockfd = va_arg( vl, int );
        auto addr = va_arg( vl,  struct sockaddr* );
        auto len = va_arg( vl, socklen_t* );
        auto ret = static_cast< int* >( retval );
        auto vfs = ctx.vfs;

        try {
            *ret = _accept4( sockfd, addr, len, 0, vfs );
        } catch ( Error & e ) {
            *err = e.code();
            *ret = -1;
        }
    }

    void accept4( __dios::Context& ctx, int* err, void* retval, va_list vl )
    {
        auto sockfd = va_arg( vl, int );
        auto addr = va_arg( vl,  struct sockaddr* );
        auto len = va_arg( vl, socklen_t* );
        auto flags = va_arg( vl, int );
        auto ret = static_cast< int* >( retval );
        auto vfs = ctx.vfs;
        try {
            *ret = _accept4( sockfd, addr, len, flags, vfs );
        } catch ( Error & e ) {
            *err = e.code();
            *ret = -1;
        }
    }

    int _renameitemat( int olddirfd, const char *oldpath, int newdirfd, const char *newpath,__dios::fs::VFS* vfs )
    {
        vfs->instance( ).renameAt( newdirfd, newpath, olddirfd, oldpath );
        return 0;
    }

    void renameat( __dios::Context& ctx, int* err, void* retval, va_list vl )
    {
        auto olddirfd = va_arg( vl, int );
        auto oldpath = va_arg( vl, const char* );
        auto newdirfd = va_arg( vl, int );
        auto newpath = va_arg( vl, const char* );
        auto ret = static_cast< int* >( retval );
        auto vfs = ctx.vfs;

        try {
            *ret = _renameitemat(olddirfd, oldpath, newdirfd, newpath, vfs);
        } catch ( Error & e ) {
            *err = e.code();
            *ret = -1;
        }

    }

    void rename( __dios::Context& ctx, int* err, void* retval, va_list vl )
    {
        auto oldpath = va_arg( vl, const char* );
        auto newpath = va_arg( vl, const char* );
        auto ret = static_cast< int* >( retval );
        auto vfs = ctx.vfs;

        try {
            *ret = _renameitemat( AT_FDCWD, oldpath, AT_FDCWD, newpath, vfs);
        } catch ( Error & e ) {
            *err = e.code();
            *ret = -1;
        }
    }

    //char *getcwd(char *buf, size_t size);
    void getcwd( __dios::Context& ctx, int* err, void* retval, va_list vl) {
        auto buff = va_arg( vl, char* );
        auto size = va_arg( vl, size_t );
        va_end( vl );
        auto ret = static_cast< char ** >( retval );
        auto vfs = ctx.vfs;

        try {
            vfs->instance( ).getCurrentWorkingDir( buff, size );
            *ret = buff;
        } catch ( Error & e ) {
            *err = e.code();
            *ret = nullptr;
        }
    }

} //end namespace __sc

extern "C" {

    void swab( const void *_from, void *_to, ssize_t n ) {
        const char *from = reinterpret_cast< const char * >( _from );
        char *to = reinterpret_cast< char * >( _to );
        for ( ssize_t i = 0; i < n / 2; ++i ) {
            *to = *( from + 1 );
            *( to + 1 ) = *from;
            to += 2;
            from += 2;
        }
    }

    DIR *fdopendir( int fd )
    {
        using __dios::fs::Mode;
        struct stat fdStat;

        int result = fstat( fd, &fdStat );
        if ( result == -1 )
            return nullptr;

        Mode fdMode( fdStat.st_mode );
        if( !fdMode.isDirectory( ) ) {
            errno = ENOTDIR;
            return nullptr;
        }

        int newFD = dup( fd );
        if ( newFD > 0 ) {
            DirWrapper *wrapper = reinterpret_cast< DirWrapper* >( FS_MALLOC( sizeof( struct DirWrapper ) ) );
            wrapper->fd = newFD;
            return wrapper;
        }else {
            errno = EBADF;
            return nullptr;
        }

    }

    DIR *opendir( const char *name )
    {
        int fd = open( name, O_DIRECTORY );
        if ( fd > 0 ) {
            DirWrapper *wrapper = reinterpret_cast< DirWrapper* >( FS_MALLOC( sizeof( struct DirWrapper ) ) );
            wrapper->fd = fd;
            return wrapper;
        }else {
            return nullptr;
        }
    }

    int closedir(DIR *dirp)
    {
        if ( dirp == nullptr )
        {
            errno = EBADF;
            return -1;
        }

        DirWrapper *wrapper = reinterpret_cast< DirWrapper* >( dirp );
        int fd = wrapper->fd;
        FS_FREE( wrapper );
        return close( fd );
    }

    int dirfd( DIR *dirp )
    {
        DirWrapper *wrapper = reinterpret_cast< DirWrapper* >( dirp );
        return wrapper->fd;
    }

    struct dirent *readdir( DIR *dirp )
    {
        if ( !dirp ) {
            errno = EBADF;
            return nullptr;
        }

        struct dirent* retval = reinterpret_cast< struct dirent * >( FS_MALLOC( sizeof( struct dirent )));
        DirWrapper *wrapper = reinterpret_cast< DirWrapper * >( dirp );
        char *dirInfo = reinterpret_cast< char * >( retval );

        int res = read( wrapper->fd, dirInfo, sizeof( struct dirent ) );
        return ( res > 0 ) ? retval : nullptr;
    }

    int readdir_r( DIR *dirp, struct dirent *entry, struct dirent **result )
    {
        DirWrapper *wrapper = reinterpret_cast< DirWrapper * >( dirp );
        char *dirInfo = reinterpret_cast< char * >(entry);

        int res = read(wrapper->fd, dirInfo, sizeof( struct dirent ));
        *result = ( res == sizeof( struct dirent ) ) ? entry : nullptr;
        return (res >= 0) ? 0 : -1;
    }

    void rewinddir( DIR *dirp )
    {
        DirWrapper *wrapper = reinterpret_cast< DirWrapper * >(dirp);
        lseek( wrapper->fd, 0, SEEK_SET );
    }

    int scandir( const char *path, struct dirent ***namelist,
              int ( *filter )( const struct dirent * ),
              int ( *compare )( const struct dirent **, const struct dirent ** ) )
    {

        using namespace __dios::fs::flags;
        int length = 0;
        DIR *dirp = opendir( path );

        if( !dirp ){
            return -1;
        }

        struct dirent **entries = nullptr;
        struct dirent *workingEntry = static_cast< struct dirent * >( FS_MALLOC( sizeof( struct dirent ) ) );

        while ( true ) {
            struct dirent* ent = readdir( dirp );
            if ( !ent )
                break;

            workingEntry->d_ino = ent->d_ino;
            char *x = std::copy( ent->d_name, ent->d_name + strlen(ent->d_name), workingEntry->d_name );
            *x = '\0';
            if ( filter && !filter( workingEntry ) )
                continue;

            struct dirent **newEntries = static_cast< struct dirent ** >( FS_MALLOC( ( length + 1 ) * sizeof( struct dirent * ) ) );
            if ( length )
                std::memcpy( newEntries, entries, length * sizeof( struct dirent * ) );
            std::swap( entries, newEntries );
            if ( newEntries )
                FS_FREE( newEntries );
            entries[ length ] = workingEntry;
            workingEntry = static_cast< struct dirent * >( FS_MALLOC( sizeof( struct dirent ) ) );
            ++length;
        }
        FS_FREE( workingEntry );
        closedir( dirp );

        typedef int( *cmp )( const void *, const void * );
        std::qsort( entries, length, sizeof( struct dirent * ), reinterpret_cast< cmp >( compare ) );

        *namelist = entries;
        return length;
    }

    long telldir( DIR *dirp )
    {
        DirWrapper *wrapper = reinterpret_cast< DirWrapper * >( dirp );
        return lseek( wrapper->fd, 0, SEEK_CUR );
    }

    void seekdir( DIR *dirp, long loc )
    {
        DirWrapper *wrapper = reinterpret_cast< DirWrapper * >( dirp );
        lseek( wrapper->fd, loc, SEEK_SET );
    }


    ssize_t send(int sockfd, const void *buf, size_t len, int flags)
    {
        return sendto(sockfd, buf, len, flags, NULL, 0);
    }

    ssize_t pwrite(int fd, const void *buf, size_t count, off_t offset)
    {
       int currPos = lseek( fd, 0, SEEK_CUR );

        int moved = lseek( fd, offset, SEEK_SET );
        if ( moved != offset ) 
            return -1;

        int writed = write( fd, buf, count );
        lseek( fd, currPos, SEEK_SET );

        return writed;
    }

    ssize_t pread(int fd, void *buf, size_t count, off_t offset)
    {
        int currPos = lseek( fd, 0, SEEK_CUR );

        int moved = lseek( fd, offset, SEEK_SET );
        if ( moved != offset ) 
            return -1;

        int readed = read( fd, buf, count );
        lseek( fd, currPos, SEEK_SET );

        return readed;
    }

    int mkfifoat(int dirfd, const char *pathname, mode_t mode)
    {
        return mknodat( dirfd, pathname, ( ACCESSPERMS & mode ) | S_IFIFO, 0 );
        
    }

    int mkfifo(const char *pathname, mode_t mode)
    {
        return mknod( pathname, ( ACCESSPERMS & mode ) | S_IFIFO, 0 );
    }

    char *ttyname(int fd)
    {
        using __dios::fs::Mode;
        struct stat fdStat;

        //just to set errno if fd is not valid file descriptor
        fstat( fd, &fdStat );
        return nullptr;
    }

    int ttyname_r(int fd, char *, size_t )
    {
        using __dios::fs::Mode;
        struct stat fdStat;

        //just to set errno if fd is not valid file descriptor
        int res = fstat( fd, &fdStat );
        return res == 0 ? ENOTTY : res;
    }

    int isatty(int fd)
    {
        using __dios::fs::Mode;
        struct stat fdStat;

        //just to set errno if fd is not valid file descriptor
        int res = fstat( fd, &fdStat );
        return res == 0 ? EINVAL : res;
    }


    #if defined(__divine__)
    int alphasort( const struct dirent **a, const struct dirent **b ) {
        return std::strcoll( (*a)->d_name, (*b)->d_name );
    }

    #endif

}