8252971: WindowsFileAttributes does not know about Unix domain sockets

Reviewed-by: alanb
This commit is contained in:
Michael McMahon 2021-02-12 13:08:08 +00:00
parent 682e78e89b
commit 9ffabf30c3
10 changed files with 235 additions and 17 deletions

View File

@ -73,6 +73,7 @@ class WindowsConstants {
// reparse point/symbolic link related constants
public static final int IO_REPARSE_TAG_SYMLINK = 0xA000000C;
public static final int IO_REPARSE_TAG_AF_UNIX = 0x80000023;
public static final int MAXIMUM_REPARSE_DATA_BUFFER_SIZE = 16 * 1024;
public static final int SYMBOLIC_LINK_FLAG_DIRECTORY = 0x1;
public static final int SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE = 0x2;
@ -107,6 +108,7 @@ class WindowsConstants {
public static final int ERROR_NOTIFY_ENUM_DIR = 1022;
public static final int ERROR_PRIVILEGE_NOT_HELD = 1314;
public static final int ERROR_NONE_MAPPED = 1332;
public static final int ERROR_CANT_ACCESS_FILE = 1920;
public static final int ERROR_NOT_A_REPARSE_POINT = 4390;
public static final int ERROR_INVALID_REPARSE_DATA = 4392;

View File

@ -431,6 +431,10 @@ class WindowsFileAttributes
return reparseTag == IO_REPARSE_TAG_SYMLINK;
}
boolean isUnixDomainSocket() {
return reparseTag == IO_REPARSE_TAG_AF_UNIX;
}
@Override
public boolean isDirectory() {
// ignore FILE_ATTRIBUTE_DIRECTORY attribute if file is a sym link

View File

@ -139,6 +139,12 @@ class WindowsFileCopy {
sm.checkPermission(new LinkPermission("symbolic"));
}
// if source is a Unix domain socket, we don't want to copy it for various
// reasons including consistency with Unix
if (sourceAttrs.isUnixDomainSocket()) {
throw new IOException("Can not copy socket file");
}
final String sourcePath = asWin32Path(source);
final String targetPath = asWin32Path(target);

View File

@ -334,6 +334,13 @@ class WindowsFileSystemProvider
0L);
fc.close();
} catch (WindowsException exc) {
try {
if (exc.lastError() == ERROR_CANT_ACCESS_FILE && isUnixDomainSocket(file)) {
// socket file is accessible
return;
}
} catch (WindowsException ignore) {}
// Windows errors are very inconsistent when the file is a directory
// (ERROR_PATH_NOT_FOUND returned for root directories for example)
// so we retry by attempting to open it as a directory.
@ -346,6 +353,11 @@ class WindowsFileSystemProvider
}
}
private static boolean isUnixDomainSocket(WindowsPath path) throws WindowsException {
WindowsFileAttributes attrs = WindowsFileAttributes.get(path, false);
return attrs.isUnixDomainSocket();
}
@Override
public void checkAccess(Path obj, AccessMode... modes) throws IOException {
WindowsPath file = WindowsPath.toWindowsPath(obj);

View File

@ -831,12 +831,52 @@ class WindowsPath implements Path {
int flags = FILE_FLAG_BACKUP_SEMANTICS;
if (!followLinks)
flags |= FILE_FLAG_OPEN_REPARSE_POINT;
try {
return openFileForReadAttributeAccess(flags);
} catch (WindowsException e) {
if (followLinks && e.lastError() == ERROR_CANT_ACCESS_FILE) {
// Object could be a Unix domain socket
try {
return openSocketForReadAttributeAccess();
} catch (WindowsException ignore) {}
}
throw e;
}
}
private long openFileForReadAttributeAccess(int flags)
throws WindowsException
{
return CreateFile(getPathForWin32Calls(),
FILE_READ_ATTRIBUTES,
(FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE),
0L,
OPEN_EXISTING,
flags);
FILE_READ_ATTRIBUTES,
(FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE),
0L,
OPEN_EXISTING,
flags);
}
/**
* Returns a handle to the file if it is a socket.
* Throws WindowsException if file is not a socket
*/
private long openSocketForReadAttributeAccess()
throws WindowsException
{
// needs to specify FILE_FLAG_OPEN_REPARSE_POINT if the file is a socket
int flags = FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT;
long handle = openFileForReadAttributeAccess(flags);
try {
WindowsFileAttributes attrs = WindowsFileAttributes.readAttributes(handle);
if (!attrs.isUnixDomainSocket()) {
throw new WindowsException("not a socket");
}
return handle;
} catch (WindowsException e) {
CloseHandle(handle);
throw e;
}
}
void checkRead() {

View File

@ -37,6 +37,7 @@
#include <io.h>
#include <limits.h>
#include <wchar.h>
#include <Winioctl.h>
#include "jni.h"
#include "io_util.h"
@ -187,6 +188,60 @@ static BOOL getFileInformation(const WCHAR *path,
return result;
}
/**
* path is likely to be a Unix domain socket.
* Verify and if it is return its attributes
*/
static DWORD getFinalAttributesUnixSocket(const WCHAR *path)
{
DWORD result;
BY_HANDLE_FILE_INFORMATION finfo;
REPARSE_GUID_DATA_BUFFER reparse;
HANDLE h = CreateFileW(path,
FILE_READ_ATTRIBUTES,
FILE_SHARE_DELETE |
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL,
OPEN_EXISTING,
FILE_FLAG_BACKUP_SEMANTICS |
FILE_FLAG_OPEN_REPARSE_POINT,
NULL);
if (h == INVALID_HANDLE_VALUE)
return INVALID_FILE_ATTRIBUTES;
if (!GetFileInformationByHandle(h, &finfo)) {
DWORD error = GetLastError();
if (CloseHandle(h)) {
SetLastError(error);
}
return INVALID_FILE_ATTRIBUTES;
}
if ((finfo.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) == 0) {
CloseHandle(h);
return INVALID_FILE_ATTRIBUTES;
}
/* check the reparse tag */
if (DeviceIoControl(h, FSCTL_GET_REPARSE_POINT, NULL, 0, &reparse,
(DWORD)sizeof(reparse), &result, NULL) == 0) {
CloseHandle(h);
return INVALID_FILE_ATTRIBUTES;
}
if (reparse.ReparseTag != IO_REPARSE_TAG_AF_UNIX) {
CloseHandle(h);
return INVALID_FILE_ATTRIBUTES;
}
CloseHandle(h);
return finfo.dwFileAttributes;
}
/**
* If the given attributes are the attributes of a reparse point, then
* read and return the attributes of the special cases.
@ -217,6 +272,11 @@ DWORD getFinalAttributes(WCHAR *path)
if (GetFileAttributesExW(path, GetFileExInfoStandard, &wfad)) {
attr = getFinalAttributesIfReparsePoint(path, wfad.dwFileAttributes);
if (attr == INVALID_FILE_ATTRIBUTES) {
if (GetLastError() == ERROR_CANT_ACCESS_FILE) {
attr = getFinalAttributesUnixSocket(path);
}
}
} else {
DWORD lerr = GetLastError();
if ((lerr == ERROR_SHARING_VIOLATION || lerr == ERROR_ACCESS_DENIED) &&

View File

@ -92,16 +92,7 @@ Java_sun_nio_ch_UnixDomainSockets_socketSupported(JNIEnv *env, jclass cl)
return JNI_FALSE;
}
closesocket(s);
/* Check for build 18362 or newer, due to Windows bug described in 8259014 */
OSVERSIONINFOEXW osvi = { sizeof(osvi), 0, 0, 0, 0, {0}, 0, 0 };
DWORDLONG cond_mask = 0;
VER_SET_CONDITION(cond_mask, VER_BUILDNUMBER, VER_GREATER_EQUAL);
osvi.dwBuildNumber = 18362; // Windows 10 (1903) or newer
return VerifyVersionInfoW(&osvi, VER_BUILDNUMBER, cond_mask) != 0;
return JNI_TRUE;
}
JNIEXPORT jint JNICALL

