[MIPS] Update Support for fcntl.

Change-Id: I743837793a84e6f4dbbc192b29b19a6eae78a5da
Signed-off-by: Pete Delaney  <piet@mips.com>
Signed-off-by: Chris Dearman <chris@mips.com>
diff --git a/ndk/sources/android/libportable/arch-mips/fcntl.c b/ndk/sources/android/libportable/arch-mips/fcntl.c
index 31874b5..b952c2b 100644
--- a/ndk/sources/android/libportable/arch-mips/fcntl.c
+++ b/ndk/sources/android/libportable/arch-mips/fcntl.c
@@ -15,47 +15,332 @@
  */
 
 #include <fcntl.h>
+#include <errno.h>
 #include <stdarg.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
 #include <fcntl_portable.h>
 
+#include <portability.h>
+
 #if F_GETLK_PORTABLE==F_GETLK
 #error Bad build environment
 #endif
 
-static inline int mips_change_cmd(int cmd)
+#define PORTABLE_TAG "fcntl_portable"
+#include <log_portable.h>
+
+static char *map_portable_cmd_to_name(int cmd)
+{
+    char *name;
+
+    switch(cmd) {
+    case F_DUPFD_PORTABLE:              name = "F_DUPFD_PORTABLE";              break;  /* 0 */
+    case F_GETFD_PORTABLE:              name = "F_GETFD_PORTABLE";              break;  /* 1 */
+    case F_SETFD_PORTABLE:              name = "F_SETFD_PORTABLE";              break;  /* 2 */
+    case F_GETFL_PORTABLE:              name = "F_GETFL_PORTABLE";              break;  /* 3 */
+    case F_SETFL_PORTABLE:              name = "F_SETFL_PORTABLE";              break;  /* 4 */
+    case F_GETLK_PORTABLE:              name = "F_GETLK_PORTABLE";              break;  /* 5 */
+    case F_SETLK_PORTABLE:              name = "F_SETLK_PORTABLE";              break;  /* 6 */
+    case F_SETLKW_PORTABLE:             name = "F_SETLKW_PORTABLE";             break;  /* 7 */
+    case F_SETOWN_PORTABLE:             name = "F_SETOWN_PORTABLE";             break;  /* 8 */
+    case F_GETOWN_PORTABLE:             name = "F_GETOWN_PORTABLE";             break;  /* 9 */
+    case F_SETSIG_PORTABLE:             name = "F_SETSIG_PORTABLE";             break;  /* 10 */
+    case F_GETSIG_PORTABLE:             name = "F_GETSIG_PORTABLE";             break;  /* 11 */
+    case F_GETLK64_PORTABLE:            name = "F_GETLK64_PORTABLE";            break;  /* 12 */
+    case F_SETLK64_PORTABLE:            name = "F_SETLK64_PORTABLE";            break;  /* 13 */
+    case F_SETLKW64_PORTABLE:           name = "F_SETLKW64_PORTABLE";           break;  /* 14 */
+    case F_SETLEASE_PORTABLE:           name = "F_SETLEASE_PORTABLE";           break;  /* 1024 */
+    case F_GETLEASE_PORTABLE:           name = "F_GETLEASE_PORTABLE";           break;  /* 1025 */
+    case F_NOTIFY_PORTABLE:             name = "F_NOTIFY_PORTABLE";             break;  /* 1026 */
+    case F_CANCELLK_PORTABLE:           name = "F_CANCELLK_PORTABLE";           break;  /* 1029 */
+    case F_DUPFD_CLOEXEC_PORTABLE:      name = "F_DUPFD_CLOEXEC_PORTABLE";      break;  /* 1030 */
+    default:                            name = "<UNKNOWN>";                     break;
+    }
+    return(name);
+}
+
+static int mips_change_cmd(int cmd)
 {
     switch(cmd) {
-    case F_GETLK_PORTABLE:
+    case F_DUPFD_PORTABLE:      /* 0 --> 0 */
+        return F_DUPFD;
+    case F_GETFD_PORTABLE:      /* 1 --> 1 */
+        return F_GETFD;
+    case F_SETFD_PORTABLE:      /* 2 --> 2 */
+        return F_SETFD;
+    case F_GETFL_PORTABLE:      /* 3 --> 3 */
+        return F_GETFL;
+    case F_SETFL_PORTABLE:      /* 4 --> 4 */
+        return F_SETFL;
+    case F_GETLK_PORTABLE:      /* 5 --> 14 */
         return F_GETLK;
-    case F_SETLK_PORTABLE:
+    case F_SETLK_PORTABLE:      /* 6 --> 6 */
         return F_SETLK;
-    case F_SETLKW_PORTABLE:
+    case F_SETLKW_PORTABLE:     /* 7 --> 7 */
         return F_SETLKW;
-    case F_SETOWN_PORTABLE:
+    case F_SETOWN_PORTABLE:     /* 8 --> 24 */
         return F_SETOWN;
-    case F_GETOWN_PORTABLE:
+    case F_GETOWN_PORTABLE:     /* 9 --> 23 */
         return F_GETOWN;
-    case F_GETLK64_PORTABLE:
+    case F_SETSIG_PORTABLE:     /* 10 --> 10 */
+        return F_SETSIG;
+    case F_GETSIG_PORTABLE:     /* 11 --> 11 */
+        return F_GETSIG;
+    case F_GETLK64_PORTABLE:    /* 12 --> 33 */
         return F_GETLK64;
-    case F_SETLK64_PORTABLE:
+    case F_SETLK64_PORTABLE:    /* 13 --> 34 */
         return F_SETLK64;
-    case F_SETLKW64_PORTABLE:
+    case F_SETLKW64_PORTABLE:   /* 14 --> 35 */
         return F_SETLKW64;
+    case F_SETLEASE_PORTABLE:   /* 1024 --> 1024 */
+        return F_SETLEASE;
+    case F_GETLEASE_PORTABLE:   /* 1025 --> 1025 */
+        return F_GETLEASE;
+    case F_NOTIFY_PORTABLE:      /* 1026 --> 1026 */
+        return F_NOTIFY;
+    case F_CANCELLK_PORTABLE:      /* 1029 --> void */
+        ALOGE("%s: case F_CANCELLK_PORTABLE: Not likely supported by MIPS.", __func__);
+        return(cmd);
+    case F_DUPFD_CLOEXEC_PORTABLE: /* 1030 --> VOID */
+        ALOGE("%s: case F_DUPFD_CLOEXEC_PORTABLE: Not likely supported by MIPS.", __func__);
+        return(cmd);
+    default:
+        ALOGE("%s: cmd:%d Not Supported", __func__, cmd);
+        /* Fall thru and default to the command being the same */
     }
-    return cmd;
+    return(cmd);
+}
+
+static int mips_change_flags(int flags)
+{
+    int mipsflags = flags & O_ACCMODE_PORTABLE;
+
+    if (flags & O_CREAT_PORTABLE)
+        mipsflags |= O_CREAT;
+    if (flags & O_EXCL_PORTABLE)
+        mipsflags |= O_EXCL;
+    if (flags & O_NOCTTY_PORTABLE)
+        mipsflags |= O_NOCTTY;
+    if (flags & O_TRUNC_PORTABLE)
+        mipsflags |= O_TRUNC;
+    if (flags & O_APPEND_PORTABLE)
+        mipsflags |= O_APPEND;
+    if (flags & O_NONBLOCK_PORTABLE)
+        mipsflags |= O_NONBLOCK;
+    if (flags & O_SYNC_PORTABLE)
+        mipsflags |= O_SYNC;
+    if (flags & FASYNC_PORTABLE)
+        mipsflags |= FASYNC;
+    if (flags & O_DIRECT_PORTABLE)
+        mipsflags |= O_DIRECT;
+    if (flags & O_LARGEFILE_PORTABLE)
+        mipsflags |= O_LARGEFILE;
+    if (flags & O_DIRECTORY_PORTABLE)
+        mipsflags |= O_DIRECTORY;
+    if (flags & O_NOFOLLOW_PORTABLE)
+        mipsflags |= O_NOFOLLOW;
+    if (flags & O_NOATIME_PORTABLE)
+        mipsflags |= O_NOATIME;
+    if (flags & O_NDELAY_PORTABLE)
+        mipsflags |= O_NDELAY;
+
+    return mipsflags;
+}
+
+static int portable_change_flags(int flags)
+{
+    int portableflags = flags & O_ACCMODE_PORTABLE;
+    if (flags & O_CREAT)
+        portableflags |= O_CREAT_PORTABLE;
+    if (flags & O_EXCL)
+        portableflags |= O_EXCL_PORTABLE;
+    if (flags & O_NOCTTY)
+        portableflags |= O_NOCTTY_PORTABLE;
+    if (flags & O_TRUNC)
+        portableflags |= O_TRUNC_PORTABLE;
+    if (flags & O_APPEND)
+        portableflags |= O_APPEND_PORTABLE;
+    if (flags & O_NONBLOCK)
+        portableflags |= O_NONBLOCK_PORTABLE;
+    if (flags & O_SYNC)
+        portableflags |= O_SYNC_PORTABLE;
+    if (flags & FASYNC)
+        portableflags |= FASYNC_PORTABLE;
+    if (flags & O_DIRECT)
+        portableflags |= O_DIRECT_PORTABLE;
+    if (flags & O_LARGEFILE)
+        portableflags |= O_LARGEFILE_PORTABLE;
+    if (flags & O_DIRECTORY)
+        portableflags |= O_DIRECTORY_PORTABLE;
+    if (flags & O_NOFOLLOW)
+        portableflags |= O_NOFOLLOW_PORTABLE;
+    if (flags & O_NOATIME)
+        portableflags |= O_NOATIME_PORTABLE;
+    if (flags & O_NDELAY)
+        portableflags |= O_NDELAY_PORTABLE;
+
+    return portableflags;
 }
 
 extern int __fcntl64(int, int, void *);
 
