From dacd1f50feb24ccdf6155b2b7a6126fe21a47ad0 Mon Sep 17 00:00:00 2001 From: Xiaoyu Yao Date: Thu, 5 May 2016 18:07:47 -0700 Subject: [PATCH] HDFS-10324. Trash directory in an encryption zone should be pre-created with correct permissions. Contributed by Wei-Chiu Chuang. --- .../apache/hadoop/fs/TrashPolicyDefault.java | 1 - .../hdfs/nfs/nfs3/TestRpcProgramNfs3.java | 6 +- .../hdfs/client/CreateEncryptionZoneFlag.java | 71 ++++++ .../apache/hadoop/hdfs/client/HdfsAdmin.java | 89 +++++++- .../hadoop/hdfs/client/package-info.java | 27 +++ .../apache/hadoop/hdfs/tools/CryptoAdmin.java | 69 +++++- .../src/site/markdown/HDFSCommands.md | 1 + .../site/markdown/TransparentEncryption.md | 13 +- .../hadoop/hdfs/TestEncryptionZones.java | 206 +++++++++++++----- .../hdfs/TestEncryptionZonesWithHA.java | 7 +- .../hdfs/TestEncryptionZonesWithKMS.java | 4 +- .../hadoop/hdfs/TestReservedRawPaths.java | 12 +- .../src/test/resources/testCryptoConf.xml | 86 ++++++++ 13 files changed, 519 insertions(+), 73 deletions(-) create mode 100644 hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/client/CreateEncryptionZoneFlag.java create mode 100644 hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/client/package-info.java diff --git a/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/fs/TrashPolicyDefault.java b/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/fs/TrashPolicyDefault.java index 012ce32df1..14f4c0c1ca 100644 --- a/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/fs/TrashPolicyDefault.java +++ b/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/fs/TrashPolicyDefault.java @@ -56,7 +56,6 @@ public class TrashPolicyDefault extends TrashPolicy { LogFactory.getLog(TrashPolicyDefault.class); private static final Path CURRENT = new Path("Current"); - private static final Path TRASH = new Path(".Trash/"); private static final FsPermission PERMISSION = new FsPermission(FsAction.ALL, FsAction.NONE, FsAction.NONE); diff --git a/hadoop-hdfs-project/hadoop-hdfs-nfs/src/test/java/org/apache/hadoop/hdfs/nfs/nfs3/TestRpcProgramNfs3.java b/hadoop-hdfs-project/hadoop-hdfs-nfs/src/test/java/org/apache/hadoop/hdfs/nfs/nfs3/TestRpcProgramNfs3.java index 10a175cb9b..811aedae25 100644 --- a/hadoop-hdfs-project/hadoop-hdfs-nfs/src/test/java/org/apache/hadoop/hdfs/nfs/nfs3/TestRpcProgramNfs3.java +++ b/hadoop-hdfs-project/hadoop-hdfs-nfs/src/test/java/org/apache/hadoop/hdfs/nfs/nfs3/TestRpcProgramNfs3.java @@ -29,7 +29,6 @@ import java.util.EnumSet; import org.apache.hadoop.crypto.key.JavaKeyStoreProvider; -import org.apache.hadoop.crypto.key.KeyProviderFactory; import org.apache.hadoop.fs.CommonConfigurationKeys; import org.apache.hadoop.fs.FSDataInputStream; import org.apache.hadoop.fs.FileSystemTestHelper; @@ -38,6 +37,7 @@ import org.apache.hadoop.hdfs.DFSTestUtil; import org.apache.hadoop.hdfs.DistributedFileSystem; import org.apache.hadoop.hdfs.MiniDFSCluster; +import org.apache.hadoop.hdfs.client.CreateEncryptionZoneFlag; import org.apache.hadoop.hdfs.client.HdfsAdmin; import org.apache.hadoop.hdfs.nfs.conf.NfsConfigKeys; import org.apache.hadoop.hdfs.nfs.conf.NfsConfiguration; @@ -118,6 +118,8 @@ public class TestRpcProgramNfs3 { private static final String TEST_KEY = "test_key"; private static FileSystemTestHelper fsHelper; private static File testRootDir; + private static final EnumSet NO_TRASH = + EnumSet.of(CreateEncryptionZoneFlag.NO_TRASH); @BeforeClass public static void setup() throws Exception { @@ -340,7 +342,7 @@ public void testEncryptedReadWrite() throws Exception { final Path zone = new Path("/zone"); hdfs.mkdirs(zone); - dfsAdmin.createEncryptionZone(zone, TEST_KEY); + dfsAdmin.createEncryptionZone(zone, TEST_KEY, NO_TRASH); final byte[] buffer = new byte[len]; for (int i = 0; i < len; i++) { diff --git a/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/client/CreateEncryptionZoneFlag.java b/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/client/CreateEncryptionZoneFlag.java new file mode 100644 index 0000000000..ccf9193265 --- /dev/null +++ b/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/client/CreateEncryptionZoneFlag.java @@ -0,0 +1,71 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.hadoop.hdfs.client; + +import org.apache.hadoop.classification.InterfaceAudience; +import org.apache.hadoop.classification.InterfaceStability; + +/** + * CreateEncryptionZoneFlag is used in + * {@link HdfsAdmin#createEncryptionZone(Path, String, EnumSet)} to indicate + * what should be done when creating an encryption zone. + * + * Use CreateEncryptionZoneFlag as follows: + *
    + *
  1. PROVISION_TRASH - provision a trash directory for the encryption zone + * to support soft delete.
  2. + *
+ */ +@InterfaceAudience.Public +@InterfaceStability.Evolving +public enum CreateEncryptionZoneFlag { + + /** + * Do not provision a trash directory in the encryption zone. + * + * @see CreateEncryptionZoneFlag#NO_TRASH + */ + NO_TRASH((short) 0x00), + /** + * Provision a trash directory .Trash/ in the + * encryption zone. + * + * @see CreateEncryptionZoneFlag#PROVISION_TRASH + */ + PROVISION_TRASH((short) 0x01); + + private final short mode; + + CreateEncryptionZoneFlag(short mode) { + this.mode = mode; + } + + public static CreateEncryptionZoneFlag valueOf(short mode) { + for (CreateEncryptionZoneFlag flag : CreateEncryptionZoneFlag.values()) { + if (flag.getMode() == mode) { + return flag; + } + } + return null; + } + + public short getMode() { + return mode; + } +} + diff --git a/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/client/HdfsAdmin.java b/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/client/HdfsAdmin.java index 7b055fc8f6..f5f29abd2e 100644 --- a/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/client/HdfsAdmin.java +++ b/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/client/HdfsAdmin.java @@ -22,14 +22,18 @@ import java.net.URI; import java.util.EnumSet; +import org.apache.hadoop.HadoopIllegalArgumentException; import org.apache.hadoop.classification.InterfaceAudience; import org.apache.hadoop.classification.InterfaceStability; import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.fs.CacheFlag; +import org.apache.hadoop.fs.FileStatus; import org.apache.hadoop.fs.FileSystem; import org.apache.hadoop.fs.Path; import org.apache.hadoop.fs.RemoteIterator; import org.apache.hadoop.fs.StorageType; +import org.apache.hadoop.fs.permission.FsAction; +import org.apache.hadoop.fs.permission.FsPermission; import org.apache.hadoop.hdfs.DFSInotifyEventInputStream; import org.apache.hadoop.hdfs.DistributedFileSystem; import org.apache.hadoop.hdfs.protocol.CacheDirectiveEntry; @@ -56,6 +60,8 @@ public class HdfsAdmin { private DistributedFileSystem dfs; + private static final FsPermission TRASH_PERMISSION = new FsPermission( + FsAction.ALL, FsAction.ALL, FsAction.ALL, true); /** * Create a new HdfsAdmin client. @@ -270,11 +276,53 @@ public RemoteIterator listCachePools() throws IOException { * @throws AccessControlException if the caller does not have access to path * @throws FileNotFoundException if the path does not exist */ + @Deprecated public void createEncryptionZone(Path path, String keyName) - throws IOException, AccessControlException, FileNotFoundException { + throws IOException, AccessControlException, FileNotFoundException { dfs.createEncryptionZone(path, keyName); } + /** + * Create an encryption zone rooted at an empty existing directory, using the + * specified encryption key. An encryption zone has an associated encryption + * key used when reading and writing files within the zone. + * + * Additional options, such as provisioning the trash directory, can be + * specified using {@link CreateEncryptionZoneFlag} flags. + * + * @param path The path of the root of the encryption zone. Must refer to + * an empty, existing directory. + * @param keyName Name of key available at the KeyProvider. + * @param flags flags for this operation. + * @throws IOException if there was a general IO exception + * @throws AccessControlException if the caller does not have access to path + * @throws FileNotFoundException if the path does not exist + * @throws HadoopIllegalArgumentException if the flags are invalid + */ + public void createEncryptionZone(Path path, String keyName, + EnumSet flags) + throws IOException, AccessControlException, FileNotFoundException, + HadoopIllegalArgumentException{ + dfs.createEncryptionZone(path, keyName); + if (flags.contains(CreateEncryptionZoneFlag.PROVISION_TRASH)) { + if (flags.contains(CreateEncryptionZoneFlag.NO_TRASH)) { + throw new HadoopIllegalArgumentException( + "can not have both PROVISION_TRASH and NO_TRASH flags"); + } + this.provisionEZTrash(path); + } + } + + /** + * Provision a trash directory for a given encryption zone. + + * @param path the root of the encryption zone + * @throws IOException if the trash directory can not be created. + */ + public void provisionEncryptionZoneTrash(Path path) throws IOException { + this.provisionEZTrash(path); + } + /** * Get the path of the encryption zone for a given file or directory. * @@ -399,4 +447,43 @@ public ErasureCodingPolicy getErasureCodingPolicy(final Path path) public ErasureCodingPolicy[] getErasureCodingPolicies() throws IOException { return dfs.getClient().getErasureCodingPolicies(); } + + private void provisionEZTrash(Path path) throws IOException { + // make sure the path is an EZ + EncryptionZone ez = dfs.getEZForPath(path); + if (ez == null) { + throw new IllegalArgumentException(path + " is not an encryption zone."); + } + + String ezPath = ez.getPath(); + if (!path.toString().equals(ezPath)) { + throw new IllegalArgumentException(path + " is not the root of an " + + "encryption zone. Do you mean " + ez.getPath() + "?"); + } + + // check if the trash directory exists + + Path trashPath = new Path(ez.getPath(), FileSystem.TRASH_PREFIX); + + if (dfs.exists(trashPath)) { + String errMessage = "Will not provision new trash directory for " + + "encryption zone " + ez.getPath() + ". Path already exists."; + FileStatus trashFileStatus = dfs.getFileStatus(trashPath); + if (!trashFileStatus.isDirectory()) { + errMessage += "\r\n" + + "Warning: " + trashPath.toString() + " is not a directory"; + } + if (!trashFileStatus.getPermission().equals(TRASH_PERMISSION)) { + errMessage += "\r\n" + + "Warning: the permission of " + + trashPath.toString() + " is not " + TRASH_PERMISSION; + } + throw new IOException(errMessage); + } + + // Update the permission bits + dfs.mkdir(trashPath, TRASH_PERMISSION); + dfs.setPermission(trashPath, TRASH_PERMISSION); + } + } diff --git a/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/client/package-info.java b/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/client/package-info.java new file mode 100644 index 0000000000..95eceb7eae --- /dev/null +++ b/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/client/package-info.java @@ -0,0 +1,27 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * This package provides the administrative APIs for HDFS. + */ +@InterfaceAudience.Public +@InterfaceStability.Evolving +package org.apache.hadoop.hdfs.client; + +import org.apache.hadoop.classification.InterfaceAudience; +import org.apache.hadoop.classification.InterfaceStability; \ No newline at end of file diff --git a/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/tools/CryptoAdmin.java b/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/tools/CryptoAdmin.java index 09825b0fed..06389a159f 100644 --- a/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/tools/CryptoAdmin.java +++ b/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/tools/CryptoAdmin.java @@ -18,15 +18,18 @@ package org.apache.hadoop.hdfs.tools; import java.io.IOException; +import java.util.EnumSet; import java.util.LinkedList; import java.util.List; import org.apache.hadoop.classification.InterfaceAudience; import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.conf.Configured; +import org.apache.hadoop.fs.FileSystem; import org.apache.hadoop.fs.Path; import org.apache.hadoop.fs.RemoteIterator; -import org.apache.hadoop.hdfs.DistributedFileSystem; +import org.apache.hadoop.hdfs.client.CreateEncryptionZoneFlag; +import org.apache.hadoop.hdfs.client.HdfsAdmin; import org.apache.hadoop.hdfs.protocol.EncryptionZone; import org.apache.hadoop.tools.TableListing; import org.apache.hadoop.util.StringUtils; @@ -103,12 +106,13 @@ public String getShortUsage() { public String getLongUsage() { final TableListing listing = AdminHelper.getOptionDescriptionListing(); listing.addRow("", "The path of the encryption zone to create. " + - "It must be an empty directory."); + "It must be an empty directory. A trash directory is provisioned " + + "under this path."); listing.addRow("", "Name of the key to use for the " + "encryption zone."); return getShortUsage() + "\n" + - "Create a new encryption zone.\n\n" + - listing.toString(); + "Create a new encryption zone.\n\n" + + listing.toString(); } @Override @@ -131,15 +135,16 @@ public int run(Configuration conf, List args) throws IOException { return 1; } - final DistributedFileSystem dfs = AdminHelper.getDFS(conf); + HdfsAdmin admin = new HdfsAdmin(FileSystem.getDefaultUri(conf), conf); + EnumSet flags = + EnumSet.of(CreateEncryptionZoneFlag.PROVISION_TRASH); try { - dfs.createEncryptionZone(new Path(path), keyName); + admin.createEncryptionZone(new Path(path), keyName, flags); System.out.println("Added encryption zone " + path); } catch (IOException e) { System.err.println(prettifyException(e)); return 2; } - return 0; } } @@ -168,12 +173,12 @@ public int run(Configuration conf, List args) throws IOException { return 1; } - final DistributedFileSystem dfs = AdminHelper.getDFS(conf); + HdfsAdmin admin = new HdfsAdmin(FileSystem.getDefaultUri(conf), conf); try { final TableListing listing = new TableListing.Builder() .addField("").addField("", true) .wrapWidth(AdminHelper.MAX_LINE_WIDTH).hideHeaders().build(); - final RemoteIterator it = dfs.listEncryptionZones(); + final RemoteIterator it = admin.listEncryptionZones(); while (it.hasNext()) { EncryptionZone ez = it.next(); listing.addRow(ez.getPath(), ez.getKeyName()); @@ -188,8 +193,50 @@ public int run(Configuration conf, List args) throws IOException { } } + private static class ProvisionTrashCommand implements AdminHelper.Command { + @Override + public String getName() { + return "-provisionTrash"; + } + + @Override + public String getShortUsage() { + return "[" + getName() + " -path ]\n"; + } + + @Override + public String getLongUsage() { + final TableListing listing = AdminHelper.getOptionDescriptionListing(); + listing.addRow("", "The path to the root of the encryption zone. "); + return getShortUsage() + "\n" + + "Provision a trash directory for an encryption zone.\n\n" + + listing.toString(); + } + + @Override + public int run(Configuration conf, List args) throws IOException { + final String path = StringUtils.popOptionWithArgument("-path", args); + + if (!args.isEmpty()) { + System.err.println("Can't understand argument: " + args.get(0)); + return 1; + } + + HdfsAdmin admin = new HdfsAdmin(FileSystem.getDefaultUri(conf), conf); + try { + admin.provisionEncryptionZoneTrash(new Path(path)); + System.out.println("Created a trash directory for " + path); + } catch (IOException ioe) { + System.err.println(prettifyException(ioe)); + return 2; + } + return 0; + } + } + private static final AdminHelper.Command[] COMMANDS = { - new CreateZoneCommand(), - new ListZonesCommand() + new CreateZoneCommand(), + new ListZonesCommand(), + new ProvisionTrashCommand() }; } diff --git a/hadoop-hdfs-project/hadoop-hdfs/src/site/markdown/HDFSCommands.md b/hadoop-hdfs-project/hadoop-hdfs/src/site/markdown/HDFSCommands.md index a6c8b4c4c9..13c5f0cece 100644 --- a/hadoop-hdfs-project/hadoop-hdfs/src/site/markdown/HDFSCommands.md +++ b/hadoop-hdfs-project/hadoop-hdfs/src/site/markdown/HDFSCommands.md @@ -338,6 +338,7 @@ Usage: hdfs crypto -createZone -keyName -path hdfs crypto -listZones + hdfs crypto -provisionTrash -path hdfs crypto -help See the [HDFS Transparent Encryption Documentation](./TransparentEncryption.html#crypto_command-line_interface) for more information. diff --git a/hadoop-hdfs-project/hadoop-hdfs/src/site/markdown/TransparentEncryption.md b/hadoop-hdfs-project/hadoop-hdfs/src/site/markdown/TransparentEncryption.md index 3b04255b99..6d0e52c891 100644 --- a/hadoop-hdfs-project/hadoop-hdfs/src/site/markdown/TransparentEncryption.md +++ b/hadoop-hdfs-project/hadoop-hdfs/src/site/markdown/TransparentEncryption.md @@ -170,7 +170,7 @@ Create a new encryption zone. | | | |:---- |:---- | -| *path* | The path of the encryption zone to create. It must be an empty directory. | +| *path* | The path of the encryption zone to create. It must be an empty directory. A trash directory is provisioned under this path.| | *keyName* | Name of the key to use for the encryption zone. | ### listZones @@ -179,6 +179,16 @@ Usage: `[-listZones]` List all encryption zones. Requires superuser permissions. +### provisionTrash + +Usage: `[-provisionTrash -path ]` + +Provision a trash directory for an encryption zone. + +| | | +|:---- |:---- | +| *path* | The path to the root of the encryption zone. | + Example usage ------------- @@ -222,6 +232,7 @@ This restriction enhances security and eases system management significantly. Al To comply with the above rule, each encryption zone has its own `.Trash` directory under the "zone directory". E.g., after `hdfs dfs rm /zone/encryptedFile`, `encryptedFile` will be moved to `/zone/.Trash`, instead of the `.Trash` directory under the user's home directory. When the entire encryption zone is deleted, the "zone directory" will be moved to the `.Trash` directory under the user's home directory. +The `crypto` command before Hadoop 2.8.0 does not provision the `.Trash` directory automatically. If an encryption zone is created before Hadoop 2.8.0, and then the cluster is upgraded to Hadoop 2.8.0 or above, the trash directory can be provisioned using `-provisionTrash` option (e.g., `hdfs crypto -provisionTrash -path /zone`). Attack vectors -------------- diff --git a/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/TestEncryptionZones.java b/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/TestEncryptionZones.java index ce2befde75..e221fe4a4f 100644 --- a/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/TestEncryptionZones.java +++ b/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/TestEncryptionZones.java @@ -29,6 +29,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; +import java.util.EnumSet; import java.util.List; import java.util.concurrent.Callable; import java.util.concurrent.CountDownLatch; @@ -59,7 +60,9 @@ import org.apache.hadoop.fs.FsShell; import org.apache.hadoop.fs.Path; import org.apache.hadoop.fs.RemoteIterator; +import org.apache.hadoop.fs.permission.FsAction; import org.apache.hadoop.fs.permission.FsPermission; +import org.apache.hadoop.hdfs.client.CreateEncryptionZoneFlag; import org.apache.hadoop.hdfs.client.HdfsAdmin; import org.apache.hadoop.hdfs.protocol.ClientProtocol; import org.apache.hadoop.hdfs.protocol.EncryptionZone; @@ -70,6 +73,7 @@ import org.apache.hadoop.hdfs.server.namenode.EncryptionZoneManager; import org.apache.hadoop.hdfs.server.namenode.FSImageTestUtil; import org.apache.hadoop.hdfs.server.namenode.NamenodeFsck; +import org.apache.hadoop.hdfs.tools.CryptoAdmin; import org.apache.hadoop.hdfs.tools.DFSck; import org.apache.hadoop.hdfs.tools.offlineImageViewer.PBImageXmlWriter; import org.apache.hadoop.hdfs.web.WebHdfsConstants; @@ -131,6 +135,9 @@ public class TestEncryptionZones { protected FileSystemTestWrapper fsWrapper; protected FileContextTestWrapper fcWrapper; + protected static final EnumSet< CreateEncryptionZoneFlag > NO_TRASH = + EnumSet.of(CreateEncryptionZoneFlag.NO_TRASH); + protected String getKeyProviderURI() { return JavaKeyStoreProvider.SCHEME_NAME + "://file" + new Path(testRootDir.toString(), "test.jks").toUri(); @@ -215,6 +222,106 @@ public void assertZonePresent(String keyName, String path) throws IOException { ); } + /** + * Make sure hdfs crypto -createZone command creates a trash directory + * with sticky bits. + * @throws Exception + */ + @Test(timeout = 60000) + public void testTrashStickyBit() throws Exception { + // create an EZ /zones/zone1, make it world writable. + final Path zoneParent = new Path("/zones"); + final Path zone1 = new Path(zoneParent, "zone1"); + CryptoAdmin cryptoAdmin = new CryptoAdmin(conf); + fsWrapper.mkdir(zone1, FsPermission.getDirDefault(), true); + fsWrapper.setPermission(zone1, + new FsPermission(FsAction.ALL, FsAction.ALL, FsAction.ALL)); + String[] cryptoArgv = new String[]{"-createZone", "-keyName", TEST_KEY, + "-path", zone1.toUri().getPath()}; + cryptoAdmin.run(cryptoArgv); + + // create a file in EZ + final Path ezfile1 = new Path(zone1, "file1"); + // Create the encrypted file in zone1 + final int len = 8192; + DFSTestUtil.createFile(fs, ezfile1, len, (short) 1, 0xFEED); + + // enable trash, delete /zones/zone1/file1, + // which moves the file to + // /zones/zone1/.Trash/$SUPERUSER/Current/zones/zone1/file1 + Configuration clientConf = new Configuration(conf); + clientConf.setLong(FS_TRASH_INTERVAL_KEY, 1); + final FsShell shell = new FsShell(clientConf); + String[] argv = new String[]{"-rm", ezfile1.toString()}; + int res = ToolRunner.run(shell, argv); + assertEquals("Can't remove a file in EZ as superuser", 0, res); + + final Path trashDir = new Path(zone1, FileSystem.TRASH_PREFIX); + assertTrue(fsWrapper.exists(trashDir)); + FileStatus trashFileStatus = fsWrapper.getFileStatus(trashDir); + assertTrue(trashFileStatus.getPermission().getStickyBit()); + + // create a non-privileged user + final UserGroupInformation user = UserGroupInformation. + createUserForTesting("user", new String[] { "mygroup" }); + + user.doAs(new PrivilegedExceptionAction() { + @Override + public Object run() throws Exception { + final Path ezfile2 = new Path(zone1, "file2"); + final int len = 8192; + // create a file /zones/zone1/file2 in EZ + // this file is owned by user:mygroup + FileSystem fs2 = FileSystem.get(cluster.getConfiguration(0)); + DFSTestUtil.createFile(fs2, ezfile2, len, (short) 1, 0xFEED); + // delete /zones/zone1/file2, + // which moves the file to + // /zones/zone1/.Trash/user/Current/zones/zone1/file2 + String[] argv = new String[]{"-rm", ezfile2.toString()}; + int res = ToolRunner.run(shell, argv); + assertEquals("Can't remove a file in EZ as user:mygroup", 0, res); + return null; + } + }); + } + + /** + * Make sure hdfs crypto -provisionTrash command creates a trash directory + * with sticky bits. + * @throws Exception + */ + @Test(timeout = 60000) + public void testProvisionTrash() throws Exception { + // create an EZ /zones/zone1 + final Path zoneParent = new Path("/zones"); + final Path zone1 = new Path(zoneParent, "zone1"); + CryptoAdmin cryptoAdmin = new CryptoAdmin(conf); + fsWrapper.mkdir(zone1, FsPermission.getDirDefault(), true); + String[] cryptoArgv = new String[]{"-createZone", "-keyName", TEST_KEY, + "-path", zone1.toUri().getPath()}; + cryptoAdmin.run(cryptoArgv); + + // remove the trash directory + Configuration clientConf = new Configuration(conf); + clientConf.setLong(FS_TRASH_INTERVAL_KEY, 1); + final FsShell shell = new FsShell(clientConf); + final Path trashDir = new Path(zone1, FileSystem.TRASH_PREFIX); + String[] argv = new String[]{"-rmdir", trashDir.toUri().getPath()}; + int res = ToolRunner.run(shell, argv); + assertEquals("Unable to delete trash directory.", 0, res); + assertFalse(fsWrapper.exists(trashDir)); + + // execute -provisionTrash command option and make sure the trash + // directory has sticky bit. + String[] provisionTrashArgv = new String[]{"-provisionTrash", "-path", + zone1.toUri().getPath()}; + cryptoAdmin.run(provisionTrashArgv); + + assertTrue(fsWrapper.exists(trashDir)); + FileStatus trashFileStatus = fsWrapper.getFileStatus(trashDir); + assertTrue(trashFileStatus.getPermission().getStickyBit()); + } + @Test(timeout = 60000) public void testBasicOperations() throws Exception { @@ -223,8 +330,9 @@ public void testBasicOperations() throws Exception { /* Test failure of create EZ on a directory that doesn't exist. */ final Path zoneParent = new Path("/zones"); final Path zone1 = new Path(zoneParent, "zone1"); + try { - dfsAdmin.createEncryptionZone(zone1, TEST_KEY); + dfsAdmin.createEncryptionZone(zone1, TEST_KEY, NO_TRASH); fail("expected /test doesn't exist"); } catch (IOException e) { assertExceptionContains("cannot find", e); @@ -232,20 +340,20 @@ public void testBasicOperations() throws Exception { /* Normal creation of an EZ */ fsWrapper.mkdir(zone1, FsPermission.getDirDefault(), true); - dfsAdmin.createEncryptionZone(zone1, TEST_KEY); + dfsAdmin.createEncryptionZone(zone1, TEST_KEY, NO_TRASH); assertNumZones(++numZones); assertZonePresent(null, zone1.toString()); /* Test failure of create EZ on a directory which is already an EZ. */ try { - dfsAdmin.createEncryptionZone(zone1, TEST_KEY); + dfsAdmin.createEncryptionZone(zone1, TEST_KEY, NO_TRASH); } catch (IOException e) { assertExceptionContains("is already an encryption zone", e); } /* create EZ on parent of an EZ should fail */ try { - dfsAdmin.createEncryptionZone(zoneParent, TEST_KEY); + dfsAdmin.createEncryptionZone(zoneParent, TEST_KEY, NO_TRASH); fail("EZ over an EZ"); } catch (IOException e) { assertExceptionContains("encryption zone for a non-empty directory", e); @@ -256,7 +364,7 @@ public void testBasicOperations() throws Exception { final Path notEmptyChild = new Path(notEmpty, "child"); fsWrapper.mkdir(notEmptyChild, FsPermission.getDirDefault(), true); try { - dfsAdmin.createEncryptionZone(notEmpty, TEST_KEY); + dfsAdmin.createEncryptionZone(notEmpty, TEST_KEY, NO_TRASH); fail("Created EZ on an non-empty directory with folder"); } catch (IOException e) { assertExceptionContains("create an encryption zone", e); @@ -266,7 +374,7 @@ public void testBasicOperations() throws Exception { /* create EZ on a folder with a file fails */ fsWrapper.createFile(notEmptyChild); try { - dfsAdmin.createEncryptionZone(notEmpty, TEST_KEY); + dfsAdmin.createEncryptionZone(notEmpty, TEST_KEY, NO_TRASH); fail("Created EZ on an non-empty directory with file"); } catch (IOException e) { assertExceptionContains("create an encryption zone", e); @@ -274,7 +382,7 @@ public void testBasicOperations() throws Exception { /* Test failure of create EZ on a file. */ try { - dfsAdmin.createEncryptionZone(notEmptyChild, TEST_KEY); + dfsAdmin.createEncryptionZone(notEmptyChild, TEST_KEY, NO_TRASH); fail("Created EZ on a file"); } catch (IOException e) { assertExceptionContains("create an encryption zone for a file.", e); @@ -285,7 +393,7 @@ public void testBasicOperations() throws Exception { fsWrapper.mkdir(zone2, FsPermission.getDirDefault(), false); final String myKeyName = "mykeyname"; try { - dfsAdmin.createEncryptionZone(zone2, myKeyName); + dfsAdmin.createEncryptionZone(zone2, myKeyName, NO_TRASH); fail("expected key doesn't exist"); } catch (IOException e) { assertExceptionContains("doesn't exist.", e); @@ -293,13 +401,13 @@ public void testBasicOperations() throws Exception { /* Test failure of empty and null key name */ try { - dfsAdmin.createEncryptionZone(zone2, ""); + dfsAdmin.createEncryptionZone(zone2, "", NO_TRASH); fail("created a zone with empty key name"); } catch (IOException e) { assertExceptionContains("Must specify a key name when creating", e); } try { - dfsAdmin.createEncryptionZone(zone2, null); + dfsAdmin.createEncryptionZone(zone2, null, NO_TRASH); fail("created a zone with null key name"); } catch (IOException e) { assertExceptionContains("Must specify a key name when creating", e); @@ -309,7 +417,7 @@ public void testBasicOperations() throws Exception { /* Test success of creating an EZ when they key exists. */ DFSTestUtil.createKey(myKeyName, cluster, conf); - dfsAdmin.createEncryptionZone(zone2, myKeyName); + dfsAdmin.createEncryptionZone(zone2, myKeyName, NO_TRASH); assertNumZones(++numZones); assertZonePresent(myKeyName, zone2.toString()); @@ -325,7 +433,7 @@ public Object run() throws Exception { final HdfsAdmin userAdmin = new HdfsAdmin(FileSystem.getDefaultUri(conf), conf); try { - userAdmin.createEncryptionZone(nonSuper, TEST_KEY); + userAdmin.createEncryptionZone(nonSuper, TEST_KEY, NO_TRASH); fail("createEncryptionZone is superuser-only operation"); } catch (AccessControlException e) { assertExceptionContains("Superuser privilege is required", e); @@ -337,7 +445,7 @@ public Object run() throws Exception { // Test success of creating an encryption zone a few levels down. Path deepZone = new Path("/d/e/e/p/zone"); fsWrapper.mkdir(deepZone, FsPermission.getDirDefault(), true); - dfsAdmin.createEncryptionZone(deepZone, TEST_KEY); + dfsAdmin.createEncryptionZone(deepZone, TEST_KEY, NO_TRASH); assertNumZones(++numZones); assertZonePresent(null, deepZone.toString()); @@ -345,7 +453,7 @@ public Object run() throws Exception { for (int i=1; i<6; i++) { final Path zonePath = new Path("/listZone" + i); fsWrapper.mkdir(zonePath, FsPermission.getDirDefault(), false); - dfsAdmin.createEncryptionZone(zonePath, TEST_KEY); + dfsAdmin.createEncryptionZone(zonePath, TEST_KEY, NO_TRASH); numZones++; assertNumZones(numZones); assertZonePresent(null, zonePath.toString()); @@ -365,7 +473,7 @@ public Object run() throws Exception { // without persisting the namespace. Path nonpersistZone = new Path("/nonpersistZone"); fsWrapper.mkdir(nonpersistZone, FsPermission.getDirDefault(), false); - dfsAdmin.createEncryptionZone(nonpersistZone, TEST_KEY); + dfsAdmin.createEncryptionZone(nonpersistZone, TEST_KEY, NO_TRASH); numZones++; cluster.restartNameNode(true); assertNumZones(numZones); @@ -379,7 +487,7 @@ public void testBasicOperationsRootDir() throws Exception { final Path zone1 = new Path(rootDir, "zone1"); /* Normal creation of an EZ on rootDir */ - dfsAdmin.createEncryptionZone(rootDir, TEST_KEY); + dfsAdmin.createEncryptionZone(rootDir, TEST_KEY, NO_TRASH); assertNumZones(++numZones); assertZonePresent(null, rootDir.toString()); @@ -407,10 +515,10 @@ public void testListEncryptionZonesAsNonSuperUser() throws Exception { final Path allPath = new Path(testRoot, "accessall"); fsWrapper.mkdir(superPath, new FsPermission((short) 0700), true); - dfsAdmin.createEncryptionZone(superPath, TEST_KEY); + dfsAdmin.createEncryptionZone(superPath, TEST_KEY, NO_TRASH); fsWrapper.mkdir(allPath, new FsPermission((short) 0707), true); - dfsAdmin.createEncryptionZone(allPath, TEST_KEY); + dfsAdmin.createEncryptionZone(allPath, TEST_KEY, NO_TRASH); user.doAs(new PrivilegedExceptionAction() { @Override @@ -450,8 +558,8 @@ public void testGetEZAsNonSuperUser() throws Exception { fsWrapper.mkdir(superPath, new FsPermission((short) 0700), false); fsWrapper.mkdir(allPath, new FsPermission((short) 0777), false); fsWrapper.mkdir(nonEZDir, new FsPermission((short) 0777), false); - dfsAdmin.createEncryptionZone(superPath, TEST_KEY); - dfsAdmin.createEncryptionZone(allPath, TEST_KEY); + dfsAdmin.createEncryptionZone(superPath, TEST_KEY, NO_TRASH); + dfsAdmin.createEncryptionZone(allPath, TEST_KEY, NO_TRASH); dfsAdmin.allowSnapshot(new Path("/")); final Path newSnap = fs.createSnapshot(new Path("/")); DFSTestUtil.createFile(fs, superPathFile, len, (short) 1, 0xFEED); @@ -556,7 +664,7 @@ private void doRenameEncryptionZone(FSTestWrapper wrapper) throws Exception { final Path pathFooBarFile = new Path(pathFooBar, "file"); final int len = 8192; wrapper.mkdir(pathFoo, FsPermission.getDirDefault(), true); - dfsAdmin.createEncryptionZone(pathFoo, TEST_KEY); + dfsAdmin.createEncryptionZone(pathFoo, TEST_KEY, NO_TRASH); wrapper.mkdir(pathFooBaz, FsPermission.getDirDefault(), true); DFSTestUtil.createFile(fs, pathFooBazFile, len, (short) 1, 0xFEED); String contents = DFSTestUtil.readFile(fs, pathFooBazFile); @@ -615,7 +723,7 @@ public void testReadWrite() throws Exception { // Create the first enc file final Path zone = new Path("/zone"); fs.mkdirs(zone); - dfsAdmin.createEncryptionZone(zone, TEST_KEY); + dfsAdmin.createEncryptionZone(zone, TEST_KEY, NO_TRASH); final Path encFile1 = new Path(zone, "myfile"); DFSTestUtil.createFile(fs, encFile1, len, (short) 1, 0xFEED); // Read them back in and compare byte-by-byte @@ -650,7 +758,7 @@ public void testReadWriteUsingWebHdfs() throws Exception { final Path zone = new Path("/zone"); fs.mkdirs(zone); - dfsAdmin.createEncryptionZone(zone, TEST_KEY); + dfsAdmin.createEncryptionZone(zone, TEST_KEY, NO_TRASH); /* Create an unencrypted file for comparison purposes. */ final Path unencFile = new Path("/unenc"); @@ -696,7 +804,7 @@ public void testVersionAndSuiteNegotiation() throws Exception { new HdfsAdmin(FileSystem.getDefaultUri(conf), conf); final Path zone = new Path("/zone"); fs.mkdirs(zone); - dfsAdmin.createEncryptionZone(zone, TEST_KEY); + dfsAdmin.createEncryptionZone(zone, TEST_KEY, NO_TRASH); // Create a file in an EZ, which should succeed DFSTestUtil .createFile(fs, new Path(zone, "success1"), 0, (short) 1, 0xFEED); @@ -827,7 +935,7 @@ public void testCreateEZWithNoProvider() throws Exception { final Path zone1 = new Path("/zone1"); fsWrapper.mkdir(zone1, FsPermission.getDirDefault(), true); try { - dfsAdmin.createEncryptionZone(zone1, TEST_KEY); + dfsAdmin.createEncryptionZone(zone1, TEST_KEY, NO_TRASH); fail("expected exception"); } catch (IOException e) { assertExceptionContains("since no key provider is available", e); @@ -869,7 +977,7 @@ private void dTIEM(Path prefix) throws Exception { // Create an encrypted file to check isEncrypted returns true final Path zone = new Path(prefix, "zone"); fsWrapper.mkdir(zone, FsPermission.getDirDefault(), true); - dfsAdmin.createEncryptionZone(zone, TEST_KEY); + dfsAdmin.createEncryptionZone(zone, TEST_KEY, NO_TRASH); final Path encFile = new Path(zone, "encfile"); fsWrapper.createFile(encFile); stat = fsWrapper.getFileStatus(encFile); @@ -1010,7 +1118,7 @@ public void testStartFileRetry() throws Exception { executor.submit(new InjectFaultTask() { @Override public void doFault() throws Exception { - dfsAdmin.createEncryptionZone(zone1, TEST_KEY); + dfsAdmin.createEncryptionZone(zone1, TEST_KEY, NO_TRASH); } @Override public void doCleanup() throws Exception { @@ -1036,14 +1144,14 @@ public void doCleanup() throws Exception { fsWrapper.mkdir(zone1, FsPermission.getDirDefault(), true); final String otherKey = "other_key"; DFSTestUtil.createKey(otherKey, cluster, conf); - dfsAdmin.createEncryptionZone(zone1, TEST_KEY); + dfsAdmin.createEncryptionZone(zone1, TEST_KEY, NO_TRASH); executor.submit(new InjectFaultTask() { @Override public void doFault() throws Exception { fsWrapper.delete(zone1, true); fsWrapper.mkdir(zone1, FsPermission.getDirDefault(), true); - dfsAdmin.createEncryptionZone(zone1, otherKey); + dfsAdmin.createEncryptionZone(zone1, otherKey, NO_TRASH); } @Override public void doCleanup() throws Exception { @@ -1056,7 +1164,7 @@ public void doCleanup() throws Exception { fsWrapper.mkdir(zone1, FsPermission.getDirDefault(), true); final String anotherKey = "another_key"; DFSTestUtil.createKey(anotherKey, cluster, conf); - dfsAdmin.createEncryptionZone(zone1, anotherKey); + dfsAdmin.createEncryptionZone(zone1, anotherKey, NO_TRASH); String keyToUse = otherKey; MyInjector injector = new MyInjector(); @@ -1068,7 +1176,7 @@ public void doCleanup() throws Exception { injector.ready.await(); fsWrapper.delete(zone1, true); fsWrapper.mkdir(zone1, FsPermission.getDirDefault(), true); - dfsAdmin.createEncryptionZone(zone1, keyToUse); + dfsAdmin.createEncryptionZone(zone1, keyToUse, NO_TRASH); if (keyToUse == otherKey) { keyToUse = anotherKey; } else { @@ -1129,7 +1237,7 @@ public void testFsckOnEncryptionZones() throws Exception { final Path zone1 = new Path(zoneParent, "zone1"); final Path zone1File = new Path(zone1, "file"); fsWrapper.mkdir(zone1, FsPermission.getDirDefault(), true); - dfsAdmin.createEncryptionZone(zone1, TEST_KEY); + dfsAdmin.createEncryptionZone(zone1, TEST_KEY, NO_TRASH); DFSTestUtil.createFile(fs, zone1File, len, (short) 1, 0xFEED); ByteArrayOutputStream bStream = new ByteArrayOutputStream(); PrintStream out = new PrintStream(bStream, true); @@ -1164,7 +1272,7 @@ public void testSnapshotsOnEncryptionZones() throws Exception { final Path zoneFile = new Path(zone, "zoneFile"); fsWrapper.mkdir(zone, FsPermission.getDirDefault(), true); dfsAdmin.allowSnapshot(zoneParent); - dfsAdmin.createEncryptionZone(zone, TEST_KEY); + dfsAdmin.createEncryptionZone(zone, TEST_KEY, NO_TRASH); DFSTestUtil.createFile(fs, zoneFile, len, (short) 1, 0xFEED); String contents = DFSTestUtil.readFile(fs, zoneFile); final Path snap1 = fs.createSnapshot(zoneParent, "snap1"); @@ -1182,7 +1290,7 @@ public void testSnapshotsOnEncryptionZones() throws Exception { dfsAdmin.getEncryptionZoneForPath(snap2Zone)); // Create the encryption zone again - dfsAdmin.createEncryptionZone(zone, TEST_KEY2); + dfsAdmin.createEncryptionZone(zone, TEST_KEY2, NO_TRASH); final Path snap3 = fs.createSnapshot(zoneParent, "snap3"); final Path snap3Zone = new Path(snap3, zone.getName()); // Check that snap3's EZ has the correct settings @@ -1245,7 +1353,7 @@ public void testEncryptionZonesWithSymlinks() throws Exception { final Path link = new Path(linkParent, "link"); final Path target = new Path(targetParent, "target"); fs.mkdirs(parent); - dfsAdmin.createEncryptionZone(parent, TEST_KEY); + dfsAdmin.createEncryptionZone(parent, TEST_KEY, NO_TRASH); fs.mkdirs(linkParent); fs.mkdirs(targetParent); DFSTestUtil.createFile(fs, target, len, (short)1, 0xFEED); @@ -1259,8 +1367,8 @@ public void testEncryptionZonesWithSymlinks() throws Exception { // encryption zones fs.mkdirs(linkParent); fs.mkdirs(targetParent); - dfsAdmin.createEncryptionZone(linkParent, TEST_KEY); - dfsAdmin.createEncryptionZone(targetParent, TEST_KEY); + dfsAdmin.createEncryptionZone(linkParent, TEST_KEY, NO_TRASH); + dfsAdmin.createEncryptionZone(targetParent, TEST_KEY, NO_TRASH); DFSTestUtil.createFile(fs, target, len, (short)1, 0xFEED); content = DFSTestUtil.readFile(fs, target); fs.createSymlink(target, link, false); @@ -1275,7 +1383,7 @@ public void testConcatFailsInEncryptionZones() throws Exception { final int len = 8192; final Path ez = new Path("/ez"); fs.mkdirs(ez); - dfsAdmin.createEncryptionZone(ez, TEST_KEY); + dfsAdmin.createEncryptionZone(ez, TEST_KEY, NO_TRASH); final Path src1 = new Path(ez, "src1"); final Path src2 = new Path(ez, "src2"); final Path target = new Path(ez, "target"); @@ -1302,7 +1410,7 @@ public void testOfflineImageViewerOnEncryptionZones() throws Exception { final Path zone1 = new Path(zoneParent, "zone1"); final Path zone1File = new Path(zone1, "file"); fsWrapper.mkdir(zone1, FsPermission.getDirDefault(), true); - dfsAdmin.createEncryptionZone(zone1, TEST_KEY); + dfsAdmin.createEncryptionZone(zone1, TEST_KEY, NO_TRASH); DFSTestUtil.createFile(fs, zone1File, len, (short) 1, 0xFEED); fs.setSafeMode(SafeModeAction.SAFEMODE_ENTER, false); fs.saveNamespace(); @@ -1332,7 +1440,7 @@ public void testEncryptionZonesOnRootPath() throws Exception { final Path rootDir = new Path("/"); final Path zoneFile = new Path(rootDir, "file"); final Path rawFile = new Path("/.reserved/raw/file"); - dfsAdmin.createEncryptionZone(rootDir, TEST_KEY); + dfsAdmin.createEncryptionZone(rootDir, TEST_KEY, NO_TRASH); DFSTestUtil.createFile(fs, zoneFile, len, (short) 1, 0xFEED); assertEquals("File can be created on the root encryption zone " + @@ -1353,7 +1461,7 @@ public void testEncryptionZonesOnRelativePath() throws Exception { final Path zoneFile = new Path("file"); fs.setWorkingDirectory(baseDir); fs.mkdirs(zoneDir); - dfsAdmin.createEncryptionZone(zoneDir, TEST_KEY); + dfsAdmin.createEncryptionZone(zoneDir, TEST_KEY, NO_TRASH); DFSTestUtil.createFile(fs, zoneFile, len, (short) 1, 0xFEED); assertNumZones(1); @@ -1367,7 +1475,7 @@ public void testEncryptionZonesOnRelativePath() throws Exception { public void testGetEncryptionZoneOnANonExistentZoneFile() throws Exception { final Path ez = new Path("/ez"); fs.mkdirs(ez); - dfsAdmin.createEncryptionZone(ez, TEST_KEY); + dfsAdmin.createEncryptionZone(ez, TEST_KEY, NO_TRASH); Path zoneFile = new Path(ez, "file"); try { fs.getEZForPath(zoneFile); @@ -1392,7 +1500,7 @@ public void testEncryptionZoneWithTrash() throws Exception { new HdfsAdmin(FileSystem.getDefaultUri(conf), conf); final Path zone1 = new Path("/zone1"); fs.mkdirs(zone1); - dfsAdmin.createEncryptionZone(zone1, TEST_KEY); + dfsAdmin.createEncryptionZone(zone1, TEST_KEY, NO_TRASH); // Create the encrypted file in zone1 final Path encFile1 = new Path(zone1, "encFile1"); @@ -1413,12 +1521,12 @@ public void testEncryptionZoneWithTrash() throws Exception { final Path topEZ = new Path("/topEZ"); fs.mkdirs(topEZ); - dfsAdmin.createEncryptionZone(topEZ, TEST_KEY); + dfsAdmin.createEncryptionZone(topEZ, TEST_KEY, NO_TRASH); final String NESTED_EZ_TEST_KEY = "nested_ez_test_key"; DFSTestUtil.createKey(NESTED_EZ_TEST_KEY, cluster, conf); final Path nestedEZ = new Path(topEZ, "nestedEZ"); fs.mkdirs(nestedEZ); - dfsAdmin.createEncryptionZone(nestedEZ, NESTED_EZ_TEST_KEY); + dfsAdmin.createEncryptionZone(nestedEZ, NESTED_EZ_TEST_KEY, NO_TRASH); final Path topEZFile = new Path(topEZ, "file"); final Path nestedEZFile = new Path(nestedEZ, "file"); DFSTestUtil.createFile(fs, topEZFile, len, (short) 1, 0xFEED); @@ -1433,7 +1541,7 @@ public void testEncryptionZoneWithTrash() throws Exception { public void testRootDirEZTrash() throws Exception { final HdfsAdmin dfsAdmin = new HdfsAdmin(FileSystem.getDefaultUri(conf), conf); - dfsAdmin.createEncryptionZone(new Path("/"), TEST_KEY); + dfsAdmin.createEncryptionZone(new Path("/"), TEST_KEY, NO_TRASH); final Path encFile = new Path("/encFile"); final int len = 8192; DFSTestUtil.createFile(fs, encFile, len, (short) 1, 0xFEED); @@ -1449,13 +1557,13 @@ public void testGetTrashRoots() throws Exception { new HdfsAdmin(FileSystem.getDefaultUri(conf), conf); Path ezRoot1 = new Path("/ez1"); fs.mkdirs(ezRoot1); - dfsAdmin.createEncryptionZone(ezRoot1, TEST_KEY); + dfsAdmin.createEncryptionZone(ezRoot1, TEST_KEY, NO_TRASH); Path ezRoot2 = new Path("/ez2"); fs.mkdirs(ezRoot2); - dfsAdmin.createEncryptionZone(ezRoot2, TEST_KEY); + dfsAdmin.createEncryptionZone(ezRoot2, TEST_KEY, NO_TRASH); Path ezRoot3 = new Path("/ez3"); fs.mkdirs(ezRoot3); - dfsAdmin.createEncryptionZone(ezRoot3, TEST_KEY); + dfsAdmin.createEncryptionZone(ezRoot3, TEST_KEY, NO_TRASH); Collection trashRootsBegin = fs.getTrashRoots(true); assertEquals("Unexpected getTrashRoots result", 0, trashRootsBegin.size()); diff --git a/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/TestEncryptionZonesWithHA.java b/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/TestEncryptionZonesWithHA.java index 476d024c7d..f3a26400af 100644 --- a/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/TestEncryptionZonesWithHA.java +++ b/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/TestEncryptionZonesWithHA.java @@ -23,6 +23,7 @@ import org.apache.hadoop.fs.permission.FsPermission; import org.apache.hadoop.fs.FileSystemTestHelper; import org.apache.hadoop.fs.Path; +import org.apache.hadoop.hdfs.client.CreateEncryptionZoneFlag; import org.apache.hadoop.hdfs.server.namenode.ha.HATestUtil; import org.apache.hadoop.hdfs.client.HdfsAdmin; import org.apache.hadoop.hdfs.server.namenode.NameNode; @@ -33,6 +34,7 @@ import java.io.File; import java.io.IOException; +import java.util.EnumSet; /** * Tests interaction of encryption zones with HA failover. @@ -49,7 +51,8 @@ public class TestEncryptionZonesWithHA { private File testRootDir; private final String TEST_KEY = "test_key"; - + protected static final EnumSet< CreateEncryptionZoneFlag > NO_TRASH = + EnumSet.of(CreateEncryptionZoneFlag.NO_TRASH); @Before public void setupCluster() throws Exception { @@ -101,7 +104,7 @@ public void testEncryptionZonesTrackedOnStandby() throws Exception { final Path dirChild = new Path(dir, "child"); final Path dirFile = new Path(dir, "file"); fs.mkdir(dir, FsPermission.getDirDefault()); - dfsAdmin0.createEncryptionZone(dir, TEST_KEY); + dfsAdmin0.createEncryptionZone(dir, TEST_KEY, NO_TRASH); fs.mkdir(dirChild, FsPermission.getDirDefault()); DFSTestUtil.createFile(fs, dirFile, len, (short) 1, 0xFEED); String contents = DFSTestUtil.readFile(fs, dirFile); diff --git a/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/TestEncryptionZonesWithKMS.java b/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/TestEncryptionZonesWithKMS.java index 59c8dd52f2..b29a108e9f 100644 --- a/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/TestEncryptionZonesWithKMS.java +++ b/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/TestEncryptionZonesWithKMS.java @@ -73,7 +73,7 @@ protected void setProvider() { public void testCreateEZPopulatesEDEKCache() throws Exception { final Path zonePath = new Path("/TestEncryptionZone"); fsWrapper.mkdir(zonePath, FsPermission.getDirDefault(), false); - dfsAdmin.createEncryptionZone(zonePath, TEST_KEY); + dfsAdmin.createEncryptionZone(zonePath, TEST_KEY, NO_TRASH); @SuppressWarnings("unchecked") KMSClientProvider kcp = (KMSClientProvider) Whitebox .getInternalState(cluster.getNamesystem().getProvider(), "extension"); @@ -102,7 +102,7 @@ public void testDelegationToken() throws Exception { public void testWarmupEDEKCacheOnStartup() throws Exception { final Path zonePath = new Path("/TestEncryptionZone"); fsWrapper.mkdir(zonePath, FsPermission.getDirDefault(), false); - dfsAdmin.createEncryptionZone(zonePath, TEST_KEY); + dfsAdmin.createEncryptionZone(zonePath, TEST_KEY, NO_TRASH); @SuppressWarnings("unchecked") KMSClientProvider spy = (KMSClientProvider) Whitebox diff --git a/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/TestReservedRawPaths.java b/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/TestReservedRawPaths.java index 84cfd80d28..449f71579f 100644 --- a/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/TestReservedRawPaths.java +++ b/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/TestReservedRawPaths.java @@ -21,6 +21,7 @@ import java.io.FileNotFoundException; import java.io.IOException; import java.security.PrivilegedExceptionAction; +import java.util.EnumSet; import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.crypto.key.JavaKeyStoreProvider; @@ -31,6 +32,7 @@ import org.apache.hadoop.fs.FileSystemTestWrapper; import org.apache.hadoop.fs.Path; import org.apache.hadoop.fs.permission.FsPermission; +import org.apache.hadoop.hdfs.client.CreateEncryptionZoneFlag; import org.apache.hadoop.hdfs.client.HdfsAdmin; import org.apache.hadoop.hdfs.server.namenode.EncryptionZoneManager; import org.apache.hadoop.hdfs.server.namenode.FSDirectory; @@ -61,6 +63,8 @@ public class TestReservedRawPaths { protected FileSystemTestWrapper fsWrapper; protected FileContextTestWrapper fcWrapper; + protected static final EnumSet< CreateEncryptionZoneFlag > NO_TRASH = + EnumSet.of(CreateEncryptionZoneFlag.NO_TRASH); @Before public void setup() throws Exception { @@ -114,7 +118,7 @@ public void testReadWriteRaw() throws Exception { // Create the first enc file final Path zone = new Path("/zone"); fs.mkdirs(zone); - dfsAdmin.createEncryptionZone(zone, TEST_KEY); + dfsAdmin.createEncryptionZone(zone, TEST_KEY, NO_TRASH); final Path encFile1 = new Path(zone, "myfile"); DFSTestUtil.createFile(fs, encFile1, len, (short) 1, 0xFEED); // Read them back in and compare byte-by-byte @@ -154,7 +158,7 @@ public void testGetFileStatus() throws Exception { final Path zone = new Path("zone"); final Path slashZone = new Path("/", zone); fs.mkdirs(slashZone); - dfsAdmin.createEncryptionZone(slashZone, TEST_KEY); + dfsAdmin.createEncryptionZone(slashZone, TEST_KEY, NO_TRASH); final Path base = new Path("base"); final Path reservedRaw = new Path("/.reserved/raw"); @@ -186,7 +190,7 @@ public void testReservedRawMkdir() throws Exception { final Path zone = new Path("zone"); final Path slashZone = new Path("/", zone); fs.mkdirs(slashZone); - dfsAdmin.createEncryptionZone(slashZone, TEST_KEY); + dfsAdmin.createEncryptionZone(slashZone, TEST_KEY, NO_TRASH); final Path rawRoot = new Path("/.reserved/raw"); final Path dir1 = new Path("dir1"); final Path rawDir1 = new Path(rawRoot, dir1); @@ -224,7 +228,7 @@ public void testAdminAccessOnly() throws Exception { final Path zone = new Path("zone"); final Path slashZone = new Path("/", zone); fs.mkdirs(slashZone); - dfsAdmin.createEncryptionZone(slashZone, TEST_KEY); + dfsAdmin.createEncryptionZone(slashZone, TEST_KEY, NO_TRASH); final Path base = new Path("base"); final Path reservedRaw = new Path("/.reserved/raw"); final int len = 8192; diff --git a/hadoop-hdfs-project/hadoop-hdfs/src/test/resources/testCryptoConf.xml b/hadoop-hdfs-project/hadoop-hdfs/src/test/resources/testCryptoConf.xml index 8a1c317785..ddd4adc445 100644 --- a/hadoop-hdfs-project/hadoop-hdfs/src/test/resources/testCryptoConf.xml +++ b/hadoop-hdfs-project/hadoop-hdfs/src/test/resources/testCryptoConf.xml @@ -68,6 +68,7 @@ -fs NAMENODE -mkdir /foo -fs NAMENODE -ls /- -createZone -path /foo -keyName myKey + -fs NAMENODE -rmdir /foo/.Trash -createZone -path /foo -keyName myKey @@ -91,7 +92,9 @@ -createZone -keyName myKey -path /foo/bar + -fs NAMENODE -rmdir /foo/bar/.Trash -fs NAMENODE -rmdir /foo/bar + -fs NAMENODE -rmdir /foo/.Trash -fs NAMENODE -rmdir /foo @@ -180,6 +183,7 @@ -createZone -path /foo/bar/baz -keyName myKey + -fs NAMENODE -rmdir /foo/bar/baz/.Trash -fs NAMENODE -rmdir /foo/bar/baz -fs NAMENODE -rmdir /foo/bar -fs NAMENODE -rmdir /foo/ @@ -205,7 +209,9 @@ -fs NAMENODE -rmdir /src/subdir + -fs NAMENODE -rmdir /src/.Trash -fs NAMENODE -rmdir /src + -fs NAMENODE -rmdir /dst/.Trash -fs NAMENODE -rmdir /dst @@ -227,6 +233,7 @@ -fs NAMENODE -rmdir /src + -fs NAMENODE -rmdir /dst/.Trash -fs NAMENODE -rmdir /dst @@ -249,6 +256,7 @@ -fs NAMENODE -rm /src/foo + -fs NAMENODE -rmdir /src/.Trash -fs NAMENODE -rmdir /src -fs NAMENODE -rmdir /dst @@ -269,6 +277,7 @@ -fs NAMENODE -ls /- + -fs NAMENODE -rmdir /src/.Trash -fs NAMENODE -rmdir /src -fs NAMENODE -rmdir /dst @@ -292,6 +301,7 @@ -fs NAMENODE -rmdir /src/subdir2/subdir1 -fs NAMENODE -rmdir /src/subdir2 + -fs NAMENODE -rmdir /src/.Trash -fs NAMENODE -rmdir /src @@ -302,5 +312,81 @@ + + Test failure of provisioning a trash dir if the EZ does not exist + + -fs NAMENODE -mkdir /src + -provisionTrash -path /src- + + + -fs NAMENODE -rmdir /src + + + + SubstringComparator + /src is not an encryption zone. + + + + + + Test failure of provisioning a trash dir if .Trash exists + + -fs NAMENODE -mkdir /src + -createZone -path /src -keyName myKey + -provisionTrash -path /src- + + + -fs NAMENODE -rm /src/.Trash + -fs NAMENODE -rmdir /src + + + + SubstringComparator + Will not provision new trash directory for encryption zone /src. Path already exists. + + + + + + Test success of provisioning a trash dir + + -fs NAMENODE -mkdir /src + -createZone -path /src -keyName myKey + -fs NAMENODE -rmdir /src/.Trash + -provisionTrash -path /src- + + + -fs NAMENODE -rmdir /src/.Trash + -fs NAMENODE -rmdir /src + + + + SubstringComparator + Created a trash directory for /src + + + + + + Test failure of provisioning a trash dir for incorrect EZ directory + + -fs NAMENODE -mkdir /src + -createZone -path /src -keyName myKey + -fs NAMENODE -mkdir /src/dir1 + -provisionTrash -path /src/dir1- + + + -fs NAMENODE -rmdir /src/dir1 + -fs NAMENODE -rmdir /src/.Trash + -fs NAMENODE -rmdir /src + + + + SubstringComparator + /src/dir1 is not the root of an encryption zone. + + +