Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ZipFile.ExtractToDirectory() fails on Android #35374

Closed
SittenSpynne opened this issue Apr 23, 2020 · 4 comments · Fixed by #56370
Closed

ZipFile.ExtractToDirectory() fails on Android #35374

SittenSpynne opened this issue Apr 23, 2020 · 4 comments · Fixed by #56370

Comments

@SittenSpynne
Copy link

The implementation of System.Compression.IO.ZipFileExtensions.ExtractToDirectory() cannot succeed on Android, because it tries to set the file's last accessed time and Android doesn't seem to universally support this.

Steps to Reproduce

  1. Create a Xamarin Forms project.
  2. Install the Xamarin.Essentials.Permissions package and follow its directions.
  3. Make sure you have WRITE_EXTERNAL_STORAGE permissions declared in AndroidManifest.xml.
  4. Follow the Pemrissions plugin's instructions to request the Storage permission and grant it.
  5. Put a .zip file somewhere you can access on an Android API 25 device.
  6. Write some code that attempts to extract that .zip file to external storage.
  7. You will get an UnauthorizedAccessException with System.IO.File.SetLastWriteTime() as the last public thing in the call stack.
    • The InnerException is an IOException with message "not permitted".
    • This happens if I try to manually set the access time of a file on external storage, too. I can't find specific documentation but assume Android doesn't like you doing this.

Here's some example code that could cause the exception, I usually like to include an example project but I'm 3 days behind and just lost a day of debugging to this so please forgive me.

    public void Reproduction(string sourcePath, string destinationDir)
    {
         ZipFile.ExtractToDirectory(sourcePath, destinationDir);
    }

Note that to generate a reasonable destinationDir parameter, you've got to do some Android-specific stuff. Here's a pretty good way to generate one in your Android-specific project (I haven't tested on iOS as it's not my development focus right now):

    public string GetDestinationDir()
    {
        var base = Android.OS.Environment.ExternalStorageDirectory.AbsolutePath;
        var target = "output";
        return Path.Combine(base, output);
    }

This doesn't reproduce if you use a private directory on internal storage. I'm currently working around it by manually iterating the entries of the zip file and using ZipEntry.Extract(Stream). This overload doesn't try to set the last access time because it doesn't know if it's working with a file, so it works.

Version Used:

Whatever version VS for Mac is using if I compile a Xamarin.Forms project targeting .NET Standard 2.0. As far as I can tell it's using the 3.1.200 SDK but it claims it's got versions all the way back to 2.1.505.

Stacktrace

