From 66c7e4ad1930b4485cfc5f561e35f8157a7099e2 Mon Sep 17 00:00:00 2001 From: ghidra1 Date: Wed, 3 Apr 2024 09:51:19 -0400 Subject: [PATCH] GP-4472 Improved handling of read-only case for headless analyzer and GhidraURL connections. --- .../app/util/headless/HeadlessAnalyzer.java | 53 ++++++++++++------- .../DefaultGhidraProtocolConnector.java | 32 ++++++----- .../Common/support/analyzeHeadlessREADME.html | 6 ++- 3 files changed, 56 insertions(+), 35 deletions(-) diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/headless/HeadlessAnalyzer.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/headless/HeadlessAnalyzer.java index c08498684f..d1d69c923f 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/headless/HeadlessAnalyzer.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/headless/HeadlessAnalyzer.java @@ -603,9 +603,6 @@ public class HeadlessAnalyzer { */ private boolean checkUpdateOptions() { - boolean isImport = !options.runScriptsNoImport; - boolean commitAllowed = isCommitAllowed(); - if (options.readOnly) { String readOnlyError = "Abort due to Headless analyzer error: The requested -readOnly option " + @@ -621,7 +618,13 @@ public class HeadlessAnalyzer { return false; } } + else if (!isInWritableProject()) { + Msg.error(this, "Processing files within read-only project/repository " + + "- the -readOnly option is required."); + return false; + } + boolean commitAllowed = isCommitAllowed(); if (options.commit && !commitAllowed) { Msg.error(this, "Commit to repository not possible (due to permission or connection issue)"); @@ -640,6 +643,7 @@ public class HeadlessAnalyzer { } if (options.overwrite) { + boolean isImport = !options.runScriptsNoImport; if (!isImport) { Msg.info(this, "Ignoring -overwrite because it is not applicable to -process mode."); @@ -654,6 +658,10 @@ public class HeadlessAnalyzer { return true; } + private boolean isInWritableProject() { + return project.getProjectData().getRootFolder().isInWritableProject(); + } + private boolean isCommitAllowed() { RepositoryAdapter repository = project.getRepository(); if (repository == null) { @@ -666,7 +674,7 @@ public class HeadlessAnalyzer { } User user = repository.getUser(); if (!user.hasWritePermission()) { - Msg.warn(this, "User '" + user.getName() + + Msg.error(this, "User '" + user.getName() + "' does not have write permission to repository - commit not allowed"); return false; } @@ -1126,31 +1134,38 @@ public class HeadlessAnalyzer { boolean keepFile = true; // if false file should be deleted after release boolean terminateCheckoutWhenDone = false; - boolean readOnlyFile = options.readOnly || domFile.isReadOnly(); + boolean readOnlyFile = + options.readOnly || domFile.isReadOnly() || !domFile.isInWritableProject(); try { // Exclusive checkout required when commit option specified - if (!readOnlyFile) { - if (domFile.isVersioned()) { - if (!domFile.isCheckedOut()) { - if (!domFile.checkout(options.commit, TaskMonitor.DUMMY)) { - Msg.warn(this, "Skipped processing for " + domFile.getPathname() + - " -- failed to get exclusive file checkout required for commit"); - return; - } - } - else if (options.commit && !domFile.isCheckedOutExclusive()) { - Msg.error(this, "Skipped processing for " + domFile.getPathname() + - " -- file is checked-out non-exclusive (commit requires exclusive checkout)"); + if (!readOnlyFile && domFile.isVersioned()) { + if (!domFile.isCheckedOut()) { + if (!domFile.canCheckout()) { + Msg.warn(this, "Skipped processing for " + domFile.getPathname() + + " within read-only repository"); return; } + if (!domFile.checkout(options.commit, TaskMonitor.DUMMY)) { + Msg.warn(this, "Skipped processing for " + domFile.getPathname() + + " -- failed to get exclusive file checkout required for commit"); + return; + } + // Only terminate checkout when done if we did the checkout + terminateCheckoutWhenDone = true; + } + else if (options.commit && !domFile.isCheckedOutExclusive()) { + Msg.error(this, "Skipped processing for " + domFile.getPathname() + + " -- file is checked-out non-exclusive (commit requires exclusive checkout)"); + return; } - terminateCheckoutWhenDone = true; } program = (Program) domFile.getDomainObject(this, true, false, TaskMonitor.DUMMY); - Msg.info(this, "REPORT: Processing project file: " + domFile.getPathname()); + String readOnlyText = readOnlyFile ? "read-only " : ""; + Msg.info(this, + "REPORT: Processing " + readOnlyText + "project file: " + domFile.getPathname()); // This method already takes into account whether the user has set the "noanalysis" // flag or not diff --git a/Ghidra/Framework/Project/src/main/java/ghidra/framework/protocol/ghidra/DefaultGhidraProtocolConnector.java b/Ghidra/Framework/Project/src/main/java/ghidra/framework/protocol/ghidra/DefaultGhidraProtocolConnector.java index c0350f7bd8..db6fa89e6f 100644 --- a/Ghidra/Framework/Project/src/main/java/ghidra/framework/protocol/ghidra/DefaultGhidraProtocolConnector.java +++ b/Ghidra/Framework/Project/src/main/java/ghidra/framework/protocol/ghidra/DefaultGhidraProtocolConnector.java @@ -65,28 +65,32 @@ public class DefaultGhidraProtocolConnector extends GhidraProtocolConnector { repositoryServerAdapter = ClientUtil.getRepositoryServer(url.getHost(), url.getPort(), true); + if (!repositoryServerAdapter.isConnected()) { + if (repositoryServerAdapter.isCancelled()) { + return statusCode; + } + Throwable t = repositoryServerAdapter.getLastConnectError(); + if (t instanceof LoginException) { + statusCode = StatusCode.UNAUTHORIZED; + } + return statusCode; + } if (repositoryName == null) { + if (repositoryServerAdapter.isReadOnly()) { + this.readOnly = true; // write access not permitted + Msg.warn(this, "User does not have write permission for server"); + } statusCode = StatusCode.OK; return statusCode; } repositoryAdapter = repositoryServerAdapter.getRepository(repositoryName); - if (repositoryServerAdapter.isConnected()) { - try { - repositoryAdapter.connect(); - } - catch (RepositoryNotFoundException e) { - statusCode = StatusCode.NOT_FOUND; - return statusCode; - } + try { + repositoryAdapter.connect(); } - else if (!repositoryServerAdapter.isCancelled()) { - Throwable t = repositoryServerAdapter.getLastConnectError(); - if (t instanceof LoginException) { - statusCode = StatusCode.UNAUTHORIZED; - } - //throw new NotConnectedException("Not connected to repository server", t); + catch (RepositoryNotFoundException e) { + statusCode = StatusCode.NOT_FOUND; return statusCode; } diff --git a/Ghidra/RuntimeScripts/Common/support/analyzeHeadlessREADME.html b/Ghidra/RuntimeScripts/Common/support/analyzeHeadlessREADME.html index fedcfddef1..6f917e9d43 100644 --- a/Ghidra/RuntimeScripts/Common/support/analyzeHeadlessREADME.html +++ b/Ghidra/RuntimeScripts/Common/support/analyzeHeadlessREADME.html @@ -415,8 +415,10 @@ The Headless Analyzer uses the command-line parameters discussed below. See -readOnly
If present in -import mode, imported files will NOT be saved to the project. If present in -process mode, any changes made to existing - files by scripts or analysis are discarded. The -overwrite option - will be ignored if this option is specified during import operations. + files by scripts or analysis are discarded. When processing a shared project or URL associated + with a read-only repository, such files will be skipped unless this option is specified. + The -overwrite option will be ignored if this option is specified + during import operations.