Skip to content

Commit 29e2456

Browse files
committed
Merge pull request #50501 from DragonFSKY
Closes gh-50501 * pr/50501: Polish "Handle root output directory when extracting layers" Handle root output directory when extracting layers
2 parents bfd1b8c + db1ebf5 commit 29e2456

2 files changed

Lines changed: 47 additions & 6 deletions

File tree

spring-boot-project/spring-boot-tools/spring-boot-jarmode-tools/src/main/java/org/springframework/boot/jarmode/tools/ExtractCommand.java

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
import java.io.PrintStream;
2727
import java.io.UncheckedIOException;
2828
import java.nio.file.Files;
29+
import java.nio.file.Path;
2930
import java.nio.file.attribute.BasicFileAttributeView;
3031
import java.nio.file.attribute.BasicFileAttributes;
3132
import java.nio.file.attribute.FileTime;
@@ -55,6 +56,7 @@
5556
* The {@code 'extract'} tools command.
5657
*
5758
* @author Moritz Halbritter
59+
* @author Dongliang Xie
5860
*/
5961
class ExtractCommand extends Command {
6062

@@ -363,14 +365,18 @@ private static void withJarEntries(File file, ManfiestWriter manfiestWriter, Thr
363365
}
364366

365367
private static File assertFileIsContainedInDirectory(File directory, File file, String name) throws IOException {
366-
String canonicalOutputPath = directory.getCanonicalPath() + File.separator;
367-
String canonicalEntryPath = file.getCanonicalPath();
368-
Assert.state(canonicalEntryPath.startsWith(canonicalOutputPath),
368+
Path canonicalOutputPath = directory.getCanonicalFile().toPath();
369+
Path canonicalEntryPath = file.getCanonicalFile().toPath();
370+
Assert.state(isFileContainedInDirectory(canonicalOutputPath, canonicalEntryPath),
369371
() -> "Entry '%s' would be written to '%s'. This is outside the output location of '%s'. Verify the contents of your archive."
370372
.formatted(name, canonicalEntryPath, canonicalOutputPath));
371373
return file;
372374
}
373375

376+
private static boolean isFileContainedInDirectory(Path directory, Path file) {
377+
return !file.equals(directory) && file.startsWith(directory);
378+
}
379+
374380
@FunctionalInterface
375381
private interface EntryNameTransformer {
376382

@@ -515,9 +521,9 @@ private boolean shouldExtractLayer(String layer) {
515521
}
516522

517523
private File assertLayerDirectoryLocation(File layerDirectory, String layerName) throws IOException {
518-
String canonicalOutputPath = this.directory.getCanonicalPath() + File.separator;
519-
String canonicalLayerPath = layerDirectory.getCanonicalPath();
520-
Assert.state(canonicalLayerPath.startsWith(canonicalOutputPath),
524+
Path canonicalOutputPath = this.directory.getCanonicalFile().toPath();
525+
Path canonicalLayerPath = layerDirectory.getCanonicalFile().toPath();
526+
Assert.state(isFileContainedInDirectory(canonicalOutputPath, canonicalLayerPath),
521527
() -> "Layer '%s' would be written to '%s'. This is outside the output location of '%s'. Verify the contents of your archive."
522528
.formatted(layerName, canonicalLayerPath, canonicalOutputPath));
523529
return layerDirectory;

spring-boot-project/spring-boot-tools/spring-boot-jarmode-tools/src/test/java/org/springframework/boot/jarmode/tools/ExtractCommandTests.java

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,13 @@
2020
import java.io.FileWriter;
2121
import java.io.IOException;
2222
import java.nio.file.Files;
23+
import java.nio.file.Path;
2324
import java.nio.file.attribute.BasicFileAttributeView;
2425
import java.nio.file.attribute.BasicFileAttributes;
2526
import java.time.Instant;
2627
import java.time.temporal.ChronoUnit;
2728
import java.util.Enumeration;
29+
import java.util.Iterator;
2830
import java.util.List;
2931
import java.util.Map;
3032
import java.util.jar.Manifest;
@@ -45,6 +47,7 @@
4547
* Tests for {@link ExtractCommand}.
4648
*
4749
* @author Moritz Halbritter
50+
* @author Dongliang Xie
4851
*/
4952
class ExtractCommandTests extends AbstractJarModeTests {
5053

@@ -370,6 +373,38 @@ void extractsOnlySelectedLayers() throws IOException {
370373
.doesNotContain("test/spring-boot-loader/org/springframework/boot/loader/launch/JarLauncher.class");
371374
}
372375

376+
@Test
377+
void extractWhenDestinationIsFileSystemRoot() throws IOException {
378+
Path layerDirectory = ExtractCommandTests.this.tempDir.toPath()
379+
.resolve("root-output")
380+
.resolve("dependencies")
381+
.toAbsolutePath()
382+
.normalize();
383+
Path outputRoot = layerDirectory.getRoot();
384+
String layerName = outputRoot.relativize(layerDirectory).toString().replace(File.separatorChar, '/');
385+
Layers layers = new Layers() {
386+
387+
@Override
388+
public Iterator<String> iterator() {
389+
return List.of(layerName).iterator();
390+
}
391+
392+
@Override
393+
public String getLayer(String entryName) {
394+
return layerName;
395+
}
396+
397+
@Override
398+
public String getApplicationLayerName() {
399+
return layerName;
400+
}
401+
402+
};
403+
runCommand((context) -> new ExtractCommand(context, layers), ExtractCommandTests.this.archive,
404+
"--destination", outputRoot.toString(), "--force", "--launcher", "--layers");
405+
assertThat(layerDirectory.resolve("BOOT-INF/lib/dependency-1.jar")).exists();
406+
}
407+
373408
}
374409

375410
}

0 commit comments

Comments
 (0)