// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements.  See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership.  The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License.  You may obtain a copy of the License at
//
//   http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied.  See the License for the
// specific language governing permissions and limitations
// under the License..

use super::*;
use alloc::boxed::Box;
use alloc::slice;
use alloc::vec::Vec;
use core::cmp;
use core::convert::TryInto;
use core::mem;
use core::ptr;
use sgx_types::*;

const MAX_OCALL_ALLOC_SIZE: size_t = 0x4000; //16K
extern "C" {
    // memory
    pub fn u_malloc_ocall(
        result: *mut *mut c_void,
        error: *mut c_int,
        size: size_t,
    ) -> sgx_status_t;
    pub fn u_free_ocall(p: *mut c_void) -> sgx_status_t;
    pub fn u_mmap_ocall(
        result: *mut *mut c_void,
        error: *mut c_int,
        start: *mut c_void,
        length: size_t,
        prot: c_int,
        flags: c_int,
        fd: c_int,
        offset: off_t,
    ) -> sgx_status_t;
    pub fn u_munmap_ocall(
        result: *mut c_int,
        error: *mut c_int,
        start: *mut c_void,
        length: size_t,
    ) -> sgx_status_t;
    pub fn u_msync_ocall(
        result: *mut c_int,
        error: *mut c_int,
        addr: *mut c_void,
        length: size_t,
        flags: c_int,
    ) -> sgx_status_t;

    pub fn u_mprotect_ocall(
        result: *mut c_int,
        error: *mut c_int,
        addr: *mut c_void,
        length: size_t,
        prot: c_int,
    ) -> sgx_status_t;
    // env
    pub fn u_getuid_ocall(result: *mut uid_t) -> sgx_status_t;
    pub fn u_environ_ocall(result: *mut *const *const c_char) -> sgx_status_t;
    pub fn u_getenv_ocall(result: *mut *const c_char, name: *const c_char) -> sgx_status_t;
    pub fn u_setenv_ocall(
        result: *mut c_int,
        error: *mut c_int,
        name: *const c_char,
        value: *const c_char,
        overwrite: c_int,
    ) -> sgx_status_t;
    pub fn u_unsetenv_ocall(
        result: *mut c_int,
        error: *mut c_int,
        name: *const c_char,
    ) -> sgx_status_t;
    pub fn u_getcwd_ocall(
        result: *mut *mut c_char,
        error: *mut c_int,
        buf: *mut c_char,
        size: size_t,
    ) -> sgx_status_t;
    pub fn u_chdir_ocall(result: *mut c_int, error: *mut c_int, dir: *const c_char)
        -> sgx_status_t;
    pub fn u_getpwuid_r_ocall(
        result: *mut c_int,
        uid: uid_t,
        pwd: *mut passwd,
        buf: *mut c_char,
        buflen: size_t,
        passwd_result: *mut *mut passwd,
    ) -> sgx_status_t;
    // file
    pub fn u_open_ocall(
        result: *mut c_int,
        error: *mut c_int,
        path: *const c_char,
        flags: c_int,
    ) -> sgx_status_t;
    pub fn u_open64_ocall(
        result: *mut c_int,
        error: *mut c_int,
        path: *const c_char,
        oflag: c_int,
        mode: c_int,
    ) -> sgx_status_t;
    pub fn u_fstat_ocall(
        result: *mut c_int,
        error: *mut c_int,
        fd: c_int,
        buf: *mut stat,
    ) -> sgx_status_t;
    pub fn u_fstat64_ocall(
        result: *mut c_int,
        error: *mut c_int,
        fd: c_int,
        buf: *mut stat64,
    ) -> sgx_status_t;
    pub fn u_stat_ocall(
        result: *mut c_int,
        error: *mut c_int,
        path: *const c_char,
        buf: *mut stat,
    ) -> sgx_status_t;
    pub fn u_stat64_ocall(
        result: *mut c_int,
        error: *mut c_int,
        path: *const c_char,
        buf: *mut stat64,
    ) -> sgx_status_t;
    pub fn u_lstat_ocall(
        result: *mut c_int,
        error: *mut c_int,
        path: *const c_char,
        buf: *mut stat,
    ) -> sgx_status_t;
    pub fn u_lstat64_ocall(
        result: *mut c_int,
        error: *mut c_int,
        path: *const c_char,
        buf: *mut stat64,
    ) -> sgx_status_t;
    pub fn u_lseek_ocall(
        result: *mut off_t,
        error: *mut c_int,
        fd: c_int,
        offset: off_t,
        whence: c_int,
    ) -> sgx_status_t;
    pub fn u_lseek64_ocall(
        result: *mut off64_t,
        error: *mut c_int,
        fd: c_int,
        offset: off64_t,
        whence: c_int,
    ) -> sgx_status_t;
    pub fn u_ftruncate_ocall(
        result: *mut c_int,
        error: *mut c_int,
        fd: c_int,
        length: off_t,
    ) -> sgx_status_t;
    pub fn u_ftruncate64_ocall(
        result: *mut c_int,
        error: *mut c_int,
        fd: c_int,
        length: off64_t,
    ) -> sgx_status_t;
    pub fn u_truncate_ocall(
        result: *mut c_int,
        error: *mut c_int,
        path: *const c_char,
        length: off_t,
    ) -> sgx_status_t;
    pub fn u_truncate64_ocall(
        result: *mut c_int,
        error: *mut c_int,
        path: *const c_char,
        length: off64_t,
    ) -> sgx_status_t;
    pub fn u_fsync_ocall(result: *mut c_int, error: *mut c_int, fd: c_int) -> sgx_status_t;
    pub fn u_fdatasync_ocall(result: *mut c_int, error: *mut c_int, fd: c_int) -> sgx_status_t;
    pub fn u_fchmod_ocall(
        result: *mut c_int,
        error: *mut c_int,
        fd: c_int,
        mode: mode_t,
    ) -> sgx_status_t;
    pub fn u_unlink_ocall(
        result: *mut c_int,
        error: *mut c_int,
        pathname: *const c_char,
    ) -> sgx_status_t;
    pub fn u_link_ocall(
        result: *mut c_int,
        error: *mut c_int,
        oldpath: *const c_char,
        newpath: *const c_char,
    ) -> sgx_status_t;
    pub fn u_linkat_ocall(
        result: *mut c_int,
        error: *mut c_int,
        olddirfd: c_int,
        oldpath: *const c_char,
        newdirfd: c_int,
        newpath: *const c_char,
        flags: c_int,
    ) -> sgx_status_t;
    pub fn u_rename_ocall(
        result: *mut c_int,
        error: *mut c_int,
        oldpath: *const c_char,
        newpath: *const c_char,
    ) -> sgx_status_t;
    pub fn u_chmod_ocall(
        result: *mut c_int,
        error: *mut c_int,
        path: *const c_char,
        mode: mode_t,
    ) -> sgx_status_t;
    pub fn u_readlink_ocall(
        result: *mut ssize_t,
        error: *mut c_int,
        path: *const c_char,
        buf: *mut c_char,
        bufsz: size_t,
    ) -> sgx_status_t;
    pub fn u_symlink_ocall(
        result: *mut c_int,
        error: *mut c_int,
        path1: *const c_char,
        path2: *const c_char,
    ) -> sgx_status_t;
    pub fn u_realpath_ocall(
        result: *mut *mut c_char,
        error: *mut c_int,
        pathname: *const c_char,
    ) -> sgx_status_t;
    pub fn u_mkdir_ocall(
        result: *mut c_int,
        error: *mut c_int,
        pathname: *const c_char,
        mode: mode_t,
    ) -> sgx_status_t;
    pub fn u_rmdir_ocall(
        result: *mut c_int,
        error: *mut c_int,
        pathname: *const c_char,
    ) -> sgx_status_t;
    pub fn u_opendir_ocall(
        result: *mut *mut DIR,
        error: *mut c_int,
        pathname: *const c_char,
    ) -> sgx_status_t;
    pub fn u_readdir64_r_ocall(
        result: *mut c_int,
        dirp: *mut DIR,
        entry: *mut dirent64,
        dirresult: *mut *mut dirent64,
    ) -> sgx_status_t;
    pub fn u_closedir_ocall(result: *mut c_int, error: *mut c_int, dirp: *mut DIR) -> sgx_status_t;
    pub fn u_dirfd_ocall(result: *mut c_int, error: *mut c_int, dirp: *mut DIR) -> sgx_status_t;
    pub fn u_fstatat64_ocall(
        result: *mut c_int,
        error: *mut c_int,
        dirfd: c_int,
        pathname: *const c_char,
        buf: *mut stat64,
        flags: c_int,
    ) -> sgx_status_t;
    // fd
    pub fn u_read_ocall(
        result: *mut ssize_t,
        errno: *mut c_int,
        fd: c_int,
        buf: *mut c_void,
        count: size_t,
    ) -> sgx_status_t;
    pub fn u_pread64_ocall(
        result: *mut ssize_t,
        errno: *mut c_int,
        fd: c_int,
        buf: *mut c_void,
        count: size_t,
        offset: off64_t,
    ) -> sgx_status_t;
    pub fn u_readv_ocall(
        result: *mut ssize_t,
        errno: *mut c_int,
        fd: c_int,
        iov: *const iovec,
        iovcnt: c_int,
    ) -> sgx_status_t;
    pub fn u_preadv64_ocall(
        result: *mut ssize_t,
        errno: *mut c_int,
        fd: c_int,
        iov: *const iovec,
        iovcnt: c_int,
        offset: off64_t,
    ) -> sgx_status_t;
    pub fn u_write_ocall(
        result: *mut ssize_t,
        errno: *mut c_int,
        fd: c_int,
        buf: *const c_void,
        count: size_t,
    ) -> sgx_status_t;
    pub fn u_pwrite64_ocall(
        result: *mut ssize_t,
        errno: *mut c_int,
        fd: c_int,
        buf: *const c_void,
        count: size_t,
        offset: off64_t,
    ) -> sgx_status_t;
    pub fn u_writev_ocall(
        result: *mut ssize_t,
        errno: *mut c_int,
        fd: c_int,
        iov: *const iovec,
        iovcnt: c_int,
    ) -> sgx_status_t;
    pub fn u_pwritev64_ocall(
        result: *mut ssize_t,
        errno: *mut c_int,
        fd: c_int,
        iov: *const iovec,
        iovcnt: c_int,
        offset: off64_t,
    ) -> sgx_status_t;
    pub fn u_fcntl_arg0_ocall(
        result: *mut c_int,
        errno: *mut c_int,
        fd: c_int,
        cmd: c_int,
    ) -> sgx_status_t;
    pub fn u_fcntl_arg1_ocall(
        result: *mut c_int,
        errno: *mut c_int,
        fd: c_int,
        cmd: c_int,
        arg: c_int,
    ) -> sgx_status_t;
    pub fn u_ioctl_arg0_ocall(
        result: *mut c_int,
        errno: *mut c_int,
        fd: c_int,
        request: c_int,
    ) -> sgx_status_t;
    pub fn u_ioctl_arg1_ocall(
        result: *mut c_int,
        errno: *mut c_int,
        fd: c_int,
        request: c_int,
        arg: *mut c_int,
    ) -> sgx_status_t;
    pub fn u_close_ocall(result: *mut c_int, errno: *mut c_int, fd: c_int) -> sgx_status_t;
    // time
    pub fn u_clock_gettime_ocall(
        result: *mut c_int,
        errno: *mut c_int,
        clk_id: clockid_t,
        tp: *mut timespec,
    ) -> sgx_status_t;
    // socket
    pub fn u_socket_ocall(
        result: *mut c_int,
        errno: *mut c_int,
        domain: c_int,
        ty: c_int,
        protocol: c_int,
    ) -> sgx_status_t;
    pub fn u_socketpair_ocall(
        result: *mut c_int,
        errno: *mut c_int,
        domain: c_int,
        ty: c_int,
        protocol: c_int,
        sv: *mut c_int,
    ) -> sgx_status_t;
    pub fn u_bind_ocall(
        result: *mut c_int,
        errno: *mut c_int,
        sockfd: c_int,
        address: *const sockaddr,
        addrlen: socklen_t,
    ) -> sgx_status_t;
    pub fn u_listen_ocall(
        result: *mut c_int,
        error: *mut c_int,
        sockfd: c_int,
        backlog: c_int,
    ) -> sgx_status_t;
    pub fn u_accept_ocall(
        result: *mut c_int,
        errno: *mut c_int,
        sockfd: c_int,
        addr: *mut sockaddr,
        addrlen_in: socklen_t,
        addrlen_out: *mut socklen_t,
    ) -> sgx_status_t;
    pub fn u_accept4_ocall(
        result: *mut c_int,
        errno: *mut c_int,
        sockfd: c_int,
        addr: *mut sockaddr,
        addrlen_in: socklen_t,
        addrlen_out: *mut socklen_t,
        flags: c_int,
    ) -> sgx_status_t;
    pub fn u_connect_ocall(
        result: *mut c_int,
        errno: *mut c_int,
        sockfd: c_int,
        address: *const sockaddr,
        addrlen: socklen_t,
    ) -> sgx_status_t;
    pub fn u_send_ocall(
        result: *mut ssize_t,
        errno: *mut c_int,
        sockfd: c_int,
        buf: *const c_void,
        len: size_t,
        flags: c_int,
    ) -> sgx_status_t;
    pub fn u_sendto_ocall(
        result: *mut ssize_t,
        errno: *mut c_int,
        sockfd: c_int,
        buf: *const c_void,
        len: size_t,
        flags: c_int,
        addr: *const sockaddr,
        addrlen: socklen_t,
    ) -> sgx_status_t;
    pub fn u_sendmsg_ocall(
        result: *mut ssize_t,
        error: *mut c_int,
        sockfd: c_int,
        msg_name: *const c_void,
        msg_namelen: socklen_t,
        msg_iov: *const iovec,
        msg_iovlen: usize,
        msg_control: *const c_void,
        msg_controllen: usize,
        flags: c_int,
    ) -> sgx_status_t;
    pub fn u_recv_ocall(
        result: *mut ssize_t,
        errno: *mut c_int,
        sockfd: c_int,
        buf: *mut c_void,
        len: size_t,
        flags: c_int,
    ) -> sgx_status_t;
    pub fn u_recvfrom_ocall(
        result: *mut ssize_t,
        errno: *mut c_int,
        sockfd: c_int,
        buf: *mut c_void,
        len: size_t,
        flags: c_int,
        addr: *mut sockaddr,
        addrlen_in: socklen_t,
        addrlen_out: *mut socklen_t,
    ) -> sgx_status_t;
    pub fn u_recvmsg_ocall(
        result: *mut ssize_t,
        error: *mut c_int,
        sockfd: c_int,
        msg_name: *mut c_void,
        msg_namelen: socklen_t,
        msg_namelen_out: *mut socklen_t,
        msg_iov: *mut iovec,
        msg_iovlen: usize,
        msg_control: *mut c_void,
        msg_controllen: usize,
        msg_controllen_out: *mut usize,
        msg_flags: *mut c_int,
        flags: c_int,
    ) -> sgx_status_t;
    pub fn u_setsockopt_ocall(
        result: *mut c_int,
        errno: *mut c_int,
        sockfd: c_int,
        level: c_int,
        optname: c_int,
        optval: *const c_void,
        optlen: socklen_t,
    ) -> sgx_status_t;
    pub fn u_getsockopt_ocall(
        result: *mut c_int,
        errno: *mut c_int,
        sockfd: c_int,
        level: c_int,
        optname: c_int,
        optval: *mut c_void,
        optlen_in: socklen_t,
        optlen_out: *mut socklen_t,
    ) -> sgx_status_t;
    pub fn u_getpeername_ocall(
        result: *mut c_int,
        errno: *mut c_int,
        sockfd: c_int,
        address: *mut sockaddr,
        addrlen_in: socklen_t,
        addrlen_out: *mut socklen_t,
    ) -> sgx_status_t;
    pub fn u_getsockname_ocall(
        result: *mut c_int,
        errno: *mut c_int,
        sockfd: c_int,
        address: *mut sockaddr,
        addrlen_in: socklen_t,
        addrlen_out: *mut socklen_t,
    ) -> sgx_status_t;
    pub fn u_shutdown_ocall(
        result: *mut c_int,
        errno: *mut c_int,
        sockfd: c_int,
        how: c_int,
    ) -> sgx_status_t;
    // net
    pub fn u_getaddrinfo_ocall(
        result: *mut c_int,
        errno: *mut c_int,
        node: *const c_char,
        service: *const c_char,
        hints: *const addrinfo,
        res: *mut *mut addrinfo,
    ) -> sgx_status_t;
    pub fn u_freeaddrinfo_ocall(res: *mut addrinfo) -> sgx_status_t;
    pub fn u_gai_strerror_ocall(result: *mut *const c_char, errcode: c_int) -> sgx_status_t;
    // async io
    pub fn u_poll_ocall(
        result: *mut c_int,
        errno: *mut c_int,
        fds: *mut pollfd,
        nfds: nfds_t,
        timeout: c_int,
    ) -> sgx_status_t;
    pub fn u_epoll_create1_ocall(
        result: *mut c_int,
        error: *mut c_int,
        flags: c_int,
    ) -> sgx_status_t;
    pub fn u_epoll_ctl_ocall(
        result: *mut c_int,
        error: *mut c_int,
        epfd: c_int,
        op: c_int,
        fd: c_int,
        event: *mut epoll_event,
    ) -> sgx_status_t;
    pub fn u_epoll_wait_ocall(
        result: *mut c_int,
        error: *mut c_int,
        epfd: c_int,
        events: *mut epoll_event,
        maxevents: c_int,
        timeout: c_int,
    ) -> sgx_status_t;
    // sys
    pub fn u_sysconf_ocall(result: *mut c_long, error: *mut c_int, name: c_int) -> sgx_status_t;
    pub fn u_prctl_ocall(
        result: *mut c_int,
        error: *mut c_int,
        option: c_int,
        arg2: c_ulong,
        arg3: c_ulong,
        arg4: c_ulong,
        arg5: c_ulong,
    ) -> sgx_status_t;
    pub fn u_sched_setaffinity_ocall(
        result: *mut c_int,
        error: *mut c_int,
        pid: pid_t,
        cpusetsize: size_t,
        mask: *const cpu_set_t,
    ) -> sgx_status_t;
    pub fn u_sched_getaffinity_ocall(
        result: *mut c_int,
        error: *mut c_int,
        pid: pid_t,
        cpusetsize: size_t,
        mask: *mut cpu_set_t,
    ) -> sgx_status_t;
    // pipe
    pub fn u_pipe_ocall(result: *mut c_int, error: *mut c_int, fds: *mut c_int) -> sgx_status_t;
    pub fn u_pipe2_ocall(
        result: *mut c_int,
        error: *mut c_int,
        fds: *mut c_int,
        flags: c_int,
    ) -> sgx_status_t;
    //thread
    pub fn u_sched_yield_ocall(result: *mut c_int, error: *mut c_int) -> sgx_status_t;
    pub fn u_nanosleep_ocall(
        result: *mut c_int,
        error: *mut c_int,
        rqtp: *const timespec,
        rmtp: *mut timespec,
    ) -> sgx_status_t;
    //signal
    pub fn u_sigaction_ocall(
        result: *mut c_int,
        error: *mut c_int,
        signum: c_int,
        act: *const sigaction,
        oldact: *mut sigaction,
        enclave_id: uint64_t,
    ) -> sgx_status_t;
    pub fn u_sigprocmask_ocall(
        result: *mut c_int,
        error: *mut c_int,
        signum: c_int,
        set: *const sigset_t,
        oldset: *mut sigset_t,
    ) -> sgx_status_t;
    pub fn u_raise_ocall(result: *mut c_int, signum: c_int) -> sgx_status_t;
    //process
    pub fn u_getpid_ocall(result: *mut pid_t) -> sgx_status_t;
}

