Skip to content

Commit a994ff2

Browse files
cpovirkGoogle Java Core Libraries
authored and
Google Java Core Libraries
committed
Fix Files.createTempDir and FileBackedOutputStream under Windows.
Based on [some discussions in GitHub](#6535 (comment)), I'm under the impression that Windows would create the temporary directory/file in a secure location even if our call to `java.nio.file.Files.createTempDirectory`/`createTempFile` passes no ACL attribute. However, in case we are in an unusual situation (Linux with NFS temporary directory???) in which ACLs are supported but the temporary directory is _not_ a secure location, I've arranged for the file to be created with an ACL that grants permissions only to the current user. I set the user's permissions to the ones that I saw on a file created in the temporary directory under Java's default settings, and I didn't do anything to set the additional permissions I was seeing for administrators. The resulting file's permissions look plausibly correct in the Windows property dialog, if slightly different than what I get when creating a file/directory myself through Explorer. Fixes #6535 Relates to: - #4011 - #2575 - #2686. (It sure would be convenient to have Windows CI set up!) RELNOTES=`io`: Fixed `Files.createTempDir` and `FileBackedOutputStream` under Windows. PiperOrigin-RevId: 538492945
1 parent 9916d82 commit a994ff2

File tree

4 files changed

+198
-10
lines changed

4 files changed

+198
-10
lines changed

Diff for: android/guava-tests/test/com/google/common/io/FilesCreateTempDirTest.java

+3
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,9 @@ public void testCreateTempDir() throws IOException {
4747
assertTrue(temp.exists());
4848
assertTrue(temp.isDirectory());
4949
assertThat(temp.listFiles()).isEmpty();
50+
File child = new File(temp, "child");
51+
assertTrue(child.createNewFile());
52+
assertTrue(child.delete());
5053

5154
if (isAndroid()) {
5255
return;

Diff for: android/guava/src/com/google/common/io/TempFileCreator.java

+96-5
Original file line numberDiff line numberDiff line change
@@ -15,17 +15,39 @@
1515
package com.google.common.io;
1616

1717
import static com.google.common.base.StandardSystemProperty.JAVA_IO_TMPDIR;
18+
import static com.google.common.base.StandardSystemProperty.USER_NAME;
19+
import static java.nio.file.attribute.AclEntryFlag.DIRECTORY_INHERIT;
20+
import static java.nio.file.attribute.AclEntryFlag.FILE_INHERIT;
21+
import static java.nio.file.attribute.AclEntryPermission.APPEND_DATA;
22+
import static java.nio.file.attribute.AclEntryPermission.DELETE;
23+
import static java.nio.file.attribute.AclEntryPermission.DELETE_CHILD;
24+
import static java.nio.file.attribute.AclEntryPermission.EXECUTE;
25+
import static java.nio.file.attribute.AclEntryPermission.READ_ACL;
26+
import static java.nio.file.attribute.AclEntryPermission.READ_ATTRIBUTES;
27+
import static java.nio.file.attribute.AclEntryPermission.READ_DATA;
28+
import static java.nio.file.attribute.AclEntryPermission.READ_NAMED_ATTRS;
29+
import static java.nio.file.attribute.AclEntryPermission.SYNCHRONIZE;
30+
import static java.nio.file.attribute.AclEntryPermission.WRITE_ACL;
31+
import static java.nio.file.attribute.AclEntryPermission.WRITE_ATTRIBUTES;
32+
import static java.nio.file.attribute.AclEntryPermission.WRITE_DATA;
33+
import static java.nio.file.attribute.AclEntryPermission.WRITE_NAMED_ATTRS;
34+
import static java.nio.file.attribute.AclEntryPermission.WRITE_OWNER;
35+
import static java.nio.file.attribute.AclEntryType.ALLOW;
1836

1937
import com.google.common.annotations.GwtIncompatible;
2038
import com.google.common.annotations.J2ktIncompatible;
39+
import com.google.common.collect.ImmutableList;
2140
import com.google.j2objc.annotations.J2ObjCIncompatible;
2241
import java.io.File;
2342
import java.io.IOException;
43+
import java.nio.file.FileSystems;
2444
import java.nio.file.Paths;
45+
import java.nio.file.attribute.AclEntry;
2546
import java.nio.file.attribute.FileAttribute;
26-
import java.nio.file.attribute.PosixFilePermission;
2747
import java.nio.file.attribute.PosixFilePermissions;
48+
import java.nio.file.attribute.UserPrincipal;
2849
import java.util.Set;
50+
import javax.annotation.CheckForNull;
2951

3052
/**
3153
* Creates temporary files and directories whose permissions are restricted to the current user or,
@@ -90,16 +112,63 @@ private static TempFileCreator pickSecureCreator() {
90112

91113
@IgnoreJRERequirement // used only when Path is available
92114
private static final class JavaNioCreator extends TempFileCreator {
93-
private static final FileAttribute<Set<PosixFilePermission>> RWX_USER_ONLY =
115+
private static final FileAttribute<?> RWX_USER_ONLY =
94116
PosixFilePermissions.asFileAttribute(PosixFilePermissions.fromString("rwx------"));
95-
private static final FileAttribute<Set<PosixFilePermission>> RW_USER_ONLY =
117+
private static final FileAttribute<?> RW_USER_ONLY =
96118
PosixFilePermissions.asFileAttribute(PosixFilePermissions.fromString("rw-------"));
119+
@CheckForNull private static FileAttribute<?> userOnly;
120+
121+
private static FileAttribute<?> userOnly() throws IOException {
122+
FileAttribute<?> result = userOnly;
123+
if (result != null) {
124+
return result;
125+
}
126+
127+
UserPrincipal user =
128+
FileSystems.getDefault()
129+
.getUserPrincipalLookupService()
130+
.lookupPrincipalByName(USER_NAME.value());
131+
ImmutableList<AclEntry> acl =
132+
ImmutableList.of(
133+
AclEntry.newBuilder()
134+
.setType(ALLOW)
135+
.setPrincipal(user)
136+
.setPermissions(
137+
APPEND_DATA,
138+
DELETE,
139+
DELETE_CHILD,
140+
EXECUTE,
141+
READ_ACL,
142+
READ_ATTRIBUTES,
143+
READ_DATA,
144+
READ_NAMED_ATTRS,
145+
SYNCHRONIZE,
146+
WRITE_ACL,
147+
WRITE_ATTRIBUTES,
148+
WRITE_DATA,
149+
WRITE_NAMED_ATTRS,
150+
WRITE_OWNER)
151+
.setFlags(DIRECTORY_INHERIT, FILE_INHERIT)
152+
.build());
153+
return userOnly =
154+
new FileAttribute<ImmutableList<AclEntry>>() {
155+
@Override
156+
public String name() {
157+
return "acl:acl";
158+
}
159+
160+
@Override
161+
public ImmutableList<AclEntry> value() {
162+
return acl;
163+
}
164+
};
165+
}
97166

98167
@Override
99168
File createTempDir() {
100169
try {
101170
return java.nio.file.Files.createTempDirectory(
102-
Paths.get(JAVA_IO_TMPDIR.value()), /* prefix= */ null, RWX_USER_ONLY)
171+
Paths.get(JAVA_IO_TMPDIR.value()), /* prefix= */ null, directoryPermissions())
103172
.toFile();
104173
} catch (IOException e) {
105174
throw new IllegalStateException("Failed to create directory", e);
@@ -112,9 +181,31 @@ File createTempFile(String prefix) throws IOException {
112181
Paths.get(JAVA_IO_TMPDIR.value()),
113182
/* prefix= */ prefix,
114183
/* suffix= */ null,
115-
RW_USER_ONLY)
184+
filePermissions())
116185
.toFile();
117186
}
187+
188+
private static FileAttribute<?> directoryPermissions() throws IOException {
189+
Set<String> views = FileSystems.getDefault().supportedFileAttributeViews();
190+
if (views.contains("posix")) {
191+
return RWX_USER_ONLY;
192+
} else if (views.contains("acl")) {
193+
return userOnly();
194+
} else {
195+
throw new IOException("unrecognized FileSystem type " + FileSystems.getDefault());
196+
}
197+
}
198+
199+
private static FileAttribute<?> filePermissions() throws IOException {
200+
Set<String> views = FileSystems.getDefault().supportedFileAttributeViews();
201+
if (views.contains("posix")) {
202+
return RW_USER_ONLY;
203+
} else if (views.contains("acl")) {
204+
return userOnly();
205+
} else {
206+
throw new IOException("unrecognized FileSystem type " + FileSystems.getDefault());
207+
}
208+
}
118209
}
119210

