HDFS-7486. Consolidate XAttr-related implementation into a single class. Contributed by Haohui Mai.

This commit is contained in:
Haohui Mai 2014-12-08 11:52:21 -08:00
parent 582f96e41d
commit 7198232b82
7 changed files with 555 additions and 501 deletions

View File

@ -187,6 +187,9 @@ Release 2.7.0 - UNRELEASED
HDFS-7384. 'getfacl' command and 'getAclStatus' output should be in sync. HDFS-7384. 'getfacl' command and 'getAclStatus' output should be in sync.
(Vinayakumar B via cnauroth) (Vinayakumar B via cnauroth)
HDFS-7486. Consolidate XAttr-related implementation into a single class.
(wheat9)
OPTIMIZATIONS OPTIMIZATIONS
HDFS-7454. Reduce memory footprint for AclEntries in NameNode. HDFS-7454. Reduce memory footprint for AclEntries in NameNode.

View File

@ -311,7 +311,8 @@ public class EncryptionZoneManager {
xattrs.add(ezXAttr); xattrs.add(ezXAttr);
// updating the xattr will call addEncryptionZone, // updating the xattr will call addEncryptionZone,
// done this way to handle edit log loading // done this way to handle edit log loading
dir.unprotectedSetXAttrs(src, xattrs, EnumSet.of(XAttrSetFlag.CREATE)); FSDirXAttrOp.unprotectedSetXAttrs(dir, src, xattrs,
EnumSet.of(XAttrSetFlag.CREATE));
return ezXAttr; return ezXAttr;
} }

View File

