diff --git a/android/guava-tests/test/com/google/common/io/FileBackedOutputStreamTest.java b/android/guava-tests/test/com/google/common/io/FileBackedOutputStreamTest.java index e3a3ccd0e875..87247e1ed4d5 100644 --- a/android/guava-tests/test/com/google/common/io/FileBackedOutputStreamTest.java +++ b/android/guava-tests/test/com/google/common/io/FileBackedOutputStreamTest.java @@ -16,9 +16,17 @@ package com.google.common.io; +import static com.google.common.base.StandardSystemProperty.JAVA_IO_TMPDIR; +import static com.google.common.truth.Truth.assertThat; +import static java.nio.file.attribute.PosixFilePermission.OWNER_READ; +import static java.nio.file.attribute.PosixFilePermission.OWNER_WRITE; +import static org.junit.Assert.assertThrows; + import java.io.File; import java.io.IOException; import java.io.OutputStream; +import java.nio.file.attribute.PosixFileAttributeView; +import java.nio.file.attribute.PosixFileAttributes; import java.util.Arrays; /** @@ -61,10 +69,21 @@ private void testThreshold( // Write data to go over the threshold if (chunk2 > 0) { + if (JAVA_IO_TMPDIR.value().equals("/sdcard")) { + assertThrows(IOException.class, () -> write(out, data, chunk1, chunk2, singleByte)); + return; + } write(out, data, chunk1, chunk2, singleByte); file = out.getFile(); assertEquals(dataSize, file.length()); assertTrue(file.exists()); + assertThat(file.getName()).contains("FileBackedOutputStream"); + if (!isAndroid()) { + PosixFileAttributes attributes = + java.nio.file.Files.getFileAttributeView(file.toPath(), PosixFileAttributeView.class) + .readAttributes(); + assertThat(attributes.permissions()).containsExactly(OWNER_READ, OWNER_WRITE); + } } out.close(); @@ -109,6 +128,10 @@ public void testWriteErrorAfterClose() throws Exception { FileBackedOutputStream out = new FileBackedOutputStream(50); ByteSource source = out.asByteSource(); + if (JAVA_IO_TMPDIR.value().equals("/sdcard")) { + assertThrows(IOException.class, () -> out.write(data)); + return; + } out.write(data); assertTrue(Arrays.equals(data, source.read())); @@ -140,4 +163,8 @@ public void testReset() throws Exception { out.close(); } + + private static boolean isAndroid() { + return System.getProperty("java.runtime.name", "").contains("Android"); + } } diff --git a/android/guava-tests/test/com/google/common/io/FilesCreateTempDirTest.java b/android/guava-tests/test/com/google/common/io/FilesCreateTempDirTest.java index 109342a8fab9..62098ef0288a 100644 --- a/android/guava-tests/test/com/google/common/io/FilesCreateTempDirTest.java +++ b/android/guava-tests/test/com/google/common/io/FilesCreateTempDirTest.java @@ -16,9 +16,17 @@ package com.google.common.io; +import static com.google.common.base.StandardSystemProperty.JAVA_IO_TMPDIR; import static com.google.common.truth.Truth.assertThat; +import static java.nio.file.attribute.PosixFilePermission.OWNER_EXECUTE; +import static java.nio.file.attribute.PosixFilePermission.OWNER_READ; +import static java.nio.file.attribute.PosixFilePermission.OWNER_WRITE; +import static org.junit.Assert.assertThrows; import java.io.File; +import java.io.IOException; +import java.nio.file.attribute.PosixFileAttributeView; +import java.nio.file.attribute.PosixFileAttributes; import junit.framework.TestCase; /** @@ -27,12 +35,32 @@ * @author Chris Nokleberg */ +@SuppressWarnings("deprecation") // tests of a deprecated method public class FilesCreateTempDirTest extends TestCase { - public void testCreateTempDir() { + public void testCreateTempDir() throws IOException { + if (JAVA_IO_TMPDIR.value().equals("/sdcard")) { + assertThrows(IllegalStateException.class, Files::createTempDir); + return; + } File temp = Files.createTempDir(); - assertTrue(temp.exists()); - assertTrue(temp.isDirectory()); - assertThat(temp.listFiles()).isEmpty(); - assertTrue(temp.delete()); + try { + assertTrue(temp.exists()); + assertTrue(temp.isDirectory()); + assertThat(temp.listFiles()).isEmpty(); + + if (isAndroid()) { + return; + } + PosixFileAttributes attributes = + java.nio.file.Files.getFileAttributeView(temp.toPath(), PosixFileAttributeView.class) + .readAttributes(); + assertThat(attributes.permissions()).containsExactly(OWNER_READ, OWNER_WRITE, OWNER_EXECUTE); + } finally { + assertTrue(temp.delete()); + } + } + + private static boolean isAndroid() { + return System.getProperty("java.runtime.name", "").contains("Android"); } } diff --git a/android/guava/src/com/google/common/io/FileBackedOutputStream.java b/android/guava/src/com/google/common/io/FileBackedOutputStream.java index d1b2a436013b..dd297e90a8b2 100644 --- a/android/guava/src/com/google/common/io/FileBackedOutputStream.java +++ b/android/guava/src/com/google/common/io/FileBackedOutputStream.java @@ -14,6 +14,7 @@ package com.google.common.io; +import static com.google.common.base.Preconditions.checkArgument; import static java.util.Objects.requireNonNull; import com.google.common.annotations.Beta; @@ -36,6 +37,14 @@ * An {@link OutputStream} that starts buffering to a byte array, but switches to file buffering * once the data reaches a configurable size. * + *
When this stream creates a temporary file, it restricts the file's permissions to the current + * user or, in the case of Android, the current app. If that is not possible (as is the case under + * the very old Android Ice Cream Sandwich release), then this stream throws an exception instead of + * creating a file that would be more accessible. (This behavior is new in Guava 32.0.0. Previous + * versions would create a file that is more accessible, as discussed in Guava issue 2575. TODO: b/283778848 - Fill + * in CVE number once it's available.) + * *
Temporary files created by this stream may live in the local filesystem until either: * *
The temporary directory is created with permissions restricted to the current user or, in + * the case of Android, the current app. If that is not possible (as is the case under the very + * old Android Ice Cream Sandwich release), then this method throws an exception instead of + * creating a directory that would be more accessible. (This behavior is new in Guava 32.0.0. + * Previous versions would create a directory that is more accessible, as discussed in CVE-2020-8908.) + * *
Use this method instead of {@link File#createTempFile(String, String)} when you wish to * create a directory, not a regular file. A common pitfall is to call {@code createTempFile}, * delete the file and create a directory in its place, but this leads a race condition which can * be exploited to create security vulnerabilities, especially when executable files are to be * written into the directory. * - *
Depending on the environment that this code is run in, the system temporary directory (and - * thus the directory this method creates) may be more visible that a program would like - files - * written to this directory may be read or overwritten by hostile programs running on the same - * machine. - * *
This method assumes that the temporary volume is writable, has free inodes and free blocks, * and that it will not be called thousands of times per second. * @@ -418,36 +417,26 @@ public static boolean equal(File file1, File file2) throws IOException { * * @return the newly-created directory * @throws IllegalStateException if the directory could not be created + * @throws UnsupportedOperationException if the system does not support creating temporary + * directories securely * @deprecated For Android users, see the Data and File * Storage overview to select an appropriate temporary directory (perhaps {@code - * context.getCacheDir()}). For developers on Java 7 or later, use {@link - * java.nio.file.Files#createTempDirectory}, transforming it to a {@link File} using {@link - * java.nio.file.Path#toFile() toFile()} if needed. + * context.getCacheDir()}), and create your own directory under that. (For example, you might + * use {@code new File(context.getCacheDir(), "directoryname").mkdir()}, or, if you need an + * arbitrary number of temporary directories, you might have to generate multiple directory + * names in a loop until {@code mkdir()} returns {@code true}.) For developers on Java 7 or + * later, use {@link java.nio.file.Files#createTempDirectory}, transforming it to a {@link + * File} using {@link java.nio.file.Path#toFile() toFile()} if needed. To restrict permissions + * as this method does, pass {@code + * PosixFilePermissions.asFileAttribute(PosixFilePermissions.fromString("rwx------"))} to your + * call to {@code createTempDirectory}. */ @Beta @Deprecated @J2ObjCIncompatible public static File createTempDir() { - File baseDir = new File(System.getProperty("java.io.tmpdir")); - @SuppressWarnings("GoodTime") // reading system time without TimeSource - String baseName = System.currentTimeMillis() + "-"; - - for (int counter = 0; counter < TEMP_DIR_ATTEMPTS; counter++) { - File tempDir = new File(baseDir, baseName + counter); - if (tempDir.mkdir()) { - return tempDir; - } - } - throw new IllegalStateException( - "Failed to create directory within " - + TEMP_DIR_ATTEMPTS - + " attempts (tried " - + baseName - + "0 to " - + baseName - + (TEMP_DIR_ATTEMPTS - 1) - + ')'); + return TempFileCreator.INSTANCE.createTempDir(); } /** diff --git a/android/guava/src/com/google/common/io/IgnoreJRERequirement.java b/android/guava/src/com/google/common/io/IgnoreJRERequirement.java new file mode 100644 index 000000000000..b1b8e1032cc0 --- /dev/null +++ b/android/guava/src/com/google/common/io/IgnoreJRERequirement.java @@ -0,0 +1,30 @@ +/* + * Copyright 2019 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ + +package com.google.common.io; + +import static java.lang.annotation.ElementType.CONSTRUCTOR; +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.TYPE; + +import java.lang.annotation.Target; + +/** + * Disables Animal Sniffer's checking of compatibility with older versions of Java/Android. + * + *
Each package's copy of this annotation needs to be listed in our {@code pom.xml}.
+ */
+@Target({METHOD, CONSTRUCTOR, TYPE})
+@ElementTypesAreNonnullByDefault
+@interface IgnoreJRERequirement {}
diff --git a/android/guava/src/com/google/common/io/TempFileCreator.java b/android/guava/src/com/google/common/io/TempFileCreator.java
new file mode 100644
index 000000000000..a28a0af927a3
--- /dev/null
+++ b/android/guava/src/com/google/common/io/TempFileCreator.java
@@ -0,0 +1,176 @@
+/*
+ * Copyright (C) 2007 The Guava Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package com.google.common.io;
+
+import static com.google.common.base.StandardSystemProperty.JAVA_IO_TMPDIR;
+
+import com.google.common.annotations.GwtIncompatible;
+import com.google.common.annotations.J2ktIncompatible;
+import com.google.j2objc.annotations.J2ObjCIncompatible;
+import java.io.File;
+import java.io.IOException;
+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.util.Set;
+
+/**
+ * Creates temporary files and directories whose permissions are restricted to the current user or,
+ * in the case of Android, the current app. If that is not possible (as is the case under the very
+ * old Android Ice Cream Sandwich release), then this class throws an exception instead of creating
+ * a file or directory that would be more accessible.
+ */
+@J2ktIncompatible
+@GwtIncompatible
+@J2ObjCIncompatible
+@ElementTypesAreNonnullByDefault
+abstract class TempFileCreator {
+ static final TempFileCreator INSTANCE = pickSecureCreator();
+
+ /**
+ * @throws IllegalStateException if the directory could not be created (to implement the contract
+ * of {@link Files#createTempDir()}
+ * @throws UnsupportedOperationException if the system does not support creating temporary
+ * directories securely
+ */
+ abstract File createTempDir();
+
+ abstract File createTempFile(String prefix) throws IOException;
+
+ private static TempFileCreator pickSecureCreator() {
+ try {
+ Class.forName("java.nio.file.Path");
+ return new JavaNioCreator();
+ } catch (ClassNotFoundException runningUnderAndroid) {
+ // Try another way.
+ }
+
+ try {
+ int version = (int) Class.forName("android.os.Build$VERSION").getField("SDK_INT").get(null);
+ int jellyBean =
+ (int) Class.forName("android.os.Build$VERSION_CODES").getField("JELLY_BEAN").get(null);
+ /*
+ * I assume that this check can't fail because JELLY_BEAN will be present only if we're
+ * running under Jelly Bean or higher. But it seems safest to check.
+ */
+ if (version < jellyBean) {
+ return new ThrowingCreator();
+ }
+
+ // Don't merge these catch() blocks, let alone use ReflectiveOperationException directly:
+ // b/65343391
+ } catch (NoSuchFieldException e) {
+ // The JELLY_BEAN field doesn't exist because we're running on a version before Jelly Bean :)
+ return new ThrowingCreator();
+ } catch (ClassNotFoundException e) {
+ // Should be impossible, but we want to return *something* so that class init succeeds.
+ return new ThrowingCreator();
+ } catch (IllegalAccessException e) {
+ // ditto
+ return new ThrowingCreator();
+ }
+
+ // Android isolates apps' temporary directories since Jelly Bean:
+ // https://github.com/google/guava/issues/4011#issuecomment-770020802
+ // So we can create files there with any permissions and still get security from the isolation.
+ return new JavaIoCreator();
+ }
+
+ @IgnoreJRERequirement // used only when Path is available
+ private static final class JavaNioCreator extends TempFileCreator {
+ private static final FileAttribute When this stream creates a temporary file, it restricts the file's permissions to the current
+ * user or, in the case of Android, the current app. If that is not possible (as is the case under
+ * the very old Android Ice Cream Sandwich release), then this stream throws an exception instead of
+ * creating a file that would be more accessible. (This behavior is new in Guava 32.0.0. Previous
+ * versions would create a file that is more accessible, as discussed in Guava issue 2575. TODO: b/283778848 - Fill
+ * in CVE number once it's available.)
+ *
* Temporary files created by this stream may live in the local filesystem until either:
*
* The temporary directory is created with permissions restricted to the current user or, in
+ * the case of Android, the current app. If that is not possible (as is the case under the very
+ * old Android Ice Cream Sandwich release), then this method throws an exception instead of
+ * creating a directory that would be more accessible. (This behavior is new in Guava 32.0.0.
+ * Previous versions would create a directory that is more accessible, as discussed in CVE-2020-8908.)
+ *
* Use this method instead of {@link File#createTempFile(String, String)} when you wish to
* create a directory, not a regular file. A common pitfall is to call {@code createTempFile},
* delete the file and create a directory in its place, but this leads a race condition which can
* be exploited to create security vulnerabilities, especially when executable files are to be
* written into the directory.
*
- * Depending on the environment that this code is run in, the system temporary directory (and
- * thus the directory this method creates) may be more visible that a program would like - files
- * written to this directory may be read or overwritten by hostile programs running on the same
- * machine.
- *
* This method assumes that the temporary volume is writable, has free inodes and free blocks,
* and that it will not be called thousands of times per second.
*
@@ -418,36 +417,26 @@ public static boolean equal(File file1, File file2) throws IOException {
*
* @return the newly-created directory
* @throws IllegalStateException if the directory could not be created
+ * @throws UnsupportedOperationException if the system does not support creating temporary
+ * directories securely
* @deprecated For Android users, see the Data and File
* Storage overview to select an appropriate temporary directory (perhaps {@code
- * context.getCacheDir()}). For developers on Java 7 or later, use {@link
- * java.nio.file.Files#createTempDirectory}, transforming it to a {@link File} using {@link
- * java.nio.file.Path#toFile() toFile()} if needed.
+ * context.getCacheDir()}), and create your own directory under that. (For example, you might
+ * use {@code new File(context.getCacheDir(), "directoryname").mkdir()}, or, if you need an
+ * arbitrary number of temporary directories, you might have to generate multiple directory
+ * names in a loop until {@code mkdir()} returns {@code true}.) For developers on Java 7 or
+ * later, use {@link java.nio.file.Files#createTempDirectory}, transforming it to a {@link
+ * File} using {@link java.nio.file.Path#toFile() toFile()} if needed. To restrict permissions
+ * as this method does, pass {@code
+ * PosixFilePermissions.asFileAttribute(PosixFilePermissions.fromString("rwx------"))} to your
+ * call to {@code createTempDirectory}.
*/
@Beta
@Deprecated
@J2ObjCIncompatible
public static File createTempDir() {
- File baseDir = new File(System.getProperty("java.io.tmpdir"));
- @SuppressWarnings("GoodTime") // reading system time without TimeSource
- String baseName = System.currentTimeMillis() + "-";
-
- for (int counter = 0; counter < TEMP_DIR_ATTEMPTS; counter++) {
- File tempDir = new File(baseDir, baseName + counter);
- if (tempDir.mkdir()) {
- return tempDir;
- }
- }
- throw new IllegalStateException(
- "Failed to create directory within "
- + TEMP_DIR_ATTEMPTS
- + " attempts (tried "
- + baseName
- + "0 to "
- + baseName
- + (TEMP_DIR_ATTEMPTS - 1)
- + ')');
+ return TempFileCreator.INSTANCE.createTempDir();
}
/**
diff --git a/guava/src/com/google/common/io/IgnoreJRERequirement.java b/guava/src/com/google/common/io/IgnoreJRERequirement.java
new file mode 100644
index 000000000000..b1b8e1032cc0
--- /dev/null
+++ b/guava/src/com/google/common/io/IgnoreJRERequirement.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2019 The Guava Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package com.google.common.io;
+
+import static java.lang.annotation.ElementType.CONSTRUCTOR;
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.ElementType.TYPE;
+
+import java.lang.annotation.Target;
+
+/**
+ * Disables Animal Sniffer's checking of compatibility with older versions of Java/Android.
+ *
+ * Each package's copy of this annotation needs to be listed in our {@code pom.xml}.
+ */
+@Target({METHOD, CONSTRUCTOR, TYPE})
+@ElementTypesAreNonnullByDefault
+@interface IgnoreJRERequirement {}
diff --git a/guava/src/com/google/common/io/TempFileCreator.java b/guava/src/com/google/common/io/TempFileCreator.java
new file mode 100644
index 000000000000..a28a0af927a3
--- /dev/null
+++ b/guava/src/com/google/common/io/TempFileCreator.java
@@ -0,0 +1,176 @@
+/*
+ * Copyright (C) 2007 The Guava Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package com.google.common.io;
+
+import static com.google.common.base.StandardSystemProperty.JAVA_IO_TMPDIR;
+
+import com.google.common.annotations.GwtIncompatible;
+import com.google.common.annotations.J2ktIncompatible;
+import com.google.j2objc.annotations.J2ObjCIncompatible;
+import java.io.File;
+import java.io.IOException;
+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.util.Set;
+
+/**
+ * Creates temporary files and directories whose permissions are restricted to the current user or,
+ * in the case of Android, the current app. If that is not possible (as is the case under the very
+ * old Android Ice Cream Sandwich release), then this class throws an exception instead of creating
+ * a file or directory that would be more accessible.
+ */
+@J2ktIncompatible
+@GwtIncompatible
+@J2ObjCIncompatible
+@ElementTypesAreNonnullByDefault
+abstract class TempFileCreator {
+ static final TempFileCreator INSTANCE = pickSecureCreator();
+
+ /**
+ * @throws IllegalStateException if the directory could not be created (to implement the contract
+ * of {@link Files#createTempDir()}
+ * @throws UnsupportedOperationException if the system does not support creating temporary
+ * directories securely
+ */
+ abstract File createTempDir();
+
+ abstract File createTempFile(String prefix) throws IOException;
+
+ private static TempFileCreator pickSecureCreator() {
+ try {
+ Class.forName("java.nio.file.Path");
+ return new JavaNioCreator();
+ } catch (ClassNotFoundException runningUnderAndroid) {
+ // Try another way.
+ }
+
+ try {
+ int version = (int) Class.forName("android.os.Build$VERSION").getField("SDK_INT").get(null);
+ int jellyBean =
+ (int) Class.forName("android.os.Build$VERSION_CODES").getField("JELLY_BEAN").get(null);
+ /*
+ * I assume that this check can't fail because JELLY_BEAN will be present only if we're
+ * running under Jelly Bean or higher. But it seems safest to check.
+ */
+ if (version < jellyBean) {
+ return new ThrowingCreator();
+ }
+
+ // Don't merge these catch() blocks, let alone use ReflectiveOperationException directly:
+ // b/65343391
+ } catch (NoSuchFieldException e) {
+ // The JELLY_BEAN field doesn't exist because we're running on a version before Jelly Bean :)
+ return new ThrowingCreator();
+ } catch (ClassNotFoundException e) {
+ // Should be impossible, but we want to return *something* so that class init succeeds.
+ return new ThrowingCreator();
+ } catch (IllegalAccessException e) {
+ // ditto
+ return new ThrowingCreator();
+ }
+
+ // Android isolates apps' temporary directories since Jelly Bean:
+ // https://github.com/google/guava/issues/4011#issuecomment-770020802
+ // So we can create files there with any permissions and still get security from the isolation.
+ return new JavaIoCreator();
+ }
+
+ @IgnoreJRERequirement // used only when Path is available
+ private static final class JavaNioCreator extends TempFileCreator {
+ private static final FileAttribute
@@ -61,7 +70,6 @@ public final class FileBackedOutputStream extends OutputStream {
private final int fileThreshold;
private final boolean resetOnFinalize;
private final ByteSource source;
- @CheckForNull private final File parentDirectory;
@GuardedBy("this")
private OutputStream out;
@@ -97,6 +105,7 @@ synchronized File getFile() {
* {@link ByteSource} returned by {@link #asByteSource} is finalized.
*
* @param fileThreshold the number of bytes before the stream should switch to buffering to a file
+ * @throws IllegalArgumentException if {@code fileThreshold} is negative
*/
public FileBackedOutputStream(int fileThreshold) {
this(fileThreshold, false);
@@ -109,16 +118,13 @@ public FileBackedOutputStream(int fileThreshold) {
* @param fileThreshold the number of bytes before the stream should switch to buffering to a file
* @param resetOnFinalize if true, the {@link #reset} method will be called when the {@link
* ByteSource} returned by {@link #asByteSource} is finalized.
+ * @throws IllegalArgumentException if {@code fileThreshold} is negative
*/
public FileBackedOutputStream(int fileThreshold, boolean resetOnFinalize) {
- this(fileThreshold, resetOnFinalize, null);
- }
-
- private FileBackedOutputStream(
- int fileThreshold, boolean resetOnFinalize, @CheckForNull File parentDirectory) {
+ checkArgument(
+ fileThreshold >= 0, "fileThreshold must be non-negative, but was %s", fileThreshold);
this.fileThreshold = fileThreshold;
this.resetOnFinalize = resetOnFinalize;
- this.parentDirectory = parentDirectory;
memory = new MemoryOutput();
out = memory;
@@ -229,7 +235,7 @@ public synchronized void flush() throws IOException {
@GuardedBy("this")
private void update(int len) throws IOException {
if (memory != null && (memory.getCount() + len > fileThreshold)) {
- File temp = File.createTempFile("FileBackedOutputStream", null, parentDirectory);
+ File temp = TempFileCreator.INSTANCE.createTempFile("FileBackedOutputStream");
if (resetOnFinalize) {
// Finalizers are not guaranteed to be called on system shutdown;
// this is insurance.
diff --git a/guava/src/com/google/common/io/Files.java b/guava/src/com/google/common/io/Files.java
index 498faf97ed41..2a25d2fcd896 100644
--- a/guava/src/com/google/common/io/Files.java
+++ b/guava/src/com/google/common/io/Files.java
@@ -73,9 +73,6 @@
@ElementTypesAreNonnullByDefault
public final class Files {
- /** Maximum loop count when creating temp directories. */
- private static final int TEMP_DIR_ATTEMPTS = 10000;
-
private Files() {}
/**
@@ -399,17 +396,19 @@ public static boolean equal(File file1, File file2) throws IOException {
* Atomically creates a new directory somewhere beneath the system's temporary directory (as
* defined by the {@code java.io.tmpdir} system property), and returns its name.
*
+ *