HDFS-5758. NameNode: complete implementation of inode modifications for ACLs. Contributed by Chris Nauroth.
git-svn-id: https://svn.apache.org/repos/asf/hadoop/common/branches/HDFS-4685@1559267 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
c90211af7a
commit
5300294fbe
|
@ -20,6 +20,7 @@ package org.apache.hadoop.fs.shell;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
import java.util.Iterator;
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
@ -32,6 +33,7 @@ import org.apache.hadoop.fs.permission.AclEntryScope;
|
||||||
import org.apache.hadoop.fs.permission.AclEntryType;
|
import org.apache.hadoop.fs.permission.AclEntryType;
|
||||||
import org.apache.hadoop.fs.permission.AclStatus;
|
import org.apache.hadoop.fs.permission.AclStatus;
|
||||||
import org.apache.hadoop.fs.permission.FsAction;
|
import org.apache.hadoop.fs.permission.FsAction;
|
||||||
|
import org.apache.hadoop.fs.permission.FsPermission;
|
||||||
import org.apache.hadoop.util.StringUtils;
|
import org.apache.hadoop.util.StringUtils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -92,10 +94,87 @@ class AclCommands extends FsCommand {
|
||||||
}
|
}
|
||||||
out.println("# flags: --" + stickyFlag);
|
out.println("# flags: --" + stickyFlag);
|
||||||
}
|
}
|
||||||
for (AclEntry entry : entries) {
|
|
||||||
out.println(entry);
|
FsPermission perm = item.stat.getPermission();
|
||||||
|
if (perm.getAclBit()) {
|
||||||
|
printExtendedAcl(perm, entries);
|
||||||
|
} else {
|
||||||
|
printMinimalAcl(perm);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prints an extended ACL, including all extended ACL entries and also the
|
||||||
|
* base entries implied by the permission bits.
|
||||||
|
*
|
||||||
|
* @param perm FsPermission of file
|
||||||
|
* @param entries List<AclEntry> containing ACL entries of file
|
||||||
|
*/
|
||||||
|
private void printExtendedAcl(FsPermission perm, List<AclEntry> entries) {
|
||||||
|
// Print owner entry implied by owner permission bits.
|
||||||
|
out.println(new AclEntry.Builder()
|
||||||
|
.setScope(AclEntryScope.ACCESS)
|
||||||
|
.setType(AclEntryType.USER)
|
||||||
|
.setPermission(perm.getUserAction())
|
||||||
|
.build());
|
||||||
|
|
||||||
|
// Print all extended access ACL entries.
|
||||||
|
Iterator<AclEntry> entryIter = entries.iterator();
|
||||||
|
AclEntry curEntry = null;
|
||||||
|
while (entryIter.hasNext()) {
|
||||||
|
curEntry = entryIter.next();
|
||||||
|
if (curEntry.getScope() == AclEntryScope.DEFAULT) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
out.println(curEntry);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Print mask entry implied by group permission bits.
|
||||||
|
out.println(new AclEntry.Builder()
|
||||||
|
.setScope(AclEntryScope.ACCESS)
|
||||||
|
.setType(AclEntryType.MASK)
|
||||||
|
.setPermission(perm.getGroupAction())
|
||||||
|
.build());
|
||||||
|
|
||||||
|
// Print other entry implied by other bits.
|
||||||
|
out.println(new AclEntry.Builder()
|
||||||
|
.setScope(AclEntryScope.ACCESS)
|
||||||
|
.setType(AclEntryType.OTHER)
|
||||||
|
.setPermission(perm.getOtherAction())
|
||||||
|
.build());
|
||||||
|
|
||||||
|
// Print default ACL entries.
|
||||||
|
if (curEntry != null && curEntry.getScope() == AclEntryScope.DEFAULT) {
|
||||||
|
out.println(curEntry);
|
||||||
|
}
|
||||||
|
while (entryIter.hasNext()) {
|
||||||
|
out.println(entryIter.next());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prints a minimal ACL, consisting of exactly 3 ACL entries implied by the
|
||||||
|
* permission bits.
|
||||||
|
*
|
||||||
|
* @param perm FsPermission of file
|
||||||
|
*/
|
||||||
|
private void printMinimalAcl(FsPermission perm) {
|
||||||
|
out.println(new AclEntry.Builder()
|
||||||
|
.setScope(AclEntryScope.ACCESS)
|
||||||
|
.setType(AclEntryType.USER)
|
||||||
|
.setPermission(perm.getUserAction())
|
||||||
|
.build());
|
||||||
|
out.println(new AclEntry.Builder()
|
||||||
|
.setScope(AclEntryScope.ACCESS)
|
||||||
|
.setType(AclEntryType.GROUP)
|
||||||
|
.setPermission(perm.getGroupAction())
|
||||||
|
.build());
|
||||||
|
out.println(new AclEntry.Builder()
|
||||||
|
.setScope(AclEntryScope.ACCESS)
|
||||||
|
.setType(AclEntryType.OTHER)
|
||||||
|
.setPermission(perm.getOtherAction())
|
||||||
|
.build());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -30,6 +30,9 @@ HDFS-4685 (Unreleased)
|
||||||
|
|
||||||
HADOOP-10220. Add ACL indicator bit to FsPermission. (cnauroth)
|
HADOOP-10220. Add ACL indicator bit to FsPermission. (cnauroth)
|
||||||
|
|
||||||
|
HDFS-5758. NameNode: complete implementation of inode modifications for
|
||||||
|
ACLs. (Chris Nauroth via wheat9)
|
||||||
|
|
||||||
OPTIMIZATIONS
|
OPTIMIZATIONS
|
||||||
|
|
||||||
BUG FIXES
|
BUG FIXES
|
||||||
|
|
|
@ -111,6 +111,7 @@ import org.apache.hadoop.fs.permission.FsPermission;
|
||||||
import org.apache.hadoop.hdfs.client.ClientMmapManager;
|
import org.apache.hadoop.hdfs.client.ClientMmapManager;
|
||||||
import org.apache.hadoop.hdfs.client.HdfsDataInputStream;
|
import org.apache.hadoop.hdfs.client.HdfsDataInputStream;
|
||||||
import org.apache.hadoop.hdfs.client.HdfsDataOutputStream;
|
import org.apache.hadoop.hdfs.client.HdfsDataOutputStream;
|
||||||
|
import org.apache.hadoop.hdfs.protocol.AclException;
|
||||||
import org.apache.hadoop.hdfs.protocol.CacheDirectiveEntry;
|
import org.apache.hadoop.hdfs.protocol.CacheDirectiveEntry;
|
||||||
import org.apache.hadoop.hdfs.protocol.CacheDirectiveInfo;
|
import org.apache.hadoop.hdfs.protocol.CacheDirectiveInfo;
|
||||||
import org.apache.hadoop.hdfs.protocol.CacheDirectiveIterator;
|
import org.apache.hadoop.hdfs.protocol.CacheDirectiveIterator;
|
||||||
|
@ -2648,10 +2649,11 @@ public class DFSClient implements java.io.Closeable {
|
||||||
namenode.modifyAclEntries(src, aclSpec);
|
namenode.modifyAclEntries(src, aclSpec);
|
||||||
} catch(RemoteException re) {
|
} catch(RemoteException re) {
|
||||||
throw re.unwrapRemoteException(AccessControlException.class,
|
throw re.unwrapRemoteException(AccessControlException.class,
|
||||||
|
AclException.class,
|
||||||
FileNotFoundException.class,
|
FileNotFoundException.class,
|
||||||
SafeModeException.class,
|
SafeModeException.class,
|
||||||
UnresolvedPathException.class,
|
SnapshotAccessControlException.class,
|
||||||
SnapshotAccessControlException.class);
|
UnresolvedPathException.class);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2662,10 +2664,11 @@ public class DFSClient implements java.io.Closeable {
|
||||||
namenode.removeAclEntries(src, aclSpec);
|
namenode.removeAclEntries(src, aclSpec);
|
||||||
} catch(RemoteException re) {
|
} catch(RemoteException re) {
|
||||||
throw re.unwrapRemoteException(AccessControlException.class,
|
throw re.unwrapRemoteException(AccessControlException.class,
|
||||||
|
AclException.class,
|
||||||
FileNotFoundException.class,
|
FileNotFoundException.class,
|
||||||
SafeModeException.class,
|
SafeModeException.class,
|
||||||
UnresolvedPathException.class,
|
SnapshotAccessControlException.class,
|
||||||
SnapshotAccessControlException.class);
|
UnresolvedPathException.class);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2675,10 +2678,11 @@ public class DFSClient implements java.io.Closeable {
|
||||||
namenode.removeDefaultAcl(src);
|
namenode.removeDefaultAcl(src);
|
||||||
} catch(RemoteException re) {
|
} catch(RemoteException re) {
|
||||||
throw re.unwrapRemoteException(AccessControlException.class,
|
throw re.unwrapRemoteException(AccessControlException.class,
|
||||||
|
AclException.class,
|
||||||
FileNotFoundException.class,
|
FileNotFoundException.class,
|
||||||
SafeModeException.class,
|
SafeModeException.class,
|
||||||
UnresolvedPathException.class,
|
SnapshotAccessControlException.class,
|
||||||
SnapshotAccessControlException.class);
|
UnresolvedPathException.class);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2688,10 +2692,11 @@ public class DFSClient implements java.io.Closeable {
|
||||||
namenode.removeAcl(src);
|
namenode.removeAcl(src);
|
||||||
} catch(RemoteException re) {
|
} catch(RemoteException re) {
|
||||||
throw re.unwrapRemoteException(AccessControlException.class,
|
throw re.unwrapRemoteException(AccessControlException.class,
|
||||||
|
AclException.class,
|
||||||
FileNotFoundException.class,
|
FileNotFoundException.class,
|
||||||
SafeModeException.class,
|
SafeModeException.class,
|
||||||
UnresolvedPathException.class,
|
SnapshotAccessControlException.class,
|
||||||
SnapshotAccessControlException.class);
|
UnresolvedPathException.class);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2701,10 +2706,11 @@ public class DFSClient implements java.io.Closeable {
|
||||||
namenode.setAcl(src, aclSpec);
|
namenode.setAcl(src, aclSpec);
|
||||||
} catch(RemoteException re) {
|
} catch(RemoteException re) {
|
||||||
throw re.unwrapRemoteException(AccessControlException.class,
|
throw re.unwrapRemoteException(AccessControlException.class,
|
||||||
|
AclException.class,
|
||||||
FileNotFoundException.class,
|
FileNotFoundException.class,
|
||||||
SafeModeException.class,
|
SafeModeException.class,
|
||||||
UnresolvedPathException.class,
|
SnapshotAccessControlException.class,
|
||||||
SnapshotAccessControlException.class);
|
UnresolvedPathException.class);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,291 @@
|
||||||
|
/**
|
||||||
|
* 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.hdfs.server.namenode;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import com.google.common.collect.Lists;
|
||||||
|
|
||||||
|
import org.apache.hadoop.classification.InterfaceAudience;
|
||||||
|
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.FsPermission;
|
||||||
|
import org.apache.hadoop.hdfs.protocol.AclException;
|
||||||
|
import org.apache.hadoop.hdfs.protocol.QuotaExceededException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* AclStorage contains utility methods that define how ACL data is stored in the
|
||||||
|
* namespace.
|
||||||
|
*
|
||||||
|
* If an inode has an ACL, then the ACL bit is set in the inode's
|
||||||
|
* {@link FsPermission} and the inode also contains an {@link AclFeature}. For
|
||||||
|
* the access ACL, the owner and other entries are identical to the owner and
|
||||||
|
* other bits stored in FsPermission, so we reuse those. The access mask entry
|
||||||
|
* is stored into the group permission bits of FsPermission. This is consistent
|
||||||
|
* with other file systems' implementations of ACLs and eliminates the need for
|
||||||
|
* special handling in various parts of the codebase. For example, if a user
|
||||||
|
* calls chmod to change group permission bits on a file with an ACL, then the
|
||||||
|
* expected behavior is to change the ACL's mask entry. By saving the mask entry
|
||||||
|
* into the group permission bits, chmod continues to work correctly without
|
||||||
|
* special handling. All remaining access entries (named users and named groups)
|
||||||
|
* are stored as explicit {@link AclEntry} instances in a list inside the
|
||||||
|
* AclFeature. Additionally, all default entries are stored in the AclFeature.
|
||||||
|
*
|
||||||
|
* The methods in this class encapsulate these rules for reading or writing the
|
||||||
|
* ACL entries to the appropriate location.
|
||||||
|
*
|
||||||
|
* The methods in this class assume that input ACL entry lists have already been
|
||||||
|
* validated and sorted according to the rules enforced by
|
||||||
|
* {@link AclTransformation}.
|
||||||
|
*/
|
||||||
|
@InterfaceAudience.Private
|
||||||
|
final class AclStorage {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reads the existing extended ACL entries of an inode. This method returns
|
||||||
|
* only the extended ACL entries stored in the AclFeature. If the inode does
|
||||||
|
* not have an ACL, then this method returns an empty list.
|
||||||
|
*
|
||||||
|
* @param inode INodeWithAdditionalFields to read
|
||||||
|
* @param snapshotId int latest snapshot ID of inode
|
||||||
|
* @return List<AclEntry> containing extended inode ACL entries
|
||||||
|
*/
|
||||||
|
public static List<AclEntry> readINodeAcl(INodeWithAdditionalFields inode,
|
||||||
|
int snapshotId) {
|
||||||
|
FsPermission perm = inode.getPermissionStatus(snapshotId).getPermission();
|
||||||
|
if (perm.getAclBit()) {
|
||||||
|
return inode.getAclFeature().getEntries();
|
||||||
|
} else {
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reads the existing ACL of an inode. This method always returns the full
|
||||||
|
* logical ACL of the inode after reading relevant data from the inode's
|
||||||
|
* {@link FsPermission} and {@link AclFeature}. Note that every inode
|
||||||
|
* logically has an ACL, even if no ACL has been set explicitly. If the inode
|
||||||
|
* does not have an extended ACL, then the result is a minimal ACL consising of
|
||||||
|
* exactly 3 entries that correspond to the owner, group and other permissions.
|
||||||
|
*
|
||||||
|
* @param inode INodeWithAdditionalFields to read
|
||||||
|
* @param snapshotId int latest snapshot ID of inode
|
||||||
|
* @return List<AclEntry> containing all logical inode ACL entries
|
||||||
|
*/
|
||||||
|
public static List<AclEntry> readINodeLogicalAcl(
|
||||||
|
INodeWithAdditionalFields inode, int snapshotId) {
|
||||||
|
final List<AclEntry> existingAcl;
|
||||||
|
FsPermission perm = inode.getPermissionStatus(snapshotId).getPermission();
|
||||||
|
if (perm.getAclBit()) {
|
||||||
|
// Split ACL entries stored in the feature into access vs. default.
|
||||||
|
List<AclEntry> featureEntries = inode.getAclFeature().getEntries();
|
||||||
|
ScopedAclEntries scoped = new ScopedAclEntries(featureEntries);
|
||||||
|
List<AclEntry> accessEntries = scoped.getAccessEntries();
|
||||||
|
List<AclEntry> defaultEntries = scoped.getDefaultEntries();
|
||||||
|
|
||||||
|
// Pre-allocate list size for the explicit entries stored in the feature
|
||||||
|
// plus the 3 implicit entries (owner, group and other) from the permission
|
||||||
|
// bits.
|
||||||
|
existingAcl = Lists.newArrayListWithCapacity(featureEntries.size() + 3);
|
||||||
|
|
||||||
|
if (!accessEntries.isEmpty()) {
|
||||||
|
// Add owner entry implied from user permission bits.
|
||||||
|
existingAcl.add(new AclEntry.Builder()
|
||||||
|
.setScope(AclEntryScope.ACCESS)
|
||||||
|
.setType(AclEntryType.USER)
|
||||||
|
.setPermission(perm.getUserAction())
|
||||||
|
.build());
|
||||||
|
|
||||||
|
// Next add all named user and group entries taken from the feature.
|
||||||
|
existingAcl.addAll(accessEntries);
|
||||||
|
|
||||||
|
// Add mask entry implied from group permission bits.
|
||||||
|
existingAcl.add(new AclEntry.Builder()
|
||||||
|
.setScope(AclEntryScope.ACCESS)
|
||||||
|
.setType(AclEntryType.MASK)
|
||||||
|
.setPermission(perm.getGroupAction())
|
||||||
|
.build());
|
||||||
|
|
||||||
|
// Add other entry implied from other permission bits.
|
||||||
|
existingAcl.add(new AclEntry.Builder()
|
||||||
|
.setScope(AclEntryScope.ACCESS)
|
||||||
|
.setType(AclEntryType.OTHER)
|
||||||
|
.setPermission(perm.getOtherAction())
|
||||||
|
.build());
|
||||||
|
} else {
|
||||||
|
// It's possible that there is a default ACL but no access ACL. In this
|
||||||
|
// case, add the minimal access ACL implied by the permission bits.
|
||||||
|
existingAcl.addAll(getMinimalAcl(perm));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add all default entries after the access entries.
|
||||||
|
existingAcl.addAll(defaultEntries);
|
||||||
|
} else {
|
||||||
|
// If the inode doesn't have an extended ACL, then return a minimal ACL.
|
||||||
|
existingAcl = getMinimalAcl(perm);
|
||||||
|
}
|
||||||
|
|
||||||
|
// The above adds entries in the correct order, so no need to sort here.
|
||||||
|
return existingAcl;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Completely removes the ACL from an inode.
|
||||||
|
*
|
||||||
|
* @param inode INodeWithAdditionalFields to update
|
||||||
|
* @param snapshotId int latest snapshot ID of inode
|
||||||
|
* @throws QuotaExceededException if quota limit is exceeded
|
||||||
|
*/
|
||||||
|
public static void removeINodeAcl(INodeWithAdditionalFields inode,
|
||||||
|
int snapshotId) throws QuotaExceededException {
|
||||||
|
FsPermission perm = inode.getPermissionStatus(snapshotId).getPermission();
|
||||||
|
if (perm.getAclBit()) {
|
||||||
|
// Restore group permissions from the feature's entry to permission bits,
|
||||||
|
// overwriting the mask, which is not part of a minimal ACL.
|
||||||
|
List<AclEntry> featureEntries = inode.getAclFeature().getEntries();
|
||||||
|
AclEntry groupEntryKey = new AclEntry.Builder()
|
||||||
|
.setScope(AclEntryScope.ACCESS)
|
||||||
|
.setType(AclEntryType.GROUP)
|
||||||
|
.build();
|
||||||
|
int groupEntryIndex = Collections.binarySearch(featureEntries,
|
||||||
|
groupEntryKey, AclTransformation.ACL_ENTRY_COMPARATOR);
|
||||||
|
assert groupEntryIndex >= 0;
|
||||||
|
|
||||||
|
// Remove the feature and turn off the ACL bit.
|
||||||
|
inode.removeAclFeature();
|
||||||
|
FsPermission newPerm = new FsPermission(perm.getUserAction(),
|
||||||
|
featureEntries.get(groupEntryIndex).getPermission(),
|
||||||
|
perm.getOtherAction(),
|
||||||
|
perm.getStickyBit(), false);
|
||||||
|
inode.setPermission(newPerm, snapshotId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates an inode with a new ACL. This method takes a full logical ACL and
|
||||||
|
* stores the entries to the inode's {@link FsPermission} and
|
||||||
|
* {@link AclFeature}.
|
||||||
|
*
|
||||||
|
* @param inode INodeWithAdditionalFields to update
|
||||||
|
* @param newAcl List<AclEntry> containing new ACL entries
|
||||||
|
* @param snapshotId int latest snapshot ID of inode
|
||||||
|
* @throws AclException if the ACL is invalid for the given inode
|
||||||
|
* @throws QuotaExceededException if quota limit is exceeded
|
||||||
|
*/
|
||||||
|
public static void updateINodeAcl(INodeWithAdditionalFields inode,
|
||||||
|
List<AclEntry> newAcl, int snapshotId) throws AclException,
|
||||||
|
QuotaExceededException {
|
||||||
|
assert newAcl.size() >= 3;
|
||||||
|
FsPermission perm = inode.getPermissionStatus(snapshotId).getPermission();
|
||||||
|
final FsPermission newPerm;
|
||||||
|
if (newAcl.size() > 3) {
|
||||||
|
// This is an extended ACL. Split entries into access vs. default.
|
||||||
|
ScopedAclEntries scoped = new ScopedAclEntries(newAcl);
|
||||||
|
List<AclEntry> accessEntries = scoped.getAccessEntries();
|
||||||
|
List<AclEntry> defaultEntries = scoped.getDefaultEntries();
|
||||||
|
|
||||||
|
// Only directories may have a default ACL.
|
||||||
|
if (!defaultEntries.isEmpty() && !inode.isDirectory()) {
|
||||||
|
throw new AclException(
|
||||||
|
"Invalid ACL: only directories may have a default ACL.");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pre-allocate list size for the explicit entries stored in the feature,
|
||||||
|
// which is all entries minus the 3 entries implicitly stored in the
|
||||||
|
// permission bits.
|
||||||
|
List<AclEntry> featureEntries = Lists.newArrayListWithCapacity(
|
||||||
|
(accessEntries.size() - 3) + defaultEntries.size());
|
||||||
|
|
||||||
|
// Calculate new permission bits. For a correctly sorted ACL, the first
|
||||||
|
// entry is the owner and the last 2 entries are the mask and other entries
|
||||||
|
// respectively. Also preserve sticky bit and toggle ACL bit on.
|
||||||
|
newPerm = new FsPermission(accessEntries.get(0).getPermission(),
|
||||||
|
accessEntries.get(accessEntries.size() - 2).getPermission(),
|
||||||
|
accessEntries.get(accessEntries.size() - 1).getPermission(),
|
||||||
|
perm.getStickyBit(), true);
|
||||||
|
|
||||||
|
// For the access ACL, the feature only needs to hold the named user and
|
||||||
|
// group entries. For a correctly sorted ACL, these will be in a
|
||||||
|
// predictable range.
|
||||||
|
if (accessEntries.size() > 3) {
|
||||||
|
featureEntries.addAll(
|
||||||
|
accessEntries.subList(1, accessEntries.size() - 2));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add all default entries to the feature.
|
||||||
|
featureEntries.addAll(defaultEntries);
|
||||||
|
|
||||||
|
// Attach entries to the feature, creating a new feature if needed.
|
||||||
|
AclFeature aclFeature = inode.getAclFeature();
|
||||||
|
if (aclFeature == null) {
|
||||||
|
aclFeature = new AclFeature();
|
||||||
|
inode.addAclFeature(aclFeature);
|
||||||
|
}
|
||||||
|
aclFeature.setEntries(featureEntries);
|
||||||
|
} else {
|
||||||
|
// This is a minimal ACL. Remove the ACL feature if it previously had one.
|
||||||
|
if (perm.getAclBit()) {
|
||||||
|
inode.removeAclFeature();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate new permission bits. For a correctly sorted ACL, the owner,
|
||||||
|
// group and other permissions are in order. Also preserve sticky bit and
|
||||||
|
// toggle ACL bit off.
|
||||||
|
newPerm = new FsPermission(newAcl.get(0).getPermission(),
|
||||||
|
newAcl.get(1).getPermission(),
|
||||||
|
newAcl.get(2).getPermission(),
|
||||||
|
perm.getStickyBit(), false);
|
||||||
|
}
|
||||||
|
|
||||||
|
inode.setPermission(newPerm, snapshotId);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* There is no reason to instantiate this class.
|
||||||
|
*/
|
||||||
|
private AclStorage() {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Translates the given permission bits to the equivalent minimal ACL.
|
||||||
|
*
|
||||||
|
* @param perm FsPermission to translate
|
||||||
|
* @return List<AclEntry> containing exactly 3 entries representing the owner,
|
||||||
|
* group and other permissions
|
||||||
|
*/
|
||||||
|
private static List<AclEntry> getMinimalAcl(FsPermission perm) {
|
||||||
|
return Lists.newArrayList(
|
||||||
|
new AclEntry.Builder()
|
||||||
|
.setScope(AclEntryScope.ACCESS)
|
||||||
|
.setType(AclEntryType.USER)
|
||||||
|
.setPermission(perm.getUserAction())
|
||||||
|
.build(),
|
||||||
|
new AclEntry.Builder()
|
||||||
|
.setScope(AclEntryScope.ACCESS)
|
||||||
|
.setType(AclEntryType.GROUP)
|
||||||
|
.setPermission(perm.getGroupAction())
|
||||||
|
.build(),
|
||||||
|
new AclEntry.Builder()
|
||||||
|
.setScope(AclEntryScope.ACCESS)
|
||||||
|
.setType(AclEntryType.OTHER)
|
||||||
|
.setPermission(perm.getOtherAction())
|
||||||
|
.build());
|
||||||
|
}
|
||||||
|
}
|
|
@ -62,7 +62,6 @@ import org.apache.hadoop.hdfs.protocol.AclException;
|
||||||
@InterfaceAudience.Private
|
@InterfaceAudience.Private
|
||||||
final class AclTransformation {
|
final class AclTransformation {
|
||||||
private static final int MAX_ENTRIES = 32;
|
private static final int MAX_ENTRIES = 32;
|
||||||
private static final int PIVOT_NOT_FOUND = -1;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Filters (discards) any existing ACL entries that have the same scope, type
|
* Filters (discards) any existing ACL entries that have the same scope, type
|
||||||
|
@ -246,7 +245,7 @@ final class AclTransformation {
|
||||||
* -other entry
|
* -other entry
|
||||||
* All access ACL entries sort ahead of all default ACL entries.
|
* All access ACL entries sort ahead of all default ACL entries.
|
||||||
*/
|
*/
|
||||||
private static final Comparator<AclEntry> ACL_ENTRY_COMPARATOR =
|
static final Comparator<AclEntry> ACL_ENTRY_COMPARATOR =
|
||||||
new Comparator<AclEntry>() {
|
new Comparator<AclEntry>() {
|
||||||
@Override
|
@Override
|
||||||
public int compare(AclEntry entry1, AclEntry entry2) {
|
public int compare(AclEntry entry1, AclEntry entry2) {
|
||||||
|
@ -294,20 +293,20 @@ final class AclTransformation {
|
||||||
}
|
}
|
||||||
// Search for the required base access entries. If there is a default ACL,
|
// Search for the required base access entries. If there is a default ACL,
|
||||||
// then do the same check on the default entries.
|
// then do the same check on the default entries.
|
||||||
int pivot = calculatePivotOnDefaultEntries(aclBuilder);
|
ScopedAclEntries scopedEntries = new ScopedAclEntries(aclBuilder);
|
||||||
for (AclEntryType type: EnumSet.of(USER, GROUP, OTHER)) {
|
for (AclEntryType type: EnumSet.of(USER, GROUP, OTHER)) {
|
||||||
AclEntry accessEntryKey = new AclEntry.Builder().setScope(ACCESS)
|
AclEntry accessEntryKey = new AclEntry.Builder().setScope(ACCESS)
|
||||||
.setType(type).build();
|
.setType(type).build();
|
||||||
if (Collections.binarySearch(aclBuilder, accessEntryKey,
|
if (Collections.binarySearch(scopedEntries.getAccessEntries(),
|
||||||
ACL_ENTRY_COMPARATOR) < 0) {
|
accessEntryKey, ACL_ENTRY_COMPARATOR) < 0) {
|
||||||
throw new AclException(
|
throw new AclException(
|
||||||
"Invalid ACL: the user, group and other entries are required.");
|
"Invalid ACL: the user, group and other entries are required.");
|
||||||
}
|
}
|
||||||
if (pivot != PIVOT_NOT_FOUND) {
|
if (!scopedEntries.getDefaultEntries().isEmpty()) {
|
||||||
AclEntry defaultEntryKey = new AclEntry.Builder().setScope(DEFAULT)
|
AclEntry defaultEntryKey = new AclEntry.Builder().setScope(DEFAULT)
|
||||||
.setType(type).build();
|
.setType(type).build();
|
||||||
if (Collections.binarySearch(aclBuilder, defaultEntryKey,
|
if (Collections.binarySearch(scopedEntries.getDefaultEntries(),
|
||||||
ACL_ENTRY_COMPARATOR) < 0) {
|
defaultEntryKey, ACL_ENTRY_COMPARATOR) < 0) {
|
||||||
throw new AclException(
|
throw new AclException(
|
||||||
"Invalid default ACL: the user, group and other entries are required.");
|
"Invalid default ACL: the user, group and other entries are required.");
|
||||||
}
|
}
|
||||||
|
@ -364,12 +363,17 @@ final class AclTransformation {
|
||||||
for (AclEntryScope scope: scopeFound) {
|
for (AclEntryScope scope: scopeFound) {
|
||||||
if (!providedMask.containsKey(scope) && maskNeeded.contains(scope) &&
|
if (!providedMask.containsKey(scope) && maskNeeded.contains(scope) &&
|
||||||
maskDirty.contains(scope)) {
|
maskDirty.contains(scope)) {
|
||||||
|
// Caller explicitly removed mask entry, but it's required.
|
||||||
throw new AclException(
|
throw new AclException(
|
||||||
"Invalid ACL: mask is required, but it was deleted.");
|
"Invalid ACL: mask is required, but it was deleted.");
|
||||||
} else if (providedMask.containsKey(scope) &&
|
} else if (providedMask.containsKey(scope) &&
|
||||||
(!scopeDirty.contains(scope) || maskDirty.contains(scope))) {
|
(!scopeDirty.contains(scope) || maskDirty.contains(scope))) {
|
||||||
|
// Caller explicitly provided new mask, or we are preserving the existing
|
||||||
|
// mask in an unchanged scope.
|
||||||
aclBuilder.add(providedMask.get(scope));
|
aclBuilder.add(providedMask.get(scope));
|
||||||
} else if (maskNeeded.contains(scope)) {
|
} else if (maskNeeded.contains(scope) || providedMask.containsKey(scope)) {
|
||||||
|
// Otherwise, if there are maskable entries present, or the ACL
|
||||||
|
// previously had a mask, then recalculate a mask automatically.
|
||||||
aclBuilder.add(new AclEntry.Builder()
|
aclBuilder.add(new AclEntry.Builder()
|
||||||
.setScope(scope)
|
.setScope(scope)
|
||||||
.setType(MASK)
|
.setType(MASK)
|
||||||
|
@ -379,23 +383,6 @@ final class AclTransformation {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the pivot point in the list between the access entries and the
|
|
||||||
* default entries. This is the index of the first element in the list that is
|
|
||||||
* a default entry.
|
|
||||||
*
|
|
||||||
* @param aclBuilder ArrayList<AclEntry> containing entries to build
|
|
||||||
* @return int pivot point, or -1 if list contains no default entries
|
|
||||||
*/
|
|
||||||
private static int calculatePivotOnDefaultEntries(List<AclEntry> aclBuilder) {
|
|
||||||
for (int i = 0; i < aclBuilder.size(); ++i) {
|
|
||||||
if (aclBuilder.get(i).getScope() == DEFAULT) {
|
|
||||||
return i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return PIVOT_NOT_FOUND;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds unspecified default entries by copying permissions from the
|
* Adds unspecified default entries by copying permissions from the
|
||||||
* corresponding access entries.
|
* corresponding access entries.
|
||||||
|
@ -404,11 +391,10 @@ final class AclTransformation {
|
||||||
*/
|
*/
|
||||||
private static void copyDefaultsIfNeeded(List<AclEntry> aclBuilder) {
|
private static void copyDefaultsIfNeeded(List<AclEntry> aclBuilder) {
|
||||||
Collections.sort(aclBuilder, ACL_ENTRY_COMPARATOR);
|
Collections.sort(aclBuilder, ACL_ENTRY_COMPARATOR);
|
||||||
int pivot = calculatePivotOnDefaultEntries(aclBuilder);
|
ScopedAclEntries scopedEntries = new ScopedAclEntries(aclBuilder);
|
||||||
if (pivot != PIVOT_NOT_FOUND) {
|
if (!scopedEntries.getDefaultEntries().isEmpty()) {
|
||||||
List<AclEntry> accessEntries = aclBuilder.subList(0, pivot);
|
List<AclEntry> accessEntries = scopedEntries.getAccessEntries();
|
||||||
List<AclEntry> defaultEntries = aclBuilder.subList(pivot,
|
List<AclEntry> defaultEntries = scopedEntries.getDefaultEntries();
|
||||||
aclBuilder.size());
|
|
||||||
List<AclEntry> copiedEntries = Lists.newArrayListWithCapacity(3);
|
List<AclEntry> copiedEntries = Lists.newArrayListWithCapacity(3);
|
||||||
for (AclEntryType type: EnumSet.of(USER, GROUP, OTHER)) {
|
for (AclEntryType type: EnumSet.of(USER, GROUP, OTHER)) {
|
||||||
AclEntry defaultEntryKey = new AclEntry.Builder().setScope(DEFAULT)
|
AclEntry defaultEntryKey = new AclEntry.Builder().setScope(DEFAULT)
|
||||||
|
|
|
@ -1151,7 +1151,18 @@ public class FSDirectory implements Closeable {
|
||||||
if (inode == null) {
|
if (inode == null) {
|
||||||
throw new FileNotFoundException("File does not exist: " + src);
|
throw new FileNotFoundException("File does not exist: " + src);
|
||||||
}
|
}
|
||||||
inode.setPermission(permissions, inodesInPath.getLatestSnapshotId());
|
int snapshotId = inodesInPath.getLatestSnapshotId();
|
||||||
|
FsPermission oldPerm = inode.getPermissionStatus(snapshotId).getPermission();
|
||||||
|
// This method cannot toggle the ACL bit.
|
||||||
|
if (oldPerm.getAclBit() != permissions.getAclBit()) {
|
||||||
|
permissions = new FsPermission(
|
||||||
|
permissions.getUserAction(),
|
||||||
|
permissions.getGroupAction(),
|
||||||
|
permissions.getOtherAction(),
|
||||||
|
permissions.getStickyBit(),
|
||||||
|
oldPerm.getAclBit());
|
||||||
|
}
|
||||||
|
inode.setPermission(permissions, snapshotId);
|
||||||
}
|
}
|
||||||
|
|
||||||
void setOwner(String src, String username, String groupname)
|
void setOwner(String src, String username, String groupname)
|
||||||
|
@ -2631,6 +2642,78 @@ public class FSDirectory implements Closeable {
|
||||||
return addINode(path, symlink) ? symlink : null;
|
return addINode(path, symlink) ? symlink : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void modifyAclEntries(String src, List<AclEntry> aclSpec) throws IOException {
|
||||||
|
writeLock();
|
||||||
|
try {
|
||||||
|
List<AclEntry> newAcl = unprotectedModifyAclEntries(src, aclSpec);
|
||||||
|
fsImage.getEditLog().logSetAcl(src, newAcl);
|
||||||
|
} finally {
|
||||||
|
writeUnlock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<AclEntry> unprotectedModifyAclEntries(String src,
|
||||||
|
List<AclEntry> aclSpec) throws IOException {
|
||||||
|
assert hasWriteLock();
|
||||||
|
INodesInPath iip = rootDir.getINodesInPath4Write(normalizePath(src), true);
|
||||||
|
INodeWithAdditionalFields inode = resolveINodeWithAdditionalFields(src, iip);
|
||||||
|
int snapshotId = iip.getLatestSnapshotId();
|
||||||
|
List<AclEntry> existingAcl = AclStorage.readINodeLogicalAcl(inode,
|
||||||
|
snapshotId);
|
||||||
|
List<AclEntry> newAcl = AclTransformation.mergeAclEntries(existingAcl,
|
||||||
|
aclSpec);
|
||||||
|
AclStorage.updateINodeAcl(inode, newAcl, snapshotId);
|
||||||
|
return newAcl;
|
||||||
|
}
|
||||||
|
|
||||||
|
void removeAclEntries(String src, List<AclEntry> aclSpec) throws IOException {
|
||||||
|
writeLock();
|
||||||
|
try {
|
||||||
|
List<AclEntry> newAcl = unprotectedRemoveAclEntries(src, aclSpec);
|
||||||
|
fsImage.getEditLog().logSetAcl(src, newAcl);
|
||||||
|
} finally {
|
||||||
|
writeUnlock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<AclEntry> unprotectedRemoveAclEntries(String src,
|
||||||
|
List<AclEntry> aclSpec) throws IOException {
|
||||||
|
assert hasWriteLock();
|
||||||
|
INodesInPath iip = rootDir.getINodesInPath4Write(normalizePath(src), true);
|
||||||
|
INodeWithAdditionalFields inode = resolveINodeWithAdditionalFields(src, iip);
|
||||||
|
int snapshotId = iip.getLatestSnapshotId();
|
||||||
|
List<AclEntry> existingAcl = AclStorage.readINodeLogicalAcl(inode,
|
||||||
|
snapshotId);
|
||||||
|
List<AclEntry> newAcl = AclTransformation.filterAclEntriesByAclSpec(
|
||||||
|
existingAcl, aclSpec);
|
||||||
|
AclStorage.updateINodeAcl(inode, newAcl, snapshotId);
|
||||||
|
return newAcl;
|
||||||
|
}
|
||||||
|
|
||||||
|
void removeDefaultAcl(String src) throws IOException {
|
||||||
|
writeLock();
|
||||||
|
try {
|
||||||
|
List<AclEntry> newAcl = unprotectedRemoveDefaultAcl(src);
|
||||||
|
fsImage.getEditLog().logSetAcl(src, newAcl);
|
||||||
|
} finally {
|
||||||
|
writeUnlock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<AclEntry> unprotectedRemoveDefaultAcl(String src)
|
||||||
|
throws IOException {
|
||||||
|
assert hasWriteLock();
|
||||||
|
INodesInPath iip = rootDir.getINodesInPath4Write(normalizePath(src), true);
|
||||||
|
INodeWithAdditionalFields inode = resolveINodeWithAdditionalFields(src, iip);
|
||||||
|
int snapshotId = iip.getLatestSnapshotId();
|
||||||
|
List<AclEntry> existingAcl = AclStorage.readINodeLogicalAcl(inode,
|
||||||
|
snapshotId);
|
||||||
|
List<AclEntry> newAcl = AclTransformation.filterDefaultAclEntries(
|
||||||
|
existingAcl);
|
||||||
|
AclStorage.updateINodeAcl(inode, newAcl, snapshotId);
|
||||||
|
return newAcl;
|
||||||
|
}
|
||||||
|
|
||||||
void removeAcl(String src) throws IOException {
|
void removeAcl(String src) throws IOException {
|
||||||
writeLock();
|
writeLock();
|
||||||
try {
|
try {
|
||||||
|
@ -2641,74 +2724,67 @@ public class FSDirectory implements Closeable {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void unprotectedRemoveAcl(String src) throws UnresolvedLinkException,
|
private void unprotectedRemoveAcl(String src) throws IOException {
|
||||||
SnapshotAccessControlException, FileNotFoundException {
|
|
||||||
assert hasWriteLock();
|
assert hasWriteLock();
|
||||||
final INodeWithAdditionalFields node = resolveINodeWithAdditionalField(src);
|
INodesInPath iip = rootDir.getINodesInPath4Write(normalizePath(src), true);
|
||||||
AclFeature f = node.getAclFeature();
|
INodeWithAdditionalFields inode = resolveINodeWithAdditionalFields(src, iip);
|
||||||
if (f != null)
|
int snapshotId = iip.getLatestSnapshotId();
|
||||||
node.removeAclFeature();
|
AclStorage.removeINodeAcl(inode, snapshotId);
|
||||||
}
|
}
|
||||||
|
|
||||||
void setAcl(String src, List<AclEntry> aclSpec) throws IOException {
|
void setAcl(String src, List<AclEntry> aclSpec) throws IOException {
|
||||||
writeLock();
|
writeLock();
|
||||||
try {
|
try {
|
||||||
unprotectedSetAcl(src, aclSpec);
|
List<AclEntry> newAcl = unprotectedSetAcl(src, aclSpec);
|
||||||
fsImage.getEditLog().logSetAcl(src, aclSpec);
|
fsImage.getEditLog().logSetAcl(src, newAcl);
|
||||||
} finally {
|
} finally {
|
||||||
writeUnlock();
|
writeUnlock();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void unprotectedSetAcl(String src, List<AclEntry> aclSpec)
|
List<AclEntry> unprotectedSetAcl(String src, List<AclEntry> aclSpec)
|
||||||
throws UnresolvedLinkException, SnapshotAccessControlException,
|
throws IOException {
|
||||||
FileNotFoundException {
|
// ACL removal is logged to edits as OP_SET_ACL with an empty list.
|
||||||
|
if (aclSpec.isEmpty()) {
|
||||||
|
unprotectedRemoveAcl(src);
|
||||||
|
return AclFeature.EMPTY_ENTRY_LIST;
|
||||||
|
}
|
||||||
|
|
||||||
assert hasWriteLock();
|
assert hasWriteLock();
|
||||||
final INodeWithAdditionalFields node = resolveINodeWithAdditionalField(src);
|
INodesInPath iip = rootDir.getINodesInPath4Write(normalizePath(src), true);
|
||||||
AclFeature f = node.getAclFeature();
|
INodeWithAdditionalFields inode = resolveINodeWithAdditionalFields(src, iip);
|
||||||
|
int snapshotId = iip.getLatestSnapshotId();
|
||||||
if (aclSpec.size() == 0) {
|
List<AclEntry> existingAcl = AclStorage.readINodeLogicalAcl(inode,
|
||||||
if (f != null)
|
snapshotId);
|
||||||
node.removeAclFeature();
|
List<AclEntry> newAcl = AclTransformation.replaceAclEntries(existingAcl,
|
||||||
return;
|
aclSpec);
|
||||||
}
|
AclStorage.updateINodeAcl(inode, newAcl, snapshotId);
|
||||||
|
return newAcl;
|
||||||
if (f == null) {
|
|
||||||
f = new AclFeature();
|
|
||||||
node.addAclFeature(f);
|
|
||||||
}
|
|
||||||
f.setEntries(aclSpec);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
AclStatus getAclStatus(String src) throws IOException {
|
AclStatus getAclStatus(String src) throws IOException {
|
||||||
readLock();
|
readLock();
|
||||||
try {
|
try {
|
||||||
final INodeWithAdditionalFields node = resolveINodeWithAdditionalField(src);
|
INodesInPath iip = rootDir.getINodesInPath4Write(normalizePath(src), true);
|
||||||
AclFeature f = node.getAclFeature();
|
final INodeWithAdditionalFields inode = resolveINodeWithAdditionalFields(
|
||||||
|
src, iip);
|
||||||
AclStatus.Builder builder = new AclStatus.Builder()
|
int snapshotId = iip.getLatestSnapshotId();
|
||||||
.owner(node.getUserName()).group(node.getGroupName())
|
List<AclEntry> acl = AclStorage.readINodeAcl(inode, snapshotId);
|
||||||
.stickyBit(node.getFsPermission().getStickyBit());
|
return new AclStatus.Builder()
|
||||||
if (f != null) {
|
.owner(inode.getUserName()).group(inode.getGroupName())
|
||||||
builder.addEntries(f.getEntries());
|
.stickyBit(inode.getFsPermission(snapshotId).getStickyBit())
|
||||||
}
|
.addEntries(acl).build();
|
||||||
return builder.build();
|
|
||||||
} finally {
|
} finally {
|
||||||
readUnlock();
|
readUnlock();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private INodeWithAdditionalFields resolveINodeWithAdditionalField(String src)
|
private static INodeWithAdditionalFields resolveINodeWithAdditionalFields(
|
||||||
throws UnresolvedLinkException, SnapshotAccessControlException,
|
String src, INodesInPath iip) throws FileNotFoundException {
|
||||||
FileNotFoundException {
|
|
||||||
String srcs = normalizePath(src);
|
|
||||||
final INodesInPath iip = rootDir.getINodesInPath4Write(srcs, true);
|
|
||||||
INode inode = iip.getLastINode();
|
INode inode = iip.getLastINode();
|
||||||
if (!(inode instanceof INodeWithAdditionalFields))
|
if (!(inode instanceof INodeWithAdditionalFields))
|
||||||
throw new FileNotFoundException("cannot find " + src);
|
throw new FileNotFoundException("cannot find " + src);
|
||||||
|
return (INodeWithAdditionalFields)inode;
|
||||||
final INodeWithAdditionalFields node = (INodeWithAdditionalFields) inode;
|
|
||||||
return node;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -7326,36 +7326,88 @@ public class FSNamesystem implements Namesystem, FSClusterStats,
|
||||||
return results;
|
return results;
|
||||||
}
|
}
|
||||||
|
|
||||||
void modifyAclEntries(String src, Iterable<AclEntry> aclSpec) {
|
void modifyAclEntries(String src, List<AclEntry> aclSpec) throws IOException {
|
||||||
throw new UnsupportedOperationException("Unimplemented");
|
FSPermissionChecker pc = getPermissionChecker();
|
||||||
}
|
|
||||||
|
|
||||||
void removeAclEntries(String src, Iterable<AclEntry> aclSpec) {
|
|
||||||
throw new UnsupportedOperationException("Unimplemented");
|
|
||||||
}
|
|
||||||
|
|
||||||
void removeDefaultAcl(String src) {
|
|
||||||
throw new UnsupportedOperationException("Unimplemented");
|
|
||||||
}
|
|
||||||
|
|
||||||
void removeAcl(String src) throws IOException {
|
|
||||||
checkOperation(OperationCategory.WRITE);
|
checkOperation(OperationCategory.WRITE);
|
||||||
|
byte[][] pathComponents = FSDirectory.getPathComponentsForReservedPath(src);
|
||||||
writeLock();
|
writeLock();
|
||||||
try {
|
try {
|
||||||
checkOperation(OperationCategory.WRITE);
|
checkOperation(OperationCategory.WRITE);
|
||||||
checkNameNodeSafeMode("Cannot remove acl on " + src);
|
checkNameNodeSafeMode("Cannot modify ACL entries on " + src);
|
||||||
|
src = FSDirectory.resolvePath(src, pathComponents, dir);
|
||||||
|
checkOwner(pc, src);
|
||||||
|
dir.modifyAclEntries(src, aclSpec);
|
||||||
|
} finally {
|
||||||
|
writeUnlock();
|
||||||
|
}
|
||||||
|
getEditLog().logSync();
|
||||||
|
logAuditEvent(true, "modifyAclEntries", src);
|
||||||
|
}
|
||||||
|
|
||||||
|
void removeAclEntries(String src, List<AclEntry> aclSpec) throws IOException {
|
||||||
|
FSPermissionChecker pc = getPermissionChecker();
|
||||||
|
checkOperation(OperationCategory.WRITE);
|
||||||
|
byte[][] pathComponents = FSDirectory.getPathComponentsForReservedPath(src);
|
||||||
|
writeLock();
|
||||||
|
try {
|
||||||
|
checkOperation(OperationCategory.WRITE);
|
||||||
|
checkNameNodeSafeMode("Cannot remove ACL entries on " + src);
|
||||||
|
src = FSDirectory.resolvePath(src, pathComponents, dir);
|
||||||
|
checkOwner(pc, src);
|
||||||
|
dir.removeAclEntries(src, aclSpec);
|
||||||
|
} finally {
|
||||||
|
writeUnlock();
|
||||||
|
}
|
||||||
|
getEditLog().logSync();
|
||||||
|
logAuditEvent(true, "removeAclEntries", src);
|
||||||
|
}
|
||||||
|
|
||||||
|
void removeDefaultAcl(String src) throws IOException {
|
||||||
|
FSPermissionChecker pc = getPermissionChecker();
|
||||||
|
checkOperation(OperationCategory.WRITE);
|
||||||
|
byte[][] pathComponents = FSDirectory.getPathComponentsForReservedPath(src);
|
||||||
|
writeLock();
|
||||||
|
try {
|
||||||
|
checkOperation(OperationCategory.WRITE);
|
||||||
|
checkNameNodeSafeMode("Cannot remove default ACL entries on " + src);
|
||||||
|
src = FSDirectory.resolvePath(src, pathComponents, dir);
|
||||||
|
checkOwner(pc, src);
|
||||||
|
dir.removeDefaultAcl(src);
|
||||||
|
} finally {
|
||||||
|
writeUnlock();
|
||||||
|
}
|
||||||
|
getEditLog().logSync();
|
||||||
|
logAuditEvent(true, "removeDefaultAcl", src);
|
||||||
|
}
|
||||||
|
|
||||||
|
void removeAcl(String src) throws IOException {
|
||||||
|
FSPermissionChecker pc = getPermissionChecker();
|
||||||
|
checkOperation(OperationCategory.WRITE);
|
||||||
|
byte[][] pathComponents = FSDirectory.getPathComponentsForReservedPath(src);
|
||||||
|
writeLock();
|
||||||
|
try {
|
||||||
|
checkOperation(OperationCategory.WRITE);
|
||||||
|
checkNameNodeSafeMode("Cannot remove ACL on " + src);
|
||||||
|
src = FSDirectory.resolvePath(src, pathComponents, dir);
|
||||||
|
checkOwner(pc, src);
|
||||||
dir.removeAcl(src);
|
dir.removeAcl(src);
|
||||||
} finally {
|
} finally {
|
||||||
writeUnlock();
|
writeUnlock();
|
||||||
}
|
}
|
||||||
|
getEditLog().logSync();
|
||||||
|
logAuditEvent(true, "removeAcl", src);
|
||||||
}
|
}
|
||||||
|
|
||||||
void setAcl(String src, List<AclEntry> aclSpec) throws IOException {
|
void setAcl(String src, List<AclEntry> aclSpec) throws IOException {
|
||||||
|
FSPermissionChecker pc = getPermissionChecker();
|
||||||
checkOperation(OperationCategory.WRITE);
|
checkOperation(OperationCategory.WRITE);
|
||||||
|
byte[][] pathComponents = FSDirectory.getPathComponentsForReservedPath(src);
|
||||||
writeLock();
|
writeLock();
|
||||||
try {
|
try {
|
||||||
checkOperation(OperationCategory.WRITE);
|
checkOperation(OperationCategory.WRITE);
|
||||||
checkNameNodeSafeMode("Cannot set acl on " + src);
|
checkNameNodeSafeMode("Cannot set ACL on " + src);
|
||||||
|
src = FSDirectory.resolvePath(src, pathComponents, dir);
|
||||||
|
checkOwner(pc, src);
|
||||||
dir.setAcl(src, aclSpec);
|
dir.setAcl(src, aclSpec);
|
||||||
} finally {
|
} finally {
|
||||||
writeUnlock();
|
writeUnlock();
|
||||||
|
|
|
@ -0,0 +1,93 @@
|
||||||
|
/**
|
||||||
|
* 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.hdfs.server.namenode;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.apache.hadoop.classification.InterfaceAudience;
|
||||||
|
import org.apache.hadoop.fs.permission.AclEntry;
|
||||||
|
import org.apache.hadoop.fs.permission.AclEntryScope;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Groups a list of ACL entries into separate lists for access entries vs.
|
||||||
|
* default entries.
|
||||||
|
*/
|
||||||
|
@InterfaceAudience.Private
|
||||||
|
final class ScopedAclEntries {
|
||||||
|
private static final int PIVOT_NOT_FOUND = -1;
|
||||||
|
|
||||||
|
private final List<AclEntry> accessEntries;
|
||||||
|
private final List<AclEntry> defaultEntries;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new ScopedAclEntries from the given list. It is assumed that the
|
||||||
|
* list is already sorted such that all access entries precede all default
|
||||||
|
* entries.
|
||||||
|
*
|
||||||
|
* @param aclEntries List<AclEntry> to separate
|
||||||
|
*/
|
||||||
|
public ScopedAclEntries(List<AclEntry> aclEntries) {
|
||||||
|
int pivot = calculatePivotOnDefaultEntries(aclEntries);
|
||||||
|
if (pivot != PIVOT_NOT_FOUND) {
|
||||||
|
accessEntries = pivot != 0 ? aclEntries.subList(0, pivot) :
|
||||||
|
Collections.<AclEntry>emptyList();
|
||||||
|
defaultEntries = aclEntries.subList(pivot, aclEntries.size());
|
||||||
|
} else {
|
||||||
|
accessEntries = !aclEntries.isEmpty() ? aclEntries : null;
|
||||||
|
defaultEntries = Collections.emptyList();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns access entries.
|
||||||
|
*
|
||||||
|
* @return List<AclEntry> containing just access entries, or an empty list if
|
||||||
|
* there are no access entries
|
||||||
|
*/
|
||||||
|
public List<AclEntry> getAccessEntries() {
|
||||||
|
return accessEntries;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns default entries.
|
||||||
|
*
|
||||||
|
* @return List<AclEntry> containing just default entries, or an empty list if
|
||||||
|
* there are no default entries
|
||||||
|
*/
|
||||||
|
public List<AclEntry> getDefaultEntries() {
|
||||||
|
return defaultEntries;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the pivot point in the list between the access entries and the
|
||||||
|
* default entries. This is the index of the first element in the list that is
|
||||||
|
* a default entry.
|
||||||
|
*
|
||||||
|
* @param aclBuilder ArrayList<AclEntry> containing entries to build
|
||||||
|
* @return int pivot point, or -1 if list contains no default entries
|
||||||
|
*/
|
||||||
|
private static int calculatePivotOnDefaultEntries(List<AclEntry> aclBuilder) {
|
||||||
|
for (int i = 0; i < aclBuilder.size(); ++i) {
|
||||||
|
if (aclBuilder.get(i).getScope() == AclEntryScope.DEFAULT) {
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return PIVOT_NOT_FOUND;
|
||||||
|
}
|
||||||
|
}
|
|
@ -31,6 +31,7 @@ import org.apache.hadoop.fs.FSDataOutputStream;
|
||||||
import org.apache.hadoop.fs.FileStatus;
|
import org.apache.hadoop.fs.FileStatus;
|
||||||
import org.apache.hadoop.fs.FileSystem;
|
import org.apache.hadoop.fs.FileSystem;
|
||||||
import org.apache.hadoop.fs.Path;
|
import org.apache.hadoop.fs.Path;
|
||||||
|
import org.apache.hadoop.fs.permission.AclEntry;
|
||||||
import org.apache.hadoop.fs.permission.FsPermission;
|
import org.apache.hadoop.fs.permission.FsPermission;
|
||||||
import org.apache.hadoop.hdfs.MiniDFSCluster.DataNodeProperties;
|
import org.apache.hadoop.hdfs.MiniDFSCluster.DataNodeProperties;
|
||||||
import org.apache.hadoop.hdfs.protocol.HdfsConstants.SafeModeAction;
|
import org.apache.hadoop.hdfs.protocol.HdfsConstants.SafeModeAction;
|
||||||
|
@ -328,12 +329,48 @@ public class TestSafeMode {
|
||||||
fs.setTimes(file1, 0, 0);
|
fs.setTimes(file1, 0, 0);
|
||||||
}});
|
}});
|
||||||
|
|
||||||
|
runFsFun("modifyAclEntries while in SM", new FSRun() {
|
||||||
|
@Override
|
||||||
|
public void run(FileSystem fs) throws IOException {
|
||||||
|
fs.modifyAclEntries(file1, Lists.<AclEntry>newArrayList());
|
||||||
|
}});
|
||||||
|
|
||||||
|
runFsFun("removeAclEntries while in SM", new FSRun() {
|
||||||
|
@Override
|
||||||
|
public void run(FileSystem fs) throws IOException {
|
||||||
|
fs.removeAclEntries(file1, Lists.<AclEntry>newArrayList());
|
||||||
|
}});
|
||||||
|
|
||||||
|
runFsFun("removeDefaultAcl while in SM", new FSRun() {
|
||||||
|
@Override
|
||||||
|
public void run(FileSystem fs) throws IOException {
|
||||||
|
fs.removeDefaultAcl(file1);
|
||||||
|
}});
|
||||||
|
|
||||||
|
runFsFun("removeAcl while in SM", new FSRun() {
|
||||||
|
@Override
|
||||||
|
public void run(FileSystem fs) throws IOException {
|
||||||
|
fs.removeAcl(file1);
|
||||||
|
}});
|
||||||
|
|
||||||
|
runFsFun("setAcl while in SM", new FSRun() {
|
||||||
|
@Override
|
||||||
|
public void run(FileSystem fs) throws IOException {
|
||||||
|
fs.setAcl(file1, Lists.<AclEntry>newArrayList());
|
||||||
|
}});
|
||||||
|
|
||||||
try {
|
try {
|
||||||
DFSTestUtil.readFile(fs, file1);
|
DFSTestUtil.readFile(fs, file1);
|
||||||
} catch (IOException ioe) {
|
} catch (IOException ioe) {
|
||||||
fail("Set times failed while in SM");
|
fail("Set times failed while in SM");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
fs.getAclStatus(file1);
|
||||||
|
} catch (IOException ioe) {
|
||||||
|
fail("getAclStatus failed while in SM");
|
||||||
|
}
|
||||||
|
|
||||||
assertFalse("Could not leave SM",
|
assertFalse("Could not leave SM",
|
||||||
dfs.setSafeMode(SafeModeAction.SAFEMODE_LEAVE));
|
dfs.setSafeMode(SafeModeAction.SAFEMODE_LEAVE));
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,96 @@
|
||||||
|
/**
|
||||||
|
* 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.hdfs.server.namenode;
|
||||||
|
|
||||||
|
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.FsAction;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper methods useful for writing ACL tests.
|
||||||
|
*/
|
||||||
|
final class AclTestHelpers {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new AclEntry with scope, type and permission (no name).
|
||||||
|
*
|
||||||
|
* @param scope AclEntryScope scope of the ACL entry
|
||||||
|
* @param type AclEntryType ACL entry type
|
||||||
|
* @param permission FsAction set of permissions in the ACL entry
|
||||||
|
* @return AclEntry new AclEntry
|
||||||
|
*/
|
||||||
|
public static AclEntry aclEntry(AclEntryScope scope, AclEntryType type,
|
||||||
|
FsAction permission) {
|
||||||
|
return new AclEntry.Builder()
|
||||||
|
.setScope(scope)
|
||||||
|
.setType(type)
|
||||||
|
.setPermission(permission)
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new AclEntry with scope, type, name and permission.
|
||||||
|
*
|
||||||
|
* @param scope AclEntryScope scope of the ACL entry
|
||||||
|
* @param type AclEntryType ACL entry type
|
||||||
|
* @param name String optional ACL entry name
|
||||||
|
* @param permission FsAction set of permissions in the ACL entry
|
||||||
|
* @return AclEntry new AclEntry
|
||||||
|
*/
|
||||||
|
public static AclEntry aclEntry(AclEntryScope scope, AclEntryType type,
|
||||||
|
String name, FsAction permission) {
|
||||||
|
return new AclEntry.Builder()
|
||||||
|
.setScope(scope)
|
||||||
|
.setType(type)
|
||||||
|
.setName(name)
|
||||||
|
.setPermission(permission)
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new AclEntry with scope, type and name (no permission).
|
||||||
|
*
|
||||||
|
* @param scope AclEntryScope scope of the ACL entry
|
||||||
|
* @param type AclEntryType ACL entry type
|
||||||
|
* @param name String optional ACL entry name
|
||||||
|
* @return AclEntry new AclEntry
|
||||||
|
*/
|
||||||
|
public static AclEntry aclEntry(AclEntryScope scope, AclEntryType type,
|
||||||
|
String name) {
|
||||||
|
return new AclEntry.Builder()
|
||||||
|
.setScope(scope)
|
||||||
|
.setType(type)
|
||||||
|
.setName(name)
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new AclEntry with scope and type (no name or permission).
|
||||||
|
*
|
||||||
|
* @param scope AclEntryScope scope of the ACL entry
|
||||||
|
* @param type AclEntryType ACL entry type
|
||||||
|
* @return AclEntry new AclEntry
|
||||||
|
*/
|
||||||
|
public static AclEntry aclEntry(AclEntryScope scope, AclEntryType type) {
|
||||||
|
return new AclEntry.Builder()
|
||||||
|
.setScope(scope)
|
||||||
|
.setType(type)
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
}
|
|
@ -20,6 +20,7 @@ package org.apache.hadoop.hdfs.server.namenode;
|
||||||
import static org.apache.hadoop.fs.permission.AclEntryScope.*;
|
import static org.apache.hadoop.fs.permission.AclEntryScope.*;
|
||||||
import static org.apache.hadoop.fs.permission.AclEntryType.*;
|
import static org.apache.hadoop.fs.permission.AclEntryType.*;
|
||||||
import static org.apache.hadoop.fs.permission.FsAction.*;
|
import static org.apache.hadoop.fs.permission.FsAction.*;
|
||||||
|
import static org.apache.hadoop.hdfs.server.namenode.AclTestHelpers.*;
|
||||||
import static org.apache.hadoop.hdfs.server.namenode.AclTransformation.*;
|
import static org.apache.hadoop.hdfs.server.namenode.AclTransformation.*;
|
||||||
import static org.junit.Assert.*;
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
|
@ -1204,39 +1205,4 @@ public class TestAclTransformation {
|
||||||
aclEntry(ACCESS, MASK, ALL));
|
aclEntry(ACCESS, MASK, ALL));
|
||||||
replaceAclEntries(existing, aclSpec);
|
replaceAclEntries(existing, aclSpec);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static AclEntry aclEntry(AclEntryScope scope, AclEntryType type,
|
|
||||||
FsAction permission) {
|
|
||||||
return new AclEntry.Builder()
|
|
||||||
.setScope(scope)
|
|
||||||
.setType(type)
|
|
||||||
.setPermission(permission)
|
|
||||||
.build();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static AclEntry aclEntry(AclEntryScope scope, AclEntryType type,
|
|
||||||
String name, FsAction permission) {
|
|
||||||
return new AclEntry.Builder()
|
|
||||||
.setScope(scope)
|
|
||||||
.setType(type)
|
|
||||||
.setName(name)
|
|
||||||
.setPermission(permission)
|
|
||||||
.build();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static AclEntry aclEntry(AclEntryScope scope, AclEntryType type,
|
|
||||||
String name) {
|
|
||||||
return new AclEntry.Builder()
|
|
||||||
.setScope(scope)
|
|
||||||
.setType(type)
|
|
||||||
.setName(name)
|
|
||||||
.build();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static AclEntry aclEntry(AclEntryScope scope, AclEntryType type) {
|
|
||||||
return new AclEntry.Builder()
|
|
||||||
.setScope(scope)
|
|
||||||
.setType(type)
|
|
||||||
.build();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,15 +17,17 @@
|
||||||
*/
|
*/
|
||||||
package org.apache.hadoop.hdfs.server.namenode;
|
package org.apache.hadoop.hdfs.server.namenode;
|
||||||
|
|
||||||
|
import static org.apache.hadoop.hdfs.server.namenode.AclTestHelpers.*;
|
||||||
|
import static org.apache.hadoop.fs.permission.AclEntryScope.*;
|
||||||
|
import static org.apache.hadoop.fs.permission.AclEntryType.*;
|
||||||
|
import static org.apache.hadoop.fs.permission.FsAction.*;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
import org.apache.hadoop.conf.Configuration;
|
import org.apache.hadoop.conf.Configuration;
|
||||||
import org.apache.hadoop.fs.Path;
|
import org.apache.hadoop.fs.Path;
|
||||||
import org.apache.hadoop.fs.permission.AclEntry;
|
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.AclStatus;
|
||||||
import org.apache.hadoop.fs.permission.FsAction;
|
|
||||||
import org.apache.hadoop.hdfs.DistributedFileSystem;
|
import org.apache.hadoop.hdfs.DistributedFileSystem;
|
||||||
import org.apache.hadoop.hdfs.MiniDFSCluster;
|
import org.apache.hadoop.hdfs.MiniDFSCluster;
|
||||||
import org.apache.hadoop.hdfs.protocol.HdfsConstants.SafeModeAction;
|
import org.apache.hadoop.hdfs.protocol.HdfsConstants.SafeModeAction;
|
||||||
|
@ -59,9 +61,8 @@ public class TestFSImageWithAcl {
|
||||||
fs.mkdirs(new Path("/23"));
|
fs.mkdirs(new Path("/23"));
|
||||||
|
|
||||||
AclEntry e = new AclEntry.Builder().setName("foo")
|
AclEntry e = new AclEntry.Builder().setName("foo")
|
||||||
.setPermission(FsAction.READ_EXECUTE).setScope(AclEntryScope.DEFAULT)
|
.setPermission(READ_EXECUTE).setScope(ACCESS).setType(USER).build();
|
||||||
.setType(AclEntryType.OTHER).build();
|
fs.modifyAclEntries(p, Lists.newArrayList(e));
|
||||||
fs.setAcl(p, Lists.newArrayList(e));
|
|
||||||
|
|
||||||
if (persistNamespace) {
|
if (persistNamespace) {
|
||||||
fs.setSafeMode(SafeModeAction.SAFEMODE_ENTER);
|
fs.setSafeMode(SafeModeAction.SAFEMODE_ENTER);
|
||||||
|
@ -75,7 +76,24 @@ public class TestFSImageWithAcl {
|
||||||
AclStatus s = cluster.getNamesystem().getAclStatus(p.toString());
|
AclStatus s = cluster.getNamesystem().getAclStatus(p.toString());
|
||||||
AclEntry[] returned = Lists.newArrayList(s.getEntries()).toArray(
|
AclEntry[] returned = Lists.newArrayList(s.getEntries()).toArray(
|
||||||
new AclEntry[0]);
|
new AclEntry[0]);
|
||||||
Assert.assertArrayEquals(new AclEntry[] { e }, returned);
|
Assert.assertArrayEquals(new AclEntry[] {
|
||||||
|
aclEntry(ACCESS, USER, "foo", READ_EXECUTE),
|
||||||
|
aclEntry(ACCESS, GROUP, READ) }, returned);
|
||||||
|
|
||||||
|
fs.removeAcl(p);
|
||||||
|
|
||||||
|
if (persistNamespace) {
|
||||||
|
fs.setSafeMode(SafeModeAction.SAFEMODE_ENTER);
|
||||||
|
fs.saveNamespace();
|
||||||
|
fs.setSafeMode(SafeModeAction.SAFEMODE_LEAVE);
|
||||||
|
}
|
||||||
|
|
||||||
|
cluster.restartNameNode();
|
||||||
|
cluster.waitActive();
|
||||||
|
|
||||||
|
s = cluster.getNamesystem().getAclStatus(p.toString());
|
||||||
|
returned = Lists.newArrayList(s.getEntries()).toArray(new AclEntry[0]);
|
||||||
|
Assert.assertArrayEquals(new AclEntry[] { }, returned);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
|
@ -0,0 +1,417 @@
|
||||||
|
/**
|
||||||
|
* 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.hdfs.server.namenode;
|
||||||
|
|
||||||
|
import static org.apache.hadoop.fs.permission.AclEntryScope.*;
|
||||||
|
import static org.apache.hadoop.fs.permission.AclEntryType.*;
|
||||||
|
import static org.apache.hadoop.fs.permission.FsAction.*;
|
||||||
|
import static org.apache.hadoop.hdfs.server.namenode.AclTestHelpers.*;
|
||||||
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
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.FsAction;
|
||||||
|
import org.apache.hadoop.fs.permission.FsPermission;
|
||||||
|
import org.apache.hadoop.fs.permission.PermissionStatus;
|
||||||
|
import org.apache.hadoop.hdfs.server.namenode.snapshot.Snapshot;
|
||||||
|
import org.apache.hadoop.security.AccessControlException;
|
||||||
|
import org.apache.hadoop.security.UserGroupInformation;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unit tests covering FSPermissionChecker. All tests in this suite have been
|
||||||
|
* cross-validated against Linux setfacl/getfacl to check for consistency of the
|
||||||
|
* HDFS implementation.
|
||||||
|
*/
|
||||||
|
public class TestFSPermissionChecker {
|
||||||
|
private static final long PREFERRED_BLOCK_SIZE = 128 * 1024 * 1024;
|
||||||
|
private static final short REPLICATION = 3;
|
||||||
|
private static final String SUPERGROUP = "supergroup";
|
||||||
|
private static final String SUPERUSER = "superuser";
|
||||||
|
private static final UserGroupInformation BRUCE =
|
||||||
|
UserGroupInformation.createUserForTesting("bruce", new String[] { });
|
||||||
|
private static final UserGroupInformation DIANA =
|
||||||
|
UserGroupInformation.createUserForTesting("diana", new String[] { "sales" });
|
||||||
|
private static final UserGroupInformation CLARK =
|
||||||
|
UserGroupInformation.createUserForTesting("clark", new String[] { "execs" });
|
||||||
|
|
||||||
|
private INodeDirectory inodeRoot;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setUp() {
|
||||||
|
PermissionStatus permStatus = PermissionStatus.createImmutable(SUPERUSER,
|
||||||
|
SUPERGROUP, FsPermission.createImmutable((short)0755));
|
||||||
|
inodeRoot = new INodeDirectory(INodeId.ROOT_INODE_ID,
|
||||||
|
INodeDirectory.ROOT_NAME, permStatus, 0L);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testAclOwner() throws IOException {
|
||||||
|
INodeFile inodeFile = createINodeFile(inodeRoot, "file1", "bruce", "execs",
|
||||||
|
(short)0640);
|
||||||
|
addAcl(inodeFile,
|
||||||
|
aclEntry(ACCESS, USER, READ_WRITE),
|
||||||
|
aclEntry(ACCESS, GROUP, READ),
|
||||||
|
aclEntry(ACCESS, MASK, READ),
|
||||||
|
aclEntry(ACCESS, OTHER, NONE));
|
||||||
|
assertPermissionGranted(BRUCE, "/file1", READ);
|
||||||
|
assertPermissionGranted(BRUCE, "/file1", WRITE);
|
||||||
|
assertPermissionGranted(BRUCE, "/file1", READ_WRITE);
|
||||||
|
assertPermissionDenied(BRUCE, "/file1", EXECUTE);
|
||||||
|
assertPermissionDenied(DIANA, "/file1", READ);
|
||||||
|
assertPermissionDenied(DIANA, "/file1", WRITE);
|
||||||
|
assertPermissionDenied(DIANA, "/file1", EXECUTE);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testAclNamedUser() throws IOException {
|
||||||
|
INodeFile inodeFile = createINodeFile(inodeRoot, "file1", "bruce", "execs",
|
||||||
|
(short)0640);
|
||||||
|
addAcl(inodeFile,
|
||||||
|
aclEntry(ACCESS, USER, READ_WRITE),
|
||||||
|
aclEntry(ACCESS, USER, "diana", READ),
|
||||||
|
aclEntry(ACCESS, GROUP, READ),
|
||||||
|
aclEntry(ACCESS, MASK, READ),
|
||||||
|
aclEntry(ACCESS, OTHER, NONE));
|
||||||
|
assertPermissionGranted(DIANA, "/file1", READ);
|
||||||
|
assertPermissionDenied(DIANA, "/file1", WRITE);
|
||||||
|
assertPermissionDenied(DIANA, "/file1", EXECUTE);
|
||||||
|
assertPermissionDenied(DIANA, "/file1", READ_WRITE);
|
||||||
|
assertPermissionDenied(DIANA, "/file1", READ_EXECUTE);
|
||||||
|
assertPermissionDenied(DIANA, "/file1", WRITE_EXECUTE);
|
||||||
|
assertPermissionDenied(DIANA, "/file1", ALL);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testAclNamedUserDeny() throws IOException {
|
||||||
|
INodeFile inodeFile = createINodeFile(inodeRoot, "file1", "bruce", "execs",
|
||||||
|
(short)0644);
|
||||||
|
addAcl(inodeFile,
|
||||||
|
aclEntry(ACCESS, USER, READ_WRITE),
|
||||||
|
aclEntry(ACCESS, USER, "diana", NONE),
|
||||||
|
aclEntry(ACCESS, GROUP, READ),
|
||||||
|
aclEntry(ACCESS, MASK, READ),
|
||||||
|
aclEntry(ACCESS, OTHER, READ));
|
||||||
|
assertPermissionGranted(BRUCE, "/file1", READ_WRITE);
|
||||||
|
assertPermissionGranted(CLARK, "/file1", READ);
|
||||||
|
assertPermissionDenied(DIANA, "/file1", READ);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testAclNamedUserTraverseDeny() throws IOException {
|
||||||
|
INodeDirectory inodeDir = createINodeDirectory(inodeRoot, "dir1", "bruce",
|
||||||
|
"execs", (short)0755);
|
||||||
|
INodeFile inodeFile = createINodeFile(inodeDir, "file1", "bruce", "execs",
|
||||||
|
(short)0644);
|
||||||
|
addAcl(inodeDir,
|
||||||
|
aclEntry(ACCESS, USER, ALL),
|
||||||
|
aclEntry(ACCESS, USER, "diana", NONE),
|
||||||
|
aclEntry(ACCESS, GROUP, READ_EXECUTE),
|
||||||
|
aclEntry(ACCESS, MASK, READ_EXECUTE),
|
||||||
|
aclEntry(ACCESS, OTHER, READ_EXECUTE));
|
||||||
|
assertPermissionGranted(BRUCE, "/dir1/file1", READ_WRITE);
|
||||||
|
assertPermissionGranted(CLARK, "/dir1/file1", READ);
|
||||||
|
assertPermissionDenied(DIANA, "/dir1/file1", READ);
|
||||||
|
assertPermissionDenied(DIANA, "/dir1/file1", WRITE);
|
||||||
|
assertPermissionDenied(DIANA, "/dir1/file1", EXECUTE);
|
||||||
|
assertPermissionDenied(DIANA, "/dir1/file1", READ_WRITE);
|
||||||
|
assertPermissionDenied(DIANA, "/dir1/file1", READ_EXECUTE);
|
||||||
|
assertPermissionDenied(DIANA, "/dir1/file1", WRITE_EXECUTE);
|
||||||
|
assertPermissionDenied(DIANA, "/dir1/file1", ALL);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testAclNamedUserMask() throws IOException {
|
||||||
|
INodeFile inodeFile = createINodeFile(inodeRoot, "file1", "bruce", "execs",
|
||||||
|
(short)0620);
|
||||||
|
addAcl(inodeFile,
|
||||||
|
aclEntry(ACCESS, USER, READ_WRITE),
|
||||||
|
aclEntry(ACCESS, USER, "diana", READ),
|
||||||
|
aclEntry(ACCESS, GROUP, READ),
|
||||||
|
aclEntry(ACCESS, MASK, WRITE),
|
||||||
|
aclEntry(ACCESS, OTHER, NONE));
|
||||||
|
assertPermissionDenied(DIANA, "/file1", READ);
|
||||||
|
assertPermissionDenied(DIANA, "/file1", WRITE);
|
||||||
|
assertPermissionDenied(DIANA, "/file1", EXECUTE);
|
||||||
|
assertPermissionDenied(DIANA, "/file1", READ_WRITE);
|
||||||
|
assertPermissionDenied(DIANA, "/file1", READ_EXECUTE);
|
||||||
|
assertPermissionDenied(DIANA, "/file1", WRITE_EXECUTE);
|
||||||
|
assertPermissionDenied(DIANA, "/file1", ALL);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testAclGroup() throws IOException {
|
||||||
|
INodeFile inodeFile = createINodeFile(inodeRoot, "file1", "bruce", "execs",
|
||||||
|
(short)0640);
|
||||||
|
addAcl(inodeFile,
|
||||||
|
aclEntry(ACCESS, USER, READ_WRITE),
|
||||||
|
aclEntry(ACCESS, GROUP, READ),
|
||||||
|
aclEntry(ACCESS, MASK, READ),
|
||||||
|
aclEntry(ACCESS, OTHER, NONE));
|
||||||
|
assertPermissionGranted(CLARK, "/file1", READ);
|
||||||
|
assertPermissionDenied(CLARK, "/file1", WRITE);
|
||||||
|
assertPermissionDenied(CLARK, "/file1", EXECUTE);
|
||||||
|
assertPermissionDenied(CLARK, "/file1", READ_WRITE);
|
||||||
|
assertPermissionDenied(CLARK, "/file1", READ_EXECUTE);
|
||||||
|
assertPermissionDenied(CLARK, "/file1", WRITE_EXECUTE);
|
||||||
|
assertPermissionDenied(CLARK, "/file1", ALL);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testAclGroupDeny() throws IOException {
|
||||||
|
INodeFile inodeFile = createINodeFile(inodeRoot, "file1", "bruce", "sales",
|
||||||
|
(short)0604);
|
||||||
|
addAcl(inodeFile,
|
||||||
|
aclEntry(ACCESS, USER, READ_WRITE),
|
||||||
|
aclEntry(ACCESS, GROUP, NONE),
|
||||||
|
aclEntry(ACCESS, MASK, NONE),
|
||||||
|
aclEntry(ACCESS, OTHER, READ));
|
||||||
|
assertPermissionGranted(BRUCE, "/file1", READ_WRITE);
|
||||||
|
assertPermissionGranted(CLARK, "/file1", READ);
|
||||||
|
assertPermissionDenied(DIANA, "/file1", READ);
|
||||||
|
assertPermissionDenied(DIANA, "/file1", WRITE);
|
||||||
|
assertPermissionDenied(DIANA, "/file1", EXECUTE);
|
||||||
|
assertPermissionDenied(DIANA, "/file1", READ_WRITE);
|
||||||
|
assertPermissionDenied(DIANA, "/file1", READ_EXECUTE);
|
||||||
|
assertPermissionDenied(DIANA, "/file1", WRITE_EXECUTE);
|
||||||
|
assertPermissionDenied(DIANA, "/file1", ALL);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testAclGroupTraverseDeny() throws IOException {
|
||||||
|
INodeDirectory inodeDir = createINodeDirectory(inodeRoot, "dir1", "bruce",
|
||||||
|
"execs", (short)0755);
|
||||||
|
INodeFile inodeFile = createINodeFile(inodeDir, "file1", "bruce", "execs",
|
||||||
|
(short)0644);
|
||||||
|
addAcl(inodeDir,
|
||||||
|
aclEntry(ACCESS, USER, ALL),
|
||||||
|
aclEntry(ACCESS, GROUP, NONE),
|
||||||
|
aclEntry(ACCESS, MASK, NONE),
|
||||||
|
aclEntry(ACCESS, OTHER, READ_EXECUTE));
|
||||||
|
assertPermissionGranted(BRUCE, "/dir1/file1", READ_WRITE);
|
||||||
|
assertPermissionGranted(DIANA, "/dir1/file1", READ);
|
||||||
|
assertPermissionDenied(CLARK, "/dir1/file1", READ);
|
||||||
|
assertPermissionDenied(CLARK, "/dir1/file1", WRITE);
|
||||||
|
assertPermissionDenied(CLARK, "/dir1/file1", EXECUTE);
|
||||||
|
assertPermissionDenied(CLARK, "/dir1/file1", READ_WRITE);
|
||||||
|
assertPermissionDenied(CLARK, "/dir1/file1", READ_EXECUTE);
|
||||||
|
assertPermissionDenied(CLARK, "/dir1/file1", WRITE_EXECUTE);
|
||||||
|
assertPermissionDenied(CLARK, "/dir1/file1", ALL);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testAclGroupTraverseDenyOnlyDefaultEntries() throws IOException {
|
||||||
|
INodeDirectory inodeDir = createINodeDirectory(inodeRoot, "dir1", "bruce",
|
||||||
|
"execs", (short)0755);
|
||||||
|
INodeFile inodeFile = createINodeFile(inodeDir, "file1", "bruce", "execs",
|
||||||
|
(short)0644);
|
||||||
|
addAcl(inodeDir,
|
||||||
|
aclEntry(ACCESS, USER, ALL),
|
||||||
|
aclEntry(ACCESS, GROUP, NONE),
|
||||||
|
aclEntry(ACCESS, OTHER, READ_EXECUTE),
|
||||||
|
aclEntry(DEFAULT, USER, ALL),
|
||||||
|
aclEntry(DEFAULT, GROUP, "sales", NONE),
|
||||||
|
aclEntry(DEFAULT, GROUP, NONE),
|
||||||
|
aclEntry(DEFAULT, OTHER, READ_EXECUTE));
|
||||||
|
assertPermissionGranted(BRUCE, "/dir1/file1", READ_WRITE);
|
||||||
|
assertPermissionGranted(DIANA, "/dir1/file1", READ);
|
||||||
|
assertPermissionDenied(CLARK, "/dir1/file1", READ);
|
||||||
|
assertPermissionDenied(CLARK, "/dir1/file1", WRITE);
|
||||||
|
assertPermissionDenied(CLARK, "/dir1/file1", EXECUTE);
|
||||||
|
assertPermissionDenied(CLARK, "/dir1/file1", READ_WRITE);
|
||||||
|
assertPermissionDenied(CLARK, "/dir1/file1", READ_EXECUTE);
|
||||||
|
assertPermissionDenied(CLARK, "/dir1/file1", WRITE_EXECUTE);
|
||||||
|
assertPermissionDenied(CLARK, "/dir1/file1", ALL);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testAclGroupMask() throws IOException {
|
||||||
|
INodeFile inodeFile = createINodeFile(inodeRoot, "file1", "bruce", "execs",
|
||||||
|
(short)0644);
|
||||||
|
addAcl(inodeFile,
|
||||||
|
aclEntry(ACCESS, USER, READ_WRITE),
|
||||||
|
aclEntry(ACCESS, GROUP, READ_WRITE),
|
||||||
|
aclEntry(ACCESS, MASK, READ),
|
||||||
|
aclEntry(ACCESS, OTHER, READ));
|
||||||
|
assertPermissionGranted(BRUCE, "/file1", READ_WRITE);
|
||||||
|
assertPermissionGranted(CLARK, "/file1", READ);
|
||||||
|
assertPermissionDenied(CLARK, "/file1", WRITE);
|
||||||
|
assertPermissionDenied(CLARK, "/file1", EXECUTE);
|
||||||
|
assertPermissionDenied(CLARK, "/file1", READ_WRITE);
|
||||||
|
assertPermissionDenied(CLARK, "/file1", READ_EXECUTE);
|
||||||
|
assertPermissionDenied(CLARK, "/file1", WRITE_EXECUTE);
|
||||||
|
assertPermissionDenied(CLARK, "/file1", ALL);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testAclNamedGroup() throws IOException {
|
||||||
|
INodeFile inodeFile = createINodeFile(inodeRoot, "file1", "bruce", "execs",
|
||||||
|
(short)0640);
|
||||||
|
addAcl(inodeFile,
|
||||||
|
aclEntry(ACCESS, USER, READ_WRITE),
|
||||||
|
aclEntry(ACCESS, GROUP, READ),
|
||||||
|
aclEntry(ACCESS, GROUP, "sales", READ),
|
||||||
|
aclEntry(ACCESS, MASK, READ),
|
||||||
|
aclEntry(ACCESS, OTHER, NONE));
|
||||||
|
assertPermissionGranted(BRUCE, "/file1", READ_WRITE);
|
||||||
|
assertPermissionGranted(CLARK, "/file1", READ);
|
||||||
|
assertPermissionGranted(DIANA, "/file1", READ);
|
||||||
|
assertPermissionDenied(DIANA, "/file1", WRITE);
|
||||||
|
assertPermissionDenied(DIANA, "/file1", EXECUTE);
|
||||||
|
assertPermissionDenied(DIANA, "/file1", READ_WRITE);
|
||||||
|
assertPermissionDenied(DIANA, "/file1", READ_EXECUTE);
|
||||||
|
assertPermissionDenied(DIANA, "/file1", ALL);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testAclNamedGroupDeny() throws IOException {
|
||||||
|
INodeFile inodeFile = createINodeFile(inodeRoot, "file1", "bruce", "sales",
|
||||||
|
(short)0644);
|
||||||
|
addAcl(inodeFile,
|
||||||
|
aclEntry(ACCESS, USER, READ_WRITE),
|
||||||
|
aclEntry(ACCESS, GROUP, READ),
|
||||||
|
aclEntry(ACCESS, GROUP, "execs", NONE),
|
||||||
|
aclEntry(ACCESS, MASK, READ),
|
||||||
|
aclEntry(ACCESS, OTHER, READ));
|
||||||
|
assertPermissionGranted(BRUCE, "/file1", READ_WRITE);
|
||||||
|
assertPermissionGranted(DIANA, "/file1", READ);
|
||||||
|
assertPermissionDenied(CLARK, "/file1", READ);
|
||||||
|
assertPermissionDenied(CLARK, "/file1", WRITE);
|
||||||
|
assertPermissionDenied(CLARK, "/file1", EXECUTE);
|
||||||
|
assertPermissionDenied(CLARK, "/file1", READ_WRITE);
|
||||||
|
assertPermissionDenied(CLARK, "/file1", READ_EXECUTE);
|
||||||
|
assertPermissionDenied(CLARK, "/file1", WRITE_EXECUTE);
|
||||||
|
assertPermissionDenied(CLARK, "/file1", ALL);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testAclNamedGroupTraverseDeny() throws IOException {
|
||||||
|
INodeDirectory inodeDir = createINodeDirectory(inodeRoot, "dir1", "bruce",
|
||||||
|
"execs", (short)0755);
|
||||||
|
INodeFile inodeFile = createINodeFile(inodeDir, "file1", "bruce", "execs",
|
||||||
|
(short)0644);
|
||||||
|
addAcl(inodeDir,
|
||||||
|
aclEntry(ACCESS, USER, ALL),
|
||||||
|
aclEntry(ACCESS, GROUP, READ_EXECUTE),
|
||||||
|
aclEntry(ACCESS, GROUP, "sales", NONE),
|
||||||
|
aclEntry(ACCESS, MASK, READ_EXECUTE),
|
||||||
|
aclEntry(ACCESS, OTHER, READ_EXECUTE));
|
||||||
|
assertPermissionGranted(BRUCE, "/dir1/file1", READ_WRITE);
|
||||||
|
assertPermissionGranted(CLARK, "/dir1/file1", READ);
|
||||||
|
assertPermissionDenied(DIANA, "/dir1/file1", READ);
|
||||||
|
assertPermissionDenied(DIANA, "/dir1/file1", WRITE);
|
||||||
|
assertPermissionDenied(DIANA, "/dir1/file1", EXECUTE);
|
||||||
|
assertPermissionDenied(DIANA, "/dir1/file1", READ_WRITE);
|
||||||
|
assertPermissionDenied(DIANA, "/dir1/file1", READ_EXECUTE);
|
||||||
|
assertPermissionDenied(DIANA, "/dir1/file1", WRITE_EXECUTE);
|
||||||
|
assertPermissionDenied(DIANA, "/dir1/file1", ALL);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testAclNamedGroupMask() throws IOException {
|
||||||
|
INodeFile inodeFile = createINodeFile(inodeRoot, "file1", "bruce", "execs",
|
||||||
|
(short)0644);
|
||||||
|
addAcl(inodeFile,
|
||||||
|
aclEntry(ACCESS, USER, READ_WRITE),
|
||||||
|
aclEntry(ACCESS, GROUP, READ),
|
||||||
|
aclEntry(ACCESS, GROUP, "sales", READ_WRITE),
|
||||||
|
aclEntry(ACCESS, MASK, READ),
|
||||||
|
aclEntry(ACCESS, OTHER, READ));
|
||||||
|
assertPermissionGranted(BRUCE, "/file1", READ_WRITE);
|
||||||
|
assertPermissionGranted(CLARK, "/file1", READ);
|
||||||
|
assertPermissionGranted(DIANA, "/file1", READ);
|
||||||
|
assertPermissionDenied(DIANA, "/file1", WRITE);
|
||||||
|
assertPermissionDenied(DIANA, "/file1", EXECUTE);
|
||||||
|
assertPermissionDenied(DIANA, "/file1", READ_WRITE);
|
||||||
|
assertPermissionDenied(DIANA, "/file1", READ_EXECUTE);
|
||||||
|
assertPermissionDenied(DIANA, "/file1", WRITE_EXECUTE);
|
||||||
|
assertPermissionDenied(DIANA, "/file1", ALL);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testAclOther() throws IOException {
|
||||||
|
INodeFile inodeFile = createINodeFile(inodeRoot, "file1", "bruce", "sales",
|
||||||
|
(short)0774);
|
||||||
|
addAcl(inodeFile,
|
||||||
|
aclEntry(ACCESS, USER, ALL),
|
||||||
|
aclEntry(ACCESS, USER, "diana", ALL),
|
||||||
|
aclEntry(ACCESS, GROUP, READ_WRITE),
|
||||||
|
aclEntry(ACCESS, MASK, ALL),
|
||||||
|
aclEntry(ACCESS, OTHER, READ));
|
||||||
|
assertPermissionGranted(BRUCE, "/file1", ALL);
|
||||||
|
assertPermissionGranted(DIANA, "/file1", ALL);
|
||||||
|
assertPermissionGranted(CLARK, "/file1", READ);
|
||||||
|
assertPermissionDenied(CLARK, "/file1", WRITE);
|
||||||
|
assertPermissionDenied(CLARK, "/file1", EXECUTE);
|
||||||
|
assertPermissionDenied(CLARK, "/file1", READ_WRITE);
|
||||||
|
assertPermissionDenied(CLARK, "/file1", READ_EXECUTE);
|
||||||
|
assertPermissionDenied(CLARK, "/file1", WRITE_EXECUTE);
|
||||||
|
assertPermissionDenied(CLARK, "/file1", ALL);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addAcl(INodeWithAdditionalFields inode, AclEntry... acl)
|
||||||
|
throws IOException {
|
||||||
|
AclStorage.updateINodeAcl((INodeWithAdditionalFields)inode,
|
||||||
|
Arrays.asList(acl), Snapshot.CURRENT_STATE_ID);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void assertPermissionGranted(UserGroupInformation user, String path,
|
||||||
|
FsAction access) throws IOException {
|
||||||
|
new FSPermissionChecker(SUPERUSER, SUPERGROUP, user).checkPermission(path,
|
||||||
|
inodeRoot, false, null, null, access, null, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void assertPermissionDenied(UserGroupInformation user, String path,
|
||||||
|
FsAction access) throws IOException {
|
||||||
|
try {
|
||||||
|
new FSPermissionChecker(SUPERUSER, SUPERGROUP, user).checkPermission(path,
|
||||||
|
inodeRoot, false, null, null, access, null, true);
|
||||||
|
fail("expected AccessControlException for user + " + user + ", path = " +
|
||||||
|
path + ", access = " + access);
|
||||||
|
} catch (AccessControlException e) {
|
||||||
|
// expected
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static INodeDirectory createINodeDirectory(INodeDirectory parent,
|
||||||
|
String name, String owner, String group, short perm) throws IOException {
|
||||||
|
PermissionStatus permStatus = PermissionStatus.createImmutable(owner, group,
|
||||||
|
FsPermission.createImmutable(perm));
|
||||||
|
INodeDirectory inodeDirectory = new INodeDirectory(
|
||||||
|
INodeId.GRANDFATHER_INODE_ID, name.getBytes("UTF-8"), permStatus, 0L);
|
||||||
|
parent.addChild(inodeDirectory);
|
||||||
|
return inodeDirectory;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static INodeFile createINodeFile(INodeDirectory parent, String name,
|
||||||
|
String owner, String group, short perm) throws IOException {
|
||||||
|
PermissionStatus permStatus = PermissionStatus.createImmutable(owner, group,
|
||||||
|
FsPermission.createImmutable(perm));
|
||||||
|
INodeFile inodeFile = new INodeFile(INodeId.GRANDFATHER_INODE_ID,
|
||||||
|
name.getBytes("UTF-8"), permStatus, 0L, 0L, null, REPLICATION,
|
||||||
|
PREFERRED_BLOCK_SIZE);
|
||||||
|
parent.addChild(inodeFile);
|
||||||
|
return inodeFile;
|
||||||
|
}
|
||||||
|
}
|
|
@ -17,54 +17,786 @@
|
||||||
*/
|
*/
|
||||||
package org.apache.hadoop.hdfs.server.namenode;
|
package org.apache.hadoop.hdfs.server.namenode;
|
||||||
|
|
||||||
|
import static org.apache.hadoop.hdfs.server.namenode.AclTestHelpers.*;
|
||||||
|
import static org.apache.hadoop.fs.permission.AclEntryScope.*;
|
||||||
|
import static org.apache.hadoop.fs.permission.AclEntryType.*;
|
||||||
|
import static org.apache.hadoop.fs.permission.FsAction.*;
|
||||||
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
|
import java.io.FileNotFoundException;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
import org.apache.hadoop.conf.Configuration;
|
import org.apache.hadoop.conf.Configuration;
|
||||||
import org.apache.hadoop.fs.FileSystem;
|
import org.apache.hadoop.fs.FileSystem;
|
||||||
import org.apache.hadoop.fs.Path;
|
import org.apache.hadoop.fs.Path;
|
||||||
import org.apache.hadoop.fs.permission.AclEntry;
|
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.AclStatus;
|
||||||
import org.apache.hadoop.fs.permission.FsAction;
|
import org.apache.hadoop.fs.permission.FsPermission;
|
||||||
import org.apache.hadoop.hdfs.MiniDFSCluster;
|
import org.apache.hadoop.hdfs.MiniDFSCluster;
|
||||||
|
import org.apache.hadoop.hdfs.protocol.AclException;
|
||||||
|
import org.apache.hadoop.io.IOUtils;
|
||||||
import org.junit.AfterClass;
|
import org.junit.AfterClass;
|
||||||
import org.junit.Assert;
|
import org.junit.Before;
|
||||||
import org.junit.BeforeClass;
|
import org.junit.BeforeClass;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import com.google.common.collect.Lists;
|
import com.google.common.collect.Lists;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests NameNode interaction for all ACL modification APIs. This test suite
|
||||||
|
* also covers interaction of setPermission with inodes that have ACLs.
|
||||||
|
*/
|
||||||
public class TestNameNodeAcl {
|
public class TestNameNodeAcl {
|
||||||
|
|
||||||
private static MiniDFSCluster cluster;
|
private static MiniDFSCluster cluster;
|
||||||
private static Configuration conf;
|
private static Configuration conf;
|
||||||
|
private static FileSystem fs;
|
||||||
|
private static int pathCount = 0;
|
||||||
|
private static Path path;
|
||||||
|
|
||||||
@BeforeClass
|
@BeforeClass
|
||||||
public static void setUp() throws Exception {
|
public static void init() throws Exception {
|
||||||
conf = new Configuration();
|
conf = new Configuration();
|
||||||
|
|
||||||
cluster = new MiniDFSCluster.Builder(conf).numDataNodes(1).build();
|
cluster = new MiniDFSCluster.Builder(conf).numDataNodes(1).build();
|
||||||
cluster.waitActive();
|
cluster.waitActive();
|
||||||
|
fs = cluster.getFileSystem();
|
||||||
}
|
}
|
||||||
|
|
||||||
@AfterClass
|
@AfterClass
|
||||||
public static void tearDown() throws Exception {
|
public static void shutdown() throws Exception {
|
||||||
cluster.shutdown();
|
IOUtils.cleanup(null, fs);
|
||||||
|
if (cluster != null) {
|
||||||
|
cluster.shutdown();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setUp() {
|
||||||
|
pathCount += 1;
|
||||||
|
path = new Path("/p" + pathCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testModifyAclEntries() throws IOException {
|
||||||
|
FileSystem.mkdirs(fs, path, FsPermission.createImmutable((short)0750));
|
||||||
|
List<AclEntry> aclSpec = Lists.newArrayList(
|
||||||
|
aclEntry(ACCESS, USER, ALL),
|
||||||
|
aclEntry(ACCESS, USER, "foo", ALL),
|
||||||
|
aclEntry(ACCESS, GROUP, READ_EXECUTE),
|
||||||
|
aclEntry(ACCESS, OTHER, NONE),
|
||||||
|
aclEntry(DEFAULT, USER, "foo", ALL));
|
||||||
|
fs.setAcl(path, aclSpec);
|
||||||
|
aclSpec = Lists.newArrayList(
|
||||||
|
aclEntry(ACCESS, USER, "foo", READ_EXECUTE),
|
||||||
|
aclEntry(DEFAULT, USER, "foo", READ_EXECUTE));
|
||||||
|
fs.modifyAclEntries(path, aclSpec);
|
||||||
|
AclStatus s = fs.getAclStatus(path);
|
||||||
|
AclEntry[] returned = s.getEntries().toArray(new AclEntry[0]);
|
||||||
|
assertArrayEquals(new AclEntry[] {
|
||||||
|
aclEntry(ACCESS, USER, "foo", READ_EXECUTE),
|
||||||
|
aclEntry(ACCESS, GROUP, READ_EXECUTE),
|
||||||
|
aclEntry(DEFAULT, USER, ALL),
|
||||||
|
aclEntry(DEFAULT, USER, "foo", READ_EXECUTE),
|
||||||
|
aclEntry(DEFAULT, GROUP, READ_EXECUTE),
|
||||||
|
aclEntry(DEFAULT, MASK, READ_EXECUTE),
|
||||||
|
aclEntry(DEFAULT, OTHER, NONE) }, returned);
|
||||||
|
assertPermission((short)02750);
|
||||||
|
assertAclFeature(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testModifyAclEntriesOnlyAccess() throws IOException {
|
||||||
|
FileSystem.create(fs, path, FsPermission.createImmutable((short)0640));
|
||||||
|
List<AclEntry> aclSpec = Lists.newArrayList(
|
||||||
|
aclEntry(ACCESS, USER, ALL),
|
||||||
|
aclEntry(ACCESS, USER, "foo", ALL),
|
||||||
|
aclEntry(ACCESS, GROUP, READ_EXECUTE),
|
||||||
|
aclEntry(ACCESS, OTHER, NONE));
|
||||||
|
fs.setAcl(path, aclSpec);
|
||||||
|
aclSpec = Lists.newArrayList(
|
||||||
|
aclEntry(ACCESS, USER, "foo", READ_EXECUTE));
|
||||||
|
fs.modifyAclEntries(path, aclSpec);
|
||||||
|
AclStatus s = fs.getAclStatus(path);
|
||||||
|
AclEntry[] returned = s.getEntries().toArray(new AclEntry[0]);
|
||||||
|
assertArrayEquals(new AclEntry[] {
|
||||||
|
aclEntry(ACCESS, USER, "foo", READ_EXECUTE),
|
||||||
|
aclEntry(ACCESS, GROUP, READ_EXECUTE) }, returned);
|
||||||
|
assertPermission((short)02750);
|
||||||
|
assertAclFeature(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testModifyAclEntriesOnlyDefault() throws IOException {
|
||||||
|
FileSystem.mkdirs(fs, path, FsPermission.createImmutable((short)0750));
|
||||||
|
List<AclEntry> aclSpec = Lists.newArrayList(
|
||||||
|
aclEntry(DEFAULT, USER, "foo", ALL));
|
||||||
|
fs.setAcl(path, aclSpec);
|
||||||
|
aclSpec = Lists.newArrayList(
|
||||||
|
aclEntry(DEFAULT, USER, "foo", READ_EXECUTE));
|
||||||
|
fs.modifyAclEntries(path, aclSpec);
|
||||||
|
AclStatus s = fs.getAclStatus(path);
|
||||||
|
AclEntry[] returned = s.getEntries().toArray(new AclEntry[0]);
|
||||||
|
assertArrayEquals(new AclEntry[] {
|
||||||
|
aclEntry(DEFAULT, USER, ALL),
|
||||||
|
aclEntry(DEFAULT, USER, "foo", READ_EXECUTE),
|
||||||
|
aclEntry(DEFAULT, GROUP, READ_EXECUTE),
|
||||||
|
aclEntry(DEFAULT, MASK, READ_EXECUTE),
|
||||||
|
aclEntry(DEFAULT, OTHER, NONE) }, returned);
|
||||||
|
assertPermission((short)02750);
|
||||||
|
assertAclFeature(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testModifyAclEntriesMinimal() throws IOException {
|
||||||
|
FileSystem.create(fs, path, FsPermission.createImmutable((short)0640));
|
||||||
|
List<AclEntry> aclSpec = Lists.newArrayList(
|
||||||
|
aclEntry(ACCESS, USER, "foo", READ_WRITE));
|
||||||
|
fs.modifyAclEntries(path, aclSpec);
|
||||||
|
AclStatus s = fs.getAclStatus(path);
|
||||||
|
AclEntry[] returned = s.getEntries().toArray(new AclEntry[0]);
|
||||||
|
assertArrayEquals(new AclEntry[] {
|
||||||
|
aclEntry(ACCESS, USER, "foo", READ_WRITE),
|
||||||
|
aclEntry(ACCESS, GROUP, READ) }, returned);
|
||||||
|
assertPermission((short)02660);
|
||||||
|
assertAclFeature(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testModifyAclEntriesMinimalDefault() throws IOException {
|
||||||
|
FileSystem.mkdirs(fs, path, FsPermission.createImmutable((short)0750));
|
||||||
|
List<AclEntry> aclSpec = Lists.newArrayList(
|
||||||
|
aclEntry(DEFAULT, USER, ALL),
|
||||||
|
aclEntry(DEFAULT, GROUP, READ_EXECUTE),
|
||||||
|
aclEntry(DEFAULT, OTHER, NONE));
|
||||||
|
fs.modifyAclEntries(path, aclSpec);
|
||||||
|
AclStatus s = fs.getAclStatus(path);
|
||||||
|
AclEntry[] returned = s.getEntries().toArray(new AclEntry[0]);
|
||||||
|
assertArrayEquals(new AclEntry[] {
|
||||||
|
aclEntry(DEFAULT, USER, ALL),
|
||||||
|
aclEntry(DEFAULT, GROUP, READ_EXECUTE),
|
||||||
|
aclEntry(DEFAULT, OTHER, NONE) }, returned);
|
||||||
|
assertPermission((short)02750);
|
||||||
|
assertAclFeature(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testModifyAclEntriesCustomMask() throws IOException {
|
||||||
|
FileSystem.create(fs, path, FsPermission.createImmutable((short)0640));
|
||||||
|
List<AclEntry> aclSpec = Lists.newArrayList(
|
||||||
|
aclEntry(ACCESS, USER, "foo", ALL),
|
||||||
|
aclEntry(ACCESS, MASK, NONE));
|
||||||
|
fs.modifyAclEntries(path, aclSpec);
|
||||||
|
AclStatus s = fs.getAclStatus(path);
|
||||||
|
AclEntry[] returned = s.getEntries().toArray(new AclEntry[0]);
|
||||||
|
assertArrayEquals(new AclEntry[] {
|
||||||
|
aclEntry(ACCESS, USER, "foo", ALL),
|
||||||
|
aclEntry(ACCESS, GROUP, READ) }, returned);
|
||||||
|
assertPermission((short)02600);
|
||||||
|
assertAclFeature(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testModifyAclEntriesStickyBit() throws IOException {
|
||||||
|
FileSystem.mkdirs(fs, path, FsPermission.createImmutable((short)01750));
|
||||||
|
List<AclEntry> aclSpec = Lists.newArrayList(
|
||||||
|
aclEntry(ACCESS, USER, ALL),
|
||||||
|
aclEntry(ACCESS, USER, "foo", ALL),
|
||||||
|
aclEntry(ACCESS, GROUP, READ_EXECUTE),
|
||||||
|
aclEntry(ACCESS, OTHER, NONE),
|
||||||
|
aclEntry(DEFAULT, USER, "foo", ALL));
|
||||||
|
fs.setAcl(path, aclSpec);
|
||||||
|
aclSpec = Lists.newArrayList(
|
||||||
|
aclEntry(ACCESS, USER, "foo", READ_EXECUTE),
|
||||||
|
aclEntry(DEFAULT, USER, "foo", READ_EXECUTE));
|
||||||
|
fs.modifyAclEntries(path, aclSpec);
|
||||||
|
AclStatus s = fs.getAclStatus(path);
|
||||||
|
AclEntry[] returned = s.getEntries().toArray(new AclEntry[0]);
|
||||||
|
assertArrayEquals(new AclEntry[] {
|
||||||
|
aclEntry(ACCESS, USER, "foo", READ_EXECUTE),
|
||||||
|
aclEntry(ACCESS, GROUP, READ_EXECUTE),
|
||||||
|
aclEntry(DEFAULT, USER, ALL),
|
||||||
|
aclEntry(DEFAULT, USER, "foo", READ_EXECUTE),
|
||||||
|
aclEntry(DEFAULT, GROUP, READ_EXECUTE),
|
||||||
|
aclEntry(DEFAULT, MASK, READ_EXECUTE),
|
||||||
|
aclEntry(DEFAULT, OTHER, NONE) }, returned);
|
||||||
|
assertPermission((short)03750);
|
||||||
|
assertAclFeature(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected=FileNotFoundException.class)
|
||||||
|
public void testModifyAclEntriesPathNotFound() throws IOException {
|
||||||
|
// Path has not been created.
|
||||||
|
List<AclEntry> aclSpec = Lists.newArrayList(
|
||||||
|
aclEntry(ACCESS, USER, ALL),
|
||||||
|
aclEntry(ACCESS, USER, "foo", ALL),
|
||||||
|
aclEntry(ACCESS, GROUP, READ_EXECUTE),
|
||||||
|
aclEntry(ACCESS, OTHER, NONE));
|
||||||
|
fs.modifyAclEntries(path, aclSpec);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected=AclException.class)
|
||||||
|
public void testModifyAclEntriesDefaultOnFile() throws IOException {
|
||||||
|
FileSystem.create(fs, path, FsPermission.createImmutable((short)0640));
|
||||||
|
List<AclEntry> aclSpec = Lists.newArrayList(
|
||||||
|
aclEntry(DEFAULT, USER, "foo", ALL));
|
||||||
|
fs.modifyAclEntries(path, aclSpec);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testRemoveAclEntries() throws IOException {
|
||||||
|
FileSystem.mkdirs(fs, path, FsPermission.createImmutable((short)0750));
|
||||||
|
List<AclEntry> aclSpec = Lists.newArrayList(
|
||||||
|
aclEntry(ACCESS, USER, ALL),
|
||||||
|
aclEntry(ACCESS, USER, "foo", ALL),
|
||||||
|
aclEntry(ACCESS, GROUP, READ_EXECUTE),
|
||||||
|
aclEntry(ACCESS, OTHER, NONE),
|
||||||
|
aclEntry(DEFAULT, USER, "foo", ALL));
|
||||||
|
fs.setAcl(path, aclSpec);
|
||||||
|
aclSpec = Lists.newArrayList(
|
||||||
|
aclEntry(ACCESS, USER, "foo"),
|
||||||
|
aclEntry(DEFAULT, USER, "foo"));
|
||||||
|
fs.removeAclEntries(path, aclSpec);
|
||||||
|
AclStatus s = fs.getAclStatus(path);
|
||||||
|
AclEntry[] returned = s.getEntries().toArray(new AclEntry[0]);
|
||||||
|
assertArrayEquals(new AclEntry[] {
|
||||||
|
aclEntry(ACCESS, GROUP, READ_EXECUTE),
|
||||||
|
aclEntry(DEFAULT, USER, ALL),
|
||||||
|
aclEntry(DEFAULT, GROUP, READ_EXECUTE),
|
||||||
|
aclEntry(DEFAULT, MASK, READ_EXECUTE),
|
||||||
|
aclEntry(DEFAULT, OTHER, NONE) }, returned);
|
||||||
|
assertPermission((short)02750);
|
||||||
|
assertAclFeature(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testRemoveAclEntriesOnlyAccess() throws IOException {
|
||||||
|
FileSystem.create(fs, path, FsPermission.createImmutable((short)0640));
|
||||||
|
List<AclEntry> aclSpec = Lists.newArrayList(
|
||||||
|
aclEntry(ACCESS, USER, ALL),
|
||||||
|
aclEntry(ACCESS, USER, "foo", ALL),
|
||||||
|
aclEntry(ACCESS, USER, "bar", READ_WRITE),
|
||||||
|
aclEntry(ACCESS, GROUP, READ_WRITE),
|
||||||
|
aclEntry(ACCESS, OTHER, NONE));
|
||||||
|
fs.setAcl(path, aclSpec);
|
||||||
|
aclSpec = Lists.newArrayList(
|
||||||
|
aclEntry(ACCESS, USER, "foo"));
|
||||||
|
fs.removeAclEntries(path, aclSpec);
|
||||||
|
AclStatus s = fs.getAclStatus(path);
|
||||||
|
AclEntry[] returned = s.getEntries().toArray(new AclEntry[0]);
|
||||||
|
assertArrayEquals(new AclEntry[] {
|
||||||
|
aclEntry(ACCESS, USER, "bar", READ_WRITE),
|
||||||
|
aclEntry(ACCESS, GROUP, READ_WRITE) }, returned);
|
||||||
|
assertPermission((short)02760);
|
||||||
|
assertAclFeature(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testRemoveAclEntriesOnlyDefault() throws IOException {
|
||||||
|
FileSystem.mkdirs(fs, path, FsPermission.createImmutable((short)0750));
|
||||||
|
List<AclEntry> aclSpec = Lists.newArrayList(
|
||||||
|
aclEntry(ACCESS, USER, ALL),
|
||||||
|
aclEntry(ACCESS, GROUP, READ_EXECUTE),
|
||||||
|
aclEntry(ACCESS, OTHER, NONE),
|
||||||
|
aclEntry(DEFAULT, USER, "foo", ALL),
|
||||||
|
aclEntry(DEFAULT, USER, "bar", READ_EXECUTE));
|
||||||
|
fs.setAcl(path, aclSpec);
|
||||||
|
aclSpec = Lists.newArrayList(
|
||||||
|
aclEntry(DEFAULT, USER, "foo"));
|
||||||
|
fs.removeAclEntries(path, aclSpec);
|
||||||
|
AclStatus s = fs.getAclStatus(path);
|
||||||
|
AclEntry[] returned = s.getEntries().toArray(new AclEntry[0]);
|
||||||
|
assertArrayEquals(new AclEntry[] {
|
||||||
|
aclEntry(DEFAULT, USER, ALL),
|
||||||
|
aclEntry(DEFAULT, USER, "bar", READ_EXECUTE),
|
||||||
|
aclEntry(DEFAULT, GROUP, READ_EXECUTE),
|
||||||
|
aclEntry(DEFAULT, MASK, READ_EXECUTE),
|
||||||
|
aclEntry(DEFAULT, OTHER, NONE) }, returned);
|
||||||
|
assertPermission((short)02750);
|
||||||
|
assertAclFeature(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testRemoveAclEntriesMinimal() throws IOException {
|
||||||
|
FileSystem.create(fs, path, FsPermission.createImmutable((short)0760));
|
||||||
|
List<AclEntry> aclSpec = Lists.newArrayList(
|
||||||
|
aclEntry(ACCESS, USER, ALL),
|
||||||
|
aclEntry(ACCESS, USER, "foo", ALL),
|
||||||
|
aclEntry(ACCESS, GROUP, READ_WRITE),
|
||||||
|
aclEntry(ACCESS, OTHER, NONE));
|
||||||
|
fs.setAcl(path, aclSpec);
|
||||||
|
aclSpec = Lists.newArrayList(
|
||||||
|
aclEntry(ACCESS, USER, "foo"),
|
||||||
|
aclEntry(ACCESS, MASK));
|
||||||
|
fs.removeAclEntries(path, aclSpec);
|
||||||
|
AclStatus s = fs.getAclStatus(path);
|
||||||
|
AclEntry[] returned = s.getEntries().toArray(new AclEntry[0]);
|
||||||
|
assertArrayEquals(new AclEntry[] { }, returned);
|
||||||
|
assertPermission((short)0760);
|
||||||
|
assertAclFeature(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testRemoveAclEntriesMinimalDefault() throws IOException {
|
||||||
|
FileSystem.mkdirs(fs, path, FsPermission.createImmutable((short)0750));
|
||||||
|
List<AclEntry> aclSpec = Lists.newArrayList(
|
||||||
|
aclEntry(ACCESS, USER, ALL),
|
||||||
|
aclEntry(ACCESS, USER, "foo", ALL),
|
||||||
|
aclEntry(ACCESS, GROUP, READ_EXECUTE),
|
||||||
|
aclEntry(ACCESS, OTHER, NONE),
|
||||||
|
aclEntry(DEFAULT, USER, "foo", ALL));
|
||||||
|
fs.setAcl(path, aclSpec);
|
||||||
|
aclSpec = Lists.newArrayList(
|
||||||
|
aclEntry(ACCESS, USER, "foo"),
|
||||||
|
aclEntry(ACCESS, MASK),
|
||||||
|
aclEntry(DEFAULT, USER, "foo"),
|
||||||
|
aclEntry(DEFAULT, MASK));
|
||||||
|
fs.removeAclEntries(path, aclSpec);
|
||||||
|
AclStatus s = fs.getAclStatus(path);
|
||||||
|
AclEntry[] returned = s.getEntries().toArray(new AclEntry[0]);
|
||||||
|
assertArrayEquals(new AclEntry[] {
|
||||||
|
aclEntry(DEFAULT, USER, ALL),
|
||||||
|
aclEntry(DEFAULT, GROUP, READ_EXECUTE),
|
||||||
|
aclEntry(DEFAULT, OTHER, NONE) }, returned);
|
||||||
|
assertPermission((short)02750);
|
||||||
|
assertAclFeature(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testRemoveAclEntriesStickyBit() throws IOException {
|
||||||
|
FileSystem.mkdirs(fs, path, FsPermission.createImmutable((short)01750));
|
||||||
|
List<AclEntry> aclSpec = Lists.newArrayList(
|
||||||
|
aclEntry(ACCESS, USER, ALL),
|
||||||
|
aclEntry(ACCESS, USER, "foo", ALL),
|
||||||
|
aclEntry(ACCESS, GROUP, READ_EXECUTE),
|
||||||
|
aclEntry(ACCESS, OTHER, NONE),
|
||||||
|
aclEntry(DEFAULT, USER, "foo", ALL));
|
||||||
|
fs.setAcl(path, aclSpec);
|
||||||
|
aclSpec = Lists.newArrayList(
|
||||||
|
aclEntry(ACCESS, USER, "foo"),
|
||||||
|
aclEntry(DEFAULT, USER, "foo"));
|
||||||
|
fs.removeAclEntries(path, aclSpec);
|
||||||
|
AclStatus s = fs.getAclStatus(path);
|
||||||
|
AclEntry[] returned = s.getEntries().toArray(new AclEntry[0]);
|
||||||
|
assertArrayEquals(new AclEntry[] {
|
||||||
|
aclEntry(ACCESS, GROUP, READ_EXECUTE),
|
||||||
|
aclEntry(DEFAULT, USER, ALL),
|
||||||
|
aclEntry(DEFAULT, GROUP, READ_EXECUTE),
|
||||||
|
aclEntry(DEFAULT, MASK, READ_EXECUTE),
|
||||||
|
aclEntry(DEFAULT, OTHER, NONE) }, returned);
|
||||||
|
assertPermission((short)03750);
|
||||||
|
assertAclFeature(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected=FileNotFoundException.class)
|
||||||
|
public void testRemoveAclEntriesPathNotFound() throws IOException {
|
||||||
|
// Path has not been created.
|
||||||
|
List<AclEntry> aclSpec = Lists.newArrayList(
|
||||||
|
aclEntry(ACCESS, USER, "foo"));
|
||||||
|
fs.removeAclEntries(path, aclSpec);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testRemoveDefaultAcl() throws IOException {
|
||||||
|
FileSystem.mkdirs(fs, path, FsPermission.createImmutable((short)0750));
|
||||||
|
List<AclEntry> aclSpec = Lists.newArrayList(
|
||||||
|
aclEntry(ACCESS, USER, ALL),
|
||||||
|
aclEntry(ACCESS, USER, "foo", ALL),
|
||||||
|
aclEntry(ACCESS, GROUP, READ_EXECUTE),
|
||||||
|
aclEntry(ACCESS, OTHER, NONE),
|
||||||
|
aclEntry(DEFAULT, USER, "foo", ALL));
|
||||||
|
fs.setAcl(path, aclSpec);
|
||||||
|
fs.removeDefaultAcl(path);
|
||||||
|
AclStatus s = fs.getAclStatus(path);
|
||||||
|
AclEntry[] returned = s.getEntries().toArray(new AclEntry[0]);
|
||||||
|
assertArrayEquals(new AclEntry[] {
|
||||||
|
aclEntry(ACCESS, USER, "foo", ALL),
|
||||||
|
aclEntry(ACCESS, GROUP, READ_EXECUTE) }, returned);
|
||||||
|
assertPermission((short)02770);
|
||||||
|
assertAclFeature(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testRemoveDefaultAclOnlyAccess() throws IOException {
|
||||||
|
FileSystem.create(fs, path, FsPermission.createImmutable((short)0640));
|
||||||
|
List<AclEntry> aclSpec = Lists.newArrayList(
|
||||||
|
aclEntry(ACCESS, USER, ALL),
|
||||||
|
aclEntry(ACCESS, USER, "foo", ALL),
|
||||||
|
aclEntry(ACCESS, GROUP, READ_EXECUTE),
|
||||||
|
aclEntry(ACCESS, OTHER, NONE));
|
||||||
|
fs.setAcl(path, aclSpec);
|
||||||
|
fs.removeDefaultAcl(path);
|
||||||
|
AclStatus s = fs.getAclStatus(path);
|
||||||
|
AclEntry[] returned = s.getEntries().toArray(new AclEntry[0]);
|
||||||
|
assertArrayEquals(new AclEntry[] {
|
||||||
|
aclEntry(ACCESS, USER, "foo", ALL),
|
||||||
|
aclEntry(ACCESS, GROUP, READ_EXECUTE) }, returned);
|
||||||
|
assertPermission((short)02770);
|
||||||
|
assertAclFeature(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testRemoveDefaultAclOnlyDefault() throws IOException {
|
||||||
|
FileSystem.mkdirs(fs, path, FsPermission.createImmutable((short)0750));
|
||||||
|
List<AclEntry> aclSpec = Lists.newArrayList(
|
||||||
|
aclEntry(DEFAULT, USER, "foo", ALL));
|
||||||
|
fs.setAcl(path, aclSpec);
|
||||||
|
fs.removeDefaultAcl(path);
|
||||||
|
AclStatus s = fs.getAclStatus(path);
|
||||||
|
AclEntry[] returned = s.getEntries().toArray(new AclEntry[0]);
|
||||||
|
assertArrayEquals(new AclEntry[] { }, returned);
|
||||||
|
assertPermission((short)0750);
|
||||||
|
assertAclFeature(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testRemoveDefaultAclMinimal() throws IOException {
|
||||||
|
FileSystem.mkdirs(fs, path, FsPermission.createImmutable((short)0750));
|
||||||
|
fs.removeDefaultAcl(path);
|
||||||
|
AclStatus s = fs.getAclStatus(path);
|
||||||
|
AclEntry[] returned = s.getEntries().toArray(new AclEntry[0]);
|
||||||
|
assertArrayEquals(new AclEntry[] { }, returned);
|
||||||
|
assertPermission((short)0750);
|
||||||
|
assertAclFeature(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testRemoveDefaultAclStickyBit() throws IOException {
|
||||||
|
FileSystem.mkdirs(fs, path, FsPermission.createImmutable((short)01750));
|
||||||
|
List<AclEntry> aclSpec = Lists.newArrayList(
|
||||||
|
aclEntry(ACCESS, USER, ALL),
|
||||||
|
aclEntry(ACCESS, USER, "foo", ALL),
|
||||||
|
aclEntry(ACCESS, GROUP, READ_EXECUTE),
|
||||||
|
aclEntry(ACCESS, OTHER, NONE),
|
||||||
|
aclEntry(DEFAULT, USER, "foo", ALL));
|
||||||
|
fs.setAcl(path, aclSpec);
|
||||||
|
fs.removeDefaultAcl(path);
|
||||||
|
AclStatus s = fs.getAclStatus(path);
|
||||||
|
AclEntry[] returned = s.getEntries().toArray(new AclEntry[0]);
|
||||||
|
assertArrayEquals(new AclEntry[] {
|
||||||
|
aclEntry(ACCESS, USER, "foo", ALL),
|
||||||
|
aclEntry(ACCESS, GROUP, READ_EXECUTE) }, returned);
|
||||||
|
assertPermission((short)03770);
|
||||||
|
assertAclFeature(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected=FileNotFoundException.class)
|
||||||
|
public void testRemoveDefaultAclPathNotFound() throws IOException {
|
||||||
|
// Path has not been created.
|
||||||
|
fs.removeDefaultAcl(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testRemoveAcl() throws IOException {
|
||||||
|
FileSystem.mkdirs(fs, path, FsPermission.createImmutable((short)0750));
|
||||||
|
List<AclEntry> aclSpec = Lists.newArrayList(
|
||||||
|
aclEntry(ACCESS, USER, ALL),
|
||||||
|
aclEntry(ACCESS, USER, "foo", ALL),
|
||||||
|
aclEntry(ACCESS, GROUP, READ_EXECUTE),
|
||||||
|
aclEntry(ACCESS, OTHER, NONE),
|
||||||
|
aclEntry(DEFAULT, USER, "foo", ALL));
|
||||||
|
fs.setAcl(path, aclSpec);
|
||||||
|
fs.removeAcl(path);
|
||||||
|
AclStatus s = fs.getAclStatus(path);
|
||||||
|
AclEntry[] returned = s.getEntries().toArray(new AclEntry[0]);
|
||||||
|
assertArrayEquals(new AclEntry[] { }, returned);
|
||||||
|
assertPermission((short)0750);
|
||||||
|
assertAclFeature(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testRemoveAclMinimalAcl() throws IOException {
|
||||||
|
FileSystem.create(fs, path, FsPermission.createImmutable((short)0640));
|
||||||
|
fs.removeAcl(path);
|
||||||
|
AclStatus s = fs.getAclStatus(path);
|
||||||
|
AclEntry[] returned = s.getEntries().toArray(new AclEntry[0]);
|
||||||
|
assertArrayEquals(new AclEntry[] { }, returned);
|
||||||
|
assertPermission((short)0640);
|
||||||
|
assertAclFeature(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testRemoveAclStickyBit() throws IOException {
|
||||||
|
FileSystem.mkdirs(fs, path, FsPermission.createImmutable((short)01750));
|
||||||
|
List<AclEntry> aclSpec = Lists.newArrayList(
|
||||||
|
aclEntry(ACCESS, USER, ALL),
|
||||||
|
aclEntry(ACCESS, USER, "foo", ALL),
|
||||||
|
aclEntry(ACCESS, GROUP, READ_EXECUTE),
|
||||||
|
aclEntry(ACCESS, OTHER, NONE),
|
||||||
|
aclEntry(DEFAULT, USER, "foo", ALL));
|
||||||
|
fs.setAcl(path, aclSpec);
|
||||||
|
fs.removeAcl(path);
|
||||||
|
AclStatus s = fs.getAclStatus(path);
|
||||||
|
AclEntry[] returned = s.getEntries().toArray(new AclEntry[0]);
|
||||||
|
assertArrayEquals(new AclEntry[] { }, returned);
|
||||||
|
assertPermission((short)01750);
|
||||||
|
assertAclFeature(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected=FileNotFoundException.class)
|
||||||
|
public void testRemoveAclPathNotFound() throws IOException {
|
||||||
|
// Path has not been created.
|
||||||
|
fs.removeAcl(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testSetAcl() throws IOException {
|
public void testSetAcl() throws IOException {
|
||||||
Path p = new Path("/p");
|
FileSystem.mkdirs(fs, path, FsPermission.createImmutable((short)0750));
|
||||||
FileSystem fs = cluster.getFileSystem();
|
List<AclEntry> aclSpec = Lists.newArrayList(
|
||||||
fs.create(p).close();
|
aclEntry(ACCESS, USER, ALL),
|
||||||
AclEntry e = new AclEntry.Builder().setName("foo")
|
aclEntry(ACCESS, USER, "foo", ALL),
|
||||||
.setPermission(FsAction.READ_EXECUTE).setScope(AclEntryScope.DEFAULT)
|
aclEntry(ACCESS, GROUP, READ_EXECUTE),
|
||||||
.setType(AclEntryType.OTHER).build();
|
aclEntry(ACCESS, OTHER, NONE),
|
||||||
fs.setAcl(p, Lists.newArrayList(e));
|
aclEntry(DEFAULT, USER, "foo", ALL));
|
||||||
AclStatus s = fs.getAclStatus(p);
|
fs.setAcl(path, aclSpec);
|
||||||
AclEntry[] returned = Lists.newArrayList(s.getEntries()).toArray(
|
AclStatus s = fs.getAclStatus(path);
|
||||||
new AclEntry[0]);
|
AclEntry[] returned = s.getEntries().toArray(new AclEntry[0]);
|
||||||
Assert.assertArrayEquals(new AclEntry[] { e }, returned);
|
assertArrayEquals(new AclEntry[] {
|
||||||
|
aclEntry(ACCESS, USER, "foo", ALL),
|
||||||
|
aclEntry(ACCESS, GROUP, READ_EXECUTE),
|
||||||
|
aclEntry(DEFAULT, USER, ALL),
|
||||||
|
aclEntry(DEFAULT, USER, "foo", ALL),
|
||||||
|
aclEntry(DEFAULT, GROUP, READ_EXECUTE),
|
||||||
|
aclEntry(DEFAULT, MASK, ALL),
|
||||||
|
aclEntry(DEFAULT, OTHER, NONE) }, returned);
|
||||||
|
assertPermission((short)02770);
|
||||||
|
assertAclFeature(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSetAclOnlyAccess() throws IOException {
|
||||||
|
FileSystem.create(fs, path, FsPermission.createImmutable((short)0640));
|
||||||
|
List<AclEntry> aclSpec = Lists.newArrayList(
|
||||||
|
aclEntry(ACCESS, USER, READ_WRITE),
|
||||||
|
aclEntry(ACCESS, USER, "foo", READ),
|
||||||
|
aclEntry(ACCESS, GROUP, READ),
|
||||||
|
aclEntry(ACCESS, OTHER, NONE));
|
||||||
|
fs.setAcl(path, aclSpec);
|
||||||
|
AclStatus s = fs.getAclStatus(path);
|
||||||
|
AclEntry[] returned = s.getEntries().toArray(new AclEntry[0]);
|
||||||
|
assertArrayEquals(new AclEntry[] {
|
||||||
|
aclEntry(ACCESS, USER, "foo", READ),
|
||||||
|
aclEntry(ACCESS, GROUP, READ) }, returned);
|
||||||
|
assertPermission((short)02640);
|
||||||
|
assertAclFeature(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSetAclOnlyDefault() throws IOException {
|
||||||
|
FileSystem.mkdirs(fs, path, FsPermission.createImmutable((short)0750));
|
||||||
|
List<AclEntry> aclSpec = Lists.newArrayList(
|
||||||
|
aclEntry(DEFAULT, USER, "foo", ALL));
|
||||||
|
fs.setAcl(path, aclSpec);
|
||||||
|
AclStatus s = fs.getAclStatus(path);
|
||||||
|
AclEntry[] returned = s.getEntries().toArray(new AclEntry[0]);
|
||||||
|
assertArrayEquals(new AclEntry[] {
|
||||||
|
aclEntry(DEFAULT, USER, ALL),
|
||||||
|
aclEntry(DEFAULT, USER, "foo", ALL),
|
||||||
|
aclEntry(DEFAULT, GROUP, READ_EXECUTE),
|
||||||
|
aclEntry(DEFAULT, MASK, ALL),
|
||||||
|
aclEntry(DEFAULT, OTHER, NONE) }, returned);
|
||||||
|
assertPermission((short)02750);
|
||||||
|
assertAclFeature(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSetAclMinimal() throws IOException {
|
||||||
|
FileSystem.create(fs, path, FsPermission.createImmutable((short)0644));
|
||||||
|
List<AclEntry> aclSpec = Lists.newArrayList(
|
||||||
|
aclEntry(ACCESS, USER, READ_WRITE),
|
||||||
|
aclEntry(ACCESS, USER, "foo", READ),
|
||||||
|
aclEntry(ACCESS, GROUP, READ),
|
||||||
|
aclEntry(ACCESS, OTHER, NONE));
|
||||||
|
fs.setAcl(path, aclSpec);
|
||||||
|
aclSpec = Lists.newArrayList(
|
||||||
|
aclEntry(ACCESS, USER, READ_WRITE),
|
||||||
|
aclEntry(ACCESS, GROUP, READ),
|
||||||
|
aclEntry(ACCESS, OTHER, NONE));
|
||||||
|
fs.setAcl(path, aclSpec);
|
||||||
|
AclStatus s = fs.getAclStatus(path);
|
||||||
|
AclEntry[] returned = s.getEntries().toArray(new AclEntry[0]);
|
||||||
|
assertArrayEquals(new AclEntry[] { }, returned);
|
||||||
|
assertPermission((short)0640);
|
||||||
|
assertAclFeature(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSetAclMinimalDefault() throws IOException {
|
||||||
|
FileSystem.mkdirs(fs, path, FsPermission.createImmutable((short)0750));
|
||||||
|
List<AclEntry> aclSpec = Lists.newArrayList(
|
||||||
|
aclEntry(DEFAULT, USER, ALL),
|
||||||
|
aclEntry(DEFAULT, GROUP, READ_EXECUTE),
|
||||||
|
aclEntry(DEFAULT, OTHER, NONE));
|
||||||
|
fs.setAcl(path, aclSpec);
|
||||||
|
AclStatus s = fs.getAclStatus(path);
|
||||||
|
AclEntry[] returned = s.getEntries().toArray(new AclEntry[0]);
|
||||||
|
assertArrayEquals(new AclEntry[] {
|
||||||
|
aclEntry(DEFAULT, USER, ALL),
|
||||||
|
aclEntry(DEFAULT, GROUP, READ_EXECUTE),
|
||||||
|
aclEntry(DEFAULT, OTHER, NONE) }, returned);
|
||||||
|
assertPermission((short)02750);
|
||||||
|
assertAclFeature(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSetAclCustomMask() throws IOException {
|
||||||
|
FileSystem.create(fs, path, FsPermission.createImmutable((short)0640));
|
||||||
|
List<AclEntry> aclSpec = Lists.newArrayList(
|
||||||
|
aclEntry(ACCESS, USER, READ_WRITE),
|
||||||
|
aclEntry(ACCESS, USER, "foo", READ),
|
||||||
|
aclEntry(ACCESS, GROUP, READ),
|
||||||
|
aclEntry(ACCESS, MASK, ALL),
|
||||||
|
aclEntry(ACCESS, OTHER, NONE));
|
||||||
|
fs.setAcl(path, aclSpec);
|
||||||
|
AclStatus s = fs.getAclStatus(path);
|
||||||
|
AclEntry[] returned = s.getEntries().toArray(new AclEntry[0]);
|
||||||
|
assertArrayEquals(new AclEntry[] {
|
||||||
|
aclEntry(ACCESS, USER, "foo", READ),
|
||||||
|
aclEntry(ACCESS, GROUP, READ) }, returned);
|
||||||
|
assertPermission((short)02670);
|
||||||
|
assertAclFeature(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSetAclStickyBit() throws IOException {
|
||||||
|
FileSystem.mkdirs(fs, path, FsPermission.createImmutable((short)01750));
|
||||||
|
List<AclEntry> aclSpec = Lists.newArrayList(
|
||||||
|
aclEntry(ACCESS, USER, ALL),
|
||||||
|
aclEntry(ACCESS, USER, "foo", ALL),
|
||||||
|
aclEntry(ACCESS, GROUP, READ_EXECUTE),
|
||||||
|
aclEntry(ACCESS, OTHER, NONE),
|
||||||
|
aclEntry(DEFAULT, USER, "foo", ALL));
|
||||||
|
fs.setAcl(path, aclSpec);
|
||||||
|
AclStatus s = fs.getAclStatus(path);
|
||||||
|
AclEntry[] returned = s.getEntries().toArray(new AclEntry[0]);
|
||||||
|
assertArrayEquals(new AclEntry[] {
|
||||||
|
aclEntry(ACCESS, USER, "foo", ALL),
|
||||||
|
aclEntry(ACCESS, GROUP, READ_EXECUTE),
|
||||||
|
aclEntry(DEFAULT, USER, ALL),
|
||||||
|
aclEntry(DEFAULT, USER, "foo", ALL),
|
||||||
|
aclEntry(DEFAULT, GROUP, READ_EXECUTE),
|
||||||
|
aclEntry(DEFAULT, MASK, ALL),
|
||||||
|
aclEntry(DEFAULT, OTHER, NONE) }, returned);
|
||||||
|
assertPermission((short)03770);
|
||||||
|
assertAclFeature(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected=FileNotFoundException.class)
|
||||||
|
public void testSetAclPathNotFound() throws IOException {
|
||||||
|
// Path has not been created.
|
||||||
|
List<AclEntry> aclSpec = Lists.newArrayList(
|
||||||
|
aclEntry(ACCESS, USER, READ_WRITE),
|
||||||
|
aclEntry(ACCESS, USER, "foo", READ),
|
||||||
|
aclEntry(ACCESS, GROUP, READ),
|
||||||
|
aclEntry(ACCESS, OTHER, NONE));
|
||||||
|
fs.setAcl(path, aclSpec);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected=AclException.class)
|
||||||
|
public void testSetAclDefaultOnFile() throws IOException {
|
||||||
|
FileSystem.create(fs, path, FsPermission.createImmutable((short)0640));
|
||||||
|
List<AclEntry> aclSpec = Lists.newArrayList(
|
||||||
|
aclEntry(DEFAULT, USER, "foo", ALL));
|
||||||
|
fs.setAcl(path, aclSpec);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSetPermission() throws IOException {
|
||||||
|
FileSystem.mkdirs(fs, path, FsPermission.createImmutable((short)0750));
|
||||||
|
List<AclEntry> aclSpec = Lists.newArrayList(
|
||||||
|
aclEntry(ACCESS, USER, ALL),
|
||||||
|
aclEntry(ACCESS, USER, "foo", ALL),
|
||||||
|
aclEntry(ACCESS, GROUP, READ_EXECUTE),
|
||||||
|
aclEntry(ACCESS, OTHER, NONE),
|
||||||
|
aclEntry(DEFAULT, USER, "foo", ALL));
|
||||||
|
fs.setAcl(path, aclSpec);
|
||||||
|
fs.setPermission(path, FsPermission.createImmutable((short)0700));
|
||||||
|
AclStatus s = fs.getAclStatus(path);
|
||||||
|
AclEntry[] returned = s.getEntries().toArray(new AclEntry[0]);
|
||||||
|
assertArrayEquals(new AclEntry[] {
|
||||||
|
aclEntry(ACCESS, USER, "foo", ALL),
|
||||||
|
aclEntry(ACCESS, GROUP, READ_EXECUTE),
|
||||||
|
aclEntry(DEFAULT, USER, ALL),
|
||||||
|
aclEntry(DEFAULT, USER, "foo", ALL),
|
||||||
|
aclEntry(DEFAULT, GROUP, READ_EXECUTE),
|
||||||
|
aclEntry(DEFAULT, MASK, ALL),
|
||||||
|
aclEntry(DEFAULT, OTHER, NONE) }, returned);
|
||||||
|
assertPermission((short)02700);
|
||||||
|
assertAclFeature(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSetPermissionOnlyAccess() throws IOException {
|
||||||
|
FileSystem.create(fs, path, FsPermission.createImmutable((short)0640));
|
||||||
|
List<AclEntry> aclSpec = Lists.newArrayList(
|
||||||
|
aclEntry(ACCESS, USER, READ_WRITE),
|
||||||
|
aclEntry(ACCESS, USER, "foo", READ),
|
||||||
|
aclEntry(ACCESS, GROUP, READ),
|
||||||
|
aclEntry(ACCESS, OTHER, NONE));
|
||||||
|
fs.setAcl(path, aclSpec);
|
||||||
|
fs.setPermission(path, FsPermission.createImmutable((short)0600));
|
||||||
|
AclStatus s = fs.getAclStatus(path);
|
||||||
|
AclEntry[] returned = s.getEntries().toArray(new AclEntry[0]);
|
||||||
|
assertArrayEquals(new AclEntry[] {
|
||||||
|
aclEntry(ACCESS, USER, "foo", READ),
|
||||||
|
aclEntry(ACCESS, GROUP, READ) }, returned);
|
||||||
|
assertPermission((short)02600);
|
||||||
|
assertAclFeature(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSetPermissionOnlyDefault() throws IOException {
|
||||||
|
FileSystem.mkdirs(fs, path, FsPermission.createImmutable((short)0750));
|
||||||
|
List<AclEntry> aclSpec = Lists.newArrayList(
|
||||||
|
aclEntry(ACCESS, USER, ALL),
|
||||||
|
aclEntry(ACCESS, GROUP, READ_EXECUTE),
|
||||||
|
aclEntry(ACCESS, OTHER, NONE),
|
||||||
|
aclEntry(DEFAULT, USER, "foo", ALL));
|
||||||
|
fs.setAcl(path, aclSpec);
|
||||||
|
fs.setPermission(path, FsPermission.createImmutable((short)0700));
|
||||||
|
AclStatus s = fs.getAclStatus(path);
|
||||||
|
AclEntry[] returned = s.getEntries().toArray(new AclEntry[0]);
|
||||||
|
assertArrayEquals(new AclEntry[] {
|
||||||
|
aclEntry(DEFAULT, USER, ALL),
|
||||||
|
aclEntry(DEFAULT, USER, "foo", ALL),
|
||||||
|
aclEntry(DEFAULT, GROUP, READ_EXECUTE),
|
||||||
|
aclEntry(DEFAULT, MASK, ALL),
|
||||||
|
aclEntry(DEFAULT, OTHER, NONE) }, returned);
|
||||||
|
assertPermission((short)02700);
|
||||||
|
assertAclFeature(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Asserts whether or not the inode for the test path has an AclFeature.
|
||||||
|
*
|
||||||
|
* @param expectAclFeature boolean true if an AclFeature must be present,
|
||||||
|
* false if an AclFeature must not be present
|
||||||
|
* @throws IOException thrown if there is an I/O error
|
||||||
|
*/
|
||||||
|
private static void assertAclFeature(boolean expectAclFeature)
|
||||||
|
throws IOException {
|
||||||
|
INode inode = cluster.getNamesystem().getFSDirectory().getRoot()
|
||||||
|
.getNode(path.toUri().getPath(), false);
|
||||||
|
assertNotNull(inode);
|
||||||
|
assertTrue(inode instanceof INodeWithAdditionalFields);
|
||||||
|
AclFeature aclFeature = ((INodeWithAdditionalFields)inode).getAclFeature();
|
||||||
|
if (expectAclFeature) {
|
||||||
|
assertNotNull(aclFeature);
|
||||||
|
} else {
|
||||||
|
assertNull(aclFeature);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Asserts the value of the FsPermission bits on the inode of the test path.
|
||||||
|
*
|
||||||
|
* @param perm short expected permission bits
|
||||||
|
* @throws IOException thrown if there is an I/O error
|
||||||
|
*/
|
||||||
|
private static void assertPermission(short perm) throws IOException {
|
||||||
|
assertEquals(FsPermission.createImmutable(perm),
|
||||||
|
fs.getFileStatus(path).getPermission());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue