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

Unix execution bits are not preserved when unzipping on macOS Catalina or Linux #183

Closed
nerdydrew opened this issue May 7, 2020 · 5 comments
Assignees
Labels
bug Something isn't working resolved

Comments

@nerdydrew
Copy link

Reproduction steps:

  1. Compress an executable file with Zip4j.
  2. Unzip it without using Zip4j—either by double-clicking the ZIP file from the Finder in macOS Catalina or by running the unzip command in macOS or Linux.
  3. The unzipped file is no longer executable, but it should be.

The unit test below also reproduces the problem as long as your system has the unzip command.

  @Test
  public void testExecutionPermissionIsPreserved() throws IOException, InterruptedException {
    // ZIP an executable file.
    ZipFile zipFile = new ZipFile(generatedZipFile);
    File inputFile = TestUtils.getTestFileFromResources("zero_byte_file.txt");
    inputFile.setExecutable(true);
    zipFile.addFile(inputFile.getPath());

    // Unzip using the system's `unzip` command instead of Zip4j.
    String command = "unzip " + generatedZipFile.getPath() + " -d " + outputFolder.getPath();
    int processOutput = Runtime.getRuntime().exec(command).waitFor();
    // Ensure the command exits successfully.
    assertThat(processOutput).isEqualTo(0);

    // Ensure the unzipped file exists and is a file.
    File unzippedFile = new File(outputFolder, "zero_byte_file.txt");
    assertThat(unzippedFile.isFile()).isTrue();
    // The file should still be executable, but this line fails.
    assertThat(unzippedFile.canExecute()).isTrue();
  }

The problem is that Zip4j uses FAT file format instead of Unix. This forum thread goes into more detail on the problem, including how to identify the file format using zipinfo and how to set the correct flags in a different language (Visual Basic?). Apache Commons Compress has setUnixMode to switch from FAT to Unix mode, but it's missing other features that Zip4j has. Would it be possible for FAT vs. Unix mode to be configurable in Zip4j?

@srikanth-lingala srikanth-lingala self-assigned this May 9, 2020
@srikanth-lingala srikanth-lingala added bug Something isn't working in-progress labels May 9, 2020
@srikanth-lingala
Copy link
Owner

srikanth-lingala commented May 20, 2020

Zip4j writes file executable information in external file attributes as specified by the zip specification. I have just investigated this issue, and I can see that the executable flag is in fact being written in the external file attributes part of the zip headers. Try extracting the zip file created with zip4j with zip4j, 7zip, The Unarchiver or Keka, and you will see that the executable flag is in fact set for the extracted file. It is only the unzip command which doesn't seem to respect this flag. But according to zip specification, this is the section of the zip headers which define the executable flag of a file. I guess the unzip command relies on one of InfoZip's extra data records to set the executable flag, which as far as I know, is not a must to be part of the zip file. To be able to define a file as executable or not, can be done with external file attributes and no additional extra data records are required for that. I am really not sure why unzip command does not respect these flags.

> The problem is that Zip4j uses FAT file format instead of Unix.

I am not sure what you mean by this. Zip4j tries to read the file attributes for the os on which the zip file is being generated. You can see the code here for Windows and here for mac and unix based systems. But in the end, these attributes are converted into a zip standard flags.

BTW, which version of zip4j are you on? These features have been vastly improved in zip4j v 2.x compared to zip4j v 1.x.

Honestly speaking, I am not quite sure if it is a bug in zip4j. AFAIK, zip4j is following the standards defined in zip specification, and as evident by other tools which set the executable flag for the files in zip generated by zip4j. I will remove the "bug" label on this issue. More research is needed on this issue to understand unzip command behaviour, but my best guess at the moment is that, as mentioned earlier, unzip command probably expects some custom InfoZip extra data record. But I will stop with my speculation here, and will do some more research into this, but at the moment it doesn't look like a bug in zip4j for me.

Update: See comment below

@srikanth-lingala srikanth-lingala removed the bug Something isn't working label May 20, 2020
@srikanth-lingala
Copy link
Owner

srikanth-lingala commented May 20, 2020

After some more research, I think I found the issue, and I understand the points you made with the FAT and Unix file format. The issue here is with the bytes defined in VersionMadeBy field of the file header in the zip. This has to be adjusted to reflect the os compatibility. I will fix this, and I will also add a way to set a mode (similar to unixMode in apache commons compress that you have mentioned). bug label added back again

@srikanth-lingala
Copy link
Owner

Issue fixed. I will include the fix in the next release. By default platform will be determined by the OS, but you will be able to force using unix mode by ZipParameters.setUnixMode(true)

@srikanth-lingala
Copy link
Owner

Issue fixed in v2.6.0 released today

@nerdydrew
Copy link
Author

Thanks! That fixes my problem.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working resolved
Projects
None yet
Development

No branches or pull requests

2 participants