CopyOnWriteFileSystem.java

net.minecraft.util.filefix.virtualfilesystem.CopyOnWriteFileSystem

信息

  • 全限定名:net.minecraft.util.filefix.virtualfilesystem.CopyOnWriteFileSystem
  • 类型:public class
  • 包:net.minecraft.util.filefix.virtualfilesystem
  • 源码路径:src/main/java/net/minecraft/util/filefix/virtualfilesystem/CopyOnWriteFileSystem.java
  • 起始行号:L28
  • 继承:FileSystem
  • 职责:

    TODO

字段/常量

  • FILE_ATTRIBUTE_VIEWS

    • 类型: Set<String>
    • 修饰符: private static final
    • 源码定位: L29
    • 说明:

      TODO

  • LOGGER

    • 类型: Logger
    • 修饰符: private static final
    • 源码定位: L30
    • 说明:

      TODO

  • store

    • 类型: CopyOnWriteFileStore
    • 修饰符: private final
    • 源码定位: L31
    • 说明:

      TODO

  • provider

    • 类型: CopyOnWriteFSProvider
    • 修饰符: private final
    • 源码定位: L32
    • 说明:

      TODO

  • baseDirectory

    • 类型: Path
    • 修饰符: private final
    • 源码定位: L33
    • 说明:

      TODO

  • skippedPaths

    • 类型: PathMatcher
    • 修饰符: private final
    • 源码定位: L34
    • 说明:

      TODO

  • tmpDirectory

    • 类型: Path
    • 修饰符: private final
    • 源码定位: L35
    • 说明:

      TODO

  • rootPath

    • 类型: CopyOnWriteFSPath
    • 修饰符: private final
    • 源码定位: L36
    • 说明:

      TODO

  • tmpFileIndex

    • 类型: AtomicInteger
    • 修饰符: private final
    • 源码定位: L37
    • 说明:

      TODO

  • fileTree

    • 类型: DirectoryNode
    • 修饰符: private
    • 源码定位: L38
    • 说明:

      TODO

内部类/嵌套类型

  • net.minecraft.util.filefix.virtualfilesystem.CopyOnWriteFileSystem.Moves
    • 类型: record
    • 修饰符: public
    • 源码定位: L294
    • 说明:

      TODO

构造器

private CopyOnWriteFileSystem(String name, Path baseDirectory, Path tmpDirectory, PathMatcher skippedPaths) @ L40

  • 构造器名:CopyOnWriteFileSystem
  • 源码定位:L40
  • 修饰符:private

参数:

  • name: String
  • baseDirectory: Path
  • tmpDirectory: Path
  • skippedPaths: PathMatcher

说明:

TODO

方法

下面的方法块按源码顺序生成。

public static CopyOnWriteFileSystem create(String name, Path baseDirectory, Path tmpDirectory, PathMatcher skippedPaths) @ L50

  • 方法名:create
  • 源码定位:L50
  • 返回类型:CopyOnWriteFileSystem
  • 修饰符:public static

参数:

  • name: String
  • baseDirectory: Path
  • tmpDirectory: Path
  • skippedPaths: PathMatcher

说明:

TODO

private DirectoryNode buildFileTreeFrom(Path baseDirectory) @ L60

  • 方法名:buildFileTreeFrom
  • 源码定位:L60
  • 返回类型:DirectoryNode
  • 修饰符:private

参数:

  • baseDirectory: Path

说明:

TODO

protected void resetFileTreeToBaseFolderContent() @ L108

  • 方法名:resetFileTreeToBaseFolderContent
  • 源码定位:L108
  • 返回类型:void
  • 修饰符:protected

参数:

说明:

TODO

public CopyOnWriteFSProvider provider() @ L113

  • 方法名:provider
  • 源码定位:L113
  • 返回类型:CopyOnWriteFSProvider
  • 修饰符:public

参数:

说明:

TODO

public void close() @ L117

  • 方法名:close
  • 源码定位:L117
  • 返回类型:void
  • 修饰符:public

参数:

说明:

TODO