120211
private static final class JavaIoCreator extends TempFileCreator {

Diff for: guava-tests/test/com/google/common/io/FilesCreateTempDirTest.java

+3
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,9 @@ public void testCreateTempDir() throws IOException {
4747
assertTrue(temp.exists());
4848
assertTrue(temp.isDirectory());
4949
assertThat(temp.listFiles()).isEmpty();
50+
File child = new File(temp, "child");
51+
assertTrue(child.createNewFile());
52+
assertTrue(child.delete());
5053

5154
if (isAndroid()) {
5255
return;

Diff for: guava/src/com/google/common/io/TempFileCreator.java

+96-5
Original file line numberDiff line numberDiff line change
@@ -15,17 +15,39 @@
1515
package com.google.common.io;
1616

1717
import static com.google.common.base.StandardSystemProperty.JAVA_IO_TMPDIR;
18+
import static com.google.common.base.StandardSystemProperty.USER_NAME;
19+
import static java.nio.file.attribute.AclEntryFlag.DIRECTORY_INHERIT;
20+
import static java.nio.file.attribute.AclEntryFlag.FILE_INHERIT;
21+
import static java.nio.file.attribute.AclEntryPermission.APPEND_DATA;
22+
import static java.nio.file.attribute.AclEntryPermission.DELETE;
23+
import static java.nio.file.attribute.AclEntryPermission.DELETE_CHILD;
24+
import static java.nio.file.attribute.AclEntryPermission.EXECUTE;
25+
import static java.nio.file.attribute.AclEntryPermission.READ_ACL;
26+
import static java.nio.file.attribute.AclEntryPermission.READ_ATTRIBUTES;
27+
import static java.nio.file.attribute.AclEntryPermission.READ_DATA;
28+
import static java.nio.file.attribute.AclEntryPermission.READ_NAMED_ATTRS;
29+
import static java.nio.file.attribute.AclEntryPermission.SYNCHRONIZE;
30+
import static java.nio.file.attribute.AclEntryPermission.WRITE_ACL;
31+
import static java.nio.file.attribute.AclEntryPermission.WRITE_ATTRIBUTES;
32+
import static java.nio.file.attribute.AclEntryPermission.WRITE_DATA;
33+
import static java.nio.file.attribute.AclEntryPermission.WRITE_NAMED_ATTRS;
34+
import static java.nio.file.attribute.AclEntryPermission.WRITE_OWNER;
35+
import static java.nio.file.attribute.AclEntryType.ALLOW;
1836

1937
import com.google.common.annotations.GwtIncompatible;
2038
import com.google.common.annotations.J2ktIncompatible;
39+
import com.google.common.collect.ImmutableList;
2140
import com.google.j2objc.annotations.J2ObjCIncompatible;
2241
import java.io.File;
2342
import java.io.IOException;
43+
import java.nio.file.FileSystems;
2444
import java.nio.file.Paths;
45+
import java.nio.file.attribute.AclEntry;
2546
import java.nio.file.attribute.FileAttribute;
26-
import java.nio.file.attribute.PosixFilePermission;
2747
import java.nio.file.attribute.PosixFilePermissions;
48+
import java.nio.file.attribute.UserPrincipal;
2849
import java.util.Set;
50+
import javax.annotation.CheckForNull;
2951

3052
/**
3153
* Creates temporary files and directories whose permissions are restricted to the current user or,
@@ -90,16 +112,63 @@ private static TempFileCreator pickSecureCreator() {
90112

91113
@IgnoreJRERequirement // used only when Path is available
92114
private static final class JavaNioCreator extends TempFileCreator {
93-
private static final FileAttribute<Set<PosixFilePermission>> RWX_USER_ONLY =
115+
private static final FileAttribute<?> RWX_USER_ONLY =
94116
PosixFilePermissions.asFileAttribute(PosixFilePermissions.fromString("rwx------"));
95-
private static final FileAttribute<Set<PosixFilePermission>> RW_USER_ONLY =
117+
private static final FileAttribute<?> RW_USER_ONLY =
96118
PosixFilePermissions.asFileAttribute(PosixFilePermissions.fromString("rw-------"));
119+
@CheckForNull private static FileAttribute<?> userOnly;
120+
121+
private static FileAttribute<?> userOnly() throws IOException {
122+
FileAttribute<?> result = userOnly;
123+
if (result != null) {
124+
return result;
125+
}
126+
127+
UserPrincipal user =
128+
FileSystems.getDefault()
129+
.getUserPrincipalLookupService()
130+
.lookupPrincipalByName(USER_NAME.value());
131+
ImmutableList<AclEntry> acl =
132+
ImmutableList.of(
133+
AclEntry.newBuilder()
134+
.setType(ALLOW)
135+
.setPrincipal(user)
136+
.setPermissions(
137+
APPEND_DATA,
138+
DELETE,
139+
DELETE_CHILD,
140+
EXECUTE,
141+
READ_ACL,
142+
READ_ATTRIBUTES,
143+
READ_DATA,
144+
READ_NAMED_ATTRS,
145+
SYNCHRONIZE,
146+
WRITE_ACL,
147+
WRITE_ATTRIBUTES,
148+
WRITE_DATA,
149+
WRITE_NAMED_ATTRS,
150+
WRITE_OWNER)
151+
.setFlags(DIRECTORY_INHERIT, FILE_INHERIT)
152+
.build());
153+
return userOnly =
154+
new FileAttribute<ImmutableList<AclEntry>>() {
155+
@Override
156+
public String name() {
157+
return "acl:acl";
158+
}
159+
160+
@Override
161+
public ImmutableList<AclEntry> value() {
162+
return acl;
163+
}
164+
};
165+
}
97166

98167
@Override
99168
File createTempDir() {
100169
try {
101170
return java.nio.file.Files.createTempDirectory(
102-
Paths.get(JAVA_IO_TMPDIR.value()), /* prefix= */ null, RWX_USER_ONLY)
171+
Paths.get(JAVA_IO_TMPDIR.value()), /* prefix= */ null, directoryPermissions())
103172
.toFile();
104173
} catch (IOException e) {
105174
throw new IllegalStateException("Failed to create directory", e);
@@ -112,9 +181,31 @@ File createTempFile(String prefix) throws IOException {
112181
Paths.get(JAVA_IO_TMPDIR.value()),
113182
/* prefix= */ prefix,
114183
/* suffix= */ null,
115-
RW_USER_ONLY)
184+
filePermissions())
116185
.toFile();
117186
}
187+
188+
private static FileAttribute<?> directoryPermissions() throws IOException {
189+
Set<String> views = FileSystems.getDefault().supportedFileAttributeViews();
190+
if (views.contains("posix")) {
191+
return RWX_USER_ONLY;
192+
} else if (views.contains("acl")) {
193+
return userOnly();
194+
} else {
195+
throw new IOException("unrecognized FileSystem type " + FileSystems.getDefault());
196+
}
197+
}
198+
199+
private static FileAttribute<?> filePermissions() throws IOException {
200+
Set<String> views = FileSystems.getDefault().supportedFileAttributeViews();
201+
if (views.contains("posix")) {
202+
return RW_USER_ONLY;
203+
} else if (views.contains("acl")) {
204+
return userOnly();
205+
} else {
206+
throw new IOException("unrecognized FileSystem type " + FileSystems.getDefault());
207+
}
208+
}
118209
}
119210

120211
private static final class JavaIoCreator extends TempFileCreator {

0 commit comments

Comments
 (0)