|
@@ -18,11 +18,15 @@
|
|
|
package org.apache.hadoop.hdfs.server.namenode;
|
|
|
|
|
|
import java.io.IOException;
|
|
|
+import java.util.ArrayList;
|
|
|
import java.util.Collection;
|
|
|
+import java.util.List;
|
|
|
import java.util.Stack;
|
|
|
|
|
|
+import com.google.common.base.Preconditions;
|
|
|
import org.apache.commons.logging.Log;
|
|
|
import org.apache.commons.logging.LogFactory;
|
|
|
+import org.apache.hadoop.fs.FSExceptionMessages;
|
|
|
import org.apache.hadoop.fs.ParentNotDirectoryException;
|
|
|
import org.apache.hadoop.fs.permission.AclEntryScope;
|
|
|
import org.apache.hadoop.fs.permission.AclEntryType;
|
|
@@ -280,9 +284,20 @@ class FSPermissionChecker implements AccessControlEnforcer {
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
+ // Each inode in the subtree has a level. The root inode has level 0.
|
|
|
+ // List subINodePath tracks the inode path in the subtree during
|
|
|
+ // traversal. The root inode is not stored because it is already in array
|
|
|
+ // components. The list index is (level - 1).
|
|
|
+ ArrayList<INodeDirectory> subINodePath = new ArrayList<>();
|
|
|
+
|
|
|
+ // The stack of levels matches the stack of directory inodes.
|
|
|
+ Stack<Integer> levels = new Stack<>();
|
|
|
+ levels.push(0); // Level 0 is the root
|
|
|
+
|
|
|
Stack<INodeDirectory> directories = new Stack<INodeDirectory>();
|
|
|
for(directories.push(inode.asDirectory()); !directories.isEmpty(); ) {
|
|
|
INodeDirectory d = directories.pop();
|
|
|
+ int level = levels.pop();
|
|
|
ReadOnlyList<INode> cList = d.getChildrenList(snapshotId);
|
|
|
if (!(cList.isEmpty() && ignoreEmptyDir)) {
|
|
|
//TODO have to figure this out with inodeattribute provider
|
|
@@ -292,11 +307,44 @@ class FSPermissionChecker implements AccessControlEnforcer {
|
|
|
throw new AccessControlException(
|
|
|
toAccessControlString(inodeAttr, d.getFullPathName(), access));
|
|
|
}
|
|
|
+
|
|
|
+ if (level > 0) {
|
|
|
+ if (level - 1 < subINodePath.size()) {
|
|
|
+ subINodePath.set(level - 1, d);
|
|
|
+ } else {
|
|
|
+ Preconditions.checkState(level - 1 == subINodePath.size());
|
|
|
+ subINodePath.add(d);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (inodeAttr.getFsPermission().getStickyBit()) {
|
|
|
+ for (INode child : cList) {
|
|
|
+ INodeAttributes childInodeAttr =
|
|
|
+ getINodeAttrs(components, pathIdx, child, snapshotId);
|
|
|
+ if (isStickyBitViolated(inodeAttr, childInodeAttr)) {
|
|
|
+ List<byte[]> allComponentList = new ArrayList<>();
|
|
|
+ for (int i = 0; i <= pathIdx; ++i) {
|
|
|
+ allComponentList.add(components[i]);
|
|
|
+ }
|
|
|
+ for (int i = 0; i < level; ++i) {
|
|
|
+ allComponentList.add(subINodePath.get(i).getLocalNameBytes());
|
|
|
+ }
|
|
|
+ allComponentList.add(child.getLocalNameBytes());
|
|
|
+ int index = pathIdx + level;
|
|
|
+ byte[][] allComponents =
|
|
|
+ allComponentList.toArray(new byte[][]{});
|
|
|
+ throwStickyBitException(
|
|
|
+ getPath(allComponents, 0, index + 1), child,
|
|
|
+ getPath(allComponents, 0, index), inode);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
for(INode child : cList) {
|
|
|
if (child.isDirectory()) {
|
|
|
directories.push(child.asDirectory());
|
|
|
+ levels.push(level + 1);
|
|
|
}
|
|
|
}
|
|
|
}
|
|
@@ -425,26 +473,43 @@ class FSPermissionChecker implements AccessControlEnforcer {
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
+ INodeAttributes inode = inodes[index + 1];
|
|
|
+ if (!isStickyBitViolated(parent, inode)) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ throwStickyBitException(getPath(components, 0, index + 1), inode,
|
|
|
+ getPath(components, 0, index), parent);
|
|
|
+ }
|
|
|
+
|
|
|
+ /** Return true when sticky bit is violated. */
|
|
|
+ private boolean isStickyBitViolated(INodeAttributes parent,
|
|
|
+ INodeAttributes inode) {
|
|
|
// If this user is the directory owner, return
|
|
|
if (parent.getUserName().equals(getUser())) {
|
|
|
- return;
|
|
|
+ return false;
|
|
|
}
|
|
|
|
|
|
- INodeAttributes inode = inodes[index + 1];
|
|
|
// if this user is the file owner, return
|
|
|
if (inode.getUserName().equals(getUser())) {
|
|
|
- return;
|
|
|
+ return false;
|
|
|
}
|
|
|
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ private void throwStickyBitException(
|
|
|
+ String inodePath, INodeAttributes inode,
|
|
|
+ String parentPath, INodeAttributes parent)
|
|
|
+ throws AccessControlException {
|
|
|
throw new AccessControlException(String.format(
|
|
|
- "Permission denied by sticky bit: user=%s, path=\"%s\":%s:%s:%s%s, " +
|
|
|
- "parent=\"%s\":%s:%s:%s%s", user,
|
|
|
- getPath(components, 0, index + 1),
|
|
|
- inode.getUserName(), inode.getGroupName(),
|
|
|
- inode.isDirectory() ? "d" : "-", inode.getFsPermission().toString(),
|
|
|
- getPath(components, 0, index),
|
|
|
- parent.getUserName(), parent.getGroupName(),
|
|
|
- parent.isDirectory() ? "d" : "-", parent.getFsPermission().toString()));
|
|
|
+ FSExceptionMessages.PERMISSION_DENIED_BY_STICKY_BIT +
|
|
|
+ ": user=%s, path=\"%s\":%s:%s:%s%s, " +
|
|
|
+ "parent=\"%s\":%s:%s:%s%s", user, inodePath, inode.getUserName(),
|
|
|
+ inode.getGroupName(), inode.isDirectory() ? "d" : "-",
|
|
|
+ inode.getFsPermission().toString(), parentPath, parent.getUserName(),
|
|
|
+ parent.getGroupName(), parent.isDirectory() ? "d" : "-",
|
|
|
+ parent.getFsPermission().toString()));
|
|
|
}
|
|
|
|
|
|
/**
|