-int fcntl_portable(int fd, int cmd, ...)
+/*
+ * For 32 bit flocks we are converting a portable/AMR struct flock to a MIPS struct flock:
+ *
+ * MIPS:                        ARM:
+ *     struct flock {           struct flock_portable {
+ *       short l_type;            short l_type;
+ *
+ *       short l_whence;          short l_whence;
+ *       off_t l_start;           loff_t l_start;
+ *       off_t l_len;             loff_t l_len;
+ *       long l_sysid;
+ *
+ *       __kernel_pid_t l_pid;    pid_t l_pid;
+ *       long pad[4];
+ *     };                       }
+ *
+ * which have identically sized structure members:
+ *
+ * For a 64 bit flocks we only have to deal with
+ * a four byte padding in the ARM/Portable structure:
+ *
+ *    MIPS:                     ARM:
+ *        struct flock64 {      struct flock64_portable {
+ *        short l_type;           short l_type;
+ *        short l_whence;         short l_whence;
+ *                                unsigned char __padding[4];   <----  NOTE
+ *        loff_t l_start;         loff_t l_start;
+ *        loff_t l_len;           loff_t l_len;
+ *        pid_t l_pid;            pid_t l_pid;
+ *      }                       }
+ */
+
+int fcntl_portable(int fd, int portable_cmd, ...)
 {
     va_list ap;
     void * arg;
+    int flags;
+    int result = 0;
+    struct flock flock;                                 /* Native MIPS structure */
+    struct flock64 flock64;                             /* Native MIPS structure */
+    int mips_cmd = mips_change_cmd(portable_cmd);
+    char *portable_cmd_name = map_portable_cmd_to_name(portable_cmd);
+    struct flock_portable *flock_portable = NULL;
+    struct flock64_portable *flock64_portable = NULL;
 
-    va_start(ap, cmd);
+    ALOGV(" ");
+    ALOGV("%s(fd:%d, portable_cmd:%d:'%s', ...): MAPPED mips_cmd = %d",  __func__,
+              fd,    portable_cmd,
+                     portable_cmd_name,                 mips_cmd);
+
+    va_start(ap, portable_cmd);
     arg = va_arg(ap, void *);
     va_end(ap);
 
-    return __fcntl64(fd, mips_change_cmd(cmd), arg);
+    switch(mips_cmd) {
+    case F_GETLK:
+    case F_SETLK:
+    case F_SETLKW:
+        flock_portable = (struct flock_portable *) arg;
+
+        if (invalid_pointer(flock_portable)) {
+            ALOGE("%s: flock_portable:%p == {NULL||-1}", __func__, flock_portable);
+            errno = EFAULT;
+            result = -1;
+            goto done;
+        }
+
+        /*
+         * Lock type and Whence are the same for all ARCHs
+         *      (F_RDLCK:0,   F_WRLCK:1,  F_UNLCK:2)
+         *      (SEEK_SET:0, SEEK_CUR:1, SEEK_END:2)
+         */
+        flock.l_type = flock_portable->l_type;
+        flock.l_whence = flock_portable->l_whence;
+        flock.l_start = (off_t) flock_portable->l_start;
+        flock.l_len =  (off_t) flock_portable->l_len;
+        flock.l_sysid = 0L;
+        flock.l_pid = flock_portable->l_pid;    /* Perhaps 0 would be better */
+
+        result = __fcntl64(fd, mips_cmd, (void *) &flock);
+
+        flock_portable->l_type = flock.l_type;
+        flock_portable->l_whence = flock.l_whence;
+        flock_portable->l_start = flock.l_start;
+        flock_portable->l_len = flock.l_len;
+        flock_portable->l_pid = flock.l_pid;
+        break;
+
+    case F_GETLK64:
+    case F_SETLK64:
+    case F_SETLKW64:
+        flock64_portable = (struct flock64_portable *) arg;
+
+        if (invalid_pointer(flock_portable)) {
+            ALOGE("%s: flock_portable:%p == {NULL||-1}", __func__, flock_portable);
+            errno = EFAULT;
+            result = -1;
+            goto done;
+        }
+
+        /*
+         * Lock type and Whence are the same for all ARCHs
+         *      (F_RDLCK:0,   F_WRLCK:1,  F_UNLCK:2)
+         *      (SEEK_SET:0, SEEK_CUR:1, SEEK_END:2)
+         */
+        flock64.l_type = flock64_portable->l_type;
+        flock64.l_whence = flock64_portable->l_whence;
+        flock64.l_start = (off_t) flock64_portable->l_start;
+        flock64.l_len =  (off_t) flock64_portable->l_len;
+        flock64.l_pid = flock64_portable->l_pid;        /* Perhaps 0 would be better */
+
+        result = __fcntl64(fd, mips_cmd, (void *) &flock);
+
+        flock64_portable->l_type = flock64.l_type;
+        flock64_portable->l_whence = flock64.l_whence;
+        flock64_portable->l_start = flock64.l_start;
+        flock64_portable->l_len = flock64.l_len;
+        flock64_portable->l_pid = flock64.l_pid;
+        break;
+
+    case F_SETFL:
+        flags = mips_change_flags((int)arg);
+        result = __fcntl64(fd, mips_change_cmd(mips_cmd), (void *)flags);
+        break;
+
+    case F_GETFL:
+        result = __fcntl64(fd, mips_change_cmd(mips_cmd), arg);
+        if (result != -1)
+            result = portable_change_flags(result);
+        break;
+
+    case F_DUPFD:
+    case F_GETFD:
+    case F_SETFD:
+    case F_SETOWN:
+    case F_GETOWN:
+    case F_SETSIG:
+    case F_GETSIG:
+    case F_SETLEASE:
+    case F_GETLEASE:
+    case F_NOTIFY:
+        result = __fcntl64(fd, mips_cmd, arg);
+        break;
+
+    default:
+        /*
+         * This is likely a rare situation, abort() would hang fcntl13 LTP test.
+         */
+        ALOGE("%s: mips_cmd:%d Doesn't appear to be supported; assume it doesn't need to be mapped!", __func__, mips_cmd);
+
+        result = __fcntl64(fd, mips_cmd, arg);
+    }
+
+done:
+    ALOGV("%s: return(result:%d); }", __func__, result);
+    return(result);
 }
 
