Skip to content

Commit 5e603a1

Browse files
committed
SCANJLIB-208 Set posix permissions when extracting tar.gz
1 parent 19221c9 commit 5e603a1

2 files changed

Lines changed: 84 additions & 12 deletions

File tree

lib/src/main/java/org/sonarsource/scanner/lib/internal/util/CompressionUtils.java

Lines changed: 49 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -25,19 +25,35 @@
2525
import java.io.OutputStream;
2626
import java.nio.file.Files;
2727
import java.nio.file.Path;
28+
import java.nio.file.StandardCopyOption;
29+
import java.nio.file.attribute.PosixFilePermission;
30+
import java.util.EnumSet;
2831
import java.util.Enumeration;
32+
import java.util.List;
33+
import java.util.Set;
2934
import java.util.function.Predicate;
3035
import java.util.zip.ZipEntry;
3136
import java.util.zip.ZipFile;
32-
import org.apache.commons.compress.archivers.ArchiveEntry;
37+
import org.apache.commons.compress.archivers.tar.TarArchiveEntry;
3338
import org.apache.commons.compress.archivers.tar.TarArchiveInputStream;
3439
import org.apache.commons.compress.compressors.gzip.GzipCompressorInputStream;
3540
import org.apache.commons.io.IOUtils;
3641

