From f1df74855dc5fe412ecf3b7672a1fca2960169b7 Mon Sep 17 00:00:00 2001 From: manika137 <80244229+manika137@users.noreply.github.com> Date: Mon, 17 Feb 2025 06:09:22 -0800 Subject: [PATCH] HADOOP-19393. [ABFS] Return FileAlreadyExistsException for UnauthorizedBlobOverwrite Rename Errors (#7312) Contributed by Manika Joshi. --- .../contracts/services/AzureServiceErrorCode.java | 2 ++ .../hadoop/fs/azurebfs/services/AbfsDfsClient.java | 11 +++++++++++ .../hadoop/fs/azurebfs/services/AbfsErrors.java | 1 + .../ITestAzureBlobFileSystemDelegationSAS.java | 14 ++++++++++++++ 4 files changed, 28 insertions(+) diff --git a/hadoop-tools/hadoop-azure/src/main/java/org/apache/hadoop/fs/azurebfs/contracts/services/AzureServiceErrorCode.java b/hadoop-tools/hadoop-azure/src/main/java/org/apache/hadoop/fs/azurebfs/contracts/services/AzureServiceErrorCode.java index 87949e36b34f3..e60e645b3bbef 100644 --- a/hadoop-tools/hadoop-azure/src/main/java/org/apache/hadoop/fs/azurebfs/contracts/services/AzureServiceErrorCode.java +++ b/hadoop-tools/hadoop-azure/src/main/java/org/apache/hadoop/fs/azurebfs/contracts/services/AzureServiceErrorCode.java @@ -65,6 +65,8 @@ public enum AzureServiceErrorCode { COPY_BLOB_ABORTED("CopyBlobAborted", HttpURLConnection.HTTP_INTERNAL_ERROR, null), BLOB_OPERATION_NOT_SUPPORTED("BlobOperationNotSupported", HttpURLConnection.HTTP_CONFLICT, null), INVALID_APPEND_OPERATION("InvalidAppendOperation", HttpURLConnection.HTTP_CONFLICT, null), + UNAUTHORIZED_BLOB_OVERWRITE("UnauthorizedBlobOverwrite", HttpURLConnection.HTTP_FORBIDDEN, + "This request is not authorized to perform blob overwrites."), UNKNOWN(null, -1, null); private final String errorCode; diff --git a/hadoop-tools/hadoop-azure/src/main/java/org/apache/hadoop/fs/azurebfs/services/AbfsDfsClient.java b/hadoop-tools/hadoop-azure/src/main/java/org/apache/hadoop/fs/azurebfs/services/AbfsDfsClient.java index 8505b533ce4c4..f571cb86ccaa7 100644 --- a/hadoop-tools/hadoop-azure/src/main/java/org/apache/hadoop/fs/azurebfs/services/AbfsDfsClient.java +++ b/hadoop-tools/hadoop-azure/src/main/java/org/apache/hadoop/fs/azurebfs/services/AbfsDfsClient.java @@ -42,6 +42,7 @@ import org.apache.hadoop.fs.FileSystem; import org.apache.hadoop.fs.Path; +import org.apache.hadoop.fs.FileAlreadyExistsException; import org.apache.hadoop.fs.azurebfs.AbfsConfiguration; import org.apache.hadoop.fs.azurebfs.AzureBlobFileSystemStore; import org.apache.hadoop.fs.azurebfs.constants.AbfsHttpConstants; @@ -132,6 +133,8 @@ import static org.apache.hadoop.fs.azurebfs.constants.HttpQueryParams.QUERY_PARAM_RETAIN_UNCOMMITTED_DATA; import static org.apache.hadoop.fs.azurebfs.contracts.services.AzureServiceErrorCode.RENAME_DESTINATION_PARENT_PATH_NOT_FOUND; import static org.apache.hadoop.fs.azurebfs.contracts.services.AzureServiceErrorCode.SOURCE_PATH_NOT_FOUND; +import static org.apache.hadoop.fs.azurebfs.contracts.services.AzureServiceErrorCode.UNAUTHORIZED_BLOB_OVERWRITE; +import static org.apache.hadoop.fs.azurebfs.services.AbfsErrors.ERR_FILE_ALREADY_EXISTS; /** * AbfsClient interacting with the DFS Endpoint. @@ -702,6 +705,14 @@ public AbfsClientRenameResult renamePath( throw e; } + // ref: HADOOP-19393. Write permission checks can occur before validating + // rename operation's validity. If there is an existing destination path, it may be rejected + // with an authorization error. Catching and throwing FileAlreadyExistsException instead. + if (op.getResult().getStorageErrorCode() + .equals(UNAUTHORIZED_BLOB_OVERWRITE.getErrorCode())){ + throw new FileAlreadyExistsException(ERR_FILE_ALREADY_EXISTS); + } + // ref: HADOOP-18242. Rename failure occurring due to a rare case of // tracking metadata being in incomplete state. if (op.getResult().getStorageErrorCode() diff --git a/hadoop-tools/hadoop-azure/src/main/java/org/apache/hadoop/fs/azurebfs/services/AbfsErrors.java b/hadoop-tools/hadoop-azure/src/main/java/org/apache/hadoop/fs/azurebfs/services/AbfsErrors.java index a5864290b139c..87c8869d6c61c 100644 --- a/hadoop-tools/hadoop-azure/src/main/java/org/apache/hadoop/fs/azurebfs/services/AbfsErrors.java +++ b/hadoop-tools/hadoop-azure/src/main/java/org/apache/hadoop/fs/azurebfs/services/AbfsErrors.java @@ -28,6 +28,7 @@ @InterfaceAudience.Public @InterfaceStability.Evolving public final class AbfsErrors { + public static final String ERR_FILE_ALREADY_EXISTS = "File already exists."; public static final String ERR_WRITE_WITHOUT_LEASE = "Attempted to write to file without lease"; public static final String ERR_LEASE_EXPIRED = "A lease ID was specified, but the lease for the resource has expired."; public static final String ERR_LEASE_EXPIRED_BLOB = "A lease ID was specified, but the lease for the blob has expired."; diff --git a/hadoop-tools/hadoop-azure/src/test/java/org/apache/hadoop/fs/azurebfs/ITestAzureBlobFileSystemDelegationSAS.java b/hadoop-tools/hadoop-azure/src/test/java/org/apache/hadoop/fs/azurebfs/ITestAzureBlobFileSystemDelegationSAS.java index d9b6f0f123f9d..70e5b23eadd83 100644 --- a/hadoop-tools/hadoop-azure/src/test/java/org/apache/hadoop/fs/azurebfs/ITestAzureBlobFileSystemDelegationSAS.java +++ b/hadoop-tools/hadoop-azure/src/test/java/org/apache/hadoop/fs/azurebfs/ITestAzureBlobFileSystemDelegationSAS.java @@ -34,6 +34,7 @@ import org.apache.hadoop.fs.FSDataInputStream; import org.apache.hadoop.fs.FSDataOutputStream; +import org.apache.hadoop.fs.FileAlreadyExistsException; import org.apache.hadoop.fs.FileStatus; import org.apache.hadoop.fs.FileSystem; import org.apache.hadoop.fs.Path; @@ -52,6 +53,7 @@ import static org.apache.hadoop.fs.azurebfs.constants.ConfigurationKeys.FS_AZURE_SAS_TOKEN_PROVIDER_TYPE; import static org.apache.hadoop.fs.azurebfs.contracts.services.AzureServiceErrorCode.AUTHORIZATION_PERMISSION_MISS_MATCH; +import static org.apache.hadoop.fs.azurebfs.services.AbfsErrors.ERR_FILE_ALREADY_EXISTS; import static org.apache.hadoop.fs.azurebfs.utils.AclTestHelpers.aclEntry; import static org.apache.hadoop.fs.contract.ContractTestUtils.assertPathDoesNotExist; import static org.apache.hadoop.fs.contract.ContractTestUtils.assertPathExists; @@ -213,6 +215,18 @@ public void testReadAndWrite() throws Exception { } } + @Test + public void checkExceptionForRenameOverwrites() throws Exception { + final AzureBlobFileSystem fs = getFileSystem(); + + Path src = new Path("a/b/f1.txt"); + Path dest = new Path("a/b/f2.txt"); + touch(src); + touch(dest); + + intercept(FileAlreadyExistsException.class, ERR_FILE_ALREADY_EXISTS, () -> fs.rename(src, dest)); + } + @Test // Test rename file and rename folder public void testRename() throws Exception {