pub unsafe fn malloc(size: size_t) -> *mut c_void {
    let mut result: *mut c_void = ptr::null_mut();
    let mut error: c_int = 0;
    let status = u_malloc_ocall(
        &mut result as *mut *mut c_void,
        &mut error as *mut c_int,
        size,
    );

    if status == sgx_status_t::SGX_SUCCESS {
        if !result.is_null() {
            if sgx_is_outside_enclave(result, size) == 0 {
                set_errno(ESGX);
                result = ptr::null_mut();
            }
        } else {
            set_errno(error);
        }
    } else {
        set_errno(ESGX);
        result = ptr::null_mut();
    }
    result
}

pub unsafe fn free(p: *mut c_void) {
    let _ = u_free_ocall(p);
}

pub unsafe fn mmap(
    start: *mut c_void,
    length: size_t,
    prot: c_int,
    flags: c_int,
    fd: c_int,
    offset: off_t,
) -> *mut c_void {
    let mut result: *mut c_void = ptr::null_mut();
    let mut error: c_int = 0;
    let status = u_mmap_ocall(
        &mut result as *mut *mut c_void,
        &mut error as *mut c_int,
        start,
        length,
        prot,
        flags,
        fd,
        offset,
    );

    if status == sgx_status_t::SGX_SUCCESS {
        if result as isize != -1 {
            if sgx_is_outside_enclave(result, length) == 0 {
                set_errno(ESGX);
                result = -1 as isize as *mut c_void;
            }
        } else {
            set_errno(error);
        }
    } else {
        set_errno(ESGX);
        result = -1 as isize as *mut c_void;
    }
    result
}

pub unsafe fn munmap(start: *mut c_void, length: size_t) -> c_int {
    let mut result: c_int = 0;
    let mut error: c_int = 0;
    let status = u_munmap_ocall(
        &mut result as *mut c_int,
        &mut error as *mut c_int,
        start,
        length,
    );

    if status == sgx_status_t::SGX_SUCCESS {
        if result == -1 {
            set_errno(error);
        }
    } else {
        set_errno(ESGX);
        result = -1;
    }
    result
}

pub unsafe fn msync(addr: *mut c_void, length: size_t, flags: c_int) -> c_int {
    let mut result: c_int = 0;
    let mut error: c_int = 0;
    let status = u_msync_ocall(
        &mut result as *mut c_int,
        &mut error as *mut c_int,
        addr,
        length,
        flags,
    );

    if status == sgx_status_t::SGX_SUCCESS {
        if result == -1 {
            set_errno(error);
        }
    } else {
        set_errno(ESGX);
        result = -1;
    }
    result
}

pub unsafe fn mprotect(addr: *mut c_void, length: size_t, prot: c_int) -> c_int {
    let mut result: c_int = 0;
    let mut error: c_int = 0;
    let status = u_mprotect_ocall(
        &mut result as *mut c_int,
        &mut error as *mut c_int,
        addr,
        length,
        prot,
    );

    if status == sgx_status_t::SGX_SUCCESS {
        if result == -1 {
            set_errno(error);
        }
    } else {
        set_errno(ESGX);
        result = -1;
    }
    result
}

pub unsafe fn getuid() -> uid_t {
    let mut result: uid_t = 0;
    let status = u_getuid_ocall(&mut result as *mut uid_t);
    if status != sgx_status_t::SGX_SUCCESS {
        set_errno(ESGX);
        result = 0;
    }
    result
}

pub unsafe fn environ() -> *const *const c_char {
    let mut result: *const *const c_char = ptr::null();
    let status = u_environ_ocall(&mut result as *mut *const *const c_char);

    if status == sgx_status_t::SGX_SUCCESS {
        let mut environ = result;
        let mut count: usize = 0;
        if !environ.is_null() {
            while !(*environ).is_null() {
                let len = strlen(*environ) + 1;
                if sgx_is_outside_enclave(*environ as *const c_void, len) == 0 {
                    return ptr::null();
                }
                count += 1;
                environ = environ.add(1);
            }
            if sgx_is_outside_enclave(
                result as *const c_void,
                mem::size_of::<*const c_char>() * count,
            ) == 0
            {
                return ptr::null();
            }
        }
    } else {
        result = ptr::null();
    }
    result
}

pub unsafe fn getenv(name: *const c_char) -> *const c_char {
    let mut result: *const c_char = ptr::null();
    let status = u_getenv_ocall(&mut result as *mut *const c_char, name);

    if status == sgx_status_t::SGX_SUCCESS {
        if !result.is_null() {
            let len: usize = strlen(result) + 1;
            if sgx_is_outside_enclave(result as *const c_void, len) == 0 {
                result = ptr::null();
            }
        }
    } else {
        result = ptr::null();
    }
    result
}

