/*
 * Copyright (C) 2011 The Android Open Source Project
 *
 * Licensed 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.
 */

package libcore.io;

import android.system.ErrnoException;
import android.system.Int32Ref;
import android.system.Int64Ref;
import java.io.FileDescriptor;
import java.nio.ByteBuffer;
import java.nio.NioUtils;

/*-[
#include "BufferUtils.h"
#include "Portability.h"
#include "TempFailureRetry.h"
#include "java/io/File.h"
#include "libcore/io/StructLinger.h"
#include "libcore/io/StructPollfd.h"
#include "libcore/io/StructStat.h"
#include "libcore/io/StructStatVfs.h"
#include "libcore/io/StructTimeval.h"
#include "libcore/io/StructUtsname.h"

#include <fcntl.h>
#include <poll.h>
#include <net/if.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/uio.h>
#include <sys/utsname.h>
#include <termios.h>
#include <unistd.h>
]-*/

public final class Posix implements Os {
  Posix() { }

  /*-[
  #pragma clang diagnostic push
  #pragma clang diagnostic ignored "-Wunused-function"
  static LibcoreIoStructStat *makeStructStat(const struct stat *sb) {
      return AUTORELEASE([[LibcoreIoStructStat alloc]
                          initWithLong:sb->st_dev
                              withLong:sb->st_ino
                               withInt:sb->st_mode
                              withLong:sb->st_nlink
                               withInt:sb->st_uid
                               withInt:sb->st_gid
                              withLong:sb->st_rdev
                              withLong:sb->st_size
                              withLong:sb->st_atime
                              withLong:sb->st_mtime
                              withLong:sb->st_ctime
                              withLong:sb->st_blksize
                              withLong:sb->st_blocks]);
  }

  static LibcoreIoStructStatVfs *makeStructStatVfs(const struct statvfs *sb) {
    return AUTORELEASE([[LibcoreIoStructStatVfs alloc]
                        initWithLong:(long long)sb->f_bsize
                            withLong:(long long)sb->f_frsize
                            withLong:(long long)sb->f_blocks
                            withLong:(long long)sb->f_bfree
                            withLong:(long long)sb->f_bavail
                            withLong:(long long)sb->f_files
                            withLong:(long long)sb->f_ffree
                            withLong:(long long)sb->f_favail
                            withLong:(long long)sb->f_fsid
                            withLong:(long long)sb->f_flag
                            withLong:255LL]);  // __DARWIN_MAXNAMLEN
  }

  static LibcoreIoStructUtsname *makeStructUtsname(const struct utsname *buf) {
    NSString *sysname = [NSString stringWithUTF8String:buf->sysname];
    NSString *nodename = [NSString stringWithUTF8String:buf->nodename];
    NSString *release = [NSString stringWithUTF8String:buf->release];
    NSString *version = [NSString stringWithUTF8String:buf->version];
    NSString *machine = [NSString stringWithUTF8String:buf->machine];
    return AUTORELEASE([[LibcoreIoStructUtsname alloc] initWithNSString:sysname
                                                           withNSString:nodename
                                                           withNSString:release
                                                           withNSString:version
                                                           withNSString:machine]);
  }

  static id doStat(NSString *path, BOOL isLstat) {
    if (!path) {
      return nil;
    }
    const char* cpath = absolutePath(path);
    struct stat sb;
    int rc = isLstat ? TEMP_FAILURE_RETRY(lstat(cpath, &sb))
                     : TEMP_FAILURE_RETRY(stat(cpath, &sb));
    if (rc == -1) {
      LibcoreIoPosix_throwErrnoExceptionWithNSString_withInt_(
          (isLstat ? @"lstat" : @"stat"), errno);
    }
    return makeStructStat(&sb);
  }

  BOOL setBlocking(int fd, bool blocking) {
    int flags = fcntl(fd, F_GETFL);
    if (flags == -1) {
      return NO;
    }

    if (!blocking) {
        flags |= O_NONBLOCK;
    } else {
        flags &= ~O_NONBLOCK;
    }

    int rc = fcntl(fd, F_SETFL, flags);
    return (rc != -1);
  }

  const char *absolutePath(NSString *path) {
    if ([path length] == 0) {
      return "";
    }
    if ([path characterAtIndex:0] != '/') {
      JavaIoFile *f = [[JavaIoFile alloc] initWithNSString:path];
      path = [f getAbsolutePath];
      RELEASE_(f);
    }
    return [path fileSystemRepresentation];
  }
  #pragma clang diagnostic pop
  ]-*/

