Compare commits

...

13 Commits

Author SHA1 Message Date
Vladimir Lagunov
860020a1fe wip: catch security exceptions 2025-02-04 13:59:31 +01:00
Vladimir Lagunov
ae2e0a1773 wip: fix JFileChooser test 2025-02-04 13:24:33 +01:00
Vladimir Lagunov
ff8e67c141 wip: mimic error messages 2025-02-03 20:22:35 +01:00
Vladimir Lagunov
c053250dbd wip 2025-01-31 12:36:52 +01:00
Vladimir Lagunov
5a1f42627b wip: File.exists 2025-01-31 12:36:52 +01:00
Vladimir Lagunov
a8500e1357 wip: trace logs 2025-01-31 12:36:51 +01:00
Vladimir Lagunov
d7931c14b8 wip 2025-01-30 20:47:14 +01:00
Vladimir Lagunov
07a86f7646 wip: fixed bugs in path canonicalization 2025-01-30 19:41:25 +01:00
Vladimir Lagunov
878e4e61ed wip: debug messages 2025-01-29 17:01:35 +01:00
Vladimir Lagunov
289a58f1f2 wip: file replace 2025-01-29 16:55:48 +01:00
Vladimir Lagunov
1372f86a0c wip: java.io over java.nio 2025-01-29 16:15:57 +01:00
Vladimir Lagunov
ee9eab7621 Don't throw ArrayIndexOutOfBoundsException from FileSystemProvider.newByteChannel on Unix
Due to a missed check, a call of `newByteChannel(Path.of(""), EnumSet.of(WRITE, CREATE_NEW))` led to `ArrayIndexOutOfBoundsException`, because there was no check for an empty path.

Now this code throws `FileAlreadyExists`, as it happens for other corner cases like `Path.of(".")"`
2025-01-29 16:15:09 +01:00
Vladimir Lagunov
d5db58c8e3 tmp delete me 2025-01-29 16:14:51 +01:00
35 changed files with 4025 additions and 73 deletions

View File

