mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2026-06-01 22:58:14 +08:00
GP-4456 Significantly improved shared project directory performance when directories contain a very large number of files.
This commit is contained in:
@@ -74,6 +74,14 @@ public interface FileSystem {
|
|||||||
*/
|
*/
|
||||||
public String[] getItemNames(String folderPath) throws IOException;
|
public String[] getItemNames(String folderPath) throws IOException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a list of the folder items contained in the given folder.
|
||||||
|
* @param folderPath the path of the folder.
|
||||||
|
* @return a list of folder items.
|
||||||
|
* @throws IOException
|
||||||
|
*/
|
||||||
|
public FolderItem[] getItems(String folderPath) throws IOException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the FolderItem in the given folder with the given name
|
* Returns the FolderItem in the given folder with the given name
|
||||||
* @param folderPath the folder path containing the item.
|
* @param folderPath the folder path containing the item.
|
||||||
@@ -108,8 +116,8 @@ public interface FileSystem {
|
|||||||
* all alphanumerics
|
* all alphanumerics
|
||||||
* @throws IOException thrown if an IO error occurs.
|
* @throws IOException thrown if an IO error occurs.
|
||||||
*/
|
*/
|
||||||
public void createFolder(String parentPath, String folderName) throws InvalidNameException,
|
public void createFolder(String parentPath, String folderName)
|
||||||
IOException;
|
throws InvalidNameException, IOException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new database item within the specified parent folder using the contents
|
* Create a new database item within the specified parent folder using the contents
|
||||||
@@ -133,8 +141,8 @@ public interface FileSystem {
|
|||||||
*/
|
*/
|
||||||
public DatabaseItem createDatabase(String parentPath, String name, String fileID,
|
public DatabaseItem createDatabase(String parentPath, String name, String fileID,
|
||||||
BufferFile bufferFile, String comment, String contentType, boolean resetDatabaseId,
|
BufferFile bufferFile, String comment, String contentType, boolean resetDatabaseId,
|
||||||
TaskMonitor monitor, String user) throws InvalidNameException, IOException,
|
TaskMonitor monitor, String user)
|
||||||
CancelledException;
|
throws InvalidNameException, IOException, CancelledException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new empty database item within the specified parent folder.
|
* Create a new empty database item within the specified parent folder.
|
||||||
@@ -176,8 +184,8 @@ public interface FileSystem {
|
|||||||
* @throws CancelledException if cancelled by monitor
|
* @throws CancelledException if cancelled by monitor
|
||||||
*/
|
*/
|
||||||
public DataFileItem createDataFile(String parentPath, String name, InputStream istream,
|
public DataFileItem createDataFile(String parentPath, String name, InputStream istream,
|
||||||
String comment, String contentType, TaskMonitor monitor) throws InvalidNameException,
|
String comment, String contentType, TaskMonitor monitor)
|
||||||
IOException, CancelledException;
|
throws InvalidNameException, IOException, CancelledException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new file item from a packed file.
|
* Creates a new file item from a packed file.
|
||||||
@@ -195,8 +203,8 @@ public interface FileSystem {
|
|||||||
* @throws CancelledException if cancelled by monitor
|
* @throws CancelledException if cancelled by monitor
|
||||||
*/
|
*/
|
||||||
public FolderItem createFile(String parentPath, String name, File packedFile,
|
public FolderItem createFile(String parentPath, String name, File packedFile,
|
||||||
TaskMonitor monitor, String user) throws InvalidNameException, IOException,
|
TaskMonitor monitor, String user)
|
||||||
CancelledException;
|
throws InvalidNameException, IOException, CancelledException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Delete the specified folder.
|
* Delete the specified folder.
|
||||||
|
|||||||
@@ -60,9 +60,8 @@ public interface FolderItem {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the file ID if one has been established or null
|
* Return the file ID if one has been established or null
|
||||||
* @throws IOException thrown if IO or access error occurs
|
|
||||||
*/
|
*/
|
||||||
String getFileID() throws IOException;
|
String getFileID();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Assign a new file-ID to this local non-versioned file.
|
* Assign a new file-ID to this local non-versioned file.
|
||||||
@@ -288,8 +287,8 @@ public interface FolderItem {
|
|||||||
* @throws IOException
|
* @throws IOException
|
||||||
* @throws CancelledException if monitor cancels operation
|
* @throws CancelledException if monitor cancels operation
|
||||||
*/
|
*/
|
||||||
public void output(File outputFile, int version, TaskMonitor monitor) throws IOException,
|
public void output(File outputFile, int version, TaskMonitor monitor)
|
||||||
CancelledException;
|
throws IOException, CancelledException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns this instance after refresh or null if item no longer exists
|
* Returns this instance after refresh or null if item no longer exists
|
||||||
|
|||||||
+3
-5
@@ -604,9 +604,8 @@ public class IndexedLocalFileSystem extends LocalFileSystem {
|
|||||||
deallocateItemStorage(parentPath, name);
|
deallocateItemStorage(parentPath, name);
|
||||||
}
|
}
|
||||||
finally {
|
finally {
|
||||||
Msg.warn(this,
|
Msg.warn(this, "Detected orphaned project file " + conflictedItemStorageName +
|
||||||
"Detected orphaned project file " + conflictedItemStorageName + ": " +
|
": " + getPath(parentPath, name));
|
||||||
getPath(parentPath, name));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -893,8 +892,7 @@ public class IndexedLocalFileSystem extends LocalFileSystem {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected String[] getItemNames(String folderPath, boolean includeHiddenFiles)
|
public String[] getItemNames(String folderPath, boolean includeHiddenFiles) throws IOException {
|
||||||
throws IOException {
|
|
||||||
if (readOnly) {
|
if (readOnly) {
|
||||||
refreshReadOnlyIndex();
|
refreshReadOnlyIndex();
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-1
@@ -134,7 +134,8 @@ public class IndexedV1LocalFileSystem extends IndexedLocalFileSystem {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public FolderItem getItem(String fileID) throws IOException, UnsupportedOperationException {
|
public LocalFolderItem getItem(String fileID)
|
||||||
|
throws IOException, UnsupportedOperationException {
|
||||||
checkDisposed();
|
checkDisposed();
|
||||||
if (fileIdMap == null) {
|
if (fileIdMap == null) {
|
||||||
return null;
|
return null;
|
||||||
|
|||||||
+14
-7
@@ -366,13 +366,9 @@ public abstract class LocalFileSystem implements FileSystem {
|
|||||||
protected abstract void deallocateItemStorage(String folderPath, String itemName)
|
protected abstract void deallocateItemStorage(String folderPath, String itemName)
|
||||||
throws IOException;
|
throws IOException;
|
||||||
|
|
||||||
protected abstract String[] getItemNames(String folderPath, boolean includeHiddenFiles)
|
public abstract String[] getItemNames(String folderPath, boolean includeHiddenFiles)
|
||||||
throws IOException;
|
throws IOException;
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @see ghidra.framework.store.FileSystem#getItemNames(java.lang.String)
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public synchronized String[] getItemNames(String folderPath) throws IOException {
|
public synchronized String[] getItemNames(String folderPath) throws IOException {
|
||||||
return getItemNames(folderPath, false);
|
return getItemNames(folderPath, false);
|
||||||
@@ -407,10 +403,21 @@ public abstract class LocalFileSystem implements FileSystem {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public FolderItem getItem(String fileID) throws IOException, UnsupportedOperationException {
|
public LocalFolderItem getItem(String fileID)
|
||||||
|
throws IOException, UnsupportedOperationException {
|
||||||
throw new UnsupportedOperationException("getItem by File-ID");
|
throw new UnsupportedOperationException("getItem by File-ID");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public LocalFolderItem[] getItems(String folderPath) throws IOException {
|
||||||
|
String[] itemNames = getItemNames(folderPath, false);
|
||||||
|
LocalFolderItem[] folderItems = new LocalFolderItem[itemNames.length];
|
||||||
|
for (int i = 0; i < itemNames.length; i++) {
|
||||||
|
folderItems[i] = getItem(folderPath, itemNames[i]);
|
||||||
|
}
|
||||||
|
return folderItems;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public synchronized LocalDatabaseItem createDatabase(String parentPath, String name,
|
public synchronized LocalDatabaseItem createDatabase(String parentPath, String name,
|
||||||
String fileID, BufferFile bufferFile, String comment, String contentType,
|
String fileID, BufferFile bufferFile, String comment, String contentType,
|
||||||
@@ -729,7 +736,7 @@ public abstract class LocalFileSystem implements FileSystem {
|
|||||||
if (folderPath.length() == 1) {
|
if (folderPath.length() == 1) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
String[] items = getItemNames(folderPath);
|
String[] items = getItemNames(folderPath, false);
|
||||||
if (items.length > 0) {
|
if (items.length > 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
+7
-11
@@ -140,8 +140,7 @@ public class MangledLocalFileSystem extends LocalFileSystem {
|
|||||||
// }
|
// }
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected String[] getItemNames(String folderPath, boolean includeHiddenFiles)
|
public String[] getItemNames(String folderPath, boolean includeHiddenFiles) throws IOException {
|
||||||
throws IOException {
|
|
||||||
|
|
||||||
File dir = getFile(folderPath);
|
File dir = getFile(folderPath);
|
||||||
File[] dirList = dir.listFiles();
|
File[] dirList = dir.listFiles();
|
||||||
@@ -294,8 +293,7 @@ public class MangledLocalFileSystem extends LocalFileSystem {
|
|||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public synchronized void renameFolder(String parentPath, String folderName,
|
public synchronized void renameFolder(String parentPath, String folderName,
|
||||||
String newFolderName)
|
String newFolderName) throws InvalidNameException, IOException {
|
||||||
throws InvalidNameException, IOException {
|
|
||||||
|
|
||||||
if (readOnly) {
|
if (readOnly) {
|
||||||
throw new ReadOnlyException();
|
throw new ReadOnlyException();
|
||||||
@@ -440,16 +438,14 @@ public class MangledLocalFileSystem extends LocalFileSystem {
|
|||||||
|
|
||||||
cleanupAfterConstruction(); // remove all temporary content
|
cleanupAfterConstruction(); // remove all temporary content
|
||||||
|
|
||||||
File tmpRoot =
|
File tmpRoot = new File(root.getCanonicalFile().getParentFile(),
|
||||||
new File(root.getCanonicalFile().getParentFile(), HIDDEN_DIR_PREFIX + '.' +
|
HIDDEN_DIR_PREFIX + '.' + root.getName());
|
||||||
root.getName());
|
|
||||||
if (tmpRoot.exists() || !tmpRoot.mkdir()) {
|
if (tmpRoot.exists() || !tmpRoot.mkdir()) {
|
||||||
throw new IOException("Failed to create data directory: " + tmpRoot);
|
throw new IOException("Failed to create data directory: " + tmpRoot);
|
||||||
}
|
}
|
||||||
|
|
||||||
IndexedV1LocalFileSystem indexedFs =
|
IndexedV1LocalFileSystem indexedFs = new IndexedV1LocalFileSystem(tmpRoot.getAbsolutePath(),
|
||||||
new IndexedV1LocalFileSystem(tmpRoot.getAbsolutePath(), isVersioned, false, false,
|
isVersioned, false, false, true);
|
||||||
true);
|
|
||||||
|
|
||||||
migrationInProgress = true;
|
migrationInProgress = true;
|
||||||
migrateFolder(SEPARATOR, indexedFs);
|
migrateFolder(SEPARATOR, indexedFs);
|
||||||
@@ -474,7 +470,7 @@ public class MangledLocalFileSystem extends LocalFileSystem {
|
|||||||
indexedFs.createFolder(folderPath, name);
|
indexedFs.createFolder(folderPath, name);
|
||||||
migrateFolder(getPath(folderPath, name), indexedFs);
|
migrateFolder(getPath(folderPath, name), indexedFs);
|
||||||
}
|
}
|
||||||
for (String name : getItemNames(folderPath)) {
|
for (String name : getItemNames(folderPath, false)) {
|
||||||
LocalFolderItem item = getItem(folderPath, name);
|
LocalFolderItem item = getItem(folderPath, name);
|
||||||
indexedFs.migrateItem(item);
|
indexedFs.migrateItem(item);
|
||||||
}
|
}
|
||||||
|
|||||||
+13
@@ -108,6 +108,19 @@ public class RemoteFileSystem implements FileSystem, RemoteAdapterListener {
|
|||||||
return names;
|
return names;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public FolderItem[] getItems(String folderPath) throws IOException {
|
||||||
|
RepositoryItem[] items = repository.getItemList(folderPath);
|
||||||
|
FolderItem[] folderItems = new FolderItem[items.length];
|
||||||
|
for (int i = 0; i < items.length; i++) {
|
||||||
|
if (items[i].getItemType() != RepositoryItem.DATABASE) {
|
||||||
|
throw new IOException("Unsupported file type");
|
||||||
|
}
|
||||||
|
folderItems[i] = new RemoteDatabaseItem(repository, items[i]);
|
||||||
|
}
|
||||||
|
return folderItems;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public synchronized FolderItem getItem(String folderPath, String name) throws IOException {
|
public synchronized FolderItem getItem(String folderPath, String name) throws IOException {
|
||||||
RepositoryItem item = repository.getItem(folderPath, name);
|
RepositoryItem item = repository.getItem(folderPath, name);
|
||||||
|
|||||||
+10
-8
@@ -907,15 +907,17 @@ public class DefaultProjectData implements ProjectData {
|
|||||||
private void findCheckedOutFiles(String folderPath, List<DomainFile> checkoutList,
|
private void findCheckedOutFiles(String folderPath, List<DomainFile> checkoutList,
|
||||||
TaskMonitor monitor) throws IOException, CancelledException {
|
TaskMonitor monitor) throws IOException, CancelledException {
|
||||||
|
|
||||||
for (String name : fileSystem.getItemNames(folderPath)) {
|
DomainFolder folder = getFolder(folderPath);
|
||||||
monitor.checkCancelled();
|
if (folder == null) {
|
||||||
LocalFolderItem item = fileSystem.getItem(folderPath, name);
|
return;
|
||||||
if (item.getCheckoutId() != FolderItem.DEFAULT_CHECKOUT_ID) {
|
|
||||||
GhidraFolderData folderData =
|
|
||||||
getRootFolderData().getFolderPathData(folderPath, false);
|
|
||||||
if (folderData != null) {
|
|
||||||
checkoutList.add(new GhidraFile(folderData.getDomainFolder(), name));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
GhidraFolderData folderData = getRootFolderData().getFolderPathData(folderPath, false);
|
||||||
|
for (String name : folderData.getFileNames()) {
|
||||||
|
monitor.checkCancelled();
|
||||||
|
GhidraFileData fileData = folderData.getFileData(name, false);
|
||||||
|
if (fileData != null && fileData.isCheckedOut()) {
|
||||||
|
checkoutList.add(new GhidraFile(folderData.getDomainFolder(), name));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -113,6 +113,46 @@ public class GhidraFileData {
|
|||||||
refresh();
|
refresh();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct a new file instance with a specified name and a corresponding parent folder using
|
||||||
|
* up-to-date folder items.
|
||||||
|
* @param parent parent folder
|
||||||
|
* @param name file name
|
||||||
|
* @param folderItem local folder item
|
||||||
|
* @param versionedFolderItem versioned folder item
|
||||||
|
*/
|
||||||
|
GhidraFileData(GhidraFolderData parent, String name, LocalFolderItem folderItem,
|
||||||
|
FolderItem versionedFolderItem) {
|
||||||
|
this.parent = parent;
|
||||||
|
this.name = name;
|
||||||
|
this.folderItem = folderItem;
|
||||||
|
this.versionedFolderItem = versionedFolderItem;
|
||||||
|
|
||||||
|
this.projectData = parent.getProjectData();
|
||||||
|
this.fileSystem = parent.getLocalFileSystem();
|
||||||
|
this.versionedFileSystem = parent.getVersionedFileSystem();
|
||||||
|
this.listener = parent.getChangeListener();
|
||||||
|
|
||||||
|
validateCheckout();
|
||||||
|
updateFileID();
|
||||||
|
}
|
||||||
|
|
||||||
|
void refresh(LocalFolderItem localFolderItem, FolderItem verFolderItem) {
|
||||||
|
icon = null;
|
||||||
|
disabledIcon = null;
|
||||||
|
|
||||||
|
this.folderItem = localFolderItem;
|
||||||
|
this.versionedFolderItem = verFolderItem;
|
||||||
|
|
||||||
|
validateCheckout();
|
||||||
|
boolean fileIDset = updateFileID();
|
||||||
|
|
||||||
|
if (parent.visited()) {
|
||||||
|
// NOTE: we should maintain some cached data so we can determine if something really changed
|
||||||
|
listener.domainFileStatusChanged(getDomainFile(), fileIDset);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private boolean refresh() throws IOException {
|
private boolean refresh() throws IOException {
|
||||||
String parentPath = parent.getPathname();
|
String parentPath = parent.getPathname();
|
||||||
if (folderItem == null) {
|
if (folderItem == null) {
|
||||||
@@ -138,9 +178,12 @@ public class GhidraFileData {
|
|||||||
if (folderItem == null && versionedFolderItem == null) {
|
if (folderItem == null && versionedFolderItem == null) {
|
||||||
throw new FileNotFoundException(name + " not found");
|
throw new FileNotFoundException(name + " not found");
|
||||||
}
|
}
|
||||||
|
return updateFileID();
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean updateFileID() {
|
||||||
boolean fileIdWasNull = fileID == null;
|
boolean fileIdWasNull = fileID == null;
|
||||||
fileID = folderItem != null ? folderItem.getFileID() : versionedFolderItem.getFileID();
|
fileID = folderItem != null ? folderItem.getFileID() : versionedFolderItem.getFileID();
|
||||||
|
|
||||||
return fileIdWasNull && fileID != null;
|
return fileIdWasNull && fileID != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -157,14 +200,16 @@ public class GhidraFileData {
|
|||||||
disabledIcon = null;
|
disabledIcon = null;
|
||||||
fileIDset |= refresh();
|
fileIDset |= refresh();
|
||||||
if (parent.visited()) {
|
if (parent.visited()) {
|
||||||
|
// NOTE: we should maintain some cached data so we can determine if something really changed
|
||||||
listener.domainFileStatusChanged(getDomainFile(), fileIDset);
|
listener.domainFileStatusChanged(getDomainFile(), fileIDset);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void validateCheckout() throws IOException {
|
private void validateCheckout() {
|
||||||
if (fileSystem.isReadOnly() || !versionedFileSystem.isOnline()) {
|
if (fileSystem.isReadOnly() || !versionedFileSystem.isOnline()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
try {
|
||||||
if (folderItem != null && folderItem.isCheckedOut()) {
|
if (folderItem != null && folderItem.isCheckedOut()) {
|
||||||
// Cleanup checkout status which may be stale
|
// Cleanup checkout status which may be stale
|
||||||
if (versionedFolderItem != null) {
|
if (versionedFolderItem != null) {
|
||||||
@@ -179,6 +224,10 @@ public class GhidraFileData {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
catch (IOException e) {
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Perform file in-use / busy check
|
* Perform file in-use / busy check
|
||||||
|
|||||||
+54
-70
@@ -24,8 +24,10 @@ import ghidra.framework.model.*;
|
|||||||
import ghidra.framework.protocol.ghidra.GhidraURL;
|
import ghidra.framework.protocol.ghidra.GhidraURL;
|
||||||
import ghidra.framework.protocol.ghidra.TransientProjectData;
|
import ghidra.framework.protocol.ghidra.TransientProjectData;
|
||||||
import ghidra.framework.store.FileSystem;
|
import ghidra.framework.store.FileSystem;
|
||||||
|
import ghidra.framework.store.FolderItem;
|
||||||
import ghidra.framework.store.FolderNotEmptyException;
|
import ghidra.framework.store.FolderNotEmptyException;
|
||||||
import ghidra.framework.store.local.LocalFileSystem;
|
import ghidra.framework.store.local.LocalFileSystem;
|
||||||
|
import ghidra.framework.store.local.LocalFolderItem;
|
||||||
import ghidra.util.*;
|
import ghidra.util.*;
|
||||||
import ghidra.util.exception.*;
|
import ghidra.util.exception.*;
|
||||||
import ghidra.util.task.TaskMonitor;
|
import ghidra.util.task.TaskMonitor;
|
||||||
@@ -60,7 +62,7 @@ class GhidraFolderData {
|
|||||||
|
|
||||||
// folderList and fileList are only be used if visited is true
|
// folderList and fileList are only be used if visited is true
|
||||||
private Set<String> folderList = new TreeSet<>();
|
private Set<String> folderList = new TreeSet<>();
|
||||||
private Set<String> fileList = new TreeSet<>();
|
|
||||||
private boolean visited; // true if full refresh was performed
|
private boolean visited; // true if full refresh was performed
|
||||||
|
|
||||||
private Map<String, GhidraFileData> fileDataCache = new HashMap<>();
|
private Map<String, GhidraFileData> fileDataCache = new HashMap<>();
|
||||||
@@ -172,7 +174,7 @@ class GhidraFolderData {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Get folder data for specified absolute or relative folderPath
|
* Get folder data for specified absolute or relative folderPath
|
||||||
* @param folderPath
|
* @param folderPath absolute or relative folder path
|
||||||
* @param lazy if true folder will not be searched for if not already discovered - in
|
* @param lazy if true folder will not be searched for if not already discovered - in
|
||||||
* this case null will be returned
|
* this case null will be returned
|
||||||
* @return folder data or null if not found or lazy=true and not yet discovered
|
* @return folder data or null if not found or lazy=true and not yet discovered
|
||||||
@@ -236,9 +238,10 @@ class GhidraFolderData {
|
|||||||
}
|
}
|
||||||
updateExistenceState();
|
updateExistenceState();
|
||||||
checkInUse();
|
checkInUse();
|
||||||
boolean sendEvent = true;
|
|
||||||
String oldName = name;
|
String oldName = name;
|
||||||
String parentPath = parent.getPathname();
|
String parentPath = parent.getPathname();
|
||||||
|
|
||||||
if (folderExists) {
|
if (folderExists) {
|
||||||
fileSystem.renameFolder(parentPath, name, newName);
|
fileSystem.renameFolder(parentPath, name, newName);
|
||||||
}
|
}
|
||||||
@@ -247,8 +250,8 @@ class GhidraFolderData {
|
|||||||
versionedFileSystem.renameFolder(parentPath, name, newName);
|
versionedFileSystem.renameFolder(parentPath, name, newName);
|
||||||
}
|
}
|
||||||
catch (IOException e) {
|
catch (IOException e) {
|
||||||
sendEvent = false;
|
|
||||||
if (folderExists) {
|
if (folderExists) {
|
||||||
|
// revert local folder name
|
||||||
fileSystem.renameFolder(parentPath, newName, name);
|
fileSystem.renameFolder(parentPath, newName, name);
|
||||||
}
|
}
|
||||||
throw e;
|
throw e;
|
||||||
@@ -260,18 +263,14 @@ class GhidraFolderData {
|
|||||||
name = newName;
|
name = newName;
|
||||||
parent.folderDataCache.put(newName, this);
|
parent.folderDataCache.put(newName, this);
|
||||||
|
|
||||||
fileDataCache.clear();
|
|
||||||
folderDataCache.clear();
|
|
||||||
|
|
||||||
GhidraFolder newFolder = getDomainFolder();
|
GhidraFolder newFolder = getDomainFolder();
|
||||||
|
|
||||||
if (parent.visited) {
|
if (parent.visited) {
|
||||||
parent.folderList.remove(oldName);
|
parent.folderList.remove(oldName);
|
||||||
parent.folderList.add(newName);
|
parent.folderList.add(newName);
|
||||||
if (sendEvent) {
|
|
||||||
listener.domainFolderRenamed(newFolder, oldName);
|
listener.domainFolderRenamed(newFolder, oldName);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
return newFolder;
|
return newFolder;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -322,7 +321,7 @@ class GhidraFolderData {
|
|||||||
boolean isEmpty() {
|
boolean isEmpty() {
|
||||||
try {
|
try {
|
||||||
refresh(false, false, null); // visited will be true upon return
|
refresh(false, false, null); // visited will be true upon return
|
||||||
return folderList.isEmpty() && fileList.isEmpty();
|
return folderList.isEmpty() && fileDataCache.isEmpty();
|
||||||
}
|
}
|
||||||
catch (IOException e) {
|
catch (IOException e) {
|
||||||
// TODO: what should we return if folder not found or error occurs?
|
// TODO: what should we return if folder not found or error occurs?
|
||||||
@@ -343,7 +342,7 @@ class GhidraFolderData {
|
|||||||
Msg.error(this, "Folder refresh failed: " + e.getMessage());
|
Msg.error(this, "Folder refresh failed: " + e.getMessage());
|
||||||
return new ArrayList<>();
|
return new ArrayList<>();
|
||||||
}
|
}
|
||||||
return new ArrayList<>(fileList);
|
return new ArrayList<>(fileDataCache.keySet());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -375,12 +374,6 @@ class GhidraFolderData {
|
|||||||
!newFileName.equals(fileData.getName())) {
|
!newFileName.equals(fileData.getName())) {
|
||||||
throw new AssertException();
|
throw new AssertException();
|
||||||
}
|
}
|
||||||
if (visited) {
|
|
||||||
fileList.remove(oldFileName);
|
|
||||||
}
|
|
||||||
if (visited) {
|
|
||||||
fileList.add(newFileName);
|
|
||||||
}
|
|
||||||
fileDataCache.put(newFileName, fileData);
|
fileDataCache.put(newFileName, fileData);
|
||||||
if (visited) {
|
if (visited) {
|
||||||
listener.domainFileRenamed(getDomainFile(newFileName), oldFileName);
|
listener.domainFileRenamed(getDomainFile(newFileName), oldFileName);
|
||||||
@@ -404,12 +397,6 @@ class GhidraFolderData {
|
|||||||
!newFileName.equals(fileData.getName())) {
|
!newFileName.equals(fileData.getName())) {
|
||||||
throw new AssertException();
|
throw new AssertException();
|
||||||
}
|
}
|
||||||
if (visited) {
|
|
||||||
fileList.remove(oldFileName);
|
|
||||||
}
|
|
||||||
if (newParent.visited) {
|
|
||||||
newParent.fileList.add(newFileName);
|
|
||||||
}
|
|
||||||
newParent.fileDataCache.put(newFileName, fileData);
|
newParent.fileDataCache.put(newFileName, fileData);
|
||||||
}
|
}
|
||||||
if (visited || newParent.visited) {
|
if (visited || newParent.visited) {
|
||||||
@@ -436,7 +423,6 @@ class GhidraFolderData {
|
|||||||
fileData.dispose();
|
fileData.dispose();
|
||||||
fileDataCache.remove(fileName);
|
fileDataCache.remove(fileName);
|
||||||
if (visited) {
|
if (visited) {
|
||||||
fileList.remove(fileName);
|
|
||||||
listener.domainFileRemoved(getDomainFolder(), fileName, fileID);
|
listener.domainFileRemoved(getDomainFolder(), fileName, fileID);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -445,21 +431,13 @@ class GhidraFolderData {
|
|||||||
if (visited) {
|
if (visited) {
|
||||||
try {
|
try {
|
||||||
fileData = addFileData(fileName);
|
fileData = addFileData(fileName);
|
||||||
|
if (fileData != null) {
|
||||||
|
listener.domainFileAdded(fileData.getDomainFile());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch (IOException e) {
|
catch (IOException e) {
|
||||||
// ignore
|
// ignore
|
||||||
}
|
}
|
||||||
if (fileData == null) {
|
|
||||||
if (fileList.remove(fileName)) {
|
|
||||||
listener.domainFileRemoved(getDomainFolder(), fileName, null);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (fileList.add(fileName)) {
|
|
||||||
listener.domainFileAdded(fileData.getDomainFile());
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
listener.domainFileStatusChanged(fileData.getDomainFile(), false);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -532,7 +510,6 @@ class GhidraFolderData {
|
|||||||
void dispose() {
|
void dispose() {
|
||||||
visited = false;
|
visited = false;
|
||||||
folderList.clear();
|
folderList.clear();
|
||||||
fileList.clear();
|
|
||||||
for (GhidraFolderData folderData : folderDataCache.values()) {
|
for (GhidraFolderData folderData : folderDataCache.values()) {
|
||||||
folderData.dispose();
|
folderData.dispose();
|
||||||
}
|
}
|
||||||
@@ -636,17 +613,28 @@ class GhidraFolderData {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private <T extends FolderItem> Map<String, T> itemMapOf(T[] items) {
|
||||||
|
Map<String, T> map = new HashMap<>();
|
||||||
|
for (T item : items) {
|
||||||
|
if (item != null) {
|
||||||
|
map.put(item.getName(), item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
|
||||||
private void refreshFiles(TaskMonitor monitor) throws IOException {
|
private void refreshFiles(TaskMonitor monitor) throws IOException {
|
||||||
|
|
||||||
String path = getPathname();
|
String path = getPathname();
|
||||||
|
|
||||||
boolean hadError = false;
|
Map<String, LocalFolderItem> localItemMap = Map.of();
|
||||||
|
Map<String, FolderItem> versionedItemMap = Map.of();
|
||||||
|
|
||||||
HashSet<String> newSet = new HashSet<>();
|
HashSet<String> newSet = new HashSet<>();
|
||||||
if (folderExists) {
|
if (folderExists) {
|
||||||
try {
|
try {
|
||||||
String[] items = fileSystem.getItemNames(path);
|
localItemMap = itemMapOf(fileSystem.getItems(path));
|
||||||
newSet.addAll(Arrays.asList(items));
|
newSet.addAll(localItemMap.keySet());
|
||||||
}
|
}
|
||||||
catch (IOException e) {
|
catch (IOException e) {
|
||||||
if (parent != null) {
|
if (parent != null) {
|
||||||
@@ -657,8 +645,8 @@ class GhidraFolderData {
|
|||||||
}
|
}
|
||||||
if (versionedFolderExists) {
|
if (versionedFolderExists) {
|
||||||
try {
|
try {
|
||||||
String[] items = versionedFileSystem.getItemNames(path);
|
versionedItemMap = itemMapOf(versionedFileSystem.getItems(path));
|
||||||
newSet.addAll(Arrays.asList(items));
|
newSet.addAll(versionedItemMap.keySet());
|
||||||
}
|
}
|
||||||
catch (Exception e) {
|
catch (Exception e) {
|
||||||
Msg.error(this, "versioned folder refresh failed: " + e.getMessage());
|
Msg.error(this, "versioned folder refresh failed: " + e.getMessage());
|
||||||
@@ -667,7 +655,7 @@ class GhidraFolderData {
|
|||||||
}
|
}
|
||||||
|
|
||||||
HashSet<String> oldSet = new HashSet<>();
|
HashSet<String> oldSet = new HashSet<>();
|
||||||
for (String file : fileList) {
|
for (String file : fileDataCache.keySet()) {
|
||||||
oldSet.add(file);
|
oldSet.add(file);
|
||||||
}
|
}
|
||||||
HashSet<String> oldSetClone = new HashSet<>(oldSet);
|
HashSet<String> oldSetClone = new HashSet<>(oldSet);
|
||||||
@@ -679,23 +667,15 @@ class GhidraFolderData {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// refresh existing
|
// refresh existing
|
||||||
for (String fileName : fileList.toArray(new String[fileList.size()])) {
|
for (GhidraFileData fileData : fileDataCache.values()) {
|
||||||
GhidraFileData fileData = fileDataCache.get(fileName);
|
String fileName = fileData.getName();
|
||||||
if (fileData != null) {
|
LocalFolderItem localFolderItem = localItemMap.get(fileName);
|
||||||
try {
|
FolderItem versionedFolderItem = versionedItemMap.get(fileName);
|
||||||
fileData.statusChanged();
|
if (localFolderItem == null && versionedFolderItem == null) {
|
||||||
}
|
|
||||||
catch (IOException e) {
|
|
||||||
if (!(e instanceof FileNotFoundException)) {
|
|
||||||
if (hadError) {
|
|
||||||
throw e;
|
|
||||||
}
|
|
||||||
hadError = true; // tolerate single file error and remove file reference
|
|
||||||
Msg.error(this,
|
|
||||||
"Domain File error on " + fileData.getPathname() + ": " + e.toString());
|
|
||||||
}
|
|
||||||
fileRemoved(fileName);
|
fileRemoved(fileName);
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
|
fileData.refresh(localItemMap.get(fileName), versionedItemMap.get(fileName));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -705,15 +685,15 @@ class GhidraFolderData {
|
|||||||
if (monitor != null && monitor.isCancelled()) {
|
if (monitor != null && monitor.isCancelled()) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
GhidraFileData fileData = addFileData(fileName);
|
LocalFolderItem localFolderItem = localItemMap.get(fileName);
|
||||||
if (fileData != null) {
|
FolderItem versionedFolderItem = versionedItemMap.get(fileName);
|
||||||
fileList.add(fileName);
|
|
||||||
|
GhidraFileData fileData = addFileData(fileName, localFolderItem, versionedFolderItem);
|
||||||
if (visited) {
|
if (visited) {
|
||||||
listener.domainFileAdded(fileData.getDomainFile());
|
listener.domainFileAdded(fileData.getDomainFile());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
private void fileRemoved(String filename) {
|
private void fileRemoved(String filename) {
|
||||||
String fileID = null;
|
String fileID = null;
|
||||||
@@ -722,7 +702,6 @@ class GhidraFolderData {
|
|||||||
fileID = fileData.getFileID();
|
fileID = fileData.getFileID();
|
||||||
fileData.dispose();
|
fileData.dispose();
|
||||||
}
|
}
|
||||||
fileList.remove(filename);
|
|
||||||
if (visited) {
|
if (visited) {
|
||||||
listener.domainFileRemoved(getDomainFolder(), filename, fileID);
|
listener.domainFileRemoved(getDomainFolder(), filename, fileID);
|
||||||
}
|
}
|
||||||
@@ -860,7 +839,7 @@ class GhidraFolderData {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (visited) {
|
if (visited) {
|
||||||
return fileList.contains(fileName);
|
return false;
|
||||||
}
|
}
|
||||||
return addFileData(fileName) != null;
|
return addFileData(fileName) != null;
|
||||||
}
|
}
|
||||||
@@ -888,6 +867,15 @@ class GhidraFolderData {
|
|||||||
return fileData;
|
return fileData;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private GhidraFileData addFileData(String fileName, LocalFolderItem folderItem,
|
||||||
|
FolderItem versionedFolderItem) {
|
||||||
|
GhidraFileData fileData =
|
||||||
|
new GhidraFileData(this, fileName, folderItem, versionedFolderItem);
|
||||||
|
fileDataCache.put(fileName, fileData);
|
||||||
|
projectData.updateFileIndex(fileData);
|
||||||
|
return fileData;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get file data for child specified by fileName
|
* Get file data for child specified by fileName
|
||||||
* @param fileName name of file
|
* @param fileName name of file
|
||||||
@@ -1101,7 +1089,7 @@ class GhidraFolderData {
|
|||||||
if (fileSystem.getFolderNames(path).length != 0) {
|
if (fileSystem.getFolderNames(path).length != 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (fileSystem.getItemNames(path).length != 0) {
|
if (fileSystem.getItemNames(path, false).length != 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
delete();
|
delete();
|
||||||
@@ -1134,7 +1122,6 @@ class GhidraFolderData {
|
|||||||
throw new IllegalArgumentException("newParent must differ from current parent");
|
throw new IllegalArgumentException("newParent must differ from current parent");
|
||||||
}
|
}
|
||||||
checkInUse();
|
checkInUse();
|
||||||
boolean sendEvent = true;
|
|
||||||
|
|
||||||
updateExistenceState();
|
updateExistenceState();
|
||||||
try {
|
try {
|
||||||
@@ -1152,8 +1139,8 @@ class GhidraFolderData {
|
|||||||
newParent.getPathname());
|
newParent.getPathname());
|
||||||
}
|
}
|
||||||
catch (IOException e) {
|
catch (IOException e) {
|
||||||
sendEvent = false;
|
|
||||||
if (folderExists) {
|
if (folderExists) {
|
||||||
|
// revert local folder move
|
||||||
fileSystem.moveFolder(newParent.getPathname(), name,
|
fileSystem.moveFolder(newParent.getPathname(), name,
|
||||||
parent.getPathname());
|
parent.getPathname());
|
||||||
}
|
}
|
||||||
@@ -1168,9 +1155,6 @@ class GhidraFolderData {
|
|||||||
}
|
}
|
||||||
parent.folderDataCache.remove(name);
|
parent.folderDataCache.remove(name);
|
||||||
|
|
||||||
fileDataCache.clear();
|
|
||||||
folderDataCache.clear();
|
|
||||||
|
|
||||||
if (newParent.visited) {
|
if (newParent.visited) {
|
||||||
newParent.folderList.add(name);
|
newParent.folderList.add(name);
|
||||||
}
|
}
|
||||||
@@ -1179,7 +1163,7 @@ class GhidraFolderData {
|
|||||||
parent = newParent;
|
parent = newParent;
|
||||||
GhidraFolder newFolder = getDomainFolder();
|
GhidraFolder newFolder = getDomainFolder();
|
||||||
|
|
||||||
if (sendEvent && (parent.visited || newParent.visited)) {
|
if (parent.visited || newParent.visited) {
|
||||||
listener.domainFolderMoved(newFolder, oldParent);
|
listener.domainFolderMoved(newFolder, oldParent);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user