  static void throwErrnoException(String message, int errorCode) throws ErrnoException {
    throw new ErrnoException(message, errorCode);
  }

  static native int throwIfMinusOne(String name, int resultCode) throws ErrnoException /*-[
    if (resultCode == -1) {
      LibcoreIoPosix_throwErrnoExceptionWithNSString_withInt_(name, errno);
    }
    return resultCode;
  ]-*/;

  public native boolean access(String path, int mode) throws ErrnoException /*-[
    if (!path) {
      return NO;
    }
    const char* cpath = absolutePath(path);
    int rc = TEMP_FAILURE_RETRY(access(cpath, mode));
    if (rc == -1) {
      LibcoreIoPosix_throwErrnoExceptionWithNSString_withInt_(@"access", errno);
    }
    return (rc == 0);
  ]-*/;

  /**
   * Like access(), but returns false on error instead of throwing an exception.
   */
  public native boolean canAccess(String path, int mode) /*-[
    if (!path) {
      return NO;
    }
    const char* cpath = absolutePath(path);
    return (TEMP_FAILURE_RETRY(access(cpath, mode)) == 0);
  ]-*/;

  public native void chmod(String path, int mode) throws ErrnoException /*-[
    if (path) {
      const char* cpath = absolutePath(path);
      int rc = TEMP_FAILURE_RETRY(chmod(cpath, mode));
      LibcoreIoPosix_throwIfMinusOneWithNSString_withInt_(@"chmod", rc);
    }
  ]-*/;

  public native void chown(String path, int uid, int gid) throws ErrnoException /*-[
    if (path) {
      const char* cpath = absolutePath(path);
      int rc = TEMP_FAILURE_RETRY(chown(cpath, uid, gid));
      LibcoreIoPosix_throwIfMinusOneWithNSString_withInt_(@"chown", rc);
    }
  ]-*/;

  public native void close(FileDescriptor javaFd) throws ErrnoException /*-[
    // Get the FileDescriptor's 'fd' field and clear it.
    // We need to do this before we can throw an IOException.
    int fd = [javaFd getInt$];
    [javaFd setInt$WithInt:-1];
    LibcoreIoPosix_throwIfMinusOneWithNSString_withInt_(@"close", close(fd));
  ]-*/;

  public native FileDescriptor dup(FileDescriptor oldFd) throws ErrnoException /*-[
    int nativeFd = TEMP_FAILURE_RETRY(dup([oldFd getInt$]));
    LibcoreIoPosix_throwIfMinusOneWithNSString_withInt_(@"dup", nativeFd);
    JavaIoFileDescriptor *newFd = AUTORELEASE([[JavaIoFileDescriptor alloc] init]);
    [newFd setInt$WithInt:nativeFd];
    return newFd;
  ]-*/;

  public native FileDescriptor dup2(FileDescriptor oldFd, int newNativeFd)
      throws ErrnoException /*-[
    int nativeFd = TEMP_FAILURE_RETRY(dup2([oldFd getInt$], newNativeFd));
    LibcoreIoPosix_throwIfMinusOneWithNSString_withInt_(@"dup2", nativeFd);
    JavaIoFileDescriptor *newFd = AUTORELEASE([[JavaIoFileDescriptor alloc] init]);
    [newFd setInt$WithInt:nativeFd];
    return newFd;
  ]-*/;

  public native void fchmod(FileDescriptor fd, int mode) throws ErrnoException /*-[
    int rc = TEMP_FAILURE_RETRY(fchmod([fd getInt$], mode));
    LibcoreIoPosix_throwIfMinusOneWithNSString_withInt_(@"fchmod", rc);
  ]-*/;

