Skip to content

Commit 5ceb1a2

Browse files
committed
Improve ApplicationTemp's temporary directory creation
Closes gh-50170
1 parent 4b0862c commit 5ceb1a2

2 files changed

Lines changed: 80 additions & 4 deletions

File tree

spring-boot-project/spring-boot/src/main/java/org/springframework/boot/system/ApplicationTemp.java

Lines changed: 55 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,17 @@
1818

1919
import java.io.File;
2020
import java.io.IOException;
21+
import java.io.UncheckedIOException;
2122
import java.nio.file.FileSystem;
2223
import java.nio.file.Files;
24+
import java.nio.file.LinkOption;
2325
import java.nio.file.Path;
2426
import java.nio.file.Paths;
2527
import java.nio.file.attribute.FileAttribute;
28+
import java.nio.file.attribute.PosixFileAttributes;
2629
import java.nio.file.attribute.PosixFilePermission;
2730
import java.nio.file.attribute.PosixFilePermissions;
31+
import java.nio.file.attribute.UserPrincipal;
2832
import java.security.MessageDigest;
2933
import java.util.EnumSet;
3034
import java.util.HexFormat;
@@ -54,6 +58,8 @@ public class ApplicationTemp {
5458

5559
private final Lock pathLock = new ReentrantLock();
5660

61+
private final UserPrincipal directoryOwner;
62+
5763
private volatile Path path;
5864

5965
/**
@@ -69,6 +75,22 @@ public ApplicationTemp() {
6975
*/
7076
public ApplicationTemp(Class<?> sourceClass) {
7177
this.sourceClass = sourceClass;
78+
this.directoryOwner = directoryOwner();
79+
}
80+
81+
private static UserPrincipal directoryOwner() {
82+
try {
83+
Path tempFile = Files.createTempFile("application-temp", "-owner");
84+
UserPrincipal owner = Files.getOwner(tempFile);
85+
Files.delete(tempFile);
86+
return owner;
87+
}
88+
catch (UnsupportedOperationException ex) {
89+
return null;
90+
}
91+
catch (IOException ex) {
92+
throw new UncheckedIOException(ex);
93+
}
7294
}
7395

7496
@Override
@@ -111,8 +133,28 @@ private Path getPath() {
111133

112134
private Path createDirectory(Path path) {
113135
try {
114-
if (!Files.exists(path)) {
115-
Files.createDirectory(path, getFileAttributes(path.getFileSystem(), DIRECTORY_PERMISSIONS));
136+
FileSystem fileSystem = path.getFileSystem();
137+
if (!Files.exists(path, LinkOption.NOFOLLOW_LINKS)) {
138+
Files.createDirectory(path, asFileAttributes(fileSystem, DIRECTORY_PERMISSIONS));
139+
}
140+
else {
141+
if (supportsPosixView(fileSystem)) {
142+
PosixFileAttributes attributes = Files.readAttributes(path, PosixFileAttributes.class,
143+
LinkOption.NOFOLLOW_LINKS);
144+
Assert.state(attributes.isDirectory(),
145+
() -> "'" + path + "' already exists but it is not a directory");
146+
Assert.state(DIRECTORY_PERMISSIONS.equals(attributes.permissions()), () -> "Existing directory '"
147+
+ path + "' does not have the permissions " + DIRECTORY_PERMISSIONS);
148+
assertDirectoryOwnership(attributes.owner(), path);
149+
}
150+
else {
151+
try {
152+
assertDirectoryOwnership(Files.getOwner(path, LinkOption.NOFOLLOW_LINKS), path);
153+
}
154+
catch (UnsupportedOperationException ex) {
155+
// Ownership check not supported. Continue.
156+
}
157+
}
116158
}
117159
return path;
118160
}
@@ -121,13 +163,22 @@ private Path createDirectory(Path path) {
121163
}
122164
}
123165

124-
private FileAttribute<?>[] getFileAttributes(FileSystem fileSystem, EnumSet<PosixFilePermission> ownerReadWrite) {
125-
if (!fileSystem.supportedFileAttributeViews().contains("posix")) {
166+
private void assertDirectoryOwnership(UserPrincipal owner, Path path) {
167+
Assert.state((this.directoryOwner == null) || this.directoryOwner.equals(owner),
168+
() -> "Existing directory '" + path + "' is not owned by " + this.directoryOwner.getName());
169+
}
170+
171+
private FileAttribute<?>[] asFileAttributes(FileSystem fileSystem, EnumSet<PosixFilePermission> ownerReadWrite) {
172+
if (!supportsPosixView(fileSystem)) {
126173
return NO_FILE_ATTRIBUTES;
127174
}
128175
return new FileAttribute<?>[] { PosixFilePermissions.asFileAttribute(ownerReadWrite) };
129176
}
130177

178+
private boolean supportsPosixView(FileSystem fileSystem) {
179+
return fileSystem.supportedFileAttributeViews().contains("posix");
180+
}
181+
131182
private Path getTempDirectory() {
132183
String property = System.getProperty("java.io.tmpdir");
133184
Assert.state(StringUtils.hasLength(property), "No 'java.io.tmpdir' property set");

spring-boot-project/spring-boot/src/test/java/org/springframework/boot/system/ApplicationTempTests.java

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import java.nio.file.Path;
2424
import java.nio.file.attribute.PosixFileAttributeView;
2525
import java.nio.file.attribute.PosixFilePermission;
26+
import java.util.EnumSet;
2627
import java.util.Set;
2728

2829
import org.junit.jupiter.api.AfterEach;
@@ -32,6 +33,7 @@
3233
import org.springframework.util.FileSystemUtils;
3334

3435
import static org.assertj.core.api.Assertions.assertThat;
36+
import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
3537

3638
/**
3739
* Tests for {@link ApplicationTemp}.
@@ -85,6 +87,29 @@ void posixPermissions() throws IOException {
8587
}
8688
}
8789

90+
@Test
91+
void whenDirectoryExistsWithWrongPermissionsGetDirThrows() throws IOException {
92+
ApplicationTemp temp = new ApplicationTemp();
93+
Path path = temp.getDir().toPath();
94+
Files.getFileAttributeView(path, PosixFileAttributeView.class)
95+
.setPermissions(EnumSet.allOf(PosixFilePermission.class));
96+
assertThatIllegalStateException().isThrownBy(new ApplicationTemp()::getDir)
97+
.withMessageContaining("does not have the permissions");
98+
FileSystemUtils.deleteRecursively(path);
99+
}
100+
101+
@Test
102+
void whenSymlinkExistsInDirectoryLocationGetDirThrows() throws IOException {
103+
ApplicationTemp temp = new ApplicationTemp();
104+
Path path = temp.getDir().toPath();
105+
FileSystemUtils.deleteRecursively(path);
106+
Path linkTarget = Files.createTempDirectory("application-test-tests");
107+
Files.createSymbolicLink(path, linkTarget);
108+
assertThatIllegalStateException().isThrownBy(new ApplicationTemp()::getDir)
109+
.withMessageContaining("already exists but it is not a directory");
110+
FileSystemUtils.deleteRecursively(path);
111+
}
112+
88113
private void assertDirectoryPermissions(Path path) throws IOException {
89114
Set<PosixFilePermission> permissions = Files.getFileAttributeView(path, PosixFileAttributeView.class)
90115
.readAttributes()

0 commit comments

Comments
 (0)