diff --git a/hadoop-common-project/hadoop-common/CHANGES.txt b/hadoop-common-project/hadoop-common/CHANGES.txt
index f43db1987a7..8fa6594e795 100644
--- a/hadoop-common-project/hadoop-common/CHANGES.txt
+++ b/hadoop-common-project/hadoop-common/CHANGES.txt
@@ -141,6 +141,9 @@ Release 2.5.0 - UNRELEASED
HADOOP-10585. Retry polices ignore interrupted exceptions (Daryn Sharp via
jeagles)
+ HADOOP-10401. ShellBasedUnixGroupsMapping#getGroups does not always return
+ primary group first (Akira AJISAKA via Colin Patrick McCabe)
+
Release 2.4.1 - UNRELEASED
INCOMPATIBLE CHANGES
diff --git a/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/security/ShellBasedUnixGroupsMapping.java b/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/security/ShellBasedUnixGroupsMapping.java
index 3689ebaa06e..11056eb00f6 100644
--- a/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/security/ShellBasedUnixGroupsMapping.java
+++ b/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/security/ShellBasedUnixGroupsMapping.java
@@ -74,7 +74,8 @@ public class ShellBasedUnixGroupsMapping
* Get the current user's group list from Unix by running the command 'groups'
* NOTE. For non-existing user it will return EMPTY list
* @param user user name
- * @return the groups list that the user
belongs to
+ * @return the groups list that the user
belongs to. The primary
+ * group is returned first.
* @throws IOException if encounter any error when running the command
*/
private static List getUnixGroups(final String user) throws IOException {
@@ -84,6 +85,7 @@ public class ShellBasedUnixGroupsMapping
} catch (ExitCodeException e) {
// if we didn't get the group - just return empty list;
LOG.warn("got exception trying to get groups for user " + user, e);
+ return new LinkedList();
}
StringTokenizer tokenizer =
@@ -92,6 +94,17 @@ public class ShellBasedUnixGroupsMapping
while (tokenizer.hasMoreTokens()) {
groups.add(tokenizer.nextToken());
}
+
+ // remove duplicated primary group
+ if (!Shell.WINDOWS) {
+ for (int i = 1; i < groups.size(); i++) {
+ if (groups.get(i).equals(groups.get(0))) {
+ groups.remove(i);
+ break;
+ }
+ }
+ }
+
return groups;
}
}
diff --git a/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/util/Shell.java b/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/util/Shell.java
index 61c0e8ddc31..0117fe5f117 100644
--- a/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/util/Shell.java
+++ b/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/util/Shell.java
@@ -132,11 +132,17 @@ abstract public class Shell {
: new String[]{"bash", "-c", "groups"};
}
- /** a Unix command to get a given user's groups list */
+ /**
+ * a Unix command to get a given user's groups list.
+ * If the OS is not WINDOWS, the command will get the user's primary group
+ * first and finally get the groups list which includes the primary group.
+ * i.e. the user's primary group will be included twice.
+ */
public static String[] getGroupsForUserCommand(final String user) {
//'groups username' command return is non-consistent across different unixes
return (WINDOWS)? new String[] { WINUTILS, "groups", "-F", "\"" + user + "\""}
- : new String [] {"bash", "-c", "id -Gn " + user};
+ : new String [] {"bash", "-c", "id -gn " + user
+ + "&& id -Gn " + user};
}
/** a Unix command to get a given netgroup's user list */