  public native void fchown(FileDescriptor fd, int uid, int gid) throws ErrnoException /*-[
    int rc = TEMP_FAILURE_RETRY(fchown([fd getInt$], uid, gid));
    LibcoreIoPosix_throwIfMinusOneWithNSString_withInt_(@"fchown", rc);
  ]-*/;

  public native int fcntlFlock(FileDescriptor fd, int cmd, StructFlock arg)
      throws ErrnoException /*-[
    struct flock64 lock;
    memset(&lock, 0, sizeof(lock));
    lock.l_type = arg->l_type_;
    lock.l_whence = arg->l_whence_;
    lock.l_start = arg->l_start_;
    lock.l_len = arg->l_len_;
    lock.l_pid = arg->l_pid_;

    int rc = TEMP_FAILURE_RETRY(fcntl((int) [fd getInt$], cmd, &lock));
    if (rc == -1) {
      LibcoreIoPosix_throwErrnoExceptionWithNSString_withInt_(@"fcntl", errno);
    }
    arg->l_type_ = lock.l_type;
    arg->l_whence_ = lock.l_whence;
    arg->l_start_ = lock.l_start;
    arg->l_len_ = lock.l_len;
    arg->l_pid_ = lock.l_pid;
    return rc;
  ]-*/;

  public native int fcntlLong(FileDescriptor fd, int cmd, long arg) throws ErrnoException /*-[
    int rc = TEMP_FAILURE_RETRY(fcntl([fd getInt$], cmd, arg));
    return LibcoreIoPosix_throwIfMinusOneWithNSString_withInt_(@"fcntl", rc);
  ]-*/;

  public native int fcntlVoid(FileDescriptor fd, int cmd) throws ErrnoException /*-[
    int rc = TEMP_FAILURE_RETRY(fcntl([fd getInt$], cmd));
    return LibcoreIoPosix_throwIfMinusOneWithNSString_withInt_(@"fcntl", rc);
  ]-*/;

  public native void fdatasync(FileDescriptor fd) throws ErrnoException /*-[
    int rc = TEMP_FAILURE_RETRY(fdatasync([fd getInt$]));
    LibcoreIoPosix_throwIfMinusOneWithNSString_withInt_(@"fdatasync", rc);
  ]-*/;

  public native StructStat fstat(FileDescriptor fd) throws ErrnoException /*-[
    struct stat sb;
    int rc = TEMP_FAILURE_RETRY(fstat([fd getInt$], &sb));
    if (rc == -1) {
        LibcoreIoPosix_throwErrnoExceptionWithNSString_withInt_(@"fstat", rc);
    }
    return makeStructStat(&sb);
  ]-*/;

  public native void fsync(FileDescriptor fd) throws ErrnoException /*-[
    int rc = TEMP_FAILURE_RETRY(fsync([fd getInt$]));
    LibcoreIoPosix_throwIfMinusOneWithNSString_withInt_(@"fsync", rc);
  ]-*/;

  public native void ftruncate(FileDescriptor fd, long length) throws ErrnoException /*-[
    int rc = TEMP_FAILURE_RETRY(ftruncate([fd getInt$], (off_t) length));
    LibcoreIoPosix_throwIfMinusOneWithNSString_withInt_(@"ftruncate", rc);
  ]-*/;

  public native String if_indextoname(int index) /*-[
    char buf[IF_NAMESIZE];
    char *name = if_indextoname(index, buf);
    // if_indextoname(3) returns NULL on failure. There's no useful information in errno,
    // so we don't bother throwing. Callers can null-check.
    return name ? [NSString stringWithUTF8String:name] : nil;
  ]-*/;

  public native int ioctlInt(FileDescriptor fd, int cmd, Int32Ref javaArg)
      throws ErrnoException /*-[
    int arg = javaArg->value_;
    int rc = TEMP_FAILURE_RETRY(ioctl([fd getInt$], cmd, &arg));
    if (rc == -1) {
      LibcoreIoPosix_throwErrnoExceptionWithNSString_withInt_(@"ioctl", rc);
    }
    javaArg->value_ = arg;
    return rc;
  ]-*/;

