From 20e808d83e7dc5a49f629e4031dbceb73f9b4cb8 Mon Sep 17 00:00:00 2001 From: Steve Loughran Date: Sun, 3 Apr 2016 16:39:14 +0100 Subject: [PATCH] HADOOP-12169 ListStatus on empty dir in S3A lists itself instead of returning an empty list. author: Pieter Reuse. --- .../AbstractContractGetFileStatusTest.java | 23 +++++++++++++++++++ .../apache/hadoop/fs/s3a/S3AFileSystem.java | 7 ++++-- .../src/test/resources/contract/s3a.xml | 5 ++++ 3 files changed, 33 insertions(+), 2 deletions(-) diff --git a/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/fs/contract/AbstractContractGetFileStatusTest.java b/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/fs/contract/AbstractContractGetFileStatusTest.java index 7ed375e648c..3e5bb12f7ad 100644 --- a/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/fs/contract/AbstractContractGetFileStatusTest.java +++ b/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/fs/contract/AbstractContractGetFileStatusTest.java @@ -19,10 +19,14 @@ package org.apache.hadoop.fs.contract; import java.io.FileNotFoundException; +import java.io.IOException; import org.apache.hadoop.fs.FileStatus; +import org.apache.hadoop.fs.FileSystem; import org.apache.hadoop.fs.Path; import org.junit.Test; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -58,4 +62,23 @@ public abstract class AbstractContractGetFileStatusTest extends handleExpectedException(e); } } + + @Test + public void testListStatusEmptyDirectory() throws IOException { + // remove the test directory + FileSystem fs = getFileSystem(); + assertTrue(fs.delete(getContract().getTestPath(), true)); + + // create a - non-qualified - Path for a subdir + Path subfolder = getContract().getTestPath().suffix("/"+testPath.getName()); + assertTrue(fs.mkdirs(subfolder)); + + // assert empty ls on the empty dir + assertEquals("ls on an empty directory not of length 0", 0, + fs.listStatus(subfolder).length); + + // assert non-empty ls on parent dir + assertTrue("ls on a non-empty directory of length 0", + fs.listStatus(getContract().getTestPath()).length > 0); + } } diff --git a/hadoop-tools/hadoop-aws/src/main/java/org/apache/hadoop/fs/s3a/S3AFileSystem.java b/hadoop-tools/hadoop-aws/src/main/java/org/apache/hadoop/fs/s3a/S3AFileSystem.java index 33db86ed81e..23d17fb0384 100644 --- a/hadoop-tools/hadoop-aws/src/main/java/org/apache/hadoop/fs/s3a/S3AFileSystem.java +++ b/hadoop-tools/hadoop-aws/src/main/java/org/apache/hadoop/fs/s3a/S3AFileSystem.java @@ -846,11 +846,14 @@ public class S3AFileSystem extends FileSystem { ObjectListing objects = s3.listObjects(request); statistics.incrementReadOps(1); + Path fQualified = f.makeQualified(uri, workingDir); + while (true) { for (S3ObjectSummary summary : objects.getObjectSummaries()) { Path keyPath = keyToPath(summary.getKey()).makeQualified(uri, workingDir); // Skip over keys that are ourselves and old S3N _$folder$ files - if (keyPath.equals(f) || summary.getKey().endsWith(S3N_FOLDER_SUFFIX)) { + if (keyPath.equals(fQualified) || + summary.getKey().endsWith(S3N_FOLDER_SUFFIX)) { if (LOG.isDebugEnabled()) { LOG.debug("Ignoring: " + keyPath); } @@ -865,7 +868,7 @@ public class S3AFileSystem extends FileSystem { } else { result.add(new S3AFileStatus(summary.getSize(), dateToLong(summary.getLastModified()), keyPath, - getDefaultBlockSize(f.makeQualified(uri, workingDir)))); + getDefaultBlockSize(fQualified))); if (LOG.isDebugEnabled()) { LOG.debug("Adding: fi: " + keyPath); } diff --git a/hadoop-tools/hadoop-aws/src/test/resources/contract/s3a.xml b/hadoop-tools/hadoop-aws/src/test/resources/contract/s3a.xml index 4f9c0818ffa..be1e7ca6535 100644 --- a/hadoop-tools/hadoop-aws/src/test/resources/contract/s3a.xml +++ b/hadoop-tools/hadoop-aws/src/test/resources/contract/s3a.xml @@ -77,6 +77,11 @@ false + + fs.contract.supports-getfilestatus + true + + fs.contract.supports-seek true