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:
parent
ea0b21af15
commit
9ee7c2a077
|
@ -19,15 +19,22 @@
|
||||||
package org.apache.hadoop.fs.shell;
|
package org.apache.hadoop.fs.shell;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.net.URI;
|
||||||
import java.text.SimpleDateFormat;
|
import java.text.SimpleDateFormat;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
|
import java.util.Set;
|
||||||
import org.apache.hadoop.util.StringUtils;
|
import org.apache.hadoop.util.StringUtils;
|
||||||
|
|
||||||
import org.apache.hadoop.classification.InterfaceAudience;
|
import org.apache.hadoop.classification.InterfaceAudience;
|
||||||
import org.apache.hadoop.classification.InterfaceStability;
|
import org.apache.hadoop.classification.InterfaceStability;
|
||||||
import org.apache.hadoop.fs.FileStatus;
|
import org.apache.hadoop.fs.FileStatus;
|
||||||
|
import org.apache.hadoop.fs.FileSystem;
|
||||||
import org.apache.hadoop.fs.Path;
|
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.
|
* 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 dirRecurse;
|
||||||
|
|
||||||
protected boolean humanReadable = false;
|
protected boolean humanReadable = false;
|
||||||
|
private Set<URI> aclNotSupportedFsSet = Sets.newHashSet();
|
||||||
|
|
||||||
protected String formatSize(long size) {
|
protected String formatSize(long size) {
|
||||||
return humanReadable
|
return humanReadable
|
||||||
? StringUtils.TraditionalBinaryPrefix.long2String(size, "", 1)
|
? StringUtils.TraditionalBinaryPrefix.long2String(size, "", 1)
|
||||||
|
@ -108,7 +117,7 @@ class Ls extends FsCommand {
|
||||||
FileStatus stat = item.stat;
|
FileStatus stat = item.stat;
|
||||||
String line = String.format(lineFormat,
|
String line = String.format(lineFormat,
|
||||||
(stat.isDirectory() ? "d" : "-"),
|
(stat.isDirectory() ? "d" : "-"),
|
||||||
stat.getPermission(),
|
stat.getPermission() + (hasAcl(item) ? "+" : ""),
|
||||||
(stat.isFile() ? stat.getReplication() : "-"),
|
(stat.isFile() ? stat.getReplication() : "-"),
|
||||||
stat.getOwner(),
|
stat.getOwner(),
|
||||||
stat.getGroup(),
|
stat.getGroup(),
|
||||||
|
@ -146,6 +155,49 @@ class Ls extends FsCommand {
|
||||||
lineFormat = fmt.toString();
|
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) {
|
private int maxLength(int n, Object value) {
|
||||||
return Math.max(n, (value != null) ? String.valueOf(value).length() : 0);
|
return Math.max(n, (value != null) ? String.valueOf(value).length() : 0);
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,15 +20,27 @@ package org.apache.hadoop.fs.shell;
|
||||||
import static org.junit.Assert.*;
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.net.URI;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import org.apache.hadoop.conf.Configuration;
|
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.FsShell;
|
||||||
|
import org.apache.hadoop.fs.Path;
|
||||||
import org.apache.hadoop.fs.permission.AclEntry;
|
import org.apache.hadoop.fs.permission.AclEntry;
|
||||||
import org.apache.hadoop.fs.permission.AclEntryScope;
|
import org.apache.hadoop.fs.permission.AclEntryScope;
|
||||||
import org.apache.hadoop.fs.permission.AclEntryType;
|
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.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.apache.hadoop.util.ToolRunner;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
@ -128,6 +140,97 @@ public class TestAclCommands {
|
||||||
assertEquals("Parsed Acl not correct", expectedList, parsedList);
|
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 {
|
private int runCommand(String[] commands) throws Exception {
|
||||||
return ToolRunner.run(conf, new FsShell(), commands);
|
return ToolRunner.run(conf, new FsShell(), commands);
|
||||||
}
|
}
|
||||||
|
|
|
@ -80,6 +80,8 @@ HDFS-4685 (Unreleased)
|
||||||
|
|
||||||
HDFS-5933. Optimize the FSImage layout for ACLs (Haohui Mai via cnauroth)
|
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
|
OPTIMIZATIONS
|
||||||
|
|
||||||
BUG FIXES
|
BUG FIXES
|
||||||
|
|
Loading…
Reference in New Issue