  public native boolean isatty(FileDescriptor fd) /*-[
    return TEMP_FAILURE_RETRY(isatty([fd getInt$])) == 1;
  ]-*/;

  public native long lseek(FileDescriptor fd, long offset, int whence) throws ErrnoException /*-[
    off_t rc = TEMP_FAILURE_RETRY(lseek([fd getInt$], offset, whence));
    if (rc == -1) {
      LibcoreIoPosix_throwErrnoExceptionWithNSString_withInt_(@"lseek", errno);
    }
    return rc;
  ]-*/;

  public native StructStat lstat(String path) throws ErrnoException /*-[
    return doStat(path, YES);
  ]-*/;

  public native void listen(FileDescriptor fd, int backlog) throws ErrnoException /*-[
    int rc = TEMP_FAILURE_RETRY(listen([fd getInt$], backlog));
    LibcoreIoPosix_throwIfMinusOneWithNSString_withInt_(@"listen", rc);
  ]-*/;

  public native void mincore(long address, long byteCount, byte[] vector) throws ErrnoException /*-[
    int rc = TEMP_FAILURE_RETRY(mincore(
        (caddr_t) address, (size_t) byteCount, (char *)vector->buffer_));
    LibcoreIoPosix_throwIfMinusOneWithNSString_withInt_(@"mincore", rc);
  ]-*/;

  public native void mkdir(String path, int mode) throws ErrnoException /*-[
    if (path) {
      const char* cpath = absolutePath(path);
      int rc = TEMP_FAILURE_RETRY(mkdir(cpath, mode));
      LibcoreIoPosix_throwIfMinusOneWithNSString_withInt_(@"mkdir", rc);
    }
  ]-*/;

  public native void mlock(long address, long byteCount) throws ErrnoException /*-[
    int rc = TEMP_FAILURE_RETRY(mlock((void *) address, (size_t) byteCount));
    LibcoreIoPosix_throwIfMinusOneWithNSString_withInt_(@"mlock", rc);
  ]-*/;

  public native long mmap(long address, long byteCount, int prot, int flags,
      FileDescriptor fd, long offset) throws ErrnoException /*-[
    void* ptr = mmap((void *) address, (size_t) byteCount, prot, flags, [fd getInt$], offset);
    if (ptr == MAP_FAILED) {
      LibcoreIoPosix_throwErrnoExceptionWithNSString_withInt_(@"mmap", errno);
    }
    return (long long) ptr;
  ]-*/;

  public native void msync(long address, long byteCount, int flags) throws ErrnoException /*-[
    int rc = TEMP_FAILURE_RETRY(msync((void *) address, (size_t) byteCount, flags));
    LibcoreIoPosix_throwIfMinusOneWithNSString_withInt_(@"msync", rc);
  ]-*/;

  public native void munlock(long address, long byteCount) throws ErrnoException /*-[
    int rc = TEMP_FAILURE_RETRY(munlock((void *) address, (size_t) byteCount));
    LibcoreIoPosix_throwIfMinusOneWithNSString_withInt_(@"munlock", rc);
  ]-*/;

  public native void munmap(long address, long byteCount) throws ErrnoException /*-[
    int rc = TEMP_FAILURE_RETRY(munmap((void *) address, (size_t) byteCount));
    LibcoreIoPosix_throwIfMinusOneWithNSString_withInt_(@"munmap", rc);
  ]-*/;

  public native FileDescriptor open(String path, int flags, int mode) throws ErrnoException /*-[
    if (!path) {
      return nil;
    }
    const char* cpath = absolutePath(path);
    int nativeFd = TEMP_FAILURE_RETRY(open(cpath, flags, mode));
    LibcoreIoPosix_throwIfMinusOneWithNSString_withInt_(@"open", nativeFd);
    JavaIoFileDescriptor *newFd = AUTORELEASE([[JavaIoFileDescriptor alloc] init]);
    [newFd setInt$WithInt:nativeFd];
    return newFd;
  ]-*/;

