Skip to content

Commit 09b06a6

Browse files
committed
HDFS-10673. Optimize FSPermissionChecker's internal path usage. Contributed by Daryn Sharp.
(cherry picked from commit 438a9f0)
1 parent 690ec78 commit 09b06a6

File tree

5 files changed

+116
-102
lines changed

5 files changed

+116
-102
lines changed

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

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1527,10 +1527,7 @@ FSPermissionChecker getPermissionChecker()
15271527
FSPermissionChecker getPermissionChecker(String fsOwner, String superGroup,
15281528
UserGroupInformation ugi) throws AccessControlException {
15291529
return new FSPermissionChecker(
1530-
fsOwner, superGroup, ugi,
1531-
attributeProvider == null ?
1532-
DefaultINodeAttributesProvider.DEFAULT_PROVIDER
1533-
: attributeProvider);
1530+
fsOwner, superGroup, ugi, attributeProvider);
15341531
}
15351532

15361533
void checkOwner(FSPermissionChecker pc, INodesInPath iip)
@@ -1660,15 +1657,12 @@ void resetLastInodeIdWithoutChecking(long newValue) {
16601657

16611658
INodeAttributes getAttributes(String fullPath, byte[] path,
16621659
INode node, int snapshot) {
1663-
INodeAttributes nodeAttrs;
1660+
INodeAttributes nodeAttrs = node.getSnapshotINode(snapshot);
16641661
if (attributeProvider != null) {
1665-
nodeAttrs = node.getSnapshotINode(snapshot);
16661662
fullPath = fullPath
16671663
+ (fullPath.endsWith(Path.SEPARATOR) ? "" : Path.SEPARATOR)
16681664
+ DFSUtil.bytes2String(path);
16691665
nodeAttrs = attributeProvider.getAttributes(fullPath, nodeAttrs);
1670-
} else {
1671-
nodeAttrs = node.getSnapshotINode(snapshot);
16721666
}
16731667
return nodeAttrs;
16741668
}

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

Lines changed: 86 additions & 89 deletions
Original file line numberDiff line numberDiff line change
@@ -45,23 +45,31 @@
4545
class FSPermissionChecker implements AccessControlEnforcer {
4646
static final Log LOG = LogFactory.getLog(UserGroupInformation.class);
4747

48+
private static String constructPath(INodeAttributes[] inodes, int end) {
49+
byte[][] components = new byte[end+1][];
50+
for (int i=0; i <= end; i++) {
51+
components[i] = inodes[i].getLocalNameBytes();
52+
}
53+
return DFSUtil.byteArray2PathString(components);
54+
}
55+
4856
/** @return a string for throwing {@link AccessControlException} */
4957
private String toAccessControlString(INodeAttributes inodeAttrib, String path,
50-
FsAction access, FsPermission mode) {
51-
return toAccessControlString(inodeAttrib, path, access, mode, false);
58+
FsAction access) {
59+
return toAccessControlString(inodeAttrib, path, access, false);
5260
}
5361

5462
/** @return a string for throwing {@link AccessControlException} */
5563
private String toAccessControlString(INodeAttributes inodeAttrib,
56-
String path, FsAction access, FsPermission mode, boolean deniedFromAcl) {
64+
String path, FsAction access, boolean deniedFromAcl) {
5765
StringBuilder sb = new StringBuilder("Permission denied: ")
5866
.append("user=").append(getUser()).append(", ")
5967
.append("access=").append(access).append(", ")
6068
.append("inode=\"").append(path).append("\":")
6169
.append(inodeAttrib.getUserName()).append(':')
6270
.append(inodeAttrib.getGroupName()).append(':')
6371
.append(inodeAttrib.isDirectory() ? 'd' : '-')
64-
.append(mode);
72+
.append(inodeAttrib.getFsPermission());
6573
if (deniedFromAcl) {
6674
sb.append("+");
6775
}
@@ -112,6 +120,11 @@ public INodeAttributeProvider getAttributesProvider() {
112120
return attributeProvider;
113121
}
114122

123+
private AccessControlEnforcer getAccessControlEnforcer() {
124+
return (attributeProvider != null)
125+
? attributeProvider.getExternalAccessControlEnforcer(this) : this;
126+
}
127+
115128
/**
116129
* Verify if the caller has the required permission. This will result into
117130
* an exception if the caller is not allowed to access the resource.
@@ -174,77 +187,54 @@ void checkPermission(INodesInPath inodesInPath, boolean doCheckOwner,
174187
final int snapshotId = inodesInPath.getPathSnapshotId();
175188
final INode[] inodes = inodesInPath.getINodesArray();
176189
final INodeAttributes[] inodeAttrs = new INodeAttributes[inodes.length];
177-
final byte[][] pathByNameArr = new byte[inodes.length][];
190+
final byte[][] components = inodesInPath.getPathComponents();
178191
for (int i = 0; i < inodes.length && inodes[i] != null; i++) {
179-
if (inodes[i] != null) {
180-
pathByNameArr[i] = inodes[i].getLocalNameBytes();
181-
inodeAttrs[i] = getINodeAttrs(pathByNameArr, i, inodes[i], snapshotId);
182-
}
192+
inodeAttrs[i] = getINodeAttrs(components, i, inodes[i], snapshotId);
183193
}
184194

185195
String path = inodesInPath.getPath();
186196
int ancestorIndex = inodes.length - 2;
187197

188-
AccessControlEnforcer enforcer =
189-
getAttributesProvider().getExternalAccessControlEnforcer(this);
198+
AccessControlEnforcer enforcer = getAccessControlEnforcer();
190199
enforcer.checkPermission(fsOwner, supergroup, callerUgi, inodeAttrs, inodes,
191-
pathByNameArr, snapshotId, path, ancestorIndex, doCheckOwner,
200+
components, snapshotId, path, ancestorIndex, doCheckOwner,
192201
ancestorAccess, parentAccess, access, subAccess, ignoreEmptyDir);
193202
}
194203

195-
/**
196-
* Check whether exception e is due to an ancestor inode's not being
197-
* directory.
198-
*/
199-
private void checkAncestorType(INode[] inodes, int checkedAncestorIndex,
200-
AccessControlException e) throws AccessControlException {
201-
for (int i = 0; i <= checkedAncestorIndex; i++) {
202-
if (inodes[i] == null) {
203-
break;
204-
}
205-
if (!inodes[i].isDirectory()) {
206-
throw new AccessControlException(
207-
e.getMessage() + " (Ancestor " + inodes[i].getFullPathName()
208-
+ " is not a directory).");
209-
}
210-
}
211-
throw e;
212-
}
213-
214204
@Override
215205
public void checkPermission(String fsOwner, String supergroup,
216206
UserGroupInformation callerUgi, INodeAttributes[] inodeAttrs,
217-
INode[] inodes, byte[][] pathByNameArr, int snapshotId, String path,
207+
INode[] inodes, byte[][] components, int snapshotId, String path,
218208
int ancestorIndex, boolean doCheckOwner, FsAction ancestorAccess,
219209
FsAction parentAccess, FsAction access, FsAction subAccess,
220210
boolean ignoreEmptyDir)
221211
throws AccessControlException {
222212
for(; ancestorIndex >= 0 && inodes[ancestorIndex] == null;
223213
ancestorIndex--);
224214

225-
checkTraverse(inodeAttrs, inodes, path, ancestorIndex);
215+
checkTraverse(inodeAttrs, ancestorIndex);
226216

227217
final INodeAttributes last = inodeAttrs[inodeAttrs.length - 1];
228218
if (parentAccess != null && parentAccess.implies(FsAction.WRITE)
229219
&& inodeAttrs.length > 1 && last != null) {
230-
checkStickyBit(inodeAttrs[inodeAttrs.length - 2], last, path);
220+
checkStickyBit(inodeAttrs, inodeAttrs.length - 2);
231221
}
232222
if (ancestorAccess != null && inodeAttrs.length > 1) {
233-
check(inodeAttrs, path, ancestorIndex, ancestorAccess);
223+
check(inodeAttrs, ancestorIndex, ancestorAccess);
234224
}
235225
if (parentAccess != null && inodeAttrs.length > 1) {
236-
check(inodeAttrs, path, inodeAttrs.length - 2, parentAccess);
226+
check(inodeAttrs, inodeAttrs.length - 2, parentAccess);
237227
}
238228
if (access != null) {
239-
check(last, path, access);
229+
check(inodeAttrs, inodeAttrs.length - 1, access);
240230
}
241231
if (subAccess != null) {
242232
INode rawLast = inodes[inodeAttrs.length - 1];
243-
checkSubAccess(pathByNameArr, inodeAttrs.length - 1, rawLast,
233+
checkSubAccess(components, inodeAttrs.length - 1, rawLast,
244234
snapshotId, subAccess, ignoreEmptyDir);
245235
}
246236
if (doCheckOwner) {
247-
checkOwner(last);
237+
checkOwner(inodeAttrs, inodeAttrs.length - 1);
248238
}
249239
}
250240

@@ -262,32 +252,35 @@ private INodeAttributes getINodeAttrs(byte[][] pathByNameArr, int pathIdx,
262252
}
263253

264254
/** Guarded by {@link FSNamesystem#readLock()} */
265-
private void checkOwner(INodeAttributes inode
266-
) throws AccessControlException {
267-
if (getUser().equals(inode.getUserName())) {
255+
private void checkOwner(INodeAttributes[] inodes, int i)
256+
throws AccessControlException {
257+
if (getUser().equals(inodes[i].getUserName())) {
268258
return;
269259
}
270260
throw new AccessControlException(
271-
"Permission denied. user="
272-
+ getUser() + " is not the owner of inode=" + inode);
261+
"Permission denied. user=" + getUser() +
262+
" is not the owner of inode=" + constructPath(inodes, i));
273263
}
274264

275265
/** Guarded by {@link FSNamesystem#readLock()} */
276-
private void checkTraverse(INodeAttributes[] inodeAttrs, INode[] inodes,
277-
String path, int last) throws AccessControlException {
278-
int j = 0;
279-
try {
280-
for (; j <= last; j++) {
281-
check(inodeAttrs[j], path, FsAction.EXECUTE);
266+
private void checkTraverse(INodeAttributes[] inodeAttrs, int last)
267+
throws AccessControlException {
268+
for (int i=0; i <= last; i++) {
269+
INodeAttributes inode = inodeAttrs[i];
270+
if (!inode.isDirectory()) {
271+
throw new AccessControlException(
272+
constructPath(inodeAttrs, i) + " (is not a directory)");
273+
}
274+
if (!hasPermission(inode, FsAction.EXECUTE)) {
275+
throw new AccessControlException(toAccessControlString(
276+
inode, constructPath(inodeAttrs, i), FsAction.EXECUTE));
282277
}
283-
} catch (AccessControlException e) {
284-
checkAncestorType(inodes, j, e);
285278
}
286279
}
287280

288281
/** Guarded by {@link FSNamesystem#readLock()} */
289-
private void checkSubAccess(byte[][] pathByNameArr, int pathIdx, INode inode,
290-
int snapshotId, FsAction access, boolean ignoreEmptyDir)
282+
private void checkSubAccess(byte[][] components, int pathIdx,
283+
INode inode, int snapshotId, FsAction access, boolean ignoreEmptyDir)
291284
throws AccessControlException {
292285
if (inode == null || !inode.isDirectory()) {
293286
return;
@@ -299,8 +292,12 @@ private void checkSubAccess(byte[][] pathByNameArr, int pathIdx, INode inode,
299292
ReadOnlyList<INode> cList = d.getChildrenList(snapshotId);
300293
if (!(cList.isEmpty() && ignoreEmptyDir)) {
301294
//TODO have to figure this out with inodeattribute provider
302-
check(getINodeAttrs(pathByNameArr, pathIdx, d, snapshotId),
303-
inode.getFullPathName(), access);
295+
INodeAttributes inodeAttr =
296+
getINodeAttrs(components, pathIdx, d, snapshotId);
297+
if (!hasPermission(inodeAttr, access)) {
298+
throw new AccessControlException(
299+
toAccessControlString(inodeAttr, d.getFullPathName(), access));
300+
}
304301
}
305302

306303
for(INode child : cList) {
@@ -312,37 +309,40 @@ private void checkSubAccess(byte[][] pathByNameArr, int pathIdx, INode inode,
312309
}
313310

314311
/** Guarded by {@link FSNamesystem#readLock()} */
315-
private void check(INodeAttributes[] inodes, String path, int i, FsAction access
316-
) throws AccessControlException {
317-
check(i >= 0 ? inodes[i] : null, path, access);
312+
private void check(INodeAttributes[] inodes, int i, FsAction access)
313+
throws AccessControlException {
314+
INodeAttributes inode = (i >= 0) ? inodes[i] : null;
315+
if (inode != null && !hasPermission(inode, access)) {
316+
throw new AccessControlException(
317+
toAccessControlString(inode, constructPath(inodes, i), access));
318+
}
318319
}
319320

320-
private void check(INodeAttributes inode, String path, FsAction access
321-
) throws AccessControlException {
321+
// return whether access is permitted. note it neither requires a path or
322+
// throws so the caller can build the path only if required for an exception.
323+
// very beneficial for subaccess checks!
324+
private boolean hasPermission(INodeAttributes inode, FsAction access) {
322325
if (inode == null) {
323-
return;
326+
return true;
324327
}
325328
final FsPermission mode = inode.getFsPermission();
326329
final AclFeature aclFeature = inode.getAclFeature();
327330
if (aclFeature != null) {
328331
// It's possible that the inode has a default ACL but no access ACL.
329332
int firstEntry = aclFeature.getEntryAt(0);
330333
if (AclEntryStatusFormat.getScope(firstEntry) == AclEntryScope.ACCESS) {
331-
checkAccessAcl(inode, path, access, mode, aclFeature);
332-
return;
334+
return hasAclPermission(inode, access, mode, aclFeature);
333335
}
334336
}
337+
final FsAction checkAction;
335338
if (getUser().equals(inode.getUserName())) { //user class
336-
if (mode.getUserAction().implies(access)) { return; }
337-
}
338-
else if (getGroups().contains(inode.getGroupName())) { //group class
339-
if (mode.getGroupAction().implies(access)) { return; }
339+
checkAction = mode.getUserAction();
340+
} else if (getGroups().contains(inode.getGroupName())) { //group class
341+
checkAction = mode.getGroupAction();
342+
} else { //other class
343+
checkAction = mode.getOtherAction();
340344
}
341-
else { //other class
342-
if (mode.getOtherAction().implies(access)) { return; }
343-
}
344-
throw new AccessControlException(
345-
toAccessControlString(inode, path, access, mode));
345+
return checkAction.implies(access);
346346
}
347347

348348
/**
@@ -368,15 +368,14 @@ else if (getGroups().contains(inode.getGroupName())) { //group class
368368
* @param aclFeature AclFeature of inode
369369
* @throws AccessControlException if the ACL denies permission
370370
*/
371-
private void checkAccessAcl(INodeAttributes inode, String path,
372-
FsAction access, FsPermission mode, AclFeature aclFeature)
373-
throws AccessControlException {
371+
private boolean hasAclPermission(INodeAttributes inode,
372+
FsAction access, FsPermission mode, AclFeature aclFeature) {
374373
boolean foundMatch = false;
375374

376375
// Use owner entry from permission bits if user is owner.
377376
if (getUser().equals(inode.getUserName())) {
378377
if (mode.getUserAction().implies(access)) {
379-
return;
378+
return true;
380379
}
381380
foundMatch = true;
382381
}
@@ -397,7 +396,7 @@ private void checkAccessAcl(INodeAttributes inode, String path,
397396
FsAction masked = AclEntryStatusFormat.getPermission(entry).and(
398397
mode.getGroupAction());
399398
if (masked.implies(access)) {
400-
return;
399+
return true;
401400
}
402401
foundMatch = true;
403402
break;
@@ -412,7 +411,7 @@ private void checkAccessAcl(INodeAttributes inode, String path,
412411
FsAction masked = AclEntryStatusFormat.getPermission(entry).and(
413412
mode.getGroupAction());
414413
if (masked.implies(access)) {
415-
return;
414+
return true;
416415
}
417416
foundMatch = true;
418417
}
@@ -421,17 +420,13 @@ private void checkAccessAcl(INodeAttributes inode, String path,
421420
}
422421

423422
// Use other entry if user was not denied by an earlier match.
424-
if (!foundMatch && mode.getOtherAction().implies(access)) {
425-
return;
426-
}
427-
428-
throw new AccessControlException(
429-
toAccessControlString(inode, path, access, mode));
423+
return !foundMatch && mode.getOtherAction().implies(access);
430424
}
431425

432426
/** Guarded by {@link FSNamesystem#readLock()} */
433-
private void checkStickyBit(INodeAttributes parent, INodeAttributes inode,
434-
String path) throws AccessControlException {
427+
private void checkStickyBit(INodeAttributes[] inodes, int index)
428+
throws AccessControlException {
429+
INodeAttributes parent = inodes[index];
435430
if (!parent.getFsPermission().getStickyBit()) {
436431
return;
437432
}
@@ -441,6 +436,7 @@ private void checkStickyBit(INodeAttributes parent, INodeAttributes inode,
441436
return;
442437
}
443438

439+
INodeAttributes inode = inodes[index + 1];
444440
// if this user is the file owner, return
445441
if (inode.getUserName().equals(getUser())) {
446442
return;
@@ -449,9 +445,10 @@ private void checkStickyBit(INodeAttributes parent, INodeAttributes inode,
449445
throw new AccessControlException(String.format(
450446
"Permission denied by sticky bit: user=%s, path=\"%s\":%s:%s:%s%s, " +
451447
"parent=\"%s\":%s:%s:%s%s", user,
452-
path, inode.getUserName(), inode.getGroupName(),
448+
constructPath(inodes, index + 1),
449+
inode.getUserName(), inode.getGroupName(),
453450
inode.isDirectory() ? "d" : "-", inode.getFsPermission().toString(),
454-
path.substring(0, path.length() - inode.toString().length() - 1 ),
451+
constructPath(inodes, index),
455452
parent.getUserName(), parent.getGroupName(),
456453
parent.isDirectory() ? "d" : "-", parent.getFsPermission().toString()));
457454
}

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

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -589,7 +589,19 @@ public String getFullPathName() {
589589
}
590590
return DFSUtil.bytes2String(path);
591591
}
592-
592+
593+
public byte[][] getPathComponents() {
594+
int n = 0;
595+
for (INode inode = this; inode != null; inode = inode.getParent()) {
596+
n++;
597+
}
598+
byte[][] components = new byte[n][];
599+
for (INode inode = this; inode != null; inode = inode.getParent()) {
600+
components[--n] = inode.getLocalNameBytes();
601+
}
602+
return components;
603+
}
604+
593605
@Override
594606
public String toString() {
595607
return getLocalName();

0 commit comments

Comments
 (0)