You are viewing a plain text version of this content. The canonical link for it is here.
Posted to common-commits@hadoop.apache.org by cn...@apache.org on 2013/12/27 21:41:17 UTC

svn commit: r1553735 - in /hadoop/common/branches/HDFS-4685/hadoop-common-project/hadoop-common/src: main/java/org/apache/hadoop/fs/permission/ main/java/org/apache/hadoop/fs/shell/ test/java/org/apache/hadoop/fs/shell/

Author: cnauroth
Date: Fri Dec 27 20:41:16 2013
New Revision: 1553735

URL: http://svn.apache.org/r1553735
Log:
HADOOP-10187. add getfacl and setfacl with minimal support for getting and setting ACLs. Contributed by Vinay.

Added:
    hadoop/common/branches/HDFS-4685/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/fs/shell/AclCommands.java
    hadoop/common/branches/HDFS-4685/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/fs/shell/TestAclCommands.java
Modified:
    hadoop/common/branches/HDFS-4685/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/fs/permission/FsAction.java
    hadoop/common/branches/HDFS-4685/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/fs/shell/FsCommand.java

Modified: hadoop/common/branches/HDFS-4685/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/fs/permission/FsAction.java
URL: http://svn.apache.org/viewvc/hadoop/common/branches/HDFS-4685/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/fs/permission/FsAction.java?rev=1553735&r1=1553734&r2=1553735&view=diff
==============================================================================
--- hadoop/common/branches/HDFS-4685/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/fs/permission/FsAction.java (original)
+++ hadoop/common/branches/HDFS-4685/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/fs/permission/FsAction.java Fri Dec 27 20:41:16 2013
@@ -69,4 +69,21 @@ public enum FsAction {
   public FsAction not() {
     return vals[7 - ordinal()];
   }
+
+  /**
+   * Get the FsAction enum for String representation of permissions
+   * 
+   * @param permission
+   *          3-character string representation of permission. ex: rwx
+   * @return Returns FsAction enum if the corresponding FsAction exists for permission.
+   *         Otherwise returns null
+   */
+  public static FsAction getFsAction(String permission) {
+    for (FsAction fsAction : vals) {
+      if (fsAction.SYMBOL.equals(permission)) {
+        return fsAction;
+      }
+    }
+    return null;
+  }
 }