  public native FileDescriptor[] pipe() throws ErrnoException /*-[
    int fds[2];
    LibcoreIoPosix_throwIfMinusOneWithNSString_withInt_(@"pipe", TEMP_FAILURE_RETRY(pipe(&fds[0])));
    IOSObjectArray *result = [IOSObjectArray arrayWithLength:2 type:JavaIoFileDescriptor_class_()];
    for (int i = 0; i < 2; ++i) {
      JavaIoFileDescriptor *fd = AUTORELEASE([[JavaIoFileDescriptor alloc] init]);
      [fd setInt$WithInt:fds[i]];
      [result replaceObjectAtIndex:i withObject:fd];
    }
    return result;
  ]-*/;

  public native int poll(StructPollfd[] fds, int timeoutMs) throws ErrnoException /*-[
    jint count = fds->size_;
    struct pollfd *pollFds = (struct pollfd *)calloc(count, sizeof(struct pollfd));
    for (jint i = 0; i < count; i++) {
      LibcoreIoStructPollfd *javaPollFd = [fds objectAtIndex:i];
      if (javaPollFd) {
        pollFds[i].fd = [javaPollFd->fd_ getInt$];
        pollFds[i].events = javaPollFd->events_;
      }
    }
    int rc = poll(pollFds, count, timeoutMs);
    if (rc == -1) {
      free(pollFds);
      LibcoreIoPosix_throwErrnoExceptionWithNSString_withInt_(@"poll", rc);
    }
    for (jint i = 0; i < count; i++) {
      LibcoreIoStructPollfd *javaPollFd = [fds objectAtIndex:i];
      if (javaPollFd) {
        javaPollFd->revents_ = pollFds[i].revents;
      }
    }
    free(pollFds);
    return rc;
  ]-*/;

  public int pread(FileDescriptor fd, ByteBuffer buffer, long offset) throws ErrnoException {
    if (buffer.isDirect()) {
      return preadBytes(fd, buffer, buffer.position(), buffer.remaining(), offset);
    } else {
      return preadBytes(fd, NioUtils.unsafeArray(buffer),
          NioUtils.unsafeArrayOffset(buffer) + buffer.position(), buffer.remaining(), offset);
    }
  }

  public int pread(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount, long offset)
      throws ErrnoException {
    return preadBytes(fd, bytes, byteOffset, byteCount, offset);
  }

  private native int preadBytes(FileDescriptor fd, Object buffer, int bufferOffset, int byteCount,
      long offset) throws ErrnoException /*-[
    char *bytes = BytesRW(buffer);
    if (!bytes) {
        return -1;
    }
    int rc =
      TEMP_FAILURE_RETRY(pread64([fd getInt$], bytes + bufferOffset, byteCount, offset));
    return LibcoreIoPosix_throwIfMinusOneWithNSString_withInt_(@"pread", rc);
  ]-*/;

  public int pwrite(FileDescriptor fd, ByteBuffer buffer, long offset)
      throws ErrnoException {
    if (buffer.isDirect()) {
      return pwriteBytes(fd, buffer, buffer.position(), buffer.remaining(),
          offset);
    } else {
      return pwriteBytes(fd, NioUtils.unsafeArray(buffer),
          NioUtils.unsafeArrayOffset(buffer) + buffer.position(),
          buffer.remaining(), offset);
    }
  }

  public int pwrite(FileDescriptor fd, byte[] bytes, int byteOffset,
      int byteCount, long offset) throws ErrnoException {
    return pwriteBytes(fd, bytes, byteOffset, byteCount, offset);
  }

  private native int pwriteBytes(FileDescriptor fd, Object buffer,
      int bufferOffset, int byteCount, long offset) throws ErrnoException /*-[
    const char *bytes = BytesRO(buffer);
    if (!bytes) {
        return -1;
    }
    if (byteCount + offset < 0) {  // If buffer overflow.
      LibcoreIoPosix_throwErrnoExceptionWithNSString_withInt_(@"pwrite", ERANGE);
    }
    int rc =
      TEMP_FAILURE_RETRY(pwrite64([fd getInt$], bytes + bufferOffset, byteCount, offset));
    return LibcoreIoPosix_throwIfMinusOneWithNSString_withInt_(@"pwrite", rc);
  ]-*/;

