Skip to content

Commit 1a12911

Browse files
committed
HADOOP-6864. Provide a JNI-based implementation of ShellBasedUnixGroupsNetgroupMapping (implementation of GroupMappingServiceProvider)
git-svn-id: https://svn.apache.org/repos/asf/hadoop/common/trunk@1052420 13f79535-47bb-0310-9956-ffa450edef68
1 parent 5242106 commit 1a12911

File tree

16 files changed

+5889
-6420
lines changed

16 files changed

+5889
-6420
lines changed

CHANGES.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -242,6 +242,9 @@ Release 0.22.0 - Unreleased
242242
classpath. (Patrick Angeles via eli)
243243

244244
HADOOP-6298. Add copyBytes to Text and BytesWritable. (omalley)
245+
246+
HADOOP-6864. Provide a JNI-based implementation of ShellBasedUnixGroupsNetgroupMapping
247+
(implementation of GroupMappingServiceProvider) (Erik Seffl via boryas)
245248

246249
OPTIMIZATIONS
247250

build.xml

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -396,6 +396,15 @@
396396
<class name="org.apache.hadoop.io.nativeio.NativeIO" />
397397
</javah>
398398

399+
<javah
400+
classpath="${build.classes}"
401+
destdir="${build.native}/src/org/apache/hadoop/security"
402+
force="yes"
403+
verbose="yes"
404+
>
405+
<class name="org.apache.hadoop.security.JniBasedUnixGroupsNetgroupMapping" />
406+
</javah>
407+
399408
<exec dir="${build.native}" executable="sh" failonerror="true">
400409
<env key="OS_NAME" value="${os.name}"/>
401410
<env key="OS_ARCH" value="${os.arch}"/>
@@ -717,6 +726,10 @@
717726
</syspropertyset>
718727
<sysproperty key="test.system.hdrc.deployed.hadoopconfdir"
719728
value="@{hadoop.conf.dir.deployed}" />
729+
<!-- user to group mapping class for TestAccessControlList -->
730+
<syspropertyset dynamic="no">
731+
<propertyref name="TestAccessControlListGroupMapping"/>
732+
</syspropertyset>
720733
<formatter type="${test.junit.output.format}" />
721734
<batchtest todir="@{test.dir}" if="tests.notestcase">
722735
<fileset dir="@{fileset.dir}/core"

src/java/org/apache/hadoop/security/GroupMappingServiceProvider.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,4 +39,15 @@ public interface GroupMappingServiceProvider {
3939
* @throws IOException
4040
*/
4141
public List<String> getGroups(String user) throws IOException;
42+
/**
43+
* Refresh the cache of groups and user mapping
44+
* @throws IOException
45+
*/
46+
public void cacheGroupsRefresh() throws IOException;
47+
/**
48+
* Caches the group user information
49+
* @param groups list of groups to add to cache
50+
* @throws IOException
51+
*/
52+
public void cacheGroupsAdd(List<String> groups) throws IOException;
4253
}

src/java/org/apache/hadoop/security/Groups.java