pub unsafe fn setenv(name: *const c_char, value: *const c_char, overwrite: c_int) -> c_int {
    let mut result: c_int = 0;
    let mut error: c_int = 0;
    let status = u_setenv_ocall(
        &mut result as *mut c_int,
        &mut error as *mut c_int,
        name,
        value,
        overwrite,
    );

    if status == sgx_status_t::SGX_SUCCESS {
        if result == -1 {
            set_errno(error);
        }
    } else {
        set_errno(ESGX);
        result = -1;
    }
    result
}

pub unsafe fn unsetenv(name: *const c_char) -> c_int {
    let mut result: c_int = 0;
    let mut error: c_int = 0;
    let status = u_unsetenv_ocall(&mut result as *mut c_int, &mut error as *mut c_int, name);

    if status == sgx_status_t::SGX_SUCCESS {
        if result == -1 {
            set_errno(error);
        }
    } else {
        set_errno(ESGX);
        result = -1;
    }
    result
}

pub unsafe fn getcwd(buf: *mut c_char, size: size_t) -> *mut c_char {
    let mut result: *mut c_char = ptr::null_mut();
    let mut error: c_int = 0;
    let status = u_getcwd_ocall(
        &mut result as *mut *mut c_char,
        &mut error as *mut c_int,
        buf,
        size,
    );

    if status == sgx_status_t::SGX_SUCCESS {
        if !result.is_null() {
            if buf.is_null() {
                let len = if size > 0 { size } else { strlen(result) + 1 };
                if sgx_is_outside_enclave(result as *const c_void, len) == 0 {
                    set_errno(ESGX);
                    result = ptr::null_mut();
                }
            } else {
                result = buf;
            }
        } else {
            set_errno(error);
        }
    } else {
        set_errno(ESGX);
        result = ptr::null_mut();
    }
    result
}

pub unsafe fn chdir(dir: *const c_char) -> c_int {
    let mut result: c_int = 0;
    let mut error: c_int = 0;
    let status = u_chdir_ocall(&mut result as *mut c_int, &mut error as *mut c_int, dir);

    if status == sgx_status_t::SGX_SUCCESS {
        if result == -1 {
            set_errno(error);
        }
    } else {
        set_errno(ESGX);
        result = -1;
    }
    result
}

pub unsafe fn getpwuid_r(
    uid: uid_t,
    pwd: *mut passwd,
    buf: *mut c_char,
    buflen: size_t,
    passwd_result: *mut *mut passwd,
) -> c_int {
    let mut result: c_int = 0;
    let status = u_getpwuid_r_ocall(
        &mut result as *mut c_int,
        uid,
        pwd,
        buf,
        buflen,
        passwd_result,
    );

    if status == sgx_status_t::SGX_SUCCESS && result == 0 {
        let pwd_ret = *passwd_result;
        if !pwd_ret.is_null() {
            let mut temp_pwd = &mut *pwd;
            let mut temp_buf = buf;
            if temp_pwd.pw_name as usize != -1_isize as usize {
                temp_pwd.pw_name = temp_buf.offset(temp_pwd.pw_name as isize);
            } else {
                temp_pwd.pw_name = ptr::null_mut();
            }
            temp_buf = buf;
            if temp_pwd.pw_passwd as usize != -1_isize as usize {
                temp_pwd.pw_passwd = temp_buf.offset(temp_pwd.pw_passwd as isize);
            } else {
                temp_pwd.pw_passwd = ptr::null_mut();
            }
            temp_buf = buf;
            if temp_pwd.pw_gecos as usize != -1_isize as usize {
                temp_pwd.pw_gecos = temp_buf.offset(temp_pwd.pw_gecos as isize);
            } else {
                temp_pwd.pw_gecos = ptr::null_mut();
            }
            temp_buf = buf;
            if temp_pwd.pw_dir as usize != -1_isize as usize {
                temp_pwd.pw_dir = temp_buf.offset(temp_pwd.pw_dir as isize);
            } else {
                temp_pwd.pw_dir = ptr::null_mut();
            }
            temp_buf = buf;
            if temp_pwd.pw_shell as usize != -1_isize as usize {
                temp_pwd.pw_shell = temp_buf.offset(temp_pwd.pw_shell as isize);
            } else {
                temp_pwd.pw_shell = ptr::null_mut();
            }
            *passwd_result = pwd;
        }
    } else {
        *passwd_result = ptr::null_mut();
        if status != sgx_status_t::SGX_SUCCESS {
            result = ESGX;
        }
    }
    result
}

pub unsafe fn open(path: *const c_char, flags: c_int) -> c_int {
    let mut result: c_int = 0;
    let mut error: c_int = 0;
    let status = u_open_ocall(
        &mut result as *mut c_int,
        &mut error as *mut c_int,
        path,
        flags,
    );

    if status == sgx_status_t::SGX_SUCCESS {
        if result == -1 {
            set_errno(error);
        }
    } else {
        set_errno(ESGX);
        result = -1;
    }
    result
}

pub unsafe fn open64(path: *const c_char, oflag: c_int, mode: c_int) -> c_int {
    let mut result: c_int = 0;
    let mut error: c_int = 0;
    let status = u_open64_ocall(
        &mut result as *mut c_int,
        &mut error as *mut c_int,
        path,
        oflag,
        mode,
    );

    if status == sgx_status_t::SGX_SUCCESS {
        if result == -1 {
            set_errno(error);
        }
    } else {
        set_errno(ESGX);
        result = -1;
    }
    result
}

pub unsafe fn fstat(fd: c_int, buf: *mut stat) -> c_int {
    let mut result: c_int = 0;
    let mut error: c_int = 0;
    let status = u_fstat_ocall(&mut result as *mut c_int, &mut error as *mut c_int, fd, buf);

    if status == sgx_status_t::SGX_SUCCESS {
        if result == -1 {
            set_errno(error);
        }
    } else {
        set_errno(ESGX);
        result = -1;
    }
    result
}

pub unsafe fn fstat64(fd: c_int, buf: *mut stat64) -> c_int {
    let mut result: c_int = 0;
    let mut error: c_int = 0;
    let status = u_fstat64_ocall(&mut result as *mut c_int, &mut error as *mut c_int, fd, buf);

    if status == sgx_status_t::SGX_SUCCESS {
        if result == -1 {
            set_errno(error);
        }
    } else {
        set_errno(ESGX);
        result = -1;
    }
    result
}

pub unsafe fn stat(path: *const c_char, buf: *mut stat) -> c_int {
    let mut result: c_int = 0;
    let mut error: c_int = 0;
    let status = u_stat_ocall(
        &mut result as *mut c_int,
        &mut error as *mut c_int,
        path,
        buf,
    );

    if status == sgx_status_t::SGX_SUCCESS {
        if result == -1 {
            set_errno(error);
        }
    } else {
        set_errno(ESGX);
        result = -1;
    }
    result
}

pub unsafe fn stat64(path: *const c_char, buf: *mut stat64) -> c_int {
    let mut result: c_int = 0;
    let mut error: c_int = 0;
    let status = u_stat64_ocall(
        &mut result as *mut c_int,
        &mut error as *mut c_int,
        path,
        buf,
    );

    if status == sgx_status_t::SGX_SUCCESS {
        if result == -1 {
            set_errno(error);
        }
    } else {
        set_errno(ESGX);
        result = -1;
    }
    result
}

pub unsafe fn lstat(path: *const c_char, buf: *mut stat) -> c_int {
    let mut result: c_int = 0;
    let mut error: c_int = 0;
    let status = u_lstat_ocall(
        &mut result as *mut c_int,
        &mut error as *mut c_int,
        path,
        buf,
    );

    if status == sgx_status_t::SGX_SUCCESS {
        if result == -1 {
            set_errno(error);
        }
    } else {
        set_errno(ESGX);
        result = -1;
    }
    result
}

pub unsafe fn lstat64(path: *const c_char, buf: *mut stat64) -> c_int {
    let mut result: c_int = 0;
    let mut error: c_int = 0;
    let status = u_lstat64_ocall(
        &mut result as *mut c_int,
        &mut error as *mut c_int,
        path,
        buf,
    );

    if status == sgx_status_t::SGX_SUCCESS {
        if result == -1 {
            set_errno(error);
        }
    } else {
        set_errno(ESGX);
        result = -1;
    }
    result
}

pub unsafe fn lseek(fd: c_int, offset: off_t, whence: c_int) -> off_t {
    let mut result: off_t = 0;
    let mut error: c_int = 0;
    let status = u_lseek_ocall(
        &mut result as *mut off_t,
        &mut error as *mut c_int,
        fd,
        offset,
        whence,
    );

    if status == sgx_status_t::SGX_SUCCESS {
        if result == -1 {
            set_errno(error);
        }
    } else {
        set_errno(ESGX);
        result = -1;
    }
    result
}

pub unsafe fn lseek64(fd: c_int, offset: off64_t, whence: c_int) -> off64_t {
    let mut result: off64_t = 0;
    let mut error: c_int = 0;
    let status = u_lseek64_ocall(
        &mut result as *mut off64_t,
        &mut error as *mut c_int,
        fd,
        offset,
        whence,
    );

    if status == sgx_status_t::SGX_SUCCESS {
        if result == -1 {
            set_errno(error);
        }
    } else {
        set_errno(ESGX);
        result = -1;
    }
    result
}

pub unsafe fn ftruncate(fd: c_int, length: off_t) -> c_int {
    let mut result: c_int = 0;
    let mut error: c_int = 0;
    let status = u_ftruncate_ocall(
        &mut result as *mut c_int,
        &mut error as *mut c_int,
        fd,
        length,
    );

    if status == sgx_status_t::SGX_SUCCESS {
        if result == -1 {
            set_errno(error);
        }
    } else {
        set_errno(ESGX);
        result = -1;
    }
    result
}

pub unsafe fn ftruncate64(fd: c_int, length: off64_t) -> c_int {
    let mut result: c_int = 0;
    let mut error: c_int = 0;
    let status = u_ftruncate64_ocall(
        &mut result as *mut c_int,
        &mut error as *mut c_int,
        fd,
        length,
    );

    if status == sgx_status_t::SGX_SUCCESS {
        if result == -1 {
            set_errno(error);
        }
    } else {
        set_errno(ESGX);
        result = -1;
    }
    result
}

pub unsafe fn truncate(path: *const c_char, length: off_t) -> c_int {
    let mut result: c_int = 0;
    let mut error: c_int = 0;
    let status = u_truncate_ocall(
        &mut result as *mut c_int,
        &mut error as *mut c_int,
        path,
        length,
    );

    if status == sgx_status_t::SGX_SUCCESS {
        if result == -1 {
            set_errno(error);
        }
    } else {
        set_errno(ESGX);
        result = -1;
    }
    result
}

pub unsafe fn truncate64(path: *const c_char, length: off64_t) -> c_int {
    let mut result: c_int = 0;
    let mut error: c_int = 0;
    let status = u_truncate64_ocall(
        &mut result as *mut c_int,
        &mut error as *mut c_int,
        path,
        length,
    );

    if status == sgx_status_t::SGX_SUCCESS {
        if result == -1 {
            set_errno(error);
        }
    } else {
        set_errno(ESGX);
        result = -1;
    }
    result
}

pub unsafe fn fsync(fd: c_int) -> c_int {
    let mut result: c_int = 0;
    let mut error: c_int = 0;
    let status = u_fsync_ocall(&mut result as *mut c_int, &mut error as *mut c_int, fd);

    if status == sgx_status_t::SGX_SUCCESS {
        if result == -1 {
            set_errno(error);
        }
    } else {
        set_errno(ESGX);
        result = -1;
    }
    result
}

pub unsafe fn fdatasync(fd: c_int) -> c_int {
    let mut result: c_int = 0;
    let mut error: c_int = 0;
    let status = u_fdatasync_ocall(&mut result as *mut c_int, &mut error as *mut c_int, fd);

    if status == sgx_status_t::SGX_SUCCESS {
        if result == -1 {
            set_errno(error);
        }
    } else {
        set_errno(ESGX);
        result = -1;
    }
    result
}

pub unsafe fn fchmod(fd: c_int, mode: mode_t) -> c_int {
    let mut result: c_int = 0;
    let mut error: c_int = 0;
    let status = u_fchmod_ocall(
        &mut result as *mut c_int,
        &mut error as *mut c_int,
        fd,
        mode,
    );

    if status == sgx_status_t::SGX_SUCCESS {
        if result == -1 {
            set_errno(error);
        }
    } else {
        set_errno(ESGX);
        result = -1;
    }
    result
}