Added: hadoop/common/branches/HDFS-4685/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/fs/shell/AclCommands.java
URL: http://svn.apache.org/viewvc/hadoop/common/branches/HDFS-4685/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/fs/shell/AclCommands.java?rev=1553735&view=auto
==============================================================================
--- hadoop/common/branches/HDFS-4685/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/fs/shell/AclCommands.java (added)
+++ hadoop/common/branches/HDFS-4685/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/fs/shell/AclCommands.java Fri Dec 27 20:41:16 2013
@@ -0,0 +1,232 @@
+/**
+ * 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.
+ */
+package org.apache.hadoop.fs.shell;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.LinkedList;
+import java.util.List;
+
+import org.apache.hadoop.HadoopIllegalArgumentException;
+import org.apache.hadoop.classification.InterfaceAudience;
+import org.apache.hadoop.classification.InterfaceStability;
+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.util.StringUtils;
+
+/**
+ * Acl related operations
+ */
+@InterfaceAudience.Private
+@InterfaceStability.Evolving
+class AclCommands extends FsCommand {
+  private static String GET_FACL = "getfacl";
+  private static String SET_FACL = "setfacl";
+
+  public static void registerCommands(CommandFactory factory) {
+    factory.addClass(GetfaclCommand.class, "-" + GET_FACL);
+    factory.addClass(SetfaclCommand.class, "-" + SET_FACL);
+  }
+
+  /**
+   * Implementing the '-getfacl' command for the the FsShell.
+   */
+  public static class GetfaclCommand extends FsCommand {
+    public static String NAME = GET_FACL;
+    public static String USAGE = "[-R] <path>";
+    public static String DESCRIPTION = "Displays the Access Control Lists"
+        + " (ACLs) of files and directories. If a directory has a default ACL,"
+        + " then getfacl also displays the default ACL.\n"
+        + "-R: List the ACLs of all files and directories recursively.\n"
+        + "<path>: File or directory to list.\n";
+
+    @Override
+    protected void processOptions(LinkedList<String> args) throws IOException {
+      CommandFormat cf = new CommandFormat(0, Integer.MAX_VALUE, "R");
+      cf.parse(args);
+      setRecursive(cf.getOpt("R"));
+      if (args.isEmpty()) {
+        throw new HadoopIllegalArgumentException("<path> is missing");
+      }
+      if (args.size() > 1) {
+        throw new HadoopIllegalArgumentException("Too many arguments");
+      }
+    }
+
+    @Override
+    protected void processPath(PathData item) throws IOException {
+      AclStatus aclStatus = item.fs.getAclStatus(item.path);
+      out.println("# file: " + item.path);
+      out.println("# owner: " + aclStatus.getOwner());
+      out.println("# group: " + aclStatus.getGroup());
+      List<AclEntry> entries = aclStatus.getEntries();
+      if (aclStatus.isStickyBit()) {
+        String stickyFlag = "T";
+        for (AclEntry aclEntry : entries) {
+          if (aclEntry.getType() == AclEntryType.OTHER
+              && aclEntry.getScope() == AclEntryScope.ACCESS
+              && aclEntry.getPermission().implies(FsAction.EXECUTE)) {
+            stickyFlag = "t";
+            break;
+          }
+        }
+        out.println("# flags: --" + stickyFlag);
+      }
+      for (AclEntry entry : entries) {
+        out.println(entry);
+      }
+    }
+  }
+
+  /**
+   * Implementing the '-setfacl' command for the the FsShell.
+   */
+  public static class SetfaclCommand extends FsCommand {
+    public static String NAME = SET_FACL;
+    public static String USAGE = "[-R] [{-b|-k} {-m|-x <acl_spec>} <path>]"
+        + "|[--set <acl_spec> <path>]";
+    public static String DESCRIPTION = "Sets Access Control Lists (ACLs)"
+        + " of files and directories.\n" 
+        + "Options:\n"
+        + "-b :Remove all but the base ACL entries. The entries for user,"
+        + " group and others are retained for compatibility with permission "
+        + "bits.\n" 
+        + "-k :Remove the default ACL.\n"
+        + "-R :Apply operations to all files and directories recursively.\n"
+        + "-m :Modify ACL. New entries are added to the ACL, and existing"
+        + " entries are retained.\n"
+        + "-x :Remove specified ACL entries. Other ACL entries are retained.\n"
+        + "--set :Fully replace the ACL, discarding all existing entries."
+        + " The <acl_spec> must include entries for user, group, and others"
+        + " for compatibility with permission bits.\n"
+        + "<acl_spec>: Comma separated list of ACL entries.\n"
+        + "<path>: File or directory to modify.\n";
+
+    private static final String DEFAULT = "default";
+
+    Path path = null;
+    CommandFormat cf = new CommandFormat(0, Integer.MAX_VALUE, "b", "k", "R",
+        "m", "x", "-set");
+    List<AclEntry> aclEntries = null;
+
+    @Override
+    protected void processOptions(LinkedList<String> args) throws IOException {
+      cf.parse(args);
+      setRecursive(cf.getOpt("R"));
+      // Mix of remove and modify acl flags are not allowed
+      boolean bothRemoveOptions = cf.getOpt("b") && cf.getOpt("k");
+      boolean bothModifyOptions = cf.getOpt("m") && cf.getOpt("x");
+      boolean oneRemoveOption = cf.getOpt("b") || cf.getOpt("k");
+      boolean oneModifyOption = cf.getOpt("m") || cf.getOpt("x");
+      boolean setOption = cf.getOpt("-set");
+      if ((bothRemoveOptions || bothModifyOptions)
+          || (oneRemoveOption && oneModifyOption)
+          || (setOption && (oneRemoveOption || oneModifyOption))) {
+        throw new HadoopIllegalArgumentException(
+            "Specified flags contains both remove and modify flags");
+      }
+
+      // Only -m, -x and --set expects <acl_spec>
+      if (oneModifyOption || setOption) {
+        if (args.size() < 2) {
+          throw new HadoopIllegalArgumentException("<acl_spec> is missing");
+        }
+        aclEntries = parseAclSpec(args.removeFirst());
+      }
+
+      if (args.isEmpty()) {
+        throw new HadoopIllegalArgumentException("<path> is missing");
+      }
+      if (args.size() > 1) {
+        throw new HadoopIllegalArgumentException("Too many arguments");
+      }
+      path = new Path(args.removeFirst());
+    }
+
+    @Override
+    protected void processPath(PathData item) throws IOException {
+      if (cf.getOpt("b")) {
+        item.fs.removeAcl(item.path);
+      } else if (cf.getOpt("k")) {
+        item.fs.removeDefaultAcl(item.path);
+      } else if (cf.getOpt("m")) {
+        item.fs.modifyAclEntries(item.path, aclEntries);
+      } else if (cf.getOpt("x")) {
+        item.fs.removeAclEntries(item.path, aclEntries);
+      } else if (cf.getOpt("-set")) {
+        item.fs.setAcl(path, aclEntries);
+      }
+    }
+
+    /**
+     * Parse the aclSpec and returns the list of AclEntry objects.
+     * 
+     * @param aclSpec
+     * @return
+     */
+    private List<AclEntry> parseAclSpec(String aclSpec) {
+      List<AclEntry> aclEntries = new ArrayList<AclEntry>();
+      Collection<String> aclStrings = StringUtils.getStringCollection(aclSpec,
+          ",");
+      for (String aclStr : aclStrings) {
+        AclEntry.Builder builder = new AclEntry.Builder();
+        // Here "::" represent one empty string.
+        // StringUtils.getStringCollection() will ignore this.
+        String[] split = aclSpec.split(":");
+        if (split.length != 3
+            && !(split.length == 4 && DEFAULT.equals(split[0]))) {
+          throw new HadoopIllegalArgumentException("Invalid <aclSpec> : "
+              + aclStr);
+        }
+        int index = 0;
+        if (split.length == 4) {
+          assert DEFAULT.equals(split[0]);
+          // default entry
+          index++;
+          builder.setScope(AclEntryScope.DEFAULT);
+        }
+        String type = split[index++];
+        AclEntryType aclType = null;
+        try {
+          aclType = Enum.valueOf(AclEntryType.class, type.toUpperCase());
+          builder.setType(aclType);
+        } catch (IllegalArgumentException iae) {
+          throw new HadoopIllegalArgumentException(
+              "Invalid type of acl in <aclSpec> :" + aclStr);
+        }
+
+        builder.setName(split[index++]);
+
+        String permission = split[index++];
+        FsAction fsAction = FsAction.getFsAction(permission);
+        if (null == fsAction) {
+          throw new HadoopIllegalArgumentException(
+              "Invalid permission in <aclSpec> : " + aclStr);
+        }
+        builder.setPermission(fsAction);
+        aclEntries.add(builder.build());
+      }
+      return aclEntries;
+    }
+  }
+}