  public int read(FileDescriptor fd, ByteBuffer buffer) throws ErrnoException {
    if (buffer.isDirect()) {
      return readBytes(fd, buffer, buffer.position(), buffer.remaining());
    } else {
      return readBytes(fd, NioUtils.unsafeArray(buffer),
          NioUtils.unsafeArrayOffset(buffer) + buffer.position(), buffer.remaining());
    }
  }

  public int read(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount)
      throws ErrnoException {
    return readBytes(fd, bytes, byteOffset, byteCount);
  }

  private native int readBytes(FileDescriptor fd, Object buffer, int offset, int byteCount)
      throws ErrnoException /*-[
    char *bytes = BytesRW(buffer);
    if (!bytes) {
      return -1;
    }
    int rc = TEMP_FAILURE_RETRY(read([fd getInt$], bytes + offset, byteCount));
    return LibcoreIoPosix_throwIfMinusOneWithNSString_withInt_(@"read", rc);
  ]-*/;

  public native int readv(FileDescriptor fd, Object[] buffers, int[] offsets, int[] byteCounts)
      throws ErrnoException /*-[
    int nIoVecs = buffers->size_;
    struct iovec *ioVecs = (struct iovec *)malloc(nIoVecs * sizeof (struct iovec));
    for (int i = 0; i < nIoVecs; i++) {
      char *bytes = BytesRW([buffers objectAtIndex:i]);
      if (!bytes) {
        free(ioVecs);
        return -1;
      }
      ioVecs[i].iov_base = ((char *) bytes) + IOSIntArray_Get(offsets, i);
      ioVecs[i].iov_len = IOSIntArray_Get(byteCounts, i);
    }
    int rc = TEMP_FAILURE_RETRY(readv([fd getInt$], ioVecs, nIoVecs));
    free(ioVecs);
    return LibcoreIoPosix_throwIfMinusOneWithNSString_withInt_(@"readv", rc);
  ]-*/;

  public native String realpath(String path) /*-[
    if (!path) {
      return nil;
    }
    NSFileManager *fileManager = [NSFileManager defaultManager];
    NSString *standardizedPath = [path stringByStandardizingPath];
    if (![fileManager fileExistsAtPath:standardizedPath]) {
      return standardizedPath;
    }
    const char* cpath = [path UTF8String];
    char *realPath = realpath(cpath, NULL);
    if (!realPath) {
      LibcoreIoPosix_throwErrnoExceptionWithNSString_withInt_(@"realpath", errno);
    }
    NSString *result =
        [[NSFileManager defaultManager] stringWithFileSystemRepresentation:realPath
                                                                    length:strlen(realPath)];
    free(realPath);
    return result;
  ]-*/;

  public native void remove(String path) throws ErrnoException /*-[
    if (path) {
      const char* cpath = absolutePath(path);
      int rc = TEMP_FAILURE_RETRY(remove(cpath));
      LibcoreIoPosix_throwIfMinusOneWithNSString_withInt_(@"remove", rc);
    }
  ]-*/;

  public native void rename(String oldPath, String newPath) throws ErrnoException /*-[
    if (oldPath && newPath) {
      const char* cOldPath = absolutePath(oldPath);
      const char* cNewPath = absolutePath(newPath);
      int rc = TEMP_FAILURE_RETRY(rename(cOldPath, cNewPath));
      LibcoreIoPosix_throwIfMinusOneWithNSString_withInt_(@"rename", rc);
    }
  ]-*/;

  public native long sendfile(FileDescriptor outFd, FileDescriptor inFd, Int64Ref inOffset,
      long byteCount) throws ErrnoException /*-[
    off_t offset = 0;
    off_t* offsetPtr = NULL;
    if (inOffset != NULL) {
      offset = inOffset->value_;
      offsetPtr = &offset;
    }
    int rc = TEMP_FAILURE_RETRY(sendfile_([outFd getInt$], [inFd getInt$],
        offsetPtr, (size_t) byteCount));
    if (inOffset != NULL) {
      inOffset->value_ = offset;
    }
    return LibcoreIoPosix_throwIfMinusOneWithNSString_withInt_(@"sendfile", rc);
  ]-*/;

