HDFS-10939. Reduce performance penalty of encryption zones. Contributed by Daryn Sharp.
This commit is contained in:
parent
cddd35e1f0
commit
76c03acc6d
|
@ -34,6 +34,12 @@ public class EncryptionFaultInjector {
|
||||||
return instance;
|
return instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
public void startFileNoKey() throws IOException {}
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
public void startFileBeforeGenerateKey() throws IOException {}
|
||||||
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
public void startFileAfterGenerateKey() throws IOException {}
|
public void startFileAfterGenerateKey() throws IOException {}
|
||||||
}
|
}
|
||||||
|
|
|
@ -260,12 +260,14 @@ public class EncryptionZoneManager {
|
||||||
*
|
*
|
||||||
* @param srcIIP source IIP
|
* @param srcIIP source IIP
|
||||||
* @param dstIIP destination IIP
|
* @param dstIIP destination IIP
|
||||||
* @param src source path, used for debugging
|
|
||||||
* @throws IOException if the src cannot be renamed to the dst
|
* @throws IOException if the src cannot be renamed to the dst
|
||||||
*/
|
*/
|
||||||
void checkMoveValidity(INodesInPath srcIIP, INodesInPath dstIIP, String src)
|
void checkMoveValidity(INodesInPath srcIIP, INodesInPath dstIIP)
|
||||||
throws IOException {
|
throws IOException {
|
||||||
assert dir.hasReadLock();
|
assert dir.hasReadLock();
|
||||||
|
if (!hasCreatedEncryptionZone()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
final EncryptionZoneInt srcParentEZI =
|
final EncryptionZoneInt srcParentEZI =
|
||||||
getParentEncryptionZoneForPath(srcIIP);
|
getParentEncryptionZoneForPath(srcIIP);
|
||||||
final EncryptionZoneInt dstParentEZI =
|
final EncryptionZoneInt dstParentEZI =
|
||||||
|
@ -274,17 +276,17 @@ public class EncryptionZoneManager {
|
||||||
final boolean dstInEZ = (dstParentEZI != null);
|
final boolean dstInEZ = (dstParentEZI != null);
|
||||||
if (srcInEZ && !dstInEZ) {
|
if (srcInEZ && !dstInEZ) {
|
||||||
throw new IOException(
|
throw new IOException(
|
||||||
src + " can't be moved from an encryption zone.");
|
srcIIP.getPath() + " can't be moved from an encryption zone.");
|
||||||
} else if (dstInEZ && !srcInEZ) {
|
} else if (dstInEZ && !srcInEZ) {
|
||||||
throw new IOException(
|
throw new IOException(
|
||||||
src + " can't be moved into an encryption zone.");
|
srcIIP.getPath() + " can't be moved into an encryption zone.");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (srcInEZ) {
|
if (srcInEZ) {
|
||||||
if (srcParentEZI != dstParentEZI) {
|
if (srcParentEZI != dstParentEZI) {
|
||||||
final String srcEZPath = getFullPathName(srcParentEZI);
|
final String srcEZPath = getFullPathName(srcParentEZI);
|
||||||
final String dstEZPath = getFullPathName(dstParentEZI);
|
final String dstEZPath = getFullPathName(dstParentEZI);
|
||||||
final StringBuilder sb = new StringBuilder(src);
|
final StringBuilder sb = new StringBuilder(srcIIP.getPath());
|
||||||
sb.append(" can't be moved from encryption zone ");
|
sb.append(" can't be moved from encryption zone ");
|
||||||
sb.append(srcEZPath);
|
sb.append(srcEZPath);
|
||||||
sb.append(" to encryption zone ");
|
sb.append(" to encryption zone ");
|
||||||
|
@ -300,15 +302,14 @@ public class EncryptionZoneManager {
|
||||||
* <p/>
|
* <p/>
|
||||||
* Called while holding the FSDirectory lock.
|
* Called while holding the FSDirectory lock.
|
||||||
*/
|
*/
|
||||||
XAttr createEncryptionZone(String src, CipherSuite suite,
|
XAttr createEncryptionZone(INodesInPath srcIIP, CipherSuite suite,
|
||||||
CryptoProtocolVersion version, String keyName)
|
CryptoProtocolVersion version, String keyName)
|
||||||
throws IOException {
|
throws IOException {
|
||||||
assert dir.hasWriteLock();
|
assert dir.hasWriteLock();
|
||||||
|
|
||||||
// Check if src is a valid path for new EZ creation
|
// Check if src is a valid path for new EZ creation
|
||||||
final INodesInPath srcIIP = dir.getINodesInPath4Write(src, false);
|
if (srcIIP.getLastINode() == null) {
|
||||||
if (srcIIP == null || srcIIP.getLastINode() == null) {
|
throw new FileNotFoundException("cannot find " + srcIIP.getPath());
|
||||||
throw new FileNotFoundException("cannot find " + src);
|
|
||||||
}
|
}
|
||||||
if (dir.isNonEmptyDirectory(srcIIP)) {
|
if (dir.isNonEmptyDirectory(srcIIP)) {
|
||||||
throw new IOException(
|
throw new IOException(
|
||||||
|
@ -322,8 +323,8 @@ public class EncryptionZoneManager {
|
||||||
|
|
||||||
if (hasCreatedEncryptionZone() && encryptionZones.
|
if (hasCreatedEncryptionZone() && encryptionZones.
|
||||||
get(srcINode.getId()) != null) {
|
get(srcINode.getId()) != null) {
|
||||||
throw new IOException("Directory " + src + " is already an encryption " +
|
throw new IOException(
|
||||||
"zone.");
|
"Directory " + srcIIP.getPath() + " is already an encryption zone.");
|
||||||
}
|
}
|
||||||
|
|
||||||
final HdfsProtos.ZoneEncryptionInfoProto proto =
|
final HdfsProtos.ZoneEncryptionInfoProto proto =
|
||||||
|
@ -335,7 +336,7 @@ 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
|
||||||
FSDirXAttrOp.unprotectedSetXAttrs(dir, src, xattrs,
|
FSDirXAttrOp.unprotectedSetXAttrs(dir, srcIIP, xattrs,
|
||||||
EnumSet.of(XAttrSetFlag.CREATE));
|
EnumSet.of(XAttrSetFlag.CREATE));
|
||||||
return ezXAttr;
|
return ezXAttr;
|
||||||
}
|
}
|
||||||
|
|
|
@ -72,8 +72,11 @@ final class FSDirEncryptionZoneOp {
|
||||||
* @return New EDEK, or null if ezKeyName is null
|
* @return New EDEK, or null if ezKeyName is null
|
||||||
* @throws IOException
|
* @throws IOException
|
||||||
*/
|
*/
|
||||||
static EncryptedKeyVersion generateEncryptedDataEncryptionKey(
|
private static EncryptedKeyVersion generateEncryptedDataEncryptionKey(
|
||||||
final FSDirectory fsd, final String ezKeyName) throws IOException {
|
final FSDirectory fsd, final String ezKeyName) throws IOException {
|
||||||
|
// must not be holding lock during this operation
|
||||||
|
assert !fsd.getFSNamesystem().hasReadLock();
|
||||||
|
assert !fsd.getFSNamesystem().hasWriteLock();
|
||||||
if (ezKeyName == null) {
|
if (ezKeyName == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -147,23 +150,21 @@ final class FSDirEncryptionZoneOp {
|
||||||
final String keyName, final boolean logRetryCache) throws IOException {
|
final String keyName, final boolean logRetryCache) throws IOException {
|
||||||
final CipherSuite suite = CipherSuite.convert(cipher);
|
final CipherSuite suite = CipherSuite.convert(cipher);
|
||||||
List<XAttr> xAttrs = Lists.newArrayListWithCapacity(1);
|
List<XAttr> xAttrs = Lists.newArrayListWithCapacity(1);
|
||||||
final String src;
|
|
||||||
// For now this is hard coded, as we only support one method.
|
// For now this is hard coded, as we only support one method.
|
||||||
final CryptoProtocolVersion version =
|
final CryptoProtocolVersion version =
|
||||||
CryptoProtocolVersion.ENCRYPTION_ZONES;
|
CryptoProtocolVersion.ENCRYPTION_ZONES;
|
||||||
|
|
||||||
|
final INodesInPath iip;
|
||||||
fsd.writeLock();
|
fsd.writeLock();
|
||||||
try {
|
try {
|
||||||
final INodesInPath iip = fsd.resolvePath(pc, srcArg);
|
iip = fsd.resolvePathForWrite(pc, srcArg);
|
||||||
src = iip.getPath();
|
final XAttr ezXAttr = fsd.ezManager.createEncryptionZone(iip, suite,
|
||||||
final XAttr ezXAttr = fsd.ezManager.createEncryptionZone(src, suite,
|
|
||||||
version, keyName);
|
version, keyName);
|
||||||
xAttrs.add(ezXAttr);
|
xAttrs.add(ezXAttr);
|
||||||
} finally {
|
} finally {
|
||||||
fsd.writeUnlock();
|
fsd.writeUnlock();
|
||||||
}
|
}
|
||||||
fsd.getEditLog().logSetXAttrs(src, xAttrs, logRetryCache);
|
fsd.getEditLog().logSetXAttrs(iip.getPath(), xAttrs, logRetryCache);
|
||||||
final INodesInPath iip = fsd.getINodesInPath4Write(src, false);
|
|
||||||
return fsd.getAuditFileInfo(iip);
|
return fsd.getAuditFileInfo(iip);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -223,8 +224,9 @@ final class FSDirEncryptionZoneOp {
|
||||||
* @param info file encryption information
|
* @param info file encryption information
|
||||||
* @throws IOException
|
* @throws IOException
|
||||||
*/
|
*/
|
||||||
static void setFileEncryptionInfo(final FSDirectory fsd, final String src,
|
static void setFileEncryptionInfo(final FSDirectory fsd,
|
||||||
final FileEncryptionInfo info) throws IOException {
|
final INodesInPath iip, final FileEncryptionInfo info)
|
||||||
|
throws IOException {
|
||||||
// Make the PB for the xattr
|
// Make the PB for the xattr
|
||||||
final HdfsProtos.PerFileEncryptionInfoProto proto =
|
final HdfsProtos.PerFileEncryptionInfoProto proto =
|
||||||
PBHelperClient.convertPerFileEncInfo(info);
|
PBHelperClient.convertPerFileEncInfo(info);
|
||||||
|
@ -235,7 +237,7 @@ final class FSDirEncryptionZoneOp {
|
||||||
xAttrs.add(fileEncryptionAttr);
|
xAttrs.add(fileEncryptionAttr);
|
||||||
fsd.writeLock();
|
fsd.writeLock();
|
||||||
try {
|
try {
|
||||||
FSDirXAttrOp.unprotectedSetXAttrs(fsd, src, xAttrs,
|
FSDirXAttrOp.unprotectedSetXAttrs(fsd, iip, xAttrs,
|
||||||
EnumSet.of(XAttrSetFlag.CREATE));
|
EnumSet.of(XAttrSetFlag.CREATE));
|
||||||
} finally {
|
} finally {
|
||||||
fsd.writeUnlock();
|
fsd.writeUnlock();
|
||||||
|
@ -246,21 +248,18 @@ final class FSDirEncryptionZoneOp {
|
||||||
* This function combines the per-file encryption info (obtained
|
* This function combines the per-file encryption info (obtained
|
||||||
* from the inode's XAttrs), and the encryption info from its zone, and
|
* from the inode's XAttrs), and the encryption info from its zone, and
|
||||||
* returns a consolidated FileEncryptionInfo instance. Null is returned
|
* returns a consolidated FileEncryptionInfo instance. Null is returned
|
||||||
* for non-encrypted files.
|
* for non-encrypted or raw files.
|
||||||
*
|
*
|
||||||
* @param fsd fsdirectory
|
* @param fsd fsdirectory
|
||||||
* @param inode inode of the file
|
|
||||||
* @param snapshotId ID of the snapshot that
|
|
||||||
* we want to get encryption info from
|
|
||||||
* @param iip inodes in the path containing the file, passed in to
|
* @param iip inodes in the path containing the file, passed in to
|
||||||
* avoid obtaining the list of inodes again; if iip is
|
* avoid obtaining the list of inodes again
|
||||||
* null then the list of inodes will be obtained again
|
|
||||||
* @return consolidated file encryption info; null for non-encrypted files
|
* @return consolidated file encryption info; null for non-encrypted files
|
||||||
*/
|
*/
|
||||||
static FileEncryptionInfo getFileEncryptionInfo(final FSDirectory fsd,
|
static FileEncryptionInfo getFileEncryptionInfo(final FSDirectory fsd,
|
||||||
final INode inode, final int snapshotId, final INodesInPath iip)
|
final INodesInPath iip) throws IOException {
|
||||||
throws IOException {
|
if (iip.isRaw() ||
|
||||||
if (!inode.isFile() || !fsd.ezManager.hasCreatedEncryptionZone()) {
|
!fsd.ezManager.hasCreatedEncryptionZone() ||
|
||||||
|
!iip.getLastINode().isFile()) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
fsd.readLock();
|
fsd.readLock();
|
||||||
|
@ -280,8 +279,8 @@ final class FSDirEncryptionZoneOp {
|
||||||
final CryptoProtocolVersion version = encryptionZone.getVersion();
|
final CryptoProtocolVersion version = encryptionZone.getVersion();
|
||||||
final CipherSuite suite = encryptionZone.getSuite();
|
final CipherSuite suite = encryptionZone.getSuite();
|
||||||
final String keyName = encryptionZone.getKeyName();
|
final String keyName = encryptionZone.getKeyName();
|
||||||
XAttr fileXAttr = FSDirXAttrOp.unprotectedGetXAttrByPrefixedName(inode,
|
XAttr fileXAttr = FSDirXAttrOp.unprotectedGetXAttrByPrefixedName(
|
||||||
snapshotId, CRYPTO_XATTR_FILE_ENCRYPTION_INFO);
|
iip, 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 " +
|
||||||
|
@ -295,15 +294,53 @@ final class FSDirEncryptionZoneOp {
|
||||||
return PBHelperClient.convert(fileProto, suite, version, keyName);
|
return PBHelperClient.convert(fileProto, suite, version, keyName);
|
||||||
} catch (InvalidProtocolBufferException e) {
|
} catch (InvalidProtocolBufferException e) {
|
||||||
throw new IOException("Could not parse file encryption info for " +
|
throw new IOException("Could not parse file encryption info for " +
|
||||||
"inode " + inode, e);
|
"inode " + iip.getPath(), e);
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
fsd.readUnlock();
|
fsd.readUnlock();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If the file and encryption key are valid, return the encryption info,
|
||||||
|
* else throw a retry exception. The startFile method generates the EDEK
|
||||||
|
* outside of the lock so the zone must be reverified.
|
||||||
|
*
|
||||||
|
* @param dir fsdirectory
|
||||||
|
* @param iip inodes in the file path
|
||||||
|
* @param ezInfo the encryption key
|
||||||
|
* @return FileEncryptionInfo for the file
|
||||||
|
* @throws RetryStartFileException if key is inconsistent with current zone
|
||||||
|
*/
|
||||||
|
static FileEncryptionInfo getFileEncryptionInfo(FSDirectory dir,
|
||||||
|
INodesInPath iip, EncryptionKeyInfo ezInfo)
|
||||||
|
throws RetryStartFileException {
|
||||||
|
FileEncryptionInfo feInfo = null;
|
||||||
|
final EncryptionZone zone = getEZForPath(dir, iip);
|
||||||
|
if (zone != null) {
|
||||||
|
// The path is now within an EZ, but we're missing encryption parameters
|
||||||
|
if (ezInfo == null) {
|
||||||
|
throw new RetryStartFileException();
|
||||||
|
}
|
||||||
|
// Path is within an EZ and we have provided encryption parameters.
|
||||||
|
// Make sure that the generated EDEK matches the settings of the EZ.
|
||||||
|
final String ezKeyName = zone.getKeyName();
|
||||||
|
if (!ezKeyName.equals(ezInfo.edek.getEncryptionKeyName())) {
|
||||||
|
throw new RetryStartFileException();
|
||||||
|
}
|
||||||
|
feInfo = new FileEncryptionInfo(ezInfo.suite, ezInfo.protocolVersion,
|
||||||
|
ezInfo.edek.getEncryptedKeyVersion().getMaterial(),
|
||||||
|
ezInfo.edek.getEncryptedKeyIv(),
|
||||||
|
ezKeyName, ezInfo.edek.getEncryptionKeyVersionName());
|
||||||
|
}
|
||||||
|
return feInfo;
|
||||||
|
}
|
||||||
|
|
||||||
static boolean isInAnEZ(final FSDirectory fsd, final INodesInPath iip)
|
static boolean isInAnEZ(final FSDirectory fsd, final INodesInPath iip)
|
||||||
throws UnresolvedLinkException, SnapshotAccessControlException {
|
throws UnresolvedLinkException, SnapshotAccessControlException {
|
||||||
|
if (!fsd.ezManager.hasCreatedEncryptionZone()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
fsd.readLock();
|
fsd.readLock();
|
||||||
try {
|
try {
|
||||||
return fsd.ezManager.isInAnEZ(iip);
|
return fsd.ezManager.isInAnEZ(iip);
|
||||||
|
@ -399,4 +436,67 @@ final class FSDirEncryptionZoneOp {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If the file is in an encryption zone, we optimistically create an
|
||||||
|
* EDEK for the file by calling out to the configured KeyProvider.
|
||||||
|
* Since this typically involves doing an RPC, the fsn lock is yielded.
|
||||||
|
*
|
||||||
|
* Since the path can flip-flop between being in an encryption zone and not
|
||||||
|
* in the meantime, the call MUST re-resolve the IIP and re-check
|
||||||
|
* preconditions if this method does not return null;
|
||||||
|
*
|
||||||
|
* @param fsn the namesystem.
|
||||||
|
* @param iip the inodes for the path
|
||||||
|
* @param supportedVersions client's supported versions
|
||||||
|
* @return EncryptionKeyInfo if the path is in an EZ, else null
|
||||||
|
*/
|
||||||
|
static EncryptionKeyInfo getEncryptionKeyInfo(FSNamesystem fsn,
|
||||||
|
INodesInPath iip, CryptoProtocolVersion[] supportedVersions)
|
||||||
|
throws IOException {
|
||||||
|
FSDirectory fsd = fsn.getFSDirectory();
|
||||||
|
// Nothing to do if the path is not within an EZ
|
||||||
|
final EncryptionZone zone = getEZForPath(fsd, iip);
|
||||||
|
if (zone == null) {
|
||||||
|
EncryptionFaultInjector.getInstance().startFileNoKey();
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
CryptoProtocolVersion protocolVersion = fsn.chooseProtocolVersion(
|
||||||
|
zone, supportedVersions);
|
||||||
|
CipherSuite suite = zone.getSuite();
|
||||||
|
String ezKeyName = zone.getKeyName();
|
||||||
|
|
||||||
|
Preconditions.checkNotNull(protocolVersion);
|
||||||
|
Preconditions.checkNotNull(suite);
|
||||||
|
Preconditions.checkArgument(!suite.equals(CipherSuite.UNKNOWN),
|
||||||
|
"Chose an UNKNOWN CipherSuite!");
|
||||||
|
Preconditions.checkNotNull(ezKeyName);
|
||||||
|
|
||||||
|
// Generate EDEK while not holding the fsn lock.
|
||||||
|
fsn.writeUnlock();
|
||||||
|
try {
|
||||||
|
EncryptionFaultInjector.getInstance().startFileBeforeGenerateKey();
|
||||||
|
return new EncryptionKeyInfo(protocolVersion, suite, ezKeyName,
|
||||||
|
generateEncryptedDataEncryptionKey(fsd, ezKeyName));
|
||||||
|
} finally {
|
||||||
|
fsn.writeLock();
|
||||||
|
EncryptionFaultInjector.getInstance().startFileAfterGenerateKey();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static class EncryptionKeyInfo {
|
||||||
|
final CryptoProtocolVersion protocolVersion;
|
||||||
|
final CipherSuite suite;
|
||||||
|
final String ezKeyName;
|
||||||
|
final KeyProviderCryptoExtension.EncryptedKeyVersion edek;
|
||||||
|
|
||||||
|
EncryptionKeyInfo(
|
||||||
|
CryptoProtocolVersion protocolVersion, CipherSuite suite,
|
||||||
|
String ezKeyName, KeyProviderCryptoExtension.EncryptedKeyVersion edek) {
|
||||||
|
this.protocolVersion = protocolVersion;
|
||||||
|
this.suite = suite;
|
||||||
|
this.ezKeyName = ezKeyName;
|
||||||
|
this.edek = edek;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -190,7 +190,7 @@ class FSDirRenameOp {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
fsd.ezManager.checkMoveValidity(srcIIP, dstIIP, src);
|
fsd.ezManager.checkMoveValidity(srcIIP, dstIIP);
|
||||||
// Ensure dst has quota to accommodate rename
|
// Ensure dst has quota to accommodate rename
|
||||||
verifyFsLimitsForRename(fsd, srcIIP, dstIIP);
|
verifyFsLimitsForRename(fsd, srcIIP, dstIIP);
|
||||||
verifyQuotaForRename(fsd, srcIIP, dstIIP);
|
verifyQuotaForRename(fsd, srcIIP, dstIIP);
|
||||||
|
@ -382,7 +382,7 @@ class FSDirRenameOp {
|
||||||
}
|
}
|
||||||
|
|
||||||
BlockStoragePolicySuite bsps = fsd.getBlockStoragePolicySuite();
|
BlockStoragePolicySuite bsps = fsd.getBlockStoragePolicySuite();
|
||||||
fsd.ezManager.checkMoveValidity(srcIIP, dstIIP, src);
|
fsd.ezManager.checkMoveValidity(srcIIP, dstIIP);
|
||||||
final INode dstInode = dstIIP.getLastINode();
|
final INode dstInode = dstIIP.getLastINode();
|
||||||
List<INodeDirectory> snapshottableDirs = new ArrayList<>();
|
List<INodeDirectory> snapshottableDirs = new ArrayList<>();
|
||||||
if (dstInode != null) { // Destination exists
|
if (dstInode != null) { // Destination exists
|
||||||
|
|
|
@ -161,7 +161,7 @@ class FSDirStatAndListingOp {
|
||||||
final INodeFile inode = INodeFile.valueOf(iip.getLastINode(), src);
|
final INodeFile inode = INodeFile.valueOf(iip.getLastINode(), src);
|
||||||
if (fsd.isPermissionEnabled()) {
|
if (fsd.isPermissionEnabled()) {
|
||||||
fsd.checkPathAccess(pc, iip, FsAction.READ);
|
fsd.checkPathAccess(pc, iip, FsAction.READ);
|
||||||
fsd.checkUnreadableBySuperuser(pc, inode, iip.getPathSnapshotId());
|
fsd.checkUnreadableBySuperuser(pc, iip);
|
||||||
}
|
}
|
||||||
|
|
||||||
final long fileSize = iip.isSnapshot()
|
final long fileSize = iip.isSnapshot()
|
||||||
|
@ -176,9 +176,8 @@ class FSDirStatAndListingOp {
|
||||||
isUc = false;
|
isUc = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
final FileEncryptionInfo feInfo = iip.isRaw() ? null
|
final FileEncryptionInfo feInfo =
|
||||||
: FSDirEncryptionZoneOp.getFileEncryptionInfo(fsd, inode,
|
FSDirEncryptionZoneOp.getFileEncryptionInfo(fsd, iip);
|
||||||
iip.getPathSnapshotId(), iip);
|
|
||||||
|
|
||||||
final LocatedBlocks blocks = bm.createLocatedBlocks(
|
final LocatedBlocks blocks = bm.createLocatedBlocks(
|
||||||
inode.getBlocks(iip.getPathSnapshotId()), fileSize, isUc, offset,
|
inode.getBlocks(iip.getPathSnapshotId()), fileSize, isUc, offset,
|
||||||
|
@ -411,22 +410,21 @@ class FSDirStatAndListingOp {
|
||||||
long size = 0; // length is zero for directories
|
long size = 0; // length is zero for directories
|
||||||
short replication = 0;
|
short replication = 0;
|
||||||
long blocksize = 0;
|
long blocksize = 0;
|
||||||
final boolean isEncrypted;
|
|
||||||
final INode node = iip.getLastINode();
|
final INode node = iip.getLastINode();
|
||||||
final int snapshot = iip.getPathSnapshotId();
|
final int snapshot = iip.getPathSnapshotId();
|
||||||
final boolean isRawPath = iip.isRaw();
|
|
||||||
LocatedBlocks loc = null;
|
LocatedBlocks loc = null;
|
||||||
|
|
||||||
final FileEncryptionInfo feInfo = isRawPath ? null : FSDirEncryptionZoneOp
|
final boolean isEncrypted = FSDirEncryptionZoneOp.isInAnEZ(fsd, iip);
|
||||||
.getFileEncryptionInfo(fsd, node, snapshot, iip);
|
FileEncryptionInfo feInfo = null;
|
||||||
|
|
||||||
if (node.isFile()) {
|
if (node.isFile()) {
|
||||||
final INodeFile fileNode = node.asFile();
|
final INodeFile fileNode = node.asFile();
|
||||||
size = fileNode.computeFileSize(snapshot);
|
size = fileNode.computeFileSize(snapshot);
|
||||||
replication = fileNode.getFileReplication(snapshot);
|
replication = fileNode.getFileReplication(snapshot);
|
||||||
blocksize = fileNode.getPreferredBlockSize();
|
blocksize = fileNode.getPreferredBlockSize();
|
||||||
isEncrypted = (feInfo != null)
|
if (isEncrypted) {
|
||||||
|| (isRawPath && FSDirEncryptionZoneOp.isInAnEZ(fsd, iip));
|
feInfo = FSDirEncryptionZoneOp.getFileEncryptionInfo(fsd, iip);
|
||||||
|
}
|
||||||
if (needLocation) {
|
if (needLocation) {
|
||||||
final boolean inSnapshot = snapshot != Snapshot.CURRENT_STATE_ID;
|
final boolean inSnapshot = snapshot != Snapshot.CURRENT_STATE_ID;
|
||||||
final boolean isUc = !inSnapshot && fileNode.isUnderConstruction();
|
final boolean isUc = !inSnapshot && fileNode.isUnderConstruction();
|
||||||
|
@ -439,8 +437,6 @@ class FSDirStatAndListingOp {
|
||||||
loc = new LocatedBlocks();
|
loc = new LocatedBlocks();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
isEncrypted = FSDirEncryptionZoneOp.isInAnEZ(fsd, iip);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int childrenNum = node.isDirectory() ?
|
int childrenNum = node.isDirectory() ?
|
||||||
|
|
|
@ -19,14 +19,10 @@ package org.apache.hadoop.hdfs.server.namenode;
|
||||||
|
|
||||||
import com.google.common.base.Preconditions;
|
import com.google.common.base.Preconditions;
|
||||||
import org.apache.hadoop.HadoopIllegalArgumentException;
|
import org.apache.hadoop.HadoopIllegalArgumentException;
|
||||||
import org.apache.hadoop.crypto.CipherSuite;
|
|
||||||
import org.apache.hadoop.crypto.CryptoProtocolVersion;
|
|
||||||
import org.apache.hadoop.crypto.key.KeyProviderCryptoExtension;
|
|
||||||
import org.apache.hadoop.hdfs.AddBlockFlag;
|
import org.apache.hadoop.hdfs.AddBlockFlag;
|
||||||
import org.apache.hadoop.fs.CreateFlag;
|
import org.apache.hadoop.fs.CreateFlag;
|
||||||
import org.apache.hadoop.fs.FileAlreadyExistsException;
|
import org.apache.hadoop.fs.FileAlreadyExistsException;
|
||||||
import org.apache.hadoop.fs.FileEncryptionInfo;
|
import org.apache.hadoop.fs.FileEncryptionInfo;
|
||||||
import org.apache.hadoop.fs.InvalidPathException;
|
|
||||||
import org.apache.hadoop.fs.XAttr;
|
import org.apache.hadoop.fs.XAttr;
|
||||||
import org.apache.hadoop.fs.permission.AclEntry;
|
import org.apache.hadoop.fs.permission.AclEntry;
|
||||||
import org.apache.hadoop.fs.permission.FsAction;
|
import org.apache.hadoop.fs.permission.FsAction;
|
||||||
|
@ -37,7 +33,6 @@ import org.apache.hadoop.hdfs.protocol.Block;
|
||||||
import org.apache.hadoop.hdfs.protocol.BlockStoragePolicy;
|
import org.apache.hadoop.hdfs.protocol.BlockStoragePolicy;
|
||||||
import org.apache.hadoop.hdfs.protocol.ClientProtocol;
|
import org.apache.hadoop.hdfs.protocol.ClientProtocol;
|
||||||
import org.apache.hadoop.hdfs.protocol.DatanodeInfo;
|
import org.apache.hadoop.hdfs.protocol.DatanodeInfo;
|
||||||
import org.apache.hadoop.hdfs.protocol.EncryptionZone;
|
|
||||||
import org.apache.hadoop.hdfs.protocol.ExtendedBlock;
|
import org.apache.hadoop.hdfs.protocol.ExtendedBlock;
|
||||||
import org.apache.hadoop.hdfs.protocol.FSLimitException;
|
import org.apache.hadoop.hdfs.protocol.FSLimitException;
|
||||||
import org.apache.hadoop.hdfs.protocol.HdfsFileStatus;
|
import org.apache.hadoop.hdfs.protocol.HdfsFileStatus;
|
||||||
|
@ -289,6 +284,37 @@ class FSDirWriteFileOp {
|
||||||
return clientNode;
|
return clientNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static INodesInPath resolvePathForStartFile(FSDirectory dir,
|
||||||
|
FSPermissionChecker pc, String src, EnumSet<CreateFlag> flag,
|
||||||
|
boolean createParent) throws IOException {
|
||||||
|
INodesInPath iip = dir.resolvePathForWrite(pc, src);
|
||||||
|
if (dir.isPermissionEnabled()) {
|
||||||
|
dir.checkAncestorAccess(pc, iip, FsAction.WRITE);
|
||||||
|
}
|
||||||
|
INode inode = iip.getLastINode();
|
||||||
|
if (inode != null) {
|
||||||
|
// Verify that the destination does not exist as a directory already.
|
||||||
|
if (inode.isDirectory()) {
|
||||||
|
throw new FileAlreadyExistsException(iip.getPath() +
|
||||||
|
" already exists as a directory");
|
||||||
|
}
|
||||||
|
// Verifies it's indeed a file and perms allow overwrite
|
||||||
|
INodeFile.valueOf(inode, src);
|
||||||
|
if (dir.isPermissionEnabled() && flag.contains(CreateFlag.OVERWRITE)) {
|
||||||
|
dir.checkPathAccess(pc, iip, FsAction.WRITE);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (!createParent) {
|
||||||
|
dir.verifyParentDir(iip, src);
|
||||||
|
}
|
||||||
|
if (!flag.contains(CreateFlag.CREATE)) {
|
||||||
|
throw new FileNotFoundException("Can't overwrite non-existent " + src);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return iip;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new file or overwrite an existing file<br>
|
* Create a new file or overwrite an existing file<br>
|
||||||
*
|
*
|
||||||
|
@ -299,88 +325,22 @@ class FSDirWriteFileOp {
|
||||||
* {@link ClientProtocol#create}
|
* {@link ClientProtocol#create}
|
||||||
*/
|
*/
|
||||||
static HdfsFileStatus startFile(
|
static HdfsFileStatus startFile(
|
||||||
FSNamesystem fsn, FSPermissionChecker pc, String src,
|
FSNamesystem fsn, INodesInPath iip,
|
||||||
PermissionStatus permissions, String holder, String clientMachine,
|
PermissionStatus permissions, String holder, String clientMachine,
|
||||||
EnumSet<CreateFlag> flag, boolean createParent,
|
EnumSet<CreateFlag> flag, boolean createParent,
|
||||||
short replication, long blockSize,
|
short replication, long blockSize,
|
||||||
EncryptionKeyInfo ezInfo, INode.BlocksMapUpdateInfo toRemoveBlocks,
|
FileEncryptionInfo feInfo, INode.BlocksMapUpdateInfo toRemoveBlocks,
|
||||||
boolean logRetryEntry)
|
boolean logRetryEntry)
|
||||||
throws IOException {
|
throws IOException {
|
||||||
assert fsn.hasWriteLock();
|
assert fsn.hasWriteLock();
|
||||||
|
|
||||||
boolean create = flag.contains(CreateFlag.CREATE);
|
|
||||||
boolean overwrite = flag.contains(CreateFlag.OVERWRITE);
|
boolean overwrite = flag.contains(CreateFlag.OVERWRITE);
|
||||||
boolean isLazyPersist = flag.contains(CreateFlag.LAZY_PERSIST);
|
boolean isLazyPersist = flag.contains(CreateFlag.LAZY_PERSIST);
|
||||||
|
|
||||||
CipherSuite suite = null;
|
final String src = iip.getPath();
|
||||||
CryptoProtocolVersion version = null;
|
|
||||||
KeyProviderCryptoExtension.EncryptedKeyVersion edek = null;
|
|
||||||
|
|
||||||
if (ezInfo != null) {
|
|
||||||
edek = ezInfo.edek;
|
|
||||||
suite = ezInfo.suite;
|
|
||||||
version = ezInfo.protocolVersion;
|
|
||||||
}
|
|
||||||
|
|
||||||
FSDirectory fsd = fsn.getFSDirectory();
|
FSDirectory fsd = fsn.getFSDirectory();
|
||||||
INodesInPath iip = fsd.resolvePathForWrite(pc, src);
|
|
||||||
src = iip.getPath();
|
|
||||||
|
|
||||||
// Verify that the destination does not exist as a directory already.
|
if (iip.getLastINode() != null) {
|
||||||
final INode inode = iip.getLastINode();
|
|
||||||
if (inode != null && inode.isDirectory()) {
|
|
||||||
throw new FileAlreadyExistsException(src +
|
|
||||||
" already exists as a directory");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (FSDirectory.isExactReservedName(src) || (FSDirectory.isReservedName(src)
|
|
||||||
&& !FSDirectory.isReservedRawName(src)
|
|
||||||
&& !FSDirectory.isReservedInodesName(src)) ) {
|
|
||||||
throw new InvalidPathException(src);
|
|
||||||
}
|
|
||||||
|
|
||||||
final INodeFile myFile = INodeFile.valueOf(inode, src, true);
|
|
||||||
if (fsd.isPermissionEnabled()) {
|
|
||||||
if (overwrite && myFile != null) {
|
|
||||||
fsd.checkPathAccess(pc, iip, FsAction.WRITE);
|
|
||||||
}
|
|
||||||
/*
|
|
||||||
* To overwrite existing file, need to check 'w' permission
|
|
||||||
* of parent (equals to ancestor in this case)
|
|
||||||
*/
|
|
||||||
fsd.checkAncestorAccess(pc, iip, FsAction.WRITE);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!createParent) {
|
|
||||||
fsd.verifyParentDir(iip, src);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (myFile == null && !create) {
|
|
||||||
throw new FileNotFoundException("Can't overwrite non-existent " +
|
|
||||||
src + " for client " + clientMachine);
|
|
||||||
}
|
|
||||||
|
|
||||||
FileEncryptionInfo feInfo = null;
|
|
||||||
|
|
||||||
final EncryptionZone zone = FSDirEncryptionZoneOp.getEZForPath(fsd, iip);
|
|
||||||
if (zone != null) {
|
|
||||||
// The path is now within an EZ, but we're missing encryption parameters
|
|
||||||
if (suite == null || edek == null) {
|
|
||||||
throw new RetryStartFileException();
|
|
||||||
}
|
|
||||||
// Path is within an EZ and we have provided encryption parameters.
|
|
||||||
// Make sure that the generated EDEK matches the settings of the EZ.
|
|
||||||
final String ezKeyName = zone.getKeyName();
|
|
||||||
if (!ezKeyName.equals(edek.getEncryptionKeyName())) {
|
|
||||||
throw new RetryStartFileException();
|
|
||||||
}
|
|
||||||
feInfo = new FileEncryptionInfo(suite, version,
|
|
||||||
edek.getEncryptedKeyVersion().getMaterial(),
|
|
||||||
edek.getEncryptedKeyIv(),
|
|
||||||
ezKeyName, edek.getEncryptionKeyVersionName());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (myFile != null) {
|
|
||||||
if (overwrite) {
|
if (overwrite) {
|
||||||
List<INode> toRemoveINodes = new ChunkedArrayList<>();
|
List<INode> toRemoveINodes = new ChunkedArrayList<>();
|
||||||
List<Long> toRemoveUCFiles = new ChunkedArrayList<>();
|
List<Long> toRemoveUCFiles = new ChunkedArrayList<>();
|
||||||
|
@ -415,11 +375,9 @@ class FSDirWriteFileOp {
|
||||||
newNode.getFileUnderConstructionFeature().getClientName(),
|
newNode.getFileUnderConstructionFeature().getClientName(),
|
||||||
newNode.getId());
|
newNode.getId());
|
||||||
if (feInfo != null) {
|
if (feInfo != null) {
|
||||||
FSDirEncryptionZoneOp.setFileEncryptionInfo(fsd, src, feInfo);
|
FSDirEncryptionZoneOp.setFileEncryptionInfo(fsd, iip, feInfo);
|
||||||
newNode = fsd.getInode(newNode.getId()).asFile();
|
|
||||||
}
|
}
|
||||||
setNewINodeStoragePolicy(fsd.getBlockManager(), newNode, iip,
|
setNewINodeStoragePolicy(fsd.getBlockManager(), iip, isLazyPersist);
|
||||||
isLazyPersist);
|
|
||||||
fsd.getEditLog().logOpenFile(src, newNode, overwrite, logRetryEntry);
|
fsd.getEditLog().logOpenFile(src, newNode, overwrite, logRetryEntry);
|
||||||
if (NameNode.stateChangeLog.isDebugEnabled()) {
|
if (NameNode.stateChangeLog.isDebugEnabled()) {
|
||||||
NameNode.stateChangeLog.debug("DIR* NameSystem.startFile: added " +
|
NameNode.stateChangeLog.debug("DIR* NameSystem.startFile: added " +
|
||||||
|
@ -428,30 +386,6 @@ class FSDirWriteFileOp {
|
||||||
return FSDirStatAndListingOp.getFileInfo(fsd, iip);
|
return FSDirStatAndListingOp.getFileInfo(fsd, iip);
|
||||||
}
|
}
|
||||||
|
|
||||||
static EncryptionKeyInfo getEncryptionKeyInfo(FSNamesystem fsn,
|
|
||||||
FSPermissionChecker pc, String src,
|
|
||||||
CryptoProtocolVersion[] supportedVersions)
|
|
||||||
throws IOException {
|
|
||||||
FSDirectory fsd = fsn.getFSDirectory();
|
|
||||||
INodesInPath iip = fsd.resolvePathForWrite(pc, src);
|
|
||||||
// Nothing to do if the path is not within an EZ
|
|
||||||
final EncryptionZone zone = FSDirEncryptionZoneOp.getEZForPath(fsd, iip);
|
|
||||||
if (zone == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
CryptoProtocolVersion protocolVersion = fsn.chooseProtocolVersion(
|
|
||||||
zone, supportedVersions);
|
|
||||||
CipherSuite suite = zone.getSuite();
|
|
||||||
String ezKeyName = zone.getKeyName();
|
|
||||||
|
|
||||||
Preconditions.checkNotNull(protocolVersion);
|
|
||||||
Preconditions.checkNotNull(suite);
|
|
||||||
Preconditions.checkArgument(!suite.equals(CipherSuite.UNKNOWN),
|
|
||||||
"Chose an UNKNOWN CipherSuite!");
|
|
||||||
Preconditions.checkNotNull(ezKeyName);
|
|
||||||
return new EncryptionKeyInfo(protocolVersion, suite, ezKeyName);
|
|
||||||
}
|
|
||||||
|
|
||||||
static INodeFile addFileForEditLog(
|
static INodeFile addFileForEditLog(
|
||||||
FSDirectory fsd, long id, INodesInPath existing, byte[] localName,
|
FSDirectory fsd, long id, INodesInPath existing, byte[] localName,
|
||||||
PermissionStatus permissions, List<AclEntry> aclEntries,
|
PermissionStatus permissions, List<AclEntry> aclEntries,
|
||||||
|
@ -790,10 +724,9 @@ class FSDirWriteFileOp {
|
||||||
NameNode.stateChangeLog.info(sb.toString());
|
NameNode.stateChangeLog.info(sb.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void setNewINodeStoragePolicy(BlockManager bm, INodeFile
|
private static void setNewINodeStoragePolicy(BlockManager bm,
|
||||||
inode, INodesInPath iip, boolean isLazyPersist)
|
INodesInPath iip, boolean isLazyPersist) throws IOException {
|
||||||
throws IOException {
|
INodeFile inode = iip.getLastINode().asFile();
|
||||||
|
|
||||||
if (isLazyPersist) {
|
if (isLazyPersist) {
|
||||||
BlockStoragePolicy lpPolicy =
|
BlockStoragePolicy lpPolicy =
|
||||||
bm.getStoragePolicy("LAZY_PERSIST");
|
bm.getStoragePolicy("LAZY_PERSIST");
|
||||||
|
@ -847,19 +780,4 @@ class FSDirWriteFileOp {
|
||||||
this.clientMachine = clientMachine;
|
this.clientMachine = clientMachine;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static class EncryptionKeyInfo {
|
|
||||||
final CryptoProtocolVersion protocolVersion;
|
|
||||||
final CipherSuite suite;
|
|
||||||
final String ezKeyName;
|
|
||||||
KeyProviderCryptoExtension.EncryptedKeyVersion edek;
|
|
||||||
|
|
||||||
EncryptionKeyInfo(
|
|
||||||
CryptoProtocolVersion protocolVersion, CipherSuite suite,
|
|
||||||
String ezKeyName) {
|
|
||||||
this.protocolVersion = protocolVersion;
|
|
||||||
this.suite = suite;
|
|
||||||
this.ezKeyName = ezKeyName;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -75,7 +75,7 @@ class FSDirXAttrOp {
|
||||||
iip = fsd.resolvePathForWrite(pc, src);
|
iip = fsd.resolvePathForWrite(pc, src);
|
||||||
src = iip.getPath();
|
src = iip.getPath();
|
||||||
checkXAttrChangeAccess(fsd, iip, xAttr, pc);
|
checkXAttrChangeAccess(fsd, iip, xAttr, pc);
|
||||||
unprotectedSetXAttrs(fsd, src, xAttrs, flag);
|
unprotectedSetXAttrs(fsd, iip, xAttrs, flag);
|
||||||
} finally {
|
} finally {
|
||||||
fsd.writeUnlock();
|
fsd.writeUnlock();
|
||||||
}
|
}
|
||||||
|
@ -253,14 +253,11 @@ class FSDirXAttrOp {
|
||||||
}
|
}
|
||||||
|
|
||||||
static INode unprotectedSetXAttrs(
|
static INode unprotectedSetXAttrs(
|
||||||
FSDirectory fsd, final String src, final List<XAttr> xAttrs,
|
FSDirectory fsd, final INodesInPath iip, final List<XAttr> xAttrs,
|
||||||
final EnumSet<XAttrSetFlag> flag)
|
final EnumSet<XAttrSetFlag> flag)
|
||||||
throws IOException {
|
throws IOException {
|
||||||
assert fsd.hasWriteLock();
|
assert fsd.hasWriteLock();
|
||||||
INodesInPath iip = fsd.getINodesInPath4Write(FSDirectory.normalizePath(src),
|
|
||||||
true);
|
|
||||||
INode inode = FSDirectory.resolveLastINode(iip);
|
INode inode = FSDirectory.resolveLastINode(iip);
|
||||||
int snapshotId = iip.getLatestSnapshotId();
|
|
||||||
List<XAttr> existingXAttrs = XAttrStorage.readINodeXAttrs(inode);
|
List<XAttr> existingXAttrs = XAttrStorage.readINodeXAttrs(inode);
|
||||||
List<XAttr> newXAttrs = setINodeXAttrs(fsd, existingXAttrs, xAttrs, flag);
|
List<XAttr> newXAttrs = setINodeXAttrs(fsd, existingXAttrs, xAttrs, flag);
|
||||||
final boolean isFile = inode.isFile();
|
final boolean isFile = inode.isFile();
|
||||||
|
@ -287,7 +284,7 @@ class FSDirXAttrOp {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
XAttrStorage.updateINodeXAttrs(inode, newXAttrs, snapshotId);
|
XAttrStorage.updateINodeXAttrs(inode, newXAttrs, iip.getLatestSnapshotId());
|
||||||
return inode;
|
return inode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -361,22 +358,20 @@ class FSDirXAttrOp {
|
||||||
return xAttrs;
|
return xAttrs;
|
||||||
}
|
}
|
||||||
|
|
||||||
static XAttr getXAttrByPrefixedName(FSDirectory fsd, INode inode,
|
static XAttr getXAttrByPrefixedName(FSDirectory fsd, INodesInPath iip,
|
||||||
int snapshotId, String prefixedName) throws IOException {
|
String prefixedName) throws IOException {
|
||||||
fsd.readLock();
|
fsd.readLock();
|
||||||
try {
|
try {
|
||||||
return XAttrStorage.readINodeXAttrByPrefixedName(inode, snapshotId,
|
return XAttrStorage.readINodeXAttrByPrefixedName(iip, prefixedName);
|
||||||
prefixedName);
|
|
||||||
} finally {
|
} finally {
|
||||||
fsd.readUnlock();
|
fsd.readUnlock();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static XAttr unprotectedGetXAttrByPrefixedName(
|
static XAttr unprotectedGetXAttrByPrefixedName(
|
||||||
INode inode, int snapshotId, String prefixedName)
|
INodesInPath iip, String prefixedName)
|
||||||
throws IOException {
|
throws IOException {
|
||||||
return XAttrStorage.readINodeXAttrByPrefixedName(inode, snapshotId,
|
return XAttrStorage.readINodeXAttrByPrefixedName(iip, prefixedName);
|
||||||
prefixedName);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void checkXAttrChangeAccess(
|
private static void checkXAttrChangeAccess(
|
||||||
|
|
|
@ -1660,11 +1660,10 @@ public class FSDirectory implements Closeable {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void checkUnreadableBySuperuser(
|
void checkUnreadableBySuperuser(FSPermissionChecker pc, INodesInPath iip)
|
||||||
FSPermissionChecker pc, INode inode, int snapshotId)
|
|
||||||
throws IOException {
|
throws IOException {
|
||||||
if (pc.isSuperUser()) {
|
if (pc.isSuperUser()) {
|
||||||
if (FSDirXAttrOp.getXAttrByPrefixedName(this, inode, snapshotId,
|
if (FSDirXAttrOp.getXAttrByPrefixedName(this, iip,
|
||||||
SECURITY_XATTR_UNREADABLE_BY_SUPERUSER) != null) {
|
SECURITY_XATTR_UNREADABLE_BY_SUPERUSER) != null) {
|
||||||
throw new AccessControlException(
|
throw new AccessControlException(
|
||||||
"Access is denied for " + pc.getUser() + " since the superuser "
|
"Access is denied for " + pc.getUser() + " since the superuser "
|
||||||
|
|
|
@ -895,7 +895,8 @@ public class FSEditLogLoader {
|
||||||
}
|
}
|
||||||
case OP_SET_XATTR: {
|
case OP_SET_XATTR: {
|
||||||
SetXAttrOp setXAttrOp = (SetXAttrOp) op;
|
SetXAttrOp setXAttrOp = (SetXAttrOp) op;
|
||||||
FSDirXAttrOp.unprotectedSetXAttrs(fsDir, setXAttrOp.src,
|
INodesInPath iip = fsDir.getINodesInPath4Write(setXAttrOp.src);
|
||||||
|
FSDirXAttrOp.unprotectedSetXAttrs(fsDir, iip,
|
||||||
setXAttrOp.xAttrs,
|
setXAttrOp.xAttrs,
|
||||||
EnumSet.of(XAttrSetFlag.CREATE,
|
EnumSet.of(XAttrSetFlag.CREATE,
|
||||||
XAttrSetFlag.REPLACE));
|
XAttrSetFlag.REPLACE));
|
||||||
|
|
|
@ -150,6 +150,7 @@ import org.apache.hadoop.fs.BatchedRemoteIterator.BatchedListEntries;
|
||||||
import org.apache.hadoop.fs.CacheFlag;
|
import org.apache.hadoop.fs.CacheFlag;
|
||||||
import org.apache.hadoop.fs.ContentSummary;
|
import org.apache.hadoop.fs.ContentSummary;
|
||||||
import org.apache.hadoop.fs.CreateFlag;
|
import org.apache.hadoop.fs.CreateFlag;
|
||||||
|
import org.apache.hadoop.fs.FileEncryptionInfo;
|
||||||
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.FsServerDefaults;
|
import org.apache.hadoop.fs.FsServerDefaults;
|
||||||
|
@ -220,6 +221,7 @@ import org.apache.hadoop.hdfs.server.common.Storage;
|
||||||
import org.apache.hadoop.hdfs.server.common.Storage.StorageDirType;
|
import org.apache.hadoop.hdfs.server.common.Storage.StorageDirType;
|
||||||
import org.apache.hadoop.hdfs.server.common.Storage.StorageDirectory;
|
import org.apache.hadoop.hdfs.server.common.Storage.StorageDirectory;
|
||||||
import org.apache.hadoop.hdfs.server.common.Util;
|
import org.apache.hadoop.hdfs.server.common.Util;
|
||||||
|
import org.apache.hadoop.hdfs.server.namenode.FSDirEncryptionZoneOp.EncryptionKeyInfo;
|
||||||
import org.apache.hadoop.hdfs.server.namenode.FsImageProto.SecretManagerSection;
|
import org.apache.hadoop.hdfs.server.namenode.FsImageProto.SecretManagerSection;
|
||||||
import org.apache.hadoop.hdfs.server.namenode.INode.BlocksMapUpdateInfo;
|
import org.apache.hadoop.hdfs.server.namenode.INode.BlocksMapUpdateInfo;
|
||||||
import org.apache.hadoop.hdfs.server.namenode.JournalSet.JournalAndStream;
|
import org.apache.hadoop.hdfs.server.namenode.JournalSet.JournalAndStream;
|
||||||
|
@ -2129,7 +2131,7 @@ public class FSNamesystem implements Namesystem, FSNamesystemMBean,
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
private HdfsFileStatus startFileInt(final String src,
|
private HdfsFileStatus startFileInt(String src,
|
||||||
PermissionStatus permissions, String holder, String clientMachine,
|
PermissionStatus permissions, String holder, String clientMachine,
|
||||||
EnumSet<CreateFlag> flag, boolean createParent, short replication,
|
EnumSet<CreateFlag> flag, boolean createParent, short replication,
|
||||||
long blockSize, CryptoProtocolVersion[] supportedVersions,
|
long blockSize, CryptoProtocolVersion[] supportedVersions,
|
||||||
|
@ -2148,7 +2150,11 @@ public class FSNamesystem implements Namesystem, FSNamesystemMBean,
|
||||||
.append(Arrays.toString(supportedVersions));
|
.append(Arrays.toString(supportedVersions));
|
||||||
NameNode.stateChangeLog.debug(builder.toString());
|
NameNode.stateChangeLog.debug(builder.toString());
|
||||||
}
|
}
|
||||||
if (!DFSUtil.isValidName(src)) {
|
if (!DFSUtil.isValidName(src) ||
|
||||||
|
FSDirectory.isExactReservedName(src) ||
|
||||||
|
(FSDirectory.isReservedName(src)
|
||||||
|
&& !FSDirectory.isReservedRawName(src)
|
||||||
|
&& !FSDirectory.isReservedInodesName(src))) {
|
||||||
throw new InvalidPathException(src);
|
throw new InvalidPathException(src);
|
||||||
}
|
}
|
||||||
blockManager.verifyReplication(src, replication, clientMachine);
|
blockManager.verifyReplication(src, replication, clientMachine);
|
||||||
|
@ -2159,69 +2165,60 @@ public class FSNamesystem implements Namesystem, FSNamesystemMBean,
|
||||||
}
|
}
|
||||||
|
|
||||||
FSPermissionChecker pc = getPermissionChecker();
|
FSPermissionChecker pc = getPermissionChecker();
|
||||||
|
INodesInPath iip = null;
|
||||||
/**
|
boolean skipSync = true; // until we do something that might create edits
|
||||||
* If the file is in an encryption zone, we optimistically create an
|
|
||||||
* EDEK for the file by calling out to the configured KeyProvider.
|
|
||||||
* Since this typically involves doing an RPC, we take the readLock
|
|
||||||
* initially, then drop it to do the RPC.
|
|
||||||
*
|
|
||||||
* Since the path can flip-flop between being in an encryption zone and not
|
|
||||||
* in the meantime, we need to recheck the preconditions when we retake the
|
|
||||||
* lock to do the create. If the preconditions are not met, we throw a
|
|
||||||
* special RetryStartFileException to ask the DFSClient to try the create
|
|
||||||
* again later.
|
|
||||||
*/
|
|
||||||
FSDirWriteFileOp.EncryptionKeyInfo ezInfo = null;
|
|
||||||
|
|
||||||
if (provider != null) {
|
|
||||||
readLock();
|
|
||||||
try {
|
|
||||||
checkOperation(OperationCategory.READ);
|
|
||||||
ezInfo = FSDirWriteFileOp
|
|
||||||
.getEncryptionKeyInfo(this, pc, src, supportedVersions);
|
|
||||||
} finally {
|
|
||||||
readUnlock();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Generate EDEK if necessary while not holding the lock
|
|
||||||
if (ezInfo != null) {
|
|
||||||
ezInfo.edek = FSDirEncryptionZoneOp
|
|
||||||
.generateEncryptedDataEncryptionKey(dir, ezInfo.ezKeyName);
|
|
||||||
}
|
|
||||||
EncryptionFaultInjector.getInstance().startFileAfterGenerateKey();
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean skipSync = false;
|
|
||||||
HdfsFileStatus stat = null;
|
HdfsFileStatus stat = null;
|
||||||
|
BlocksMapUpdateInfo toRemoveBlocks = null;
|
||||||
|
|
||||||
// Proceed with the create, using the computed cipher suite and
|
checkOperation(OperationCategory.WRITE);
|
||||||
// generated EDEK
|
|
||||||
BlocksMapUpdateInfo toRemoveBlocks = new BlocksMapUpdateInfo();
|
|
||||||
writeLock();
|
writeLock();
|
||||||
try {
|
try {
|
||||||
checkOperation(OperationCategory.WRITE);
|
checkOperation(OperationCategory.WRITE);
|
||||||
checkNameNodeSafeMode("Cannot create file" + src);
|
checkNameNodeSafeMode("Cannot create file" + src);
|
||||||
|
|
||||||
|
iip = FSDirWriteFileOp.resolvePathForStartFile(
|
||||||
|
dir, pc, src, flag, createParent);
|
||||||
|
|
||||||
|
FileEncryptionInfo feInfo = null;
|
||||||
|
if (provider != null) {
|
||||||
|
EncryptionKeyInfo ezInfo = FSDirEncryptionZoneOp.getEncryptionKeyInfo(
|
||||||
|
this, iip, supportedVersions);
|
||||||
|
// if the path has an encryption zone, the lock was released while
|
||||||
|
// generating the EDEK. re-resolve the path to ensure the namesystem
|
||||||
|
// and/or EZ has not mutated
|
||||||
|
if (ezInfo != null) {
|
||||||
|
checkOperation(OperationCategory.WRITE);
|
||||||
|
iip = FSDirWriteFileOp.resolvePathForStartFile(
|
||||||
|
dir, pc, iip.getPath(), flag, createParent);
|
||||||
|
feInfo = FSDirEncryptionZoneOp.getFileEncryptionInfo(
|
||||||
|
dir, iip, ezInfo);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
skipSync = false; // following might generate edits
|
||||||
|
toRemoveBlocks = new BlocksMapUpdateInfo();
|
||||||
dir.writeLock();
|
dir.writeLock();
|
||||||
try {
|
try {
|
||||||
stat = FSDirWriteFileOp.startFile(this, pc, src, permissions, holder,
|
stat = FSDirWriteFileOp.startFile(this, iip, permissions, holder,
|
||||||
clientMachine, flag, createParent,
|
clientMachine, flag, createParent,
|
||||||
replication, blockSize, ezInfo,
|
replication, blockSize, feInfo,
|
||||||
toRemoveBlocks, logRetryCache);
|
toRemoveBlocks, logRetryCache);
|
||||||
|
} catch (IOException e) {
|
||||||
|
skipSync = e instanceof StandbyException;
|
||||||
|
throw e;
|
||||||
} finally {
|
} finally {
|
||||||
dir.writeUnlock();
|
dir.writeUnlock();
|
||||||
}
|
}
|
||||||
} catch (IOException e) {
|
|
||||||
skipSync = e instanceof StandbyException;
|
|
||||||
throw e;
|
|
||||||
} finally {
|
} finally {
|
||||||
writeUnlock();
|
writeUnlock();
|
||||||
// There might be transactions logged while trying to recover the lease.
|
// There might be transactions logged while trying to recover the lease.
|
||||||
// They need to be sync'ed even when an exception was thrown.
|
// They need to be sync'ed even when an exception was thrown.
|
||||||
if (!skipSync) {
|
if (!skipSync) {
|
||||||
getEditLog().logSync();
|
getEditLog().logSync();
|
||||||
removeBlocks(toRemoveBlocks);
|
if (toRemoveBlocks != null) {
|
||||||
toRemoveBlocks.clear();
|
removeBlocks(toRemoveBlocks);
|
||||||
|
toRemoveBlocks.clear();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -51,9 +51,10 @@ public class XAttrStorage {
|
||||||
* @param prefixedName xAttr name with prefix
|
* @param prefixedName xAttr name with prefix
|
||||||
* @return the xAttr
|
* @return the xAttr
|
||||||
*/
|
*/
|
||||||
public static XAttr readINodeXAttrByPrefixedName(INode inode,
|
public static XAttr readINodeXAttrByPrefixedName(INodesInPath iip,
|
||||||
int snapshotId, String prefixedName) {
|
String prefixedName) {
|
||||||
XAttrFeature f = inode.getXAttrFeature(snapshotId);
|
XAttrFeature f =
|
||||||
|
iip.getLastINode().getXAttrFeature(iip.getPathSnapshotId());
|
||||||
return f == null ? null : f.getXAttr(prefixedName);
|
return f == null ? null : f.getXAttr(prefixedName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -19,7 +19,6 @@ package org.apache.hadoop.hdfs;
|
||||||
|
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileNotFoundException;
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.PrintStream;
|
import java.io.PrintStream;
|
||||||
import java.io.RandomAccessFile;
|
import java.io.RandomAccessFile;
|
||||||
|
@ -1048,7 +1047,7 @@ public class TestEncryptionZones {
|
||||||
}
|
}
|
||||||
|
|
||||||
private class MyInjector extends EncryptionFaultInjector {
|
private class MyInjector extends EncryptionFaultInjector {
|
||||||
int generateCount;
|
volatile int generateCount;
|
||||||
CountDownLatch ready;
|
CountDownLatch ready;
|
||||||
CountDownLatch wait;
|
CountDownLatch wait;
|
||||||
|
|
||||||
|
@ -1058,13 +1057,27 @@ public class TestEncryptionZones {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void startFileAfterGenerateKey() throws IOException {
|
public void startFileNoKey() throws IOException {
|
||||||
|
generateCount = -1;
|
||||||
|
syncWithLatches();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void startFileBeforeGenerateKey() throws IOException {
|
||||||
|
syncWithLatches();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void syncWithLatches() throws IOException {
|
||||||
ready.countDown();
|
ready.countDown();
|
||||||
try {
|
try {
|
||||||
wait.await();
|
wait.await();
|
||||||
} catch (InterruptedException e) {
|
} catch (InterruptedException e) {
|
||||||
throw new IOException(e);
|
throw new IOException(e);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void startFileAfterGenerateKey() throws IOException {
|
||||||
generateCount++;
|
generateCount++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1100,10 +1113,14 @@ public class TestEncryptionZones {
|
||||||
Future<Void> future =
|
Future<Void> future =
|
||||||
executor.submit(new CreateFileTask(fsWrapper, file));
|
executor.submit(new CreateFileTask(fsWrapper, file));
|
||||||
injector.ready.await();
|
injector.ready.await();
|
||||||
// Do the fault
|
try {
|
||||||
doFault();
|
// Do the fault
|
||||||
// Allow create to proceed
|
doFault();
|
||||||
injector.wait.countDown();
|
// Allow create to proceed
|
||||||
|
} finally {
|
||||||
|
// Always decrement latch to avoid hanging the tests on failure.
|
||||||
|
injector.wait.countDown();
|
||||||
|
}
|
||||||
future.get();
|
future.get();
|
||||||
// Cleanup and postconditions
|
// Cleanup and postconditions
|
||||||
doCleanup();
|
doCleanup();
|
||||||
|
@ -1126,20 +1143,21 @@ public class TestEncryptionZones {
|
||||||
fsWrapper.mkdir(zone1, FsPermission.getDirDefault(), true);
|
fsWrapper.mkdir(zone1, FsPermission.getDirDefault(), true);
|
||||||
ExecutorService executor = Executors.newSingleThreadExecutor();
|
ExecutorService executor = Executors.newSingleThreadExecutor();
|
||||||
|
|
||||||
// Test when the parent directory becomes an EZ
|
// Test when the parent directory becomes an EZ. With no initial EZ,
|
||||||
|
// the fsn lock must not be yielded.
|
||||||
executor.submit(new InjectFaultTask() {
|
executor.submit(new InjectFaultTask() {
|
||||||
@Override
|
|
||||||
public void doFault() throws Exception {
|
|
||||||
dfsAdmin.createEncryptionZone(zone1, TEST_KEY, NO_TRASH);
|
|
||||||
}
|
|
||||||
@Override
|
@Override
|
||||||
public void doCleanup() throws Exception {
|
public void doCleanup() throws Exception {
|
||||||
assertEquals("Expected a startFile retry", 2, injector.generateCount);
|
assertEquals("Expected no startFile key generation",
|
||||||
|
-1, injector.generateCount);
|
||||||
fsWrapper.delete(file, false);
|
fsWrapper.delete(file, false);
|
||||||
}
|
}
|
||||||
}).get();
|
}).get();
|
||||||
|
|
||||||
// Test when the parent directory unbecomes an EZ
|
// Test when the parent directory unbecomes an EZ. The generation of
|
||||||
|
// the EDEK will yield the lock, then re-resolve the path and use the
|
||||||
|
// previous EDEK.
|
||||||
|
dfsAdmin.createEncryptionZone(zone1, TEST_KEY, NO_TRASH);
|
||||||
executor.submit(new InjectFaultTask() {
|
executor.submit(new InjectFaultTask() {
|
||||||
@Override
|
@Override
|
||||||
public void doFault() throws Exception {
|
public void doFault() throws Exception {
|
||||||
|
@ -1152,7 +1170,9 @@ public class TestEncryptionZones {
|
||||||
}
|
}
|
||||||
}).get();
|
}).get();
|
||||||
|
|
||||||
// Test when the parent directory becomes a different EZ
|
// Test when the parent directory becomes a different EZ. The generation
|
||||||
|
// of the EDEK will yield the lock, re-resolve will detect the EZ has
|
||||||
|
// changed, and client will be asked to retry a 2nd time
|
||||||
fsWrapper.mkdir(zone1, FsPermission.getDirDefault(), true);
|
fsWrapper.mkdir(zone1, FsPermission.getDirDefault(), true);
|
||||||
final String otherKey = "other_key";
|
final String otherKey = "other_key";
|
||||||
DFSTestUtil.createKey(otherKey, cluster, conf);
|
DFSTestUtil.createKey(otherKey, cluster, conf);
|
||||||
|
|
Loading…
Reference in New Issue