mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
fixed excessive VFS load and assertion added in tests for accessing VFS outside the project roots
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
package com.intellij.openapi.vfs;
|
||||
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.util.SystemInfo;
|
||||
import com.intellij.openapi.util.io.FileUtil;
|
||||
import com.intellij.openapi.util.registry.Registry;
|
||||
import com.intellij.openapi.vfs.impl.win32.Win32LocalFileSystem;
|
||||
@@ -19,6 +20,22 @@ import java.util.Arrays;
|
||||
public class LocalFileSystemTest extends IdeaTestCase{
|
||||
private static final String KEY = "filesystem.useNative";
|
||||
|
||||
public static void setContentOnDisk(File file, byte[] bom, String content, Charset charset) throws IOException {
|
||||
FileOutputStream stream = new FileOutputStream(file);
|
||||
stream.write(bom);
|
||||
OutputStreamWriter writer = new OutputStreamWriter(stream, charset);
|
||||
writer.write(content);
|
||||
writer.close();
|
||||
}
|
||||
|
||||
public static VirtualFile createTempFile(@NonNls String ext, byte[] bom, @NonNls String content, Charset charset) throws IOException {
|
||||
File temp = FileUtil.createTempFile("copy", "." + ext);
|
||||
setContentOnDisk(temp, bom, content, charset);
|
||||
|
||||
myFilesToDelete.add(temp);
|
||||
return LocalFileSystem.getInstance().refreshAndFindFileByIoFile(temp);
|
||||
}
|
||||
|
||||
public void testChildrenAccessedButNotCached() throws Exception{
|
||||
ApplicationManager.getApplication().runWriteAction(
|
||||
new Runnable() {
|
||||
@@ -116,7 +133,7 @@ public class LocalFileSystemTest extends IdeaTestCase{
|
||||
new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try{
|
||||
try {
|
||||
File fromDir = createTempDirectory();
|
||||
File toDir = createTempDirectory();
|
||||
|
||||
@@ -132,7 +149,7 @@ public class LocalFileSystemTest extends IdeaTestCase{
|
||||
assertEquals(newName, copy.getName());
|
||||
assertTrue(Arrays.equals(byteContent, copy.contentsToByteArray()));
|
||||
}
|
||||
catch(Exception e){
|
||||
catch (Exception e) {
|
||||
LOG.error(e);
|
||||
}
|
||||
}
|
||||
@@ -222,22 +239,22 @@ public class LocalFileSystemTest extends IdeaTestCase{
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
public static void setContentOnDisk(File file, byte[] bom, String content, Charset charset) throws IOException {
|
||||
FileOutputStream stream = new FileOutputStream(file);
|
||||
stream.write(bom);
|
||||
OutputStreamWriter writer = new OutputStreamWriter(stream, charset);
|
||||
writer.write(content);
|
||||
writer.close();
|
||||
}
|
||||
public void testFindRoot() {
|
||||
VirtualFile file = LocalFileSystem.getInstance().findFileByPath("wrong_path");
|
||||
assertNull(file);
|
||||
|
||||
public static VirtualFile createTempFile(@NonNls String ext, byte[] bom, @NonNls String content, Charset charset) throws IOException {
|
||||
File temp = FileUtil.createTempFile("copy", "." + ext);
|
||||
setContentOnDisk(temp, bom, content, charset);
|
||||
if (SystemInfo.isWindows && new File("c:").exists()) {
|
||||
VirtualFile root = LocalFileSystem.getInstance().findFileByPath("c:");
|
||||
assertNotNull(root);
|
||||
}
|
||||
if (SystemInfo.isUnix) {
|
||||
VirtualFile root = LocalFileSystem.getInstance().findFileByPath("/");
|
||||
assertNotNull(root);
|
||||
}
|
||||
|
||||
myFilesToDelete.add(temp);
|
||||
return LocalFileSystem.getInstance().refreshAndFindFileByIoFile(temp);
|
||||
VirtualFile root = LocalFileSystem.getInstance().findFileByPath("");
|
||||
assertNotNull(root);
|
||||
}
|
||||
}
|
||||
|
||||
+26
-8
@@ -30,6 +30,8 @@ import com.intellij.openapi.util.TextRange;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.openapi.vfs.VfsUtil;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.openapi.vfs.VirtualFileSystem;
|
||||
import com.intellij.openapi.vfs.newvfs.NewVirtualFileSystem;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.impl.PsiManagerImpl;
|
||||
import com.intellij.psi.impl.source.resolve.ResolveCache;
|
||||
@@ -193,21 +195,37 @@ public class FileReference implements FileReferenceOwner, PsiPolyVariantReferenc
|
||||
else {
|
||||
final String decoded = decode(text);
|
||||
if (decoded != null) {
|
||||
processVariants(context, new PsiFileSystemItemProcessor() {
|
||||
public boolean acceptItem(String name, boolean isDirectory) {
|
||||
return caseSensitive ? decoded.equals(name) : decoded.compareToIgnoreCase(name) == 0;
|
||||
if (context instanceof PsiDirectory && caseSensitivityApplies((PsiDirectory)context, caseSensitive)) {
|
||||
// optimization: do not load all children into VFS
|
||||
PsiDirectory directory = (PsiDirectory)context;
|
||||
PsiFileSystemItem child = directory.findFile(decoded);
|
||||
if (child == null) child = directory.findSubdirectory(decoded);
|
||||
if (child != null) {
|
||||
result.add(new PsiElementResolveResult(getOriginalFile(child)));
|
||||
}
|
||||
}
|
||||
else {
|
||||
processVariants(context, new PsiFileSystemItemProcessor() {
|
||||
public boolean acceptItem(String name, boolean isDirectory) {
|
||||
return caseSensitive ? decoded.equals(name) : decoded.compareToIgnoreCase(name) == 0;
|
||||
}
|
||||
|
||||
public boolean execute(PsiFileSystemItem element) {
|
||||
result.add(new PsiElementResolveResult(getOriginalFile(element)));
|
||||
return true;
|
||||
}
|
||||
});
|
||||
public boolean execute(PsiFileSystemItem element) {
|
||||
result.add(new PsiElementResolveResult(getOriginalFile(element)));
|
||||
return true;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean caseSensitivityApplies(PsiDirectory context, boolean caseSensitive) {
|
||||
VirtualFileSystem fs = context.getVirtualFile().getFileSystem();
|
||||
return fs instanceof NewVirtualFileSystem && ((NewVirtualFileSystem)fs).isCaseSensitive() == caseSensitive;
|
||||
}
|
||||
|
||||
private boolean isAllowedEmptyPath(String text) {
|
||||
return text.length() == 0 && isLast() &&
|
||||
(StringUtil.isEmpty(myFileReferenceSet.getPathString()) && myFileReferenceSet.isEmptyPathAllowed() ||
|
||||
|
||||
@@ -139,7 +139,8 @@ public class IdeaGateway {
|
||||
|
||||
public static Iterable<VirtualFile> iterateDBChildren(VirtualFile f) {
|
||||
if (!(f instanceof NewVirtualFile)) return ContainerUtil.emptyIterable();
|
||||
return ((NewVirtualFile)f).iterInDbChildren();
|
||||
NewVirtualFile nf = (NewVirtualFile)f;
|
||||
return nf.getCachedChildren();
|
||||
}
|
||||
|
||||
public RootEntry createTransientRootEntry() {
|
||||
|
||||
+2
-1
@@ -20,6 +20,7 @@ import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.util.SystemInfo;
|
||||
import com.intellij.openapi.util.io.FileUtil;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.openapi.vfs.*;
|
||||
import com.intellij.openapi.vfs.ex.VirtualFileManagerEx;
|
||||
import com.intellij.openapi.vfs.newvfs.NewVirtualFile;
|
||||
@@ -126,7 +127,7 @@ public abstract class LocalFileSystemBase extends LocalFileSystem {
|
||||
}
|
||||
|
||||
public boolean exists(@NotNull final VirtualFile fileOrDirectory) {
|
||||
if (fileOrDirectory.getParent() == null) return true;
|
||||
if (StringUtil.isEmpty(fileOrDirectory.getPath())) return true; // fake top dir for Windows
|
||||
return convertToIOFile(fileOrDirectory).exists();
|
||||
}
|
||||
|
||||
|
||||
+125
@@ -19,8 +19,25 @@
|
||||
*/
|
||||
package com.intellij.openapi.vfs.newvfs.impl;
|
||||
|
||||
import com.intellij.openapi.application.Application;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.application.PathManager;
|
||||
import com.intellij.openapi.application.impl.ApplicationImpl;
|
||||
import com.intellij.openapi.module.Module;
|
||||
import com.intellij.openapi.module.ModuleManager;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.project.ProjectManager;
|
||||
import com.intellij.openapi.roots.ModuleRootManager;
|
||||
import com.intellij.openapi.roots.OrderEntry;
|
||||
import com.intellij.openapi.roots.OrderRootType;
|
||||
import com.intellij.openapi.roots.ProjectRootManager;
|
||||
import com.intellij.openapi.util.Condition;
|
||||
import com.intellij.openapi.util.Pair;
|
||||
import com.intellij.openapi.util.io.FileUtil;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.openapi.vfs.JarFileSystem;
|
||||
import com.intellij.openapi.vfs.LocalFileSystem;
|
||||
import com.intellij.openapi.vfs.VfsUtil;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.openapi.vfs.newvfs.NewVirtualFile;
|
||||
import com.intellij.openapi.vfs.newvfs.NewVirtualFileSystem;
|
||||
@@ -28,6 +45,8 @@ import com.intellij.openapi.vfs.newvfs.RefreshQueue;
|
||||
import com.intellij.openapi.vfs.newvfs.events.VFileCreateEvent;
|
||||
import com.intellij.openapi.vfs.newvfs.persistent.PersistentFS;
|
||||
import com.intellij.util.ArrayUtil;
|
||||
import com.intellij.util.PathUtil;
|
||||
import com.intellij.util.SystemProperties;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import com.intellij.util.text.CaseInsensitiveStringHashingStrategy;
|
||||
import gnu.trove.THashMap;
|
||||
@@ -36,14 +55,18 @@ import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.annotations.TestOnly;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.net.URISyntaxException;
|
||||
import java.net.URL;
|
||||
import java.util.*;
|
||||
|
||||
public class VirtualDirectoryImpl extends VirtualFileSystemEntry {
|
||||
private static final VirtualFileSystemEntry NULL_VIRTUAL_FILE = new VirtualFileImpl("*?;%NULL", null, -42);
|
||||
private final NewVirtualFileSystem myFS;
|
||||
private static final boolean IS_UNIT_TESTS = ApplicationManager.getApplication().isUnitTestMode();
|
||||
|
||||
// guarded by this
|
||||
private Object myChildren; // Either HashMap<String, VFile> or VFile[]
|
||||
@@ -141,6 +164,7 @@ public class VirtualDirectoryImpl extends VirtualFileSystemEntry {
|
||||
}
|
||||
else {
|
||||
child = new VirtualFileImpl(name, this, id);
|
||||
assertAccessInTests(child);
|
||||
}
|
||||
|
||||
if (fs.markNewFilesAsDirty()) {
|
||||
@@ -150,6 +174,107 @@ public class VirtualDirectoryImpl extends VirtualFileSystemEntry {
|
||||
return child;
|
||||
}
|
||||
|
||||
private static void assertAccessInTests(VirtualFileSystemEntry child) {
|
||||
if (IS_UNIT_TESTS && ApplicationManager.getApplication() instanceof ApplicationImpl && ((ApplicationImpl)ApplicationManager.getApplication()).isComponentsCreated()) {
|
||||
NewVirtualFileSystem fileSystem = child.getFileSystem();
|
||||
if (fileSystem != LocalFileSystem.getInstance() && fileSystem != JarFileSystem.getInstance()) {
|
||||
return;
|
||||
}
|
||||
// root' children are loaded always
|
||||
if (child.getParent() == null || child.getParent().getParent() == null) return;
|
||||
|
||||
Set<String> allowed = allowedRoots();
|
||||
boolean isUnder = allowed == null;
|
||||
if (!isUnder) {
|
||||
for (String root : allowed) {
|
||||
String childPath = child.getPath();
|
||||
if (child.getFileSystem() == JarFileSystem.getInstance()) {
|
||||
VirtualFile local = JarFileSystem.getInstance().getVirtualFileForJar(child);
|
||||
childPath = local.getPath();
|
||||
}
|
||||
if (FileUtil.startsWith(childPath, root)) {
|
||||
isUnder = true;
|
||||
break;
|
||||
}
|
||||
if (root.startsWith(JarFileSystem.PROTOCOL_PREFIX)) {
|
||||
String rootLocalPath = FileUtil.toSystemIndependentName(PathUtil.toPresentableUrl(root));
|
||||
isUnder = FileUtil.startsWith(childPath, rootLocalPath);
|
||||
if (isUnder) break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!isUnder) {
|
||||
if (!allowed.isEmpty()) {
|
||||
assert false : "File accessed outside project: " + child +"; project roots: "+new ArrayList(allowed);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// null means we were unable to get roots, so do not check access
|
||||
private static Set<String> allowedRoots() {
|
||||
if (insideGettingRoots) return null;
|
||||
Project[] openProjects = ProjectManager.getInstance().getOpenProjects();
|
||||
if (openProjects.length == 0) return null;
|
||||
Set<String> allowed = new THashSet<String>();
|
||||
String homePath = PathManager.getHomePath();
|
||||
allowed.add(FileUtil.toSystemIndependentName(homePath));
|
||||
try {
|
||||
URL outUrl = Application.class.getResource("/");
|
||||
String output = new File(outUrl.toURI()).getParentFile().getParentFile().getPath();
|
||||
allowed.add(FileUtil.toSystemIndependentName(output));
|
||||
}
|
||||
catch (URISyntaxException ignored) {
|
||||
}
|
||||
String javaHome = SystemProperties.getJavaHome();
|
||||
allowed.add(FileUtil.toSystemIndependentName(javaHome));
|
||||
String tempDirectorySpecific = new File(FileUtil.getTempDirectory()).getParent();
|
||||
allowed.add(FileUtil.toSystemIndependentName(tempDirectorySpecific));
|
||||
String tempDirectory = System.getProperty("java.io.tmpdir");
|
||||
allowed.add(FileUtil.toSystemIndependentName(tempDirectory));
|
||||
String home = SystemProperties.getUserHome();
|
||||
allowed.add(FileUtil.toSystemIndependentName(home));
|
||||
for (Project project : openProjects) {
|
||||
if (!project.isInitialized()) {
|
||||
return null; // all is allowed
|
||||
}
|
||||
for (VirtualFile root : ProjectRootManager.getInstance(project).getContentRoots()) {
|
||||
allowed.add(root.getPath());
|
||||
}
|
||||
for (VirtualFile root : getAllRoots(project)) {
|
||||
allowed.add(StringUtil.trimEnd(root.getPath(), JarFileSystem.JAR_SEPARATOR));
|
||||
}
|
||||
String location = project.getLocation();
|
||||
allowed.add(FileUtil.toSystemIndependentName(location));
|
||||
}
|
||||
|
||||
return allowed;
|
||||
}
|
||||
|
||||
private static boolean insideGettingRoots;
|
||||
private static VirtualFile[] getAllRoots(Project project) {
|
||||
insideGettingRoots = true;
|
||||
Set<VirtualFile> roots = new THashSet<VirtualFile>();
|
||||
final Module[] modules = ModuleManager.getInstance(project).getModules();
|
||||
for (Module module : modules) {
|
||||
final ModuleRootManager moduleRootManager = ModuleRootManager.getInstance(module);
|
||||
final OrderEntry[] orderEntries = moduleRootManager.getOrderEntries();
|
||||
for (OrderEntry entry : orderEntries) {
|
||||
VirtualFile[] files;
|
||||
files = entry.getFiles(OrderRootType.CLASSES);
|
||||
ContainerUtil.addAll(roots, files);
|
||||
files = entry.getFiles(OrderRootType.SOURCES);
|
||||
ContainerUtil.addAll(roots, files);
|
||||
files = entry.getFiles(OrderRootType.CLASSES_AND_OUTPUT);
|
||||
ContainerUtil.addAll(roots, files);
|
||||
}
|
||||
}
|
||||
insideGettingRoots = false;
|
||||
return VfsUtil.toVirtualFileArray(roots);
|
||||
}
|
||||
|
||||
|
||||
@Nullable
|
||||
private VirtualFileSystemEntry createAndFindChildWithEventFire(@NotNull String name) {
|
||||
final NewVirtualFileSystem delegate = getFileSystem();
|
||||
|
||||
+6
@@ -688,6 +688,12 @@ public class PersistentFS extends ManagingFS implements ApplicationComponent {
|
||||
public VirtualFile[] getChildren() {
|
||||
return getRoots(fs);
|
||||
}
|
||||
|
||||
@Override
|
||||
public VirtualFileSystemEntry findChild(@NotNull String name) {
|
||||
if (name.length() == 0) return null;
|
||||
return findRoot(name, fs);
|
||||
}
|
||||
};
|
||||
}
|
||||
if (!fs.exists(root)) return null;
|
||||
|
||||
Reference in New Issue
Block a user