pub unsafe fn unlink(pathname: *const c_char) -> c_int {
    let mut result: c_int = 0;
    let mut error: c_int = 0;
    let status = u_unlink_ocall(
        &mut result as *mut c_int,
        &mut error as *mut c_int,
        pathname,
    );

    if status == sgx_status_t::SGX_SUCCESS {
        if result == -1 {
            set_errno(error);
        }
    } else {
        set_errno(ESGX);
        result = -1;
    }
    result
}

pub unsafe fn link(oldpath: *const c_char, newpath: *const c_char) -> c_int {
    let mut result: c_int = 0;
    let mut error: c_int = 0;
    let status = u_link_ocall(
        &mut result as *mut c_int,
        &mut error as *mut c_int,
        oldpath,
        newpath,
    );

    if status == sgx_status_t::SGX_SUCCESS {
        if result == -1 {
            set_errno(error);
        }
    } else {
        set_errno(ESGX);
        result = -1;
    }
    result
}

pub unsafe fn linkat(
    olddirfd: c_int,
    oldpath: *const c_char,
    newdirfd: c_int,
    newpath: *const c_char,
    flags: c_int,
) -> c_int {
    let mut result: c_int = 0;
    let mut error: c_int = 0;
    let status = u_linkat_ocall(
        &mut result as *mut c_int,
        &mut error as *mut c_int,
        olddirfd,
        oldpath,
        newdirfd,
        newpath,
        flags,
    );

    if status == sgx_status_t::SGX_SUCCESS {
        if result == -1 {
            set_errno(error);
        }
    } else {
        set_errno(ESGX);
        result = -1;
    }
    result
}

pub unsafe fn rename(oldpath: *const c_char, newpath: *const c_char) -> c_int {
    let mut result: c_int = 0;
    let mut error: c_int = 0;
    let status = u_rename_ocall(
        &mut result as *mut c_int,
        &mut error as *mut c_int,
        oldpath,
        newpath,
    );

    if status == sgx_status_t::SGX_SUCCESS {
        if result == -1 {
            set_errno(error);
        }
    } else {
        set_errno(ESGX);
        result = -1;
    }
    result
}

pub unsafe fn chmod(path: *const c_char, mode: mode_t) -> c_int {
    let mut result: c_int = 0;
    let mut error: c_int = 0;
    let status = u_chmod_ocall(
        &mut result as *mut c_int,
        &mut error as *mut c_int,
        path,
        mode,
    );

    if status == sgx_status_t::SGX_SUCCESS {
        if result == -1 {
            set_errno(error);
        }
    } else {
        set_errno(ESGX);
        result = -1;
    }
    result
}

pub unsafe fn readlink(path: *const c_char, buf: *mut c_char, bufsz: size_t) -> ssize_t {
    let mut result: ssize_t = 0;
    let mut error: c_int = 0;
    let status = u_readlink_ocall(
        &mut result as *mut ssize_t,
        &mut error as *mut c_int,
        path,
        buf,
        bufsz,
    );

    if status == sgx_status_t::SGX_SUCCESS {
        if result == -1 {
            set_errno(error);
        }
    } else {
        set_errno(ESGX);
        result = -1;
    }
    result
}

pub unsafe fn symlink(path1: *const c_char, path2: *const c_char) -> c_int {
    let mut result: c_int = 0;
    let mut error: c_int = 0;
    let status = u_symlink_ocall(
        &mut result as *mut c_int,
        &mut error as *mut c_int,
        path1,
        path2,
    );

    if status == sgx_status_t::SGX_SUCCESS {
        if result == -1 {
            set_errno(error);
        }
    } else {
        set_errno(ESGX);
        result = -1;
    }
    result
}

pub unsafe fn realpath(pathname: *const c_char) -> *mut c_char {
    let mut result: *mut c_char = ptr::null_mut();
    let mut error: c_int = 0;
    let status = u_realpath_ocall(
        &mut result as *mut *mut c_char,
        &mut error as *mut c_int,
        pathname,
    );

    if status == sgx_status_t::SGX_SUCCESS {
        if !result.is_null() {
            let len: usize = strlen(result) + 1;
            if sgx_is_outside_enclave(result as *const c_void, len) == 0 {
                set_errno(ESGX);
                result = ptr::null_mut();
            }
        } else {
            set_errno(error);
        }
    } else {
        set_errno(ESGX);
        result = ptr::null_mut();
    }
    result
}

pub unsafe fn mkdir(pathname: *const c_char, mode: mode_t) -> c_int {
    let mut error: c_int = 0;
    let mut result: c_int = 0;
    let status = u_mkdir_ocall(
        &mut result as *mut c_int,
        &mut error as *mut c_int,
        pathname,
        mode,
    );

    if status == sgx_status_t::SGX_SUCCESS {
        if result == -1 {
            set_errno(error);
        }
    } else {
        set_errno(ESGX);
        result = -1;
    }
    result
}

pub unsafe fn rmdir(pathname: *const c_char) -> c_int {
    let mut error: c_int = 0;
    let mut result: c_int = 0;
    let status = u_rmdir_ocall(
        &mut result as *mut c_int,
        &mut error as *mut c_int,
        pathname,
    );

    if status == sgx_status_t::SGX_SUCCESS {
        if result == -1 {
            set_errno(error);
        }
    } else {
        set_errno(ESGX);
        result = -1;
    }
    result
}

pub unsafe fn opendir(pathname: *const c_char) -> *mut DIR {
    let mut result: *mut DIR = ptr::null_mut();
    let mut error: c_int = 0;
    let status = u_opendir_ocall(
        &mut result as *mut *mut DIR,
        &mut error as *mut c_int,
        pathname,
    );

    if status == sgx_status_t::SGX_SUCCESS {
        if result.is_null() {
            set_errno(error);
        }
    } else {
        set_errno(ESGX);
        result = ptr::null_mut();
    }
    result
}

pub unsafe fn readdir64_r(
    dirp: *mut DIR,
    entry: *mut dirent64,
    dirresult: *mut *mut dirent64,
) -> c_int {
    let mut result: c_int = 0;
    let status = u_readdir64_r_ocall(&mut result as *mut c_int, dirp, entry, dirresult);

    if status == sgx_status_t::SGX_SUCCESS && result == 0 {
        let dir_ret = *dirresult;
        if !dir_ret.is_null() {
            *dirresult = entry;
        }
    } else {
        *dirresult = ptr::null_mut();
        if status != sgx_status_t::SGX_SUCCESS {
            result = ESGX;
        }
    }
    result
}

pub unsafe fn closedir(dirp: *mut DIR) -> c_int {
    let mut result: c_int = 0;
    let mut error: c_int = 0;
    let status = u_closedir_ocall(&mut result as *mut c_int, &mut error as *mut c_int, dirp);

    if status == sgx_status_t::SGX_SUCCESS {
        if result == -1 {
            set_errno(error);
        }
    } else {
        set_errno(ESGX);
        result = -1;
    }
    result
}

pub unsafe fn dirfd(dirp: *mut DIR) -> c_int {
    let mut result: c_int = 0;
    let mut error: c_int = 0;
    let status = u_dirfd_ocall(&mut result as *mut c_int, &mut error as *mut c_int, dirp);

    if status == sgx_status_t::SGX_SUCCESS {
        if result == -1 {
            set_errno(error);
        }
    } else {
        set_errno(ESGX);
        result = -1;
    }
    result
}

pub unsafe fn fstatat64(
    dirfd: c_int,
    pathname: *const c_char,
    buf: *mut stat64,
    flags: c_int,
) -> c_int {
    let mut result: c_int = 0;
    let mut error: c_int = 0;
    let status = u_fstatat64_ocall(
        &mut result as *mut c_int,
        &mut error as *mut c_int,
        dirfd,
        pathname,
        buf,
        flags,
    );

    if status == sgx_status_t::SGX_SUCCESS {
        if result == -1 {
            set_errno(error);
        }
    } else {
        set_errno(ESGX);
        result = -1;
    }
    result
}

pub unsafe fn read(fd: c_int, buf: *mut c_void, count: size_t) -> ssize_t {
    let mut result: ssize_t = 0;
    let mut error: c_int = 0;

    if buf.is_null() || sgx_is_within_enclave(buf, count) == 0 {
        set_errno(EINVAL);
        return -1;
    }
    if count >= usize::MAX {
        set_errno(EINVAL);
        return -1;
    }

    let tmp_buf = if count <= MAX_OCALL_ALLOC_SIZE {
        sgx_ocalloc(count)
    } else {
        malloc(count)
    };
    if tmp_buf.is_null() {
        set_errno(ENOMEM);
        return -1;
    }
    tmp_buf.write_bytes(0_u8, count);

    let status = u_read_ocall(
        &mut result as *mut ssize_t,
        &mut error as *mut c_int,
        fd,
        tmp_buf,
        count,
    );

    if status == sgx_status_t::SGX_SUCCESS {
        if result == -1 {
            set_errno(error);
        }
    } else {
        set_errno(ESGX);
        result = -1;
    }

    if result != -1 {
        ptr::copy_nonoverlapping(
            tmp_buf as *const u8,
            buf as *mut u8,
            cmp::min(count, result.try_into().unwrap_or(0)),
        );
    }
    if count <= MAX_OCALL_ALLOC_SIZE {
        sgx_ocfree();
    } else {
        free(tmp_buf);
    }
    result
}

pub unsafe fn pread64(fd: c_int, buf: *mut c_void, count: size_t, offset: off64_t) -> ssize_t {
    let mut result: ssize_t = 0;
    let mut error: c_int = 0;

    if buf.is_null() || sgx_is_within_enclave(buf, count) == 0 {
        set_errno(EINVAL);
        return -1;
    }
    if count >= usize::MAX {
        set_errno(EINVAL);
        return -1;
    }

    let tmp_buf = if count <= MAX_OCALL_ALLOC_SIZE {
        sgx_ocalloc(count)
    } else {
        malloc(count)
    };
    if tmp_buf.is_null() {
        set_errno(ENOMEM);
        return -1;
    }
    tmp_buf.write_bytes(0_u8, count);

    let status = u_pread64_ocall(
        &mut result as *mut ssize_t,
        &mut error as *mut c_int,
        fd,
        tmp_buf,
        count,
        offset,
    );

    if status == sgx_status_t::SGX_SUCCESS {
        if result == -1 {
            set_errno(error);
        }
    } else {
        set_errno(ESGX);
        result = -1;
    }

    if result != -1 {
        ptr::copy_nonoverlapping(
            tmp_buf as *const u8,
            buf as *mut u8,
            cmp::min(count, result.try_into().unwrap_or(0)),
        );
    }
    if count <= MAX_OCALL_ALLOC_SIZE {
        sgx_ocfree();
    } else {
        free(tmp_buf);
    }
    result
}

pub unsafe fn readv(fd: c_int, iov: *const iovec, iovcnt: c_int) -> ssize_t {
    let mut result: ssize_t = 0;
    let mut error: c_int = 0;
    let mut ptr: *mut u8 = ptr::null_mut();
    let mut total_size: usize = 0;

    if iov.is_null()
        || iovcnt <= 0
        || sgx_is_within_enclave(
            iov as *const c_void,
            iovcnt as usize * mem::size_of::<iovec>(),
        ) == 0
    {
        set_errno(EINVAL);
        return -1;
    }

    let v = slice::from_raw_parts(iov, iovcnt as usize);
    for io in v {
        if !io.iov_base.is_null()
            && io.iov_len > 0
            && sgx_is_within_enclave(io.iov_base, io.iov_len) != 0
        {
            if let Some(io_size) = total_size.checked_add(io.iov_len) {
                total_size = io_size;
            } else {
                set_errno(EINVAL);
                return -1;
            }
        } else {
            set_errno(EINVAL);
            return -1;
        }
    }

    let iobase = if total_size <= MAX_OCALL_ALLOC_SIZE {
        sgx_ocalloc(total_size)
    } else {
        malloc(total_size)
    } as *mut u8;
    if iobase.is_null() {
        set_errno(ENOMEM);
        return -1;
    }
    iobase.write_bytes(0_u8, total_size);

    let mut tmpiovec: Vec<iovec> = Vec::with_capacity(iovcnt as usize);
    ptr = iobase;
    for io in v {
        let tmpiov = iovec {
            iov_base: ptr as *mut c_void,
            iov_len: io.iov_len,
        };
        tmpiovec.push(tmpiov);
        ptr = ptr.add(io.iov_len);
    }

    let status = u_readv_ocall(
        &mut result as *mut ssize_t,
        &mut error as *mut c_int,
        fd,
        tmpiovec.as_slice().as_ptr(),
        iovcnt,
    );

    if status == sgx_status_t::SGX_SUCCESS {
        if result == -1 {
            set_errno(error);
        }
    } else {
        set_errno(ESGX);
        result = -1;
    }

    if result != -1 {
        let mut remaining_bytes: usize = result.try_into().unwrap_or(0);
        for i in 0..v.len() {
            if remaining_bytes == 0 {
                break;
            }
            // Here, we only copy the remaining bytes if there are less than the iov_len.
            // Otherwise, the default 0s are copied into the buffer and overwrite data that should not be overwritten.
            ptr::copy_nonoverlapping(
                tmpiovec[i].iov_base as *const u8,
                v[i].iov_base as *mut u8,
                cmp::min(v[i].iov_len, remaining_bytes),
            );
            remaining_bytes = remaining_bytes.saturating_sub(v[i].iov_len);
        }
    }

    if total_size <= MAX_OCALL_ALLOC_SIZE {
        sgx_ocfree();
    } else {
        free(iobase as *mut c_void);
    }
    result
}

