From eeba1b3e88956db5d3bcb77cb2b3edbab9b8684a Mon Sep 17 00:00:00 2001
From: AnimatedSwine37 <24914353+AnimatedSwine37@users.noreply.github.com>
Date: Sun, 16 Mar 2025 09:05:42 +1000
Subject: [PATCH 1/3] Fix QueryFullAttributesFile failing to emulate files
after all handles are closed
---
FileEmulationFramework/FileAccessServer.cs | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/FileEmulationFramework/FileAccessServer.cs b/FileEmulationFramework/FileAccessServer.cs
index 9297053..a4103a6 100644
--- a/FileEmulationFramework/FileAccessServer.cs
+++ b/FileEmulationFramework/FileAccessServer.cs
@@ -343,8 +343,10 @@ private static int NtCreateFileImpl(IntPtr* handle, FileAccess access, OBJECT_AT
lock (ThreadLock)
{
- HandleToInfoMap[hndl] = new(newFilePath, 0, emulatedFile);
+ fileInfo = new(newFilePath, 0, emulatedFile);
+ HandleToInfoMap[hndl] = fileInfo;
PathToHandleMap[newFilePath] = hndl;
+ PathToVirtualFileMap[newFilePath] = fileInfo;
}
return ntStatus;
}
From c95ac269e5a74730d7855dac29622bcf3d5273a5 Mon Sep 17 00:00:00 2001
From: AnimatedSwine37 <24914353+AnimatedSwine37@users.noreply.github.com>
Date: Thu, 20 Mar 2025 21:53:59 +1000
Subject: [PATCH 2/3] Update Atlus Script Tools to fix non English characters
breaking in messages
---
Submodules/Atlus-Script-Tools | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Submodules/Atlus-Script-Tools b/Submodules/Atlus-Script-Tools
index fce608b..0a74cf0 160000
--- a/Submodules/Atlus-Script-Tools
+++ b/Submodules/Atlus-Script-Tools
@@ -1 +1 @@
-Subproject commit fce608b6238f645b4f7bf6c7a5d70c0305d2499b
+Subproject commit 0a74cf05de30118ba60e9b1ff7b6cd61677085ed
From cec8a5c54edf7528b8d02a51df8451720ece1cdc Mon Sep 17 00:00:00 2001
From: AnimatedSwine37 <24914353+AnimatedSwine37@users.noreply.github.com>
Date: Sun, 23 Mar 2025 10:58:27 +1000
Subject: [PATCH 3/3] Fix QueryAttributesFileImpl not checking for files that
need to be created, use PathToHandleMap instead of PathToVirtualFileMap to
emulate attributes of new files
---
.../Utilities/Native.cs | 5 +
FileEmulationFramework/FileAccessServer.cs | 98 +++++++++++--------
2 files changed, 60 insertions(+), 43 deletions(-)
diff --git a/FileEmulationFramework.Lib/Utilities/Native.cs b/FileEmulationFramework.Lib/Utilities/Native.cs
index 1ac9a94..2f27cf7 100644
--- a/FileEmulationFramework.Lib/Utilities/Native.cs
+++ b/FileEmulationFramework.Lib/Utilities/Native.cs
@@ -11,6 +11,11 @@ namespace FileEmulationFramework.Lib.Utilities;
[ExcludeFromCodeCoverage(Justification = "Definitions for Interop.")]
public static class Native
{
+ ///
+ /// The value of a handle that is invalid
+ ///
+ public static IntPtr INVALID_HANDLE_VALUE = new IntPtr(-1);
+
///
///
/// Loads the specified module into the address space of the calling process. The specified module may cause other modules to be loaded.
diff --git a/FileEmulationFramework/FileAccessServer.cs b/FileEmulationFramework/FileAccessServer.cs
index a4103a6..68807af 100644
--- a/FileEmulationFramework/FileAccessServer.cs
+++ b/FileEmulationFramework/FileAccessServer.cs
@@ -32,7 +32,7 @@ public static unsafe class FileAccessServer
private static List Emulators { get; } = new();
private static readonly ConcurrentDictionary HandleToInfoMap = new();
- private static readonly ConcurrentDictionary PathToVirtualFileMap = new(StringComparer.OrdinalIgnoreCase);
+ private static readonly ConcurrentDictionary PathToVirtualFileMap = new(StringComparer.OrdinalIgnoreCase);
private static readonly ConcurrentDictionary PathToHandleMap = new(StringComparer.OrdinalIgnoreCase);
private static readonly object ThreadLock = new();
@@ -148,44 +148,11 @@ private static int QueryFullAttributesFileImpl(OBJECT_ATTRIBUTES* attributes, FI
var path = Strings.TrimWindowsPrefixes(attributes->ObjectName);
- SafeFileHandle? safeHandle = null;
-
- if (!PathToHandleMap.TryGetValue(path, out var hfile) || !HandleToInfoMap.TryGetValue(hfile, out var info))
- {
- // We haven't tried to create an emulated file for this yet, try it
- if (!PathToVirtualFileMap.TryGetValue(path, out info))
- {
- // Prevent recursion
- PathToVirtualFileMap[path] = null;
-
- // There is a virtual file but no handle exists for it, we need to make one
- try
- {
- safeHandle = File.OpenHandle(path);
- }
- catch (Exception e)
- {
- // If we couldn't make the handle this probably isn't a file we can emulate
- return result;
- }
-
- hfile = safeHandle.DangerousGetHandle();
-
- // We tried to make one but failed, this file isn't emulated
- if (!HandleToInfoMap.TryGetValue(hfile, out info))
- {
- safeHandle.Close();
- return result;
- }
- }
-
- // We've tried to create an emulated file for this before but failed, this file isn't emulated
- if (info == null)
- return result;
- }
+ if (!TryGetFileInfoFromPath(path, out var hfile, out var info, out var newFileHandle))
+ return result;
var oldSize = information->EndOfFile;
- var newSize = info.File.GetFileSize(hfile, info);
+ var newSize = info!.File.GetFileSize(hfile, info);
if (newSize != -1)
information->EndOfFile = newSize;
@@ -200,8 +167,7 @@ private static int QueryFullAttributesFileImpl(OBJECT_ATTRIBUTES* attributes, FI
}
// Clean up if we needed to make a new handle
- if (safeHandle != null)
- safeHandle.Close();
+ newFileHandle?.Close();
return result;
}
@@ -212,16 +178,19 @@ private static int QueryAttributesFileImpl(OBJECT_ATTRIBUTES* attributes, FILE_B
var result = _getFileAttributesHook.OriginalFunction.Value.Invoke(attributes, information);
var path = Strings.TrimWindowsPrefixes(attributes->ObjectName);
- if (!PathToHandleMap.TryGetValue(path, out var hfile) || !HandleToInfoMap.TryGetValue(hfile, out var info))
+ if (!TryGetFileInfoFromPath(path, out var hfile, out var info, out var newFileHandle))
return result;
- if (info.File.TryGetLastWriteTime(hfile, info, out var newWriteTime))
+ if (info!.File.TryGetLastWriteTime(hfile, info, out var newWriteTime))
{
var oldWriteTime = information->LastWriteTime.ToDateTime();
information->LastWriteTime = new LARGE_INTEGER(newWriteTime!.Value.ToFileTimeUtc());
_logger.Info("[FileAccessServer] File Last Write Override | Old: {0}, New: {1} | {2}", oldWriteTime,
newWriteTime, path);
}
+
+ // Clean up if we needed to make a new handle
+ newFileHandle?.Close();
return result;
}
@@ -323,7 +292,7 @@ private static int NtCreateFileImpl(IntPtr* handle, FileAccess access, OBJECT_AT
_logger.Debug("[FileAccessServer] Accessing: {0}, {1}, Route: {2}", hndl, newFilePath, _currentRoute.FullPath);
// Try Accept New File (virtual override)
- if (PathToVirtualFileMap.TryGetValue(newFilePath, out var fileInfo) && fileInfo != null)
+ if (PathToVirtualFileMap.TryGetValue(newFilePath, out var fileInfo))
{
// Reuse of emulated file (backed by stream) is safe because file access is single threaded.
lock (ThreadLock)
@@ -346,7 +315,6 @@ private static int NtCreateFileImpl(IntPtr* handle, FileAccess access, OBJECT_AT
fileInfo = new(newFilePath, 0, emulatedFile);
HandleToInfoMap[hndl] = fileInfo;
PathToHandleMap[newFilePath] = hndl;
- PathToVirtualFileMap[newFilePath] = fileInfo;
}
return ntStatus;
}
@@ -359,6 +327,50 @@ private static int NtCreateFileImpl(IntPtr* handle, FileAccess access, OBJECT_AT
}
}
+ ///
+ /// Tries to get the FileInformation for an emulated file based on its path.
+ /// If a file at the specified path has never been created before this will attempt to create it, making the emulated file.
+ ///
+ /// The path to the file to try and get information for
+ /// The handle to the emulated file if there was one
+ /// The FileInformation for the emulated file if there is one, null otherwise
+ /// A safe handle to the new file that was created, if one had to be created. Make sure to close this when you are done with the file info!
+ /// True if the file information for an emulated file with the specified path was found, false otherwise.
+ private static bool TryGetFileInfoFromPath(string path, out IntPtr hfile, out FileInformation? fileInfo, out SafeFileHandle? newFileHandle)
+ {
+ newFileHandle = null;
+ fileInfo = null;
+
+ // We haven't tried to create an emulated file for this yet, try it
+ if (!PathToHandleMap.TryGetValue(path, out hfile) || (hfile != INVALID_HANDLE_VALUE && !HandleToInfoMap.TryGetValue(hfile, out fileInfo)))
+ {
+ // Prevent recursion
+ PathToHandleMap[path] = INVALID_HANDLE_VALUE;
+
+ // There is a virtual file but no handle exists for it, we need to make one
+ try
+ {
+ newFileHandle = File.OpenHandle(path);
+ }
+ catch (Exception e)
+ {
+ // If we couldn't make the handle this probably isn't a file we can emulate
+ return false;
+ }
+
+ hfile = newFileHandle.DangerousGetHandle();
+
+ // We tried to make one but failed, this file isn't emulated
+ if (!HandleToInfoMap.TryGetValue(hfile, out fileInfo))
+ {
+ newFileHandle.Close();
+ return false;
+ }
+ }
+
+ return fileInfo != null;
+ }
+
///
/// Determines if the given handle refers to a directory.
///