public boolean isOpen() @ L124

  • 方法名:isOpen
  • 源码定位:L124
  • 返回类型:boolean
  • 修饰符:public

参数:

说明:

TODO

public boolean isReadOnly() @ L129

  • 方法名:isReadOnly
  • 源码定位:L129
  • 返回类型:boolean
  • 修饰符:public

参数:

说明:

TODO

public String getSeparator() @ L134

  • 方法名:getSeparator
  • 源码定位:L134
  • 返回类型:String
  • 修饰符:public

参数:

说明:

TODO

public Iterable<Path> getRootDirectories() @ L139

  • 方法名:getRootDirectories
  • 源码定位:L139
  • 返回类型:Iterable
  • 修饰符:public

参数:

说明:

TODO

public Iterable<FileStore> getFileStores() @ L144

  • 方法名:getFileStores
  • 源码定位:L144
  • 返回类型:Iterable
  • 修饰符:public

参数:

说明:

TODO

public Set<String> supportedFileAttributeViews() @ L149

  • 方法名:supportedFileAttributeViews
  • 源码定位:L149
  • 返回类型:Set
  • 修饰符:public

参数:

说明:

TODO

public CopyOnWriteFSPath getPath(String first, String... more) @ L154

  • 方法名:getPath
  • 源码定位:L154
  • 返回类型:CopyOnWriteFSPath
  • 修饰符:public

参数:

  • first: String
  • more: String…

说明:

TODO

public PathMatcher getPathMatcher(String syntaxAndPattern) @ L158

  • 方法名:getPathMatcher
  • 源码定位:L158
  • 返回类型:PathMatcher
  • 修饰符:public

参数:

  • syntaxAndPattern: String

说明:

TODO

public UserPrincipalLookupService getUserPrincipalLookupService() @ L163

  • 方法名:getUserPrincipalLookupService
  • 源码定位:L163
  • 返回类型:UserPrincipalLookupService
  • 修饰符:public

参数:

说明:

TODO

public WatchService newWatchService() @ L168

  • 方法名:newWatchService
  • 源码定位:L168
  • 返回类型:WatchService
  • 修饰符:public

参数:

说明:

TODO

public CopyOnWriteFileStore store() @ L173

  • 方法名:store
  • 源码定位:L173
  • 返回类型:CopyOnWriteFileStore
  • 修饰符:public

参数:

说明:

TODO

public CopyOnWriteFSPath rootPath() @ L177

  • 方法名:rootPath
  • 源码定位:L177
  • 返回类型:CopyOnWriteFSPath
  • 修饰符:public

参数:

说明:

TODO

DirectoryNode fileTree() @ L181

  • 方法名:fileTree
  • 源码定位:L181
  • 返回类型:DirectoryNode
  • 修饰符:package-private

参数:

说明:

TODO

public Path baseDirectory() @ L185

  • 方法名:baseDirectory
  • 源码定位:L185
  • 返回类型:Path
  • 修饰符:public

参数:

说明:

TODO

public Path tmpDirectory() @ L189

  • 方法名:tmpDirectory
  • 源码定位:L189
  • 返回类型:Path
  • 修饰符:public

参数:

说明:

TODO

Path createTemporaryFilePath() @ L193

  • 方法名:createTemporaryFilePath
  • 源码定位:L193
  • 返回类型:Path
  • 修饰符:package-private

参数:

说明:

TODO

public FileSystem backingFileSystem() @ L197

  • 方法名:backingFileSystem
  • 源码定位:L197
  • 返回类型:FileSystem
  • 修饰符:public

参数:

说明:

TODO

public CopyOnWriteFileSystem.Moves collectMoveOperations(Path outPath) @ L201

  • 方法名:collectMoveOperations
  • 源码定位:L201
  • 返回类型:CopyOnWriteFileSystem.Moves
  • 修饰符:public

参数:

  • outPath: Path

说明:

TODO

private void collectMoveOperations(Path outPath, DirectoryNode folder, CopyOnWriteFileSystem.Moves result) @ L207

  • 方法名:collectMoveOperations
  • 源码定位:L207
  • 返回类型:void
  • 修饰符:private

