|
16 | 16 |
|
17 | 17 | import static com.google.common.base.StandardSystemProperty.JAVA_IO_TMPDIR;
|
18 | 18 | import static com.google.common.base.StandardSystemProperty.USER_NAME;
|
| 19 | +import static com.google.common.base.Throwables.throwIfUnchecked; |
19 | 20 | import static java.nio.file.attribute.AclEntryFlag.DIRECTORY_INHERIT;
|
20 | 21 | import static java.nio.file.attribute.AclEntryFlag.FILE_INHERIT;
|
21 | 22 | import static java.nio.file.attribute.AclEntryType.ALLOW;
|
22 | 23 | import static java.nio.file.attribute.PosixFilePermissions.asFileAttribute;
|
| 24 | +import static java.util.Objects.requireNonNull; |
23 | 25 |
|
24 | 26 | import com.google.common.annotations.GwtIncompatible;
|
25 | 27 | import com.google.common.annotations.J2ktIncompatible;
|
| 28 | +import com.google.common.annotations.VisibleForTesting; |
26 | 29 | import com.google.common.collect.ImmutableList;
|
27 | 30 | import com.google.j2objc.annotations.J2ObjCIncompatible;
|
28 | 31 | import java.io.File;
|
29 | 32 | import java.io.IOException;
|
| 33 | +import java.lang.reflect.InvocationTargetException; |
| 34 | +import java.lang.reflect.Method; |
30 | 35 | import java.nio.file.FileSystems;
|
31 | 36 | import java.nio.file.Paths;
|
32 | 37 | import java.nio.file.attribute.AclEntry;
|
@@ -98,6 +103,17 @@ private static TempFileCreator pickSecureCreator() {
|
98 | 103 | return new JavaIoCreator();
|
99 | 104 | }
|
100 | 105 |
|
| 106 | + /** |
| 107 | + * Creates the permissions normally used for Windows filesystems, looking up the user afresh, even |
| 108 | + * if previous calls have initialized the PermissionSupplier fields. |
| 109 | + */ |
| 110 | + @IgnoreJRERequirement // used only when Path is available (and only from tests) |
| 111 | + @VisibleForTesting |
| 112 | + static void testMakingUserPermissionsFromScratch() throws IOException { |
| 113 | + // All we're testing is whether it throws. |
| 114 | + FileAttribute<?> unused = JavaNioCreator.userPermissions().get(); |
| 115 | + } |
| 116 | + |
101 | 117 | @IgnoreJRERequirement // used only when Path is available
|
102 | 118 | private static final class JavaNioCreator extends TempFileCreator {
|
103 | 119 | @Override
|
@@ -150,7 +166,7 @@ private static PermissionSupplier userPermissions() {
|
150 | 166 | UserPrincipal user =
|
151 | 167 | FileSystems.getDefault()
|
152 | 168 | .getUserPrincipalLookupService()
|
153 |
| - .lookupPrincipalByName(USER_NAME.value()); |
| 169 | + .lookupPrincipalByName(getUsername()); |
154 | 170 | ImmutableList<AclEntry> acl =
|
155 | 171 | ImmutableList.of(
|
156 | 172 | AclEntry.newBuilder()
|
@@ -179,6 +195,62 @@ public ImmutableList<AclEntry> value() {
|
179 | 195 | };
|
180 | 196 | }
|
181 | 197 | }
|
| 198 | + |
| 199 | + private static String getUsername() { |
| 200 | + /* |
| 201 | + * https://github.com/google/guava/issues/6634: ProcessHandle has more accurate information, |
| 202 | + * but that class isn't available under all environments that we support. We use it if |
| 203 | + * available and fall back if not. |
| 204 | + */ |
| 205 | + String fromSystemProperty = requireNonNull(USER_NAME.value()); |
| 206 | + |
| 207 | + try { |
| 208 | + Class<?> processHandleClass = Class.forName("java.lang.ProcessHandle"); |
| 209 | + Class<?> processHandleInfoClass = Class.forName("java.lang.ProcessHandle$Info"); |
| 210 | + Class<?> optionalClass = Class.forName("java.util.Optional"); |
| 211 | + /* |
| 212 | + * We don't *need* to use reflection to access Optional: It's available on all JDKs we |
| 213 | + * support, and Android code won't get this far, anyway, because ProcessHandle is |
| 214 | + * unavailable. But given how much other reflection we're using, we might as well use it |
| 215 | + * here, too, so that we don't need to also suppress an AndroidApiChecker error. |
| 216 | + */ |
| 217 | + |
| 218 | + Method currentMethod = processHandleClass.getMethod("current"); |
| 219 | + Method infoMethod = processHandleClass.getMethod("info"); |
| 220 | + Method userMethod = processHandleInfoClass.getMethod("user"); |
| 221 | + Method orElseMethod = optionalClass.getMethod("orElse", Object.class); |
| 222 | + |
| 223 | + Object current = currentMethod.invoke(null); |
| 224 | + Object info = infoMethod.invoke(current); |
| 225 | + Object user = userMethod.invoke(info); |
| 226 | + return (String) requireNonNull(orElseMethod.invoke(user, fromSystemProperty)); |
| 227 | + } catch (ClassNotFoundException runningUnderAndroidOrJava8) { |
| 228 | + /* |
| 229 | + * I'm not sure that we could actually get here for *Android*: I would expect us to enter |
| 230 | + * the POSIX code path instead. And if we tried this code path, we'd have trouble unless we |
| 231 | + * were running under a new enough version of Android to support NIO. |
| 232 | + * |
| 233 | + * So this is probably just the "Windows Java 8" case. In that case, if we wanted *another* |
| 234 | + * layer of fallback before consulting the system property, we could try |
| 235 | + * com.sun.security.auth.module.NTSystem. |
| 236 | + * |
| 237 | + * But for now, we use the value from the system property as our best guess. |
| 238 | + */ |
| 239 | + return fromSystemProperty; |
| 240 | + } catch (InvocationTargetException e) { |
| 241 | + throwIfUnchecked(e.getCause()); // in case it's an Error or something |
| 242 | + return fromSystemProperty; // should be impossible |
| 243 | + } catch (NoSuchMethodException shouldBeImpossible) { |
| 244 | + return fromSystemProperty; |
| 245 | + } catch (IllegalAccessException shouldBeImpossible) { |
| 246 | + /* |
| 247 | + * We don't merge these into `catch (ReflectiveOperationException ...)` or an equivalent |
| 248 | + * multicatch because ReflectiveOperationException isn't available under Android: |
| 249 | + * b/124188803 |
| 250 | + */ |
| 251 | + return fromSystemProperty; |
| 252 | + } |
| 253 | + } |
182 | 254 | }
|
183 | 255 |
|
184 | 256 | private static final class JavaIoCreator extends TempFileCreator {
|
|
0 commit comments