[vfs] makes visitors safe against symlink loops

This commit is contained in:
Roman Shevchenko
2016-12-24 20:47:36 +01:00
parent c4df8497dc
commit 2cc25d5893
5 changed files with 67 additions and 118 deletions
@@ -18,6 +18,7 @@ package com.intellij.openapi.vfs;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.roots.ContentIterator;
import com.intellij.openapi.util.Comparing;
import com.intellij.openapi.util.Ref;
import com.intellij.openapi.util.SystemInfo;
import com.intellij.openapi.util.SystemInfoRt;
import com.intellij.openapi.util.io.BufferExposingByteArrayInputStream;
@@ -440,6 +441,7 @@ public class VfsUtilCore {
}
@NotNull
@SuppressWarnings("SpellCheckingInspection")
public static String fixURLforIDEA(@NotNull String url) {
// removeLocalhostPrefix - false due to backward compatibility reasons
return toIdeaUrl(url, false);
@@ -590,27 +592,20 @@ public class VfsUtilCore {
return file;
}
public static boolean processFilesRecursively(@NotNull VirtualFile root, @NotNull Processor<VirtualFile> processor) {
if (!processor.process(root)) return false;
if (root.isDirectory()) {
final LinkedList<VirtualFile[]> queue = new LinkedList<VirtualFile[]>();
queue.add(root.getChildren());
do {
final VirtualFile[] files = queue.removeFirst();
for (VirtualFile file : files) {
if (!processor.process(file)) return false;
if (file.isDirectory()) {
queue.add(file.getChildren());
}
public static boolean processFilesRecursively(@NotNull final VirtualFile root, @NotNull final Processor<VirtualFile> processor) {
final Ref<Boolean> result = Ref.create(true);
visitChildrenRecursively(root, new VirtualFileVisitor() {
@NotNull
@Override
public Result visitFileEx(@NotNull VirtualFile file) {
if (!processor.process(file)) {
result.set(Boolean.FALSE);
return skipTo(root);
}
} while (!queue.isEmpty());
}
return true;
return CONTINUE;
}
});
return result.get();
}
/**
@@ -676,8 +671,7 @@ public class VfsUtilCore {
* this collection will keep only distinct files/folders, e.g. C:\foo\bar will be removed when C:\foo is added
*/
public static class DistinctVFilesRootsCollection extends DistinctRootsCollection<VirtualFile> {
public DistinctVFilesRootsCollection() {
}
public DistinctVFilesRootsCollection() { }
public DistinctVFilesRootsCollection(Collection<VirtualFile> virtualFiles) {
super(virtualFiles);
@@ -693,6 +687,8 @@ public class VfsUtilCore {
}
}
//<editor-fold desc="Deprecated stuff.">
/** @deprecated does not handle recursive symlinks, use {@link #visitChildrenRecursively(VirtualFile, VirtualFileVisitor)} (to be removed in IDEA 2018) */
public static void processFilesRecursively(@NotNull VirtualFile root,
@NotNull Processor<VirtualFile> processor,
@NotNull Convertor<VirtualFile, Boolean> directoryFilter) {
@@ -715,4 +711,5 @@ public class VfsUtilCore {
} while (!queue.isEmpty());
}
}
}
//</editor-fold>
}
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2015 JetBrains s.r.o.
* Copyright 2000-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -30,9 +30,9 @@ import com.intellij.openapi.roots.FileIndexFacade;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vfs.VfsUtilCore;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.openapi.vfs.VirtualFileVisitor;
import com.intellij.project.ProjectKt;
import com.intellij.util.LineSeparator;
import com.intellij.util.containers.Convertor;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -93,22 +93,16 @@ public abstract class AbstractConvertLineSeparatorsAction extends AnAction {
VirtualFile projectVirtualDirectory = ProjectKt.getStateStore(project).getDirectoryStoreFile();
final FileTypeRegistry fileTypeManager = FileTypeRegistry.getInstance();
for (VirtualFile file : virtualFiles) {
VfsUtilCore.processFilesRecursively(
file,
file1 -> {
if (shouldProcess(file1, project)) {
changeLineSeparators(project, file1, mySeparator);
}
return true;
},
new Convertor<VirtualFile, Boolean>() {
@Override
public Boolean convert(VirtualFile dir) {
return !dir.equals(projectVirtualDirectory)
&& !fileTypeManager.isFileIgnored(dir); // Exclude files like '.git'
VfsUtilCore.visitChildrenRecursively(file, new VirtualFileVisitor() {
@NotNull
@Override
public Result visitFileEx(@NotNull VirtualFile file) {
if (shouldProcess(file, project)) {
changeLineSeparators(project, file, mySeparator);
}
return file.isDirectory() && (file.equals(projectVirtualDirectory) || fileTypeManager.isFileIgnored(file)) ? SKIP_CHILDREN : CONTINUE;
}
);
});
}
}
@@ -28,7 +28,6 @@ import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vfs.newvfs.NewVirtualFile;
import com.intellij.util.*;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.containers.Convertor;
import com.intellij.util.io.URLUtil;
import gnu.trove.THashSet;
import org.jetbrains.annotations.NonNls;
@@ -451,11 +450,14 @@ public class VfsUtil extends VfsUtilCore {
return result;
}
public static void processFileRecursivelyWithoutIgnored(@NotNull final VirtualFile root, @NotNull final Processor<VirtualFile> processor) {
final FileTypeManager ftm = FileTypeManager.getInstance();
processFilesRecursively(root, processor, new Convertor<VirtualFile, Boolean>() {
public Boolean convert(final VirtualFile vf) {
return ! ftm.isFileIgnored(vf);
public static void processFileRecursivelyWithoutIgnored(@NotNull VirtualFile root, @NotNull Processor<VirtualFile> processor) {
FileTypeManager ftm = FileTypeManager.getInstance();
visitChildrenRecursively(root, new VirtualFileVisitor() {
@NotNull
@Override
public Result visitFileEx(@NotNull VirtualFile file) {
if (!processor.process(file)) return skipTo(root);
return file.isDirectory() && ftm.isFileIgnored(file) ? SKIP_CHILDREN : CONTINUE;
}
});
}
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2010 JetBrains s.r.o.
* Copyright 2000-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -18,62 +18,40 @@ package com.intellij.cvsSupport2.cvsoperations.common;
import com.intellij.cvsSupport2.CvsUtil;
import com.intellij.openapi.vcs.FilePath;
import com.intellij.openapi.vcs.ObjectsConvertor;
import com.intellij.openapi.vfs.VfsUtil;
import com.intellij.openapi.vfs.VfsUtilCore;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.util.Processor;
import com.intellij.util.containers.Convertor;
import com.intellij.openapi.vfs.VirtualFileVisitor;
import org.jetbrains.annotations.NotNull;
import java.util.Arrays;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
public class FindAllRootsHelper {
private FindAllRootsHelper() {
}
private FindAllRootsHelper() { }
public static List<VirtualFile> findVersionedUnder(final List<VirtualFile> coll) {
final List<FilePath> pathList = ObjectsConvertor.vf2fp(coll);
return impl(pathList.iterator());
}
final MyVisitor visitor = new MyVisitor();
public static FilePath[] findVersionedUnder(final FilePath[] roots) {
final List<VirtualFile> found = impl(Arrays.asList(roots).iterator());
return ObjectsConvertor.vf2fp(found).toArray(new FilePath[found.size()]);
}
private static List<VirtualFile> impl(final Iterator<FilePath> iterator) {
final MyProcessor processor = new MyProcessor();
for (; iterator.hasNext();) {
final FilePath root = iterator.next();
for (FilePath root : pathList) {
final VirtualFile vf = root.getVirtualFile();
if (vf == null) continue;
VfsUtil.processFilesRecursively(vf, processor, processor);
VfsUtilCore.visitChildrenRecursively(vf, visitor);
}
return processor.getFound();
return visitor.found;
}
private static class MyProcessor implements Processor<VirtualFile>, Convertor<VirtualFile, Boolean> {
private final List<VirtualFile> myFound;
private static class MyVisitor extends VirtualFileVisitor {
private final List<VirtualFile> found = new LinkedList<>();
private MyProcessor() {
myFound = new LinkedList<>();
}
public Boolean convert(VirtualFile o) {
return ! myFound.contains(o);
}
public boolean process(VirtualFile file) {
@NotNull
@Override
public Result visitFileEx(@NotNull VirtualFile file) {
if (CvsUtil.fileIsUnderCvsMaybeWithVfs(file)) {
myFound.add(file);
found.add(file);
}
return true;
}
public List<VirtualFile> getFound() {
return myFound;
return file.isDirectory() && found.contains(file) ? SKIP_CHILDREN : CONTINUE;
}
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2014 JetBrains s.r.o.
* Copyright 2000-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -21,12 +21,12 @@ import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Comparing;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.vcs.changes.LocalChangeList;
import com.intellij.openapi.vfs.VfsUtil;
import com.intellij.openapi.vfs.VfsUtilCore;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.util.Processor;
import com.intellij.openapi.vfs.VirtualFileVisitor;
import com.intellij.util.TimeoutUtil;
import com.intellij.util.containers.Convertor;
import junit.framework.Assert;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.idea.svn.info.Info;
import org.jetbrains.idea.svn.status.StatusType;
@@ -107,44 +107,22 @@ public class ConflictCreator {
}
}
}
final IOException[] ioe = new IOException[1];
VfsUtil.processFilesRecursively(myTheirsDir, new Processor<VirtualFile>() {
VfsUtilCore.visitChildrenRecursively(myTheirsDir, new VirtualFileVisitor() {
@NotNull
@Override
public boolean process(VirtualFile file) {
if (myTheirsDir.equals(file)) return true;
if (file.isDirectory() && file.getChildren().length == 0) {
public Result visitFileEx(@NotNull VirtualFile file) {
if (!myTheirsDir.equals(file) && file.isDirectory() && file.getChildren().length == 0) {
try {
myClientRunner.delete(myTheirsDir, file.getPath());
}
catch (IOException e) {
ioe[0] = e;
throw new VisitorException(e);
}
}
return true;
return file.isDirectory() && SvnUtil.isAdminDirectory(file) ? SKIP_CHILDREN : CONTINUE;
}
}, new Convertor<VirtualFile, Boolean>() {
@Override
public Boolean convert(VirtualFile o) {
return ! SvnUtil.isAdminDirectory(o);
}
});
/*FileUtil.processFilesRecursively(new File(myTheirsDir.getPath()), new Processor<File>() {
@Override
public boolean process(File file) {
if (file.isDirectory() && file.listFiles().length == 0) {
try {
myClientRunner.delete(myTheirsDir, file.getPath());
}
catch (IOException e) {
ioe[0] = e;
}
}
return true;
}
});*/
if (ioe[0] != null) {
throw ioe[0];
}
}, IOException.class);
// this will commit all patch changes
myClientRunner.checkin(myTheirsDir);