Skip to content

Commit e3cb957

Browse files
authored
HADOOP-18662. ListFiles with recursive fails with FNF. (#5477). Contributed by Ayush Saxena.
Reviewed-by: Steve Loughran <[email protected]
1 parent 67e02a9 commit e3cb957

File tree

2 files changed

+62
-2
lines changed

2 files changed

+62
-2
lines changed

hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/fs/FileSystem.java

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2413,8 +2413,14 @@ private void handleFileStat(LocatedFileStatus stat) throws IOException {
24132413
if (stat.isFile()) { // file
24142414
curFile = stat;
24152415
} else if (recursive) { // directory
2416-
itors.push(curItor);
2417-
curItor = listLocatedStatus(stat.getPath());
2416+
try {
2417+
RemoteIterator<LocatedFileStatus> newDirItor = listLocatedStatus(stat.getPath());
2418+
itors.push(curItor);
2419+
curItor = newDirItor;
2420+
} catch (FileNotFoundException ignored) {
2421+
LOGGER.debug("Directory {} deleted while attempting for recursive listing",
2422+
stat.getPath());
2423+
}
24182424
}
24192425
}
24202426

hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/TestDistributedFileSystem.java

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import static org.apache.hadoop.hdfs.DFSConfigKeys.DFS_NAMENODE_FILE_CLOSE_NUM_COMMITTED_ALLOWED_KEY;
2323
import static org.apache.hadoop.hdfs.client.HdfsAdmin.TRASH_PERMISSION;
2424
import static org.apache.hadoop.hdfs.client.HdfsClientConfigKeys.DFS_CLIENT_CONTEXT;
25+
import static org.assertj.core.api.Assertions.assertThat;
2526
import static org.junit.Assert.assertEquals;
2627
import static org.junit.Assert.assertFalse;
2728
import static org.junit.Assert.assertNotNull;
@@ -31,6 +32,7 @@
3132
import static org.mockito.ArgumentMatchers.eq;
3233
import static org.mockito.Mockito.inOrder;
3334
import static org.mockito.Mockito.mock;
35+
import static org.mockito.Mockito.spy;
3436

3537
import java.io.File;
3638
import java.io.FileNotFoundException;
@@ -123,9 +125,11 @@
123125
import org.apache.hadoop.util.DataChecksum;
124126
import org.apache.hadoop.util.Time;
125127
import org.apache.hadoop.util.concurrent.HadoopExecutors;
128+
import org.apache.hadoop.util.functional.RemoteIterators;
126129
import org.junit.Assert;
127130
import org.junit.Test;
128131
import org.mockito.InOrder;
132+
import org.mockito.Mockito;
129133
import org.slf4j.Logger;
130134
import org.slf4j.LoggerFactory;
131135
import org.slf4j.event.Level;
@@ -1557,6 +1561,56 @@ public void testListFiles() throws IOException {
15571561
}
15581562
}
15591563

1564+
@Test
1565+
public void testListFilesRecursive() throws IOException {
1566+
Configuration conf = getTestConfiguration();
1567+
1568+
try (MiniDFSCluster cluster = new MiniDFSCluster.Builder(conf).build();) {
1569+
DistributedFileSystem fs = cluster.getFileSystem();
1570+
1571+
// Create some directories and files.
1572+
Path dir = new Path("/dir");
1573+
Path subDir1 = fs.makeQualified(new Path(dir, "subDir1"));
1574+
Path subDir2 = fs.makeQualified(new Path(dir, "subDir2"));
1575+
1576+
fs.create(new Path(dir, "foo1")).close();
1577+
fs.create(new Path(dir, "foo2")).close();
1578+
fs.create(new Path(subDir1, "foo3")).close();
1579+
fs.create(new Path(subDir2, "foo4")).close();
1580+
1581+
// Mock the filesystem, and throw FNF when listing is triggered for the subdirectory.
1582+
FileSystem mockFs = spy(fs);
1583+
Mockito.doThrow(new FileNotFoundException("")).when(mockFs).listLocatedStatus(eq(subDir1));
1584+
List<LocatedFileStatus> str = RemoteIterators.toList(mockFs.listFiles(dir, true));
1585+
assertThat(str).hasSize(3);
1586+
1587+
// Mock the filesystem to depict a scenario where the directory got deleted and a file
1588+
// got created with the same name.
1589+
Mockito.doReturn(getMockedIterator(subDir1)).when(mockFs).listLocatedStatus(eq(subDir1));
1590+
1591+
str = RemoteIterators.toList(mockFs.listFiles(dir, true));
1592+
assertThat(str).hasSize(4);
1593+
}
1594+
}
1595+
1596+
private static RemoteIterator<LocatedFileStatus> getMockedIterator(Path subDir1) {
1597+
return new RemoteIterator<LocatedFileStatus>() {
1598+
private int remainingEntries = 1;
1599+
1600+
@Override
1601+
public boolean hasNext() throws IOException {
1602+
return remainingEntries > 0;
1603+
}
1604+
1605+
@Override
1606+
public LocatedFileStatus next() throws IOException {
1607+
remainingEntries--;
1608+
return new LocatedFileStatus(0, false, 1, 1024, 0L, 0, null, null, null, null, subDir1,
1609+
false, false, false, null);
1610+
}
1611+
};
1612+
}
1613+
15601614
@Test
15611615
public void testListStatusOfSnapshotDirs() throws IOException {
15621616
MiniDFSCluster cluster = new MiniDFSCluster.Builder(getTestConfiguration())

0 commit comments

Comments
 (0)