Skip to content

ZipFile.ExtractToDirectory() fails on Android #35374

@SittenSpynne

Description

@SittenSpynne

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]

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions