| /** |
| * 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.permission; |
| |
| import java.util.regex.Matcher; |
| import java.util.regex.Pattern; |
| |
| import org.apache.hadoop.classification.InterfaceAudience; |
| import org.apache.hadoop.classification.InterfaceStability; |
| |
| /** |
| * Base class for parsing either chmod permissions or umask permissions. |
| * Includes common code needed by either operation as implemented in |
| * UmaskParser and ChmodParser classes. |
| */ |
| @InterfaceAudience.Private |
| @InterfaceStability.Unstable |
| class PermissionParser { |
| protected boolean symbolic = false; |
| protected short userMode; |
| protected short groupMode; |
| protected short othersMode; |
| protected short stickyMode; |
| protected char userType = '+'; |
| protected char groupType = '+'; |
| protected char othersType = '+'; |
| protected char stickyBitType = '+'; |
| |
| /** |
| * Begin parsing permission stored in modeStr |
| * |
| * @param modeStr Permission mode, either octal or symbolic |
| * @param symbolic Use-case specific symbolic pattern to match against |
| * @throws IllegalArgumentException if unable to parse modeStr |
| */ |
| public PermissionParser(String modeStr, Pattern symbolic, Pattern octal) |
| throws IllegalArgumentException { |
| Matcher matcher = null; |
| |
| if ((matcher = symbolic.matcher(modeStr)).find()) { |
| applyNormalPattern(modeStr, matcher); |
| } else if ((matcher = octal.matcher(modeStr)).matches()) { |
| applyOctalPattern(modeStr, matcher); |
| } else { |
| throw new IllegalArgumentException(modeStr); |
| } |
| } |
| |
| private void applyNormalPattern(String modeStr, Matcher matcher) { |
| // Are there multiple permissions stored in one chmod? |
| boolean commaSeperated = false; |
| |
| for (int i = 0; i < 1 || matcher.end() < modeStr.length(); i++) { |
| if (i > 0 && (!commaSeperated || !matcher.find())) { |
| throw new IllegalArgumentException(modeStr); |
| } |
| |
| /* |
| * groups : 1 : [ugoa]* 2 : [+-=] 3 : [rwxXt]+ 4 : [,\s]* |
| */ |
| |
| String str = matcher.group(2); |
| char type = str.charAt(str.length() - 1); |
| |
| boolean user, group, others, stickyBit; |
| user = group = others = stickyBit = 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; |
| case 't': |
| stickyBit = true; |
| break; |
| default: |
| throw new RuntimeException("Unexpected"); |
| } |
| } |
| |
| if (user) { |
| userMode = mode; |
| userType = type; |
| } |
| |
| if (group) { |
| groupMode = mode; |
| groupType = type; |
| } |
| |
| if (others) { |
| othersMode = mode; |
| othersType = type; |
| |
| stickyMode = (short) (stickyBit ? 1 : 0); |
| stickyBitType = type; |
| } |
| |
| commaSeperated = matcher.group(4).contains(","); |
| } |
| symbolic = true; |
| } |
| |
| private void applyOctalPattern(String modeStr, Matcher matcher) { |
| userType = groupType = othersType = '='; |
| |
| // Check if sticky bit is specified |
| String sb = matcher.group(1); |
| if (!sb.isEmpty()) { |
| stickyMode = Short.valueOf(sb.substring(0, 1)); |
| stickyBitType = '='; |
| } |
| |
| String str = matcher.group(2); |
| userMode = Short.valueOf(str.substring(0, 1)); |
| groupMode = Short.valueOf(str.substring(1, 2)); |
| othersMode = Short.valueOf(str.substring(2, 3)); |
| } |
| |
| protected int combineModes(int existing, boolean exeOk) { |
| return combineModeSegments(stickyBitType, stickyMode, |
| (existing>>>9), false) << 9 | |
| combineModeSegments(userType, userMode, |
| (existing>>>6)&7, exeOk) << 6 | |
| combineModeSegments(groupType, groupMode, |
| (existing>>>3)&7, exeOk) << 3 | |
| combineModeSegments(othersType, othersMode, existing&7, exeOk); |
| } |
| |
| protected int combineModeSegments(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; |
| } |
| } |