blob: a728c77cd7cd31f11e81536f5bcf3880109dcbd4 [file] [log] [blame]
/**
* 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>
// Assuming the average of user name length of 15 bytes,
// 8KB buffer will be large enough for a group with about 500 members.
// 2MB buffer will be large enough for a group with about 130K members.
#define INITIAL_GROUP_BUFFER_SIZE (8*1024)
#define MAX_GROUP_BUFFER_SIZE (2*1024*1024)
struct hadoop_group_info *hadoop_group_info_alloc(void)
{
struct hadoop_group_info *ginfo;
char *buf;
ginfo = calloc(1, sizeof(struct hadoop_group_info));
buf = malloc(INITIAL_GROUP_BUFFER_SIZE);
if (!buf) {
free(ginfo);
return NULL;
}
ginfo->buf_sz = INITIAL_GROUP_BUFFER_SIZE;
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 ret;
size_t buf_sz;
char *nbuf;
hadoop_group_info_clear(ginfo);
for (;;) {
// On success, the following call returns 0 and group is set to non-NULL.
group = NULL;
ret = getgrgid_r(gid, &ginfo->group, ginfo->buf,
ginfo->buf_sz, &group);
switch(ret) {
case 0:
if (!group) {
// Not found.
return ENOENT;
}
// Found.
return 0;
case EINTR:
// EINTR: a signal was handled and this thread was allowed to continue.
break;
case ERANGE:
// ERANGE: the buffer was not big enough.
if (ginfo->buf_sz == MAX_GROUP_BUFFER_SIZE) {
// Already tried with the max size.
return ENOMEM;
}
buf_sz = ginfo->buf_sz * 2;
if (buf_sz > MAX_GROUP_BUFFER_SIZE) {
buf_sz = MAX_GROUP_BUFFER_SIZE;
}
nbuf = realloc(ginfo->buf, buf_sz);
if (!nbuf) {
return ENOMEM;
}
ginfo->buf = nbuf;
ginfo->buf_sz = buf_sz;
break;
default:
// Lookup failed.
return getgrgid_error_translate(ret);
}
}
}
#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