参数:

  • outPath: Path
  • folder: DirectoryNode
  • result: CopyOnWriteFileSystem.Moves

说明:

TODO

public static void createDirectories(List<Path> directories) @ L229

  • 方法名:createDirectories
  • 源码定位:L229
  • 返回类型:void
  • 修饰符:public static

参数:

  • directories: List

说明:

TODO

public static void hardLinkFiles(List<FileMove> moves) @ L235

  • 方法名:hardLinkFiles
  • 源码定位:L235
  • 返回类型:void
  • 修饰符:public static

参数:

  • moves: List

说明:

TODO

public static void moveFiles(List<FileMove> moves) @ L247

  • 方法名:moveFiles
  • 源码定位:L247
  • 返回类型:void
  • 修饰符:public static

参数:

  • moves: List

说明:

TODO

public static void moveFilesWithRetry(List<FileMove> moves, CopyOption... options) @ L253

  • 方法名:moveFilesWithRetry
  • 源码定位:L253
  • 返回类型:void
  • 修饰符:public static

参数:

  • moves: List
  • options: CopyOption…

说明:

TODO

public static List<FileMove> tryRevertMoves(List<FileMove> moves, CopyOption... options) @ L265

  • 方法名:tryRevertMoves
  • 源码定位:L265
  • 返回类型:List
  • 修饰符:public static

参数:

  • moves: List
  • options: CopyOption…

说明:

TODO

代码

public class CopyOnWriteFileSystem extends FileSystem {
    private static final Set<String> FILE_ATTRIBUTE_VIEWS = Set.of("basic");
    private static final Logger LOGGER = LogUtils.getLogger();
    private final CopyOnWriteFileStore store;
    private final CopyOnWriteFSProvider provider;
    private final Path baseDirectory;
    private final PathMatcher skippedPaths;
    private final Path tmpDirectory;
    private final CopyOnWriteFSPath rootPath;
    private final AtomicInteger tmpFileIndex = new AtomicInteger();
    private DirectoryNode fileTree;
 
    private CopyOnWriteFileSystem(String name, Path baseDirectory, Path tmpDirectory, PathMatcher skippedPaths) throws IOException {
        this.baseDirectory = baseDirectory;
        this.tmpDirectory = tmpDirectory;
        this.skippedPaths = skippedPaths;
        this.provider = new CopyOnWriteFSProvider(this);
        this.store = new CopyOnWriteFileStore(name, this);
        this.rootPath = this.getPath("/");
        this.fileTree = this.buildFileTreeFrom(baseDirectory);
    }
 
    public static CopyOnWriteFileSystem create(String name, Path baseDirectory, Path tmpDirectory, PathMatcher skippedPaths) throws IOException {
        if (Files.exists(tmpDirectory)) {
            throw new CowFSCreationException("Temporary directory already exists: " + tmpDirectory);
        } else {
            CopyOnWriteFileSystem fileSystem = new CopyOnWriteFileSystem(name, baseDirectory, tmpDirectory, skippedPaths);
            Files.createDirectory(tmpDirectory);
            return fileSystem;
        }
    }
 
