From 44eb8c39d274ab70809a3b64d323530d49bf1083 Mon Sep 17 00:00:00 2001 From: Phillip Webb Date: Thu, 2 Sep 2021 11:11:39 -0700 Subject: [PATCH] Align ApplicationTemp with Files.createTempDirectory Update `ApplicationTemp` to align the way that it creates temp folders with the way that `Files.createTempDirectory` works. Closes gh-27857 --- .../boot/system/ApplicationTemp.java | 76 ++++++++++++++----- .../boot/system/ApplicationTempTests.java | 37 ++++++++- 2 files changed, 91 insertions(+), 22 deletions(-) diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/system/ApplicationTemp.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/system/ApplicationTemp.java index 65171a6dc1..9be31b9d96 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/system/ApplicationTemp.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/system/ApplicationTemp.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2019 the original author or authors. + * Copyright 2012-2021 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. @@ -17,7 +17,16 @@ package org.springframework.boot.system; import java.io.File; +import java.io.IOException; +import java.nio.file.FileSystem; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.attribute.FileAttribute; +import java.nio.file.attribute.PosixFilePermission; +import java.nio.file.attribute.PosixFilePermissions; import java.security.MessageDigest; +import java.util.EnumSet; import org.springframework.util.Assert; import org.springframework.util.StringUtils; @@ -34,9 +43,14 @@ public class ApplicationTemp { private static final char[] HEX_CHARS = "0123456789ABCDEF".toCharArray(); + private static final FileAttribute[] NO_FILE_ATTRIBUTES = {}; + + private static final EnumSet DIRECTORY_PERMISSIONS = EnumSet.of(PosixFilePermission.OWNER_READ, + PosixFilePermission.OWNER_WRITE, PosixFilePermission.OWNER_EXECUTE); + private final Class sourceClass; - private volatile File dir; + private volatile Path path; /** * Create a new {@link ApplicationTemp} instance. @@ -58,40 +72,60 @@ public class ApplicationTemp { return getDir().getAbsolutePath(); } + /** + * Return the directory to be used for application specific temp files. + * @return the application temp directory + */ + public File getDir() { + return getPath().toFile(); + } + /** * Return a sub-directory of the application temp. * @param subDir the sub-directory name * @return a sub-directory */ public File getDir(String subDir) { - File dir = new File(getDir(), subDir); - dir.mkdirs(); - return dir; + return createDirectory(getPath().resolve(subDir)).toFile(); } - /** - * Return the directory to be used for application specific temp files. - * @return the application temp directory - */ - public File getDir() { - if (this.dir == null) { + private Path getPath() { + if (this.path == null) { synchronized (this) { - byte[] hash = generateHash(this.sourceClass); - this.dir = new File(getTempDirectory(), toHexString(hash)); - this.dir.mkdirs(); - Assert.state(this.dir.exists(), () -> "Unable to create temp directory " + this.dir); + String hash = toHexString(generateHash(this.sourceClass)); + this.path = createDirectory(getTempDirectory().resolve(hash)); } } - return this.dir; + return this.path; + } + + private Path createDirectory(Path path) { + try { + if (!Files.exists(path)) { + Files.createDirectory(path, getFileAttributes(path.getFileSystem(), DIRECTORY_PERMISSIONS)); + } + return path; + } + catch (IOException ex) { + throw new IllegalStateException("Unable to create application temp directory " + path, ex); + } + } + + private FileAttribute[] getFileAttributes(FileSystem fileSystem, EnumSet ownerReadWrite) { + if (!fileSystem.supportedFileAttributeViews().contains("posix")) { + return NO_FILE_ATTRIBUTES; + } + return new FileAttribute[] { PosixFilePermissions.asFileAttribute(ownerReadWrite) }; } - private File getTempDirectory() { + private Path getTempDirectory() { String property = System.getProperty("java.io.tmpdir"); Assert.state(StringUtils.hasLength(property), "No 'java.io.tmpdir' property set"); - File file = new File(property); - Assert.state(file.exists(), () -> "Temp directory " + file + " does not exist"); - Assert.state(file.isDirectory(), () -> "Temp location " + file + " is not a directory"); - return file; + Path tempDirectory = Paths.get(property); + Assert.state(Files.exists(tempDirectory), () -> "Temp directory '" + tempDirectory + "' does not exist"); + Assert.state(Files.isDirectory(tempDirectory), + () -> "Temp location '" + tempDirectory + "' is not a directory"); + return tempDirectory; } private byte[] generateHash(Class sourceClass) { diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/system/ApplicationTempTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/system/ApplicationTempTests.java index ec92435726..bbbcc81804 100644 --- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/system/ApplicationTempTests.java +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/system/ApplicationTempTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2019 the original author or authors. + * Copyright 2012-2021 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. @@ -17,9 +17,20 @@ package org.springframework.boot.system; import java.io.File; +import java.io.IOException; +import java.nio.file.FileSystem; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.attribute.PosixFileAttributeView; +import java.nio.file.attribute.PosixFilePermission; +import java.util.Set; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.springframework.util.FileSystemUtils; + import static org.assertj.core.api.Assertions.assertThat; /** @@ -29,6 +40,12 @@ import static org.assertj.core.api.Assertions.assertThat; */ class ApplicationTempTests { + @BeforeEach + @AfterEach + void cleanup() { + FileSystemUtils.deleteRecursively(new ApplicationTemp().getDir()); + } + @Test void generatesConsistentTemp() { ApplicationTemp t1 = new ApplicationTemp(); @@ -57,4 +74,22 @@ class ApplicationTempTests { assertThat(temp.getDir("abc")).isEqualTo(new File(temp.getDir(), "abc")); } + @Test + void posixPermissions() throws IOException { + ApplicationTemp temp = new ApplicationTemp(); + Path path = temp.getDir().toPath(); + FileSystem fileSystem = path.getFileSystem(); + if (fileSystem.supportedFileAttributeViews().contains("posix")) { + assertDirectoryPermissions(path); + assertDirectoryPermissions(temp.getDir("sub").toPath()); + } + } + + private void assertDirectoryPermissions(Path path) throws IOException { + Set permissions = Files.getFileAttributeView(path, PosixFileAttributeView.class) + .readAttributes().permissions(); + assertThat(permissions).containsExactlyInAnyOrder(PosixFilePermission.OWNER_READ, + PosixFilePermission.OWNER_WRITE, PosixFilePermission.OWNER_EXECUTE); + } + }