pub unsafe fn preadv64(fd: c_int, iov: *const iovec, iovcnt: c_int, offset: off64_t) -> ssize_t {
    let mut result: ssize_t = 0;
    let mut error: c_int = 0;
    let mut ptr: *mut u8 = ptr::null_mut();
    let mut total_size: usize = 0;

    if iov.is_null()
        || iovcnt <= 0
        || sgx_is_within_enclave(
            iov as *const c_void,
            iovcnt as usize * mem::size_of::<iovec>(),
        ) == 0
    {
        set_errno(EINVAL);
        return -1;
    }

    let v = slice::from_raw_parts(iov, iovcnt as usize);
    for io in v {
        if !io.iov_base.is_null()
            && io.iov_len > 0
            && sgx_is_within_enclave(io.iov_base, io.iov_len) != 0
        {
            if let Some(io_size) = total_size.checked_add(io.iov_len) {
                total_size = io_size;
            } else {
                set_errno(EINVAL);
                return -1;
            }
        } else {
            set_errno(EINVAL);
            return -1;
        }
    }

    let iobase = if total_size <= MAX_OCALL_ALLOC_SIZE {
        sgx_ocalloc(total_size)
    } else {
        malloc(total_size)
    } as *mut u8;
    if iobase.is_null() {
        set_errno(ENOMEM);
        return -1;
    }
    iobase.write_bytes(0_u8, total_size);

    let mut tmpiovec: Vec<iovec> = Vec::with_capacity(iovcnt as usize);
    ptr = iobase;
    for io in v {
        let tmpiov = iovec {
            iov_base: ptr as *mut c_void,
            iov_len: io.iov_len,
        };
        tmpiovec.push(tmpiov);
        ptr = ptr.add(io.iov_len);
    }

    let status = u_preadv64_ocall(
        &mut result as *mut ssize_t,
        &mut error as *mut c_int,
        fd,
        tmpiovec.as_slice().as_ptr(),
        iovcnt,
        offset,
    );

    if status == sgx_status_t::SGX_SUCCESS {
        if result == -1 {
            set_errno(error);
        }
    } else {
        set_errno(ESGX);
        result = -1;
    }

    if result != -1 {
        let mut remaining_bytes: usize = result.try_into().unwrap_or(0);
        for i in 0..v.len() {
            if remaining_bytes == 0 {
                break;
            }
            ptr::copy_nonoverlapping(
                tmpiovec[i].iov_base as *const u8,
                v[i].iov_base as *mut u8,
                cmp::min(v[i].iov_len, remaining_bytes),
            );
            remaining_bytes = remaining_bytes.saturating_sub(v[i].iov_len);
        }
    }

    if total_size <= MAX_OCALL_ALLOC_SIZE {
        sgx_ocfree();
    } else {
        free(iobase as *mut c_void);
    }
    result
}

pub unsafe fn write(fd: c_int, buf: *const c_void, count: size_t) -> ssize_t {
    let mut result: ssize_t = 0;
    let mut error: c_int = 0;

    if buf.is_null() || sgx_is_within_enclave(buf, count) == 0 {
        set_errno(EINVAL);
        return -1;
    }
    if count >= usize::MAX {
        set_errno(EINVAL);
        return -1;
    }

    let tmp_buf = if count <= MAX_OCALL_ALLOC_SIZE {
        sgx_ocalloc(count)
    } else {
        malloc(count)
    };
    if tmp_buf.is_null() {
        set_errno(ENOMEM);
        return -1;
    }
    ptr::copy_nonoverlapping(buf as *const u8, tmp_buf as *mut u8, count);

    let status = u_write_ocall(
        &mut result as *mut ssize_t,
        &mut error as *mut c_int,
        fd,
        tmp_buf,
        count,
    );

    if status == sgx_status_t::SGX_SUCCESS {
        if result == -1 {
            set_errno(error);
        }
    } else {
        set_errno(ESGX);
        result = -1;
    }

    if count <= MAX_OCALL_ALLOC_SIZE {
        sgx_ocfree();
    } else {
        free(tmp_buf);
    }
    result
}

pub unsafe fn pwrite64(fd: c_int, buf: *const c_void, count: size_t, offset: off64_t) -> ssize_t {
    let mut result: ssize_t = 0;
    let mut error: c_int = 0;

    if buf.is_null() || sgx_is_within_enclave(buf, count) == 0 {
        set_errno(EINVAL);
        return -1;
    }
    if count >= usize::MAX {
        set_errno(EINVAL);
        return -1;
    }

    let tmp_buf = if count <= MAX_OCALL_ALLOC_SIZE {
        sgx_ocalloc(count)
    } else {
        malloc(count)
    };
    if tmp_buf.is_null() {
        set_errno(ENOMEM);
        return -1;
    }
    ptr::copy_nonoverlapping(buf as *const u8, tmp_buf as *mut u8, count);

    let status = u_pwrite64_ocall(
        &mut result as *mut ssize_t,
        &mut error as *mut c_int,
        fd,
        tmp_buf,
        count,
        offset,
    );

    if status == sgx_status_t::SGX_SUCCESS {
        if result == -1 {
            set_errno(error);
        }
    } else {
        set_errno(ESGX);
        result = -1;
    }

    if count <= MAX_OCALL_ALLOC_SIZE {
        sgx_ocfree();
    } else {
        free(tmp_buf);
    }
    result
}

pub unsafe fn writev(fd: c_int, iov: *const iovec, iovcnt: c_int) -> ssize_t {
    let mut result: ssize_t = 0;
    let mut error: c_int = 0;
    let mut ptr: *mut u8 = ptr::null_mut();
    let mut total_size: usize = 0;

    if iov.is_null()
        || iovcnt <= 0
        || sgx_is_within_enclave(
            iov as *const c_void,
            iovcnt as usize * mem::size_of::<iovec>(),
        ) == 0
    {
        set_errno(EINVAL);
        return -1;
    }

    let v = slice::from_raw_parts(iov, iovcnt as usize);
    for io in v {
        if !io.iov_base.is_null()
            && io.iov_len > 0
            && sgx_is_within_enclave(io.iov_base, io.iov_len) != 0
        {
            if let Some(io_size) = total_size.checked_add(io.iov_len) {
                total_size = io_size;
            } else {
                set_errno(EINVAL);
                return -1;
            }
        } else {
            set_errno(EINVAL);
            return -1;
        }
    }

    let iobase = if total_size <= MAX_OCALL_ALLOC_SIZE {
        sgx_ocalloc(total_size)
    } else {
        malloc(total_size)
    } as *mut u8;
    if iobase.is_null() {
        set_errno(ENOMEM);
        return -1;
    }
    iobase.write_bytes(0_u8, total_size);

    let mut tmpiovec: Vec<iovec> = Vec::with_capacity(iovcnt as usize);
    ptr = iobase;
    for io in v {
        let tmpiov = iovec {
            iov_base: ptr as *mut c_void,
            iov_len: io.iov_len,
        };
        ptr::copy_nonoverlapping(
            io.iov_base as *const u8,
            tmpiov.iov_base as *mut u8,
            io.iov_len as usize,
        );
        tmpiovec.push(tmpiov);
        ptr = ptr.add(io.iov_len);
    }

    let status = u_writev_ocall(
        &mut result as *mut ssize_t,
        &mut error as *mut c_int,
        fd,
        tmpiovec.as_slice().as_ptr(),
        iovcnt,
    );

    if status == sgx_status_t::SGX_SUCCESS {
        if result == -1 {
            set_errno(error);
        }
    } else {
        set_errno(ESGX);
        result = -1;
    }

    if total_size <= MAX_OCALL_ALLOC_SIZE {
        sgx_ocfree();
    } else {
        free(iobase as *mut c_void);
    }
    result
}

pub unsafe fn pwritev64(fd: c_int, iov: *const iovec, iovcnt: c_int, offset: off64_t) -> ssize_t {
    let mut result: ssize_t = 0;
    let mut error: c_int = 0;
    let mut ptr: *mut u8 = ptr::null_mut();
    let mut total_size: usize = 0;

    if iov.is_null()
        || iovcnt <= 0
        || sgx_is_within_enclave(
            iov as *const c_void,
            iovcnt as usize * mem::size_of::<iovec>(),
        ) == 0
    {
        set_errno(EINVAL);
        return -1;
    }

    let v = slice::from_raw_parts(iov, iovcnt as usize);
    for io in v {
        if !io.iov_base.is_null()
            && io.iov_len > 0
            && sgx_is_within_enclave(io.iov_base, io.iov_len) != 0
        {
            if let Some(io_size) = total_size.checked_add(io.iov_len) {
                total_size = io_size;
            } else {
                set_errno(EINVAL);
                return -1;
            }
        } else {
            set_errno(EINVAL);
            return -1;
        }
    }

    let iobase = if total_size <= MAX_OCALL_ALLOC_SIZE {
        sgx_ocalloc(total_size)
    } else {
        malloc(total_size)
    } as *mut u8;
    if iobase.is_null() {
        set_errno(ENOMEM);
        return -1;
    }
    iobase.write_bytes(0_u8, total_size);

    let mut tmpiovec: Vec<iovec> = Vec::with_capacity(iovcnt as usize);
    ptr = iobase;
    for io in v {
        let tmpiov = iovec {
            iov_base: ptr as *mut c_void,
            iov_len: io.iov_len,
        };
        ptr::copy_nonoverlapping(
            io.iov_base as *const u8,
            tmpiov.iov_base as *mut u8,
            io.iov_len as usize,
        );
        tmpiovec.push(tmpiov);
        ptr = ptr.add(io.iov_len);
    }

    let status = u_pwritev64_ocall(
        &mut result as *mut ssize_t,
        &mut error as *mut c_int,
        fd,
        tmpiovec.as_slice().as_ptr(),
        iovcnt,
        offset,
    );

    if status == sgx_status_t::SGX_SUCCESS {
        if result == -1 {
            set_errno(error);
        }
    } else {
        set_errno(ESGX);
        result = -1;
    }

    if total_size <= MAX_OCALL_ALLOC_SIZE {
        sgx_ocfree();
    } else {
        free(iobase as *mut c_void);
    }
    result
}

pub unsafe fn fcntl_arg0(fd: c_int, cmd: c_int) -> c_int {
    let mut result: c_int = 0;
    let mut error: c_int = 0;
    let status = u_fcntl_arg0_ocall(&mut result as *mut c_int, &mut error as *mut c_int, fd, cmd);

    if status == sgx_status_t::SGX_SUCCESS {
        if result == -1 {
            set_errno(error);
        }
    } else {
        set_errno(ESGX);
        result = -1;
    }
    result
}

pub unsafe fn fcntl_arg1(fd: c_int, cmd: c_int, arg: c_int) -> c_int {
    let mut result: c_int = 0;
    let mut error: c_int = 0;
    let status = u_fcntl_arg1_ocall(
        &mut result as *mut c_int,
        &mut error as *mut c_int,
        fd,
        cmd,
        arg,
    );

    if status == sgx_status_t::SGX_SUCCESS {
        if result == -1 {
            set_errno(error);
        }
    } else {
        set_errno(ESGX);
        result = -1;
    }
    result
}

pub unsafe fn ioctl_arg0(fd: c_int, request: c_int) -> c_int {
    let mut result: c_int = 0;
    let mut error: c_int = 0;
    let status = u_ioctl_arg0_ocall(
        &mut result as *mut c_int,
        &mut error as *mut c_int,
        fd,
        request,
    );

    if status == sgx_status_t::SGX_SUCCESS {
        if result == -1 {
            set_errno(error);
        }
    } else {
        set_errno(ESGX);
        result = -1;
    }
    result
}

