Powershell: `Compress-Archive` on Windows PowerShell generates an archive incompatible with OS X Archiver

Created on 31 Aug 2016  路  10Comments  路  Source: PowerShell/PowerShell

Steps to reproduce

  1. Launch Windows PowerShell.
  2. Type the following commands.
PS > $p = "$env:TEMP\$([System.Guid]::NewGuid().ToString('n'))"
PS > md $p | cd
PS > md dir1; cd dir1;
PS > '1' | sc file1
PS > cd ..
PS > Compress-Archive . zipped.zip
PS > explorer .
  1. Send the zipped.zip to a Mac with OS X El Capitan 10.11.3;
  2. Acquire the zipped.zip and decompress it with Archiver (if the Mac is brand-new, double-clicking the file should work).

    Expected behavior

The decompression gives a folder <the guid generated>, in which there is a folder dir1, in which there is a file file1.

Actual behavior

The decompression gives a file <the guid generated>\dir1\file1.

Environment data

> $PSVersionTable
Name                           Value
----                           -----
PSVersion                      5.1.14393.82
PSEdition                      Desktop
PSCompatibleVersions           {1.0, 2.0, 3.0, 4.0...}
BuildVersion                   10.0.14393.82
CLRVersion                     4.0.30319.42000
WSManStackVersion              3.0
PSRemotingProtocolVersion      2.3
SerializationVersion           1.1.0.1

Question / Discussion

Please identify if this is a problem with Compress-Archive cmdlet or OS X Archiver. If this is a problem with Compress-Archive, please fix the cmdlet in the next version. If this is a problem with OS X Archiver, please notify me so that I can forward the issue to Apple.

Also, I cannot test Compress-Archive in PowerShell Core or Expand-Archive in PowerShell Core at this time. If someone would like to test this, please do not hesitate to report it here.

Area-Cmdlets Issue-Bug Resolution-External

Most helpful comment

I just stumbled upon the same issue on Linux when opening an Zip-File that I created with Compress-Archive on a Win10 host. After uncompressing such Archives on Linux, nested subfolders and files will appear with concatenated names such as "folder\subfolder\test.txt", all cluttered in the root folder.

All 10 comments

Thank you for pointing it out!

I verified the problem.
I actually seen this problem before, with unpacking our AppVeyor artifacts on OS X.

I researched the problem a little bit and here is what I found

  • on FullCLR .NET 4.5.1 [System.IO.Compression.ZipFile]::CreateFromDirectory provides the same incompatible archive (that's what we use in our AppVeyor artifacts packaing)
  • on CoreCLR 1.0.0 [System.IO.Compression.ZipFile]::CreateFromDirectory creates a good, compatible archive
  • We use method CreateEntry in System.IO.Compression.ZipArchive class inside Compress-Archive. We may use it incorrectly or there could be a problem in corefx. Need more investigations here.

https://github.com/PowerShell/Microsoft.PowerShell.Archive/issues/11

We should track Archive issues in its own repo. The Archive module in PowerShell/PowerShell will be a submodule: https://github.com/PowerShell/PowerShell/issues/1979

We got a similar bug report in the dotnet/corefx repo, and the issue seems to only happen with files generated using PowerShell's Compress-Archive cmdlet, (no other compression tools show this problem): https://github.com/dotnet/corefx/issues/32788

When using PowerShell in Windows to create a zip file, it cannot be extracted in Linux/Unix using .NET Core libraries because of the directory separator enforced by PowerShell.

PowerShell should either use the libraries offered for .NET Core to generate zip files (which ensure the correct directory separator is always used independently of the OS) or modify their existing code (which inserts each zip file entry individually to the zip file) to ensure the paths of the zip files use the universally accepted directory separator "/".

Note: There used to be a bug on our side (as pointed out by @vors) but it has already been fixed both in .NET Core and in .NET Framework.

Steps to reproduce

Can repro both in the latest version of PowerShell and in 1.0 (the one included with Windows 10).

  1. In Windows, put a text file inside a couple of subfolders, then compress them compress them using PowerShell:
PS D:\source\repos> New-Item -Type Directory -Path Folder
PS D:\source\repos> New-Item -Type Directory -Path Folder\SubFolder
PS D:\source\repos> echo "Hello world" > Folder\SubFolder\file.txt
PS D:\source\repos> Compress-Archive Folder compressed.zip
  1. Copy the file to Linux. Create a C# .NET Core program that extracts zip files using ZipFileExtensions.ExtractToDirectory. An Exception will be thrown, as it was detailed here.

Proposed fixes

The ZipFileExtensions.ExtractToDirectory function avoids making assumptions about separators, in case the user is interacting with a zip file that does not contain files and folders, but something else. But if the zip file happens to contain files and folders, the character used as separator in Linux should always be "/".

But because PowerShell inserted each one of the zip archive entries into the zip file manually, the directory separator used was "", which is only valid in Windows. When attempting to extract this file in Linux/Unix using .NET Core, it's not possible because the file has invalid characters that prevent ensuring the correct creation of its subfolders.

There are two places in the PowerShell code that could be modified to ensure the universally accepted directory separator is used:

A) https://github.com/PowerShell/Microsoft.PowerShell.Archive/blob/master/Microsoft.PowerShell.Archive/Microsoft.PowerShell.Archive.psm1#L828

if($null -ne $currentFileStream)
{
    $srcStream = New-Object System.IO.BinaryReader $currentFileStream
    $entryPath = DirectorySeparatorNormalizeHelper $relativeFilePath
    $currentArchiveEntry = $zipArchive.CreateEntry($entryPath, $compression)

B) https://github.com/PowerShell/Microsoft.PowerShell.Archive/blob/master/Microsoft.PowerShell.Archive/Microsoft.PowerShell.Archive.psm1#L872

else
{
    $entryPath = DirectorySeparatorNormalizeHelper $relativeFilePath
    $currentArchiveEntry = $zipArchive.CreateEntry($entryPath, $compression)
    $numberOfItemsArchived += 1
    $addItemtoArchiveFileMessage = ($LocalizedData.AddItemtoArchiveFile -f $currentFilePath)
}

In both places, the lines that generate the $entryPath string should instead be:

$entryPath = DirectorySeparatorNormalizeHelper $relativeFilePath.Replace("\", "/")

Alternatively, PowerShell could be initialized with the ZipArchiveEntry.FullName Path Separator AppContextSwitchOverride turned off: https://docs.microsoft.com/en-us/dotnet/framework/migration-guide/mitigation-ziparchiveentry-fullname-path-separator

Please let me know if we can reopen this bug or if I should create a new one.

I just stumbled upon the same issue on Linux when opening an Zip-File that I created with Compress-Archive on a Win10 host. After uncompressing such Archives on Linux, nested subfolders and files will appear with concatenated names such as "folder\subfolder\test.txt", all cluttered in the root folder.

Mentioning @SteveL-MSFT in case the comments get buried.
Edit: My bad, I added a space after the @ character.

@SteveL-MSFT since Github apparently decided to eat that mention 馃し鈥嶁檪

The fix was merged. @anmenaga will publish a version to PSGallery by next week.

Yay! Thank you! 馃帀

Great - thanks a lot!

Was this page helpful?
0 / 5 - 0 ratings

Related issues

lzybkr picture lzybkr  路  3Comments

garegin16 picture garegin16  路  3Comments

andschwa picture andschwa  路  3Comments

SteveL-MSFT picture SteveL-MSFT  路  3Comments

aragula12 picture aragula12  路  3Comments