42+
import static org.apache.commons.lang3.SystemUtils.IS_OS_WINDOWS;
43+
3744
public final class CompressionUtils {
3845

3946
private static final String ERROR_CREATING_DIRECTORY = "Error creating directory: ";
4047

48+
// indexed by the standard binary representation of permission
49+
// if permission is 644; in binary 110 100 100
50+
// the positions of the ones (little endian) give the index of the permission in the list below
51+
private static final List<PosixFilePermission> POSIX_PERMISSIONS = List.of(
52+
PosixFilePermission.OTHERS_EXECUTE, PosixFilePermission.OTHERS_WRITE, PosixFilePermission.OTHERS_READ,
53+
PosixFilePermission.GROUP_EXECUTE, PosixFilePermission.GROUP_WRITE, PosixFilePermission.GROUP_READ,
54+
PosixFilePermission.OWNER_EXECUTE, PosixFilePermission.OWNER_WRITE, PosixFilePermission.OWNER_READ);
55+
private static final int MAX_MODE = (1 << POSIX_PERMISSIONS.size()) - 1;
56+
4157
private CompressionUtils() {
4258
// utility class
4359
}
@@ -110,23 +126,44 @@ public static void extractTarGz(Path compressedFile, Path targetDir) throws IOEx
110126
try (InputStream fis = Files.newInputStream(compressedFile);
111127
InputStream bis = new BufferedInputStream(fis);
112128
InputStream gzis = new GzipCompressorInputStream(bis);
113-
TarArchiveInputStream archive = new TarArchiveInputStream(gzis)) {
114-
ArchiveEntry entry;
115-
while ((entry = archive.getNextEntry()) != null) {
116-
if (!archive.canReadEntryData(entry)) {
129+
TarArchiveInputStream tarArchiveInputStream = new TarArchiveInputStream(gzis)) {
130+
TarArchiveEntry targzEntry;
131+
while ((targzEntry = tarArchiveInputStream.getNextEntry()) != null) {
132+
if (!tarArchiveInputStream.canReadEntryData(targzEntry)) {
117133
continue;
118134
}
119-
var f = targetDir.resolve(entry.getName());
120-
if (entry.isDirectory()) {
121-
Files.createDirectories(f);
135+
var entry = targetDir.resolve(targzEntry.getName());
136+
if (targzEntry.isDirectory()) {
137+
Files.createDirectories(entry);
122138
} else {
123-
var parent = f.getParent();
124-
Files.createDirectories(parent);
125-
try (OutputStream o = Files.newOutputStream(f)) {
126-
IOUtils.copy(archive, o);
139+
if (!Files.isDirectory(entry.getParent())) {
140+
Files.createDirectories(entry.getParent());
141+
}
142+
Files.copy(tarArchiveInputStream, entry, StandardCopyOption.REPLACE_EXISTING);
143+
int mode = targzEntry.getMode();
144+
if (mode != 0 && !IS_OS_WINDOWS) {
145+
Set<PosixFilePermission> permissions = fromFileMode(mode);
146+
Files.setPosixFilePermissions(entry, permissions);
127147
}
128148
}
129149
}
130150
}
131151
}
152+
153+
static Set<PosixFilePermission> fromFileMode(final int fileMode) {
154+
if ((fileMode & MAX_MODE) != fileMode) {
155+
throw new IllegalStateException(
156+
"Invalid file mode '" + Integer.toOctalString(fileMode) + "'. File mode must be between 0 and " + MAX_MODE + " (" + Integer.toOctalString(MAX_MODE) + " in octal)");
157+
}
158+
159+
final Set<PosixFilePermission> ret = EnumSet.noneOf(PosixFilePermission.class);
160+
161+
for (int i = 0; i < POSIX_PERMISSIONS.size(); i++) {
162+
if ((fileMode & (1 << i)) != 0) {
163+
ret.add(POSIX_PERMISSIONS.get(i));
164+
}
165+
}
166+
167+
return ret;
168+
}
132169
}

lib/src/test/java/org/sonarsource/scanner/lib/internal/util/CompressionUtilsTest.java

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,15 @@
2525
import org.junit.jupiter.api.Test;
2626
import org.junit.jupiter.api.io.TempDir;
2727

28+
import static java.nio.file.attribute.PosixFilePermission.GROUP_EXECUTE;
29+
import static java.nio.file.attribute.PosixFilePermission.GROUP_READ;
30+
import static java.nio.file.attribute.PosixFilePermission.GROUP_WRITE;
31+
import static java.nio.file.attribute.PosixFilePermission.OTHERS_EXECUTE;
32+
import static java.nio.file.attribute.PosixFilePermission.OTHERS_READ;
33+
import static java.nio.file.attribute.PosixFilePermission.OTHERS_WRITE;
34+
import static java.nio.file.attribute.PosixFilePermission.OWNER_EXECUTE;
35+
import static java.nio.file.attribute.PosixFilePermission.OWNER_READ;
36+
import static java.nio.file.attribute.PosixFilePermission.OWNER_WRITE;
2837
import static org.assertj.core.api.Assertions.assertThat;
2938
import static org.assertj.core.api.Assertions.assertThatThrownBy;
3039

@@ -69,4 +78,30 @@ void extract_tar_gz() throws IOException {
6978
CompressionUtils.extractTarGz(tar, toDir);
7079
assertThat(toDir.toFile().list()).hasSize(3);
7180
}
81+
82+
@Test
83+
void fileMode_conversion() {
84+
assertThat(CompressionUtils.fromFileMode(Integer.parseInt("000", 8))).isEmpty();
85+
assertThat(CompressionUtils.fromFileMode(Integer.parseInt("001", 8))).containsExactlyInAnyOrder(OTHERS_EXECUTE);
86+
assertThat(CompressionUtils.fromFileMode(Integer.parseInt("002", 8))).containsExactlyInAnyOrder(OTHERS_WRITE);
87+
assertThat(CompressionUtils.fromFileMode(Integer.parseInt("003", 8))).containsExactlyInAnyOrder(OTHERS_WRITE, OTHERS_EXECUTE);
88+
assertThat(CompressionUtils.fromFileMode(Integer.parseInt("004", 8))).containsExactlyInAnyOrder(OTHERS_READ);
89+
assertThat(CompressionUtils.fromFileMode(Integer.parseInt("005", 8))).containsExactlyInAnyOrder(OTHERS_READ, OTHERS_EXECUTE);
90+
assertThat(CompressionUtils.fromFileMode(Integer.parseInt("006", 8))).containsExactlyInAnyOrder(OTHERS_READ, OTHERS_WRITE);
91+
assertThat(CompressionUtils.fromFileMode(Integer.parseInt("007", 8))).containsExactlyInAnyOrder(OTHERS_READ, OTHERS_WRITE, OTHERS_EXECUTE);
92+
assertThat(CompressionUtils.fromFileMode(Integer.parseInt("010", 8))).containsExactlyInAnyOrder(GROUP_EXECUTE);
93+
assertThat(CompressionUtils.fromFileMode(Integer.parseInt("020", 8))).containsExactlyInAnyOrder(GROUP_WRITE);
94+
assertThat(CompressionUtils.fromFileMode(Integer.parseInt("030", 8))).containsExactlyInAnyOrder(GROUP_WRITE, GROUP_EXECUTE);
95+
assertThat(CompressionUtils.fromFileMode(Integer.parseInt("040", 8))).containsExactlyInAnyOrder(GROUP_READ);
96+
assertThat(CompressionUtils.fromFileMode(Integer.parseInt("050", 8))).containsExactlyInAnyOrder(GROUP_READ, GROUP_EXECUTE);
97+
assertThat(CompressionUtils.fromFileMode(Integer.parseInt("060", 8))).containsExactlyInAnyOrder(GROUP_READ, GROUP_WRITE);
98+
assertThat(CompressionUtils.fromFileMode(Integer.parseInt("070", 8))).containsExactlyInAnyOrder(GROUP_READ, GROUP_WRITE, GROUP_EXECUTE);
99+
assertThat(CompressionUtils.fromFileMode(Integer.parseInt("100", 8))).containsExactlyInAnyOrder(OWNER_EXECUTE);
100+
assertThat(CompressionUtils.fromFileMode(Integer.parseInt("200", 8))).containsExactlyInAnyOrder(OWNER_WRITE);
101+
assertThat(CompressionUtils.fromFileMode(Integer.parseInt("300", 8))).containsExactlyInAnyOrder(OWNER_WRITE, OWNER_EXECUTE);
102+
assertThat(CompressionUtils.fromFileMode(Integer.parseInt("400", 8))).containsExactlyInAnyOrder(OWNER_READ);
103+
assertThat(CompressionUtils.fromFileMode(Integer.parseInt("500", 8))).containsExactlyInAnyOrder(OWNER_READ, OWNER_EXECUTE);
104+
assertThat(CompressionUtils.fromFileMode(Integer.parseInt("600", 8))).containsExactlyInAnyOrder(OWNER_READ, OWNER_WRITE);
105+
assertThat(CompressionUtils.fromFileMode(Integer.parseInt("700", 8))).containsExactlyInAnyOrder(OWNER_READ, OWNER_WRITE, OWNER_EXECUTE);
106+
}
72107
}

0 commit comments

Comments
 (0)