diff --git a/ndk/sources/android/libportable/common/include/fcntl_portable.h b/ndk/sources/android/libportable/common/include/fcntl_portable.h
index 7240ad3..19f49c2 100644
--- a/ndk/sources/android/libportable/common/include/fcntl_portable.h
+++ b/ndk/sources/android/libportable/common/include/fcntl_portable.h
@@ -18,7 +18,7 @@
 #define _FCNTL_PORTABLE_H_
 
 /* Derived from development/ndk/platforms/android-3/arch-arm/include/asm/fcntl.h */
-/* NB x86 does not have these and only uses the generic definitions */
+/* NB x86 does not have these and only uses the generic definitions. */
 #define O_DIRECTORY_PORTABLE    040000
 #define O_NOFOLLOW_PORTABLE     0100000
 #define O_DIRECT_PORTABLE       0200000
@@ -72,6 +72,21 @@
 #define O_NDELAY_PORTABLE   O_NONBLOCK_PORTABLE
 #endif
 
+/*
+ * For use with F_GETLK and F_SETLK
+ */
+struct flock_portable {
+   short l_type;
+   short l_whence;
+   off_t l_start;
+   off_t l_len;
+   pid_t l_pid;
+   __ARCH_FLOCK64_PAD
+};
+
+/*
+ * For use with F_GETLK64 and F_SETLK64
+ */
 struct flock64_portable {
    short l_type;
    short l_whence;
@@ -82,9 +97,10 @@
    __ARCH_FLOCK64_PAD
 };
 
