/** * 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; } }