Compare commits

..

1 Commits

Author SHA1 Message Date
Vitaly Provodin
7714eae794 update exclude list on results of 25.0.1_259.33 test runs 2025-12-27 04:36:55 +04:00
5 changed files with 57 additions and 202 deletions

View File

@@ -28,6 +28,7 @@ package java.io;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.file.Files;
import java.nio.file.InvalidPathException;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;
import java.util.Arrays;
@@ -157,11 +158,25 @@ public class FileInputStream extends InputStream
try (var guard = IoOverNio.RecursionGuard.create(FileInputStream.class)) {
IoOverNio.blackhole(guard);
Path nioPath = IoOverNioFileSystem.getNioPath(file, true);
useNio = nioPath != null;
java.nio.file.FileSystem nioFs = IoOverNioFileSystem.acquireNioFs(path);
Path nioPath = null;
if (nioFs != null && path != null) {
try {
nioPath = nioFs.getPath(path);
isRegularFile = Files.isRegularFile(nioPath);
} catch (InvalidPathException _) {
// Nothing.
}
}
// Two significant differences between the legacy java.io and java.nio.files:
// * java.nio.file allows to open directories as streams, java.io.FileInputStream doesn't.
// * java.nio.file doesn't work well with pseudo devices, i.e., `seek()` fails, while java.io works well.
useNio = nioPath != null && isRegularFile == Boolean.TRUE;
if (useNio) {
var bundle = IoOverNioFileSystem.initializeStreamUsingNio(
this, nioPath.getFileSystem(), file, nioPath, Set.of(StandardOpenOption.READ), channelCleanable);
this, nioFs, file, nioPath, Set.of(StandardOpenOption.READ), channelCleanable);
channel = bundle.channel();
fd = bundle.fd();
externalChannelHolder = bundle.externalChannelHolder();

View File

@@ -227,10 +227,11 @@ public class FileOutputStream extends OutputStream
try (var guard = IoOverNio.RecursionGuard.create(FileOutputStream.class)) {
IoOverNio.blackhole(guard);
Path nioPath = IoOverNioFileSystem.getNioPath(file, false);
useNio = nioPath != null;
java.nio.file.FileSystem nioFs = IoOverNioFileSystem.acquireNioFs(path);
useNio = path != null && nioFs != null;
if (useNio) {
java.nio.file.FileSystem nioFs = nioPath.getFileSystem();
Path nioPath = nioFs.getPath(path);
// java.io backend doesn't open DOS hidden files for writing, but java.nio.file opens.
// This code mimics the old behavior.
if (nioFs.getSeparator().equals("\\")) {

View File

@@ -41,7 +41,6 @@ import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.InvalidPathException;
import java.nio.file.LinkOption;
import java.nio.file.NoSuchFileException;
import java.nio.file.OpenOption;
import java.nio.file.Path;
import java.nio.file.StandardCopyOption;
@@ -59,7 +58,6 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.EnumSet;
import java.util.List;
import java.util.Locale;
import java.util.Objects;
import java.util.Set;
@@ -125,69 +123,6 @@ class IoOverNioFileSystem extends FileSystem {
return result;
}
static Path getNioPath(File file, boolean mustBeRegularFile) {
String path = file.getPath();
java.nio.file.FileSystem nioFs = IoOverNioFileSystem.acquireNioFs(path);
if (nioFs == null) {
return null;
}
Path nioPath;
try {
nioPath = nioFs.getPath(path);
} catch (InvalidPathException _) {
return null;
}
if (!mustBeRegularFile) {
return nioPath;
}
if (isWindowsPipe(nioPath)) {
// https://learn.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-createnamedpipea see nMaxInstances:
//
// Getting file attributes in this case is dangerous.
// GetFileAttributesW acquires a connection to the pipe internally,
// occupying a place on the server side.
// The server and the client are very likely two different processes, and it takes time to deliver
// the connection closing message to the server.
// If the caller invokes CreateFileW fast enough after GetFileAttributesW and nMaxInstances = 1,
// CreateFileW is called before the server closes the previous connection created by GetFileAttributesW
// and ERROR_PIPE_BUSY is returned.
//
// Anyway, `readAttributes(nioPath).isRegularFile()` returns true for pipes, so it's safe to return here.
return nioPath;
}
// Two significant differences between the legacy java.io and java.nio.files:
// * java.nio.file allows to open directories as streams, java.io.FileInputStream doesn't.
// * java.nio.file doesn't work well with pseudo devices, i.e., `seek()` fails, while java.io works well.
try {
if (Files.readAttributes(nioPath, BasicFileAttributes.class, LinkOption.NOFOLLOW_LINKS).isRegularFile()) {
return nioPath;
}
} catch (NoSuchFileException _) {
return nioPath;
} catch (IOException _) {
// Ignored.
}
return null;
}
/**
* <a href="https://learn.microsoft.com/en-us/windows/win32/ipc/pipe-names">
* The pipe path format: {@code ^\\(\w+|\.)\pipe\.*}
* </a>
*/
private static boolean isWindowsPipe(Path path) {
// A small JMH benchmark shows that this code takes less than a microsecond,
// and the JIT compiler does its job very well here.
return path.isAbsolute() &&
path.getRoot().toString().startsWith("\\\\") &&
path.getRoot().toString().toLowerCase(Locale.getDefault()).endsWith("\\pipe\\");
}
private static boolean setPermission0(java.nio.file.FileSystem nioFs, File f, int access, boolean enable, boolean owneronly) {
if (f.getPath().isEmpty()) {
if (nioFs.getSeparator().equals("\\")) {

View File

@@ -27,11 +27,16 @@ package java.io;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.file.Path;
import java.nio.channels.NonWritableChannelException;
import java.nio.file.FileSystem;
import java.nio.file.Files;
import java.nio.file.InvalidPathException;
import java.nio.file.LinkOption;
import java.nio.file.NoSuchFileException;
import java.nio.file.OpenOption;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.Arrays;
import java.util.HashSet;
@@ -276,11 +281,35 @@ public class RandomAccessFile implements DataOutput, DataInput, Closeable {
try (var guard = IoOverNio.RecursionGuard.create(RandomAccessFile.class)) {
IoOverNio.blackhole(guard);
Path nioPath = IoOverNioFileSystem.getNioPath(file, true);
useNio = nioPath != null;
FileSystem nioFs = IoOverNioFileSystem.acquireNioFs(path);
Path nioPath = null;
if (nioFs != null) {
try {
nioPath = nioFs.getPath(path);
} catch (InvalidPathException _) {
// Nothing.
}
}
// Two significant differences between the legacy java.io and java.nio.files:
// * java.nio.file allows to open directories as streams, java.io.FileInputStream doesn't.
// * java.nio.file doesn't work well with pseudo devices, i.e., `seek()` fails, while java.io works well.
boolean isRegularFile;
try {
isRegularFile = nioPath != null &&
Files.readAttributes(nioPath, BasicFileAttributes.class, LinkOption.NOFOLLOW_LINKS).isRegularFile();
}
catch (NoSuchFileException _) {
isRegularFile = true;
}
catch (IOException _) {
isRegularFile = false;
}
useNio = nioPath != null && isRegularFile;
if (useNio) {
var bundle = IoOverNioFileSystem.initializeStreamUsingNio(
this, nioPath.getFileSystem(), file, nioPath, optionsForChannel(imode), channelCleanable);
this, nioFs, file, nioPath, optionsForChannel(imode), channelCleanable);
channel = bundle.channel();
fd = bundle.fd();
externalChannelHolder = bundle.externalChannelHolder();

View File

@@ -24,14 +24,11 @@
/* @test
* @summary java.io.RandomAccessFileTest uses java.nio.file inside.
* @library testNio
* @compile --enable-preview --source 25 RandomAccessFileTest.java
* @run junit/othervm
* -Djava.nio.file.spi.DefaultFileSystemProvider=testNio.ManglingFileSystemProvidera
* -Djava.nio.file.spi.DefaultFileSystemProvider=testNio.ManglingFileSystemProvider
* -Djbr.java.io.use.nio=true
* --add-opens jdk.unsupported/com.sun.nio.file=ALL-UNNAMED
* --add-opens java.base/java.io=ALL-UNNAMED
* --enable-native-access=ALL-UNNAMED
* --enable-preview
* RandomAccessFileTest
*/
@@ -48,9 +45,6 @@ import testNio.ManglingFileSystemProvider;
import java.io.EOFException;
import java.io.File;
import java.io.RandomAccessFile;
import java.lang.foreign.*;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.VarHandle;
import java.lang.reflect.Field;
import java.nio.channels.FileChannel;
import java.nio.channels.FileLock;
@@ -61,7 +55,6 @@ import java.util.Arrays;
import java.util.Collections;
import java.util.EnumSet;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicBoolean;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -300,122 +293,4 @@ public class RandomAccessFileTest {
FileSystems.getDefault().provider().newFileChannel(file.toPath(), Collections.singleton(option)).close();
}
}
/**
* JBR-9779
*/
@Test
public void testWindowsPipe() throws Throwable {
Assume.assumeTrue("Windows-only test", System.getProperty("os.name").toLowerCase().startsWith("win"));
// Creating a pipe.
Linker linker = Linker.nativeLinker();
SymbolLookup loader = SymbolLookup.libraryLookup("kernel32", Arena.global());
StructLayout captureLayout = Linker.Option.captureStateLayout();
VarHandle GetLastError = captureLayout.varHandle(MemoryLayout.PathElement.groupElement("GetLastError"));
MethodHandle CreateNamedPipeW = linker.downcallHandle(
loader.find("CreateNamedPipeW").get(),
FunctionDescriptor.of(ValueLayout.JAVA_LONG,
ValueLayout.ADDRESS, ValueLayout.JAVA_INT, ValueLayout.JAVA_INT,
ValueLayout.JAVA_INT, ValueLayout.JAVA_INT, ValueLayout.JAVA_INT, ValueLayout.JAVA_INT,
ValueLayout.ADDRESS),
Linker.Option.captureCallState("GetLastError")
);
MethodHandle ConnectNamedPipe = linker.downcallHandle(
loader.find("ConnectNamedPipe").get(),
FunctionDescriptor.of(ValueLayout.JAVA_INT, ValueLayout.JAVA_LONG, ValueLayout.ADDRESS),
Linker.Option.captureCallState("GetLastError")
);
MethodHandle DisconnectNamedPipe = linker.downcallHandle(
loader.find("DisconnectNamedPipe").get(),
FunctionDescriptor.of(ValueLayout.JAVA_INT, ValueLayout.JAVA_LONG)
);
MethodHandle PeekNamedPipe = linker.downcallHandle(
loader.find("PeekNamedPipe").get(),
FunctionDescriptor.of(ValueLayout.JAVA_INT, ValueLayout.JAVA_LONG,
ValueLayout.ADDRESS, ValueLayout.JAVA_INT, ValueLayout.ADDRESS,
ValueLayout.ADDRESS, ValueLayout.ADDRESS)
);
MethodHandle CloseHandle = linker.downcallHandle(
loader.find("CloseHandle").get(),
FunctionDescriptor.of(ValueLayout.JAVA_INT, ValueLayout.JAVA_LONG)
);
String pipeName = "\\\\.\\pipe\\jbr-test-pipe-" + System.nanoTime();
Arena arena = Arena.ofAuto();
char[] nameChars = (pipeName + "\0").toCharArray(); // `char` on Windows is UTF-16, as WinAPI expects.
MemorySegment pipeWinPath = arena.allocateFrom(ValueLayout.JAVA_CHAR, nameChars);
MemorySegment capturedState = arena.allocate(captureLayout);
final long INVALID_HANDLE_VALUE = -1L;
long hPipe = (long) CreateNamedPipeW.invokeExact(
capturedState,
pipeWinPath,
0x00000003, // dwOpenMode = PIPE_ACCESS_DUPLEX
0x00000000, // dwPipeMode = PIPE_TYPE_BYTE
1, // nMaxInstances. Limit to 1 to force ERROR_PIPE_BUSY.
1024, // nOutBufferSize
1024, // nInBufferSize
0, // nDefaultTimeOut
MemorySegment.NULL // lpSecurityAttributes
);
if (hPipe == INVALID_HANDLE_VALUE) {
int errorCode = (int) GetLastError.get(capturedState);
throw new Exception("CreateNamedPipeW failed: " + errorCode);
}
AtomicBoolean keepRunning = new AtomicBoolean(true);
Thread serverThread = new Thread(() -> {
// This server accepts a connection and does nothing until the client disconnects explicitly.
try {
int i = 0;
while (keepRunning.get()) {
int connected = (int) ConnectNamedPipe.invokeExact(capturedState, hPipe, MemorySegment.NULL);
if (connected == 0) {
int errorCode = (int) GetLastError.get(capturedState);
// https://learn.microsoft.com/en-us/windows/win32/debug/system-error-codes--0-499-
if (errorCode == 6 && !keepRunning.get()) { // ERROR_INVALID_HANDLE
break;
}
throw new Exception("ConnectNamedPipe failed: " + errorCode);
}
try {
int peekResult;
do {
// Random timeout. The timeout must be big enough to reveal possible consequent
// attempts to connect to the pipe.
Thread.sleep(1000);
// Check if the pipe is still connected by peeking at it.
peekResult = (int) PeekNamedPipe.invokeExact(hPipe, MemorySegment.NULL, 0,
MemorySegment.NULL, MemorySegment.NULL, MemorySegment.NULL);
}
while (keepRunning.get() && peekResult != 0);
} finally {
int disconnected = (int) DisconnectNamedPipe.invokeExact(hPipe);
}
}
} catch (Throwable e) {
e.printStackTrace();
}
});
serverThread.setDaemon(true);
serverThread.start();
try {
new RandomAccessFile(pipeName, "rw").close();
} finally {
keepRunning.set(false);
int closed = (int) CloseHandle.invokeExact(hPipe);
}
}
}