pub unsafe fn ioctl_arg1(fd: c_int, request: c_int, arg: *mut c_int) -> c_int {
    let mut result: c_int = 0;
    let mut error: c_int = 0;
    let status = u_ioctl_arg1_ocall(
        &mut result as *mut c_int,
        &mut error as *mut c_int,
        fd,
        request,
        arg,
    );

    if status == sgx_status_t::SGX_SUCCESS {
        if result == -1 {
            set_errno(error);
        }
    } else {
        set_errno(ESGX);
        result = -1;
    }
    result
}

pub unsafe fn close(fd: c_int) -> c_int {
    let mut result: c_int = 0;
    let mut error: c_int = 0;
    let status = u_close_ocall(&mut result as *mut c_int, &mut error as *mut c_int, fd);

    if status == sgx_status_t::SGX_SUCCESS {
        if result == -1 {
            set_errno(error);
        }
    } else {
        set_errno(ESGX);
        result = -1;
    }
    result
}

// time
pub unsafe fn clock_gettime(clk_id: clockid_t, tp: *mut timespec) -> c_int {
    let mut result: c_int = 0;
    let mut error: c_int = 0;
    let status = u_clock_gettime_ocall(
        &mut result as *mut c_int,
        &mut error as *mut c_int,
        clk_id,
        tp,
    );

    if status == sgx_status_t::SGX_SUCCESS {
        if result == -1 {
            set_errno(error);
        }
    } else {
        set_errno(ESGX);
        result = -1;
    }
    result
}

pub unsafe fn socket(domain: c_int, ty: c_int, protocol: c_int) -> c_int {
    let mut result: c_int = 0;
    let mut error: c_int = 0;
    let status = u_socket_ocall(
        &mut result as *mut c_int,
        &mut error as *mut c_int,
        domain,
        ty,
        protocol,
    );

    if status == sgx_status_t::SGX_SUCCESS {
        if result == -1 {
            set_errno(error);
        }
    } else {
        set_errno(ESGX);
        result = -1;
    }
    result
}

pub unsafe fn socketpair(domain: c_int, ty: c_int, protocol: c_int, sv: *mut c_int) -> c_int {
    let mut result: c_int = 0;
    let mut error: c_int = 0;
    let status = u_socketpair_ocall(
        &mut result as *mut c_int,
        &mut error as *mut c_int,
        domain,
        ty,
        protocol,
        sv,
    );

    if status == sgx_status_t::SGX_SUCCESS {
        if result == -1 {
            set_errno(error);
        }
    } else {
        set_errno(ESGX);
        result = -1;
    }
    result
}

pub unsafe fn bind(sockfd: c_int, address: *const sockaddr, addrlen: socklen_t) -> c_int {
    let mut result: c_int = 0;
    let mut error: c_int = 0;
    let status = u_bind_ocall(
        &mut result as *mut c_int,
        &mut error as *mut c_int,
        sockfd,
        address,
        addrlen,
    );

    if status == sgx_status_t::SGX_SUCCESS {
        if result == -1 {
            set_errno(error);
        }
    } else {
        set_errno(ESGX);
        result = -1;
    }
    result
}

pub unsafe fn listen(sockfd: c_int, backlog: c_int) -> c_int {
    let mut result: c_int = 0;
    let mut error: c_int = 0;
    let status = u_listen_ocall(
        &mut result as *mut c_int,
        &mut error as *mut c_int,
        sockfd,
        backlog,
    );

    if status == sgx_status_t::SGX_SUCCESS {
        if result == -1 {
            set_errno(error);
        }
    } else {
        set_errno(ESGX);
        result = -1;
    }
    result
}

pub unsafe fn accept(sockfd: c_int, addr: *mut sockaddr, addrlen: *mut socklen_t) -> c_int {
    let mut result: c_int = 0;
    let mut error: c_int = 0;
    let len_in: socklen_t = if !addrlen.is_null() { *addrlen } else { 0 };
    let mut len_out: socklen_t = 0 as socklen_t;
    let status = u_accept_ocall(
        &mut result as *mut c_int,
        &mut error as *mut c_int,
        sockfd,
        addr,
        len_in, // This additional arg is just for EDL
        &mut len_out as *mut socklen_t,
    );

    if status == sgx_status_t::SGX_SUCCESS {
        if result == -1 {
            set_errno(error);
        }
    } else {
        set_errno(ESGX);
        result = -1;
    }

    if !addrlen.is_null() {
        *addrlen = len_out;
    }
    result
}

pub unsafe fn accept4(
    sockfd: c_int,
    addr: *mut sockaddr,
    addrlen: *mut socklen_t,
    flags: c_int,
) -> c_int {
    let mut result: c_int = 0;
    let mut error: c_int = 0;
    let len_in: socklen_t = if !addrlen.is_null() { *addrlen } else { 0 };
    let mut len_out: socklen_t = 0 as socklen_t;
    let status = u_accept4_ocall(
        &mut result as *mut c_int,
        &mut error as *mut c_int,
        sockfd,
        addr,
        len_in, // This additional arg is just for EDL
        &mut len_out as *mut socklen_t,
        flags,
    );

    if status == sgx_status_t::SGX_SUCCESS {
        if result == -1 {
            set_errno(error);
        }
    } else {
        set_errno(ESGX);
        result = -1;
    }

    if !addrlen.is_null() {
        *addrlen = len_out;
    }
    result
}

pub unsafe fn connect(sockfd: c_int, address: *const sockaddr, addrlen: socklen_t) -> c_int {
    let mut result: c_int = 0;
    let mut error: c_int = 0;
    let status = u_connect_ocall(
        &mut result as *mut c_int,
        &mut error as *mut c_int,
        sockfd,
        address,
        addrlen,
    );

    if status == sgx_status_t::SGX_SUCCESS {
        if result == -1 {
            set_errno(error);
        }
    } else {
        set_errno(ESGX);
        result = -1;
    }
    result
}

pub unsafe fn send(sockfd: c_int, buf: *const c_void, len: size_t, flags: c_int) -> ssize_t {
    let mut result: ssize_t = 0;
    let mut error: c_int = 0;

    if buf.is_null() || sgx_is_within_enclave(buf, len) == 0 {
        set_errno(EINVAL);
        return -1;
    }
    if len >= usize::MAX {
        set_errno(EINVAL);
        return -1;
    }

    let tmp_buf = if len <= MAX_OCALL_ALLOC_SIZE {
        sgx_ocalloc(len)
    } else {
        malloc(len)
    };
    if tmp_buf.is_null() {
        set_errno(ENOMEM);
        return -1;
    }
    ptr::copy_nonoverlapping(buf as *const u8, tmp_buf as *mut u8, len);

    let status = u_send_ocall(
        &mut result as *mut ssize_t,
        &mut error as *mut c_int,
        sockfd,
        tmp_buf,
        len,
        flags,
    );

    if status == sgx_status_t::SGX_SUCCESS {
        if result == -1 {
            set_errno(error);
        }
    } else {
        set_errno(ESGX);
        result = -1;
    }

    if len <= MAX_OCALL_ALLOC_SIZE {
        sgx_ocfree();
    } else {
        free(tmp_buf);
    }
    result
}

pub unsafe fn sendto(
    sockfd: c_int,
    buf: *const c_void,
    len: size_t,
    flags: c_int,
    addr: *const sockaddr,
    addrlen: socklen_t,
) -> ssize_t {
    let mut result: ssize_t = 0;
    let mut error: c_int = 0;

    if buf.is_null() || sgx_is_within_enclave(buf, len) == 0 {
        set_errno(EINVAL);
        return -1;
    }
    if len >= usize::MAX {
        set_errno(EINVAL);
        return -1;
    }

    let tmp_buf = if len <= MAX_OCALL_ALLOC_SIZE {
        sgx_ocalloc(len)
    } else {
        malloc(len)
    };
    if tmp_buf.is_null() {
        set_errno(ENOMEM);
        return -1;
    }
    ptr::copy_nonoverlapping(buf as *const u8, tmp_buf as *mut u8, len);

    let status = u_sendto_ocall(
        &mut result as *mut ssize_t,
        &mut error as *mut c_int,
        sockfd,
        tmp_buf,
        len,
        flags,
        addr,
        addrlen,
    );

    if status == sgx_status_t::SGX_SUCCESS {
        if result == -1 {
            set_errno(error);
        }
    } else {
        set_errno(ESGX);
        result = -1;
    }

    if len <= MAX_OCALL_ALLOC_SIZE {
        sgx_ocfree();
    } else {
        free(tmp_buf);
    }
    result
}

pub unsafe fn sendmsg(sockfd: c_int, msg: *const msghdr, flags: c_int) -> ssize_t {
    let mut result: ssize_t = 0;
    let mut error: c_int = 0;
    let mut total_size: usize = 0;
    let mut ptr: *mut u8 = ptr::null_mut();

    if msg.is_null() || sgx_is_within_enclave(msg as *const c_void, mem::size_of::<msghdr>()) == 0 {
        set_errno(EINVAL);
        return -1;
    }

    let mhdr = &*msg;
    let (msg_name, msg_namelen) = if !mhdr.msg_name.is_null() && mhdr.msg_namelen > 0 {
        (mhdr.msg_name, mhdr.msg_namelen)
    } else {
        (ptr::null_mut(), 0)
    };

    let iovecs = if !mhdr.msg_iov.is_null()
        && mhdr.msg_iovlen > 0
        && sgx_is_within_enclave(
            mhdr.msg_iov as *const c_void,
            mhdr.msg_iovlen * mem::size_of::<iovec>(),
        ) != 0
    {
        let iovs = slice::from_raw_parts(mhdr.msg_iov, mhdr.msg_iovlen);
        for io in iovs.iter() {
            if !io.iov_base.is_null()
                && io.iov_len > 0
                && sgx_is_within_enclave(io.iov_base, io.iov_len) != 0
            {
                if let Some(io_size) = total_size.checked_add(io.iov_len) {
                    total_size = io_size;
                } else {
                    set_errno(EINVAL);
                    return -1;
                }
            } else {
                set_errno(EINVAL);
                return -1;
            }
        }
        iovs
    } else {
        set_errno(EINVAL);
        return -1;
    };

    let (msg_control, msg_controllen) = if !mhdr.msg_control.is_null() && mhdr.msg_controllen > 0 {
        (mhdr.msg_control, mhdr.msg_controllen)
    } else {
        (ptr::null_mut(), 0)
    };

    let io_base = malloc(total_size) as *mut u8;
    if io_base.is_null() {
        set_errno(ENOMEM);
        return -1;
    }
    io_base.write_bytes(0_u8, total_size);

    ptr = io_base;
    let io_data: Vec<iovec> = iovecs
        .iter()
        .map(|v| {
            let iov = iovec {
                iov_base: ptr as *mut c_void,
                iov_len: v.iov_len,
            };
            ptr::copy_nonoverlapping(v.iov_base as *const u8, iov.iov_base as *mut u8, v.iov_len);
            ptr = ptr.add(v.iov_len);
            iov
        })
        .collect();

    let status = u_sendmsg_ocall(
        &mut result as *mut ssize_t,
        &mut error as *mut c_int,
        sockfd,
        msg_name as *const c_void,
        msg_namelen as socklen_t,
        io_data.as_ptr(),
        io_data.len(),
        msg_control as *const c_void,
        msg_controllen,
        flags,
    );

    if status == sgx_status_t::SGX_SUCCESS {
        if result == -1 {
            set_errno(error);
        }
    } else {
        set_errno(ESGX);
        result = -1;
    }

    free(io_base as *mut c_void);
    result
}

pub unsafe fn recv(sockfd: c_int, buf: *mut c_void, len: size_t, flags: c_int) -> ssize_t {
    let mut result: ssize_t = 0;
    let mut error: c_int = 0;

    if buf.is_null() || sgx_is_within_enclave(buf, len) == 0 {
        set_errno(EINVAL);
        return -1;
    }
    if len >= usize::MAX {
        set_errno(EINVAL);
        return -1;
    }

    let tmp_buf = if len <= MAX_OCALL_ALLOC_SIZE {
        sgx_ocalloc(len)
    } else {
        malloc(len)
    };
    if tmp_buf.is_null() {
        set_errno(ENOMEM);
        return -1;
    }
    tmp_buf.write_bytes(0_u8, len);

    let status = u_recv_ocall(
        &mut result as *mut ssize_t,
        &mut error as *mut c_int,
        sockfd,
        tmp_buf,
        len,
        flags,
    );

    if status == sgx_status_t::SGX_SUCCESS {
        if result == -1 {
            set_errno(error);
        }
    } else {
        set_errno(ESGX);
        result = -1;
    }

    if result != -1 {
        ptr::copy_nonoverlapping(tmp_buf as *const u8, buf as *mut u8, len);
    }
    if len <= MAX_OCALL_ALLOC_SIZE {
        sgx_ocfree();
    } else {
        free(tmp_buf);
    }
    result
}

