HDFS-5932. Ls should display the ACL bit. Contributed by Chris Nauroth.

git-svn-id: https://svn.apache.org/repos/asf/hadoop/common/branches/HDFS-4685@1567850 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Haohui Mai 2014-02-13 05:51:28 +00:00
parent ea0b21af15
commit 9ee7c2a077
3 changed files with 158 additions and 1 deletions

View File

@ -19,15 +19,22 @@
package org.apache.hadoop.fs.shell;
import java.io.IOException;
import java.net.URI;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.LinkedList;
import java.util.Set;
import org.apache.hadoop.util.StringUtils;
import org.apache.hadoop.classification.InterfaceAudience;
import org.apache.hadoop.classification.InterfaceStability;
import org.apache.hadoop.fs.FileStatus;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.ipc.RemoteException;
import org.apache.hadoop.ipc.RpcNoSuchMethodException;
import com.google.common.collect.Sets;
/**
* Get a listing of all files in that match the file patterns.
@ -66,6 +73,8 @@ class Ls extends FsCommand {
protected boolean dirRecurse;
protected boolean humanReadable = false;
private Set<URI> aclNotSupportedFsSet = Sets.newHashSet();
protected String formatSize(long size) {
return humanReadable
? StringUtils.TraditionalBinaryPrefix.long2String(size, "", 1)
@ -108,7 +117,7 @@ class Ls extends FsCommand {
FileStatus stat = item.stat;
String line = String.format(lineFormat,
(stat.isDirectory() ? "d" : "-"),
stat.getPermission(),
stat.getPermission() + (hasAcl(item) ? "+" : ""),
(stat.isFile() ? stat.getReplication() : "-"),
stat.getOwner(),
stat.getGroup(),
@ -146,6 +155,49 @@ class Ls extends FsCommand {
lineFormat = fmt.toString();
}
/**
* Calls getAclStatus to determine if the given item has an ACL. For
* compatibility, this method traps errors caused by the RPC method missing
* from the server side. This would happen if the client was connected to an
* old NameNode that didn't have the ACL APIs. This method also traps the
* case of the client-side FileSystem not implementing the ACL APIs.
* FileSystem instances that do not support ACLs are remembered. This
* prevents the client from sending multiple failing RPC calls during a
* recursive ls.
*
* @param item PathData item to check
* @return boolean true if item has an ACL
* @throws IOException if there is a failure
*/
private boolean hasAcl(PathData item) throws IOException {
FileSystem fs = item.fs;
if (aclNotSupportedFsSet.contains(fs.getUri())) {
// This FileSystem failed to run the ACL API in an earlier iteration.
return false;
}
try {
return !fs.getAclStatus(item.path).getEntries().isEmpty();
} catch (RemoteException e) {
// If this is a RpcNoSuchMethodException, then the client is connected to
// an older NameNode that doesn't support ACLs. Keep going.
IOException e2 = e.unwrapRemoteException(RpcNoSuchMethodException.class);
if (!(e2 instanceof RpcNoSuchMethodException)) {
throw e;
}
} catch (IOException e) {
// The NameNode supports ACLs, but they are not enabled. Keep going.
String message = e.getMessage();
if (message != null && !message.contains("ACLs has been disabled")) {
throw e;
}
} catch (UnsupportedOperationException e) {
// The underlying FileSystem doesn't implement ACLs. Keep going.
}
// Remember that this FileSystem cannot support ACLs.
aclNotSupportedFsSet.add(fs.getUri());
return false;
}
private int maxLength(int n, Object value) {
return Math.max(n, (value != null) ? String.valueOf(value).length() : 0);
}

View File