View File

@ -0,0 +1,97 @@
/*
* Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/**
* @test
* @bug 8252971
* @library /test/lib
* @run testng FileAttributes
*/
import java.io.IOException;
import java.io.File;
import java.net.*;
import java.nio.channels.*;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.Arrays;
import org.testng.annotations.Test;
import org.testng.SkipException;
import static java.net.StandardProtocolFamily.UNIX;
import static org.testng.Assert.assertFalse;
import static org.testng.Assert.assertThrows;
import static org.testng.Assert.assertTrue;
/**
*/
public class FileAttributes {
@Test
public static void test() throws Exception {
checkSupported();
Path path = null;
try (var chan = SocketChannel.open(UNIX)) {
path = Path.of("foo.sock");
var addr = UnixDomainSocketAddress.of(path);
chan.bind(addr);
// Check file exists
File f = path.toFile();
assertTrue(f.exists(), "File.exists failed");
assertTrue(Files.exists(path), "Files.exists failed");
// Check basic attributes
BasicFileAttributes attrs = Files.readAttributes(path, BasicFileAttributes.class);
assertFalse(attrs.isDirectory(), "file is not a directory");
assertTrue(attrs.isOther(), "file is other");
assertFalse(attrs.isRegularFile(), "file is not a regular file");
assertFalse(attrs.isSymbolicLink(), "file is not a symbolic link");
// Check can't copy
final Path src = path;
final Path dest = Path.of("bar.sock");
assertThrows(IOException.class, () -> Files.copy(src, dest));
// Check deletion
assertTrue(f.delete(), "File.delete failed");
} finally {
Files.deleteIfExists(path);
}
}
static void checkSupported() {
try {
SocketChannel.open(UNIX).close();
} catch (UnsupportedOperationException e) {
throw new SkipException("Unix domain channels not supported");
} catch (Exception e) {
// continue test to see what problem is
}
}
}

View File

@ -162,6 +162,7 @@ public class Security {
public static void testPolicy3() throws Exception {
Path sock1 = Path.of("sock3");
Path sock2 = null;
Files.deleteIfExists(sock1);
final UnixDomainSocketAddress saddr = UnixDomainSocketAddress.of(sock1);
try (var s1 = ServerSocketChannel.open(UNIX)) {
@ -169,8 +170,7 @@ public class Security {
try (var s2 = ServerSocketChannel.open(UNIX)) {
s2.bind(null);
var add2 = (UnixDomainSocketAddress)s2.getLocalAddress();
saddr.getPath().toFile().deleteOnExit();
add2.getPath().toFile().deleteOnExit();
sock2 = add2.getPath();
// Now set security manager and check if we can see addresses
@ -194,6 +194,10 @@ public class Security {
throw new RuntimeException("address should have been empty");
}
}
} finally {
System.setSecurityManager(null);
Files.deleteIfExists(sock1);
Files.deleteIfExists(sock2);
}
}
}

View File

@ -23,4 +23,6 @@
grant {
// No permission
permission java.io.FilePermission "sock", "delete";
permission java.lang.RuntimePermission "setSecurityManager";
};