mirror of
https://github.com/JetBrains/JetBrainsRuntime.git
synced 2025-12-06 01:19:28 +01:00
Reapply "JBR-9531 Prevent unexpected recursive usage of java.io over nio wrappers"
This reverts commit 77da73f22f.
This commit is contained in:
@@ -159,5 +159,84 @@ public class IoOverNio {
|
||||
* <p>
|
||||
* The problem was found with the test {@code jtreg:test/jdk/java/io/FileDescriptor/Sharing.java}.
|
||||
*/
|
||||
public static final ThreadLocal<Closeable> PARENT_FOR_FILE_CHANNEL_IMPL = new ThreadLocal<>();
|
||||
public static class ParentForFileChannelImplHolder {
|
||||
private static final ThreadLocal<Closeable> holder = new ThreadLocal<>();
|
||||
|
||||
private ParentForFileChannelImplHolder() {}
|
||||
|
||||
public static Closeable get() {
|
||||
return holder.get();
|
||||
}
|
||||
|
||||
public static void set(Closeable parent) {
|
||||
RecursionGuard.ensureActive();
|
||||
holder.set(parent);
|
||||
}
|
||||
|
||||
public static void remove() {
|
||||
RecursionGuard.ensureActive();
|
||||
holder.remove();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>With java.io over java.nio backend, it's possible that some code invokes file system operations while
|
||||
* already executing a similar operation. An example is when a classloader uses {@link FileSystems#getDefault()}
|
||||
* during class loading. Such cases break usage of {@link ParentForFileChannelImplHolder}.</p>
|
||||
*
|
||||
* <p>This class is to be used around places that can hypothetically access {@link ParentForFileChannelImplHolder}
|
||||
* recursively.</p>
|
||||
*/
|
||||
public static class RecursionGuard implements Closeable {
|
||||
private static final ThreadLocal<RecursionGuard> HEAD = new ThreadLocal<>();
|
||||
|
||||
private final RecursionGuard parent;
|
||||
private final Object label;
|
||||
private final ThreadLocalCloseable additionalClosable;
|
||||
|
||||
/**
|
||||
* @param label A unique object for a specific method. The object is used for reference equality.
|
||||
* A static string or a reference to a class is a good candidate.
|
||||
*/
|
||||
public static RecursionGuard create(Object label) {
|
||||
ThreadLocalCloseable additionalClosable = null;
|
||||
for (var guard = HEAD.get(); guard != null; guard = guard.parent) {
|
||||
if (guard.label == label) {
|
||||
additionalClosable = disableInThisThread();
|
||||
break;
|
||||
}
|
||||
}
|
||||
var result = new RecursionGuard(HEAD.get(), label, additionalClosable);
|
||||
HEAD.set(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
private RecursionGuard(RecursionGuard parent, Object label, ThreadLocalCloseable additionalClosable) {
|
||||
this.parent = parent;
|
||||
this.label = label;
|
||||
this.additionalClosable = additionalClosable;
|
||||
}
|
||||
|
||||
public static void ensureActive() {
|
||||
if (HEAD.get() == null) {
|
||||
throw new Error("RecursionGuard is not installed");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
HEAD.set(parent);
|
||||
if (additionalClosable != null) {
|
||||
additionalClosable.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Intended only for suppressing warnings about unused variables.
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public static void blackhole(Object any) {
|
||||
// Nothing here.
|
||||
}
|
||||
}
|
||||
@@ -175,37 +175,40 @@ public class FileInputStream extends InputStream
|
||||
}
|
||||
path = file.getPath();
|
||||
|
||||
java.nio.file.FileSystem nioFs = IoOverNioFileSystem.acquireNioFs(path);
|
||||
Path nioPath = null;
|
||||
if (nioFs != null && path != null) {
|
||||
try {
|
||||
nioPath = nioFs.getPath(path);
|
||||
isRegularFile = Files.isRegularFile(nioPath);
|
||||
} catch (InvalidPathException|SecurityException ignored) {
|
||||
// Nothing.
|
||||
try (var guard = IoOverNio.RecursionGuard.create(FileInputStream.class)) {
|
||||
IoOverNio.blackhole(guard);
|
||||
java.nio.file.FileSystem nioFs = IoOverNioFileSystem.acquireNioFs(path);
|
||||
Path nioPath = null;
|
||||
if (nioFs != null && path != null) {
|
||||
try {
|
||||
nioPath = nioFs.getPath(path);
|
||||
isRegularFile = Files.isRegularFile(nioPath);
|
||||
} catch (InvalidPathException|SecurityException ignored) {
|
||||
// Nothing.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Two significant differences between the legacy java.io and java.nio.files:
|
||||
// * java.nio.file allows to open directories as streams, java.io.FileInputStream doesn't.
|
||||
// * java.nio.file doesn't work well with pseudo devices, i.e., `seek()` fails, while java.io works well.
|
||||
useNio = nioPath != null && isRegularFile == Boolean.TRUE;
|
||||
// Two significant differences between the legacy java.io and java.nio.files:
|
||||
// * java.nio.file allows to open directories as streams, java.io.FileInputStream doesn't.
|
||||
// * java.nio.file doesn't work well with pseudo devices, i.e., `seek()` fails, while java.io works well.
|
||||
useNio = nioPath != null && isRegularFile == Boolean.TRUE;
|
||||
|
||||
if (useNio) {
|
||||
var bundle = IoOverNioFileSystem.initializeStreamUsingNio(
|
||||
this, nioFs, file, nioPath, Set.of(StandardOpenOption.READ), channelCleanable);
|
||||
channel = bundle.channel();
|
||||
fd = bundle.fd();
|
||||
externalChannelHolder = bundle.externalChannelHolder();
|
||||
} else {
|
||||
fd = new FileDescriptor();
|
||||
fd.attach(this);
|
||||
open(path);
|
||||
FileCleanable.register(fd); // open set the fd, register the cleanup
|
||||
externalChannelHolder = null;
|
||||
}
|
||||
if (DEBUG.writeTraces()) {
|
||||
System.err.printf("Created a FileInputStream for %s%n", file);
|
||||
if (useNio) {
|
||||
var bundle = IoOverNioFileSystem.initializeStreamUsingNio(
|
||||
this, nioFs, file, nioPath, Set.of(StandardOpenOption.READ), channelCleanable);
|
||||
channel = bundle.channel();
|
||||
fd = bundle.fd();
|
||||
externalChannelHolder = bundle.externalChannelHolder();
|
||||
} else {
|
||||
fd = new FileDescriptor();
|
||||
fd.attach(this);
|
||||
open(path);
|
||||
FileCleanable.register(fd); // open set the fd, register the cleanup
|
||||
externalChannelHolder = null;
|
||||
}
|
||||
if (DEBUG.writeTraces()) {
|
||||
System.err.printf("Created a FileInputStream for %s%n", file);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -247,45 +247,49 @@ public class FileOutputStream extends OutputStream
|
||||
}
|
||||
|
||||
this.path = name;
|
||||
java.nio.file.FileSystem nioFs = IoOverNioFileSystem.acquireNioFs(path);
|
||||
useNio = path != null && nioFs != null;
|
||||
if (useNio) {
|
||||
Path nioPath = nioFs.getPath(path);
|
||||
try (var guard = IoOverNio.RecursionGuard.create(FileOutputStream.class)) {
|
||||
IoOverNio.blackhole(guard);
|
||||
java.nio.file.FileSystem nioFs = IoOverNioFileSystem.acquireNioFs(path);
|
||||
useNio = path != null && nioFs != null;
|
||||
if (useNio) {
|
||||
Path nioPath = nioFs.getPath(path);
|
||||
|
||||
// java.io backend doesn't open DOS hidden files for writing, but java.nio.file opens.
|
||||
// This code mimics the old behavior.
|
||||
if (nioFs.getSeparator().equals("\\")) {
|
||||
DosFileAttributes attrs = null;
|
||||
try {
|
||||
var view = Files.getFileAttributeView(nioPath, DosFileAttributeView.class);
|
||||
if (view != null) {
|
||||
attrs = view.readAttributes();
|
||||
// java.io backend doesn't open DOS hidden files for writing, but java.nio.file opens.
|
||||
// This code mimics the old behavior.
|
||||
if (nioFs.getSeparator().equals("\\")) {
|
||||
DosFileAttributes attrs = null;
|
||||
try {
|
||||
var view = Files.getFileAttributeView(nioPath, DosFileAttributeView.class);
|
||||
if (view != null) {
|
||||
attrs = view.readAttributes();
|
||||
}
|
||||
} catch (IOException | UnsupportedOperationException | SecurityException ignored) {
|
||||
// Windows paths without DOS attributes? Not a problem in this case.
|
||||
}
|
||||
if (attrs != null && (attrs.isHidden() || attrs.isDirectory())) {
|
||||
throw new FileNotFoundException(file.getPath() + " (Access is denied)");
|
||||
}
|
||||
} catch (IOException | UnsupportedOperationException | SecurityException ignored) {
|
||||
// Windows paths without DOS attributes? Not a problem in this case.
|
||||
}
|
||||
if (attrs != null && (attrs.isHidden() || attrs.isDirectory())) {
|
||||
throw new FileNotFoundException(file.getPath() + " (Access is denied)");
|
||||
}
|
||||
}
|
||||
|
||||
Set<OpenOption> options = append
|
||||
? Set.of(StandardOpenOption.WRITE, StandardOpenOption.CREATE, StandardOpenOption.APPEND)
|
||||
: Set.of(StandardOpenOption.WRITE, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING);
|
||||
var bundle = IoOverNioFileSystem.initializeStreamUsingNio(
|
||||
this, nioFs, file, nioPath, options, channelCleanable);
|
||||
channel = bundle.channel();
|
||||
fd = bundle.fd();
|
||||
externalChannelHolder = bundle.externalChannelHolder();
|
||||
} else {
|
||||
this.fd = new FileDescriptor();
|
||||
fd.attach(this);
|
||||
open(name, append);
|
||||
FileCleanable.register(fd); // open sets the fd, register the cleanup
|
||||
externalChannelHolder = null;
|
||||
}
|
||||
if (DEBUG.writeTraces()) {
|
||||
System.err.printf("Created a FileOutputStream for %s%n", file);
|
||||
Set<OpenOption> options = append
|
||||
? Set.of(StandardOpenOption.WRITE, StandardOpenOption.CREATE, StandardOpenOption.APPEND)
|
||||
: Set.of(StandardOpenOption.WRITE, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING);
|
||||
var bundle = IoOverNioFileSystem.initializeStreamUsingNio(
|
||||
this, nioFs, file, nioPath, options, channelCleanable);
|
||||
channel = bundle.channel();
|
||||
fd = bundle.fd();
|
||||
externalChannelHolder = bundle.externalChannelHolder();
|
||||
} else {
|
||||
this.fd = new FileDescriptor();
|
||||
fd.attach(this);
|
||||
|
||||
open(name, append);
|
||||
FileCleanable.register(fd); // open sets the fd, register the cleanup
|
||||
externalChannelHolder = null;
|
||||
}
|
||||
if (DEBUG.writeTraces()) {
|
||||
System.err.printf("Created a FileOutputStream for %s%n", file);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -275,10 +275,10 @@ class IoOverNioFileSystem extends FileSystem {
|
||||
try {
|
||||
// This tricky thread local variable allows specifying an argument for sun.nio.ch.FileChannelImpl.<init>
|
||||
// which is not present in the NIO public API and which is not easy to specify another way.
|
||||
IoOverNio.PARENT_FOR_FILE_CHANNEL_IMPL.set(owner);
|
||||
IoOverNio.ParentForFileChannelImplHolder.set(owner);
|
||||
return initializeStreamsUsingNio0(owner, nioFs, file, nioPath, optionsForChannel, channelCleanable);
|
||||
} finally {
|
||||
IoOverNio.PARENT_FOR_FILE_CHANNEL_IMPL.remove();
|
||||
IoOverNio.ParentForFileChannelImplHolder.remove();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -869,7 +869,8 @@ class IoOverNioFileSystem extends FileSystem {
|
||||
|
||||
@Override
|
||||
public boolean delete(File f) {
|
||||
try {
|
||||
try (var guard = IoOverNio.RecursionGuard.create("IoOverNioFileSystem.delete")) {
|
||||
IoOverNio.blackhole(guard);
|
||||
boolean result = delete0(f, true);
|
||||
if (DEBUG.writeTraces()) {
|
||||
System.err.printf("IoOverNioFileSystem.delete(%s) = %b%n", f, result);
|
||||
|
||||
@@ -302,47 +302,51 @@ public class RandomAccessFile implements DataOutput, DataInput, Closeable {
|
||||
throw new FileNotFoundException("Invalid file path");
|
||||
}
|
||||
path = name;
|
||||
FileSystem nioFs = IoOverNioFileSystem.acquireNioFs(path);
|
||||
Path nioPath = null;
|
||||
if (nioFs != null) {
|
||||
try {
|
||||
nioPath = nioFs.getPath(path);
|
||||
} catch (InvalidPathException ignored) {
|
||||
// Nothing.
|
||||
|
||||
try (var guard = IoOverNio.RecursionGuard.create(RandomAccessFile.class)) {
|
||||
IoOverNio.blackhole(guard);
|
||||
FileSystem nioFs = IoOverNioFileSystem.acquireNioFs(path);
|
||||
Path nioPath = null;
|
||||
if (nioFs != null) {
|
||||
try {
|
||||
nioPath = nioFs.getPath(path);
|
||||
} catch (InvalidPathException ignored) {
|
||||
// Nothing.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Two significant differences between the legacy java.io and java.nio.files:
|
||||
// * java.nio.file allows to open directories as streams, java.io.FileInputStream doesn't.
|
||||
// * java.nio.file doesn't work well with pseudo devices, i.e., `seek()` fails, while java.io works well.
|
||||
boolean isRegularFile;
|
||||
try {
|
||||
isRegularFile = nioPath != null &&
|
||||
Files.readAttributes(nioPath, BasicFileAttributes.class, LinkOption.NOFOLLOW_LINKS).isRegularFile();
|
||||
}
|
||||
catch (NoSuchFileException ignored) {
|
||||
isRegularFile = true;
|
||||
}
|
||||
catch (IOException ignored) {
|
||||
isRegularFile = false;
|
||||
}
|
||||
// Two significant differences between the legacy java.io and java.nio.files:
|
||||
// * java.nio.file allows to open directories as streams, java.io.FileInputStream doesn't.
|
||||
// * java.nio.file doesn't work well with pseudo devices, i.e., `seek()` fails, while java.io works well.
|
||||
boolean isRegularFile;
|
||||
try {
|
||||
isRegularFile = nioPath != null &&
|
||||
Files.readAttributes(nioPath, BasicFileAttributes.class, LinkOption.NOFOLLOW_LINKS).isRegularFile();
|
||||
}
|
||||
catch (NoSuchFileException ignored) {
|
||||
isRegularFile = true;
|
||||
}
|
||||
catch (IOException ignored) {
|
||||
isRegularFile = false;
|
||||
}
|
||||
|
||||
useNio = nioPath != null && isRegularFile;
|
||||
if (useNio) {
|
||||
var bundle = IoOverNioFileSystem.initializeStreamUsingNio(
|
||||
this, nioFs, file, nioPath, optionsForChannel(imode), channelCleanable);
|
||||
channel = bundle.channel();
|
||||
fd = bundle.fd();
|
||||
externalChannelHolder = bundle.externalChannelHolder();
|
||||
} else {
|
||||
fd = new FileDescriptor();
|
||||
fd.attach(this);
|
||||
open(name, imode);
|
||||
FileCleanable.register(fd); // open sets the fd, register the cleanup
|
||||
externalChannelHolder = null;
|
||||
}
|
||||
if (DEBUG.writeTraces()) {
|
||||
System.err.printf("Created a RandomAccessFile for %s%n", file);
|
||||
useNio = nioPath != null && isRegularFile;
|
||||
if (useNio) {
|
||||
var bundle = IoOverNioFileSystem.initializeStreamUsingNio(
|
||||
this, nioFs, file, nioPath, optionsForChannel(imode), channelCleanable);
|
||||
channel = bundle.channel();
|
||||
fd = bundle.fd();
|
||||
externalChannelHolder = bundle.externalChannelHolder();
|
||||
} else {
|
||||
fd = new FileDescriptor();
|
||||
fd.attach(this);
|
||||
open(name, imode);
|
||||
FileCleanable.register(fd); // open sets the fd, register the cleanup
|
||||
externalChannelHolder = null;
|
||||
}
|
||||
if (DEBUG.writeTraces()) {
|
||||
System.err.printf("Created a RandomAccessFile for %s%n", file);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -131,7 +131,7 @@ public class FileChannelImpl
|
||||
this.writable = writable;
|
||||
this.direct = direct;
|
||||
if (parent == null) {
|
||||
parent = IoOverNio.PARENT_FOR_FILE_CHANNEL_IMPL.get();
|
||||
parent = IoOverNio.ParentForFileChannelImplHolder.get();
|
||||
}
|
||||
this.parent = parent;
|
||||
if (direct) {
|
||||
|
||||
@@ -49,16 +49,19 @@ public class ManglingFileSystem extends FileSystem {
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
ManglingFileSystemProvider.triggerSporadicFileAccess();
|
||||
provider.defaultFs.close();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isOpen() {
|
||||
ManglingFileSystemProvider.triggerSporadicFileAccess();
|
||||
return provider.defaultFs.isOpen();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isReadOnly() {
|
||||
ManglingFileSystemProvider.triggerSporadicFileAccess();
|
||||
return provider.defaultFs.isReadOnly();
|
||||
}
|
||||
|
||||
@@ -69,6 +72,7 @@ public class ManglingFileSystem extends FileSystem {
|
||||
|
||||
@Override
|
||||
public Iterable<Path> getRootDirectories() {
|
||||
ManglingFileSystemProvider.triggerSporadicFileAccess();
|
||||
Iterable<Path> delegateRoots = provider.defaultFs.getRootDirectories();
|
||||
List<Path> result = new ArrayList<>();
|
||||
for (Path delegateRoot : delegateRoots) {
|
||||
@@ -86,11 +90,13 @@ public class ManglingFileSystem extends FileSystem {
|
||||
|
||||
@Override
|
||||
public Iterable<FileStore> getFileStores() {
|
||||
ManglingFileSystemProvider.triggerSporadicFileAccess();
|
||||
return provider.defaultFs.getFileStores();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> supportedFileAttributeViews() {
|
||||
ManglingFileSystemProvider.triggerSporadicFileAccess();
|
||||
return provider.defaultFs.supportedFileAttributeViews();
|
||||
}
|
||||
|
||||
|
||||
@@ -23,19 +23,12 @@
|
||||
|
||||
package testNio;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.nio.channels.FileChannel;
|
||||
import java.nio.channels.SeekableByteChannel;
|
||||
import java.nio.file.AccessDeniedException;
|
||||
import java.nio.file.AccessMode;
|
||||
import java.nio.file.CopyOption;
|
||||
import java.nio.file.DirectoryStream;
|
||||
import java.nio.file.FileStore;
|
||||
import java.nio.file.FileSystem;
|
||||
import java.nio.file.LinkOption;
|
||||
import java.nio.file.OpenOption;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.*;
|
||||
import java.nio.file.attribute.BasicFileAttributeView;
|
||||
import java.nio.file.attribute.BasicFileAttributes;
|
||||
import java.nio.file.attribute.DosFileAttributeView;
|
||||
@@ -48,6 +41,7 @@ import java.nio.file.spi.FileSystemProvider;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
|
||||
public class ManglingFileSystemProvider extends FileSystemProvider {
|
||||
public static final String extraContent = "3x7r4";
|
||||
@@ -168,6 +162,7 @@ public class ManglingFileSystemProvider extends FileSystemProvider {
|
||||
|
||||
@Override
|
||||
public FileChannel newFileChannel(Path path, Set<? extends OpenOption> options, FileAttribute<?>... attrs) throws IOException {
|
||||
triggerSporadicFileAccess();
|
||||
return new ManglingFileChannel(defaultProvider.newFileChannel(unwrap(path), options, attrs));
|
||||
}
|
||||
|
||||
@@ -178,11 +173,13 @@ public class ManglingFileSystemProvider extends FileSystemProvider {
|
||||
|
||||
@Override
|
||||
public DirectoryStream<Path> newDirectoryStream(Path dir, DirectoryStream.Filter<? super Path> filter) throws IOException {
|
||||
triggerSporadicFileAccess();
|
||||
return new ManglingDirectoryStream(manglingFs, (ManglingPath) dir, defaultProvider.newDirectoryStream(unwrap(dir), filter));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void createDirectory(Path dir, FileAttribute<?>... attrs) throws IOException {
|
||||
triggerSporadicFileAccess();
|
||||
if (prohibitFileTreeModifications) {
|
||||
throw new AccessDeniedException(dir.toString(), null, "Test: All file tree modifications are prohibited");
|
||||
}
|
||||
@@ -191,6 +188,7 @@ public class ManglingFileSystemProvider extends FileSystemProvider {
|
||||
|
||||
@Override
|
||||
public void delete(Path path) throws IOException {
|
||||
triggerSporadicFileAccess();
|
||||
if (prohibitFileTreeModifications) {
|
||||
throw new AccessDeniedException(path.toString(), null, "Test: All file tree modifications are prohibited");
|
||||
}
|
||||
@@ -199,6 +197,7 @@ public class ManglingFileSystemProvider extends FileSystemProvider {
|
||||
|
||||
@Override
|
||||
public void copy(Path source, Path target, CopyOption... options) throws IOException {
|
||||
triggerSporadicFileAccess();
|
||||
if (prohibitFileTreeModifications) {
|
||||
throw new AccessDeniedException(source.toString(), null, "Test: All file tree modifications are prohibited");
|
||||
}
|
||||
@@ -209,6 +208,7 @@ public class ManglingFileSystemProvider extends FileSystemProvider {
|
||||
|
||||
@Override
|
||||
public void move(Path source, Path target, CopyOption... options) throws IOException {
|
||||
triggerSporadicFileAccess();
|
||||
if (prohibitFileTreeModifications) {
|
||||
throw new AccessDeniedException(source.toString(), null, "Test: All file tree modifications are prohibited");
|
||||
}
|
||||
@@ -219,6 +219,7 @@ public class ManglingFileSystemProvider extends FileSystemProvider {
|
||||
|
||||
@Override
|
||||
public boolean isSameFile(Path path, Path path2) throws IOException {
|
||||
triggerSporadicFileAccess();
|
||||
Path source2 = unwrap(path);
|
||||
Path target2 = unwrap(path2);
|
||||
return defaultProvider.isSameFile(source2, target2);
|
||||
@@ -226,16 +227,19 @@ public class ManglingFileSystemProvider extends FileSystemProvider {
|
||||
|
||||
@Override
|
||||
public boolean isHidden(Path path) throws IOException {
|
||||
triggerSporadicFileAccess();
|
||||
return defaultProvider.isHidden(unwrap(path));
|
||||
}
|
||||
|
||||
@Override
|
||||
public FileStore getFileStore(Path path) throws IOException {
|
||||
triggerSporadicFileAccess();
|
||||
return defaultProvider.getFileStore(unwrap(path));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void checkAccess(Path path, AccessMode... modes) throws IOException {
|
||||
triggerSporadicFileAccess();
|
||||
defaultProvider.checkAccess(unwrap(path), modes);
|
||||
if (denyAccessToEverything) {
|
||||
throw new AccessDeniedException(path.toString(), null, "Test: No access rules to anything");
|
||||
@@ -244,11 +248,13 @@ public class ManglingFileSystemProvider extends FileSystemProvider {
|
||||
|
||||
@Override
|
||||
public <V extends FileAttributeView> V getFileAttributeView(Path path, Class<V> type, LinkOption... options) {
|
||||
triggerSporadicFileAccess();
|
||||
return wrap(defaultProvider.getFileAttributeView(unwrap(path), type, options));
|
||||
}
|
||||
|
||||
@Override
|
||||
public <A extends BasicFileAttributes> A readAttributes(Path path, Class<A> type, LinkOption... options) throws IOException {
|
||||
triggerSporadicFileAccess();
|
||||
return wrap(defaultProvider.readAttributes(unwrap(path), type, options));
|
||||
}
|
||||
|
||||
@@ -259,6 +265,7 @@ public class ManglingFileSystemProvider extends FileSystemProvider {
|
||||
|
||||
@Override
|
||||
public void setAttribute(Path path, String attribute, Object value, LinkOption... options) throws IOException {
|
||||
triggerSporadicFileAccess();
|
||||
if (prohibitFileTreeModifications) {
|
||||
throw new AccessDeniedException(path.toString(), null, "Test: All file tree modifications are prohibited");
|
||||
}
|
||||
@@ -267,11 +274,13 @@ public class ManglingFileSystemProvider extends FileSystemProvider {
|
||||
|
||||
@Override
|
||||
public boolean exists(Path path, LinkOption... options) {
|
||||
triggerSporadicFileAccess();
|
||||
return defaultProvider.exists(unwrap(path), options);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void createSymbolicLink(Path link, Path target, FileAttribute<?>... attrs) throws IOException {
|
||||
triggerSporadicFileAccess();
|
||||
if (prohibitFileTreeModifications) {
|
||||
throw new AccessDeniedException(link.toString(), null, "Test: All file tree modifications are prohibited");
|
||||
}
|
||||
@@ -303,4 +312,26 @@ public class ManglingFileSystemProvider extends FileSystemProvider {
|
||||
}
|
||||
return view;
|
||||
}
|
||||
|
||||
private static final AtomicLong counter = new AtomicLong();
|
||||
|
||||
static void triggerSporadicFileAccess() {
|
||||
if (FileSystems.getDefault() != null) {
|
||||
try {
|
||||
var tempFile = new File(System.getProperty("java.io.tmpdir"), "test.tmp." + counter.incrementAndGet());
|
||||
try {
|
||||
try (var out = new java.io.FileOutputStream(tempFile)) {
|
||||
out.write(1);
|
||||
}
|
||||
try (var in = new java.io.FileInputStream(tempFile)) {
|
||||
in.read();
|
||||
}
|
||||
} finally {
|
||||
tempFile.delete();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
throw new Error(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -166,6 +166,7 @@ public class ManglingPath implements Path {
|
||||
|
||||
@Override
|
||||
public Path toRealPath(LinkOption... options) throws IOException {
|
||||
ManglingFileSystemProvider.triggerSporadicFileAccess();
|
||||
return new ManglingPath(fileSystem, delegate.toRealPath(options));
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user