/* * Copyright 2013 Google Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.google.common.jimfs; import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.jimfs.Feature.FILE_CHANNEL; import static com.google.common.jimfs.Feature.LINKS; import static com.google.common.jimfs.Feature.SECURE_DIRECTORY_STREAM; import static com.google.common.jimfs.Feature.SYMBOLIC_LINKS; import static com.google.common.jimfs.PathNormalization.CASE_FOLD_ASCII; import static com.google.common.jimfs.PathNormalization.NFC; import static com.google.common.jimfs.PathNormalization.NFD; import com.google.common.base.MoreObjects; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Lists; import com.google.common.collect.Sets; import java.nio.channels.FileChannel; import java.nio.file.FileSystem; import java.nio.file.InvalidPathException; import java.nio.file.SecureDirectoryStream; import java.nio.file.WatchService; import java.nio.file.attribute.BasicFileAttributeView; import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.regex.Pattern; import org.checkerframework.checker.nullness.compatqual.NullableDecl; /** * Immutable configuration for an in-memory file system. A {@code Configuration} is passed to a * method in {@link Jimfs} such as {@link Jimfs#newFileSystem(Configuration)} to create a new {@link * FileSystem} instance. * * @author Colin Decker */ public final class Configuration { /** * Returns the default configuration for a UNIX-like file system. A file system created with this * configuration: * *
To create a modified version of this configuration, such as to include the full set of UNIX * file attribute views, {@linkplain #toBuilder() create a builder}. * *
Example: * *
* Configuration config = Configuration.unix().toBuilder() * .setAttributeViews("basic", "owner", "posix", "unix") * .setWorkingDirectory("/home/user") * .build();*/ public static Configuration unix() { return UnixHolder.UNIX; } private static final class UnixHolder { private static final Configuration UNIX = Configuration.builder(PathType.unix()) .setDisplayName("Unix") .setRoots("/") .setWorkingDirectory("/work") .setAttributeViews("basic") .setSupportedFeatures(LINKS, SYMBOLIC_LINKS, SECURE_DIRECTORY_STREAM, FILE_CHANNEL) .build(); } /** * Returns the default configuration for a Mac OS X-like file system. * *
The primary differences between this configuration and the default {@link #unix()} * configuration are that this configuration does Unicode normalization on the display and * canonical forms of filenames and does case insensitive file lookup. * *
A file system created with this configuration: * *
To create a modified version of this configuration, such as to include the full set of UNIX * file attribute views or to use full Unicode case insensitivity, {@linkplain #toBuilder() create * a builder}. * *
Example: * *
* Configuration config = Configuration.osX().toBuilder() * .setAttributeViews("basic", "owner", "posix", "unix") * .setNameCanonicalNormalization(NFD, CASE_FOLD_UNICODE) * .setWorkingDirectory("/Users/user") * .build();*/ public static Configuration osX() { return OsxHolder.OS_X; } private static final class OsxHolder { private static final Configuration OS_X = unix().toBuilder() .setDisplayName("OSX") .setNameDisplayNormalization(NFC) // matches JDK 1.7u40+ behavior .setNameCanonicalNormalization(NFD, CASE_FOLD_ASCII) // NFD is default in HFS+ .setSupportedFeatures(LINKS, SYMBOLIC_LINKS, FILE_CHANNEL) .build(); } /** * Returns the default configuration for a Windows-like file system. A file system created with * this configuration: * *
To create a modified version of this configuration, such as to include the full set of * Windows file attribute views or to use full Unicode case insensitivity, {@linkplain * #toBuilder() create a builder}. * *
Example: * *
* Configuration config = Configuration.windows().toBuilder() * .setAttributeViews("basic", "owner", "dos", "acl", "user") * .setNameCanonicalNormalization(CASE_FOLD_UNICODE) * .setWorkingDirectory("C:\\Users\\user") // or "C:/Users/user" * .build();*/ public static Configuration windows() { return WindowsHolder.WINDOWS; } private static final class WindowsHolder { private static final Configuration WINDOWS = Configuration.builder(PathType.windows()) .setDisplayName("Windows") .setRoots("C:\\") .setWorkingDirectory("C:\\work") .setNameCanonicalNormalization(CASE_FOLD_ASCII) .setPathEqualityUsesCanonicalForm(true) // matches real behavior of WindowsPath .setAttributeViews("basic") .setSupportedFeatures(LINKS, SYMBOLIC_LINKS, FILE_CHANNEL) .build(); } /** * Returns a default configuration appropriate to the current operating system. * *
More specifically, if the operating system is Windows, {@link Configuration#windows()} is * returned; if the operating system is Mac OS X, {@link Configuration#osX()} is returned; * otherwise, {@link Configuration#unix()} is returned. * *
This is the configuration used by the {@code Jimfs.newFileSystem} methods that do not take a
* {@code Configuration} parameter.
*
* @since 1.1
*/
public static Configuration forCurrentPlatform() {
String os = System.getProperty("os.name");
if (os.contains("Windows")) {
return windows();
} else if (os.contains("OS X")) {
return osX();
} else {
return unix();
}
}
/** Creates a new mutable {@link Configuration} builder using the given path type. */
public static Builder builder(PathType pathType) {
return new Builder(pathType);
}
// Path configuration
final PathType pathType;
final ImmutableSet The default is false.
*/
public Builder setPathEqualityUsesCanonicalForm(boolean useCanonicalForm) {
this.pathEqualityUsesCanonicalForm = useCanonicalForm;
return this;
}
/**
* Sets the block size (in bytes) for the file system to use. All regular files will be
* allocated blocks of the given size, so this is the minimum granularity for file size.
*
* The default is 8192 bytes (8 KB).
*/
public Builder setBlockSize(int blockSize) {
checkArgument(blockSize > 0, "blockSize (%s) must be positive", blockSize);
this.blockSize = blockSize;
return this;
}
/**
* Sets the maximum size (in bytes) for the file system's in-memory file storage. This maximum
* size determines the maximum number of blocks that can be allocated to regular files, so it
* should generally be a multiple of the {@linkplain #setBlockSize(int) block size}. The actual
* maximum size will be the nearest multiple of the block size that is less than or equal to the
* given size.
*
* Note: The in-memory file storage will not be eagerly initialized to this size, so
* it won't use more memory than is needed for the files you create. Also note that in addition
* to this limit, you will of course be limited by the amount of heap space available to the JVM
* and the amount of heap used by other objects, both in the file system and elsewhere.
*
* The default is 4 GB.
*/
public Builder setMaxSize(long maxSize) {
checkArgument(maxSize > 0, "maxSize (%s) must be positive", maxSize);
this.maxSize = maxSize;
return this;
}
/**
* Sets the maximum amount of unused space (in bytes) in the file system's in-memory file
* storage that should be cached for reuse. By default, this will be equal to the {@linkplain
* #setMaxSize(long) maximum size} of the storage, meaning that all space that is freed when
* files are truncated or deleted is cached for reuse. This helps to avoid lots of garbage
* collection when creating and deleting many files quickly. This can be set to 0 to disable
* caching entirely (all freed blocks become available for garbage collection) or to some other
* number to put an upper bound on the maximum amount of unused space the file system will keep
* around.
*
* Like the maximum size, the actual value will be the closest multiple of the block size
* that is less than or equal to the given size.
*/
public Builder setMaxCacheSize(long maxCacheSize) {
checkArgument(maxCacheSize >= 0, "maxCacheSize (%s) may not be negative", maxCacheSize);
this.maxCacheSize = maxCacheSize;
return this;
}
/**
* Sets the attribute views the file system should support. By default, the following views may
* be specified:
*
* If any other views should be supported, attribute providers for those views must be
* {@linkplain #addAttributeProvider(AttributeProvider) added}.
*/
public Builder setAttributeViews(String first, String... more) {
this.attributeViews = ImmutableSet.copyOf(Lists.asList(first, more));
return this;
}
/** Adds an attribute provider for a custom view for the file system to support. */
public Builder addAttributeProvider(AttributeProvider provider) {
checkNotNull(provider);
if (attributeProviders == null) {
attributeProviders = new HashSet<>();
}
attributeProviders.add(provider);
return this;
}
/**
* Sets the default value to use for the given file attribute when creating new files. The
* attribute must be in the form "view:attribute". The value must be of a type that the provider
* for the view accepts.
*
* For the included attribute views, default values can be set for the following attributes:
*
*
*
*
*
*
* Name
* View Interface
* Attributes Interface
*
*
* {@code "basic"}
* {@link java.nio.file.attribute.BasicFileAttributeView BasicFileAttributeView}
* {@link java.nio.file.attribute.BasicFileAttributes BasicFileAttributes}
*
*
* {@code "owner"}
* {@link java.nio.file.attribute.FileOwnerAttributeView FileOwnerAttributeView}
* --
*
*
* {@code "posix"}
* {@link java.nio.file.attribute.PosixFileAttributeView PosixFileAttributeView}
* {@link java.nio.file.attribute.PosixFileAttributes PosixFileAttributes}
*
*
* {@code "unix"}
* --
* --
*
*
* {@code "dos"}
* {@link java.nio.file.attribute.DosFileAttributeView DosFileAttributeView}
* {@link java.nio.file.attribute.DosFileAttributes DosFileAttributes}
*
*
* {@code "acl"}
* {@link java.nio.file.attribute.AclFileAttributeView AclFileAttributeView}
* --
*
*
* {@code "user"}
* {@link java.nio.file.attribute.UserDefinedFileAttributeView UserDefinedFileAttributeView}
* --
*
*
*/
public Builder setDefaultAttributeValue(String attribute, Object value) {
checkArgument(
ATTRIBUTE_PATTERN.matcher(attribute).matches(),
"attribute (%s) must be of the form \"view:attribute\"",
attribute);
checkNotNull(value);
if (defaultAttributeValues == null) {
defaultAttributeValues = new HashMap<>();
}
defaultAttributeValues.put(attribute, value);
return this;
}
private static final Pattern ATTRIBUTE_PATTERN = Pattern.compile("[^:]+:[^:]+");
/**
* Sets the roots for the file system.
*
* @throws InvalidPathException if any of the given roots is not a valid path for this builder's
* path type
* @throws IllegalArgumentException if any of the given roots is a valid path for this builder's
* path type but is not a root path with no name elements
*/
public Builder setRoots(String first, String... more) {
List
*
* Attribute
* Legal Types
*
*
* {@code "owner:owner"}
* {@code String} (user name)
*
*
* {@code "posix:group"}
* {@code String} (group name)
*
*
* {@code "posix:permissions"}
* {@code String} (format "rwxrw-r--"), {@code Set
*
*
* {@code "dos:readonly"}
* {@code Boolean}
*
*
* {@code "dos:hidden"}
* {@code Boolean}
*
*
* {@code "dos:archive"}
* {@code Boolean}
*
*
* {@code "dos:system"}
* {@code Boolean}
*
*
* {@code "acl:acl"}
* {@code List
*