Skip to content

Commit 667e74b

Browse files
committed
[GR-60022] Consider newDelegatingFileSystem API.
1 parent ad42202 commit 667e74b

File tree

10 files changed

+613
-199
lines changed

10 files changed

+613
-199
lines changed

sdk/CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ This changelog summarizes major changes between GraalVM SDK versions. The main f
88
* GR-57817 Starting with JDK 24 users now need to configure native access privileges to the java executable in order to avoid warnings from being printed by the JDK.
99
For usages of the module-path pass the `--enable-native-access=org.graalvm.truffle` option and for class-path usages pass the `--enable-native-access=ALL-UNNAMED` option to resolve the new warning. Note that Truffle automatically forwards the native access capability to all loaded languages and tools, therefore no further configuration is required. If the native access is denied by the user with `--illegal-native-access=deny` then loading the optimizing runtime will fail and the fallback runtime will be used. More information can be found in the integrity-by-default [JEP-472](https://openjdk.org/jeps/472).
1010
* GR-54300 `Context` and `Engine` is now automatically closed when no longer strongly referenced. A reachable `Value` or `PolyglotException` will keep the associated `Context` reachable. Additionally, the `Context` remains reachable when explicitly entered or if there is an active polyglot thread within it. The `Engine` remains reachable when there is a strongly reachable `Language`, `Instrument`, or `Context` instance. However, it is still recommended not to rely on garbage collection for closing. Instead, use the try-with-resources pattern for explicit context and engine management. For more information, refer to the [Automatic Close on GC documentation](https://github.com/oracle/graal/blob/master/truffle/docs/CloseOnGc.md).
11+
* GR-60022 Introduced the [FileSystem.newCompositeFileSystem](https://www.graalvm.org/truffle/javadoc/org/graalvm/polyglot/io/FileSystem.html#newCompositeFileSystem(org.graalvm.polyglot.io.FileSystem,org.graalvm.polyglot.io.FileSystem.Selector...)) method, which creates a composite `FileSystem` delegating operations to the specified delegate FileSystem instances based on the provided selectors.
12+
* GR-60022 Introduced the [FileSystem.newDenyIOFileSystem](https://www.graalvm.org/truffle/javadoc/org/graalvm/polyglot/io/FileSystem.html#newDenyIOFileSystem()) method, which creates a `FileSystem` that denies all file operations except for path parsing.
1113

1214
## Version 24.1.0
1315
* GR-51177 Enable random offsets of runtime compiled function entry points for the UNTRUSTED polyglot sandbox policy.

sdk/src/org.graalvm.polyglot/snapshot.sigtest

Lines changed: 33 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,15 @@ meth public abstract java.lang.annotation.ElementType[] value()
122122
CLSS public abstract interface java.lang.constant.Constable
123123
meth public abstract java.util.Optional<? extends java.lang.constant.ConstantDesc> describeConstable()
124124

125+
CLSS public abstract interface java.util.function.Predicate<%0 extends java.lang.Object>
126+
anno 0 java.lang.FunctionalInterface()
127+
meth public abstract boolean test({java.util.function.Predicate%0})
128+
meth public java.util.function.Predicate<{java.util.function.Predicate%0}> and(java.util.function.Predicate<? super {java.util.function.Predicate%0}>)
129+
meth public java.util.function.Predicate<{java.util.function.Predicate%0}> negate()
130+
meth public java.util.function.Predicate<{java.util.function.Predicate%0}> or(java.util.function.Predicate<? super {java.util.function.Predicate%0}>)
131+
meth public static <%0 extends java.lang.Object> java.util.function.Predicate<{%%0}> isEqual(java.lang.Object)
132+
meth public static <%0 extends java.lang.Object> java.util.function.Predicate<{%%0}> not(java.util.function.Predicate<? super {%%0}>)
133+
125134
CLSS public final org.graalvm.polyglot.Context
126135
innr public final Builder
127136
intf java.lang.AutoCloseable
@@ -147,7 +156,7 @@ meth public void leave()
147156
meth public void resetLimits()
148157
meth public void safepoint()
149158
supr java.lang.Object
150-
hfds ALL_HOST_CLASSES,EMPTY,NO_HOST_CLASSES,UNSET_HOST_LOOKUP,currentAPI,dispatch,engine,receiver
159+
hfds ALL_HOST_CLASSES,EMPTY,NO_HOST_CLASSES,UNSET_HOST_LOOKUP,creatorContext,currentAPI,dispatch,engine,parent,receiver
151160

152161
CLSS public final org.graalvm.polyglot.Context$Builder
153162
outer org.graalvm.polyglot.Context
@@ -213,8 +222,8 @@ meth public static org.graalvm.polyglot.Engine$Builder newBuilder()
213222
meth public void close()
214223
meth public void close(boolean)
215224
supr java.lang.Object
216-
hfds EMPTY,ENGINES,currentAPI,dispatch,initializationException,receiver,shutdownHookInitialized
217-
hcls APIAccessImpl,ClassPathIsolation,EngineShutDownHook,ImplHolder,PolyglotInvalid
225+
hfds EMPTY,ENGINES,creatorEngine,currentAPI,dispatch,initializationException,receiver,shutdownHookInitialized
226+
hcls APIAccessImpl,CleanableReference,ContextReference,EngineReference,EngineShutDownHook,ImplHolder,PolyglotInvalid
218227

219228
CLSS public final org.graalvm.polyglot.Engine$Builder
220229
outer org.graalvm.polyglot.Engine
@@ -336,16 +345,20 @@ supr java.lang.Enum<org.graalvm.polyglot.HostAccess$TargetMappingPrecedence>
336345

337346
CLSS public final org.graalvm.polyglot.Instrument
338347
meth public <%0 extends java.lang.Object> {%%0} lookup(java.lang.Class<{%%0}>)
348+
meth public boolean equals(java.lang.Object)
349+
meth public int hashCode()
339350
meth public java.lang.String getId()
340351
meth public java.lang.String getName()
341352
meth public java.lang.String getVersion()
342353
meth public java.lang.String getWebsite()
343354
meth public org.graalvm.options.OptionDescriptors getOptions()
344355
supr java.lang.Object
345-
hfds dispatch,receiver
356+
hfds dispatch,engine,receiver
346357

347358
CLSS public final org.graalvm.polyglot.Language
359+
meth public boolean equals(java.lang.Object)
348360
meth public boolean isInteractive()
361+
meth public int hashCode()
349362
meth public java.lang.String getDefaultMimeType()
350363
meth public java.lang.String getId()
351364
meth public java.lang.String getImplementationName()
@@ -355,7 +368,7 @@ meth public java.lang.String getWebsite()
355368
meth public java.util.Set<java.lang.String> getMimeTypes()
356369
meth public org.graalvm.options.OptionDescriptors getOptions()
357370
supr java.lang.Object
358-
hfds dispatch,receiver
371+
hfds dispatch,engine,receiver
359372

360373
CLSS public final org.graalvm.polyglot.PolyglotAccess
361374
fld public final static org.graalvm.polyglot.PolyglotAccess ALL
@@ -403,7 +416,7 @@ meth public void printStackTrace(java.io.PrintStream)
403416
meth public void printStackTrace(java.io.PrintWriter)
404417
meth public void setStackTrace(java.lang.StackTraceElement[])
405418
supr java.lang.RuntimeException
406-
hfds dispatch,impl
419+
hfds anchor,dispatch,impl
407420

408421
CLSS public final org.graalvm.polyglot.PolyglotException$StackFrame
409422
outer org.graalvm.polyglot.PolyglotException
@@ -654,7 +667,9 @@ meth public org.graalvm.polyglot.io.ByteSequence subSequence(int,int)
654667
meth public static org.graalvm.polyglot.io.ByteSequence create(byte[])
655668

656669
CLSS public abstract interface org.graalvm.polyglot.io.FileSystem
670+
innr public abstract static Selector
657671
meth public !varargs boolean isSameFile(java.nio.file.Path,java.nio.file.Path,java.nio.file.LinkOption[]) throws java.io.IOException
672+
meth public !varargs static org.graalvm.polyglot.io.FileSystem newCompositeFileSystem(org.graalvm.polyglot.io.FileSystem,org.graalvm.polyglot.io.FileSystem$Selector[])
658673
meth public !varargs void copy(java.nio.file.Path,java.nio.file.Path,java.nio.file.CopyOption[]) throws java.io.IOException
659674
meth public !varargs void createSymbolicLink(java.nio.file.Path,java.nio.file.Path,java.nio.file.attribute.FileAttribute<?>[]) throws java.io.IOException
660675
meth public !varargs void move(java.nio.file.Path,java.nio.file.Path,java.nio.file.CopyOption[]) throws java.io.IOException
@@ -679,11 +694,22 @@ meth public static org.graalvm.polyglot.io.FileSystem allowInternalResourceAcces
679694
meth public static org.graalvm.polyglot.io.FileSystem allowLanguageHomeAccess(org.graalvm.polyglot.io.FileSystem)
680695
anno 0 java.lang.Deprecated(boolean forRemoval=false, java.lang.String since="")
681696
meth public static org.graalvm.polyglot.io.FileSystem newDefaultFileSystem()
697+
meth public static org.graalvm.polyglot.io.FileSystem newDenyIOFileSystem()
682698
meth public static org.graalvm.polyglot.io.FileSystem newFileSystem(java.nio.file.FileSystem)
683699
meth public static org.graalvm.polyglot.io.FileSystem newReadOnlyFileSystem(org.graalvm.polyglot.io.FileSystem)
684700
meth public void createLink(java.nio.file.Path,java.nio.file.Path) throws java.io.IOException
685701
meth public void setCurrentWorkingDirectory(java.nio.file.Path)
686702

703+
CLSS public abstract static org.graalvm.polyglot.io.FileSystem$Selector
704+
outer org.graalvm.polyglot.io.FileSystem
705+
cons protected init(org.graalvm.polyglot.io.FileSystem)
706+
intf java.util.function.Predicate<java.nio.file.Path>
707+
meth public abstract boolean test(java.nio.file.Path)
708+
meth public final org.graalvm.polyglot.io.FileSystem getFileSystem()
709+
meth public static org.graalvm.polyglot.io.FileSystem$Selector of(org.graalvm.polyglot.io.FileSystem,java.util.function.Predicate<java.nio.file.Path>)
710+
supr java.lang.Object
711+
hfds fileSystem
712+
687713
CLSS public final org.graalvm.polyglot.io.IOAccess
688714
fld public final static org.graalvm.polyglot.io.IOAccess ALL
689715
fld public final static org.graalvm.polyglot.io.IOAccess NONE
@@ -772,7 +798,7 @@ intf java.lang.AutoCloseable
772798
meth public static org.graalvm.polyglot.management.ExecutionListener$Builder newBuilder()
773799
meth public void close()
774800
supr java.lang.Object
775-
hfds EMPTY,dispatch,receiver
801+
hfds EMPTY,creatorEngine,dispatch,receiver
776802

777803
CLSS public final org.graalvm.polyglot.management.ExecutionListener$Builder
778804
outer org.graalvm.polyglot.management.ExecutionListener

sdk/src/org.graalvm.polyglot/src/org/graalvm/polyglot/Engine.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1955,6 +1955,16 @@ public FileSystem newNIOFileSystem(java.nio.file.FileSystem fileSystem) {
19551955
throw noPolyglotImplementationFound();
19561956
}
19571957

1958+
@Override
1959+
public FileSystem newCompositeFileSystem(FileSystem fallbackFileSystem, FileSystem.Selector... delegates) {
1960+
throw noPolyglotImplementationFound();
1961+
}
1962+
1963+
@Override
1964+
public FileSystem newDenyIOFileSystem() {
1965+
throw noPolyglotImplementationFound();
1966+
}
1967+
19581968
@Override
19591969
public ByteSequence asByteSequence(Object object) {
19601970
throw noPolyglotImplementationFound();

sdk/src/org.graalvm.polyglot/src/org/graalvm/polyglot/impl/AbstractPolyglotImpl.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1438,6 +1438,14 @@ public FileSystem newNIOFileSystem(java.nio.file.FileSystem fileSystem) {
14381438
return getNext().newNIOFileSystem(fileSystem);
14391439
}
14401440

1441+
public FileSystem newCompositeFileSystem(FileSystem fallbackFileSystem, FileSystem.Selector... delegates) {
1442+
return getNext().newCompositeFileSystem(fallbackFileSystem, delegates);
1443+
}
1444+
1445+
public FileSystem newDenyIOFileSystem() {
1446+
return getNext().newDenyIOFileSystem();
1447+
}
1448+
14411449
public ByteSequence asByteSequence(Object object) {
14421450
return getNext().asByteSequence(object);
14431451
}

sdk/src/org.graalvm.polyglot/src/org/graalvm/polyglot/io/FileSystem.java

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@
6666
import java.util.Map;
6767
import java.util.Objects;
6868
import java.util.Set;
69+
import java.util.function.Predicate;
6970

7071
import org.graalvm.polyglot.Context;
7172
import org.graalvm.polyglot.Engine;
@@ -578,4 +579,119 @@ static FileSystem newReadOnlyFileSystem(FileSystem fileSystem) {
578579
static FileSystem newFileSystem(java.nio.file.FileSystem fileSystem) {
579580
return IOHelper.ImplHolder.IMPL.newNIOFileSystem(fileSystem);
580581
}
582+
583+
/**
584+
* Creates a {@link FileSystem} that denies all file operations except for path parsing. Any
585+
* attempt to perform file operations such as reading, writing, or deletion will result in a
586+
* {@link SecurityException} being thrown.
587+
* <p>
588+
* Typically, this file system does not need to be explicitly installed to restrict access to
589+
* host file systems. Instead, use {@code Context.newBuilder().allowIO(IOAccess.NONE)}. This
590+
* method is intended primarily for use as a fallback file system in a
591+
* {@link #newCompositeFileSystem(FileSystem, Selector...) composite file system}.
592+
*
593+
* @since 24.2
594+
*/
595+
static FileSystem newDenyIOFileSystem() {
596+
return IOHelper.ImplHolder.IMPL.newDenyIOFileSystem();
597+
}
598+
599+
/**
600+
* Creates a composite {@link FileSystem} that delegates operations to the provided
601+
* {@code delegates}. The {@link FileSystem} of the first {@code delegate} whose
602+
* {@link Selector#test(Path)} method accepts the path is used for the file system operation. If
603+
* no {@code delegate} accepts the path, the {@code fallbackFileSystem} is used.
604+
* <p>
605+
* The {@code fallbackFileSystem} is responsible for parsing {@link Path} objects. All provided
606+
* file systems must use the same {@link Path} type, {@link #getSeparator() separator}, and
607+
* {@link #getPathSeparator() path separator}. If any file system does not meet this
608+
* requirement, an {@link IllegalArgumentException} is thrown.
609+
* <p>
610+
* The composite file system maintains its own notion of the current working directory and
611+
* ensures that the {@link #setCurrentWorkingDirectory(Path)} method is not invoked on any of
612+
* the delegates. When a request to set the current working directory is received, the composite
613+
* file system verifies that the specified path corresponds to an existing directory by
614+
* consulting either the appropriate delegate or the {@code fallbackFileSystem}. If an explicit
615+
* current working directory has been set, the composite file system normalizes and resolves all
616+
* relative paths to absolute paths prior to delegating operations. Conversely, if no explicit
617+
* current working directory is set, the composite file system directly forwards the incoming
618+
* path, whether relative or absolute, to the appropriate delegate. Furthermore, when an
619+
* explicit current working directory is set, the composite file system does not delegate
620+
* {@code toAbsolutePath} operations, as delegates do not maintain an independent notion of the
621+
* current working directory. If the current working directory is unset, {@code toAbsolutePath}
622+
* operations are delegated to the {@code fallbackFileSystem}.
623+
* <p>
624+
* Operations that are independent of path context, including {@code getTempDirectory},
625+
* {@code getSeparator}, and {@code getPathSeparator}, are handled exclusively by the
626+
* {@code fallbackFileSystem}.
627+
*
628+
* @throws IllegalArgumentException if the file systems do not use the same {@link Path} type,
629+
* {@link #getSeparator() separator}, or {@link #getPathSeparator() path separator}
630+
* @since 24.2
631+
*/
632+
static FileSystem newCompositeFileSystem(FileSystem fallbackFileSystem, Selector... delegates) {
633+
return IOHelper.ImplHolder.IMPL.newCompositeFileSystem(fallbackFileSystem, delegates);
634+
}
635+
636+
/**
637+
* A selector for determining which {@link FileSystem} should handle operations on a given
638+
* {@link Path}. This class encapsulates a {@link FileSystem} and defines a condition for
639+
* selecting it.
640+
*
641+
* @since 24.2
642+
*/
643+
abstract class Selector implements Predicate<Path> {
644+
645+
private final FileSystem fileSystem;
646+
647+
/**
648+
* Creates a {@link Selector} for the specified {@link FileSystem}.
649+
*
650+
* @since 24.2
651+
*/
652+
protected Selector(FileSystem fileSystem) {
653+
this.fileSystem = Objects.requireNonNull(fileSystem, "FileSystem must be non-null");
654+
}
655+
656+
/**
657+
* Returns the {@link FileSystem} associated with this selector.
658+
*
659+
* @since 24.2
660+
*/
661+
public final FileSystem getFileSystem() {
662+
return fileSystem;
663+
}
664+
665+
/**
666+
* Tests whether the {@link FileSystem} associated with this selector can handle operations
667+
* on the specified {@link Path}.
668+
*
669+
* @param path the path to test, provided as a normalized absolute path. The given
670+
* {@code path} has no path components equal to {@code "."} or {@code ".."}.
671+
* @return {@code true} if the associated {@link FileSystem} can handle the {@code path};
672+
* {@code false} otherwise
673+
* @since 24.2
674+
*/
675+
public abstract boolean test(Path path);
676+
677+
/**
678+
* Creates a {@link Selector} for the specified {@link FileSystem} using the provided
679+
* {@link Predicate}.
680+
*
681+
* @param fileSystem the {@link FileSystem} to associate with the selector
682+
* @param predicate the condition to determine if the {@link FileSystem} can handle a given
683+
* path
684+
* @return a new {@link Selector} that delegates path testing to the {@code predicate}
685+
* @since 24.2
686+
*/
687+
public static Selector of(FileSystem fileSystem, Predicate<Path> predicate) {
688+
Objects.requireNonNull(predicate, "Predicate must be non-null");
689+
return new Selector(fileSystem) {
690+
@Override
691+
public boolean test(Path path) {
692+
return predicate.test(path);
693+
}
694+
};
695+
}
696+
}
581697
}

0 commit comments

Comments
 (0)