Author: onealj
Date: Wed Sep 14 12:57:39 2016
New Revision: 1760702

URL: http://svn.apache.org/viewvc?rev=1760702&view=rev
Log:
bug 60128: close open file descriptors when exceptions are thrown from 
OPCPackage.open

Added:
    poi/trunk/test-data/openxml4j/invalid.xlsx   (with props)
Modified:
    poi/trunk/src/ooxml/java/org/apache/poi/openxml4j/opc/OPCPackage.java
    poi/trunk/src/ooxml/java/org/apache/poi/openxml4j/opc/ZipPackage.java
    poi/trunk/src/ooxml/java/org/apache/poi/openxml4j/util/ZipSecureFile.java
    
poi/trunk/src/ooxml/testcases/org/apache/poi/extractor/TestExtractorFactory.java
    poi/trunk/src/ooxml/testcases/org/apache/poi/openxml4j/opc/TestPackage.java
    
poi/trunk/src/ooxml/testcases/org/apache/poi/openxml4j/opc/TestZipPackage.java

Modified: poi/trunk/src/ooxml/java/org/apache/poi/openxml4j/opc/OPCPackage.java
URL: 
http://svn.apache.org/viewvc/poi/trunk/src/ooxml/java/org/apache/poi/openxml4j/opc/OPCPackage.java?rev=1760702&r1=1760701&r2=1760702&view=diff
==============================================================================
--- poi/trunk/src/ooxml/java/org/apache/poi/openxml4j/opc/OPCPackage.java 
(original)
+++ poi/trunk/src/ooxml/java/org/apache/poi/openxml4j/opc/OPCPackage.java Wed 
Sep 14 12:57:39 2016
@@ -248,9 +248,10 @@ public abstract class OPCPackage impleme
         * @throws InvalidFormatException
         *             If the specified file doesn't exist, and a parsing error
         *             occur.
+        * @throws InvalidOperationException
         */
        public static OPCPackage open(String path, PackageAccess access)
