srikanth-lingala / zip4j

A Java library for zip files and streams
Apache License 2.0
2.06k stars 310 forks source link

Issue when I try to extract a zip file in a remote shared directory (SMB file share) #464

Open cquizo opened 2 years ago

cquizo commented 2 years ago

I am using the last release of the library.

What I have is two different servers, one where my App is running using the library and another one where I have a shared directory (SMB file share) both servers are Windows Server 2019.

On the running time, I got the following exception.

Caused by: java.io.IOException: There are no more files
at java.io.WinNTFileSystem.canonicalize0(Native Method) ~[?:?]
at java.io.WinNTFileSystem.canonicalize(WinNTFileSystem.java:483) ~[?:?]
at java.io.File.getCanonicalPath(File.java:626) ~[?:?]
at net.lingala.zip4j.tasks.AbstractExtractFileTask.assertCanonicalPathsAreSame(AbstractExtractFileTask.java:75) ~[zip4j-2.11.1.jar:?]
at net.lingala.zip4j.tasks.AbstractExtractFileTask.extractFile(AbstractExtractFileTask.java:52) ~[zip4j-2.11.1.jar:?]
at net.lingala.zip4j.tasks.ExtractAllFilesTask.executeTask(ExtractAllFilesTask.java:41) ~[zip4j-2.11.1.jar:?]
at net.lingala.zip4j.tasks.ExtractAllFilesTask.executeTask(ExtractAllFilesTask.java:17) ~[zip4j-2.11.1.jar:?]
at net.lingala.zip4j.tasks.AsyncZipTask.performTaskWithErrorHandling(AsyncZipTask.java:51) ~[zip4j-2.11.1.jar:?]
at net.lingala.zip4j.tasks.AsyncZipTask.execute(AsyncZipTask.java:45) ~[zip4j-2.11.1.jar:?]

First I looked for a solution for the issue in the method “java.io.File.getCanonicalPath”, but I didn’t find something that make sense for my case. What I found was an opened ticket for a similar issue in the OpenJDK project (https://bugs.openjdk.org/browse/JDK-8234363) Then I checked what the “net.lingala.zip4j.tasks.AbstractExtractFileTask.assertCanonicalPathsAreSame” method does, and I found that it is more likely a validation. So, I commented on the line where is called this method. After that my App started working properly. I already tested with local directory and remote shared directory on Windows Server 2019, and Windows 11 Home all of them x64-based processors.

vishr commented 2 years ago

@srikanth-lingala Thanks for your work on this library. Do you think this if @cquizo provides a fix it could be accepted?

srikanth-lingala commented 2 years ago

Hi, I was a little busy the last days. I will look into this this week.

srikanth-lingala commented 2 years ago

Canonical path check is needed to avoid a zip slip exploit. I cannot unfortunately remove this check. When you debug zip4j, what is the value of outputCanonicalPath and outputFileCanonicalPath on this line?

cquizo commented 2 years ago

Hi @srikanth-lingala thanks for your response We don't know because the exception is thrown in this line as you can see here:

Caused by: java.io.IOException: There are no more files
at java.io.WinNTFileSystem.canonicalize0(Native Method) ~[?:?]
at java.io.WinNTFileSystem.canonicalize(WinNTFileSystem.java:483) ~[?:?]
at java.io.File.getCanonicalPath(File.java:626) ~[?:?]
at net.lingala.zip4j.tasks.AbstractExtractFileTask.assertCanonicalPathsAreSame(AbstractExtractFileTask.java:75) ~[zip4j-2.11.1.jar:?]
at net.lingala.zip4j.tasks.AbstractExtractFileTask.extractFile(AbstractExtractFileTask.java:52) ~[zip4j-2.11.1.jar:?]
at net.lingala.zip4j.tasks.ExtractAllFilesTask.executeTask(ExtractAllFilesTask.java:41) ~[zip4j-2.11.1.jar:?]
at net.lingala.zip4j.tasks.ExtractAllFilesTask.executeTask(ExtractAllFilesTask.java:17) ~[zip4j-2.11.1.jar:?]
at net.lingala.zip4j.tasks.AsyncZipTask.performTaskWithErrorHandling(AsyncZipTask.java:51) ~[zip4j-2.11.1.jar:?]
at net.lingala.zip4j.tasks.AsyncZipTask.execute(AsyncZipTask.java:45) ~[zip4j-2.11.1.jar:?]
srikanth-lingala commented 2 years ago

Based on my quick read on this issue, I think the bug lies somewhere in OpenJDK/Windows OS. Here is another answer which briefly explains the issue. Unfortunately, I cannot remove that check in zip4j. As I mentioned in my earlier reply, this check is to protect against a zip exploit.

cquizo commented 1 year ago

Hi @srikanth-lingala, I got back to the issue. Apparently, Windows will not create a patch for the issue soon. So I added a method to retry to get the canonical path. The result was very satisfactory that is why I am writing to you again. This is the method:

private String getCanonicalPathWithRetry(final File outputFile, final int sleepTime) throws IOException { try { return outputFile.getCanonicalPath(); } catch (IOException e) { if (sleepTime <= 0) { throw e; } System.out.println("Retrying to get the canonical path in '"+ sleepTime +"' milliseconds > " + outputFile ); try { Thread.sleep(sleepTime); } catch (InterruptedException ex) { throw new RuntimeException(ex); } return getCanonicalPathWithRetry(outputFile, (sleepTime - 100)); } }

And I used it in the line where "getCanonicalPath" method fails, in this case at "net.lingala.zip4j.tasks.AbstractExtractFileTask.assertCanonicalPathsAreSame(AbstractExtractFileTask.java:75)" like this:

String outputFileCanonicalPath = getCanonicalPathWithRetry(outputFile, 500);

Do you think this fix could be accepted?