@ -20,15 +20,27 @@ package org.apache.hadoop.fs.shell;
import static org.junit.Assert.*;
import java.io.IOException;
import java.net.URI;
import java.util.ArrayList;
import java.util.List;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.CommonConfigurationKeys;
import org.apache.hadoop.fs.FSDataInputStream;
import org.apache.hadoop.fs.FSDataOutputStream;
import org.apache.hadoop.fs.FileStatus;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.FsShell;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.fs.permission.AclEntry;
import org.apache.hadoop.fs.permission.AclEntryScope;
import org.apache.hadoop.fs.permission.AclEntryType;
import org.apache.hadoop.fs.permission.AclStatus;
import org.apache.hadoop.fs.permission.FsAction;
import org.apache.hadoop.fs.permission.FsPermission;
import org.apache.hadoop.ipc.RemoteException;
import org.apache.hadoop.ipc.RpcNoSuchMethodException;
import org.apache.hadoop.util.Progressable;
import org.apache.hadoop.util.ToolRunner;
import org.junit.Before;
import org.junit.Test;
@ -128,6 +140,97 @@ public class TestAclCommands {
assertEquals("Parsed Acl not correct", expectedList, parsedList);
}
@Test
public void testLsNoRpcForGetAclStatus() throws Exception {
Configuration conf = new Configuration();
conf.set(CommonConfigurationKeys.FS_DEFAULT_NAME_KEY, "stubfs:///");
conf.setClass("fs.stubfs.impl", StubFileSystem.class, FileSystem.class);
conf.setBoolean("stubfs.noRpcForGetAclStatus", true);
assertEquals("ls must succeed even if getAclStatus RPC does not exist.",
0, ToolRunner.run(conf, new FsShell(), new String[] { "-ls", "/" }));
}
@Test
public void testLsAclsUnsupported() throws Exception {
Configuration conf = new Configuration();
conf.set(CommonConfigurationKeys.FS_DEFAULT_NAME_KEY, "stubfs:///");
conf.setClass("fs.stubfs.impl", StubFileSystem.class, FileSystem.class);
assertEquals("ls must succeed even if FileSystem does not implement ACLs.",
0, ToolRunner.run(conf, new FsShell(), new String[] { "-ls", "/" }));
}
public static class StubFileSystem extends FileSystem {
public FSDataOutputStream append(Path f, int bufferSize,
Progressable progress) throws IOException {
return null;
}
public FSDataOutputStream create(Path f, FsPermission permission,
boolean overwrite, int bufferSize, short replication, long blockSize,
Progressable progress) throws IOException {
return null;
}
@Override
public boolean delete(Path f, boolean recursive) throws IOException {
return false;
}
public AclStatus getAclStatus(Path path) throws IOException {
if (getConf().getBoolean("stubfs.noRpcForGetAclStatus", false)) {
throw new RemoteException(RpcNoSuchMethodException.class.getName(),
"test exception");
}
return super.getAclStatus(path);
}
@Override
public FileStatus getFileStatus(Path f) throws IOException {
return null;
}
@Override
public URI getUri() {
return URI.create("stubfs:///");
}
@Override
public Path getWorkingDirectory() {
return null;
}
@Override
public FileStatus[] listStatus(Path f) throws IOException {
FsPermission perm = new FsPermission(FsAction.ALL, FsAction.READ_EXECUTE,
FsAction.READ_EXECUTE);
Path path = new Path("/foo");
FileStatus stat = new FileStatus(1000, true, 3, 1000, 0, 0, perm, "owner",
"group", path);
return new FileStatus[] { stat };
}
@Override
public boolean mkdirs(Path f, FsPermission permission)
throws IOException {
return false;
}
@Override
public FSDataInputStream open(Path f, int bufferSize) throws IOException {
return null;
}
@Override
public boolean rename(Path src, Path dst) throws IOException {
return false;
}
@Override
public void setWorkingDirectory(Path dir) {
}
}
private int runCommand(String[] commands) throws Exception {
return ToolRunner.run(conf, new FsShell(), commands);
}

View File

@ -80,6 +80,8 @@ HDFS-4685 (Unreleased)
HDFS-5933. Optimize the FSImage layout for ACLs (Haohui Mai via cnauroth)
HDFS-5932. Ls should display the ACL bit (Chris Nauroth via wheat9)
OPTIMIZATIONS
BUG FIXES