HDFS-6951. Correctly persist raw namespace xattrs to edit log and fsimage. Contributed by Charles Lamb.

(cherry picked from commit 04915a0814)
This commit is contained in:
Andrew Wang 2014-09-08 16:59:30 -07:00
parent bdcf5e940f
commit dc4da242f2
9 changed files with 93 additions and 35 deletions

View File

@ -111,6 +111,18 @@ public class EncryptionZoneManager {
*/ */
void addEncryptionZone(Long inodeId, String keyName) { void addEncryptionZone(Long inodeId, String keyName) {
assert dir.hasWriteLock(); assert dir.hasWriteLock();
unprotectedAddEncryptionZone(inodeId, keyName);
}
/**
* Add a new encryption zone.
* <p/>
* Does not assume that the FSDirectory lock is held.
*
* @param inodeId of the encryption zone
* @param keyName encryption zone key name
*/
void unprotectedAddEncryptionZone(Long inodeId, String keyName) {
final EncryptionZoneInt ez = new EncryptionZoneInt(inodeId, keyName); final EncryptionZoneInt ez = new EncryptionZoneInt(inodeId, keyName);
encryptionZones.put(inodeId, ez); encryptionZones.put(inodeId, ez);
} }

View File

@ -2100,7 +2100,7 @@ public class FSDirectory implements Closeable {
for (XAttr xattr : xattrs) { for (XAttr xattr : xattrs) {
final String xaName = XAttrHelper.getPrefixName(xattr); final String xaName = XAttrHelper.getPrefixName(xattr);
if (CRYPTO_XATTR_ENCRYPTION_ZONE.equals(xaName)) { if (CRYPTO_XATTR_ENCRYPTION_ZONE.equals(xaName)) {
ezManager.addEncryptionZone(inode.getId(), ezManager.unprotectedAddEncryptionZone(inode.getId(),
new String(xattr.getValue())); new String(xattr.getValue()));
} }
} }

View File

@ -83,7 +83,12 @@ public final class FSImageFormatPBINode {
private static final int XATTR_NAMESPACE_OFFSET = 30; private static final int XATTR_NAMESPACE_OFFSET = 30;
private static final int XATTR_NAME_MASK = (1 << 24) - 1; private static final int XATTR_NAME_MASK = (1 << 24) - 1;
private static final int XATTR_NAME_OFFSET = 6; private static final int XATTR_NAME_OFFSET = 6;
private static final XAttr.NameSpace[] XATTR_NAMESPACE_VALUES =
/* See the comments in fsimage.proto for an explanation of the following. */
private static final int XATTR_NAMESPACE_EXT_OFFSET = 5;
private static final int XATTR_NAMESPACE_EXT_MASK = 1;
private static final XAttr.NameSpace[] XATTR_NAMESPACE_VALUES =
XAttr.NameSpace.values(); XAttr.NameSpace.values();
@ -121,6 +126,8 @@ public final class FSImageFormatPBINode {
int v = xAttrCompactProto.getName(); int v = xAttrCompactProto.getName();
int nid = (v >> XATTR_NAME_OFFSET) & XATTR_NAME_MASK; int nid = (v >> XATTR_NAME_OFFSET) & XATTR_NAME_MASK;
int ns = (v >> XATTR_NAMESPACE_OFFSET) & XATTR_NAMESPACE_MASK; int ns = (v >> XATTR_NAMESPACE_OFFSET) & XATTR_NAMESPACE_MASK;
ns |=
((v >> XATTR_NAMESPACE_EXT_OFFSET) & XATTR_NAMESPACE_EXT_MASK) << 2;
String name = stringTable[nid]; String name = stringTable[nid];
byte[] value = null; byte[] value = null;
if (xAttrCompactProto.getValue() != null) { if (xAttrCompactProto.getValue() != null) {
@ -367,10 +374,13 @@ public final class FSImageFormatPBINode {
for (XAttr a : f.getXAttrs()) { for (XAttr a : f.getXAttrs()) {
XAttrCompactProto.Builder xAttrCompactBuilder = XAttrCompactProto. XAttrCompactProto.Builder xAttrCompactBuilder = XAttrCompactProto.
newBuilder(); newBuilder();
int v = ((a.getNameSpace().ordinal() & XATTR_NAMESPACE_MASK) << int nsOrd = a.getNameSpace().ordinal();
XATTR_NAMESPACE_OFFSET) Preconditions.checkArgument(nsOrd < 8, "Too many namespaces.");
| ((stringMap.getId(a.getName()) & XATTR_NAME_MASK) << int v = ((nsOrd & XATTR_NAMESPACE_MASK) << XATTR_NAMESPACE_OFFSET)
| ((stringMap.getId(a.getName()) & XATTR_NAME_MASK) <<
XATTR_NAME_OFFSET); XATTR_NAME_OFFSET);
v |= (((nsOrd >> 2) & XATTR_NAMESPACE_EXT_MASK) <<
XATTR_NAMESPACE_EXT_OFFSET);
xAttrCompactBuilder.setName(v); xAttrCompactBuilder.setName(v);
if (a.getValue() != null) { if (a.getValue() != null) {
xAttrCompactBuilder.setValue(PBHelper.getByteString(a.getValue())); xAttrCompactBuilder.setValue(PBHelper.getByteString(a.getValue()));

View File

@ -67,7 +67,8 @@ public class NameNodeLayoutVersion {
EDITLOG_LENGTH(-56, "Add length field to every edit log op"), EDITLOG_LENGTH(-56, "Add length field to every edit log op"),
XATTRS(-57, "Extended attributes"), XATTRS(-57, "Extended attributes"),
CREATE_OVERWRITE(-58, "Use single editlog record for " + CREATE_OVERWRITE(-58, "Use single editlog record for " +
"creating file with overwrite"); "creating file with overwrite"),
XATTRS_NAMESPACE_EXT(-59, "Increase number of xattr namespaces");
private final FeatureInfo info; private final FeatureInfo info;

View File

@ -113,8 +113,12 @@ message INodeSection {
* *
* [0:2) -- the namespace of XAttr (XAttrNamespaceProto) * [0:2) -- the namespace of XAttr (XAttrNamespaceProto)
* [2:26) -- the name of the entry, which is an ID that points to a * [2:26) -- the name of the entry, which is an ID that points to a
* string in the StringTableSection. * string in the StringTableSection.
* [26:32) -- reserved for future uses. * [26:27) -- namespace extension. Originally there were only 4 namespaces
* so only 2 bits were needed. At that time, this bit was reserved. When a
* 5th namespace was created (raw) this bit became used as a 3rd namespace
* bit.
* [27:32) -- reserved for future uses.
*/ */
required fixed32 name = 1; required fixed32 name = 1;
optional bytes value = 2; optional bytes value = 2;

View File

@ -49,6 +49,7 @@ import org.apache.hadoop.fs.permission.FsPermission;
import org.apache.hadoop.hdfs.client.HdfsAdmin; import org.apache.hadoop.hdfs.client.HdfsAdmin;
import org.apache.hadoop.hdfs.protocol.EncryptionZone; import org.apache.hadoop.hdfs.protocol.EncryptionZone;
import org.apache.hadoop.hdfs.protocol.LocatedBlocks; import org.apache.hadoop.hdfs.protocol.LocatedBlocks;
import org.apache.hadoop.hdfs.protocol.HdfsConstants.SafeModeAction;
import org.apache.hadoop.hdfs.server.namenode.EncryptionFaultInjector; import org.apache.hadoop.hdfs.server.namenode.EncryptionFaultInjector;
import org.apache.hadoop.hdfs.server.namenode.EncryptionZoneManager; import org.apache.hadoop.hdfs.server.namenode.EncryptionZoneManager;
import org.apache.hadoop.security.AccessControlException; import org.apache.hadoop.security.AccessControlException;
@ -314,6 +315,13 @@ public class TestEncryptionZones {
assertNumZones(numZones); assertNumZones(numZones);
assertZonePresent(null, zonePath.toString()); assertZonePresent(null, zonePath.toString());
} }
fs.setSafeMode(SafeModeAction.SAFEMODE_ENTER);
fs.saveNamespace();
fs.setSafeMode(SafeModeAction.SAFEMODE_LEAVE);
cluster.restartNameNode(true);
assertNumZones(numZones);
assertZonePresent(null, zone1.toString());
} }
/** /**

View File

@ -56,6 +56,7 @@ import org.junit.BeforeClass;
import org.junit.Test; import org.junit.Test;
import com.google.common.collect.Lists; import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
/** /**
* Tests NameNode interaction for all XAttr APIs. * Tests NameNode interaction for all XAttr APIs.
@ -129,51 +130,73 @@ public class FSXAttrBaseTest {
*/ */
@Test(timeout = 120000) @Test(timeout = 120000)
public void testCreateXAttr() throws Exception { public void testCreateXAttr() throws Exception {
FileSystem.mkdirs(fs, path, FsPermission.createImmutable((short)0750)); Map<String, byte[]> expectedXAttrs = Maps.newHashMap();
fs.setXAttr(path, name1, value1, EnumSet.of(XAttrSetFlag.CREATE)); expectedXAttrs.put(name1, value1);
expectedXAttrs.put(name2, null);
doTestCreateXAttr(path, expectedXAttrs);
expectedXAttrs.put(raw1, value1);
doTestCreateXAttr(rawPath, expectedXAttrs);
}
private void doTestCreateXAttr(Path usePath, Map<String,
byte[]> expectedXAttrs) throws Exception {
FileSystem.mkdirs(fs, usePath, FsPermission.createImmutable((short)0750));
fs.setXAttr(usePath, name1, value1, EnumSet.of(XAttrSetFlag.CREATE));
Map<String, byte[]> xattrs = fs.getXAttrs(path); Map<String, byte[]> xattrs = fs.getXAttrs(usePath);
Assert.assertEquals(xattrs.size(), 1); Assert.assertEquals(xattrs.size(), 1);
Assert.assertArrayEquals(value1, xattrs.get(name1)); Assert.assertArrayEquals(value1, xattrs.get(name1));
fs.removeXAttr(path, name1); fs.removeXAttr(usePath, name1);
xattrs = fs.getXAttrs(path); xattrs = fs.getXAttrs(usePath);
Assert.assertEquals(xattrs.size(), 0); Assert.assertEquals(xattrs.size(), 0);
// Create xattr which already exists. // Create xattr which already exists.
fs.setXAttr(path, name1, value1, EnumSet.of(XAttrSetFlag.CREATE)); fs.setXAttr(usePath, name1, value1, EnumSet.of(XAttrSetFlag.CREATE));
try { try {
fs.setXAttr(path, name1, value1, EnumSet.of(XAttrSetFlag.CREATE)); fs.setXAttr(usePath, name1, value1, EnumSet.of(XAttrSetFlag.CREATE));
Assert.fail("Creating xattr which already exists should fail."); Assert.fail("Creating xattr which already exists should fail.");
} catch (IOException e) { } catch (IOException e) {
} }
fs.removeXAttr(path, name1); fs.removeXAttr(usePath, name1);
// Create two xattrs // Create the xattrs
fs.setXAttr(path, name1, value1, EnumSet.of(XAttrSetFlag.CREATE)); for (Map.Entry<String, byte[]> ent : expectedXAttrs.entrySet()) {
fs.setXAttr(path, name2, null, EnumSet.of(XAttrSetFlag.CREATE)); fs.setXAttr(usePath, ent.getKey(), ent.getValue(),
xattrs = fs.getXAttrs(path); EnumSet.of(XAttrSetFlag.CREATE));
Assert.assertEquals(xattrs.size(), 2); }
Assert.assertArrayEquals(value1, xattrs.get(name1)); xattrs = fs.getXAttrs(usePath);
Assert.assertArrayEquals(new byte[0], xattrs.get(name2)); Assert.assertEquals(xattrs.size(), expectedXAttrs.size());
for (Map.Entry<String, byte[]> ent : expectedXAttrs.entrySet()) {
final byte[] val =
(ent.getValue() == null) ? new byte[0] : ent.getValue();
Assert.assertArrayEquals(val, xattrs.get(ent.getKey()));
}
restart(false); restart(false);
initFileSystem(); initFileSystem();
xattrs = fs.getXAttrs(path); xattrs = fs.getXAttrs(usePath);
Assert.assertEquals(xattrs.size(), 2); Assert.assertEquals(xattrs.size(), expectedXAttrs.size());
Assert.assertArrayEquals(value1, xattrs.get(name1)); for (Map.Entry<String, byte[]> ent : expectedXAttrs.entrySet()) {
Assert.assertArrayEquals(new byte[0], xattrs.get(name2)); final byte[] val =
(ent.getValue() == null) ? new byte[0] : ent.getValue();
Assert.assertArrayEquals(val, xattrs.get(ent.getKey()));
}
restart(true); restart(true);
initFileSystem(); initFileSystem();
xattrs = fs.getXAttrs(path); xattrs = fs.getXAttrs(usePath);
Assert.assertEquals(xattrs.size(), 2); Assert.assertEquals(xattrs.size(), expectedXAttrs.size());
Assert.assertArrayEquals(value1, xattrs.get(name1)); for (Map.Entry<String, byte[]> ent : expectedXAttrs.entrySet()) {
Assert.assertArrayEquals(new byte[0], xattrs.get(name2)); final byte[] val =
(ent.getValue() == null) ? new byte[0] : ent.getValue();
fs.removeXAttr(path, name1); Assert.assertArrayEquals(val, xattrs.get(ent.getKey()));
fs.removeXAttr(path, name2); }
for (Map.Entry<String, byte[]> ent : expectedXAttrs.entrySet()) {
fs.removeXAttr(usePath, ent.getKey());
}
} }
/** /**

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<EDITS> <EDITS>
<EDITS_VERSION>-58</EDITS_VERSION> <EDITS_VERSION>-59</EDITS_VERSION>
<RECORD> <RECORD>
<OPCODE>OP_START_LOG_SEGMENT</OPCODE> <OPCODE>OP_START_LOG_SEGMENT</OPCODE>
<DATA> <DATA>