    private DirectoryNode buildFileTreeFrom(Path baseDirectory) throws IOException {
        final DirectoryNode fileTree = new DirectoryNode(this.rootPath);
        Files.walkFileTree(baseDirectory, new SimpleFileVisitor<Path>() {
            {
                Objects.requireNonNull(CopyOnWriteFileSystem.this);
            }
 
            public FileVisitResult visitFile(Path realPath, BasicFileAttributes attrs) throws IOException {
                checkAttributes(realPath, attrs);
                if (CopyOnWriteFileSystem.this.skippedPaths.matches(realPath)) {
                    return FileVisitResult.CONTINUE;
                } else {
                    CopyOnWriteFSPath cowPath = this.toCowPath(realPath);
                    DirectoryNode parentNode = fileTree.directoryByPath(Objects.requireNonNull(cowPath.getParent()));
                    parentNode.addChild(new FileNode(cowPath, realPath, false));
                    return FileVisitResult.CONTINUE;
                }
            }
 
            public FileVisitResult preVisitDirectory(Path realPath, BasicFileAttributes attrs) throws IOException {
                checkAttributes(realPath, attrs);
                if (CopyOnWriteFileSystem.this.skippedPaths.matches(realPath)) {
                    return FileVisitResult.SKIP_SUBTREE;
                } else if (realPath.equals(baseDirectory)) {
                    return FileVisitResult.CONTINUE;
                } else {
                    CopyOnWriteFSPath cowPath = this.toCowPath(realPath);
                    DirectoryNode parentNode = fileTree.directoryByPath(Objects.requireNonNull(cowPath.getParent()));
                    parentNode.addChild(new DirectoryNode(cowPath));
                    return FileVisitResult.CONTINUE;
                }
            }
 
            private static void checkAttributes(Path realPath, BasicFileAttributes attrs) throws CowFSCreationException {
                if (!attrs.isRegularFile() && !attrs.isDirectory()) {
                    throw new CowFSSymlinkException("Cannot build copy-on-write file system when symlink is present: " + realPath);
                } else if (!Files.isWritable(realPath)) {
                    throw new CowFSCreationException("Cannot build copy-on-write file system, missing write access for file: " + realPath);
                }
            }
 
            private CopyOnWriteFSPath toCowPath(Path realPath) {
                return fileTree.path().resolve(baseDirectory.relativize(realPath).toString());
            }
        });
        return fileTree;
    }
 
    @VisibleForTesting
    protected void resetFileTreeToBaseFolderContent() throws IOException {
        this.fileTree = this.buildFileTreeFrom(this.baseDirectory);
    }
 
    public CopyOnWriteFSProvider provider() {
        return this.provider;
    }
 
    @Override
    public void close() throws IOException {
        if (Files.exists(this.tmpDirectory)) {
            PathUtils.deleteDirectory(this.tmpDirectory);
        }
    }
 
    @Override
    public boolean isOpen() {
        return true;
    }
 
    @Override
    public boolean isReadOnly() {
        return false;
    }
 
    @Override
    public String getSeparator() {
        return this.backingFileSystem().getSeparator();
    }
 
    @Override
    public Iterable<Path> getRootDirectories() {
        return List.of(this.rootPath());
    }
 
    @Override
    public Iterable<FileStore> getFileStores() {
        return List.of(this.store);
    }
 
    @Override
    public Set<String> supportedFileAttributeViews() {
        return FILE_ATTRIBUTE_VIEWS;
    }
 
    public CopyOnWriteFSPath getPath(String first, String... more) {
        return CopyOnWriteFSPath.of(this, first, more);
    }
 
    @Override
    public PathMatcher getPathMatcher(String syntaxAndPattern) {
        throw new UnsupportedOperationException();
    }
 
    @Override
    public UserPrincipalLookupService getUserPrincipalLookupService() {
        throw new UnsupportedOperationException();
    }
 
    @Override
    public WatchService newWatchService() {
        throw new UnsupportedOperationException();
    }
 
    public CopyOnWriteFileStore store() {
        return this.store;
    }
 
    public CopyOnWriteFSPath rootPath() {
        return this.rootPath;
    }
 
    DirectoryNode fileTree() {
        return this.fileTree;
    }
 
    public Path baseDirectory() {
        return this.baseDirectory;
    }
 
    public Path tmpDirectory() {
        return this.tmpDirectory;
    }
 
    Path createTemporaryFilePath() {
        return this.tmpDirectory.resolve("tmp_" + this.tmpFileIndex.incrementAndGet());
    }
 
    public FileSystem backingFileSystem() {
        return this.tmpDirectory.getFileSystem();
    }
 
    public CopyOnWriteFileSystem.Moves collectMoveOperations(Path outPath) {
        CopyOnWriteFileSystem.Moves result = new CopyOnWriteFileSystem.Moves(new ArrayList<>(), new ArrayList<>(), new ArrayList<>());
        this.collectMoveOperations(outPath, this.fileTree, result);
        return result;
    }
 
