Skip to content

Commit a8ef227

Browse files
ashvinaahussein
authored andcommitted
HDFS-14856. Fetch file ACLs while mounting external store. (apache#1478)
1 parent 56e9f37 commit a8ef227

File tree

10 files changed

+321
-40
lines changed

10 files changed

+321
-40
lines changed

hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/DFSConfigKeys.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -379,6 +379,9 @@ public class DFSConfigKeys extends CommonConfigurationKeys {
379379
public static final String DFS_PROVIDED_ALIASMAP_TEXT_WRITE_DIR_DEFAULT = "file:///tmp/";
380380

381381
public static final String DFS_PROVIDED_ALIASMAP_LEVELDB_PATH = "dfs.provided.aliasmap.leveldb.path";
382+
public static final String DFS_PROVIDED_ACLS_IMPORT_ENABLED =
383+
"dfs.provided.acls.import.enabled";
384+
public static final boolean DFS_PROVIDED_ACLS_IMPORT_ENABLED_DEFAULT = false;
382385

383386
public static final String DFS_LIST_LIMIT = "dfs.ls.limit";
384387
public static final int DFS_LIST_LIMIT_DEFAULT = 1000;

hadoop-hdfs-project/hadoop-hdfs/src/main/resources/hdfs-default.xml

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5357,6 +5357,17 @@
53575357
</description>
53585358
</property>
53595359

5360+
<property>
5361+
<name>dfs.provided.acls.import.enabled</name>
5362+
<value>false</value>
5363+
<description>
5364+
Set to true to inherit ACLs (Access Control Lists) from remote stores
5365+
during mount. Disabled by default, i.e., ACLs are not inherited from
5366+
remote stores. Note had HDFS ACLs have to be enabled
5367+
(dfs.namenode.acls.enabled must be set to true) for this to take effect.
5368+
</description>
5369+
</property>
5370+
53605371
<property>
53615372
<name>dfs.provided.aliasmap.load.retries</name>
53625373
<value>0</value>

hadoop-tools/hadoop-fs2img/src/main/java/org/apache/hadoop/hdfs/server/namenode/FSTreeWalk.java

Lines changed: 42 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,14 @@
2929
import org.apache.hadoop.fs.FileStatus;
3030
import org.apache.hadoop.fs.FileSystem;
3131
import org.apache.hadoop.fs.Path;
32+
import org.apache.hadoop.fs.permission.AclStatus;
33+
import org.slf4j.Logger;
34+
import org.slf4j.LoggerFactory;
35+
36+
import static org.apache.hadoop.hdfs.DFSConfigKeys.DFS_NAMENODE_ACLS_ENABLED_DEFAULT;
37+
import static org.apache.hadoop.hdfs.DFSConfigKeys.DFS_NAMENODE_ACLS_ENABLED_KEY;
38+
import static org.apache.hadoop.hdfs.DFSConfigKeys.DFS_PROVIDED_ACLS_IMPORT_ENABLED;
39+
import static org.apache.hadoop.hdfs.DFSConfigKeys.DFS_PROVIDED_ACLS_IMPORT_ENABLED_DEFAULT;
3240

3341
/**
3442
* Traversal of an external FileSystem.
@@ -37,12 +45,28 @@
3745
@InterfaceStability.Unstable
3846
public class FSTreeWalk extends TreeWalk {
3947

48+
public static final Logger LOG =
49+
LoggerFactory.getLogger(FSTreeWalk.class);
50+
4051
private final Path root;
4152
private final FileSystem fs;
53+
private final boolean enableACLs;
4254

4355
public FSTreeWalk(Path root, Configuration conf) throws IOException {
4456
this.root = root;
4557
fs = root.getFileSystem(conf);
58+
59+
boolean mountACLsEnabled = conf.getBoolean(DFS_PROVIDED_ACLS_IMPORT_ENABLED,
60+
DFS_PROVIDED_ACLS_IMPORT_ENABLED_DEFAULT);
61+
boolean localACLsEnabled = conf.getBoolean(DFS_NAMENODE_ACLS_ENABLED_KEY,
62+
DFS_NAMENODE_ACLS_ENABLED_DEFAULT);
63+
if (!localACLsEnabled && mountACLsEnabled) {
64+
LOG.warn("Mount ACLs have been enabled but HDFS ACLs are not. " +
65+
"Disabling ACLs on the mount {}", root);
66+
this.enableACLs = false;
67+
} else {
68+
this.enableACLs = mountACLsEnabled;
69+
}
4670
}
4771

4872
@Override
@@ -55,7 +79,8 @@ protected Iterable<TreePath> getChildren(TreePath path, long id,
5579
try {
5680
ArrayList<TreePath> ret = new ArrayList<>();
5781
for (FileStatus s : fs.listStatus(path.getFileStatus().getPath())) {
58-
ret.add(new TreePath(s, id, i, fs));
82+
AclStatus aclStatus = getAclStatus(fs, s.getPath());
83+
ret.add(new TreePath(s, id, i, fs, aclStatus));
5984
}
6085
return ret;
6186
} catch (FileNotFoundException e) {
@@ -71,20 +96,19 @@ private FSTreeIterator() {
7196
}
7297

7398
FSTreeIterator(TreePath p) {
74-
getPendingQueue().addFirst(
75-
new TreePath(p.getFileStatus(), p.getParentId(), this, fs));
99+
this(p.getFileStatus(), p.getParentId());
76100
}
77101

78-
FSTreeIterator(Path p) throws IOException {
102+
FSTreeIterator(FileStatus fileStatus, long parentId) {
103+
Path path = fileStatus.getPath();
104+
AclStatus acls;
79105
try {
80-
FileStatus s = fs.getFileStatus(root);
81-
getPendingQueue().addFirst(new TreePath(s, -1L, this, fs));
82-
} catch (FileNotFoundException e) {
83-
if (p.equals(root)) {
84-
throw e;
85-
}
86-
throw new ConcurrentModificationException("FS modified");
106+
acls = getAclStatus(fs, path);
107+
} catch (IOException e) {
108+
throw new RuntimeException(e);
87109
}
110+
TreePath treePath = new TreePath(fileStatus, parentId, this, fs, acls);
111+
getPendingQueue().addFirst(treePath);
88112
}
89113

90114
@Override
@@ -97,10 +121,16 @@ public TreeIterator fork() {
97121

98122
}
99123

124+
private AclStatus getAclStatus(FileSystem fileSystem, Path path)
125+
throws IOException {
126+
return enableACLs ? fileSystem.getAclStatus(path) : null;
127+
}
128+
100129
@Override
101130
public TreeIterator iterator() {
102131
try {
103-
return new FSTreeIterator(root);
132+
FileStatus s = fs.getFileStatus(root);
133+
return new FSTreeIterator(s, -1L);
104134
} catch (IOException e) {
105135
throw new RuntimeException(e);
106136
}

hadoop-tools/hadoop-fs2img/src/main/java/org/apache/hadoop/hdfs/server/namenode/SingleUGIResolver.java

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@
2323
import org.apache.hadoop.classification.InterfaceStability;
2424
import org.apache.hadoop.conf.Configurable;
2525
import org.apache.hadoop.conf.Configuration;
26-
import org.apache.hadoop.fs.FileStatus;
2726
import org.apache.hadoop.security.UserGroupInformation;
2827

2928
/**
@@ -73,12 +72,12 @@ public Configuration getConf() {
7372
}
7473

7574
@Override
76-
public String user(FileStatus s) {
75+
public String user(String s) {
7776
return user;
7877
}
7978

8079
@Override
81-
public String group(FileStatus s) {
80+
public String group(String s) {
8281
return group;
8382
}
8483

hadoop-tools/hadoop-fs2img/src/main/java/org/apache/hadoop/hdfs/server/namenode/TreePath.java

Lines changed: 36 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919

2020
import java.io.IOException;
2121

22+
import com.google.common.annotations.VisibleForTesting;
2223
import com.google.protobuf.ByteString;
2324

2425
import org.apache.hadoop.classification.InterfaceAudience;
@@ -27,6 +28,7 @@
2728
import org.apache.hadoop.fs.FileSystem;
2829
import org.apache.hadoop.fs.Options;
2930
import org.apache.hadoop.fs.PathHandle;
31+
import org.apache.hadoop.fs.permission.AclStatus;
3032
import org.apache.hadoop.hdfs.protocol.HdfsConstants;
3133
import org.apache.hadoop.hdfs.protocol.proto.HdfsProtos.BlockProto;
3234
import org.apache.hadoop.hdfs.server.common.FileRegion;
@@ -52,32 +54,47 @@ public class TreePath {
5254
private final FileStatus stat;
5355
private final TreeWalk.TreeIterator i;
5456
private final FileSystem fs;
57+
private final AclStatus acls;
5558

56-
protected TreePath(FileStatus stat, long parentId, TreeWalk.TreeIterator i,
57-
FileSystem fs) {
59+
@VisibleForTesting
60+
public TreePath(FileStatus stat, long parentId, TreeWalk.TreeIterator i) {
61+
this(stat, parentId, i, null, null);
62+
}
63+
64+
public TreePath(FileStatus stat, long parentId, TreeWalk.TreeIterator i,
65+
FileSystem fs, AclStatus acls) {
5866
this.i = i;
5967
this.stat = stat;
6068
this.parentId = parentId;
6169
this.fs = fs;
70+
this.acls = acls;
6271
}
6372

6473
public FileStatus getFileStatus() {
6574
return stat;
6675
}
6776

77+
public AclStatus getAclStatus() {
78+
return acls;
79+
}
80+
6881
public long getParentId() {
6982
return parentId;
7083
}
7184

85+
public TreeWalk.TreeIterator getIterator() {
86+
return i;
87+
}
88+
7289
public long getId() {
7390
if (id < 0) {
7491
throw new IllegalStateException();
7592
}
7693
return id;
7794
}
7895

79-
void accept(long id) {
80-
this.id = id;
96+
public void accept(long pathId) {
97+
this.id = pathId;
8198
i.onAccept(this, id);
8299
}
83100

@@ -121,14 +138,14 @@ void writeBlock(long blockId, long offset, long length, long genStamp,
121138
INode toFile(UGIResolver ugi, BlockResolver blk,
122139
BlockAliasMap.Writer<FileRegion> out) throws IOException {
123140
final FileStatus s = getFileStatus();
124-
ugi.addUser(s.getOwner());
125-
ugi.addGroup(s.getGroup());
141+
final AclStatus aclStatus = getAclStatus();
142+
long permissions = ugi.getPermissionsProto(s, aclStatus);
126143
INodeFile.Builder b = INodeFile.newBuilder()
127144
.setReplication(blk.getReplication(s))
128145
.setModificationTime(s.getModificationTime())
129146
.setAccessTime(s.getAccessTime())
130147
.setPreferredBlockSize(blk.preferredBlockSize(s))
131-
.setPermission(ugi.resolve(s))
148+
.setPermission(permissions)
132149
.setStoragePolicyID(HdfsConstants.PROVIDED_STORAGE_POLICY_ID);
133150

134151
// pathhandle allows match as long as the file matches exactly.
@@ -141,7 +158,11 @@ INode toFile(UGIResolver ugi, BlockResolver blk,
141158
"Exact path handle not supported by filesystem " + fs.toString());
142159
}
143160
}
144-
// TODO: storage policy should be configurable per path; use BlockResolver
161+
if (aclStatus != null) {
162+
throw new UnsupportedOperationException(
163+
"ACLs not supported by ImageWriter");
164+
}
165+
//TODO: storage policy should be configurable per path; use BlockResolver
145166
long off = 0L;
146167
for (BlockProto block : blk.resolve(s)) {
147168
b.addBlocks(block);
@@ -159,13 +180,17 @@ INode toFile(UGIResolver ugi, BlockResolver blk,
159180

160181
INode toDirectory(UGIResolver ugi) {
161182
final FileStatus s = getFileStatus();
162-
ugi.addUser(s.getOwner());
163-
ugi.addGroup(s.getGroup());
183+
final AclStatus aclStatus = getAclStatus();
184+
long permissions = ugi.getPermissionsProto(s, aclStatus);
164185
INodeDirectory.Builder b = INodeDirectory.newBuilder()
165186
.setModificationTime(s.getModificationTime())
166187
.setNsQuota(DEFAULT_NAMESPACE_QUOTA)
167188
.setDsQuota(DEFAULT_STORAGE_SPACE_QUOTA)
168-
.setPermission(ugi.resolve(s));
189+
.setPermission(permissions);
190+
if (aclStatus != null) {
191+
throw new UnsupportedOperationException(
192+
"ACLs not supported by ImageWriter");
193+
}
169194
INode.Builder ib = INode.newBuilder()
170195
.setType(INode.Type.DIRECTORY)
171196
.setId(id)

hadoop-tools/hadoop-fs2img/src/main/java/org/apache/hadoop/hdfs/server/namenode/TreeWalk.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ public abstract class TreeIterator implements Iterator<TreePath> {
5050

5151
private final Deque<TreePath> pending;
5252

53-
TreeIterator() {
53+
public TreeIterator() {
5454
this(new ArrayDeque<TreePath>());
5555
}
5656

hadoop-tools/hadoop-fs2img/src/main/java/org/apache/hadoop/hdfs/server/namenode/UGIResolver.java

Lines changed: 71 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,9 @@
2424
import org.apache.hadoop.classification.InterfaceAudience;
2525
import org.apache.hadoop.classification.InterfaceStability;
2626
import org.apache.hadoop.fs.FileStatus;
27+
import org.apache.hadoop.fs.permission.AclEntry;
28+
import org.apache.hadoop.fs.permission.AclEntryType;
29+
import org.apache.hadoop.fs.permission.AclStatus;
2730
import org.apache.hadoop.fs.permission.FsPermission;
2831

2932
/**
@@ -34,9 +37,9 @@
3437
@InterfaceStability.Unstable
3538
public abstract class UGIResolver {
3639

37-
static final int USER_STRID_OFFSET = 40;
38-
static final int GROUP_STRID_OFFSET = 16;
39-
static final long USER_GROUP_STRID_MASK = (1 << 24) - 1;
40+
public static final int USER_STRID_OFFSET = 40;
41+
public static final int GROUP_STRID_OFFSET = 16;
42+
public static final long USER_GROUP_STRID_MASK = (1 << 24) - 1;
4043

4144
/**
4245
* Permission is serialized as a 64-bit long. [0:24):[25:48):[48:64) (in Big
@@ -117,19 +120,77 @@ protected void resetUGInfo() {
117120
}
118121

119122
public long resolve(FileStatus s) {
120-
return buildPermissionStatus(user(s), group(s), permission(s).toShort());
123+
String resolvedGroup = group(s.getGroup());
124+
String resolvedOwner = user(s.getOwner());
125+
FsPermission resolvedPermission = permission(s.getPermission());
126+
return buildPermissionStatus(
127+
resolvedOwner, resolvedGroup, resolvedPermission.toShort());
121128
}
122129

123-
public String user(FileStatus s) {
124-
return s.getOwner();
130+
private long resolve(AclStatus aclStatus) {
131+
String resolvedOwner = user(aclStatus.getOwner());
132+
String resolvedGroup = group(aclStatus.getGroup());
133+
FsPermission resolvedPermision = permission(aclStatus.getPermission());
134+
return buildPermissionStatus(
135+
resolvedOwner, resolvedGroup, resolvedPermision.toShort());
125136
}
126137

127-
public String group(FileStatus s) {
128-
return s.getGroup();
138+
protected String user(String s) {
139+
return s;
129140
}
130141

131-
public FsPermission permission(FileStatus s) {
132-
return s.getPermission();
142+
protected String group(String s) {
143+
return s;
133144
}
134145

146+
public FsPermission permission(FsPermission s) {
147+
return s;
148+
}
149+
150+
/**
151+
* Get the serialized, local permissions for the external
152+
* {@link FileStatus} or {@link AclStatus}. {@code remoteAcl} is used when it
153+
* is not null, otherwise {@code remoteStatus} is used.
154+
*
155+
* @param remoteStatus FileStatus on remote store.
156+
* @param remoteAcl AclStatus on external store.
157+
* @return serialized, local permissions the FileStatus or AclStatus map to.
158+
*/
159+
public long getPermissionsProto(FileStatus remoteStatus,
160+
AclStatus remoteAcl) {
161+
addUGI(remoteStatus, remoteAcl);
162+
if (remoteAcl == null) {
163+
return resolve(remoteStatus);
164+
} else {
165+
return resolve(remoteAcl);
166+
}
167+
}
168+
169+
/**
170+
* Add the users and groups specified by the given {@link FileStatus} and
171+
* {@link AclStatus}.
172+
*
173+
* @param remoteStatus
174+
* @param remoteAcl
175+
*/
176+
private void addUGI(FileStatus remoteStatus, AclStatus remoteAcl) {
177+
if (remoteAcl != null) {
178+
addUser(remoteAcl.getOwner());
179+
addGroup(remoteAcl.getGroup());
180+
for (AclEntry entry : remoteAcl.getEntries()) {
181+
// add the users and groups in this acl entry to ugi
182+
String name = entry.getName();
183+
if (name != null) {
184+
if (entry.getType() == AclEntryType.USER) {
185+
addUser(name);
186+
} else if (entry.getType() == AclEntryType.GROUP) {
187+
addGroup(name);
188+
}
189+
}
190+
}
191+
} else {
192+
addUser(remoteStatus.getOwner());
193+
addGroup(remoteStatus.getGroup());
194+
}
195+
}
135196
}

0 commit comments

Comments
 (0)