@@ -1052,6 +1052,7 @@ void InstanceKlass::clean_initialization_error_table() {
}
};
assert_locked_or_safepoint(ClassInitError_lock);
InitErrorTableCleaner cleaner;
if (_initialization_error_table != nullptr) {

View File

@@ -0,0 +1,86 @@
/*
* Copyright 2025 JetBrains s.r.o.
* 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* 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.
*/
package com.sun; // TODO better package
import jdk.internal.misc.VM;
import sun.security.action.GetPropertyAction;
public class IoOverNio {
private IoOverNio() { }
public static final ThreadLocal<Boolean> ALLOW_IO_OVER_NIO = ThreadLocal.withInitial(() -> true);
public enum Debug {
NO(false, false),
ERROR(true, false),
NO_ERROR(false, true),
ALL(true, true);
private final boolean writeErrors;
private final boolean writeTraces;
Debug(boolean writeErrors, boolean writeTraces) {
this.writeErrors = writeErrors;
this.writeTraces = writeTraces;
}
private boolean mayWriteAnything() {
return VM.isBooted() && IoOverNio.ALLOW_IO_OVER_NIO.get();
}
public boolean writeErrors() {
return writeErrors && mayWriteAnything();
}
public boolean writeTraces() {
return writeTraces && mayWriteAnything();
}
}
public static final Debug DEBUG;
static {
String value = GetPropertyAction.privilegedGetProperty("jbr.java.io.use.nio.debug");
if (value == null) {
DEBUG = Debug.NO;
} else {
switch (value) {
case "error":
DEBUG = Debug.ERROR;
break;
case "no_error":
DEBUG = Debug.NO_ERROR;
break;
case "all":
DEBUG = Debug.ALL;
break;
default:
DEBUG = Debug.NO;
break;
}
}
}
}

View File

@@ -35,7 +35,10 @@ import java.nio.file.Path;
import java.security.SecureRandom;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Supplier;
import jdk.internal.util.StaticProperty;
import sun.security.action.GetPropertyAction;
/**
* An abstract representation of file and directory pathnames.
@@ -148,11 +151,23 @@ import jdk.internal.util.StaticProperty;
public class File
implements Serializable, Comparable<File>
{
static final Supplier<java.nio.file.FileSystem> acquireNioFs;
/**
* The FileSystem object representing the platform's local file system.
*/
private static final FileSystem FS = DefaultFileSystem.getFileSystem();
private static final FileSystem FS;
static {
if (GetPropertyAction.privilegedGetBooleanProp("jbr.java.io.use.nio", true, null)) {
IoOverNioFileSystem ioOverNio = new IoOverNioFileSystem(DefaultFileSystem.getFileSystem());
FS = ioOverNio;
acquireNioFs = ioOverNio::acquireNioFs;
} else {
FS = DefaultFileSystem.getFileSystem();
acquireNioFs = () -> null;
}
}
/**
* This abstract pathname's normalized pathname string. A normalized

View File

@@ -25,12 +25,21 @@
package java.io;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;
import java.util.Arrays;
import java.util.Set;
import jdk.internal.misc.Blocker;
import jdk.internal.util.ArraysSupport;
import sun.nio.ch.FileChannelImpl;
import static com.sun.IoOverNio.DEBUG;
/**
* A {@code FileInputStream} obtains input bytes
* from a file in a file system. What files
@@ -75,6 +84,8 @@ public class FileInputStream extends InputStream
private volatile boolean closed;
private final boolean useNio;
/**
* Creates a {@code FileInputStream} by
* opening a connection to an actual file,
@@ -146,11 +157,46 @@ public class FileInputStream extends InputStream
if (file.isInvalid()) {
throw new FileNotFoundException("Invalid file path");
}
fd = new FileDescriptor();
fd.attach(this);
path = name;
open(name);
FileCleanable.register(fd); // open set the fd, register the cleanup
java.nio.file.FileSystem nioFs = File.acquireNioFs.get();
useNio = nioFs != null;
if (useNio) {
Path nioPath = nioFs.getPath(name);
if (Files.isDirectory(nioPath)) {
// Unfortunately, java.nio allows opening directories as file channels, and there's no way
// to determine if an opened nio channel belongs to a directory.
throw new FileNotFoundException(name + " (Is a directory)");
}
try {
// NB: the channel will be closed in the close() method
// TODO Handle UnsupportedOperationException from newFileChannel
var ch = nioFs.provider().newFileChannel(nioPath, Set.of(StandardOpenOption.READ));
channel = ch;
if (ch instanceof FileChannelImpl fci) {
fci.setUninterruptible();
fd = fci.getFD(); // TODO: this is a temporary workaround
fd.attach(this);
FileCleanable.register(fd);
} else {
fd = new FileDescriptor();
}
} catch (IOException e) {
if (DEBUG.writeErrors()) {
new Throwable(String.format("Can't create a FileInputStream for %s with %s", file, nioFs), e)
.printStackTrace(System.err);
}
throw IoOverNioFileSystem.convertNioToIoExceptionInStreams(e);
}
} else {
fd = new FileDescriptor();
fd.attach(this);
open(name);
FileCleanable.register(fd); // open set the fd, register the cleanup
}
if (DEBUG.writeTraces()) {
System.err.printf("Created a FileInputStream for %s%n", file);
}
}
/**
@@ -178,6 +224,8 @@ public class FileInputStream extends InputStream
* @see SecurityManager#checkRead(java.io.FileDescriptor)
*/
public FileInputStream(FileDescriptor fdObj) {
useNio = false;
@SuppressWarnings("removal")
SecurityManager security = System.getSecurityManager();
if (fdObj == null) {
@@ -228,7 +276,7 @@ public class FileInputStream extends InputStream
public int read() throws IOException {
long comp = Blocker.begin();
try {
return read0();
return implRead();
} finally {
Blocker.end(comp);
}
@@ -236,6 +284,16 @@ public class FileInputStream extends InputStream
private native int read0() throws IOException;
private int implRead() throws IOException {
if (useNio) {
ByteBuffer buffer = ByteBuffer.allocate(1);
int nRead = getChannel().read(buffer);
buffer.rewind();
return nRead == 1 ? (buffer.get() & 0xFF) : -1;
}
return read0();
}
/**
* Reads a subarray as a sequence of bytes.
* @param b the data to be written
@@ -260,12 +318,25 @@ public class FileInputStream extends InputStream
public int read(byte[] b) throws IOException {
long comp = Blocker.begin();
try {
return readBytes(b, 0, b.length);
return implRead(b);
} finally {
Blocker.end(comp);
}
}
private int implRead(byte[] b) throws IOException {
if (useNio) {
try {
ByteBuffer buffer = ByteBuffer.wrap(b);
return getChannel().read(buffer);
} catch (OutOfMemoryError e) {
// May fail to allocate direct buffer memory due to small -XX:MaxDirectMemorySize
return readBytes(b, 0, b.length);
}
}
return readBytes(b, 0, b.length);
}
/**
* Reads up to {@code len} bytes of data from this input stream
* into an array of bytes. If {@code len} is not zero, the method
@@ -284,12 +355,25 @@ public class FileInputStream extends InputStream
public int read(byte[] b, int off, int len) throws IOException {
long comp = Blocker.begin();
try {
return readBytes(b, off, len);
return implRead(b, off, len);
} finally {
Blocker.end(comp);
}
}
private int implRead(byte[] b, int off, int len) throws IOException {
if (useNio) {
try {
ByteBuffer buffer = ByteBuffer.wrap(b, off, len);
return getChannel().read(buffer);
} catch (OutOfMemoryError e) {
// May fail to allocate direct buffer memory due to small -XX:MaxDirectMemorySize
return readBytes(b, off, len);
}
}
return readBytes(b, off, len);
}
@Override
public byte[] readAllBytes() throws IOException {
long length = length();
@@ -396,6 +480,9 @@ public class FileInputStream extends InputStream
private long length() throws IOException {
long comp = Blocker.begin();
try {
if (useNio) {
return getChannel().size();
}
return length0();
} finally {
Blocker.end(comp);
@@ -406,6 +493,9 @@ public class FileInputStream extends InputStream
private long position() throws IOException {
long comp = Blocker.begin();
try {
if (useNio) {
return getChannel().position();
}
return position0();
} finally {
Blocker.end(comp);
@@ -441,6 +531,12 @@ public class FileInputStream extends InputStream
public long skip(long n) throws IOException {
long comp = Blocker.begin();
try {
if (useNio) {
getChannel();
long startPos = channel.position();
channel.position(startPos + n);
return channel.position() - startPos;
}
return skip0(n);
} finally {
Blocker.end(comp);
@@ -470,7 +566,15 @@ public class FileInputStream extends InputStream
public int available() throws IOException {
long comp = Blocker.begin();
try {
return available0();
if (!useNio || path == null) {
return available0();
} else {
FileChannel channel = getChannel();
long size = channel.size();
long pos = channel.position();
long avail = size > pos ? (size - pos) : 0;
return avail > Integer.MAX_VALUE ? Integer.MAX_VALUE : (int)avail;
}
} finally {
Blocker.end(comp);
}

View File

@@ -25,11 +25,19 @@
package java.io;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;
import java.util.Set;
import jdk.internal.access.SharedSecrets;
import jdk.internal.access.JavaIOFileDescriptorAccess;
import jdk.internal.misc.Blocker;
import sun.nio.ch.FileChannelImpl;
import static com.sun.IoOverNio.DEBUG;
/**
@@ -89,6 +97,8 @@ public class FileOutputStream extends OutputStream
private volatile boolean closed;
private final boolean useNio;
/**
* Creates a file output stream to write to the file with the
* specified name. A new {@code FileDescriptor} object is
@@ -223,12 +233,48 @@ public class FileOutputStream extends OutputStream
if (file.isInvalid()) {
throw new FileNotFoundException("Invalid file path");
}
this.fd = new FileDescriptor();
fd.attach(this);
this.path = name;
open(name, append);
FileCleanable.register(fd); // open sets the fd, register the cleanup
this.path = name;
java.nio.file.FileSystem nioFs = File.acquireNioFs.get();
useNio = nioFs != null;
if (useNio) {
Path nioPath = nioFs.getPath(name);
if (Files.isDirectory(nioPath)) {
throw new FileNotFoundException(name + " (Is a directory)");
}
try {
// NB: the channel will be closed in the close() method
// TODO Handle UOE
var ch = FileSystems.getDefault().provider().newFileChannel(
nioPath,
append ? Set.of(StandardOpenOption.WRITE, StandardOpenOption.CREATE, StandardOpenOption.APPEND)
: Set.of(StandardOpenOption.WRITE, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING));
channel = ch;
if (ch instanceof FileChannelImpl fci) {
fci.setUninterruptible();
fd = fci.getFD(); // TODO: this is a temporary workaround
fd.attach(this);
FileCleanable.register(fd);
} else {
fd = new FileDescriptor();
}
} catch (IOException e) {
if (DEBUG.writeErrors()) {
new Throwable(String.format("Can't create a FileOutputStream for %s with %s", file, nioFs), e)
.printStackTrace(System.err);
}
// Since we can't throw IOException...
throw IoOverNioFileSystem.convertNioToIoExceptionInStreams(e);
}
} else {
this.fd = new FileDescriptor();
fd.attach(this);
open(name, append);
FileCleanable.register(fd); // open sets the fd, register the cleanup
}
if (DEBUG.writeTraces()) {
System.err.printf("Created a FileOutputStream for %s%n", file);
}
}
/**
@@ -255,6 +301,8 @@ public class FileOutputStream extends OutputStream
* @see java.lang.SecurityManager#checkWrite(java.io.FileDescriptor)
*/
public FileOutputStream(FileDescriptor fdObj) {
useNio = false;
@SuppressWarnings("removal")
SecurityManager security = System.getSecurityManager();
if (fdObj == null) {
@@ -313,12 +361,24 @@ public class FileOutputStream extends OutputStream
boolean append = FD_ACCESS.getAppend(fd);
long comp = Blocker.begin();
try {
write(b, append);
implWrite(b, append);
} finally {
Blocker.end(comp);
}
}
private void implWrite(int b, boolean append) throws IOException {
if (useNio) {
// 'append' is ignored; the channel is supposed to obey the mode in which the file was opened
byte[] array = new byte[1];
array[0] = (byte) b;
ByteBuffer buffer = ByteBuffer.wrap(array);
getChannel().write(buffer);
} else {
write(b, append);
}
}
/**
* Writes a sub array as a sequence of bytes.
* @param b the data to be written
@@ -343,7 +403,7 @@ public class FileOutputStream extends OutputStream
boolean append = FD_ACCESS.getAppend(fd);
long comp = Blocker.begin();
try {
writeBytes(b, 0, b.length, append);
implWriteBytes(b, 0, b.length, append);
} finally {
Blocker.end(comp);
}
@@ -364,12 +424,27 @@ public class FileOutputStream extends OutputStream
boolean append = FD_ACCESS.getAppend(fd);
long comp = Blocker.begin();
try {
writeBytes(b, off, len, append);
implWriteBytes(b, off, len, append);
} finally {
Blocker.end(comp);
}
}
private void implWriteBytes(byte[] b, int off, int len, boolean append) throws IOException {
if (useNio) {
// 'append' is ignored; the channel is supposed to obey the mode in which the file was opened
try {
ByteBuffer buffer = ByteBuffer.wrap(b, off, len);
getChannel().write(buffer);
} catch (OutOfMemoryError e) {
// May fail to allocate direct buffer memory due to small -XX:MaxDirectMemorySize
writeBytes(b, off, len, append);
}
} else {
writeBytes(b, off, len, append);
}
}
/**
* Closes this file output stream and releases any system resources
* associated with this stream. This file output stream may no longer

View File

@@ -0,0 +1,970 @@
/*
* Copyright 2025 JetBrains s.r.o.
* 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* 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.
*/
package java.io;
import com.sun.IoOverNio;
import jdk.internal.misc.VM;
import java.nio.file.*;
import java.nio.file.attribute.*;
import java.security.AccessControlException;
import java.util.*;
import static com.sun.IoOverNio.DEBUG;
class IoOverNioFileSystem extends FileSystem {
private final FileSystem defaultFileSystem;
IoOverNioFileSystem(FileSystem defaultFileSystem) {
this.defaultFileSystem = defaultFileSystem;
}
java.nio.file.FileSystem acquireNioFs() {
if (!VM.isBooted()) {
return null;
}
if (!IoOverNio.ALLOW_IO_OVER_NIO.get()) {
return null;
}
try {
return FileSystems.getDefault();
} catch (NullPointerException ignored) {
// TODO Explain.
// TODO Probably, it can be removed now.
}
return null;
}
static FileNotFoundException convertNioToIoExceptionInStreams(IOException source) {
String message = convertNioToIoExceptionMessage(source);
if (source instanceof FileSystemException s && s.getFile() != null) {
message = s.getFile() + " (" + message + ")";
}
FileNotFoundException result = new FileNotFoundException(message);
result.initCause(source);
return result;
}
private static IOException convertNioToIoExceptionInFile(IOException source) {
return new IOException(convertNioToIoExceptionMessage(source), source);
}
private static String convertNioToIoExceptionMessage(IOException source) {
return switch (source) {
case AccessDeniedException s -> "Permission denied";
case NotDirectoryException s -> "Not a directory";
case NoSuchFileException s -> "No such file or directory";
case FileSystemLoopException s -> "Too many levels of symbolic links";
case FileSystemException s -> {
String message = source.getMessage();
String expectedPrefix = s.getFile() + ": ";
if (message.startsWith(expectedPrefix)) {
message = message.substring(expectedPrefix.length());
if (message.equals("Too many levels of symbolic links or unable to access attributes of symbolic link")) {
yield "Too many levels of symbolic links";
}
}
yield message;
}
default -> source.getMessage();
};
}
private static boolean setPermission0(java.nio.file.FileSystem nioFs, File f, int access, boolean enable, boolean owneronly) {
Path path;
try {
path = nioFs.getPath(f.getPath());
} catch (InvalidPathException err) {
throw new InternalError(err.getMessage(), err);
}
// See the comment inside getBooleanAttributes that explains the order of file attribute view classes.
BasicFileAttributeView view;
try {
view = Files.getFileAttributeView(path, PosixFileAttributeView.class);
} catch (UnsupportedOperationException ignored) {
view = null;
}
if (view == null) {
try {
view = Files.getFileAttributeView(path, DosFileAttributeView.class);
} catch (UnsupportedOperationException err) {
if (DEBUG.writeErrors()) {
new Throwable(String.format("File system %s supports neither posix nor dos attributes", nioFs), err)
.printStackTrace(System.err);
}
return false;
}
}
boolean result = true;
if (view instanceof DosFileAttributeView dosView) {
if (((access & ACCESS_READ) != 0)) {
try {
dosView.setReadOnly(enable);
} catch (IOException e) {
if (DEBUG.writeErrors()) {
new Throwable(String.format("Can't set read only attributes for %s", f), e)
.printStackTrace(System.err);
}
result = false;
}
}
} else if (view instanceof PosixFileAttributeView posixView) {
try {
Set<PosixFilePermission> perms = posixView.readAttributes().permissions();
if ((access & ACCESS_READ) != 0) {
if (enable) {
perms.add(PosixFilePermission.OWNER_READ);
} else {
perms.remove(PosixFilePermission.OWNER_READ);
}
if (!owneronly) {
if (enable) {
perms.add(PosixFilePermission.GROUP_READ);
perms.add(PosixFilePermission.OTHERS_READ);
} else {
perms.remove(PosixFilePermission.GROUP_READ);
perms.remove(PosixFilePermission.OTHERS_READ);
}
}
}
if ((access & ACCESS_WRITE) != 0) {
if (enable) {
perms.add(PosixFilePermission.OWNER_WRITE);
} else {
perms.remove(PosixFilePermission.OWNER_WRITE);
}
if (!owneronly) {
if (enable) {
perms.add(PosixFilePermission.GROUP_WRITE);
perms.add(PosixFilePermission.OTHERS_WRITE);
} else {
perms.remove(PosixFilePermission.GROUP_WRITE);
perms.remove(PosixFilePermission.OTHERS_WRITE);
}
}
}
if ((access & ACCESS_EXECUTE) != 0) {
if (enable) {
perms.add(PosixFilePermission.OWNER_EXECUTE);
} else {
perms.remove(PosixFilePermission.OWNER_EXECUTE);
}
if (!owneronly) {
if (enable) {
perms.add(PosixFilePermission.GROUP_EXECUTE);
perms.add(PosixFilePermission.OTHERS_EXECUTE);
} else {
perms.remove(PosixFilePermission.GROUP_EXECUTE);
perms.remove(PosixFilePermission.OTHERS_EXECUTE);
}
}
}
posixView.setPermissions(perms);
} catch (IOException e) {
if (DEBUG.writeErrors()) {
new Throwable(String.format("Can't set posix attributes for %s", f), e)
.printStackTrace(System.err);
}
result = false;
}
}
return result;
}
@Override
public char getSeparator() {
return defaultFileSystem.getSeparator();
}
@Override
public char getPathSeparator() {
return defaultFileSystem.getPathSeparator();
}
@Override
public String normalize(String path) {
// java.nio.file.Path.normalize works differently compared to this normalizer.
// Especially, Path.normalize converts "." into an empty string, which breaks
// even tests in OpenJDK.
return defaultFileSystem.normalize(path);
}
@Override
public int prefixLength(String path) {
// Although it's possible to represent this function via `Path.getRoot`,
// the default file system and java.nio handle corner cases with incorrect paths differently.
return defaultFileSystem.prefixLength(path);
}
@Override
public String resolve(String parent, String child) {
// java.nio is stricter to various invalid symbols than java.io.
return defaultFileSystem.resolve(parent, child);
}
@Override
public String getDefaultParent() {
java.nio.file.FileSystem nioFs = acquireNioFs();
if (nioFs != null) {
return nioFs.getSeparator();
}
return defaultFileSystem.getDefaultParent();
}
@Override
public String fromURIPath(String path) {
return defaultFileSystem.fromURIPath(path);
}
@Override
public boolean isAbsolute(File f) {
boolean result = isAbsolute0(f);
if (DEBUG.writeTraces()) {
System.err.printf("IoOverNioFileSystem.isAbsolute(%s) = %b%n", f, result);
}
return result;
}
private boolean isAbsolute0(File f) {
java.nio.file.FileSystem nioFs = acquireNioFs();
if (nioFs != null) {
try {
return nioFs.getPath(f.getPath()).isAbsolute();
} catch (InvalidPathException err) {
// Path parsing in java.nio is stricter than in java.io.
// Giving a chance to the original implementation.
if (DEBUG.writeErrors()) {
new Throwable(String.format("Can't check if a path %s is absolute", f), err)
.printStackTrace(System.err);
}
}
}
return defaultFileSystem.isAbsolute(f);
}
@Override
public boolean isInvalid(File f) {
java.nio.file.FileSystem nioFs = acquireNioFs();
if (nioFs != null) {
try {
Path ignored = nioFs.getPath(f.getPath());
return false;
} catch (InvalidPathException ignored) {
return true;
}
}
return defaultFileSystem.isInvalid(f);
}
@Override
public String resolve(File f) {
try {
String result = resolve0(f);
if (DEBUG.writeTraces()) {
System.err.printf("IoOverNioFileSystem.resolve(%s) = %s%n", f, result);
}
return result;
} catch (RuntimeException err) {
if (DEBUG.writeErrors()) {
new Throwable(String.format("IoOverNioFileSystem.resolve(%s) threw an error%n", f), err)
.printStackTrace(System.err);
}
throw err;
}
}
private String resolve0(File f) {
java.nio.file.FileSystem nioFs = acquireNioFs();
if (nioFs != null) {
try {
return nioFs.getPath(f.getPath()).toAbsolutePath().toString();
} catch (InvalidPathException err) {
// Path parsing in java.nio is stricter than in java.io.
// Giving a chance to the original implementation.
if (DEBUG.writeErrors()) {
new Throwable(String.format("Can't resolve a path %s", f), err)
.printStackTrace(System.err);
}
}
}
return defaultFileSystem.resolve(f);
}
@Override
public String canonicalize(String path) throws IOException {
try {
String result = canonicalize0(path);
if (DEBUG.writeTraces()) {
System.err.printf("IoOverNioFileSystem.canonicalize(%s) = %s%n", path, result);
}
return result;
} catch (RuntimeException err) {
if (DEBUG.writeErrors()) {
new Throwable(String.format("IoOverNioFileSystem.canonicalize(%s) threw an error%n", path), err)
.printStackTrace(System.err);
}
throw err;
}
}
private String canonicalize0(String path) throws IOException {
java.nio.file.FileSystem nioFs = acquireNioFs();
if (nioFs != null) {
try {
Path nioPath = nioFs.getPath(path);
if (!nioPath.isAbsolute()) {
nioPath = Path.of(System.getProperty("user.dir")).resolve(nioPath);
}
Path suffix = nioFs.getPath("");
while (!nioPath.equals(nioPath.getRoot())) {
Path parent = nioPath.getParent();
if (parent == null) {
parent = nioPath.getRoot();
}
String fileNameStr = nioPath.getFileName().toString();
if (!fileNameStr.isEmpty() && !fileNameStr.equals(".")) {
try {
nioPath = nioPath.toRealPath();
break;
} catch (IOException err) {
// Nothing.
}
suffix = nioPath.getFileName().resolve(suffix);
}
nioPath = parent;
}
return nioPath.resolve(suffix).normalize().toString();
} catch (InvalidPathException err) {
// Path parsing in java.nio is stricter than in java.io.
// Giving a chance to the original implementation.
if (DEBUG.writeErrors()) {
new Throwable(String.format("Can't canonicalize a path %s", path), err)
.printStackTrace(System.err);
}
}
}
return defaultFileSystem.canonicalize(path);
}
@Override
public boolean hasBooleanAttributes(File f, int attributes) {
try {
boolean result = hasBooleanAttributes0(f, attributes);
if (DEBUG.writeTraces()) {
System.err.printf("IoOverNioFileSystem.hasBooleanAttributes(%s, %s) = %b%n", f, attributes, result);
}
return result;
} catch (RuntimeException err) {
if (DEBUG.writeErrors()) {
new Throwable(String.format("IoOverNioFileSystem.hasBooleanAttributes(%s, %s) threw an error", f, attributes), err)
.printStackTrace(System.err);
}
throw err;
}
}
private boolean hasBooleanAttributes0(File f, int attributes) {
if (acquireNioFs() != null) {
return (getBooleanAttributes0(f) & attributes) == attributes;
}
return defaultFileSystem.hasBooleanAttributes(f, attributes);
}
@Override
public int getBooleanAttributes(File f) {
int result = getBooleanAttributes0(f);
if (DEBUG.writeTraces()) {
System.err.printf("IoOverNioFileSystem.getBooleanAttributes(%s) = %d%n", f, result);
}
return result;
}
private int getBooleanAttributes0(File f) {
java.nio.file.FileSystem nioFs = acquireNioFs();
if (nioFs != null) {
try {
Path path = nioFs.getPath(f.getPath());
if (path.getFileName() == null || path.getFileName().toString().isEmpty()) {
// `stat` returns errors for such calls.
return 0;
}
// The order of checking Posix attributes first and DOS attributes next is deliberate.
// WindowsFileSystem supports these attributes: basic, dos, acl, owner, user.
// LinuxFileSystem supports: basic, posix, unix, owner, dos, user.
// MacOSFileSystem and BsdFileSystem support: basic, posix, unix, owner, user.
//
// Notice that Linux FS supports DOS attributes, which is unexpected.
// The DOS adapter implementation from LinuxFileSystem uses `open` for getting attributes.
// It's not only more expensive than `stat`, but also doesn't work with Unix sockets.
//
// Also, notice that Windows FS does not support Posix attributes, which is expected.
// Checking for Posix attributes allows filtering Windows out,
// even though Posix permissions aren't used in this method.
BasicFileAttributes attrs;
try {
attrs = Files.readAttributes(path, PosixFileAttributes.class);
} catch (UnsupportedOperationException ignored) {
attrs = Files.readAttributes(path, DosFileAttributes.class);
}
return BA_EXISTS
| (attrs.isDirectory() ? BA_DIRECTORY : 0)
| (attrs.isRegularFile() ? BA_REGULAR : 0)
| (attrs instanceof DosFileAttributes dosAttrs && dosAttrs.isHidden() ? BA_HIDDEN : 0);
} catch (InvalidPathException err) {
// Path parsing in java.nio is stricter than in java.io.
// Giving a chance to the original implementation.
if (DEBUG.writeErrors()) {
new Throwable(String.format("Can't get attributes for a path %s", f), err)
.printStackTrace(System.err);
}
} catch (@SuppressWarnings("removal") IOException | AccessControlException e) {
if (DEBUG.writeErrors()) {
new Throwable(String.format("Can't get attributes for a path %s", f), e)
.printStackTrace(System.err);
}
return 0;
}
}
return defaultFileSystem.getBooleanAttributes(f);
}
@Override
public boolean checkAccess(File f, int access) {
boolean result = checkAccess0(f, access);
if (DEBUG.writeTraces()) {
System.err.printf("IoOverNioFileSystem.checkAccess(%s, %d) = %b%n", f, access, result);
}
return result;
}
private boolean checkAccess0(File f, int access) {
java.nio.file.FileSystem nioFs = acquireNioFs();
if (nioFs != null) {
try {
Path path = nioFs.getPath(f.getPath());
int i = 0;
AccessMode[] accessMode = new AccessMode[3];
if ((access & ACCESS_READ) != 0) {
accessMode[i++] = AccessMode.READ;
}
if ((access & ACCESS_WRITE) != 0) {
accessMode[i++] = AccessMode.WRITE;
}
if ((access & ACCESS_EXECUTE) != 0) {
accessMode[i++] = AccessMode.EXECUTE;
}
accessMode = Arrays.copyOf(accessMode, i);
nioFs.provider().checkAccess(path, accessMode);
return true;
} catch (@SuppressWarnings("removal") IOException | AccessControlException e) {
if (DEBUG.writeErrors()) {
new Throwable(String.format("Can't check access for a path %s", f), e)
.printStackTrace(System.err);
}
return false;
} catch (InvalidPathException err) {
throw new InternalError(err.getMessage(), err);
}
}
return defaultFileSystem.checkAccess(f, access);
}
@Override
public boolean setPermission(File f, int access, boolean enable, boolean owneronly) {
boolean result = setPermission0(f, access, enable, owneronly);
if (DEBUG.writeTraces()) {
System.err.printf("IoOverNioFileSystem.setPermission(%s, %d, %b, %b) = %b%n", f, access, enable, owneronly, result);
}
return result;
}
private boolean setPermission0(File f, int access, boolean enable, boolean owneronly) {
java.nio.file.FileSystem nioFs = acquireNioFs();
if (nioFs != null) {
return setPermission0(nioFs, f, access, enable, owneronly);
}
return defaultFileSystem.setPermission(f, access, enable, owneronly);
}
@Override
public long getLastModifiedTime(File f) {
try {
long result = getLastModifiedTime0(f);
if (DEBUG.writeTraces()) {
System.err.printf("IoOverNioFileSystem.getLastModifiedTime(%s) = %d%n", f, result);
}
return result;
} catch (RuntimeException err) {
if (DEBUG.writeErrors()) {
new Throwable(String.format("IoOverNioFileSystem.getLastModifiedTime(%s) threw an error", f), err)
.printStackTrace(System.err);
}
throw err;
}
}
private long getLastModifiedTime0(File f) {
java.nio.file.FileSystem nioFs = acquireNioFs();
if (nioFs != null) {
try {
Path path = nioFs.getPath(f.getPath());
nioFs.provider().readAttributes(path, BasicFileAttributes.class).lastModifiedTime();
} catch (IOException err) {
if (DEBUG.writeErrors()) {
new Throwable(String.format("Can't get last modified time for a path %s", f), err)
.printStackTrace(System.err);
}
return 0;
} catch (InvalidPathException err) {
throw new InternalError(err.getMessage(), err);
}
}
return defaultFileSystem.getLastModifiedTime(f);
}
@Override
public long getLength(File f) {
try {
long result = getLength0(f);
if (DEBUG.writeTraces()) {
System.err.printf("IoOverNioFileSystem.getLength(%s) = %d%n", f, result);
}
return result;
} catch (RuntimeException err) {
if (DEBUG.writeErrors()) {
new Throwable(String.format("IoOverNioFileSystem.getLength(%s) threw an error%n", f), err)
.printStackTrace(System.err);
}
throw err;
}
}
private long getLength0(File f) {
java.nio.file.FileSystem nioFs = acquireNioFs();
try {
if (nioFs != null) {
Path path = nioFs.getPath(f.getPath());
return nioFs.provider().readAttributes(path, BasicFileAttributes.class).size();
}
} catch (IOException e) {
if (DEBUG.writeErrors()) {
new Throwable(String.format("Can't get file length for a path %s", f), e)
.printStackTrace(System.err);
}
return 0;
} catch (InvalidPathException e) {
throw new InternalError(e.getMessage(), e);
}
return defaultFileSystem.getLength(f);
}
@Override
public boolean createFileExclusively(String pathname) throws IOException {
try {
boolean result = createFileExclusively0(pathname);
if (DEBUG.writeTraces()) {
System.err.printf("IoOverNioFileSystem.createFileExclusively(%s) = %b%n", pathname, result);
}
return result;
} catch (IOException err) {
if (DEBUG.writeErrors()) {
new Throwable(String.format("IoOverNioFileSystem.createFileExclusively(%s) threw an error", pathname), err)
.printStackTrace(System.err);
}
throw err;
}
}
private boolean createFileExclusively0(String pathname) throws IOException {
java.nio.file.FileSystem nioFs = acquireNioFs();
try {
if (nioFs != null) {
Path path = nioFs.getPath(pathname);
nioFs.provider().newByteChannel(
path,
EnumSet.of(StandardOpenOption.CREATE_NEW, StandardOpenOption.WRITE)
).close();
return true;
}
} catch (FileAlreadyExistsException e) {
if (DEBUG.writeErrors()) {
new Throwable(String.format("Can't exclusively create a file %s", pathname), e)
.printStackTrace(System.err);
}
return false;
} catch (InvalidPathException e) {
throw new IOException(e.getMessage(), e); // The default file system would throw IOException too.
} catch (IOException e) {
throw convertNioToIoExceptionInFile(e);
}
return defaultFileSystem.createFileExclusively(pathname);
}
@Override
public boolean delete(File f) {
try {
boolean result = delete0(f);
if (DEBUG.writeTraces()) {
System.err.printf("IoOverNioFileSystem.delete(%s) = %b%n", f, result);
}
return result;
} catch (RuntimeException err) {
if (DEBUG.writeErrors()) {
new Throwable(String.format("IoOverNioFileSystem.delete(%s) threw an error", f), err)
.printStackTrace(System.err);
}
throw err;
}
}
private boolean delete0(File f) {
java.nio.file.FileSystem nioFs = acquireNioFs();
if (nioFs != null) {
try {
Path path = nioFs.getPath(f.getPath());
nioFs.provider().delete(path);
return true;
} catch (InvalidPathException e) {
throw new InternalError(e.getMessage(), e);
} catch (IOException e) {
if (DEBUG.writeErrors()) {
new Throwable(String.format("Can't delete a path %s", f), e)
.printStackTrace(System.err);
}
return false;
}
}
return defaultFileSystem.delete(f);
}
@Override
public String[] list(File f) {
try {
String[] result = list0(f);
if (DEBUG.writeTraces()) {
System.err.printf("IoOverNioFileSystem.list(%s) = %s%n", f, Arrays.toString(result));
}
return result;
} catch (RuntimeException err) {
if (DEBUG.writeErrors()) {
new Throwable(String.format("IoOverNioFileSystem.list(%s) threw an error", f), err)
.printStackTrace(System.err);
}
throw err;
}
}
private String[] list0(File f) {
java.nio.file.FileSystem nioFs = acquireNioFs();
if (nioFs != null) {
try {
Path path = nioFs.getPath(f.getPath());
try (DirectoryStream<Path> children = nioFs.provider().newDirectoryStream(path, AcceptAllFilter.FILTER)) {
List<String> result = new ArrayList<>();
for (Path child : children) {
result.add(child.getFileName().toString());
}
return result.toArray(String[]::new);
}
} catch (InvalidPathException e) {
throw new InternalError(e.getMessage(), e);
} catch (IOException e) {
if (DEBUG.writeErrors()) {
new Throwable(String.format("Can't list a path %s", f), e)
.printStackTrace(System.err);
}
return null;
}
}
return defaultFileSystem.list(f);
}
@Override
public boolean createDirectory(File f) {
try {
boolean result = createDirectory0(f);
if (DEBUG.writeTraces()) {
System.err.printf("IoOverNioFileSystem.createDirectory(%s) = %b%n", f, result);
}
return result;
} catch (RuntimeException err) {
if (DEBUG.writeErrors()) {
new Throwable(String.format("IoOverNioFileSystem.createDirectory(%s) threw an error", f), err)
.printStackTrace(System.err);
}
throw err;
}
}
private boolean createDirectory0(File f) {
java.nio.file.FileSystem nioFs = acquireNioFs();
if (nioFs != null) {
try {
Path path = nioFs.getPath(f.getPath());
nioFs.provider().createDirectory(path);
return true;
} catch (InvalidPathException e) {
throw new InternalError(e.getMessage(), e);
} catch (IOException e) {
if (DEBUG.writeErrors()) {
new Throwable(String.format("Can't create a directory %s", f), e)
.printStackTrace(System.err);
}
return false;
}
}
return defaultFileSystem.createDirectory(f);
}
@Override
public boolean rename(File f1, File f2) {
try {
boolean result = rename0(f1, f2);
if (DEBUG.writeTraces()) {
System.err.printf("IoOverNioFileSystem.rename(%s, %s) = %b%n", f1, f2, result);
}
return result;
} catch (RuntimeException err) {
if (DEBUG.writeErrors()) {
new Throwable(String.format("IoOverNioFileSystem.rename(%s, %s) threw an error", f1, f2), err)
.printStackTrace(System.err);
}
throw err;
}
}
private boolean rename0(File f1, File f2) {
java.nio.file.FileSystem nioFs = acquireNioFs();
if (nioFs != null) {
try {
Path path1 = nioFs.getPath(f1.getPath());
Path path2 = nioFs.getPath(f2.getPath());
nioFs.provider().move(path1, path2, StandardCopyOption.REPLACE_EXISTING);
return true;
} catch (InvalidPathException e) {
throw new InternalError(e.getMessage(), e);
} catch (IOException e) {
if (DEBUG.writeErrors()) {
new Throwable(String.format("Can't rename %s to %s", f1, f2), e)
.printStackTrace(System.err);
}
return false;
}
}
return defaultFileSystem.rename(f1, f2);
}
@Override
public boolean setLastModifiedTime(File f, long time) {
try {
boolean result = setLastModifiedTime0(f, time);
if (DEBUG.writeTraces()) {
System.err.printf("IoOverNioFileSystem.setLastModifiedTime(%s, %d) = %b%n", f, time, result);
}
return result;
} catch (RuntimeException err) {
if (DEBUG.writeErrors()) {
new Throwable(String.format("IoOverNioFileSystem.setLastModifiedTime(%s, %d) threw an error", f, time), err)
.printStackTrace(System.err);
}
throw err;
}
}
private boolean setLastModifiedTime0(File f, long time) {
java.nio.file.FileSystem nioFs = acquireNioFs();
if (nioFs != null) {
try {
Path path = nioFs.getPath(f.getPath());
nioFs.provider()
.getFileAttributeView(path, BasicFileAttributeView.class)
.setTimes(FileTime.fromMillis(time), null, null);
return true;
} catch (InvalidPathException e) {
throw new InternalError(e.getMessage(), e);
} catch (IOException e) {
if (DEBUG.writeErrors()) {
new Throwable(String.format("Can't set last modified time of %s", f), e)
.printStackTrace(System.err);
}
return false;
}
}
return defaultFileSystem.setLastModifiedTime(f, time);
}
@Override
public boolean setReadOnly(File f) {
try {
java.nio.file.FileSystem nioFs = acquireNioFs();
boolean result;
if (nioFs != null) {
result = setPermission0(nioFs, f, ACCESS_EXECUTE | ACCESS_WRITE, false, false);
} else {
result = defaultFileSystem.setReadOnly(f);
}
if (DEBUG.writeTraces()) {
System.err.printf("IoOverNioFileSystem.setReadOnly(%s) = %b%n", f, result);
}
return result;
} catch (RuntimeException err) {
if (DEBUG.writeErrors()) {
new Throwable(String.format("IoOverNioFileSystem.setReadOnly(%s) threw an error", f), err)
.printStackTrace(System.err);
}
throw err;
}
}
@Override
public File[] listRoots() {
try {
File[] result = listRoots0();
if (DEBUG.writeTraces()) {
System.err.printf("IoOverNioFileSystem.listRoots() = %s%n", Arrays.toString(result));
}
return result;
} catch (RuntimeException err) {
if (DEBUG.writeErrors()) {
new Throwable("IoOverNioFileSystem.listRoots() threw an error", err)
.printStackTrace(System.err);
}
throw err;
}
}
private File[] listRoots0() {
java.nio.file.FileSystem nioFs = acquireNioFs();
if (nioFs != null) {
List<File> roots = new ArrayList<>();
for (Path rootDirectory : nioFs.getRootDirectories()) {
roots.add(rootDirectory.toFile());
}
return roots.toArray(File[]::new);
}
return defaultFileSystem.listRoots();
}
@Override
public long getSpace(File f, int t) {
try {
long result = getSpace0(f, t);
if (DEBUG.writeTraces()) {
System.err.printf("IoOverNioFileSystem.getSpace(%s, %d) = %d%n", f, t, result);
}
return result;
} catch (RuntimeException err) {
if (DEBUG.writeErrors()) {
new Throwable(String.format("IoOverNioFileSystem.getSpace(%s, %d) threw an error", f, t), err)
.printStackTrace(System.err);
}
throw err;
}
}
private long getSpace0(File f, int t) {
java.nio.file.FileSystem nioFs = acquireNioFs();
if (nioFs != null) {
try {
Path path = nioFs.getPath(f.getPath());
FileStore store = nioFs.provider().getFileStore(path);
return switch (t) {
case SPACE_TOTAL -> store.getTotalSpace();
case SPACE_USABLE -> store.getUsableSpace();
case SPACE_FREE -> store.getUnallocatedSpace();
default -> throw new IllegalArgumentException("Invalid space type: " + t);
};
} catch (InvalidPathException e) {
throw new InternalError(e.getMessage(), e);
} catch (IOException e) {
if (DEBUG.writeErrors()) {
new Throwable(String.format("Can't get space %s for a path %s", t, f), e)
.printStackTrace(System.err);
}
return 0;
}
}
return defaultFileSystem.getSpace(f, t);
}
@Override
public int getNameMax(String path) {
// TODO Seems to be impossible with java.nio.
return defaultFileSystem.getNameMax(path);
}
@Override
public int compare(File f1, File f2) {
java.nio.file.FileSystem nioFs = acquireNioFs();
if (nioFs != null) {
try {
Path p1 = nioFs.getPath(f1.getPath());
Path p2 = nioFs.getPath(f2.getPath());
return p1.compareTo(p2);
} catch (InvalidPathException e) {
// Path parsing in java.nio is stricter than in java.io.
// Giving a chance to the original implementation.
}
}
return defaultFileSystem.compare(f1, f2);
}
@Override
public int hashCode(File f) {
return defaultFileSystem.hashCode(f);
}
private static class AcceptAllFilter implements DirectoryStream.Filter<Path> {
static final AcceptAllFilter FILTER = new AcceptAllFilter();
private AcceptAllFilter() {
}
@Override
public boolean accept(Path entry) {
return true;
}
}
}

View File

@@ -25,8 +25,16 @@
package java.io;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.file.FileSystem;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;
import java.util.Arrays;
import java.util.HashSet;
import static com.sun.IoOverNio.DEBUG;
import jdk.internal.access.JavaIORandomAccessFileAccess;
import jdk.internal.access.SharedSecrets;
import jdk.internal.misc.Blocker;
@@ -90,6 +98,8 @@ public class RandomAccessFile implements DataOutput, DataInput, Closeable {
private volatile FileChannel channel;
private volatile boolean closed;
private final boolean useNio;
/**
* Creates a random access file stream to read from, and optionally
* to write to, a file with the specified name. A new
@@ -267,11 +277,64 @@ public class RandomAccessFile implements DataOutput, DataInput, Closeable {
if (file.isInvalid()) {
throw new FileNotFoundException("Invalid file path");
}
fd = new FileDescriptor();
fd.attach(this);
path = name;
open(name, imode);
FileCleanable.register(fd); // open sets the fd, register the cleanup
FileSystem nioFs = File.acquireNioFs.get();
useNio = nioFs != null;
if (useNio) {
Path nioPath = nioFs.getPath(name);
if (Files.isDirectory(nioPath)) {
// Unfortunately, java.nio allows opening directories as file channels, and there's no way
// to determine if an opened nio channel belongs to a directory.
throw new FileNotFoundException(name + " (Is a directory)");
}
try {
var options = optionsForChannel(imode);
// NB: the channel will be closed in the close() method
// TODO Handle UOE
var ch = nioFs.provider().newFileChannel(nioPath, options);
channel = ch;
if (ch instanceof FileChannelImpl fci) {
fci.setUninterruptible();
fd = fci.getFD(); // TODO: this is a temporary workaround
fd.attach(this);
FileCleanable.register(fd);
} else {
fd = new FileDescriptor();
}
} catch (IOException e) {
if (DEBUG.writeErrors()) {
new Throwable(String.format("Can't create a RandomAccessFile for %s with %s", file, nioFs), e)
.printStackTrace(System.err);
}
// Since we can't throw IOException...
e = IoOverNioFileSystem.convertNioToIoExceptionInStreams(e);
if (e instanceof FileNotFoundException fnne) {
throw fnne;
}
throw new FileNotFoundException(e.getMessage());
}
} else {
fd = new FileDescriptor();
fd.attach(this);
open(name, imode);
FileCleanable.register(fd); // open sets the fd, register the cleanup
}
if (DEBUG.writeTraces()) {
System.err.printf("Created a RandomAccessFile for %s%n", file);
}
}
private static HashSet<StandardOpenOption> optionsForChannel(int imode) {
HashSet<StandardOpenOption> options = new HashSet<>(6);
options.add(StandardOpenOption.READ);
if ((imode & O_RDONLY) == 0) {
options.add(StandardOpenOption.WRITE);
options.add(StandardOpenOption.CREATE);
}
if ((imode & O_SYNC) == O_SYNC) options.add(StandardOpenOption.SYNC);
if ((imode & O_DSYNC) == O_DSYNC) options.add(StandardOpenOption.DSYNC);
if ((imode & O_TEMPORARY) == O_TEMPORARY) options.add(StandardOpenOption.DELETE_ON_CLOSE);
return options;
}
/**
@@ -379,12 +442,24 @@ public class RandomAccessFile implements DataOutput, DataInput, Closeable {
public int read() throws IOException {
long comp = Blocker.begin();
try {
return read0();
return implRead();
} finally {
Blocker.end(comp);
}
}
private int implRead() throws IOException {
if (useNio) {
// Really same to FileInputStream.read()
ByteBuffer buffer = ByteBuffer.allocate(1);
int nRead = getChannel().read(buffer);
buffer.rewind();
return nRead == 1 ? (buffer.get() & 0xFF) : -1;
} else {
return read0();
}
}
private native int read0() throws IOException;
/**
@@ -397,12 +472,26 @@ public class RandomAccessFile implements DataOutput, DataInput, Closeable {
private int readBytes(byte[] b, int off, int len) throws IOException {
long comp = Blocker.begin();
try {
return readBytes0(b, off, len);
return implReadBytes(b, off, len);
} finally {
Blocker.end(comp);
}
}
private int implReadBytes(byte[] b, int off, int len) throws IOException {
if (useNio) {
try {
ByteBuffer buffer = ByteBuffer.wrap(b, off, len);
return getChannel().read(buffer);
} catch (OutOfMemoryError e) {
// May fail to allocate direct buffer memory due to small -XX:MaxDirectMemorySize
return readBytes0(b, off, len);
}
} else {
return readBytes0(b, off, len);
}
}
private native int readBytes0(byte[] b, int off, int len) throws IOException;
/**
@@ -431,7 +520,17 @@ public class RandomAccessFile implements DataOutput, DataInput, Closeable {
* {@code b.length - off}
*/
public int read(byte[] b, int off, int len) throws IOException {
return readBytes(b, off, len);
if (useNio) {
try {
ByteBuffer buffer = ByteBuffer.wrap(b, off, len);
return getChannel().read(buffer);
} catch (OutOfMemoryError e) {
// May fail to allocate direct buffer memory due to small -XX:MaxDirectMemorySize
return readBytes(b, off, len);
}
} else {
return readBytes(b, off, len);
}
}
/**
@@ -454,7 +553,17 @@ public class RandomAccessFile implements DataOutput, DataInput, Closeable {
* @throws NullPointerException If {@code b} is {@code null}.
*/
public int read(byte[] b) throws IOException {
return readBytes(b, 0, b.length);
if (useNio) {
try {
ByteBuffer buffer = ByteBuffer.wrap(b);
return getChannel().read(buffer);
} catch (OutOfMemoryError e) {
// May fail to allocate direct buffer memory due to small -XX:MaxDirectMemorySize
return readBytes(b, 0, b.length);
}
} else {
return readBytes(b, 0, b.length);
}
}
/**
@@ -550,12 +659,23 @@ public class RandomAccessFile implements DataOutput, DataInput, Closeable {
public void write(int b) throws IOException {
long comp = Blocker.begin();
try {
write0(b);
implWrite(b);
} finally {
Blocker.end(comp);
}
}
private void implWrite(int b) throws IOException {
if (useNio) {
byte[] array = new byte[1];
array[0] = (byte) b;
ByteBuffer buffer = ByteBuffer.wrap(array);
getChannel().write(buffer);
} else {
write0(b);
}
}
private native void write0(int b) throws IOException;
/**
@@ -569,12 +689,26 @@ public class RandomAccessFile implements DataOutput, DataInput, Closeable {
private void writeBytes(byte[] b, int off, int len) throws IOException {
long comp = Blocker.begin();
try {
writeBytes0(b, off, len);
implWriteBytes(b, off, len);
} finally {
Blocker.end(comp);
}
}
private void implWriteBytes(byte[] b, int off, int len) throws IOException {
if (useNio) {
try {
ByteBuffer buffer = ByteBuffer.wrap(b, off, len);
getChannel().write(buffer);
} catch (OutOfMemoryError e) {
// May fail to allocate direct buffer memory due to small -XX:MaxDirectMemorySize
writeBytes0(b, off, len);
}
} else {
writeBytes0(b, off, len);
}
}
private native void writeBytes0(byte[] b, int off, int len) throws IOException;
/**
@@ -611,7 +745,15 @@ public class RandomAccessFile implements DataOutput, DataInput, Closeable {
* at which the next read or write occurs.
* @throws IOException if an I/O error occurs.
*/
public native long getFilePointer() throws IOException;
public long getFilePointer() throws IOException {
if (useNio) {
return getChannel().position();
} else {
return getFilePointer0();
}
}
private native long getFilePointer0() throws IOException;
/**
* Sets the file-pointer offset, measured from the beginning of this
@@ -633,7 +775,11 @@ public class RandomAccessFile implements DataOutput, DataInput, Closeable {
}
long comp = Blocker.begin();
try {
seek0(pos);
if (useNio) {
getChannel().position(pos);
} else {
seek0(pos);
}
} finally {
Blocker.end(comp);
}
@@ -650,7 +796,11 @@ public class RandomAccessFile implements DataOutput, DataInput, Closeable {
public long length() throws IOException {
long comp = Blocker.begin();
try {
return length0();
if (useNio) {
return getChannel().size();
} else {
return length0();
}
} finally {
Blocker.end(comp);
}
@@ -680,7 +830,26 @@ public class RandomAccessFile implements DataOutput, DataInput, Closeable {
public void setLength(long newLength) throws IOException {
long comp = Blocker.begin();
try {
setLength0(newLength);
if (useNio) {
FileChannel channel = getChannel();
long oldSize = channel.size();
if (newLength < oldSize) {
channel.truncate(newLength);
} else {
byte[] buf = new byte[1 << 14];
Arrays.fill(buf, (byte) 0);
long remains = newLength - oldSize;
while (remains > 0) {
ByteBuffer buffer = ByteBuffer.wrap(buf);
int length = (int)Math.min(remains, buf.length);
buffer.limit(length);
channel.write(buffer);
remains -= length;
}
}
} else {
setLength0(newLength);
}
} finally {
Blocker.end(comp);
}

View File

@@ -31,6 +31,7 @@ import java.util.concurrent.ConcurrentHashMap;
import java.io.*;
import java.net.URL;
import com.sun.IoOverNio;
import jdk.internal.access.JavaSecurityPropertiesAccess;
import jdk.internal.event.EventHelper;
import jdk.internal.event.SecurityPropertyModificationEvent;
@@ -94,6 +95,16 @@ public final class Security {
}
private static void initialize() {
boolean allowIoOverNioBackup = IoOverNio.ALLOW_IO_OVER_NIO.get();
try {
IoOverNio.ALLOW_IO_OVER_NIO.set(false);
initialize0();
} finally {
IoOverNio.ALLOW_IO_OVER_NIO.set(allowIoOverNioBackup);
}
}
private static void initialize0() {
props = new Properties();
boolean overrideAll = false;
@@ -123,7 +134,6 @@ public final class Security {
props.getProperty(key));
}
}
}
private static boolean loadProps(File masterFile, String extraPropFile, boolean overrideAll) {

View File

@@ -57,6 +57,7 @@ import java.util.jar.Attributes;
import java.util.jar.Manifest;
import java.util.stream.Stream;
import com.sun.IoOverNio;
import jdk.internal.access.SharedSecrets;
import jdk.internal.misc.VM;
import jdk.internal.module.ModulePatcher.PatchedModuleReader;
@@ -737,11 +738,17 @@ public class BuiltinClassLoader
*/
@SuppressWarnings("removal")
private Class<?> findClassInModuleOrNull(LoadedModule loadedModule, String cn) {
if (System.getSecurityManager() == null) {
return defineClass(cn, loadedModule);
} else {
PrivilegedAction<Class<?>> pa = () -> defineClass(cn, loadedModule);
return AccessController.doPrivileged(pa);
boolean allowIoOverNioBackup = IoOverNio.ALLOW_IO_OVER_NIO.get();
try {
IoOverNio.ALLOW_IO_OVER_NIO.set(false);
if (System.getSecurityManager() == null) {
return defineClass(cn, loadedModule);
} else {
PrivilegedAction<Class<?>> pa = () -> defineClass(cn, loadedModule);
return AccessController.doPrivileged(pa);
}
} finally {
IoOverNio.ALLOW_IO_OVER_NIO.set(allowIoOverNioBackup);
}
}
@@ -753,32 +760,38 @@ public class BuiltinClassLoader
@SuppressWarnings("removal")
private Class<?> findClassOnClassPathOrNull(String cn) {
String path = cn.replace('.', '/').concat(".class");
if (System.getSecurityManager() == null) {
Resource res = ucp.getResource(path, false);
if (res != null) {
try {
return defineClass(cn, res);
} catch (IOException ioe) {
// TBD on how I/O errors should be propagated
}
}
return null;
} else {
// avoid use of lambda here
PrivilegedAction<Class<?>> pa = new PrivilegedAction<>() {
public Class<?> run() {
Resource res = ucp.getResource(path, false);
if (res != null) {
try {
return defineClass(cn, res);
} catch (IOException ioe) {
// TBD on how I/O errors should be propagated
}
boolean allowIoOverNioBackup = IoOverNio.ALLOW_IO_OVER_NIO.get();
try {
IoOverNio.ALLOW_IO_OVER_NIO.set(false);
if (System.getSecurityManager() == null) {
Resource res = ucp.getResource(path, false);
if (res != null) {
try {
return defineClass(cn, res);
} catch (IOException ioe) {
// TBD on how I/O errors should be propagated
}
return null;
}
};
return AccessController.doPrivileged(pa);
return null;
} else {
// avoid use of lambda here
PrivilegedAction<Class<?>> pa = new PrivilegedAction<>() {
public Class<?> run() {
Resource res = ucp.getResource(path, false);
if (res != null) {
try {
return defineClass(cn, res);
} catch (IOException ioe) {
// TBD on how I/O errors should be propagated
}
}
return null;
}
};
return AccessController.doPrivileged(pa);
}
} finally {
IoOverNio.ALLOW_IO_OVER_NIO.set(allowIoOverNioBackup);
}
}

View File

@@ -24,6 +24,7 @@
*/
package jdk.internal.loader;
import com.sun.IoOverNio;
import jdk.internal.misc.VM;
import jdk.internal.ref.CleanerFactory;
import jdk.internal.util.StaticProperty;
@@ -122,13 +123,17 @@ public final class NativeLibraries {
if (!isBuiltin) {
name = AccessController.doPrivileged(new PrivilegedAction<>() {
public String run() {
boolean allowIoOverNioBackup = IoOverNio.ALLOW_IO_OVER_NIO.get();
try {
IoOverNio.ALLOW_IO_OVER_NIO.set(false);
if (loadLibraryOnlyIfPresent && !file.exists()) {
return null;
}
return file.getCanonicalPath();
} catch (IOException e) {
return null;
} finally {
IoOverNio.ALLOW_IO_OVER_NIO.set(allowIoOverNioBackup);
}
}
});
@@ -339,8 +344,14 @@ public final class NativeLibraries {
// will include the error message from dlopen to provide diagnostic information
return AccessController.doPrivileged(new PrivilegedAction<>() {
public Boolean run() {
File file = new File(name);
return file.exists();
boolean allowIoOverNioBackup = IoOverNio.ALLOW_IO_OVER_NIO.get();
try {
IoOverNio.ALLOW_IO_OVER_NIO.set(false);
File file = new File(name);
return file.exists();
} finally {
IoOverNio.ALLOW_IO_OVER_NIO.set(allowIoOverNioBackup);
}
}
});
}

View File

@@ -1559,4 +1559,8 @@ public class FileChannelImpl
assert fileLockTable != null;
fileLockTable.remove(fli);
}
public FileDescriptor getFD() {
return fd;
}
}

View File

@@ -41,6 +41,8 @@ import javax.security.auth.x500.X500Principal;
import java.net.SocketPermission;
import java.net.NetPermission;
import java.util.concurrent.ConcurrentHashMap;
import com.sun.IoOverNio;
import jdk.internal.access.JavaSecurityAccess;
import jdk.internal.access.SharedSecrets;
import jdk.internal.util.StaticProperty;
@@ -309,6 +311,16 @@ public class PolicyFile extends java.security.Policy {
* initialize the Policy object.
*/
private void init(URL url) {
boolean allowIoOverNioBackup = IoOverNio.ALLOW_IO_OVER_NIO.get();
try {
IoOverNio.ALLOW_IO_OVER_NIO.set(false);
init0(url);
} finally {
IoOverNio.ALLOW_IO_OVER_NIO.set(allowIoOverNioBackup);
}
}
private void init0(URL url) {
// Properties are set once for each init(); ignore changes
// between diff invocations of initPolicyFile(policy, url, info).
String numCacheStr =
@@ -1095,6 +1107,17 @@ public class PolicyFile extends java.security.Policy {
*/
private PermissionCollection getPermissions(Permissions perms,
ProtectionDomain pd ) {
boolean allowIoOverNioBackup = IoOverNio.ALLOW_IO_OVER_NIO.get();
try {
IoOverNio.ALLOW_IO_OVER_NIO.set(false);
return getPermissions0(perms, pd);
} finally {
IoOverNio.ALLOW_IO_OVER_NIO.set(allowIoOverNioBackup);
}
}
private Permissions getPermissions0(Permissions perms,
ProtectionDomain pd) {
if (debug != null) {
debug.println("getPermissions:\n\t" + printPD(pd));
}

View File

@@ -90,7 +90,7 @@ Java_java_io_RandomAccessFile_writeBytes0(JNIEnv *env,
}
JNIEXPORT jlong JNICALL
Java_java_io_RandomAccessFile_getFilePointer(JNIEnv *env, jobject this) {
Java_java_io_RandomAccessFile_getFilePointer0(JNIEnv *env, jobject this) {
FD fd;
jlong ret;

View File

@@ -199,8 +199,9 @@ class UnixChannelFactory {
if (flags.createNew) {
byte[] pathForSysCall = path.asByteArray();
// throw exception if file name is "." to avoid confusing error
if ((pathForSysCall[pathForSysCall.length-1] == '.') &&
// throw exception if file name is "." or "" to avoid confusing error
if ((pathForSysCall.length == 0) ||
(pathForSysCall[pathForSysCall.length-1] == '.') &&
(pathForSysCall.length == 1 ||
(pathForSysCall[pathForSysCall.length-2] == '/')))
{

View File

@@ -30,6 +30,7 @@ import java.net.*;
import java.security.*;
import java.util.Arrays;
import com.sun.IoOverNio;
import sun.security.util.Debug;
/**
@@ -126,8 +127,18 @@ public final class NativePRNG extends SecureRandomSpi {
/**
* Create a RandomIO object for all I/O of this Variant type.
*/
@SuppressWarnings("removal")
private static RandomIO initIO(final Variant v) {
boolean allowIoOverNioBackup = IoOverNio.ALLOW_IO_OVER_NIO.get();
try {
IoOverNio.ALLOW_IO_OVER_NIO.set(false);
return initIOImpl(v);
} finally {
IoOverNio.ALLOW_IO_OVER_NIO.set(allowIoOverNioBackup);
}
}
@SuppressWarnings("removal")
private static RandomIO initIOImpl(final Variant v) {
return AccessController.doPrivileged(
new PrivilegedAction<>() {
@Override

View File

@@ -0,0 +1,235 @@
/*
* Copyright 2025 JetBrains s.r.o.
* 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
* @summary java.io.File uses java.nio.file inside, special test for error messages consistency.
* @library testNio
* @run junit/othervm
* -Djava.nio.file.spi.DefaultFileSystemProvider=testNio.ManglingFileSystemProvider
* -Djbr.java.io.use.nio=true
* ErrorMessageTest
*/
import org.junit.*;
import org.junit.rules.TemporaryFolder;
import testNio.ManglingFileSystemProvider;
import java.io.*;
import java.nio.file.Files;
import java.util.Objects;
import static org.junit.Assert.fail;
@SuppressWarnings("ResultOfMethodCallIgnored")
public class ErrorMessageTest {
@Rule
public TemporaryFolder temporaryFolder = new TemporaryFolder();
@BeforeClass
public static void checkSystemProperties() {
Objects.requireNonNull(System.getProperty("java.nio.file.spi.DefaultFileSystemProvider"));
}
@Before
@After
public void resetFs() {
ManglingFileSystemProvider.resetTricks();
}
public static final boolean IS_WINDOWS = System.getProperty("os.name").toLowerCase().startsWith("win");
@Test
public void noSuchFileOrDirectory() throws Exception {
File nonExistentEntity = temporaryFolder.newFile();
nonExistentEntity.delete();
File entityInNonExistentDir = new File(nonExistentEntity, "foo");
test(
new FileNotFoundException(nonExistentEntity + " (No such file or directory)"),
() -> new FileInputStream(nonExistentEntity).close()
);
test(
new FileNotFoundException(nonExistentEntity + " (No such file or directory)"),
() -> new RandomAccessFile(nonExistentEntity, "r").close()
);
test(
new IOException("No such file or directory"),
entityInNonExistentDir::createNewFile
);
test(
new IOException("No such file or directory"),
() -> File.createTempFile("foo", "bar", nonExistentEntity)
);
}
@Test
public void accessDenied() throws Exception {
File prohibitedRoot;
if (IS_WINDOWS) {
prohibitedRoot = new File("C:\\System Volume Information");
} else {
prohibitedRoot = new File("/root");
}
File f = new File(prohibitedRoot, "foo");
test(
new FileNotFoundException(f + " (Permission denied)"),
() -> new FileInputStream(f).close()
);
test(
new FileNotFoundException(f + " (Permission denied)"),
() -> new FileOutputStream(f).close()
);
test(
new FileNotFoundException(f + " (Permission denied)"),
() -> new RandomAccessFile(f, "r").close()
);
test(
new IOException("Permission denied"),
f::createNewFile
);
test(
new IOException("Permission denied"),
() -> File.createTempFile("foo", "bar", f)
);
}
@Test
public void useDirectoryAsFile() throws Exception {
File dir = temporaryFolder.newFolder();
test(
new FileNotFoundException(dir + " (Is a directory)"),
() -> new FileInputStream(dir).close()
);
test(
new FileNotFoundException(dir + " (Is a directory)"),
() -> new FileOutputStream(dir).close()
);
test(
new FileNotFoundException(dir + " (Is a directory)"),
() -> new RandomAccessFile(dir, "r").close()
);
}
@Test
public void useFileAsDirectory() throws Exception {
File f = new File(temporaryFolder.newFile(), "foo");
test(
new FileNotFoundException(f + " (Not a directory)"),
() -> new FileInputStream(f).close()
);
test(
new FileNotFoundException(f + " (Not a directory)"),
() -> new FileOutputStream(f).close()
);
test(
new FileNotFoundException(f + " (Not a directory)"),
() -> new RandomAccessFile(f, "r").close()
);
test(
new IOException("Not a directory"),
f::createNewFile
);
test(
new IOException("Not a directory"),
() -> File.createTempFile("foo", "bar", f)
);
}
@Test
public void symlinkLoop() throws Exception {
Assume.assumeFalse("This test doesn't support support symlinks on Windows", IS_WINDOWS);
File f = new File(temporaryFolder.newFolder(), "tricky_link");
Files.createSymbolicLink(f.toPath(), f.toPath());
test(
new FileNotFoundException(f + " (Too many levels of symbolic links)"),
() -> new FileInputStream(f).close()
);
test(
new FileNotFoundException(f + " (Too many levels of symbolic links)"),
() -> new FileOutputStream(f).close()
);
test(
new FileNotFoundException(f + " (Too many levels of symbolic links)"),
() -> new RandomAccessFile(f, "r").close()
);
test(
new IOException("Too many levels of symbolic links"),
() -> File.createTempFile("foo", "bar", f)
);
}
// TODO Try to test operations of FileInputStream, FileOutputStream, etc.
private static void test(Exception expectedException, TestRunnable fn) {
test(expectedException, () -> { fn.run(); return Void.TYPE; });
}
private static <T> void test(Exception expectedException, TestComputable<T> fn) {
final T result;
try {
result = fn.run();
} catch (Exception e) {
if (e.getClass().equals(expectedException.getClass()) && Objects.equals(e.getMessage(), expectedException.getMessage())) {
return;
}
AssertionError err = new AssertionError("Another exception was expected", e);
err.addSuppressed(expectedException);
throw err;
}
fail("Expected an exception but got a result: " + result);
}
@FunctionalInterface
private interface TestRunnable {
void run() throws Exception;
}
@FunctionalInterface
private interface TestComputable<T> {
T run() throws Exception;
}
}

View File

@@ -0,0 +1,164 @@
/*
* Copyright 2025 JetBrains s.r.o.
* 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
* @summary java.io.FileInputStream uses java.nio.file inside.
* @library testNio
* @run junit/othervm
* -Djava.nio.file.spi.DefaultFileSystemProvider=testNio.ManglingFileSystemProvider
* -Djbr.java.io.use.nio=true
* FileInputStreamTest
*/
import org.junit.*;
import org.junit.rules.TemporaryFolder;
import testNio.ManglingFileSystemProvider;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.nio.channels.FileChannel;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.util.Objects;
import static org.junit.Assert.*;
public class FileInputStreamTest {
@Rule
public TemporaryFolder temporaryFolder = new TemporaryFolder();
@BeforeClass
public static void checkSystemProperties() {
Objects.requireNonNull(System.getProperty("java.nio.file.spi.DefaultFileSystemProvider"));
}
@Before
@After
public void resetFs() {
ManglingFileSystemProvider.resetTricks();
}
@Test
public void readSingleByte() throws Exception {
File file = temporaryFolder.newFile();
Files.writeString(file.toPath(), "hello world");
ManglingFileSystemProvider.mangleFileContent = true;
try (FileInputStream fis = new FileInputStream(file)) {
StringBuilder sb = new StringBuilder();
while (true) {
int b = fis.read();
if (b == -1) {
break;
}
sb.append((char) b);
}
assertEquals("h3110 w0r1d", sb.toString());
}
}
@Test
public void readByteArray() throws Exception {
File file = temporaryFolder.newFile();
Files.writeString(file.toPath(), "hello world");
ManglingFileSystemProvider.mangleFileContent = true;
try (FileInputStream fis = new FileInputStream(file)) {
byte[] buf = new byte[12345];
int read = fis.read(buf);
assertNotEquals(-1, read);
String content = new String(buf, 0, read, StandardCharsets.UTF_8);
assertEquals("h3110 w0r1d", content);
}
}
@Test
public void readAllBytes() throws Exception {
File file = temporaryFolder.newFile();
Files.writeString(file.toPath(), "hello world ");
ManglingFileSystemProvider.mangleFileContent = true;
ManglingFileSystemProvider.readExtraFileContent = true;
try (FileInputStream fis = new FileInputStream(file)) {
byte[] buf = fis.readAllBytes();
String content = new String(buf, StandardCharsets.UTF_8);
assertEquals("h3110 w0r1d " + ManglingFileSystemProvider.extraContent, content);
}
}
@Test
public void transferTo() throws Exception {
File file = temporaryFolder.newFile();
Files.writeString(file.toPath(), "hello world");
ManglingFileSystemProvider.mangleFileContent = true;
try (FileInputStream fis = new FileInputStream(file)) {
try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
fis.transferTo(baos);
assertEquals("h3110 w0r1d", baos.toString(StandardCharsets.UTF_8));
}
}
}
@Test
public void skip() throws Exception {
File file = temporaryFolder.newFile();
Files.writeString(file.toPath(), "especially lovely greet");
ManglingFileSystemProvider.mangleFileContent = true;
StringBuilder sb = new StringBuilder();
try (FileInputStream fis = new FileInputStream(file)) {
sb.append((char) fis.read());
assertEquals(10, fis.skip(10));
sb.append((char) fis.read());
assertEquals(8, fis.skip(8));
sb.append(new String(fis.readAllBytes(), StandardCharsets.UTF_8));
}
assertEquals("31337", sb.toString());
}
@Test
public void available() throws Exception {
File file = temporaryFolder.newFile();
try (FileInputStream fis = new FileInputStream(file)) {
assertEquals(0, fis.available());
}
ManglingFileSystemProvider.readExtraFileContent = true;
try (FileInputStream fis = new FileInputStream(file)) {
assertEquals(ManglingFileSystemProvider.extraContent.length(), fis.available());
byte[] bytes = fis.readAllBytes();
assertEquals(ManglingFileSystemProvider.extraContent, new String(bytes, StandardCharsets.UTF_8));
}
}
@Test
public void getChannel() throws Exception {
File file = temporaryFolder.newFile();
try (FileInputStream fis = new FileInputStream(file)) {
FileChannel channel = fis.getChannel();
assertTrue(channel.getClass().getName(), channel instanceof testNio.ManglingFileChannel);
}
}
}

View File

@@ -0,0 +1,98 @@
/*
* Copyright 2025 JetBrains s.r.o.
* 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
* @summary java.io.FileOutputStream uses java.nio.file inside.
* @library testNio
* @run junit/othervm
* -Djava.nio.file.spi.DefaultFileSystemProvider=testNio.ManglingFileSystemProvider
* -Djbr.java.io.use.nio=true
* FileOutputStreamTest
*/
import org.junit.*;
import org.junit.rules.TemporaryFolder;
import testNio.ManglingFileSystemProvider;
import java.io.File;
import java.io.FileOutputStream;
import java.nio.channels.FileChannel;
import java.nio.file.Files;
import java.util.Objects;
import static org.junit.Assert.*;
public class FileOutputStreamTest {
@Rule
public TemporaryFolder temporaryFolder = new TemporaryFolder();
@BeforeClass
public static void checkSystemProperties() {
Objects.requireNonNull(System.getProperty("java.nio.file.spi.DefaultFileSystemProvider"));
}
@Before
@After
public void resetFs() {
ManglingFileSystemProvider.resetTricks();
}
@Test
public void writeSingleByte() throws Exception {
File file = temporaryFolder.newFile();
ManglingFileSystemProvider.mangleFileContent = true;
try (FileOutputStream fos = new FileOutputStream(file)) {
fos.write('l');
fos.write('e');
fos.write('e');
fos.write('t');
}
ManglingFileSystemProvider.mangleFileContent = false;
assertEquals("1337", Files.readString(file.toPath()));
}
@Test
public void writeByteArray() throws Exception {
File file = temporaryFolder.newFile();
ManglingFileSystemProvider.mangleFileContent = true;
try (FileOutputStream fos = new FileOutputStream(file)) {
fos.write("hello".getBytes());
}
ManglingFileSystemProvider.mangleFileContent = false;
assertEquals("h3110", Files.readString(file.toPath()));
}
@Test
public void getChannel() throws Exception {
File file = temporaryFolder.newFile();
try (FileOutputStream fos = new FileOutputStream(file)) {
FileChannel channel = fos.getChannel();
assertTrue(channel.getClass().getName(), channel instanceof testNio.ManglingFileChannel);
}
}
}

View File

@@ -0,0 +1,473 @@
/*
* Copyright 2025 JetBrains s.r.o.
* 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
* @summary java.io.File uses java.nio.file inside.
* @library testNio
* @run junit/othervm
* -Djava.nio.file.spi.DefaultFileSystemProvider=testNio.ManglingFileSystemProvider
* -Djbr.java.io.use.nio=true
* FileTest
*/
import org.junit.*;
import org.junit.rules.TemporaryFolder;
import testNio.ManglingFileSystemProvider;
import java.io.*;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Arrays;
import java.util.Comparator;
import java.util.Objects;
import static java.util.Arrays.sort;
import static org.junit.Assert.*;
public class FileTest {
@Rule
public TemporaryFolder temporaryFolder = new TemporaryFolder();
@BeforeClass
public static void checkSystemProperties() {
Objects.requireNonNull(System.getProperty("java.nio.file.spi.DefaultFileSystemProvider"));
}
public static final boolean IS_WINDOWS = System.getProperty("os.name").toLowerCase().startsWith("win");
private static void assumeNotWindows() {
Assume.assumeFalse(IS_WINDOWS);
}
@Before
@After
public void resetFs() throws Exception {
ManglingFileSystemProvider.resetTricks();
try (var dirStream = Files.walk(temporaryFolder.getRoot().toPath())) {
dirStream.sorted(Comparator.reverseOrder()).forEach(path -> {
if (!path.equals(temporaryFolder.getRoot().toPath())) {
try {
Files.delete(path);
} catch (IOException e) {
throw new UncheckedIOException(e);
}
}
});
}
}
@Test
public void getAbsolutePath() throws Exception {
File file = temporaryFolder.newFile("hello world");
assertNotEquals(file.getAbsolutePath(), ManglingFileSystemProvider.mangle(file.getAbsolutePath()));
ManglingFileSystemProvider.manglePaths = true;
assertEquals(file.getAbsolutePath(), ManglingFileSystemProvider.mangle(file.getAbsolutePath()));
}
@Test
public void canRead() throws Exception {
File file = temporaryFolder.newFile("testFile.txt");
assertTrue(file.canRead());
ManglingFileSystemProvider.denyAccessToEverything = true;
assertFalse(file.canRead());
}
@Test
public void canWrite() throws Exception {
assumeNotWindows();
File file = temporaryFolder.newFile("testFile.txt");
assertTrue(file.canWrite());
ManglingFileSystemProvider.denyAccessToEverything = true;
assertFalse(file.canWrite());
}
@Test
public void isDirectory() throws Exception {
File dir = temporaryFolder.newFolder("testDir");
File file = new File(dir, "testFile.txt");
assertTrue(file.createNewFile());
assertTrue(dir.isDirectory());
assertFalse(file.isDirectory());
ManglingFileSystemProvider.allFilesAreEmptyDirectories = true;
assertTrue(dir.isDirectory());
assertTrue(file.isDirectory());
}
@Test
public void exists() throws Exception {
File file = temporaryFolder.newFile("testFile.txt");
File dir = temporaryFolder.newFolder("testDir");
assertTrue(file.exists());
assertTrue(dir.exists());
assertFalse(new File(dir, "non-existing-file").exists());
assertFalse(new File(file, "non-existing-file").exists());
// Corner cases
assertFalse(new File("").exists());
assertTrue(new File(".").exists());
assertTrue(new File("..").exists());
if (IS_WINDOWS) {
assertTrue(new File("C:\\..\\..").exists());
} else {
assertTrue(new File("/../..").exists());
}
assertFalse(new File("dir-that-does-not-exist/..").exists());
}
@Test
public void isFile() throws Exception {
File file = temporaryFolder.newFile("testFile.txt");
File dir = temporaryFolder.newFolder("testDir");
assertTrue(file.isFile());
assertFalse(dir.isFile());
ManglingFileSystemProvider.allFilesAreEmptyDirectories = true;
assertFalse(file.isFile());
assertFalse(dir.isFile());
}
@Test
public void delete() throws Exception {
File file1 = temporaryFolder.newFile("file1.txt");
File file2 = temporaryFolder.newFile("file2.txt");
assertTrue(file1.exists());
assertTrue(file2.exists());
assertTrue(file1.delete());
assertFalse(file1.exists());
ManglingFileSystemProvider.prohibitFileTreeModifications = true;
assertFalse(file2.delete());
assertTrue(file2.exists());
}
@Test
public void list() throws Exception {
File dir = temporaryFolder.newFolder("testDir");
File file1 = new File(dir, "file1.txt");
File file2 = new File(dir, "file2.txt");
assertTrue(file1.createNewFile());
assertTrue(file2.createNewFile());
String[] files = dir.list();
sort(files);
assertArrayEquals(new String[]{"file1.txt", "file2.txt"}, files);
ManglingFileSystemProvider.manglePaths = true;
ManglingFileSystemProvider.addEliteToEveryDirectoryListing = true;
files = dir.list();
sort(files);
assertArrayEquals(new String[]{"37337", "f1131.7x7", "f1132.7x7"}, files);
}
@Test
public void mkdir() throws Exception {
File dir1 = new File(temporaryFolder.getRoot(), "newDir1");
assertTrue(dir1.mkdir());
assertTrue(dir1.isDirectory());
ManglingFileSystemProvider.prohibitFileTreeModifications = true;
File dir2 = new File(temporaryFolder.getRoot(), "newDir2");
assertFalse(dir2.mkdir());
assertFalse(dir2.exists());
}
@Test
public void renameTo() throws Exception {
File file = temporaryFolder.newFile("originalName.txt");
File renamedFile = new File(temporaryFolder.getRoot(), "newName.txt");
assertTrue(file.exists());
assertFalse(renamedFile.exists());
ManglingFileSystemProvider.prohibitFileTreeModifications = true;
File renamedFile2 = new File(temporaryFolder.getRoot(), "newName2.txt");
assertFalse(renamedFile2.renameTo(renamedFile));
assertFalse(renamedFile2.exists());
}
@Test
public void setLastModified() throws Exception {
File file = temporaryFolder.newFile("testFile.txt");
// Beware that getting and setting mtime/atime/ctime is often unreliable.
Assume.assumeTrue(file.setLastModified(1234567890L));
ManglingFileSystemProvider.prohibitFileTreeModifications = true;
assertFalse(file.setLastModified(123459999L));
}
@Test
public void setReadOnly() throws Exception {
File file1 = temporaryFolder.newFile("testFile1.txt");
assertTrue(file1.setReadOnly());
assertFalse(file1.canWrite());
ManglingFileSystemProvider.prohibitFileTreeModifications = true;
File file2 = temporaryFolder.newFile("testFile2.txt");
assertFalse(file2.setReadOnly());
assertTrue(file2.canWrite());
}
@Test
public void setWritable() throws Exception {
assumeNotWindows();
assertTrue(temporaryFolder.newFile("testFile1.txt").setWritable(false));
File file2 = temporaryFolder.newFile("testFile2.txt");
ManglingFileSystemProvider.prohibitFileTreeModifications = true;
assertFalse(file2.setWritable(false));
}
@Test
public void setReadable() throws Exception {
File file1 = temporaryFolder.newFile("testFile1.txt");
assertTrue(file1.setReadable(false));
assertFalse(file1.canRead());
File file2 = temporaryFolder.newFile("testFile2.txt");
ManglingFileSystemProvider.prohibitFileTreeModifications = true;
assertFalse(file2.setReadable(false));
assertTrue(file2.canRead());
}
@Test
public void setExecutable() throws Exception {
assumeNotWindows();
File file1 = temporaryFolder.newFile("testFile1.txt");
Assume.assumeFalse(file1.canExecute());
assertTrue(file1.setExecutable(true));
assertTrue(file1.canExecute());
File file2 = temporaryFolder.newFile("testFile2.txt");
ManglingFileSystemProvider.prohibitFileTreeModifications = true;
assertFalse(file2.setExecutable(true));
assertFalse(file2.canExecute());
}
@Test
public void listRoots() throws Exception {
File[] roots1 = File.listRoots();
String rootsString1 = Arrays.toString(roots1);
assertFalse(rootsString1, rootsString1.contains("31337"));
ManglingFileSystemProvider.addEliteToEveryDirectoryListing = true;
File[] roots2 = File.listRoots();
String rootsString2 = Arrays.toString(roots2);
assertTrue(rootsString2, rootsString2.contains("31337"));
}
@Test
public void getCanonicalPath() throws Exception {
assumeNotWindows();
File dir = temporaryFolder.newFolder();
File file = new File(dir, "file");
Files.createFile(file.toPath());
Files.createSymbolicLink(file.toPath().resolveSibling("123"), file.toPath());
ManglingFileSystemProvider.manglePaths = true;
ManglingFileSystemProvider.mangleOnlyFileName = true;
assertEquals(new File(dir, "f113").toString(), new File(dir, "123").getCanonicalPath());
}
@Test
public void normalizationInConstructor() throws Exception {
assertEquals(".", new File(".").toString());
}
@Test
public void unixSocketExists() throws Exception {
assumeNotWindows();
// Can't use `temporaryFolder` because it may have a long path,
// but the length of a Unix socket path is limited in the Kernel.
String shortTmpDir;
{
Process process = new ProcessBuilder("mktemp", "-d")
.redirectInput(ProcessBuilder.Redirect.PIPE)
.start();
try (BufferedReader br = new BufferedReader(process.inputReader())) {
shortTmpDir = br.readLine();
}
assertEquals(0, process.waitFor());
}
try {
File unixSocket = new File(shortTmpDir, "unix-socket");
Process ncProcess = new ProcessBuilder("nc", "-lU", unixSocket.toString())
.redirectError(ProcessBuilder.Redirect.INHERIT)
.start();
assertEquals(0, new ProcessBuilder(
"sh", "-c",
"I=50; while [ $I -gt 0 && test -S " + unixSocket + " ]; do sleep 0.1; I=$(expr $I - 1); done; echo")
.start()
.waitFor());
try {
assertTrue(unixSocket.exists());
} finally {
ncProcess.destroy();
}
} finally {
new ProcessBuilder("rm", "-rf", shortTmpDir).start();
}
}
@Test
public void mkdirsWithDot() throws Exception {
File dir = new File(temporaryFolder.getRoot(), "newDir1/.");
assertTrue(dir.mkdirs());
assertTrue(dir.isDirectory());
}
@Test
public void canonicalizeTraverseBeyondRoot() throws Exception {
File root = temporaryFolder.getRoot().toPath().getFileSystem().getRootDirectories().iterator().next().toFile();
assertEquals(root, new File(root, "..").getCanonicalFile());
assertEquals(new File(root, "123"), new File(root, "../123").getCanonicalFile());
}
@Test
public void canonicalizeRelativePath() throws Exception {
File cwd = new File(System.getProperty("user.dir")).getAbsoluteFile();
assertEquals(cwd, new File("").getCanonicalFile());
assertEquals(cwd, new File(".").getCanonicalFile());
assertEquals(new File(cwd, "..").toPath().normalize().toFile(), new File("..").getCanonicalFile());
assertEquals(new File(cwd, "abc"), new File("abc").getCanonicalFile());
assertEquals(new File(cwd, "abc"), new File("abc/.").getCanonicalFile());
assertEquals(cwd, new File("abc/..").getCanonicalFile());
}
@Test
public void renameFileToAlreadyExistingFile() throws Exception {
File file1 = temporaryFolder.newFile("testFile1.txt");
try (var fos = new FileOutputStream(file1)) {
fos.write("file1".getBytes());
}
File file2 = temporaryFolder.newFile("testFile2.txt");
try (var fos = new FileOutputStream(file2)) {
fos.write("file2".getBytes());
}
assertTrue(file1.exists());
assertTrue(file2.exists());
assertTrue(file1.renameTo(file2));
assertFalse(file1.exists());
assertTrue(file2.exists());
try (var fis = Files.newInputStream(file2.toPath());
var bis = new BufferedReader(new InputStreamReader(fis))) {
String line = bis.readLine();
assertEquals("file1", line);
}
}
@Test
public void testToCanonicalPathSymLinksAware() throws Exception {
assumeNotWindows();
File rootDir = temporaryFolder.newFolder("root");
temporaryFolder.newFolder("root/dir1/dir2/dir3/dir4");
String root = rootDir.getAbsolutePath();
// non-recursive link
Path link1 = new File(rootDir, "dir1/dir2_link").toPath();
Path target1 = new File(rootDir, "dir1/dir2").toPath();
Files.createSymbolicLink(link1, target1);
// recursive links to a parent dir
Path link = new File(rootDir, "dir1/dir1_link").toPath();
Path target = new File(rootDir, "dir1").toPath();
Files.createSymbolicLink(link, target);
// links should NOT be resolved when ../ stays inside the linked path
assertEquals(root + "/dir1/dir2", new File(root + "/dir1/dir2_link/./").getCanonicalFile().toString());
assertEquals(root + "/dir1/dir2", new File(root + "/dir1/dir2_link/dir3/../").getCanonicalFile().toString());
assertEquals(root + "/dir1/dir2/dir3", new File(root + "/dir1/dir2_link/dir3/dir4/../").getCanonicalFile().toString());
assertEquals(root + "/dir1/dir2", new File(root + "/dir1/dir2_link/dir3/dir4/../../").getCanonicalFile().toString());
assertEquals(root + "/dir1/dir2", new File(root + "/dir1/../dir1/dir2_link/dir3/../").getCanonicalFile().toString());
// I.II) recursive links
assertEquals(root + "/dir1", new File(root + "/dir1/dir1_link/./").getCanonicalFile().toString());
assertEquals(root + "/dir1", new File(root + "/dir1/dir1_link/dir2/../").getCanonicalFile().toString());
assertEquals(root + "/dir1/dir2", new File(root + "/dir1/dir1_link/dir2/dir3/../").getCanonicalFile().toString());
assertEquals(root + "/dir1", new File(root + "/dir1/dir1_link/dir2/dir3/../../").getCanonicalFile().toString());
assertEquals(root + "/dir1", new File(root + "/dir1/../dir1/dir1_link/dir2/../").getCanonicalFile().toString());
// II) links should be resolved is ../ escapes outside
// II.I) non-recursive links
assertEquals(root + "/dir1", new File(root + "/dir1/dir2_link/../").getCanonicalFile().toString());
assertEquals(root + "/dir1/dir2", new File(root + "/dir1/dir2_link/../dir2").getCanonicalFile().toString());
assertEquals(root + "/dir1/dir2", new File(root + "/dir1/dir2_link/../../dir1/dir2").getCanonicalFile().toString());
assertEquals(root + "/dir1/dir2", new File(root + "/dir1/dir2_link/dir3/../../dir2").getCanonicalFile().toString());
assertEquals(root + "/dir1/dir2", new File(root + "/dir1/dir2_link/dir3/../../../dir1/dir2").getCanonicalFile().toString());
assertEquals(root + "/dir1/dir2", new File(root + "/dir1/../dir1/dir2_link/../dir2").getCanonicalFile().toString());
assertEquals(root, new File(root + "/dir1/dir1_link/../").getCanonicalFile().toString());
assertEquals(root + "/dir1", new File(root + "/dir1/dir1_link/../dir1").getCanonicalFile().toString());
assertEquals(root + "/dir1", new File(root + "/dir1/dir1_link/../../root/dir1").getCanonicalFile().toString());
assertEquals(root + "/dir1", new File(root + "/dir1/dir1_link/dir2/../../dir1").getCanonicalFile().toString());
assertEquals(root + "/dir1", new File(root + "/dir1/dir1_link/dir2/../../../root/dir1").getCanonicalFile().toString());
assertEquals(root + "/dir1", new File(root + "/dir1/../dir1/dir1_link/../dir1").getCanonicalFile().toString());
}
// TODO Test file size.
// @Test
// public void getTotalSpace() throws Exception {
// throw new UnsupportedOperationException();
// }
//
// @Test
// public void getFreeSpace() throws Exception {
// throw new UnsupportedOperationException();
// }
//
// @Test
// public void getUsableSpace() throws Exception {
// throw new UnsupportedOperationException();
// }
//
// @Test
// public void compareTo() throws Exception {
// throw new UnsupportedOperationException();
// }
}

View File

@@ -0,0 +1,205 @@
/*
* Copyright 2025 JetBrains s.r.o.
* 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
* @summary java.io.RandomAccessFileTest uses java.nio.file inside.
* @library testNio
* @run junit/othervm
* -Djava.nio.file.spi.DefaultFileSystemProvider=testNio.ManglingFileSystemProvider
* -Djbr.java.io.use.nio=true
* RandomAccessFileTest
*/
import org.junit.*;
import org.junit.rules.TemporaryFolder;
import testNio.ManglingFileSystemProvider;
import java.io.EOFException;
import java.io.File;
import java.io.RandomAccessFile;
import java.nio.channels.FileChannel;
import java.nio.file.Files;
import java.util.Arrays;
import java.util.Objects;
import static org.junit.Assert.*;
public class RandomAccessFileTest {
@Rule
public TemporaryFolder temporaryFolder = new TemporaryFolder();
@BeforeClass
public static void checkSystemProperties() {
Objects.requireNonNull(System.getProperty("java.nio.file.spi.DefaultFileSystemProvider"));
}
private static void assumeNotWindows() {
Assume.assumeFalse(System.getProperty("os.name").toLowerCase().startsWith("win"));
}
@Before
@After
public void resetFs() {
ManglingFileSystemProvider.resetTricks();
}
@Test
public void getChannel() throws Exception {
File file = temporaryFolder.newFile();
try (RandomAccessFile rac = new RandomAccessFile(file, "r")) {
FileChannel channel = rac.getChannel();
assertTrue(channel.getClass().getName(), channel instanceof testNio.ManglingFileChannel);
}
try (RandomAccessFile rac = new RandomAccessFile(file, "rw")) {
FileChannel channel = rac.getChannel();
assertTrue(channel.getClass().getName(), channel instanceof testNio.ManglingFileChannel);
}
}
@Test
public void getFilePointer() throws Exception {
File file = temporaryFolder.newFile();
try (RandomAccessFile rac = new RandomAccessFile(file, "r")) {
assertEquals(0, rac.getFilePointer());
}
ManglingFileSystemProvider.readExtraFileContent = true;
try (RandomAccessFile rac = new RandomAccessFile(file, "r")) {
rac.readLine();
assertEquals(ManglingFileSystemProvider.extraContent.length(), rac.getFilePointer());
}
}
@Test
public void length() throws Exception {
File file = temporaryFolder.newFile();
String content = "hello";
Files.writeString(file.toPath(), content);
ManglingFileSystemProvider.readExtraFileContent = true;
try (RandomAccessFile rac = new RandomAccessFile(file, "r")) {
assertEquals(content.length() + ManglingFileSystemProvider.extraContent.length(), rac.length());
}
}
@Test
public void read() throws Exception {
File file = temporaryFolder.newFile();
ManglingFileSystemProvider.readExtraFileContent = true;
try (RandomAccessFile rac = new RandomAccessFile(file, "r")) {
String extraContent = ManglingFileSystemProvider.extraContent;
for (int i = 0; i < extraContent.length(); i++) {
assertEquals((int) extraContent.charAt(i), rac.read());
}
assertEquals(-1, rac.read());
}
}
@Test
public void readFully() throws Exception {
File file = temporaryFolder.newFile();
Files.writeString(file.toPath(), "adapters everywhere");
ManglingFileSystemProvider.mangleFileContent = true;
try (RandomAccessFile rac = new RandomAccessFile(file, "r")) {
byte[] data = new byte[10];
rac.readFully(data);
assertEquals("4d4p73r5 3", new String(data));
Arrays.fill(data, (byte)' ');
try {
rac.readFully(data);
fail("An error should have been thrown but wasn't");
} catch (EOFException ignored) {
// Nothing.
}
assertEquals("v3rywh3r3 ", new String(data));
}
}
@Test
public void seek() throws Exception {
File file = temporaryFolder.newFile();
ManglingFileSystemProvider.readExtraFileContent = true;
try (RandomAccessFile rac = new RandomAccessFile(file, "r")) {
String extraContent = ManglingFileSystemProvider.extraContent;
for (int i = extraContent.length() - 1; i >= 0; i--) {
rac.seek(i);
assertEquals(extraContent.charAt(i), (char) rac.readByte());
}
}
}
@Test
public void setLength() throws Exception {
String extraContent = ManglingFileSystemProvider.extraContent;
assertTrue(extraContent.length() > 2);
File file = temporaryFolder.newFile();
String content = "hello";
Files.writeString(file.toPath(), content);
ManglingFileSystemProvider.mangleFileContent = true;
ManglingFileSystemProvider.readExtraFileContent = true;
try (RandomAccessFile rac = new RandomAccessFile(file, "rw")) {
rac.setLength(content.length() + 2);
rac.seek(0);
assertEquals(rac.readLine(), "h3110" + extraContent.substring(0, 2));
}
// TODO Bigger length.
}
@Test
public void writeSingleByte() throws Exception {
File file = temporaryFolder.newFile();
ManglingFileSystemProvider.mangleFileContent = true;
try (RandomAccessFile rac = new RandomAccessFile(file, "rw")) {
rac.write('f');
rac.write('o');
rac.write('o');
rac.write('b');
rac.write('a');
rac.write('r');
}
ManglingFileSystemProvider.mangleFileContent = false;
assertEquals("f00b4r", Files.readString(file.toPath()));
}
@Test
public void writeBytes() throws Exception {
File file = temporaryFolder.newFile();
ManglingFileSystemProvider.mangleFileContent = true;
try (RandomAccessFile rac = new RandomAccessFile(file, "rw")) {
rac.writeBytes("herpderp");
}
ManglingFileSystemProvider.mangleFileContent = false;
assertEquals("h3rpd3rp", Files.readString(file.toPath()));
}
}

View File

@@ -0,0 +1,56 @@
/*
* Copyright 2025 JetBrains s.r.o.
* 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.
*/
package testNio;
import java.io.IOException;
import java.nio.file.AccessDeniedException;
import java.nio.file.attribute.BasicFileAttributeView;
import java.nio.file.attribute.BasicFileAttributes;
import java.nio.file.attribute.FileTime;
class ManglingBasicFileAttributeView implements BasicFileAttributeView {
private final BasicFileAttributeView view;
ManglingBasicFileAttributeView(BasicFileAttributeView view) {
this.view = view;
}
@Override
public String name() {
return view.name();
}
@Override
public BasicFileAttributes readAttributes() throws IOException {
return new ManglingBasicFileAttributes(view.readAttributes());
}
@Override
public void setTimes(FileTime lastModifiedTime, FileTime lastAccessTime, FileTime createTime) throws IOException {
if (ManglingFileSystemProvider.prohibitFileTreeModifications) {
throw new AccessDeniedException(null, null, "Test: Prohibiting file tree modifications");
}
view.setTimes(lastModifiedTime, lastAccessTime, createTime);
}
}

View File

@@ -0,0 +1,80 @@
/*
* Copyright 2025 JetBrains s.r.o.
* 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.
*/
package testNio;
import java.nio.file.attribute.BasicFileAttributes;
import java.nio.file.attribute.FileTime;
class ManglingBasicFileAttributes implements BasicFileAttributes {
private final BasicFileAttributes attrs;
ManglingBasicFileAttributes(BasicFileAttributes attrs) {
this.attrs = attrs;
}
@Override
public FileTime lastModifiedTime() {
return attrs.lastModifiedTime();
}
@Override
public FileTime lastAccessTime() {
return attrs.lastAccessTime();
}
@Override
public FileTime creationTime() {
return attrs.creationTime();
}
@Override
public boolean isRegularFile() {
return attrs.isRegularFile() && !ManglingFileSystemProvider.allFilesAreEmptyDirectories;
}
@Override
public boolean isDirectory() {
return attrs.isDirectory() || ManglingFileSystemProvider.allFilesAreEmptyDirectories;
}
@Override
public boolean isSymbolicLink() {
return attrs.isSymbolicLink() && !ManglingFileSystemProvider.denyAccessToEverything;
}
@Override
public boolean isOther() {
return attrs.isOther() && !ManglingFileSystemProvider.denyAccessToEverything;
}
@Override
public long size() {
return attrs.size();
}
@Override
public Object fileKey() {
return attrs.fileKey();
}
}

View File

@@ -0,0 +1,79 @@
/*
* Copyright 2025 JetBrains s.r.o.
* 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.
*/
package testNio;
import java.io.IOException;
import java.nio.file.DirectoryStream;
import java.nio.file.Path;
import java.util.Iterator;
import java.util.NoSuchElementException;
class ManglingDirectoryStream implements DirectoryStream<Path> {
private final ManglingFileSystem manglingFileSystem;
private final ManglingPath dir;
private final DirectoryStream<Path> delegateResult;
private boolean addEliteElement = ManglingFileSystemProvider.addEliteToEveryDirectoryListing;
public ManglingDirectoryStream(ManglingFileSystem manglingFileSystem, ManglingPath dir, DirectoryStream<Path> delegateResult) {
this.manglingFileSystem = manglingFileSystem;
this.dir = dir;
this.delegateResult = delegateResult;
}
@Override
public void close() throws IOException {
delegateResult.close();
}
@Override
public Iterator<Path> iterator() {
return new ManglingPathIterator();
}
class ManglingPathIterator implements Iterator<Path> {
private final Iterator<Path> delegateIterator = delegateResult.iterator();
@Override
public boolean hasNext() {
if (delegateIterator.hasNext()) return true;
if (addEliteElement) {
addEliteElement = false;
return true;
}
return false;
}
@Override
public Path next() {
try {
return new ManglingPath(manglingFileSystem, delegateIterator.next());
} catch (NoSuchElementException err) {
if (ManglingFileSystemProvider.addEliteToEveryDirectoryListing) {
return dir.resolve("37337");
}
throw err;
}
}
}
}

View File

@@ -0,0 +1,75 @@
/*
* Copyright 2025 JetBrains s.r.o.
* 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.
*/
package testNio;
import java.io.IOException;
import java.nio.file.AccessDeniedException;
import java.nio.file.attribute.DosFileAttributeView;
import java.nio.file.attribute.DosFileAttributes;
class ManglingDosFileAttributeView extends ManglingBasicFileAttributeView implements DosFileAttributeView {
private final DosFileAttributeView view;
ManglingDosFileAttributeView(DosFileAttributeView view) {
super(view);
this.view = view;
}
@Override
public DosFileAttributes readAttributes() throws IOException {
return new ManglingDosFileAttributes(view.readAttributes());
}
@Override
public void setReadOnly(boolean value) throws IOException {
if (ManglingFileSystemProvider.prohibitFileTreeModifications) {
throw new AccessDeniedException(null, null, "Test: Prohibiting file tree modifications");
}
view.setReadOnly(value);
}
@Override
public void setHidden(boolean value) throws IOException {
if (ManglingFileSystemProvider.prohibitFileTreeModifications) {
throw new AccessDeniedException(null, null, "Test: Prohibiting file tree modifications");
}
view.setHidden(value);
}
@Override
public void setSystem(boolean value) throws IOException {
if (ManglingFileSystemProvider.prohibitFileTreeModifications) {
throw new AccessDeniedException(null, null, "Test: Prohibiting file tree modifications");
}
view.setSystem(value);
}
@Override
public void setArchive(boolean value) throws IOException {
if (ManglingFileSystemProvider.prohibitFileTreeModifications) {
throw new AccessDeniedException(null, null, "Test: Prohibiting file tree modifications");
}
view.setArchive(value);
}
}

View File

@@ -0,0 +1,55 @@
/*
* Copyright 2025 JetBrains s.r.o.
* 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.
*/
package testNio;
import java.nio.file.attribute.DosFileAttributes;
class ManglingDosFileAttributes extends ManglingBasicFileAttributes implements DosFileAttributes {
private final DosFileAttributes attrs;
ManglingDosFileAttributes(DosFileAttributes attrs) {
super(attrs);
this.attrs = attrs;
}
@Override
public boolean isReadOnly() {
return attrs.isReadOnly() && !ManglingFileSystemProvider.denyAccessToEverything;
}
@Override
public boolean isHidden() {
return attrs.isHidden();
}
@Override
public boolean isArchive() {
return attrs.isArchive();
}
@Override
public boolean isSystem() {
return attrs.isSystem();
}
}

View File

@@ -0,0 +1,205 @@
/*
* Copyright 2025 JetBrains s.r.o.
* 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.
*/
package testNio;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.channels.FileLock;
import java.nio.channels.ReadableByteChannel;
import java.nio.channels.WritableByteChannel;
import java.nio.charset.StandardCharsets;
public class ManglingFileChannel extends FileChannel {
private final FileChannel delegateChannel;
private byte[] extraBytes;
private int extraBytesPosition = -1;
public ManglingFileChannel(FileChannel delegateChannel) {
this.delegateChannel = delegateChannel;
if (ManglingFileSystemProvider.readExtraFileContent) {
extraBytes = ManglingFileSystemProvider.extraContent.getBytes(StandardCharsets.UTF_8);
} else {
extraBytes = new byte[0];
}
}
@Override
public int read(ByteBuffer dst) throws IOException {
int read = delegateChannel.read(dst);
if (ManglingFileSystemProvider.mangleFileContent) {
for (int i = 0; i < read; i++) {
dst.put(i, (byte) ManglingFileSystemProvider.mangle(dst.get(i)));
}
}
if (!dst.hasRemaining()) {
return read;
}
if (extraBytesPosition == -1) {
extraBytesPosition = 0;
}
if (extraBytesPosition < extraBytes.length) {
if (read == -1) {
read = 0;
}
do {
dst.put(extraBytes[extraBytesPosition++]);
read++;
} while (dst.hasRemaining() && extraBytesPosition < extraBytes.length);
}
return read;
}
@Override
public long read(ByteBuffer[] dsts, int offset, int length) throws IOException {
throw new UnsupportedOperationException();
}
@Override
public int write(ByteBuffer src) throws IOException {
if (ManglingFileSystemProvider.mangleFileContent) {
byte[] srcArray = new byte[src.remaining()];
src.get(srcArray);
src = ByteBuffer.wrap(ManglingFileSystemProvider.mangle(srcArray));
}
return delegateChannel.write(src);
}
@Override
public long write(ByteBuffer[] srcs, int offset, int length) throws IOException {
throw new UnsupportedOperationException();
}
@Override
public long position() throws IOException {
long delegatePosition = delegateChannel.position();
if (extraBytesPosition != -1) {
return extraBytesPosition + delegatePosition;
}
return delegatePosition;
}
@Override
public FileChannel position(long newPosition) throws IOException {
long size = delegateChannel.size();
if (newPosition > size) {
assert newPosition <= size + extraBytes.length;
delegateChannel.position(size);
extraBytesPosition = (int)(newPosition - size);
} else {
extraBytesPosition = -1;
delegateChannel.position(newPosition);
}
return this;
}
@Override
public long size() throws IOException {
return delegateChannel.size() + extraBytes.length;
}
@Override
public FileChannel truncate(long size) throws IOException {
long actualSize = delegateChannel.size();
if (size < actualSize) {
delegateChannel.truncate(size);
extraBytesPosition = -1;
} else {
size -= actualSize;
if (size < extraBytes.length) {
byte[] newExtraBytes = new byte[(int)size];
System.arraycopy(extraBytes, 0, newExtraBytes, 0, (int)size);
extraBytes = newExtraBytes;
extraBytesPosition = Math.min(extraBytesPosition, extraBytes.length - 1);
}
}
return this;
}
@Override
public void force(boolean metaData) throws IOException {
delegateChannel.force(metaData);
}
@Override
public long transferTo(long position, long count, WritableByteChannel target) throws IOException {
throw new UnsupportedOperationException();
}
@Override
public long transferFrom(ReadableByteChannel src, long position, long count) throws IOException {
throw new UnsupportedOperationException();
}
@Override
public int read(ByteBuffer dst, long position) throws IOException {
int read = delegateChannel.read(dst, position);
if (ManglingFileSystemProvider.mangleFileContent) {
for (int i = 0; i < dst.remaining(); i++) {
dst.put(i, (byte) ManglingFileSystemProvider.mangle(dst.get(i)));
}
}
if (dst.hasRemaining() && extraBytesPosition == -1) {
extraBytesPosition = 0;
}
while (dst.hasRemaining() && extraBytesPosition < extraBytes.length) {
dst.put(extraBytes[extraBytesPosition++]);
read++;
}
return read;
}
@Override
public int write(ByteBuffer src, long position) throws IOException {
if (ManglingFileSystemProvider.mangleFileContent) {
byte[] srcArray = new byte[src.remaining()];
src.get(srcArray);
src = ByteBuffer.wrap(ManglingFileSystemProvider.mangle(srcArray));
return delegateChannel.write(src);
}
return delegateChannel.write(src, position);
}
@Override
public MappedByteBuffer map(MapMode mode, long position, long size) throws IOException {
throw new UnsupportedOperationException();
}
@Override
public FileLock lock(long position, long size, boolean shared) throws IOException {
return delegateChannel.lock(position, size, shared);
}
@Override
public FileLock tryLock(long position, long size, boolean shared) throws IOException {
return delegateChannel.tryLock(position, size, shared);
}
@Override
protected void implCloseChannel() throws IOException {
extraBytesPosition = -1;
delegateChannel.close();
}
}

View File

@@ -0,0 +1,111 @@
/*
* Copyright 2025 JetBrains s.r.o.
* 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.
*/
package testNio;
import java.io.IOException;
import java.nio.file.*;
import java.nio.file.attribute.UserPrincipalLookupService;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
public class ManglingFileSystem extends FileSystem {
private final ManglingFileSystemProvider provider;
public ManglingFileSystem(ManglingFileSystemProvider fileSystemProvider) {
this.provider = fileSystemProvider;
}
@Override
public ManglingFileSystemProvider provider() {
return provider;
}
@Override
public void close() throws IOException {
provider.defaultFs.close();
}
@Override
public boolean isOpen() {
return provider.defaultFs.isOpen();
}
@Override
public boolean isReadOnly() {
return provider.defaultFs.isReadOnly();
}
@Override
public String getSeparator() {
return provider.defaultFs.getSeparator();
}
@Override
public Iterable<Path> getRootDirectories() {
Iterable<Path> delegateRoots = provider.defaultFs.getRootDirectories();
List<Path> result = new ArrayList<>();
for (Path delegateRoot : delegateRoots) {
result.add(new ManglingPath(this, delegateRoot));
}
if (ManglingFileSystemProvider.addEliteToEveryDirectoryListing) {
if (getSeparator().equals("/")) {
result.add(new ManglingPath(this, Paths.get("/31337")));
} else {
result.add(new ManglingPath(this, Paths.get("\\\\31337\\")));
}
}
return result;
}
@Override
public Iterable<FileStore> getFileStores() {
return provider.defaultFs.getFileStores();
}
@Override
public Set<String> supportedFileAttributeViews() {
return provider.defaultFs.supportedFileAttributeViews();
}
@Override
public Path getPath(final String first, final String... more) {
return new ManglingPath(this, provider.defaultFs.getPath(first, more));
}
@Override
public PathMatcher getPathMatcher(String syntaxAndPattern) {
throw new UnsupportedOperationException();
}
@Override
public UserPrincipalLookupService getUserPrincipalLookupService() {
throw new UnsupportedOperationException();
}
@Override
public WatchService newWatchService() throws IOException {
throw new UnsupportedOperationException();
}
}

View File

@@ -0,0 +1,306 @@
/*
* Copyright 2025 JetBrains s.r.o.
* 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.
*/
package testNio;
import java.io.IOException;
import java.net.URI;
import java.nio.channels.*;
import java.nio.file.*;
import java.nio.file.attribute.*;
import java.nio.file.spi.FileSystemProvider;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
public class ManglingFileSystemProvider extends FileSystemProvider {
final FileSystemProvider defaultProvider;
final FileSystem defaultFs;
final ManglingFileSystem manglingFs = new ManglingFileSystem(this);
;
public static boolean mangleFileContent = false;
public static boolean readExtraFileContent = false;
public static boolean manglePaths = false;
public static boolean mangleOnlyFileName = false;
public static boolean denyAccessToEverything = false;
public static boolean allFilesAreEmptyDirectories = false;
public static boolean prohibitFileTreeModifications = false;
public static boolean addEliteToEveryDirectoryListing = false;
public static final String extraContent = "3x7r4";
public static void resetTricks() {
mangleFileContent = false;
readExtraFileContent = false;
manglePaths = false;
mangleOnlyFileName = false;
denyAccessToEverything = false;
allFilesAreEmptyDirectories = false;
prohibitFileTreeModifications = false;
addEliteToEveryDirectoryListing = false;
}
static {
// TODO It's a tricky workaround. Would be better to get rid of it.
Class<?> ignored = ManglingPath.class;
ignored = ManglingBasicFileAttributeView.class;
ignored = ManglingBasicFileAttributes.class;
ignored = ManglingDirectoryStream.ManglingPathIterator.class;
ignored = ManglingDirectoryStream.class;
ignored = ManglingDosFileAttributeView.class;
ignored = ManglingFileChannel.class;
ignored = ManglingPosixFileAttributeView.class;
ignored = ManglingPosixFileAttributes.class;
}
private final static byte[] manglingTable = new byte[256];
static {
for (int i = 0; i < manglingTable.length; i++) {
manglingTable[i] = (byte) i;
}
manglingTable['A'] = '4';
manglingTable['a'] = '4';
manglingTable['E'] = '3';
manglingTable['e'] = '3';
manglingTable['G'] = '9';
manglingTable['g'] = '9';
manglingTable['I'] = '1';
manglingTable['i'] = '1';
manglingTable['L'] = '1';
manglingTable['l'] = '1';
manglingTable['O'] = '0';
manglingTable['o'] = '0';
manglingTable['S'] = '5';
manglingTable['s'] = '5';
manglingTable['T'] = '7';
manglingTable['t'] = '7';
manglingTable['Z'] = '2';
manglingTable['z'] = '2';
assert mangle("eleet haxor").equals("31337 h4x0r");
}
public static String mangle(String source) {
StringBuilder sb = new StringBuilder();
for (char c : source.toCharArray()) {
if ((int) c < manglingTable.length) {
sb.append((char) manglingTable[c]);
} else {
sb.append(c);
}
}
return sb.toString();
}
public static byte[] mangle(byte[] source) {
byte[] result = new byte[source.length];
for (int i = 0; i < source.length; i++) {
result[i] = manglingTable[source[i]];
}
return result;
}
public static int mangle(int source) {
if (source >= 0 && source < manglingTable.length) {
return manglingTable[source];
}
return source;
}
public ManglingFileSystemProvider(FileSystemProvider defaultProvider) {
super();
this.defaultProvider = defaultProvider;
this.defaultFs = defaultProvider.getFileSystem(URI.create("file:/"));
}
@Override
public String getScheme() {
return defaultProvider.getScheme();
}
@Override
public FileSystem newFileSystem(URI uri, Map<String, ?> env) throws IOException {
throw new UnsupportedOperationException();
}
@Override
public FileSystem getFileSystem(URI uri) {
URI rootUri = URI.create("file:/");
if (!Objects.equals(uri, rootUri)) {
throw new UnsupportedOperationException();
}
return manglingFs;
}
@Override
public Path getPath(URI uri) {
throw new UnsupportedOperationException();
}
static Path unwrap(Path path) {
if (path instanceof ManglingPath pcp) {
return pcp.delegate;
}
throw new IllegalArgumentException("Wrong path " + path + " (class: " + path.getClass() + ")");
}
@Override
public FileChannel newFileChannel(Path path, Set<? extends OpenOption> options, FileAttribute<?>... attrs) throws IOException {
return new ManglingFileChannel(defaultProvider.newFileChannel(unwrap(path), options, attrs));
}
@Override
public SeekableByteChannel newByteChannel(Path path, Set<? extends OpenOption> options, FileAttribute<?>... attrs) throws IOException {
return newFileChannel(path, options, attrs);
}
@Override
public DirectoryStream<Path> newDirectoryStream(Path dir, DirectoryStream.Filter<? super Path> filter) throws IOException {
return new ManglingDirectoryStream(manglingFs, (ManglingPath) dir, defaultProvider.newDirectoryStream(unwrap(dir), filter));
}
@Override
public void createDirectory(Path dir, FileAttribute<?>... attrs) throws IOException {
if (prohibitFileTreeModifications) {
throw new AccessDeniedException(dir.toString(), null, "Test: All file tree modifications are prohibited");
}
defaultProvider.createDirectory(unwrap(dir), attrs);
}
@Override
public void delete(Path path) throws IOException {
if (prohibitFileTreeModifications) {
throw new AccessDeniedException(path.toString(), null, "Test: All file tree modifications are prohibited");
}
defaultProvider.delete(unwrap(path));
}
@Override
public void copy(Path source, Path target, CopyOption... options) throws IOException {
if (prohibitFileTreeModifications) {
throw new AccessDeniedException(source.toString(), null, "Test: All file tree modifications are prohibited");
}
Path source2 = unwrap(source);
Path target2 = unwrap(target);
defaultProvider.copy(source2, target2, options);
}
@Override
public void move(Path source, Path target, CopyOption... options) throws IOException {
if (prohibitFileTreeModifications) {
throw new AccessDeniedException(source.toString(), null, "Test: All file tree modifications are prohibited");
}
Path source2 = unwrap(source);
Path target2 = unwrap(target);
defaultProvider.move(source2, target2, options);
}
@Override
public boolean isSameFile(Path path, Path path2) throws IOException {
Path source2 = unwrap(path);
Path target2 = unwrap(path2);
return defaultProvider.isSameFile(source2, target2);
}
@Override
public boolean isHidden(Path path) throws IOException {
return defaultProvider.isHidden(unwrap(path));
}
@Override
public FileStore getFileStore(Path path) throws IOException {
return defaultProvider.getFileStore(unwrap(path));
}
@Override
public void checkAccess(Path path, AccessMode... modes) throws IOException {
defaultProvider.checkAccess(unwrap(path), modes);
if (denyAccessToEverything) {
throw new AccessDeniedException(path.toString(), null, "Test: No access rules to anything");
}
}
@Override
public <V extends FileAttributeView> V getFileAttributeView(Path path, Class<V> type, LinkOption... options) {
return wrap(defaultProvider.getFileAttributeView(unwrap(path), type, options));
}
@Override
public <A extends BasicFileAttributes> A readAttributes(Path path, Class<A> type, LinkOption... options) throws IOException {
return wrap(defaultProvider.readAttributes(unwrap(path), type, options));
}
@Override
public Map<String, Object> readAttributes(Path path, String attributes, LinkOption... options) throws IOException {
throw new UnsupportedOperationException();
}
@Override
public void setAttribute(Path path, String attribute, Object value, LinkOption... options) throws IOException {
if (prohibitFileTreeModifications) {
throw new AccessDeniedException(path.toString(), null, "Test: All file tree modifications are prohibited");
}
defaultProvider.setAttribute(unwrap(path), attribute, value, options);
}
@Override
public boolean exists(Path path, LinkOption... options) {
return defaultProvider.exists(unwrap(path), options);
}
@Override
public void createSymbolicLink(Path link, Path target, FileAttribute<?>... attrs) throws IOException {
if (prohibitFileTreeModifications) {
throw new AccessDeniedException(link.toString(), null, "Test: All file tree modifications are prohibited");
}
defaultProvider.createSymbolicLink(unwrap(link), unwrap(target), attrs);
}
private <A extends BasicFileAttributes> A wrap(A attrs) {
if (attrs instanceof DosFileAttributes dosAttrs) {
return (A) new ManglingDosFileAttributes(dosAttrs);
}
if (attrs instanceof PosixFileAttributes posixAttrs) {
return (A) new ManglingPosixFileAttributes(posixAttrs);
}
if (attrs instanceof BasicFileAttributes basicAttrs) {
return (A) new ManglingBasicFileAttributes(basicAttrs);
}
return attrs;
}
private <V extends FileAttributeView> V wrap(V view) {
if (view instanceof DosFileAttributeView dosView) {
return (V) new ManglingDosFileAttributeView(dosView);
}
if (view instanceof PosixFileAttributeView posixView) {
return (V) new ManglingPosixFileAttributeView(posixView);
}
if (view instanceof BasicFileAttributeView basicView) {
return (V) new ManglingBasicFileAttributeView(basicView);
}
return view;
}
}

View File

@@ -0,0 +1,179 @@
/*
* Copyright 2025 JetBrains s.r.o.
* 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.
*/
package testNio;
import java.io.IOException;
import java.net.URI;
import java.nio.file.*;
import java.util.Objects;
public class ManglingPath implements Path {
private final ManglingFileSystem fileSystem;
final Path delegate;
ManglingPath(ManglingFileSystem fileSystem, Path delegate) {
this.fileSystem = fileSystem;
this.delegate = Objects.requireNonNull(delegate);
}
@Override
public boolean equals(Object o) {
if (o == null || getClass() != o.getClass()) return false;
ManglingPath paths = (ManglingPath) o;
return Objects.equals(fileSystem, paths.fileSystem) && Objects.equals(delegate, paths.delegate);
}
@Override
public int hashCode() {
return Objects.hash(fileSystem, delegate);
}
@Override
public String toString() {
if (!ManglingFileSystemProvider.manglePaths) {
return delegate.toString();
} else if (!ManglingFileSystemProvider.mangleOnlyFileName) {
return ManglingFileSystemProvider.mangle(delegate.toString());
} else if (delegate.getParent() == null) {
return ManglingFileSystemProvider.mangle(delegate.toString());
} else {
String mangledFileName = ManglingFileSystemProvider.mangle(delegate.getFileName().toString());
return delegate.getParent().resolve(mangledFileName).toString();
}
}
@Override
public FileSystem getFileSystem() {
return fileSystem;
}
@Override
public boolean isAbsolute() {
return delegate.isAbsolute();
}
@Override
public Path getRoot() {
Path delegateRoot = delegate.getRoot();
if (delegateRoot == null) {
return null;
}
return new ManglingPath(fileSystem, delegateRoot);
}
@Override
public Path getFileName() {
return new ManglingPath(fileSystem, delegate.getFileName());
}
@Override
public Path getParent() {
Path parent = delegate.getParent();
if (parent != null) {
return new ManglingPath(fileSystem, parent);
} else {
return null;
}
}
@Override
public int getNameCount() {
return delegate.getNameCount();
}
@Override
public Path getName(int index) {
return new ManglingPath(fileSystem, delegate.getName(index));
}
@Override
public Path subpath(int beginIndex, int endIndex) {
return new ManglingPath(fileSystem, delegate.subpath(beginIndex, endIndex));
}
@Override
public boolean startsWith(Path other) {
if (other instanceof ManglingPath pcp) {
return delegate.startsWith(pcp.delegate);
}
throw new IllegalArgumentException("Wrong path " + other + " (class: " + other.getClass() + ")");
}
@Override
public boolean endsWith(Path other) {
if (other instanceof ManglingPath pcp) {
return delegate.endsWith(pcp.delegate);
}
throw new IllegalArgumentException("Wrong path " + other + " (class: " + other.getClass() + ")");
}
@Override
public Path normalize() {
return new ManglingPath(fileSystem, delegate.normalize());
}
@Override
public Path resolve(Path other) {
if (other instanceof ManglingPath pcp) {
return new ManglingPath(fileSystem, delegate.resolve(pcp.delegate));
}
throw new IllegalArgumentException("Wrong path " + other + " (class: " + other.getClass() + ")");
}
@Override
public Path relativize(Path other) {
if (other instanceof ManglingPath pcp) {
return new ManglingPath(fileSystem, delegate.relativize(pcp.delegate));
}
throw new IllegalArgumentException("Wrong path " + other + " (class: " + other.getClass() + ")");
}
@Override
public URI toUri() {
return delegate.toUri();
}
@Override
public Path toAbsolutePath() {
return new ManglingPath(fileSystem, delegate.toAbsolutePath());
}
@Override
public Path toRealPath(LinkOption... options) throws IOException {
return new ManglingPath(fileSystem, delegate.toRealPath(options));
}
@Override
public WatchKey register(WatchService watcher, WatchEvent.Kind<?>[] events, WatchEvent.Modifier... modifiers) throws IOException {
throw new UnsupportedOperationException();
}
@Override
public int compareTo(Path other) {
if (other instanceof ManglingPath pcp) {
return delegate.compareTo(pcp.delegate);
}
return -1;
}
}

View File

@@ -0,0 +1,66 @@
/*
* Copyright 2025 JetBrains s.r.o.
* 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.
*/
package testNio;
import java.io.IOException;
import java.nio.file.AccessDeniedException;
import java.nio.file.attribute.*;
import java.util.Set;
class ManglingPosixFileAttributeView extends ManglingBasicFileAttributeView implements PosixFileAttributeView {
private final PosixFileAttributeView view;
ManglingPosixFileAttributeView(PosixFileAttributeView view) {
super(view);
this.view = view;
}
@Override
public PosixFileAttributes readAttributes() throws IOException {
return new ManglingPosixFileAttributes(view.readAttributes());
}
@Override
public void setPermissions(Set<PosixFilePermission> perms) throws IOException {
if (ManglingFileSystemProvider.prohibitFileTreeModifications) {
throw new AccessDeniedException(null, null, "Test: Prohibiting file tree modifications");
}
view.setPermissions(perms);
}
@Override
public void setGroup(GroupPrincipal group) throws IOException {
view.setGroup(group);
}
@Override
public UserPrincipal getOwner() throws IOException {
return view.getOwner();
}
@Override
public void setOwner(UserPrincipal owner) throws IOException {
view.setOwner(owner);
}
}

View File

@@ -0,0 +1,57 @@
/*
* Copyright 2025 JetBrains s.r.o.
* 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.
*/
package testNio;
import java.nio.file.attribute.GroupPrincipal;
import java.nio.file.attribute.PosixFileAttributes;
import java.nio.file.attribute.PosixFilePermission;
import java.nio.file.attribute.UserPrincipal;
import java.util.Set;
class ManglingPosixFileAttributes extends ManglingBasicFileAttributes implements PosixFileAttributes {
private final PosixFileAttributes attrs;
ManglingPosixFileAttributes(PosixFileAttributes attrs) {
super(attrs);
this.attrs = attrs;
}
@Override
public UserPrincipal owner() {
return attrs.owner();
}
@Override
public GroupPrincipal group() {
return attrs.group();
}
@Override
public Set<PosixFilePermission> permissions() {
if (ManglingFileSystemProvider.denyAccessToEverything) {
return Set.of();
}
return attrs.permissions();
}
}

View File

@@ -36,8 +36,8 @@
* @requires !vm.musl
* @requires vm.flagless
* @library /test/lib
* @run main/othervm/native/timeout=300 -Djava.security.manager=allow Basic
* @run main/othervm/native/timeout=300 -Djava.security.manager=allow -Djdk.lang.Process.launchMechanism=fork Basic
* @run main/othervm/native/timeout=300 -Djbr.java.io.use.nio=false -Djava.security.manager=allow Basic
* @run main/othervm/native/timeout=300 -Djbr.java.io.use.nio=false -Djava.security.manager=allow -Djdk.lang.Process.launchMechanism=fork Basic
* @author Martin Buchholz
*/
@@ -48,7 +48,7 @@
* java.base/jdk.internal.misc
* @requires (os.family == "linux" & !vm.musl)
* @library /test/lib
* @run main/othervm/timeout=300 -Djava.security.manager=allow -Djdk.lang.Process.launchMechanism=posix_spawn Basic
* @run main/othervm/timeout=300 -Djbr.java.io.use.nio=false -Djava.security.manager=allow -Djdk.lang.Process.launchMechanism=posix_spawn Basic
*/
import java.lang.ProcessBuilder.Redirect;

View File

@@ -26,6 +26,7 @@
* @bug 4244896
* @summary Test for the various platform specific implementations of
* destroyForcibly.
* @run main/othervm -Djbr.java.io.use.nio=false DestroyTest
*/
import java.io.BufferedReader;

View File

@@ -30,16 +30,17 @@
*/
import java.io.IOException;
import java.io.File;
import java.nio.file.FileAlreadyExistsException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;
import java.nio.file.attribute.AclEntry;
import java.nio.file.attribute.AclEntryPermission;
import java.nio.file.attribute.AclEntryType;
import java.nio.file.attribute.AclFileAttributeView;
import java.nio.file.attribute.DosFileAttributeView;
import java.nio.file.attribute.UserPrincipal;
import java.util.EnumSet;
import java.util.List;
import jdk.test.lib.Platform;
@@ -55,6 +56,7 @@ public class Misc {
testIsSameFile(dir);
testFileTypeMethods(dir);
testAccessMethods(dir);
testEmptyPathForByteStream();
} finally {
TestUtil.removeAll(dir);
}
@@ -360,6 +362,18 @@ public class Misc {
}
}
static void testEmptyPathForByteStream() throws IOException {
Path emptyPath = Path.of("");
try {
emptyPath.getFileSystem().provider()
.newByteChannel(emptyPath, EnumSet.of(StandardOpenOption.WRITE, StandardOpenOption.CREATE_NEW))
.close();
throw new RuntimeException("FileAlreadyExistsException was not thrown");
} catch (FileAlreadyExistsException e) {
// The expected behavior.
}
}
static void assertTrue(boolean okay) {
if (!okay)
throw new RuntimeException("Assertion Failed");

View File

@@ -45,8 +45,8 @@ import jdk.test.lib.RandomFactory;
* @library /test/lib
* @build TestMaxCachedBufferSize
* @run main/othervm/timeout=150 TestMaxCachedBufferSize
* @run main/othervm/timeout=150 -Djdk.nio.maxCachedBufferSize=0 TestMaxCachedBufferSize
* @run main/othervm/timeout=150 -Djdk.nio.maxCachedBufferSize=2000 TestMaxCachedBufferSize
* @run main/othervm/timeout=150 -Djbr.java.io.use.nio=false -Djdk.nio.maxCachedBufferSize=0 TestMaxCachedBufferSize
* @run main/othervm/timeout=150 -Djbr.java.io.use.nio=false -Djdk.nio.maxCachedBufferSize=2000 TestMaxCachedBufferSize
* @run main/othervm/timeout=150 -Djdk.nio.maxCachedBufferSize=100000 TestMaxCachedBufferSize
* @run main/othervm/timeout=150 -Djdk.nio.maxCachedBufferSize=10000000 TestMaxCachedBufferSize
* @summary Test the implementation of the jdk.nio.maxCachedBufferSize property