  public native void shutdown(FileDescriptor fd, int how) throws ErrnoException /*-[
    int rc = TEMP_FAILURE_RETRY(shutdown([fd getInt$], how));
    LibcoreIoPosix_throwIfMinusOneWithNSString_withInt_(@"shutdown", rc);
  ]-*/;

  public native FileDescriptor socket(int domain, int type, int protocol) throws ErrnoException /*-[
    int nativeFd = TEMP_FAILURE_RETRY(socket(domain, type, protocol));
    LibcoreIoPosix_throwIfMinusOneWithNSString_withInt_(@"socket", nativeFd);
    JavaIoFileDescriptor *newFd = AUTORELEASE([[JavaIoFileDescriptor alloc] init]);
    [newFd setInt$WithInt:nativeFd];
    return newFd;
  ]-*/;

  public native void socketpair(int domain, int type, int protocol, FileDescriptor fd1,
      FileDescriptor fd2) throws ErrnoException /*-[
    int fds[2];
    int rc = TEMP_FAILURE_RETRY(socketpair(domain, type, protocol, fds));
    if (rc != -1) {
      [fd1 setInt$WithInt:fds[0]];
      [fd2 setInt$WithInt:fds[1]];
    }
    LibcoreIoPosix_throwIfMinusOneWithNSString_withInt_(@"socketpair", rc);
  ]-*/;

  public native StructStat stat(String path) throws ErrnoException /*-[
    return doStat(path, NO);
  ]-*/;

  public native StructStatVfs statvfs(String path) throws ErrnoException /*-[
    if (!path) {
      return nil;
    }
    const char* cpath = absolutePath(path);
    struct statvfs sb;
    int rc = TEMP_FAILURE_RETRY(statvfs(cpath, &sb));
    if (rc == -1) {
      LibcoreIoPosix_throwErrnoExceptionWithNSString_withInt_(@"statvfs", errno);
    }
    return makeStructStatVfs(&sb);
  ]-*/;

  public native String strerror(int errno) /*-[
    char buffer[BUFSIZ];
    int ret = strerror_r(errno_, buffer, BUFSIZ);
    if (ret != 0) {  // If not successful...
      snprintf(buffer, BUFSIZ, "errno %d", errno_);
    }
    return [NSString stringWithUTF8String:buffer];
  ]-*/;

  public native long sysconf(int name) /*-[
    // Since -1 is a valid result from sysconf(3), detecting failure is a little more awkward.
    errno = 0;
    long result = sysconf(name);
    if (result == -1L && errno == EINVAL) {
      LibcoreIoPosix_throwErrnoExceptionWithNSString_withInt_(@"sysconf", errno);
    }
    return result;
  ]-*/;

  public native void symlink(String oldPath, String newPath) throws ErrnoException /*-[
    if (oldPath && newPath) {
      const char* cOldPath = [oldPath UTF8String];
      const char* cNewPath = [newPath UTF8String];
      int rc = TEMP_FAILURE_RETRY(symlink(cOldPath, cNewPath));
      LibcoreIoPosix_throwIfMinusOneWithNSString_withInt_(@"symlink", rc);
    }
  ]-*/;

  public native void tcdrain(FileDescriptor fd) throws ErrnoException /*-[
    int rc = TEMP_FAILURE_RETRY(tcdrain([fd getInt$]));
    LibcoreIoPosix_throwIfMinusOneWithNSString_withInt_(@"fcntl", rc);
  ]-*/;

  public native StructUtsname uname() /*-[
    struct utsname buf;
    if (TEMP_FAILURE_RETRY(uname(&buf)) == -1) {
      return nil;
    }
    return makeStructUtsname(&buf);
  ]-*/;