-                       throws InvalidFormatException {
+                       throws InvalidFormatException, 
InvalidOperationException {
                if (path == null || "".equals(path.trim())) {
                        throw new IllegalArgumentException("'path' must be 
given");
                }
@@ -261,8 +262,20 @@ public abstract class OPCPackage impleme
                }
 
                OPCPackage pack = new ZipPackage(path, access);
+               boolean success = false;
                if (pack.partList == null && access != PackageAccess.WRITE) {
-                       pack.getParts();
+                       try {
+                               pack.getParts();
+                               success = true;
+                       } finally {
+                               if (! success) {
+                                       try {
+                                               pack.close();
+                                       } catch (final IOException e) {
+                                               throw new 
InvalidOperationException("Could not close OPCPackage while cleaning up", e);
+                                       }
+                               }
+                       }
                }
                pack.originalPackagePath = new File(path).getAbsolutePath();
                return pack;

Modified: poi/trunk/src/ooxml/java/org/apache/poi/openxml4j/opc/ZipPackage.java
URL: 
http://svn.apache.org/viewvc/poi/trunk/src/ooxml/java/org/apache/poi/openxml4j/opc/ZipPackage.java?rev=1760702&r1=1760701&r2=1760702&view=diff
==============================================================================
--- poi/trunk/src/ooxml/java/org/apache/poi/openxml4j/opc/ZipPackage.java 
(original)
+++ poi/trunk/src/ooxml/java/org/apache/poi/openxml4j/opc/ZipPackage.java Wed 
Sep 14 12:57:39 2016
@@ -19,6 +19,7 @@ package org.apache.poi.openxml4j.opc;
 
 import java.io.File;
 import java.io.FileInputStream;
+import java.io.FileNotFoundException;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
@@ -88,9 +89,18 @@ public final class ZipPackage extends OP
      */
     ZipPackage(InputStream in, PackageAccess access) throws IOException {
         super(access);
-        @SuppressWarnings("resource")
         ThresholdInputStream zis = ZipHelper.openZipStream(in);
-        this.zipArchive = new ZipInputStreamZipEntrySource(zis);
+        try {
+            this.zipArchive = new ZipInputStreamZipEntrySource(zis);
+        } catch (final IOException e) {
+            try {
+                zis.close();
+            } catch (final IOException e2) {
+                e2.addSuppressed(e);
+                throw new IOException("Failed to close zip input stream while 
cleaning up", e2);
+            }
+            throw new IOException("Failed to read zip entry source", e);
+        }
     }
 
     /**
@@ -100,8 +110,9 @@ public final class ZipPackage extends OP
      *            The path of the file to open or create.
      * @param access
      *            The package access mode.
+     * @throws InvalidOperationException
      */
-    ZipPackage(String path, PackageAccess access) {
+    ZipPackage(String path, PackageAccess access) throws 
InvalidOperationException {
         this(new File(path), access);
     }
 
@@ -112,9 +123,9 @@ public final class ZipPackage extends OP
      *            The file to open or create.
      * @param access
      *            The package access mode.
+     * @throws InvalidOperationException
      */
-    @SuppressWarnings("resource")
-    ZipPackage(File file, PackageAccess access) {
+    ZipPackage(File file, PackageAccess access) throws 
InvalidOperationException {
         super(access);
 
         ZipEntrySource ze;
@@ -127,35 +138,71 @@ public final class ZipPackage extends OP
                 throw new InvalidOperationException("Can't open the specified 
file: '" + file + "'", e);
             }
             logger.log(POILogger.ERROR, "Error in zip file "+file+" - falling 
back to stream processing (i.e. ignoring zip central directory)");
-            // some zips can't be opened via ZipFile in JDK6, as the central 
directory
-            // contains either non-latin entries or the compression type can't 
be handled
-            // the workaround is to iterate over the stream and not the 
directory
-            FileInputStream fis = null;
-            ThresholdInputStream zis = null;
+            ze = openZipEntrySourceStream(file);
+        }
+        this.zipArchive = ze;
+    }
+    
+    private static ZipEntrySource openZipEntrySourceStream(File file) throws 
InvalidOperationException {
+        final FileInputStream fis;
+        // Acquire a resource that is needed to read the next level of 
openZipEntrySourceStream
+        try {
+            // open the file input stream
+            fis = new FileInputStream(file);
+        } catch (final FileNotFoundException e) {
+            // If the source cannot be acquired, abort (no resources to free 
at this level)
+            throw new InvalidOperationException("Can't open the specified file 
input stream from file: '" + file + "'", e);
+        }
+        
+        // If an error occurs while reading the next level of 
openZipEntrySourceStream, free the acquired resource
+        try {
+            // read from the file input stream
+            return openZipEntrySourceStream(fis);
+        } catch (final Exception e) {
             try {
-                fis = new FileInputStream(file);
-                zis = ZipHelper.openZipStream(fis);
-                ze = new ZipInputStreamZipEntrySource(zis);
-            } catch (IOException e2) {
-                if (zis != null) {
-                    try {
-                        zis.close();
-                    } catch (IOException e3) {
-                        throw new InvalidOperationException("Can't open the 
specified file: '" + file + "'"+
-                                " and couldn't close the file input stream", 
e);
-                    }
-                } else if (fis != null) {
-                    try {
-                        fis.close();
-                    } catch (IOException e3) {
-                        throw new InvalidOperationException("Can't open the 
specified file: '" + file + "'"+
-                                " and couldn't close the file input stream", 
e);
-                    }
-                }
-                throw new InvalidOperationException("Can't open the specified 
file: '" + file + "'", e);
+                // abort: close the file input stream
+                fis.close();
+            } catch (final IOException e2) {
+                throw new InvalidOperationException("Could not close the 
specified file input stream from file: '" + file + "'", e2);
             }
+            throw new InvalidOperationException("Failed to read the file input 
stream from file: '" + file + "'", e);
+        }
+    }
+    
+    private static ZipEntrySource openZipEntrySourceStream(FileInputStream 
fis) throws InvalidOperationException {
+        final ThresholdInputStream zis;
+        // Acquire a resource that is needed to read the next level of 
openZipEntrySourceStream
+        try {
+            // open the zip input stream
+            zis = ZipHelper.openZipStream(fis);
+        } catch (final IOException e) {
+            // If the source cannot be acquired, abort (no resources to free 
at this level)
+            throw new InvalidOperationException("Could not open the file input 
stream", e);
+        }
+        
+        // If an error occurs while reading the next level of 
openZipEntrySourceStream, free the acquired resource
+        try {
+            // read from the zip input stream
+            return openZipEntrySourceStream(zis);
+        } catch (final Exception e) {
+            try {
+                // abort: close the zip input stream
+                zis.close();
+            } catch (final IOException e2) {
+                throw new InvalidOperationException("Failed to read the zip 
entry source stream and could not close the zip input stream", e2);
+            }
+            throw new InvalidOperationException("Failed to read the zip entry 
source stream");
+        }
+    }
+    
+    private static ZipEntrySource 
openZipEntrySourceStream(ThresholdInputStream zis) throws 
InvalidOperationException {
+        // Acquire the final level resource. If this is acquired successfully, 
the zip package was read successfully from the input stream
+        try {
+            // open the zip entry source stream
+            return new ZipInputStreamZipEntrySource(zis);
+        } catch (IOException e) {
+            throw new InvalidOperationException("Could not open the specified 
zip entry source stream", e);
         }
-        this.zipArchive = ze;
     }
 
     /**
@@ -220,11 +267,12 @@ public final class ZipPackage extends OP
             boolean hasSettingsXML = false;
             entries = this.zipArchive.getEntries();
             while (entries.hasMoreElements()) {
-                ZipEntry entry = entries.nextElement();
-                if (entry.getName().equals("mimetype")) {
+                final ZipEntry entry = entries.nextElement();
+                final String name = entry.getName();
+                if ("mimetype".equals(name)) {
                     hasMimetype = true;
                 }
-                if (entry.getName().equals("settings.xml")) {
+                if ("settings.xml".equals(name)) {
                     hasSettingsXML = true;
                 }
                 numEntries++;

Modified: 
poi/trunk/src/ooxml/java/org/apache/poi/openxml4j/util/ZipSecureFile.java
URL: 
http://svn.apache.org/viewvc/poi/trunk/src/ooxml/java/org/apache/poi/openxml4j/util/ZipSecureFile.java?rev=1760702&r1=1760701&r2=1760702&view=diff
==============================================================================
--- poi/trunk/src/ooxml/java/org/apache/poi/openxml4j/util/ZipSecureFile.java 
(original)
+++ poi/trunk/src/ooxml/java/org/apache/poi/openxml4j/util/ZipSecureFile.java 
Wed Sep 14 12:57:39 2016
@@ -134,15 +134,15 @@ public class ZipSecureFile extends ZipFi
         return MAX_TEXT_SIZE;
     }
 
-    public ZipSecureFile(File file, int mode) throws IOException {
+    public ZipSecureFile(File file, int mode) throws ZipException, IOException 
{
         super(file, mode);
     }
 
-    public ZipSecureFile(File file) throws IOException {
+    public ZipSecureFile(File file) throws ZipException, IOException {
         super(file);
     }
 
-    public ZipSecureFile(String name) throws IOException {
+    public ZipSecureFile(String name) throws ZipException, IOException {
         super(name);
     }
 

Modified: 
poi/trunk/src/ooxml/testcases/org/apache/poi/extractor/TestExtractorFactory.java
URL: 
http://svn.apache.org/viewvc/poi/trunk/src/ooxml/testcases/org/apache/poi/extractor/TestExtractorFactory.java?rev=1760702&r1=1760701&r2=1760702&view=diff
==============================================================================
--- 
poi/trunk/src/ooxml/testcases/org/apache/poi/extractor/TestExtractorFactory.java
 (original)
+++ 
poi/trunk/src/ooxml/testcases/org/apache/poi/extractor/TestExtractorFactory.java
 Wed Sep 14 12:57:39 2016
@@ -682,9 +682,12 @@ public class TestExtractorFactory {
         // Text
         try {
             ExtractorFactory.createExtractor(OPCPackage.open(txt.toString()));
-            fail();
+            fail("TestExtractorFactory.testPackage() failed on " + 
txt.toString());
         } catch(UnsupportedFileFormatException e) {
             // Good
+        } catch (Exception e) {
+            System.out.println("TestExtractorFactory.testPackage() failed on " 
+ txt.toString());
+            throw e;
         }
     }
 
@@ -942,6 +945,7 @@ public class TestExtractorFactory {
         
"openxml4j/OPCCompliance_CoreProperties_OnlyOneCorePropertiesPartFAIL.docx",
         
"openxml4j/OPCCompliance_CoreProperties_UnauthorizedXMLLangAttributeFAIL.docx",
         "openxml4j/OPCCompliance_DerivedPartNameFAIL.docx",
+        "openxml4j/invalid.xlsx",
         "spreadsheet/54764-2.xlsx",   // see TestXSSFBugs.bug54764()
         "spreadsheet/54764.xlsx",     // see TestXSSFBugs.bug54764()
         "spreadsheet/Simple.xlsb",

Modified: 
poi/trunk/src/ooxml/testcases/org/apache/poi/openxml4j/opc/TestPackage.java
URL: 
http://svn.apache.org/viewvc/poi/trunk/src/ooxml/testcases/org/apache/poi/openxml4j/opc/TestPackage.java?rev=1760702&r1=1760701&r2=1760702&view=diff
==============================================================================
--- poi/trunk/src/ooxml/testcases/org/apache/poi/openxml4j/opc/TestPackage.java 
(original)
+++ poi/trunk/src/ooxml/testcases/org/apache/poi/openxml4j/opc/TestPackage.java 
Wed Sep 14 12:57:39 2016
@@ -943,4 +943,22 @@ public final class TestPackage {
             ZipSecureFile.setMaxTextSize(before);
         }
     }
+    
+    // bug 60128
+    @Test
+    public void testCorruptFile() throws IOException {
+        OPCPackage pkg = null;
+        File file = OpenXML4JTestDataSamples.getSampleFile("invalid.xlsx");
+        try {
+            pkg = OPCPackage.open(file, PackageAccess.READ);
+        } catch (Exception e) {
+            System.out.println(e.getClass().getName());
+            System.out.println(e.getMessage());
+            e.printStackTrace();
+        } finally {
+            if (pkg != null) {
+                pkg.close();
+            }
+        }
+    }
 }

Modified: 
poi/trunk/src/ooxml/testcases/org/apache/poi/openxml4j/opc/TestZipPackage.java
URL: 
http://svn.apache.org/viewvc/poi/trunk/src/ooxml/testcases/org/apache/poi/openxml4j/opc/TestZipPackage.java?rev=1760702&r1=1760701&r2=1760702&view=diff
==============================================================================
--- 
poi/trunk/src/ooxml/testcases/org/apache/poi/openxml4j/opc/TestZipPackage.java 
(original)
+++ 
poi/trunk/src/ooxml/testcases/org/apache/poi/openxml4j/opc/TestZipPackage.java 
Wed Sep 14 12:57:39 2016
@@ -184,6 +184,7 @@ public class TestZipPackage {
     public void testClosingStreamOnException() throws IOException {
         InputStream is = 
OpenXML4JTestDataSamples.openSampleStream("dcterms_bug_56479.zip");
         File tmp = File.createTempFile("poi-test-truncated-zip", "");
+        // create a corrupted zip file by truncating a valid zip file to the 
first 100 bytes
         OutputStream os = new FileOutputStream(tmp);
         for (int i = 0; i < 100; i++) {
             os.write(is.read());
@@ -192,10 +193,15 @@ public class TestZipPackage {
         os.close();
         is.close();
 
+        // feed the corrupted zip file to OPCPackage
         try {
             OPCPackage.open(tmp, PackageAccess.READ);
         } catch (Exception e) {
+            // expected: the zip file is invalid
+            // this test does not care if open() throws an exception or not.
         }
+        // If the stream is not closed on exception, it will keep a file 
descriptor to tmp,
+        // and requests to the OS to delete the file will fail.
         assertTrue("Can't delete tmp file", tmp.delete());
     }
     

Added: poi/trunk/test-data/openxml4j/invalid.xlsx
URL: 
http://svn.apache.org/viewvc/poi/trunk/test-data/openxml4j/invalid.xlsx?rev=1760702&view=auto
==============================================================================
Binary file - no diff available.

Propchange: poi/trunk/test-data/openxml4j/invalid.xlsx
------------------------------------------------------------------------------
--- svn:mime-type (added)
+++ svn:mime-type Wed Sep 14 12:57:39 2016
@@ -0,0 +1 @@
+application/vnd.openxmlformats-officedocument.spreadsheetml.sheet



---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to