Sfoglia il codice sorgente

HADOOP-9439. JniBasedUnixGroupsMapping: fix some crash bugs (Colin Patrick McCabe)

git-svn-id: https://svn.apache.org/repos/asf/hadoop/common/branches/branch-2.1-beta@1496117 13f79535-47bb-0310-9956-ffa450edef68
Colin McCabe 12 anni fa
parent
commit
f4dc716312
15 ha cambiato i file con 688 aggiunte e 270 eliminazioni
  1. 3 0
      hadoop-common-project/hadoop-common/CHANGES.txt
  2. 2 1
      hadoop-common-project/hadoop-common/src/CMakeLists.txt
  3. 1 1
      hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/io/nativeio/NativeIO.java
  4. 26 4
      hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/security/JniBasedUnixGroupsMapping.java
  5. 9 0
      hadoop-common-project/hadoop-common/src/main/native/src/exception.c
  6. 8 0
      hadoop-common-project/hadoop-common/src/main/native/src/exception.h
  7. 1 1
      hadoop-common-project/hadoop-common/src/main/native/src/org/apache/hadoop/io/nativeio/NativeIO.c
  8. 0 8
      hadoop-common-project/hadoop-common/src/main/native/src/org/apache/hadoop/net/unix/DomainSocket.c
  9. 149 64
      hadoop-common-project/hadoop-common/src/main/native/src/org/apache/hadoop/security/JniBasedUnixGroupsMapping.c
  10. 9 2
      hadoop-common-project/hadoop-common/src/main/native/src/org/apache/hadoop/security/JniBasedUnixGroupsMappingWin.c
  11. 0 189
      hadoop-common-project/hadoop-common/src/main/native/src/org/apache/hadoop/security/getGroup.c
  12. 148 0
      hadoop-common-project/hadoop-common/src/main/native/src/org/apache/hadoop/security/hadoop_group_info.c
  13. 56 0
      hadoop-common-project/hadoop-common/src/main/native/src/org/apache/hadoop/security/hadoop_group_info.h
  14. 203 0
      hadoop-common-project/hadoop-common/src/main/native/src/org/apache/hadoop/security/hadoop_user_info.c
  15. 73 0
      hadoop-common-project/hadoop-common/src/main/native/src/org/apache/hadoop/security/hadoop_user_info.h

+ 3 - 0
hadoop-common-project/hadoop-common/CHANGES.txt

@@ -256,6 +256,9 @@ Release 2.1.0-beta - UNRELEASED
     HADOOP-9624. TestFSMainOperationsLocalFileSystem failed when the Hadoop test
     root path has "X" in its name. (Xi Fang via cnauroth)
 
+    HADOOP-9439.  JniBasedUnixGroupsMapping: fix some crash bugs. (Colin
+    Patrick McCabe)
+
   BREAKDOWN OF HADOOP-8562 SUBTASKS AND RELATED JIRAS
 
     HADOOP-8924. Hadoop Common creating package-info.java must not depend on

+ 2 - 1
hadoop-common-project/hadoop-common/src/CMakeLists.txt

