Skip to content

Commit b60ca37

Browse files
committed
Fix potential FSImage corruption. Contributed by Daryn Sharp.
1 parent 8e5365e commit b60ca37

File tree

15 files changed

+383
-251
lines changed

15 files changed

+383
-251
lines changed

hadoop-hdfs-project/hadoop-hdfs-client/src/main/java/org/apache/hadoop/hdfs/util/LongBitFormat.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,10 @@
2626
public class LongBitFormat implements Serializable {
2727
private static final long serialVersionUID = 1L;
2828

29+
public interface Enum {
30+
int getLength();
31+
}
32+
2933
private final String NAME;
3034
/** Bit offset */
3135
private final int OFFSET;
@@ -69,4 +73,8 @@ public long combine(long value, long record) {
6973
public long getMin() {
7074
return MIN;
7175
}
76+
77+
public int getLength() {
78+
return LENGTH;
79+
}
7280
}

hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/namenode/AclEntryStatusFormat.java

Lines changed: 55 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -31,25 +31,23 @@
3131
/**
3232
* Class to pack an AclEntry into an integer. <br>
3333
* An ACL entry is represented by a 32-bit integer in Big Endian format. <br>
34-
* The bits can be divided in four segments: <br>
35-
* [0:1) || [1:3) || [3:6) || [6:7) || [7:32) <br>
36-
* <br>
37-
* [0:1) -- the scope of the entry (AclEntryScope) <br>
38-
* [1:3) -- the type of the entry (AclEntryType) <br>
39-
* [3:6) -- the permission of the entry (FsAction) <br>
40-
* [6:7) -- A flag to indicate whether Named entry or not <br>
41-
* [7:8) -- Reserved <br>
42-
* [8:32) -- the name of the entry, which is an ID that points to a <br>
43-
* string in the StringTableSection. <br>
34+
*
35+
* Note: this format is used both in-memory and on-disk. Changes will be
36+
* incompatible.
37+
*
4438
*/
45-
public enum AclEntryStatusFormat {
39+
public enum AclEntryStatusFormat implements LongBitFormat.Enum {
40+
41+
PERMISSION(null, 3),
42+
TYPE(PERMISSION.BITS, 2),
43+
SCOPE(TYPE.BITS, 1),
44+
NAME(SCOPE.BITS, 24);
4645

47-
SCOPE(null, 1),
48-
TYPE(SCOPE.BITS, 2),
49-
PERMISSION(TYPE.BITS, 3),
50-
NAMED_ENTRY_CHECK(PERMISSION.BITS, 1),
51-
RESERVED(NAMED_ENTRY_CHECK.BITS, 1),
52-
NAME(RESERVED.BITS, 24);
46+
private static final FsAction[] FSACTION_VALUES = FsAction.values();
47+
private static final AclEntryScope[] ACL_ENTRY_SCOPE_VALUES =
48+
AclEntryScope.values();
49+
private static final AclEntryType[] ACL_ENTRY_TYPE_VALUES =
50+
AclEntryType.values();
5351

5452
private final LongBitFormat BITS;
5553

@@ -59,30 +57,29 @@ private AclEntryStatusFormat(LongBitFormat previous, int length) {
5957

6058
static AclEntryScope getScope(int aclEntry) {
6159
int ordinal = (int) SCOPE.BITS.retrieve(aclEntry);
62-
return AclEntryScope.values()[ordinal];
60+
return ACL_ENTRY_SCOPE_VALUES[ordinal];
6361
}
6462

6563
static AclEntryType getType(int aclEntry) {
6664
int ordinal = (int) TYPE.BITS.retrieve(aclEntry);
67-
return AclEntryType.values()[ordinal];
65+
return ACL_ENTRY_TYPE_VALUES[ordinal];
6866
}
6967

7068
static FsAction getPermission(int aclEntry) {
7169
int ordinal = (int) PERMISSION.BITS.retrieve(aclEntry);
72-
return FsAction.values()[ordinal];
70+
return FSACTION_VALUES[ordinal];
7371
}
7472

7573
static String getName(int aclEntry) {
76-
int nameExists = (int) NAMED_ENTRY_CHECK.BITS.retrieve(aclEntry);
77-
if (nameExists == 0) {
78-
return null;
79-
}
80-
int id = (int) NAME.BITS.retrieve(aclEntry);
81-
AclEntryType type = getType(aclEntry);
82-
if (type == AclEntryType.USER) {
83-
return SerialNumberManager.INSTANCE.getUser(id);
84-
} else if (type == AclEntryType.GROUP) {
85-
return SerialNumberManager.INSTANCE.getGroup(id);
74+
return getName(aclEntry, null);
75+
}
76+
77+
static String getName(int aclEntry,
78+
SerialNumberManager.StringTable stringTable) {
79+
SerialNumberManager snm = getSerialNumberManager(getType(aclEntry));
80+
if (snm != null) {
81+
int nid = (int)NAME.BITS.retrieve(aclEntry);
82+
return snm.getString(nid, stringTable);
8683
}
8784
return null;
8885
}
@@ -94,29 +91,26 @@ static int toInt(AclEntry aclEntry) {
9491
aclEntryInt = TYPE.BITS.combine(aclEntry.getType().ordinal(), aclEntryInt);
9592
aclEntryInt = PERMISSION.BITS.combine(aclEntry.getPermission().ordinal(),
9693
aclEntryInt);
97-
if (aclEntry.getName() != null) {
98-
aclEntryInt = NAMED_ENTRY_CHECK.BITS.combine(1, aclEntryInt);
99-
if (aclEntry.getType() == AclEntryType.USER) {
100-
int userId = SerialNumberManager.INSTANCE.getUserSerialNumber(aclEntry
101-
.getName());
102-
aclEntryInt = NAME.BITS.combine(userId, aclEntryInt);
103-
} else if (aclEntry.getType() == AclEntryType.GROUP) {
104-
int groupId = SerialNumberManager.INSTANCE
105-
.getGroupSerialNumber(aclEntry.getName());
106-
aclEntryInt = NAME.BITS.combine(groupId, aclEntryInt);
107-
}
94+
SerialNumberManager snm = getSerialNumberManager(aclEntry.getType());
95+
if (snm != null) {
96+
int nid = snm.getSerialNumber(aclEntry.getName());
97+
aclEntryInt = NAME.BITS.combine(nid, aclEntryInt);
10898
}
10999
return (int) aclEntryInt;
110100
}
111101

112102
static AclEntry toAclEntry(int aclEntry) {
113-
AclEntry.Builder builder = new AclEntry.Builder();
114-
builder.setScope(getScope(aclEntry)).setType(getType(aclEntry))
115-
.setPermission(getPermission(aclEntry));
116-
if (getName(aclEntry) != null) {
117-
builder.setName(getName(aclEntry));
118-
}
119-
return builder.build();
103+
return toAclEntry(aclEntry, null);
104+
}
105+
106+
static AclEntry toAclEntry(int aclEntry,
107+
SerialNumberManager.StringTable stringTable) {
108+
return new AclEntry.Builder()
109+
.setScope(getScope(aclEntry))
110+
.setType(getType(aclEntry))
111+
.setPermission(getPermission(aclEntry))
112+
.setName(getName(aclEntry, stringTable))
113+
.build();
120114
}
121115

122116
public static int[] toInt(List<AclEntry> aclEntries) {
@@ -127,12 +121,19 @@ public static int[] toInt(List<AclEntry> aclEntries) {
127121
return entries;
128122
}
129123

130-
public static ImmutableList<AclEntry> toAclEntries(int[] entries) {
131-
ImmutableList.Builder<AclEntry> b = new ImmutableList.Builder<AclEntry>();
132-
for (int entry : entries) {
133-
AclEntry aclEntry = toAclEntry(entry);
134-
b.add(aclEntry);
124+
private static SerialNumberManager getSerialNumberManager(AclEntryType type) {
125+
switch (type) {
126+
case USER:
127+
return SerialNumberManager.USER;
128+
case GROUP:
129+
return SerialNumberManager.GROUP;
130+
default:
131+
return null;
135132
}
136-
return b.build();
133+
}
134+
135+
@Override
136+
public int getLength() {
137+
return BITS.getLength();
137138
}
138139
}

hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/namenode/FSImageFormatPBINode.java

Lines changed: 26 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -31,10 +31,6 @@
3131
import org.apache.hadoop.HadoopIllegalArgumentException;
3232
import org.apache.hadoop.classification.InterfaceAudience;
3333
import org.apache.hadoop.fs.permission.AclEntry;
34-
import org.apache.hadoop.fs.permission.AclEntryScope;
35-
import org.apache.hadoop.fs.permission.AclEntryType;
36-
import org.apache.hadoop.fs.permission.FsAction;
37-
import org.apache.hadoop.fs.permission.FsPermission;
3834
import org.apache.hadoop.fs.permission.PermissionStatus;
3935
import org.apache.hadoop.fs.StorageType;
4036
import org.apache.hadoop.fs.XAttr;
@@ -60,6 +56,8 @@
6056
import org.apache.hadoop.hdfs.server.namenode.FsImageProto.INodeSection.XAttrFeatureProto;
6157
import org.apache.hadoop.hdfs.server.namenode.FsImageProto.INodeSection.QuotaByStorageTypeEntryProto;
6258
import org.apache.hadoop.hdfs.server.namenode.FsImageProto.INodeSection.QuotaByStorageTypeFeatureProto;
59+
import org.apache.hadoop.hdfs.server.namenode.INodeWithAdditionalFields.PermissionStatusFormat;
60+
import org.apache.hadoop.hdfs.server.namenode.SerialNumberManager.StringTable;
6361
import org.apache.hadoop.hdfs.server.namenode.snapshot.Snapshot;
6462
import org.apache.hadoop.hdfs.server.namenode.startupprogress.Phase;
6563
import org.apache.hadoop.hdfs.server.namenode.startupprogress.StartupProgress;
@@ -74,22 +72,11 @@
7472

7573
@InterfaceAudience.Private
7674
public final class FSImageFormatPBINode {
77-
private final static long USER_GROUP_STRID_MASK = (1 << 24) - 1;
78-
private final static int USER_STRID_OFFSET = 40;
79-
private final static int GROUP_STRID_OFFSET = 16;
80-
8175
public static final int ACL_ENTRY_NAME_MASK = (1 << 24) - 1;
8276
public static final int ACL_ENTRY_NAME_OFFSET = 6;
8377
public static final int ACL_ENTRY_TYPE_OFFSET = 3;
8478
public static final int ACL_ENTRY_SCOPE_OFFSET = 5;
8579
public static final int ACL_ENTRY_PERM_MASK = 7;
86-
private static final int ACL_ENTRY_TYPE_MASK = 3;
87-
private static final int ACL_ENTRY_SCOPE_MASK = 1;
88-
private static final FsAction[] FSACTION_VALUES = FsAction.values();
89-
private static final AclEntryScope[] ACL_ENTRY_SCOPE_VALUES = AclEntryScope
90-
.values();
91-
private static final AclEntryType[] ACL_ENTRY_TYPE_VALUES = AclEntryType
92-
.values();
9380

9481
public static final int XATTR_NAMESPACE_MASK = 3;
9582
public static final int XATTR_NAMESPACE_OFFSET = 30;
@@ -100,56 +87,36 @@ public final class FSImageFormatPBINode {
10087
public static final int XATTR_NAMESPACE_EXT_OFFSET = 5;
10188
public static final int XATTR_NAMESPACE_EXT_MASK = 1;
10289

103-
private static final XAttr.NameSpace[] XATTR_NAMESPACE_VALUES =
104-
XAttr.NameSpace.values();
105-
106-
10790
private static final Logger LOG =
10891
LoggerFactory.getLogger(FSImageFormatPBINode.class);
10992

93+
// the loader must decode all fields referencing serial number based fields
94+
// via to<Item> methods with the string table.
11095
public final static class Loader {
11196
public static PermissionStatus loadPermission(long id,
112-
final String[] stringTable) {
113-
short perm = (short) (id & ((1 << GROUP_STRID_OFFSET) - 1));
114-
int gsid = (int) ((id >> GROUP_STRID_OFFSET) & USER_GROUP_STRID_MASK);
115-
int usid = (int) ((id >> USER_STRID_OFFSET) & USER_GROUP_STRID_MASK);
116-
return new PermissionStatus(stringTable[usid], stringTable[gsid],
117-
new FsPermission(perm));
97+
final StringTable stringTable) {
98+
return PermissionStatusFormat.toPermissionStatus(id, stringTable);
11899
}
119100

120101
public static ImmutableList<AclEntry> loadAclEntries(
121-
AclFeatureProto proto, final String[] stringTable) {
102+
AclFeatureProto proto, final StringTable stringTable) {
122103
ImmutableList.Builder<AclEntry> b = ImmutableList.builder();
123104
for (int v : proto.getEntriesList()) {
124-
int p = v & ACL_ENTRY_PERM_MASK;
125-
int t = (v >> ACL_ENTRY_TYPE_OFFSET) & ACL_ENTRY_TYPE_MASK;
126-
int s = (v >> ACL_ENTRY_SCOPE_OFFSET) & ACL_ENTRY_SCOPE_MASK;
127-
int nid = (v >> ACL_ENTRY_NAME_OFFSET) & ACL_ENTRY_NAME_MASK;
128-
String name = stringTable[nid];
129-
b.add(new AclEntry.Builder().setName(name)
130-
.setPermission(FSACTION_VALUES[p])
131-
.setScope(ACL_ENTRY_SCOPE_VALUES[s])
132-
.setType(ACL_ENTRY_TYPE_VALUES[t]).build());
105+
b.add(AclEntryStatusFormat.toAclEntry(v, stringTable));
133106
}
134107
return b.build();
135108
}
136109

137110
public static List<XAttr> loadXAttrs(
138-
XAttrFeatureProto proto, final String[] stringTable) {
111+
XAttrFeatureProto proto, final StringTable stringTable) {
139112
List<XAttr> b = new ArrayList<>();
140113
for (XAttrCompactProto xAttrCompactProto : proto.getXAttrsList()) {
141114
int v = xAttrCompactProto.getName();
142-
int nid = (v >> XATTR_NAME_OFFSET) & XATTR_NAME_MASK;
143-
int ns = (v >> XATTR_NAMESPACE_OFFSET) & XATTR_NAMESPACE_MASK;
144-
ns |=
145-
((v >> XATTR_NAMESPACE_EXT_OFFSET) & XATTR_NAMESPACE_EXT_MASK) << 2;
146-
String name = stringTable[nid];
147115
byte[] value = null;
148116
if (xAttrCompactProto.getValue() != null) {
149117
value = xAttrCompactProto.getValue().toByteArray();
150118
}
151-
b.add(new XAttr.Builder().setNameSpace(XATTR_NAMESPACE_VALUES[ns])
152-
.setName(name).setValue(value).build());
119+
b.add(XAttrFormat.toXAttr(v, value, stringTable));
153120
}
154121

155122
return b;
@@ -439,46 +406,30 @@ private void loadRootINode(INodeSection.INode p) {
439406
}
440407
}
441408

409+
// the saver can directly write out fields referencing serial numbers.
410+
// the serial number maps will be compacted when loading.
442411
public final static class Saver {
443412
private long numImageErrors;
444413

445-
private static long buildPermissionStatus(INodeAttributes n,
446-
final SaverContext.DeduplicationMap<String> stringMap) {
447-
long userId = stringMap.getId(n.getUserName());
448-
long groupId = stringMap.getId(n.getGroupName());
449-
return ((userId & USER_GROUP_STRID_MASK) << USER_STRID_OFFSET)
450-
| ((groupId & USER_GROUP_STRID_MASK) << GROUP_STRID_OFFSET)
451-
| n.getFsPermissionShort();
414+
private static long buildPermissionStatus(INodeAttributes n) {
415+
return n.getPermissionLong();
452416
}
453417

454-
private static AclFeatureProto.Builder buildAclEntries(AclFeature f,
455-
final SaverContext.DeduplicationMap<String> map) {
418+
private static AclFeatureProto.Builder buildAclEntries(AclFeature f) {
456419
AclFeatureProto.Builder b = AclFeatureProto.newBuilder();
457420
for (int pos = 0, e; pos < f.getEntriesSize(); pos++) {
458421
e = f.getEntryAt(pos);
459-
int nameId = map.getId(AclEntryStatusFormat.getName(e));
460-
int v = ((nameId & ACL_ENTRY_NAME_MASK) << ACL_ENTRY_NAME_OFFSET)
461-
| (AclEntryStatusFormat.getType(e).ordinal() << ACL_ENTRY_TYPE_OFFSET)
462-
| (AclEntryStatusFormat.getScope(e).ordinal() << ACL_ENTRY_SCOPE_OFFSET)
463-
| (AclEntryStatusFormat.getPermission(e).ordinal());
464-
b.addEntries(v);
422+
b.addEntries(e);
465423
}
466424
return b;
467425
}
468-
469-
private static XAttrFeatureProto.Builder buildXAttrs(XAttrFeature f,
470-
final SaverContext.DeduplicationMap<String> stringMap) {
426+
427+
private static XAttrFeatureProto.Builder buildXAttrs(XAttrFeature f) {
471428
XAttrFeatureProto.Builder b = XAttrFeatureProto.newBuilder();
472429
for (XAttr a : f.getXAttrs()) {
473430
XAttrCompactProto.Builder xAttrCompactBuilder = XAttrCompactProto.
474431
newBuilder();
475-
int nsOrd = a.getNameSpace().ordinal();
476-
Preconditions.checkArgument(nsOrd < 8, "Too many namespaces.");
477-
int v = ((nsOrd & XATTR_NAMESPACE_MASK) << XATTR_NAMESPACE_OFFSET)
478-
| ((stringMap.getId(a.getName()) & XATTR_NAME_MASK) <<
479-
XATTR_NAME_OFFSET);
480-
v |= (((nsOrd >> 2) & XATTR_NAMESPACE_EXT_MASK) <<
481-
XATTR_NAMESPACE_EXT_OFFSET);
432+
int v = XAttrFormat.toInt(a);
482433
xAttrCompactBuilder.setName(v);
483434
if (a.getValue() != null) {
484435
xAttrCompactBuilder.setValue(PBHelperClient.getByteString(a.getValue()));
@@ -510,7 +461,7 @@ public static INodeSection.INodeFile.Builder buildINodeFile(
510461
INodeSection.INodeFile.Builder b = INodeSection.INodeFile.newBuilder()
511462
.setAccessTime(file.getAccessTime())
512463
.setModificationTime(file.getModificationTime())
513-
.setPermission(buildPermissionStatus(file, state.getStringMap()))
464+
.setPermission(buildPermissionStatus(file))
514465
.setPreferredBlockSize(file.getPreferredBlockSize())
515466
.setStoragePolicyID(file.getLocalStoragePolicyID())
516467
.setBlockType(PBHelperClient.convert(file.getBlockType()));
@@ -523,11 +474,11 @@ public static INodeSection.INodeFile.Builder buildINodeFile(
523474

524475
AclFeature f = file.getAclFeature();
525476
if (f != null) {
526-
b.setAcl(buildAclEntries(f, state.getStringMap()));
477+
b.setAcl(buildAclEntries(f));
527478
}
528479
XAttrFeature xAttrFeature = file.getXAttrFeature();
529480
if (xAttrFeature != null) {
530-
b.setXAttrs(buildXAttrs(xAttrFeature, state.getStringMap()));
481+
b.setXAttrs(buildXAttrs(xAttrFeature));
531482
}
532483
return b;
533484
}
@@ -539,19 +490,19 @@ public static INodeSection.INodeDirectory.Builder buildINodeDirectory(
539490
.newBuilder().setModificationTime(dir.getModificationTime())
540491
.setNsQuota(quota.getNameSpace())
541492
.setDsQuota(quota.getStorageSpace())
542-
.setPermission(buildPermissionStatus(dir, state.getStringMap()));
493+
.setPermission(buildPermissionStatus(dir));
543494

544495
if (quota.getTypeSpaces().anyGreaterOrEqual(0)) {
545496
b.setTypeQuotas(buildQuotaByStorageTypeEntries(quota));
546497
}
547498

548499
AclFeature f = dir.getAclFeature();
549500
if (f != null) {
550-
b.setAcl(buildAclEntries(f, state.getStringMap()));
501+
b.setAcl(buildAclEntries(f));
551502
}
552503
XAttrFeature xAttrFeature = dir.getXAttrFeature();
553504
if (xAttrFeature != null) {
554-
b.setXAttrs(buildXAttrs(xAttrFeature, state.getStringMap()));
505+
b.setXAttrs(buildXAttrs(xAttrFeature));
555506
}
556507
return b;
557508
}
@@ -712,7 +663,7 @@ private void save(OutputStream out, INodeSymlink n) throws IOException {
712663
SaverContext state = parent.getSaverContext();
713664
INodeSection.INodeSymlink.Builder b = INodeSection.INodeSymlink
714665
.newBuilder()
715-
.setPermission(buildPermissionStatus(n, state.getStringMap()))
666+
.setPermission(buildPermissionStatus(n))
716667
.setTarget(ByteString.copyFrom(n.getSymlink()))
717668
.setModificationTime(n.getModificationTime())
718669
.setAccessTime(n.getAccessTime());

0 commit comments

Comments
 (0)