From 2cc25d58931dadecf2a97cb736a350e067bc81b3 Mon Sep 17 00:00:00 2001 From: Roman Shevchenko Date: Sat, 24 Dec 2016 20:47:36 +0100 Subject: [PATCH] [vfs] makes visitors safe against symlink loops --- .../com/intellij/openapi/vfs/VfsUtilCore.java | 43 +++++++------- .../AbstractConvertLineSeparatorsAction.java | 26 ++++----- .../src/com/intellij/openapi/vfs/VfsUtil.java | 14 +++-- .../common/FindAllRootsHelper.java | 56 ++++++------------- .../jetbrains/idea/svn/ConflictCreator.java | 46 ++++----------- 5 files changed, 67 insertions(+), 118 deletions(-) diff --git a/platform/core-api/src/com/intellij/openapi/vfs/VfsUtilCore.java b/platform/core-api/src/com/intellij/openapi/vfs/VfsUtilCore.java index d90bbc2c1863..61cce2b0d7d5 100644 --- a/platform/core-api/src/com/intellij/openapi/vfs/VfsUtilCore.java +++ b/platform/core-api/src/com/intellij/openapi/vfs/VfsUtilCore.java @@ -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 processor) { - if (!processor.process(root)) return false; - - if (root.isDirectory()) { - final LinkedList queue = new LinkedList(); - - 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 processor) { + final Ref 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 { - public DistinctVFilesRootsCollection() { - } + public DistinctVFilesRootsCollection() { } public DistinctVFilesRootsCollection(Collection virtualFiles) { super(virtualFiles); @@ -693,6 +687,8 @@ public class VfsUtilCore { } } + // + /** @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 processor, @NotNull Convertor directoryFilter) { @@ -715,4 +711,5 @@ public class VfsUtilCore { } while (!queue.isEmpty()); } } -} + // +} \ No newline at end of file diff --git a/platform/editor-ui-ex/src/com/intellij/codeStyle/AbstractConvertLineSeparatorsAction.java b/platform/editor-ui-ex/src/com/intellij/codeStyle/AbstractConvertLineSeparatorsAction.java index eb3416298d0a..0de60204e633 100644 --- a/platform/editor-ui-ex/src/com/intellij/codeStyle/AbstractConvertLineSeparatorsAction.java +++ b/platform/editor-ui-ex/src/com/intellij/codeStyle/AbstractConvertLineSeparatorsAction.java @@ -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() { - @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; } - ); + }); } } diff --git a/platform/platform-api/src/com/intellij/openapi/vfs/VfsUtil.java b/platform/platform-api/src/com/intellij/openapi/vfs/VfsUtil.java index b8ccd76a9ff1..17c897d80cc3 100644 --- a/platform/platform-api/src/com/intellij/openapi/vfs/VfsUtil.java +++ b/platform/platform-api/src/com/intellij/openapi/vfs/VfsUtil.java @@ -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 processor) { - final FileTypeManager ftm = FileTypeManager.getInstance(); - processFilesRecursively(root, processor, new Convertor() { - public Boolean convert(final VirtualFile vf) { - return ! ftm.isFileIgnored(vf); + public static void processFileRecursivelyWithoutIgnored(@NotNull VirtualFile root, @NotNull Processor 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; } }); } diff --git a/plugins/cvs/cvs-plugin/src/com/intellij/cvsSupport2/cvsoperations/common/FindAllRootsHelper.java b/plugins/cvs/cvs-plugin/src/com/intellij/cvsSupport2/cvsoperations/common/FindAllRootsHelper.java index 756e079f04bd..902844542e30 100644 --- a/plugins/cvs/cvs-plugin/src/com/intellij/cvsSupport2/cvsoperations/common/FindAllRootsHelper.java +++ b/plugins/cvs/cvs-plugin/src/com/intellij/cvsSupport2/cvsoperations/common/FindAllRootsHelper.java @@ -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 findVersionedUnder(final List coll) { final List pathList = ObjectsConvertor.vf2fp(coll); - return impl(pathList.iterator()); - } + final MyVisitor visitor = new MyVisitor(); - public static FilePath[] findVersionedUnder(final FilePath[] roots) { - final List found = impl(Arrays.asList(roots).iterator()); - return ObjectsConvertor.vf2fp(found).toArray(new FilePath[found.size()]); - } - - private static List impl(final Iterator 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, Convertor { - private final List myFound; + private static class MyVisitor extends VirtualFileVisitor { + private final List 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 getFound() { - return myFound; + return file.isDirectory() && found.contains(file) ? SKIP_CHILDREN : CONTINUE; } } } diff --git a/plugins/svn4idea/testSource/org/jetbrains/idea/svn/ConflictCreator.java b/plugins/svn4idea/testSource/org/jetbrains/idea/svn/ConflictCreator.java index e26c2c9fca7a..af024c768ba5 100644 --- a/plugins/svn4idea/testSource/org/jetbrains/idea/svn/ConflictCreator.java +++ b/plugins/svn4idea/testSource/org/jetbrains/idea/svn/ConflictCreator.java @@ -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() { + + 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() { - @Override - public Boolean convert(VirtualFile o) { - return ! SvnUtil.isAdminDirectory(o); - } - }); - /*FileUtil.processFilesRecursively(new File(myTheirsDir.getPath()), new Processor() { - @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);