@ -0,0 +1,460 @@
/**
* 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 com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Charsets;
import com.google.common.base.Preconditions;
import com.google.common.collect.Lists;
import org.apache.hadoop.HadoopIllegalArgumentException;
import org.apache.hadoop.fs.XAttr;
import org.apache.hadoop.fs.XAttrSetFlag;
import org.apache.hadoop.fs.permission.FsAction;
import org.apache.hadoop.hdfs.DFSConfigKeys;
import org.apache.hadoop.hdfs.XAttrHelper;
import org.apache.hadoop.hdfs.protocol.HdfsFileStatus;
import org.apache.hadoop.hdfs.protocol.proto.HdfsProtos;
import org.apache.hadoop.hdfs.protocolPB.PBHelper;
import org.apache.hadoop.security.AccessControlException;
import java.io.IOException;
import java.util.EnumSet;
import java.util.List;
import java.util.ListIterator;
import static org.apache.hadoop.hdfs.server.common.HdfsServerConstants.CRYPTO_XATTR_ENCRYPTION_ZONE;
import static org.apache.hadoop.hdfs.server.common.HdfsServerConstants.SECURITY_XATTR_UNREADABLE_BY_SUPERUSER;
class FSDirXAttrOp {
private static final XAttr KEYID_XATTR =
XAttrHelper.buildXAttr(CRYPTO_XATTR_ENCRYPTION_ZONE, null);
private static final XAttr UNREADABLE_BY_SUPERUSER_XATTR =
XAttrHelper.buildXAttr(SECURITY_XATTR_UNREADABLE_BY_SUPERUSER, null);
/**
* Set xattr for a file or directory.
*
* @param src
* - path on which it sets the xattr
* @param xAttr
* - xAttr details to set
* @param flag
* - xAttrs flags
* @throws IOException
*/
static HdfsFileStatus setXAttr(
FSDirectory fsd, String src, XAttr xAttr, EnumSet<XAttrSetFlag> flag,
boolean logRetryCache)
throws IOException {
checkXAttrsConfigFlag(fsd);
checkXAttrSize(fsd, xAttr);
FSPermissionChecker pc = fsd.getPermissionChecker();
XAttrPermissionFilter.checkPermissionForApi(
pc, xAttr, FSDirectory.isReservedRawName(src));
byte[][] pathComponents = FSDirectory.getPathComponentsForReservedPath(
src);
src = fsd.resolvePath(pc, src, pathComponents);
final INodesInPath iip = fsd.getINodesInPath4Write(src);
checkXAttrChangeAccess(fsd, iip, xAttr, pc);
List<XAttr> xAttrs = Lists.newArrayListWithCapacity(1);
xAttrs.add(xAttr);
fsd.writeLock();
try {
unprotectedSetXAttrs(fsd, src, xAttrs, flag);
} finally {
fsd.writeUnlock();
}
fsd.getEditLog().logSetXAttrs(src, xAttrs, logRetryCache);
return fsd.getAuditFileInfo(src, false);
}
static List<XAttr> getXAttrs(FSDirectory fsd, final String srcArg,
List<XAttr> xAttrs)
throws IOException {
String src = srcArg;
checkXAttrsConfigFlag(fsd);
FSPermissionChecker pc = fsd.getPermissionChecker();
final boolean isRawPath = FSDirectory.isReservedRawName(src);
boolean getAll = xAttrs == null || xAttrs.isEmpty();
if (!getAll) {
XAttrPermissionFilter.checkPermissionForApi(pc, xAttrs, isRawPath);
}
byte[][] pathComponents = FSDirectory.getPathComponentsForReservedPath(src);
src = fsd.resolvePath(pc, src, pathComponents);
final INodesInPath iip = fsd.getINodesInPath(src, true);
if (fsd.isPermissionEnabled()) {
fsd.checkPathAccess(pc, iip, FsAction.READ);
}
List<XAttr> all = FSDirXAttrOp.getXAttrs(fsd, src);
List<XAttr> filteredAll = XAttrPermissionFilter.
filterXAttrsForApi(pc, all, isRawPath);
if (getAll) {
return filteredAll;
}
if (filteredAll == null || filteredAll.isEmpty()) {
return null;
}
List<XAttr> toGet = Lists.newArrayListWithCapacity(xAttrs.size());
for (XAttr xAttr : xAttrs) {
boolean foundIt = false;
for (XAttr a : filteredAll) {
if (xAttr.getNameSpace() == a.getNameSpace() && xAttr.getName().equals(
a.getName())) {
toGet.add(a);
foundIt = true;
break;
}
}
if (!foundIt) {
throw new IOException(
"At least one of the attributes provided was not found.");
}
}
return toGet;
}
static List<XAttr> listXAttrs(
FSDirectory fsd, String src) throws IOException {
FSDirXAttrOp.checkXAttrsConfigFlag(fsd);
final FSPermissionChecker pc = fsd.getPermissionChecker();
final boolean isRawPath = FSDirectory.isReservedRawName(src);
byte[][] pathComponents = FSDirectory.getPathComponentsForReservedPath(src);
src = fsd.resolvePath(pc, src, pathComponents);
final INodesInPath iip = fsd.getINodesInPath(src, true);
if (fsd.isPermissionEnabled()) {
/* To access xattr names, you need EXECUTE in the owning directory. */
fsd.checkParentAccess(pc, iip, FsAction.EXECUTE);
}
final List<XAttr> all = FSDirXAttrOp.getXAttrs(fsd, src);
return XAttrPermissionFilter.
filterXAttrsForApi(pc, all, isRawPath);
}
/**
* Remove an xattr for a file or directory.
*
* @param src
* - path to remove the xattr from
* @param xAttr
* - xAttr to remove
* @throws IOException
*/
static HdfsFileStatus removeXAttr(
FSDirectory fsd, String src, XAttr xAttr, boolean logRetryCache)
throws IOException {
FSDirXAttrOp.checkXAttrsConfigFlag(fsd);
FSPermissionChecker pc = fsd.getPermissionChecker();
XAttrPermissionFilter.checkPermissionForApi(
pc, xAttr, FSDirectory.isReservedRawName(src));
byte[][] pathComponents = FSDirectory.getPathComponentsForReservedPath(
src);
src = fsd.resolvePath(pc, src, pathComponents);
final INodesInPath iip = fsd.getINodesInPath4Write(src);
checkXAttrChangeAccess(fsd, iip, xAttr, pc);
List<XAttr> xAttrs = Lists.newArrayListWithCapacity(1);
xAttrs.add(xAttr);
fsd.writeLock();
try {
List<XAttr> removedXAttrs = unprotectedRemoveXAttrs(fsd, src, xAttrs);
if (removedXAttrs != null && !removedXAttrs.isEmpty()) {
fsd.getEditLog().logRemoveXAttrs(src, removedXAttrs, logRetryCache);
} else {
throw new IOException(
"No matching attributes found for remove operation");
}
} finally {
fsd.writeUnlock();
}
return fsd.getAuditFileInfo(src, false);
}
static List<XAttr> unprotectedRemoveXAttrs(
FSDirectory fsd, final String src, final List<XAttr> toRemove)
throws IOException {
assert fsd.hasWriteLock();
INodesInPath iip = fsd.getINodesInPath4Write(
FSDirectory.normalizePath(src), true);
INode inode = FSDirectory.resolveLastINode(src, iip);
int snapshotId = iip.getLatestSnapshotId();
List<XAttr> existingXAttrs = XAttrStorage.readINodeXAttrs(inode);
List<XAttr> removedXAttrs = Lists.newArrayListWithCapacity(toRemove.size());
List<XAttr> newXAttrs = filterINodeXAttrs(existingXAttrs, toRemove,
removedXAttrs);
if (existingXAttrs.size() != newXAttrs.size()) {
XAttrStorage.updateINodeXAttrs(inode, newXAttrs, snapshotId);
return removedXAttrs;
}
return null;
}
/**
* Filter XAttrs from a list of existing XAttrs. Removes matched XAttrs from
* toFilter and puts them into filtered. Upon completion,
* toFilter contains the filter XAttrs that were not found, while
* fitleredXAttrs contains the XAttrs that were found.
*
* @param existingXAttrs Existing XAttrs to be filtered
* @param toFilter XAttrs to filter from the existing XAttrs
* @param filtered Return parameter, XAttrs that were filtered
* @return List of XAttrs that does not contain filtered XAttrs
*/
@VisibleForTesting
static List<XAttr> filterINodeXAttrs(
final List<XAttr> existingXAttrs, final List<XAttr> toFilter,
final List<XAttr> filtered)
throws AccessControlException {
if (existingXAttrs == null || existingXAttrs.isEmpty() ||
toFilter == null || toFilter.isEmpty()) {
return existingXAttrs;
}
// Populate a new list with XAttrs that pass the filter
List<XAttr> newXAttrs =
Lists.newArrayListWithCapacity(existingXAttrs.size());
for (XAttr a : existingXAttrs) {
boolean add = true;
for (ListIterator<XAttr> it = toFilter.listIterator(); it.hasNext()
;) {
XAttr filter = it.next();
Preconditions.checkArgument(
!KEYID_XATTR.equalsIgnoreValue(filter),
"The encryption zone xattr should never be deleted.");
if (UNREADABLE_BY_SUPERUSER_XATTR.equalsIgnoreValue(filter)) {
throw new AccessControlException("The xattr '" +
SECURITY_XATTR_UNREADABLE_BY_SUPERUSER + "' can not be deleted.");
}
if (a.equalsIgnoreValue(filter)) {
add = false;
it.remove();
filtered.add(filter);
break;
}
}
if (add) {
newXAttrs.add(a);
}
}
return newXAttrs;
}
static INode unprotectedSetXAttrs(
FSDirectory fsd, final String src, final List<XAttr> xAttrs,
final EnumSet<XAttrSetFlag> flag)
throws IOException {
assert fsd.hasWriteLock();
INodesInPath iip = fsd.getINodesInPath4Write(FSDirectory.normalizePath(src), true);
INode inode = FSDirectory.resolveLastINode(src, iip);
int snapshotId = iip.getLatestSnapshotId();
List<XAttr> existingXAttrs = XAttrStorage.readINodeXAttrs(inode);
List<XAttr> newXAttrs = setINodeXAttrs(fsd, existingXAttrs, xAttrs, flag);
final boolean isFile = inode.isFile();
for (XAttr xattr : newXAttrs) {
final String xaName = XAttrHelper.getPrefixName(xattr);
/*
* If we're adding the encryption zone xattr, then add src to the list
* of encryption zones.
*/
if (CRYPTO_XATTR_ENCRYPTION_ZONE.equals(xaName)) {
final HdfsProtos.ZoneEncryptionInfoProto ezProto =
HdfsProtos.ZoneEncryptionInfoProto.parseFrom(xattr.getValue());
fsd.ezManager.addEncryptionZone(inode.getId(),
PBHelper.convert(ezProto.getSuite()),
PBHelper.convert(
ezProto.getCryptoProtocolVersion()),
ezProto.getKeyName());
}
if (!isFile && SECURITY_XATTR_UNREADABLE_BY_SUPERUSER.equals(xaName)) {
throw new IOException("Can only set '" +
SECURITY_XATTR_UNREADABLE_BY_SUPERUSER + "' on a file.");
}
}
XAttrStorage.updateINodeXAttrs(inode, newXAttrs, snapshotId);
return inode;
}
static List<XAttr> setINodeXAttrs(
FSDirectory fsd, final List<XAttr> existingXAttrs,
final List<XAttr> toSet, final EnumSet<XAttrSetFlag> flag)
throws IOException {
// Check for duplicate XAttrs in toSet
// We need to use a custom comparator, so using a HashSet is not suitable
for (int i = 0; i < toSet.size(); i++) {
for (int j = i + 1; j < toSet.size(); j++) {
if (toSet.get(i).equalsIgnoreValue(toSet.get(j))) {
throw new IOException("Cannot specify the same XAttr to be set " +
"more than once");
}
}
}
// Count the current number of user-visible XAttrs for limit checking
int userVisibleXAttrsNum = 0; // Number of user visible xAttrs
// The XAttr list is copied to an exactly-sized array when it's stored,
// so there's no need to size it precisely here.
int newSize = (existingXAttrs != null) ? existingXAttrs.size() : 0;
newSize += toSet.size();
List<XAttr> xAttrs = Lists.newArrayListWithCapacity(newSize);
// Check if the XAttr already exists to validate with the provided flag
for (XAttr xAttr: toSet) {
boolean exist = false;
if (existingXAttrs != null) {
for (XAttr a : existingXAttrs) {
if (a.equalsIgnoreValue(xAttr)) {
exist = true;
break;
}
}
}
XAttrSetFlag.validate(xAttr.getName(), exist, flag);
// add the new XAttr since it passed validation
xAttrs.add(xAttr);
if (isUserVisible(xAttr)) {
userVisibleXAttrsNum++;
}
}
// Add the existing xattrs back in, if they weren't already set
if (existingXAttrs != null) {
for (XAttr existing : existingXAttrs) {
boolean alreadySet = false;
for (XAttr set : toSet) {
if (set.equalsIgnoreValue(existing)) {
alreadySet = true;
break;
}
}
if (!alreadySet) {
xAttrs.add(existing);
if (isUserVisible(existing)) {
userVisibleXAttrsNum++;
}
}
}
}
if (userVisibleXAttrsNum > fsd.getInodeXAttrsLimit()) {
throw new IOException("Cannot add additional XAttr to inode, "
+ "would exceed limit of " + fsd.getInodeXAttrsLimit());
}
return xAttrs;
}
static List<XAttr> getXAttrs(FSDirectory fsd, INode inode, int snapshotId)
throws IOException {
fsd.readLock();
try {
return XAttrStorage.readINodeXAttrs(inode, snapshotId);
} finally {
fsd.readUnlock();
}
}
static XAttr unprotectedGetXAttrByName(
INode inode, int snapshotId, String xAttrName)
throws IOException {
List<XAttr> xAttrs = XAttrStorage.readINodeXAttrs(inode, snapshotId);
if (xAttrs == null) {
return null;
}
for (XAttr x : xAttrs) {
if (XAttrHelper.getPrefixName(x)
.equals(xAttrName)) {
return x;
}
}
return null;
}
private static void checkXAttrChangeAccess(
FSDirectory fsd, INodesInPath iip, XAttr xAttr,
FSPermissionChecker pc)
throws AccessControlException {
if (fsd.isPermissionEnabled() && xAttr.getNameSpace() == XAttr.NameSpace
.USER) {
final INode inode = iip.getLastINode();
if (inode != null &&
inode.isDirectory() &&
inode.getFsPermission().getStickyBit()) {
if (!pc.isSuperUser()) {
fsd.checkOwner(pc, iip);
}
} else {
fsd.checkPathAccess(pc, iip, FsAction.WRITE);
}
}
}
/**
* Verifies that the combined size of the name and value of an xattr is within
* the configured limit. Setting a limit of zero disables this check.
*/
private static void checkXAttrSize(FSDirectory fsd, XAttr xAttr) {
if (fsd.getXattrMaxSize() == 0) {
return;
}
int size = xAttr.getName().getBytes(Charsets.UTF_8).length;
if (xAttr.getValue() != null) {
size += xAttr.getValue().length;
}
if (size > fsd.getXattrMaxSize()) {
throw new HadoopIllegalArgumentException(
"The XAttr is too big. The maximum combined size of the"
+ " name and value is " + fsd.getXattrMaxSize()
+ ", but the total size is " + size);
}
}
private static void checkXAttrsConfigFlag(FSDirectory fsd) throws
IOException {
if (!fsd.isXattrsEnabled()) {
throw new IOException(String.format(
"The XAttr operation has been rejected. "
+ "Support for XAttrs has been disabled by setting %s to false.",
DFSConfigKeys.DFS_NAMENODE_XATTRS_ENABLED_KEY));
}
}
private static List<XAttr> getXAttrs(FSDirectory fsd,
String src) throws IOException {
String srcs = FSDirectory.normalizePath(src);
fsd.readLock();
try {
INodesInPath iip = fsd.getLastINodeInPath(srcs, true);
INode inode = FSDirectory.resolveLastINode(src, iip);
int snapshotId = iip.getPathSnapshotId();
return XAttrStorage.readINodeXAttrs(inode, snapshotId);
} finally {
fsd.readUnlock();
}
}
private static boolean isUserVisible(XAttr xAttr) {
XAttr.NameSpace ns = xAttr.getNameSpace();
return ns == XAttr.NameSpace.USER || ns == XAttr.NameSpace.TRUSTED;
}
}

