HADOOP-9439. JniBasedUnixGroupsMapping: fix some crash bugs (Colin Patrick McCabe)
git-svn-id: https://svn.apache.org/repos/asf/hadoop/common/trunk@1496112 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
87d67c8c55
commit
ca35235b04
|
@ -551,6 +551,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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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];
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
char *grpBuf = NULL;
|
||||
cuser = (*env)->GetStringUTFChars(env, juser, NULL);
|
||||
if (cuser == NULL) {
|
||||
goto cleanup;
|
||||
}
|
||||
extern jobject pw_lock_object;
|
||||
|
||||
/*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;
|
||||
}
|
||||
JNIEXPORT void JNICALL
|
||||
Java_org_apache_hadoop_security_JniBasedUnixGroupsMapping_anchorNative(
|
||||
JNIEnv *env, jclass clazz)
|
||||
{
|
||||
jobject string_clazz;
|
||||
|
||||
jgroups = (jobjectArray)(*env)->NewObjectArray(env, ngroups,
|
||||
(*env)->FindClass(env, "java/lang/String"), NULL);
|
||||
if (jgroups == NULL) {
|
||||
error = -1;
|
||||
goto cleanup;
|
||||
g_log_error_method = (*env)->GetStaticMethodID(env, clazz, "logError",
|
||||
"(ILjava/lang/String;)V");
|
||||
if (!g_log_error_method) {
|
||||
return; // an exception has been raised
|
||||
}
|
||||
|
||||
/*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;
|
||||
string_clazz = (*env)->FindClass(env, "java/lang/String");
|
||||
if (!string_clazz) {
|
||||
return; // an exception has been raised
|
||||
}
|
||||
|
||||
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;
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
pw_lock_locked = 1;
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
done:
|
||||
if (pw_lock_locked) {
|
||||
(*env)->MonitorExit(env, pw_lock_object);
|
||||
}
|
||||
if (username) {
|
||||
(*env)->ReleaseStringUTFChars(env, jusername, username);
|
||||
}
|
||||
if (uinfo) {
|
||||
hadoop_user_info_free(uinfo);
|
||||
}
|
||||
if (ginfo) {
|
||||
hadoop_group_info_free(ginfo);
|
||||
}
|
||||
if (jgroupname) {
|
||||
(*env)->DeleteLocalRef(env, jgroupname);
|
||||
}
|
||||
return jgroups;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
Loading…
Reference in New Issue