PowerShell / Microsoft.PowerShell.Archive

Archive PowerShell module contains cmdlets for working with ZIP archives
https://technet.microsoft.com/en-us/library/dn818910.aspx
MIT License
93 stars 37 forks source link

Compress-Archive creates a strange directory entry for certain file system configurations #145

Open ForNeVeR opened 1 year ago

ForNeVeR commented 1 year ago

Prerequisites

Steps to reproduce

Sometimes, Compress-Archive creates a weird directory-like entry in the produced archive. The details I was able to find:

So, exact steps to reproduce:

$ md playground/pt/fspt/d
$ echo xxx >> playground/pt/fspt/d/file.txt
$ Compress-Archive playground/* -DestinationPath 'file.zip' -Force

Now, examine the resulting archive using zipinfo (I wasn't able to quickly find a zipinfo distribution for Windows, so installed sudo apt install unzip on my Ubuntu into WSL):

$ zipinfo file.zip
Archive:  file.zip
Zip file size: 233 bytes, number of entries: 2
-rw----     2.0 fat        0 b- stor 22-Dec-07 20:56 pt/fspt/
-rw----     2.0 fat        5 b- defN 22-Dec-07 20:56 pt/fspt/d/file.txt
2 files, 5 bytes uncompressed, 7 bytes compressed:  -40.0%

The weird path is the first entry:

-rw----     2.0 fat        0 b- stor 22-Dec-07 20:56 pt/fspt/

Notably, the weird entry goes away if I create a file in directory pt/fspt:

$ echo 123 >> playground/pt/fspt/111.txt
$ Compress-Archive playground/* -DestinationPath 'file.zip' -force && wsl -d Ubuntu zipinfo file.zip
Archive:  file.zip
Zip file size: 254 bytes, number of entries: 2
-rw----     2.0 fat        5 b- defN 22-Dec-07 21:15 pt/fspt/111.txt
-rw----     2.0 fat        5 b- defN 22-Dec-07 20:56 pt/fspt/d/file.txt
2 files, 10 bytes uncompressed, 14 bytes compressed:  -40.0%

I've seen two kinds of other archives:

  1. The ones that contain no directory entries at all.
  2. The ones that contain directory entries with drwxr-xr-x flags instead of -rw----.

At the same time, [System.IO.Compression.ZipFile]::CreateFromDirectory works well:

$ [System.IO.Compression.ZipFile]::CreateFromDirectory($(Resolve-Path playground), "$(Resolve-Path '.')/file2.zip")
$ zipinfo file2.zip
Archive:  file2.zip
Zip file size: 141 bytes, number of entries: 1
-rw----     2.0 fat        5 b- defN 22-Dec-07 20:56 pt/fspt/d/file.txt
1 file, 5 bytes uncompressed, 7 bytes compressed:  -40.0%

So, System.IO.Compression.ZipFile]::CreateFromDirectory doesn't have this problem.

For empty directory, it will still create an entry with -rw---- flags, though, so it is not without problems.

Expected behavior

I'd say that Compress-Archive should work the same way as System.IO.Compression.ZipFile]::CreateFromDirectory.

Actual behavior

Compress-Archive behaves differently from System.IO.Compression.ZipFile]::CreateFromDirectory, and in a very strange way that is.

Environment data

The issue reproduces in both Windows PowerShell 5.1 and PowerShell 7.3.

# pwsh 7.3
Name                           Value
----                           -----
PSVersion                      7.3.0
PSEdition                      Core
GitCommitId                    7.3.0
OS                             Microsoft Windows 10.0.22000
Platform                       Win32NT
PSCompatibleVersions           {1.0, 2.0, 3.0, 4.0…}
PSRemotingProtocolVersion      2.3
SerializationVersion           1.1.0.1
WSManStackVersion              3.0

# powershell 5
Name                           Value
----                           -----
PSVersion                      5.1.22000.832
PSEdition                      Desktop
PSCompatibleVersions           {1.0, 2.0, 3.0, 4.0...}
BuildVersion                   10.0.22000.832
CLRVersion                     4.0.30319.42000
WSManStackVersion              3.0
PSRemotingProtocolVersion      2.3
SerializationVersion           1.1.0.1
237dmitry commented 1 year ago

Zip listing strange but resulting zip-archive is correct.

zip

ForNeVeR commented 1 year ago

It depends on your definition of "correct".

237dmitry commented 1 year ago

It depends on your definition of "correct".

Look at uzip. And unzipping as expected:

7z x file.zip

 $ (dir ./pt -Recurse).FullName
/home/herz/Desktop/pt/fspt
/home/herz/Desktop/pt/fspt/d
/home/herz/Desktop/pt/fspt/d/file.txt

Perhaps this is a compression algorithm when empty directories are "merged" with a file.

ForNeVeR commented 1 year ago

If your definition of "correct" is "whatever 7zip works with", then yes, it is correct.

It is not correct w.r.t. every interop scenario, though.

In particular, java.util.zip.ZipEntry::isDirectory has problems with such an archive: it reports directory entries to be files, which breaks things.

Though, after taking a closer look, I see that it only has problems with an archive created by Windows PowerShell 5.1, not PowerShell 7.3: it's the kind of the final slash that turns out to be important for it[^1]. Not the flags reported by zipinfo.

The logic of creating directory entries only for directories of certain nesting level and with no files still looks kinda weird.

Notably, the version from the master branch doesn't have this strange logic: it creates a new entry for every directory it seems.

[^1]: Not sure I mentioned that fact above, but the only notable difference between archives created by pwsh 5.1 and pwsh 7.3 is that 7.3 generated paths inside archives separated with / (even on Windows), while pwsh 5.1 separates paths with \.

237dmitry commented 1 year ago

If your definition of "correct" is "whatever 7zip works with", then yes, it is correct.

Yes. file-roller, unzip, 7-zip, mc and Far plugins. All of them extract file.zip without errors.I can't test the other archivers.