org.graalvm.polyglot.io.FileSystem Maven / Gradle / Ivy
/*
* Copyright (c) 2018, 2023, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* The Universal Permissive License (UPL), Version 1.0
*
* Subject to the condition set forth below, permission is hereby granted to any
* person obtaining a copy of this software, associated documentation and/or
* data (collectively the "Software"), free of charge and under any and all
* copyright rights in the Software, and any and all patent rights owned or
* freely licensable by each licensor hereunder covering either (i) the
* unmodified Software as contributed to or provided by such licensor, or (ii)
* the Larger Works (as defined below), to deal in both
*
* (a) the Software, and
*
* (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if
* one is included with the Software each a "Larger Work" to which the Software
* is contributed by such licensors),
*
* without restriction, including without limitation the rights to copy, create
* derivative works of, display, perform, and distribute the Software and make,
* use, sell, offer for sale, import, export, have made, and have sold the
* Software and the Larger Work(s), and to sublicense the foregoing rights on
* either these or other terms.
*
* This license is subject to the following condition:
*
* The above copyright notice and either this complete permission notice or at a
* minimum a reference to the UPL must be included in all copies or substantial
* portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package org.graalvm.polyglot.io;
import java.io.File;
import java.io.IOException;
import java.net.URI;
import java.nio.channels.SeekableByteChannel;
import java.nio.charset.Charset;
import java.nio.file.AccessMode;
import java.nio.file.AtomicMoveNotSupportedException;
import java.nio.file.CopyOption;
import java.nio.file.DirectoryNotEmptyException;
import java.nio.file.DirectoryStream;
import java.nio.file.FileAlreadyExistsException;
import java.nio.file.LinkOption;
import java.nio.file.NoSuchFileException;
import java.nio.file.NotDirectoryException;
import java.nio.file.NotLinkException;
import java.nio.file.OpenOption;
import java.nio.file.Path;
import java.nio.file.StandardCopyOption;
import java.nio.file.StandardOpenOption;
import java.nio.file.attribute.FileAttribute;
import java.nio.file.attribute.FileAttributeView;
import java.nio.file.spi.FileSystemProvider;
import java.util.Collection;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import org.graalvm.polyglot.Context;
import org.graalvm.polyglot.io.IOAccess.Builder;
/**
* Service-provider for {@code Truffle} files.
*
* @since 19.0
*/
public interface FileSystem {
/**
* Parses a path from an {@link URI}.
*
* @param uri the {@link URI} to be converted to {@link Path}
* @return the {@link Path} representing given {@link URI}
* @throws UnsupportedOperationException when {@link URI} scheme is not supported
* @throws IllegalArgumentException if preconditions on the {@code uri} do not hold. The format
* of the URI is {@link FileSystem} specific.
* @since 19.0
*/
Path parsePath(URI uri);
/**
* Parses a path from a {@link String}. This method is called only on the {@link FileSystem}
* with {@code file} scheme.
*
* @param path the string path to be converted to {@link Path}
* @return the {@link Path}
* @throws UnsupportedOperationException when the {@link FileSystem} supports only {@link URI}
* @throws IllegalArgumentException if the {@code path} string cannot be converted to a
* {@link Path}
* @since 19.0
*/
Path parsePath(String path);
/**
* Checks existence and accessibility of a file.
*
* @param path the path to the file to check
* @param modes the access modes to check, possibly empty to check existence only.
* @param linkOptions options determining how the symbolic links should be handled
* @throws NoSuchFileException if the file denoted by the path does not exist
* @throws IOException in case of IO error
* @throws SecurityException if this {@link FileSystem} denied the operation
* @since 19.0
*/
void checkAccess(Path path, Set extends AccessMode> modes, LinkOption... linkOptions) throws IOException;
/**
* Creates a directory.
*
* @param dir the directory to create
* @param attrs the optional attributes to set atomically when creating the directory
* @throws FileAlreadyExistsException if a file on given path already exists
* @throws IOException in case of IO error
* @throws UnsupportedOperationException if the attributes contain an attribute which cannot be
* set atomically
* @throws SecurityException if this {@link FileSystem} denied the operation
* @since 19.0
*/
void createDirectory(Path dir, FileAttribute>... attrs) throws IOException;
/**
* Deletes a file.
*
* @param path the path to the file to delete
* @throws NoSuchFileException if a file on given path does not exist
* @throws DirectoryNotEmptyException if the path denotes a non empty directory
* @throws IOException in case of IO error
* @throws SecurityException if this {@link FileSystem} denied the operation
* @since 19.0
*/
void delete(Path path) throws IOException;
/**
* Opens or creates a file returning a {@link SeekableByteChannel} to access the file content.
*
* @param path the path to the file to open
* @param options the options specifying how the file should be opened
* @param attrs the optional attributes to set atomically when creating the new file
* @return the created {@link SeekableByteChannel}
* @throws FileAlreadyExistsException if {@link StandardOpenOption#CREATE_NEW} option is set and
* a file already exists on given path
* @throws IOException in case of IO error
* @throws UnsupportedOperationException if the attributes contain an attribute which cannot be
* set atomically
* @throws IllegalArgumentException in case of invalid options combination
* @throws SecurityException if this {@link FileSystem} denied the operation
* @since 19.0
*/
SeekableByteChannel newByteChannel(Path path, Set extends OpenOption> options, FileAttribute>... attrs) throws IOException;
/**
* Returns directory entries.
*
* @param dir the path to the directory to iterate entries for
* @param filter the filter
* @return the new {@link DirectoryStream}
* @throws NotDirectoryException when given path does not denote a directory
* @throws IOException in case of IO error
* @throws SecurityException if this {@link FileSystem} denied the operation
* @since 19.0
*/
DirectoryStream newDirectoryStream(Path dir, DirectoryStream.Filter super Path> filter) throws IOException;
/**
* Resolves given path to an absolute path.
*
* @param path the path to resolve, may be a non normalized path
* @return an absolute {@link Path}
* @throws SecurityException if this {@link FileSystem} denied the operation
* @since 19.0
*/
Path toAbsolutePath(Path path);
/**
* Returns the real (canonical) path of an existing file.
*
* @param path the path to resolve, may be a non normalized path
* @param linkOptions options determining how the symbolic links should be handled
* @return an absolute canonical path
* @throws IOException in case of IO error
* @throws SecurityException if this {@link FileSystem} denied the operation
* @since 19.0
*/
Path toRealPath(Path path, LinkOption... linkOptions) throws IOException;
/**
* Reads a file's attributes as a bulk operation.
*
* @param path the path to file to read attributes for
* @param attributes the attributes to read. The {@code attributes} parameter has the form:
* {@code [view-name:]attribute-list}. The optional {@code view-name} corresponds to
* {@link FileAttributeView#name()} and determines the set of attributes, the default
* value is {@code "basic"}. The {@code attribute-list} is a comma separated list of
* attributes. If the {@code attribute-list} contains {@code '*'} then all the
* attributes from given view are read.
* @param options the options determining how the symbolic links should be handled
* @return the {@link Map} containing the file attributes. The map's keys are attribute names,
* map's values are the attribute values. The map may contain a subset of required
* attributes in case when the {@code FileSystem} does not support some of the required
* attributes.
* @throws UnsupportedOperationException if the attribute view is not supported. At least the
* {@code "basic"} attribute view has to be supported by the file system.
* @throws IllegalArgumentException is the {@code attribute-list} is empty or contains an
* unknown attribute
* @throws IOException in case of IO error
* @throws SecurityException if this {@link FileSystem} denied the operation
* @since 19.0
*/
Map readAttributes(Path path, String attributes, LinkOption... options) throws IOException;
/**
* Sets a file's attribute.
*
* @param path the path to file to set an attribute to
* @param attribute the attribute to set. The {@code attribute} parameter has the form:
* {@code [view-name:]attribute-name}. The optional {@code view-name} corresponds to
* {@link FileAttributeView#name()} and determines the set of attributes, the default
* value is {@code "basic"}. The {@code attribute-name} is a name of an attribute.
* @param value the attribute value
* @param options the options determining how the symbolic links should be handled
* @throws ClassCastException if {@code value} is not of the expected type or {@code value} is a
* {@link Collection} containing element of a non expected type
* @throws UnsupportedOperationException if the attribute view is not supported.
* @throws IllegalArgumentException is the {@code attribute-name} is an unknown attribute or
* {@code value} has an inappropriate value
* @throws IOException in case of IO error
* @throws SecurityException if this {@link FileSystem} denied the operation
* @since 19.0
*/
default void setAttribute(Path path, String attribute, Object value, LinkOption... options) throws IOException {
throw new UnsupportedOperationException("Setting attributes is not supported");
}
/**
* Copies source file to target file.
*
* @param source the path to file to copy
* @param target the path to the target file
* @param options the options specifying how the copy should be performed, see
* {@link StandardCopyOption}
* @throws UnsupportedOperationException if {@code options} contains unsupported option
* @throws FileAlreadyExistsException if the target path already exists and the {@code options}
* don't contain {@link StandardCopyOption#REPLACE_EXISTING} option
* @throws DirectoryNotEmptyException if the {@code options} contain
* {@link StandardCopyOption#REPLACE_EXISTING} but the {@code target} is a non empty
* directory
* @throws IOException in case of IO error
* @throws SecurityException if this {@link FileSystem} denied the operation
* @since 19.0
*/
default void copy(Path source, Path target, CopyOption... options) throws IOException {
IOHelper.copy(source, target, this, options);
}
/**
* Moves (renames) source file to target file.
*
* @param source the path to file to move
* @param target the path to the target file
* @param options the options specifying how the move should be performed, see
* {@link StandardCopyOption}
* @throws UnsupportedOperationException if {@code options} contains unsupported option
* @throws FileAlreadyExistsException if the target path already exists and the {@code options}
* don't contain {@link StandardCopyOption#REPLACE_EXISTING} option
* @throws DirectoryNotEmptyException if the {@code options} contain
* {@link StandardCopyOption#REPLACE_EXISTING} but the {@code target} is a non empty
* directory
* @throws AtomicMoveNotSupportedException if the {@code options} contain
* {@link StandardCopyOption#ATOMIC_MOVE} but file cannot be moved atomically
* @throws IOException in case of IO error
* @throws SecurityException if this {@link FileSystem} denied the operation
* @since 19.0
*/
default void move(Path source, Path target, CopyOption... options) throws IOException {
IOHelper.move(source, target, this, options);
}
/**
* Creates a new link for an existing file.
*
* @param link the path to link to create
* @param existing the path to existing file
* @throws UnsupportedOperationException if links are not supported by file system
* @throws FileAlreadyExistsException if a file on given link path already exists
* @throws IOException in case of IO error
* @throws SecurityException if this {@link FileSystem} denied the operation
* @since 19.0
*/
default void createLink(Path link, Path existing) throws IOException {
throw new UnsupportedOperationException("Links are not supported");
}
/**
* Creates a new symbolic link.
*
* @param link the path to symbolic link to create
* @param target the target path of the symbolic link
* @param attrs the optional attributes to set atomically when creating the new symbolic link
* @throws UnsupportedOperationException if symbolic links are not supported by file system
* @throws FileAlreadyExistsException if a file on given link path already exists
* @throws IOException in case of IO error
* @throws SecurityException if this {@link FileSystem} denied the operation
* @since 19.0
*/
default void createSymbolicLink(Path link, Path target, FileAttribute>... attrs) throws IOException {
throw new UnsupportedOperationException("Links are not supported");
}
/**
* Reads the target of the symbolic link.
*
* @param link the path to symbolic link to read
* @return the {@link Path} representing the symbolic link target
* @throws UnsupportedOperationException if symbolic links are not supported by file system
* @throws NotLinkException if the {@code link} does not denote a symbolic link
* @throws IOException in case of IO error
* @throws SecurityException if this {@link FileSystem} denied the operation
* @since 19.0
*/
default Path readSymbolicLink(Path link) throws IOException {
throw new UnsupportedOperationException("Links are not supported");
}
/**
* Sets the current working directory. The current working directory is used to resolve non
* absolute paths in {@link FileSystem} operations.
*
* @param currentWorkingDirectory the new current working directory
* @throws UnsupportedOperationException if setting of the current working directory is not
* supported
* @throws IllegalArgumentException if the {@code currentWorkingDirectory} is not a valid
* current working directory
* @throws SecurityException if {@code currentWorkingDirectory} is not readable
* @since 19.0
*/
default void setCurrentWorkingDirectory(Path currentWorkingDirectory) {
throw new UnsupportedOperationException("Setting current working directory is not supported.");
}
/**
* Returns the name separator used to separate names in a path string. The separator is used
* when creating path strings by invoking the {@link Path#toString() toString()} method.
*
* @return the name separator
* @since 19.0
*/
default String getSeparator() {
return parsePath("").getFileSystem().getSeparator();
}
/**
* Returns the path separator used to separate filenames in a path list. On UNIX the path
* separator is {@code ':'}. On Windows it's {@code ';'}.
*
* @return the path separator
* @since 19.1.0
*/
default String getPathSeparator() {
return File.pathSeparator;
}
/**
* Returns a MIME type for given path. An optional operation for {@link FileSystem filesystem}
* implementations which can provide MIME types in an efficient way.
*
* @param path the file to find a MIME type for
* @return the MIME type or {@code null} if the MIME type is not recognized or the
* {@link FileSystem filesystem} does not support MIME type detection
* @since 19.0
*/
default String getMimeType(Path path) {
Objects.requireNonNull(path);
return null;
}
/**
* Returns an file encoding for given path. An optional operation for {@link FileSystem
* filesystem} implementations which can provide file encodings in an efficient way.
*
* @param path the file to find an file encoding for
* @return the file encoding or {@code null} if the file encoding is not detected or the
* {@link FileSystem filesystem} does not support file encoding detection
* @since 19.0
*/
default Charset getEncoding(Path path) {
Objects.requireNonNull(path);
return null;
}
/**
* Returns the default temporary directory.
*
* @since 19.3.0
*/
default Path getTempDirectory() {
throw new UnsupportedOperationException("Temporary directories not supported");
}
/**
* Tests if the given paths refer to the same physical file.
*
* The default implementation firstly converts the paths into absolute paths. If the absolute
* paths are equal it returns {@code true} without checking if the file exists. Otherwise, this
* method converts the paths into canonical representations and tests the canonical paths for
* equality. The {@link FileSystem} may re-implement the method with a more efficient test. When
* re-implemented the method must have the same security privileges as the
* {@link #toAbsolutePath(Path) toAbsolutePath} and {@link #toRealPath(Path, LinkOption...)
* toRealPath}.
*
* @param path1 the path to the file
* @param path2 the other path
* @param options the options determining how the symbolic links should be handled
* @return {@code true} if the given paths refer to the same physical file
* @throws IOException in case of IO error
* @throws SecurityException if this {@link FileSystem} denied the operation
* @since 20.2.0
*/
default boolean isSameFile(Path path1, Path path2, LinkOption... options) throws IOException {
if (toAbsolutePath(path1).equals(toAbsolutePath(path2))) {
return true;
}
return toRealPath(path1, options).equals(toRealPath(path2, options));
}
/**
* Creates a {@link FileSystem} implementation based on the host Java NIO. The returned instance
* can be used as a delegate by a decorating {@link FileSystem}.
*
* For an untrusted code execution, access to the host filesystem should be prevented either by
* using {@link IOAccess#NONE} or an {@link #newFileSystem(java.nio.file.FileSystem)} in-memory
* filesystem}. For more details on executing untrusted code, see the
* Polyglot Sandboxing
* Security Guide.
*
* The following example shows a {@link FileSystem} logging filesystem operations.
*
*
* class TracingFileSystem implements FileSystem {
*
* private static final Logger LOGGER = Logger.getLogger(TracingFileSystem.class.getName());
*
* private final FileSystem delegate;
*
* TracingFileSystem() {
* this.delegate = FileSystem.newDefaultFileSystem();
* }
*
* @Override
* public Path parsePath(String path) {
* return delegate.parsePath(path);
* }
*
* @Override
* public Path parsePath(URI uri) {
* return delegate.parsePath(uri);
* }
*
* @Override
* public SeekableByteChannel newByteChannel(Path path,
* Set<? extends OpenOption> options,
* FileAttribute<?>... attrs) throws IOException {
* boolean success = false;
* try {
* SeekableByteChannel result = delegate.newByteChannel(path, options, attrs);
* success = true;
* return result;
* } finally {
* trace("newByteChannel", path, success);
* }
* }
*
* ...
*
* private void trace(String operation, Path path, boolean success) {
* LOGGER.log(Level.FINE, "The {0} request for the path {1} {2}.",new Object[] {
* operation, path, success ? "was successful" : "failed"
* });
* }
* }
*
*
* @see Builder#fileSystem(FileSystem)
* @see org.graalvm.polyglot.Context.Builder#allowIO(IOAccess)
*
* @since 20.2.0
*/
static FileSystem newDefaultFileSystem() {
return IOHelper.IMPL.newDefaultFileSystem();
}
/**
* Decorates the given {@code fileSystem} by an implementation that forwards access to files in
* the language home to the default file system. The method is intended to be used by custom
* filesystem implementations with non default storage to allow guest languages to access files
* in the languages homes. As the returned filesystem uses a default file system to access files
* in the language home, the {@code fileSystem} has to use the same {@link Path} type,
* {@link #getSeparator() separator} and {@link #getPathSeparator() path separator} as the
* {@link #newDefaultFileSystem() default filesystem}.
*
* @throws IllegalArgumentException when the {@code fileSystem} does not use the same
* {@link Path} type or has a different {@link #getSeparator() separator} or
* {@link #getPathSeparator() path separator} as the {@link #newDefaultFileSystem()
* default file system}.
* @since 22.2
*/
static FileSystem allowLanguageHomeAccess(FileSystem fileSystem) {
return IOHelper.IMPL.allowLanguageHomeAccess(fileSystem);
}
/**
* Decorates the given {@code fileSystem} by an implementation that makes the passed
* {@code fileSystem} read-only by forbidding all write operations. This method can be used to
* make an existing file system, such as the {@link #newDefaultFileSystem() default filesystem},
* read-only.
*
* @since 22.2
*/
static FileSystem newReadOnlyFileSystem(FileSystem fileSystem) {
return IOHelper.IMPL.newReadOnlyFileSystem(fileSystem);
}
/**
* Creates a {@link FileSystem} implementation based on the given Java NIO filesystem. The
* returned {@link FileSystem} delegates all operations to {@code fileSystem}'s
* {@link FileSystemProvider provider}.
*
*
* The following example shows how to configure {@link Context} so that languages read files
* from a prepared zip file.
*
*
* Path zipFile = Paths.get("filesystem.zip");
* try (java.nio.file.FileSystem nioFs = FileSystems.newFileSystem(zipFile)) {
* IOAccess ioAccess = IOAccess.newBuilder().fileSystem(FileSystem.newFileSystem(nioFs)).build();
* try (Context ctx = Context.newBuilder().allowIO(ioAccess).build()) {
* Value result = ctx.eval("js", "load('scripts/app.sh'); execute()");
* }
* }
*
*
* @see IOAccess
* @since 23.0
*/
static FileSystem newFileSystem(java.nio.file.FileSystem fileSystem) {
return IOHelper.IMPL.newNIOFileSystem(fileSystem);
}
}