|
@@ -0,0 +1,289 @@
|
|
|
+/**
|
|
|
+ * 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;
|
|
|
+
|
|
|
+import java.io.IOException;
|
|
|
+import java.util.regex.Matcher;
|
|
|
+import java.util.regex.Pattern;
|
|
|
+
|
|
|
+import org.apache.hadoop.fs.FsShell.CmdHandler;
|
|
|
+import org.apache.hadoop.fs.permission.FsPermission;
|
|
|
+
|
|
|
+
|
|
|
+/**
|
|
|
+ * This class is the home for file permissions related commands.
|
|
|
+ * Moved to this seperate class since FsShell is getting too large.
|
|
|
+ */
|
|
|
+class FsShellPermissions {
|
|
|
+
|
|
|
+ /*========== chmod ==========*/
|
|
|
+
|
|
|
+ /* The pattern is alsmost as flexible as mode allowed by
|
|
|
+ * chmod shell command. The main restriction is that we recognize only rwxX.
|
|
|
+ * To reduce errors we also enforce 3 digits for octal mode.
|
|
|
+ */
|
|
|
+ private static Pattern chmodNormalPattern =
|
|
|
+ Pattern.compile("\\G\\s*([ugoa]*)([+=-]+)([rwxX]+)([,\\s]*)\\s*");
|
|
|
+ private static Pattern chmodOctalPattern =
|
|
|
+ Pattern.compile("^\\s*[+]?([0-7]{3})\\s*$");
|
|
|
+
|
|
|
+ static String CHMOD_USAGE =
|
|
|
+ "-chmod [-R] <MODE[,MODE]... | OCTALMODE> PATH...";
|
|
|
+
|
|
|
+ private static class ChmodHandler extends CmdHandler {
|
|
|
+
|
|
|
+ private short userMode, groupMode, othersMode;
|
|
|
+ private char userType = '+', groupType = '+', othersType='+';
|
|
|
+
|
|
|
+ private void applyNormalPattern(String modeStr, Matcher matcher)
|
|
|
+ throws IOException {
|
|
|
+ boolean commaSeperated = false;
|
|
|
+
|
|
|
+ for(int i=0; i < 1 || matcher.end() < modeStr.length(); i++) {
|
|
|
+ if (i>0 && (!commaSeperated || !matcher.find())) {
|
|
|
+ patternError(modeStr);
|
|
|
+ }
|
|
|
+
|
|
|
+ /* groups : 1 : [ugoa]*
|
|
|
+ * 2 : [+-=]
|
|
|
+ * 3 : [rwxX]+
|
|
|
+ * 4 : [,\s]*
|
|
|
+ */
|
|
|
+
|
|
|
+ String str = matcher.group(2);
|
|
|
+ char type = str.charAt(str.length() - 1);
|
|
|
+
|
|
|
+ boolean user, group, others;
|
|
|
+ user = group = others = false;
|
|
|
+
|
|
|
+ for(char c : matcher.group(1).toCharArray()) {
|
|
|
+ switch (c) {
|
|
|
+ case 'u' : user = true; break;
|
|
|
+ case 'g' : group = true; break;
|
|
|
+ case 'o' : others = true; break;
|
|
|
+ case 'a' : break;
|
|
|
+ default : throw new RuntimeException("Unexpected");
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!(user || group || others)) { // same as specifying 'a'
|
|
|
+ user = group = others = true;
|
|
|
+ }
|
|
|
+
|
|
|
+ short mode = 0;
|
|
|
+ for(char c : matcher.group(3).toCharArray()) {
|
|
|
+ switch (c) {
|
|
|
+ case 'r' : mode |= 4; break;
|
|
|
+ case 'w' : mode |= 2; break;
|
|
|
+ case 'x' : mode |= 1; break;
|
|
|
+ case 'X' : mode |= 8; break;
|
|
|
+ default : throw new RuntimeException("Unexpected");
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if ( user ) {
|
|
|
+ userMode = mode;
|
|
|
+ userType = type;
|
|
|
+ }
|
|
|
+
|
|
|
+ if ( group ) {
|
|
|
+ groupMode = mode;
|
|
|
+ groupType = type;
|
|
|
+ }
|
|
|
+
|
|
|
+ if ( others ) {
|
|
|
+ othersMode = mode;
|
|
|
+ othersType = type;
|
|
|
+ }
|
|
|
+
|
|
|
+ commaSeperated = matcher.group(4).contains(",");
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private void applyOctalPattern(String modeStr, Matcher matcher) {
|
|
|
+ userType = groupType = othersType = '=';
|
|
|
+ String str = matcher.group(1);
|
|
|
+ userMode = Short.valueOf(str.substring(0, 1));
|
|
|
+ groupMode = Short.valueOf(str.substring(1, 2));
|
|
|
+ othersMode = Short.valueOf(str.substring(2, 3));
|
|
|
+ }
|
|
|
+
|
|
|
+ private void patternError(String mode) throws IOException {
|
|
|
+ throw new IOException("chmod : mode '" + mode +
|
|
|
+ "' does not match the expected pattern.");
|
|
|
+ }
|
|
|
+
|
|
|
+ ChmodHandler(FileSystem fs, String modeStr) throws IOException {
|
|
|
+ super("chmod", fs);
|
|
|
+ Matcher matcher = null;
|
|
|
+
|
|
|
+ if ((matcher = chmodNormalPattern.matcher(modeStr)).find()) {
|
|
|
+ applyNormalPattern(modeStr, matcher);
|
|
|
+ } else if ((matcher = chmodOctalPattern.matcher(modeStr)).matches()) {
|
|
|
+ applyOctalPattern(modeStr, matcher);
|
|
|
+ } else {
|
|
|
+ patternError(modeStr);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private int applyChmod(char type, int mode, int existing, boolean exeOk) {
|
|
|
+ boolean capX = false;
|
|
|
+
|
|
|
+ if ((mode&8) != 0) { // convert X to x;
|
|
|
+ capX = true;
|
|
|
+ mode &= ~8;
|
|
|
+ mode |= 1;
|
|
|
+ }
|
|
|
+
|
|
|
+ switch (type) {
|
|
|
+ case '+' : mode = mode | existing; break;
|
|
|
+ case '-' : mode = (~mode) & existing; break;
|
|
|
+ case '=' : break;
|
|
|
+ default : throw new RuntimeException("Unexpected");
|
|
|
+ }
|
|
|
+
|
|
|
+ // if X is specified add 'x' only if exeOk or x was already set.
|
|
|
+ if (capX && !exeOk && (mode&1) != 0 && (existing&1) == 0) {
|
|
|
+ mode &= ~1; // remove x
|
|
|
+ }
|
|
|
+
|
|
|
+ return mode;
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public void run(FileStatus file) throws IOException {
|
|
|
+ FsPermission perms = file.getPermission();
|
|
|
+ int existing = perms.toShort();
|
|
|
+ boolean exeOk = file.isDir() || (existing & 0111) != 0;
|
|
|
+ int newperms = ( applyChmod(userType, userMode,
|
|
|
+ (existing>>>6)&7, exeOk) << 6 |
|
|
|
+ applyChmod(groupType, groupMode,
|
|
|
+ (existing>>>3)&7, exeOk) << 3 |
|
|
|
+ applyChmod(othersType, othersMode, existing&7, exeOk) );
|
|
|
+
|
|
|
+ if (existing != newperms) {
|
|
|
+ try {
|
|
|
+ getFS().setPermission(file.getPath(),
|
|
|
+ new FsPermission((short)newperms));
|
|
|
+ } catch (IOException e) {
|
|
|
+ System.err.println(getName() + ": changing permissions of '" +
|
|
|
+ file.getPath() + "':" + e.getMessage());
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /*========== chown ==========*/
|
|
|
+
|
|
|
+ ///allows only alpha-numberic names for owner group
|
|
|
+ static private Pattern chownPattern =
|
|
|
+ Pattern.compile("^\\s*(\\p{Alnum}+)?([:.](\\p{Alnum}*))?\\s*$");
|
|
|
+ static private Pattern chgrpPattern =
|
|
|
+ Pattern.compile("^\\s*(\\p{Alnum}+)\\s*$");
|
|
|
+
|
|
|
+ static String CHOWN_USAGE = "-chown [-R] [OWNER][:[GROUP]] PATH...";
|
|
|
+ static String CHGRP_USAGE = "-chown [-R] GROUP PATH...";
|
|
|
+
|
|
|
+ private static class ChownHandler extends CmdHandler {
|
|
|
+ protected String owner = null;
|
|
|
+ protected String group = null;
|
|
|
+
|
|
|
+ protected ChownHandler(String cmd, FileSystem fs) { //for chgrp
|
|
|
+ super(cmd, fs);
|
|
|
+ }
|
|
|
+
|
|
|
+ ChownHandler(FileSystem fs, String ownerStr) throws IOException {
|
|
|
+ super("chown", fs);
|
|
|
+ Matcher matcher = chownPattern.matcher(ownerStr);
|
|
|
+ if (!matcher.matches()) {
|
|
|
+ throw new IOException("'" + ownerStr + "' does not match " +
|
|
|
+ "expected pattern for [owner][:group].");
|
|
|
+ }
|
|
|
+ owner = matcher.group(1);
|
|
|
+ group = matcher.group(3);
|
|
|
+ if (group != null && group.length() == 0) {
|
|
|
+ group = null;
|
|
|
+ }
|
|
|
+ if (owner == null && group == null) {
|
|
|
+ throw new IOException("'" + ownerStr + "' does not specify " +
|
|
|
+ " onwer or group.");
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public void run(FileStatus file) throws IOException {
|
|
|
+ //Should we do case insensitive match?
|
|
|
+ String newOwner = (owner == null || owner.equals(file.getOwner())) ?
|
|
|
+ null : owner;
|
|
|
+ String newGroup = (group == null || group.equals(file.getGroup())) ?
|
|
|
+ null : group;
|
|
|
+
|
|
|
+ if (newOwner != null || newGroup != null) {
|
|
|
+ try {
|
|
|
+ fs.setOwner(file.getPath(), newOwner, newGroup);
|
|
|
+ } catch (IOException e) {
|
|
|
+ System.err.println(getName() + ": changing ownership of '" +
|
|
|
+ file.getPath() + "':" + e.getMessage());
|
|
|
+
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /*========== chgrp ==========*/
|
|
|
+
|
|
|
+ private static class ChgrpHandler extends ChownHandler {
|
|
|
+ ChgrpHandler(FileSystem fs, String groupStr) throws IOException {
|
|
|
+ super("chgrp", fs);
|
|
|
+
|
|
|
+ Matcher matcher = chgrpPattern.matcher(groupStr);
|
|
|
+ if (!matcher.matches()) {
|
|
|
+ throw new IOException("'" + groupStr + "' does not match " +
|
|
|
+ "expected pattern for group");
|
|
|
+ }
|
|
|
+ group = matcher.group(1);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ static void changePermissions(FileSystem fs, String cmd,
|
|
|
+ String argv[], int startIndex)
|
|
|
+ throws IOException {
|
|
|
+ CmdHandler handler = null;
|
|
|
+ boolean recursive = false;
|
|
|
+
|
|
|
+ // handle common arguments, currently only "-R"
|
|
|
+ for (; startIndex < argv.length && argv[startIndex].equals("-R");
|
|
|
+ startIndex++) {
|
|
|
+ recursive = true;
|
|
|
+ }
|
|
|
+
|
|
|
+ if ( startIndex >= argv.length ) {
|
|
|
+ throw new IOException("Not enough arguments for the command");
|
|
|
+ }
|
|
|
+
|
|
|
+ if (cmd.equals("-chmod")) {
|
|
|
+ handler = new ChmodHandler(fs, argv[startIndex++]);
|
|
|
+ } else if (cmd.equals("-chown")) {
|
|
|
+ handler = new ChownHandler(fs, argv[startIndex++]);
|
|
|
+ } else if (cmd.equals("-chgrp")) {
|
|
|
+ handler = new ChgrpHandler(fs, argv[startIndex++]);
|
|
|
+ }
|
|
|
+
|
|
|
+ FsShell.runCmdHandler(handler, argv, startIndex, recursive);
|
|
|
+ }
|
|
|
+}
|