@@ -180,7 +180,8 @@ add_dual_library(hadoop
     ${D}/net/unix/DomainSocket.c
     ${D}/security/JniBasedUnixGroupsMapping.c
     ${D}/security/JniBasedUnixGroupsNetgroupMapping.c
-    ${D}/security/getGroup.c
+    ${D}/security/hadoop_group_info.c
+    ${D}/security/hadoop_user_info.c
     ${D}/util/NativeCodeLoader.c
     ${D}/util/NativeCrc32.c
     ${D}/util/bulk_crc32.c

+ 1 - 1
hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/io/nativeio/NativeIO.java

@@ -98,7 +98,7 @@ public class NativeIO {
 
     static final String WORKAROUND_NON_THREADSAFE_CALLS_KEY =
       "hadoop.workaround.non.threadsafe.getpwuid";
-    static final boolean WORKAROUND_NON_THREADSAFE_CALLS_DEFAULT = false;
+    static final boolean WORKAROUND_NON_THREADSAFE_CALLS_DEFAULT = true;
 
     private static long cacheTimeout = -1;
 

+ 26 - 4
hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/security/JniBasedUnixGroupsMapping.java

@@ -40,22 +40,44 @@ public class JniBasedUnixGroupsMapping implements GroupMappingServiceProvider {
   
   private static final Log LOG = 
     LogFactory.getLog(JniBasedUnixGroupsMapping.class);
-  
-  native String[] getGroupForUser(String user);
-  
+
   static {
     if (!NativeCodeLoader.isNativeCodeLoaded()) {
       throw new RuntimeException("Bailing out since native library couldn't " +
         "be loaded");
     }
+    anchorNative();
     LOG.debug("Using JniBasedUnixGroupsMapping for Group resolution");
   }
 
+  /**
+   * Set up our JNI resources.
+   *
+   * @throws                 RuntimeException if setup fails.
+   */
+  native static void anchorNative();
+
+  /**
+   * Get the set of groups associated with a user.
+   *
+   * @param username           The user name
+   *
+   * @return                   The set of groups associated with a user.
+   */
+  native static String[] getGroupsForUser(String username);
+
+  /**
+   * Log an error message about a group.  Used from JNI.
+   */
+  static private void logError(int groupId, String error) {
+    LOG.error("error looking up the name of group " + groupId + ": " + error);
+  }
+
   @Override
   public List<String> getGroups(String user) throws IOException {
     String[] groups = new String[0];
     try {
-      groups = getGroupForUser(user);
+      groups = getGroupsForUser(user);
     } catch (Exception e) {
       if (LOG.isDebugEnabled()) {
         LOG.debug("Error getting groups for " + user, e);

+ 9 - 0
hadoop-common-project/hadoop-common/src/main/native/src/exception.c

@@ -107,3 +107,12 @@ jthrowable newIOException(JNIEnv* env, const char *fmt, ...)
   va_end(ap);
   return jthr;
 }
+
+const char* terror(int errnum)
+{
+  if ((errnum < 0) || (errnum >= sys_nerr)) {
+    return "unknown error.";
+  }
+  return sys_errlist[errnum];
+}
+

+ 8 - 0
hadoop-common-project/hadoop-common/src/main/native/src/exception.h

@@ -79,4 +79,12 @@ jthrowable newRuntimeException(JNIEnv* env, const char *fmt, ...)
 jthrowable newIOException(JNIEnv* env, const char *fmt, ...)
     __attribute__((format(printf, 2, 3)));
 
+/**
+ * Thread-safe strerror alternative.
+ *
+ * @param errnum        Error number.
+ * @return              Statically allocated error string.
+ */
+const char* terror(int errnum);
+
 #endif

+ 1 - 1
hadoop-common-project/hadoop-common/src/main/native/src/org/apache/hadoop/io/nativeio/NativeIO.c

@@ -59,7 +59,7 @@ static jmethodID nioe_ctor;
 // the monitor used for working around non-threadsafe implementations
 // of getpwuid_r, observed on platforms including RHEL 6.0.
 // Please see HADOOP-7156 for details.
-static jobject pw_lock_object;
+jobject pw_lock_object;
 
 // Internal functions
 static void throw_ioe(JNIEnv* env, int errnum);

+ 0 - 8
hadoop-common-project/hadoop-common/src/main/native/src/org/apache/hadoop/net/unix/DomainSocket.c

@@ -107,14 +107,6 @@ static jthrowable newSocketException(JNIEnv *env, int errnum,
   return jthr;
 }
 
-static const char* terror(int errnum)
-{
-  if ((errnum < 0) || (errnum >= sys_nerr)) {
-    return "unknown error.";
-  }
-  return sys_errlist[errnum];
-}
-
 /**
  * Flexible buffer that will try to fit data on the stack, and fall back
  * to the heap if necessary.

+ 149 - 64
hadoop-common-project/hadoop-common/src/main/native/src/org/apache/hadoop/security/JniBasedUnixGroupsMapping.c

@@ -30,88 +30,173 @@
 #include <pwd.h>
 #include <string.h>
 
+#include "exception.h"
 #include "org_apache_hadoop_security_JniBasedUnixGroupsMapping.h"
 #include "org_apache_hadoop.h"
+#include "hadoop_group_info.h"
+#include "hadoop_user_info.h"
 
-static jobjectArray emptyGroups = NULL;
+static jmethodID g_log_error_method;
 
-JNIEXPORT jobjectArray JNICALL 
-Java_org_apache_hadoop_security_JniBasedUnixGroupsMapping_getGroupForUser 
-(JNIEnv *env, jobject jobj, jstring juser) {
-  extern int getGroupIDList(const char *user, int *ngroups, gid_t **groups);
-  extern int getGroupDetails(gid_t group, char **grpBuf);
-  const char *cuser = NULL;
-  jobjectArray jgroups = NULL;
-  int error = -1;
+static jclass g_string_clazz;
 
-  if (emptyGroups == NULL) {
-    jobjectArray lEmptyGroups = (jobjectArray)(*env)->NewObjectArray(env, 0,
-            (*env)->FindClass(env, "java/lang/String"), NULL);
-    if (lEmptyGroups == NULL) {
-      goto cleanup;
-    }
-    emptyGroups = (*env)->NewGlobalRef(env, lEmptyGroups);
-    if (emptyGroups == NULL) {
-      goto cleanup;
-    }
+extern jobject pw_lock_object;
+
+JNIEXPORT void JNICALL
+Java_org_apache_hadoop_security_JniBasedUnixGroupsMapping_anchorNative(
+JNIEnv *env, jclass clazz)
+{
+  jobject string_clazz;
+
+  g_log_error_method = (*env)->GetStaticMethodID(env, clazz, "logError",
+        "(ILjava/lang/String;)V");
+  if (!g_log_error_method) {
+    return; // an exception has been raised
   }
-  char *grpBuf = NULL;
-  cuser = (*env)->GetStringUTFChars(env, juser, NULL);
-  if (cuser == NULL) {
-    goto cleanup;
+  string_clazz = (*env)->FindClass(env, "java/lang/String");
+  if (!string_clazz) {
+    return; // an exception has been raised
   }
-
-  /*Get the number of the groups, and their IDs, this user belongs to*/
-  gid_t *groups = NULL;
-  int ngroups = 0;
-  error = getGroupIDList(cuser, &ngroups, &groups);
-  if (error != 0) {
-    goto cleanup; 
+  g_string_clazz = (*env)->NewGlobalRef(env, string_clazz);
+  if (!g_string_clazz) {
+    jthrowable jthr = newRuntimeException(env,
+        "JniBasedUnixGroupsMapping#anchorNative: failed to make "
+        "a global reference to the java.lang.String class\n");
+    (*env)->Throw(env, jthr);
+    return;
   }
+}
 
-  jgroups = (jobjectArray)(*env)->NewObjectArray(env, ngroups, 
-            (*env)->FindClass(env, "java/lang/String"), NULL);
-  if (jgroups == NULL) {
-    error = -1;
-    goto cleanup; 
+/**
+ * Log an error about a failure to look up a group ID
+ *
+ * @param env   The JNI environment
+ * @param clazz JniBasedUnixGroupsMapping class
+ * @param gid   The gid we failed to look up
+ * @param ret   Failure code
+ */
+static void logError(JNIEnv *env, jclass clazz, jint gid, int ret)
+{
+  jstring error_msg;
+
+  error_msg = (*env)->NewStringUTF(env, terror(ret));
+  if (!error_msg) {
+    (*env)->ExceptionClear(env);
+    return;
+  }
+  (*env)->CallStaticVoidMethod(env, clazz, g_log_error_method, gid, error_msg);
+  if ((*env)->ExceptionCheck(env)) {
+    (*env)->ExceptionClear(env);
+    return;
   }
+  (*env)->DeleteLocalRef(env, error_msg);
+}
 
-  /*Iterate over the groupIDs and get the group structure for each*/
-  int i = 0;
-  for (i = 0; i < ngroups; i++) {
-    error = getGroupDetails(groups[i],&grpBuf);
-    if (error != 0) {
-      goto cleanup;
-    }
-    jstring jgrp = (*env)->NewStringUTF(env, ((struct group*)grpBuf)->gr_name);
-    if (jgrp == NULL) {
-      error = -1;
-      goto cleanup;
+JNIEXPORT jobjectArray JNICALL 
+Java_org_apache_hadoop_security_JniBasedUnixGroupsMapping_getGroupsForUser 
+(JNIEnv *env, jclass clazz, jstring jusername)
+{
+  const char *username = NULL;
+  struct hadoop_user_info *uinfo = NULL;
+  struct hadoop_group_info *ginfo = NULL;
+  jstring jgroupname = NULL;
+  int i, ret, nvalid;
+  int pw_lock_locked = 0;
+  jobjectArray jgroups = NULL, jnewgroups = NULL;
+
+  if (pw_lock_object != NULL) {
+    if ((*env)->MonitorEnter(env, pw_lock_object) != JNI_OK) {
+      goto done; // exception thrown
     }
-    (*env)->SetObjectArrayElement(env, jgroups,i,jgrp);
-    free(grpBuf);
-    grpBuf = NULL;
+    pw_lock_locked = 1;
   }
-
-cleanup:
-  if (error == ENOMEM) {
+  username = (*env)->GetStringUTFChars(env, jusername, NULL);
+  if (username == NULL) {
+    goto done; // exception thrown
+  }
+  uinfo = hadoop_user_info_alloc();
+  if (!uinfo) {
     THROW(env, "java/lang/OutOfMemoryError", NULL);
+    goto done;
+  }
+  ret = hadoop_user_info_fetch(uinfo, username);
+  if (ret == ENOENT) {
+    jgroups = (*env)->NewObjectArray(env, 0, g_string_clazz, NULL);
+    goto done;
+  }
+  ginfo = hadoop_group_info_alloc();
+  if (!ginfo) {
+    THROW(env, "java/lang/OutOfMemoryError", NULL);
+    goto done;
+  }
+  ret = hadoop_user_info_getgroups(uinfo);
+  if (ret) {
+    if (ret == ENOMEM) {
+      THROW(env, "java/lang/OutOfMemoryError", NULL);
+    } else {
+      char buf[128];
+      snprintf(buf, sizeof(buf), "getgrouplist error %d (%s)",
+               ret, terror(ret));
+      THROW(env, "java/lang/RuntimeException", buf);
+    }
+    goto done;
+  }
+  jgroups = (jobjectArray)(*env)->NewObjectArray(env, uinfo->num_gids,
+                                                 g_string_clazz, NULL);
+  for (nvalid = 0, i = 0; i < uinfo->num_gids; i++) {
+    ret = hadoop_group_info_fetch(ginfo, uinfo->gids[i]);
+    if (ret) {
+      logError(env, clazz, uinfo->gids[i], ret);
+    } else {
+      jgroupname = (*env)->NewStringUTF(env, ginfo->group.gr_name);
+      if (!jgroupname) { // exception raised
+        (*env)->DeleteLocalRef(env, jgroups);
+        jgroups = NULL;
+        goto done;
+      }
+      (*env)->SetObjectArrayElement(env, jgroups, nvalid++, jgroupname);
+      // We delete the local reference once the element is in the array.
+      // This is OK because the array has a reference to it.
+      // Technically JNI only mandates that the JVM allow up to 16 local
+      // references at a time  (though many JVMs allow more than that.)
+      (*env)->DeleteLocalRef(env, jgroupname);
+    }
+  }
+  if (nvalid != uinfo->num_gids) {
+    // If some group names could not be looked up, allocate a smaller array
+    // with just the entries that could be resolved.  Java has no equivalent to
+    // realloc, so we have to do this manually.
+    jnewgroups = (jobjectArray)(*env)->NewObjectArray(env, nvalid,
+            (*env)->FindClass(env, "java/lang/String"), NULL);
+    if (!jnewgroups) { // exception raised
+      (*env)->DeleteLocalRef(env, jgroups);
+      jgroups = NULL;
+      goto done;
+    }
+    for (i = 0; i < nvalid; i++) {
+      jgroupname = (*env)->GetObjectArrayElement(env, jgroups, i);
+      (*env)->SetObjectArrayElement(env, jnewgroups, i, jgroupname);
+      (*env)->DeleteLocalRef(env, jgroupname);
+    }
+    (*env)->DeleteLocalRef(env, jgroups);
+    jgroups = jnewgroups;
   }
-  if (error == ENOENT) {
-    THROW(env, "java/io/IOException", "No entry for user");
+
+done:
+  if (pw_lock_locked) {
+    (*env)->MonitorExit(env, pw_lock_object);
   }
-  if (groups != NULL) {
-    free(groups);
+  if (username) {
+    (*env)->ReleaseStringUTFChars(env, jusername, username);
   }
-  if (grpBuf != NULL) {
-    free(grpBuf);
+  if (uinfo) {
+    hadoop_user_info_free(uinfo);
   }
-  if (cuser != NULL) {
-    (*env)->ReleaseStringUTFChars(env, juser, cuser);
+  if (ginfo) {
+    hadoop_group_info_free(ginfo);
   }
-  if (error == 0) {
-    return jgroups;
-  } else {
-    return emptyGroups;
+  if (jgroupname) {
+    (*env)->DeleteLocalRef(env, jgroupname);
   }
+  return jgroups;
 }

+ 9 - 2
hadoop-common-project/hadoop-common/src/main/native/src/org/apache/hadoop/security/JniBasedUnixGroupsMappingWin.c

@@ -25,6 +25,13 @@
 
 static jobjectArray emptyGroups = NULL;
 
+JNIEXPORT void JNICALL
+Java_org_apache_hadoop_security_JniBasedUnixGroupsMapping_anchorNative(
+JNIEnv *env, jclass clazz)
+{
+  // no-op until full port of HADOOP-9439 to Windows is available
+}
+
 /*
  * Throw a java.IO.IOException, generating the message from errno.
  */
@@ -57,8 +64,8 @@ static void throw_ioexception(JNIEnv* env, DWORD errnum)
 }
 
 JNIEXPORT jobjectArray JNICALL 
-Java_org_apache_hadoop_security_JniBasedUnixGroupsMapping_getGroupForUser 
-(JNIEnv *env, jobject jobj, jstring juser) {
+Java_org_apache_hadoop_security_JniBasedUnixGroupsMapping_getGroupsForUser
+(JNIEnv *env, jclass clazz, jstring juser) {
   const WCHAR *user = NULL;
   jobjectArray jgroups = NULL;
   DWORD dwRtnCode = ERROR_SUCCESS;

+ 0 - 189
hadoop-common-project/hadoop-common/src/main/native/src/org/apache/hadoop/security/getGroup.c

@@ -1,189 +0,0 @@
-/**
- * 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.
- */
-#include <grp.h>
-#include <stdio.h>
-#include <unistd.h>
-#include <pwd.h>
-#include <errno.h>
-#include <string.h>
-#include <stdlib.h>
-
-/*Helper functions for the JNI implementation of unix group mapping service*/
-
-
-/**
- * Gets the group IDs for a given user. The groups argument is allocated
- * internally, and it contains the list of groups. The ngroups is updated to 
- * the number of groups
- * Returns 0 on success (on success, the caller must free the memory allocated
- * internally)
- */
-int getGroupIDList(const char *user, int *ngroups, gid_t **groups) {
-  int getPW(const char *user, char **pwbuf);
-  *ngroups = 0;
-  char *pwbuf = NULL;
-  *groups = NULL;
-  /*Look up the password database first*/
-  int error = getPW(user, &pwbuf);
-  if (error != 0) {
-    if (pwbuf != NULL) {
-      free(pwbuf);
-    }
-    return error;
-  } 
-  struct passwd *pw = (struct passwd*)pwbuf;
-  int ng = 0;
-  /*Get the groupIDs that this user belongs to*/
-  if (getgrouplist(user, pw->pw_gid, NULL, &ng) < 0) {
-    *ngroups = ng;
-    *groups = (gid_t *) malloc(ng * sizeof (gid_t));
-    if (!*groups) {
-      *ngroups = 0;
-      free(pwbuf);
-      return ENOMEM;
-    }
-    if (getgrouplist(user, pw->pw_gid, *groups, &ng) < 0) {
-      *ngroups = 0;
-      free(pwbuf);
-      free(*groups);
-      *groups = NULL;
-      return ENOENT;
-    }
-  }
-  free(pwbuf);
-  return 0;
-}
-
-/**
- * Gets the group structure for a given group ID. 
- * The grpBuf argument is allocated internally and it contains the 
- * struct group for the given group ID. 
- * Returns 0 on success (on success, the caller must free the memory allocated
- * internally)
- */
-int getGroupDetails(gid_t group, char **grpBuf) {
-  struct group * grp = NULL;
-  long currBufferSize = sysconf(_SC_GETGR_R_SIZE_MAX);
-  if (currBufferSize < 1024) {
-    currBufferSize = 1024;
-  }
-  *grpBuf = NULL; 
-  char *buf = (char*)malloc(sizeof(char) * currBufferSize);
-
-  if (!buf) {
-    return ENOMEM;
-  }
-  int error;
-  for (;;) {
-    error = getgrgid_r(group, (struct group*)buf,
-                       buf + sizeof(struct group),
-                       currBufferSize - sizeof(struct group), &grp);
-    if(error != ERANGE) {
-       break;
-    }
-    free(buf);
-    currBufferSize *= 2;
-    buf = malloc(sizeof(char) * currBufferSize);
-    if(!buf) {
-      return ENOMEM;
-    }
-  }
-  if(!grp && !error) {
-    free(buf);
-    return ENOENT;
-  } else  if (error) {
-    free(buf);
-    return error;
-  }
-  *grpBuf = buf;
-  return 0;
-}
-
-/**
- * Gets the password database entry for a given user. 
- * The pwbuf argument is allocated internally and it contains the 
- * broken out fields for the password database entry
- * Returns 0 on success (on success, the caller must free the memory allocated 
- * internally).
- */
-int getPW(const char *user, char **pwbuf) {
-  struct passwd *pwbufp = NULL;
-  long currBufferSize = sysconf(_SC_GETPW_R_SIZE_MAX);
-  if (currBufferSize < 1024) {
-    currBufferSize = 1024;
-  }
-  *pwbuf = NULL;
-  char *buf = (char*)malloc(sizeof(char) * currBufferSize);
-  
-  if (!buf) {
-    return ENOMEM;
-  } 
-  int error;
-  
-  for (;;) {
-    error = getpwnam_r(user, (struct passwd*)buf, buf + sizeof(struct passwd),
-                       currBufferSize - sizeof(struct passwd), &pwbufp);
-    if (error != ERANGE) {
-      break;
-    }
-    free(buf);
-    currBufferSize *= 2;
-    buf = (char*)malloc(sizeof(char) * currBufferSize);
-    if (!buf) {
-      return ENOMEM;
-    }
-  } 
-  if (!pwbufp && !error) {
-    free(buf);
-    return ENOENT;
-  } else  if (error) {
-    free(buf);
-    return error;
-  }
-  *pwbuf = buf;
-  return 0;
-} 
-
-#undef TESTING
-
-#ifdef TESTING
-/**
- * A main() is provided so that quick testing of this
- * library can be done. 
- */
-int main(int argc, char **argv) {
-  int ngroups;
-  gid_t *groups = NULL;
-  char *user = "ddas";
-  if (argc == 2) user = argv[1];
-  int error = getGroupIDList(user, &ngroups, &groups);
-  if (error != 0) {
-    printf("Couldn't obtain grp for user %s", user);
-    return;
-  }
-  int i;
-  for (i = 0; i < ngroups; i++) {
-    char *grpbuf = NULL;
-    error = getGroupDetails(groups[i], &grpbuf);
-    printf("grps[%d]: %s ",i, ((struct group*)grpbuf)->gr_name);
-    free(grpbuf);
-  }
-  free(groups);
-  return 0;
-}
-#endif

+ 148 - 0
hadoop-common-project/hadoop-common/src/main/native/src/org/apache/hadoop/security/hadoop_group_info.c

@@ -0,0 +1,148 @@
+/**
+ * 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.
+ */
+
+#include "hadoop_group_info.h"
+
+#include <errno.h>
+#include <grp.h>
+#include <pthread.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+struct hadoop_group_info *hadoop_group_info_alloc(void)
+{
+  struct hadoop_group_info *ginfo;
+  size_t buf_sz;
+  char *buf;
+
+  ginfo = calloc(1, sizeof(struct hadoop_group_info));
+  buf_sz = sysconf(_SC_GETGR_R_SIZE_MAX);
+  if (buf_sz < 1024) {
+    buf_sz = 1024;
+  }
+  buf = malloc(buf_sz);
+  if (!buf) {
+    free(ginfo);
+    return NULL;
+  }
+  ginfo->buf_sz = buf_sz;
+  ginfo->buf = buf;
+  return ginfo;
+}
+
+void hadoop_group_info_clear(struct hadoop_group_info *ginfo)
+{
+  struct group *group = &ginfo->group;
+
+  group->gr_name = NULL;
+  group->gr_passwd = NULL;
+  group->gr_gid = 0;
+  group->gr_mem = NULL;
+}
+
+void hadoop_group_info_free(struct hadoop_group_info *ginfo)
+{
+  free(ginfo->buf);
+  free(ginfo);
+}
+
+/**
+ * Different platforms use different error codes to represent "group not found."
+ * So whitelist the errors which do _not_ mean "group not found."
+ *
+ * @param err           The errno
+ *
+ * @return              The error code to use
+ */
+static int getgrgid_error_translate(int err)
+{
+  if ((err == EIO) || (err == EMFILE) || (err == ENFILE) ||
+      (err == ENOMEM) || (err == ERANGE)) {
+    return err;
+  }
+  return ENOENT;
+}
+
+int hadoop_group_info_fetch(struct hadoop_group_info *ginfo, gid_t gid)
+{
+  struct group *group;
+  int err;
+  size_t buf_sz;
+  char *nbuf;
+
+  hadoop_group_info_clear(ginfo);
+  for (;;) {
+    do {
+      group = NULL;
+      err = getgrgid_r(gid, &ginfo->group, ginfo->buf,
+                         ginfo->buf_sz, &group);
+    } while ((!group) && (err == EINTR));
+    if (group) {
+      return 0;
+    }
+    if (err != ERANGE) {
+      return getgrgid_error_translate(errno);
+    }
+    buf_sz = ginfo->buf_sz * 2;
+    nbuf = realloc(ginfo->buf, buf_sz);
+    if (!nbuf) {
+      return ENOMEM;
+    }
+    ginfo->buf = nbuf;
+    ginfo->buf_sz = buf_sz;
+  }
+}
+
+#ifdef GROUP_TESTING
+/**
+ * A main() is provided so that quick testing of this
+ * library can be done. 
+ */
+int main(int argc, char **argv) {
+  char **groupname;
+  struct hadoop_group_info *ginfo;
+  int ret;
+  
+  ginfo = hadoop_group_info_alloc();
+  if (!ginfo) {
+    fprintf(stderr, "hadoop_group_info_alloc returned NULL.\n");
+    return EXIT_FAILURE;
+  }
+  for (groupname = argv + 1; *groupname; groupname++) {
+    gid_t gid = atoi(*groupname);
+    if (gid == 0) {
+      fprintf(stderr, "won't accept non-parseable group-name or gid 0: %s\n",
+              *groupname);
+      return EXIT_FAILURE;
+    }
+    ret = hadoop_group_info_fetch(ginfo, gid);
+    if (!ret) {
+      fprintf(stderr, "gid[%lld] : gr_name = %s\n",
+              (long long)gid, ginfo->group.gr_name);
+    } else {
+      fprintf(stderr, "group[%lld] : error %d (%s)\n",
+              (long long)gid, ret, strerror(ret));
+    }
+  }
+  hadoop_group_info_free(ginfo);
+  return EXIT_SUCCESS;
+}
+#endif

+ 56 - 0
hadoop-common-project/hadoop-common/src/main/native/src/org/apache/hadoop/security/hadoop_group_info.h

@@ -0,0 +1,56 @@
+/*
+ *  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.
+ */
+#ifndef HADOOP_GROUP_INFO_DOT_H
+#define HADOOP_GROUP_INFO_DOT_H
+
+#include <grp.h> /* for struct group */
+#include <unistd.h> /* for size_t */
+
+struct hadoop_group_info {
+  size_t buf_sz;
+  struct group group;
+  char *buf;
+};
+
+/**
+ * Allocate a hadoop group info context.
+ *
+ * @return                        NULL on OOM; the context otherwise.
+ */
+struct hadoop_group_info *hadoop_group_info_alloc(void);
+
+/**
+ * Free a hadoop group info context.
+ *
+ * @param uinfo                   The hadoop group info context to free.
+ */
+void hadoop_group_info_free(struct hadoop_group_info *ginfo);
+
+/**
+ * Look up information for a group id.
+ *
+ * @param uinfo                   The hadoop group info context.
+ *                                Existing data in this context will be cleared.
+ * @param gid                     The group id to look up.
+ *
+ * @return                        ENOENT if the group wasn't found;
+ *                                0 on success;
+ *                                EIO, EMFILE, ENFILE, or ENOMEM if appropriate.
+ */
+int hadoop_group_info_fetch(struct hadoop_group_info *ginfo, gid_t gid);
+
+#endif

+ 203 - 0
hadoop-common-project/hadoop-common/src/main/native/src/org/apache/hadoop/security/hadoop_user_info.c

@@ -0,0 +1,203 @@
+/**
+ * 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.
+ */
+
+#include "hadoop_user_info.h"
+
+#include <errno.h>
+#include <grp.h>
+#include <pthread.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#define INITIAL_GIDS_SIZE 32
+
+struct hadoop_user_info *hadoop_user_info_alloc(void)
+{
+  struct hadoop_user_info *uinfo;
+  size_t buf_sz;
+  char *buf;
+
+  uinfo = calloc(1, sizeof(struct hadoop_user_info));
+  buf_sz = sysconf(_SC_GETPW_R_SIZE_MAX);
+  if (buf_sz < 1024) {
+    buf_sz = 1024;
+  }
+  buf = malloc(buf_sz);
+  if (!buf) {
+    free(uinfo);
+    return NULL;
+  }
+  uinfo->buf_sz = buf_sz;
+  uinfo->buf = buf;
+  return uinfo;
+}
+
+static void hadoop_user_info_clear(struct hadoop_user_info *uinfo)
+{
+  struct passwd *pwd = &uinfo->pwd;
+
+  pwd->pw_name = NULL;
+  pwd->pw_uid = 0;
+  pwd->pw_gid = 0;
+  pwd->pw_passwd = NULL;
+  pwd->pw_gecos = NULL;
+  pwd->pw_dir = NULL;
+  pwd->pw_shell = NULL;
+  free(uinfo->gids);
+  uinfo->gids = 0;
+  uinfo->num_gids = 0;
+  uinfo->gids_size = 0;
+}
+
+void hadoop_user_info_free(struct hadoop_user_info *uinfo)
+{
+  free(uinfo->buf);
+  hadoop_user_info_clear(uinfo);
+  free(uinfo);
+}
+
+/**
+ * Different platforms use different error codes to represent "user not found."
+ * So whitelist the errors which do _not_ mean "user not found."
+ *
+ * @param err           The errno
+ *
+ * @return              The error code to use
+ */
+static int getpwnam_error_translate(int err)
+{
+  if ((err == EIO) || (err == EMFILE) || (err == ENFILE) ||
+      (err == ENOMEM) || (err == ERANGE)) {
+    return err;
+  }
+  return ENOENT;
+}
+
+int hadoop_user_info_fetch(struct hadoop_user_info *uinfo,
+                           const char *username)
+{
+  struct passwd *pwd;
+  int err;
+  size_t buf_sz;
+  char *nbuf;
+
+  hadoop_user_info_clear(uinfo);
+  for (;;) {
+    do {
+      pwd = NULL;
+      err = getpwnam_r(username, &uinfo->pwd, uinfo->buf,
+                         uinfo->buf_sz, &pwd);
+    } while ((!pwd) && (errno == EINTR));
+    if (pwd) {
+      return 0;
+    }
+    if (err != ERANGE) {
+      return getpwnam_error_translate(errno);
+    }
+    buf_sz = uinfo->buf_sz * 2;
+    nbuf = realloc(uinfo->buf, buf_sz);
+    if (!nbuf) {
+      return ENOMEM;
+    }
+    uinfo->buf = nbuf;
+    uinfo->buf_sz = buf_sz;
+  }
+}
+
+int hadoop_user_info_getgroups(struct hadoop_user_info *uinfo)
+{
+  int ret, ngroups;
+  gid_t *ngids;
+
+  if (!uinfo->pwd.pw_name) {
+    return EINVAL; // invalid user info
+  }
+  uinfo->num_gids = 0;
+  if (!uinfo->gids) {
+    uinfo->gids = malloc(sizeof(uinfo->gids[0]) * INITIAL_GIDS_SIZE);
+    if (!uinfo->gids) {
+      return ENOMEM;
+    }
+    uinfo->gids_size = INITIAL_GIDS_SIZE;
+  }
+  ngroups = uinfo->gids_size;
+  ret = getgrouplist(uinfo->pwd.pw_name, uinfo->pwd.pw_gid, 
+                         uinfo->gids, &ngroups);
+  if (ret != -1) {
+    uinfo->num_gids = ngroups;
+    return 0;
+  }
+  ngids = realloc(uinfo->gids, sizeof(uinfo->gids[0]) * ngroups);
+  if (!ngids) {
+    return ENOMEM;
+  }
+  uinfo->gids = ngids;
+  uinfo->gids_size = ngroups;
+  ret = getgrouplist(uinfo->pwd.pw_name, uinfo->pwd.pw_gid, 
+                         uinfo->gids, &ngroups);
+  if (ret != -1) {
+    uinfo->num_gids = ngroups;
+    return 0;
+  }
+  return EIO;
+}
+
+#ifdef USER_TESTING
+/**
+ * A main() is provided so that quick testing of this
+ * library can be done. 
+ */
+int main(int argc, char **argv) {
+  char **username, *prefix;
+  struct hadoop_user_info *uinfo;
+  int i, ret;
+  
+  uinfo = hadoop_user_info_alloc();
+  if (!uinfo) {
+    fprintf(stderr, "hadoop_user_info_alloc returned NULL.\n");
+    return EXIT_FAILURE;
+  }
+  for (username = argv + 1; *username; username++) {
+    ret = hadoop_user_info_fetch(uinfo, *username);
+    if (!ret) {
+      fprintf(stderr, "user[%s] : pw_uid = %lld\n",
+              *username, (long long)uinfo->pwd.pw_uid);
+    } else {
+      fprintf(stderr, "user[%s] : error %d (%s)\n",
+              *username, ret, strerror(ret));
+    }
+    ret = hadoop_user_info_getgroups(uinfo);
+    if (!ret) {
+      fprintf(stderr, "          getgroups: ");
+      prefix = "";
+      for (i = 0; i < uinfo->num_gids; i++) {
+        fprintf(stderr, "%s%lld", prefix, (long long)uinfo->gids[i]);
+        prefix = ", ";
+      }
+      fprintf(stderr, "\n");
+    } else {
+      fprintf(stderr, "          getgroups: error %d\n", ret);
+    }
+  }
+  hadoop_user_info_free(uinfo);
+  return EXIT_SUCCESS;
+}
+#endif

+ 73 - 0
hadoop-common-project/hadoop-common/src/main/native/src/org/apache/hadoop/security/hadoop_user_info.h

@@ -0,0 +1,73 @@
+/*
+ *  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.
+ */
+#ifndef HADOOP_USER_INFO_DOT_H
+#define HADOOP_USER_INFO_DOT_H
+
+#include <pwd.h> /* for struct passwd */
+#include <unistd.h> /* for size_t */
+
+struct hadoop_user_info {
+  size_t buf_sz;
+  struct passwd pwd;
+  char *buf;
+  gid_t *gids;
+  int num_gids;
+  int gids_size;
+};
+
+/**
+ * Allocate a hadoop user info context.
+ *
+ * @return                        NULL on OOM; the context otherwise.
+ */
+struct hadoop_user_info *hadoop_user_info_alloc(void);
+
+/**
+ * Free a hadoop user info context.
+ *
+ * @param uinfo                   The hadoop user info context to free.
+ */
+void hadoop_user_info_free(struct hadoop_user_info *uinfo);
+
+/**
+ * Look up information for a user name.
+ *
+ * @param uinfo                   The hadoop user info context.
+ *                                Existing data in this context will be cleared.
+ * @param username                The user name to look up.
+ *
+ * @return                        ENOENT if the user wasn't found;
+ *                                0 on success;
+ *                                EIO, EMFILE, ENFILE, or ENOMEM if appropriate.
+ */
+int hadoop_user_info_fetch(struct hadoop_user_info *uinfo,
+                           const char *username);
+
+/**
+ * Look up the groups this user belongs to. 
+ *
+ * @param uinfo                   The hadoop user info context.
+ *                                uinfo->gids will be filled in on a successful
+ *                                return;
+ *
+ * @return                        0 on success.
+ *                                ENOMEM if we ran out of memory.
+ *                                EINVAL if the uinfo was invalid.
+ */
+int hadoop_user_info_getgroups(struct hadoop_user_info *uinfo);
+
+#endif