// Copyright 2017 The Bazel Authors. All rights reserved. // // 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.devtools.build.android.junctions; import com.google.common.base.Preconditions; import com.google.devtools.build.lib.windows.jni.WindowsFileOperations; import java.io.IOException; import java.nio.file.Path; import java.util.HashMap; import java.util.Map; import javax.annotation.Nullable; /** * Junction creator implementation for Windows. * *

Creates a junction (or uses a cached one) for a path. If the path is a directory, the junction * points to it, and the returned path is the junction's path. If the path is a file, the junction * points to its parent, and the returned path is the file's path through the junction. * *

The `close` method deletes all junctions that this object created, along with the `dir` * directory where the junctions are created. The purpose of this is to avoid other methods (such as * ScopedTemporaryDirectory.close) to traverse these junctions believing they are regular * directories and deleting files in them that are actually outside of the directory tree. */ public final class WindowsJunctionCreator implements JunctionCreator { private final Path dir; private Map paths; // allocated lazily, but semantically final private int junctionIndex = 0; public WindowsJunctionCreator(Path dir) { this.dir = Preconditions.checkNotNull(dir); } @Nullable public Path create(@Nullable Path path) throws IOException { if (path == null) { return null; } if (paths == null) { paths = new HashMap<>(); } path = path.toAbsolutePath(); if (path.toFile().isDirectory()) { Path link = paths.get(path); if (link == null) { link = dir.resolve(Integer.toString(junctionIndex++)); WindowsFileOperations.createJunction(link.toString(), path.toString()); paths.put(path, link); } return link; } Path parent = path.getParent(); return (parent == null) ? path : create(parent).resolve(path.getFileName()); } @Override public void close() throws IOException { // Delete all junctions, otherwise the temp directory deleter would follow them and delete files // from directories they point to. if (paths != null) { for (Path link : paths.values()) { link.toFile().delete(); } } dir.toFile().delete(); } }