This is the stack trace if I use ZipFile.ExtractToDirectory(). A similar stack trace happens if I try to call `

  at Interop.ThrowExceptionForIoErrno (Interop+ErrorInfo errorInfo, System.String path, System.Boolean isDirectory, System.Func`2[T,TResult] errorRewriter) [0x0000c] in /Users/builder/jenkins/workspace/archive-mono/2019-10/android/release/external/corefx/src/Common/src/CoreLib/Interop/Unix/Interop.IOErrors.cs:23 
  at Interop.CheckIo (System.Int64 result, System.String path, System.Boolean isDirectory, System.Func`2[T,TResult] errorRewriter) [0x00005] in /Users/builder/jenkins/workspace/archive-mono/2019-10/android/release/external/corefx/src/Common/src/CoreLib/Interop/Unix/Interop.IOErrors.cs:50 
  at Interop.CheckIo (System.Int32 result, System.String path, System.Boolean isDirectory, System.Func`2[T,TResult] errorRewriter) [0x00000] in /Users/builder/jenkins/workspace/archive-mono/2019-10/android/release/external/corefx/src/Common/src/CoreLib/Interop/Unix/Interop.IOErrors.cs:66 
  at System.IO.FileStatus.SetAccessWriteTimes (System.String path, System.Nullable`1[T] accessSec, System.Nullable`1[T] accessUSec, System.Nullable`1[T] writeSec, System.Nullable`1[T] writeUSec) [0x000bc] in /Users/builder/jenkins/workspace/archive-mono/2019-10/android/release/external/corefx/src/System.IO.FileSystem/src/System/IO/FileStatus.Unix.cs:229 
  at System.IO.FileStatus.SetLastWriteTime (System.String path, System.DateTimeOffset time) [0x00000] in /Users/builder/jenkins/workspace/archive-mono/2019-10/android/release/external/corefx/src/System.IO.FileSystem/src/System/IO/FileStatus.Unix.cs:212 
  at System.IO.FileSystemInfo.set_LastWriteTimeCore (System.DateTimeOffset value) [0x00000] in /Users/builder/jenkins/workspace/archive-mono/2019-10/android/release/external/corefx/src/System.IO.FileSystem/src/System/IO/FileSystemInfo.Unix.cs:61 
  at (wrapper remoting-invoke-with-check) System.IO.FileSystemInfo.set_LastWriteTimeCore(System.DateTimeOffset)
  at System.IO.FileSystem.SetLastWriteTime (System.String fullPath, System.DateTimeOffset time, System.Boolean asDirectory) [0x00017] in /Users/builder/jenkins/workspace/archive-mono/2019-10/android/release/external/corefx/src/System.IO.FileSystem/src/System/IO/FileSystem.Unix.cs:554 
  at System.IO.File.SetLastWriteTime (System.String path, System.DateTime lastWriteTime) [0x00006] in /Users/builder/jenkins/workspace/archive-mono/2019-10/android/release/external/corefx/src/System.IO.FileSystem/src/System/IO/File.cs:221 
  at System.IO.Compression.ZipFileExtensions.ExtractToFile (System.IO.Compression.ZipArchiveEntry source, System.String destinationFileName, System.Boolean overwrite) [0x00058] in /Users/builder/jenkins/workspace/archive-mono/2019-10/android/release/external/corefx/src/System.IO.Compression.ZipFile/src/System/IO/Compression/ZipFileExtensions.cs:313 
  at System.IO.Compression.ZipFileExtensions.ExtractToDirectory (System.IO.Compression.ZipArchive source, System.String destinationDirectoryName, System.Boolean overwrite) [0x000c3] in /Users/builder/jenkins/workspace/archive-mono/2019-10/android/release/external/corefx/src/System.IO.Compression.ZipFile/src/System/IO/Compression/ZipFileExtensions.cs:187 
  at System.IO.Compression.ZipFile.ExtractToDirectory (System.String sourceArchiveFileName, System.String destinationDirectoryName, System.Text.Encoding entryNameEncoding, System.Boolean overwrite) [0x00017] in /Users/builder/jenkins/workspace/archive-mono/2019-10/android/release/external/corefx/src/System.IO.Compression.ZipFile/src/System/IO/Compression/ZipFile.cs:569 
  at System.IO.Compression.ZipFile.ExtractToDirectory (System.String sourceArchiveFileName, System.String destinationDirectoryName, System.Text.Encoding entryNameEncoding) [0x00000] in /Users/builder/jenkins/workspace/archive-mono/2019-10/android/release/external/corefx/src/System.IO.Compression.ZipFile/src/System/IO/Compression/ZipFile.cs:506 
  at System.IO.Compression.ZipFile.ExtractToDirectory (System.String sourceArchiveFileName, System.String destinationDirectoryName) [0x00000] in /Users/builder/jenkins/workspace/archive-mono/2019-10/android/release/external/corefx/src/System.IO.Compression.ZipFile/src/System/IO/Compression/ZipFile.cs:413 
  [Private Code]
@Dotnet-GitSync-Bot Dotnet-GitSync-Bot added area-System.IO.Compression untriaged New issue has not been triaged by the area owner labels Apr 23, 2020
@ghost
Copy link

ghost commented Apr 23, 2020

Tagging subscribers to this area:
Notify danmosemsft if you want to be subscribed.

@danmoseley
Copy link
Member

@akoeplinger is SetLastWriteTime expected to consistently work?

@akoeplinger
Copy link
Member

This looks similar to the issue we had with fchmod(): mono/mono#17133

@marek-safar marek-safar added this to the 6.0.0 milestone Jun 24, 2020
@marek-safar marek-safar removed the untriaged New issue has not been triaged by the area owner label Jun 24, 2020
adamsitnik added a commit to adamsitnik/runtime that referenced this issue Jul 27, 2021
@ghost ghost added the in-pr There is an active PR which will close this issue when it is merged label Jul 27, 2021
@ghost ghost removed the in-pr There is an active PR which will close this issue when it is merged label Jul 28, 2021
@adamsitnik
Copy link
Member

@SittenSpynne big thanks for a detailed bug report! it got fixed in #56370 and is going to be a part of .NET 6 RC1

@ghost ghost locked as resolved and limited conversation to collaborators Aug 27, 2021
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
6 participants