Ethanlm commented on a change in pull request #3366: URL: https://github.com/apache/storm/pull/3366#discussion_r669093788
########## File path: storm-core/src/native/worker-launcher/impl/utils/storm_user_info.c ########## @@ -0,0 +1,275 @@ +/** + * 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 "storm_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 +// 1KB buffer should be large enough to store a passwd record in most +// cases, but it can get bigger if each field is maximally used. The +// max is defined to avoid buggy libraries making us run out of memory. +#define MAX_USER_BUFFER_SIZE (32*1024) + +struct storm_user_info *storm_user_info_alloc(void) +{ + struct storm_user_info *uinfo; + long buf_sz; + char *buf; + + uinfo = calloc(1, sizeof(struct storm_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 storm_user_info_clear(struct storm_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 storm_user_info_free(struct storm_user_info *uinfo) +{ + free(uinfo->buf); + storm_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 storm_user_info_fetch(struct storm_user_info *uinfo, + const char *username) +{ + struct passwd *pwd; + int ret; + size_t buf_sz; + char *nbuf; + + storm_user_info_clear(uinfo); + for (;;) { + // On success, the following call returns 0 and pwd is set to non-NULL. + pwd = NULL; + ret = getpwnam_r(username, &uinfo->pwd, uinfo->buf, + uinfo->buf_sz, &pwd); + switch(ret) { + case 0: + if (!pwd) { + // 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 (uinfo->buf_sz == MAX_USER_BUFFER_SIZE) { + // Already tried with the max size. + return ENOMEM; + } + buf_sz = uinfo->buf_sz * 2; + if (buf_sz > MAX_USER_BUFFER_SIZE) { + buf_sz = MAX_USER_BUFFER_SIZE; + } + nbuf = realloc(uinfo->buf, buf_sz); + if (!nbuf) { + return ENOMEM; + } + uinfo->buf = nbuf; + uinfo->buf_sz = buf_sz; + break; + default: + // Lookup failed. + return getpwnam_error_translate(ret); + } + } +} + +static int put_primary_gid_first(struct storm_user_info *uinfo) +{ + int i, num_gids = uinfo->num_gids; + gid_t first_gid; + gid_t gid; + gid_t primary = uinfo->pwd.pw_gid; + + if (num_gids < 1) { + // There are no gids, but we expected at least one. + return EINVAL; + } + first_gid = uinfo->gids[0]; + if (first_gid == primary) { + // First gid is already the primary. + return 0; + } + for (i = 1; i < num_gids; i++) { + gid = uinfo->gids[i]; + if (gid == primary) { + // swap first gid and this gid. + uinfo->gids[0] = gid; + uinfo->gids[i] = first_gid; + return 0; + } + } + // Did not find the primary gid in the list. + return EINVAL; +} + +int storm_user_info_getgroups(struct storm_user_info *uinfo) +{ + int ret, ngroups; + gid_t *ngids; + + if (!uinfo->pwd.pw_name) { + // invalid user info + return EINVAL; + } + 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); + // Return value is different on Linux vs. FreeBSD. Linux: the number of groups + // or -1 on error. FreeBSD: 0 on success or -1 on error. Unfortunately, we + // can't accept a 0 return on Linux, because buggy implementations have been + // observed to return 0 but leave the other out parameters in an indeterminate + // state. This deviates from the man page, but it has been observed in + // practice. See issue HADOOP-10989 for details. +#ifdef __linux__ + if (ret > 0) { +#else + if (ret >= 0) { +#endif + uinfo->num_gids = ngroups; + ret = put_primary_gid_first(uinfo); + if (ret) { + return ret; + } + return 0; + } else if (ret != -1) { + // Any return code that is not -1 is considered as error. Review comment: when it is on linux, we have ``` if (ret > 0) { } else if (ret != -1) { //ret could be zero } ``` I will avoid causing confusion here. The original statement look fine to me. -- This is an automated message from the Apache Git Service. To respond to the message, please log on to GitHub and use the URL above to go to the specific comment. To unsubscribe, e-mail: dev-unsubscr...@storm.apache.org For queries about this service, please contact Infrastructure at: us...@infra.apache.org