From 1990d2ed4b8837d798a863190a25366dc8a44cd8 Mon Sep 17 00:00:00 2001 From: Ryan Kurtz Date: Thu, 6 Mar 2025 11:38:37 -0500 Subject: [PATCH] GP-5458: GhidraProject and DefaultProjectManager methods for opening projects now throw more granular exceptions --- .../ghidra/dbg/isf/IsfServerLauncher.java | 7 +-- .../ghidra/base/project/GhidraProject.java | 59 ++++++++----------- .../src/main/java/ghidra/test/TestEnv.java | 4 +- .../project/tool/AbstractToolSavingTest.java | 6 +- .../framework/model/ProjectManager.java | 7 ++- .../project/DefaultProjectManager.java | 26 ++++---- 6 files changed, 51 insertions(+), 58 deletions(-) diff --git a/Ghidra/Debug/Debugger-isf/src/main/java/ghidra/dbg/isf/IsfServerLauncher.java b/Ghidra/Debug/Debugger-isf/src/main/java/ghidra/dbg/isf/IsfServerLauncher.java index 0fef4fcb24..039692c259 100644 --- a/Ghidra/Debug/Debugger-isf/src/main/java/ghidra/dbg/isf/IsfServerLauncher.java +++ b/Ghidra/Debug/Debugger-isf/src/main/java/ghidra/dbg/isf/IsfServerLauncher.java @@ -4,9 +4,9 @@ * 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. @@ -16,7 +16,6 @@ package ghidra.dbg.isf; import java.io.File; -import java.io.IOException; import ghidra.GhidraApplicationLayout; import ghidra.GhidraLaunchable; @@ -46,7 +45,7 @@ public class IsfServerLauncher implements GhidraLaunchable { server.startServer(); } - GhidraProject parseArgs(String[] args) throws IOException { + GhidraProject parseArgs(String[] args) throws Exception { if (args != null && args.length < 1) { usage(); return null; diff --git a/Ghidra/Features/Base/src/main/java/ghidra/base/project/GhidraProject.java b/Ghidra/Features/Base/src/main/java/ghidra/base/project/GhidraProject.java index 4d4a1625ee..f0df10343f 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/base/project/GhidraProject.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/base/project/GhidraProject.java @@ -40,7 +40,6 @@ import ghidra.test.ProjectTestUtils; import ghidra.util.*; import ghidra.util.exception.*; import ghidra.util.task.TaskMonitor; -import ghidra.util.task.TaskMonitorAdapter; /** * Helper class for using Ghidra in a "batch" mode. This class provides methods @@ -67,10 +66,14 @@ public class GhidraProject { * @param projectsDir the directory containing the Ghidra project. * @param projectName the name of the ghidra project. * @return an open ghidra project. - * @throws IOException if there was a problem accessing the project + * @throws NotFoundException if the file for the project was + * not found. + * @throws NotOwnerException if the project owner is not the user + * @throws LockException if the project is already opened by another user + * @throws IOException if an IO-related problem occurred */ public static GhidraProject openProject(String projectsDir, String projectName) - throws IOException { + throws NotFoundException, NotOwnerException, LockException, IOException { return new GhidraProject(projectsDir, projectName, false); } @@ -82,15 +85,19 @@ public class GhidraProject { * @param projectName the name of the ghidra project. * @param restoreProject if true the project tool state is restored * @return an open ghidra project. - * @throws IOException if there was a problem accessing the project + * @throws NotFoundException if the file for the project was not found. + * @throws NotOwnerException if the project owner is not the user + * @throws LockException if the project is already opened by another user + * @throws IOException if an IO-related problem occurred */ public static GhidraProject openProject(String projectsDir, String projectName, - boolean restoreProject) throws IOException { + boolean restoreProject) + throws NotFoundException, NotOwnerException, LockException, IOException { return new GhidraProject(projectsDir, projectName, restoreProject); } private GhidraProject(String projectParentDir, String projectName, boolean restoreProject) - throws IOException { + throws NotFoundException, NotOwnerException, LockException, IOException { if (!ghidra.framework.Application.isInitialized()) { throw new AssertException("The GhidraProject requires the system to be " + "initialized before usage. See GhidraApplication.initialize() for more " + @@ -98,25 +105,8 @@ public class GhidraProject { } ProjectLocator projectLocator = new ProjectLocator(projectParentDir, projectName); - try { - project = projectManager.openProject(projectLocator, restoreProject, false); - if (project == null) { - throw new IOException("Failed to open project: " + projectName); - } - projectData = project.getProjectData(); - } - catch (MalformedURLException e) { - throw new IOException("Bad Project URL: " + projectLocator, e); - } - catch (NotFoundException e) { - throw new IOException("Project not found: " + projectLocator, e); - } - catch (NotOwnerException e) { - throw new IOException("Not project owner: " + projectName, e); - } - catch (LockException e) { - throw new IOException("Project is locked: " + projectName, e); - } + project = projectManager.openProject(projectLocator, restoreProject, false); + projectData = project.getProjectData(); } /** @@ -146,9 +136,10 @@ public class GhidraProject { * * @param host Ghidra Server host * @param port Ghidra Server port (0 = use default port) - * @param repositoryName + * @param repositoryName The repository name * @param createIfNeeded if true repository will be created if it does not exist - * @throws DuplicateNameException + * @throws DuplicateNameException if the repository name already exists + * @return A {@link RepositoryAdapter handle} to the new repository */ public static RepositoryAdapter getServerRepository(String host, int port, String repositoryName, boolean createIfNeeded) throws DuplicateNameException { @@ -226,15 +217,15 @@ public class GhidraProject { } /** - * Returns the underlying Project instance or null if project was opened for - * READ access only. + * {@return the underlying Project instance or null if project was opened for + * READ access only.} */ public Project getProject() { return project; } /** - * Returns the underlying ProjectData instance. + * {@return the underlying ProjectData instance.} */ public ProjectData getProjectData() { return projectData; @@ -376,7 +367,7 @@ public class GhidraProject { } /** - * Get the root folder for the Ghidra project. + * {@return the root folder for the Ghidra project.} */ public DomainFolder getRootFolder() { return projectData.getRootFolder(); @@ -490,7 +481,7 @@ public class GhidraProject { throw new DuplicateFileException("File already exists: " + file); } } - program.saveToPackedFile(file, TaskMonitorAdapter.DUMMY); + program.saveToPackedFile(file, TaskMonitor.DUMMY); } catch (CancelledException e1) { throw new IOException("Cancelled"); @@ -563,9 +554,9 @@ public class GhidraProject { } /** - * Returns a PropertList containing all the analysis option properties that + * {@return a PropertList containing all the analysis option properties that * can be set. Changing the value of the analysis properties will affect - * what happens when the analyze call is made. + * what happens when the analyze call is made.} * * @param program * the program whose analysis options are to be set. diff --git a/Ghidra/Features/Base/src/main/java/ghidra/test/TestEnv.java b/Ghidra/Features/Base/src/main/java/ghidra/test/TestEnv.java index d1e780aa57..7f9ccfad62 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/test/TestEnv.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/test/TestEnv.java @@ -590,9 +590,9 @@ public class TestEnv { * A convenience method to close and then reopen the default project created by this TestEnv * instance. This will not delete the project between opening and closing and will restore * the project to its previous state. - * @throws IOException if any exception occurs while saving and reopening + * @throws Exception if any exception occurs while saving and reopening */ - public void closeAndReopenProject() throws IOException { + public void closeAndReopenProject() throws Exception { gp.setDeleteOnClose(false); Project project = gp.getProject(); ProjectLocator projectLocator = project.getProjectLocator(); diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/framework/project/tool/AbstractToolSavingTest.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/framework/project/tool/AbstractToolSavingTest.java index 8c50d3f28a..e2a0fd670d 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/framework/project/tool/AbstractToolSavingTest.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/framework/project/tool/AbstractToolSavingTest.java @@ -4,9 +4,9 @@ * 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. @@ -114,7 +114,7 @@ public abstract class AbstractToolSavingTest extends AbstractGhidraHeadedIntegra testEnv.getProject().save(); testEnv.closeAndReopenProject(); } - catch (IOException e) { + catch (Exception e) { AssertionFailedError afe = new AssertionFailedError(); afe.initCause(e); throw afe; diff --git a/Ghidra/Framework/Project/src/main/java/ghidra/framework/model/ProjectManager.java b/Ghidra/Framework/Project/src/main/java/ghidra/framework/model/ProjectManager.java index ae2798ffc1..d31f0c092e 100644 --- a/Ghidra/Framework/Project/src/main/java/ghidra/framework/model/ProjectManager.java +++ b/Ghidra/Framework/Project/src/main/java/ghidra/framework/model/ProjectManager.java @@ -4,9 +4,9 @@ * 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. @@ -111,9 +111,10 @@ public interface ProjectManager { * not found. * @throws NotOwnerException if the project owner is not the user * @throws LockException if the project is already opened by another user + * @throws IOException if there was an IO-related error */ public Project openProject(ProjectLocator projectLocator, boolean doRestore, boolean resetOwner) - throws NotFoundException, NotOwnerException, LockException; + throws NotFoundException, NotOwnerException, LockException, IOException; /** * Delete the project in the given location. diff --git a/Ghidra/Framework/Project/src/main/java/ghidra/framework/project/DefaultProjectManager.java b/Ghidra/Framework/Project/src/main/java/ghidra/framework/project/DefaultProjectManager.java index 4a02e26917..c438c4595c 100644 --- a/Ghidra/Framework/Project/src/main/java/ghidra/framework/project/DefaultProjectManager.java +++ b/Ghidra/Framework/Project/src/main/java/ghidra/framework/project/DefaultProjectManager.java @@ -4,9 +4,9 @@ * 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. @@ -119,12 +119,12 @@ public class DefaultProjectManager implements ProjectManager { @Override public Project openProject(ProjectLocator projectLocator, boolean doRestore, boolean resetOwner) - throws NotFoundException, NotOwnerException, LockException { + throws NotFoundException, NotOwnerException, LockException, IOException { if (currentProject != null) { - Msg.error(this, - "Current project must be closed before establishing a new active project"); - return null; + String msg = "Current project must be closed before establishing a new active project"; + Msg.error(this, msg); + throw new LockException(msg); } if (!projectLocator.getMarkerFile().exists()) { @@ -141,7 +141,6 @@ public class DefaultProjectManager implements ProjectManager { try { currentProject = new DefaultProject(this, projectLocator, resetOwner); - AppInfo.setActiveProject(currentProject); if (doRestore) { currentProject.restore(); } @@ -152,17 +151,22 @@ public class DefaultProjectManager implements ProjectManager { return currentProject; } catch (LockException e) { - return null; + Msg.showError(LOG, null, "Locked Project!", + "Cannot open locked project: " + projectLocator, e); + throw e; } catch (ReadOnlyException e) { Msg.showError(LOG, null, "Read-only Project!", - "Cannot open project for update: " + projectLocator); + "Could not open project for update: " + projectLocator, e); + throw e; } catch (IOException e) { Msg.showError(LOG, null, "Open Project Failed!", - "Could not open project " + projectLocator + "\n \nCAUSE: " + e.getMessage()); + "Could not open project " + projectLocator + "\n \nCAUSE: " + e.getMessage(), e); + throw e; } finally { + AppInfo.setActiveProject(currentProject); if (currentProject == null) { File dirFile = projectLocator.getProjectDir(); if (!dirFile.exists() || !dirFile.isDirectory()) { @@ -170,8 +174,6 @@ public class DefaultProjectManager implements ProjectManager { } } } - AppInfo.setActiveProject(null); - return null; } /**