mirror of
https://github.com/par274/sharpemu.git
synced 2026-07-25 20:28:48 +08:00
fix(kernel): finish Posix -1/errno for file ops and open EACCES (#567)
Map UnauthorizedAccess on open to PERMISSION_DENIED and route Posix lseek/pread/pwrite/rename/etc failures through PosixFailure so libc-style callers get RAX=-1 plus TLS errno, matching open/read/write.
This commit is contained in:
@@ -46,7 +46,13 @@ public static partial class KernelMemoryCompatExports
|
||||
|
||||
[SysAbiExport(Nid = "ezv-RSBNKqI", ExportName = "pread",
|
||||
Target = Generation.Gen4 | Generation.Gen5, LibraryName = "libKernel")]
|
||||
public static int PosixPread(CpuContext ctx) => KernelPreadCore(ctx);
|
||||
public static int PosixPread(CpuContext ctx)
|
||||
{
|
||||
var result = KernelPreadCore(ctx);
|
||||
return result == (int)OrbisGen2Result.ORBIS_GEN2_OK
|
||||
? 0
|
||||
: PosixFailure(ctx, result, notFoundErrno: Ebadf);
|
||||
}
|
||||
|
||||
[SysAbiExport(Nid = "+r3rMFwItV4", ExportName = "sceKernelPread",
|
||||
Target = Generation.Gen4 | Generation.Gen5, LibraryName = "libKernel")]
|
||||
@@ -97,7 +103,13 @@ public static partial class KernelMemoryCompatExports
|
||||
|
||||
[SysAbiExport(Nid = "C2kJ-byS5rM", ExportName = "pwrite",
|
||||
Target = Generation.Gen4 | Generation.Gen5, LibraryName = "libKernel")]
|
||||
public static int PosixPwrite(CpuContext ctx) => KernelPwriteCore(ctx);
|
||||
public static int PosixPwrite(CpuContext ctx)
|
||||
{
|
||||
var result = KernelPwriteCore(ctx);
|
||||
return result == (int)OrbisGen2Result.ORBIS_GEN2_OK
|
||||
? 0
|
||||
: PosixFailure(ctx, result, notFoundErrno: Ebadf);
|
||||
}
|
||||
|
||||
[SysAbiExport(Nid = "nKWi-N2HBV4", ExportName = "sceKernelPwrite",
|
||||
Target = Generation.Gen4 | Generation.Gen5, LibraryName = "libKernel")]
|
||||
@@ -149,7 +161,13 @@ public static partial class KernelMemoryCompatExports
|
||||
|
||||
[SysAbiExport(Nid = "juWbTNM+8hw", ExportName = "fsync",
|
||||
Target = Generation.Gen4 | Generation.Gen5, LibraryName = "libKernel")]
|
||||
public static int PosixFsync(CpuContext ctx) => KernelFsyncCore(ctx);
|
||||
public static int PosixFsync(CpuContext ctx)
|
||||
{
|
||||
var result = KernelFsyncCore(ctx);
|
||||
return result == (int)OrbisGen2Result.ORBIS_GEN2_OK
|
||||
? 0
|
||||
: PosixFailure(ctx, result, notFoundErrno: Ebadf);
|
||||
}
|
||||
|
||||
[SysAbiExport(Nid = "fTx66l5iWIA", ExportName = "sceKernelFsync",
|
||||
Target = Generation.Gen4 | Generation.Gen5, LibraryName = "libKernel")]
|
||||
@@ -157,7 +175,13 @@ public static partial class KernelMemoryCompatExports
|
||||
|
||||
[SysAbiExport(Nid = "KIbJFQ0I1Cg", ExportName = "fdatasync",
|
||||
Target = Generation.Gen4 | Generation.Gen5, LibraryName = "libKernel")]
|
||||
public static int PosixFdatasync(CpuContext ctx) => KernelFsyncCore(ctx);
|
||||
public static int PosixFdatasync(CpuContext ctx)
|
||||
{
|
||||
var result = KernelFsyncCore(ctx);
|
||||
return result == (int)OrbisGen2Result.ORBIS_GEN2_OK
|
||||
? 0
|
||||
: PosixFailure(ctx, result, notFoundErrno: Ebadf);
|
||||
}
|
||||
|
||||
[SysAbiExport(Nid = "30Rh4ixbKy4", ExportName = "sceKernelFdatasync",
|
||||
Target = Generation.Gen4 | Generation.Gen5, LibraryName = "libKernel")]
|
||||
@@ -210,7 +234,13 @@ public static partial class KernelMemoryCompatExports
|
||||
|
||||
[SysAbiExport(Nid = "ih4CD9-gghM", ExportName = "ftruncate",
|
||||
Target = Generation.Gen4 | Generation.Gen5, LibraryName = "libKernel")]
|
||||
public static int PosixFtruncate(CpuContext ctx) => KernelFtruncateCore(ctx);
|
||||
public static int PosixFtruncate(CpuContext ctx)
|
||||
{
|
||||
var result = KernelFtruncateCore(ctx);
|
||||
return result == (int)OrbisGen2Result.ORBIS_GEN2_OK
|
||||
? 0
|
||||
: PosixFailure(ctx, result, notFoundErrno: Ebadf);
|
||||
}
|
||||
|
||||
[SysAbiExport(Nid = "VW3TVZiM4-E", ExportName = "sceKernelFtruncate",
|
||||
Target = Generation.Gen4 | Generation.Gen5, LibraryName = "libKernel")]
|
||||
@@ -246,7 +276,13 @@ public static partial class KernelMemoryCompatExports
|
||||
|
||||
[SysAbiExport(Nid = "ayrtszI7GBg", ExportName = "truncate",
|
||||
Target = Generation.Gen4 | Generation.Gen5, LibraryName = "libKernel")]
|
||||
public static int PosixTruncate(CpuContext ctx) => KernelTruncateCore(ctx);
|
||||
public static int PosixTruncate(CpuContext ctx)
|
||||
{
|
||||
var result = KernelTruncateCore(ctx);
|
||||
return result == (int)OrbisGen2Result.ORBIS_GEN2_OK
|
||||
? 0
|
||||
: PosixFailure(ctx, result);
|
||||
}
|
||||
|
||||
[SysAbiExport(Nid = "WlyEA-sLDf0", ExportName = "sceKernelTruncate",
|
||||
Target = Generation.Gen4 | Generation.Gen5, LibraryName = "libKernel")]
|
||||
@@ -294,7 +330,13 @@ public static partial class KernelMemoryCompatExports
|
||||
|
||||
[SysAbiExport(Nid = "NN01qLRhiqU", ExportName = "rename",
|
||||
Target = Generation.Gen4 | Generation.Gen5, LibraryName = "libKernel")]
|
||||
public static int PosixRename(CpuContext ctx) => KernelRenameCore(ctx);
|
||||
public static int PosixRename(CpuContext ctx)
|
||||
{
|
||||
var result = KernelRenameCore(ctx);
|
||||
return result == (int)OrbisGen2Result.ORBIS_GEN2_OK
|
||||
? 0
|
||||
: PosixFailure(ctx, result);
|
||||
}
|
||||
|
||||
[SysAbiExport(Nid = "52NcYU9+lEo", ExportName = "sceKernelRename",
|
||||
Target = Generation.Gen4 | Generation.Gen5, LibraryName = "libKernel")]
|
||||
@@ -363,7 +405,10 @@ public static partial class KernelMemoryCompatExports
|
||||
{
|
||||
if (!_openFiles.TryGetValue(fd, out var stream))
|
||||
{
|
||||
return (int)OrbisGen2Result.ORBIS_GEN2_ERROR_NOT_FOUND;
|
||||
return PosixFailure(
|
||||
ctx,
|
||||
(int)OrbisGen2Result.ORBIS_GEN2_ERROR_NOT_FOUND,
|
||||
notFoundErrno: Ebadf);
|
||||
}
|
||||
|
||||
// POSIX dup shares the open file description (and offset), which is
|
||||
@@ -386,7 +431,10 @@ public static partial class KernelMemoryCompatExports
|
||||
{
|
||||
if (!_openFiles.TryGetValue(oldFd, out var stream))
|
||||
{
|
||||
return (int)OrbisGen2Result.ORBIS_GEN2_ERROR_NOT_FOUND;
|
||||
return PosixFailure(
|
||||
ctx,
|
||||
(int)OrbisGen2Result.ORBIS_GEN2_ERROR_NOT_FOUND,
|
||||
notFoundErrno: Ebadf);
|
||||
}
|
||||
|
||||
if (oldFd == newFd)
|
||||
@@ -412,7 +460,13 @@ public static partial class KernelMemoryCompatExports
|
||||
|
||||
[SysAbiExport(Nid = "8nY19bKoiZk", ExportName = "fcntl",
|
||||
Target = Generation.Gen4 | Generation.Gen5, LibraryName = "libKernel")]
|
||||
public static int PosixFcntl(CpuContext ctx) => KernelFcntlCore(ctx);
|
||||
public static int PosixFcntl(CpuContext ctx)
|
||||
{
|
||||
var result = KernelFcntlCore(ctx);
|
||||
return result == (int)OrbisGen2Result.ORBIS_GEN2_OK
|
||||
? 0
|
||||
: PosixFailure(ctx, result, notFoundErrno: Ebadf);
|
||||
}
|
||||
|
||||
[SysAbiExport(Nid = "SoZkxZkCHaw", ExportName = "sceKernelFcntl",
|
||||
Target = Generation.Gen4 | Generation.Gen5, LibraryName = "libKernel")]
|
||||
|
||||
@@ -1558,7 +1558,7 @@ public static partial class KernelMemoryCompatExports
|
||||
{
|
||||
LogOpenTrace($"_open fail path='{guestPath}' host='{hostPath}' flags=0x{flags:X8} ex={ex.GetType().Name}: {ex.Message}");
|
||||
return ex is UnauthorizedAccessException
|
||||
? (int)OrbisGen2Result.ORBIS_GEN2_ERROR_INVALID_ARGUMENT
|
||||
? (int)OrbisGen2Result.ORBIS_GEN2_ERROR_PERMISSION_DENIED
|
||||
: (int)OrbisGen2Result.ORBIS_GEN2_ERROR_NOT_FOUND;
|
||||
}
|
||||
}
|
||||
@@ -2228,8 +2228,7 @@ public static partial class KernelMemoryCompatExports
|
||||
|
||||
if (result != OrbisGen2Result.ORBIS_GEN2_OK)
|
||||
{
|
||||
ctx[CpuRegister.Rax] = ulong.MaxValue;
|
||||
return (int)OrbisGen2Result.ORBIS_GEN2_OK;
|
||||
return PosixFailure(ctx, (int)result, notFoundErrno: Ebadf);
|
||||
}
|
||||
|
||||
ctx[CpuRegister.Rax] = unchecked((ulong)position);
|
||||
|
||||
@@ -125,6 +125,125 @@ public sealed class KernelMemoryCompatExportsTests
|
||||
Assert.Equal(ulong.MaxValue, context[CpuRegister.Rax]);
|
||||
}
|
||||
|
||||
// FreeBSD/PS4 EBADF; PosixFailure maps ORBIS NOT_FOUND on fd calls to this.
|
||||
private const int Ebadf = 9;
|
||||
// FreeBSD/PS4 EACCES; PosixFailure maps ORBIS PERMISSION_DENIED to this.
|
||||
private const int Eacces = 13;
|
||||
// TLS slot used by KernelRuntimeCompatExports.TrySetErrno (FsBase + 0x40).
|
||||
private const ulong TlsErrnoOffset = 0x40;
|
||||
|
||||
[Fact]
|
||||
public void PosixLseek_BadDescriptorReturnsMinusOneWithEbadf()
|
||||
{
|
||||
const ulong memoryBase = 0x1_0000_0000;
|
||||
const ulong fsBase = memoryBase + 0x100;
|
||||
var memory = new FakeCpuMemory(memoryBase, 0x1000);
|
||||
var context = new CpuContext(memory, Generation.Gen5)
|
||||
{
|
||||
FsBase = fsBase,
|
||||
};
|
||||
context[CpuRegister.Rdi] = 0x80020002; // never-opened / sentinel fd
|
||||
context[CpuRegister.Rsi] = 0;
|
||||
context[CpuRegister.Rdx] = 0; // SEEK_SET
|
||||
|
||||
var result = KernelMemoryCompatExports.PosixLseek(context);
|
||||
|
||||
Assert.Equal(-1, result);
|
||||
Assert.Equal(ulong.MaxValue, context[CpuRegister.Rax]);
|
||||
Assert.Equal(Ebadf, ReadErrno(memory, fsBase));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void PosixPread_BadDescriptorReturnsMinusOneWithEbadf()
|
||||
{
|
||||
const ulong memoryBase = 0x1_0000_0000;
|
||||
const ulong bufferAddress = memoryBase + 0x200;
|
||||
const ulong fsBase = memoryBase + 0x100;
|
||||
var memory = new FakeCpuMemory(memoryBase, 0x1000);
|
||||
var context = new CpuContext(memory, Generation.Gen5)
|
||||
{
|
||||
FsBase = fsBase,
|
||||
};
|
||||
context[CpuRegister.Rdi] = 0x80020002; // never-opened / sentinel fd
|
||||
context[CpuRegister.Rsi] = bufferAddress;
|
||||
context[CpuRegister.Rdx] = 0x40;
|
||||
context[CpuRegister.Rcx] = 0; // offset
|
||||
|
||||
var result = KernelMemoryCompatExports.PosixPread(context);
|
||||
|
||||
Assert.Equal(-1, result);
|
||||
Assert.Equal(ulong.MaxValue, context[CpuRegister.Rax]);
|
||||
Assert.Equal(Ebadf, ReadErrno(memory, fsBase));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void KernelPread_BadDescriptorStillReturnsOrbisNotFound()
|
||||
{
|
||||
// sceKernel* entry points keep the raw Orbis ABI; only Posix* maps to -1/errno.
|
||||
const ulong memoryBase = 0x1_0000_0000;
|
||||
const ulong bufferAddress = memoryBase + 0x200;
|
||||
var memory = new FakeCpuMemory(memoryBase, 0x1000);
|
||||
var context = new CpuContext(memory, Generation.Gen5);
|
||||
context[CpuRegister.Rdi] = 0x80020002;
|
||||
context[CpuRegister.Rsi] = bufferAddress;
|
||||
context[CpuRegister.Rdx] = 0x40;
|
||||
context[CpuRegister.Rcx] = 0;
|
||||
|
||||
var result = KernelMemoryCompatExports.KernelPread(context);
|
||||
|
||||
Assert.Equal((int)OrbisGen2Result.ORBIS_GEN2_ERROR_NOT_FOUND, result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void PosixOpen_MutatingApp0ReturnsMinusOneWithEacces()
|
||||
{
|
||||
// /app0 is read-only for mutating opens (retail semantics). That path
|
||||
// returns PERMISSION_DENIED which PosixFailure maps to EACCES - the same
|
||||
// errno UnauthorizedAccessException open failures now produce.
|
||||
var tempRoot = Path.Combine(
|
||||
Path.GetTempPath(),
|
||||
$"sharpemu-posix-open-eacces-{Guid.NewGuid():N}");
|
||||
var app0Root = Path.Combine(tempRoot, "app0");
|
||||
Directory.CreateDirectory(app0Root);
|
||||
KernelMemoryCompatExports.RegisterGuestPathMount("/app0", app0Root);
|
||||
|
||||
try
|
||||
{
|
||||
const ulong memoryBase = 0x1_0000_0000;
|
||||
const ulong pathAddress = memoryBase + 0x200;
|
||||
const ulong fsBase = memoryBase + 0x100;
|
||||
var memory = new FakeCpuMemory(memoryBase, 0x1000);
|
||||
var context = new CpuContext(memory, Generation.Gen5)
|
||||
{
|
||||
FsBase = fsBase,
|
||||
};
|
||||
memory.WriteCString(pathAddress, "/app0/readonly-create.bin");
|
||||
context[CpuRegister.Rdi] = pathAddress;
|
||||
context[CpuRegister.Rsi] = 0x0201; // O_WRONLY | O_CREAT
|
||||
|
||||
var result = KernelMemoryCompatExports.PosixOpen(context);
|
||||
|
||||
Assert.Equal(-1, result);
|
||||
Assert.Equal(ulong.MaxValue, context[CpuRegister.Rax]);
|
||||
Assert.Equal(Eacces, ReadErrno(memory, fsBase));
|
||||
}
|
||||
finally
|
||||
{
|
||||
KernelMemoryCompatExports.UnregisterGuestPathMount("/app0");
|
||||
if (Directory.Exists(tempRoot))
|
||||
{
|
||||
Directory.Delete(tempRoot, recursive: true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static int ReadErrno(FakeCpuMemory memory, ulong fsBase)
|
||||
{
|
||||
Span<byte> bytes = stackalloc byte[sizeof(int)];
|
||||
Assert.True(memory.TryRead(fsBase + TlsErrnoOffset, bytes));
|
||||
return BitConverter.ToInt32(bytes);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Sprintf_ReadsVariadicDoubleFromXmmRegister()
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user