Skip to content

Commit 0fe4903

Browse files
committed
HDFS-15243. Add an option to prevent sub-directories of protected directories from deletion. Contributed by liuyanyu.
1 parent bd342be commit 0fe4903

File tree

5 files changed

+121
-0
lines changed

5 files changed

+121
-0
lines changed

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

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1460,6 +1460,16 @@ public class DFSConfigKeys extends CommonConfigurationKeys {
14601460
"dfs.namenode.state.context.enabled";
14611461
public static final boolean DFS_NAMENODE_STATE_CONTEXT_ENABLED_DEFAULT = false;
14621462

1463+
/**
1464+
* whether to protect the subdirectories of directories which
1465+
* set on fs.protected.directories.
1466+
*/
1467+
public static final String DFS_PROTECTED_SUBDIRECTORIES_ENABLE =
1468+
"dfs.protected.subdirectories.enable";
1469+
// Default value for DFS_PROTECTED_SUBDIRECTORIES_ENABLE.
1470+
public static final boolean DFS_PROTECTED_SUBDIRECTORIES_ENABLE_DEFAULT =
1471+
false;
1472+
14631473
// dfs.client.retry confs are moved to HdfsClientConfigKeys.Retry
14641474
@Deprecated
14651475
public static final String DFS_CLIENT_RETRY_POLICY_ENABLED_KEY

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

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1787,6 +1787,18 @@ public static void checkProtectedDescendants(
17871787
+ descendant);
17881788
}
17891789
}
1790+
1791+
if (fsd.isProtectedSubDirectoriesEnable()) {
1792+
while (!src.isEmpty()) {
1793+
int index = src.lastIndexOf(Path.SEPARATOR_CHAR);
1794+
src = src.substring(0, index);
1795+
if (protectedDirs.contains(src)) {
1796+
throw new AccessControlException(
1797+
"Cannot delete/rename subdirectory under protected subdirectory "
1798+
+ src);
1799+
}
1800+
}
1801+
}
17901802
}
17911803

