Skip to content

Commit

Permalink
Merge pull request #8 from diffplug/archiveFix
Browse files Browse the repository at this point in the history
ArchiveUnpacker now preserves "executable" permissions in Tar and Zip
  • Loading branch information
akhikhl committed Sep 16, 2015
2 parents da59a97 + f6de1cc commit 54eb61b
Show file tree
Hide file tree
Showing 2 changed files with 58 additions and 9 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,14 @@ import java.io.FileOutputStream
import java.io.IOException
import java.io.OutputStream
import java.util.zip.GZIPInputStream
import java.util.zip.ZipEntry
import java.util.zip.ZipInputStream

import org.apache.commons.compress.archivers.ArchiveEntry
import org.apache.commons.compress.archivers.ArchiveException
import org.apache.commons.compress.archivers.ArchiveStreamFactory
import org.apache.commons.compress.archivers.tar.TarArchiveEntry
import org.apache.commons.compress.archivers.tar.TarArchiveInputStream
import org.apache.commons.compress.archivers.zip.ZipArchiveEntry
import org.apache.commons.compress.archivers.zip.ZipArchiveInputStream
import org.apache.commons.io.IOUtils
import org.apache.commons.lang3.StringUtils

Expand Down Expand Up @@ -102,6 +103,9 @@ final class ArchiveUnpacker {
outputFile.withOutputStream { outputFileStream ->
IOUtils.copy(tarInputStream, outputFileStream)
}
if (isExecutable(entry, entry.getMode())) {
outputFile.setExecutable(true);
}
}
}
} finally {
Expand All @@ -112,9 +116,9 @@ final class ArchiveUnpacker {

void unZip(final File inputFile, final File outputDir) throws IOException, ArchiveException {
console.startProgress("Unzipping file: ${inputFile.getName()}")
ZipInputStream zis = new ZipInputStream(new FileInputStream(inputFile))
ZipArchiveInputStream zis = new ZipArchiveInputStream(new FileInputStream(inputFile))
try {
ZipEntry ze = zis.getNextEntry()
ZipArchiveEntry ze = zis.getNextEntry()
while (ze != null) {
final File outputFile = new File(outputDir, ze.getName())
console.info(ze.getName())
Expand All @@ -125,6 +129,9 @@ final class ArchiveUnpacker {
outputFile.withOutputStream { fos ->
IOUtils.copy(zis, fos)
}
if (isExecutable(ze, ze.getUnixMode())) {
outputFile.setExecutable(true);
}
}
ze = zis.getNextEntry()
}
Expand All @@ -133,4 +140,46 @@ final class ArchiveUnpacker {
console.endProgress()
}
}

/** Returns true if the given unix filemode contains any executable flags. */
public static boolean isExecutable(ArchiveEntry entry, int mode) {
try {
return parseFilePermissions(mode).contains("x");
} catch (Exception e) {
System.err.println("Ignoring permissions for " + entry.getName() + " mode= " + mode + " because " + e.getMessage());
}
}

/** Returns a modeStr which is compatible with PosixFilePermissions.fromString.
*
* TODO: When unpuzzle upgrades to Java >= 7
* This modeStr is compatible with PosixFilePermissions.fromString
* which is a Java7 API. In the meantime, we will settle for
* File.setExecutable() which is available since 6.
*/
public static String parseFilePermissions(int mode) {
// add octal 1 000 (to ensure that the string has at least 4 characters)
String modeNumStr = Integer.toOctalString(mode + 01000);
if (modeNumStr.length() < 3) {
throw new IllegalArgumentException("There should be at least 4 digits, but this only had " )
}
CharSequence permissions = modeNumStr.substring(modeNumStr.length()-3)

StringBuffer b = new StringBuffer(9);
for (int i = 0; i < 3; i++) {
char c = permissions.charAt(i);
switch (c) {
case '0': b.append("---"); break;
case '1': b.append("--x"); break;
case '2': b.append("-w-"); break;
case '3': b.append("-wx"); break;
case '4': b.append("r--"); break;
case '5': b.append("r-x"); break;
case '6': b.append("rw-"); break;
case '7': b.append("rwx"); break;
default: throw new IllegalArgumentException("Each digit should be 0-7, was " + c);
}
}
return b.toString();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import spock.lang.Specification
* Unit-test for {@link org.akhikhl.unpuzzle.utils.ArchiveUnpacker} class.
* @author akhikhl
*/
class ArchiveUnpackerTest {
class ArchiveUnpackerTest extends Specification {

static console

Expand All @@ -37,9 +37,9 @@ class ArchiveUnpackerTest {
File[] unpackedFiles = testFolder.listFiles()
then:
unpackedFiles != null
unpackedFiles.length() == 1
unpackedFiles.length == 1
unpackedFiles[0].name == 'sample.txt'
unpackedFiles[0].text == '0042bfe2-8812-11e3-85b6-6bf8a04179ee'
unpackedFiles[0].text == '0042bfe2-8812-11e3-85b6-6bf8a04179ee\n'
}

def 'should unpack .zip files'() {
Expand All @@ -49,9 +49,9 @@ class ArchiveUnpackerTest {
File[] unpackedFiles = testFolder.listFiles()
then:
unpackedFiles != null
unpackedFiles.length() == 1
unpackedFiles.length == 1
unpackedFiles[0].name == 'sample.txt'
unpackedFiles[0].text == '0042bfe2-8812-11e3-85b6-6bf8a04179ee'
unpackedFiles[0].text == '0042bfe2-8812-11e3-85b6-6bf8a04179ee\n'
}
}

0 comments on commit 54eb61b

Please sign in to comment.