pub unsafe fn recvfrom(
    sockfd: c_int,
    buf: *mut c_void,
    len: size_t,
    flags: c_int,
    addr: *mut sockaddr,
    addrlen: *mut socklen_t,
) -> ssize_t {
    let mut result: ssize_t = 0;
    let mut error: c_int = 0;
    let len_in: socklen_t = if !addrlen.is_null() { *addrlen } else { 0 };
    let mut len_out: socklen_t = 0 as socklen_t;

    if buf.is_null() || sgx_is_within_enclave(buf, len) == 0 {
        set_errno(EINVAL);
        return -1;
    }
    if len >= usize::MAX {
        set_errno(EINVAL);
        return -1;
    }

    let tmp_buf = if len <= MAX_OCALL_ALLOC_SIZE {
        sgx_ocalloc(len)
    } else {
        malloc(len)
    };
    if tmp_buf.is_null() {
        set_errno(ENOMEM);
        return -1;
    }
    tmp_buf.write_bytes(0_u8, len);

    let status = u_recvfrom_ocall(
        &mut result as *mut ssize_t,
        &mut error as *mut c_int,
        sockfd,
        tmp_buf,
        len,
        flags,
        addr,
        len_in, // This additional arg is just for EDL
        &mut len_out as *mut socklen_t,
    );

    if status == sgx_status_t::SGX_SUCCESS {
        if result == -1 {
            set_errno(error);
        }
    } else {
        set_errno(ESGX);
        result = -1;
    }

    if result != -1 {
        ptr::copy_nonoverlapping(tmp_buf as *const u8, buf as *mut u8, len);
    }
    if len <= MAX_OCALL_ALLOC_SIZE {
        sgx_ocfree();
    } else {
        free(tmp_buf);
    }

    if !addrlen.is_null() {
        *addrlen = len_out;
    }
    result
}

pub unsafe fn recvmsg(sockfd: c_int, msg: *mut msghdr, flags: c_int) -> ssize_t {
    let mut result: ssize_t = 0;
    let mut error: c_int = 0;
    let mut total_size: usize = 0;
    let mut ptr: *mut u8 = ptr::null_mut();

    if msg.is_null() || sgx_is_within_enclave(msg as *const c_void, mem::size_of::<msghdr>()) == 0 {
        set_errno(EINVAL);
        return -1;
    }

    let mhdr = &mut *msg;
    let (msg_name, msg_namelen) = if !mhdr.msg_name.is_null() && mhdr.msg_namelen > 0 {
        (mhdr.msg_name, mhdr.msg_namelen)
    } else {
        (ptr::null_mut(), 0)
    };

    let iovecs = if !mhdr.msg_iov.is_null()
        && mhdr.msg_iovlen > 0
        && sgx_is_within_enclave(
            mhdr.msg_iov as *const c_void,
            mhdr.msg_iovlen * mem::size_of::<iovec>(),
        ) != 0
    {
        let iovs = slice::from_raw_parts_mut(mhdr.msg_iov, mhdr.msg_iovlen);
        for io in iovs.iter() {
            if !io.iov_base.is_null()
                && io.iov_len > 0
                && sgx_is_within_enclave(io.iov_base, io.iov_len) != 0
            {
                if let Some(io_size) = total_size.checked_add(io.iov_len) {
                    total_size = io_size;
                } else {
                    set_errno(EINVAL);
                    return -1;
                }
            } else {
                set_errno(EINVAL);
                return -1;
            }
        }
        iovs
    } else {
        set_errno(EINVAL);
        return -1;
    };

    let (msg_control, msg_controllen) = if !mhdr.msg_control.is_null() && mhdr.msg_controllen > 0 {
        (mhdr.msg_control, mhdr.msg_controllen)
    } else {
        (ptr::null_mut(), 0)
    };

    let io_base = malloc(total_size) as *mut u8;
    if io_base.is_null() {
        set_errno(ENOMEM);
        return -1;
    }
    io_base.write_bytes(0_u8, total_size);

    let mut msg_namelen_out = 0_u32;
    let mut msg_controllen_out = 0_usize;
    let mut msg_flags = 0;

    ptr = io_base;
    let mut io_data: Vec<iovec> = iovecs
        .iter()
        .map(|v| {
            let iov = iovec {
                iov_base: ptr as *mut c_void,
                iov_len: v.iov_len,
            };
            ptr = ptr.add(v.iov_len);
            iov
        })
        .collect();

    let status = u_recvmsg_ocall(
        &mut result as *mut ssize_t,
        &mut error as *mut c_int,
        sockfd,
        msg_name as *mut c_void,
        msg_namelen as socklen_t,
        &mut msg_namelen_out as *mut socklen_t,
        io_data.as_mut_ptr(),
        io_data.len(),
        msg_control as *mut c_void,
        msg_controllen,
        &mut msg_controllen_out as *mut usize,
        &mut msg_flags as *mut i32,
        flags,
    );

    if status == sgx_status_t::SGX_SUCCESS {
        if result == -1 {
            set_errno(error);
        }
    } else {
        set_errno(ESGX);
        result = -1;
    }

    if msg_namelen_out > msg_namelen || msg_controllen_out > msg_controllen {
        set_errno(ESGX);
        result = -1;
    }

    if result >= 0 {
        let nrecv = result.try_into().unwrap_or(0);
        let mut remaining_bytes = cmp::min(nrecv, total_size);
        for i in 0..iovecs.len() {
            let copy_len = cmp::min(iovecs[i].iov_len, remaining_bytes);
            ptr::copy_nonoverlapping(
                io_data[i].iov_base as *const u8,
                iovecs[i].iov_base as *mut u8,
                copy_len,
            );
            remaining_bytes -= copy_len;
            if remaining_bytes == 0 {
                break;
            }
        }

        mhdr.msg_namelen = msg_namelen_out as u32;
        mhdr.msg_controllen = msg_controllen_out;
        mhdr.msg_flags = msg_flags;
    }

    free(io_base as *mut c_void);
    result
}

pub unsafe fn setsockopt(
    sockfd: c_int,
    level: c_int,
    optname: c_int,
    optval: *const c_void,
    optlen: socklen_t,
) -> c_int {
    let mut result: c_int = 0;
    let mut error: c_int = 0;
    let status = u_setsockopt_ocall(
        &mut result as *mut c_int,
        &mut error as *mut c_int,
        sockfd,
        level,
        optname,
        optval,
        optlen,
    );

    if status == sgx_status_t::SGX_SUCCESS {
        if result == -1 {
            set_errno(error);
        }
    } else {
        set_errno(ESGX);
        result = -1;
    }
    result
}

pub unsafe fn getsockopt(
    sockfd: c_int,
    level: c_int,
    optname: c_int,
    optval: *mut c_void,
    optlen: *mut socklen_t,
) -> c_int {
    let mut result: c_int = 0;
    let mut error: c_int = 0;
    let len_in: socklen_t = if !optlen.is_null() { *optlen } else { 0 };
    let mut len_out: socklen_t = 0 as socklen_t;

    let status = u_getsockopt_ocall(
        &mut result as *mut c_int,
        &mut error as *mut c_int,
        sockfd,
        level,
        optname,
        optval,
        len_in,
        &mut len_out as *mut socklen_t,
    );

    if status == sgx_status_t::SGX_SUCCESS {
        if result == -1 {
            set_errno(error);
        }
    } else {
        set_errno(ESGX);
        result = -1;
    }

    if !optlen.is_null() {
        *optlen = len_out;
    }
    result
}

pub unsafe fn getpeername(sockfd: c_int, address: *mut sockaddr, addrlen: *mut socklen_t) -> c_int {
    let mut result: c_int = 0;
    let mut error: c_int = 0;
    let len_in: socklen_t = if !addrlen.is_null() { *addrlen } else { 0 };
    let mut len_out: socklen_t = 0 as socklen_t;
    let status = u_getpeername_ocall(
        &mut result as *mut c_int,
        &mut error as *mut c_int,
        sockfd,
        address,
        len_in,
        &mut len_out as *mut socklen_t,
    );

    if status == sgx_status_t::SGX_SUCCESS {
        if result == -1 {
            set_errno(error);
        }
    } else {
        set_errno(ESGX);
        result = -1;
    }

    if !addrlen.is_null() {
        *addrlen = len_out;
    }
    result
}

pub unsafe fn getsockname(sockfd: c_int, address: *mut sockaddr, addrlen: *mut socklen_t) -> c_int {
    let mut result: c_int = 0;
    let mut error: c_int = 0;
    let len_in: socklen_t = if !addrlen.is_null() { *addrlen } else { 0 };
    let mut len_out: socklen_t = 0 as socklen_t;
    let status = u_getsockname_ocall(
        &mut result as *mut c_int,
        &mut error as *mut c_int,
        sockfd,
        address,
        len_in,
        &mut len_out as *mut socklen_t,
    );

    if status == sgx_status_t::SGX_SUCCESS {
        if result == -1 {
            set_errno(error);
        }
    } else {
        set_errno(ESGX);
        result = -1;
    }

    if !addrlen.is_null() {
        *addrlen = len_out;
    }
    result
}

pub unsafe fn shutdown(sockfd: c_int, how: c_int) -> c_int {
    let mut result: c_int = 0;
    let mut error: c_int = 0;
    let status = u_shutdown_ocall(
        &mut result as *mut c_int,
        &mut error as *mut c_int,
        sockfd,
        how,
    );

    if status == sgx_status_t::SGX_SUCCESS {
        if result == -1 {
            set_errno(error);
        }
    } else {
        set_errno(ESGX);
        result = -1;
    }
    result
}