Modified: hadoop/common/branches/HDFS-4685/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/fs/shell/FsCommand.java
URL: http://svn.apache.org/viewvc/hadoop/common/branches/HDFS-4685/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/fs/shell/FsCommand.java?rev=1553735&r1=1553734&r2=1553735&view=diff
==============================================================================
--- hadoop/common/branches/HDFS-4685/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/fs/shell/FsCommand.java (original)
+++ hadoop/common/branches/HDFS-4685/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/fs/shell/FsCommand.java Fri Dec 27 20:41:16 2013
@@ -43,6 +43,7 @@ abstract public class FsCommand extends 
    * @param factory where to register the class
    */
   public static void registerCommands(CommandFactory factory) {
+    factory.registerCommands(AclCommands.class);
     factory.registerCommands(CopyCommands.class);
     factory.registerCommands(Count.class);
     factory.registerCommands(Delete.class);

Added: hadoop/common/branches/HDFS-4685/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/fs/shell/TestAclCommands.java
URL: http://svn.apache.org/viewvc/hadoop/common/branches/HDFS-4685/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/fs/shell/TestAclCommands.java?rev=1553735&view=auto
==============================================================================
--- hadoop/common/branches/HDFS-4685/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/fs/shell/TestAclCommands.java (added)
+++ hadoop/common/branches/HDFS-4685/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/fs/shell/TestAclCommands.java Fri Dec 27 20:41:16 2013
@@ -0,0 +1,65 @@
+/**
+ * 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.
+ */
+package org.apache.hadoop.fs.shell;
+
+import static org.junit.Assert.assertFalse;
+
+import java.io.IOException;
+
+import org.apache.hadoop.conf.Configuration;
+import org.apache.hadoop.fs.FsShell;
+import org.apache.hadoop.util.ToolRunner;
+import org.junit.Before;
+import org.junit.Test;
+
+public class TestAclCommands {
+
+  private Configuration conf = null;
+
+  @Before
+  public void setup() throws IOException {
+    conf = new Configuration();
+  }
+
+  @Test
+  public void testGetfaclValidations() throws Exception {
+    assertFalse("getfacl should fail without path",
+        0 == runCommand(new String[] { "-getfacl" }));
+    assertFalse("getfacl should fail with extra argument",
+        0 == runCommand(new String[] { "-getfacl", "/test", "extraArg" }));
+  }
+
+  @Test
+  public void testSetfaclValidations() throws Exception {
+    assertFalse("setfacl should fail without path",
+        0 == runCommand(new String[] { "-setfacl" }));
+    assertFalse("setfacl should fail without aclSpec",
+        0 == runCommand(new String[] { "-setfacl", "-m", "/path" }));
+    assertFalse("setfacl should fail with conflicting options",
+        0 == runCommand(new String[] { "-setfacl", "-m", "/path" }));
+    assertFalse("setfacl should fail with extra arguments",
+        0 == runCommand(new String[] { "-setfacl", "/path", "extra" }));
+    assertFalse("setfacl should fail with extra arguments",
+        0 == runCommand(new String[] { "-setfacl", "--set",
+            "default:user::rwx", "/path", "extra" }));
+  }
+
+  private int runCommand(String[] commands) throws Exception {
+    return ToolRunner.run(conf, new FsShell(), commands);
+  }
+}