Lines changed: 39 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -98,22 +98,56 @@ public List<String> getGroups(String user) throws IOException {
9898
*/
9999
public void refresh() {
100100
LOG.info("clearing userToGroupsMap cache");
101+
try {
102+
impl.cacheGroupsRefresh();
103+
} catch (IOException e) {
104+
LOG.warn("Error refreshing groups cache", e);
105+
}
101106
userToGroupsMap.clear();
102107
}
103-
108+
109+
/**
110+
* Add groups to cache
111+
*
112+
* @param groups list of groups to add to cache
113+
*/
114+
public void cacheGroupsAdd(List<String> groups) {
115+
try {
116+
impl.cacheGroupsAdd(groups);
117+
} catch (IOException e) {
118+
LOG.warn("Error caching groups", e);
119+
}
120+
}
121+
122+
/**
123+
* Class to hold the cached groups
124+
*/
104125
private static class CachedGroups {
105126
final long timestamp;
106127
final List<String> groups;
107128

129+
/**
130+
* Create and initialize group cache
131+
*/
108132
CachedGroups(List<String> groups) {
109133
this.groups = groups;
110134
this.timestamp = System.currentTimeMillis();
111135
}
112136

137+
/**
138+
* Returns time of last cache update
139+
*
140+
* @return time of last cache update
141+
*/
113142
public long getTimestamp() {
114143
return timestamp;
115144
}
116145

146+
/**
147+
* Get list of cached groups
148+
*
149+
* @return cached groups
150+
*/
117151
public List<String> getGroups() {
118152
return groups;
119153
}
@@ -128,13 +162,15 @@ public List<String> getGroups() {
128162
public static Groups getUserToGroupsMappingService() {
129163
return getUserToGroupsMappingService(new Configuration());
130164
}
131-
165+
132166
/**
133167
* Get the groups being used to map user-to-groups.
134168
* @param conf
135169
* @return the groups being used to map user-to-groups.
136170
*/
137-
public static Groups getUserToGroupsMappingService(Configuration conf) {
171+
public static synchronized Groups getUserToGroupsMappingService(
172+
Configuration conf) {
173+
138174
if(GROUPS == null) {
139175
if(LOG.isDebugEnabled()) {
140176
LOG.debug(" Creating new Groups object");

src/java/org/apache/hadoop/security/JniBasedUnixGroupsMapping.java

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,9 @@
2222
import java.util.Arrays;
2323
import java.util.List;
2424

25+
import org.apache.hadoop.classification.InterfaceAudience;
26+
import org.apache.hadoop.classification.InterfaceStability;
27+
2528
import org.apache.commons.logging.Log;
2629
import org.apache.commons.logging.LogFactory;
2730
import org.apache.hadoop.util.NativeCodeLoader;
@@ -31,17 +34,19 @@
3134
* that invokes libC calls to get the group
3235
* memberships of a given user.
3336
*/
37+
@InterfaceAudience.LimitedPrivate({"HDFS", "MapReduce"})
38+
@InterfaceStability.Evolving
3439
public class JniBasedUnixGroupsMapping implements GroupMappingServiceProvider {
3540

3641
private static final Log LOG =
37-
LogFactory.getLog(ShellBasedUnixGroupsMapping.class);
42+
LogFactory.getLog(JniBasedUnixGroupsMapping.class);
3843

3944
native String[] getGroupForUser(String user);
4045

4146
static {
4247
if (!NativeCodeLoader.isNativeCodeLoaded()) {
4348
throw new RuntimeException("Bailing out since native library couldn't " +
44-
"be loaded");
49+
"be loaded");
4550
}
4651
LOG.info("Using JniBasedUnixGroupsMapping for Group resolution");
4752
}
@@ -52,9 +57,18 @@ public List<String> getGroups(String user) throws IOException {
5257
try {
5358
groups = getGroupForUser(user);
5459
} catch (Exception e) {
55-
LOG.warn("Got exception while trying to obtain the groups for user "
56-
+ user);
60+
LOG.warn("Error getting groups for " + user, e);
5761
}
5862
return Arrays.asList(groups);
5963
}
64+
65+
@Override
66+
public void cacheGroupsRefresh() throws IOException {
67+
// does nothing in this provider of user to groups mapping
68+
}
69+
70+
@Override
71+
public void cacheGroupsAdd(List<String> groups) throws IOException {
72+
// does nothing in this provider of user to groups mapping
73+
}
6074
}
Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
/**
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing, software
13+
* distributed under the License is distributed on an "AS IS" BASIS,
14+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
* See the License for the specific language governing permissions and
16+
* limitations under the License.
17+
*/
18+
19+
package org.apache.hadoop.security;
20+
21+
import java.io.IOException;
22+
import java.util.Arrays;
23+
import java.util.List;
24+
import java.util.LinkedList;
25+
26+
import org.apache.hadoop.classification.InterfaceAudience;
27+
import org.apache.hadoop.classification.InterfaceStability;
28+
29+
import org.apache.commons.logging.Log;
30+
import org.apache.commons.logging.LogFactory;
31+
import org.apache.hadoop.util.NativeCodeLoader;
32+
33+
import org.apache.hadoop.security.NetgroupCache;
34+
35+
/**
36+
* A JNI-based implementation of {@link GroupMappingServiceProvider}
37+
* that invokes libC calls to get the group
38+
* memberships of a given user.
39+
*/
40+
@InterfaceAudience.LimitedPrivate({"HDFS", "MapReduce"})
41+
@InterfaceStability.Evolving
42+
public class JniBasedUnixGroupsNetgroupMapping
43+
extends JniBasedUnixGroupsMapping {
44+
45+
private static final Log LOG = LogFactory.getLog(
46+
JniBasedUnixGroupsNetgroupMapping.class);
47+
48+
native String[] getUsersForNetgroupJNI(String group);
49+
50+
static {
51+
if (!NativeCodeLoader.isNativeCodeLoaded()) {
52+
throw new RuntimeException("Bailing out since native library couldn't " +
53+
"be loaded");
54+
}
55+
LOG.info("Using JniBasedUnixGroupsNetgroupMapping for Netgroup resolution");
56+
}
57+
58+
/**
59+
* Gets unix groups and netgroups for the user.
60+
*
61+
* It gets all unix groups as returned by id -Gn but it
62+
* only returns netgroups that are used in ACLs (there is
63+
* no way to get all netgroups for a given user, see
64+
* documentation for getent netgroup)
65+
*/
66+
@Override
67+
public List<String> getGroups(String user) throws IOException {
68+
// parent gets unix groups
69+
List<String> groups = new LinkedList<String>(super.getGroups(user));
70+
NetgroupCache.getNetgroups(user, groups);
71+
return groups;
72+
}
73+
74+
/**
75+
* Refresh the netgroup cache
76+
*/
77+
@Override
78+
public void cacheGroupsRefresh() throws IOException {
79+
List<String> groups = NetgroupCache.getNetgroupNames();
80+
NetgroupCache.clear();
81+
cacheGroupsAdd(groups);
82+
}
83+
84+
/**
85+
* Add a group to cache, only netgroups are cached
86+
*
87+
* @param groups list of group names to add to cache
88+
*/
89+
@Override
90+
public void cacheGroupsAdd(List<String> groups) throws IOException {
91+
for(String group: groups) {
92+
if(group.length() == 0) {
93+
// better safe than sorry (should never happen)
94+
} else if(group.charAt(0) == '@') {
95+
if(!NetgroupCache.isCached(group)) {
96+
NetgroupCache.add(group, getUsersForNetgroup(group));
97+
}
98+
} else {
99+
// unix group, not caching
100+
}
101+
}
102+
}
103+
104+
/**
105+
* Calls JNI function to get users for a netgroup, since C functions
106+
* are not reentrant we need to make this synchronized (see
107+
* documentation for setnetgrent, getnetgrent and endnetgrent)
108+
*
109+
* @param netgroup return users for this netgroup
110+
* @return list of users for a given netgroup
111+
*/
112+
protected synchronized List<String> getUsersForNetgroup(String netgroup) {
113+
String[] users = null;
114+
try {
115+
// JNI code does not expect '@' at the begining of the group name
116+
users = getUsersForNetgroupJNI(netgroup.substring(1));
117+
} catch (Exception e) {
118+
LOG.warn("error getting users for netgroup " + netgroup, e);
119+
}
120+
if (users != null && users.length != 0) {
121+
return Arrays.asList(users);
122+
}
123+
return new LinkedList<String>();
124+
}
125+
}

0 commit comments

Comments
 (0)