17921804
/**

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

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,8 @@
9090
import static org.apache.hadoop.hdfs.DFSConfigKeys.DFS_NAMENODE_ACCESSTIME_PRECISION_KEY;
9191
import static org.apache.hadoop.hdfs.DFSConfigKeys.DFS_QUOTA_BY_STORAGETYPE_ENABLED_DEFAULT;
9292
import static org.apache.hadoop.hdfs.DFSConfigKeys.DFS_QUOTA_BY_STORAGETYPE_ENABLED_KEY;
93+
import static org.apache.hadoop.hdfs.DFSConfigKeys.DFS_PROTECTED_SUBDIRECTORIES_ENABLE;
94+
import static org.apache.hadoop.hdfs.DFSConfigKeys.DFS_PROTECTED_SUBDIRECTORIES_ENABLE_DEFAULT;
9395
import static org.apache.hadoop.hdfs.server.common.HdfsServerConstants.CRYPTO_XATTR_ENCRYPTION_ZONE;
9496
import static org.apache.hadoop.hdfs.server.common.HdfsServerConstants.SECURITY_XATTR_UNREADABLE_BY_SUPERUSER;
9597
import static org.apache.hadoop.hdfs.server.common.HdfsServerConstants.XATTR_SATISFY_STORAGE_POLICY;
@@ -170,6 +172,7 @@ private static INodeDirectory createRoot(FSNamesystem namesystem) {
170172
//
171173
// Each entry in this set must be a normalized path.
172174
private volatile SortedSet<String> protectedDirectories;
175+
private final boolean isProtectedSubDirectoriesEnable;
173176

174177
private final boolean isPermissionEnabled;
175178
private final boolean isPermissionContentSummarySubAccess;
@@ -382,6 +385,9 @@ public enum DirOp {
382385
DFSConfigKeys.DFS_NAMENODE_MAX_XATTRS_PER_INODE_DEFAULT);
383386

384387
this.protectedDirectories = parseProtectedDirectories(conf);
388+
this.isProtectedSubDirectoriesEnable = conf.getBoolean(
389+
DFS_PROTECTED_SUBDIRECTORIES_ENABLE,
390+
DFS_PROTECTED_SUBDIRECTORIES_ENABLE_DEFAULT);
385391

386392
Preconditions.checkArgument(this.inodeXAttrsLimit >= 0,
387393
"Cannot set a negative limit on the number of xattrs per inode (%s).",
@@ -542,6 +548,10 @@ public SortedSet<String> getProtectedDirectories() {
542548
return protectedDirectories;
543549
}
544550

551+
public boolean isProtectedSubDirectoriesEnable() {
552+
return isProtectedSubDirectoriesEnable;
553+
}
554+
545555
/**
546556
* Set directories that cannot be removed unless empty, even by an
547557
* administrator.

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

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5870,4 +5870,12 @@
58705870
directories when permissions is enabled. Default value is false;
58715871
</description>
58725872
</property>
5873+
5874+
<property>
5875+
<name>dfs.protected.subdirectories.enable</name>
5876+
<value>false</value>
5877+
<description>whether to protect the subdirectories of directories which
5878+
set on fs.protected.directories.
5879+
</description>
5880+
</property>
58735881
</configuration>

hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/server/namenode/TestProtectedDirectories.java

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
import java.io.IOException;
4040
import java.util.*;
4141

42+
import static org.apache.hadoop.hdfs.DFSConfigKeys.DFS_PROTECTED_SUBDIRECTORIES_ENABLE;
4243
import static org.hamcrest.core.Is.is;
4344
import static org.junit.Assert.assertEquals;
4445
import static org.junit.Assert.assertFalse;
@@ -193,6 +194,25 @@ private Collection<TestMatrixEntry> createTestMatrix() {
193194
return matrix;
194195
}
195196

197+
private Collection<TestMatrixEntry> createTestMatrixForProtectSubDirs() {
198+
Collection<TestMatrixEntry> matrix = new ArrayList<TestMatrixEntry>();
199+
200+
// Nested unprotected dirs.
201+
matrix.add(TestMatrixEntry.get()
202+
.addUnprotectedDir("/1", true)
203+
.addUnprotectedDir("/1/2", true)
204+
.addUnprotectedDir("/1/2/3", true)
205+
.addUnprotectedDir("/1/2/3/4", true));
206+
207+
// Non-empty protected dir.
208+
matrix.add(TestMatrixEntry.get()
209+
.addProtectedDir("/1", false)
210+
.addUnprotectedDir("/1/2", false)
211+
.addUnprotectedDir("/1/2/3", false)
212+
.addUnprotectedDir("/1/2/3/4", true));
213+
return matrix;
214+
}
215+
196216
@Test
197217
public void testReconfigureProtectedPaths() throws Throwable {
198218
Configuration conf = new HdfsConfiguration();
@@ -292,6 +312,67 @@ public void testRename() throws Throwable {
292312
}
293313
}
294314

315+
@Test
316+
public void testRenameProtectSubDirs() throws Throwable {
317+
for (TestMatrixEntry testMatrixEntry :
318+
createTestMatrixForProtectSubDirs()) {
319+
Configuration conf = new HdfsConfiguration();
320+
conf.setBoolean(DFS_PROTECTED_SUBDIRECTORIES_ENABLE, true);
321+
MiniDFSCluster cluster = setupTestCase(
322+
conf, testMatrixEntry.getProtectedPaths(),
323+
testMatrixEntry.getUnprotectedPaths());
324+
325+
try {
326+
LOG.info("Running {}", testMatrixEntry);
327+
FileSystem fs = cluster.getFileSystem();
328+
for (Path srcPath : testMatrixEntry.getAllPathsToBeDeleted()) {
329+
assertThat(
330+
testMatrixEntry + ": Testing whether "
331+
+ srcPath + " can be renamed",
332+
renamePath(fs, srcPath,
333+
new Path(srcPath.toString() + "_renamed")),
334+
is(testMatrixEntry.canPathBeRenamed(srcPath)));
335+
}
336+
} finally {
337+
cluster.shutdown();
338+
}
339+
}
340+
}
341+
342+
@Test
343+
public void testDeleteProtectSubDirs() throws Throwable {
344+
for (TestMatrixEntry testMatrixEntry :
345+
createTestMatrixForProtectSubDirs()) {
346+
Configuration conf = new HdfsConfiguration();
347+
conf.setBoolean(DFS_PROTECTED_SUBDIRECTORIES_ENABLE, true);
348+
MiniDFSCluster cluster = setupTestCase(
349+
conf, testMatrixEntry.getProtectedPaths(),
350+
testMatrixEntry.getUnprotectedPaths());
351+
352+
try {
353+
LOG.info("Running {}", testMatrixEntry);
354+
FileSystem fs = cluster.getFileSystem();
355+
for (Path path : testMatrixEntry.getAllPathsToBeDeleted()) {
356+
final long countBefore = cluster.getNamesystem().getFilesTotal();
357+
assertThat(
358+
testMatrixEntry + ": Testing whether "
359+
+ path + " can be deleted",
360+
deletePath(fs, path),
361+
is(testMatrixEntry.canPathBeDeleted(path)));
362+
final long countAfter = cluster.getNamesystem().getFilesTotal();
363+
364+
if (!testMatrixEntry.canPathBeDeleted(path)) {
365+
assertThat(
366+
"Either all paths should be deleted or none",
367+
countAfter, is(countBefore));
368+
}
369+
}
370+
} finally {
371+
cluster.shutdown();
372+
}
373+
}
374+
}
375+
295376
/**
296377
* Verify that configured paths are normalized by removing
297378
* redundant separators.

0 commit comments

Comments
 (0)