mirror of
https://github.com/openjdk/jdk.git
synced 2026-02-06 00:18:34 +00:00
8252971: WindowsFileAttributes does not know about Unix domain sockets
Reviewed-by: alanb
This commit is contained in:
parent
682e78e89b
commit
9ffabf30c3
@ -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;
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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);
|
||||
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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() {
|
||||
|
||||
@ -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) &&
|
||||
|
||||
@ -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
|
||||
|
||||
97
test/jdk/java/nio/channels/unixdomain/FileAttributes.java
Normal file
97
test/jdk/java/nio/channels/unixdomain/FileAttributes.java
Normal 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
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -23,4 +23,6 @@
|
||||
grant {
|
||||
// No permission
|
||||
permission java.io.FilePermission "sock", "delete";
|
||||
|
||||
permission java.lang.RuntimePermission "setSecurityManager";
|
||||
};
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user