sharpemu/tests/SharpEmu.Libs.Tests/Kernel/KernelFileRenameTests.cs

103 lines
4.1 KiB
C#

// Copyright (C) 2026 SharpEmu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
using SharpEmu.HLE;
using SharpEmu.Libs.Kernel;
using Xunit;
namespace SharpEmu.Libs.Tests.Kernel;
[Collection(KernelMemoryCompatStateCollection.Name)]
public sealed class KernelFileRenameTests
{
private const ulong MemoryBase = 0x1_0000_0000;
private const ulong SourcePathAddress = MemoryBase + 0x100;
private const ulong DestinationPathAddress = MemoryBase + 0x300;
private const ulong ReadBufferAddress = MemoryBase + 0x500;
[Fact]
public void KernelRename_ReplacesDestinationWhileSourceDescriptorRemainsOpen()
{
var identity = Guid.NewGuid().ToString("N");
var mountPoint = $"/rename-{identity}";
var hostRoot = Path.Combine(Path.GetTempPath(), $"sharpemu-rename-{identity}");
var sourceHostPath = Path.Combine(hostRoot, "STEMP000.DAT");
var destinationHostPath = Path.Combine(hostRoot, "SDATA000.DAT");
var sourceGuestPath = $"{mountPoint}/STEMP000.DAT";
var destinationGuestPath = $"{mountPoint}/SDATA000.DAT";
byte[] payload = [0x10, 0x20, 0x30, 0x40];
var fileDescriptor = -1;
Directory.CreateDirectory(hostRoot);
File.WriteAllBytes(sourceHostPath, payload);
File.WriteAllBytes(destinationHostPath, [0xFF]);
KernelMemoryCompatExports.RegisterGuestPathMount(mountPoint, hostRoot);
var memory = new FakeCpuMemory(MemoryBase, 0x1000);
var context = new CpuContext(memory, Generation.Gen5);
memory.WriteCString(SourcePathAddress, sourceGuestPath);
memory.WriteCString(DestinationPathAddress, destinationGuestPath);
try
{
context[CpuRegister.Rdi] = SourcePathAddress;
context[CpuRegister.Rsi] = 0x2;
Assert.Equal(
(int)OrbisGen2Result.ORBIS_GEN2_OK,
KernelExports.KernelOpen(context));
fileDescriptor = unchecked((int)context[CpuRegister.Rax]);
Assert.True(fileDescriptor >= 3);
context[CpuRegister.Rdi] = SourcePathAddress;
context[CpuRegister.Rsi] = DestinationPathAddress;
Assert.Equal(
(int)OrbisGen2Result.ORBIS_GEN2_OK,
KernelMemoryCompatExports.KernelRename(context));
Assert.False(File.Exists(sourceHostPath));
var renamedPayload = new byte[payload.Length];
using (var destinationStream = new FileStream(
destinationHostPath,
FileMode.Open,
FileAccess.Read,
FileShare.ReadWrite | FileShare.Delete))
{
destinationStream.ReadExactly(renamedPayload);
}
Assert.Equal(payload, renamedPayload);
context[CpuRegister.Rdi] = unchecked((uint)fileDescriptor);
context[CpuRegister.Rsi] = ReadBufferAddress;
context[CpuRegister.Rdx] = unchecked((uint)payload.Length);
Assert.Equal(
(int)OrbisGen2Result.ORBIS_GEN2_OK,
KernelMemoryCompatExports.KernelReadUnderscore(context));
Assert.Equal((ulong)payload.Length, context[CpuRegister.Rax]);
Span<byte> actual = stackalloc byte[payload.Length];
Assert.True(memory.TryRead(ReadBufferAddress, actual));
Assert.True(actual.SequenceEqual(payload));
context[CpuRegister.Rdi] = unchecked((uint)fileDescriptor);
Assert.Equal(
(int)OrbisGen2Result.ORBIS_GEN2_OK,
KernelMemoryCompatExports.KernelClose(context));
fileDescriptor = -1;
Assert.Equal(payload, File.ReadAllBytes(destinationHostPath));
}
finally
{
if (fileDescriptor >= 0)
{
context[CpuRegister.Rdi] = unchecked((uint)fileDescriptor);
_ = KernelMemoryCompatExports.KernelClose(context);
}
_ = KernelMemoryCompatExports.UnregisterGuestPathMount(mountPoint);
if (Directory.Exists(hostRoot))
{
Directory.Delete(hostRoot, recursive: true);
}
}
}
}