+#if 0
 /*
-The X86 Version is
-
+ * The X86 Version is
+ */
 struct flock64 {
    short l_type;
    short l_whence;
@@ -93,22 +109,48 @@
    pid_t l_pid;
    __ARCH_FLOCK64_PAD
 };
-*/
+#endif /* 0 */
+
+
+#ifndef F_DUPFD_PORTABLE
+#define F_DUPFD_PORTABLE 0
+#define F_GETFD_PORTABLE 1
+#define F_SETFD_PORTABLE 2
+#define F_GETFL_PORTABLE 3
+#define F_SETFL_PORTABLE 4
+#endif
 
 #ifndef F_GETLK_PORTABLE
 #define F_GETLK_PORTABLE 5
 #define F_SETLK_PORTABLE 6
 #define F_SETLKW_PORTABLE 7
 #endif
+
 #ifndef F_SETOWN_PORTABLE
 #define F_SETOWN_PORTABLE 8
 #define F_GETOWN_PORTABLE 9
 #endif
 
+#ifndef F_SETSIG_PORTABLE
+#define F_SETSIG_PORTABLE 10
+#define F_GETSIG_PORTABLE 11
+#endif
+
 #ifndef F_GETLK64_PORTABLE
 #define F_GETLK64_PORTABLE 12
 #define F_SETLK64_PORTABLE 13
 #define F_SETLKW64_PORTABLE 14
 #endif
 
+/* This constant seems to be the same for all ARCH's */
+#define F_LINUX_SPECIFIC_BASE_PORTABLE 1024
+
+#define F_SETLEASE_PORTABLE             (F_LINUX_SPECIFIC_BASE+0)       /* 1024 */
+#define F_GETLEASE_PORTABLE             (F_LINUX_SPECIFIC_BASE+1)       /* 1025 */
+#define F_NOTIFY_PORTABLE               (F_LINUX_SPECIFIC_BASE+2)       /* 1026 */
+
+/* Currently these are only supported by X86_64 */
+#define F_CANCELLK_PORTABLE             (F_LINUX_SPECIFIC_BASE+5)       /* 1029 */
+#define F_DUPFD_CLOEXEC_PORTABLE        (F_LINUX_SPECIFIC_BASE+6)       /* 1030 */
+
 #endif /* _FCNTL_PORTABLE_H */