pub unsafe fn getaddrinfo(
    node: *const c_char,
    service: *const c_char,
    hints: *const addrinfo,
    res: *mut *mut addrinfo,
) -> c_int {
    let mut result: c_int = 0;
    let mut error: c_int = 0;
    let mut ret_res: *mut addrinfo = ptr::null_mut();
    let hint: addrinfo = if !hints.is_null() {
        addrinfo {
            ai_flags: (*hints).ai_flags,
            ai_family: (*hints).ai_family,
            ai_socktype: (*hints).ai_socktype,
            ai_protocol: (*hints).ai_protocol,
            ai_addrlen: 0,
            ai_addr: ptr::null_mut(),
            ai_canonname: ptr::null_mut(),
            ai_next: ptr::null_mut(),
        }
    } else {
        addrinfo {
            ai_flags: AI_V4MAPPED | AI_ADDRCONFIG,
            ai_family: AF_UNSPEC,
            ai_socktype: 0,
            ai_protocol: 0,
            ai_addrlen: 0,
            ai_addr: ptr::null_mut(),
            ai_canonname: ptr::null_mut(),
            ai_next: ptr::null_mut(),
        }
    };

    let status = u_getaddrinfo_ocall(
        &mut result as *mut c_int,
        &mut error as *mut c_int,
        node,
        service,
        &hint as *const addrinfo,
        &mut ret_res as *mut *mut addrinfo,
    );
    if status == sgx_status_t::SGX_SUCCESS {
        if result == 0 {
            *res = ptr::null_mut();
            let mut cur_ptr: *mut addrinfo = ret_res;
            let mut addrinfo_vec: Vec<Box<addrinfo>> = Vec::new();
            while cur_ptr != ptr::null_mut() {
                if sgx_is_outside_enclave(cur_ptr as *const c_void, mem::size_of::<addrinfo>()) == 0
                {
                    result = EAI_SYSTEM;
                    break;
                }
                let cur: &addrinfo = &*cur_ptr;
                let mut info = addrinfo {
                    ai_flags: cur.ai_flags,
                    ai_family: cur.ai_family,
                    ai_socktype: cur.ai_socktype,
                    ai_protocol: cur.ai_protocol,
                    ai_addrlen: 0,
                    ai_addr: ptr::null_mut(),
                    ai_canonname: ptr::null_mut(),
                    ai_next: ptr::null_mut(),
                };

                if !cur.ai_addr.is_null() && cur.ai_addrlen > 0 {
                    if sgx_is_outside_enclave(cur.ai_addr as *const c_void, cur.ai_addrlen as usize)
                        == 0
                    {
                        result = EAI_SYSTEM;
                        break;
                    }
                    let mut addr_vec = vec![0u8; cur.ai_addrlen as usize];
                    let addr_slice: &[u8] =
                        slice::from_raw_parts(cur.ai_addr as *const u8, cur.ai_addrlen as usize);
                    addr_vec.copy_from_slice(addr_slice);
                    addr_vec.shrink_to_fit();
                    info.ai_addrlen = cur.ai_addrlen;
                    info.ai_addr = addr_vec.as_mut_ptr() as *mut sockaddr;
                    mem::forget(addr_vec);
                }

                if !cur.ai_canonname.is_null() {
                    let len: usize = strlen(cur.ai_canonname) + 1;
                    if sgx_is_outside_enclave(cur.ai_canonname as *const c_void, len) == 0 {
                        result = EAI_SYSTEM;
                        break;
                    }
                    let mut name_vec = vec![0u8; len];
                    let name_slice: &[u8] =
                        slice::from_raw_parts(cur.ai_canonname as *const u8, len);
                    name_vec.copy_from_slice(name_slice);
                    name_vec.shrink_to_fit();
                    info.ai_canonname = name_vec.as_mut_ptr() as *mut c_char;
                    mem::forget(name_vec);
                }

                addrinfo_vec.push(Box::new(info));
                cur_ptr = cur.ai_next;
            }

            if addrinfo_vec.len() > 0 {
                if result == 0 {
                    for i in 0..addrinfo_vec.len() - 1 {
                        addrinfo_vec[i].ai_next = addrinfo_vec[i + 1].as_mut() as *mut addrinfo;
                    }
                    let res_ptr = addrinfo_vec[0].as_mut() as *mut addrinfo;

                    for info in addrinfo_vec {
                        let _ = Box::into_raw(info);
                    }
                    *res = res_ptr;
                } else {
                    set_errno(ESGX);
                    for addrinfo in addrinfo_vec {
                        if !addrinfo.ai_addr.is_null() {
                            let len: usize = addrinfo.ai_addrlen as usize;
                            let addr_vec =
                                Vec::from_raw_parts(addrinfo.ai_addr as *mut u8, len, len);
                            drop(addr_vec);
                        }
                        if !addrinfo.ai_canonname.is_null() {
                            let len: usize = strlen(addrinfo.ai_canonname) + 1;
                            let name_vec =
                                Vec::from_raw_parts(addrinfo.ai_addr as *mut u8, len, len);
                            drop(name_vec);
                        }
                    }
                }
            }
            if !ret_res.is_null() {
                let _ = u_freeaddrinfo_ocall(ret_res);
            }
        } else if result == EAI_SYSTEM {
            *res = ptr::null_mut();
            set_errno(error);
        }
    } else {
        *res = ptr::null_mut();
        set_errno(ESGX);
        result = EAI_SYSTEM;
    }
    result
}

pub unsafe fn freeaddrinfo(res: *mut addrinfo) {
    let mut cur_ptr: *mut addrinfo = res;
    let mut addrinfo_vec: Vec<Box<addrinfo>> = Vec::new();
    while cur_ptr != ptr::null_mut() {
        let cur: &addrinfo = &*cur_ptr;
        if !cur.ai_addr.is_null() && cur.ai_addrlen > 0 {
            let addr_vec = Vec::from_raw_parts(
                cur.ai_addr as *mut u8,
                cur.ai_addrlen as usize,
                cur.ai_addrlen as usize,
            );
            drop(addr_vec);
        }
        if !cur.ai_canonname.is_null() {
            let len: usize = strlen(cur.ai_canonname) + 1;
            let name_vec = Vec::from_raw_parts(cur.ai_canonname as *mut u8, len, len);
            drop(name_vec);
        }
        addrinfo_vec.push(Box::from_raw(cur_ptr));
        cur_ptr = cur.ai_next;
    }
    drop(addrinfo_vec);
}

pub unsafe fn gai_strerror(errcode: c_int) -> *const c_char {
    let mut result: *const c_char = ptr::null();
    let status = u_gai_strerror_ocall(&mut result as *mut *const c_char, errcode);
    if status != sgx_status_t::SGX_SUCCESS {
        set_errno(ESGX);
    }
    result
}

pub unsafe fn poll(fds: *mut pollfd, nfds: nfds_t, timeout: c_int) -> c_int {
    let mut result: c_int = 0;
    let mut error: c_int = 0;
    let status = u_poll_ocall(
        &mut result as *mut c_int,
        &mut error as *mut c_int,
        fds,
        nfds,
        timeout,
    );

    if status == sgx_status_t::SGX_SUCCESS {
        if result == -1 {
            set_errno(error);
        }
    } else {
        set_errno(ESGX);
        result = -1;
    }
    result
}

pub unsafe fn epoll_create1(flags: c_int) -> c_int {
    let mut result: c_int = 0;
    let mut error: c_int = 0;
    let status = u_epoll_create1_ocall(&mut result as *mut c_int, &mut error as *mut c_int, flags);
    if status == sgx_status_t::SGX_SUCCESS {
        if result == -1 {
            set_errno(error);
        }
    } else {
        set_errno(ESGX);
        result = -1;
    }
    result
}

pub unsafe fn epoll_ctl(epfd: c_int, op: c_int, fd: c_int, event: *mut epoll_event) -> c_int {
    let mut result: c_int = 0;
    let mut error: c_int = 0;
    let status = u_epoll_ctl_ocall(
        &mut result as *mut c_int,
        &mut error as *mut c_int,
        epfd,
        op,
        fd,
        event,
    );
    if status == sgx_status_t::SGX_SUCCESS {
        if result == -1 {
            set_errno(error);
        }
    } else {
        set_errno(ESGX);
        result = -1;
    }
    result
}

pub unsafe fn epoll_wait(
    epfd: c_int,
    events: *mut epoll_event,
    maxevents: c_int,
    timeout: c_int,
) -> c_int {
    let mut result: c_int = 0;
    let mut error: c_int = 0;
    let status = u_epoll_wait_ocall(
        &mut result as *mut c_int,
        &mut error as *mut c_int,
        epfd,
        events,
        maxevents,
        timeout,
    );
    if status == sgx_status_t::SGX_SUCCESS {
        if result == -1 {
            set_errno(error);
        }
    } else {
        set_errno(ESGX);
        result = -1;
    }
    result
}

pub unsafe fn sysconf(name: c_int) -> c_long {
    let mut result: c_long = 0;
    let mut error: c_int = 0;
    let status = u_sysconf_ocall(&mut result as *mut c_long, &mut error as *mut c_int, name);
    if status == sgx_status_t::SGX_SUCCESS {
        if result == -1 {
            set_errno(error);
        }
    } else {
        set_errno(ESGX);
        result = -1;
    }
    result
}

pub unsafe fn prctl(
    option: c_int,
    arg2: c_ulong,
    arg3: c_ulong,
    arg4: c_ulong,
    arg5: c_ulong,
) -> c_int {
    let mut result: c_int = 0;
    let mut error: c_int = 0;
    let status = u_prctl_ocall(
        &mut result as *mut c_int,
        &mut error as *mut c_int,
        option,
        arg2,
        arg3,
        arg4,
        arg5,
    );
    if status == sgx_status_t::SGX_SUCCESS {
        if result == -1 {
            set_errno(error);
        }
    } else {
        set_errno(ESGX);
        result = -1;
    }
    result
}

pub unsafe fn sched_setaffinity(pid: pid_t, cpusetsize: size_t, mask: *const cpu_set_t) -> c_int {
    let mut result: c_int = 0;
    let mut error: c_int = 0;
    let status = u_sched_setaffinity_ocall(
        &mut result as *mut c_int,
        &mut error as *mut c_int,
        pid,
        cpusetsize,
        mask,
    );
    if status == sgx_status_t::SGX_SUCCESS {
        if result == -1 {
            set_errno(error);
        }
    } else {
        set_errno(ESGX);
        result = -1;
    }
    result
}

pub unsafe fn sched_getaffinity(pid: pid_t, cpusetsize: size_t, mask: *mut cpu_set_t) -> c_int {
    let mut result: c_int = 0;
    let mut error: c_int = 0;
    let status = u_sched_getaffinity_ocall(
        &mut result as *mut c_int,
        &mut error as *mut c_int,
        pid,
        cpusetsize,
        mask,
    );
    if status == sgx_status_t::SGX_SUCCESS {
        if result == -1 {
            set_errno(error);
        }
    } else {
        set_errno(ESGX);
        result = -1;
    }
    result
}

pub unsafe fn pipe(fds: *mut c_int) -> c_int {
    let mut result: c_int = 0;
    let mut error: c_int = 0;
    let status = u_pipe_ocall(&mut result as *mut c_int, &mut error as *mut c_int, fds);
    if status == sgx_status_t::SGX_SUCCESS {
        if result == -1 {
            set_errno(error);
        }
    } else {
        set_errno(ESGX);
        result = -1;
    }
    result
}

pub unsafe fn pipe2(fds: *mut c_int, flags: c_int) -> c_int {
    let mut result: c_int = 0;
    let mut error: c_int = 0;
    let status = u_pipe2_ocall(
        &mut result as *mut c_int,
        &mut error as *mut c_int,
        fds,
        flags,
    );
    if status == sgx_status_t::SGX_SUCCESS {
        if result == -1 {
            set_errno(error);
        }
    } else {
        set_errno(ESGX);
        result = -1;
    }
    result
}

pub unsafe fn sched_yield() -> c_int {
    let mut result: c_int = 0;
    let mut error: c_int = 0;
    let status = u_sched_yield_ocall(&mut result as *mut c_int, &mut error as *mut c_int);
    if status == sgx_status_t::SGX_SUCCESS {
        if result == -1 {
            set_errno(error);
        }
    } else {
        set_errno(ESGX);
        result = -1;
    }
    result
}

pub unsafe fn nanosleep(rqtp: *const timespec, rmtp: *mut timespec) -> c_int {
    let mut result: c_int = 0;
    let mut error: c_int = 0;
    let status = u_nanosleep_ocall(
        &mut result as *mut c_int,
        &mut error as *mut c_int,
        rqtp,
        rmtp,
    );
    if status == sgx_status_t::SGX_SUCCESS {
        if result == -1 {
            set_errno(error);
        }
    } else {
        set_errno(ESGX);
        result = -1;
    }
    result
}

pub unsafe fn sigaction(
    signum: c_int,
    act: *const sigaction,
    oldact: *mut sigaction,
    enclave_id: uint64_t,
) -> c_int {
    let mut result: c_int = 0;
    let mut error: c_int = 0;
    let status = u_sigaction_ocall(
        &mut result as *mut c_int,
        &mut error as *mut c_int,
        signum,
        act,
        oldact,
        enclave_id,
    );
    if status == sgx_status_t::SGX_SUCCESS {
        if result == -1 {
            set_errno(error);
        }
    } else {
        set_errno(ESGX);
        result = -1;
    }
    result
}

pub unsafe fn sigprocmask(signum: c_int, set: *const sigset_t, oldset: *mut sigset_t) -> c_int {
    let mut result: c_int = 0;
    let mut error: c_int = 0;
    let status = u_sigprocmask_ocall(
        &mut result as *mut c_int,
        &mut error as *mut c_int,
        signum,
        set,
        oldset,
    );
    if status == sgx_status_t::SGX_SUCCESS {
        if result == -1 {
            set_errno(error);
        }
    } else {
        set_errno(ESGX);
        result = -1;
    }
    result
}

pub unsafe fn raise(signum: c_int) -> c_int {
    let mut result: c_int = -1;
    let status = u_raise_ocall(&mut result as *mut c_int, signum);
    if status != sgx_status_t::SGX_SUCCESS {
        result = -1;
    }
    result
}

pub unsafe fn pthread_sigmask(signum: c_int, set: &sigset_t, oldset: &mut sigset_t) -> c_int {
    sigprocmask(signum, set, oldset)
}

pub unsafe fn getpid() -> pid_t {
    let mut result = -1;
    let status = u_getpid_ocall(&mut result as *mut pid_t);
    if status != sgx_status_t::SGX_SUCCESS {
        result = -1;
    }
    result
}