View File

@ -20,7 +20,6 @@ package org.apache.hadoop.hdfs.server.namenode;
import static org.apache.hadoop.fs.BatchedRemoteIterator.BatchedListEntries; import static org.apache.hadoop.fs.BatchedRemoteIterator.BatchedListEntries;
import static org.apache.hadoop.hdfs.server.common.HdfsServerConstants.CRYPTO_XATTR_ENCRYPTION_ZONE; import static org.apache.hadoop.hdfs.server.common.HdfsServerConstants.CRYPTO_XATTR_ENCRYPTION_ZONE;
import static org.apache.hadoop.hdfs.server.common.HdfsServerConstants.CRYPTO_XATTR_FILE_ENCRYPTION_INFO; import static org.apache.hadoop.hdfs.server.common.HdfsServerConstants.CRYPTO_XATTR_FILE_ENCRYPTION_INFO;
import static org.apache.hadoop.hdfs.server.common.HdfsServerConstants.SECURITY_XATTR_UNREADABLE_BY_SUPERUSER;
import static org.apache.hadoop.util.Time.now; import static org.apache.hadoop.util.Time.now;
import java.io.Closeable; import java.io.Closeable;
@ -30,7 +29,6 @@ import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.EnumSet; import java.util.EnumSet;
import java.util.List; import java.util.List;
import java.util.ListIterator;
import java.util.concurrent.locks.ReentrantReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock;
import com.google.protobuf.InvalidProtocolBufferException; import com.google.protobuf.InvalidProtocolBufferException;
@ -122,10 +120,6 @@ public class FSDirectory implements Closeable {
public final static String DOT_INODES_STRING = ".inodes"; public final static String DOT_INODES_STRING = ".inodes";
public final static byte[] DOT_INODES = public final static byte[] DOT_INODES =
DFSUtil.string2Bytes(DOT_INODES_STRING); DFSUtil.string2Bytes(DOT_INODES_STRING);
private final XAttr KEYID_XATTR =
XAttrHelper.buildXAttr(CRYPTO_XATTR_ENCRYPTION_ZONE, null);
private final XAttr UNREADABLE_BY_SUPERUSER_XATTR =
XAttrHelper.buildXAttr(SECURITY_XATTR_UNREADABLE_BY_SUPERUSER, null);
INodeDirectory rootDir; INodeDirectory rootDir;
private final FSNamesystem namesystem; private final FSNamesystem namesystem;
@ -136,6 +130,7 @@ public class FSDirectory implements Closeable {
private final int contentCountLimit; // max content summary counts per run private final int contentCountLimit; // max content summary counts per run
private final INodeMap inodeMap; // Synchronized by dirLock private final INodeMap inodeMap; // Synchronized by dirLock
private long yieldCount = 0; // keep track of lock yield count. private long yieldCount = 0; // keep track of lock yield count.
private final int inodeXAttrsLimit; //inode xattrs max limit private final int inodeXAttrsLimit; //inode xattrs max limit
// lock to protect the directory and BlockMap // lock to protect the directory and BlockMap
@ -148,6 +143,8 @@ public class FSDirectory implements Closeable {
* ACL-related operations. * ACL-related operations.
*/ */
private final boolean aclsEnabled; private final boolean aclsEnabled;
private final boolean xattrsEnabled;
private final int xattrMaxSize;
private final String fsOwnerShortUserName; private final String fsOwnerShortUserName;
private final String supergroup; private final String supergroup;
private final INodeId inodeId; private final INodeId inodeId;
@ -213,6 +210,18 @@ public class FSDirectory implements Closeable {
DFSConfigKeys.DFS_NAMENODE_ACLS_ENABLED_KEY, DFSConfigKeys.DFS_NAMENODE_ACLS_ENABLED_KEY,
DFSConfigKeys.DFS_NAMENODE_ACLS_ENABLED_DEFAULT); DFSConfigKeys.DFS_NAMENODE_ACLS_ENABLED_DEFAULT);
LOG.info("ACLs enabled? " + aclsEnabled); LOG.info("ACLs enabled? " + aclsEnabled);
this.xattrsEnabled = conf.getBoolean(
DFSConfigKeys.DFS_NAMENODE_XATTRS_ENABLED_KEY,
DFSConfigKeys.DFS_NAMENODE_XATTRS_ENABLED_DEFAULT);
LOG.info("XAttrs enabled? " + xattrsEnabled);
this.xattrMaxSize = conf.getInt(
DFSConfigKeys.DFS_NAMENODE_MAX_XATTR_SIZE_KEY,
DFSConfigKeys.DFS_NAMENODE_MAX_XATTR_SIZE_DEFAULT);
Preconditions.checkArgument(xattrMaxSize >= 0,
"Cannot set a negative value for the maximum size of an xattr (%s).",
DFSConfigKeys.DFS_NAMENODE_MAX_XATTR_SIZE_KEY);
final String unlimited = xattrMaxSize == 0 ? " (unlimited)" : "";
LOG.info("Maximum size of an xattr: " + xattrMaxSize + unlimited);
int configuredLimit = conf.getInt( int configuredLimit = conf.getInt(
DFSConfigKeys.DFS_LIST_LIMIT, DFSConfigKeys.DFS_LIST_LIMIT_DEFAULT); DFSConfigKeys.DFS_LIST_LIMIT, DFSConfigKeys.DFS_LIST_LIMIT_DEFAULT);
this.lsLimit = configuredLimit>0 ? this.lsLimit = configuredLimit>0 ?
@ -274,6 +283,10 @@ public class FSDirectory implements Closeable {
boolean isAclsEnabled() { boolean isAclsEnabled() {
return aclsEnabled; return aclsEnabled;
} }
boolean isXattrsEnabled() {
return xattrsEnabled;
}
int getXattrMaxSize() { return xattrMaxSize; }
int getLsLimit() { int getLsLimit() {
return lsLimit; return lsLimit;
@ -283,6 +296,10 @@ public class FSDirectory implements Closeable {
return contentCountLimit; return contentCountLimit;
} }
int getInodeXAttrsLimit() {
return inodeXAttrsLimit;
}
FSEditLog getEditLog() { FSEditLog getEditLog() {
return editLog; return editLog;
} }
@ -613,8 +630,11 @@ public class FSDirectory implements Closeable {
int latestSnapshotId) throws IOException { int latestSnapshotId) throws IOException {
List<XAttr> existingXAttrs = XAttrStorage.readINodeXAttrs(inode); List<XAttr> existingXAttrs = XAttrStorage.readINodeXAttrs(inode);
XAttr xAttr = BlockStoragePolicySuite.buildXAttr(policyId); XAttr xAttr = BlockStoragePolicySuite.buildXAttr(policyId);
List<XAttr> newXAttrs = setINodeXAttrs(existingXAttrs, Arrays.asList(xAttr), List<XAttr> newXAttrs = FSDirXAttrOp.setINodeXAttrs(this, existingXAttrs,
EnumSet.of(XAttrSetFlag.CREATE, XAttrSetFlag.REPLACE)); Arrays.asList(xAttr),
EnumSet.of(
XAttrSetFlag.CREATE,
XAttrSetFlag.REPLACE));
XAttrStorage.updateINodeXAttrs(inode, newXAttrs, latestSnapshotId); XAttrStorage.updateINodeXAttrs(inode, newXAttrs, latestSnapshotId);
} }
@ -1560,90 +1580,6 @@ public class FSDirectory implements Closeable {
return addINode(path, symlink) ? symlink : null; return addINode(path, symlink) ? symlink : null;
} }
/**
* Removes a list of XAttrs from an inode at a path.
*
* @param src path of inode
* @param toRemove XAttrs to be removed
* @return List of XAttrs that were removed
* @throws IOException if the inode does not exist, if quota is exceeded
*/
List<XAttr> removeXAttrs(final String src, final List<XAttr> toRemove)
throws IOException {
writeLock();
try {
return unprotectedRemoveXAttrs(src, toRemove);
} finally {
writeUnlock();
}
}
List<XAttr> unprotectedRemoveXAttrs(final String src,
final List<XAttr> toRemove) throws IOException {
assert hasWriteLock();
INodesInPath iip = getINodesInPath4Write(normalizePath(src), true);
INode inode = resolveLastINode(src, iip);
int snapshotId = iip.getLatestSnapshotId();
List<XAttr> existingXAttrs = XAttrStorage.readINodeXAttrs(inode);
List<XAttr> removedXAttrs = Lists.newArrayListWithCapacity(toRemove.size());
List<XAttr> newXAttrs = filterINodeXAttrs(existingXAttrs, toRemove,
removedXAttrs);
if (existingXAttrs.size() != newXAttrs.size()) {
XAttrStorage.updateINodeXAttrs(inode, newXAttrs, snapshotId);
return removedXAttrs;
}
return null;
}
/**
* Filter XAttrs from a list of existing XAttrs. Removes matched XAttrs from
* toFilter and puts them into filtered. Upon completion,
* toFilter contains the filter XAttrs that were not found, while
* fitleredXAttrs contains the XAttrs that were found.
*
* @param existingXAttrs Existing XAttrs to be filtered
* @param toFilter XAttrs to filter from the existing XAttrs
* @param filtered Return parameter, XAttrs that were filtered
* @return List of XAttrs that does not contain filtered XAttrs
*/
@VisibleForTesting
List<XAttr> filterINodeXAttrs(final List<XAttr> existingXAttrs,
final List<XAttr> toFilter, final List<XAttr> filtered)
throws AccessControlException {
if (existingXAttrs == null || existingXAttrs.isEmpty() ||
toFilter == null || toFilter.isEmpty()) {
return existingXAttrs;
}
// Populate a new list with XAttrs that pass the filter
List<XAttr> newXAttrs =
Lists.newArrayListWithCapacity(existingXAttrs.size());
for (XAttr a : existingXAttrs) {
boolean add = true;
for (ListIterator<XAttr> it = toFilter.listIterator(); it.hasNext()
;) {
XAttr filter = it.next();
Preconditions.checkArgument(!KEYID_XATTR.equalsIgnoreValue(filter),
"The encryption zone xattr should never be deleted.");
if (UNREADABLE_BY_SUPERUSER_XATTR.equalsIgnoreValue(filter)) {
throw new AccessControlException("The xattr '" +
SECURITY_XATTR_UNREADABLE_BY_SUPERUSER + "' can not be deleted.");
}
if (a.equalsIgnoreValue(filter)) {
add = false;
it.remove();
filtered.add(filter);
break;
}
}
if (add) {
newXAttrs.add(a);
}
}
return newXAttrs;
}
boolean isInAnEZ(INodesInPath iip) boolean isInAnEZ(INodesInPath iip)
throws UnresolvedLinkException, SnapshotAccessControlException { throws UnresolvedLinkException, SnapshotAccessControlException {
readLock(); readLock();
@ -1709,7 +1645,8 @@ public class FSDirectory implements Closeable {
writeLock(); writeLock();
try { try {
unprotectedSetXAttrs(src, xAttrs, EnumSet.of(XAttrSetFlag.CREATE)); FSDirXAttrOp.unprotectedSetXAttrs(this, src, xAttrs,
EnumSet.of(XAttrSetFlag.CREATE));
} finally { } finally {
writeUnlock(); writeUnlock();
} }
@ -1752,8 +1689,9 @@ public class FSDirectory implements Closeable {
final CipherSuite suite = encryptionZone.getSuite(); final CipherSuite suite = encryptionZone.getSuite();
final String keyName = encryptionZone.getKeyName(); final String keyName = encryptionZone.getKeyName();
XAttr fileXAttr = unprotectedGetXAttrByName(inode, snapshotId, XAttr fileXAttr = FSDirXAttrOp.unprotectedGetXAttrByName(inode,
CRYPTO_XATTR_FILE_ENCRYPTION_INFO); snapshotId,
CRYPTO_XATTR_FILE_ENCRYPTION_INFO);
if (fileXAttr == null) { if (fileXAttr == null) {
NameNode.LOG.warn("Could not find encryption XAttr for file " + NameNode.LOG.warn("Could not find encryption XAttr for file " +
@ -1775,173 +1713,6 @@ public class FSDirectory implements Closeable {
} }
} }
void setXAttrs(final String src, final List<XAttr> xAttrs,
final EnumSet<XAttrSetFlag> flag) throws IOException {
writeLock();
try {
unprotectedSetXAttrs(src, xAttrs, flag);
} finally {
writeUnlock();
}
}
INode unprotectedSetXAttrs(final String src, final List<XAttr> xAttrs,
final EnumSet<XAttrSetFlag> flag)
throws QuotaExceededException, IOException {
assert hasWriteLock();
INodesInPath iip = getINodesInPath4Write(normalizePath(src), true);
INode inode = resolveLastINode(src, iip);
int snapshotId = iip.getLatestSnapshotId();
List<XAttr> existingXAttrs = XAttrStorage.readINodeXAttrs(inode);
List<XAttr> newXAttrs = setINodeXAttrs(existingXAttrs, xAttrs, flag);
final boolean isFile = inode.isFile();
for (XAttr xattr : newXAttrs) {
final String xaName = XAttrHelper.getPrefixName(xattr);
/*
* If we're adding the encryption zone xattr, then add src to the list
* of encryption zones.
*/
if (CRYPTO_XATTR_ENCRYPTION_ZONE.equals(xaName)) {
final HdfsProtos.ZoneEncryptionInfoProto ezProto =
HdfsProtos.ZoneEncryptionInfoProto.parseFrom(xattr.getValue());
ezManager.addEncryptionZone(inode.getId(),
PBHelper.convert(ezProto.getSuite()),
PBHelper.convert(ezProto.getCryptoProtocolVersion()),
ezProto.getKeyName());
}
if (!isFile && SECURITY_XATTR_UNREADABLE_BY_SUPERUSER.equals(xaName)) {
throw new IOException("Can only set '" +
SECURITY_XATTR_UNREADABLE_BY_SUPERUSER + "' on a file.");
}
}
XAttrStorage.updateINodeXAttrs(inode, newXAttrs, snapshotId);
return inode;
}
List<XAttr> setINodeXAttrs(final List<XAttr> existingXAttrs,
final List<XAttr> toSet, final EnumSet<XAttrSetFlag> flag)
throws IOException {
// Check for duplicate XAttrs in toSet
// We need to use a custom comparator, so using a HashSet is not suitable
for (int i = 0; i < toSet.size(); i++) {
for (int j = i + 1; j < toSet.size(); j++) {
if (toSet.get(i).equalsIgnoreValue(toSet.get(j))) {
throw new IOException("Cannot specify the same XAttr to be set " +
"more than once");
}
}
}
// Count the current number of user-visible XAttrs for limit checking
int userVisibleXAttrsNum = 0; // Number of user visible xAttrs
// The XAttr list is copied to an exactly-sized array when it's stored,
// so there's no need to size it precisely here.
int newSize = (existingXAttrs != null) ? existingXAttrs.size() : 0;
newSize += toSet.size();
List<XAttr> xAttrs = Lists.newArrayListWithCapacity(newSize);
// Check if the XAttr already exists to validate with the provided flag
for (XAttr xAttr: toSet) {
boolean exist = false;
if (existingXAttrs != null) {
for (XAttr a : existingXAttrs) {
if (a.equalsIgnoreValue(xAttr)) {
exist = true;
break;
}
}
}
XAttrSetFlag.validate(xAttr.getName(), exist, flag);
// add the new XAttr since it passed validation
xAttrs.add(xAttr);
if (isUserVisible(xAttr)) {
userVisibleXAttrsNum++;
}
}
// Add the existing xattrs back in, if they weren't already set
if (existingXAttrs != null) {
for (XAttr existing : existingXAttrs) {
boolean alreadySet = false;
for (XAttr set : toSet) {
if (set.equalsIgnoreValue(existing)) {
alreadySet = true;
break;
}
}
if (!alreadySet) {
xAttrs.add(existing);
if (isUserVisible(existing)) {
userVisibleXAttrsNum++;
}
}
}
}
if (userVisibleXAttrsNum > inodeXAttrsLimit) {
throw new IOException("Cannot add additional XAttr to inode, "
+ "would exceed limit of " + inodeXAttrsLimit);
}
return xAttrs;
}
private boolean isUserVisible(XAttr xAttr) {
if (xAttr.getNameSpace() == XAttr.NameSpace.USER ||
xAttr.getNameSpace() == XAttr.NameSpace.TRUSTED) {
return true;
}
return false;
}
List<XAttr> getXAttrs(String src) throws IOException {
String srcs = normalizePath(src);
readLock();
try {
INodesInPath iip = getLastINodeInPath(srcs, true);
INode inode = resolveLastINode(src, iip);
int snapshotId = iip.getPathSnapshotId();
return unprotectedGetXAttrs(inode, snapshotId);
} finally {
readUnlock();
}
}
List<XAttr> getXAttrs(INode inode, int snapshotId) throws IOException {
readLock();
try {
return unprotectedGetXAttrs(inode, snapshotId);
} finally {
readUnlock();
}
}
private List<XAttr> unprotectedGetXAttrs(INode inode, int snapshotId)
throws IOException {
return XAttrStorage.readINodeXAttrs(inode, snapshotId);
}
private XAttr unprotectedGetXAttrByName(INode inode, int snapshotId,
String xAttrName)
throws IOException {
List<XAttr> xAttrs = XAttrStorage.readINodeXAttrs(inode, snapshotId);
if (xAttrs == null) {
return null;
}
for (XAttr x : xAttrs) {
if (XAttrHelper.getPrefixName(x)
.equals(xAttrName)) {
return x;
}
}
return null;
}
static INode resolveLastINode(String src, INodesInPath iip) static INode resolveLastINode(String src, INodesInPath iip)
throws FileNotFoundException { throws FileNotFoundException {
INode[] inodes = iip.getINodes(); INode[] inodes = iip.getINodes();

View File

@ -538,7 +538,8 @@ public class FSEditLogLoader {
} }
case OP_SET_GENSTAMP_V1: { case OP_SET_GENSTAMP_V1: {
SetGenstampV1Op setGenstampV1Op = (SetGenstampV1Op)op; SetGenstampV1Op setGenstampV1Op = (SetGenstampV1Op)op;
fsNamesys.getBlockIdManager().setGenerationStampV1(setGenstampV1Op.genStampV1); fsNamesys.getBlockIdManager().setGenerationStampV1(
setGenstampV1Op.genStampV1);
break; break;
} }
case OP_SET_PERMISSIONS: { case OP_SET_PERMISSIONS: {
@ -734,12 +735,14 @@ public class FSEditLogLoader {
} }
case OP_SET_GENSTAMP_V2: { case OP_SET_GENSTAMP_V2: {
SetGenstampV2Op setGenstampV2Op = (SetGenstampV2Op) op; SetGenstampV2Op setGenstampV2Op = (SetGenstampV2Op) op;
fsNamesys.getBlockIdManager().setGenerationStampV2(setGenstampV2Op.genStampV2); fsNamesys.getBlockIdManager().setGenerationStampV2(
setGenstampV2Op.genStampV2);
break; break;
} }
case OP_ALLOCATE_BLOCK_ID: { case OP_ALLOCATE_BLOCK_ID: {
AllocateBlockIdOp allocateBlockIdOp = (AllocateBlockIdOp) op; AllocateBlockIdOp allocateBlockIdOp = (AllocateBlockIdOp) op;
fsNamesys.getBlockIdManager().setLastAllocatedBlockId(allocateBlockIdOp.blockId); fsNamesys.getBlockIdManager().setLastAllocatedBlockId(
allocateBlockIdOp.blockId);
break; break;
} }
case OP_ROLLING_UPGRADE_START: { case OP_ROLLING_UPGRADE_START: {
@ -832,8 +835,10 @@ public class FSEditLogLoader {
} }
case OP_SET_XATTR: { case OP_SET_XATTR: {
SetXAttrOp setXAttrOp = (SetXAttrOp) op; SetXAttrOp setXAttrOp = (SetXAttrOp) op;
fsDir.unprotectedSetXAttrs(setXAttrOp.src, setXAttrOp.xAttrs, FSDirXAttrOp.unprotectedSetXAttrs(fsDir, setXAttrOp.src,
EnumSet.of(XAttrSetFlag.CREATE, XAttrSetFlag.REPLACE)); setXAttrOp.xAttrs,
EnumSet.of(XAttrSetFlag.CREATE,
XAttrSetFlag.REPLACE));
if (toAddRetryCache) { if (toAddRetryCache) {
fsNamesys.addCacheEntry(setXAttrOp.rpcClientId, setXAttrOp.rpcCallId); fsNamesys.addCacheEntry(setXAttrOp.rpcClientId, setXAttrOp.rpcCallId);
} }
@ -841,8 +846,8 @@ public class FSEditLogLoader {
} }
case OP_REMOVE_XATTR: { case OP_REMOVE_XATTR: {
RemoveXAttrOp removeXAttrOp = (RemoveXAttrOp) op; RemoveXAttrOp removeXAttrOp = (RemoveXAttrOp) op;
fsDir.unprotectedRemoveXAttrs(removeXAttrOp.src, FSDirXAttrOp.unprotectedRemoveXAttrs(fsDir, removeXAttrOp.src,
removeXAttrOp.xAttrs); removeXAttrOp.xAttrs);
if (toAddRetryCache) { if (toAddRetryCache) {
fsNamesys.addCacheEntry(removeXAttrOp.rpcClientId, fsNamesys.addCacheEntry(removeXAttrOp.rpcClientId,
removeXAttrOp.rpcCallId); removeXAttrOp.rpcCallId);

View File

@ -521,9 +521,6 @@ public class FSNamesystem implements Namesystem, FSNamesystemMBean,
private final RetryCache retryCache; private final RetryCache retryCache;
private final boolean xattrsEnabled;
private final int xattrMaxSize;
private KeyProviderCryptoExtension provider = null; private KeyProviderCryptoExtension provider = null;
private volatile boolean imageLoaded = false; private volatile boolean imageLoaded = false;
@ -840,19 +837,6 @@ public class FSNamesystem implements Namesystem, FSNamesystemMBean,
this.isDefaultAuditLogger = auditLoggers.size() == 1 && this.isDefaultAuditLogger = auditLoggers.size() == 1 &&
auditLoggers.get(0) instanceof DefaultAuditLogger; auditLoggers.get(0) instanceof DefaultAuditLogger;
this.retryCache = ignoreRetryCache ? null : initRetryCache(conf); this.retryCache = ignoreRetryCache ? null : initRetryCache(conf);
this.xattrsEnabled = conf.getBoolean(
DFSConfigKeys.DFS_NAMENODE_XATTRS_ENABLED_KEY,
DFSConfigKeys.DFS_NAMENODE_XATTRS_ENABLED_DEFAULT);
LOG.info("XAttrs enabled? " + xattrsEnabled);
this.xattrMaxSize = conf.getInt(
DFSConfigKeys.DFS_NAMENODE_MAX_XATTR_SIZE_KEY,
DFSConfigKeys.DFS_NAMENODE_MAX_XATTR_SIZE_DEFAULT);
Preconditions.checkArgument(xattrMaxSize >= 0,
"Cannot set a negative value for the maximum size of an xattr (%s).",
DFSConfigKeys.DFS_NAMENODE_MAX_XATTR_SIZE_KEY);
final String unlimited = xattrMaxSize == 0 ? " (unlimited)" : "";
LOG.info("Maximum size of an xattr: " + xattrMaxSize + unlimited);
} catch(IOException e) { } catch(IOException e) {
LOG.error(getClass().getSimpleName() + " initialization failed.", e); LOG.error(getClass().getSimpleName() + " initialization failed.", e);
close(); close();
@ -5827,7 +5811,7 @@ public class FSNamesystem implements Namesystem, FSNamesystemMBean,
INode inode, int snapshotId) INode inode, int snapshotId)
throws IOException { throws IOException {
if (pc.isSuperUser()) { if (pc.isSuperUser()) {
for (XAttr xattr : dir.getXAttrs(inode, snapshotId)) { for (XAttr xattr : FSDirXAttrOp.getXAttrs(dir, inode, snapshotId)) {
if (XAttrHelper.getPrefixName(xattr). if (XAttrHelper.getPrefixName(xattr).
equals(SECURITY_XATTR_UNREADABLE_BY_SUPERUSER)) { equals(SECURITY_XATTR_UNREADABLE_BY_SUPERUSER)) {
throw new AccessControlException("Access is denied for " + throw new AccessControlException("Access is denied for " +
@ -7967,136 +7951,35 @@ public class FSNamesystem implements Namesystem, FSNamesystemMBean,
} }
} }
/**
* Set xattr for a file or directory.
*
* @param src
* - path on which it sets the xattr
* @param xAttr
* - xAttr details to set
* @param flag
* - xAttrs flags
* @throws AccessControlException
* @throws SafeModeException
* @throws UnresolvedLinkException
* @throws IOException
*/
void setXAttr(String src, XAttr xAttr, EnumSet<XAttrSetFlag> flag, void setXAttr(String src, XAttr xAttr, EnumSet<XAttrSetFlag> flag,
boolean logRetryCache) boolean logRetryCache)
throws AccessControlException, SafeModeException, throws IOException {
UnresolvedLinkException, IOException {
try {
setXAttrInt(src, xAttr, flag, logRetryCache);
} catch (AccessControlException e) {
logAuditEvent(false, "setXAttr", src);
throw e;
}
}
private void setXAttrInt(final String srcArg, XAttr xAttr,
EnumSet<XAttrSetFlag> flag, boolean logRetryCache) throws IOException {
String src = srcArg;
checkXAttrsConfigFlag();
checkXAttrSize(xAttr);
HdfsFileStatus resultingStat = null;
FSPermissionChecker pc = getPermissionChecker();
XAttrPermissionFilter.checkPermissionForApi(pc, xAttr,
FSDirectory.isReservedRawName(src));
checkOperation(OperationCategory.WRITE); checkOperation(OperationCategory.WRITE);
byte[][] pathComponents = FSDirectory.getPathComponentsForReservedPath(src); HdfsFileStatus auditStat = null;
writeLock(); writeLock();
try { try {
checkOperation(OperationCategory.WRITE); checkOperation(OperationCategory.WRITE);
checkNameNodeSafeMode("Cannot set XAttr on " + src); checkNameNodeSafeMode("Cannot set XAttr on " + src);
src = dir.resolvePath(pc, src, pathComponents); auditStat = FSDirXAttrOp.setXAttr(dir, src, xAttr, flag, logRetryCache);
final INodesInPath iip = dir.getINodesInPath4Write(src); } catch (AccessControlException e) {
checkXAttrChangeAccess(iip, xAttr, pc); logAuditEvent(false, "setXAttr", src);
List<XAttr> xAttrs = Lists.newArrayListWithCapacity(1); throw e;
xAttrs.add(xAttr);
dir.setXAttrs(src, xAttrs, flag);
getEditLog().logSetXAttrs(src, xAttrs, logRetryCache);
resultingStat = getAuditFileInfo(src, false);
} finally { } finally {
writeUnlock(); writeUnlock();
} }
getEditLog().logSync(); getEditLog().logSync();
logAuditEvent(true, "setXAttr", srcArg, null, resultingStat); logAuditEvent(true, "setXAttr", src, null, auditStat);
} }
/** List<XAttr> getXAttrs(final String src, List<XAttr> xAttrs)
* Verifies that the combined size of the name and value of an xattr is within
* the configured limit. Setting a limit of zero disables this check.
*/
private void checkXAttrSize(XAttr xAttr) {
if (xattrMaxSize == 0) {
return;
}
int size = xAttr.getName().getBytes(Charsets.UTF_8).length;
if (xAttr.getValue() != null) {
size += xAttr.getValue().length;
}
if (size > xattrMaxSize) {
throw new HadoopIllegalArgumentException(
"The XAttr is too big. The maximum combined size of the"
+ " name and value is " + xattrMaxSize
+ ", but the total size is " + size);
}
}
List<XAttr> getXAttrs(final String srcArg, List<XAttr> xAttrs)
throws IOException { throws IOException {
String src = srcArg;
checkXAttrsConfigFlag();
FSPermissionChecker pc = getPermissionChecker();
final boolean isRawPath = FSDirectory.isReservedRawName(src);
boolean getAll = xAttrs == null || xAttrs.isEmpty();
if (!getAll) {
try {
XAttrPermissionFilter.checkPermissionForApi(pc, xAttrs, isRawPath);
} catch (AccessControlException e) {
logAuditEvent(false, "getXAttrs", srcArg);
throw e;
}
}
checkOperation(OperationCategory.READ); checkOperation(OperationCategory.READ);
byte[][] pathComponents = FSDirectory.getPathComponentsForReservedPath(src);
readLock(); readLock();
try { try {
checkOperation(OperationCategory.READ); checkOperation(OperationCategory.READ);
src = dir.resolvePath(pc, src, pathComponents); return FSDirXAttrOp.getXAttrs(dir, src, xAttrs);
final INodesInPath iip = dir.getINodesInPath(src, true);
if (isPermissionEnabled) {
dir.checkPathAccess(pc, iip, FsAction.READ);
}
List<XAttr> all = dir.getXAttrs(src);
List<XAttr> filteredAll = XAttrPermissionFilter.
filterXAttrsForApi(pc, all, isRawPath);
if (getAll) {
return filteredAll;
} else {
if (filteredAll == null || filteredAll.isEmpty()) {
return null;
}
List<XAttr> toGet = Lists.newArrayListWithCapacity(xAttrs.size());
for (XAttr xAttr : xAttrs) {
boolean foundIt = false;
for (XAttr a : filteredAll) {
if (xAttr.getNameSpace() == a.getNameSpace()
&& xAttr.getName().equals(a.getName())) {
toGet.add(a);
foundIt = true;
break;
}
}
if (!foundIt) {
throw new IOException(
"At least one of the attributes provided was not found.");
}
}
return toGet;
}
} catch (AccessControlException e) { } catch (AccessControlException e) {
logAuditEvent(false, "getXAttrs", srcArg); logAuditEvent(false, "getXAttrs", src);
throw e; throw e;
} finally { } finally {
readUnlock(); readUnlock();
@ -8104,23 +7987,11 @@ public class FSNamesystem implements Namesystem, FSNamesystemMBean,
} }
List<XAttr> listXAttrs(String src) throws IOException { List<XAttr> listXAttrs(String src) throws IOException {
checkXAttrsConfigFlag();
final FSPermissionChecker pc = getPermissionChecker();
final boolean isRawPath = FSDirectory.isReservedRawName(src);
checkOperation(OperationCategory.READ); checkOperation(OperationCategory.READ);
byte[][] pathComponents = FSDirectory.getPathComponentsForReservedPath(src);
readLock(); readLock();
try { try {
checkOperation(OperationCategory.READ); checkOperation(OperationCategory.READ);
src = dir.resolvePath(pc, src, pathComponents); return FSDirXAttrOp.listXAttrs(dir, src);
final INodesInPath iip = dir.getINodesInPath(src, true);
if (isPermissionEnabled) {
/* To access xattr names, you need EXECUTE in the owning directory. */
dir.checkParentAccess(pc, iip, FsAction.EXECUTE);
}
final List<XAttr> all = dir.getXAttrs(src);
return XAttrPermissionFilter.
filterXAttrsForApi(pc, all, isRawPath);
} catch (AccessControlException e) { } catch (AccessControlException e) {
logAuditEvent(false, "listXAttrs", src); logAuditEvent(false, "listXAttrs", src);
throw e; throw e;
@ -8128,78 +7999,24 @@ public class FSNamesystem implements Namesystem, FSNamesystemMBean,
readUnlock(); readUnlock();
} }
} }
/**
* Remove an xattr for a file or directory.
*
* @param src
* - path to remove the xattr from
* @param xAttr
* - xAttr to remove
* @throws AccessControlException
* @throws SafeModeException
* @throws UnresolvedLinkException
* @throws IOException
*/
void removeXAttr(String src, XAttr xAttr, boolean logRetryCache) void removeXAttr(String src, XAttr xAttr, boolean logRetryCache)
throws IOException { throws IOException {
try {
removeXAttrInt(src, xAttr, logRetryCache);
} catch (AccessControlException e) {
logAuditEvent(false, "removeXAttr", src);
throw e;
}
}
void removeXAttrInt(final String srcArg, XAttr xAttr, boolean logRetryCache)
throws IOException {
String src = srcArg;
checkXAttrsConfigFlag();
HdfsFileStatus resultingStat = null;
FSPermissionChecker pc = getPermissionChecker();
XAttrPermissionFilter.checkPermissionForApi(pc, xAttr,
FSDirectory.isReservedRawName(src));
checkOperation(OperationCategory.WRITE); checkOperation(OperationCategory.WRITE);
byte[][] pathComponents = FSDirectory.getPathComponentsForReservedPath(src); HdfsFileStatus auditStat = null;
writeLock(); writeLock();
try { try {
checkOperation(OperationCategory.WRITE); checkOperation(OperationCategory.WRITE);
checkNameNodeSafeMode("Cannot remove XAttr entry on " + src); checkNameNodeSafeMode("Cannot remove XAttr entry on " + src);
src = dir.resolvePath(pc, src, pathComponents); auditStat = FSDirXAttrOp.removeXAttr(dir, src, xAttr, logRetryCache);
final INodesInPath iip = dir.getINodesInPath4Write(src); } catch (AccessControlException e) {
checkXAttrChangeAccess(iip, xAttr, pc); logAuditEvent(false, "removeXAttr", src);
throw e;
List<XAttr> xAttrs = Lists.newArrayListWithCapacity(1);
xAttrs.add(xAttr);
List<XAttr> removedXAttrs = dir.removeXAttrs(src, xAttrs);
if (removedXAttrs != null && !removedXAttrs.isEmpty()) {
getEditLog().logRemoveXAttrs(src, removedXAttrs, logRetryCache);
} else {
throw new IOException(
"No matching attributes found for remove operation");
}
resultingStat = getAuditFileInfo(src, false);
} finally { } finally {
writeUnlock(); writeUnlock();
} }
getEditLog().logSync(); getEditLog().logSync();
logAuditEvent(true, "removeXAttr", srcArg, null, resultingStat); logAuditEvent(true, "removeXAttr", src, null, auditStat);
}
private void checkXAttrChangeAccess(INodesInPath iip, XAttr xAttr,
FSPermissionChecker pc) throws AccessControlException {
if (isPermissionEnabled && xAttr.getNameSpace() == XAttr.NameSpace.USER) {
final INode inode = iip.getLastINode();
if (inode != null &&
inode.isDirectory() &&
inode.getFsPermission().getStickyBit()) {
if (!pc.isSuperUser()) {
dir.checkOwner(pc, iip);
}
} else {
dir.checkPathAccess(pc, iip, FsAction.WRITE);
}
}
} }
void checkAccess(String src, FsAction mode) throws IOException { void checkAccess(String src, FsAction mode) throws IOException {
@ -8312,13 +8129,5 @@ public class FSNamesystem implements Namesystem, FSNamesystemMBean,
} }
} }
private void checkXAttrsConfigFlag() throws IOException {
if (!xattrsEnabled) {
throw new IOException(String.format(
"The XAttr operation has been rejected. "
+ "Support for XAttrs has been disabled by setting %s to false.",
DFSConfigKeys.DFS_NAMENODE_XATTRS_ENABLED_KEY));
}
}
} }

View File

@ -40,7 +40,6 @@ import org.apache.hadoop.hdfs.DistributedFileSystem;
import org.apache.hadoop.hdfs.MiniDFSCluster; import org.apache.hadoop.hdfs.MiniDFSCluster;
import org.apache.hadoop.test.GenericTestUtils; import org.apache.hadoop.test.GenericTestUtils;
import org.junit.After; import org.junit.After;
import org.junit.Assert;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
@ -202,8 +201,9 @@ public class TestFSDirectory {
List<XAttr> newXAttrs = Lists.newArrayListWithCapacity(2); List<XAttr> newXAttrs = Lists.newArrayListWithCapacity(2);
newXAttrs.add(newSystemXAttr); newXAttrs.add(newSystemXAttr);
newXAttrs.add(newRawXAttr); newXAttrs.add(newRawXAttr);
List<XAttr> xAttrs = fsdir.setINodeXAttrs(existingXAttrs, newXAttrs, List<XAttr> xAttrs = FSDirXAttrOp.setINodeXAttrs(fsdir, existingXAttrs,
EnumSet.of(XAttrSetFlag.CREATE, XAttrSetFlag.REPLACE)); newXAttrs, EnumSet.of(
XAttrSetFlag.CREATE, XAttrSetFlag.REPLACE));
assertEquals(xAttrs.size(), 4); assertEquals(xAttrs.size(), 4);
// Adding a trusted namespace xAttr, is affected by inode xAttrs limit. // Adding a trusted namespace xAttr, is affected by inode xAttrs limit.
@ -212,8 +212,9 @@ public class TestFSDirectory {
setValue(new byte[]{0x34, 0x34, 0x34}).build(); setValue(new byte[]{0x34, 0x34, 0x34}).build();
newXAttrs.set(0, newXAttr1); newXAttrs.set(0, newXAttr1);
try { try {
fsdir.setINodeXAttrs(existingXAttrs, newXAttrs, FSDirXAttrOp.setINodeXAttrs(fsdir, existingXAttrs, newXAttrs,
EnumSet.of(XAttrSetFlag.CREATE, XAttrSetFlag.REPLACE)); EnumSet.of(XAttrSetFlag.CREATE,
XAttrSetFlag.REPLACE));
fail("Setting user visible xattr on inode should fail if " + fail("Setting user visible xattr on inode should fail if " +
"reaching limit."); "reaching limit.");
} catch (IOException e) { } catch (IOException e) {
@ -276,8 +277,9 @@ public class TestFSDirectory {
for (int i = 0; i < toAdd.size(); i++) { for (int i = 0; i < toAdd.size(); i++) {
LOG.info("Will add XAttr " + toAdd.get(i)); LOG.info("Will add XAttr " + toAdd.get(i));
} }
List<XAttr> newXAttrs = fsdir.setINodeXAttrs(existingXAttrs, toAdd, List<XAttr> newXAttrs = FSDirXAttrOp.setINodeXAttrs(fsdir, existingXAttrs,
EnumSet.of(XAttrSetFlag.CREATE)); toAdd, EnumSet.of(
XAttrSetFlag.CREATE));
verifyXAttrsPresent(newXAttrs, numExpectedXAttrs); verifyXAttrsPresent(newXAttrs, numExpectedXAttrs);
existingXAttrs = newXAttrs; existingXAttrs = newXAttrs;
} }
@ -297,8 +299,9 @@ public class TestFSDirectory {
final int expectedNumToRemove = toRemove.size(); final int expectedNumToRemove = toRemove.size();
LOG.info("Attempting to remove " + expectedNumToRemove + " XAttrs"); LOG.info("Attempting to remove " + expectedNumToRemove + " XAttrs");
List<XAttr> removedXAttrs = Lists.newArrayList(); List<XAttr> removedXAttrs = Lists.newArrayList();
List<XAttr> newXAttrs = fsdir.filterINodeXAttrs(existingXAttrs, List<XAttr> newXAttrs = FSDirXAttrOp.filterINodeXAttrs(existingXAttrs,
toRemove, removedXAttrs); toRemove,
removedXAttrs);
assertEquals("Unexpected number of removed XAttrs", assertEquals("Unexpected number of removed XAttrs",
expectedNumToRemove, removedXAttrs.size()); expectedNumToRemove, removedXAttrs.size());
verifyXAttrsPresent(newXAttrs, numExpectedXAttrs); verifyXAttrsPresent(newXAttrs, numExpectedXAttrs);
@ -317,8 +320,8 @@ public class TestFSDirectory {
toAdd.add(generatedXAttrs.get(2)); toAdd.add(generatedXAttrs.get(2));
toAdd.add(generatedXAttrs.get(0)); toAdd.add(generatedXAttrs.get(0));
try { try {
fsdir.setINodeXAttrs(existingXAttrs, toAdd, EnumSet.of(XAttrSetFlag FSDirXAttrOp.setINodeXAttrs(fsdir, existingXAttrs, toAdd,
.CREATE)); EnumSet.of(XAttrSetFlag.CREATE));
fail("Specified the same xattr to be set twice"); fail("Specified the same xattr to be set twice");
} catch (IOException e) { } catch (IOException e) {
GenericTestUtils.assertExceptionContains("Cannot specify the same " + GenericTestUtils.assertExceptionContains("Cannot specify the same " +
@ -329,15 +332,15 @@ public class TestFSDirectory {
toAdd.remove(generatedXAttrs.get(0)); toAdd.remove(generatedXAttrs.get(0));
existingXAttrs.add(generatedXAttrs.get(0)); existingXAttrs.add(generatedXAttrs.get(0));
try { try {
fsdir.setINodeXAttrs(existingXAttrs, toAdd, EnumSet.of(XAttrSetFlag FSDirXAttrOp.setINodeXAttrs(fsdir, existingXAttrs, toAdd,
.CREATE)); EnumSet.of(XAttrSetFlag.CREATE));
fail("Set XAttr that is already set without REPLACE flag"); fail("Set XAttr that is already set without REPLACE flag");
} catch (IOException e) { } catch (IOException e) {
GenericTestUtils.assertExceptionContains("already exists", e); GenericTestUtils.assertExceptionContains("already exists", e);
} }
try { try {
fsdir.setINodeXAttrs(existingXAttrs, toAdd, EnumSet.of(XAttrSetFlag FSDirXAttrOp.setINodeXAttrs(fsdir, existingXAttrs, toAdd,
.REPLACE)); EnumSet.of(XAttrSetFlag.REPLACE));
fail("Set XAttr that does not exist without the CREATE flag"); fail("Set XAttr that does not exist without the CREATE flag");
} catch (IOException e) { } catch (IOException e) {
GenericTestUtils.assertExceptionContains("does not exist", e); GenericTestUtils.assertExceptionContains("does not exist", e);
@ -345,8 +348,9 @@ public class TestFSDirectory {
// Sanity test for CREATE // Sanity test for CREATE
toAdd.remove(generatedXAttrs.get(0)); toAdd.remove(generatedXAttrs.get(0));
List<XAttr> newXAttrs = fsdir.setINodeXAttrs(existingXAttrs, toAdd, List<XAttr> newXAttrs = FSDirXAttrOp.setINodeXAttrs(fsdir, existingXAttrs,
EnumSet.of(XAttrSetFlag.CREATE)); toAdd, EnumSet.of(
XAttrSetFlag.CREATE));
assertEquals("Unexpected toAdd size", 2, toAdd.size()); assertEquals("Unexpected toAdd size", 2, toAdd.size());
for (XAttr x : toAdd) { for (XAttr x : toAdd) {
assertTrue("Did not find added XAttr " + x, newXAttrs.contains(x)); assertTrue("Did not find added XAttr " + x, newXAttrs.contains(x));
@ -363,8 +367,8 @@ public class TestFSDirectory {
.build(); .build();
toAdd.add(xAttr); toAdd.add(xAttr);
} }
newXAttrs = fsdir.setINodeXAttrs(existingXAttrs, toAdd, newXAttrs = FSDirXAttrOp.setINodeXAttrs(fsdir, existingXAttrs, toAdd,
EnumSet.of(XAttrSetFlag.REPLACE)); EnumSet.of(XAttrSetFlag.REPLACE));
assertEquals("Unexpected number of new XAttrs", 3, newXAttrs.size()); assertEquals("Unexpected number of new XAttrs", 3, newXAttrs.size());
for (int i=0; i<3; i++) { for (int i=0; i<3; i++) {
assertArrayEquals("Unexpected XAttr value", assertArrayEquals("Unexpected XAttr value",
@ -377,8 +381,9 @@ public class TestFSDirectory {
for (int i=0; i<4; i++) { for (int i=0; i<4; i++) {
toAdd.add(generatedXAttrs.get(i)); toAdd.add(generatedXAttrs.get(i));
} }
newXAttrs = fsdir.setINodeXAttrs(existingXAttrs, toAdd, newXAttrs = FSDirXAttrOp.setINodeXAttrs(fsdir, existingXAttrs, toAdd,
EnumSet.of(XAttrSetFlag.CREATE, XAttrSetFlag.REPLACE)); EnumSet.of(XAttrSetFlag.CREATE,
XAttrSetFlag.REPLACE));
verifyXAttrsPresent(newXAttrs, 4); verifyXAttrsPresent(newXAttrs, 4);
} }
} }