| // 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. |
| * |
| * <p>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. |
| * |
| * <p>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<Path, Path> 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(); |
| } |
| } |