  public int write(FileDescriptor fd, ByteBuffer buffer) throws ErrnoException {
    if (buffer.isDirect()) {
      return writeBytes(fd, buffer, buffer.position(), buffer.remaining());
    } else {
      return writeBytes(fd, NioUtils.unsafeArray(buffer),
          NioUtils.unsafeArrayOffset(buffer) + buffer.position(), buffer.remaining());
    }
  }

  public native int write(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount)
      throws ErrnoException /*-[
    if (!bytes) {
      return -1;
    }
    IOSArray_checkRange(bytes->size_, byteOffset, byteCount);
    int rc =
        TEMP_FAILURE_RETRY(write([fd getInt$], bytes->buffer_ + byteOffset, byteCount));
    return LibcoreIoPosix_throwIfMinusOneWithNSString_withInt_(@"write", rc);
  ]-*/;

  private native int writeBytes(FileDescriptor fd, Object buffer, int offset, int byteCount)
      throws ErrnoException /*-[
    const char *bytes = BytesRO(buffer);
    if (!bytes) {
      return -1;
    }
    int rc = TEMP_FAILURE_RETRY(write([fd getInt$], bytes + offset, byteCount));
    return LibcoreIoPosix_throwIfMinusOneWithNSString_withInt_(@"write", rc);
  ]-*/;

  public native int writev(FileDescriptor fd, Object[] buffers, int[] offsets, int[] byteCounts)
      throws ErrnoException /*-[
    int nIoVecs = buffers->size_;
    struct iovec *ioVecs = (struct iovec *)malloc(nIoVecs * sizeof (struct iovec));
    for (int i = 0; i < nIoVecs; i++) {
      const char *bytes = BytesRO([buffers objectAtIndex:i]);
      if (!bytes) {
        free(ioVecs);
        return -1;
      }
      ioVecs[i].iov_base = ((char *) bytes) + IOSIntArray_Get(offsets, i);
      ioVecs[i].iov_len = IOSIntArray_Get(byteCounts, i);
    }
    int rc = TEMP_FAILURE_RETRY(writev([fd getInt$], ioVecs, nIoVecs));
    free(ioVecs);
    return LibcoreIoPosix_throwIfMinusOneWithNSString_withInt_(@"writev", rc);
  ]-*/;

  @Override
  public native int getpid() /*-[
    return getpid();
  ]-*/;

  @Override
  public native int getppid() /*-[
    return getppid();
  ]-*/;

// Uncomment and implement as Os interface grows.
//  public native String[] environ();

//  public native StructStatFs fstatfs(FileDescriptor fd) throws ErrnoException;
//  public native int getegid();
//  public native int geteuid();
//  public native int getgid();
//  public native String getenv(String name);
//  public native StructPasswd getpwnam(String name) throws ErrnoException;
//  public native StructPasswd getpwuid(int uid) throws ErrnoException;
//  public native int getuid();
//  public native void kill(int pid, int signal) throws ErrnoException;
//  public native void lchown(String path, int uid, int gid) throws ErrnoException;
//  public native void listen(FileDescriptor fd, int backlog) throws ErrnoException;
//  public native void setegid(int egid) throws ErrnoException;
//  public native void setenv(String name, String value, boolean overwrite) throws ErrnoException;
//  public native void seteuid(int euid) throws ErrnoException;
//  public native void setgid(int gid) throws ErrnoException;
//  public native int setsid() throws ErrnoException;
//  public native void setuid(int uid) throws ErrnoException;
//  public native FileDescriptor socket(int domain, int type, int protocol) throws ErrnoException;
//  public native StructStatFs statfs(String path) throws ErrnoException;
//  public native void tcsendbreak(FileDescriptor fd, int duration) throws ErrnoException;
//  public int umask(int mask) {
//    if ((mask & 0777) != mask) {
//      throw new IllegalArgumentException("Invalid umask: " + mask);
//    }
//    return umaskImpl(mask);
//  }
//  private native int umaskImpl(int mask);
//  public native void unsetenv(String name) throws ErrnoException;
//  public native int waitpid(int pid, Int32Ref status, int options) throws ErrnoException;
}
