Apply Gradle fileMode and dirMode consistently in jar and war archives

Fixes gh-37496
2.7.x
Scott Frederick 1 year ago
parent fce64878b4
commit aeeb5cf1f8

@ -1,5 +1,5 @@
/*
* Copyright 2012-2022 the original author or authors.
* Copyright 2012-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -47,6 +47,7 @@ import org.gradle.api.tasks.util.PatternSet;
*
* @author Andy Wilkinson
* @author Phillip Webb
* @author Scott Frederick
* @see BootJar
* @see BootWar
*/
@ -113,6 +114,8 @@ class BootArchiveSupport {
File output = jar.getArchiveFile().get().getAsFile();
Manifest manifest = jar.getManifest();
boolean preserveFileTimestamps = jar.isPreserveFileTimestamps();
Integer dirMode = jar.getDirMode();
Integer fileMode = jar.getFileMode();
boolean includeDefaultLoader = isUsingDefaultLoader(jar);
Spec<FileTreeElement> requiresUnpack = this.requiresUnpack.getAsSpec();
Spec<FileTreeElement> exclusions = this.exclusions.getAsExcludeSpec();
@ -120,9 +123,9 @@ class BootArchiveSupport {
Spec<FileCopyDetails> librarySpec = this.librarySpec;
Function<FileCopyDetails, ZipCompression> compressionResolver = this.compressionResolver;
String encoding = jar.getMetadataCharset();
CopyAction action = new BootZipCopyAction(output, manifest, preserveFileTimestamps, includeDefaultLoader,
layerToolsLocation, requiresUnpack, exclusions, launchScript, librarySpec, compressionResolver,
encoding, layerResolver);
CopyAction action = new BootZipCopyAction(output, manifest, preserveFileTimestamps, dirMode, fileMode,
includeDefaultLoader, layerToolsLocation, requiresUnpack, exclusions, launchScript, librarySpec,
compressionResolver, encoding, layerResolver);
return jar.isReproducibleFileOrder() ? new ReproducibleOrderingCopyAction(action) : action;
}

@ -77,6 +77,10 @@ class BootZipCopyAction implements CopyAction {
private final boolean preserveFileTimestamps;
private final Integer dirMode;
private final Integer fileMode;
private final boolean includeDefaultLoader;
private final String layerToolsLocation;
@ -95,14 +99,16 @@ class BootZipCopyAction implements CopyAction {
private final LayerResolver layerResolver;
BootZipCopyAction(File output, Manifest manifest, boolean preserveFileTimestamps, boolean includeDefaultLoader,
String layerToolsLocation, Spec<FileTreeElement> requiresUnpack, Spec<FileTreeElement> exclusions,
LaunchScriptConfiguration launchScript, Spec<FileCopyDetails> librarySpec,
BootZipCopyAction(File output, Manifest manifest, boolean preserveFileTimestamps, Integer dirMode, Integer fileMode,
boolean includeDefaultLoader, String layerToolsLocation, Spec<FileTreeElement> requiresUnpack,
Spec<FileTreeElement> exclusions, LaunchScriptConfiguration launchScript, Spec<FileCopyDetails> librarySpec,
Function<FileCopyDetails, ZipCompression> compressionResolver, String encoding,
LayerResolver layerResolver) {
this.output = output;
this.manifest = manifest;
this.preserveFileTimestamps = preserveFileTimestamps;
this.dirMode = dirMode;
this.fileMode = fileMode;
this.includeDefaultLoader = includeDefaultLoader;
this.layerToolsLocation = layerToolsLocation;
this.requiresUnpack = requiresUnpack;
@ -225,7 +231,7 @@ class BootZipCopyAction implements CopyAction {
private void processDirectory(FileCopyDetails details) throws IOException {
String name = details.getRelativePath().getPathString();
ZipArchiveEntry entry = new ZipArchiveEntry(name + '/');
prepareEntry(entry, name, getTime(details), UnixStat.FILE_FLAG | details.getMode());
prepareEntry(entry, name, getTime(details), getFileMode(details));
this.out.putArchiveEntry(entry);
this.out.closeArchiveEntry();
this.writtenDirectories.add(name);
@ -234,7 +240,7 @@ class BootZipCopyAction implements CopyAction {
private void processFile(FileCopyDetails details) throws IOException {
String name = details.getRelativePath().getPathString();
ZipArchiveEntry entry = new ZipArchiveEntry(name);
prepareEntry(entry, name, getTime(details), UnixStat.FILE_FLAG | details.getMode());
prepareEntry(entry, name, getTime(details), getFileMode(details));
ZipCompression compression = BootZipCopyAction.this.compressionResolver.apply(details);
if (compression == ZipCompression.STORED) {
prepareStoredEntry(details, entry);
@ -255,7 +261,7 @@ class BootZipCopyAction implements CopyAction {
String parentDirectory = getParentDirectory(name);
if (parentDirectory != null && this.writtenDirectories.add(parentDirectory)) {
ZipArchiveEntry entry = new ZipArchiveEntry(parentDirectory + '/');
prepareEntry(entry, parentDirectory, time, UnixStat.DIR_FLAG | UnixStat.DEFAULT_DIR_PERM);
prepareEntry(entry, parentDirectory, time, getDirMode());
this.out.putArchiveEntry(entry);
this.out.closeArchiveEntry();
}
@ -285,7 +291,7 @@ class BootZipCopyAction implements CopyAction {
// Always write loader entries after META-INF directory (see gh-16698)
return;
}
LoaderZipEntries loaderEntries = new LoaderZipEntries(getTime());
LoaderZipEntries loaderEntries = new LoaderZipEntries(getTime(), getDirMode(), getFileMode());
this.writtenLoaderEntries = loaderEntries.writeTo(this.out);
if (BootZipCopyAction.this.layerResolver != null) {
for (String name : this.writtenLoaderEntries.getFiles()) {
@ -350,7 +356,7 @@ class BootZipCopyAction implements CopyAction {
private void writeEntry(String name, ZipEntryContentWriter entryWriter, boolean addToLayerIndex,
ZipEntryCustomizer entryCustomizer) throws IOException {
ZipArchiveEntry entry = new ZipArchiveEntry(name);
prepareEntry(entry, name, getTime(), UnixStat.FILE_FLAG | UnixStat.DEFAULT_FILE_PERM);
prepareEntry(entry, name, getTime(), getFileMode());
entryCustomizer.customize(entry);
this.out.putArchiveEntry(entry);
entryWriter.writeTo(this.out);
@ -394,6 +400,21 @@ class BootZipCopyAction implements CopyAction {
return null;
}
private int getDirMode() {
return (BootZipCopyAction.this.dirMode != null) ? BootZipCopyAction.this.dirMode
: UnixStat.DIR_FLAG | UnixStat.DEFAULT_DIR_PERM;
}
private int getFileMode() {
return (BootZipCopyAction.this.fileMode != null) ? BootZipCopyAction.this.fileMode
: UnixStat.FILE_FLAG | UnixStat.DEFAULT_FILE_PERM;
}
private int getFileMode(FileCopyDetails details) {
return (BootZipCopyAction.this.fileMode != null) ? BootZipCopyAction.this.fileMode
: UnixStat.FILE_FLAG | details.getMode();
}
}
/**

@ -24,7 +24,6 @@ import java.util.Set;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import org.apache.commons.compress.archivers.zip.UnixStat;
import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
import org.apache.commons.compress.archivers.zip.ZipArchiveOutputStream;
import org.gradle.api.file.FileTreeElement;
@ -42,8 +41,14 @@ class LoaderZipEntries {
private final Long entryTime;
LoaderZipEntries(Long entryTime) {
private final int dirMode;
private final int fileMode;
LoaderZipEntries(Long entryTime, int dirMode, int fileMode) {
this.entryTime = entryTime;
this.dirMode = dirMode;
this.fileMode = fileMode;
}
WrittenEntries writeTo(ZipArchiveOutputStream out) throws IOException {
@ -67,13 +72,13 @@ class LoaderZipEntries {
}
private void writeDirectory(ZipArchiveEntry entry, ZipArchiveOutputStream out) throws IOException {
prepareEntry(entry, UnixStat.DIR_FLAG | UnixStat.DEFAULT_DIR_PERM);
prepareEntry(entry, this.dirMode);
out.putArchiveEntry(entry);
out.closeArchiveEntry();
}
private void writeClass(ZipArchiveEntry entry, ZipInputStream in, ZipArchiveOutputStream out) throws IOException {
prepareEntry(entry, UnixStat.FILE_FLAG | UnixStat.DEFAULT_FILE_PERM);
prepareEntry(entry, this.fileMode);
out.putArchiveEntry(entry);
copy(in, out);
out.closeArchiveEntry();

@ -29,6 +29,7 @@ import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Locale;
@ -45,6 +46,9 @@ import java.util.stream.Collectors;
import java.util.stream.Stream;
import java.util.zip.ZipEntry;
import org.apache.commons.compress.archivers.zip.UnixStat;
import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
import org.apache.commons.compress.archivers.zip.ZipFile;
import org.gradle.testkit.runner.BuildResult;
import org.gradle.testkit.runner.TaskOutcome;
import org.junit.jupiter.api.TestTemplate;
@ -62,6 +66,7 @@ import static org.assertj.core.api.Assertions.assertThat;
*
* @author Andy Wilkinson
* @author Madhura Bhave
* @author Scott Frederick
*/
abstract class AbstractBootArchiveIntegrationTests {
@ -515,6 +520,48 @@ abstract class AbstractBootArchiveIntegrationTests {
}
}
@TestTemplate
void defaultDirAndFileModesAreUsed() throws IOException {
BuildResult result = this.gradleBuild.build(this.taskName);
assertThat(result.task(":" + this.taskName).getOutcome()).isEqualTo(TaskOutcome.SUCCESS);
try (ZipFile jarFile = new ZipFile(new File(this.gradleBuild.getProjectDir(), "build/libs").listFiles()[0])) {
Enumeration<ZipArchiveEntry> entries = jarFile.getEntries();
while (entries.hasMoreElements()) {
ZipArchiveEntry entry = entries.nextElement();
if (entry.getName().startsWith("META-INF/")) {
continue;
}
if (entry.isDirectory()) {
assertEntryMode(entry, UnixStat.DIR_FLAG | UnixStat.DEFAULT_DIR_PERM);
}
else {
assertEntryMode(entry, UnixStat.FILE_FLAG | UnixStat.DEFAULT_FILE_PERM);
}
}
}
}
@TestTemplate
void dirModeAndFileModeAreApplied() throws IOException {
BuildResult result = this.gradleBuild.build(this.taskName);
assertThat(result.task(":" + this.taskName).getOutcome()).isEqualTo(TaskOutcome.SUCCESS);
try (ZipFile jarFile = new ZipFile(new File(this.gradleBuild.getProjectDir(), "build/libs").listFiles()[0])) {
Enumeration<ZipArchiveEntry> entries = jarFile.getEntries();
while (entries.hasMoreElements()) {
ZipArchiveEntry entry = entries.nextElement();
if (entry.getName().startsWith("META-INF/")) {
continue;
}
if (entry.isDirectory()) {
assertEntryMode(entry, 0500);
}
else {
assertEntryMode(entry, 0400);
}
}
}
}
private void copyMainClassApplication() throws IOException {
copyApplication("main");
}
@ -650,4 +697,11 @@ abstract class AbstractBootArchiveIntegrationTests {
return false;
}
private static void assertEntryMode(ZipArchiveEntry entry, int expectedMode) {
assertThat(entry.getUnixMode())
.withFailMessage(() -> "Expected mode " + Integer.toOctalString(expectedMode) + " for entry "
+ entry.getName() + " but actual is " + Integer.toOctalString(entry.getUnixMode()))
.isEqualTo(expectedMode);
}
}

@ -0,0 +1,10 @@
plugins {
id 'java'
id 'org.springframework.boot' version '{version}'
}
tasks.named("bootJar") {
fileMode = 0400
dirMode = 0500
mainClass = 'com.example.Application'
}

@ -0,0 +1,10 @@
plugins {
id 'war'
id 'org.springframework.boot' version '{version}'
}
tasks.named("bootWar") {
fileMode = 0400
dirMode = 0500
mainClass = 'com.example.Application'
}
Loading…
Cancel
Save