From fd3aa382312876713ab48d188e7ee6f9c1a37642 Mon Sep 17 00:00:00 2001 From: cnauroth Date: Thu, 25 Sep 2014 13:33:37 -0700 Subject: [PATCH] HDFS-7119. Split error checks in AtomicFileOutputStream#close into separate conditions to improve diagnostics. Contributed by Chris Nauroth. (cherry picked from commit 9f9a2222a2e142a47537bc57ca98fb07a7a78ad4) --- hadoop-hdfs-project/hadoop-hdfs/CHANGES.txt | 3 ++ .../hdfs/util/AtomicFileOutputStream.java | 14 +++++++-- .../hdfs/util/TestAtomicFileOutputStream.java | 29 +++++++++++++++++++ 3 files changed, 43 insertions(+), 3 deletions(-) diff --git a/hadoop-hdfs-project/hadoop-hdfs/CHANGES.txt b/hadoop-hdfs-project/hadoop-hdfs/CHANGES.txt index d6b0448218e..0d9c50117b4 100644 --- a/hadoop-hdfs-project/hadoop-hdfs/CHANGES.txt +++ b/hadoop-hdfs-project/hadoop-hdfs/CHANGES.txt @@ -243,6 +243,9 @@ Release 2.6.0 - UNRELEASED HDFS-6808. Add command line option to ask DataNode reload configuration. (Lei Xu via Colin Patrick McCabe) + HDFS-7119. Split error checks in AtomicFileOutputStream#close into separate + conditions to improve diagnostics. (cnauroth) + OPTIMIZATIONS HDFS-6690. Deduplicate xattr names in memory. (wang) diff --git a/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/util/AtomicFileOutputStream.java b/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/util/AtomicFileOutputStream.java index 3c7d8d884b5..a89b8cb07b2 100644 --- a/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/util/AtomicFileOutputStream.java +++ b/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/util/AtomicFileOutputStream.java @@ -26,6 +26,8 @@ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.hadoop.io.IOUtils; +import org.apache.hadoop.io.nativeio.NativeIO; +import org.apache.hadoop.io.nativeio.NativeIOException; /** * A FileOutputStream that has the property that it will only show @@ -73,9 +75,15 @@ public void close() throws IOException { boolean renamed = tmpFile.renameTo(origFile); if (!renamed) { // On windows, renameTo does not replace. - if (!origFile.delete() || !tmpFile.renameTo(origFile)) { - throw new IOException("Could not rename temporary file " + - tmpFile + " to " + origFile); + if (origFile.exists() && !origFile.delete()) { + throw new IOException("Could not delete original file " + origFile); + } + try { + NativeIO.renameTo(tmpFile, origFile); + } catch (NativeIOException e) { + throw new IOException("Could not rename temporary file " + tmpFile + + " to " + origFile + " due to failure in native rename. " + + e.toString()); } } } else { diff --git a/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/util/TestAtomicFileOutputStream.java b/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/util/TestAtomicFileOutputStream.java index fa9131105b6..b9946c5a29b 100644 --- a/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/util/TestAtomicFileOutputStream.java +++ b/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/util/TestAtomicFileOutputStream.java @@ -21,6 +21,7 @@ import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; +import static org.junit.Assume.assumeTrue; import java.io.File; import java.io.FileNotFoundException; @@ -30,9 +31,13 @@ import org.apache.hadoop.fs.FileUtil; import org.apache.hadoop.hdfs.DFSTestUtil; +import org.apache.hadoop.io.IOUtils; import org.apache.hadoop.test.PathUtils; +import org.apache.hadoop.util.Shell; import org.junit.Before; +import org.junit.Rule; import org.junit.Test; +import org.junit.rules.ExpectedException; import com.google.common.base.Joiner; @@ -44,6 +49,9 @@ public class TestAtomicFileOutputStream { private static final File TEST_DIR = PathUtils.getTestDir(TestAtomicFileOutputStream.class); private static final File DST_FILE = new File(TEST_DIR, "test.txt"); + + @Rule + public ExpectedException exception = ExpectedException.none(); @Before public void cleanupTestDir() throws IOException { @@ -119,6 +127,27 @@ public void testFailToFlush() throws IOException { DST_FILE.getName(), Joiner.on(",").join(TEST_DIR.list())); } + @Test + public void testFailToRename() throws IOException { + assumeTrue(Shell.WINDOWS); + OutputStream fos = null; + try { + fos = new AtomicFileOutputStream(DST_FILE); + fos.write(TEST_STRING.getBytes()); + FileUtil.setWritable(TEST_DIR, false); + exception.expect(IOException.class); + exception.expectMessage("failure in native rename"); + try { + fos.close(); + } finally { + fos = null; + } + } finally { + IOUtils.cleanup(null, fos); + FileUtil.setWritable(TEST_DIR, true); + } + } + /** * Create a stream that fails to flush at close time */