Skip to content

Commit 676c220

Browse files
authored
Merge pull request #26 from AnimatedSwine37/localisation-framework-stuff
Fix file write time not being emulated after all handles were close, update script tools to fix bugs in bf emulator
2 parents d76bca9 + cec8a5c commit 676c220

File tree

3 files changed

+63
-44
lines changed

3 files changed

+63
-44
lines changed

FileEmulationFramework.Lib/Utilities/Native.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,11 @@ namespace FileEmulationFramework.Lib.Utilities;
1111
[ExcludeFromCodeCoverage(Justification = "Definitions for Interop.")]
1212
public static class Native
1313
{
14+
/// <summary>
15+
/// The value of a handle that is invalid
16+
/// </summary>
17+
public static IntPtr INVALID_HANDLE_VALUE = new IntPtr(-1);
18+
1419
/// <summary>
1520
/// <para>
1621
/// Loads the specified module into the address space of the calling process. The specified module may cause other modules to be loaded.

FileEmulationFramework/FileAccessServer.cs

Lines changed: 57 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ public static unsafe class FileAccessServer
3232
private static List<IEmulator> Emulators { get; } = new();
3333

3434
private static readonly ConcurrentDictionary<IntPtr, FileInformation> HandleToInfoMap = new();
35-
private static readonly ConcurrentDictionary<string, FileInformation?> PathToVirtualFileMap = new(StringComparer.OrdinalIgnoreCase);
35+
private static readonly ConcurrentDictionary<string, FileInformation> PathToVirtualFileMap = new(StringComparer.OrdinalIgnoreCase);
3636
private static readonly ConcurrentDictionary<string, IntPtr> PathToHandleMap = new(StringComparer.OrdinalIgnoreCase);
3737

3838
private static readonly object ThreadLock = new();
@@ -148,44 +148,11 @@ private static int QueryFullAttributesFileImpl(OBJECT_ATTRIBUTES* attributes, FI
148148

149149
var path = Strings.TrimWindowsPrefixes(attributes->ObjectName);
150150

151-
SafeFileHandle? safeHandle = null;
152-
153-
if (!PathToHandleMap.TryGetValue(path, out var hfile) || !HandleToInfoMap.TryGetValue(hfile, out var info))
154-
{
155-
// We haven't tried to create an emulated file for this yet, try it
156-
if (!PathToVirtualFileMap.TryGetValue(path, out info))
157-
{
158-
// Prevent recursion
159-
PathToVirtualFileMap[path] = null;
160-
161-
// There is a virtual file but no handle exists for it, we need to make one
162-
try
163-
{
164-
safeHandle = File.OpenHandle(path);
165-
}
166-
catch (Exception e)
167-
{
168-
// If we couldn't make the handle this probably isn't a file we can emulate
169-
return result;
170-
}
171-
172-
hfile = safeHandle.DangerousGetHandle();
173-
174-
// We tried to make one but failed, this file isn't emulated
175-
if (!HandleToInfoMap.TryGetValue(hfile, out info))
176-
{
177-
safeHandle.Close();
178-
return result;
179-
}
180-
}
181-
182-
// We've tried to create an emulated file for this before but failed, this file isn't emulated
183-
if (info == null)
184-
return result;
185-
}
151+
if (!TryGetFileInfoFromPath(path, out var hfile, out var info, out var newFileHandle))
152+
return result;
186153

187154
var oldSize = information->EndOfFile;
188-
var newSize = info.File.GetFileSize(hfile, info);
155+
var newSize = info!.File.GetFileSize(hfile, info);
189156
if (newSize != -1)
190157
information->EndOfFile = newSize;
191158

@@ -200,8 +167,7 @@ private static int QueryFullAttributesFileImpl(OBJECT_ATTRIBUTES* attributes, FI
200167
}
201168

202169
// Clean up if we needed to make a new handle
203-
if (safeHandle != null)
204-
safeHandle.Close();
170+
newFileHandle?.Close();
205171

206172
return result;
207173
}
@@ -212,16 +178,19 @@ private static int QueryAttributesFileImpl(OBJECT_ATTRIBUTES* attributes, FILE_B
212178
var result = _getFileAttributesHook.OriginalFunction.Value.Invoke(attributes, information);
213179
var path = Strings.TrimWindowsPrefixes(attributes->ObjectName);
214180

215-
if (!PathToHandleMap.TryGetValue(path, out var hfile) || !HandleToInfoMap.TryGetValue(hfile, out var info))
181+
if (!TryGetFileInfoFromPath(path, out var hfile, out var info, out var newFileHandle))
216182
return result;
217183

218-
if (info.File.TryGetLastWriteTime(hfile, info, out var newWriteTime))
184+
if (info!.File.TryGetLastWriteTime(hfile, info, out var newWriteTime))
219185
{
220186
var oldWriteTime = information->LastWriteTime.ToDateTime();
221187
information->LastWriteTime = new LARGE_INTEGER(newWriteTime!.Value.ToFileTimeUtc());
222188
_logger.Info("[FileAccessServer] File Last Write Override | Old: {0}, New: {1} | {2}", oldWriteTime,
223189
newWriteTime, path);
224190
}
191+
192+
// Clean up if we needed to make a new handle
193+
newFileHandle?.Close();
225194

226195
return result;
227196
}
@@ -329,7 +298,7 @@ private static NT_STATUS NtCreateFileImpl(IntPtr* handle, FileAccess access, OBJ
329298
_logger.Debug("[FileAccessServer] Accessing: {0}, {1}, Route: {2}", hndl, newFilePath, _currentRoute.FullPath);
330299

331300
// Try Accept New File (virtual override)
332-
if (PathToVirtualFileMap.TryGetValue(newFilePath, out var fileInfo) && fileInfo != null)
301+
if (PathToVirtualFileMap.TryGetValue(newFilePath, out var fileInfo))
333302
{
334303
// Reuse of emulated file (backed by stream) is safe because file access is single threaded.
335304
lock (ThreadLock)
@@ -349,7 +318,8 @@ private static NT_STATUS NtCreateFileImpl(IntPtr* handle, FileAccess access, OBJ
349318

350319
lock (ThreadLock)
351320
{
352-
HandleToInfoMap[hndl] = new(newFilePath, 0, emulatedFile);
321+
fileInfo = new(newFilePath, 0, emulatedFile);
322+
HandleToInfoMap[hndl] = fileInfo;
353323
PathToHandleMap[newFilePath] = hndl;
354324
}
355325
return ntStatus;
@@ -363,6 +333,50 @@ private static NT_STATUS NtCreateFileImpl(IntPtr* handle, FileAccess access, OBJ
363333
}
364334
}
365335

336+
/// <summary>
337+
/// Tries to get the FileInformation for an emulated file based on its path.
338+
/// If a file at the specified path has never been created before this will attempt to create it, making the emulated file.
339+
/// </summary>
340+
/// <param name="path">The path to the file to try and get information for</param>
341+
/// <param name="hfile">The handle to the emulated file if there was one</param>
342+
/// <param name="fileInfo">The FileInformation for the emulated file if there is one, null otherwise</param>
343+
/// <param name="newFileHandle">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!</param>
344+
/// <returns>True if the file information for an emulated file with the specified path was found, false otherwise.</returns>
345+
private static bool TryGetFileInfoFromPath(string path, out IntPtr hfile, out FileInformation? fileInfo, out SafeFileHandle? newFileHandle)
346+
{
347+
newFileHandle = null;
348+
fileInfo = null;
349+
350+
// We haven't tried to create an emulated file for this yet, try it
351+
if (!PathToHandleMap.TryGetValue(path, out hfile) || (hfile != INVALID_HANDLE_VALUE && !HandleToInfoMap.TryGetValue(hfile, out fileInfo)))
352+
{
353+
// Prevent recursion
354+
PathToHandleMap[path] = INVALID_HANDLE_VALUE;
355+
356+
// There is a virtual file but no handle exists for it, we need to make one
357+
try
358+
{
359+
newFileHandle = File.OpenHandle(path);
360+
}
361+
catch (Exception e)
362+
{
363+
// If we couldn't make the handle this probably isn't a file we can emulate
364+
return false;
365+
}
366+
367+
hfile = newFileHandle.DangerousGetHandle();
368+
369+
// We tried to make one but failed, this file isn't emulated
370+
if (!HandleToInfoMap.TryGetValue(hfile, out fileInfo))
371+
{
372+
newFileHandle.Close();
373+
return false;
374+
}
375+
}
376+
377+
return fileInfo != null;
378+
}
379+
366380
/// <summary>
367381
/// Determines if the given handle refers to a directory.
368382
/// </summary>

0 commit comments

Comments
 (0)