    private void collectMoveOperations(Path outPath, DirectoryNode folder, CopyOnWriteFileSystem.Moves result) {
        for (Node childNode : folder.children()) {
            Path target = outPath.resolve(Objects.requireNonNull(childNode.name()));
            switch (childNode) {
                case FileNode fileNode:
                    FileMove move = new FileMove(fileNode.storagePath(), target);
                    if (fileNode.isCopy) {
                        result.copiedFiles.add(move);
                    } else {
                        result.preexistingFiles.add(move);
                    }
                    break;
                case DirectoryNode directoryNode:
                    result.directories.add(target);
                    this.collectMoveOperations(target, directoryNode, result);
                    break;
                default:
                    throw new MatchException(null, null);
            }
        }
    }
 
    public static void createDirectories(List<Path> directories) throws IOException {
        for (Path directory : directories) {
            Files.createDirectory(directory);
        }
    }
 
    public static void hardLinkFiles(List<FileMove> moves) throws IOException {
        for (FileMove move : moves) {
            if (!Files.exists(move.to())) {
                if (!Files.isRegularFile(move.from())) {
                    throw new IllegalStateException("Not a regular file: " + move.from());
                }
 
                Files.createLink(move.to(), move.from());
            }
        }
    }
 
    public static void moveFiles(List<FileMove> moves) throws IOException {
        for (FileMove move : moves) {
            Files.move(move.from(), move.to());
        }
    }
 
    public static void moveFilesWithRetry(List<FileMove> moves, CopyOption... options) throws IOException {
        for (FileMove move : moves) {
            if (Files.exists(move.from()) || !Files.exists(move.to())) {
                if (!Files.isRegularFile(move.from())) {
                    throw new IOException("Not a regular file: " + move.from());
                }
 
                Files.move(move.from(), move.to(), options);
            }
        }
    }
 
    public static List<FileMove> tryRevertMoves(List<FileMove> moves, CopyOption... options) {
        List<FileMove> failedMoves = new ArrayList<>();
 
        for (FileMove move : moves) {
            if (Files.exists(move.to()) || !Files.exists(move.from())) {
                if (Files.isRegularFile(move.to())) {
                    boolean success = Util.safeMoveFile(move.to(), move.from(), options);
                    if (success) {
                        LOGGER.info("Reverted move from {} to {}", move.from(), move.to());
                    } else {
                        LOGGER.error("Failed to revert move from {} to {}", move.from(), move.to());
                        failedMoves.add(move);
                    }
                } else {
                    LOGGER.error("Skipping reverting move from {} to {} as it's not a file", move.from(), move.to());
                    failedMoves.add(move);
                }
            }
        }
 
        if (failedMoves.isEmpty()) {
            LOGGER.info("Successfully reverted back to previous world state");
        } else {
            LOGGER.error("Completed reverting with errors");
        }
 
        return failedMoves;
    }
 
    public record Moves(List<Path> directories, List<FileMove> copiedFiles, List<FileMove> preexistingFiles) {
    }
}

引用的其他类

  • Util

    • 引用位置: 方法调用
    • 关联成员: Util.safeMoveFile()
  • CopyOnWriteFSPath

    • 引用位置: 字段/方法调用/返回值
    • 关联成员: CopyOnWriteFSPath.of()
  • CopyOnWriteFSProvider

    • 引用位置: 字段/构造调用/返回值
    • 关联成员: CopyOnWriteFSProvider()
  • CopyOnWriteFileStore

    • 引用位置: 字段/构造调用/返回值
    • 关联成员: CopyOnWriteFileStore()
  • DirectoryNode

    • 引用位置: 参数/字段/构造调用/返回值
    • 关联成员: DirectoryNode()
  • FileMove

    • 引用位置: 参数/构造调用/返回值
    • 关联成员: FileMove()
  • FileNode

    • 引用位置: 构造调用
    • 关联成员: FileNode()
  • CowFSCreationException

    • 引用位置: 构造调用
    • 关联成员: CowFSCreationException()
  • CowFSSymlinkException

    • 引用位置: 构造调用
    • 关联成员: CowFSSymlinkException()