Modified: hadoop/common/trunk/src/native/configure.ac URL: http://svn.apache.org/viewvc/hadoop/common/trunk/src/native/configure.ac?rev=1030646&r1=1030645&r2=1030646&view=diff ============================================================================== --- hadoop/common/trunk/src/native/configure.ac (original) +++ hadoop/common/trunk/src/native/configure.ac Wed Nov 3 20:20:33 2010 @@ -86,15 +86,16 @@ AC_SUBST([JNI_CPPFLAGS]) dnl Check for zlib headers AC_CHECK_HEADERS([zlib.h zconf.h], AC_COMPUTE_NEEDED_DSO(z,HADOOP_ZLIB_LIBRARY), AC_MSG_ERROR(Zlib headers were not found... native-hadoop library needs zlib to build. Please install the requisite zlib development package.)) +dnl Check for headers needed by the native Group resolution implementation +AC_CHECK_HEADERS([fcntl.h stdlib.h string.h unistd.h], [], AC_MSG_ERROR(Some system headers not found... please ensure their presence on your platform.)) + # Checks for typedefs, structures, and compiler characteristics. AC_C_CONST # Checks for library functions. AC_CHECK_FUNCS([memset]) -AC_CONFIG_FILES([Makefile - src/org/apache/hadoop/io/compress/zlib/Makefile - lib/Makefile]) +AC_CONFIG_FILES([Makefile]) AC_OUTPUT #
Added: hadoop/common/trunk/src/native/src/org/apache/hadoop/security/JniBasedUnixGroupsMapping.c URL: http://svn.apache.org/viewvc/hadoop/common/trunk/src/native/src/org/apache/hadoop/security/JniBasedUnixGroupsMapping.c?rev=1030646&view=auto ============================================================================== --- hadoop/common/trunk/src/native/src/org/apache/hadoop/security/JniBasedUnixGroupsMapping.c (added) +++ hadoop/common/trunk/src/native/src/org/apache/hadoop/security/JniBasedUnixGroupsMapping.c Wed Nov 3 20:20:33 2010 @@ -0,0 +1,117 @@ +/** + * 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 <jni.h> +#include <sys/types.h> +#include <sys/ipc.h> +#include <sys/shm.h> +#include <sys/mman.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <unistd.h> +#include <stdlib.h> +#include <errno.h> +#include <grp.h> +#include <stdio.h> +#include <pwd.h> +#include <string.h> + +#include "org_apache_hadoop_security_JniBasedUnixGroupsMapping.h" +#include "org_apache_hadoop.h" + +static jobjectArray emptyGroups = NULL; + +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); + + jobjectArray jgroups; + int error = -1; + + 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; + } + } + char *grpBuf = NULL; + const char *cuser = (*env)->GetStringUTFChars(env, juser, NULL); + if (cuser == NULL) { + goto cleanup; + } + + /*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; + } + + jgroups = (jobjectArray)(*env)->NewObjectArray(env, ngroups, + (*env)->FindClass(env, "java/lang/String"), NULL); + if (jgroups == NULL) { + error = -1; + goto cleanup; + } + + /*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; + } + (*env)->SetObjectArrayElement(env, jgroups,i,jgrp); + free(grpBuf); + grpBuf = NULL; + } + +cleanup: + if (error == ENOMEM) { + THROW(env, "java/lang/OutOfMemoryError", NULL); + } + if (error == ENOENT) { + THROW(env, "java/io/IOException", "No entry for user"); + } + if (groups != NULL) { + free(groups); + } + if (grpBuf != NULL) { + free(grpBuf); + } + if (cuser != NULL) { + (*env)->ReleaseStringUTFChars(env, juser, cuser); + } + if (error == 0) { + return jgroups; + } else { + return emptyGroups; + } +} Added: hadoop/common/trunk/src/native/src/org/apache/hadoop/security/getGroup.c URL: http://svn.apache.org/viewvc/hadoop/common/trunk/src/native/src/org/apache/hadoop/security/getGroup.c?rev=1030646&view=auto ============================================================================== --- hadoop/common/trunk/src/native/src/org/apache/hadoop/security/getGroup.c (added) +++ hadoop/common/trunk/src/native/src/org/apache/hadoop/security/getGroup.c Wed Nov 3 20:20:33 2010 @@ -0,0 +1,189 @@ +/** + * 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; + size_t 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; + size_t 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 Added: hadoop/common/trunk/src/test/core/org/apache/hadoop/security/TestJNIGroupsMapping.java URL: http://svn.apache.org/viewvc/hadoop/common/trunk/src/test/core/org/apache/hadoop/security/TestJNIGroupsMapping.java?rev=1030646&view=auto ============================================================================== --- hadoop/common/trunk/src/test/core/org/apache/hadoop/security/TestJNIGroupsMapping.java (added) +++ hadoop/common/trunk/src/test/core/org/apache/hadoop/security/TestJNIGroupsMapping.java Wed Nov 3 20:20:33 2010 @@ -0,0 +1,76 @@ +/** + * 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. + */ +package org.apache.hadoop.security; +import static org.junit.Assume.assumeTrue; +import static org.junit.Assert.*; + +import java.util.Arrays; +import java.util.List; +import java.util.SortedSet; +import java.util.TreeSet; + +import org.apache.hadoop.fs.CommonConfigurationKeys; +import org.apache.hadoop.security.GroupMappingServiceProvider; +import org.apache.hadoop.security.JniBasedUnixGroupsMapping; +import org.apache.hadoop.security.ShellBasedUnixGroupsMapping; +import org.apache.hadoop.security.UserGroupInformation; +import org.apache.hadoop.util.NativeCodeLoader; +import org.apache.hadoop.util.ReflectionUtils; +import org.junit.Before; +import org.junit.Test; + + + +public class TestJNIGroupsMapping { + + @Before + public void isNativeCodeLoaded() { + assumeTrue(NativeCodeLoader.isNativeCodeLoaded()); + } + + @Test + public void testJNIGroupsMapping() throws Exception { + //for the user running the test, check whether the + //ShellBasedUnixGroupsMapping and the JniBasedUnixGroupsMapping + //return the same groups + String user = UserGroupInformation.getCurrentUser().getShortUserName(); + testForUser(user); + //check for a dummy non-existent user (both the implementations should + //return an empty list + testForUser("fooBarBaz1234DoesNotExist"); + } + private void testForUser(String user) throws Exception { + GroupMappingServiceProvider g = new ShellBasedUnixGroupsMapping(); + List<String> shellBasedGroups = g.getGroups(user); + g = new JniBasedUnixGroupsMapping(); + List<String> jniBasedGroups = g.getGroups(user); + + String[] shellBasedGroupsArray = shellBasedGroups.toArray(new String[0]); + Arrays.sort(shellBasedGroupsArray); + String[] jniBasedGroupsArray = jniBasedGroups.toArray(new String[0]); + Arrays.sort(jniBasedGroupsArray); + + if (!Arrays.equals(shellBasedGroupsArray, jniBasedGroupsArray)) { + fail("Groups returned by " + + ShellBasedUnixGroupsMapping.class.getCanonicalName() + + " and " + + JniBasedUnixGroupsMapping.class.getCanonicalName() + + " didn't match for " + user); + } + } +}